[mod_auth] http_auth_info_t digest abstraction

This commit is contained in:
Glenn Strauss 2019-03-03 19:00:59 -05:00
parent 07fef25867
commit 60f4cf3ad8
5 changed files with 126 additions and 69 deletions

View File

@ -137,6 +137,19 @@ void http_auth_setenv(connection *con, const char *username, size_t ulen, const
http_header_env_set(con, CONST_STR_LEN("AUTH_TYPE"), auth_type, alen);
}
unsigned int http_auth_digest_len (int algo)
{
if (algo & (HTTP_AUTH_DIGEST_SHA256 | HTTP_AUTH_DIGEST_SHA512_256)) {
/* HTTP_AUTH_DIGEST_SHA512_256_BINLEN */
return HTTP_AUTH_DIGEST_SHA256_BINLEN;
}
if (algo & HTTP_AUTH_DIGEST_MD5) {
return HTTP_AUTH_DIGEST_MD5_BINLEN;
}
return 0;
}
int http_auth_digest_hex2bin (const char *hexstr, size_t len, unsigned char *bin, size_t binlen)
{
/* validate and transform 32-byte MD5 hex string to 16-byte binary MD5,

View File

@ -8,6 +8,20 @@
void http_auth_dumbdata_reset (void);
typedef enum http_auth_digest_type {
HTTP_AUTH_DIGEST_NONE = 0
,HTTP_AUTH_DIGEST_SESS = 0x01
,HTTP_AUTH_DIGEST_MD5 = 0x02
,HTTP_AUTH_DIGEST_SHA256 = 0x04
,HTTP_AUTH_DIGEST_SHA512_256 = 0x08
} http_auth_digest_type;
#define HTTP_AUTH_DIGEST_MD5_BINLEN 16 /* MD5_DIGEST_LENGTH */
#define HTTP_AUTH_DIGEST_SHA256_BINLEN 32 /* SHA256_DIGEST_LENGTH */
#define HTTP_AUTH_DIGEST_SHA512_256_BINLEN 32 /* SHA512_256_DIGEST_LENGTH */
unsigned int http_auth_digest_len (int algo);
struct http_auth_scheme_t;
struct http_auth_require_t;
struct http_auth_backend_t;
@ -16,6 +30,7 @@ typedef struct http_auth_require_t {
const struct http_auth_scheme_t *scheme;
buffer *realm;
int valid_user;
int algorithm;
array *user;
array *group;
array *host;
@ -25,10 +40,21 @@ http_auth_require_t * http_auth_require_init (void);
void http_auth_require_free (http_auth_require_t *require);
int http_auth_match_rules (const http_auth_require_t *require, const char *user, const char *group, const char *host);
typedef struct http_auth_info_t {
int dalgo;
unsigned int dlen;
const char *username;
size_t ulen;
const char *realm;
size_t rlen;
/*(must be >= largest binary digest length accepted above)*/
unsigned char digest[32];
} http_auth_info_t;
typedef struct http_auth_backend_t {
const char *name;
handler_t(*basic)(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw);
handler_t(*digest)(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]);
handler_t(*digest)(server *srv, connection *con, void *p_d, http_auth_info_t *ai);
void *p_d;
} http_auth_backend_t;

View File

@ -560,6 +560,7 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
const char *m = NULL;
int i;
buffer *b;
http_auth_info_t ai;
li_MD5_CTX Md5Ctx;
HASH HA1;
@ -670,7 +671,14 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
return mod_auth_send_400_bad_request(srv, con);
}
if (!buffer_is_equal_string(require->realm, realm, strlen(realm))) {
ai.dalgo = HTTP_AUTH_DIGEST_MD5;
ai.dlen = HTTP_AUTH_DIGEST_MD5_BINLEN;
ai.username = username;
ai.ulen = strlen(username);
ai.realm = realm;
ai.rlen = strlen(realm);
if (!buffer_is_equal_string(require->realm, ai.realm, ai.rlen)) {
log_error_write(srv, __FILE__, __LINE__, "s",
"digest: realm mismatch");
buffer_free(b);
@ -721,8 +729,7 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
}
}
/* password-string == HA1 */
switch (backend->digest(srv, con, backend->p_d, username, realm, HA1)) {
switch (backend->digest(srv, con, backend->p_d, &ai)) {
case HANDLER_GO_ON:
break;
case HANDLER_WAIT_FOR_EVENT:
@ -736,6 +743,7 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(srv, con, require->realm, 0);
}
memcpy(HA1, ai.digest, ai.dlen);
if (algorithm &&
strcasecmp(algorithm, "md5-sess") == 0) {
@ -823,7 +831,7 @@ static handler_t mod_auth_check_digest(server *srv, connection *con, void *p_d,
} /*(future: might send nextnonce when expiration is imminent)*/
}
http_auth_setenv(con, username, strlen(username), CONST_STR_LEN("Digest"));
http_auth_setenv(con, ai.username, ai.ulen, CONST_STR_LEN("Digest"));
buffer_free(b);

View File

@ -59,9 +59,9 @@ typedef struct {
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_digest(server *srv, connection *con, void *p_d, http_auth_info_t *ai);
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_digest(server *srv, connection *con, void *p_d, http_auth_info_t *ai);
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);
@ -197,20 +197,9 @@ static int mod_authn_file_patch_connection(server *srv, connection *con, plugin_
#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;
static int mod_authn_file_htdigest_get_loop(server *srv, FILE *fp, const buffer *auth_fn, http_auth_info_t *ai) {
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;
@ -246,47 +235,63 @@ static int mod_authn_file_htdigest_get(server *srv, const buffer *auth_fn, const
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))) {
if (ai->ulen == u_len && ai->rlen == r_len
&& 0 == memcmp(ai->username, f_user, u_len)
&& 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;
fclose(fp);
if (pwd_len != (ai->dlen << 1)) continue;
return http_auth_digest_hex2bin(f_pwd, pwd_len,
HA1, sizeof(HA1));
ai->digest, sizeof(ai->digest));
}
}
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]) {
static int mod_authn_file_htdigest_get(server *srv, connection *con, void *p_d, http_auth_info_t *ai) {
plugin_data *p = (plugin_data *)p_d;
buffer *username_buf = buffer_init_string(username);
buffer *realm_buf = buffer_init_string(realm);
int rc;
const buffer *auth_fn;
FILE *fp;
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;
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(srv, fp, auth_fn, ai);
fclose(fp);
return rc;
}
else {
log_error_write(srv, __FILE__, __LINE__, "sbss", "opening digest-userfile", auth_fn, "failed:", strerror(errno));
return -1;
}
}
static handler_t mod_authn_file_htdigest_digest(server *srv, connection *con, void *p_d, http_auth_info_t *ai) {
return (0 == mod_authn_file_htdigest_get(srv, con, p_d, ai))
? 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;
http_auth_info_t ai;
ai.dalgo = HTTP_AUTH_DIGEST_MD5;
ai.dlen = HTTP_AUTH_DIGEST_MD5_BINLEN;
ai.username = username->ptr;
ai.ulen = buffer_string_length(username);
ai.realm = require->realm->ptr;
ai.rlen = buffer_string_length(require->realm);
if (mod_authn_file_htdigest_get(srv, con, p_d, &ai)) return HANDLER_ERROR;
li_MD5_Init(&Md5Ctx);
li_MD5_Update(&Md5Ctx, CONST_BUF_LEN(username));
@ -296,8 +301,7 @@ static handler_t mod_authn_file_htdigest_basic(server *srv, connection *con, voi
li_MD5_Update(&Md5Ctx, (unsigned char *)pw, strlen(pw));
li_MD5_Final(HA1, &Md5Ctx);
UNUSED(con);
return (0 == memcmp(HA1, htdigest, sizeof(HA1))
return (0 == memcmp(HA1, ai.digest, ai.dlen)
&& http_auth_match_rules(require, username->ptr, NULL, NULL))
? HANDLER_GO_ON
: HANDLER_ERROR;
@ -364,12 +368,13 @@ static int mod_authn_file_htpasswd_get(server *srv, const buffer *auth_fn, const
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]) {
static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void *p_d, http_auth_info_t *ai) {
plugin_data *p = (plugin_data *)p_d;
buffer *username_buf = buffer_init_string(username);
buffer *username_buf = buffer_init();
buffer *password_buf = buffer_init();/* password-string from auth-backend */
int rc;
mod_authn_file_patch_connection(srv, con, p);
buffer_copy_string_len(username_buf, ai->username, ai->ulen);
rc = mod_authn_file_htpasswd_get(srv, p->conf.auth_plain_userfile, username_buf, password_buf);
if (0 == rc) {
/* generate password from plain-text */
@ -377,10 +382,10 @@ static handler_t mod_authn_file_plain_digest(server *srv, connection *con, void
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, (unsigned char *)ai->realm, ai->rlen);
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);
li_MD5_Final(ai->digest, &Md5Ctx);
}
buffer_free(password_buf);
buffer_free(username_buf);

View File

@ -135,7 +135,7 @@ static void mod_authn_mysql_sock_error(server *srv, plugin_config *pconf) {
}
static handler_t mod_authn_mysql_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_mysql_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]);
static handler_t mod_authn_mysql_digest(server *srv, connection *con, void *p_d, http_auth_info_t *dig);
INIT_FUNC(mod_authn_mysql_init) {
static http_auth_backend_t http_auth_backend_mysql =
@ -387,7 +387,7 @@ static int mod_authn_mysql_password_cmp(const char *userpw, unsigned long userpw
return -1;
}
static int mod_authn_mysql_result(server *srv, plugin_data *p, const char *pw, unsigned char HA1[16]) {
static int mod_authn_mysql_result(server *srv, plugin_data *p, http_auth_info_t *ai, const char *pw) {
MYSQL_RES *result = mysql_store_result(p->conf.mysql_conn);
int rc = -1;
my_ulonglong num_rows;
@ -413,8 +413,11 @@ static int mod_authn_mysql_result(server *srv, plugin_data *p, const char *pw, u
rc = mod_authn_mysql_password_cmp(row[0], lengths[0], pw);
}
else { /* used with HTTP Digest auth */
rc = http_auth_digest_hex2bin(row[0], lengths[0],
HA1, sizeof(HA1));
/*(currently supports only single row, single digest algorithm)*/
if (lengths[0] == (ai->dlen << 1)) {
rc = http_auth_digest_hex2bin(row[0], lengths[0],
ai->digest, sizeof(ai->digest));
}
}
}
else if (0 == num_rows) {
@ -428,7 +431,7 @@ static int mod_authn_mysql_result(server *srv, plugin_data *p, const char *pw, u
return rc;
}
static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, const char *username, const char *realm, const char *pw, unsigned char HA1[16]) {
static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d, http_auth_info_t *ai, const char *pw) {
plugin_data *p = (plugin_data *)p_d;
int rc = -1;
@ -443,14 +446,12 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d,
}
do {
size_t unamelen = strlen(username);
size_t urealmlen = strlen(realm);
char q[1024], uname[512], urealm[512];
unsigned long mrc;
if (unamelen > sizeof(uname)/2-1)
if (ai->ulen > sizeof(uname)/2-1)
return HANDLER_ERROR;
if (urealmlen > sizeof(urealm)/2-1)
if (ai->rlen > sizeof(urealm)/2-1)
return HANDLER_ERROR;
if (!mod_authn_mysql_sock_acquire(srv, &p->conf)) {
@ -458,20 +459,20 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d,
}
#if 0
mrc = mysql_real_escape_string_quote(p->conf.mysql_conn,uname,username,
(unsigned long)unamelen, '\'');
mrc = mysql_real_escape_string_quote(p->conf.mysql_conn, uname,
ai->username, ai->ulen, '\'');
if ((unsigned long)~0 == mrc) break;
mrc = mysql_real_escape_string_quote(p->conf.mysql_conn,urealm,realm,
(unsigned long)urealmlen, '\'');
mrc = mysql_real_escape_string_quote(p->conf.mysql_conn, urealm,
ai->realm, ai->rlen, '\'');
if ((unsigned long)~0 == mrc) break;
#else
mrc = mysql_real_escape_string(p->conf.mysql_conn, uname,
username, (unsigned long)unamelen);
ai->username, ai->ulen);
if ((unsigned long)~0 == mrc) break;
mrc = mysql_real_escape_string(p->conf.mysql_conn, urealm,
realm, (unsigned long)urealmlen);
ai->realm, ai->rlen);
if ((unsigned long)~0 == mrc) break;
#endif
@ -512,7 +513,7 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d,
}
}
rc = mod_authn_mysql_result(srv, p, pw, HA1);
rc = mod_authn_mysql_result(srv, p, ai, pw);
} while (0);
@ -522,19 +523,23 @@ static handler_t mod_authn_mysql_query(server *srv, connection *con, void *p_d,
}
static handler_t mod_authn_mysql_basic(server *srv, connection *con, void *p_d, const http_auth_require_t *require, const buffer *username, const char *pw) {
/*(HA1 is not written since pw passed should not be NULL;
* avoid passing NULL since subroutine expects unsigned char HA1[16] arg)*/
static unsigned char HA1[16];
char *realm = require->realm->ptr;
handler_t rc =mod_authn_mysql_query(srv,con,p_d,username->ptr,realm,pw,HA1);
handler_t rc;
http_auth_info_t ai;
ai.dalgo = HTTP_AUTH_DIGEST_NONE;
ai.dlen = 0;
ai.username = username->ptr;
ai.ulen = buffer_string_length(username);
ai.realm = require->realm->ptr;
ai.rlen = buffer_string_length(require->realm);
rc = mod_authn_mysql_query(srv, con, p_d, &ai, pw);
if (HANDLER_GO_ON != rc) return rc;
return http_auth_match_rules(require, username->ptr, NULL, NULL)
? HANDLER_GO_ON /* access granted */
: HANDLER_ERROR;
}
static handler_t mod_authn_mysql_digest(server *srv, connection *con, void *p_d, const char *username, const char *realm, unsigned char HA1[16]) {
return mod_authn_mysql_query(srv,con,p_d,username,realm,NULL,HA1);
static handler_t mod_authn_mysql_digest(server *srv, connection *con, void *p_d, http_auth_info_t *ai) {
return mod_authn_mysql_query(srv, con, p_d, ai, NULL);
}
int mod_authn_mysql_plugin_init(plugin *p);