diff options
Diffstat (limited to 'src/microhttpd/digestauth.c')
-rw-r--r-- | src/microhttpd/digestauth.c | 536 |
1 files changed, 249 insertions, 287 deletions
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c index 114f72b8..f4eecd03 100644 --- a/src/microhttpd/digestauth.c +++ b/src/microhttpd/digestauth.c | |||
@@ -134,7 +134,7 @@ | |||
134 | /** | 134 | /** |
135 | * Maximum length of the response in digest authentication. | 135 | * Maximum length of the response in digest authentication. |
136 | */ | 136 | */ |
137 | #define MAX_AUTH_RESPONSE_LENGTH 256 | 137 | #define MAX_AUTH_RESPONSE_LENGTH (MAX_DIGEST * 2) |
138 | 138 | ||
139 | /** | 139 | /** |
140 | * The token for MD5 algorithm. | 140 | * The token for MD5 algorithm. |
@@ -1305,19 +1305,56 @@ check_argument_match (struct MHD_Connection *connection, | |||
1305 | */ | 1305 | */ |
1306 | #define _MHD_STATIC_UNQ_BUFFER_SIZE 128 | 1306 | #define _MHD_STATIC_UNQ_BUFFER_SIZE 128 |
1307 | 1307 | ||
1308 | |||
1308 | /** | 1309 | /** |
1309 | * The result of parameter unquoting | 1310 | * Get the pointer to buffer with required size |
1311 | * @param tmp1 the first buffer with fixed size | ||
1312 | * @param ptmp2 the pointer to pointer to malloc'ed buffer | ||
1313 | * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 | ||
1314 | * @param required_size the required size in buffer | ||
1315 | * @return the pointer to the buffer or NULL if failed to allocate buffer with | ||
1316 | * requested size | ||
1310 | */ | 1317 | */ |
1318 | static char * | ||
1319 | get_buffer_for_size (char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], | ||
1320 | char **ptmp2, | ||
1321 | size_t *ptmp2_size, | ||
1322 | size_t required_size) | ||
1323 | { | ||
1324 | mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); | ||
1325 | mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); | ||
1326 | mhd_assert ((0 == *ptmp2_size) || \ | ||
1327 | (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); | ||
1328 | |||
1329 | if (required_size <= _MHD_STATIC_UNQ_BUFFER_SIZE) | ||
1330 | return tmp1; | ||
1331 | |||
1332 | if (required_size <= *ptmp2_size) | ||
1333 | return *ptmp2; | ||
1334 | |||
1335 | if (required_size > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) | ||
1336 | return NULL; | ||
1337 | if (NULL != *ptmp2) | ||
1338 | free (*ptmp2); | ||
1339 | *ptmp2 = (char *) malloc (required_size); | ||
1340 | if (NULL == *ptmp2) | ||
1341 | *ptmp2_size = 0; | ||
1342 | else | ||
1343 | *ptmp2_size = required_size; | ||
1344 | return *ptmp2; | ||
1345 | } | ||
1346 | |||
1347 | |||
1348 | /** | ||
1349 | * The result of parameter unquoting | ||
1350 | */ | ||
1311 | enum _MHD_GetUnqResult | 1351 | enum _MHD_GetUnqResult |
1312 | { | 1352 | { |
1313 | _MHD_UNQ_NON_EMPTY = 0, /**< The sting is not empty */ | 1353 | _MHD_UNQ_OK = 0, /**< Got unquoted string */ |
1314 | _MHD_UNQ_NO_STRING = MHD_DAUTH_WRONG_HEADER, /**< No string (no such parameter) */ | 1354 | _MHD_UNQ_TOO_LARGE = -7, /**< The string is too large to unqoute */ |
1315 | _MHD_UNQ_EMPTY = 1, /**< The string is empty */ | 1355 | _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ |
1316 | _MHD_UNQ_TOO_LARGE = 2, /**< The string is too large to unqoute */ | ||
1317 | _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ | ||
1318 | }; | 1356 | }; |
1319 | 1357 | ||
1320 | |||
1321 | /** | 1358 | /** |
1322 | * Get Digest authorisation parameter as unquoted string. | 1359 | * Get Digest authorisation parameter as unquoted string. |
1323 | * @param param the parameter to process | 1360 | * @param param the parameter to process |
@@ -1328,60 +1365,79 @@ enum _MHD_GetUnqResult | |||
1328 | * @return enum code indicating result of the process | 1365 | * @return enum code indicating result of the process |
1329 | */ | 1366 | */ |
1330 | static enum _MHD_GetUnqResult | 1367 | static enum _MHD_GetUnqResult |
1331 | get_unqouted_param (const struct MHD_RqDAuthParam *param, | 1368 | get_unquoted_param (const struct MHD_RqDAuthParam *param, |
1332 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], | 1369 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], |
1333 | char **ptmp2, | 1370 | char **ptmp2, |
1334 | size_t *ptmp2_size, | 1371 | size_t *ptmp2_size, |
1335 | struct _MHD_cstr_w_len *unquoted) | 1372 | struct _MHD_str_w_len *unquoted) |
1336 | { | 1373 | { |
1337 | char *str; | 1374 | char *str; |
1338 | size_t len; | 1375 | size_t len; |
1339 | mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); | 1376 | mhd_assert (NULL != param->value.str); |
1340 | mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); | 1377 | mhd_assert (0 != param->value.len); |
1341 | mhd_assert ((0 == *ptmp2_size) || \ | ||
1342 | (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); | ||
1343 | 1378 | ||
1344 | if (NULL == param->value.str) | ||
1345 | { | ||
1346 | const struct _MHD_cstr_w_len res = {NULL, 0}; | ||
1347 | mhd_assert (! param->quoted); | ||
1348 | mhd_assert (0 == param->value.len); | ||
1349 | memcpy (unquoted, &res, sizeof(res)); | ||
1350 | return _MHD_UNQ_NO_STRING; | ||
1351 | } | ||
1352 | if (! param->quoted) | 1379 | if (! param->quoted) |
1353 | { | 1380 | { |
1354 | memcpy (unquoted, ¶m->value, sizeof(param->value)); | 1381 | unquoted->str = param->value.str; |
1355 | return (0 == param->value.len) ? _MHD_UNQ_EMPTY : _MHD_UNQ_NON_EMPTY; | 1382 | unquoted->len = param->value.len; |
1383 | return _MHD_UNQ_OK; | ||
1356 | } | 1384 | } |
1357 | /* The value is present and is quoted, needs to be copied and unquoted */ | 1385 | /* The value is present and is quoted, needs to be copied and unquoted */ |
1358 | mhd_assert (0 != param->value.len); | 1386 | str = get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len); |
1359 | if (param->value.len <= _MHD_STATIC_UNQ_BUFFER_SIZE) | 1387 | if (NULL == str) |
1360 | str = tmp1; | 1388 | return (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? |
1361 | else if (param->value.len <= *ptmp2_size) | 1389 | _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; |
1362 | str = *ptmp2; | ||
1363 | else | ||
1364 | { | ||
1365 | if (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) | ||
1366 | return _MHD_UNQ_TOO_LARGE; | ||
1367 | if (NULL != *ptmp2) | ||
1368 | free (*ptmp2); | ||
1369 | *ptmp2 = (char *) malloc (param->value.len); | ||
1370 | if (NULL == *ptmp2) | ||
1371 | return _MHD_UNQ_OUT_OF_MEM; | ||
1372 | *ptmp2_size = param->value.len; | ||
1373 | str = *ptmp2; | ||
1374 | } | ||
1375 | 1390 | ||
1376 | len = MHD_str_unquote (param->value.str, param->value.len, str); | 1391 | len = MHD_str_unquote (param->value.str, param->value.len, str); |
1377 | if (1) | 1392 | unquoted->str = str; |
1393 | unquoted->len = len; | ||
1394 | mhd_assert (0 != unquoted->len); | ||
1395 | mhd_assert (unquoted->len < param->value.len); | ||
1396 | return _MHD_UNQ_OK; | ||
1397 | } | ||
1398 | |||
1399 | |||
1400 | /** | ||
1401 | * Get copy of Digest authorisation parameter as unquoted string. | ||
1402 | * @param param the parameter to process | ||
1403 | * @param tmp1 the small buffer in stack | ||
1404 | * @param ptmp2 the pointer to pointer to malloc'ed buffer | ||
1405 | * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 | ||
1406 | * @param[out] unquoted the pointer to store the result, NOT zero terminated, | ||
1407 | * but with enough space to zero-terminate | ||
1408 | * @return enum code indicating result of the process | ||
1409 | */ | ||
1410 | static enum _MHD_GetUnqResult | ||
1411 | get_unquoted_param_copy (const struct MHD_RqDAuthParam *param, | ||
1412 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], | ||
1413 | char **ptmp2, | ||
1414 | size_t *ptmp2_size, | ||
1415 | struct _MHD_mstr_w_len *unquoted) | ||
1416 | { | ||
1417 | mhd_assert (NULL != param->value.str); | ||
1418 | mhd_assert (0 != param->value.len); | ||
1419 | |||
1420 | /* The value is present and is quoted, needs to be copied and unquoted */ | ||
1421 | /* Allocate buffer with one more additional byte for zero-termination */ | ||
1422 | unquoted->str = | ||
1423 | get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len + 1); | ||
1424 | |||
1425 | if (NULL == unquoted->str) | ||
1426 | return (param->value.len + 1 > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? | ||
1427 | _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; | ||
1428 | |||
1429 | if (! param->quoted) | ||
1378 | { | 1430 | { |
1379 | const struct _MHD_cstr_w_len res = {str, len}; | 1431 | memcpy (unquoted->str, param->value.str, param->value.len); |
1380 | memcpy (unquoted, &res, sizeof(res)); | 1432 | unquoted->len = param->value.len; |
1433 | return _MHD_UNQ_OK; | ||
1381 | } | 1434 | } |
1435 | |||
1436 | unquoted->len = | ||
1437 | MHD_str_unquote (param->value.str, param->value.len, unquoted->str); | ||
1382 | mhd_assert (0 != unquoted->len); | 1438 | mhd_assert (0 != unquoted->len); |
1383 | mhd_assert (unquoted->len < param->value.len); | 1439 | mhd_assert (unquoted->len < param->value.len); |
1384 | return _MHD_UNQ_NON_EMPTY; | 1440 | return _MHD_UNQ_OK; |
1385 | } | 1441 | } |
1386 | 1442 | ||
1387 | 1443 | ||
@@ -1424,18 +1480,21 @@ is_param_equal (const struct MHD_RqDAuthParam *param, | |||
1424 | * (must contain "da->digest_size" bytes or be NULL) | 1480 | * (must contain "da->digest_size" bytes or be NULL) |
1425 | * @param nonce_timeout The amount of time for a nonce to be | 1481 | * @param nonce_timeout The amount of time for a nonce to be |
1426 | * invalid in seconds | 1482 | * invalid in seconds |
1483 | * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, | ||
1484 | * to be free if not NULL upon return | ||
1427 | * @return #MHD_DAUTH_OK if authenticated, | 1485 | * @return #MHD_DAUTH_OK if authenticated, |
1428 | * error code otherwise. | 1486 | * error code otherwise. |
1429 | * @ingroup authentication | 1487 | * @ingroup authentication |
1430 | */ | 1488 | */ |
1431 | static enum MHD_DigestAuthResult | 1489 | static enum MHD_DigestAuthResult |
1432 | digest_auth_check_all (struct MHD_Connection *connection, | 1490 | digest_auth_check_all_inner (struct MHD_Connection *connection, |
1433 | struct DigestAlgorithm *da, | 1491 | struct DigestAlgorithm *da, |
1434 | const char *realm, | 1492 | const char *realm, |
1435 | const char *username, | 1493 | const char *username, |
1436 | const char *password, | 1494 | const char *password, |
1437 | const uint8_t *digest, | 1495 | const uint8_t *digest, |
1438 | unsigned int nonce_timeout) | 1496 | unsigned int nonce_timeout, |
1497 | char **pbuf) | ||
1439 | { | 1498 | { |
1440 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); | 1499 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); |
1441 | char cnonce[MAX_CLIENT_NONCE_LENGTH]; | 1500 | char cnonce[MAX_CLIENT_NONCE_LENGTH]; |
@@ -1452,91 +1511,63 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1452 | char *qmark; | 1511 | char *qmark; |
1453 | const struct MHD_RqDAuth *params; | 1512 | const struct MHD_RqDAuth *params; |
1454 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; /**< Temporal buffer in stack for unqouting */ | 1513 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; /**< Temporal buffer in stack for unqouting */ |
1455 | char *tmp2; /**< Temporal malloc'ed buffer for unqouting */ | 1514 | char **const ptmp2 = pbuf; /**< Temporal malloc'ed buffer for unqouting */ |
1456 | size_t tmp2_size; /**< The size of @a tmp2 buffer */ | 1515 | size_t tmp2_size; /**< The size of @a tmp2 buffer */ |
1457 | struct _MHD_cstr_w_len unquoted; | 1516 | struct _MHD_str_w_len unquoted; |
1517 | struct _MHD_mstr_w_len unq_copy; | ||
1458 | enum _MHD_GetUnqResult unq_res; | 1518 | enum _MHD_GetUnqResult unq_res; |
1459 | enum MHD_DigestAuthResult ret; | 1519 | enum MHD_DigestAuthResult ret; |
1460 | #ifdef HAVE_MESSAGES | ||
1461 | bool err_logged; | ||
1462 | #endif /* HAVE_MESSAGES */ | ||
1463 | size_t username_len; | 1520 | size_t username_len; |
1464 | size_t realm_len; | 1521 | size_t realm_len; |
1465 | 1522 | ||
1466 | tmp2 = NULL; | ||
1467 | tmp2_size = 0; | 1523 | tmp2_size = 0; |
1468 | #ifdef HAVE_MESSAGES | ||
1469 | err_logged = false; | ||
1470 | #endif /* HAVE_MESSAGES */ | ||
1471 | 1524 | ||
1472 | do /* Only to avoid "goto" */ | 1525 | do /* Only to avoid "goto" */ |
1473 | { | 1526 | { |
1474 | 1527 | ||
1475 | params = get_rq_dauth_params (connection); | 1528 | params = get_rq_dauth_params (connection); |
1476 | if (NULL == params) | 1529 | if (NULL == params) |
1477 | { | 1530 | return MHD_DAUTH_WRONG_HEADER; |
1478 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1479 | break; | ||
1480 | } | ||
1481 | 1531 | ||
1482 | /* Check 'username' */ | 1532 | /* Check 'username' */ |
1483 | if (NULL == params->username.value.str) | 1533 | if (NULL == params->username.value.str) |
1484 | { | 1534 | return MHD_DAUTH_WRONG_HEADER; |
1485 | ret = MHD_DAUTH_WRONG_HEADER; | 1535 | |
1486 | break; | ||
1487 | } | ||
1488 | username_len = strlen (username); | 1536 | username_len = strlen (username); |
1489 | if (! is_param_equal (¶ms->username, username, username_len)) | 1537 | if (! is_param_equal (¶ms->username, username, username_len)) |
1490 | { | 1538 | return MHD_DAUTH_WRONG_USERNAME; |
1491 | ret = MHD_DAUTH_WRONG_USERNAME; | ||
1492 | break; | ||
1493 | } | ||
1494 | /* 'username' valid */ | 1539 | /* 'username' valid */ |
1495 | 1540 | ||
1496 | /* Check 'realm' */ | 1541 | /* Check 'realm' */ |
1497 | if (NULL == params->realm.value.str) | 1542 | if (NULL == params->realm.value.str) |
1498 | { | 1543 | return MHD_DAUTH_WRONG_HEADER; |
1499 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1500 | break; | ||
1501 | } | ||
1502 | realm_len = strlen (realm); | 1544 | realm_len = strlen (realm); |
1503 | if (! is_param_equal (¶ms->realm, realm, realm_len)) | 1545 | if (! is_param_equal (¶ms->realm, realm, realm_len)) |
1504 | { | 1546 | return MHD_DAUTH_WRONG_REALM; |
1505 | ret = MHD_DAUTH_WRONG_REALM; | ||
1506 | break; | ||
1507 | } | ||
1508 | /* 'realm' valid */ | 1547 | /* 'realm' valid */ |
1509 | 1548 | ||
1510 | /* Check 'nonce' */ | 1549 | /* Check 'nonce' */ |
1511 | unq_res = get_unqouted_param (¶ms->nonce, tmp1, &tmp2, &tmp2_size, | 1550 | if (NULL == params->nonce.value.str) |
1551 | return MHD_DAUTH_WRONG_HEADER; | ||
1552 | else if (0 == params->nonce.value.len) | ||
1553 | return MHD_DAUTH_NONCE_WRONG; | ||
1554 | else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len) | ||
1555 | return MHD_DAUTH_NONCE_WRONG; | ||
1556 | |||
1557 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, | ||
1512 | &unquoted); | 1558 | &unquoted); |
1513 | if (_MHD_UNQ_NON_EMPTY != unq_res) | 1559 | if (_MHD_UNQ_OK != unq_res) |
1514 | { | 1560 | return MHD_DAUTH_ERROR; |
1515 | if (_MHD_UNQ_NO_STRING == unq_res) | 1561 | if (NONCE_STD_LEN (digest_size) != unquoted.len) |
1516 | ret = MHD_DAUTH_WRONG_HEADER; | 1562 | return MHD_DAUTH_NONCE_WRONG; |
1517 | else if (_MHD_UNQ_EMPTY == unq_res) | 1563 | |
1518 | ret = MHD_DAUTH_NONCE_WRONG; | ||
1519 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1520 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1521 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1522 | ret = MHD_DAUTH_ERROR; | ||
1523 | else | ||
1524 | { | ||
1525 | mhd_assert (0); /* Must not happen */ | ||
1526 | ret = MHD_DAUTH_ERROR; | ||
1527 | } | ||
1528 | break; | ||
1529 | } | ||
1530 | /* TODO: check correct 'nonce' length */ | ||
1531 | if (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)) | 1564 | if (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)) |
1532 | { | 1565 | { |
1533 | #ifdef HAVE_MESSAGES | 1566 | #ifdef HAVE_MESSAGES |
1534 | MHD_DLOG (daemon, | 1567 | MHD_DLOG (daemon, |
1535 | _ ("Authentication failed, invalid timestamp format.\n")); | 1568 | _ ("Authentication failed, invalid timestamp format.\n")); |
1536 | err_logged = true; | ||
1537 | #endif | 1569 | #endif |
1538 | ret = MHD_DAUTH_NONCE_WRONG; | 1570 | return MHD_DAUTH_NONCE_WRONG; |
1539 | break; | ||
1540 | } | 1571 | } |
1541 | t = MHD_monotonic_msec_counter (); | 1572 | t = MHD_monotonic_msec_counter (); |
1542 | /* | 1573 | /* |
@@ -1545,11 +1576,7 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1545 | * invalid. | 1576 | * invalid. |
1546 | */ | 1577 | */ |
1547 | if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) | 1578 | if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) |
1548 | { | 1579 | return MHD_DAUTH_NONCE_STALE; /* too old */ |
1549 | /* too old */ | ||
1550 | ret = MHD_DAUTH_NONCE_STALE; | ||
1551 | break; | ||
1552 | } | ||
1553 | 1580 | ||
1554 | calculate_nonce (nonce_time, | 1581 | calculate_nonce (nonce_time, |
1555 | connection->method, | 1582 | connection->method, |
@@ -1571,107 +1598,66 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1571 | */ | 1598 | */ |
1572 | if ((0 != strncmp (noncehashexp, unquoted.str, unquoted.len)) || | 1599 | if ((0 != strncmp (noncehashexp, unquoted.str, unquoted.len)) || |
1573 | (0 != noncehashexp[unquoted.len])) | 1600 | (0 != noncehashexp[unquoted.len])) |
1574 | { | 1601 | return MHD_DAUTH_NONCE_WRONG; |
1575 | ret = MHD_DAUTH_NONCE_WRONG; | ||
1576 | break; | ||
1577 | } | ||
1578 | /* 'nonce' valid */ | 1602 | /* 'nonce' valid */ |
1579 | 1603 | ||
1580 | /* Get 'cnonce' */ | 1604 | /* Get 'cnonce' */ |
1581 | unq_res = get_unqouted_param (¶ms->cnonce, tmp1, &tmp2, &tmp2_size, | 1605 | if (NULL == params->cnonce.value.str) |
1606 | return MHD_DAUTH_WRONG_HEADER; | ||
1607 | else if (0 == params->cnonce.value.len) | ||
1608 | return MHD_DAUTH_WRONG_HEADER; | ||
1609 | unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, | ||
1582 | &unquoted); | 1610 | &unquoted); |
1583 | if (_MHD_UNQ_NON_EMPTY != unq_res) | 1611 | if (_MHD_UNQ_OK != unq_res) |
1584 | { | 1612 | return (_MHD_UNQ_TOO_LARGE == unq_res) ? |
1585 | if (_MHD_UNQ_NO_STRING == unq_res) | 1613 | MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; |
1586 | ret = MHD_DAUTH_WRONG_HEADER; | 1614 | |
1587 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1588 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1589 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1590 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1591 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1592 | ret = MHD_DAUTH_ERROR; | ||
1593 | else | ||
1594 | { | ||
1595 | mhd_assert (0); /* Must not happen */ | ||
1596 | ret = MHD_DAUTH_ERROR; | ||
1597 | } | ||
1598 | break; | ||
1599 | } | ||
1600 | if (sizeof(cnonce) <= unquoted.len) | 1615 | if (sizeof(cnonce) <= unquoted.len) |
1601 | { | 1616 | return MHD_DAUTH_ERROR; /* TODO: handle large client nonces */ |
1602 | /* TODO: handle large client nonces */ | 1617 | |
1603 | ret = MHD_DAUTH_ERROR; | ||
1604 | break; | ||
1605 | } | ||
1606 | /* TODO: avoid memcpy() */ | 1618 | /* TODO: avoid memcpy() */ |
1607 | memcpy (cnonce, unquoted.str, unquoted.len); | 1619 | memcpy (cnonce, unquoted.str, unquoted.len); |
1608 | cnonce[unquoted.len] = 0; | 1620 | cnonce[unquoted.len] = 0; |
1609 | /* Got 'cnonce' */ | 1621 | /* Got 'cnonce' */ |
1610 | 1622 | ||
1611 | /* Get 'qop' */ | 1623 | /* Get 'qop' */ |
1612 | unq_res = get_unqouted_param (¶ms->qop, tmp1, &tmp2, &tmp2_size, | 1624 | if (NULL == params->qop.value.str) |
1625 | return MHD_DAUTH_WRONG_HEADER; | ||
1626 | else if (0 == params->qop.value.len) | ||
1627 | return MHD_DAUTH_WRONG_QOP; | ||
1628 | else if (MHD_STATICSTR_LEN_ ("auth-int") * 2 < params->qop.value.len) | ||
1629 | return MHD_DAUTH_WRONG_QOP; | ||
1630 | unq_res = get_unquoted_param (¶ms->qop, tmp1, ptmp2, &tmp2_size, | ||
1613 | &unquoted); | 1631 | &unquoted); |
1614 | if (_MHD_UNQ_NON_EMPTY != unq_res) | 1632 | if (_MHD_UNQ_OK != unq_res) |
1615 | { | 1633 | return MHD_DAUTH_ERROR; |
1616 | if (_MHD_UNQ_NO_STRING == unq_res) | 1634 | |
1617 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1618 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1619 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1620 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1621 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1622 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1623 | ret = MHD_DAUTH_ERROR; | ||
1624 | else | ||
1625 | { | ||
1626 | mhd_assert (0); /* Must not happen */ | ||
1627 | ret = MHD_DAUTH_ERROR; | ||
1628 | } | ||
1629 | break; | ||
1630 | } | ||
1631 | if (sizeof(qop) <= unquoted.len) | 1635 | if (sizeof(qop) <= unquoted.len) |
1632 | { | 1636 | return MHD_DAUTH_ERROR; /* TODO: handle large client qop */ |
1633 | /* TODO: handle large client qop */ | ||
1634 | ret = MHD_DAUTH_ERROR; | ||
1635 | break; | ||
1636 | } | ||
1637 | /* TODO: avoid memcpy() */ | 1637 | /* TODO: avoid memcpy() */ |
1638 | memcpy (qop, unquoted.str, unquoted.len); | 1638 | memcpy (qop, unquoted.str, unquoted.len); |
1639 | qop[unquoted.len] = 0; | 1639 | qop[unquoted.len] = 0; |
1640 | /* TODO: use caseless match, use dedicated return code */ | 1640 | /* TODO: Really support empty value, support "auth-int" */ |
1641 | if ( (0 != strcmp (qop, "auth")) && | 1641 | if ( ((MHD_STATICSTR_LEN_ ("auth") != unquoted.len) || |
1642 | (! MHD_str_equal_caseless_bin_n_ (qop, "auth", unquoted.len))) && | ||
1642 | (0 != strcmp (qop,"")) ) | 1643 | (0 != strcmp (qop,"")) ) |
1643 | { | 1644 | return MHD_DAUTH_WRONG_QOP; |
1644 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1645 | break; | ||
1646 | } | ||
1647 | /* Got 'qop' */ | 1645 | /* Got 'qop' */ |
1648 | 1646 | ||
1649 | /* Get 'nc' */ | 1647 | /* Get 'nc' */ |
1650 | unq_res = get_unqouted_param (¶ms->nc, tmp1, &tmp2, &tmp2_size, | 1648 | if (NULL == params->nc.value.str) |
1649 | return MHD_DAUTH_WRONG_HEADER; | ||
1650 | else if (0 == params->nc.value.len) | ||
1651 | return MHD_DAUTH_WRONG_HEADER; | ||
1652 | else if (4 * 8 < params->nc.value.len) /* Four time more than needed */ | ||
1653 | return MHD_DAUTH_NONCE_WRONG; | ||
1654 | unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, | ||
1651 | &unquoted); | 1655 | &unquoted); |
1652 | if (_MHD_UNQ_NON_EMPTY != unq_res) | 1656 | if (_MHD_UNQ_OK != unq_res) |
1653 | { | 1657 | return MHD_DAUTH_ERROR; |
1654 | if (_MHD_UNQ_NO_STRING == unq_res) | 1658 | |
1655 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1656 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1657 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1658 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1659 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1660 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1661 | ret = MHD_DAUTH_ERROR; | ||
1662 | else | ||
1663 | { | ||
1664 | mhd_assert (0); /* Must not happen */ | ||
1665 | ret = MHD_DAUTH_ERROR; | ||
1666 | } | ||
1667 | break; | ||
1668 | } | ||
1669 | if (sizeof(nc) <= unquoted.len) | 1659 | if (sizeof(nc) <= unquoted.len) |
1670 | { | 1660 | return MHD_DAUTH_ERROR; |
1671 | /* TODO: handle large client nc */ | ||
1672 | ret = MHD_DAUTH_ERROR; | ||
1673 | break; | ||
1674 | } | ||
1675 | /* TODO: avoid memcpy() */ | 1661 | /* TODO: avoid memcpy() */ |
1676 | memcpy (nc, unquoted.str, unquoted.len); | 1662 | memcpy (nc, unquoted.str, unquoted.len); |
1677 | nc[unquoted.len] = 0; | 1663 | nc[unquoted.len] = 0; |
@@ -1682,49 +1668,37 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1682 | #ifdef HAVE_MESSAGES | 1668 | #ifdef HAVE_MESSAGES |
1683 | MHD_DLOG (daemon, | 1669 | MHD_DLOG (daemon, |
1684 | _ ("Authentication failed, invalid nc format.\n")); | 1670 | _ ("Authentication failed, invalid nc format.\n")); |
1685 | err_logged = true; | ||
1686 | #endif | 1671 | #endif |
1687 | ret = MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ | 1672 | return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ |
1688 | break; | ||
1689 | } | 1673 | } |
1690 | if (0 == nci) | 1674 | if (0 == nci) |
1691 | { | 1675 | { |
1692 | #ifdef HAVE_MESSAGES | 1676 | #ifdef HAVE_MESSAGES |
1693 | MHD_DLOG (daemon, | 1677 | MHD_DLOG (daemon, |
1694 | _ ("Authentication failed, invalid 'nc' value.\n")); | 1678 | _ ("Authentication failed, invalid 'nc' value.\n")); |
1695 | err_logged = true; | ||
1696 | #endif | 1679 | #endif |
1697 | ret = MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ | 1680 | return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ |
1698 | break; | ||
1699 | } | 1681 | } |
1700 | /* Got 'nc' */ | 1682 | /* Got 'nc' */ |
1701 | 1683 | ||
1702 | /* Get 'response' */ | 1684 | /* Get 'response' */ |
1703 | unq_res = get_unqouted_param (¶ms->response, tmp1, &tmp2, &tmp2_size, | 1685 | if (NULL == params->response.value.str) |
1686 | return MHD_DAUTH_WRONG_HEADER; | ||
1687 | else if (0 == params->response.value.len) | ||
1688 | return MHD_DAUTH_RESPONSE_WRONG; | ||
1689 | else if (digest_size * 4 < params->response.value.len) | ||
1690 | return MHD_DAUTH_RESPONSE_WRONG; | ||
1691 | unq_res = get_unquoted_param (¶ms->response, tmp1, ptmp2, &tmp2_size, | ||
1704 | &unquoted); | 1692 | &unquoted); |
1705 | if (_MHD_UNQ_NON_EMPTY != unq_res) | 1693 | if (_MHD_UNQ_OK != unq_res) |
1706 | { | 1694 | return MHD_DAUTH_ERROR; |
1707 | if (_MHD_UNQ_NO_STRING == unq_res) | 1695 | if (digest_size * 2 != unquoted.len) |
1708 | ret = MHD_DAUTH_WRONG_HEADER; | 1696 | return MHD_DAUTH_RESPONSE_WRONG; |
1709 | else if (_MHD_UNQ_EMPTY == unq_res) | 1697 | |
1710 | ret = MHD_DAUTH_WRONG_HEADER; | 1698 | mhd_assert (sizeof(response) > unquoted.len); |
1711 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | 1699 | |
1712 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1713 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1714 | ret = MHD_DAUTH_ERROR; | ||
1715 | else | ||
1716 | { | ||
1717 | mhd_assert (0); /* Must not happen */ | ||
1718 | ret = MHD_DAUTH_ERROR; | ||
1719 | } | ||
1720 | break; | ||
1721 | } | ||
1722 | if (sizeof(response) <= unquoted.len) | 1700 | if (sizeof(response) <= unquoted.len) |
1723 | { | 1701 | return MHD_DAUTH_ERROR; |
1724 | /* TODO: handle large client response */ | ||
1725 | ret = MHD_DAUTH_ERROR; | ||
1726 | break; | ||
1727 | } | ||
1728 | /* TODO: avoid memcpy() */ | 1702 | /* TODO: avoid memcpy() */ |
1729 | memcpy (response, unquoted.str, unquoted.len); | 1703 | memcpy (response, unquoted.str, unquoted.len); |
1730 | response[unquoted.len] = 0; | 1704 | response[unquoted.len] = 0; |
@@ -1749,10 +1723,8 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1749 | MHD_DLOG (daemon, | 1723 | MHD_DLOG (daemon, |
1750 | _ ("Stale nonce received. If this happens a lot, you should " | 1724 | _ ("Stale nonce received. If this happens a lot, you should " |
1751 | "probably increase the size of the nonce array.\n")); | 1725 | "probably increase the size of the nonce array.\n")); |
1752 | err_logged = true; | ||
1753 | #endif | 1726 | #endif |
1754 | ret = MHD_DAUTH_NONCE_STALE; | 1727 | return MHD_DAUTH_NONCE_STALE; |
1755 | break; | ||
1756 | } | 1728 | } |
1757 | else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check) | 1729 | else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check) |
1758 | { | 1730 | { |
@@ -1760,34 +1732,23 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1760 | MHD_DLOG (daemon, | 1732 | MHD_DLOG (daemon, |
1761 | _ ("Received nonce that technically valid, but was not " | 1733 | _ ("Received nonce that technically valid, but was not " |
1762 | "generated by MHD. This may indicate an attack attempt.\n")); | 1734 | "generated by MHD. This may indicate an attack attempt.\n")); |
1763 | err_logged = true; | ||
1764 | #endif | 1735 | #endif |
1765 | ret = MHD_DAUTH_NONCE_WRONG; | 1736 | return MHD_DAUTH_NONCE_WRONG; |
1766 | break; | ||
1767 | } | 1737 | } |
1768 | mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); | 1738 | mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); |
1769 | } | 1739 | } |
1770 | 1740 | ||
1771 | /* Get 'uri' */ | 1741 | /* Get 'uri' */ |
1772 | unq_res = get_unqouted_param (¶ms->uri, tmp1, &tmp2, &tmp2_size, | 1742 | if (NULL == params->uri.value.str) |
1773 | &unquoted); | 1743 | return MHD_DAUTH_WRONG_HEADER; |
1774 | if (_MHD_UNQ_NON_EMPTY != unq_res) | 1744 | else if (0 == params->uri.value.len) |
1775 | { | 1745 | return MHD_DAUTH_WRONG_URI; |
1776 | if (_MHD_UNQ_NO_STRING == unq_res) | 1746 | unq_res = get_unquoted_param_copy (¶ms->uri, tmp1, ptmp2, &tmp2_size, |
1777 | ret = MHD_DAUTH_WRONG_HEADER; | 1747 | &unq_copy); |
1778 | else if (_MHD_UNQ_EMPTY == unq_res) | 1748 | if (_MHD_UNQ_OK != unq_res) |
1779 | ret = MHD_DAUTH_WRONG_HEADER; | 1749 | return (_MHD_UNQ_TOO_LARGE == unq_res) ? |
1780 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | 1750 | MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; |
1781 | ret = MHD_DAUTH_WRONG_HEADER; | 1751 | |
1782 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1783 | ret = MHD_DAUTH_ERROR; | ||
1784 | else | ||
1785 | { | ||
1786 | mhd_assert (0); /* Must not happen */ | ||
1787 | ret = MHD_DAUTH_ERROR; | ||
1788 | } | ||
1789 | break; | ||
1790 | } | ||
1791 | if (NULL != digest) | 1752 | if (NULL != digest) |
1792 | { | 1753 | { |
1793 | /* This will initialize da->digest_hex (ha1) */ | 1754 | /* This will initialize da->digest_hex (ha1) */ |
@@ -1821,45 +1782,16 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1821 | cnonce, | 1782 | cnonce, |
1822 | qop, | 1783 | qop, |
1823 | connection->method, | 1784 | connection->method, |
1824 | unquoted.str, | 1785 | unq_copy.str, |
1825 | unquoted.len, | 1786 | unq_copy.len, |
1826 | hentity, | 1787 | hentity, |
1827 | da); | 1788 | da); |
1828 | if (1) | 1789 | if (1) |
1829 | { | 1790 | { |
1830 | char *uri; | 1791 | char *uri; |
1831 | size_t uri_len; | 1792 | size_t uri_len; |
1832 | uri_len = unquoted.len; | 1793 | uri = unq_copy.str; |
1833 | /* TODO: simplify string copy, avoid potential double copy */ | 1794 | uri_len = unq_copy.len; |
1834 | if ( ((tmp1 != unquoted.str) && (tmp2 != unquoted.str)) || | ||
1835 | ((tmp1 == unquoted.str) && (sizeof(tmp1) >= unquoted.len)) || | ||
1836 | ((tmp2 == unquoted.str) && (tmp2_size >= unquoted.len))) | ||
1837 | { | ||
1838 | char *buf; | ||
1839 | mhd_assert ((tmp1 != unquoted.str) || \ | ||
1840 | (sizeof(tmp1) == unquoted.len)); | ||
1841 | mhd_assert ((tmp2 != unquoted.str) || \ | ||
1842 | (tmp2_size == unquoted.len)); | ||
1843 | buf = malloc (unquoted.len + 1); | ||
1844 | if (NULL == buf) | ||
1845 | { | ||
1846 | ret = MHD_DAUTH_ERROR; | ||
1847 | break; | ||
1848 | } | ||
1849 | memcpy (buf, unquoted.str, unquoted.len); | ||
1850 | if (NULL != tmp2) | ||
1851 | free (tmp2); | ||
1852 | tmp2 = buf; | ||
1853 | tmp2_size = unquoted.len + 1; | ||
1854 | uri = tmp2; | ||
1855 | } | ||
1856 | else if (tmp1 == unquoted.str) | ||
1857 | uri = tmp1; | ||
1858 | else | ||
1859 | { | ||
1860 | mhd_assert (tmp2 == unquoted.str); | ||
1861 | uri = tmp2; | ||
1862 | } | ||
1863 | 1795 | ||
1864 | uri[uri_len] = 0; | 1796 | uri[uri_len] = 0; |
1865 | qmark = memchr (uri, | 1797 | qmark = memchr (uri, |
@@ -1878,10 +1810,8 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1878 | #ifdef HAVE_MESSAGES | 1810 | #ifdef HAVE_MESSAGES |
1879 | MHD_DLOG (daemon, | 1811 | MHD_DLOG (daemon, |
1880 | _ ("Authentication failed, URI does not match.\n")); | 1812 | _ ("Authentication failed, URI does not match.\n")); |
1881 | err_logged = true; | ||
1882 | #endif | 1813 | #endif |
1883 | ret = MHD_DAUTH_WRONG_URI; | 1814 | return MHD_DAUTH_WRONG_URI; |
1884 | break; | ||
1885 | } | 1815 | } |
1886 | 1816 | ||
1887 | if (1) | 1817 | if (1) |
@@ -1899,10 +1829,8 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1899 | #ifdef HAVE_MESSAGES | 1829 | #ifdef HAVE_MESSAGES |
1900 | MHD_DLOG (daemon, | 1830 | MHD_DLOG (daemon, |
1901 | _ ("Authentication failed, arguments do not match.\n")); | 1831 | _ ("Authentication failed, arguments do not match.\n")); |
1902 | err_logged = true; | ||
1903 | #endif | 1832 | #endif |
1904 | ret = MHD_DAUTH_WRONG_URI; | 1833 | return MHD_DAUTH_WRONG_URI; |
1905 | break; | ||
1906 | } | 1834 | } |
1907 | } | 1835 | } |
1908 | 1836 | ||
@@ -1912,19 +1840,53 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1912 | : MHD_DAUTH_RESPONSE_WRONG; | 1840 | : MHD_DAUTH_RESPONSE_WRONG; |
1913 | } | 1841 | } |
1914 | } while (0); | 1842 | } while (0); |
1915 | if (NULL != tmp2) | ||
1916 | free (tmp2); | ||
1917 | |||
1918 | if ((MHD_DAUTH_OK != ret) && ! err_logged) | ||
1919 | { | ||
1920 | (void) 0; /* TODO: add logging */ | ||
1921 | } | ||
1922 | 1843 | ||
1923 | return ret; | 1844 | return ret; |
1924 | } | 1845 | } |
1925 | 1846 | ||
1926 | 1847 | ||
1927 | /** | 1848 | /** |
1849 | * Authenticates the authorization header sent by the client | ||
1850 | * | ||
1851 | * @param connection The MHD connection structure | ||
1852 | * @param[in,out] da digest algorithm to use for checking (written to as | ||
1853 | * part of the calculations, but the values left in the struct | ||
1854 | * are not actually expected to be useful for the caller) | ||
1855 | * @param realm The realm presented to the client | ||
1856 | * @param username The username needs to be authenticated | ||
1857 | * @param password The password used in the authentication | ||
1858 | * @param digest An optional binary hash | ||
1859 | * of the precalculated hash value "username:realm:password" | ||
1860 | * (must contain "da->digest_size" bytes or be NULL) | ||
1861 | * @param nonce_timeout The amount of time for a nonce to be | ||
1862 | * invalid in seconds | ||
1863 | * @return #MHD_DAUTH_OK if authenticated, | ||
1864 | * error code otherwise. | ||
1865 | * @ingroup authentication | ||
1866 | */ | ||
1867 | static enum MHD_DigestAuthResult | ||
1868 | digest_auth_check_all (struct MHD_Connection *connection, | ||
1869 | struct DigestAlgorithm *da, | ||
1870 | const char *realm, | ||
1871 | const char *username, | ||
1872 | const char *password, | ||
1873 | const uint8_t *digest, | ||
1874 | unsigned int nonce_timeout) | ||
1875 | { | ||
1876 | enum MHD_DigestAuthResult res; | ||
1877 | char *buf; | ||
1878 | |||
1879 | buf = NULL; | ||
1880 | res = digest_auth_check_all_inner (connection, da, realm, username, password, | ||
1881 | digest, nonce_timeout, &buf); | ||
1882 | if (NULL != buf) | ||
1883 | free (buf); | ||
1884 | |||
1885 | return res; | ||
1886 | } | ||
1887 | |||
1888 | |||
1889 | /** | ||
1928 | * Authenticates the authorization header sent by the client. | 1890 | * Authenticates the authorization header sent by the client. |
1929 | * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility). | 1891 | * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility). |
1930 | * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future. | 1892 | * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future. |