⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mod_auth_digest.c

📁 apache 安装教程 apache 安装教程
💻 C
📖 第 1 页 / 共 4 页
字号:
}static const char *set_group_file(cmd_parms *cmd, void *config,				  const char *file){    ((digest_config_rec *) config)->grpfile = file;    return NULL;}static const char *set_qop(cmd_parms *cmd, void *config, const char *op){    digest_config_rec *conf = (digest_config_rec *) config;    char **tmp;    int cnt;    if (!strcasecmp(op, "none")) {	if (conf->qop_list[0] == NULL) {	    conf->qop_list = ap_palloc(cmd->pool, 2 * sizeof(char*));	    conf->qop_list[1] = NULL;	}	conf->qop_list[0] = "none";	return NULL;    }    if (!strcasecmp(op, "auth-int"))	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, cmd->server,		     "Digest: WARNING: qop `auth-int' currently only works "		     "correctly for responses with no entity");    else if (strcasecmp(op, "auth"))	return ap_pstrcat(cmd->pool, "Unrecognized qop: ", op, NULL);    for (cnt=0; conf->qop_list[cnt] != NULL; cnt++)	;    tmp = ap_palloc(cmd->pool, (cnt+2)*sizeof(char*));    memcpy(tmp, conf->qop_list, cnt*sizeof(char*));    tmp[cnt]   = ap_pstrdup(cmd->pool, op);    tmp[cnt+1] = NULL;    conf->qop_list = (const char **)tmp;    return NULL;}static const char *set_nonce_lifetime(cmd_parms *cmd, void *config,				      const char *t){    char *endptr;    long  lifetime;    lifetime = ap_strtol(t, &endptr, 10);    if (endptr < (t+strlen(t)) && !ap_isspace(*endptr))	return ap_pstrcat(cmd->pool, "Invalid time in AuthDigestNonceLifetime: ", t, NULL);    ((digest_config_rec *) config)->nonce_lifetime = lifetime;    return NULL;}static const char *set_nonce_format(cmd_parms *cmd, void *config,				    const char *fmt){    ((digest_config_rec *) config)->nonce_format = fmt;    return "AuthDigestNonceFormat is not implemented (yet)";}static const char *set_nc_check(cmd_parms *cmd, void *config, int flag){    ((digest_config_rec *) config)->check_nc = flag;    return NULL;}static const char *set_algorithm(cmd_parms *cmd, void *config, const char *alg){    if (!strcasecmp(alg, "MD5-sess"))#ifdef HAVE_SHMEM_MM	;#else	/* HAVE_SHMEM_MM */	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, cmd->server,		     "Digest: WARNING: algorithm `MD5-sess' is currently not "		     "correctly implemented");#endif	/* HAVE_SHMEM_MM */    else if (strcasecmp(alg, "MD5"))	return ap_pstrcat(cmd->pool, "Invalid algorithm in AuthDigestAlgorithm: ", alg, NULL);    ((digest_config_rec *) config)->algorithm = alg;    return NULL;}static const char *set_uri_list(cmd_parms *cmd, void *config, const char *uri){    digest_config_rec *c = (digest_config_rec *) config;    if (c->uri_list) {	c->uri_list[strlen(c->uri_list)-1] = '\0';	c->uri_list = ap_pstrcat(cmd->pool, c->uri_list, " ", uri, "\"", NULL);    }    else	c->uri_list = ap_pstrcat(cmd->pool, ", domain=\"", uri, "\"", NULL);    return NULL;}static const command_rec digest_cmds[] ={    {"AuthName", set_realm, NULL, OR_AUTHCFG, TAKE1,     "The authentication realm (e.g. \"Members Only\")"},    {"AuthDigestFile", set_digest_file, NULL, OR_AUTHCFG, TAKE1,     "The name of the file containing the usernames and password hashes"},    {"AuthDigestGroupFile", set_group_file, NULL, OR_AUTHCFG, TAKE1,     "The name of the file containing the group names and members"},    {"AuthDigestQop", set_qop, NULL, OR_AUTHCFG, ITERATE,     "A list of quality-of-protection options"},    {"AuthDigestNonceLifetime", set_nonce_lifetime, NULL, OR_AUTHCFG, TAKE1,     "Maximum lifetime of the server nonce (seconds)"},    {"AuthDigestNonceFormat", set_nonce_format, NULL, OR_AUTHCFG, TAKE1,     "The format to use when generating the server nonce"},    {"AuthDigestNcCheck", set_nc_check, NULL, OR_AUTHCFG, FLAG,     "Whether or not to check the nonce-count sent by the client"},    {"AuthDigestAlgorithm", set_algorithm, NULL, OR_AUTHCFG, TAKE1,     "The algorithm used for the hash calculation"},    {"AuthDigestDomain", set_uri_list, NULL, OR_AUTHCFG, ITERATE,     "A list of URI's which belong to the same protection space as the current URI"},    {NULL, NULL, NULL, 0, 0, NULL}};#ifdef HAVE_SHMEM_MM/* * client list code * * Each client is assigned a number, which is transferred in the opaque * field of the WWW-Authenticate and Authorization headers. The number * is just a simple counter which is incremented for each new client. * Clients can't forge this number because it is hashed up into the * server nonce, and that is checked. * * The clients are kept in a simple hash table, which consists of an * array of client_entry's, each with a linked list of entries hanging * off it. The client's number modulo the size of the array gives the * bucket number. * * The clients are garbage collected whenever a new client is allocated * but there is not enough space left in the shared memory segment. A * simple semi-LRU is used for this: whenever a client entry is accessed * it is moved to the beginning of the linked list in its bucket (this * also makes for faster lookups for current clients). The garbage * collecter then just removes the oldest entry (i.e. the one at the * end of the list) in each bucket. * * The main advantages of the above scheme are that it's easy to implement * and it keeps the hash table evenly balanced (i.e. same number of entries * in each bucket). The major disadvantage is that you may be throwing * entries out which are in active use. This is not tragic, as these * clients will just be sent a new client id (opaque field) and nonce * with a stale=true (i.e. it will just look like the nonce expired, * thereby forcing an extra round trip). If the shared memory segment * has enough headroom over the current client set size then this should * not occur too often. * * To help tune the size of the shared memory segment (and see if the * above algorithm is really sufficient) a set of counters is kept * indicating the number of clients held, the number of garbage collected * clients, and the number of erroneously purged clients. These are printed * out at each garbage collection run. Note that access to the counters is * not synchronized because they are just indicaters, and whether they are * off by a few doesn't matter; and for the same reason no attempt is made * to guarantee the num_renewed is correct in the face of clients spoofing * the opaque field. *//* * Get the client given its client number (the key). Returns the entry, * or NULL if its not found. * * Access to the list itself is synchronized via locks. However, access * to the entry returned by get_client() is NOT synchronized. This means * that there are potentially problems if a client uses multiple, * simultaneous connections to access url's within the same protection * space. However, these problems are not new: when using multiple * connections you have no guarantee of the order the requests are * processed anyway, so you have problems with the nonce-count and * one-time nonces anyway. */static client_entry *get_client(unsigned long key, const request_rec *r){    int bucket;    client_entry *entry, *prev = NULL;    if (!key || !client_mm)  return NULL;    bucket = key % client_list->tbl_len;    entry  = client_list->table[bucket];    mm_lock(client_mm, MM_LOCK_RD);    while(entry && key != entry->key) {	prev  = entry;	entry = entry->next;    }    if (entry && prev) {		/* move entry to front of list */	prev->next  = entry->next;	entry->next = client_list->table[bucket];	client_list->table[bucket] = entry;    }    mm_unlock(client_mm);    if (entry)	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,		      "get_client(): client %lu found", key);    else	ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, r,		      "get_client(): client %lu not found", key);    return entry;}/* A simple garbage-collecter to remove unused clients. It removes the * last entry in each bucket and updates the counters. Returns the * number of removed entries. */static long gc(void){    client_entry *entry, *prev;    unsigned long num_removed = 0, idx;    /* garbage collect all last entries */    for (idx=0; idx<client_list->tbl_len; idx++) {	entry = client_list->table[idx];	prev  = NULL;	while (entry->next) {	/* find last entry */	    prev  = entry;	    entry = entry->next;	}	if (prev)  prev->next = NULL;	/* cut list */	else       client_list->table[idx] = NULL;	if (entry) {			/* remove entry */	    mm_free(client_mm, entry);	    num_removed++;	}    }    /* update counters and log */    client_list->num_entries -= num_removed;    client_list->num_removed += num_removed;    return num_removed;}/* * Add a new client to the list. Returns the entry if successful, NULL * otherwise. This triggers the garbage collection if memory is low. */static client_entry *add_client(unsigned long key, client_entry *info,				server_rec *s){    int bucket;    client_entry *entry;    if (!key || !client_mm)  return NULL;    bucket = key % client_list->tbl_len;    entry  = client_list->table[bucket];    mm_lock(client_mm, MM_LOCK_RW);    /* try to allocate a new entry */    entry = mm_malloc(client_mm, sizeof(client_entry));    if (!entry) {	long num_removed = gc();	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, s,		     "Digest: gc'd %ld client entries. Total new clients: "		     "%ld; Total removed clients: %ld; Total renewed clients: "		     "%ld", num_removed,		     client_list->num_created - client_list->num_renewed,		     client_list->num_removed, client_list->num_renewed);	entry = mm_malloc(client_mm, sizeof(client_entry));	if (!entry)  return NULL;	/* give up */    }    /* now add the entry */    memcpy(entry, info, sizeof(client_entry));    entry->key  = key;    entry->next = client_list->table[bucket];    client_list->table[bucket] = entry;    client_list->num_created++;    client_list->num_entries++;    mm_unlock(client_mm);    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, s,		 "allocated new client %lu", key);    return entry;}#else	/* HAVE_SHMEM_MM */static client_entry *get_client(unsigned long key, const request_rec *r){    return NULL;}#endif	/* HAVE_SHMEM_MM *//* * Authorization header parser code *//* Parse the Authorization header, if it exists */static int get_digest_rec(request_rec *r, digest_header_rec *resp){    const char *auth_line;    size_t l;    int vk = 0, vv = 0;    char *key, *value;    auth_line = ap_table_get(r->headers_in,			     r->proxyreq == STD_PROXY ? "Proxy-Authorization"						      : "Authorization");    if (!auth_line) {	resp->auth_hdr_sts = NO_HEADER;	return !OK;    }    resp->scheme = ap_getword_white(r->pool, &auth_line);    if (strcasecmp(resp->scheme, "Digest")) {	resp->auth_hdr_sts = NOT_DIGEST;	return !OK;    }    l = strlen(auth_line);    key   = ap_palloc(r->pool, l+1);    value = ap_palloc(r->pool, l+1);    while (auth_line[0] != '\0') {	/* find key */	while (ap_isspace(auth_line[0])) auth_line++;	vk = 0;	while (auth_line[0] != '=' && auth_line[0] != ','	       && auth_line[0] != '\0' && !ap_isspace(auth_line[0]))	    key[vk++] = *auth_line++;	key[vk] = '\0';	while (ap_isspace(auth_line[0])) auth_line++;	/* find value */	if (auth_line[0] == '=') {	    auth_line++;	    while (ap_isspace(auth_line[0])) auth_line++;	    vv = 0;	    if (auth_line[0] == '\"') {		/* quoted string */		auth_line++;		while (auth_line[0] != '\"' && auth_line[0] != '\0') {		    if (auth_line[0] == '\\' && auth_line[1] != '\0')			auth_line++;		/* escaped char */		    value[vv++] = *auth_line++;		}		if (auth_line[0] != '\0') auth_line++;	    }	    else {				 /* token */		while (auth_line[0] != ',' && auth_line[0] != '\0'		       && !ap_isspace(auth_line[0]))		    value[vv++] = *auth_line++;	    }	    value[vv] = '\0';	}	while (auth_line[0] != ',' && auth_line[0] != '\0')  auth_line++;	if (auth_line[0] != '\0') auth_line++;	if (!strcasecmp(key, "username"))	    resp->username = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "realm"))	    resp->realm = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "nonce"))	    resp->nonce = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "uri"))	    resp->uri = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "response"))	    resp->digest = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "algorithm"))	    resp->algorithm = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "cnonce"))	    resp->cnonce = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "opaque"))	    resp->opaque = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "qop"))	    resp->message_qop = ap_pstrdup(r->pool, value);	else if (!strcasecmp(key, "nc"))	    resp->nonce_count = ap_pstrdup(r->pool, value);    }    if (!resp->username || !resp->realm || !resp->nonce || !resp->uri	|| !resp->digest	|| (resp->message_qop && (!resp->cnonce || !resp->nonce_count))) {	resp->auth_hdr_sts = INVALID;	return !OK;    }    if (resp->opaque)	resp->opaque_num = (unsigned long) ap_strtol(resp->opaque, NULL, 16);    resp->auth_hdr_sts = VALID;    return OK;}/* Because the browser may preemptively send auth info, incrementing the * nonce-count when it does, and because the client does not get notified * if the URI didn't need authentication after all, we need to be sure to * update the nonce-count each time we receive an Authorization header no * matter what the final outcome of the request. Furthermore this is a * convenient place to get the request-uri (before any subrequests etc * are initiated) and to initialize the request_config. * * 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){    digest_header_rec *resp;    int res;    if (!ap_is_initial_req(r))	return DECLINED;    resp = ap_pcalloc(r->pool, sizeof(digest_header_rec));    resp->raw_request_uri = r->unparsed_uri;    resp->psd_request_uri = &r->parsed_uri;    resp->needed_auth = 0;    ap_set_module_config(r->request_config, &digest_auth_module, resp);    res = get_digest_rec(r, resp);    resp->client = get_client(resp->opaque_num, r);    if (res == OK && resp->client)	resp->client->nonce_count++;    return DECLINED;}/* * Nonce generation code *//* The hash part of the nonce is a SHA-1 hash of the time, realm, server host * and port, opaque, and our secret. */static void gen_nonce_hash(char *hash, const char *timestr, const char *opaque,			   const server_rec *server,			   const digest_config_rec *conf){    const char *hex = "0123456789abcdef";    unsigned char sha1[SHA_DIGESTSIZE];    AP_SHA1_CTX ctx;    int idx;    memcpy(&ctx, &conf->nonce_ctx, sizeof(ctx));    /*    ap_SHA1Update_binary(&ctx, (const unsigned char *) server->server_hostname,			 strlen(server->server_hostname));    ap_SHA1Update_binary(&ctx, (const unsigned char *) &server->port,			 sizeof(server->port));     */    ap_SHA1Update_binary(&ctx, (const unsigned char *) timestr, strlen(timestr));    if (opaque)	ap_SHA1Update_binary(&ctx, (const unsigned char *) opaque,			     strlen(opaque));    ap_SHA1Final(sha1, &ctx);    for (idx=0; idx<SHA_DIGESTSIZE; idx++) {	*hash++ = hex[sha1[idx] >> 4];	*hash++ = hex[sha1[idx] & 0xF];    }    *hash++ = '\0';}/* The nonce has the format b64(time)+hash . */static const char *gen_nonce(pool *p, time_t now, const char *opaque,			     const server_rec *server,			     const digest_config_rec *conf){    char *nonce = ap_palloc(p, NONCE_LEN+1);    time_rec t;    if (conf->nonce_lifetime != 0)	t.time = now;    else#ifdef HAVE_SHMEM_MM	/* this counter is not synch'd, because it doesn't really matter	 * if it counts exactly.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -