📄 mod_auth_digest.c
字号:
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_shm) { return NULL; } bucket = key % client_list->tbl_len; entry = client_list->table[bucket]; apr_global_mutex_lock(client_lock); /* try to allocate a new entry */ entry = (client_entry *)apr_rmm_malloc(client_rmm, sizeof(client_entry)); if (!entry) { long num_removed = gc(); ap_log_error(APLOG_MARK, APLOG_INFO, 0, 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 = (client_entry *)apr_rmm_malloc(client_rmm, 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++; apr_global_mutex_unlock(client_lock); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "allocated new client %lu", key); return entry;}/* * 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; apr_size_t l; int vk = 0, vv = 0; char *key, *value; auth_line = apr_table_get(r->headers_in, (PROXYREQ_PROXY == r->proxyreq) ? "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 = apr_palloc(r->pool, l+1); value = apr_palloc(r->pool, l+1); while (auth_line[0] != '\0') { /* find key */ while (apr_isspace(auth_line[0])) { auth_line++; } vk = 0; while (auth_line[0] != '=' && auth_line[0] != ',' && auth_line[0] != '\0' && !apr_isspace(auth_line[0])) { key[vk++] = *auth_line++; } key[vk] = '\0'; while (apr_isspace(auth_line[0])) { auth_line++; } /* find value */ if (auth_line[0] == '=') { auth_line++; while (apr_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' && !apr_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 = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "realm")) resp->realm = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "nonce")) resp->nonce = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "uri")) resp->uri = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "response")) resp->digest = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "algorithm")) resp->algorithm = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "cnonce")) resp->cnonce = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "opaque")) resp->opaque = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "qop")) resp->message_qop = apr_pstrdup(r->pool, value); else if (!strcasecmp(key, "nc")) resp->nonce_count = apr_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) 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 parse_hdr_and_update_nc(request_rec *r){ digest_header_rec *resp; int res; if (!ap_is_initial_req(r)) { return DECLINED; } resp = apr_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; resp->method = r->method; ap_set_module_config(r->request_config, &auth_digest_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[APR_SHA1_DIGESTSIZE]; apr_sha1_ctx_t ctx; int idx; memcpy(&ctx, &conf->nonce_ctx, sizeof(ctx)); /* apr_sha1_update_binary(&ctx, (const unsigned char *) server->server_hostname, strlen(server->server_hostname)); apr_sha1_update_binary(&ctx, (const unsigned char *) &server->port, sizeof(server->port)); */ apr_sha1_update_binary(&ctx, (const unsigned char *) timestr, strlen(timestr)); if (opaque) { apr_sha1_update_binary(&ctx, (const unsigned char *) opaque, strlen(opaque)); } apr_sha1_final(sha1, &ctx); for (idx=0; idx<APR_SHA1_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(apr_pool_t *p, apr_time_t now, const char *opaque, const server_rec *server, const digest_config_rec *conf){ char *nonce = apr_palloc(p, NONCE_LEN+1); int len; time_rec t; if (conf->nonce_lifetime != 0) { t.time = now; } else if (otn_counter) { /* this counter is not synch'd, because it doesn't really matter * if it counts exactly. */ t.time = (*otn_counter)++; } else { /* XXX: WHAT IS THIS CONSTANT? */ t.time = 42; } len = apr_base64_encode_binary(nonce, t.arr, sizeof(t.arr)); gen_nonce_hash(nonce+NONCE_TIME_LEN, nonce, opaque, server, conf); return nonce;}/* * Opaque and hash-table management *//* * Generate a new client entry, add it to the list, and return the * entry. Returns NULL if failed. */static client_entry *gen_client(const request_rec *r){ unsigned long op; client_entry new_entry = { 0, NULL, 0, "", "" }, *entry; if (!opaque_cntr) { return NULL; } apr_global_mutex_lock(opaque_lock); op = (*opaque_cntr)++; apr_global_mutex_lock(opaque_lock); if (!(entry = add_client(op, &new_entry, r->server))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Digest: failed to allocate client entry - ignoring " "client"); return NULL; } return entry;}/* * MD5-sess code. * * If you want to use algorithm=MD5-sess you must write get_userpw_hash() * yourself (see below). The dummy provided here just uses the hash from * the auth-file, i.e. it is only useful for testing client implementations * of MD5-sess . *//* * get_userpw_hash() will be called each time a new session needs to be * generated and is expected to return the equivalent of * * h_urp = ap_md5(r->pool, * apr_pstrcat(r->pool, username, ":", ap_auth_name(r), ":", passwd)) * ap_md5(r->pool, * (unsigned char *) apr_pstrcat(r->pool, h_urp, ":", resp->nonce, ":", * resp->cnonce, NULL)); * * or put differently, it must return * * MD5(MD5(username ":" realm ":" password) ":" nonce ":" cnonce) * * If something goes wrong, the failure must be logged and NULL returned. * * You must implement this yourself, which will probably consist of code * contacting the password server with the necessary information (typically * the username, realm, nonce, and cnonce) and receiving the hash from it. * * TBD: This function should probably be in a seperate source file so that * people need not modify mod_auth_digest.c each time they install a new * version of apache. */static const char *get_userpw_hash(const request_rec *r, const digest_header_rec *resp, const digest_config_rec *conf){ return ap_md5(r->pool, (unsigned char *) apr_pstrcat(r->pool, conf->ha1, ":", resp->nonce, ":", resp->cnonce, NULL));}/* Retrieve current session H(A1). If there is none and "generate" is * true then a new session for MD5-sess is generated and stored in the * client struct; if generate is false, or a new session could not be * generated then NULL is returned (in case of failure to generate the * failure reason will have been logged already). */static const char *get_session_HA1(const request_rec *r, digest_header_rec *resp, const digest_config_rec *conf, int generate){ const char *ha1 = NULL; /* return the current sessions if there is one */ if (resp->opaque && resp->client && resp->client->ha1[0]) { return resp->client->ha1; } else if (!generate) { return NULL; } /* generate a new session */ if (!resp->client) { resp->client = gen_client(r); } if (resp->client) { ha1 = get_userpw_hash(r, resp, conf); if (ha1) { memcpy(resp->client->ha1, ha1, sizeof(resp->client->ha1));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -