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 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 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 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 pthread_mutex_unlock (&connection->daemon->nnc_lock);
00341 return MHD_YES;
00342 }
00343
00344
00352 char *
00353 MHD_digest_auth_get_username(struct MHD_Connection *connection)
00354 {
00355 size_t len;
00356 char user[MAX_USERNAME_LENGTH];
00357 const char *header;
00358
00359 if (NULL == (header = MHD_lookup_connection_value (connection,
00360 MHD_HEADER_KIND,
00361 MHD_HTTP_HEADER_AUTHORIZATION)))
00362 return NULL;
00363 if (0 != strncmp (header, _BASE, strlen (_BASE)))
00364 return NULL;
00365 header += strlen (_BASE);
00366 if (0 == (len = lookup_sub_value (user,
00367 sizeof (user),
00368 header,
00369 "username")))
00370 return NULL;
00371 return strdup (user);
00372 }
00373
00374
00388 static void
00389 calculate_nonce (uint32_t nonce_time,
00390 const char *method,
00391 const char *rnd,
00392 unsigned int rnd_size,
00393 const char *uri,
00394 const char *realm,
00395 char *nonce)
00396 {
00397 struct MD5Context md5;
00398 unsigned char timestamp[4];
00399 unsigned char tmpnonce[MD5_DIGEST_SIZE];
00400 char timestamphex[sizeof(timestamp) * 2 + 1];
00401
00402 MD5Init (&md5);
00403 timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
00404 timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
00405 timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
00406 timestamp[3] = (nonce_time & 0x000000ff);
00407 MD5Update (&md5, timestamp, 4);
00408 MD5Update (&md5, ":", 1);
00409 MD5Update (&md5, method, strlen(method));
00410 MD5Update (&md5, ":", 1);
00411 if (rnd_size > 0)
00412 MD5Update (&md5, rnd, rnd_size);
00413 MD5Update (&md5, ":", 1);
00414 MD5Update (&md5, uri, strlen(uri));
00415 MD5Update (&md5, ":", 1);
00416 MD5Update (&md5, realm, strlen(realm));
00417 MD5Final (tmpnonce, &md5);
00418 cvthex (tmpnonce, sizeof (tmpnonce), nonce);
00419 cvthex (timestamp, 4, timestamphex);
00420 strncat (nonce, timestamphex, 8);
00421 }
00422
00423
00434 static int
00435 test_header (struct MHD_Connection *connection,
00436 const char *key,
00437 const char *value)
00438 {
00439 struct MHD_HTTP_Header *pos;
00440
00441 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00442 {
00443 if (MHD_GET_ARGUMENT_KIND != pos->kind)
00444 continue;
00445 if (0 != strcmp (key, pos->header))
00446 continue;
00447 if ( (NULL == value) &&
00448 (NULL == pos->value) )
00449 return MHD_YES;
00450 if ( (NULL == value) ||
00451 (NULL == pos->value) ||
00452 (0 != strcmp (value, pos->value)) )
00453 continue;
00454 return MHD_YES;
00455 }
00456 return MHD_NO;
00457 }
00458
00459
00470 static int
00471 check_argument_match (struct MHD_Connection *connection,
00472 const char *args)
00473 {
00474 struct MHD_HTTP_Header *pos;
00475 size_t slen = strlen (args) + 1;
00476 char argb[slen];
00477 char *argp;
00478 char *equals;
00479 char *amper;
00480 unsigned int num_headers;
00481
00482 num_headers = 0;
00483 memcpy (argb, args, slen);
00484 argp = argb;
00485 while ( (NULL != argp) &&
00486 ('\0' != argp[0]) )
00487 {
00488 equals = strchr (argp, '=');
00489 if (NULL == equals)
00490 {
00491
00492 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00493 connection,
00494 argp);
00495 if (MHD_YES != test_header (connection, argp, NULL))
00496 return MHD_NO;
00497 num_headers++;
00498 break;
00499 }
00500 equals[0] = '\0';
00501 equals++;
00502 amper = strchr (equals, '&');
00503 if (NULL != amper)
00504 {
00505 amper[0] = '\0';
00506 amper++;
00507 }
00508 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00509 connection,
00510 argp);
00511 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00512 connection,
00513 equals);
00514 if (! test_header (connection, argp, equals))
00515 return MHD_NO;
00516 num_headers++;
00517 argp = amper;
00518 }
00519
00520
00521 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00522 {
00523 if (MHD_GET_ARGUMENT_KIND != pos->kind)
00524 continue;
00525 num_headers--;
00526 }
00527 if (0 != num_headers)
00528 return MHD_NO;
00529 return MHD_YES;
00530 }
00531
00532
00545 int
00546 MHD_digest_auth_check (struct MHD_Connection *connection,
00547 const char *realm,
00548 const char *username,
00549 const char *password,
00550 unsigned int nonce_timeout)
00551 {
00552 size_t len;
00553 const char *header;
00554 char *end;
00555 char nonce[MAX_NONCE_LENGTH];
00556 char cnonce[MAX_NONCE_LENGTH];
00557 char qop[15];
00558 char nc[20];
00559 char response[MAX_AUTH_RESPONSE_LENGTH];
00560 const char *hentity = NULL;
00561 char ha1[HASH_MD5_HEX_LEN + 1];
00562 char respexp[HASH_MD5_HEX_LEN + 1];
00563 char noncehashexp[HASH_MD5_HEX_LEN + 9];
00564 uint32_t nonce_time;
00565 uint32_t t;
00566 size_t left;
00567 unsigned long int nci;
00568
00569 header = MHD_lookup_connection_value (connection,
00570 MHD_HEADER_KIND,
00571 MHD_HTTP_HEADER_AUTHORIZATION);
00572 if (NULL == header)
00573 return MHD_NO;
00574 if (0 != strncmp(header, _BASE, strlen(_BASE)))
00575 return MHD_NO;
00576 header += strlen (_BASE);
00577 left = strlen (header);
00578
00579 {
00580 char un[MAX_USERNAME_LENGTH];
00581
00582 len = lookup_sub_value (un,
00583 sizeof (un),
00584 header, "username");
00585 if ( (0 == len) ||
00586 (0 != strcmp(username, un)) )
00587 return MHD_NO;
00588 left -= strlen ("username") + len;
00589 }
00590
00591 {
00592 char r[MAX_REALM_LENGTH];
00593
00594 len = lookup_sub_value(r,
00595 sizeof (r),
00596 header, "realm");
00597 if ( (0 == len) ||
00598 (0 != strcmp(realm, r)) )
00599 return MHD_NO;
00600 left -= strlen ("realm") + len;
00601 }
00602
00603 if (0 == (len = lookup_sub_value (nonce,
00604 sizeof (nonce),
00605 header, "nonce")))
00606 return MHD_NO;
00607 left -= strlen ("nonce") + len;
00608
00609 {
00610 char uri[left];
00611
00612 if (0 == lookup_sub_value(uri,
00613 sizeof (uri),
00614 header, "uri"))
00615 return MHD_NO;
00616
00617
00618 nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);
00619 t = (uint32_t) MHD_monotonic_time();
00620
00621
00622
00623
00624
00625 if ( (t > nonce_time + nonce_timeout) ||
00626 (nonce_time + nonce_timeout < nonce_time) )
00627 return MHD_INVALID_NONCE;
00628 if (0 != strncmp (uri,
00629 connection->url,
00630 strlen (connection->url)))
00631 {
00632 #if HAVE_MESSAGES
00633 MHD_DLOG (connection->daemon,
00634 "Authentication failed, URI does not match.\n");
00635 #endif
00636 return MHD_NO;
00637 }
00638 {
00639 const char *args = strchr (uri, '?');
00640
00641 if (NULL == args)
00642 args = "";
00643 else
00644 args++;
00645 if (MHD_YES !=
00646 check_argument_match (connection,
00647 args) )
00648 {
00649 #if HAVE_MESSAGES
00650 MHD_DLOG (connection->daemon,
00651 "Authentication failed, arguments do not match.\n");
00652 #endif
00653 return MHD_NO;
00654 }
00655 }
00656 calculate_nonce (nonce_time,
00657 connection->method,
00658 connection->daemon->digest_auth_random,
00659 connection->daemon->digest_auth_rand_size,
00660 connection->url,
00661 realm,
00662 noncehashexp);
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673 if (0 != strcmp (nonce, noncehashexp))
00674 return MHD_INVALID_NONCE;
00675 if ( (0 == lookup_sub_value (cnonce,
00676 sizeof (cnonce),
00677 header, "cnonce")) ||
00678 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
00679 ( (0 != strcmp (qop, "auth")) &&
00680 (0 != strcmp (qop, "")) ) ||
00681 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
00682 (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
00683 {
00684 #if HAVE_MESSAGES
00685 MHD_DLOG (connection->daemon,
00686 "Authentication failed, invalid format.\n");
00687 #endif
00688 return MHD_NO;
00689 }
00690 nci = strtoul (nc, &end, 16);
00691 if ( ('\0' != *end) ||
00692 ( (LONG_MAX == nci) &&
00693 (ERANGE == errno) ) )
00694 {
00695 #if HAVE_MESSAGES
00696 MHD_DLOG (connection->daemon,
00697 "Authentication failed, invalid format.\n");
00698 #endif
00699 return MHD_NO;
00700 }
00701
00702
00703
00704
00705
00706
00707 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
00708 return MHD_NO;
00709
00710 digest_calc_ha1("md5",
00711 username,
00712 realm,
00713 password,
00714 nonce,
00715 cnonce,
00716 ha1);
00717 digest_calc_response (ha1,
00718 nonce,
00719 nc,
00720 cnonce,
00721 qop,
00722 connection->method,
00723 uri,
00724 hentity,
00725 respexp);
00726 return (0 == strcmp(response, respexp))
00727 ? MHD_YES
00728 : MHD_NO;
00729 }
00730 }
00731
00732
00743 int
00744 MHD_queue_auth_fail_response (struct MHD_Connection *connection,
00745 const char *realm,
00746 const char *opaque,
00747 struct MHD_Response *response,
00748 int signal_stale)
00749 {
00750 int ret;
00751 size_t hlen;
00752 char nonce[HASH_MD5_HEX_LEN + 9];
00753
00754
00755 calculate_nonce ((uint32_t) MHD_monotonic_time(),
00756 connection->method,
00757 connection->daemon->digest_auth_random,
00758 connection->daemon->digest_auth_rand_size,
00759 connection->url,
00760 realm,
00761 nonce);
00762 if (MHD_YES != check_nonce_nc (connection, nonce, 0))
00763 {
00764 #if HAVE_MESSAGES
00765 MHD_DLOG (connection->daemon,
00766 "Could not register nonce (is the nonce array size zero?).\n");
00767 #endif
00768 return MHD_NO;
00769 }
00770
00771 hlen = snprintf (NULL,
00772 0,
00773 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00774 realm,
00775 nonce,
00776 opaque,
00777 signal_stale
00778 ? ",stale=\"true\""
00779 : "");
00780 {
00781 char header[hlen + 1];
00782
00783 snprintf (header,
00784 sizeof(header),
00785 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00786 realm,
00787 nonce,
00788 opaque,
00789 signal_stale
00790 ? ",stale=\"true\""
00791 : "");
00792 ret = MHD_add_response_header(response,
00793 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00794 header);
00795 }
00796 if (MHD_YES == ret)
00797 ret = MHD_queue_response(connection,
00798 MHD_HTTP_UNAUTHORIZED,
00799 response);
00800 return ret;
00801 }
00802
00803
00804