diff options
author | Christian Grothoff <christian@grothoff.org> | 2018-07-14 11:09:19 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2018-07-14 13:27:45 +0200 |
commit | 2ed04522e24b801251d7fd1768b7fccfd7b8deac (patch) | |
tree | 99b8cd397d1a2e17f1b559f24343e42e0549dac3 | |
parent | 2ade9eaad8d61ffdf18de113ff0934ad84565ffb (diff) | |
download | libmicrohttpd-2ed04522e24b801251d7fd1768b7fccfd7b8deac.tar.gz libmicrohttpd-2ed04522e24b801251d7fd1768b7fccfd7b8deac.zip |
add support for digest auth with hashed password
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | doc/libmicrohttpd.texi | 15 | ||||
-rw-r--r-- | src/include/microhttpd.h | 36 | ||||
-rw-r--r-- | src/microhttpd/digestauth.c | 213 |
4 files changed, 215 insertions, 54 deletions
@@ -1,3 +1,8 @@ | |||
1 | Sat Jul 14 11:03:37 CEST 2018 | ||
2 | Integrate patch for checking digest authentication based on | ||
3 | a digest, allowing servers to store passwords only hashed. | ||
4 | Adding new function MHD_digest_auth_check_digest(). -CG/FIXME: ack co-author! | ||
5 | |||
1 | Sat Mar 10 12:15:35 CET 2018 | 6 | Sat Mar 10 12:15:35 CET 2018 |
2 | Upgrade to gettext-0.19.8.1. Switching to more canonical | 7 | Upgrade to gettext-0.19.8.1. Switching to more canonical |
3 | gettext integration. -CG | 8 | gettext integration. -CG |
diff --git a/doc/libmicrohttpd.texi b/doc/libmicrohttpd.texi index cac5dd29..e4437441 100644 --- a/doc/libmicrohttpd.texi +++ b/doc/libmicrohttpd.texi | |||
@@ -2393,6 +2393,21 @@ most probably it will be the result of a lookup of the username against a local | |||
2393 | Most of the time it is sound to specify 300 seconds as its values. | 2393 | Most of the time it is sound to specify 300 seconds as its values. |
2394 | @end deftypefun | 2394 | @end deftypefun |
2395 | 2395 | ||
2396 | @deftypefun int MHD_digest_auth_check_digest (struct MHD_Connection *connection, const char *realm, const char *username, const unsigned char digest[MHD_MD5_DIGEST_SIZE], unsigned int nonce_timeout) | ||
2397 | Checks if the provided values in the WWW-Authenticate header are valid | ||
2398 | and sound according to RFC2716. If valid return @code{MHD_YES}, otherwise return @code{MHD_NO}. | ||
2399 | |||
2400 | @var{realm} must reference to a zero-terminated string representing the realm. | ||
2401 | |||
2402 | @var{username} must reference to a zero-terminated string representing the username, | ||
2403 | it is usually the returned value from MHD_digest_auth_get_username. | ||
2404 | |||
2405 | @var{digest} pointer to the binary MD5 sum for the precalculated hash value ``userame:realm:password'' of @code{MHD_MD5_DIGEST_SIZE} bytes. | ||
2406 | |||
2407 | @var{nonce_timeout} is the amount of time in seconds for a nonce to be invalid. | ||
2408 | Most of the time it is sound to specify 300 seconds as its values. | ||
2409 | @end deftypefun | ||
2410 | |||
2396 | @deftypefun int MHD_queue_auth_fail_response (struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale) | 2411 | @deftypefun int MHD_queue_auth_fail_response (struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale) |
2397 | Queues a response to request authentication from the client, | 2412 | Queues a response to request authentication from the client, |
2398 | return @code{MHD_YES} if successful, otherwise @code{MHD_NO}. | 2413 | return @code{MHD_YES} if successful, otherwise @code{MHD_NO}. |
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index cff084ed..cdbde609 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h | |||
@@ -294,6 +294,12 @@ _MHD_DEPR_MACRO("Macro MHD_LONG_LONG_PRINTF is deprecated, use MHD_UNSIGNED_LONG | |||
294 | 294 | ||
295 | 295 | ||
296 | /** | 296 | /** |
297 | * Length of the binary output of the MD5 hash function. | ||
298 | */ | ||
299 | #define MHD_MD5_DIGEST_SIZE 16 | ||
300 | |||
301 | |||
302 | /** | ||
297 | * @defgroup httpcode HTTP response codes. | 303 | * @defgroup httpcode HTTP response codes. |
298 | * These are the status codes defined for HTTP responses. | 304 | * These are the status codes defined for HTTP responses. |
299 | * @{ | 305 | * @{ |
@@ -3144,10 +3150,32 @@ MHD_free (void *ptr); | |||
3144 | */ | 3150 | */ |
3145 | _MHD_EXTERN int | 3151 | _MHD_EXTERN int |
3146 | MHD_digest_auth_check (struct MHD_Connection *connection, | 3152 | MHD_digest_auth_check (struct MHD_Connection *connection, |
3147 | const char *realm, | 3153 | const char *realm, |
3148 | const char *username, | 3154 | const char *username, |
3149 | const char *password, | 3155 | const char *password, |
3150 | unsigned int nonce_timeout); | 3156 | unsigned int nonce_timeout); |
3157 | |||
3158 | /** | ||
3159 | * Authenticates the authorization header sent by the client | ||
3160 | * | ||
3161 | * @param connection The MHD connection structure | ||
3162 | * @param realm The realm presented to the client | ||
3163 | * @param username The username needs to be authenticated | ||
3164 | * @param digest An `unsigned char *' pointer to the binary MD5 sum | ||
3165 | * for the precalculated hash value "username:realm:password" | ||
3166 | * of #MHD_MD5_DIGEST_SIZE bytes | ||
3167 | * @param nonce_timeout The amount of time for a nonce to be | ||
3168 | * invalid in seconds | ||
3169 | * @return #MHD_YES if authenticated, #MHD_NO if not, | ||
3170 | * #MHD_INVALID_NONCE if nonce is invalid | ||
3171 | * @ingroup authentication | ||
3172 | */ | ||
3173 | _MHD_EXTERN int | ||
3174 | MHD_digest_auth_check_digest (struct MHD_Connection *connection, | ||
3175 | const char *realm, | ||
3176 | const char *username, | ||
3177 | const uint8_t digest[MHD_MD5_DIGEST_SIZE], | ||
3178 | unsigned int nonce_timeout); | ||
3151 | 3179 | ||
3152 | 3180 | ||
3153 | /** | 3181 | /** |
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c index b0e7ce00..0c5baffb 100644 --- a/src/microhttpd/digestauth.c +++ b/src/microhttpd/digestauth.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | This file is part of libmicrohttpd | 2 | This file is part of libmicrohttpd |
3 | Copyright (C) 2010, 2011, 2012, 2015 Daniel Pittman and Christian Grothoff | 3 | Copyright (C) 2010, 2011, 2012, 2015, 2018 Daniel Pittman and Christian Grothoff |
4 | 4 | ||
5 | This library is free software; you can redistribute it and/or | 5 | This library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public | 6 | modify it under the terms of the GNU Lesser General Public |
@@ -37,7 +37,7 @@ | |||
37 | #include <windows.h> | 37 | #include <windows.h> |
38 | #endif /* MHD_W32_MUTEX_ */ | 38 | #endif /* MHD_W32_MUTEX_ */ |
39 | 39 | ||
40 | #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE) | 40 | #define HASH_MD5_HEX_LEN (2 * MHD_MD5_DIGEST_SIZE) |
41 | /* 32 bit value is 4 bytes */ | 41 | /* 32 bit value is 4 bytes */ |
42 | #define TIMESTAMP_BIN_SIZE 4 | 42 | #define TIMESTAMP_BIN_SIZE 4 |
43 | #define TIMESTAMP_HEX_LEN (2 * TIMESTAMP_BIN_SIZE) | 43 | #define TIMESTAMP_HEX_LEN (2 * TIMESTAMP_BIN_SIZE) |
@@ -93,8 +93,65 @@ cvthex (const unsigned char *bin, | |||
93 | 93 | ||
94 | 94 | ||
95 | /** | 95 | /** |
96 | * calculate H(A1) as per RFC2617 spec and store the | 96 | * calculate H(A1) from given hash as per RFC2617 spec |
97 | * result in 'sessionkey'. | 97 | * and store the * result in 'sessionkey'. |
98 | * | ||
99 | * @param alg The hash algorithm used, can be "md5" or "md5-sess" | ||
100 | * @param digest An `unsigned char *' pointer to the binary MD5 sum | ||
101 | * for the precalculated hash value "username:realm:password" | ||
102 | * of #MHD_MD5_DIGEST_SIZE bytes | ||
103 | * @param nonce A `char *' pointer to the nonce value | ||
104 | * @param cnonce A `char *' pointer to the cnonce value | ||
105 | * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes | ||
106 | */ | ||
107 | static void | ||
108 | digest_calc_ha1_from_digest (const char *alg, | ||
109 | const uint8_t digest[MHD_MD5_DIGEST_SIZE], | ||
110 | const char *nonce, | ||
111 | const char *cnonce, | ||
112 | char sessionkey[HASH_MD5_HEX_LEN + 1]) | ||
113 | { | ||
114 | struct MD5Context md5; | ||
115 | |||
116 | if (MHD_str_equal_caseless_(alg, | ||
117 | "md5-sess")) | ||
118 | { | ||
119 | unsigned char ha1[MHD_MD5_DIGEST_SIZE]; | ||
120 | |||
121 | MD5Init (&md5); | ||
122 | MD5Update (&md5, | ||
123 | digest, | ||
124 | MHD_MD5_DIGEST_SIZE); | ||
125 | MD5Update (&md5, | ||
126 | (const unsigned char *) ":", | ||
127 | 1); | ||
128 | MD5Update (&md5, | ||
129 | (const unsigned char *) nonce, | ||
130 | strlen (nonce)); | ||
131 | MD5Update (&md5, | ||
132 | (const unsigned char *) ":", | ||
133 | 1); | ||
134 | MD5Update (&md5, | ||
135 | (const unsigned char *) cnonce, | ||
136 | strlen (cnonce)); | ||
137 | MD5Final (ha1, | ||
138 | &md5); | ||
139 | cvthex (ha1, | ||
140 | sizeof (ha1), | ||
141 | sessionkey); | ||
142 | } | ||
143 | else | ||
144 | { | ||
145 | cvthex (digest, | ||
146 | MHD_MD5_DIGEST_SIZE, | ||
147 | sessionkey); | ||
148 | } | ||
149 | } | ||
150 | |||
151 | |||
152 | /** | ||
153 | * calculate H(A1) from username, realm and password as per RFC2617 spec | ||
154 | * and store the result in 'sessionkey'. | ||
98 | * | 155 | * |
99 | * @param alg The hash algorithm used, can be "md5" or "md5-sess" | 156 | * @param alg The hash algorithm used, can be "md5" or "md5-sess" |
100 | * @param username A `char *' pointer to the username value | 157 | * @param username A `char *' pointer to the username value |
@@ -105,16 +162,16 @@ cvthex (const unsigned char *bin, | |||
105 | * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes | 162 | * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes |
106 | */ | 163 | */ |
107 | static void | 164 | static void |
108 | digest_calc_ha1 (const char *alg, | 165 | digest_calc_ha1_from_user (const char *alg, |
109 | const char *username, | 166 | const char *username, |
110 | const char *realm, | 167 | const char *realm, |
111 | const char *password, | 168 | const char *password, |
112 | const char *nonce, | 169 | const char *nonce, |
113 | const char *cnonce, | 170 | const char *cnonce, |
114 | char sessionkey[HASH_MD5_HEX_LEN + 1]) | 171 | char sessionkey[HASH_MD5_HEX_LEN + 1]) |
115 | { | 172 | { |
116 | struct MD5Context md5; | 173 | struct MD5Context md5; |
117 | unsigned char ha1[MD5_DIGEST_SIZE]; | 174 | unsigned char ha1[MHD_MD5_DIGEST_SIZE]; |
118 | 175 | ||
119 | MD5Init (&md5); | 176 | MD5Init (&md5); |
120 | MD5Update (&md5, | 177 | MD5Update (&md5, |
@@ -134,31 +191,11 @@ digest_calc_ha1 (const char *alg, | |||
134 | strlen (password)); | 191 | strlen (password)); |
135 | MD5Final (ha1, | 192 | MD5Final (ha1, |
136 | &md5); | 193 | &md5); |
137 | if (MHD_str_equal_caseless_(alg, | 194 | digest_calc_ha1_from_digest(alg, |
138 | "md5-sess")) | 195 | ha1, |
139 | { | 196 | nonce, |
140 | MD5Init (&md5); | 197 | cnonce, |
141 | MD5Update (&md5, | 198 | sessionkey); |
142 | (const unsigned char *) ha1, | ||
143 | sizeof (ha1)); | ||
144 | MD5Update (&md5, | ||
145 | (const unsigned char *) ":", | ||
146 | 1); | ||
147 | MD5Update (&md5, | ||
148 | (const unsigned char *) nonce, | ||
149 | strlen (nonce)); | ||
150 | MD5Update (&md5, | ||
151 | (const unsigned char *) ":", | ||
152 | 1); | ||
153 | MD5Update (&md5, | ||
154 | (const unsigned char *) cnonce, | ||
155 | strlen (cnonce)); | ||
156 | MD5Final (ha1, | ||
157 | &md5); | ||
158 | } | ||
159 | cvthex (ha1, | ||
160 | sizeof (ha1), | ||
161 | sessionkey); | ||
162 | } | 199 | } |
163 | 200 | ||
164 | 201 | ||
@@ -187,8 +224,8 @@ digest_calc_response (const char ha1[HASH_MD5_HEX_LEN + 1], | |||
187 | char response[HASH_MD5_HEX_LEN + 1]) | 224 | char response[HASH_MD5_HEX_LEN + 1]) |
188 | { | 225 | { |
189 | struct MD5Context md5; | 226 | struct MD5Context md5; |
190 | unsigned char ha2[MD5_DIGEST_SIZE]; | 227 | unsigned char ha2[MHD_MD5_DIGEST_SIZE]; |
191 | unsigned char resphash[MD5_DIGEST_SIZE]; | 228 | unsigned char resphash[MHD_MD5_DIGEST_SIZE]; |
192 | char ha2hex[HASH_MD5_HEX_LEN + 1]; | 229 | char ha2hex[HASH_MD5_HEX_LEN + 1]; |
193 | (void)hentity; /* Unused. Silent compiler warning. */ | 230 | (void)hentity; /* Unused. Silent compiler warning. */ |
194 | 231 | ||
@@ -220,7 +257,7 @@ digest_calc_response (const char ha1[HASH_MD5_HEX_LEN + 1], | |||
220 | MD5Final (ha2, | 257 | MD5Final (ha2, |
221 | &md5); | 258 | &md5); |
222 | cvthex (ha2, | 259 | cvthex (ha2, |
223 | MD5_DIGEST_SIZE, | 260 | MHD_MD5_DIGEST_SIZE, |
224 | ha2hex); | 261 | ha2hex); |
225 | MD5Init (&md5); | 262 | MD5Init (&md5); |
226 | /* calculate response */ | 263 | /* calculate response */ |
@@ -518,7 +555,7 @@ calculate_nonce (uint32_t nonce_time, | |||
518 | { | 555 | { |
519 | struct MD5Context md5; | 556 | struct MD5Context md5; |
520 | unsigned char timestamp[TIMESTAMP_BIN_SIZE]; | 557 | unsigned char timestamp[TIMESTAMP_BIN_SIZE]; |
521 | unsigned char tmpnonce[MD5_DIGEST_SIZE]; | 558 | unsigned char tmpnonce[MHD_MD5_DIGEST_SIZE]; |
522 | char timestamphex[TIMESTAMP_HEX_LEN + 1]; | 559 | char timestamphex[TIMESTAMP_HEX_LEN + 1]; |
523 | 560 | ||
524 | MD5Init (&md5); | 561 | MD5Init (&md5); |
@@ -667,17 +704,21 @@ check_argument_match (struct MHD_Connection *connection, | |||
667 | * @param realm The realm presented to the client | 704 | * @param realm The realm presented to the client |
668 | * @param username The username needs to be authenticated | 705 | * @param username The username needs to be authenticated |
669 | * @param password The password used in the authentication | 706 | * @param password The password used in the authentication |
707 | * @param digest An optional `unsigned char *' pointer to the binary MD5 sum | ||
708 | * for the precalculated hash value "username:realm:password" | ||
709 | * of #MHD_MD5_DIGEST_SIZE bytes | ||
670 | * @param nonce_timeout The amount of time for a nonce to be | 710 | * @param nonce_timeout The amount of time for a nonce to be |
671 | * invalid in seconds | 711 | * invalid in seconds |
672 | * @return #MHD_YES if authenticated, #MHD_NO if not, | 712 | * @return #MHD_YES if authenticated, #MHD_NO if not, |
673 | * #MHD_INVALID_NONCE if nonce is invalid | 713 | * #MHD_INVALID_NONCE if nonce is invalid |
674 | * @ingroup authentication | 714 | * @ingroup authentication |
675 | */ | 715 | */ |
676 | int | 716 | static int |
677 | MHD_digest_auth_check (struct MHD_Connection *connection, | 717 | digest_auth_check_all (struct MHD_Connection *connection, |
678 | const char *realm, | 718 | const char *realm, |
679 | const char *username, | 719 | const char *username, |
680 | const char *password, | 720 | const char *password, |
721 | const uint8_t digest[MHD_MD5_DIGEST_SIZE], | ||
681 | unsigned int nonce_timeout) | 722 | unsigned int nonce_timeout) |
682 | { | 723 | { |
683 | struct MHD_Daemon *daemon = connection->daemon; | 724 | struct MHD_Daemon *daemon = connection->daemon; |
@@ -871,13 +912,24 @@ MHD_digest_auth_check (struct MHD_Connection *connection, | |||
871 | return MHD_NO; | 912 | return MHD_NO; |
872 | } | 913 | } |
873 | 914 | ||
874 | digest_calc_ha1 ("md5", | 915 | if (NULL != digest) |
875 | username, | 916 | { |
876 | realm, | 917 | digest_calc_ha1_from_digest ("md5", |
877 | password, | 918 | digest, |
878 | nonce, | 919 | nonce, |
879 | cnonce, | 920 | cnonce, |
880 | ha1); | 921 | ha1); |
922 | } | ||
923 | else | ||
924 | { | ||
925 | digest_calc_ha1_from_user ("md5", | ||
926 | username, | ||
927 | realm, | ||
928 | password, | ||
929 | nonce, | ||
930 | cnonce, | ||
931 | ha1); | ||
932 | } | ||
881 | digest_calc_response (ha1, | 933 | digest_calc_response (ha1, |
882 | nonce, | 934 | nonce, |
883 | nc, | 935 | nc, |
@@ -888,6 +940,7 @@ MHD_digest_auth_check (struct MHD_Connection *connection, | |||
888 | hentity, | 940 | hentity, |
889 | respexp); | 941 | respexp); |
890 | 942 | ||
943 | |||
891 | /* Need to unescape URI before comparing with connection->url */ | 944 | /* Need to unescape URI before comparing with connection->url */ |
892 | daemon->unescape_callback (daemon->unescape_callback_cls, | 945 | daemon->unescape_callback (daemon->unescape_callback_cls, |
893 | connection, | 946 | connection, |
@@ -934,6 +987,66 @@ MHD_digest_auth_check (struct MHD_Connection *connection, | |||
934 | 987 | ||
935 | 988 | ||
936 | /** | 989 | /** |
990 | * Authenticates the authorization header sent by the client | ||
991 | * | ||
992 | * @param connection The MHD connection structure | ||
993 | * @param realm The realm presented to the client | ||
994 | * @param username The username needs to be authenticated | ||
995 | * @param password The password used in the authentication | ||
996 | * @param nonce_timeout The amount of time for a nonce to be | ||
997 | * invalid in seconds | ||
998 | * @return #MHD_YES if authenticated, #MHD_NO if not, | ||
999 | * #MHD_INVALID_NONCE if nonce is invalid | ||
1000 | * @ingroup authentication | ||
1001 | */ | ||
1002 | _MHD_EXTERN int | ||
1003 | MHD_digest_auth_check (struct MHD_Connection *connection, | ||
1004 | const char *realm, | ||
1005 | const char *username, | ||
1006 | const char *password, | ||
1007 | unsigned int nonce_timeout) | ||
1008 | { | ||
1009 | return digest_auth_check_all(connection, | ||
1010 | realm, | ||
1011 | username, | ||
1012 | password, | ||
1013 | NULL, | ||
1014 | nonce_timeout); | ||
1015 | } | ||
1016 | |||
1017 | |||
1018 | /** | ||
1019 | * Authenticates the authorization header sent by the client | ||
1020 | * | ||
1021 | * @param connection The MHD connection structure | ||
1022 | * @param realm The realm presented to the client | ||
1023 | * @param username The username needs to be authenticated | ||
1024 | * @param digest An `unsigned char *' pointer to the binary MD5 sum | ||
1025 | * for the precalculated hash value "username:realm:password" | ||
1026 | * of #MHD_MD5_DIGEST_SIZE bytes | ||
1027 | * @param nonce_timeout The amount of time for a nonce to be | ||
1028 | * invalid in seconds | ||
1029 | * @return #MHD_YES if authenticated, #MHD_NO if not, | ||
1030 | * #MHD_INVALID_NONCE if nonce is invalid | ||
1031 | * @ingroup authentication | ||
1032 | */ | ||
1033 | _MHD_EXTERN int | ||
1034 | MHD_digest_auth_check_digest (struct MHD_Connection *connection, | ||
1035 | const char *realm, | ||
1036 | const char *username, | ||
1037 | const uint8_t digest[MD5_DIGEST_SIZE], | ||
1038 | unsigned int nonce_timeout) | ||
1039 | { | ||
1040 | return digest_auth_check_all (connection, | ||
1041 | realm, | ||
1042 | username, | ||
1043 | NULL, | ||
1044 | digest, | ||
1045 | nonce_timeout); | ||
1046 | } | ||
1047 | |||
1048 | |||
1049 | /** | ||
937 | * Queues a response to request authentication from the client | 1050 | * Queues a response to request authentication from the client |
938 | * | 1051 | * |
939 | * @param connection The MHD connection structure | 1052 | * @param connection The MHD connection structure |