[mod_auth] refactor mod_auth_check_digest()

refactor mod_auth_check_digest()
- smaller functions
- collect parsed Authorization header into http_auth_digest_params_t
- use string references rather than copying and modifying Authorization
master
Glenn Strauss 2 years ago
parent 60b773a6cb
commit 20c56cecc3

@ -721,6 +721,8 @@ static handler_t mod_auth_send_400_bad_request(request_st * const r) {
return HANDLER_FINISHED;
}
__attribute_noinline__
static handler_t mod_auth_send_401_unauthorized_basic(request_st * const r, const buffer * const realm) {
r->http_status = 401;
@ -838,7 +840,32 @@ static handler_t mod_auth_check_basic(request_st * const r, void *p_d, const str
}
static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const char *uri, const char *nonce, const char *cnonce, const char *nc, const char *qop) {
enum http_auth_digest_params_e {
e_username = 0
,e_realm
,e_nonce
,e_uri
,e_algorithm
,e_qop
,e_cnonce
,e_nc
,e_response
,http_auth_digest_params_sz /*(last item)*/
};
typedef struct http_auth_digest_params_t {
const char *ptr[http_auth_digest_params_sz];
uint16_t len[http_auth_digest_params_sz];
time_t send_nextnonce_ts;
unsigned char rdigest[MD_DIGEST_LENGTH_MAX]; /*(last member)*/
} http_auth_digest_params_t;
static void
mod_auth_digest_mutate (http_auth_info_t * const ai, const http_auth_digest_params_t * const dp, const char * const method)
{
force_assert(method);
li_md_iov_fn digest_iov = MD5_iov;
/* (ai->dalgo & HTTP_AUTH_DIGEST_MD5) default */
#ifdef USE_LIB_CRYPTO
@ -863,27 +890,28 @@ static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const ch
iov[0].iov_len = ai->dlen*2;
iov[1].iov_base = ":";
iov[1].iov_len = 1;
iov[2].iov_base = nonce;
iov[2].iov_len = strlen(nonce);
iov[2].iov_base = dp->ptr[e_nonce];
iov[2].iov_len = dp->len[e_nonce];
iov[3].iov_base = ":";
iov[3].iov_len = 1;
iov[4].iov_base = cnonce;
iov[4].iov_len = strlen(cnonce);
iov[4].iov_base = dp->ptr[e_cnonce];
iov[4].iov_len = dp->len[e_cnonce];
digest_iov(ai->digest, iov, 5);
li_tohex(a1, sizeof(a1), (const char *)ai->digest, ai->dlen);
}
/* calculate H(A2) */
iov[0].iov_base = m;
iov[0].iov_len = strlen(m);
iov[0].iov_base = method;
iov[0].iov_len = strlen(method);
iov[1].iov_base = ":";
iov[1].iov_len = 1;
iov[2].iov_base = uri;
iov[2].iov_len = strlen(uri);
iov[2].iov_base = dp->ptr[e_uri];
iov[2].iov_len = dp->len[e_uri];
n = 3;
#if 0
/* qop=auth-int not supported, already checked in caller */
if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){
if (dp->ptr[e_qop] && buffer_eq_icase_ss(dp->ptr[e_qop], dp->len[e_qop],
CONST_STR_LEN("auth-int"))) {
iov[3].iov_base = ":";
iov[3].iov_len = 1;
iov[4].iov_base = [body checksum];
@ -899,22 +927,22 @@ static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const ch
iov[0].iov_len = ai->dlen*2;
iov[1].iov_base = ":";
iov[1].iov_len = 1;
iov[2].iov_base = nonce;
iov[2].iov_len = strlen(nonce);
iov[2].iov_base = dp->ptr[e_nonce];
iov[2].iov_len = dp->len[e_nonce];
iov[3].iov_base = ":";
iov[3].iov_len = 1;
n = 4;
if (qop && *qop) {
iov[4].iov_base = nc;
iov[4].iov_len = strlen(nc);
if (dp->len[e_qop]) {
iov[4].iov_base = dp->ptr[e_nc];
iov[4].iov_len = dp->len[e_nc];
iov[5].iov_base = ":";
iov[5].iov_len = 1;
iov[6].iov_base = cnonce;
iov[6].iov_len = strlen(cnonce);
iov[6].iov_base = dp->ptr[e_cnonce];
iov[6].iov_len = dp->len[e_cnonce];
iov[7].iov_base = ":";
iov[7].iov_len = 1;
iov[8].iov_base = qop;
iov[8].iov_len = strlen(qop);
iov[8].iov_base = dp->ptr[e_qop];
iov[8].iov_len = dp->len[e_qop];
iov[9].iov_base = ":";
iov[9].iov_len = 1;
n = 10;
@ -924,7 +952,10 @@ static void mod_auth_digest_mutate(http_auth_info_t *ai, const char *m, const ch
digest_iov(ai->digest, iov, n+1);
}
static void mod_auth_append_nonce(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo, int *rndptr) {
static void
mod_auth_append_nonce (buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo, int *rndptr)
{
buffer_append_uint_hex(b, (uintmax_t)cur_ts);
buffer_append_string_len(b, CONST_STR_LEN(":"));
const buffer * const nonce_secret = require->nonce_secret;
@ -985,7 +1016,10 @@ static void mod_auth_append_nonce(buffer *b, time_t cur_ts, const struct http_au
li_tohex(buffer_extend(b, n*2), n*2+1, (const char *)h, n);
}
static void mod_auth_digest_www_authenticate(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int nonce_stale) {
static void
mod_auth_digest_www_authenticate (buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int nonce_stale)
{
int algos = nonce_stale ? nonce_stale : require->algorithm;
int n = 0;
int algoid[3];
@ -1033,16 +1067,31 @@ static void mod_auth_digest_www_authenticate(buffer *b, time_t cur_ts, const str
}
}
__attribute_noinline__
static handler_t mod_auth_send_401_unauthorized_digest(request_st *r, const struct http_auth_require_t *require, int nonce_stale);
static handler_t
mod_auth_send_401_unauthorized_digest(request_st * const r, const struct http_auth_require_t * const require, int nonce_stale)
{
r->http_status = 401;
r->handler_module = NULL;
mod_auth_digest_www_authenticate(
http_header_response_set_ptr(r, HTTP_HEADER_WWW_AUTHENTICATE,
CONST_STR_LEN("WWW-Authenticate")),
log_epoch_secs, require, nonce_stale);
return HANDLER_FINISHED;
}
static void mod_auth_digest_authentication_info(buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo) {
static void
mod_auth_digest_authentication_info (buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo)
{
buffer_clear(b);
buffer_append_string_len(b, CONST_STR_LEN("nextnonce=\""));
mod_auth_append_nonce(b, cur_ts, require, dalgo, NULL);
buffer_append_string_len(b, CONST_STR_LEN("\""));
}
static handler_t
mod_auth_digest_get (request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend, http_auth_info_t * const ai)
{
@ -1092,321 +1141,298 @@ mod_auth_digest_get (request_st * const r, void *p_d, const struct http_auth_req
return rc;
}
typedef struct {
const char *key;
int key_len;
char **ptr;
} digest_kv;
static handler_t mod_auth_check_digest(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) {
char *username = NULL;
char *realm = NULL;
char *nonce = NULL;
char *uri = NULL;
char *algorithm = NULL;
char *qop = NULL;
char *cnonce = NULL;
char *nc = NULL;
char *respons = NULL;
char *e, *c;
int i;
buffer *b;
http_auth_info_t ai;
unsigned char rdigest[MD_DIGEST_LENGTH_MAX];
/* init pointers */
#define S(x) \
x, sizeof(x)-1, NULL
digest_kv dkv[10] = {
{ S("username=") },
{ S("realm=") },
{ S("nonce=") },
{ S("uri=") },
{ S("algorithm=") },
{ S("qop=") },
{ S("cnonce=") },
{ S("nc=") },
{ S("response=") },
{ NULL, 0, NULL }
};
#undef S
dkv[0].ptr = &username;
dkv[1].ptr = &realm;
dkv[2].ptr = &nonce;
dkv[3].ptr = &uri;
dkv[4].ptr = &algorithm;
dkv[5].ptr = &qop;
dkv[6].ptr = &cnonce;
dkv[7].ptr = &nc;
dkv[8].ptr = &respons;
if (NULL == backend || NULL == backend->digest) {
if (NULL == backend)
log_error(r->conf.errh, __FILE__, __LINE__,
"auth.backend not configured for %s", r->uri.path.ptr);
else
log_error(r->conf.errh, __FILE__, __LINE__,
"auth.require \"method\" => \"digest\" invalid "
"(try \"basic\"?) for %s",
r->uri.path.ptr);
r->http_status = 500;
r->handler_module = NULL;
return HANDLER_FINISHED;
}
const buffer * const vb =
http_header_request_get(r, HTTP_HEADER_AUTHORIZATION,
CONST_STR_LEN("Authorization"));
__attribute_cold__
static handler_t
mod_auth_digest_misconfigured (request_st * const r, const struct http_auth_backend_t * const backend)
{
if (NULL == backend)
log_error(r->conf.errh, __FILE__, __LINE__,
"auth.backend not configured for %s", r->uri.path.ptr);
else
log_error(r->conf.errh, __FILE__, __LINE__,
"auth.require \"method\" => \"digest\" invalid "
"(try \"basic\"?) for %s", r->uri.path.ptr);
r->http_status = 500;
r->handler_module = NULL;
return HANDLER_FINISHED;
}
if (NULL == vb || !buffer_eq_icase_ssn(vb->ptr, CONST_STR_LEN("Digest "))) {
return mod_auth_send_401_unauthorized_digest(r, require, 0);
} else {
size_t n = buffer_clen(vb);
#ifdef __COVERITY__
if (n < sizeof("Digest ")-1) {
return mod_auth_send_400_bad_request(r);
}
#endif
n -= (sizeof("Digest ")-1);
b = buffer_init();
buffer_copy_string_len(b,vb->ptr+sizeof("Digest ")-1,n);
}
/* parse credentials from client */
for (c = b->ptr; *c; c++) {
/* skip whitespaces */
while (*c == ' ' || *c == '\t') c++;
if (!*c) break;
for (i = 0; dkv[i].key; i++) {
if ((0 == strncmp(c, dkv[i].key, dkv[i].key_len))) {
if ((c[dkv[i].key_len] == '"') &&
(NULL != (e = strchr(c + dkv[i].key_len + 1, '"')))) {
/* value with "..." */
*(dkv[i].ptr) = c + dkv[i].key_len + 1;
c = e;
*e = '\0';
} else if (NULL != (e = strchr(c + dkv[i].key_len, ','))) {
/* value without "...", terminated by ',' */
*(dkv[i].ptr) = c + dkv[i].key_len;
c = e;
*e = '\0';
} else {
/* value without "...", terminated by EOL */
*(dkv[i].ptr) = c + dkv[i].key_len;
c += strlen(c) - 1;
}
break;
}
}
}
static void
mod_auth_digest_parse_authorization (http_auth_digest_params_t * const dp, const buffer * const vb)
{
struct digest_kv {
const char *key;
uint32_t klen;
enum http_auth_digest_params_e id;
};
/* check if everything is transmitted */
if (!username ||
!realm ||
!nonce ||
!uri ||
(qop && (!nc || !cnonce)) ||
!respons ) {
/* missing field */
static const struct digest_kv dkv[] = {
{ CONST_STR_LEN("username="), e_username },
{ CONST_STR_LEN("realm="), e_realm },
{ CONST_STR_LEN("nonce="), e_nonce },
{ CONST_STR_LEN("uri="), e_uri },
{ CONST_STR_LEN("algorithm="), e_algorithm },
{ CONST_STR_LEN("qop="), e_qop },
{ CONST_STR_LEN("cnonce="), e_cnonce },
{ CONST_STR_LEN("nc="), e_nc },
{ CONST_STR_LEN("response="), e_response },
{ NULL, 0, http_auth_digest_params_sz }
};
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: missing field");
/* parse credentials from client */
/* note: parsing does not recognize and handle BWS (bad whitespace) */
/* XXX: might find end of token and strncmp() only when len matches */
/* (caller already checked that vb->ptr begins with "Digest ") */
/* coverity[overflow_sink : FALSE] */
for (const char *c = vb->ptr+sizeof("Digest ")-1, *e; *c; c++) {
/* skip whitespaces */
while (*c == ' ' || *c == '\t') c++;
if (!*c) break;
for (int i = 0; dkv[i].key; ++i) {
if (0 != strncmp(c, dkv[i].key, dkv[i].klen))
continue;
buffer_free(b);
return mod_auth_send_400_bad_request(r);
}
if ((c[dkv[i].klen] == '"') &&
(NULL != (e = strchr(c + dkv[i].klen + 1, '"')))) {
/* value with "..." */
c += dkv[i].klen + 1;
dp->ptr[dkv[i].id] = c;
dp->len[dkv[i].id] = (uint16_t)(e - c);
c = e;
}
else if (NULL != (e = strchr(c + dkv[i].klen, ','))) {
/* value without "...", terminated by ',' */
c += dkv[i].klen;
dp->ptr[dkv[i].id] = c;
dp->len[dkv[i].id] = (uint16_t)(e - c);
c = e;
}
else {
/* value without "...", terminated by EOL */
c += dkv[i].klen;
dp->ptr[dkv[i].id] = c;
c += (dp->len[dkv[i].id] = (uint16_t)strlen(c)) - 1;
}
break;
}
}
}
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(r->conf.errh, __FILE__, __LINE__,
"digest: realm mismatch");
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
static handler_t
mod_auth_digest_validate_params (request_st * const r, const struct http_auth_require_t * const require, http_auth_digest_params_t * const dp, http_auth_info_t * const ai)
{
/* check for required parameters */
if ((!dp->ptr[e_qop] || (dp->ptr[e_nc] && dp->ptr[e_cnonce]))
&& dp->ptr[e_username]
&& dp->ptr[e_realm]
&& dp->ptr[e_nonce]
&& dp->ptr[e_uri]
&& dp->ptr[e_response]) {
ai->username = dp->ptr[e_username];
ai->ulen = dp->len[e_username];
ai->realm = dp->ptr[e_realm];
ai->rlen = dp->len[e_realm];
}
else {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: missing field");
return mod_auth_send_400_bad_request(r);
}
if (!mod_auth_algorithm_parse(&ai, algorithm, strlen(algorithm))
|| !(require->algorithm & ai.dalgo & ~HTTP_AUTH_DIGEST_SESS)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: (%s): invalid", algorithm);
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
if (!buffer_eq_slen(require->realm, ai->realm, ai->rlen)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: realm mismatch");
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
/**
* protect the md5-sess against missing cnonce and nonce
*/
if ((ai.dalgo & HTTP_AUTH_DIGEST_SESS) && (!nonce || !cnonce)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: (%s): missing field", algorithm);
if (!mod_auth_algorithm_parse(ai,dp->ptr[e_algorithm],dp->len[e_algorithm])
|| !(require->algorithm & ai->dalgo & ~HTTP_AUTH_DIGEST_SESS)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: (%.*s): invalid",
(int)dp->len[e_algorithm], dp->ptr[e_algorithm]);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
buffer_free(b);
return mod_auth_send_400_bad_request(r);
}
/* *-sess requires nonce and cnonce */
if ((ai->dalgo & HTTP_AUTH_DIGEST_SESS)
&& (!dp->ptr[e_nonce] || !dp->ptr[e_cnonce])) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: (%.*s): missing field",
(int)dp->len[e_algorithm], dp->ptr[e_algorithm]);
return mod_auth_send_400_bad_request(r);
}
{
size_t resplen = strlen(respons);
if (0 != li_hex2bin(rdigest, sizeof(rdigest), respons, resplen)
|| resplen != (ai.dlen << 1)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: (%s): invalid format", respons);
buffer_free(b);
return mod_auth_send_400_bad_request(r);
}
}
if (0 != li_hex2bin(dp->rdigest, sizeof(dp->rdigest),
dp->ptr[e_response], dp->len[e_response])
|| dp->len[e_response] != (ai->dlen << 1)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: (%s): invalid format", dp->ptr[e_response]);
return mod_auth_send_400_bad_request(r);
}
if (qop && buffer_eq_icase_ss(qop, strlen(qop), CONST_STR_LEN("auth-int"))){
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: qop=auth-int not supported");
if (dp->ptr[e_qop]&& buffer_eq_icase_ss(dp->ptr[e_qop], dp->len[e_qop],
CONST_STR_LEN("auth-int"))) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: qop=auth-int not supported");
return mod_auth_send_400_bad_request(r);
}
buffer_free(b);
return mod_auth_send_400_bad_request(r);
}
/* detect if attacker is attempting to reuse valid digest for one uri
* on a different request uri. Might also happen if intermediate proxy
* altered client request line. (Altered request would not result in
* the same digest as that calculated by the client.)
* Internal redirects such as with mod_rewrite will modify request uri.
* Reauthentication is done to detect crossing auth realms, but this
* uri validation step is bypassed. r->target_orig is
* original request-target sent in client request. */
if (!buffer_eq_slen(&r->target_orig, dp->ptr[e_uri], dp->len[e_uri])) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: auth failed: uri mismatch (%s != %.*s), IP: %s",
r->target_orig.ptr, (int)dp->len[e_uri], dp->ptr[e_uri],
r->con->dst_addr_buf->ptr);
return mod_auth_send_400_bad_request(r);
}
/* detect if attacker is attempting to reuse valid digest for one uri
* on a different request uri. Might also happen if intermediate proxy
* altered client request line. (Altered request would not result in
* the same digest as that calculated by the client.)
* Internal redirects such as with mod_rewrite will modify request uri.
* Reauthentication is done to detect crossing auth realms, but this
* uri validation step is bypassed. r->target_orig is
* original request-target sent in client request. */
{
const size_t ulen = strlen(uri);
if (!buffer_is_equal_string(&r->target_orig, uri, ulen)) {
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: auth failed: uri mismatch (%s != %s), IP: %s",
r->target_orig.ptr, uri, r->con->dst_addr_buf->ptr);
buffer_free(b);
return mod_auth_send_400_bad_request(r);
}
}
return HANDLER_GO_ON;
}
/* check age of nonce. Note, random data is used in nonce generation
* in mod_auth_send_401_unauthorized_digest(). If that were replaced
* with nanosecond time, then nonce secret would remain unique enough
* for the purposes of Digest auth, and would be reproducible (and
* verifiable) if nanoseconds were included with seconds as part of the
* nonce "timestamp:secret". However, doing so would expose a high
* precision timestamp of the system to attackers. timestamp in nonce
* could theoretically be modified and still produce same md5sum, but
* that is highly unlikely within a 10 min (moving) window of valid
* time relative to current time (now).
* If it is desired to validate that nonces were generated by server,
* then specify auth.require = ( ... => ( "secret" => "..." ) )
* When secret is specified, then instead of nanoseconds, the random
* data value (included for unique nonces) will be exposed in the nonce
* along with the timestamp, and the additional secret will be used to
* validate that the server generated the nonce using that secret. */
time_t send_nextnonce;
{
time_t ts = 0;
const unsigned char * const nonce_uns = (unsigned char *)nonce;
for (i = 0; i < 8 && light_isxdigit(nonce_uns[i]); ++i) {
ts =(time_t)((uint32_t)ts << 4) | hex2int(nonce_uns[i]);
}
const time_t cur_ts = log_epoch_secs;
if (nonce[i] != ':'
|| ts > cur_ts || cur_ts - ts > 600) { /*(10 mins)*/
/* nonce is stale; have client regenerate digest */
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, ai.dalgo);
}
send_nextnonce = (cur_ts - ts > 540) ? cur_ts : 0; /*(9 mins)*/
static handler_t
mod_auth_digest_validate_nonce (request_st * const r, const struct http_auth_require_t * const require, http_auth_digest_params_t * const dp, http_auth_info_t * const ai)
{
/* check age of nonce. Note, random data is used in nonce generation
* in mod_auth_send_401_unauthorized_digest(). If that were replaced
* with nanosecond time, then nonce secret would remain unique enough
* for the purposes of Digest auth, and would be reproducible (and
* verifiable) if nanoseconds were included with seconds as part of the
* nonce "timestamp:secret". However, doing so would expose a high
* precision timestamp of the system to attackers. timestamp in nonce
* could theoretically be modified and still produce same md5sum, but
* that is highly unlikely within a 10 min (moving) window of valid
* time relative to current time (now).
* If it is desired to validate that nonces were generated by server,
* then specify auth.require = ( ... => ( "secret" => "..." ) )
* When secret is specified, then instead of nanoseconds, the random
* data value (included for unique nonces) will be exposed in the nonce
* along with the timestamp, and the additional secret will be used to
* validate that the server generated the nonce using that secret. */
time_t ts = 0;
const unsigned char * const nonce = (unsigned char *)dp->ptr[e_nonce];
int i;
for (i = 0; i < 8 && light_isxdigit(nonce[i]); ++i)
ts =(time_t)((uint32_t)ts << 4) | hex2int(nonce[i]);
const time_t cur_ts = log_epoch_secs;
if (nonce[i] != ':' || ts > cur_ts || cur_ts - ts > 600) { /*(10 mins)*/
/* nonce is stale; have client regenerate digest */
return mod_auth_send_401_unauthorized_digest(r, require, ai->dalgo);
}
if (require->nonce_secret) {
unsigned int rnd = 0;
for (int j = i+8; i < j && light_isxdigit(nonce_uns[i]); ++i) {
rnd = (rnd << 4) + hex2int(nonce_uns[i]);
}
if (nonce[i] != ':') {
/* nonce is invalid;
* expect extra field w/ require->nonce_secret */
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: nonce invalid");
buffer_free(b);
return mod_auth_send_400_bad_request(r);
}
buffer * const tb = r->tmp_buf;
buffer_clear(tb);
mod_auth_append_nonce(tb, cur_ts, require, ai.dalgo, (int *)&rnd);
if (!buffer_eq_slen(tb, nonce, strlen(nonce))) {
/* nonce not generated using current require->nonce_secret */
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: nonce mismatch");
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
}
}
if (cur_ts - ts > 540) /*(9 mins)*/
dp->send_nextnonce_ts = cur_ts;
handler_t rc;
rc = mod_auth_digest_get(r, p_d, require, backend, &ai);
if (__builtin_expect( (HANDLER_GO_ON != rc), 0)) {
buffer_free(b);
return rc;
}
if (require->nonce_secret) {
unsigned int rnd = 0;
for (int j = i+8; i < j && light_isxdigit(nonce[i]); ++i) {
rnd = (rnd << 4) + hex2int(nonce[i]);
}
if (nonce[i] != ':') {
/* nonce is invalid;
* expect extra field w/ require->nonce_secret */
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: nonce invalid");
return mod_auth_send_400_bad_request(r);
}
buffer * const tb = r->tmp_buf;
buffer_clear(tb);
mod_auth_append_nonce(tb, cur_ts, require, ai->dalgo, (int *)&rnd);
if (!buffer_eq_slen(tb, dp->ptr[e_nonce], dp->len[e_nonce])) {
/* nonce not generated using current require->nonce_secret */
log_error(r->conf.errh, __FILE__, __LINE__,
"digest: nonce mismatch");
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
}
const char *m = get_http_method_name(r->http_method);
force_assert(m);
return HANDLER_GO_ON;
}
mod_auth_digest_mutate(&ai,m,uri,nonce,cnonce,nc,qop);
if (!ck_memeq_const_time_fixed_len(rdigest, ai.digest, ai.dlen)) {
/*ck_memzero(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",
username, r->con->dst_addr_buf->ptr);
r->keep_alive = -1; /*(disable keep-alive if bad password)*/
static handler_t
mod_auth_check_digest (request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend)
{
if (NULL == backend || NULL == backend->digest)
return mod_auth_digest_misconfigured(r, backend);
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
/*ck_memzero(ai.digest, ai.dlen);*//* skip clear since mutated */
const buffer * const vb =
http_header_request_get(r, HTTP_HEADER_AUTHORIZATION,
CONST_STR_LEN("Authorization"));
if (NULL == vb || !buffer_eq_icase_ssn(vb->ptr, CONST_STR_LEN("Digest ")))
return mod_auth_send_401_unauthorized_digest(r, require, 0);
#ifdef __COVERITY__
if (buffer_clen(vb) < sizeof("Digest ")-1)
return mod_auth_send_400_bad_request(r);
#endif
/* value is our allow-rules */
if (!http_auth_match_rules(require, username, NULL, NULL)) {
buffer_free(b);
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
http_auth_digest_params_t dp;
http_auth_info_t ai;
handler_t rc;
if (send_nextnonce) {
/*(send nextnonce when expiration is approaching)*/
mod_auth_digest_authentication_info(
http_header_response_set_ptr(r, HTTP_HEADER_OTHER,
CONST_STR_LEN("Authentication-Info")),
send_nextnonce /*(cur_ts)*/, require, ai.dalgo);
}
memset(&dp, 0, sizeof(dp) - sizeof(dp.rdigest));
http_auth_setenv(r, ai.username, ai.ulen, CONST_STR_LEN("Digest"));
mod_auth_digest_parse_authorization(&dp, vb);
buffer_free(b);
rc = mod_auth_digest_validate_params(r, require, &dp, &ai);
if (__builtin_expect( (HANDLER_GO_ON != rc), 0))
return rc;
return HANDLER_GO_ON;
}
rc = mod_auth_digest_validate_nonce(r, require, &dp, &ai);
if (__builtin_expect( (HANDLER_GO_ON != rc), 0))
return rc;
static handler_t mod_auth_send_401_unauthorized_digest(request_st * const r, const struct http_auth_require_t * const require, int nonce_stale) {
r->http_status = 401;
r->handler_module = NULL;
mod_auth_digest_www_authenticate(
http_header_response_set_ptr(r, HTTP_HEADER_WWW_AUTHENTICATE,
CONST_STR_LEN("WWW-Authenticate")),
log_epoch_secs, require, nonce_stale);
return HANDLER_FINISHED;
rc = mod_auth_digest_get(r, p_d, require, backend, &ai);
if (__builtin_expect( (HANDLER_GO_ON != rc), 0))
return rc;
mod_auth_digest_mutate(&ai, &dp, get_http_method_name(r->http_method));
if (!ck_memeq_const_time_fixed_len(dp.rdigest, ai.digest, ai.dlen)) {
/*ck_memzero(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",
(int)ai.ulen, ai.username, r->con->dst_addr_buf->ptr);
r->keep_alive = -1; /*(disable keep-alive if bad password)*/
return mod_auth_send_401_unauthorized_digest(r, require, 0);
}
/*ck_memzero(ai.digest, ai.dlen);*//* skip clear since mutated */
/* check authorization (authz); string args must be '\0'-terminated) */
buffer * const tb = r->tmp_buf;
buffer_copy_string_len(tb, ai.username, ai.ulen);
if (!http_auth_match_rules(require, tb->ptr, NULL, NULL))
return mod_auth_send_401_unauthorized_digest(r, require, 0);
if (dp.send_nextnonce_ts) {
/*(send nextnonce when expiration is approaching)*/
mod_auth_digest_authentication_info(
http_header_response_set_ptr(r, HTTP_HEADER_OTHER,
CONST_STR_LEN("Authentication-Info")),
dp.send_nextnonce_ts /*(cur_ts)*/, require, ai.dalgo);
}
http_auth_setenv(r, ai.username, ai.ulen, CONST_STR_LEN("Digest"));
return HANDLER_GO_ON;
}
static handler_t mod_auth_check_extern(request_st * const r, void *p_d, const struct http_auth_require_t * const require, const struct http_auth_backend_t * const backend) {
/* require REMOTE_USER already set */
const buffer *vb = http_header_env_get(r, CONST_STR_LEN("REMOTE_USER"));

Loading…
Cancel
Save