Fix multiple WWW-Authenticate headers
authorHardening <rdp.effort@gmail.com>
Fri, 18 Apr 2014 21:08:34 +0000 (23:08 +0200)
committerHardening <rdp.effort@gmail.com>
Sat, 19 Apr 2014 07:14:14 +0000 (09:14 +0200)
This patch fixes the case with the server trying to negociate the authentication
method and returning multiple WWW-Authenticate headers.

libfreerdp/core/gateway/http.c
libfreerdp/core/gateway/http.h
libfreerdp/core/gateway/ncacn_http.c

index b71683d..c9f33f0 100644 (file)
@@ -345,51 +345,38 @@ BOOL http_response_parse_header_field(HttpResponse* http_response, char* name, c
        {
                http_response->ContentLength = atoi(value);
        }
-       else if (_stricmp(name, "Authorization") == 0)
+       else if (_stricmp(name, "WWW-Authenticate") == 0)
        {
                char* separator;
-
-               http_response->Authorization = _strdup(value);
-               if (!http_response->Authorization)
-                       return FALSE;
+               char *authScheme, *authValue;
 
                separator = strchr(value, ' ');
 
                if (separator != NULL)
                {
+                       /* WWW-Authenticate: Basic realm=""
+                        * WWW-Authenticate: NTLM base64token
+                        * WWW-Authenticate: Digest realm="testrealm@host.com", qop="auth, auth-int",
+                        *                                      nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",
+                        *                                      opaque="5ccc069c403ebaf9f0171e9517f40e41"
+                        */
                        *separator = '\0';
-                       http_response->AuthScheme = _strdup(value);
-                       http_response->AuthParam = _strdup(separator + 1);
-                       if (!http_response->AuthScheme || !http_response->AuthParam)
+
+                       authScheme = _strdup(value);
+                       authValue = _strdup(separator + 1);
+                       if (!authScheme || !authValue)
                                return FALSE;
                        *separator = ' ';
                }
-       }
-       else if (_stricmp(name, "WWW-Authenticate") == 0)
-       {
-               char* separator;
-
-               separator = strstr(value, "=\"");
-
-               if (separator != NULL)
+               else
                {
-                       /* WWW-Authenticate: parameter with spaces="value" */
-                       return FALSE;
+                       authScheme = _strdup(value);
+                       if (!authScheme)
+                               return FALSE;
+                       authValue = NULL;
                }
 
-               separator = strchr(value, ' ');
-
-               if (separator != NULL)
-               {
-                       /* WWW-Authenticate: NTLM base64token */
-
-                       *separator = '\0';
-                       http_response->AuthScheme = _strdup(value);
-                       http_response->AuthParam = _strdup(separator + 1);
-                       *separator = ' ';
-
-                       return TRUE;
-               }
+               return ListDictionary_Add(http_response->Authenticates, authScheme, authValue);
        }
        return TRUE;
 }
@@ -594,31 +581,53 @@ out_free:
        return NULL;
 }
 
+static BOOL strings_equals_nocase(void *obj1, void *obj2)
+{
+       if (!obj1 || !obj2)
+               return FALSE;
+
+       return _stricmp(obj1, obj2) == 0;
+}
+
+static void string_free(void *obj1)
+{
+       if (!obj1)
+               return;
+       free(obj1);
+}
+
 HttpResponse* http_response_new()
 {
-       return (HttpResponse *)calloc(1, sizeof(HttpResponse));
+       HttpResponse *ret = (HttpResponse *)calloc(1, sizeof(HttpResponse));
+       if (!ret)
+               return NULL;
+
+       ret->Authenticates = ListDictionary_New(FALSE);
+       ListDictionary_KeyObject(ret->Authenticates)->fnObjectEquals = strings_equals_nocase;
+       ListDictionary_KeyObject(ret->Authenticates)->fnObjectFree = string_free;
+       ListDictionary_ValueObject(ret->Authenticates)->fnObjectEquals = strings_equals_nocase;
+       ListDictionary_ValueObject(ret->Authenticates)->fnObjectFree = string_free;
+       return ret;
 }
 
 void http_response_free(HttpResponse* http_response)
 {
        int i;
 
-       if (http_response != NULL)
-       {
-               for (i = 0; i < http_response->count; i++)
-                       free(http_response->lines[i]);
+       if (!http_response)
+               return;
+
+       for (i = 0; i < http_response->count; i++)
+               free(http_response->lines[i]);
 
-               free(http_response->lines);
+       free(http_response->lines);
 
-               free(http_response->ReasonPhrase);
+       free(http_response->ReasonPhrase);
 
-               free(http_response->AuthParam);
-               free(http_response->AuthScheme);
-               free(http_response->Authorization);
+       ListDictionary_Free(http_response->Authenticates);
 
-               if (http_response->ContentLength > 0)
-                       free(http_response->Content);
+       if (http_response->ContentLength > 0)
+               free(http_response->Content);
 
-               free(http_response);
-       }
+       free(http_response);
 }
index ac223f1..748b45a 100644 (file)
@@ -82,9 +82,7 @@ struct _http_response
        int StatusCode;
        char* ReasonPhrase;
 
-       char* AuthScheme;
-       char* AuthParam;
-       char* Authorization;
+       wListDictionary *Authenticates;
        int ContentLength;
        char* Content;
 };
index ca070ee..2820d8e 100644 (file)
@@ -92,23 +92,27 @@ int rpc_ncacn_http_send_in_channel_request(rdpRpc* rpc)
 
 int rpc_ncacn_http_recv_in_channel_response(rdpRpc* rpc)
 {
-       int ntlm_token_length;
-       BYTE* ntlm_token_data;
+       int ntlm_token_length = 0;
+       BYTE* ntlm_token_data = NULL;
        HttpResponse* http_response;
        rdpNtlm* ntlm = rpc->NtlmHttpIn->ntlm;
 
        http_response = http_response_recv(rpc->TlsIn);
 
-       if (http_response->AuthParam)
+       if (ListDictionary_Contains(http_response->Authenticates, "NTLM"))
        {
-               ntlm_token_data = NULL;
-               crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
-                               &ntlm_token_data, &ntlm_token_length);
+               char *token64 = ListDictionary_GetItemValue(http_response->Authenticates, "NTLM");
+               if (!token64)
+                       goto out;
 
-               ntlm->inputBuffer[0].pvBuffer = ntlm_token_data;
-               ntlm->inputBuffer[0].cbBuffer = ntlm_token_length;
+               ntlm_token_data = NULL;
+               crypto_base64_decode((BYTE*) token64, strlen(token64), &ntlm_token_data, &ntlm_token_length);
        }
 
+       ntlm->inputBuffer[0].pvBuffer = ntlm_token_data;
+       ntlm->inputBuffer[0].cbBuffer = ntlm_token_length;
+
+out:
        http_response_free(http_response);
 
        return 0;
@@ -231,10 +235,10 @@ int rpc_ncacn_http_recv_out_channel_response(rdpRpc* rpc)
        http_response = http_response_recv(rpc->TlsOut);
 
        ntlm_token_data = NULL;
-       if (http_response && http_response->AuthParam)
+       if (http_response && ListDictionary_Contains(http_response->Authenticates, "NTLM"))
        {
-               crypto_base64_decode((BYTE*) http_response->AuthParam, strlen(http_response->AuthParam),
-                               &ntlm_token_data, &ntlm_token_length);
+               char *token64 = ListDictionary_GetItemValue(http_response->Authenticates, "NTLM");
+               crypto_base64_decode((BYTE*) token64, strlen(token64), &ntlm_token_data, &ntlm_token_length);
        }
 
        ntlm->inputBuffer[0].pvBuffer = ntlm_token_data;