ronald 99/11/03 22:09:58
Modified: src/modules/standard mod_auth_digest.c Log: First cut at port to apache-2.0. Simple tests are working. Error handling is still not quite clear in some cases (especially in conjunction with ap_pcfg_openfile), and random-number generation still needs to be moved into APR. Revision Changes Path 1.3 +114 -64 apache-2.0/src/modules/standard/mod_auth_digest.c Index: mod_auth_digest.c =================================================================== RCS file: /home/cvs/apache-2.0/src/modules/standard/mod_auth_digest.c,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- mod_auth_digest.c 1999/10/22 16:01:25 1.2 +++ mod_auth_digest.c 1999/11/04 06:09:54 1.3 @@ -130,6 +130,12 @@ #include "util_uri.h" #include "util_md5.h" #include "ap_sha1.h" + +#ifdef WIN32 +/* Crypt APIs are available on Win95 with OSR 2 */ +#include <wincrypt.h> +#endif + #ifdef HAVE_SHMEM_MM #include "mm.h" #endif /* HAVE_SHMEM_MM */ @@ -160,7 +166,7 @@ #define NONCE_TIME_LEN (((sizeof(time_t)+2)/3)*4) -#define NONCE_HASH_LEN 40 +#define NONCE_HASH_LEN (2*SHA_DIGESTSIZE) #define NONCE_LEN (NONCE_TIME_LEN + NONCE_HASH_LEN) #define SECRET_LEN 20 @@ -172,12 +178,12 @@ unsigned long key; /* the key for this entry */ struct hash_entry *next; /* next entry in the bucket */ unsigned long nonce_count; /* for nonce-count checking */ - char ha1[17]; /* for algorithm=MD5-sess */ + char ha1[2*MD5_DIGESTSIZE+1]; /* for algorithm=MD5-sess */ char last_nonce[NONCE_LEN+1]; /* for one-time nonce's */ } client_entry; static struct hash_table { - client_entry **table; + client_entry **ap_table_t; unsigned long tbl_len; unsigned long num_entries; unsigned long num_created; @@ -249,7 +255,7 @@ */ #ifdef HAVE_SHMEM_MM -static void cleanup_tables(void *not_used) +static ap_status_t cleanup_tables(void *not_used) { fprintf(stderr, "Digest: cleaning up shared memory\n"); fflush(stderr); @@ -268,43 +274,64 @@ mm_destroy(otn_count_mm); otn_count_mm = NULL; } + + return APR_SUCCESS; } #endif /* HAVE_SHMEM_MM */ +#ifdef WIN32 +/* TODO: abstract out the random number generation. APR? */ +static void initialize_secret(server_rec *s) +{ + HCRYPTPROV hProv; + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, + "Digest: generating secret for digest authentication ..."); + if (!CryptAcquireContext(&hProv,NULL,NULL,PROV_RSA_FULL,0)) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "Digest: Error acquiring context. Errno = %d", + GetLastError()); + exit(EXIT_FAILURE); + } + if (!CryptGenRandom(hProv,sizeof(secret),secret)) { + ap_log_error(APLOG_MARK, APLOG_CRIT, s, + "Digest: Error generating secret. Errno = %d", + GetLastError()); + exit(EXIT_FAILURE); + } + + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "Digest: done"); +} +#else static void initialize_secret(server_rec *s) { #ifdef DEV_RANDOM - FILE *rnd; + int rnd; size_t got, tot; #else extern int randbyte(void); /* from the truerand library */ unsigned int idx; #endif - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, s, "Digest: generating secret for digest authentication ..."); #ifdef DEV_RANDOM #define XSTR(x) #x #define STR(x) XSTR(x) - if ((rnd = fopen(STR(DEV_RANDOM), "rb")) == NULL) { + if ((rnd = open(STR(DEV_RANDOM), O_RDONLY)) == NULL) { ap_log_error(APLOG_MARK, APLOG_CRIT, s, "Digest: Couldn't open " STR(DEV_RANDOM)); exit(EXIT_FAILURE); } - if (setvbuf(rnd, NULL, _IONBF, 0) != 0) { - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_CRIT, s, - "Digest: Error trying to disable buffering for " STR(DEV_RANDOM)); - exit(EXIT_FAILURE); - } for (tot=0; tot<sizeof(secret); tot += got) { - if ((got = fread(secret+tot, 1, sizeof(secret)-tot, rnd)) < 1) { + if ((got = read(rnd, secret+tot, sizeof(secret)-tot)) < 0) { ap_log_error(APLOG_MARK, APLOG_CRIT, s, "Digest: Error reading " STR(DEV_RANDOM)); exit(EXIT_FAILURE); } } - fclose(rnd); + close(rnd); #undef STR #undef XSTR #else /* use truerand */ @@ -315,8 +342,9 @@ secret[idx] = (unsigned char) randbyte(); #endif /* DEV_RANDOM */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "Digest: done"); + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, 0, s, "Digest: done"); } +#endif #ifdef HAVE_SHMEM_MM static void initialize_tables(server_rec *s) @@ -404,7 +432,8 @@ } #endif /* HAVE_SHMEM_MM */ -static void initialize_module(server_rec *s, pool *p) +static void initialize_module(ap_context_t *p, ap_context_t *plog, + ap_context_t *ptemp, server_rec *s) { /* keep from doing the init more than once at startup, and delay * the init until the second round @@ -428,7 +457,6 @@ * creating a creeping memory leak. */ initialize_tables(s); - /* atexit(cleanup_tables); */ ap_register_cleanup(p, NULL, cleanup_tables, ap_null_cleanup); #endif /* HAVE_SHMEM_MM */ } @@ -438,7 +466,7 @@ * configuration code */ -static void *create_digest_dir_config(pool *p, char *dir) +static void *create_digest_dir_config(ap_context_t *p, char *dir) { digest_config_rec *conf; @@ -509,7 +537,7 @@ } if (!strcasecmp(op, "auth-int")) - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, cmd->server, + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, cmd->server, "Digest: WARNING: qop `auth-int' currently only works " "correctly for responses with no entity"); else if (strcasecmp(op, "auth")) @@ -559,7 +587,7 @@ #ifdef HAVE_SHMEM_MM ; #else /* HAVE_SHMEM_MM */ - ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, cmd->server, + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, 0, cmd->server, "Digest: WARNING: algorithm `MD5-sess' is currently not " "correctly implemented"); #endif /* HAVE_SHMEM_MM */ @@ -909,7 +937,7 @@ * Note that this must be called after mod_proxy had its go so that * r->proxyreq is set correctly. */ -static int update_nonce_count(request_rec *r) +static int parse_hdr_and_update_nc(request_rec *r) { digest_header_rec *resp; int res; @@ -969,7 +997,7 @@ /* The nonce has the format b64(time)+hash . */ -static const char *gen_nonce(pool *p, time_t now, const char *opaque, +static const char *gen_nonce(ap_context_t *p, time_t now, const char *opaque, const server_rec *server, const digest_config_rec *conf) { @@ -1075,12 +1103,12 @@ if (ha1 == NULL || ha1[0] == '\0') { urp = get_userpw_hash(r, resp, conf); ha1 = ap_md5(r->pool, - (unsigned char *) ap_pstrcat(r->pool, ha1, ":", resp->nonce, + (unsigned char *) ap_pstrcat(r->pool, urp, ":", resp->nonce, ":", resp->cnonce, NULL)); if (!resp->client) resp->client = gen_client(r); if (resp->client) - memcpy(resp->client->ha1, ha1, 17); + memcpy(resp->client->ha1, ha1, sizeof(resp->client->ha1)); } return ha1; @@ -1098,8 +1126,8 @@ * Authorization challenge generation code (for WWW-Authenticate) */ -static const char *guess_domain(pool *p, const char *uri, const char *filename, - const char *dir) +static const char *guess_domain(ap_context_t *p, const char *uri, + const char *filename, const char *dir) { size_t u_len = strlen(uri), f_len = strlen(filename), d_len = strlen(dir); const char *u, *f; @@ -1163,7 +1191,7 @@ } -static const char *ltox(pool *p, unsigned long num) +static const char *ltox(ap_context_t *p, unsigned long num) { if (num != 0) return ap_psprintf(p, "%lx", num); @@ -1241,14 +1269,16 @@ * unneccessarily (it's usually > 200 bytes!). */ - if (conf->uri_list) + if (r->proxyreq) + domain = NULL; /* don't send domain for proxy requests */ + else if (conf->uri_list) domain = conf->uri_list; else { /* They didn't specify any domain, so let's guess at it */ domain = guess_domain(r->pool, resp->request_uri->path, r->filename, conf->dir_name); if (domain[0] == '/' && domain[1] == '\0') - domain = ""; /* "/" is the default, so no need to send it */ + domain = NULL; /* "/" is the default, so no need to send it */ else domain = ap_pstrcat(r->pool, ", domain=\"", domain, "\"", NULL); } @@ -1275,8 +1305,9 @@ char l[MAX_STRING_LEN]; const char *rpw; char *w, *x; + ap_status_t sts; - if (!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) { + if ((sts = ap_pcfg_openfile(&f, r->pool, auth_pwfile)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "Digest: Could not open password file: %s", auth_pwfile); return NULL; @@ -1370,7 +1401,7 @@ if (dt > conf->nonce_lifetime) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0,r, "Digest: user %s: nonce expired - sending new nonce", - r->connection->user); + r->user); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } @@ -1379,7 +1410,7 @@ if (memcmp(resp->client->last_nonce, resp->nonce, NONCE_LEN)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r, "Digest: user %s: one-time-nonce mismatch - sending " - "new nonce", r->connection->user); + "new nonce", r->user); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } @@ -1451,7 +1482,6 @@ digest_config_rec *conf; digest_header_rec *resp; request_rec *mainreq; - conn_rec *conn = r->connection; const char *t; int res; @@ -1500,21 +1530,39 @@ return AUTH_REQUIRED; } - r->connection->user = (char *) resp->username; - r->connection->ap_auth_type = (char *) "Digest"; + r->user = (char *) resp->username; + r->ap_auth_type = (char *) "Digest"; /* check the auth attributes */ if (strcmp(resp->uri, resp->request_uri->path)) { uri_components *r_uri = resp->request_uri, d_uri; - ap_parse_uri_components(r->pool, resp->uri, &d_uri); + int port; + + if (ap_parse_uri_components(r->pool, resp->uri, &d_uri) != HTTP_OK) { + ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, + "Digest: invalid uri <%s> in Authorization header", + resp->uri); + return BAD_REQUEST; + } + if (d_uri.hostname) + ap_unescape_url(d_uri.hostname); + if (d_uri.path) + ap_unescape_url(d_uri.path); + if (d_uri.query) + ap_unescape_url(d_uri.query); + if (r_uri->query) + ap_unescape_url(r_uri->query); + port = ap_get_server_port(r); + if ((d_uri.hostname && d_uri.hostname[0] != '\0' - && strcasecmp(d_uri.hostname, r->server->server_hostname)) - || (d_uri.port_str && d_uri.port != r->server->port) - || (!d_uri.port_str && r->server->port != 80) - || strcmp(d_uri.path, r_uri->path) + && strcasecmp(d_uri.hostname, ap_get_server_name(r))) + || (d_uri.port_str && d_uri.port != port) + || (d_uri.hostname && d_uri.hostname[0] != '\0' + && !d_uri.port_str && port != ap_default_port(r)) + || !d_uri.path || strcmp(d_uri.path, r_uri->path) || (d_uri.query != r_uri->query && (!d_uri.query || !r_uri->query || strcmp(d_uri.query, r_uri->query))) @@ -1556,10 +1604,10 @@ if (!conf->pwfile) return DECLINED; - if (!(conf->ha1 = get_hash(r, conn->user, conf->realm, conf->pwfile))) { + if (!(conf->ha1 = get_hash(r, r->user, conf->realm, conf->pwfile))) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "Digest: user `%s' in realm `%s' not found: %s", - conn->user, conf->realm, r->uri); + r->user, conf->realm, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } @@ -1568,7 +1616,7 @@ /* old (rfc-2069) style digest */ if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "Digest: user %s: password mismatch: %s", conn->user, + "Digest: user %s: password mismatch: %s", r->user, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; @@ -1595,7 +1643,7 @@ if (strcmp(resp->digest, new_digest(r, resp, conf))) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, - "Digest: user %s: password mismatch: %s", conn->user, + "Digest: user %s: password mismatch: %s", r->user, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; @@ -1620,16 +1668,17 @@ * Checking ID */ -static table *groups_for_user(request_rec *r, const char *user, +static ap_table_t *groups_for_user(request_rec *r, const char *user, const char *grpfile) { configfile_t *f; - table *grps = ap_make_table(r->pool, 15); + ap_table_t *grps = ap_make_table(r->pool, 15); ap_context_t *sp; char l[MAX_STRING_LEN]; const char *group_name, *ll, *w; + ap_status_t sts; - if (!(f = ap_pcfg_openfile(r->pool, grpfile))) { + if ((sts = ap_pcfg_openfile(&f, r->pool, grpfile)) != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, errno, r, "Digest: Could not open group file: %s", grpfile); return NULL; @@ -1666,13 +1715,13 @@ const digest_config_rec *conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config, &digest_auth_module); - const char *user = r->connection->user; + const char *user = r->user; int m = r->method_number; int method_restricted = 0; register int x; const char *t, *w; - table *grpstatus; - const array_header *reqs_arr; + ap_table_t *grpstatus; + const ap_array_header_t *reqs_arr; require_line *reqs; if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest")) @@ -1746,7 +1795,7 @@ */ #ifdef SEND_DIGEST -static const char *hdr(const table *tbl, const char *name) +static const char *hdr(const ap_table_t *tbl, const char *name) { const char *val = ap_table_get(tbl, name); if (val) @@ -1895,26 +1944,27 @@ } +static void register_hooks(void) +{ + static const char * const cfgPost[]={ "http_core.c", NULL }; + static const char * const parsePre[]={ "mod_proxy.c", NULL }; + + ap_hook_post_config(initialize_module, NULL, cfgPost, NULL); + ap_hook_post_read_request(parse_hdr_and_update_nc, parsePre, NULL, NULL); + ap_hook_check_user_id(authenticate_digest_user, NULL, NULL, HOOK_MIDDLE); + ap_hook_auth_checker(digest_check_auth, NULL, NULL, HOOK_MIDDLE); + ap_hook_fixups(add_auth_info, NULL, NULL, HOOK_MIDDLE); +} + module MODULE_VAR_EXPORT digest_auth_module = { - STANDARD_MODULE_STUFF, - initialize_module, /* initializer */ + STANDARD20_MODULE_STUFF, create_digest_dir_config, /* dir config creater */ NULL, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ digest_cmds, /* command table */ NULL, /* handlers */ - NULL, /* filename translation */ - authenticate_digest_user, /* check_user_id */ - digest_check_auth, /* check auth */ - NULL, /* check access */ - NULL, /* type_checker */ - add_auth_info, /* fixups */ - NULL, /* logger */ - NULL, /* header parser */ - NULL, /* child_init */ - NULL, /* child_exit */ - update_nonce_count /* post read-request */ + register_hooks /* register hooks */ };