00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00025 #include "platform.h"
00026 #include <limits.h>
00027 #include "internal.h"
00028 #include "md5.h"
00029
00030 #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
00031
00035 #define _BASE "Digest "
00036
00040 #define MAX_USERNAME_LENGTH 128
00041
00045 #define MAX_REALM_LENGTH 256
00046
00050 #define MAX_AUTH_RESPONSE_LENGTH 128
00051
00052
00060 static void
00061 cvthex (const unsigned char *bin,
00062 size_t len,
00063 char *hex)
00064 {
00065 size_t i;
00066 unsigned int j;
00067
00068 for (i = 0; i < len; ++i)
00069 {
00070 j = (bin[i] >> 4) & 0x0f;
00071 hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00072 j = bin[i] & 0x0f;
00073 hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00074 }
00075 hex[len * 2] = '\0';
00076 }
00077
00078
00091 static void
00092 digest_calc_ha1 (const char *alg,
00093 const char *username,
00094 const char *realm,
00095 const char *password,
00096 const char *nonce,
00097 const char *cnonce,
00098 char *sessionkey)
00099 {
00100 struct MD5Context md5;
00101 unsigned char ha1[MD5_DIGEST_SIZE];
00102
00103 MD5Init (&md5);
00104 MD5Update (&md5, username, strlen (username));
00105 MD5Update (&md5, ":", 1);
00106 MD5Update (&md5, realm, strlen (realm));
00107 MD5Update (&md5, ":", 1);
00108 MD5Update (&md5, password, strlen (password));
00109 MD5Final (ha1, &md5);
00110 if (0 == strcasecmp (alg, "md5-sess"))
00111 {
00112 MD5Init (&md5);
00113 MD5Update (&md5, ha1, sizeof (ha1));
00114 MD5Update (&md5, ":", 1);
00115 MD5Update (&md5, nonce, strlen (nonce));
00116 MD5Update (&md5, ":", 1);
00117 MD5Update (&md5, cnonce, strlen (cnonce));
00118 MD5Final (ha1, &md5);
00119 }
00120 cvthex (ha1, sizeof (ha1), sessionkey);
00121 }
00122
00123
00137 static void
00138 digest_calc_response (const char *ha1,
00139 const char *nonce,
00140 const char *noncecount,
00141 const char *cnonce,
00142 const char *qop,
00143 const char *method,
00144 const char *uri,
00145 const char *hentity,
00146 char *response)
00147 {
00148 struct MD5Context md5;
00149 unsigned char ha2[MD5_DIGEST_SIZE];
00150 unsigned char resphash[MD5_DIGEST_SIZE];
00151 char ha2hex[HASH_MD5_HEX_LEN + 1];
00152
00153 MD5Init (&md5);
00154 MD5Update (&md5, method, strlen(method));
00155 MD5Update (&md5, ":", 1);
00156 MD5Update (&md5, uri, strlen(uri));
00157 #if 0
00158 if (0 == strcasecmp(qop, "auth-int"))
00159 {
00160
00161
00162 MD5Update (&md5, ":", 1);
00163 if (NULL != hentity)
00164 MD5Update (&md5, hentity, strlen(hentity));
00165 }
00166 #endif
00167 MD5Final (ha2, &md5);
00168 cvthex (ha2, MD5_DIGEST_SIZE, ha2hex);
00169 MD5Init (&md5);
00170
00171 MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
00172 MD5Update (&md5, ":", 1);
00173 MD5Update (&md5, nonce, strlen(nonce));
00174 MD5Update (&md5, ":", 1);
00175 if ('\0' != *qop)
00176 {
00177 MD5Update (&md5, noncecount, strlen(noncecount));
00178 MD5Update (&md5, ":", 1);
00179 MD5Update (&md5, cnonce, strlen(cnonce));
00180 MD5Update (&md5, ":", 1);
00181 MD5Update (&md5, qop, strlen(qop));
00182 MD5Update (&md5, ":", 1);
00183 }
00184 MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
00185 MD5Final (resphash, &md5);
00186 cvthex (resphash, sizeof (resphash), response);
00187 }
00188
00189
00204 static int
00205 lookup_sub_value (char *dest,
00206 size_t size,
00207 const char *data,
00208 const char *key)
00209 {
00210 size_t keylen;
00211 size_t len;
00212 const char *ptr;
00213 const char *eq;
00214 const char *q1;
00215 const char *q2;
00216 const char *qn;
00217
00218 if (0 == size)
00219 return 0;
00220 keylen = strlen (key);
00221 ptr = data;
00222 while ('\0' != *ptr)
00223 {
00224 if (NULL == (eq = strchr (ptr, '=')))
00225 return 0;
00226 q1 = eq + 1;
00227 while (' ' == *q1)
00228 q1++;
00229 if ('\"' != *q1)
00230 {
00231 q2 = strchr (q1, ',');
00232 qn = q2;
00233 }
00234 else
00235 {
00236 q1++;
00237 q2 = strchr (q1, '\"');
00238 if (NULL == q2)
00239 return 0;
00240 qn = q2 + 1;
00241 }
00242 if ( (0 == strncasecmp (ptr,
00243 key,
00244 keylen)) &&
00245 (eq == &ptr[keylen]) )
00246 {
00247 if (NULL == q2)
00248 {
00249 len = strlen (q1) + 1;
00250 if (size > len)
00251 size = len;
00252 size--;
00253 strncpy (dest,
00254 q1,
00255 size);
00256 dest[size] = '\0';
00257 return size;
00258 }
00259 else
00260 {
00261 if (size > (q2 - q1) + 1)
00262 size = (q2 - q1) + 1;
00263 size--;
00264 memcpy (dest,
00265 q1,
00266 size);
00267 dest[size] = '\0';
00268 return size;
00269 }
00270 }
00271 if (NULL == qn)
00272 return 0;
00273 ptr = strchr (qn, ',');
00274 if (NULL == ptr)
00275 return 0;
00276 ptr++;
00277 while (' ' == *ptr)
00278 ptr++;
00279 }
00280 return 0;
00281 }
00282
00283
00293 static int
00294 check_nonce_nc (struct MHD_Connection *connection,
00295 const char *nonce,
00296 unsigned long int nc)
00297 {
00298 uint32_t off;
00299 uint32_t mod;
00300 const char *np;
00301
00302 mod = connection->daemon->nonce_nc_size;
00303 if (0 == mod)
00304 return MHD_NO;
00305
00306 off = 0;
00307 np = nonce;
00308 while ('\0' != *np)
00309 {
00310 off = (off << 8) | (*np ^ (off >> 24));
00311 np++;
00312 }
00313 off = off % mod;
00314
00315
00316
00317
00318
00319
00320 (void) pthread_mutex_lock (&connection->daemon->nnc_lock);
00321 if (0 == nc)
00322 {
00323 strcpy(connection->daemon->nnc[off].nonce,
00324 nonce);
00325 connection->daemon->nnc[off].nc = 0;
00326 (void) pthread_mutex_unlock (&connection->daemon->nnc_lock);
00327 return MHD_YES;
00328 }
00329 if ( (nc <= connection->daemon->nnc[off].nc) ||
00330 (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
00331 {
00332 (void) pthread_mutex_unlock (&connection->daemon->nnc_lock);
00333 #if HAVE_MESSAGES
00334 MHD_DLOG (connection->daemon,
00335 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n");
00336 #endif
00337 return MHD_NO;
00338 }
00339 connection->daemon->nnc[off].nc = nc;
00340 (void) pthread_mutex_unlock (&connection->daemon->nnc_lock);
00341 return MHD_YES;
00342 }
00343
00344
00353 char *
00354 MHD_digest_auth_get_username(struct MHD_Connection *connection)
00355 {
00356 size_t len;
00357 char user[MAX_USERNAME_LENGTH];
00358 const char *header;
00359
00360 if (NULL == (header = MHD_lookup_connection_value (connection,
00361 MHD_HEADER_KIND,
00362 MHD_HTTP_HEADER_AUTHORIZATION)))
00363 return NULL;
00364 if (0 != strncmp (header, _BASE, strlen (_BASE)))
00365 return NULL;
00366 header += strlen (_BASE);
00367 if (0 == (len = lookup_sub_value (user,
00368 sizeof (user),
00369 header,
00370 "username")))
00371 return NULL;
00372 return strdup (user);
00373 }
00374
00375
00389 static void
00390 calculate_nonce (uint32_t nonce_time,
00391 const char *method,
00392 const char *rnd,
00393 unsigned int rnd_size,
00394 const char *uri,
00395 const char *realm,
00396 char *nonce)
00397 {
00398 struct MD5Context md5;
00399 unsigned char timestamp[4];
00400 unsigned char tmpnonce[MD5_DIGEST_SIZE];
00401 char timestamphex[sizeof(timestamp) * 2 + 1];
00402
00403 MD5Init (&md5);
00404 timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
00405 timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
00406 timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
00407 timestamp[3] = (nonce_time & 0x000000ff);
00408 MD5Update (&md5, timestamp, 4);
00409 MD5Update (&md5, ":", 1);
00410 MD5Update (&md5, method, strlen(method));
00411 MD5Update (&md5, ":", 1);
00412 if (rnd_size > 0)
00413 MD5Update (&md5, rnd, rnd_size);
00414 MD5Update (&md5, ":", 1);
00415 MD5Update (&md5, uri, strlen(uri));
00416 MD5Update (&md5, ":", 1);
00417 MD5Update (&md5, realm, strlen(realm));
00418 MD5Final (tmpnonce, &md5);
00419 cvthex (tmpnonce, sizeof (tmpnonce), nonce);
00420 cvthex (timestamp, 4, timestamphex);
00421 strncat (nonce, timestamphex, 8);
00422 }
00423
00424
00435 static int
00436 test_header (struct MHD_Connection *connection,
00437 const char *key,
00438 const char *value)
00439 {
00440 struct MHD_HTTP_Header *pos;
00441
00442 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00443 {
00444 if (MHD_GET_ARGUMENT_KIND != pos->kind)
00445 continue;
00446 if (0 != strcmp (key, pos->header))
00447 continue;
00448 if ( (NULL == value) &&
00449 (NULL == pos->value) )
00450 return MHD_YES;
00451 if ( (NULL == value) ||
00452 (NULL == pos->value) ||
00453 (0 != strcmp (value, pos->value)) )
00454 continue;
00455 return MHD_YES;
00456 }
00457 return MHD_NO;
00458 }
00459
00460
00471 static int
00472 check_argument_match (struct MHD_Connection *connection,
00473 const char *args)
00474 {
00475 struct MHD_HTTP_Header *pos;
00476 size_t slen = strlen (args) + 1;
00477 char argb[slen];
00478 char *argp;
00479 char *equals;
00480 char *amper;
00481 unsigned int num_headers;
00482
00483 num_headers = 0;
00484 memcpy (argb, args, slen);
00485 argp = argb;
00486 while ( (NULL != argp) &&
00487 ('\0' != argp[0]) )
00488 {
00489 equals = strchr (argp, '=');
00490 if (NULL == equals)
00491 {
00492
00493 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00494 connection,
00495 argp);
00496 if (MHD_YES != test_header (connection, argp, NULL))
00497 return MHD_NO;
00498 num_headers++;
00499 break;
00500 }
00501 equals[0] = '\0';
00502 equals++;
00503 amper = strchr (equals, '&');
00504 if (NULL != amper)
00505 {
00506 amper[0] = '\0';
00507 amper++;
00508 }
00509 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00510 connection,
00511 argp);
00512 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00513 connection,
00514 equals);
00515 if (! test_header (connection, argp, equals))
00516 return MHD_NO;
00517 num_headers++;
00518 argp = amper;
00519 }
00520
00521
00522 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00523 {
00524 if (MHD_GET_ARGUMENT_KIND != pos->kind)
00525 continue;
00526 num_headers--;
00527 }
00528 if (0 != num_headers)
00529 return MHD_NO;
00530 return MHD_YES;
00531 }
00532
00533
00547 int
00548 MHD_digest_auth_check (struct MHD_Connection *connection,
00549 const char *realm,
00550 const char *username,
00551 const char *password,
00552 unsigned int nonce_timeout)
00553 {
00554 size_t len;
00555 const char *header;
00556 char *end;
00557 char nonce[MAX_NONCE_LENGTH];
00558 char cnonce[MAX_NONCE_LENGTH];
00559 char qop[15];
00560 char nc[20];
00561 char response[MAX_AUTH_RESPONSE_LENGTH];
00562 const char *hentity = NULL;
00563 char ha1[HASH_MD5_HEX_LEN + 1];
00564 char respexp[HASH_MD5_HEX_LEN + 1];
00565 char noncehashexp[HASH_MD5_HEX_LEN + 9];
00566 uint32_t nonce_time;
00567 uint32_t t;
00568 size_t left;
00569 unsigned long int nci;
00570
00571 header = MHD_lookup_connection_value (connection,
00572 MHD_HEADER_KIND,
00573 MHD_HTTP_HEADER_AUTHORIZATION);
00574 if (NULL == header)
00575 return MHD_NO;
00576 if (0 != strncmp(header, _BASE, strlen(_BASE)))
00577 return MHD_NO;
00578 header += strlen (_BASE);
00579 left = strlen (header);
00580
00581 {
00582 char un[MAX_USERNAME_LENGTH];
00583
00584 len = lookup_sub_value (un,
00585 sizeof (un),
00586 header, "username");
00587 if ( (0 == len) ||
00588 (0 != strcmp(username, un)) )
00589 return MHD_NO;
00590 left -= strlen ("username") + len;
00591 }
00592
00593 {
00594 char r[MAX_REALM_LENGTH];
00595
00596 len = lookup_sub_value(r,
00597 sizeof (r),
00598 header, "realm");
00599 if ( (0 == len) ||
00600 (0 != strcmp(realm, r)) )
00601 return MHD_NO;
00602 left -= strlen ("realm") + len;
00603 }
00604
00605 if (0 == (len = lookup_sub_value (nonce,
00606 sizeof (nonce),
00607 header, "nonce")))
00608 return MHD_NO;
00609 left -= strlen ("nonce") + len;
00610 if (left > 32 * 1024)
00611 {
00612
00613
00614
00615
00616
00617
00618
00619 return MHD_NO;
00620 }
00621 {
00622 char uri[left];
00623
00624 if (0 == lookup_sub_value (uri,
00625 sizeof (uri),
00626 header, "uri"))
00627 return MHD_NO;
00628
00629
00630 nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16);
00631 t = (uint32_t) MHD_monotonic_time();
00632
00633
00634
00635
00636
00637 if ( (t > nonce_time + nonce_timeout) ||
00638 (nonce_time + nonce_timeout < nonce_time) )
00639 return MHD_INVALID_NONCE;
00640 if (0 != strncmp (uri,
00641 connection->url,
00642 strlen (connection->url)))
00643 {
00644 #if HAVE_MESSAGES
00645 MHD_DLOG (connection->daemon,
00646 "Authentication failed, URI does not match.\n");
00647 #endif
00648 return MHD_NO;
00649 }
00650 {
00651 const char *args = strchr (uri, '?');
00652
00653 if (NULL == args)
00654 args = "";
00655 else
00656 args++;
00657 if (MHD_YES !=
00658 check_argument_match (connection,
00659 args) )
00660 {
00661 #if HAVE_MESSAGES
00662 MHD_DLOG (connection->daemon,
00663 "Authentication failed, arguments do not match.\n");
00664 #endif
00665 return MHD_NO;
00666 }
00667 }
00668 calculate_nonce (nonce_time,
00669 connection->method,
00670 connection->daemon->digest_auth_random,
00671 connection->daemon->digest_auth_rand_size,
00672 connection->url,
00673 realm,
00674 noncehashexp);
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685 if (0 != strcmp (nonce, noncehashexp))
00686 return MHD_INVALID_NONCE;
00687 if ( (0 == lookup_sub_value (cnonce,
00688 sizeof (cnonce),
00689 header, "cnonce")) ||
00690 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
00691 ( (0 != strcmp (qop, "auth")) &&
00692 (0 != strcmp (qop, "")) ) ||
00693 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
00694 (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
00695 {
00696 #if HAVE_MESSAGES
00697 MHD_DLOG (connection->daemon,
00698 "Authentication failed, invalid format.\n");
00699 #endif
00700 return MHD_NO;
00701 }
00702 nci = strtoul (nc, &end, 16);
00703 if ( ('\0' != *end) ||
00704 ( (LONG_MAX == nci) &&
00705 (ERANGE == errno) ) )
00706 {
00707 #if HAVE_MESSAGES
00708 MHD_DLOG (connection->daemon,
00709 "Authentication failed, invalid format.\n");
00710 #endif
00711 return MHD_NO;
00712 }
00713
00714
00715
00716
00717
00718
00719 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
00720 return MHD_NO;
00721
00722 digest_calc_ha1("md5",
00723 username,
00724 realm,
00725 password,
00726 nonce,
00727 cnonce,
00728 ha1);
00729 digest_calc_response (ha1,
00730 nonce,
00731 nc,
00732 cnonce,
00733 qop,
00734 connection->method,
00735 uri,
00736 hentity,
00737 respexp);
00738 return (0 == strcmp(response, respexp))
00739 ? MHD_YES
00740 : MHD_NO;
00741 }
00742 }
00743
00744
00759 int
00760 MHD_queue_auth_fail_response (struct MHD_Connection *connection,
00761 const char *realm,
00762 const char *opaque,
00763 struct MHD_Response *response,
00764 int signal_stale)
00765 {
00766 int ret;
00767 size_t hlen;
00768 char nonce[HASH_MD5_HEX_LEN + 9];
00769
00770
00771 calculate_nonce ((uint32_t) MHD_monotonic_time(),
00772 connection->method,
00773 connection->daemon->digest_auth_random,
00774 connection->daemon->digest_auth_rand_size,
00775 connection->url,
00776 realm,
00777 nonce);
00778 if (MHD_YES != check_nonce_nc (connection, nonce, 0))
00779 {
00780 #if HAVE_MESSAGES
00781 MHD_DLOG (connection->daemon,
00782 "Could not register nonce (is the nonce array size zero?).\n");
00783 #endif
00784 return MHD_NO;
00785 }
00786
00787 hlen = snprintf (NULL,
00788 0,
00789 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00790 realm,
00791 nonce,
00792 opaque,
00793 signal_stale
00794 ? ",stale=\"true\""
00795 : "");
00796 {
00797 char header[hlen + 1];
00798
00799 snprintf (header,
00800 sizeof(header),
00801 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00802 realm,
00803 nonce,
00804 opaque,
00805 signal_stale
00806 ? ",stale=\"true\""
00807 : "");
00808 ret = MHD_add_response_header(response,
00809 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00810 header);
00811 }
00812 if (MHD_YES == ret)
00813 ret = MHD_queue_response(connection,
00814 MHD_HTTP_UNAUTHORIZED,
00815 response);
00816 return ret;
00817 }
00818
00819
00820