xref: /lighttpd1.4/src/mod_authn_file.c (revision 49e9f0ac)
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.h"
18 #ifdef USE_OPENSSL_CRYPTO
19 #include <openssl/md4.h>
20 #endif
21 
22 #include "safe_memclear.h"
23 /*(htpasswd)*/
24 
25 
26 #include "base.h"
27 #include "plugin.h"
28 #include "http_auth.h"
29 #include "log.h"
30 
31 #include "algo_sha1.h"
32 #include "base64.h"
33 #include "md5.h"
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 
38 #include <stdlib.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 
45 /*
46  * htdigest, htpasswd, plain auth backends
47  */
48 
49 typedef struct {
50     buffer *auth_plain_groupfile;
51     buffer *auth_plain_userfile;
52     buffer *auth_htdigest_userfile;
53     buffer *auth_htpasswd_userfile;
54 } plugin_config;
55 
56 typedef struct {
57     PLUGIN_DATA;
58     plugin_config **config_storage;
59     plugin_config conf;
60 } plugin_data;
61 
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);
67 
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));
76 
77     /* register http_auth_backend_htdigest */
78     http_auth_backend_htdigest.p_d = p;
79     http_auth_backend_set(&http_auth_backend_htdigest);
80 
81     /* register http_auth_backend_htpasswd */
82     http_auth_backend_htpasswd.p_d = p;
83     http_auth_backend_set(&http_auth_backend_htpasswd);
84 
85     /* register http_auth_backend_plain */
86     http_auth_backend_plain.p_d = p;
87     http_auth_backend_set(&http_auth_backend_plain);
88 
89     return p;
90 }
91 
92 FREE_FUNC(mod_authn_file_free) {
93     plugin_data *p = p_d;
94 
95     UNUSED(srv);
96 
97     if (!p) return HANDLER_GO_ON;
98 
99     if (p->config_storage) {
100         size_t i;
101         for (i = 0; i < srv->config_context->used; i++) {
102             plugin_config *s = p->config_storage[i];
103 
104             if (NULL == s) continue;
105 
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);
110 
111             free(s);
112         }
113         free(p->config_storage);
114     }
115 
116     free(p);
117 
118     return HANDLER_GO_ON;
119 }
120 
121 SETDEFAULTS_FUNC(mod_authn_file_set_defaults) {
122     plugin_data *p = p_d;
123     size_t i;
124 
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 }
131     };
132 
133     p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
134 
135     for (i = 0; i < srv->config_context->used; i++) {
136         data_config const* config = (data_config const*)srv->config_context->data[i];
137         plugin_config *s;
138 
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();
144 
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;
149 
150         p->config_storage[i] = s;
151 
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;
154         }
155     }
156 
157     return HANDLER_GO_ON;
158 }
159 
160 #define PATCH(x) \
161     p->conf.x = s->x;
162 static int mod_authn_file_patch_connection(server *srv, connection *con, plugin_data *p) {
163     size_t i, j;
164     plugin_config *s = p->config_storage[0];
165 
166     PATCH(auth_plain_groupfile);
167     PATCH(auth_plain_userfile);
168     PATCH(auth_htdigest_userfile);
169     PATCH(auth_htpasswd_userfile);
170 
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];
175 
176         /* condition didn't match */
177         if (!config_check_cond(srv, con, dc)) continue;
178 
179         /* merge config */
180         for (j = 0; j < dc->value->used; j++) {
181             data_unset *du = dc->value->data[j];
182 
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);
191             }
192         }
193     }
194 
195     return 0;
196 }
197 #undef PATCH
198 
199 
200 
201 
202 #ifdef USE_OPENSSL_CRYPTO
203 
204 static void mod_authn_file_digest_sha256(http_auth_info_t *ai, const char *pw, size_t pwlen) {
205     SHA256_CTX ctx;
206     SHA256_Init(&ctx);
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);
213 }
214 
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) {
217     SHA512_CTX ctx;
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);
225 }
226 #endif
227 
228 #endif
229 
230 static void mod_authn_file_digest_md5(http_auth_info_t *ai, const char *pw, size_t pwlen) {
231     li_MD5_CTX ctx;
232     li_MD5_Init(&ctx);
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);
239 }
240 
241 static void mod_authn_file_digest(http_auth_info_t *ai, const char *pw, size_t pwlen) {
242 
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);
251    #endif
252   #endif
253 }
254 
255 
256 
257 
258 static int mod_authn_file_htdigest_get_loop(server *srv, FILE *fp, const buffer *auth_fn, http_auth_info_t *ai) {
259     char f_user[1024];
260 
261     while (NULL != fgets(f_user, sizeof(f_user), fp)) {
262         char *f_pwd, *f_realm;
263         size_t u_len, r_len;
264 
265         /* skip blank lines and comment lines (beginning '#') */
266         if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
267 
268         /*
269          * htdigest format
270          *
271          * user:realm:md5(user:realm:password)
272          */
273 
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'");
278 
279             continue; /* skip bad lines */
280         }
281 
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'");
286 
287             continue; /* skip bad lines */
288         }
289 
290         /* get pointers to the fields */
291         u_len = f_realm - f_user;
292         f_realm++;
293         r_len = f_pwd - f_realm;
294         f_pwd++;
295 
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)) {
299             /* found */
300 
301             size_t pwd_len = strlen(f_pwd);
302             if (f_pwd[pwd_len-1] == '\n') --pwd_len;
303 
304             if (pwd_len != (ai->dlen << 1)) continue;
305             return http_auth_digest_hex2bin(f_pwd, pwd_len,
306                                             ai->digest, sizeof(ai->digest));
307         }
308     }
309 
310     return -1;
311 }
312 
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;
316     FILE *fp;
317 
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;
321 
322     fp = fopen(auth_fn->ptr, "r");
323     if (NULL != fp) {
324         int rc = mod_authn_file_htdigest_get_loop(srv, fp, auth_fn, ai);
325         fclose(fp);
326         return rc;
327     }
328     else {
329         log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", auth_fn, "failed:", strerror(errno));
330         return -1;
331     }
332 }
333 
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))
336       ? HANDLER_GO_ON
337       : HANDLER_ERROR;
338 }
339 
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) {
341     http_auth_info_t ai;
342     unsigned char htdigest[sizeof(ai.digest)];
343 
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);
351 
352     if (mod_authn_file_htdigest_get(srv, con, p_d, &ai)) return HANDLER_ERROR;
353 
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)*/
356 
357     mod_authn_file_digest(&ai, pw, strlen(pw));
358 
359     return (0 == memcmp(htdigest, ai.digest, ai.dlen)
360             && http_auth_match_rules(require, username->ptr, NULL, NULL))
361       ? HANDLER_GO_ON
362       : HANDLER_ERROR;
363 }
364 
365 
366 
367 
368 static int mod_authn_file_htpasswd_get(server *srv, const buffer *auth_fn, const char *username, size_t userlen, buffer *password) {
369     FILE *fp;
370     char f_user[1024];
371 
372     if (NULL == username) return -1;
373 
374     if (buffer_string_is_empty(auth_fn)) return -1;
375     fp = fopen(auth_fn->ptr, "r");
376     if (NULL == fp) {
377         log_error_write(srv, __FILE__, __LINE__, "sbss",
378                 "opening plain-userfile", auth_fn, "failed:", strerror(errno));
379 
380         return -1;
381     }
382 
383     while (NULL != fgets(f_user, sizeof(f_user), fp)) {
384         char *f_pwd;
385         size_t u_len;
386 
387         /* skip blank lines and comment lines (beginning '#') */
388         if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue;
389 
390         /*
391          * htpasswd format
392          *
393          * user:crypted passwd
394          */
395 
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'");
400 
401             continue; /* skip bad lines */
402         }
403 
404         /* get pointers to the fields */
405         u_len = f_pwd - f_user;
406         f_pwd++;
407 
408         if (userlen == u_len && 0 == memcmp(username, f_user, u_len)) {
409             /* found */
410 
411             size_t pwd_len = strlen(f_pwd);
412             if (f_pwd[pwd_len-1] == '\n') --pwd_len;
413 
414             buffer_copy_string_len(password, f_pwd, pwd_len);
415 
416             fclose(fp);
417             return 0;
418         }
419     }
420 
421     fclose(fp);
422     return -1;
423 }
424 
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 */
428     int rc;
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);
431     if (0 == rc) {
432         /* generate password from plain-text */
433         mod_authn_file_digest(ai, CONST_BUF_LEN(password_buf));
434     }
435     buffer_free(password_buf);
436     return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
437 }
438 
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 */
442     int rc;
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);
445     if (0 == rc) {
446         rc = http_auth_const_time_memeq(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1;
447     }
448     buffer_free(password_buf);
449     return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL)
450       ? HANDLER_GO_ON
451       : HANDLER_ERROR;
452 }
453 
454 
455 
456 
457 /**
458  * the $apr1$ handling is taken from apache 1.3.x
459  */
460 
461 /*
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  * <[email protected]> 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  * ----------------------------------------------------------------------------
470  */
471 
472 #define APR_MD5_DIGESTSIZE 16
473 #define APR1_ID "$apr1$"
474 
475 /*
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.
479  */
480 
481 static void to64(char *s, unsigned long v, int n)
482 {
483     static const unsigned char itoa64[] =         /* 0 ... 63 => ASCII - 64 */
484         "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
485 
486     while (--n >= 0) {
487         *s++ = itoa64[v&0x3f];
488         v >>= 6;
489     }
490 }
491 
492 static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) {
493     /*
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.
497      */
498 
499     char passwd[120], *p;
500     const char *sp, *ep;
501     unsigned char final[APR_MD5_DIGESTSIZE];
502     ssize_t sl, pl, i;
503     li_MD5_CTX ctx, ctx1;
504     unsigned long l;
505 
506     /*
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.
510      */
511     sp = salt;
512 
513     /*
514      * If it starts with the magic string, then skip that.
515      */
516     if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) {
517         sp += strlen(APR1_ID);
518     }
519 
520     /*
521      * It stops at the first '$' or 8 chars, whichever comes first
522      */
523     for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) {
524         continue;
525     }
526 
527     /*
528      * Get the length of the true salt
529      */
530     sl = ep - sp;
531 
532     /*
533      * 'Time to make the doughnuts..'
534      */
535     li_MD5_Init(&ctx);
536 
537     /*
538      * The password first, since that is what is most unknown
539      */
540     li_MD5_Update(&ctx, pw, strlen(pw));
541 
542     /*
543      * Then our magic string
544      */
545     li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID));
546 
547     /*
548      * Then the raw salt
549      */
550     li_MD5_Update(&ctx, sp, sl);
551 
552     /*
553      * Then just as many characters of the MD5(pw, salt, pw)
554      */
555     li_MD5_Init(&ctx1);
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);
563     }
564 
565     /*
566      * Don't leave anything around in vm they could use.
567      */
568     memset(final, 0, sizeof(final));
569 
570     /*
571      * Then something really weird...
572      */
573     for (i = strlen(pw); i != 0; i >>= 1) {
574         if (i & 1) {
575             li_MD5_Update(&ctx, final, 1);
576         }
577         else {
578             li_MD5_Update(&ctx, pw, 1);
579         }
580     }
581 
582     /*
583      * Now make the output string.  We know our limitations, so we
584      * can use the string routines without bounds checking.
585      */
586     strcpy(passwd, APR1_ID);
587     strncat(passwd, sp, sl);
588     strcat(passwd, "$");
589 
590     li_MD5_Final(final, &ctx);
591 
592     /*
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...
596      */
597     for (i = 0; i < 1000; i++) {
598         li_MD5_Init(&ctx1);
599         if (i & 1) {
600             li_MD5_Update(&ctx1, pw, strlen(pw));
601         }
602         else {
603             li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
604         }
605         if (i % 3) {
606             li_MD5_Update(&ctx1, sp, sl);
607         }
608 
609         if (i % 7) {
610             li_MD5_Update(&ctx1, pw, strlen(pw));
611         }
612 
613         if (i & 1) {
614             li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE);
615         }
616         else {
617             li_MD5_Update(&ctx1, pw, strlen(pw));
618         }
619         li_MD5_Final(final,&ctx1);
620     }
621 
622     p = passwd + strlen(passwd);
623 
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;
630     *p = '\0';
631 
632     /*
633      * Don't leave anything around in vm they could use.
634      */
635     safe_memclear(final, sizeof(final));
636 
637     /* FIXME
638      */
639 #define apr_cpystrn strncpy
640     apr_cpystrn(result, passwd, nbytes - 1);
641 }
642 
643 static void apr_sha_encode(const char *pw, char *result, size_t nbytes) {
644     unsigned char digest[20];
645     size_t base64_written;
646     SHA_CTX sha1;
647 
648     SHA1_Init(&sha1);
649     SHA1_Update(&sha1, (const unsigned char *) pw, strlen(pw));
650     SHA1_Final(digest, &sha1);
651 
652     memset(result, 0, nbytes);
653 
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;
656 
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 */
661 }
662 
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 */
666     int rc;
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);
669     if (0 == rc) {
670         char sample[256];
671         rc = -1;
672         if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) {
673             /*
674              * The hash was created using $apr1$ custom algorithm.
675              */
676             apr_md5_encode(pw, password->ptr, sample, sizeof(sample));
677             rc = strcmp(sample, password->ptr);
678         }
679         else if (0 == strncmp(password->ptr, "{SHA}", 5)) {
680             apr_sha_encode(pw, sample, sizeof(sample));
681             rc = strcmp(sample, password->ptr);
682         }
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) {
686             char *crypted;
687            #if defined(HAVE_CRYPT_R)
688             struct crypt_data crypt_tmp_data;
689             #ifdef _AIX
690             memset(&crypt_tmp_data, 0, sizeof(crypt_tmp_data));
691             #else
692             crypt_tmp_data.initialized = 0;
693             #endif
694            #endif
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()) */
715                     char ntlmhash[16];
716                     char ntlmhex[33]; /*(sizeof(ntlmhash)*2 + 1)*/
717                     MD4_CTX c;
718                     MD4_Init(&c);
719                     if (pwlen) {
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)];
725                             sample[i+1] = 0;
726                         }
727                         MD4_Update(&c, (unsigned char *)sample, pwlen);
728                     }
729                     MD4_Final((unsigned char *)ntlmhash, &c);
730                     li_tohex(ntlmhex,sizeof(ntlmhex),ntlmhash,sizeof(ntlmhash));
731 
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);
738                    #else
739                     crypted = crypt(ntlmhex, sample);
740                    #endif
741                     if (NULL != crypted
742                         && 0 == strncmp(crypted, "$1$", sizeof("$1$")-1)) {
743                         rc = strcmp(b, crypted+3); /*skip crypted "$1$" prefix*/
744                     }
745                 }
746             }
747             else
748            #endif
749            #endif
750             {
751                #if defined(HAVE_CRYPT_R)
752                 crypted = crypt_r(pw, password->ptr, &crypt_tmp_data);
753                #else
754                 crypted = crypt(pw, password->ptr);
755                #endif
756                 if (NULL != crypted) {
757                     rc = strcmp(password->ptr, crypted);
758                 }
759             }
760         }
761       #endif
762     }
763     buffer_free(password);
764     return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL)
765       ? HANDLER_GO_ON
766       : HANDLER_ERROR;
767 }
768 
769 
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;
777 
778     p->data        = NULL;
779 
780     return 0;
781 }
782