📄 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 * It is still fairly new and problems may turn up - submit problem * reports to the Apache bug-database, or send them directly to me * at 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"? * - expired nonces give amaya fits. */#include "apr_sha1.h"#include "apr_base64.h"#include "apr_lib.h"#include "apr_time.h"#include "apr_errno.h"#include "apr_global_mutex.h"#include "apr_strings.h"#define APR_WANT_STRFUNC#include "apr_want.h"#include "ap_config.h"#include "httpd.h"#include "http_config.h"#include "http_core.h"#include "http_request.h"#include "http_log.h"#include "http_protocol.h"#include "apr_uri.h"#include "util_md5.h"#include "apr_shm.h"#include "apr_rmm.h"#include "ap_provider.h"#include "mod_auth.h"/* Disable shmem until pools/init gets sorted out * remove following two lines when fixed */#undef APR_HAS_SHARED_MEMORY#define APR_HAS_SHARED_MEMORY 0/* struct to hold the configuration info */typedef struct digest_config_struct { const char *dir_name; authn_provider_list *providers; const char *realm; char **qop_list; apr_sha1_ctx_t nonce_ctx; apr_time_t 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 apr_time_from_sec(300)#define NEXTNONCE_DELTA apr_time_from_sec(30)#define NONCE_TIME_LEN (((sizeof(apr_time_t)+2)/3)*4)#define NONCE_HASH_LEN (2*APR_SHA1_DIGESTSIZE)#define NONCE_LEN (int )(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*APR_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 *method; 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 */ apr_time_t nonce_time; enum hdr_sts auth_hdr_sts; const char *raw_request_uri; apr_uri_t *psd_request_uri; int needed_auth; client_entry *client;} digest_header_rec;/* (mostly) nonce stuff */typedef union time_union { apr_time_t time; unsigned char arr[sizeof(apr_time_t)];} time_rec;static unsigned char secret[SECRET_LEN];/* client-list, opaque, and one-time-nonce stuff */static apr_shm_t *client_shm = NULL;static apr_rmm_t *client_rmm = NULL;static unsigned long *opaque_cntr;static apr_time_t *otn_counter; /* one-time-nonce counter */static apr_global_mutex_t *client_lock = NULL;static apr_global_mutex_t *opaque_lock = NULL;static char client_lock_name[L_tmpnam];static char opaque_lock_name[L_tmpnam];#define DEF_SHMEM_SIZE 1000L /* ~ 12 entries */#define DEF_NUM_BUCKETS 15L#define HASH_DEPTH 5static long shmem_size = DEF_SHMEM_SIZE;static long num_buckets = DEF_NUM_BUCKETS;module AP_MODULE_DECLARE_DATA auth_digest_module;/* * initialization code */static apr_status_t cleanup_tables(void *not_used){ ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL, "Digest: cleaning up shared memory"); fflush(stderr); if (client_shm) { apr_shm_destroy(client_shm); client_shm = NULL; } if (client_lock) { apr_global_mutex_destroy(client_lock); client_lock = NULL; } if (opaque_lock) { apr_global_mutex_destroy(opaque_lock); opaque_lock = NULL; } return APR_SUCCESS;}static apr_status_t initialize_secret(server_rec *s){ apr_status_t status; ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "Digest: generating secret for digest authentication ...");#if APR_HAS_RANDOM status = apr_generate_random_bytes(secret, sizeof(secret));#else#error APR random number support is missing; you probably need to install the truerand library.#endif if (status != APR_SUCCESS) { char buf[120]; ap_log_error(APLOG_MARK, APLOG_CRIT, status, s, "Digest: error generating secret: %s", apr_strerror(status, buf, sizeof(buf))); return status; } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, "Digest: done"); return APR_SUCCESS;}static void log_error_and_cleanup(char *msg, apr_status_t sts, server_rec *s){ ap_log_error(APLOG_MARK, APLOG_ERR, sts, s, "Digest: %s - all nonce-count checking, one-time nonces, and " "MD5-sess algorithm disabled", msg); cleanup_tables(NULL);}#if APR_HAS_SHARED_MEMORYstatic void initialize_tables(server_rec *s, apr_pool_t *ctx){ unsigned long idx; apr_status_t sts; /* set up client list */ sts = apr_shm_create(&client_shm, shmem_size, tmpnam(NULL), ctx); if (sts != APR_SUCCESS) { log_error_and_cleanup("failed to create shared memory segments", sts, s); return; } client_list = apr_rmm_malloc(client_rmm, sizeof(*client_list) + sizeof(client_entry*)*num_buckets); if (!client_list) { log_error_and_cleanup("failed to allocate shared memory", -1, s); return; } 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; tmpnam(client_lock_name); /* FIXME: get the client_lock_name from a directive so we're portable * to non-process-inheriting operating systems, like Win32. */ sts = apr_global_mutex_create(&client_lock, client_lock_name, APR_LOCK_DEFAULT, ctx); if (sts != APR_SUCCESS) { log_error_and_cleanup("failed to create lock (client_lock)", sts, s); return; } /* setup opaque */ opaque_cntr = apr_rmm_malloc(client_rmm, sizeof(*opaque_cntr)); if (opaque_cntr == NULL) { log_error_and_cleanup("failed to allocate shared memory", -1, s); return; } *opaque_cntr = 1UL; tmpnam(opaque_lock_name); /* FIXME: get the opaque_lock_name from a directive so we're portable * to non-process-inheriting operating systems, like Win32. */ sts = apr_global_mutex_create(&opaque_lock, opaque_lock_name, APR_LOCK_DEFAULT, ctx); if (sts != APR_SUCCESS) { log_error_and_cleanup("failed to create lock (opaque_lock)", sts, s); return; } /* setup one-time-nonce counter */ otn_counter = apr_rmm_malloc(client_rmm, sizeof(*otn_counter)); if (otn_counter == NULL) { log_error_and_cleanup("failed to allocate shared memory", -1, s); return; } *otn_counter = 0; /* no lock here */ /* success */ return;}#endif /* APR_HAS_SHARED_MEMORY */static int initialize_module(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s){ void *data; const char *userdata_key = "auth_digest_init"; /* initialize_module() will be called twice, and if it's a DSO * then all static data from the first call will be lost. Only * set up our static data on the second call. */ apr_pool_userdata_get(&data, userdata_key, s->process->pool); if (!data) { apr_pool_userdata_set((const void *)1, userdata_key, apr_pool_cleanup_null, s->process->pool); return OK; } if (initialize_secret(s) != APR_SUCCESS) { return !OK; }#if APR_HAS_SHARED_MEMORY /* 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 apr_shm_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, p); apr_pool_cleanup_register(p, NULL, cleanup_tables, apr_pool_cleanup_null);#endif /* APR_HAS_SHARED_MEMORY */ return OK;}static void initialize_child(apr_pool_t *p, server_rec *s){ apr_status_t sts; if (!client_shm) { return; } /* FIXME: get the client_lock_name from a directive so we're portable * to non-process-inheriting operating systems, like Win32. */ sts = apr_global_mutex_child_init(&client_lock, client_lock_name, p); if (sts != APR_SUCCESS) { log_error_and_cleanup("failed to create lock (client_lock)", sts, s); return; } /* FIXME: get the opaque_lock_name from a directive so we're portable * to non-process-inheriting operating systems, like Win32. */ sts = apr_global_mutex_child_init(&opaque_lock, opaque_lock_name, p); if (sts != APR_SUCCESS) { log_error_and_cleanup("failed to create lock (opaque_lock)", sts, s); return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -