diff options
author | MCApollo <34170230+MCApollo@users.noreply.github.com> | 2019-04-29 17:53:00 +0000 |
---|---|---|
committer | MCApollo <34170230+MCApollo@users.noreply.github.com> | 2019-04-29 17:53:00 +0000 |
commit | 59f5fd20e2f59ae186d5a461aef7782dd55e4e7b (patch) | |
tree | 10475faf0b6dcb812b64da679f4ae8ff01c45874 /data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c | |
parent | f06e297cd175cca39745f6d1970225ae88aa68f1 (diff) |
Updated lighttpd.
Diffstat (limited to 'data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c')
-rw-r--r-- | data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c | 734 |
1 files changed, 734 insertions, 0 deletions
diff --git a/data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c new file mode 100644 index 000000000..db1a241cb --- /dev/null +++ b/data/lighttpd/lighttpd-1.4.53/src/mod_authn_file.c @@ -0,0 +1,734 @@ +#include "first.h" + + +/*(htpasswd)*/ +#ifdef HAVE_CRYPT_H +# include <crypt.h> +#elif defined(__linux__) +/* linux needs _XOPEN_SOURCE */ +# define _XOPEN_SOURCE +#endif + +#if defined(HAVE_LIBCRYPT) && !defined(HAVE_CRYPT) +/* always assume crypt() is present if we have -lcrypt */ +# define HAVE_CRYPT +#endif + +#include "sys-crypto.h" +#ifdef USE_OPENSSL_CRYPTO +#include <openssl/md4.h> +#endif + +#include "safe_memclear.h" +/*(htpasswd)*/ + + +#include "base.h" +#include "plugin.h" +#include "http_auth.h" +#include "log.h" + +#include "algo_sha1.h" +#include "base64.h" +#include "md5.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +/* + * htdigest, htpasswd, plain auth backends + */ + +typedef struct { + buffer *auth_plain_groupfile; + buffer *auth_plain_userfile; + buffer *auth_htdigest_userfile; + buffer *auth_htpasswd_userfile; +} plugin_config; + +typedef struct { + PLUGIN_DATA; + plugin_config **config_storage; + plugin_config conf; +} plugin_data; + +static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]); +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); +static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]); +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); +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); + +INIT_FUNC(mod_authn_file_init) { + static http_auth_backend_t http_auth_backend_htdigest = + { "htdigest", mod_authn_file_htdigest_basic, mod_authn_file_htdigest_digest, NULL }; + static http_auth_backend_t http_auth_backend_htpasswd = + { "htpasswd", mod_authn_file_htpasswd_basic, NULL, NULL }; + static http_auth_backend_t http_auth_backend_plain = + { "plain", mod_authn_file_plain_basic, mod_authn_file_plain_digest, NULL }; + plugin_data *p = calloc(1, sizeof(*p)); + + /* register http_auth_backend_htdigest */ + http_auth_backend_htdigest.p_d = p; + http_auth_backend_set(&http_auth_backend_htdigest); + + /* register http_auth_backend_htpasswd */ + http_auth_backend_htpasswd.p_d = p; + http_auth_backend_set(&http_auth_backend_htpasswd); + + /* register http_auth_backend_plain */ + http_auth_backend_plain.p_d = p; + http_auth_backend_set(&http_auth_backend_plain); + + return p; +} + +FREE_FUNC(mod_authn_file_free) { + plugin_data *p = p_d; + + UNUSED(srv); + + if (!p) return HANDLER_GO_ON; + + if (p->config_storage) { + size_t i; + for (i = 0; i < srv->config_context->used; i++) { + plugin_config *s = p->config_storage[i]; + + if (NULL == s) continue; + + buffer_free(s->auth_plain_groupfile); + buffer_free(s->auth_plain_userfile); + buffer_free(s->auth_htdigest_userfile); + buffer_free(s->auth_htpasswd_userfile); + + free(s); + } + free(p->config_storage); + } + + free(p); + + return HANDLER_GO_ON; +} + +SETDEFAULTS_FUNC(mod_authn_file_set_defaults) { + plugin_data *p = p_d; + size_t i; + + config_values_t cv[] = { + { "auth.backend.plain.groupfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ + { "auth.backend.plain.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ + { "auth.backend.htdigest.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ + { "auth.backend.htpasswd.userfile", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ + { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } + }; + + p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *)); + + for (i = 0; i < srv->config_context->used; i++) { + data_config const* config = (data_config const*)srv->config_context->data[i]; + plugin_config *s; + + s = calloc(1, sizeof(plugin_config)); + s->auth_plain_groupfile = buffer_init(); + s->auth_plain_userfile = buffer_init(); + s->auth_htdigest_userfile = buffer_init(); + s->auth_htpasswd_userfile = buffer_init(); + + cv[0].destination = s->auth_plain_groupfile; + cv[1].destination = s->auth_plain_userfile; + cv[2].destination = s->auth_htdigest_userfile; + cv[3].destination = s->auth_htpasswd_userfile; + + p->config_storage[i] = s; + + if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { + return HANDLER_ERROR; + } + } + + return HANDLER_GO_ON; +} + +#define PATCH(x) \ + p->conf.x = s->x; +static int mod_authn_file_patch_connection(server *srv, connection *con, plugin_data *p) { + size_t i, j; + plugin_config *s = p->config_storage[0]; + + PATCH(auth_plain_groupfile); + PATCH(auth_plain_userfile); + PATCH(auth_htdigest_userfile); + PATCH(auth_htpasswd_userfile); + + /* skip the first, the global context */ + for (i = 1; i < srv->config_context->used; i++) { + data_config *dc = (data_config *)srv->config_context->data[i]; + s = p->config_storage[i]; + + /* condition didn't match */ + if (!config_check_cond(srv, con, dc)) continue; + + /* merge config */ + for (j = 0; j < dc->value->used; j++) { + data_unset *du = dc->value->data[j]; + + if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.groupfile"))) { + PATCH(auth_plain_groupfile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.plain.userfile"))) { + PATCH(auth_plain_userfile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htdigest.userfile"))) { + PATCH(auth_htdigest_userfile); + } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("auth.backend.htpasswd.userfile"))) { + PATCH(auth_htpasswd_userfile); + } + } + } + + return 0; +} +#undef PATCH + + +static int mod_authn_file_htdigest_get(server *srv, const buffer *auth_fn, const buffer *username, const buffer *realm, unsigned char HA1[16]) { + FILE *fp; + char f_user[1024]; + + if (buffer_string_is_empty(auth_fn)) return -1; + if (buffer_is_empty(username) || buffer_is_empty(realm)) return -1; + + fp = fopen(auth_fn->ptr, "r"); + if (NULL == fp) { + log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", auth_fn, "failed:", strerror(errno)); + + return -1; + } + + while (NULL != fgets(f_user, sizeof(f_user), fp)) { + char *f_pwd, *f_realm; + size_t u_len, r_len; + + /* skip blank lines and comment lines (beginning '#') */ + if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue; + + /* + * htdigest format + * + * user:realm:md5(user:realm:password) + */ + + if (NULL == (f_realm = strchr(f_user, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, + "expected 'username:realm:hashed password'"); + + continue; /* skip bad lines */ + } + + if (NULL == (f_pwd = strchr(f_realm + 1, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, + "expected 'username:realm:hashed password'"); + + continue; /* skip bad lines */ + } + + /* get pointers to the fields */ + u_len = f_realm - f_user; + f_realm++; + r_len = f_pwd - f_realm; + f_pwd++; + + if (buffer_string_length(username) == u_len && + (buffer_string_length(realm) == r_len) && + (0 == strncmp(username->ptr, f_user, u_len)) && + (0 == strncmp(realm->ptr, f_realm, r_len))) { + /* found */ + + size_t pwd_len = strlen(f_pwd); + if (f_pwd[pwd_len-1] == '\n') --pwd_len; + + fclose(fp); + + return http_auth_md5_hex2bin(f_pwd, pwd_len, HA1); + } + } + + fclose(fp); + return -1; +} + +static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) { + plugin_data *p = (plugin_data *)p_d; + buffer *username_buf = buffer_init_string(username); + buffer *realm_buf = buffer_init_string(realm); + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htdigest_get(srv, p->conf.auth_htdigest_userfile, username_buf, realm_buf, HA1); + buffer_free(realm_buf); + buffer_free(username_buf); + UNUSED(con); + return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +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) { + plugin_data *p = (plugin_data *)p_d; + li_MD5_CTX Md5Ctx; + unsigned char HA1[16]; + unsigned char htdigest[16]; + + mod_authn_file_patch_connection(srv, con, p); + if (mod_authn_file_htdigest_get(srv, p->conf.auth_htdigest_userfile, username, require->realm, htdigest)) return HANDLER_ERROR; + + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(username)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(require->realm)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw)); + li_MD5_Final(HA1, &Md5Ctx); + + UNUSED(con); + return (0 == memcmp(HA1, htdigest, sizeof(HA1)) + && http_auth_match_rules(require, username->ptr, NULL, NULL)) + ? HANDLER_GO_ON + : HANDLER_ERROR; +} + + + + +static int mod_authn_file_htpasswd_get(server *srv, const buffer *auth_fn, const buffer *username, buffer *password) { + FILE *fp; + char f_user[1024]; + + if (buffer_is_empty(username)) return -1; + + if (buffer_string_is_empty(auth_fn)) return -1; + fp = fopen(auth_fn->ptr, "r"); + if (NULL == fp) { + log_error_write(srv, __FILE__, __LINE__, "sbss", + "opening plain-userfile", auth_fn, "failed:", strerror(errno)); + + return -1; + } + + while (NULL != fgets(f_user, sizeof(f_user), fp)) { + char *f_pwd; + size_t u_len; + + /* skip blank lines and comment lines (beginning '#') */ + if (f_user[0] == '#' || f_user[0] == '\n' || f_user[0] == '\0') continue; + + /* + * htpasswd format + * + * user:crypted passwd + */ + + if (NULL == (f_pwd = strchr(f_user, ':'))) { + log_error_write(srv, __FILE__, __LINE__, "sbs", + "parsed error in", auth_fn, + "expected 'username:hashed password'"); + + continue; /* skip bad lines */ + } + + /* get pointers to the fields */ + u_len = f_pwd - f_user; + f_pwd++; + + if (buffer_string_length(username) == u_len && + (0 == strncmp(username->ptr, f_user, u_len))) { + /* found */ + + size_t pwd_len = strlen(f_pwd); + if (f_pwd[pwd_len-1] == '\n') --pwd_len; + + buffer_copy_string_len(password, f_pwd, pwd_len); + + fclose(fp); + return 0; + } + } + + fclose(fp); + return -1; +} + +static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) { + plugin_data *p = (plugin_data *)p_d; + buffer *username_buf = buffer_init_string(username); + buffer *password_buf = buffer_init();/* password-string from auth-backend */ + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username_buf, password_buf); + if (0 == rc) { + /* generate password from plain-text */ + li_MD5_CTX Md5Ctx; + li_MD5_Init(&Md5Ctx); + li_MD5_Update(&Md5Ctx, (unsigned char *)username_buf->ptr, buffer_string_length(username_buf)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)realm, strlen(realm)); + li_MD5_Update(&Md5Ctx, CONST_STR_LEN(":")); + li_MD5_Update(&Md5Ctx, (unsigned char *)password_buf->ptr, buffer_string_length(password_buf)); + li_MD5_Final(HA1, &Md5Ctx); + } + buffer_free(password_buf); + buffer_free(username_buf); + UNUSED(con); + return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR; +} + +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) { + plugin_data *p = (plugin_data *)p_d; + buffer *password_buf = buffer_init();/* password-string from auth-backend */ + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username, password_buf); + if (0 == rc) { + rc = http_auth_const_time_memeq(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1; + } + buffer_free(password_buf); + UNUSED(con); + return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON + : HANDLER_ERROR; +} + + + + +/** + * the $apr1$ handling is taken from apache 1.3.x + */ + +/* + * The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0 + * MD5 crypt() function, which is licenced as follows: + * ---------------------------------------------------------------------------- + * "THE BEER-WARE LICENSE" (Revision 42): + * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you + * can do whatever you want with this stuff. If we meet some day, and you think + * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp + * ---------------------------------------------------------------------------- + */ + +#define APR_MD5_DIGESTSIZE 16 +#define APR1_ID "$apr1$" + +/* + * The following MD5 password encryption code was largely borrowed from + * the FreeBSD 3.0 /usr/src/lib/libcrypt/crypt.c file, which is + * licenced as stated above. + */ + +static void to64(char *s, unsigned long v, int n) +{ + static const unsigned char itoa64[] = /* 0 ... 63 => ASCII - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +static void apr_md5_encode(const char *pw, const char *salt, char *result, size_t nbytes) { + /* + * Minimum size is 8 bytes for salt, plus 1 for the trailing NUL, + * plus 4 for the '$' separators, plus the password hash itself. + * Let's leave a goodly amount of leeway. + */ + + char passwd[120], *p; + const char *sp, *ep; + unsigned char final[APR_MD5_DIGESTSIZE]; + ssize_t sl, pl, i; + li_MD5_CTX ctx, ctx1; + unsigned long l; + + /* + * Refine the salt first. It's possible we were given an already-hashed + * string as the salt argument, so extract the actual salt value from it + * if so. Otherwise just use the string up to the first '$' as the salt. + */ + sp = salt; + + /* + * If it starts with the magic string, then skip that. + */ + if (!strncmp(sp, APR1_ID, strlen(APR1_ID))) { + sp += strlen(APR1_ID); + } + + /* + * It stops at the first '$' or 8 chars, whichever comes first + */ + for (ep = sp; (*ep != '\0') && (*ep != '$') && (ep < (sp + 8)); ep++) { + continue; + } + + /* + * Get the length of the true salt + */ + sl = ep - sp; + + /* + * 'Time to make the doughnuts..' + */ + li_MD5_Init(&ctx); + + /* + * The password first, since that is what is most unknown + */ + li_MD5_Update(&ctx, pw, strlen(pw)); + + /* + * Then our magic string + */ + li_MD5_Update(&ctx, APR1_ID, strlen(APR1_ID)); + + /* + * Then the raw salt + */ + li_MD5_Update(&ctx, sp, sl); + + /* + * Then just as many characters of the MD5(pw, salt, pw) + */ + li_MD5_Init(&ctx1); + li_MD5_Update(&ctx1, pw, strlen(pw)); + li_MD5_Update(&ctx1, sp, sl); + li_MD5_Update(&ctx1, pw, strlen(pw)); + li_MD5_Final(final, &ctx1); + for (pl = strlen(pw); pl > 0; pl -= APR_MD5_DIGESTSIZE) { + li_MD5_Update(&ctx, final, + (pl > APR_MD5_DIGESTSIZE) ? APR_MD5_DIGESTSIZE : pl); + } + + /* + * Don't leave anything around in vm they could use. + */ + memset(final, 0, sizeof(final)); + + /* + * Then something really weird... + */ + for (i = strlen(pw); i != 0; i >>= 1) { + if (i & 1) { + li_MD5_Update(&ctx, final, 1); + } + else { + li_MD5_Update(&ctx, pw, 1); + } + } + + /* + * Now make the output string. We know our limitations, so we + * can use the string routines without bounds checking. + */ + strcpy(passwd, APR1_ID); + strncat(passwd, sp, sl); + strcat(passwd, "$"); + + li_MD5_Final(final, &ctx); + + /* + * And now, just to make sure things don't run too fast.. + * On a 60 Mhz Pentium this takes 34 msec, so you would + * need 30 seconds to build a 1000 entry dictionary... + */ + for (i = 0; i < 1000; i++) { + li_MD5_Init(&ctx1); + if (i & 1) { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + else { + li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + if (i % 3) { + li_MD5_Update(&ctx1, sp, sl); + } + + if (i % 7) { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + + if (i & 1) { + li_MD5_Update(&ctx1, final, APR_MD5_DIGESTSIZE); + } + else { + li_MD5_Update(&ctx1, pw, strlen(pw)); + } + li_MD5_Final(final,&ctx1); + } + + p = passwd + strlen(passwd); + + l = (final[ 0]<<16) | (final[ 6]<<8) | final[12]; to64(p, l, 4); p += 4; + l = (final[ 1]<<16) | (final[ 7]<<8) | final[13]; to64(p, l, 4); p += 4; + l = (final[ 2]<<16) | (final[ 8]<<8) | final[14]; to64(p, l, 4); p += 4; + l = (final[ 3]<<16) | (final[ 9]<<8) | final[15]; to64(p, l, 4); p += 4; + l = (final[ 4]<<16) | (final[10]<<8) | final[ 5]; to64(p, l, 4); p += 4; + l = final[11] ; to64(p, l, 2); p += 2; + *p = '\0'; + + /* + * Don't leave anything around in vm they could use. + */ + safe_memclear(final, sizeof(final)); + + /* FIXME + */ +#define apr_cpystrn strncpy + apr_cpystrn(result, passwd, nbytes - 1); +} + +static void apr_sha_encode(const char *pw, char *result, size_t nbytes) { + unsigned char digest[20]; + size_t base64_written; + SHA_CTX sha1; + + SHA1_Init(&sha1); + SHA1_Update(&sha1, (const unsigned char *) pw, strlen(pw)); + SHA1_Final(digest, &sha1); + + memset(result, 0, nbytes); + + /* need 5 bytes for "{SHA}", 28 for base64 (3 bytes -> 4 bytes) of SHA1 (20 bytes), 1 terminating */ + if (nbytes < 5 + 28 + 1) return; + + memcpy(result, "{SHA}", 5); + base64_written = li_to_base64(result + 5, nbytes - 5, digest, 20, BASE64_STANDARD); + force_assert(base64_written == 28); + result[5 + base64_written] = '\0'; /* terminate string */ +} + +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) { + plugin_data *p = (plugin_data *)p_d; + buffer *password = buffer_init();/* password-string from auth-backend */ + int rc; + mod_authn_file_patch_connection(srv, con, p); + rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_htpasswd_userfile, username, password); + if (0 == rc) { + char sample[256]; + rc = -1; + if (!strncmp(password->ptr, APR1_ID, strlen(APR1_ID))) { + /* + * The hash was created using $apr1$ custom algorithm. + */ + apr_md5_encode(pw, password->ptr, sample, sizeof(sample)); + rc = strcmp(sample, password->ptr); + } + else if (0 == strncmp(password->ptr, "{SHA}", 5)) { + apr_sha_encode(pw, sample, sizeof(sample)); + rc = strcmp(sample, password->ptr); + } + #if defined(HAVE_CRYPT_R) || defined(HAVE_CRYPT) + /* a simple DES password is 2 + 11 characters. everything else should be longer. */ + else if (buffer_string_length(password) >= 13) { + char *crypted; + #if defined(HAVE_CRYPT_R) + struct crypt_data crypt_tmp_data; + #ifdef _AIX + memset(&crypt_tmp_data, 0, sizeof(crypt_tmp_data)); + #else + crypt_tmp_data.initialized = 0; + #endif + #endif + #ifdef USE_OPENSSL_CRYPTO /* (for MD4_*() (e.g. MD4_Update())) */ + #ifndef NO_MD4 /*(e.g. wolfSSL built without MD4)*/ + if (0 == memcmp(password->ptr, CONST_STR_LEN("$1+ntlm$"))) { + /* CRYPT-MD5-NTLM algorithm + * This algorithm allows for the construction of (slight more) + * secure, salted password hashes from an environment where only + * legacy NTLM hashes are available and where it is not feasible + * to re-hash all the passwords with the MD5-based crypt(). */ + /* Note: originally, LM password were limited to 14 chars. + * NTLM passwords limited to 127 chars, and encoding to UCS-2LE + * requires double that, so sample[256] buf is large enough. + * Prior sample[120] size likely taken from apr_md5_encode(). */ + char *b = password->ptr+sizeof("$1+ntlm$")-1; + char *e = strchr(b, '$'); + size_t slen = (NULL != e) ? (size_t)(e - b) : sizeof(sample); + size_t pwlen = strlen(pw) * 2; + if (slen < sizeof(sample) - (sizeof("$1$")-1) + && pwlen < sizeof(sample)) { + /* compute NTLM hash and convert to lowercase hex chars + * (require lc hex chars from li_tohex()) */ + char ntlmhash[16]; + char ntlmhex[33]; /*(sizeof(ntlmhash)*2 + 1)*/ + MD4_CTX c; + MD4_Init(&c); + if (pwlen) { + /*(reuse sample buffer to encode pw into UCS-2LE) + *(Note: assumes pw input in ISO-8859-1) */ + /*(buffer sizes checked above)*/ + for (int i=0; i < (int)pwlen; i+=2) { + sample[i] = pw[(i >> 1)]; + sample[i+1] = 0; + } + MD4_Update(&c, (unsigned char *)sample, pwlen); + } + MD4_Final((unsigned char *)ntlmhash, &c); + li_tohex(ntlmhex,sizeof(ntlmhex),ntlmhash,sizeof(ntlmhash)); + + /*(reuse sample buffer for salt (FYI: expect slen == 8))*/ + memcpy(sample, "$1$", sizeof("$1$")-1); + memcpy(sample+sizeof("$1$")-1, b, slen); + sample[sizeof("$1$")-1+slen] = '\0'; + #if defined(HAVE_CRYPT_R) + crypted = crypt_r(ntlmhex, sample, &crypt_tmp_data); + #else + crypted = crypt(ntlmhex, sample); + #endif + if (NULL != crypted + && 0 == strncmp(crypted, "$1$", sizeof("$1$")-1)) { + rc = strcmp(b, crypted+3); /*skip crypted "$1$" prefix*/ + } + } + } + else + #endif + #endif + { + #if defined(HAVE_CRYPT_R) + crypted = crypt_r(pw, password->ptr, &crypt_tmp_data); + #else + crypted = crypt(pw, password->ptr); + #endif + if (NULL != crypted) { + rc = strcmp(password->ptr, crypted); + } + } + } + #endif + } + buffer_free(password); + UNUSED(con); + return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL) + ? HANDLER_GO_ON + : HANDLER_ERROR; +} + + +int mod_authn_file_plugin_init(plugin *p); +int mod_authn_file_plugin_init(plugin *p) { + p->version = LIGHTTPD_VERSION_ID; + p->name = buffer_init_string("authn_file"); + p->init = mod_authn_file_init; + p->set_defaults= mod_authn_file_set_defaults; + p->cleanup = mod_authn_file_free; + + p->data = NULL; + + return 0; +} |