6 #include "inet_ntop_cache.h"
12 #elif defined(__linux__)
13 /* linux needs _XOPEN_SOURCE */
14 # define _XOPEN_SOURCE
17 #if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT)
18 /* always assume crypt() is present if we have -lcrypt */
22 #include <sys/types.h>
37 #include <openssl/sha.h>
40 #include "safe_memclear.h"
44 typedef unsigned char HASH
[HASHLEN
];
45 typedef char HASHHEX
[HASHHEXLEN
+1];
47 static void CvtHex(const HASH Bin
, char (*Hex
)[33]) {
48 li_tohex(*Hex
, sizeof(*Hex
), (const char*) Bin
, 16);
53 * the $apr1$ handling is taken from apache 1.3.x
57 * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
58 * MD5 crypt() function, which is licenced as follows:
59 * ----------------------------------------------------------------------------
60 * "THE BEER-WARE LICENSE" (Revision 42):
61 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
62 * can do whatever you want with this stuff. If we meet some day, and you think
63 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
64 * ----------------------------------------------------------------------------
67 handler_t
auth_ldap_init(server
*srv
, mod_auth_plugin_config
*s
);
69 static int http_auth_get_password(server
*srv
, mod_auth_plugin_data
*p
, buffer
*username
, buffer
*realm
, buffer
*password
) {
70 if (buffer_is_empty(username
) || buffer_is_empty(realm
)) return -1;
72 if (p
->conf
.auth_backend
== AUTH_BACKEND_HTDIGEST
) {
76 if (buffer_string_is_empty(p
->conf
.auth_htdigest_userfile
)) return -1;
78 fp
= fopen(p
->conf
.auth_htdigest_userfile
->ptr
, "r");
80 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "opening digest-userfile", p
->conf
.auth_htdigest_userfile
, "failed:", strerror(errno
));
85 while (NULL
!= fgets(f_user
, sizeof(f_user
), fp
)) {
86 char *f_pwd
, *f_realm
;
89 /* skip blank lines and comment lines (beginning '#') */
90 if (f_user
[0] == '#' || f_user
[0] == '\n' || f_user
[0] == '\0') continue;
95 * user:realm:md5(user:realm:password)
98 if (NULL
== (f_realm
= strchr(f_user
, ':'))) {
99 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
100 "parsed error in", p
->conf
.auth_htdigest_userfile
,
101 "expected 'username:realm:hashed password'");
103 continue; /* skip bad lines */
106 if (NULL
== (f_pwd
= strchr(f_realm
+ 1, ':'))) {
107 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
108 "parsed error in", p
->conf
.auth_plain_userfile
,
109 "expected 'username:realm:hashed password'");
111 continue; /* skip bad lines */
114 /* get pointers to the fields */
115 u_len
= f_realm
- f_user
;
117 r_len
= f_pwd
- f_realm
;
120 if (buffer_string_length(username
) == u_len
&&
121 (buffer_string_length(realm
) == r_len
) &&
122 (0 == strncmp(username
->ptr
, f_user
, u_len
)) &&
123 (0 == strncmp(realm
->ptr
, f_realm
, r_len
))) {
126 size_t pwd_len
= strlen(f_pwd
);
127 if (f_pwd
[pwd_len
-1] == '\n') --pwd_len
;
129 buffer_copy_string_len(password
, f_pwd
, pwd_len
);
137 } else if (p
->conf
.auth_backend
== AUTH_BACKEND_HTPASSWD
||
138 p
->conf
.auth_backend
== AUTH_BACKEND_PLAIN
) {
143 auth_fn
= (p
->conf
.auth_backend
== AUTH_BACKEND_HTPASSWD
) ? p
->conf
.auth_htpasswd_userfile
: p
->conf
.auth_plain_userfile
;
145 if (buffer_string_is_empty(auth_fn
)) return -1;
147 fp
= fopen(auth_fn
->ptr
, "r");
149 log_error_write(srv
, __FILE__
, __LINE__
, "sbss",
150 "opening plain-userfile", auth_fn
, "failed:", strerror(errno
));
155 while (NULL
!= fgets(f_user
, sizeof(f_user
), fp
)) {
159 /* skip blank lines and comment lines (beginning '#') */
160 if (f_user
[0] == '#' || f_user
[0] == '\n' || f_user
[0] == '\0') continue;
165 * user:crypted passwd
168 if (NULL
== (f_pwd
= strchr(f_user
, ':'))) {
169 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
170 "parsed error in", auth_fn
,
171 "expected 'username:hashed password'");
173 continue; /* skip bad lines */
176 /* get pointers to the fields */
177 u_len
= f_pwd
- f_user
;
180 if (buffer_string_length(username
) == u_len
&&
181 (0 == strncmp(username
->ptr
, f_user
, u_len
))) {
184 size_t pwd_len
= strlen(f_pwd
);
185 if (f_pwd
[pwd_len
-1] == '\n') --pwd_len
;
187 buffer_copy_string_len(password
, f_pwd
, pwd_len
);
195 } else if (p
->conf
.auth_backend
== AUTH_BACKEND_LDAP
) {
202 int http_auth_match_rules(server
*srv
, array
*req
, const char *username
, const char *group
, const char *host
) {
203 const char *r
= NULL
, *rules
= NULL
;
205 data_string
*require
;
210 require
= (data_string
*)array_get_element(req
, "require");
212 /* if we get here, the user we got a authed user */
213 if (0 == strcmp(require
->value
->ptr
, "valid-user")) {
217 /* user=name1|group=name3|host=name4 */
219 /* seperate the string by | */
221 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "rules", require
->value
);
224 username_len
= username
? strlen(username
) : 0;
226 r
= rules
= require
->value
->ptr
;
230 const char *k
, *v
, *e
;
231 int k_len
, v_len
, r_len
;
238 r_len
= strlen(rules
) - (r
- rules
);
241 /* from r to r + r_len is a rule */
243 if (0 == strncmp(r
, "valid-user", r_len
)) {
244 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
245 "parsing the 'require' section in 'auth.require' failed: valid-user cannot be combined with other require rules",
250 /* search for = in the rules */
251 if (NULL
== (eq
= strchr(r
, '='))) {
252 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
253 "parsing the 'require' section in 'auth.require' failed: a = is missing",
259 if (eq
> r
+ r_len
) {
260 log_error_write(srv
, __FILE__
, __LINE__
, "sb",
261 "parsing the 'require' section in 'auth.require' failed: = out of range",
267 /* the part before the = is user|group|host */
272 v_len
= r_len
- k_len
- 1;
275 if (0 == strncmp(k
, "user", k_len
)) {
277 username_len
== v_len
&&
278 0 == strncmp(username
, v
, v_len
)) {
281 } else if (0 == strncmp(k
, "host", k_len
)) {
282 log_error_write(srv
, __FILE__
, __LINE__
, "s", "host ... (not implemented)");
284 log_error_write(srv
, __FILE__
, __LINE__
, "s", "unknown key");
287 } else if (k_len
== 5) {
288 if (0 == strncmp(k
, "group", k_len
)) {
289 log_error_write(srv
, __FILE__
, __LINE__
, "s", "group ... (not implemented)");
291 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "unknown key", k
);
295 log_error_write(srv
, __FILE__
, __LINE__
, "s", "unknown key");
303 log_error_write(srv
, __FILE__
, __LINE__
, "s", "nothing matched");
308 #define APR_MD5_DIGESTSIZE 16
309 #define APR1_ID "$apr1$"
312 * The following MD5 password encryption code was largely borrowed from
313 * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
314 * licenced as stated at the top of this file.
317 static void to64(char *s
, unsigned long v
, int n
)
319 static const unsigned char itoa64
[] = /* 0 ... 63 => ASCII - 64 */
320 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
323 *s
++ = itoa64
[v
&0x3f];
328 static void apr_md5_encode(const char *pw
, const char *salt
, char *result
, size_t nbytes
) {
330 * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
331 * plus 4 for the '$' separators, plus the password hash itself.
332 * Let's leave a goodly amount of leeway.
335 char passwd
[120], *p
;
337 unsigned char final
[APR_MD5_DIGESTSIZE
];
339 li_MD5_CTX ctx
, ctx1
;
343 * Refine the salt first. It's possible we were given an already-hashed
344 * string as the salt argument, so extract the actual salt value from it
345 * if so. Otherwise just use the string up to the first '$' as the salt.
350 * If it starts with the magic string, then skip that.
352 if (!strncmp(sp
, APR1_ID
, strlen(APR1_ID
))) {
353 sp
+= strlen(APR1_ID
);
357 * It stops at the first '$' or 8 chars, whichever comes first
359 for (ep
= sp
; (*ep
!= '\0') && (*ep
!= '$') && (ep
< (sp
+ 8)); ep
++) {
364 * Get the length of the true salt
369 * 'Time to make the doughnuts..'
374 * The password first, since that is what is most unknown
376 li_MD5_Update(&ctx
, pw
, strlen(pw
));
379 * Then our magic string
381 li_MD5_Update(&ctx
, APR1_ID
, strlen(APR1_ID
));
386 li_MD5_Update(&ctx
, sp
, sl
);
389 * Then just as many characters of the MD5(pw, salt, pw)
392 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
393 li_MD5_Update(&ctx1
, sp
, sl
);
394 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
395 li_MD5_Final(final
, &ctx1
);
396 for (pl
= strlen(pw
); pl
> 0; pl
-= APR_MD5_DIGESTSIZE
) {
399 (pl
> APR_MD5_DIGESTSIZE
) ? APR_MD5_DIGESTSIZE
: pl
);
403 * Don't leave anything around in vm they could use.
405 memset(final
, 0, sizeof(final
));
408 * Then something really weird...
410 for (i
= strlen(pw
); i
!= 0; i
>>= 1) {
412 li_MD5_Update(&ctx
, final
, 1);
415 li_MD5_Update(&ctx
, pw
, 1);
420 * Now make the output string. We know our limitations, so we
421 * can use the string routines without bounds checking.
423 strcpy(passwd
, APR1_ID
);
424 strncat(passwd
, sp
, sl
);
427 li_MD5_Final(final
, &ctx
);
430 * And now, just to make sure things don't run too fast..
431 * On a 60 Mhz Pentium this takes 34 msec, so you would
432 * need 30 seconds to build a 1000 entry dictionary...
434 for (i
= 0; i
< 1000; i
++) {
437 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
440 li_MD5_Update(&ctx1
, final
, APR_MD5_DIGESTSIZE
);
443 li_MD5_Update(&ctx1
, sp
, sl
);
447 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
451 li_MD5_Update(&ctx1
, final
, APR_MD5_DIGESTSIZE
);
454 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
456 li_MD5_Final(final
,&ctx1
);
459 p
= passwd
+ strlen(passwd
);
461 l
= (final
[ 0]<<16) | (final
[ 6]<<8) | final
[12]; to64(p
, l
, 4); p
+= 4;
462 l
= (final
[ 1]<<16) | (final
[ 7]<<8) | final
[13]; to64(p
, l
, 4); p
+= 4;
463 l
= (final
[ 2]<<16) | (final
[ 8]<<8) | final
[14]; to64(p
, l
, 4); p
+= 4;
464 l
= (final
[ 3]<<16) | (final
[ 9]<<8) | final
[15]; to64(p
, l
, 4); p
+= 4;
465 l
= (final
[ 4]<<16) | (final
[10]<<8) | final
[ 5]; to64(p
, l
, 4); p
+= 4;
466 l
= final
[11] ; to64(p
, l
, 2); p
+= 2;
470 * Don't leave anything around in vm they could use.
472 safe_memclear(final
, sizeof(final
));
476 #define apr_cpystrn strncpy
477 apr_cpystrn(result
, passwd
, nbytes
- 1);
481 static void apr_sha_encode(const char *pw
, char *result
, size_t nbytes
) {
482 unsigned char digest
[20];
483 size_t base64_written
;
485 SHA1((const unsigned char*) pw
, strlen(pw
), digest
);
487 memset(result
, 0, nbytes
);
489 /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
490 if (nbytes
< 5 + 28 + 1) return;
492 memcpy(result
, "{SHA}", 5);
493 base64_written
= li_to_base64(result
+ 5, nbytes
- 5, digest
, 20, BASE64_STANDARD
);
494 force_assert(base64_written
== 28);
495 result
[5 + base64_written
] = '\0'; /* terminate string */
502 * @param password password-string from the auth-backend
503 * @param pw password-string from the client
506 static int http_auth_basic_password_compare(server
*srv
, mod_auth_plugin_data
*p
, array
*req
, buffer
*username
, buffer
*realm
, buffer
*password
, const char *pw
) {
510 if (p
->conf
.auth_backend
== AUTH_BACKEND_HTDIGEST
) {
514 * user:realm:md5(user:realm:password)
521 li_MD5_Init(&Md5Ctx
);
522 li_MD5_Update(&Md5Ctx
, CONST_BUF_LEN(username
));
523 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
524 li_MD5_Update(&Md5Ctx
, CONST_BUF_LEN(realm
));
525 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
526 li_MD5_Update(&Md5Ctx
, (unsigned char *)pw
, strlen(pw
));
527 li_MD5_Final(HA1
, &Md5Ctx
);
531 if (0 == strcmp(password
->ptr
, a1
)) {
534 } else if (p
->conf
.auth_backend
== AUTH_BACKEND_HTPASSWD
) {
536 if (!strncmp(password
->ptr
, APR1_ID
, strlen(APR1_ID
))) {
538 * The hash was created using $apr1$ custom algorithm.
540 apr_md5_encode(pw
, password
->ptr
, sample
, sizeof(sample
));
541 return (strcmp(sample
, password
->ptr
) == 0) ? 0 : 1;
543 } else if (0 == strncmp(password
->ptr
, "{SHA}", 5)) {
544 apr_sha_encode(pw
, sample
, sizeof(sample
));
545 return (strcmp(sample
, password
->ptr
) == 0) ? 0 : 1;
548 #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)
550 #if defined(HAVE_CRYPT_R)
551 struct crypt_data crypt_tmp_data
;
552 crypt_tmp_data
.initialized
= 0;
555 /* a simple DES password is 2 + 11 characters. everything else should be longer. */
556 if (buffer_string_length(password
) < 13) {
560 #if defined(HAVE_CRYPT_R)
561 if (0 == (crypted
= crypt_r(pw
, password
->ptr
, &crypt_tmp_data
))) {
563 if (0 == (crypted
= crypt(pw
, password
->ptr
))) {
569 if (0 == strcmp(password
->ptr
, crypted
)) {
574 } else if (p
->conf
.auth_backend
== AUTH_BACKEND_PLAIN
) {
575 if (0 == strcmp(password
->ptr
, pw
)) {
578 } else if (p
->conf
.auth_backend
== AUTH_BACKEND_LDAP
) {
581 LDAPMessage
*lm
, *first
;
584 char *attrs
[] = { LDAP_NO_ATTRS
, NULL
};
587 /* for now we stay synchronous */
590 * 1. connect anonymously (done in plugin init)
591 * 2. get DN for uid = username
592 * 3. auth against ldap server
593 * 4. (optional) check a field
600 * we have to protect us againt username which modifies out filter in
604 len
= buffer_string_length(username
);
605 for (i
= 0; i
< len
; i
++) {
606 char c
= username
->ptr
[i
];
616 log_error_write(srv
, __FILE__
, __LINE__
, "sbd",
617 "ldap: invalid character (- _.@a-zA-Z0-9 allowed) in username:", username
, i
);
623 if (p
->conf
.auth_ldap_allow_empty_pw
!= 1 && pw
[0] == '\0')
627 buffer_copy_buffer(p
->ldap_filter
, p
->conf
.ldap_filter_pre
);
628 buffer_append_string_buffer(p
->ldap_filter
, username
);
629 buffer_append_string_buffer(p
->ldap_filter
, p
->conf
.ldap_filter_post
);
633 if (p
->anon_conf
->ldap
== NULL
||
634 LDAP_SUCCESS
!= (ret
= ldap_search_s(p
->anon_conf
->ldap
, p
->conf
.auth_ldap_basedn
->ptr
, LDAP_SCOPE_SUBTREE
, p
->ldap_filter
->ptr
, attrs
, 0, &lm
))) {
636 /* try again; the ldap library sometimes fails for the first call but reconnects */
637 if (p
->anon_conf
->ldap
== NULL
|| ret
!= LDAP_SERVER_DOWN
||
638 LDAP_SUCCESS
!= (ret
= ldap_search_s(p
->anon_conf
->ldap
, p
->conf
.auth_ldap_basedn
->ptr
, LDAP_SCOPE_SUBTREE
, p
->ldap_filter
->ptr
, attrs
, 0, &lm
))) {
640 if (auth_ldap_init(srv
, p
->anon_conf
) != HANDLER_GO_ON
)
643 if (NULL
== p
->anon_conf
->ldap
) return -1;
645 if (LDAP_SUCCESS
!= (ret
= ldap_search_s(p
->anon_conf
->ldap
, p
->conf
.auth_ldap_basedn
->ptr
, LDAP_SCOPE_SUBTREE
, p
->ldap_filter
->ptr
, attrs
, 0, &lm
))) {
646 log_error_write(srv
, __FILE__
, __LINE__
, "sssb",
647 "ldap:", ldap_err2string(ret
), "filter:", p
->ldap_filter
);
653 if (NULL
== (first
= ldap_first_entry(p
->anon_conf
->ldap
, lm
))) {
654 log_error_write(srv
, __FILE__
, __LINE__
, "s", "ldap ...");
661 if (NULL
== (dn
= ldap_get_dn(p
->anon_conf
->ldap
, first
))) {
662 log_error_write(srv
, __FILE__
, __LINE__
, "s", "ldap ...");
673 if (NULL
== (ldap
= ldap_init(p
->conf
.auth_ldap_hostname
->ptr
, LDAP_PORT
))) {
674 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "ldap ...", strerror(errno
));
679 if (LDAP_OPT_SUCCESS
!= (ret
= ldap_set_option(ldap
, LDAP_OPT_PROTOCOL_VERSION
, &ret
))) {
680 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "ldap:", ldap_err2string(ret
));
687 if (p
->conf
.auth_ldap_starttls
== 1) {
688 if (LDAP_OPT_SUCCESS
!= (ret
= ldap_start_tls_s(ldap
, NULL
, NULL
))) {
689 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "ldap startTLS failed:", ldap_err2string(ret
));
698 if (LDAP_SUCCESS
!= (ret
= ldap_simple_bind_s(ldap
, dn
, pw
))) {
699 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "ldap:", ldap_err2string(ret
));
709 /* everything worked, good, access granted */
717 int http_auth_basic_check(server
*srv
, connection
*con
, mod_auth_plugin_data
*p
, array
*req
, const char *realm_str
) {
718 buffer
*username
, *password
;
723 realm
= (data_string
*)array_get_element(req
, "realm");
725 username
= buffer_init();
727 if (!buffer_append_base64_decode(username
, realm_str
, strlen(realm_str
), BASE64_STANDARD
)) {
728 log_error_write(srv
, __FILE__
, __LINE__
, "sb", "decodeing base64-string failed", username
);
730 buffer_free(username
);
734 /* r2 == user:password */
735 if (NULL
== (pw
= strchr(username
->ptr
, ':'))) {
736 log_error_write(srv
, __FILE__
, __LINE__
, "sb", ": is missing in", username
);
738 buffer_free(username
);
742 buffer_string_set_length(username
, pw
- username
->ptr
);
745 password
= buffer_init();
746 /* copy password to r1 */
747 if (http_auth_get_password(srv
, p
, username
, realm
->value
, password
)) {
748 buffer_free(username
);
749 buffer_free(password
);
751 if (AUTH_BACKEND_UNSET
== p
->conf
.auth_backend
) {
752 log_error_write(srv
, __FILE__
, __LINE__
, "s", "auth.backend is not set");
754 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "get_password failed, IP:", inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
760 /* password doesn't match */
761 if (http_auth_basic_password_compare(srv
, p
, req
, username
, realm
->value
, password
, pw
)) {
762 log_error_write(srv
, __FILE__
, __LINE__
, "sbsBss", "password doesn't match for", con
->uri
.path
, "username:", username
, ", IP:", inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
764 buffer_free(username
);
765 buffer_free(password
);
770 /* value is our allow-rules */
771 if (http_auth_match_rules(srv
, req
, username
->ptr
, NULL
, NULL
)) {
772 buffer_free(username
);
773 buffer_free(password
);
775 log_error_write(srv
, __FILE__
, __LINE__
, "s", "rules didn't match");
780 /* remember the username */
781 buffer_copy_buffer(p
->auth_user
, username
);
783 buffer_free(username
);
784 buffer_free(password
);
795 /* return values: -1: error/bad request, 0: failed, 1: success */
796 int http_auth_digest_check(server
*srv
, connection
*con
, mod_auth_plugin_data
*p
, array
*req
, const char *realm_str
) {
800 char *username
= NULL
;
804 char *algorithm
= NULL
;
808 char *respons
= NULL
;
811 const char *m
= NULL
;
813 buffer
*password
, *b
, *username_buf
, *realm_buf
;
825 digest_kv dkv
[10] = {
840 dkv
[0].ptr
= &username
;
844 dkv
[4].ptr
= &algorithm
;
846 dkv
[6].ptr
= &cnonce
;
848 dkv
[8].ptr
= &respons
;
852 if (p
->conf
.auth_backend
!= AUTH_BACKEND_HTDIGEST
&&
853 p
->conf
.auth_backend
!= AUTH_BACKEND_PLAIN
) {
854 log_error_write(srv
, __FILE__
, __LINE__
, "s",
855 "digest: unsupported backend (only htdigest or plain)");
860 b
= buffer_init_string(realm_str
);
862 /* parse credentials from client */
863 for (c
= b
->ptr
; *c
; c
++) {
864 /* skip whitespaces */
865 while (*c
== ' ' || *c
== '\t') c
++;
868 for (i
= 0; dkv
[i
].key
; i
++) {
869 if ((0 == strncmp(c
, dkv
[i
].key
, dkv
[i
].key_len
))) {
870 if ((c
[dkv
[i
].key_len
] == '"') &&
871 (NULL
!= (e
= strchr(c
+ dkv
[i
].key_len
+ 1, '"')))) {
872 /* value with "..." */
873 *(dkv
[i
].ptr
) = c
+ dkv
[i
].key_len
+ 1;
877 } else if (NULL
!= (e
= strchr(c
+ dkv
[i
].key_len
, ','))) {
878 /* value without "...", terminated by ',' */
879 *(dkv
[i
].ptr
) = c
+ dkv
[i
].key_len
;
884 /* value without "...", terminated by EOL */
885 *(dkv
[i
].ptr
) = c
+ dkv
[i
].key_len
;
892 if (p
->conf
.auth_debug
> 1) {
893 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "username", username
);
894 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "realm", realm
);
895 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "nonce", nonce
);
896 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "uri", uri
);
897 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "algorithm", algorithm
);
898 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "qop", qop
);
899 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "cnonce", cnonce
);
900 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "nc", nc
);
901 log_error_write(srv
, __FILE__
, __LINE__
, "ss", "response", respons
);
904 /* check if everything is transmitted */
909 (qop
&& (!nc
|| !cnonce
)) ||
913 log_error_write(srv
, __FILE__
, __LINE__
, "s",
914 "digest: missing field");
921 * protect the md5-sess against missing cnonce and nonce
924 0 == strcasecmp(algorithm
, "md5-sess") &&
925 (!nonce
|| !cnonce
)) {
926 log_error_write(srv
, __FILE__
, __LINE__
, "s",
927 "digest: (md5-sess: missing field");
933 if (qop
&& strcasecmp(qop
, "auth-int") == 0) {
934 log_error_write(srv
, __FILE__
, __LINE__
, "s",
935 "digest: qop=auth-int not supported");
941 m
= get_http_method_name(con
->request
.http_method
);
944 /* password-string == HA1 */
945 password
= buffer_init();
946 username_buf
= buffer_init_string(username
);
947 realm_buf
= buffer_init_string(realm
);
948 if (http_auth_get_password(srv
, p
, username_buf
, realm_buf
, password
)) {
949 buffer_free(password
);
951 buffer_free(username_buf
);
952 buffer_free(realm_buf
);
956 buffer_free(username_buf
);
957 buffer_free(realm_buf
);
959 if (p
->conf
.auth_backend
== AUTH_BACKEND_PLAIN
) {
960 /* generate password from plain-text */
961 li_MD5_Init(&Md5Ctx
);
962 li_MD5_Update(&Md5Ctx
, (unsigned char *)username
, strlen(username
));
963 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
964 li_MD5_Update(&Md5Ctx
, (unsigned char *)realm
, strlen(realm
));
965 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
966 li_MD5_Update(&Md5Ctx
, CONST_BUF_LEN(password
));
967 li_MD5_Final(HA1
, &Md5Ctx
);
968 } else if (p
->conf
.auth_backend
== AUTH_BACKEND_HTDIGEST
) {
970 /* transform the 32-byte-hex-md5 to a 16-byte-md5 */
971 for (i
= 0; i
< HASHLEN
; i
++) {
972 HA1
[i
] = hex2int(password
->ptr
[i
*2]) << 4;
973 HA1
[i
] |= hex2int(password
->ptr
[i
*2+1]);
976 /* we already check that above */
980 buffer_free(password
);
983 strcasecmp(algorithm
, "md5-sess") == 0) {
984 li_MD5_Init(&Md5Ctx
);
985 /* Errata ID 1649: http://www.rfc-editor.org/errata_search.php?rfc=2617 */
987 li_MD5_Update(&Md5Ctx
, (unsigned char *)a1
, HASHHEXLEN
);
988 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
989 li_MD5_Update(&Md5Ctx
, (unsigned char *)nonce
, strlen(nonce
));
990 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
991 li_MD5_Update(&Md5Ctx
, (unsigned char *)cnonce
, strlen(cnonce
));
992 li_MD5_Final(HA1
, &Md5Ctx
);
997 /* calculate H(A2) */
998 li_MD5_Init(&Md5Ctx
);
999 li_MD5_Update(&Md5Ctx
, (unsigned char *)m
, strlen(m
));
1000 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
1001 li_MD5_Update(&Md5Ctx
, (unsigned char *)uri
, strlen(uri
));
1002 /* qop=auth-int not supported, already checked above */
1004 if (qop && strcasecmp(qop, "auth-int") == 0) {
1005 li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":"));
1006 li_MD5_Update(&Md5Ctx, (unsigned char *) [body checksum], HASHHEXLEN);
1009 li_MD5_Final(HA2
, &Md5Ctx
);
1010 CvtHex(HA2
, &HA2Hex
);
1012 /* calculate response */
1013 li_MD5_Init(&Md5Ctx
);
1014 li_MD5_Update(&Md5Ctx
, (unsigned char *)a1
, HASHHEXLEN
);
1015 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
1016 li_MD5_Update(&Md5Ctx
, (unsigned char *)nonce
, strlen(nonce
));
1017 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
1019 li_MD5_Update(&Md5Ctx
, (unsigned char *)nc
, strlen(nc
));
1020 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
1021 li_MD5_Update(&Md5Ctx
, (unsigned char *)cnonce
, strlen(cnonce
));
1022 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
1023 li_MD5_Update(&Md5Ctx
, (unsigned char *)qop
, strlen(qop
));
1024 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN(":"));
1026 li_MD5_Update(&Md5Ctx
, (unsigned char *)HA2Hex
, HASHHEXLEN
);
1027 li_MD5_Final(RespHash
, &Md5Ctx
);
1028 CvtHex(RespHash
, &a2
);
1030 if (0 != strcmp(a2
, respons
)) {
1033 if (p
->conf
.auth_debug
) {
1034 log_error_write(srv
, __FILE__
, __LINE__
, "sss",
1035 "digest: digest mismatch", a2
, respons
);
1038 log_error_write(srv
, __FILE__
, __LINE__
, "ssss",
1039 "digest: auth failed for ", username
, ": wrong password, IP:", inet_ntop_cache_get_ip(srv
, &(con
->dst_addr
)));
1045 /* value is our allow-rules */
1046 if (http_auth_match_rules(srv
, req
, username
, NULL
, NULL
)) {
1049 log_error_write(srv
, __FILE__
, __LINE__
, "s",
1050 "digest: rules did match");
1055 /* remember the username */
1056 buffer_copy_string(p
->auth_user
, username
);
1060 if (p
->conf
.auth_debug
) {
1061 log_error_write(srv
, __FILE__
, __LINE__
, "s",
1068 int http_auth_digest_generate_nonce(server
*srv
, mod_auth_plugin_data
*p
, buffer
*fn
, char (*out
)[33]) {
1071 char hh
[LI_ITOSTRING_LENGTH
];
1075 /* generate shared-secret */
1076 li_MD5_Init(&Md5Ctx
);
1077 li_MD5_Update(&Md5Ctx
, CONST_BUF_LEN(fn
));
1078 li_MD5_Update(&Md5Ctx
, CONST_STR_LEN("+"));
1080 /* we assume sizeof(time_t) == 4 here, but if not it ain't a problem at all */
1081 li_itostrn(hh
, sizeof(hh
), srv
->cur_ts
);
1082 li_MD5_Update(&Md5Ctx
, (unsigned char *)hh
, strlen(hh
));
1083 li_MD5_Update(&Md5Ctx
, (unsigned char *)srv
->entropy
, sizeof(srv
->entropy
));
1084 li_itostrn(hh
, sizeof(hh
), rand());
1085 li_MD5_Update(&Md5Ctx
, (unsigned char *)hh
, strlen(hh
));
1087 li_MD5_Final(h
, &Md5Ctx
);