Browse Source

[mod_authn_file] wipe password/digest after use

slurp password/digest file into memory and then clear after use
(avoid stdio, which buffers by default and does not wipe those buffers)

password/digest files are not expected to be very large
e.g. a password file with 1000 entries is expected to be < 64k
If files are larger, mod_authn_dbi or other mod_authn_* is recommended
master
Glenn Strauss 7 months ago
parent
commit
9ac307ec14
  1. 3
      src/mod_auth.c
  2. 117
      src/mod_authn_file.c

3
src/mod_auth.c

@ -829,6 +829,7 @@ static handler_t mod_auth_check_basic(request_st * const r, void *p_d, const str
break;
}
safe_memclear(pw, pwlen);
buffer_free(username);
return (HANDLER_UNSET != rc) ? rc : mod_auth_send_401_unauthorized_basic(r, require->realm);
}
@ -1475,6 +1476,7 @@ static handler_t mod_auth_check_digest(request_st * const r, void *p_d, const st
mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop);
if (!http_auth_const_time_memeq(rdigest, ai.digest, ai.dlen)) {
/*safe_memclear(ai.digest, ai.dlen);*//* skip clear since mutated */
/* digest not ok */
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: auth failed for %s: wrong password, IP: %s",
@ -1484,6 +1486,7 @@ static handler_t mod_auth_check_digest(request_st * const r, void *p_d, const st
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
/*safe_memclear(ai.digest, ai.dlen);*//* skip clear since mutated */
/* value is our allow-rules */
if (!http_auth_match_rules(require, username, NULL, NULL)) {

117
src/mod_authn_file.c

@ -20,18 +20,14 @@
#include "base.h"
#include "plugin.h"
#include "fdevent.h"
#include "http_auth.h"
#include "log.h"
#include "base64.h"
#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
/*
* htdigest, htpasswd, plain auth backends
@ -207,15 +203,21 @@ static void mod_authn_file_digest(http_auth_info_t *ai, const char *pw, size_t p
static int mod_authn_file_htdigest_get_loop(FILE *fp, const buffer *auth_fn, http_auth_info_t *ai, log_error_st *errh) {
char f_user[1024];
static int mod_authn_file_htdigest_get_loop(const char *data, const buffer *auth_fn, http_auth_info_t *ai, log_error_st *errh) {
const char *f_user = data, *n;
do {
n = strchr(f_user, '\n');
/* (last line might not end in '\n') */
if (NULL == n) n = f_user + strlen(f_user);
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;
if (f_user[0] == '\n' || f_user[0] == '\r' ||
f_user[0] == '#' || f_user[0] == '\0') continue;
/* skip excessively long lines */
if (n - f_user > 1024) continue;
/*
* htdigest format
@ -223,10 +225,10 @@ static int mod_authn_file_htdigest_get_loop(FILE *fp, const buffer *auth_fn, htt
* user:realm:md5(user:realm:password)
*/
if (NULL == (f_realm = strchr(f_user, ':'))
|| NULL == (f_pwd = strchr(f_realm + 1, ':'))) {
if (NULL == (f_realm = memchr(f_user, ':', n - f_user))
|| NULL == (f_pwd = memchr(f_realm+1, ':', n - (f_realm+1)))) {
log_error(errh, __FILE__, __LINE__,
"parsed error in %s expected 'username:realm:hashed password'",
"parse error in %s expected 'username:realm:hashed password'",
auth_fn->ptr);
continue; /* skip bad lines */
}
@ -242,38 +244,32 @@ static int mod_authn_file_htdigest_get_loop(FILE *fp, const buffer *auth_fn, htt
&& 0 == memcmp(ai->realm, f_realm, r_len)) {
/* found */
size_t pwd_len = strlen(f_pwd);
if (f_pwd[pwd_len-1] == '\n') --pwd_len;
size_t pwd_len = n - f_pwd;
if (f_pwd[pwd_len-1] == '\r') --pwd_len;
if (pwd_len != (ai->dlen << 1)) continue;
return http_auth_digest_hex2bin(f_pwd, pwd_len,
ai->digest, sizeof(ai->digest));
}
}
} while (*n && *(f_user = n+1));
return -1;
}
static int mod_authn_file_htdigest_get(request_st * const r, void *p_d, http_auth_info_t * const ai) {
plugin_data *p = (plugin_data *)p_d;
const buffer *auth_fn;
FILE *fp;
mod_authn_file_patch_config(r, p);
auth_fn = p->conf.auth_htdigest_userfile;
const buffer * const auth_fn = p->conf.auth_htdigest_userfile;
if (buffer_string_is_empty(auth_fn)) return -1;
fp = fopen(auth_fn->ptr, "r");
if (NULL != fp) {
int rc = mod_authn_file_htdigest_get_loop(fp,auth_fn,ai,r->conf.errh);
fclose(fp);
return rc;
}
else {
log_perror(r->conf.errh, __FILE__, __LINE__,
"opening digest-userfile %s", auth_fn->ptr);
return -1;
}
off_t dlen = 64*1024*1024;/*(arbitrary limit: 64 MB file; expect < 1 MB)*/
char *data = fdevent_load_file(auth_fn->ptr,&dlen,r->conf.errh,malloc,free);
if (NULL == data) return -1;
int rc = mod_authn_file_htdigest_get_loop(data, auth_fn, ai, r->conf.errh);
safe_memclear(data, (size_t)dlen);
free(data);
return rc;
}
static handler_t mod_authn_file_htdigest_digest(request_st * const r, void *p_d, http_auth_info_t * const ai) {
@ -296,41 +292,48 @@ static handler_t mod_authn_file_htdigest_basic(request_st * const r, void *p_d,
if (mod_authn_file_htdigest_get(r, p_d, &ai)) return HANDLER_ERROR;
if (ai.dlen > sizeof(htdigest)) return HANDLER_ERROR;/*(should not happen)*/
if (ai.dlen > sizeof(htdigest)) {
safe_memclear(ai.digest, ai.dlen);
return HANDLER_ERROR;/*(should not happen)*/
}
memcpy(htdigest, ai.digest, ai.dlen); /*(save digest before reuse of ai)*/
mod_authn_file_digest(&ai, pw, strlen(pw));
return (http_auth_const_time_memeq(htdigest, ai.digest, ai.dlen)
&& http_auth_match_rules(require, username->ptr, NULL, NULL))
? HANDLER_GO_ON
: HANDLER_ERROR;
int rc = (http_auth_const_time_memeq(htdigest, ai.digest, ai.dlen)
&& http_auth_match_rules(require, username->ptr, NULL, NULL));
safe_memclear(htdigest, ai.dlen);
safe_memclear(ai.digest, ai.dlen);
return rc ? HANDLER_GO_ON : HANDLER_ERROR;
}
static int mod_authn_file_htpasswd_get(const buffer *auth_fn, const char *username, size_t userlen, buffer *password, log_error_st *errh) {
FILE *fp;
char f_user[1024];
if (NULL == username) return -1;
if (buffer_string_is_empty(auth_fn)) return -1;
fp = fopen(auth_fn->ptr, "r");
if (NULL == fp) {
log_perror(errh, __FILE__, __LINE__,
"opening plain-userfile %s", auth_fn->ptr);
return -1;
}
off_t dlen = 64*1024*1024;/*(arbitrary limit: 64 MB file; expect < 1 MB)*/
char *data = fdevent_load_file(auth_fn->ptr, &dlen, errh, malloc, free);
if (NULL == data) return -1;
int rc = -1;
const char *f_user = data, *n;
do {
n = strchr(f_user, '\n');
/* (last line might not end in '\n') */
if (NULL == n) n = f_user + strlen(f_user);
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;
if (f_user[0] == '\n' || f_user[0] == '\r' ||
f_user[0] == '#' || f_user[0] == '\0') continue;
/* skip excessively long lines */
if (n - f_user > 1024) continue;
/*
* htpasswd format
@ -338,7 +341,7 @@ static int mod_authn_file_htpasswd_get(const buffer *auth_fn, const char *userna
* user:crypted passwd
*/
if (NULL == (f_pwd = strchr(f_user, ':'))) {
if (NULL == (f_pwd = memchr(f_user, ':', n - f_user))) {
log_error(errh, __FILE__, __LINE__,
"parsed error in %s expected 'username:hashed password'",
auth_fn->ptr);
@ -352,18 +355,19 @@ static int mod_authn_file_htpasswd_get(const buffer *auth_fn, const char *userna
if (userlen == u_len && 0 == memcmp(username, f_user, u_len)) {
/* found */
size_t pwd_len = strlen(f_pwd);
if (f_pwd[pwd_len-1] == '\n') --pwd_len;
size_t pwd_len = n - f_pwd;
if (f_pwd[pwd_len-1] == '\r') --pwd_len;
buffer_copy_string_len(password, f_pwd, pwd_len);
fclose(fp);
return 0;
rc = 0;
break;
}
}
} while (*n && *(f_user = n+1));
fclose(fp);
return -1;
safe_memclear(data, (size_t)dlen);
free(data);
return rc;
}
static handler_t mod_authn_file_plain_digest(request_st * const r, void *p_d, http_auth_info_t * const ai) {
@ -376,6 +380,7 @@ static handler_t mod_authn_file_plain_digest(request_st * const r, void *p_d, ht
/* generate password from plain-text */
mod_authn_file_digest(ai, CONST_BUF_LEN(password_buf));
}
safe_memclear(password_buf->ptr, password_buf->size);
buffer_free(password_buf);
return (0 == rc) ? HANDLER_GO_ON : HANDLER_ERROR;
}
@ -389,6 +394,7 @@ static handler_t mod_authn_file_plain_basic(request_st * const r, void *p_d, con
if (0 == rc) {
rc = http_auth_const_time_memeq_pad(CONST_BUF_LEN(password_buf), pw, strlen(pw)) ? 0 : -1;
}
safe_memclear(password_buf->ptr, password_buf->size);
buffer_free(password_buf);
return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL)
? HANDLER_GO_ON
@ -702,6 +708,7 @@ static handler_t mod_authn_file_htpasswd_basic(request_st * const r, void *p_d,
}
#endif
}
safe_memclear(password->ptr, password->size);
buffer_free(password);
return 0 == rc && http_auth_match_rules(require, username->ptr, NULL, NULL)
? HANDLER_GO_ON

Loading…
Cancel
Save