#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_protocol.h"
#include "util_md5.h"
typedef
struct
digest_config_struct {
char
*pwfile;
} digest_config_rec;
typedef
struct
digest_header_struct {
char
*username;
char
*realm;
char
*nonce;
char
*requested_uri;
char
*digest;
} digest_header_rec;
static
void
*create_digest_dir_config(pool *p,
char
*d)
{
return
ap_pcalloc(p,
sizeof
(digest_config_rec));
}
static
const
char
*set_digest_slot(cmd_parms *cmd,
void
*offset,
char
*f,
char
*t)
{
if
(t &&
strcmp
(t,
"standard"
))
return
ap_pstrcat(cmd->pool,
"Invalid auth file type: "
, t, NULL);
return
ap_set_string_slot(cmd, offset, f);
}
static
const
command_rec digest_cmds[] =
{
{
"AuthDigestFile"
, set_digest_slot,
(
void
*) XtOffsetOf(digest_config_rec, pwfile), OR_AUTHCFG, TAKE12, NULL},
{NULL}
};
module MODULE_VAR_EXPORT digest_module;
static
char
*get_hash(request_rec *r,
char
*user,
char
*auth_pwfile)
{
configfile_t *f;
char
l[MAX_STRING_LEN];
const
char
*rpw;
char
*w, *x;
if
(!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
"Could not open password file: %s"
, auth_pwfile);
return
NULL;
}
while
(!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
if
((l[0] ==
'#'
) || (!l[0]))
continue
;
rpw = l;
w = ap_getword(r->pool, &rpw,
':'
);
x = ap_getword(r->pool, &rpw,
':'
);
if
(x && w && !
strcmp
(user, w) && !
strcmp
(ap_auth_name(r), x)) {
ap_cfg_closefile(f);
return
ap_pstrdup(r->pool, rpw);
}
}
ap_cfg_closefile(f);
return
NULL;
}
static
int
get_digest_rec(request_rec *r, digest_header_rec * response)
{
const
char
*auth_line;
int
l;
int
s, vk = 0, vv = 0;
const
char
*t;
char
*key, *value;
const
char
*scheme;
if
(!(t = ap_auth_type(r)) || strcasecmp(t,
"Digest"
))
return
DECLINED;
if
(!ap_auth_name(r)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"need AuthName: %s"
, r->uri);
return
SERVER_ERROR;
}
auth_line = ap_table_get(r->headers_in,
r->proxyreq == STD_PROXY ?
"Proxy-Authorization"
:
"Authorization"
);
if
(!auth_line) {
ap_note_digest_auth_failure(r);
return
AUTH_REQUIRED;
}
if
(strcasecmp(scheme = ap_getword_white(r->pool, &auth_line),
"Digest"
)) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
"client used wrong authentication scheme: %s for %s"
,
scheme, r->uri);
ap_note_digest_auth_failure(r);
return
AUTH_REQUIRED;
}
l =
strlen
(auth_line);
key = ap_palloc(r->pool, l);
value = ap_palloc(r->pool, l);
#define D_KEY 0
#define D_VALUE 1
#define D_STRING 2
#define D_EXIT -1
s = D_KEY;
while
(s != D_EXIT) {
switch
(s) {
case
D_STRING:
if
(auth_line[0] ==
'\"'
) {
s = D_VALUE;
}
else
{
value[vv] = auth_line[0];
vv++;
}
auth_line++;
break
;
case
D_VALUE:
if
(ap_isalnum(auth_line[0])) {
value[vv] = auth_line[0];
vv++;
}
else
if
(auth_line[0] ==
'\"'
) {
s = D_STRING;
}
else
{
value[vv] =
'\0'
;
if
(!strcasecmp(key,
"username"
))
response->username = ap_pstrdup(r->pool, value);
else
if
(!strcasecmp(key,
"realm"
))
response->realm = ap_pstrdup(r->pool, value);
else
if
(!strcasecmp(key,
"nonce"
))
response->nonce = ap_pstrdup(r->pool, value);
else
if
(!strcasecmp(key,
"uri"
))
response->requested_uri = ap_pstrdup(r->pool, value);
else
if
(!strcasecmp(key,
"response"
))
response->digest = ap_pstrdup(r->pool, value);
vv = 0;
s = D_KEY;
}
auth_line++;
break
;
case
D_KEY:
if
(ap_isalnum(auth_line[0])) {
key[vk] = auth_line[0];
vk++;
}
else
if
(auth_line[0] ==
'='
) {
key[vk] =
'\0'
;
vk = 0;
s = D_VALUE;
}
auth_line++;
break
;
}
if
(auth_line[-1] ==
'\0'
)
s = D_EXIT;
}
if
(!response->username || !response->realm || !response->nonce ||
!response->requested_uri || !response->digest) {
ap_note_digest_auth_failure(r);
return
AUTH_REQUIRED;
}
r->connection->user = response->username;
r->connection->ap_auth_type =
"Digest"
;
return
OK;
}
static
char
*find_digest(request_rec *r, digest_header_rec * h,
char
*a1)
{
return
ap_md5(r->pool,
(unsigned
char
*)ap_pstrcat(r->pool, a1,
":"
, h->nonce,
":"
,
ap_md5(r->pool,
(unsigned
char
*)ap_pstrcat(r->pool, r->method,
":"
,
h->requested_uri, NULL)),
NULL));
}
static
int
authenticate_digest_user(request_rec *r)
{
digest_config_rec *sec =
(digest_config_rec *) ap_get_module_config(r->per_dir_config,
&digest_module);
digest_header_rec *response = ap_pcalloc(r->pool,
sizeof
(digest_header_rec));
conn_rec *c = r->connection;
char
*a1;
int
res;
if
((res = get_digest_rec(r, response)))
return
res;
if
(!sec->pwfile)
return
DECLINED;
if
(!(a1 = get_hash(r, c->user, sec->pwfile))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s not found: %s"
, c->user, r->uri);
ap_note_digest_auth_failure(r);
return
AUTH_REQUIRED;
}
if
(
strcmp
(response->digest, find_digest(r, response, a1))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"user %s: password mismatch: %s"
, c->user, r->uri);
ap_note_digest_auth_failure(r);
return
AUTH_REQUIRED;
}
return
OK;
}
static
int
digest_check_auth(request_rec *r)
{
char
*user = r->connection->user;
int
m = r->method_number;
int
method_restricted = 0;
register
int
x;
const
char
*t;
char
*w;
const
array_header *reqs_arr;
require_line *reqs;
if
(!(t = ap_auth_type(r)) || strcasecmp(t,
"Digest"
))
return
DECLINED;
reqs_arr = ap_requires(r);
if
(!reqs_arr)
return
OK;
reqs = (require_line *) reqs_arr->elts;
for
(x = 0; x < reqs_arr->nelts; x++) {
if
(!(reqs[x].method_mask & (1 << m)))
continue
;
method_restricted = 1;
t = reqs[x].requirement;
w = ap_getword_white(r->pool, &t);
if
(!
strcmp
(w,
"valid-user"
))
return
OK;
else
if
(!
strcmp
(w,
"user"
)) {
while
(t[0]) {
w = ap_getword_conf(r->pool, &t);
if
(!
strcmp
(user, w))
return
OK;
}
}
else
return
DECLINED;
}
if
(!method_restricted)
return
OK;
ap_note_digest_auth_failure(r);
return
AUTH_REQUIRED;
}
module MODULE_VAR_EXPORT digest_module =
{
STANDARD_MODULE_STUFF,
NULL,
create_digest_dir_config,
NULL,
NULL,
NULL,
digest_cmds,
NULL,
NULL,
authenticate_digest_user,
digest_check_auth,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};