diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/curl_sasl.c | 283 | ||||
| -rw-r--r-- | lib/curl_sasl.h | 9 | ||||
| -rw-r--r-- | lib/http_digest.c | 291 | 
3 files changed, 320 insertions, 263 deletions
| diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 3169730b5..fd2585e3d 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -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() */ @@ -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.   * @@ -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 @@ -791,6 +844,236 @@ CURLcode Curl_sasl_decode_digest_http_message(const char *chlg,  }  /* + * 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()   *   * This is used to clean up the digest specific data. diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index da6511c3d..e9ecab348 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -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 diff --git a/lib/http_digest.c b/lib/http_digest.c index 19e7c8392..44ccd90bb 100644 --- a/lib/http_digest.c +++ b/lib/http_digest.c @@ -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> @@ -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; @@ -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 @@ -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; +  result = Curl_sasl_create_digest_http_message(data, userp, passwdp, request, +                                                path, d, &response, &len); +  free(path); +  if(result) +    return result; -  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; - -  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\"", -               proxy?"Proxy-":"", -               userp_quoted, -               d->realm, -               d->nonce, -               urilen, uripath, /* this is the PATH part of the URL */ -               request_digest); -  } -  Curl_safefree(userp_quoted); +  *allocuserpwd = aprintf("%sAuthorization: Digest %s\r\n", +                          proxy ? "Proxy-" : "", +                          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;  } | 
