Loading lib/curl_sasl.c +283 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include "curl_memory.h" #include "strtok.h" #include "rawstr.h" #include "non-ascii.h" /* included for Curl_convert_... prototypes */ #ifdef USE_NSS #include "vtls/nssg.h" /* for Curl_nss_force_init() */ Loading Loading @@ -73,6 +74,17 @@ extern void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); #define DIGEST_MAX_VALUE_LENGTH 256 #define DIGEST_MAX_CONTENT_LENGTH 1024 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines. It converts digest text to ASCII so the MD5 will be correct for what ultimately goes over the network. */ #define CURL_OUTPUT_DIGEST_CONV(a, b) \ result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \ if(result) { \ free(b); \ return result; \ } /* * Return 0 on success and then the buffers are filled in fine. * Loading Loading @@ -141,6 +153,47 @@ static int sasl_digest_get_pair(const char *str, char *value, char *content, return 0; /* all is fine! */ } /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ static void sasl_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ unsigned char *dest) /* 33 bytes */ { int i; for(i = 0; i < 16; i++) snprintf((char *)&dest[i*2], 3, "%02x", source[i]); } /* Perform quoted-string escaping as described in RFC2616 and its errata */ static char *sasl_digest_string_quoted(const char *source) { char *dest, *d; const char *s = source; size_t n = 1; /* null terminator */ /* Calculate size needed */ while(*s) { ++n; if(*s == '"' || *s == '\\') { ++n; } ++s; } dest = malloc(n); if(dest) { s = source; d = dest; while(*s) { if(*s == '"' || *s == '\\') { *d++ = '\\'; } *d++ = *s++; } *d = 0; } return dest; } #if !defined(USE_WINDOWS_SSPI) /* Retrieves the value for a corresponding key from the challenge string * returns TRUE if the key could be found, FALSE if it does not exists Loading Loading @@ -790,6 +843,236 @@ CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, return CURLE_OK; } /* * Curl_sasl_create_digest_http_message() * * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name. * passdwp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, const char *userp, const char *passwdp, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen) { CURLcode result; unsigned char md5buf[16]; /* 16 bytes/128 bits */ unsigned char request_digest[33]; unsigned char *md5this; unsigned char ha1[33];/* 32 digits and 1 zero byte */ unsigned char ha2[33];/* 32 digits and 1 zero byte */ char cnoncebuf[33]; char *cnonce = NULL; size_t cnonce_sz = 0; char *userp_quoted; char *response = NULL; char *tmp = NULL; if(!digest->nc) digest->nc = 1; if(!digest->cnonce) { snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", Curl_rand(data), Curl_rand(data), Curl_rand(data), Curl_rand(data)); result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce, &cnonce_sz); if(result) return result; digest->cnonce = cnonce; } /* if the algorithm is "MD5" or unspecified (which then defaults to MD5): A1 = unq(username-value) ":" unq(realm-value) ":" passwd if the algorithm is "MD5-sess" then: A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value) */ md5this = (unsigned char *) aprintf("%s:%s:%s", userp, digest->realm, passwdp); if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); sasl_digest_md5_to_ascii(md5buf, ha1); if(digest->algo == CURLDIGESTALGO_MD5SESS) { /* nonce and cnonce are OUTSIDE the hash */ tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); if(!tmp) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ Curl_md5it(md5buf, (unsigned char *)tmp); Curl_safefree(tmp); sasl_digest_md5_to_ascii(md5buf, ha1); } /* If the "qop" directive's value is "auth" or is unspecified, then A2 is: A2 = Method ":" digest-uri-value If the "qop" value is "auth-int", then A2 is: A2 = Method ":" digest-uri-value ":" H(entity-body) (The "Method" value is the HTTP request method as specified in section 5.1.1 of RFC 2616) */ md5this = (unsigned char *)aprintf("%s:%s", request, uripath); if(digest->qop && Curl_raw_equal(digest->qop, "auth-int")) { /* We don't support auth-int for PUT or POST at the moment. TODO: replace md5 of empty string with entity-body for PUT/POST */ unsigned char *md5this2 = (unsigned char *) aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); Curl_safefree(md5this); md5this = md5this2; } if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); sasl_digest_md5_to_ascii(md5buf, ha2); if(digest->qop) { md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, digest->cnonce, digest->qop, ha2); } else { md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, digest->nonce, ha2); } if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); sasl_digest_md5_to_ascii(md5buf, request_digest); /* for test case 64 (snooped from a Mozilla 1.3a request) Authorization: Digest username="testuser", realm="testrealm", \ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by the user will need double quotes and backslashes within it escaped. For the other fields, this shouldn't be an issue. realm, nonce, and opaque are copied as is from the server, escapes and all. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe chracters. */ userp_quoted = sasl_digest_string_quoted(userp); if(!userp_quoted) return CURLE_OUT_OF_MEMORY; if(digest->qop) { response = aprintf("username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%s\", " "cnonce=\"%s\", " "nc=%08x, " "qop=%s, " "response=\"%s\"", userp_quoted, digest->realm, digest->nonce, uripath, digest->cnonce, digest->nc, digest->qop, request_digest); if(Curl_raw_equal(digest->qop, "auth")) digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded which tells to the server how many times you are using the same nonce in the qop=auth mode */ } else { response = aprintf("username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%s\", " "response=\"%s\"", userp_quoted, digest->realm, digest->nonce, uripath, request_digest); } Curl_safefree(userp_quoted); if(!response) return CURLE_OUT_OF_MEMORY; /* Add the optional fields */ if(digest->opaque) { /* Append the opaque */ tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque); if(!tmp) return CURLE_OUT_OF_MEMORY; free(response); response = tmp; } if(digest->algorithm) { /* Append the algorithm */ tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm); if(!tmp) return CURLE_OUT_OF_MEMORY; free(response); response = tmp; } /* Return the output */ *outptr = response; *outlen = strlen(response); return CURLE_OK; } /* * Curl_sasl_digest_cleanup() * Loading lib/curl_sasl.h +9 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,15 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, struct digestdata *digest); /* This is used to generate a HTTP DIGEST response message */ CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, const char *userp, const char *passwdp, const unsigned char *request, const unsigned char *uri, struct digestdata *digest, char **outptr, size_t *outlen); /* This is used to clean up the digest specific data */ void Curl_sasl_digest_cleanup(struct digestdata *digest); #endif Loading lib/http_digest.c +28 −263 Original line number Diff line number Diff line Loading @@ -26,15 +26,9 @@ #include "urldata.h" #include "rawstr.h" #include "curl_base64.h" #include "curl_md5.h" #include "curl_sasl.h" #include "http_digest.h" #include "strtok.h" #include "curl_memory.h" #include "vtls/vtls.h" /* for Curl_rand() */ #include "non-ascii.h" /* included for Curl_convert_... prototypes */ #include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> Loading Loading @@ -74,85 +68,29 @@ CURLcode Curl_input_digest(struct connectdata *conn, return Curl_sasl_decode_digest_http_message(header, d); } /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ static void md5_to_ascii(unsigned char *source, /* 16 bytes */ unsigned char *dest) /* 33 bytes */ { int i; for(i=0; i<16; i++) snprintf((char *)&dest[i*2], 3, "%02x", source[i]); } /* Perform quoted-string escaping as described in RFC2616 and its errata */ static char *string_quoted(const char *source) { char *dest, *d; const char *s = source; size_t n = 1; /* null terminator */ /* Calculate size needed */ while(*s) { ++n; if(*s == '"' || *s == '\\') { ++n; } ++s; } dest = malloc(n); if(dest) { s = source; d = dest; while(*s) { if(*s == '"' || *s == '\\') { *d++ = '\\'; } *d++ = *s++; } *d = 0; } return dest; } CURLcode Curl_output_digest(struct connectdata *conn, bool proxy, const unsigned char *request, const unsigned char *uripath) { /* We have a Digest setup for this, use it! Now, to get all the details for this sorted out, I must urge you dear friend to read up on the RFC2617 section 3.2.2, */ size_t urilen; unsigned char md5buf[16]; /* 16 bytes/128 bits */ unsigned char request_digest[33]; unsigned char *md5this; unsigned char ha1[33];/* 32 digits and 1 zero byte */ unsigned char ha2[33];/* 32 digits and 1 zero byte */ char cnoncebuf[33]; char *cnonce = NULL; size_t cnonce_sz = 0; char *tmp = NULL; CURLcode result; struct SessionHandle *data = conn->data; unsigned char *path; unsigned char *tmp; char *response; size_t len; /* Point to the address of the pointer that holds the string to send to the server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; size_t userlen; /* Point to the name and password for this */ const char *userp; char *userp_quoted; const char *passwdp; struct auth *authp; struct SessionHandle *data = conn->data; /* Point to the correct struct with this */ struct digestdata *d; CURLcode result; /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines. It converts digest text to ASCII so the MD5 will be correct for what ultimately goes over the network. */ #define CURL_OUTPUT_DIGEST_CONV(a, b) \ result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \ if(result) { \ free(b); \ return result; \ } struct auth *authp; if(proxy) { d = &data->state.proxydigest; Loading Loading @@ -182,68 +120,6 @@ CURLcode Curl_output_digest(struct connectdata *conn, authp->done = FALSE; return CURLE_OK; } authp->done = TRUE; if(!d->nc) d->nc = 1; if(!d->cnonce) { snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", Curl_rand(data), Curl_rand(data), Curl_rand(data), Curl_rand(data)); result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce, &cnonce_sz); if(result) return result; d->cnonce = cnonce; } /* if the algorithm is "MD5" or unspecified (which then defaults to MD5): A1 = unq(username-value) ":" unq(realm-value) ":" passwd if the algorithm is "MD5-sess" then: A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value) */ md5this = (unsigned char *) aprintf("%s:%s:%s", userp, d->realm, passwdp); if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); md5_to_ascii(md5buf, ha1); if(d->algo == CURLDIGESTALGO_MD5SESS) { /* nonce and cnonce are OUTSIDE the hash */ tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce); if(!tmp) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ Curl_md5it(md5buf, (unsigned char *)tmp); Curl_safefree(tmp); md5_to_ascii(md5buf, ha1); } /* If the "qop" directive's value is "auth" or is unspecified, then A2 is: A2 = Method ":" digest-uri-value If the "qop" value is "auth-int", then A2 is: A2 = Method ":" digest-uri-value ":" H(entity-body) (The "Method" value is the HTTP request method as specified in section 5.1.1 of RFC 2616) */ /* So IE browsers < v7 cut off the URI part at the query part when they evaluate the MD5 and some (IIS?) servers work with them so we may need to Loading @@ -258,141 +134,30 @@ CURLcode Curl_output_digest(struct connectdata *conn, http://www.fngtps.com/2006/09/http-authentication */ if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) urilen = tmp - (char *)uripath; else urilen = strlen((char *)uripath); md5this = (unsigned char *)aprintf("%s:%.*s", request, urilen, uripath); if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) { size_t urilen = tmp - (char *)uripath; if(d->qop && Curl_raw_equal(d->qop, "auth-int")) { /* We don't support auth-int for PUT or POST at the moment. TODO: replace md5 of empty string with entity-body for PUT/POST */ unsigned char *md5this2 = (unsigned char *) aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); Curl_safefree(md5this); md5this = md5this2; path = aprintf("%.*s", urilen, uripath); } else path = strdup((char *) uripath); if(!md5this) if(!path) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); md5_to_ascii(md5buf, ha2); if(d->qop) { md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s", ha1, d->nonce, d->nc, d->cnonce, d->qop, ha2); } else { md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, d->nonce, ha2); } if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); md5_to_ascii(md5buf, request_digest); /* for test case 64 (snooped from a Mozilla 1.3a request) Authorization: Digest username="testuser", realm="testrealm", \ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by the user will need double quotes and backslashes within it escaped. For the other fields, this shouldn't be an issue. realm, nonce, and opaque are copied as is from the server, escapes and all. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe chracters. */ userp_quoted = string_quoted(userp); if(!userp_quoted) return CURLE_OUT_OF_MEMORY; result = Curl_sasl_create_digest_http_message(data, userp, passwdp, request, path, d, &response, &len); free(path); if(result) return result; if(d->qop) { *allocuserpwd = aprintf( "%sAuthorization: Digest " "username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%.*s\", " "cnonce=\"%s\", " "nc=%08x, " "qop=%s, " "response=\"%s\"", proxy?"Proxy-":"", userp_quoted, d->realm, d->nonce, urilen, uripath, /* this is the PATH part of the URL */ d->cnonce, d->nc, d->qop, request_digest); if(Curl_raw_equal(d->qop, "auth")) d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded which tells to the server how many times you are using the same nonce in the qop=auth mode. */ } else { *allocuserpwd = aprintf( "%sAuthorization: Digest " "username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%.*s\", " "response=\"%s\"", *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", proxy ? "Proxy-" : "", userp_quoted, d->realm, d->nonce, urilen, uripath, /* this is the PATH part of the URL */ request_digest); } Curl_safefree(userp_quoted); response); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; /* Add optional fields */ if(d->opaque) { /* append opaque */ tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque); if(!tmp) return CURLE_OUT_OF_MEMORY; free(*allocuserpwd); *allocuserpwd = tmp; } if(d->algorithm) { /* append algorithm */ tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm); if(!tmp) return CURLE_OUT_OF_MEMORY; free(*allocuserpwd); *allocuserpwd = tmp; } /* append CRLF + zero (3 bytes) to the userpwd header */ userlen = strlen(*allocuserpwd); tmp = realloc(*allocuserpwd, userlen + 3); if(!tmp) return CURLE_OUT_OF_MEMORY; strcpy(&tmp[userlen], "\r\n"); /* append the data */ *allocuserpwd = tmp; authp->done = TRUE; return CURLE_OK; } Loading Loading
lib/curl_sasl.c +283 −0 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ #include "curl_memory.h" #include "strtok.h" #include "rawstr.h" #include "non-ascii.h" /* included for Curl_convert_... prototypes */ #ifdef USE_NSS #include "vtls/nssg.h" /* for Curl_nss_force_init() */ Loading Loading @@ -73,6 +74,17 @@ extern void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); #define DIGEST_MAX_VALUE_LENGTH 256 #define DIGEST_MAX_CONTENT_LENGTH 1024 /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines. It converts digest text to ASCII so the MD5 will be correct for what ultimately goes over the network. */ #define CURL_OUTPUT_DIGEST_CONV(a, b) \ result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \ if(result) { \ free(b); \ return result; \ } /* * Return 0 on success and then the buffers are filled in fine. * Loading Loading @@ -141,6 +153,47 @@ static int sasl_digest_get_pair(const char *str, char *value, char *content, return 0; /* all is fine! */ } /* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ static void sasl_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ unsigned char *dest) /* 33 bytes */ { int i; for(i = 0; i < 16; i++) snprintf((char *)&dest[i*2], 3, "%02x", source[i]); } /* Perform quoted-string escaping as described in RFC2616 and its errata */ static char *sasl_digest_string_quoted(const char *source) { char *dest, *d; const char *s = source; size_t n = 1; /* null terminator */ /* Calculate size needed */ while(*s) { ++n; if(*s == '"' || *s == '\\') { ++n; } ++s; } dest = malloc(n); if(dest) { s = source; d = dest; while(*s) { if(*s == '"' || *s == '\\') { *d++ = '\\'; } *d++ = *s++; } *d = 0; } return dest; } #if !defined(USE_WINDOWS_SSPI) /* Retrieves the value for a corresponding key from the challenge string * returns TRUE if the key could be found, FALSE if it does not exists Loading Loading @@ -790,6 +843,236 @@ CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, return CURLE_OK; } /* * Curl_sasl_create_digest_http_message() * * This is used to generate a HTTP DIGEST response message ready for sending * to the recipient. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name. * passdwp [in] - The user's password. * request [in] - The HTTP request. * uripath [in] - The path of the HTTP uri. * digest [in/out] - The digest data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, const char *userp, const char *passwdp, const unsigned char *request, const unsigned char *uripath, struct digestdata *digest, char **outptr, size_t *outlen) { CURLcode result; unsigned char md5buf[16]; /* 16 bytes/128 bits */ unsigned char request_digest[33]; unsigned char *md5this; unsigned char ha1[33];/* 32 digits and 1 zero byte */ unsigned char ha2[33];/* 32 digits and 1 zero byte */ char cnoncebuf[33]; char *cnonce = NULL; size_t cnonce_sz = 0; char *userp_quoted; char *response = NULL; char *tmp = NULL; if(!digest->nc) digest->nc = 1; if(!digest->cnonce) { snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", Curl_rand(data), Curl_rand(data), Curl_rand(data), Curl_rand(data)); result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce, &cnonce_sz); if(result) return result; digest->cnonce = cnonce; } /* if the algorithm is "MD5" or unspecified (which then defaults to MD5): A1 = unq(username-value) ":" unq(realm-value) ":" passwd if the algorithm is "MD5-sess" then: A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value) */ md5this = (unsigned char *) aprintf("%s:%s:%s", userp, digest->realm, passwdp); if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); sasl_digest_md5_to_ascii(md5buf, ha1); if(digest->algo == CURLDIGESTALGO_MD5SESS) { /* nonce and cnonce are OUTSIDE the hash */ tmp = aprintf("%s:%s:%s", ha1, digest->nonce, digest->cnonce); if(!tmp) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ Curl_md5it(md5buf, (unsigned char *)tmp); Curl_safefree(tmp); sasl_digest_md5_to_ascii(md5buf, ha1); } /* If the "qop" directive's value is "auth" or is unspecified, then A2 is: A2 = Method ":" digest-uri-value If the "qop" value is "auth-int", then A2 is: A2 = Method ":" digest-uri-value ":" H(entity-body) (The "Method" value is the HTTP request method as specified in section 5.1.1 of RFC 2616) */ md5this = (unsigned char *)aprintf("%s:%s", request, uripath); if(digest->qop && Curl_raw_equal(digest->qop, "auth-int")) { /* We don't support auth-int for PUT or POST at the moment. TODO: replace md5 of empty string with entity-body for PUT/POST */ unsigned char *md5this2 = (unsigned char *) aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); Curl_safefree(md5this); md5this = md5this2; } if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); sasl_digest_md5_to_ascii(md5buf, ha2); if(digest->qop) { md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s", ha1, digest->nonce, digest->nc, digest->cnonce, digest->qop, ha2); } else { md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, digest->nonce, ha2); } if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); sasl_digest_md5_to_ascii(md5buf, request_digest); /* for test case 64 (snooped from a Mozilla 1.3a request) Authorization: Digest username="testuser", realm="testrealm", \ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by the user will need double quotes and backslashes within it escaped. For the other fields, this shouldn't be an issue. realm, nonce, and opaque are copied as is from the server, escapes and all. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe chracters. */ userp_quoted = sasl_digest_string_quoted(userp); if(!userp_quoted) return CURLE_OUT_OF_MEMORY; if(digest->qop) { response = aprintf("username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%s\", " "cnonce=\"%s\", " "nc=%08x, " "qop=%s, " "response=\"%s\"", userp_quoted, digest->realm, digest->nonce, uripath, digest->cnonce, digest->nc, digest->qop, request_digest); if(Curl_raw_equal(digest->qop, "auth")) digest->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded which tells to the server how many times you are using the same nonce in the qop=auth mode */ } else { response = aprintf("username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%s\", " "response=\"%s\"", userp_quoted, digest->realm, digest->nonce, uripath, request_digest); } Curl_safefree(userp_quoted); if(!response) return CURLE_OUT_OF_MEMORY; /* Add the optional fields */ if(digest->opaque) { /* Append the opaque */ tmp = aprintf("%s, opaque=\"%s\"", response, digest->opaque); if(!tmp) return CURLE_OUT_OF_MEMORY; free(response); response = tmp; } if(digest->algorithm) { /* Append the algorithm */ tmp = aprintf("%s, algorithm=\"%s\"", response, digest->algorithm); if(!tmp) return CURLE_OUT_OF_MEMORY; free(response); response = tmp; } /* Return the output */ *outptr = response; *outlen = strlen(response); return CURLE_OK; } /* * Curl_sasl_digest_cleanup() * Loading
lib/curl_sasl.h +9 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,15 @@ CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, CURLcode Curl_sasl_decode_digest_http_message(const char *chlg, struct digestdata *digest); /* This is used to generate a HTTP DIGEST response message */ CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, const char *userp, const char *passwdp, const unsigned char *request, const unsigned char *uri, struct digestdata *digest, char **outptr, size_t *outlen); /* This is used to clean up the digest specific data */ void Curl_sasl_digest_cleanup(struct digestdata *digest); #endif Loading
lib/http_digest.c +28 −263 Original line number Diff line number Diff line Loading @@ -26,15 +26,9 @@ #include "urldata.h" #include "rawstr.h" #include "curl_base64.h" #include "curl_md5.h" #include "curl_sasl.h" #include "http_digest.h" #include "strtok.h" #include "curl_memory.h" #include "vtls/vtls.h" /* for Curl_rand() */ #include "non-ascii.h" /* included for Curl_convert_... prototypes */ #include "warnless.h" #define _MPRINTF_REPLACE /* use our functions only */ #include <curl/mprintf.h> Loading Loading @@ -74,85 +68,29 @@ CURLcode Curl_input_digest(struct connectdata *conn, return Curl_sasl_decode_digest_http_message(header, d); } /* convert md5 chunk to RFC2617 (section 3.1.3) -suitable ascii string*/ static void md5_to_ascii(unsigned char *source, /* 16 bytes */ unsigned char *dest) /* 33 bytes */ { int i; for(i=0; i<16; i++) snprintf((char *)&dest[i*2], 3, "%02x", source[i]); } /* Perform quoted-string escaping as described in RFC2616 and its errata */ static char *string_quoted(const char *source) { char *dest, *d; const char *s = source; size_t n = 1; /* null terminator */ /* Calculate size needed */ while(*s) { ++n; if(*s == '"' || *s == '\\') { ++n; } ++s; } dest = malloc(n); if(dest) { s = source; d = dest; while(*s) { if(*s == '"' || *s == '\\') { *d++ = '\\'; } *d++ = *s++; } *d = 0; } return dest; } CURLcode Curl_output_digest(struct connectdata *conn, bool proxy, const unsigned char *request, const unsigned char *uripath) { /* We have a Digest setup for this, use it! Now, to get all the details for this sorted out, I must urge you dear friend to read up on the RFC2617 section 3.2.2, */ size_t urilen; unsigned char md5buf[16]; /* 16 bytes/128 bits */ unsigned char request_digest[33]; unsigned char *md5this; unsigned char ha1[33];/* 32 digits and 1 zero byte */ unsigned char ha2[33];/* 32 digits and 1 zero byte */ char cnoncebuf[33]; char *cnonce = NULL; size_t cnonce_sz = 0; char *tmp = NULL; CURLcode result; struct SessionHandle *data = conn->data; unsigned char *path; unsigned char *tmp; char *response; size_t len; /* Point to the address of the pointer that holds the string to send to the server, which is for a plain host or for a HTTP proxy */ char **allocuserpwd; size_t userlen; /* Point to the name and password for this */ const char *userp; char *userp_quoted; const char *passwdp; struct auth *authp; struct SessionHandle *data = conn->data; /* Point to the correct struct with this */ struct digestdata *d; CURLcode result; /* The CURL_OUTPUT_DIGEST_CONV macro below is for non-ASCII machines. It converts digest text to ASCII so the MD5 will be correct for what ultimately goes over the network. */ #define CURL_OUTPUT_DIGEST_CONV(a, b) \ result = Curl_convert_to_network(a, (char *)b, strlen((const char*)b)); \ if(result) { \ free(b); \ return result; \ } struct auth *authp; if(proxy) { d = &data->state.proxydigest; Loading Loading @@ -182,68 +120,6 @@ CURLcode Curl_output_digest(struct connectdata *conn, authp->done = FALSE; return CURLE_OK; } authp->done = TRUE; if(!d->nc) d->nc = 1; if(!d->cnonce) { snprintf(cnoncebuf, sizeof(cnoncebuf), "%08x%08x%08x%08x", Curl_rand(data), Curl_rand(data), Curl_rand(data), Curl_rand(data)); result = Curl_base64_encode(data, cnoncebuf, strlen(cnoncebuf), &cnonce, &cnonce_sz); if(result) return result; d->cnonce = cnonce; } /* if the algorithm is "MD5" or unspecified (which then defaults to MD5): A1 = unq(username-value) ":" unq(realm-value) ":" passwd if the algorithm is "MD5-sess" then: A1 = H( unq(username-value) ":" unq(realm-value) ":" passwd ) ":" unq(nonce-value) ":" unq(cnonce-value) */ md5this = (unsigned char *) aprintf("%s:%s:%s", userp, d->realm, passwdp); if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); md5_to_ascii(md5buf, ha1); if(d->algo == CURLDIGESTALGO_MD5SESS) { /* nonce and cnonce are OUTSIDE the hash */ tmp = aprintf("%s:%s:%s", ha1, d->nonce, d->cnonce); if(!tmp) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, tmp); /* convert on non-ASCII machines */ Curl_md5it(md5buf, (unsigned char *)tmp); Curl_safefree(tmp); md5_to_ascii(md5buf, ha1); } /* If the "qop" directive's value is "auth" or is unspecified, then A2 is: A2 = Method ":" digest-uri-value If the "qop" value is "auth-int", then A2 is: A2 = Method ":" digest-uri-value ":" H(entity-body) (The "Method" value is the HTTP request method as specified in section 5.1.1 of RFC 2616) */ /* So IE browsers < v7 cut off the URI part at the query part when they evaluate the MD5 and some (IIS?) servers work with them so we may need to Loading @@ -258,141 +134,30 @@ CURLcode Curl_output_digest(struct connectdata *conn, http://www.fngtps.com/2006/09/http-authentication */ if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) urilen = tmp - (char *)uripath; else urilen = strlen((char *)uripath); md5this = (unsigned char *)aprintf("%s:%.*s", request, urilen, uripath); if(authp->iestyle && ((tmp = strchr((char *)uripath, '?')) != NULL)) { size_t urilen = tmp - (char *)uripath; if(d->qop && Curl_raw_equal(d->qop, "auth-int")) { /* We don't support auth-int for PUT or POST at the moment. TODO: replace md5 of empty string with entity-body for PUT/POST */ unsigned char *md5this2 = (unsigned char *) aprintf("%s:%s", md5this, "d41d8cd98f00b204e9800998ecf8427e"); Curl_safefree(md5this); md5this = md5this2; path = aprintf("%.*s", urilen, uripath); } else path = strdup((char *) uripath); if(!md5this) if(!path) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); md5_to_ascii(md5buf, ha2); if(d->qop) { md5this = (unsigned char *)aprintf("%s:%s:%08x:%s:%s:%s", ha1, d->nonce, d->nc, d->cnonce, d->qop, ha2); } else { md5this = (unsigned char *)aprintf("%s:%s:%s", ha1, d->nonce, ha2); } if(!md5this) return CURLE_OUT_OF_MEMORY; CURL_OUTPUT_DIGEST_CONV(data, md5this); /* convert on non-ASCII machines */ Curl_md5it(md5buf, md5this); Curl_safefree(md5this); md5_to_ascii(md5buf, request_digest); /* for test case 64 (snooped from a Mozilla 1.3a request) Authorization: Digest username="testuser", realm="testrealm", \ nonce="1053604145", uri="/64", response="c55f7f30d83d774a3d2dcacf725abaca" Digest parameters are all quoted strings. Username which is provided by the user will need double quotes and backslashes within it escaped. For the other fields, this shouldn't be an issue. realm, nonce, and opaque are copied as is from the server, escapes and all. cnonce is generated with web-safe characters. uri is already percent encoded. nc is 8 hex characters. algorithm and qop with standard values only contain web-safe chracters. */ userp_quoted = string_quoted(userp); if(!userp_quoted) return CURLE_OUT_OF_MEMORY; result = Curl_sasl_create_digest_http_message(data, userp, passwdp, request, path, d, &response, &len); free(path); if(result) return result; if(d->qop) { *allocuserpwd = aprintf( "%sAuthorization: Digest " "username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%.*s\", " "cnonce=\"%s\", " "nc=%08x, " "qop=%s, " "response=\"%s\"", proxy?"Proxy-":"", userp_quoted, d->realm, d->nonce, urilen, uripath, /* this is the PATH part of the URL */ d->cnonce, d->nc, d->qop, request_digest); if(Curl_raw_equal(d->qop, "auth")) d->nc++; /* The nc (from RFC) has to be a 8 hex digit number 0 padded which tells to the server how many times you are using the same nonce in the qop=auth mode. */ } else { *allocuserpwd = aprintf( "%sAuthorization: Digest " "username=\"%s\", " "realm=\"%s\", " "nonce=\"%s\", " "uri=\"%.*s\", " "response=\"%s\"", *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", proxy ? "Proxy-" : "", userp_quoted, d->realm, d->nonce, urilen, uripath, /* this is the PATH part of the URL */ request_digest); } Curl_safefree(userp_quoted); response); if(!*allocuserpwd) return CURLE_OUT_OF_MEMORY; /* Add optional fields */ if(d->opaque) { /* append opaque */ tmp = aprintf("%s, opaque=\"%s\"", *allocuserpwd, d->opaque); if(!tmp) return CURLE_OUT_OF_MEMORY; free(*allocuserpwd); *allocuserpwd = tmp; } if(d->algorithm) { /* append algorithm */ tmp = aprintf("%s, algorithm=\"%s\"", *allocuserpwd, d->algorithm); if(!tmp) return CURLE_OUT_OF_MEMORY; free(*allocuserpwd); *allocuserpwd = tmp; } /* append CRLF + zero (3 bytes) to the userpwd header */ userlen = strlen(*allocuserpwd); tmp = realloc(*allocuserpwd, userlen + 3); if(!tmp) return CURLE_OUT_OF_MEMORY; strcpy(&tmp[userlen], "\r\n"); /* append the data */ *allocuserpwd = tmp; authp->done = TRUE; return CURLE_OK; } Loading