From f568a69bb2dd28c0b04dfe9ffe94f11b3ef3d936 Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Mon, 1 Apr 2013 18:39:42 +0300 Subject: [PATCH] http: implement 401 response handling --- src/core/sipe-http-request.c | 139 ++++++++++++++++++++++++++++++++++++++--- src/core/sipe-http-transport.h | 4 +- src/core/sipe-http.h | 5 +- 3 files changed, 135 insertions(+), 13 deletions(-) diff --git a/src/core/sipe-http-request.c b/src/core/sipe-http-request.c index 288b6eed..6cd097a2 100644 --- a/src/core/sipe-http-request.c +++ b/src/core/sipe-http-request.c @@ -25,6 +25,7 @@ * * - request handling: creation, parameters, deletion, cancelling * - session handling: creation, closing + * - client authorization handling * - connection request queue handling * - compile HTTP header contents and hand-off to transport layer * - process HTTP response and hand-off to user callback @@ -39,6 +40,7 @@ #include #include "sipmsg.h" +#include "sip-sec.h" #include "sipe-backend.h" #include "sipe-core.h" #include "sipe-core-private.h" @@ -62,6 +64,7 @@ struct sipe_http_request { gchar *headers; gchar *body; /* NULL for GET */ gchar *content_type; /* NULL if body == NULL */ + gchar *authorization; const gchar *domain; /* not copied */ const gchar *user; /* not copied */ @@ -75,6 +78,7 @@ struct sipe_http_request { #define SIPE_HTTP_REQUEST_FLAG_FIRST 0x00000001 #define SIPE_HTTP_REQUEST_FLAG_REDIRECT 0x00000002 +#define SIPE_HTTP_REQUEST_FLAG_AUTHDATA 0x00000004 static void sipe_http_request_free(struct sipe_core_private *sipe_private, struct sipe_http_request *req) @@ -86,6 +90,7 @@ static void sipe_http_request_free(struct sipe_core_private *sipe_private, g_free(req->headers); g_free(req->body); g_free(req->content_type); + g_free(req->authorization); g_free(req); } @@ -108,16 +113,21 @@ static void sipe_http_request_send(struct sipe_http_connection_public *conn_publ header = g_strdup_printf("%s /%s HTTP/1.1\r\n" "Host: %s\r\n" "User-Agent: Sipe/" PACKAGE_VERSION "\r\n" - "%s%s%s", + "%s%s%s%s", content ? "POST" : "GET", req->path, conn_public->host, + req->authorization ? req->authorization : "", req->headers ? req->headers : "", cookie ? cookie : "", content ? content : ""); g_free(cookie); g_free(content); + /* only use authorization once */ + g_free(req->authorization); + req->authorization = NULL; + sipe_http_transport_send(conn_public, header, req->body); @@ -134,11 +144,94 @@ void sipe_http_request_next(struct sipe_http_connection_public *conn_public) sipe_http_request_send(conn_public); } -void sipe_http_request_response(struct sipe_http_connection_public *conn_public, - struct sipmsg *msg) +static void sipe_http_request_response_unauthorized(struct sipe_core_private *sipe_private, + struct sipe_http_request *req, + struct sipmsg *msg) +{ + const gchar *header = NULL; + const gchar *name; + guint type; + gboolean failed = TRUE; + +#if defined(HAVE_LIBKRB5) || defined(HAVE_SSPI) +#define DEBUG_STRING "NTLM and Negotiate authentications are" + /* Use "Negotiate" unless the user requested "NTLM" */ + if (sipe_private->authentication_type != SIPE_AUTHENTICATION_TYPE_NTLM) + header = sipmsg_find_auth_header(msg, "Negotiate"); + if (header) { + type = SIPE_AUTHENTICATION_TYPE_NEGOTIATE; + name = "Negotiate"; + } else +#else +#define DEBUG_STRING "NTLM authentication is" +#endif + { + header = sipmsg_find_auth_header(msg, "NTLM"); + type = SIPE_AUTHENTICATION_TYPE_NTLM; + name = "NTLM"; + } + + if (header) { + struct sipe_http_connection_public *conn_public = req->connection; + + if (!conn_public->context) { + gboolean valid = req->flags & SIPE_HTTP_REQUEST_FLAG_AUTHDATA; + conn_public->context = sip_sec_create_context(type, + !valid, /* Single Sign-On flag */ + TRUE, /* connection-based for HTTP */ + valid ? req->domain : NULL, + valid ? req->user : NULL, + valid ? req->password : NULL); + } + + + if (conn_public->context) { + gchar **parts = g_strsplit(header, " ", 0); + gchar *spn = g_strdup_printf("HTTP/%s", conn_public->host); + gchar *token; + + SIPE_DEBUG_INFO("sipe_http_request_response_unauthorized: init context target '%s' token '%s'", + spn, parts[1] ? parts[1] : ""); + + if (sip_sec_init_context_step(conn_public->context, + spn, + parts[1], + &token, + NULL)) { + + /* generate authorization header */ + req->authorization = g_strdup_printf("Authorization: %s %s\r\n", + name, + token ? token : ""); + g_free(token); + + /* resend request with authorization */ + sipe_http_request_send(conn_public); + failed = FALSE; + + } else + SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: security context init step failed"); + + g_free(spn); + g_strfreev(parts); + } else + SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: security context creation failed"); + } else + SIPE_DEBUG_INFO_NOFORMAT("sipe_http_request_response_unauthorized: only " DEBUG_STRING " supported"); + + if (failed) { + /* Callback: authentication failed */ + (*req->cb)(sipe_private, 0, NULL, NULL, req->cb_data); + + /* remove completed request */ + sipe_http_request_cancel(req); + } +} + +static void sipe_http_request_response_callback(struct sipe_core_private *sipe_private, + struct sipe_http_request *req, + struct sipmsg *msg) { - struct sipe_core_private *sipe_private = conn_public->sipe_private; - struct sipe_http_request *req = conn_public->pending_requests->data; const gchar *hdr; /* Set-Cookie: RMID=732423sdfs73242; expires=Fri, 31-Dec-2010 23:59:59 GMT; path=/; domain=.example.net */ @@ -169,7 +262,7 @@ void sipe_http_request_response(struct sipe_http_connection_public *conn_public, if (new) { req->session->cookie = new; - SIPE_DEBUG_INFO("sipe_http_request_response: cookie: %s", new); + SIPE_DEBUG_INFO("sipe_http_request_response_callback: cookie: %s", new); } } @@ -184,6 +277,22 @@ void sipe_http_request_response(struct sipe_http_connection_public *conn_public, sipe_http_request_cancel(req); } +void sipe_http_request_response(struct sipe_http_connection_public *conn_public, + struct sipmsg *msg) +{ + struct sipe_core_private *sipe_private = conn_public->sipe_private; + struct sipe_http_request *req = conn_public->pending_requests->data; + + if (msg->response == SIPE_HTTP_STATUS_CLIENT_UNAUTHORIZED) { + sipe_http_request_response_unauthorized(sipe_private, req, msg); + /* req may no longer be valid */ + } else { + /* All other cases are passed on to the user */ + sipe_http_request_response_callback(sipe_private, req, msg); + /* req is no longer valid */ + } +} + void sipe_http_request_shutdown(struct sipe_http_connection_public *conn_public) { if (conn_public->pending_requests) { @@ -196,6 +305,11 @@ void sipe_http_request_shutdown(struct sipe_http_connection_public *conn_public) g_slist_free(conn_public->pending_requests); conn_public->pending_requests = NULL; } + + if (conn_public->context) { + sip_sec_destroy_context(conn_public->context); + conn_public->context = NULL; + } } struct sipe_http_request *sipe_http_request_new(struct sipe_core_private *sipe_private, @@ -219,10 +333,14 @@ struct sipe_http_request *sipe_http_request_new(struct sipe_core_private *sipe_p req->content_type = g_strdup(content_type); } + req->flags = 0; + /* default authentication */ - req->domain = sipe_private->authdomain; - req->user = sipe_private->authuser; - req->password = sipe_private->password; + if (!SIPE_CORE_PRIVATE_FLAG_IS(SSO)) + sipe_http_request_authentication(req, + sipe_private->authdomain, + sipe_private->authuser, + sipe_private->password); req->cb = callback; req->cb_data = callback_data; @@ -231,7 +349,7 @@ struct sipe_http_request *sipe_http_request_new(struct sipe_core_private *sipe_p host, port); if (!sipe_http_request_pending(conn_public)) - req->flags = SIPE_HTTP_REQUEST_FLAG_FIRST; + req->flags |= SIPE_HTTP_REQUEST_FLAG_FIRST; conn_public->pending_requests = g_slist_append(conn_public->pending_requests, req); @@ -290,6 +408,7 @@ void sipe_http_request_authentication(struct sipe_http_request *request, const gchar *user, const gchar *password) { + request->flags |= SIPE_HTTP_REQUEST_FLAG_AUTHDATA; request->domain = domain; request->user = user; request->password = password; diff --git a/src/core/sipe-http-transport.h b/src/core/sipe-http-transport.h index 8cf51542..2832a728 100644 --- a/src/core/sipe-http-transport.h +++ b/src/core/sipe-http-transport.h @@ -34,11 +34,13 @@ /* Forward declarations */ struct sipe_core_private; +struct sip_sec_context; struct sipe_http_connection_public { struct sipe_core_private *sipe_private; - GSList *pending_requests; /* handled by sipe-http-request.c */ + GSList *pending_requests; /* handled by sipe-http-request.c */ + struct sip_sec_context *context; /* handled by sipe-http-request.c */ gchar *host; guint32 port; diff --git a/src/core/sipe-http.h b/src/core/sipe-http.h index 06b23911..055474aa 100644 --- a/src/core/sipe-http.h +++ b/src/core/sipe-http.h @@ -51,8 +51,9 @@ typedef void (sipe_http_response_callback)(struct sipe_core_private *sipe_privat gpointer callback_data); /* HTTP response status codes */ -#define SIPE_HTTP_STATUS_OK 200 -#define SIPE_HTTP_STATUS_CLIENT_ERROR 400 /* - 499 */ +#define SIPE_HTTP_STATUS_OK 200 +#define SIPE_HTTP_STATUS_CLIENT_ERROR 400 /* - 499 */ +#define SIPE_HTTP_STATUS_CLIENT_UNAUTHORIZED 401 /** * Free HTTP data -- 2.11.4.GIT