📄 mod_auth_digest.c
字号:
}}/* * configuration code */static void *create_digest_dir_config(apr_pool_t *p, char *dir){ digest_config_rec *conf; if (dir == NULL) { return NULL; } conf = (digest_config_rec *) apr_pcalloc(p, sizeof(digest_config_rec)); if (conf) { conf->qop_list = apr_palloc(p, sizeof(char*)); conf->qop_list[0] = NULL; conf->nonce_lifetime = DFLT_NONCE_LIFE; conf->dir_name = apr_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) */ apr_sha1_init(&conf->nonce_ctx); apr_sha1_update_binary(&conf->nonce_ctx, secret, sizeof(secret)); apr_sha1_update_binary(&conf->nonce_ctx, (const unsigned char *) realm, strlen(realm)); return DECLINE_CMD;}static const char *add_authn_provider(cmd_parms *cmd, void *config, const char *arg){ digest_config_rec *conf = (digest_config_rec*)config; authn_provider_list *newp; newp = apr_pcalloc(cmd->pool, sizeof(authn_provider_list)); newp->provider_name = apr_pstrdup(cmd->pool, arg); /* lookup and cache the actual provider now */ newp->provider = ap_lookup_provider(AUTHN_PROVIDER_GROUP, newp->provider_name, "0"); if (newp->provider == NULL) { /* by the time they use it, the provider should be loaded and registered with us. */ return apr_psprintf(cmd->pool, "Unknown Authn provider: %s", newp->provider_name); } if (!newp->provider->get_realm_hash) { /* if it doesn't provide the appropriate function, reject it */ return apr_psprintf(cmd->pool, "The '%s' Authn provider doesn't support " "Digest Authentication", newp->provider_name); } /* Add it to the list now. */ if (!conf->providers) { conf->providers = newp; } else { authn_provider_list *last = conf->providers; while (last->next) { last = last->next; } last->next = newp; } 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 = apr_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_WARNING, 0, cmd->server, "Digest: WARNING: qop `auth-int' currently only works " "correctly for responses with no entity"); } else if (strcasecmp(op, "auth")) { return apr_pstrcat(cmd->pool, "Unrecognized qop: ", op, NULL); } for (cnt = 0; conf->qop_list[cnt] != NULL; cnt++) ; tmp = apr_palloc(cmd->pool, (cnt + 2) * sizeof(char*)); memcpy(tmp, conf->qop_list, cnt*sizeof(char*)); tmp[cnt] = apr_pstrdup(cmd->pool, op); tmp[cnt+1] = NULL; conf->qop_list = tmp; return NULL;}static const char *set_nonce_lifetime(cmd_parms *cmd, void *config, const char *t){ char *endptr; long lifetime; lifetime = strtol(t, &endptr, 10); if (endptr < (t+strlen(t)) && !apr_isspace(*endptr)) { return apr_pstrcat(cmd->pool, "Invalid time in AuthDigestNonceLifetime: ", t, NULL); } ((digest_config_rec *) config)->nonce_lifetime = apr_time_from_sec(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){ if (flag && !client_shm) ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "Digest: WARNING: nonce-count checking " "is not supported on platforms without shared-memory " "support - disabling check"); ((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")) { if (!client_shm) { ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, "Digest: WARNING: algorithm `MD5-sess' " "is not supported on platforms without shared-memory " "support - reverting to MD5"); alg = "MD5"; } } else if (strcasecmp(alg, "MD5")) { return apr_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 = apr_pstrcat(cmd->pool, c->uri_list, " ", uri, "\"", NULL); } else { c->uri_list = apr_pstrcat(cmd->pool, ", domain=\"", uri, "\"", NULL); } return NULL;}static const char *set_shmem_size(cmd_parms *cmd, void *config, const char *size_str){ char *endptr; long size, min; size = strtol(size_str, &endptr, 10); while (apr_isspace(*endptr)) endptr++; if (*endptr == '\0' || *endptr == 'b' || *endptr == 'B') { ; } else if (*endptr == 'k' || *endptr == 'K') { size *= 1024; } else if (*endptr == 'm' || *endptr == 'M') { size *= 1048576; } else { return apr_pstrcat(cmd->pool, "Invalid size in AuthDigestShmemSize: ", size_str, NULL); } min = sizeof(*client_list) + sizeof(client_entry*) + sizeof(client_entry); if (size < min) { return apr_psprintf(cmd->pool, "size in AuthDigestShmemSize too small: " "%ld < %ld", size, min); } shmem_size = size; num_buckets = (size - sizeof(*client_list)) / (sizeof(client_entry*) + HASH_DEPTH * sizeof(client_entry)); if (num_buckets == 0) { num_buckets = 1; } ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, cmd->server, "Digest: Set shmem-size: %ld, num-buckets: %ld", shmem_size, num_buckets); return NULL;}static const command_rec digest_cmds[] ={ AP_INIT_TAKE1("AuthName", set_realm, NULL, OR_AUTHCFG, "The authentication realm (e.g. \"Members Only\")"), AP_INIT_ITERATE("AuthDigestProvider", add_authn_provider, NULL, OR_AUTHCFG, "specify the auth providers for a directory or location"), AP_INIT_ITERATE("AuthDigestQop", set_qop, NULL, OR_AUTHCFG, "A list of quality-of-protection options"), AP_INIT_TAKE1("AuthDigestNonceLifetime", set_nonce_lifetime, NULL, OR_AUTHCFG, "Maximum lifetime of the server nonce (seconds)"), AP_INIT_TAKE1("AuthDigestNonceFormat", set_nonce_format, NULL, OR_AUTHCFG, "The format to use when generating the server nonce"), AP_INIT_FLAG("AuthDigestNcCheck", set_nc_check, NULL, OR_AUTHCFG, "Whether or not to check the nonce-count sent by the client"), AP_INIT_TAKE1("AuthDigestAlgorithm", set_algorithm, NULL, OR_AUTHCFG, "The algorithm used for the hash calculation"), AP_INIT_ITERATE("AuthDigestDomain", set_uri_list, NULL, OR_AUTHCFG, "A list of URI's which belong to the same protection space as the current URI"), AP_INIT_TAKE1("AuthDigestShmemSize", set_shmem_size, NULL, RSRC_CONF, "The amount of shared memory to allocate for keeping track of clients"), {NULL}};/* * 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 it's 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_shm) return NULL; bucket = key % client_list->tbl_len; entry = client_list->table[bucket]; apr_global_mutex_lock(client_lock); 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; } apr_global_mutex_unlock(client_lock); if (entry) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "get_client(): client %lu found", key); } else { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, 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 */ apr_rmm_free(client_rmm, (apr_rmm_off_t)entry); num_removed++; } } /* update counters and log */ client_list->num_entries -= num_removed; client_list->num_removed += num_removed;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -