00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00027 #include "platform.h"
00028 #include <limits.h>
00029 #include "internal.h"
00030 #include "md5.h"
00031 #include "base64.h"
00032
00033 #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
00034
00038 #define _BASE "Digest "
00039
00043 #define _BASIC_BASE "Basic "
00044
00048 #define MAX_USERNAME_LENGTH 128
00049
00053 #define MAX_REALM_LENGTH 256
00054
00058 #define MAX_AUTH_RESPONSE_LENGTH 128
00059
00067 static void
00068 cvthex(const unsigned char *bin,
00069 size_t len,
00070 char *hex)
00071 {
00072 size_t i;
00073 unsigned int j;
00074
00075 for (i = 0; i < len; ++i)
00076 {
00077 j = (bin[i] >> 4) & 0x0f;
00078 hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00079 j = bin[i] & 0x0f;
00080 hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00081 }
00082 hex[len * 2] = '\0';
00083 }
00084
00097 static void
00098 digest_calc_ha1(const char *alg,
00099 const char *username,
00100 const char *realm,
00101 const char *password,
00102 const char *nonce,
00103 const char *cnonce,
00104 char *sessionkey)
00105 {
00106 struct MD5Context md5;
00107 unsigned char ha1[MD5_DIGEST_SIZE];
00108
00109 MD5Init (&md5);
00110 MD5Update (&md5, username, strlen (username));
00111 MD5Update (&md5, ":", 1);
00112 MD5Update (&md5, realm, strlen (realm));
00113 MD5Update (&md5, ":", 1);
00114 MD5Update (&md5, password, strlen (password));
00115 MD5Final (ha1, &md5);
00116 if (0 == strcasecmp(alg, "md5-sess"))
00117 {
00118 MD5Init (&md5);
00119 MD5Update (&md5, ha1, sizeof (ha1));
00120 MD5Update (&md5, ":", 1);
00121 MD5Update (&md5, nonce, strlen (nonce));
00122 MD5Update (&md5, ":", 1);
00123 MD5Update (&md5, cnonce, strlen (cnonce));
00124 MD5Final (ha1, &md5);
00125 }
00126 cvthex(ha1, sizeof (ha1), sessionkey);
00127 }
00128
00129
00143 static void
00144 digest_calc_response(const char *ha1,
00145 const char *nonce,
00146 const char *noncecount,
00147 const char *cnonce,
00148 const char *qop,
00149 const char *method,
00150 const char *uri,
00151 const char *hentity,
00152 char *response)
00153 {
00154 struct MD5Context md5;
00155 unsigned char ha2[MD5_DIGEST_SIZE];
00156 unsigned char resphash[MD5_DIGEST_SIZE];
00157 char ha2hex[HASH_MD5_HEX_LEN + 1];
00158
00159 MD5Init (&md5);
00160 MD5Update (&md5, method, strlen(method));
00161 MD5Update (&md5, ":", 1);
00162 MD5Update (&md5, uri, strlen(uri));
00163 #if 0
00164 if (strcasecmp(qop, "auth-int") == 0)
00165 {
00166
00167
00168 MD5Update (&md5, ":", 1);
00169 if (hentity != NULL)
00170 MD5Update (&md5, hentity, strlen(hentity));
00171 }
00172 #endif
00173 MD5Final (ha2, &md5);
00174 cvthex(ha2, MD5_DIGEST_SIZE, ha2hex);
00175 MD5Init (&md5);
00176
00177 MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
00178 MD5Update (&md5, ":", 1);
00179 MD5Update (&md5, nonce, strlen(nonce));
00180 MD5Update (&md5, ":", 1);
00181 if ('\0' != *qop)
00182 {
00183 MD5Update (&md5, noncecount, strlen(noncecount));
00184 MD5Update (&md5, ":", 1);
00185 MD5Update (&md5, cnonce, strlen(cnonce));
00186 MD5Update (&md5, ":", 1);
00187 MD5Update (&md5, qop, strlen(qop));
00188 MD5Update (&md5, ":", 1);
00189 }
00190 MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
00191 MD5Final (resphash, &md5);
00192 cvthex(resphash, sizeof (resphash), response);
00193 }
00194
00195
00210 static int
00211 lookup_sub_value(char *dest,
00212 size_t size,
00213 const char *data,
00214 const char *key)
00215 {
00216 size_t keylen = strlen(key);
00217 size_t len;
00218 const char *ptr = data;
00219 const char *eq;
00220 const char *q1;
00221 const char *q2;
00222 const char *qn;
00223
00224 if (0 == size)
00225 return 0;
00226 while ('\0' != *ptr)
00227 {
00228 if (NULL == (eq = strstr (ptr, "=")))
00229 return 0;
00230 q1 = eq + 1;
00231 while (' ' == *q1)
00232 q1++;
00233 if ('\"' != *q1)
00234 {
00235 q2 = strstr (q1, ",");
00236 qn = q2;
00237 }
00238 else
00239 {
00240 q1++;
00241 q2 = strstr (q1, "\"");
00242 if (NULL == q2)
00243 return 0;
00244 qn = q2 + 1;
00245 }
00246 if ( (0 == strncasecmp (ptr,
00247 key,
00248 keylen)) &&
00249 (eq == &ptr[keylen]) )
00250 {
00251 if (q2 == NULL)
00252 {
00253 len = strlen (q1) + 1;
00254 if (size > len)
00255 size = len;
00256 size--;
00257 strncpy (dest,
00258 q1,
00259 size);
00260 dest[size] = '\0';
00261 return size;
00262 }
00263 else
00264 {
00265 if (size > (q2 - q1) + 1)
00266 size = (q2 - q1) + 1;
00267 size--;
00268 memcpy (dest,
00269 q1,
00270 size);
00271 dest[size] = '\0';
00272 return size;
00273 }
00274 }
00275 if (NULL == qn)
00276 return 0;
00277 ptr = strstr (qn, ",");
00278 if (NULL == ptr)
00279 return 0;
00280 ptr++;
00281 while (' ' == *ptr)
00282 ptr++;
00283 }
00284 return 0;
00285 }
00286
00287
00297 static int
00298 check_nonce_nc (struct MHD_Connection *connection,
00299 const char *nonce,
00300 unsigned long int nc)
00301 {
00302 uint32_t off;
00303 uint32_t mod;
00304 const char *np;
00305
00306 mod = connection->daemon->nonce_nc_size;
00307 if (0 == mod)
00308 return MHD_NO;
00309
00310 off = 0;
00311 np = nonce;
00312 while (*np != '\0')
00313 {
00314 off = (off << 8) | (*np ^ (off >> 24));
00315 np++;
00316 }
00317 off = off % mod;
00318
00319
00320
00321
00322
00323
00324 pthread_mutex_lock(&connection->daemon->nnc_lock);
00325 if (nc == 0)
00326 {
00327 strcpy(connection->daemon->nnc[off].nonce,
00328 nonce);
00329 connection->daemon->nnc[off].nc = 0;
00330 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00331 return MHD_YES;
00332 }
00333 if ( (nc <= connection->daemon->nnc[off].nc) ||
00334 (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
00335 {
00336 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00337 #if HAVE_MESSAGES
00338 MHD_DLOG (connection->daemon,
00339 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n");
00340 #endif
00341 return MHD_NO;
00342 }
00343 connection->daemon->nnc[off].nc = nc;
00344 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00345 return MHD_YES;
00346 }
00347
00348
00356 char *
00357 MHD_digest_auth_get_username(struct MHD_Connection *connection)
00358 {
00359 size_t len;
00360 char user[MAX_USERNAME_LENGTH];
00361 const char *header;
00362
00363 header = MHD_lookup_connection_value(connection,
00364 MHD_HEADER_KIND,
00365 MHD_HTTP_HEADER_AUTHORIZATION);
00366 if (header == NULL)
00367 return NULL;
00368 if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00369 return NULL;
00370 header += strlen (_BASE);
00371 len = lookup_sub_value(user,
00372 sizeof (user),
00373 header,
00374 "username");
00375 if (!len)
00376 return NULL;
00377 return strdup(user);
00378 }
00379
00380
00394 static void
00395 calculate_nonce (uint32_t nonce_time,
00396 const char *method,
00397 const char *rnd,
00398 unsigned int rnd_size,
00399 const char *uri,
00400 const char *realm,
00401 char *nonce)
00402 {
00403 struct MD5Context md5;
00404 unsigned char timestamp[4];
00405 unsigned char tmpnonce[MD5_DIGEST_SIZE];
00406 char timestamphex[sizeof(timestamp)*2+1];
00407
00408 MD5Init (&md5);
00409 timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
00410 timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
00411 timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
00412 timestamp[3] = (nonce_time & 0x000000ff);
00413 MD5Update(&md5, timestamp, 4);
00414 MD5Update(&md5, ":", 1);
00415 MD5Update(&md5, method, strlen(method));
00416 MD5Update(&md5, ":", 1);
00417 if (rnd_size > 0)
00418 MD5Update(&md5, rnd, rnd_size);
00419 MD5Update(&md5, ":", 1);
00420 MD5Update(&md5, uri, strlen(uri));
00421 MD5Update(&md5, ":", 1);
00422 MD5Update(&md5, realm, strlen(realm));
00423 MD5Final (tmpnonce, &md5);
00424 cvthex(tmpnonce, sizeof (tmpnonce), nonce);
00425 cvthex(timestamp, 4, timestamphex);
00426 strncat(nonce, timestamphex, 8);
00427 }
00428
00429
00440 static int
00441 test_header (struct MHD_Connection *connection,
00442 const char *key,
00443 const char *value)
00444 {
00445 struct MHD_HTTP_Header *pos;
00446
00447 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00448 {
00449 if (MHD_GET_ARGUMENT_KIND != pos->kind)
00450 continue;
00451 if (0 != strcmp (key, pos->header))
00452 continue;
00453 if ( (NULL == value) && (NULL == pos->value))
00454 return MHD_YES;
00455 if ( (NULL == value) || (NULL == pos->value))
00456 continue;
00457 if (0 != strcmp (value, pos->value))
00458 continue;
00459 return MHD_YES;
00460 }
00461 return MHD_NO;
00462 }
00463
00464
00475 static int
00476 check_argument_match (struct MHD_Connection *connection,
00477 const char *args)
00478 {
00479 struct MHD_HTTP_Header *pos;
00480 size_t slen = strlen (args) + 1;
00481 char argb[slen];
00482 char *argp;
00483 char *equals;
00484 char *amper;
00485 unsigned int num_headers;
00486
00487 num_headers = 0;
00488 memcpy (argb, args, slen);
00489 argp = argb;
00490 while ( (argp != NULL) &&
00491 (argp[0] != '\0') )
00492 {
00493 equals = strstr (argp, "=");
00494 if (equals == NULL)
00495 {
00496
00497 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00498 connection,
00499 argp);
00500 if (MHD_YES != test_header (connection, argp, NULL))
00501 return MHD_NO;
00502 num_headers++;
00503 break;
00504 }
00505 equals[0] = '\0';
00506 equals++;
00507 amper = strstr (equals, "&");
00508 if (amper != NULL)
00509 {
00510 amper[0] = '\0';
00511 amper++;
00512 }
00513 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00514 connection,
00515 argp);
00516 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
00517 connection,
00518 equals);
00519 if (! test_header (connection, argp, equals))
00520 return MHD_NO;
00521 num_headers++;
00522 argp = amper;
00523 }
00524
00525
00526 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
00527 {
00528 if (MHD_GET_ARGUMENT_KIND != pos->kind)
00529 continue;
00530 num_headers--;
00531 }
00532 if (0 != num_headers)
00533 return MHD_NO;
00534 return MHD_YES;
00535 }
00536
00537
00550 int
00551 MHD_digest_auth_check(struct MHD_Connection *connection,
00552 const char *realm,
00553 const char *username,
00554 const char *password,
00555 unsigned int nonce_timeout)
00556 {
00557 size_t len;
00558 const char *header;
00559 char *end;
00560 char nonce[MAX_NONCE_LENGTH];
00561 char cnonce[MAX_NONCE_LENGTH];
00562 char qop[15];
00563 char nc[20];
00564 char response[MAX_AUTH_RESPONSE_LENGTH];
00565 const char *hentity = NULL;
00566 char ha1[HASH_MD5_HEX_LEN + 1];
00567 char respexp[HASH_MD5_HEX_LEN + 1];
00568 char noncehashexp[HASH_MD5_HEX_LEN + 9];
00569 uint32_t nonce_time;
00570 uint32_t t;
00571 size_t left;
00572 unsigned long int nci;
00573
00574 header = MHD_lookup_connection_value(connection,
00575 MHD_HEADER_KIND,
00576 MHD_HTTP_HEADER_AUTHORIZATION);
00577 if (header == NULL)
00578 return MHD_NO;
00579 if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00580 return MHD_NO;
00581 header += strlen (_BASE);
00582 left = strlen (header);
00583
00584 {
00585 char un[MAX_USERNAME_LENGTH];
00586 len = lookup_sub_value(un,
00587 sizeof (un),
00588 header, "username");
00589 if ( (!len) ||
00590 (strcmp(username, un) != 0) )
00591 return MHD_NO;
00592 left -= strlen ("username") + len;
00593 }
00594
00595 {
00596 char r[MAX_REALM_LENGTH];
00597 len = lookup_sub_value(r,
00598 sizeof (r),
00599 header, "realm");
00600 if ( (!len) ||
00601 (strcmp(realm, r) != 0) )
00602 return MHD_NO;
00603 left -= strlen ("realm") + len;
00604 }
00605
00606 if (0 == (len = lookup_sub_value(nonce,
00607 sizeof (nonce),
00608 header, "nonce")))
00609 return MHD_NO;
00610 left -= strlen ("nonce") + len;
00611
00612 {
00613 char uri[left];
00614
00615 if (0 == lookup_sub_value(uri,
00616 sizeof (uri),
00617 header, "uri"))
00618 return MHD_NO;
00619
00620
00621 nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);
00622 t = (uint32_t) MHD_monotonic_time();
00623
00624
00625
00626
00627
00628 if ( (t > nonce_time + nonce_timeout) ||
00629 (nonce_time + nonce_timeout < nonce_time) )
00630 return MHD_INVALID_NONCE;
00631 if (0 != strncmp (uri,
00632 connection->url,
00633 strlen (connection->url)))
00634 {
00635 #if HAVE_MESSAGES
00636 MHD_DLOG (connection->daemon,
00637 "Authentication failed, URI does not match.\n");
00638 #endif
00639 return MHD_NO;
00640 }
00641 {
00642 const char *args = strstr (uri, "?");
00643 if (args == NULL)
00644 args = "";
00645 else
00646 args++;
00647 if (MHD_YES !=
00648 check_argument_match (connection,
00649 args) )
00650 {
00651 #if HAVE_MESSAGES
00652 MHD_DLOG (connection->daemon,
00653 "Authentication failed, arguments do not match.\n");
00654 #endif
00655 return MHD_NO;
00656 }
00657 }
00658 calculate_nonce (nonce_time,
00659 connection->method,
00660 connection->daemon->digest_auth_random,
00661 connection->daemon->digest_auth_rand_size,
00662 connection->url,
00663 realm,
00664 noncehashexp);
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675 if (0 != strcmp(nonce, noncehashexp))
00676 return MHD_INVALID_NONCE;
00677 if ( (0 == lookup_sub_value(cnonce,
00678 sizeof (cnonce),
00679 header, "cnonce")) ||
00680 (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) ||
00681 ( (0 != strcmp (qop, "auth")) &&
00682 (0 != strcmp (qop, "")) ) ||
00683 (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) ||
00684 (0 == lookup_sub_value(response, sizeof (response), header, "response")) )
00685 {
00686 #if HAVE_MESSAGES
00687 MHD_DLOG (connection->daemon,
00688 "Authentication failed, invalid format.\n");
00689 #endif
00690 return MHD_NO;
00691 }
00692 nci = strtoul (nc, &end, 16);
00693 if ( ('\0' != *end) ||
00694 ( (LONG_MAX == nci) && (errno == ERANGE) ) )
00695 {
00696 #if HAVE_MESSAGES
00697 MHD_DLOG (connection->daemon,
00698 "Authentication failed, invalid format.\n");
00699 #endif
00700 return MHD_NO;
00701 }
00702
00703
00704
00705
00706
00707
00708 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
00709 return MHD_NO;
00710
00711 digest_calc_ha1("md5",
00712 username,
00713 realm,
00714 password,
00715 nonce,
00716 cnonce,
00717 ha1);
00718 digest_calc_response(ha1,
00719 nonce,
00720 nc,
00721 cnonce,
00722 qop,
00723 connection->method,
00724 uri,
00725 hentity,
00726 respexp);
00727 return strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO;
00728 }
00729 }
00730
00731
00742 int
00743 MHD_queue_auth_fail_response(struct MHD_Connection *connection,
00744 const char *realm,
00745 const char *opaque,
00746 struct MHD_Response *response,
00747 int signal_stale)
00748 {
00749 int ret;
00750 size_t hlen;
00751 char nonce[HASH_MD5_HEX_LEN + 9];
00752
00753
00754 calculate_nonce ((uint32_t) MHD_monotonic_time(),
00755 connection->method,
00756 connection->daemon->digest_auth_random,
00757 connection->daemon->digest_auth_rand_size,
00758 connection->url,
00759 realm,
00760 nonce);
00761 if (MHD_YES != check_nonce_nc (connection, nonce, 0))
00762 {
00763 #if HAVE_MESSAGES
00764 MHD_DLOG (connection->daemon,
00765 "Could not register nonce (is the nonce array size zero?).\n");
00766 #endif
00767 return MHD_NO;
00768 }
00769
00770 hlen = snprintf(NULL,
00771 0,
00772 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00773 realm,
00774 nonce,
00775 opaque,
00776 signal_stale ? ",stale=\"true\"" : "");
00777 {
00778 char header[hlen + 1];
00779 snprintf(header,
00780 sizeof(header),
00781 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00782 realm,
00783 nonce,
00784 opaque,
00785 signal_stale ? ",stale=\"true\"" : "");
00786 ret = MHD_add_response_header(response,
00787 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00788 header);
00789 }
00790 if (MHD_YES == ret)
00791 ret = MHD_queue_response(connection,
00792 MHD_HTTP_UNAUTHORIZED,
00793 response);
00794 return ret;
00795 }
00796
00797
00806 char *
00807 MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
00808 char** password)
00809 {
00810 const char *header;
00811 char *decode;
00812 const char *separator;
00813 char *user;
00814
00815 header = MHD_lookup_connection_value(connection,
00816 MHD_HEADER_KIND,
00817 MHD_HTTP_HEADER_AUTHORIZATION);
00818 if (header == NULL)
00819 return NULL;
00820 if (strncmp(header, _BASIC_BASE, strlen(_BASIC_BASE)) != 0)
00821 return NULL;
00822 header += strlen(_BASIC_BASE);
00823 decode = BASE64Decode(header);
00824 if (decode == NULL)
00825 {
00826 #if HAVE_MESSAGES
00827 MHD_DLOG(connection->daemon,
00828 "Error decoding basic authentication\n");
00829 #endif
00830 return NULL;
00831 }
00832
00833 separator = strstr(decode, ":");
00834 if (separator == NULL)
00835 {
00836 #if HAVE_MESSAGES
00837 MHD_DLOG(connection->daemon,
00838 "Basic authentication doesn't contain ':' separator\n");
00839 #endif
00840 free(decode);
00841 return NULL;
00842 }
00843 user = strdup(decode);
00844 if (NULL == user)
00845 {
00846 free (decode);
00847 return NULL;
00848 }
00849 user[separator - decode] = '\0';
00850 if (password != NULL)
00851 {
00852 *password = strdup(separator + 1);
00853 if (NULL == *password)
00854 {
00855 #if HAVE_MESSAGES
00856 MHD_DLOG(connection->daemon,
00857 "Failed to allocate memory for password\n");
00858 #endif
00859 free (decode);
00860 free (user);
00861 return NULL;
00862 }
00863 }
00864 free(decode);
00865 return user;
00866 }
00867
00868
00876 int
00877 MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection,
00878 const char *realm,
00879 struct MHD_Response *response)
00880 {
00881 int ret;
00882 size_t hlen = strlen(realm) + strlen("Basic realm=\"\"") + 1;
00883 char header[hlen];
00884
00885 snprintf(header,
00886 sizeof (header),
00887 "Basic realm=\"%s\"",
00888 realm);
00889 ret = MHD_add_response_header(response,
00890 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00891 header);
00892 if (MHD_YES == ret)
00893 ret = MHD_queue_response(connection,
00894 MHD_HTTP_UNAUTHORIZED,
00895 response);
00896 return ret;
00897 }
00898
00899