📄 mod_auth_digest.c
字号:
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * mod_auth_digest: MD5 digest authentication * * Originally by Alexei Kosut <akosut@nueva.pvt.k12.ca.us> * Updated to RFC-2617 by Ronald Tschal鋜 <ronald@innovation.ch> * based on mod_auth, by Rob McCool and Robert S. Thau * * This module an updated version of modules/standard/mod_digest.c * However, it has not been extensively tested yet, and is therefore * currently marked experimental. Send problem reports to me * (ronald@innovation.ch) * * Requires either /dev/random (or equivalent) or the truerand library, * available for instance from * ftp://research.att.com/dist/mab/librand.shar * * Open Issues: * - qop=auth-int (when streams and trailer support available) * - nonce-format configurability * - Proxy-Authorization-Info header is set by this module, but is * currently ignored by mod_proxy (needs patch to mod_proxy) * - generating the secret takes a while (~ 8 seconds) if using the * truerand library * - The source of the secret should be run-time directive (with server * scope: RSRC_CONF). However, that could be tricky when trying to * choose truerand vs. file... * - shared-mem not completely tested yet. Seems to work ok for me, * but... (definitely won't work on Windoze) * - Sharing a realm among multiple servers has following problems: * o Server name and port can't be included in nonce-hash * (we need two nonce formats, which must be configured explicitly) * o Nonce-count check can't be for equal, or then nonce-count checking * must be disabled. What we could do is the following: * (expected < received) ? set expected = received : issue error * The only problem is that it allows replay attacks when somebody * captures a packet sent to one server and sends it to another * one. Should we add "AuthDigestNcCheck Strict"? *//* The section for the Configure script: * MODULE-DEFINITION-START * Name: digest_auth_module * ConfigStart RULE_DEV_RANDOM=`./helpers/CutRule DEV_RANDOM $file` if [ "$RULE_DEV_RANDOM" = "default" ]; then if [ -r "/dev/random" ]; then RULE_DEV_RANDOM="/dev/random" elif [ -r "/dev/urandom" ]; then RULE_DEV_RANDOM="/dev/urandom" else RULE_DEV_RANDOM="truerand" if helpers/TestCompile func randbyte; then : elif helpers/TestCompile lib rand randbyte; then : else echo " (mod_auth_digest) truerand library missing!" echo "** This will most probably defeat successful compilation." echo "** See Rule DEV_RANDOM in src/Configuration.tmpl for more information." fi fi fi if [ "$RULE_DEV_RANDOM" = "truerand" ]; then echo " using truerand library (-lrand) for the random seed" LIBS="$LIBS -L/usr/local/lib -lrand" else echo " using $RULE_DEV_RANDOM for the random seed" CFLAGS="$CFLAGS -DDEV_RANDOM=$RULE_DEV_RANDOM" fi * ConfigEnd * MODULE-DEFINITION-END */#include "httpd.h"#include "http_config.h"#include "http_conf_globals.h"#include "http_core.h"#include "http_request.h"#include "http_log.h"#include "http_protocol.h"#include "ap_config.h"#include "ap_ctype.h"#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 *//* struct to hold the configuration info */typedef struct digest_config_struct { const char *dir_name; const char *pwfile; const char *grpfile; const char *realm; const char **qop_list; AP_SHA1_CTX nonce_ctx; long nonce_lifetime; const char *nonce_format; int check_nc; const char *algorithm; char *uri_list; const char *ha1;} digest_config_rec;#define DFLT_ALGORITHM "MD5"#define DFLT_NONCE_LIFE 300L#define NEXTNONCE_DELTA 30#define NONCE_TIME_LEN (((sizeof(time_t)+2)/3)*4)#define NONCE_HASH_LEN (2*SHA_DIGESTSIZE)#define NONCE_LEN (NONCE_TIME_LEN + NONCE_HASH_LEN)#define SECRET_LEN 20/* client list definitions */typedef struct hash_entry { 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[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; unsigned long tbl_len; unsigned long num_entries; unsigned long num_created; unsigned long num_removed; unsigned long num_renewed;} *client_list;/* struct to hold a parsed Authorization header */enum hdr_sts { NO_HEADER, NOT_DIGEST, INVALID, VALID };typedef struct digest_header_struct { const char *scheme; const char *realm; const char *username; char *nonce; const char *uri; const char *digest; const char *algorithm; const char *cnonce; const char *opaque; unsigned long opaque_num; const char *message_qop; const char *nonce_count; /* the following fields are not (directly) from the header */ time_t nonce_time; enum hdr_sts auth_hdr_sts; const char *raw_request_uri; uri_components *psd_request_uri; int needed_auth; client_entry *client;} digest_header_rec;/* (mostly) nonce stuff */typedef union time_union { time_t time; unsigned char arr[sizeof(time_t)];} time_rec;static unsigned char secret[SECRET_LEN];static int call_cnt = 0;#ifdef HAVE_SHMEM_MM/* opaque stuff */static MM *opaque_mm;static unsigned long *opaque_cntr;static MM *client_mm;static MM *otn_count_mm;static time_t *otn_counter; /* one-time-nonce counter */#define SHMEM_SIZE 1000 /* ~ 12 entries */#define NUM_BUCKETS 15UL#else /* HAVE_SHMEM_MM */static void *client_mm = NULL;#endif /* HAVE_SHMEM_MM */module MODULE_VAR_EXPORT digest_auth_module;/* * initialization code */#ifdef HAVE_SHMEM_MMstatic void cleanup_tables(void *not_used){ fprintf(stderr, "Digest: cleaning up shared memory\n"); fflush(stderr); if (client_mm) { mm_destroy(client_mm); client_mm = NULL; } if (opaque_mm) { mm_destroy(opaque_mm); opaque_mm = NULL; } if (otn_count_mm) { mm_destroy(otn_count_mm); otn_count_mm = NULL; }}#endif /* HAVE_SHMEM_MM */#ifdef __OpenBSD__static void initialize_secret(server_rec *s){ u_int32_t rnd = 0, i; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "Digest: generating secret for digest authentication ..."); for (i = 0; i < sizeof(secret); i++) { if (i % 4 == 0) rnd = arc4random(); secret[i] = rnd; rnd >>= 8; } ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "Digest: done");}#elif defined(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");}#elsestatic void initialize_secret(server_rec *s){#ifdef DEV_RANDOM int rnd; ssize_t got; size_t tot;#else extern int randbyte(void); /* from the truerand library */ unsigned int idx;#endif ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "Digest: generating secret for digest authentication ...");#ifdef DEV_RANDOM#define XSTR(x) #x#define STR(x) XSTR(x) if ((rnd = open(STR(DEV_RANDOM), O_RDONLY)) == -1) { ap_log_error(APLOG_MARK, APLOG_CRIT, s, "Digest: Couldn't open " STR(DEV_RANDOM)); exit(EXIT_FAILURE); } for (tot=0; tot<sizeof(secret); tot += got) { 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); } } close(rnd);#undef STR#undef XSTR#else /* use truerand */ /* this will increase the startup time of the server, unfortunately... * (generating 20 bytes takes about 8 seconds) */ for (idx=0; idx<sizeof(secret); idx++) secret[idx] = (unsigned char) randbyte();#endif /* DEV_RANDOM */ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "Digest: done");}#endif#ifdef HAVE_SHMEM_MMstatic void initialize_tables(server_rec *s){ unsigned long idx; /* set up client list */ client_mm = mm_create(SHMEM_SIZE, tmpnam(NULL)); if (client_mm == NULL) goto failed;#ifdef MPE if (geteuid() == 1) {#else if (geteuid() == 0) {#endif if (mm_permission(client_mm, 0600, ap_user_id, ap_group_id)) goto failed; } client_list = mm_malloc(client_mm, sizeof(*client_list) + sizeof(client_entry*)*NUM_BUCKETS); if (!client_list) goto failed; client_list->table = (client_entry**) (client_list + 1); for (idx=0; idx<NUM_BUCKETS; idx++) client_list->table[idx] = NULL; client_list->tbl_len = NUM_BUCKETS; client_list->num_entries = 0; /* setup opaque */ opaque_mm = mm_create(sizeof(*opaque_cntr), tmpnam(NULL)); if (opaque_mm == NULL) goto failed;#ifdef MPE if (geteuid() == 1) {#else if (geteuid() == 0) {#endif if (mm_permission(opaque_mm, 0600, ap_user_id, ap_group_id)) goto failed; } opaque_cntr = mm_malloc(opaque_mm, sizeof(*opaque_cntr)); if (opaque_cntr == NULL) goto failed; *opaque_cntr = 1UL; /* setup one-time-nonce counter */ otn_count_mm = mm_create(sizeof(*otn_counter), tmpnam(NULL)); if (otn_count_mm == NULL) goto failed;#ifdef MPE if (geteuid() == 1) {#else if (geteuid() == 0) {#endif if (mm_permission(otn_count_mm, 0600, ap_user_id, ap_group_id)) goto failed; } otn_counter = mm_malloc(otn_count_mm, sizeof(*otn_counter)); if (otn_counter == NULL) goto failed; *otn_counter = 0; /* success */ return;failed: if (!client_mm || (client_list && client_list->table && !opaque_mm) || (opaque_cntr && !otn_count_mm)) ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, "Digest: failed to create shared memory segments; reason " "was `%s' - all nonce-count checking, one-time nonces, " "and MD5-sess algorithm disabled", mm_error()); else ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, "Digest: failed to allocate shared mem; reason was `%s' " "- all nonce-count checking, one-time nonces, and " "MD5-sess algorithm disabled", mm_error()); cleanup_tables(NULL);}#endif /* HAVE_SHMEM_MM */static void initialize_module(server_rec *s, pool *p){ /* keep from doing the init more than once at startup, and delay * the init until the second round */ if (++call_cnt < 2) return; /* only initialize the secret on startup, not on restarts */ if (call_cnt == 2) initialize_secret(s);#ifdef HAVE_SHMEM_MM /* Note: this stuff is currently fixed for the lifetime of the server, * i.e. even across restarts. This means that A) any shmem-size * configuration changes are ignored, and B) certain optimizations, * such as only allocating the smallest necessary entry for each * client, can't be done. However, the alternative is a nightmare: * we can't call mm_destroy on a graceful restart because there will * be children using the tables, and we also don't know when the * last child dies. Therefore we can never clean up the old stuff, * 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 */}/* * configuration code */static void *create_digest_dir_config(pool *p, char *dir){ digest_config_rec *conf; if (dir == NULL) return NULL; conf = (digest_config_rec *) ap_pcalloc(p, sizeof(digest_config_rec)); if (conf) { conf->qop_list = ap_palloc(p, sizeof(char*)); conf->qop_list[0] = NULL; conf->nonce_lifetime = DFLT_NONCE_LIFE; conf->dir_name = ap_pstrdup(p, dir); conf->algorithm = DFLT_ALGORITHM; } return conf;}static const char *set_realm(cmd_parms *cmd, void *config, const char *realm){ digest_config_rec *conf = (digest_config_rec *) config; /* The core already handles the realm, but it's just too convenient to * grab it ourselves too and cache some setups. However, we need to * let the core get at it too, which is why we decline at the end - * this relies on the fact that http_core is last in the list. */ conf->realm = realm; /* we precompute the part of the nonce hash that is constant (well, * the host:port would be too, but that varies for .htaccess files * and directives outside a virtual host section) */ ap_SHA1Init(&conf->nonce_ctx); ap_SHA1Update_binary(&conf->nonce_ctx, secret, sizeof(secret)); ap_SHA1Update_binary(&conf->nonce_ctx, (const unsigned char *) realm, strlen(realm)); return DECLINE_CMD;}static const char *set_digest_file(cmd_parms *cmd, void *config, const char *file){ ((digest_config_rec *) config)->pwfile = file; return NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -