00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00026 #include "platform.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
00059 static void
00060 cvthex(const unsigned char *bin,
00061 size_t len,
00062 char *hex)
00063 {
00064 size_t i;
00065 unsigned int j;
00066
00067 for (i = 0; i < len; ++i)
00068 {
00069 j = (bin[i] >> 4) & 0x0f;
00070 hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00071 j = bin[i] & 0x0f;
00072 hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
00073 }
00074 hex[len * 2] = '\0';
00075 }
00076
00089 static void
00090 digest_calc_ha1(const char *alg,
00091 const char *username,
00092 const char *realm,
00093 const char *password,
00094 const char *nonce,
00095 const char *cnonce,
00096 char *sessionkey)
00097 {
00098 struct MD5Context md5;
00099 unsigned char ha1[MD5_DIGEST_SIZE];
00100
00101 MD5Init (&md5);
00102 MD5Update (&md5, username, strlen (username));
00103 MD5Update (&md5, ":", 1);
00104 MD5Update (&md5, realm, strlen (realm));
00105 MD5Update (&md5, ":", 1);
00106 MD5Update (&md5, password, strlen (password));
00107 MD5Final (ha1, &md5);
00108 if (0 == strcasecmp(alg, "md5-sess"))
00109 {
00110 MD5Init (&md5);
00111 MD5Update (&md5, ha1, sizeof (ha1));
00112 MD5Update (&md5, ":", 1);
00113 MD5Update (&md5, nonce, strlen (nonce));
00114 MD5Update (&md5, ":", 1);
00115 MD5Update (&md5, cnonce, strlen (cnonce));
00116 MD5Final (ha1, &md5);
00117 }
00118 cvthex(ha1, sizeof (ha1), sessionkey);
00119 }
00120
00121
00135 static void
00136 digest_calc_response(const char *ha1,
00137 const char *nonce,
00138 const char *noncecount,
00139 const char *cnonce,
00140 const char *qop,
00141 const char *method,
00142 const char *uri,
00143 const char *hentity,
00144 char *response)
00145 {
00146 struct MD5Context md5;
00147 unsigned char ha2[MD5_DIGEST_SIZE];
00148 unsigned char resphash[MD5_DIGEST_SIZE];
00149 char ha2hex[HASH_MD5_HEX_LEN + 1];
00150
00151 MD5Init (&md5);
00152 MD5Update (&md5, method, strlen(method));
00153 MD5Update (&md5, ":", 1);
00154 MD5Update (&md5, uri, strlen(uri));
00155 #if 0
00156 if (strcasecmp(qop, "auth-int") == 0)
00157 {
00158
00159
00160 MD5Update (&md5, ":", 1);
00161 if (hentity != NULL)
00162 MD5Update (&md5, hentity, strlen(hentity));
00163 }
00164 #endif
00165 MD5Final (ha2, &md5);
00166 cvthex(ha2, MD5_DIGEST_SIZE, ha2hex);
00167 MD5Init (&md5);
00168
00169 MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
00170 MD5Update (&md5, ":", 1);
00171 MD5Update (&md5, nonce, strlen(nonce));
00172 MD5Update (&md5, ":", 1);
00173 if ('\0' != *qop)
00174 {
00175 MD5Update (&md5, noncecount, strlen(noncecount));
00176 MD5Update (&md5, ":", 1);
00177 MD5Update (&md5, cnonce, strlen(cnonce));
00178 MD5Update (&md5, ":", 1);
00179 MD5Update (&md5, qop, strlen(qop));
00180 MD5Update (&md5, ":", 1);
00181 }
00182 MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
00183 MD5Final (resphash, &md5);
00184 cvthex(resphash, sizeof (resphash), response);
00185 }
00186
00187
00202 static int
00203 lookup_sub_value(char *dest,
00204 size_t size,
00205 const char *data,
00206 const char *key)
00207 {
00208 size_t keylen = strlen(key);
00209 size_t len;
00210 const char *ptr = data;
00211 const char *eq;
00212 const char *q1;
00213 const char *q2;
00214 const char *qn;
00215
00216 if (0 == size)
00217 return 0;
00218 while ('\0' != *ptr)
00219 {
00220 if (NULL == (eq = strstr (ptr, "=")))
00221 return 0;
00222 q1 = eq + 1;
00223 while (' ' == *q1)
00224 q1++;
00225 if ('\"' != *q1)
00226 {
00227 q2 = strstr (q1, ",");
00228 qn = q2;
00229 }
00230 else
00231 {
00232 q1++;
00233 q2 = strstr (q1, "\"");
00234 if (NULL == q2)
00235 return 0;
00236 qn = q2 + 1;
00237 }
00238 if ( (0 == strncasecmp (ptr,
00239 key,
00240 keylen)) &&
00241 (eq == &ptr[keylen]) )
00242 {
00243 if (q2 == NULL)
00244 {
00245 len = strlen (q1) + 1;
00246 if (size > len)
00247 size = len;
00248 size--;
00249 strncpy (dest,
00250 q1,
00251 size);
00252 dest[size] = '\0';
00253 return size;
00254 }
00255 else
00256 {
00257 if (size > (q2 - q1) + 1)
00258 size = (q2 - q1) + 1;
00259 size--;
00260 memcpy (dest,
00261 q1,
00262 size);
00263 dest[size] = '\0';
00264 return size;
00265 }
00266 }
00267 if (NULL == qn)
00268 return 0;
00269 ptr = strstr (qn, ",");
00270 if (NULL == ptr)
00271 return 0;
00272 ptr++;
00273 while (' ' == *ptr)
00274 ptr++;
00275 }
00276 return 0;
00277 }
00278
00279
00289 static int
00290 check_nonce_nc (struct MHD_Connection *connection,
00291 const char *nonce,
00292 unsigned int nc)
00293 {
00294 uint32_t off;
00295 uint32_t mod;
00296 const char *np;
00297
00298 mod = connection->daemon->nonce_nc_size;
00299 if (0 == mod)
00300 return MHD_NO;
00301
00302 off = 0;
00303 np = nonce;
00304 while (*np != '\0')
00305 {
00306 off = (off << 8) | (*np & (off >> 24));
00307 np++;
00308 }
00309 off = off % mod;
00310
00311
00312
00313
00314
00315
00316 pthread_mutex_lock(&connection->daemon->nnc_lock);
00317 if (nc == 0)
00318 {
00319 strcpy(connection->daemon->nnc[off].nonce,
00320 nonce);
00321 connection->daemon->nnc[off].nc = 0;
00322 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00323 return MHD_YES;
00324 }
00325 if ( (nc <= connection->daemon->nnc[off].nc) ||
00326 (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
00327 {
00328 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00329 #if HAVE_MESSAGES
00330 MHD_DLOG (connection->daemon,
00331 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n");
00332 #endif
00333 return MHD_NO;
00334 }
00335 connection->daemon->nnc[off].nc = nc;
00336 pthread_mutex_unlock(&connection->daemon->nnc_lock);
00337 return MHD_YES;
00338 }
00339
00340
00348 char *
00349 MHD_digest_auth_get_username(struct MHD_Connection *connection)
00350 {
00351 size_t len;
00352 char user[MAX_USERNAME_LENGTH];
00353 const char *header;
00354
00355 header = MHD_lookup_connection_value(connection,
00356 MHD_HEADER_KIND,
00357 MHD_HTTP_HEADER_AUTHORIZATION);
00358 if (header == NULL)
00359 return NULL;
00360 if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00361 return NULL;
00362 header += strlen (_BASE);
00363 len = lookup_sub_value(user,
00364 sizeof (user),
00365 header,
00366 "username");
00367 if (!len)
00368 return NULL;
00369 return strdup(user);
00370 }
00371
00372
00386 static void
00387 calculate_nonce (uint32_t nonce_time,
00388 const char *method,
00389 const char *rnd,
00390 unsigned int rnd_size,
00391 const char *uri,
00392 const char *realm,
00393 char *nonce)
00394 {
00395 struct MD5Context md5;
00396 unsigned char timestamp[4];
00397 unsigned char tmpnonce[MD5_DIGEST_SIZE];
00398 char timestamphex[sizeof(timestamp)*2+1];
00399
00400 MD5Init (&md5);
00401 timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
00402 timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
00403 timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
00404 timestamp[3] = (nonce_time & 0x000000ff);
00405 MD5Update(&md5, timestamp, 4);
00406 MD5Update(&md5, ":", 1);
00407 MD5Update(&md5, method, strlen(method));
00408 MD5Update(&md5, ":", 1);
00409 if (rnd_size > 0)
00410 MD5Update(&md5, rnd, rnd_size);
00411 MD5Update(&md5, ":", 1);
00412 MD5Update(&md5, uri, strlen(uri));
00413 MD5Update(&md5, ":", 1);
00414 MD5Update(&md5, realm, strlen(realm));
00415 MD5Final (tmpnonce, &md5);
00416 cvthex(tmpnonce, sizeof (tmpnonce), nonce);
00417 cvthex(timestamp, 4, timestamphex);
00418 strncat(nonce, timestamphex, 8);
00419 }
00420
00421
00434 int
00435 MHD_digest_auth_check(struct MHD_Connection *connection,
00436 const char *realm,
00437 const char *username,
00438 const char *password,
00439 unsigned int nonce_timeout)
00440 {
00441 size_t len;
00442 const char *header;
00443 char nonce[MAX_NONCE_LENGTH];
00444 char cnonce[MAX_NONCE_LENGTH];
00445 char qop[15];
00446 char nc[20];
00447 char response[MAX_AUTH_RESPONSE_LENGTH];
00448 const char *hentity = NULL;
00449 char ha1[HASH_MD5_HEX_LEN + 1];
00450 char respexp[HASH_MD5_HEX_LEN + 1];
00451 char noncehashexp[HASH_MD5_HEX_LEN + 9];
00452 uint32_t nonce_time;
00453 uint32_t t;
00454 size_t left;
00455 unsigned int nci;
00456
00457 header = MHD_lookup_connection_value(connection,
00458 MHD_HEADER_KIND,
00459 MHD_HTTP_HEADER_AUTHORIZATION);
00460 if (header == NULL)
00461 return MHD_NO;
00462 if (strncmp(header, _BASE, strlen(_BASE)) != 0)
00463 return MHD_NO;
00464 header += strlen (_BASE);
00465 left = strlen (header);
00466
00467 {
00468 char un[MAX_USERNAME_LENGTH];
00469 len = lookup_sub_value(un,
00470 sizeof (un),
00471 header, "username");
00472 if ( (!len) ||
00473 (strcmp(username, un) != 0) )
00474 return MHD_NO;
00475 left -= strlen ("username") + len;
00476 }
00477
00478 {
00479 char r[MAX_REALM_LENGTH];
00480 len = lookup_sub_value(r,
00481 sizeof (r),
00482 header, "realm");
00483 if ( (!len) ||
00484 (strcmp(realm, r) != 0) )
00485 return MHD_NO;
00486 left -= strlen ("realm") + len;
00487 }
00488
00489 if (0 == (len = lookup_sub_value(nonce,
00490 sizeof (nonce),
00491 header, "nonce")))
00492 return MHD_NO;
00493 left -= strlen ("nonce") + len;
00494
00495 {
00496 char uri[left];
00497
00498 if (0 == lookup_sub_value(uri,
00499 sizeof (uri),
00500 header, "uri"))
00501 return MHD_NO;
00502
00503
00504 nonce_time = strtoul(nonce + len - 8, (char **)NULL, 16);
00505 t = (uint32_t) time(NULL);
00506
00507
00508
00509
00510
00511
00512 if (t > nonce_time + nonce_timeout)
00513 return MHD_INVALID_NONCE;
00514 calculate_nonce (nonce_time,
00515 connection->method,
00516 connection->daemon->digest_auth_random,
00517 connection->daemon->digest_auth_rand_size,
00518 uri,
00519 realm,
00520 noncehashexp);
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531 if (0 != strcmp(nonce, noncehashexp))
00532 return MHD_INVALID_NONCE;
00533 if ( (0 == lookup_sub_value(cnonce,
00534 sizeof (cnonce),
00535 header, "cnonce")) ||
00536 (0 == lookup_sub_value(qop, sizeof (qop), header, "qop")) ||
00537 ( (0 != strcmp (qop, "auth")) &&
00538 (0 != strcmp (qop, "")) ) ||
00539 (0 == lookup_sub_value(nc, sizeof (nc), header, "nc")) ||
00540 (1 != sscanf (nc, "%u", &nci)) ||
00541 (0 == lookup_sub_value(response, sizeof (response), header, "response")) )
00542 return MHD_NO;
00543
00544
00545
00546
00547
00548
00549
00550 if (MHD_YES != check_nonce_nc (connection, nonce, nci))
00551 return MHD_NO;
00552
00553 digest_calc_ha1("md5",
00554 username,
00555 realm,
00556 password,
00557 nonce,
00558 cnonce,
00559 ha1);
00560 digest_calc_response(ha1,
00561 nonce,
00562 nc,
00563 cnonce,
00564 qop,
00565 connection->method,
00566 uri,
00567 hentity,
00568 respexp);
00569 return strcmp(response, respexp) == 0 ? MHD_YES : MHD_NO;
00570 }
00571 }
00572
00573
00584 int
00585 MHD_queue_auth_fail_response(struct MHD_Connection *connection,
00586 const char *realm,
00587 const char *opaque,
00588 struct MHD_Response *response,
00589 int signal_stale)
00590 {
00591 int ret;
00592 size_t hlen;
00593 char nonce[HASH_MD5_HEX_LEN + 9];
00594
00595
00596 calculate_nonce ((uint32_t) time(NULL),
00597 connection->method,
00598 connection->daemon->digest_auth_random,
00599 connection->daemon->digest_auth_rand_size,
00600 connection->url,
00601 realm,
00602 nonce);
00603 if (MHD_YES != check_nonce_nc (connection, nonce, 0))
00604 {
00605 #if HAVE_MESSAGES
00606 MHD_DLOG (connection->daemon,
00607 "Could not register nonce (is the nonce array size zero?).\n");
00608 #endif
00609 return MHD_NO;
00610 }
00611
00612 hlen = snprintf(NULL,
00613 0,
00614 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00615 realm,
00616 nonce,
00617 opaque,
00618 signal_stale ? ",stale=\"true\"" : "");
00619 {
00620 char header[hlen + 1];
00621 snprintf(header,
00622 sizeof(header),
00623 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
00624 realm,
00625 nonce,
00626 opaque,
00627 signal_stale ? ",stale=\"true\"" : "");
00628 ret = MHD_add_response_header(response,
00629 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
00630 header);
00631 }
00632 if (MHD_YES == ret)
00633 ret = MHD_queue_response(connection,
00634 MHD_HTTP_UNAUTHORIZED,
00635 response);
00636 return ret;
00637 }
00638
00639
00640