7 #elif defined(__linux__)
8 /* linux needs _XOPEN_SOURCE */
12 #if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT)
13 /* always assume crypt() is present if we have -lcrypt */
17 #include "sys-crypto.h"
18 #ifdef USE_OPENSSL_CRYPTO
19 #include <openssl/md4.h>
22 #include "safe_memclear.h"
28 #include "http_auth.h"
31 #include "algo_sha1.h"
35 #include <sys/types.h>
46 * htdigest, htpasswd, plain auth backends
50 buffer
*auth_plain_groupfile
;
51 buffer
*auth_plain_userfile
;
52 buffer
*auth_htdigest_userfile
;
53 buffer
*auth_htpasswd_userfile
;
58 plugin_config
**config_storage
;
62 static handler_t
mod_authn_file_htdigest_digest(server
*srv
, connection
*con
, void *p_d
, http_auth_info_t
*ai
);
63 static handler_t
mod_authn_file_htdigest_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
);
64 static handler_t
mod_authn_file_plain_digest(server
*srv
, connection
*con
, void *p_d
, http_auth_info_t
*ai
);
65 static handler_t
mod_authn_file_plain_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
);
66 static handler_t
mod_authn_file_htpasswd_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
);
68 INIT_FUNC(mod_authn_file_init
) {
69 static http_auth_backend_t http_auth_backend_htdigest
=
70 { "htdigest", mod_authn_file_htdigest_basic
, mod_authn_file_htdigest_digest
, NULL
};
71 static http_auth_backend_t http_auth_backend_htpasswd
=
72 { "htpasswd", mod_authn_file_htpasswd_basic
, NULL
, NULL
};
73 static http_auth_backend_t http_auth_backend_plain
=
74 { "plain", mod_authn_file_plain_basic
, mod_authn_file_plain_digest
, NULL
};
75 plugin_data
*p
= calloc(1, sizeof(*p
));
77 /* register http_auth_backend_htdigest */
78 http_auth_backend_htdigest
.p_d
= p
;
79 http_auth_backend_set(&http_auth_backend_htdigest
);
81 /* register http_auth_backend_htpasswd */
82 http_auth_backend_htpasswd
.p_d
= p
;
83 http_auth_backend_set(&http_auth_backend_htpasswd
);
85 /* register http_auth_backend_plain */
86 http_auth_backend_plain
.p_d
= p
;
87 http_auth_backend_set(&http_auth_backend_plain
);
92 FREE_FUNC(mod_authn_file_free
) {
97 if (!p
) return HANDLER_GO_ON
;
99 if (p
->config_storage
) {
101 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
102 plugin_config
*s
= p
->config_storage
[i
];
104 if (NULL
== s
) continue;
106 buffer_free(s
->auth_plain_groupfile
);
107 buffer_free(s
->auth_plain_userfile
);
108 buffer_free(s
->auth_htdigest_userfile
);
109 buffer_free(s
->auth_htpasswd_userfile
);
113 free(p
->config_storage
);
118 return HANDLER_GO_ON
;
121 SETDEFAULTS_FUNC(mod_authn_file_set_defaults
) {
122 plugin_data
*p
= p_d
;
125 config_values_t cv
[] = {
126 { "auth.backend.plain.groupfile", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 0 */
127 { "auth.backend.plain.userfile", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 1 */
128 { "auth.backend.htdigest.userfile", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 2 */
129 { "auth.backend.htpasswd.userfile", NULL
, T_CONFIG_STRING
, T_CONFIG_SCOPE_CONNECTION
}, /* 3 */
130 { NULL
, NULL
, T_CONFIG_UNSET
, T_CONFIG_SCOPE_UNSET
}
133 p
->config_storage
= calloc(srv
->config_context
->used
, sizeof(plugin_config
*));
135 for (i
= 0; i
< srv
->config_context
->used
; i
++) {
136 data_config
const* config
= (data_config
const*)srv
->config_context
->data
[i
];
139 s
= calloc(1, sizeof(plugin_config
));
140 s
->auth_plain_groupfile
= buffer_init();
141 s
->auth_plain_userfile
= buffer_init();
142 s
->auth_htdigest_userfile
= buffer_init();
143 s
->auth_htpasswd_userfile
= buffer_init();
145 cv
[0].destination
= s
->auth_plain_groupfile
;
146 cv
[1].destination
= s
->auth_plain_userfile
;
147 cv
[2].destination
= s
->auth_htdigest_userfile
;
148 cv
[3].destination
= s
->auth_htpasswd_userfile
;
150 p
->config_storage
[i
] = s
;
152 if (0 != config_insert_values_global(srv
, config
->value
, cv
, i
== 0 ? T_CONFIG_SCOPE_SERVER
: T_CONFIG_SCOPE_CONNECTION
)) {
153 return HANDLER_ERROR
;
157 return HANDLER_GO_ON
;
162 static int mod_authn_file_patch_connection(server
*srv
, connection
*con
, plugin_data
*p
) {
164 plugin_config
*s
= p
->config_storage
[0];
166 PATCH(auth_plain_groupfile
);
167 PATCH(auth_plain_userfile
);
168 PATCH(auth_htdigest_userfile
);
169 PATCH(auth_htpasswd_userfile
);
171 /* skip the first, the global context */
172 for (i
= 1; i
< srv
->config_context
->used
; i
++) {
173 data_config
*dc
= (data_config
*)srv
->config_context
->data
[i
];
174 s
= p
->config_storage
[i
];
176 /* condition didn't match */
177 if (!config_check_cond(srv
, con
, dc
)) continue;
180 for (j
= 0; j
< dc
->value
->used
; j
++) {
181 data_unset
*du
= dc
->value
->data
[j
];
183 if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.plain.groupfile"))) {
184 PATCH(auth_plain_groupfile
);
185 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.plain.userfile"))) {
186 PATCH(auth_plain_userfile
);
187 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.htdigest.userfile"))) {
188 PATCH(auth_htdigest_userfile
);
189 } else if (buffer_is_equal_string(du
->key
, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) {
190 PATCH(auth_htpasswd_userfile
);
202 #ifdef USE_OPENSSL_CRYPTO
204 static void mod_authn_file_digest_sha256(http_auth_info_t
*ai
, const char *pw
, size_t pwlen
) {
207 SHA256_Update(&ctx
, (const unsigned char *)ai
->username
, ai
->ulen
);
208 SHA256_Update(&ctx
, CONST_STR_LEN(":"));
209 SHA256_Update(&ctx
, (const unsigned char *)ai
->realm
, ai
->rlen
);
210 SHA256_Update(&ctx
, CONST_STR_LEN(":"));
211 SHA256_Update(&ctx
, (const unsigned char *)pw
, pwlen
);
212 SHA256_Final(ai
->digest
, &ctx
);
215 #ifdef SHA512_256_DIGEST_LENGTH
216 static void mod_authn_file_digest_sha512_256(http_auth_info_t
*ai
, const char *pw
, size_t pwlen
) {
218 SHA512_256_Init(&ctx
);
219 SHA512_256_Update(&ctx
, (const unsigned char *)ai
->username
, ai
->ulen
);
220 SHA512_256_Update(&ctx
, CONST_STR_LEN(":"));
221 SHA512_256_Update(&ctx
, (const unsigned char *)ai
->realm
, ai
->rlen
);
222 SHA512_256_Update(&ctx
, CONST_STR_LEN(":"));
223 SHA512_256_Update(&ctx
, (const unsigned char *)pw
, pwlen
);
224 SHA512_256_Final(ai
->digest
, &ctx
);
230 static void mod_authn_file_digest_md5(http_auth_info_t
*ai
, const char *pw
, size_t pwlen
) {
233 li_MD5_Update(&ctx
, (const unsigned char *)ai
->username
, ai
->ulen
);
234 li_MD5_Update(&ctx
, CONST_STR_LEN(":"));
235 li_MD5_Update(&ctx
, (const unsigned char *)ai
->realm
, ai
->rlen
);
236 li_MD5_Update(&ctx
, CONST_STR_LEN(":"));
237 li_MD5_Update(&ctx
, (const unsigned char *)pw
, pwlen
);
238 li_MD5_Final(ai
->digest
, &ctx
);
241 static void mod_authn_file_digest(http_auth_info_t
*ai
, const char *pw
, size_t pwlen
) {
243 if (ai
->dalgo
& HTTP_AUTH_DIGEST_MD5
)
244 mod_authn_file_digest_md5(ai
, pw
, pwlen
);
245 #ifdef USE_OPENSSL_CRYPTO
246 else if (ai
->dalgo
& HTTP_AUTH_DIGEST_SHA256
)
247 mod_authn_file_digest_sha256(ai
, pw
, pwlen
);
248 #ifdef SHA512_256_DIGEST_LENGTH
249 else if (ai
->dalgo
& HTTP_AUTH_DIGEST_SHA512_256
)
250 mod_authn_file_digest_sha512_256(ai
, pw
, pwlen
);
258 static int mod_authn_file_htdigest_get_loop(server
*srv
, FILE *fp
, const buffer
*auth_fn
, http_auth_info_t
*ai
) {
261 while (NULL
!= fgets(f_user
, sizeof(f_user
), fp
)) {
262 char *f_pwd
, *f_realm
;
265 /* skip blank lines and comment lines (beginning '#') */
266 if (f_user
[0] == '#' || f_user
[0] == '\n' || f_user
[0] == '\0') continue;
271 * user:realm:md5(user:realm:password)
274 if (NULL
== (f_realm
= strchr(f_user
, ':'))) {
275 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
276 "parsed error in", auth_fn
,
277 "expected 'username:realm:hashed password'");
279 continue; /* skip bad lines */
282 if (NULL
== (f_pwd
= strchr(f_realm
+ 1, ':'))) {
283 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
284 "parsed error in", auth_fn
,
285 "expected 'username:realm:hashed password'");
287 continue; /* skip bad lines */
290 /* get pointers to the fields */
291 u_len
= f_realm
- f_user
;
293 r_len
= f_pwd
- f_realm
;
296 if (ai
->ulen
== u_len
&& ai
->rlen
== r_len
297 && 0 == memcmp(ai
->username
, f_user
, u_len
)
298 && 0 == memcmp(ai
->realm
, f_realm
, r_len
)) {
301 size_t pwd_len
= strlen(f_pwd
);
302 if (f_pwd
[pwd_len
-1] == '\n') --pwd_len
;
304 if (pwd_len
!= (ai
->dlen
<< 1)) continue;
305 return http_auth_digest_hex2bin(f_pwd
, pwd_len
,
306 ai
->digest
, sizeof(ai
->digest
));
313 static int mod_authn_file_htdigest_get(server
*srv
, connection
*con
, void *p_d
, http_auth_info_t
*ai
) {
314 plugin_data
*p
= (plugin_data
*)p_d
;
315 const buffer
*auth_fn
;
318 mod_authn_file_patch_connection(srv
, con
, p
);
319 auth_fn
= p
->conf
.auth_htdigest_userfile
;
320 if (buffer_string_is_empty(auth_fn
)) return -1;
322 fp
= fopen(auth_fn
->ptr
, "r");
324 int rc
= mod_authn_file_htdigest_get_loop(srv
, fp
, auth_fn
, ai
);
329 log_error_write(srv
, __FILE__
, __LINE__
, "sbss", "opening digest-userfile", auth_fn
, "failed:", strerror(errno
));
334 static handler_t
mod_authn_file_htdigest_digest(server
*srv
, connection
*con
, void *p_d
, http_auth_info_t
*ai
) {
335 return (0 == mod_authn_file_htdigest_get(srv
, con
, p_d
, ai
))
340 static handler_t
mod_authn_file_htdigest_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
) {
342 unsigned char htdigest
[sizeof(ai
.digest
)];
344 /* supports single choice of algorithm for digest stored in htdigest file */
345 ai
.dalgo
= (require
->algorithm
& ~HTTP_AUTH_DIGEST_SESS
);
346 ai
.dlen
= http_auth_digest_len(ai
.dalgo
);
347 ai
.username
= username
->ptr
;
348 ai
.ulen
= buffer_string_length(username
);
349 ai
.realm
= require
->realm
->ptr
;
350 ai
.rlen
= buffer_string_length(require
->realm
);
352 if (mod_authn_file_htdigest_get(srv
, con
, p_d
, &ai
)) return HANDLER_ERROR
;
354 if (ai
.dlen
> sizeof(htdigest
)) return HANDLER_ERROR
;/*(should not happen)*/
355 memcpy(htdigest
, ai
.digest
, ai
.dlen
); /*(save digest before reuse of ai)*/
357 mod_authn_file_digest(&ai
, pw
, strlen(pw
));
359 return (0 == memcmp(htdigest
, ai
.digest
, ai
.dlen
)
360 && http_auth_match_rules(require
, username
->ptr
, NULL
, NULL
))
368 static int mod_authn_file_htpasswd_get(server
*srv
, const buffer
*auth_fn
, const char *username
, size_t userlen
, buffer
*password
) {
372 if (NULL
== username
) return -1;
374 if (buffer_string_is_empty(auth_fn
)) return -1;
375 fp
= fopen(auth_fn
->ptr
, "r");
377 log_error_write(srv
, __FILE__
, __LINE__
, "sbss",
378 "opening plain-userfile", auth_fn
, "failed:", strerror(errno
));
383 while (NULL
!= fgets(f_user
, sizeof(f_user
), fp
)) {
387 /* skip blank lines and comment lines (beginning '#') */
388 if (f_user
[0] == '#' || f_user
[0] == '\n' || f_user
[0] == '\0') continue;
393 * user:crypted passwd
396 if (NULL
== (f_pwd
= strchr(f_user
, ':'))) {
397 log_error_write(srv
, __FILE__
, __LINE__
, "sbs",
398 "parsed error in", auth_fn
,
399 "expected 'username:hashed password'");
401 continue; /* skip bad lines */
404 /* get pointers to the fields */
405 u_len
= f_pwd
- f_user
;
408 if (userlen
== u_len
&& 0 == memcmp(username
, f_user
, u_len
)) {
411 size_t pwd_len
= strlen(f_pwd
);
412 if (f_pwd
[pwd_len
-1] == '\n') --pwd_len
;
414 buffer_copy_string_len(password
, f_pwd
, pwd_len
);
425 static handler_t
mod_authn_file_plain_digest(server
*srv
, connection
*con
, void *p_d
, http_auth_info_t
*ai
) {
426 plugin_data
*p
= (plugin_data
*)p_d
;
427 buffer
*password_buf
= buffer_init();/* password-string from auth-backend */
429 mod_authn_file_patch_connection(srv
, con
, p
);
430 rc
= mod_authn_file_htpasswd_get(srv
, p
->conf
.auth_plain_userfile
, ai
->username
, ai
->ulen
, password_buf
);
432 /* generate password from plain-text */
433 mod_authn_file_digest(ai
, CONST_BUF_LEN(password_buf
));
435 buffer_free(password_buf
);
436 return (0 == rc
) ? HANDLER_GO_ON
: HANDLER_ERROR
;
439 static handler_t
mod_authn_file_plain_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
) {
440 plugin_data
*p
= (plugin_data
*)p_d
;
441 buffer
*password_buf
= buffer_init();/* password-string from auth-backend */
443 mod_authn_file_patch_connection(srv
, con
, p
);
444 rc
= mod_authn_file_htpasswd_get(srv
, p
->conf
.auth_plain_userfile
, CONST_BUF_LEN(username
), password_buf
);
446 rc
= http_auth_const_time_memeq(CONST_BUF_LEN(password_buf
), pw
, strlen(pw
)) ? 0 : -1;
448 buffer_free(password_buf
);
449 return 0 == rc
&& http_auth_match_rules(require
, username
->ptr
, NULL
, NULL
)
458 * the $apr1$ handling is taken from apache 1.3.x
462 * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
463 * MD5 crypt() function, which is licenced as follows:
464 * ----------------------------------------------------------------------------
465 * "THE BEER-WARE LICENSE" (Revision 42):
466 * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
467 * can do whatever you want with this stuff. If we meet some day, and you think
468 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
469 * ----------------------------------------------------------------------------
472 #define APR_MD5_DIGESTSIZE 16
473 #define APR1_ID "$apr1$"
476 * The following MD5 password encryption code was largely borrowed from
477 * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
478 * licenced as stated above.
481 static void to64(char *s
, unsigned long v
, int n
)
483 static const unsigned char itoa64
[] = /* 0 ... 63 => ASCII - 64 */
484 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
487 *s
++ = itoa64
[v
&0x3f];
492 static void apr_md5_encode(const char *pw
, const char *salt
, char *result
, size_t nbytes
) {
494 * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
495 * plus 4 for the '$' separators, plus the password hash itself.
496 * Let's leave a goodly amount of leeway.
499 char passwd
[120], *p
;
501 unsigned char final
[APR_MD5_DIGESTSIZE
];
503 li_MD5_CTX ctx
, ctx1
;
507 * Refine the salt first. It's possible we were given an already-hashed
508 * string as the salt argument, so extract the actual salt value from it
509 * if so. Otherwise just use the string up to the first '$' as the salt.
514 * If it starts with the magic string, then skip that.
516 if (!strncmp(sp
, APR1_ID
, strlen(APR1_ID
))) {
517 sp
+= strlen(APR1_ID
);
521 * It stops at the first '$' or 8 chars, whichever comes first
523 for (ep
= sp
; (*ep
!= '\0') && (*ep
!= '$') && (ep
< (sp
+ 8)); ep
++) {
528 * Get the length of the true salt
533 * 'Time to make the doughnuts..'
538 * The password first, since that is what is most unknown
540 li_MD5_Update(&ctx
, pw
, strlen(pw
));
543 * Then our magic string
545 li_MD5_Update(&ctx
, APR1_ID
, strlen(APR1_ID
));
550 li_MD5_Update(&ctx
, sp
, sl
);
553 * Then just as many characters of the MD5(pw, salt, pw)
556 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
557 li_MD5_Update(&ctx1
, sp
, sl
);
558 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
559 li_MD5_Final(final
, &ctx1
);
560 for (pl
= strlen(pw
); pl
> 0; pl
-= APR_MD5_DIGESTSIZE
) {
561 li_MD5_Update(&ctx
, final
,
562 (pl
> APR_MD5_DIGESTSIZE
) ? APR_MD5_DIGESTSIZE
: pl
);
566 * Don't leave anything around in vm they could use.
568 memset(final
, 0, sizeof(final
));
571 * Then something really weird...
573 for (i
= strlen(pw
); i
!= 0; i
>>= 1) {
575 li_MD5_Update(&ctx
, final
, 1);
578 li_MD5_Update(&ctx
, pw
, 1);
583 * Now make the output string. We know our limitations, so we
584 * can use the string routines without bounds checking.
586 strcpy(passwd
, APR1_ID
);
587 strncat(passwd
, sp
, sl
);
590 li_MD5_Final(final
, &ctx
);
593 * And now, just to make sure things don't run too fast..
594 * On a 60 Mhz Pentium this takes 34 msec, so you would
595 * need 30 seconds to build a 1000 entry dictionary...
597 for (i
= 0; i
< 1000; i
++) {
600 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
603 li_MD5_Update(&ctx1
, final
, APR_MD5_DIGESTSIZE
);
606 li_MD5_Update(&ctx1
, sp
, sl
);
610 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
614 li_MD5_Update(&ctx1
, final
, APR_MD5_DIGESTSIZE
);
617 li_MD5_Update(&ctx1
, pw
, strlen(pw
));
619 li_MD5_Final(final
,&ctx1
);
622 p
= passwd
+ strlen(passwd
);
624 l
= (final
[ 0]<<16) | (final
[ 6]<<8) | final
[12]; to64(p
, l
, 4); p
+= 4;
625 l
= (final
[ 1]<<16) | (final
[ 7]<<8) | final
[13]; to64(p
, l
, 4); p
+= 4;
626 l
= (final
[ 2]<<16) | (final
[ 8]<<8) | final
[14]; to64(p
, l
, 4); p
+= 4;
627 l
= (final
[ 3]<<16) | (final
[ 9]<<8) | final
[15]; to64(p
, l
, 4); p
+= 4;
628 l
= (final
[ 4]<<16) | (final
[10]<<8) | final
[ 5]; to64(p
, l
, 4); p
+= 4;
629 l
= final
[11] ; to64(p
, l
, 2); p
+= 2;
633 * Don't leave anything around in vm they could use.
635 safe_memclear(final
, sizeof(final
));
639 #define apr_cpystrn strncpy
640 apr_cpystrn(result
, passwd
, nbytes
- 1);
643 static void apr_sha_encode(const char *pw
, char *result
, size_t nbytes
) {
644 unsigned char digest
[20];
645 size_t base64_written
;
649 SHA1_Update(&sha1
, (const unsigned char *) pw
, strlen(pw
));
650 SHA1_Final(digest
, &sha1
);
652 memset(result
, 0, nbytes
);
654 /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
655 if (nbytes
< 5 + 28 + 1) return;
657 memcpy(result
, "{SHA}", 5);
658 base64_written
= li_to_base64(result
+ 5, nbytes
- 5, digest
, 20, BASE64_STANDARD
);
659 force_assert(base64_written
== 28);
660 result
[5 + base64_written
] = '\0'; /* terminate string */
663 static handler_t
mod_authn_file_htpasswd_basic(server
*srv
, connection
*con
, void *p_d
, const http_auth_require_t
*require
, const buffer
*username
, const char *pw
) {
664 plugin_data
*p
= (plugin_data
*)p_d
;
665 buffer
*password
= buffer_init();/* password-string from auth-backend */
667 mod_authn_file_patch_connection(srv
, con
, p
);
668 rc
= mod_authn_file_htpasswd_get(srv
, p
->conf
.auth_htpasswd_userfile
, CONST_BUF_LEN(username
), password
);
672 if (!strncmp(password
->ptr
, APR1_ID
, strlen(APR1_ID
))) {
674 * The hash was created using $apr1$ custom algorithm.
676 apr_md5_encode(pw
, password
->ptr
, sample
, sizeof(sample
));
677 rc
= strcmp(sample
, password
->ptr
);
679 else if (0 == strncmp(password
->ptr
, "{SHA}", 5)) {
680 apr_sha_encode(pw
, sample
, sizeof(sample
));
681 rc
= strcmp(sample
, password
->ptr
);
683 #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)
684 /* a simple DES password is 2 + 11 characters. everything else should be longer. */
685 else if (buffer_string_length(password
) >= 13) {
687 #if defined(HAVE_CRYPT_R)
688 struct crypt_data crypt_tmp_data
;
690 memset(&crypt_tmp_data
, 0, sizeof(crypt_tmp_data
));
692 crypt_tmp_data
.initialized
= 0;
695 #ifdef USE_OPENSSL_CRYPTO /* (for MD4_*() (e.g. MD4_Update())) */
696 #ifndef NO_MD4 /*(e.g. wolfSSL built without MD4)*/
697 if (0 == memcmp(password
->ptr
, CONST_STR_LEN("$1+ntlm$"))) {
698 /* CRYPT-MD5-NTLM algorithm
699 * This algorithm allows for the construction of (slight more)
700 * secure, salted password hashes from an environment where only
701 * legacy NTLM hashes are available and where it is not feasible
702 * to re-hash all the passwords with the MD5-based crypt(). */
703 /* Note: originally, LM password were limited to 14 chars.
704 * NTLM passwords limited to 127 chars, and encoding to UCS-2LE
705 * requires double that, so sample[256] buf is large enough.
706 * Prior sample[120] size likely taken from apr_md5_encode(). */
707 char *b
= password
->ptr
+sizeof("$1+ntlm$")-1;
708 char *e
= strchr(b
, '$');
709 size_t slen
= (NULL
!= e
) ? (size_t)(e
- b
) : sizeof(sample
);
710 size_t pwlen
= strlen(pw
) * 2;
711 if (slen
< sizeof(sample
) - (sizeof("$1$")-1)
712 && pwlen
< sizeof(sample
)) {
713 /* compute NTLM hash and convert to lowercase hex chars
714 * (require lc hex chars from li_tohex()) */
716 char ntlmhex
[33]; /*(sizeof(ntlmhash)*2 + 1)*/
720 /*(reuse sample buffer to encode pw into UCS-2LE)
721 *(Note: assumes pw input in ISO-8859-1) */
722 /*(buffer sizes checked above)*/
723 for (int i
=0; i
< (int)pwlen
; i
+=2) {
724 sample
[i
] = pw
[(i
>> 1)];
727 MD4_Update(&c
, (unsigned char *)sample
, pwlen
);
729 MD4_Final((unsigned char *)ntlmhash
, &c
);
730 li_tohex(ntlmhex
,sizeof(ntlmhex
),ntlmhash
,sizeof(ntlmhash
));
732 /*(reuse sample buffer for salt (FYI: expect slen == 8))*/
733 memcpy(sample
, "$1$", sizeof("$1$")-1);
734 memcpy(sample
+sizeof("$1$")-1, b
, slen
);
735 sample
[sizeof("$1$")-1+slen
] = '\0';
736 #if defined(HAVE_CRYPT_R)
737 crypted
= crypt_r(ntlmhex
, sample
, &crypt_tmp_data
);
739 crypted
= crypt(ntlmhex
, sample
);
742 && 0 == strncmp(crypted
, "$1$", sizeof("$1$")-1)) {
743 rc
= strcmp(b
, crypted
+3); /*skip crypted "$1$" prefix*/
751 #if defined(HAVE_CRYPT_R)
752 crypted
= crypt_r(pw
, password
->ptr
, &crypt_tmp_data
);
754 crypted
= crypt(pw
, password
->ptr
);
756 if (NULL
!= crypted
) {
757 rc
= strcmp(password
->ptr
, crypted
);
763 buffer_free(password
);
764 return 0 == rc
&& http_auth_match_rules(require
, username
->ptr
, NULL
, NULL
)
770 int mod_authn_file_plugin_init(plugin
*p
);
771 int mod_authn_file_plugin_init(plugin
*p
) {
772 p
->version
= LIGHTTPD_VERSION_ID
;
773 p
->name
= buffer_init_string("authn_file");
774 p
->init
= mod_authn_file_init
;
775 p
->set_defaults
= mod_authn_file_set_defaults
;
776 p
->cleanup
= mod_authn_file_free
;