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 "internal.h"
00029 #include "md5.h"
00030 #include "base64.h"
00031
00032 #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
00033
00037 #define _BASE "Digest "
00038
00042 #define _BASIC_BASE "Basic "
00043
00047 #define MAX_USERNAME_LENGTH 128
00048
00052 #define MAX_REALM_LENGTH 256
00053
00057 #define MAX_AUTH_RESPONSE_LENGTH 128
00058
00066 static void
00067 cvthex(const unsigned char *bin,
00068 size_t len,
00069 char *hex)
00070 {
00071 size_t i;
00072 unsigned int j;
00073
00074 for (i = 0; i < len; ++i)
00075 {
00076 j = (bin[i] >> 4) & 0x0f;
00077 hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00078 j = bin[i] & 0x0f;
00079 hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00080 }
00081 hex[len * 2] = '\0';
00082 }
00083
00096 static void
00097 digest_calc_ha1(const char *alg,
00098 const char *username,
00099 const char *realm,
00100 const char *password,
00101 const char *nonce,
00102 const char *cnonce,
00103 char *sessionkey)
00104 {
00105 struct MD5Context md5;
00106 unsigned char ha1[MD5_DIGEST_SIZE];
00107
00108 MD5Init (&md5);
00109 MD5Update (&md5, username, strlen (username));
00110 MD5Update (&md5, ":", 1);
00111 MD5Update (&md5, realm, strlen (realm));
00112 MD5Update (&md5, ":", 1);
00113 MD5Update (&md5, password, strlen (password));
00114 MD5Final (ha1, &md5);
00115 if (0 == strcasecmp(alg, "md5-sess"))
00116 {
00117 MD5Init (&md5);
00118 MD5Update (&md5, ha1, sizeof (ha1));
00119 MD5Update (&md5, ":", 1);
00120 MD5Update (&md5, nonce, strlen (nonce));
00121 MD5Update (&md5, ":", 1);
00122 MD5Update (&md5, cnonce, strlen (cnonce));
00123 MD5Final (ha1, &md5);
00124 }
00125 cvthex(ha1, sizeof (ha1), sessionkey);
00126 }
00127
00128
00142 static void
00143 digest_calc_response(const char *ha1,
00144 const char *nonce,
00145 const char *noncecount,
00146 const char *cnonce,
00147 const char *qop,
00148 const char *method,
00149 const char *uri,
00150 const char *hentity,
00151 char *response)
00152 {
00153 struct MD5Context md5;
00154 unsigned char ha2[MD5_DIGEST_SIZE];
00155 unsigned char resphash[MD5_DIGEST_SIZE];
00156 char ha2hex[HASH_MD5_HEX_LEN + 1];
00157
00158 MD5Init (&md5);
00159 MD5Update (&md5, method, strlen(method));
00160 MD5Update (&md5, ":", 1);
00161 MD5Update (&md5, uri, strlen(uri));
00162 #if 0
00163 if (strcasecmp(qop, "auth-int") == 0)
00164 {
00165
00166
00167 MD5Update (&md5, ":", 1);
00168 if (hentity != NULL)
00169 MD5Update (&md5, hentity, strlen(hentity));
00170 }
00171 #endif
00172 MD5Final (ha2, &md5);
00173 cvthex(ha2, MD5_DIGEST_SIZE, ha2hex);
00174 MD5Init (&md5);
00175
00176 MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
00177 MD5Update (&md5, ":", 1);
00178 MD5Update (&md5, nonce, strlen(nonce));
00179 MD5Update (&md5, ":", 1);
00180 if ('\0' != *qop)
00181 {
00182 MD5Update (&md5, noncecount, strlen(noncecount));
00183 MD5Update (&md5, ":", 1);
00184 MD5Update (&md5, cnonce, strlen(cnonce));
00185 MD5Update (&md5, ":", 1);
00186 MD5Update (&md5, qop, strlen(qop));
00187 MD5Update (&md5, ":", 1);
00188 }
00189 MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
00190 MD5Final (resphash, &md5);
00191 cvthex(resphash, sizeof (resphash), response);
00192 }
00193
00194
00209 static int
00210 lookup_sub_value(char *dest,
00211 size_t size,
00212 const char *data,
00213 const char *key)
00214 {
00215 size_t keylen = strlen(key);
00216 size_t len;
00217 const char *ptr = data;
00218 const char *eq;
00219 const char *q1;
00220 const char *q2;
00221 const char *qn;
00222
00223 if (0 == size)
00224 return 0;
00225 while ('\0' != *ptr)
00226 {
00227 if (NULL == (eq = strstr (ptr, "=")))
00228 return 0;
00229 q1 = eq + 1;
00230 while (' ' == *q1)
00231 q1++;
00232 if ('\"' != *q1)
00233 {
00234 q2 = strstr (q1, ",");
00235 qn = q2;
00236 }
00237 else
00238 {
00239 q1++;
00240 q2 = strstr (q1, "\"");
00241 if (NULL == q2)
00242 return 0;
00243 qn = q2 + 1;
00244 }
00245 if ( (0 == strncasecmp (ptr,
00246 key,
00247 keylen)) &&
00248 (eq == &ptr[keylen]) )
00249 {
00250 if (q2 == NULL)
00251 {
00252 len = strlen (q1) + 1;
00253 if (size > len)
00254 size = len;
00255 size--;
00256 strncpy (dest,
00257 q1,
00258 size);
00259 dest[size] = '\0';
00260 return size;
00261 }
00262 else
00263 {
00264 if (size > (q2 - q1) + 1)
00265 size = (q2 - q1) + 1;
00266 size--;
00267 memcpy (dest,
00268 q1,
00269 size);
00270 dest[size] = '\0';
00271 return size;
00272 }
00273 }
00274 if (NULL == qn)
00275 return 0;
00276 ptr = strstr (qn, ",");
00277 if (NULL == ptr)
00278 return 0;
00279 ptr++;
00280 while (' ' == *ptr)
00281 ptr++;
00282 }
00283 return 0;
00284 }
00285
00286
00296 static int
00297 check_nonce_nc (struct MHD_Connection *connection,
00298 const char *nonce,
00299 unsigned int nc)
00300 {
00301 uint32_t off;
00302 uint32_t mod;
00303 const char *np;
00304
00305 mod = connection->daemon->nonce_nc_size;
00306 if (0 == mod)
00307 return MHD_NO;
00308
00309 off = 0;
00310 np = nonce;
00311 while (*np != '\0')
00312 {
00313 off = (off << 8) | (*np ^ (off >> 24));
00314 np++;
00315 }
00316 off = off % mod;
00317
00318
00319
00320
00321
00322
00323 pthread_mutex_lock(&connection->daemon->nnc_lock);
00324 if (nc == 0)
00325 {
00326 strcpy(connection->daemon->nnc[off].nonce,
00327 nonce);
00328 connection->daemon->nnc[off].nc = 0;
00329 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00330 return MHD_YES;
00331 }
00332 if ( (nc <= connection->daemon->nnc[off].nc) ||
00333 (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
00334 {
00335 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00336 #if HAVE_MESSAGES
00337 MHD_DLOG (connection->daemon,
00338 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n");
00339 #endif
00340 return MHD_NO;
00341 }
00342 connection->daemon->nnc[off].nc = nc;
00343 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00344 return MHD_YES;
00345 }
00346
00347
00355 char *
00356 MHD_digest_auth_get_username(struct MHD_Connection *connection)
00357 {
00358 size_t len;
00359 char user[MAX_USERNAME_LENGTH];
00360 const char *header;
00361
00362 header = MHD_lookup_connection_value(connection,
00363 MHD_HEADER_KIND,
00364 MHD_HTTP_HEADER_AUTHORIZATION);
00365 if (header == NULL)
00366 return NULL;
00367 if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00368 return NULL;
00369 header += strlen (_BASE);
00370 len = lookup_sub_value(user,
00371 sizeof (user),
00372 header,
00373 "username");
00374 if (!len)
00375 return NULL;
00376 return strdup(user);
00377 }
00378
00379
00393 static void
00394 calculate_nonce (uint32_t nonce_time,
00395 const char *method,
00396 const char *rnd,
00397 unsigned int rnd_size,
00398 const char *uri,
00399 const char *realm,
00400 char *nonce)
00401 {
00402 struct MD5Context md5;
00403 unsigned char timestamp[4];
00404 unsigned char tmpnonce[MD5_DIGEST_SIZE];
00405 char timestamphex[sizeof(timestamp)*2+1];
00406
00407 MD5Init (&md5);
00408 timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
00409 timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
00410 timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
00411 timestamp[3] = (nonce_time & 0x000000ff);
00412 MD5Update(&md5, timestamp, 4);
00413 MD5Update(&md5, ":", 1);
00414 MD5Update(&md5, method, strlen(method));
00415 MD5Update(&md5, ":", 1);
00416 if (rnd_size > 0)
00417 MD5Update(&md5, rnd, rnd_size);
00418 MD5Update(&md5, ":", 1);
00419 MD5Update(&md5, uri, strlen(uri));
00420 MD5Update(&md5, ":", 1);
00421 MD5Update(&md5, realm, strlen(realm));
00422 MD5Final (tmpnonce, &md5);
00423 cvthex(tmpnonce, sizeof (tmpnonce), nonce);
00424 cvthex(timestamp, 4, timestamphex);
00425 strncat(nonce, timestamphex, 8);
00426 }
00427
00428
00441 int
00442 MHD_digest_auth_check(struct MHD_Connection *connection,
00443 const char *realm,
00444 const char *username,
00445 const char *password,
00446 unsigned int nonce_timeout)
00447 {
00448 size_t len;
00449 const char *header;
00450 char nonce[MAX_NONCE_LENGTH];
00451 char cnonce[MAX_NONCE_LENGTH];
00452 char qop[15];
00453 char nc[20];
00454 char response[MAX_AUTH_RESPONSE_LENGTH];
00455 const char *hentity = NULL;
00456 char ha1[HASH_MD5_HEX_LEN + 1];
00457 char respexp[HASH_MD5_HEX_LEN + 1];
00458 char noncehashexp[HASH_MD5_HEX_LEN + 9];
00459 uint32_t nonce_time;
00460 uint32_t t;
00461 size_t left;
00462 unsigned int nci;
00463
00464 header = MHD_lookup_connection_value(connection,
00465 MHD_HEADER_KIND,
00466 MHD_HTTP_HEADER_AUTHORIZATION);
00467 if (header == NULL)
00468 return MHD_NO;
00469 if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00470 return MHD_NO;
00471 header += strlen (_BASE);
00472 left = strlen (header);
00473
00474 {
00475 char un[MAX_USERNAME_LENGTH];
00476 len = lookup_sub_value(un,
00477 sizeof (un),
00478 header, "username");
00479 if ( (!len) ||
00480 (strcmp(username, un) != 0) )
00481 return MHD_NO;
00482 left -= strlen ("username") + len;
00483 }
00484
00485 {
00486 char r[MAX_REALM_LENGTH];
00487 len = lookup_sub_value(r,
00488 sizeof (r),
00489 header, "realm");
00490 if ( (!len) ||
00491 (strcmp(realm, r) != 0) )
00492 return MHD_NO;
00493 left -= strlen ("realm") + len;
00494 }
00495
00496 if (0 == (len = lookup_sub_value(nonce,
00497 sizeof (nonce),
00498 header, "nonce")))
00499 return MHD_NO;
00500 left -= strlen ("nonce") + len;
00501
00502 {
00503 char uri[left];
00504
00505 if (0 == lookup_sub_value(uri,
00506 sizeof (uri),
00507 header, "uri"))
00508 return MHD_NO;
00509
00510
00511 nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);
00512 t = (uint32_t) time(NULL);
00513
00514
00515
00516
00517
00518
00519 if (t > nonce_time + nonce_timeout)
00520 return MHD_INVALID_NONCE;
00521 calculate_nonce (nonce_time,
00522 connection->method,
00523 connection->daemon->digest_auth_random,
00524 connection->daemon->digest_auth_rand_size,
00525 uri,
00526 realm,
00527 noncehashexp);
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538 if (0 != strcmp(nonce, noncehashexp))
00539 return MHD_INVALID_NONCE;
00540 if ( (0 == lookup_sub_value(cnonce,
00541 sizeof (cnonce),
00542 header, "cnonce")) ||
00543 (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) ||
00544 ( (0 != strcmp (qop, "auth")) &&
00545 (0 != strcmp (qop, "")) ) ||
00546 (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) ||
00547 (1 != sscanf (nc, "%u", &nci)) ||
00548 (0 == lookup_sub_value(response, sizeof (response), header, "response")) )
00549 return MHD_NO;
00550
00551
00552
00553
00554
00555
00556
00557 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
00558 return MHD_NO;
00559
00560 digest_calc_ha1("md5",
00561 username,
00562 realm,
00563 password,
00564 nonce,
00565 cnonce,
00566 ha1);
00567 digest_calc_response(ha1,
00568 nonce,
00569 nc,
00570 cnonce,
00571 qop,
00572 connection->method,
00573 uri,
00574 hentity,
00575 respexp);
00576 return strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO;
00577 }
00578 }
00579
00580
00591 int
00592 MHD_queue_auth_fail_response(struct MHD_Connection *connection,
00593 const char *realm,
00594 const char *opaque,
00595 struct MHD_Response *response,
00596 int signal_stale)
00597 {
00598 int ret;
00599 size_t hlen;
00600 char nonce[HASH_MD5_HEX_LEN + 9];
00601
00602
00603 calculate_nonce ((uint32_t) time(NULL),
00604 connection->method,
00605 connection->daemon->digest_auth_random,
00606 connection->daemon->digest_auth_rand_size,
00607 connection->url,
00608 realm,
00609 nonce);
00610 if (MHD_YES != check_nonce_nc (connection, nonce, 0))
00611 {
00612 #if HAVE_MESSAGES
00613 MHD_DLOG (connection->daemon,
00614 "Could not register nonce (is the nonce array size zero?).\n");
00615 #endif
00616 return MHD_NO;
00617 }
00618
00619 hlen = snprintf(NULL,
00620 0,
00621 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00622 realm,
00623 nonce,
00624 opaque,
00625 signal_stale ? ",stale=\"true\"" : "");
00626 {
00627 char header[hlen + 1];
00628 snprintf(header,
00629 sizeof(header),
00630 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00631 realm,
00632 nonce,
00633 opaque,
00634 signal_stale ? ",stale=\"true\"" : "");
00635 ret = MHD_add_response_header(response,
00636 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00637 header);
00638 }
00639 if (MHD_YES == ret)
00640 ret = MHD_queue_response(connection,
00641 MHD_HTTP_UNAUTHORIZED,
00642 response);
00643 return ret;
00644 }
00645
00646
00655 char *
00656 MHD_basic_auth_get_username_password(struct MHD_Connection *connection,
00657 char** password)
00658 {
00659 const char *header;
00660 char *decode;
00661 const char *separator;
00662 char *user;
00663
00664 header = MHD_lookup_connection_value(connection,
00665 MHD_HEADER_KIND,
00666 MHD_HTTP_HEADER_AUTHORIZATION);
00667 if (header == NULL)
00668 return NULL;
00669 if (strncmp(header, _BASIC_BASE, strlen(_BASIC_BASE)) != 0)
00670 return NULL;
00671 header += strlen(_BASIC_BASE);
00672 decode = BASE64Decode(header);
00673 if (decode == NULL)
00674 {
00675 #if HAVE_MESSAGES
00676 MHD_DLOG(connection->daemon,
00677 "Error decoding basic authentication\n");
00678 #endif
00679 return NULL;
00680 }
00681
00682 separator = strstr(decode, ":");
00683 if (separator == NULL)
00684 {
00685 #if HAVE_MESSAGES
00686 MHD_DLOG(connection->daemon,
00687 "Basic authentication doesn't contain ':' separator\n");
00688 #endif
00689 free(decode);
00690 return NULL;
00691 }
00692 user = strdup(decode);
00693 if (NULL == user)
00694 {
00695 free (decode);
00696 return NULL;
00697 }
00698 user[separator - decode] = '\0';
00699 if (password != NULL)
00700 {
00701 *password = strdup(separator + 1);
00702 if (NULL == *password)
00703 {
00704 #if HAVE_MESSAGES
00705 MHD_DLOG(connection->daemon,
00706 "Failed to allocate memory for password\n");
00707 #endif
00708 free (decode);
00709 free (user);
00710 return NULL;
00711 }
00712 }
00713 free(decode);
00714 return user;
00715 }
00716
00717
00725 int
00726 MHD_queue_basic_auth_fail_response(struct MHD_Connection *connection,
00727 const char *realm,
00728 struct MHD_Response *response)
00729 {
00730 int ret;
00731 size_t hlen = strlen(realm) + strlen("Basic realm=\"\"");
00732 char header[hlen + 1];
00733
00734 if (hlen !=
00735 snprintf(header,
00736 hlen + 1,
00737 "Basic realm=\"%s\"",
00738 realm))
00739 {
00740 EXTRA_CHECK (0);
00741 return MHD_NO;
00742 }
00743 ret = MHD_add_response_header(response,
00744 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00745 header);
00746 if (MHD_YES == ret)
00747 ret = MHD_queue_response(connection,
00748 MHD_HTTP_UNAUTHORIZED,
00749 response);
00750 return ret;
00751 }
00752
00753