xref: /lighttpd1.4/src/mod_authn_file.c (revision 9570cc2f)
1 #include "first.h"
2 
3 
4 /*(htpasswd)*/
5 #ifdef HAVE_CRYPT_H
6 # include <crypt.h>
7 #elif defined(__linux__)
8 /* linux needs _XOPEN_SOURCE */
9 # define _XOPEN_SOURCE
10 #endif
11 
12 #if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT)
13 /* always assume crypt() is present if we have -lcrypt */
14 # define HAVE_CRYPT
15 #endif
16 
17 #include "sys-crypto-md.h" /* USE_LIB_CRYPTO */
18 
19 #include "safe_memclear.h"
20 
21 #include "base.h"
22 #include "plugin.h"
23 #include "http_auth.h"
24 #include "log.h"
25 
26 #include "base64.h"
27 
28 #include <sys/types.h>
29 
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 
36 /*
37  * htdigest, htpasswd, plain auth backends
38  */
39 
40 typedef struct {
41     const buffer *auth_plain_groupfile;
42     const buffer *auth_plain_userfile;
43     const buffer *auth_htdigest_userfile;
44     const buffer *auth_htpasswd_userfile;
45 } plugin_config;
46 
47 typedef struct {
48     PLUGIN_DATA;
49     plugin_config defaults;
50     plugin_config conf;
51 } plugin_data;
52 
53 static handler_t mod_authn_file_htdigest_digest(request_st *r, void *p_d, http_auth_info_t *ai);
54 static handler_t mod_authn_file_htdigest_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw);
55 static handler_t mod_authn_file_plain_digest(request_st *r, void *p_d, http_auth_info_t *ai);
56 static handler_t mod_authn_file_plain_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw);
57 static handler_t mod_authn_file_htpasswd_basic(request_st *r, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw);
58 
59 INIT_FUNC(mod_authn_file_init) {
60     static http_auth_backend_t http_auth_backend_htdigest =
61       { "htdigest", mod_authn_file_htdigest_basic, mod_authn_file_htdigest_digest, NULL };
62     static http_auth_backend_t http_auth_backend_htpasswd =
63       { "htpasswd", mod_authn_file_htpasswd_basic, NULL, NULL };
64     static http_auth_backend_t http_auth_backend_plain =
65       { "plain", mod_authn_file_plain_basic, mod_authn_file_plain_digest, NULL };
66     plugin_data *p = calloc(1, sizeof(*p));
67 
68     /* register http_auth_backend_htdigest */
69     http_auth_backend_htdigest.p_d = p;
70     http_auth_backend_set(&http_auth_backend_htdigest);
71 
72     /* register http_auth_backend_htpasswd */
73     http_auth_backend_htpasswd.p_d = p;
74     http_auth_backend_set(&http_auth_backend_htpasswd);
75 
76     /* register http_auth_backend_plain */
77     http_auth_backend_plain.p_d = p;
78     http_auth_backend_set(&http_auth_backend_plain);
79 
80     return p;
81 }
82 
83 static void mod_authn_file_merge_config_cpv(plugin_config * const pconf, const config_plugin_value_t * const cpv) {
84     switch (cpv->k_id) { /* index into static config_plugin_keys_t cpk[] */
85       case 0: /* auth.backend.plain.groupfile */
86         pconf->auth_plain_groupfile = cpv->v.b;
87         break;
88       case 1: /* auth.backend.plain.userfile */
89         pconf->auth_plain_userfile = cpv->v.b;
90         break;
91       case 2: /* auth.backend.htdigest.userfile */
92         pconf->auth_htdigest_userfile = cpv->v.b;
93         break;
94       case 3: /* auth.backend.htpasswd.userfile */
95         pconf->auth_htpasswd_userfile = cpv->v.b;
96         break;
97       default:/* should not happen */
98         return;
99     }
100 }
101 
102 static void mod_authn_file_merge_config(plugin_config * const pconf, const config_plugin_value_t *cpv) {
103     do {
104         mod_authn_file_merge_config_cpv(pconf, cpv);
105     } while ((++cpv)->k_id != -1);
106 }
107 
108 static void mod_authn_file_patch_config(request_st * const r, plugin_data * const p) {
109     p->conf = p->defaults; /* copy small struct instead of memcpy() */
110     /*memcpy(&p->conf, &p->defaults, sizeof(plugin_config));*/
111     for (int i = 1, used = p->nconfig; i < used; ++i) {
112         if (config_check_cond(r, (uint32_t)p->cvlist[i].k_id))
113             mod_authn_file_merge_config(&p->conf,
114                                         p->cvlist + p->cvlist[i].v.u2[0]);
115     }
116 }
117 
118 SETDEFAULTS_FUNC(mod_authn_file_set_defaults) {
119     static const config_plugin_keys_t cpk[] = {
120       { CONST_STR_LEN("auth.backend.plain.groupfile"),
121         T_CONFIG_STRING,
122         T_CONFIG_SCOPE_CONNECTION }
123      ,{ CONST_STR_LEN("auth.backend.plain.userfile"),
124         T_CONFIG_STRING,
125         T_CONFIG_SCOPE_CONNECTION }
126      ,{ CONST_STR_LEN("auth.backend.htdigest.userfile"),
127         T_CONFIG_STRING,
128         T_CONFIG_SCOPE_CONNECTION }
129      ,{ CONST_STR_LEN("auth.backend.htpasswd.userfile"),
130         T_CONFIG_STRING,
131         T_CONFIG_SCOPE_CONNECTION }
132      ,{ NULL, 0,
133         T_CONFIG_UNSET,
134         T_CONFIG_SCOPE_UNSET }
135     };
136 
137     plugin_data * const p = p_d;
138     if (!config_plugin_values_init(srv, p, cpk, "mod_authn_file"))
139         return HANDLER_ERROR;
140 
141     /* initialize p->defaults from global config context */
142     if (p->nconfig > 0 && p->cvlist->v.u2[1]) {
143         const config_plugin_value_t *cpv = p->cvlist + p->cvlist->v.u2[0];
144         if (-1 != cpv->k_id)
145             mod_authn_file_merge_config(&p->defaults, cpv);
146     }
147 
148     return HANDLER_GO_ON;
149 }
150 
151 
152 
153 
154 #ifdef USE_LIB_CRYPTO
155 
156 static void mod_authn_file_digest_sha256(http_auth_info_t *ai, const char *pw, size_t pwlen) {
157     SHA256_CTX ctx;
158     SHA256_Init(&ctx);
159     SHA256_Update(&ctx, (const unsigned char *)ai->username, ai->ulen);
160     SHA256_Update(&ctx, CONST_STR_LEN(":"));
161     SHA256_Update(&ctx, (const unsigned char *)ai->realm, ai->rlen);
162     SHA256_Update(&ctx, CONST_STR_LEN(":"));
163     SHA256_Update(&ctx, (const unsigned char *)pw, pwlen);
164     SHA256_Final(ai->digest, &ctx);
165 }
166 
167 #ifdef USE_LIB_CRYPTO_SHA512_256
168 static void mod_authn_file_digest_sha512_256(http_auth_info_t *ai, const char *pw, size_t pwlen) {
169     SHA512_CTX ctx;
170     SHA512_256_Init(&ctx);
171     SHA512_256_Update(&ctx, (const unsigned char *)ai->username, ai->ulen);
172     SHA512_256_Update(&ctx, CONST_STR_LEN(":"));
173     SHA512_256_Update(&ctx, (const unsigned char *)ai->realm, ai->rlen);
174     SHA512_256_Update(&ctx, CONST_STR_LEN(":"));
175     SHA512_256_Update(&ctx, (const unsigned char *)pw, pwlen);
176     SHA512_256_Final(ai->digest, &ctx);
177 }
178 #endif
179 
180 #endif
181 
182 static void mod_authn_file_digest_md5(http_auth_info_t *ai, const char *pw, size_t pwlen) {
183     li_MD5_CTX ctx;
184     li_MD5_Init(&ctx);
185     li_MD5_Update(&ctx, (const unsigned char *)ai->username, ai->ulen);
186     li_MD5_Update(&ctx, CONST_STR_LEN(":"));
187     li_MD5_Update(&ctx, (const unsigned char *)ai->realm, ai->rlen);
188     li_MD5_Update(&ctx, CONST_STR_LEN(":"));
189     li_MD5_Update(&ctx, (const unsigned char *)pw, pwlen);
190     li_MD5_Final(ai->digest, &ctx);
191 }
192 
193 static void mod_authn_file_digest(http_auth_info_t *ai, const char *pw, size_t pwlen) {
194 
195     if (ai->dalgo & HTTP_AUTH_DIGEST_MD5)
196         mod_authn_file_digest_md5(ai, pw, pwlen);
197   #ifdef USE_LIB_CRYPTO
198     else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA256)
199         mod_authn_file_digest_sha256(ai, pw, pwlen);
200    #ifdef USE_LIB_CRYPTO_SHA512_256
201     else if (ai->dalgo & HTTP_AUTH_DIGEST_SHA512_256)
202         mod_authn_file_digest_sha512_256(ai, pw, pwlen);
203    #endif
204   #endif
205 }
206 
207 
208 
209 
210 static int mod_authn_file_htdigest_get_loop(FILE *fp, const buffer *auth_fn, http_auth_info_t *ai, log_error_st *errh) {
211     char f_user[1024];
212 
213     while (NULL != fgets(f_user, sizeof(f_user), fp)) {
214         char *f_pwd, *f_realm;
215         size_t u_len, r_len;
216 
217         /* skip blank lines and comment lines (beginning '#') */
218         if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
219 
220         /*
221          * htdigest format
222          *
223          * user:realm:md5(user:realm:password)
224          */
225 
226         if (NULL == (f_realm = strchr(f_user, ':'))
227             || NULL == (f_pwd = strchr(f_realm + 1, ':'))) {
228             log_error(errh, __FILE__, __LINE__,
229               "parsed error in %s expected 'username:realm:hashed password'",
230               auth_fn->ptr);
231             continue; /* skip bad lines */
232         }
233 
234         /* get pointers to the fields */
235         u_len = f_realm - f_user;
236         f_realm++;
237         r_len = f_pwd - f_realm;
238         f_pwd++;
239 
240         if (ai->ulen == u_len && ai->rlen == r_len
241             && 0 == memcmp(ai->username, f_user, u_len)
242             && 0 == memcmp(ai->realm, f_realm, r_len)) {
243             /* found */
244 
245             size_t pwd_len = strlen(f_pwd);
246             if (f_pwd[pwd_len-1] == '\n') --pwd_len;
247 
248             if (pwd_len != (ai->dlen << 1)) continue;
249             return http_auth_digest_hex2bin(f_pwd, pwd_len,
250                                             ai->digest, sizeof(ai->digest));
251         }
252     }
253 
254     return -1;
255 }
256 
257 static int mod_authn_file_htdigest_get(request_st * const r, void *p_d, http_auth_info_t * const ai) {
258     plugin_data *p = (plugin_data *)p_d;
259     const buffer *auth_fn;
260     FILE *fp;
261 
262     mod_authn_file_patch_config(r, p);
263     auth_fn = p->conf.auth_htdigest_userfile;
264     if (buffer_string_is_empty(auth_fn)) return -1;
265 
266     fp = fopen(auth_fn->ptr, "r");
267     if (NULL != fp) {
268         int rc = mod_authn_file_htdigest_get_loop(fp,auth_fn,ai,r->conf.errh);
269         fclose(fp);
270         return rc;
271     }
272     else {
273         log_perror(r->conf.errh, __FILE__, __LINE__,
274           "opening digest-userfile %s", auth_fn->ptr);
275         return -1;
276     }
277 }
278 
279 static handler_t mod_authn_file_htdigest_digest(request_st * const r, void *p_d, http_auth_info_t * const ai) {
280     return (0 == mod_authn_file_htdigest_get(r, p_d, ai))
281       ? HANDLER_GO_ON
282       : HANDLER_ERROR;
283 }
284 
285 static handler_t mod_authn_file_htdigest_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) {
286     http_auth_info_t ai;
287     unsigned char htdigest[sizeof(ai.digest)];
288 
289     /* supports single choice of algorithm for digest stored in htdigest file */
290     ai.dalgo    = (require->algorithm & ~HTTP_AUTH_DIGEST_SESS);
291     ai.dlen     = http_auth_digest_len(ai.dalgo);
292     ai.username = username->ptr;
293     ai.ulen     = buffer_string_length(username);
294     ai.realm    = require->realm->ptr;
295     ai.rlen     = buffer_string_length(require->realm);
296 
297     if (mod_authn_file_htdigest_get(r, p_d, &ai)) return HANDLER_ERROR;
298 
299     if (ai.dlen > sizeof(htdigest)) return HANDLER_ERROR;/*(should not happen)*/
300     memcpy(htdigest, ai.digest, ai.dlen); /*(save digest before reuse of ai)*/
301 
302     mod_authn_file_digest(&ai, pw, strlen(pw));
303 
304     return (http_auth_const_time_memeq(htdigest, ai.digest, ai.dlen)
305             && http_auth_match_rules(require, username->ptr, NULL, NULL))
306       ? HANDLER_GO_ON
307       : HANDLER_ERROR;
308 }
309 
310 
311 
312 
313 static int mod_authn_file_htpasswd_get(const buffer *auth_fn, const char *username, size_t userlen, buffer *password, log_error_st *errh) {
314     FILE *fp;
315     char f_user[1024];
316 
317     if (NULL == username) return -1;
318 
319     if (buffer_string_is_empty(auth_fn)) return -1;
320     fp = fopen(auth_fn->ptr, "r");
321     if (NULL == fp) {
322         log_perror(errh, __FILE__, __LINE__,
323                 "opening plain-userfile %s", auth_fn->ptr);
324 
325         return -1;
326     }
327 
328     while (NULL != fgets(f_user, sizeof(f_user), fp)) {
329         char *f_pwd;
330         size_t u_len;
331 
332         /* skip blank lines and comment lines (beginning '#') */
333         if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
334 
335         /*
336          * htpasswd format
337          *
338          * user:crypted passwd
339          */
340 
341         if (NULL == (f_pwd = strchr(f_user, ':'))) {
342             log_error(errh, __FILE__, __LINE__,
343               "parsed error in %s expected 'username:hashed password'",
344               auth_fn->ptr);
345             continue; /* skip bad lines */
346         }
347 
348         /* get pointers to the fields */
349         u_len = f_pwd - f_user;
350         f_pwd++;
351 
352         if (userlen == u_len && 0 == memcmp(username, f_user, u_len)) {
353             /* found */
354 
355             size_t pwd_len = strlen(f_pwd);
356             if (f_pwd[pwd_len-1] == '\n') --pwd_len;
357 
358             buffer_copy_string_len(password, f_pwd, pwd_len);
359 
360             fclose(fp);
361             return 0;
362         }
363     }
364 
365     fclose(fp);
366     return -1;
367 }
368 
369 static handler_t mod_authn_file_plain_digest(request_st * const r, void *p_d, http_auth_info_t * const ai) {
370     plugin_data *p = (plugin_data *)p_d;
371     buffer *password_buf = buffer_init();/* password-string from auth-backend */
372     int rc;
373     mod_authn_file_patch_config(r, p);
374     rc = mod_authn_file_htpasswd_get(p->conf.auth_plain_userfile, ai->username, ai->ulen, password_buf, r->conf.errh);
375     if (0 == rc) {
376         /* generate password from plain-text */
377         mod_authn_file_digest(ai, CONST_BUF_LEN(password_buf));
378     }
379     buffer_free(password_buf);
380     return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
381 }
382 
383 static handler_t mod_authn_file_plain_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) {
384     plugin_data *p = (plugin_data *)p_d;
385     buffer *password_buf = buffer_init();/* password-string from auth-backend */
386     int rc;
387     mod_authn_file_patch_config(r, p);
388     rc = mod_authn_file_htpasswd_get(p->conf.auth_plain_userfile, CONST_BUF_LEN(username), password_buf, r->conf.errh);
389     if (0 == rc) {
390         rc = http_auth_const_time_memeq_pad(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1;
391     }
392     buffer_free(password_buf);
393     return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL)
394       ? HANDLER_GO_ON
395       : HANDLER_ERROR;
396 }
397 
398 
399 
400 
401 /**
402  * the $apr1$ handling is taken from apache 1.3.x
403  */
404 
405 /*
406  * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
407  * MD5 crypt() function, which is licenced as follows:
408  * ----------------------------------------------------------------------------
409  * "THE BEER-WARE LICENSE" (Revision 42):
410  * <[email protected]> wrote this file.  As long as you retain this notice you
411  * can do whatever you want with this stuff. If we meet some day, and you think
412  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
413  * ----------------------------------------------------------------------------
414  */
415 
416 #define APR_MD5_DIGESTSIZE 16
417 #define APR1_ID "$apr1$"
418 
419 /*
420  * The following MD5 password encryption code was largely borrowed from
421  * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is
422  * licenced as stated above.
423  */
424 
425 static void to64(char *s, unsigned long v, int n)
426 {
427     static const unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
428         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
429 
430     while (--n >= 0) {
431         *s++ = itoa64[v&0x3f];
432         v >>= 6;
433     }
434 }
435 
436 static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
437     /*
438      * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL,
439      * plus 4 for the '$' separators, plus the password hash itself.
440      * Let's leave a goodly amount of leeway.
441      */
442 
443     char passwd[120], *p;
444     const char *sp, *ep;
445     unsigned char final[APR_MD5_DIGESTSIZE];
446     ssize_t sl, pl, i;
447     li_MD5_CTX ctx, ctx1;
448     unsigned long l;
449 
450     /*
451      * Refine the salt first.  It's possible we were given an already-hashed
452      * string as the salt argument, so extract the actual salt value from it
453      * if so.  Otherwise just use the string up to the first '$' as the salt.
454      */
455     sp = salt;
456 
457     /*
458      * If it starts with the magic string, then skip that.
459      */
460     if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
461         sp += strlen(APR1_ID);
462     }
463 
464     /*
465      * It stops at the first '$' or 8 chars, whichever comes first
466      */
467     for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
468         continue;
469     }
470 
471     /*
472      * Get the length of the true salt
473      */
474     sl = ep - sp;
475 
476     /*
477      * 'Time to make the doughnuts..'
478      */
479     li_MD5_Init(&ctx);
480 
481     /*
482      * The password first, since that is what is most unknown
483      */
484     li_MD5_Update(&ctx, pw, strlen(pw));
485 
486     /*
487      * Then our magic string
488      */
489     li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
490 
491     /*
492      * Then the raw salt
493      */
494     li_MD5_Update(&ctx, sp, sl);
495 
496     /*
497      * Then just as many characters of the MD5(pw, salt, pw)
498      */
499     li_MD5_Init(&ctx1);
500     li_MD5_Update(&ctx1, pw, strlen(pw));
501     li_MD5_Update(&ctx1, sp, sl);
502     li_MD5_Update(&ctx1, pw, strlen(pw));
503     li_MD5_Final(final, &ctx1);
504     for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) {
505         li_MD5_Update(&ctx, final,
506                       (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl);
507     }
508 
509     /*
510      * Don't leave anything around in vm they could use.
511      */
512     memset(final, 0, sizeof(final));
513 
514     /*
515      * Then something really weird...
516      */
517     for (i = strlen(pw); i != 0; i >>= 1) {
518         if (i & 1) {
519             li_MD5_Update(&ctx, final, 1);
520         }
521         else {
522             li_MD5_Update(&ctx, pw, 1);
523         }
524     }
525 
526     /*
527      * Now make the output string.  We know our limitations, so we
528      * can use the string routines without bounds checking.
529      */
530     strcpy(passwd, APR1_ID);
531     strncat(passwd, sp, sl);
532     strcat(passwd, "$");
533 
534     li_MD5_Final(final, &ctx);
535 
536     /*
537      * And now, just to make sure things don't run too fast..
538      * On a 60 Mhz Pentium this takes 34 msec, so you would
539      * need 30 seconds to build a 1000 entry dictionary...
540      */
541     for (i = 0; i < 1000; i++) {
542         li_MD5_Init(&ctx1);
543         if (i & 1) {
544             li_MD5_Update(&ctx1, pw, strlen(pw));
545         }
546         else {
547             li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
548         }
549         if (i % 3) {
550             li_MD5_Update(&ctx1, sp, sl);
551         }
552 
553         if (i % 7) {
554             li_MD5_Update(&ctx1, pw, strlen(pw));
555         }
556 
557         if (i & 1) {
558             li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
559         }
560         else {
561             li_MD5_Update(&ctx1, pw, strlen(pw));
562         }
563         li_MD5_Final(final,&ctx1);
564     }
565 
566     p = passwd + strlen(passwd);
567 
568     l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4;
569     l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4;
570     l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4;
571     l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4;
572     l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4;
573     l =                    final[11]                ; to64(p, l, 2); p += 2;
574     *p = '\0';
575 
576     /*
577      * Don't leave anything around in vm they could use.
578      */
579     safe_memclear(final, sizeof(final));
580 
581     /* FIXME
582      */
583 #define apr_cpystrn strncpy
584     apr_cpystrn(result, passwd, nbytes - 1);
585 }
586 
587 static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
588     unsigned char digest[20];
589     size_t base64_written;
590     SHA_CTX sha1;
591 
592     SHA1_Init(&sha1);
593     SHA1_Update(&sha1, (const unsigned char *) pw, strlen(pw));
594     SHA1_Final(digest, &sha1);
595 
596     memset(result, 0, nbytes);
597 
598     /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */
599     if (nbytes < 5 + 28 + 1) return;
600 
601     memcpy(result, "{SHA}", 5);
602     base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD);
603     force_assert(base64_written == 28);
604     result[5 + base64_written] = '\0'; /* terminate string */
605 }
606 
607 static handler_t mod_authn_file_htpasswd_basic(request_st * const r, void *p_d, const http_auth_require_t * const require, const buffer * const username, const char * const pw) {
608     plugin_data *p = (plugin_data *)p_d;
609     buffer *password = buffer_init();/* password-string from auth-backend */
610     int rc;
611     mod_authn_file_patch_config(r, p);
612     rc = mod_authn_file_htpasswd_get(p->conf.auth_htpasswd_userfile, CONST_BUF_LEN(username), password, r->conf.errh);
613     if (0 == rc) {
614         char sample[256];
615         rc = -1;
616         if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) {
617             /*
618              * The hash was created using $apr1$ custom algorithm.
619              */
620             apr_md5_encode(pw, password->ptr, sample, sizeof(sample));
621             rc = strcmp(sample, password->ptr);
622         }
623         else if (0 == strncmp(password->ptr, "{SHA}", 5)) {
624             apr_sha_encode(pw, sample, sizeof(sample));
625             rc = strcmp(sample, password->ptr);
626         }
627       #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT)
628         /* a simple DES password is 2 + 11 characters. everything else should be longer. */
629         else if (buffer_string_length(password) >= 13) {
630             char *crypted;
631            #if defined(HAVE_CRYPT_R)
632             struct crypt_data crypt_tmp_data;
633             #ifdef _AIX
634             memset(&crypt_tmp_data, 0, sizeof(crypt_tmp_data));
635             #else
636             crypt_tmp_data.initialized = 0;
637             #endif
638            #endif
639            #ifdef USE_LIB_CRYPTO_MD4 /*(for MD4_*() (e.g. MD4_Update()))*/
640             if (0 == memcmp(password->ptr, CONST_STR_LEN("$1+ntlm$"))) {
641                 /* CRYPT-MD5-NTLM algorithm
642                  * This algorithm allows for the construction of (slight more)
643                  * secure, salted password hashes from an environment where only
644                  * legacy NTLM hashes are available and where it is not feasible
645                  * to re-hash all the passwords with the MD5-based crypt(). */
646                 /* Note: originally, LM password were limited to 14 chars.
647                  * NTLM passwords limited to 127 chars, and encoding to UCS-2LE
648                  * requires double that, so sample[256] buf is large enough.
649                  * Prior sample[120] size likely taken from apr_md5_encode(). */
650                 char *b = password->ptr+sizeof("$1+ntlm$")-1;
651                 char *e = strchr(b, '$');
652                 size_t slen = (NULL != e) ? (size_t)(e - b) : sizeof(sample);
653                 size_t pwlen = strlen(pw) * 2;
654                 if (slen < sizeof(sample) - (sizeof("$1$")-1)
655                     && pwlen < sizeof(sample)) {
656                     /* compute NTLM hash and convert to lowercase hex chars
657                      * (require lc hex chars from li_tohex()) */
658                     char ntlmhash[16];
659                     char ntlmhex[33]; /*(sizeof(ntlmhash)*2 + 1)*/
660                     MD4_CTX c;
661                     MD4_Init(&c);
662                     if (pwlen) {
663                         /*(reuse sample buffer to encode pw into UCS-2LE)
664                          *(Note: assumes pw input in ISO-8859-1) */
665                         /*(buffer sizes checked above)*/
666                         for (int i=0; i < (int)pwlen; i+=2) {
667                             sample[i] = pw[(i >> 1)];
668                             sample[i+1] = 0;
669                         }
670                         MD4_Update(&c, (unsigned char *)sample, pwlen);
671                     }
672                     MD4_Final((unsigned char *)ntlmhash, &c);
673                     li_tohex(ntlmhex,sizeof(ntlmhex),ntlmhash,sizeof(ntlmhash));
674 
675                     /*(reuse sample buffer for salt  (FYI: expect slen == 8))*/
676                     memcpy(sample, "$1$", sizeof("$1$")-1);
677                     memcpy(sample+sizeof("$1$")-1, b, slen);
678                     sample[sizeof("$1$")-1+slen] = '\0';
679                    #if defined(HAVE_CRYPT_R)
680                     crypted = crypt_r(ntlmhex, sample, &crypt_tmp_data);
681                    #else
682                     crypted = crypt(ntlmhex, sample);
683                    #endif
684                     if (NULL != crypted
685                         && 0 == strncmp(crypted, "$1$", sizeof("$1$")-1)) {
686                         rc = strcmp(b, crypted+3); /*skip crypted "$1$" prefix*/
687                     }
688                 }
689             }
690             else
691            #endif
692             {
693                #if defined(HAVE_CRYPT_R)
694                 crypted = crypt_r(pw, password->ptr, &crypt_tmp_data);
695                #else
696                 crypted = crypt(pw, password->ptr);
697                #endif
698                 if (NULL != crypted) {
699                     rc = strcmp(password->ptr, crypted);
700                 }
701             }
702         }
703       #endif
704     }
705     buffer_free(password);
706     return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL)
707       ? HANDLER_GO_ON
708       : HANDLER_ERROR;
709 }
710 
711 
712 int mod_authn_file_plugin_init(plugin *p);
713 int mod_authn_file_plugin_init(plugin *p) {
714     p->version     = LIGHTTPD_VERSION_ID;
715     p->name        = "authn_file";
716     p->init        = mod_authn_file_init;
717     p->set_defaults= mod_authn_file_set_defaults;
718 
719     return 0;
720 }
721