📄 mod_auth_digest.c
字号:
*/ t.time = (*otn_counter)++;#else /* HAVE_SHMEM_MM */ t.time = 42;#endif /* HAVE_SHMEM_MM */ ap_base64encode_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 */#ifdef HAVE_SHMEM_MM/* * 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_mm) return 0; mm_lock(opaque_mm, MM_LOCK_RW); op = (*opaque_cntr)++; mm_unlock(opaque_mm); if (!(entry = add_client(op, &new_entry, r->server))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "Digest: failed to allocate client entry - ignoring " "client"); return NULL; } return entry;}#else /* HAVE_SHMEM_MM */static client_entry *gen_client(const request_rec *r) { return NULL; }#endif /* HAVE_SHMEM_MM *//* * 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, * ap_pstrcat(r->pool, username, ":", ap_auth_name(r), ":", passwd)) * ap_md5(r->pool, * (unsigned char *) ap_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 *) ap_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)); } return ha1;}static void clear_session(const digest_header_rec *resp){ if (resp->client) resp->client->ha1[0] = '\0';}/* * Authorization challenge generation code (for WWW-Authenticate) */static const char *ltox(pool *p, unsigned long num){ if (num != 0) return ap_psprintf(p, "%lx", num); else return "";}static void note_digest_auth_failure(request_rec *r, const digest_config_rec *conf, digest_header_rec *resp, int stale){ const char *qop, *opaque, *opaque_param, *domain, *nonce; int cnt; /* Setup qop */ if (conf->qop_list[0] == NULL) qop = ", qop=\"auth\""; else if (!strcasecmp(conf->qop_list[0], "none")) qop = ""; else { qop = ap_pstrcat(r->pool, ", qop=\"", conf->qop_list[0], NULL); for (cnt=1; conf->qop_list[cnt] != NULL; cnt++) qop = ap_pstrcat(r->pool, qop, ",", conf->qop_list[cnt], NULL); qop = ap_pstrcat(r->pool, qop, "\"", NULL); } /* Setup opaque */ if (resp->opaque == NULL) { /* new client */ if ((conf->check_nc || conf->nonce_lifetime == 0 || !strcasecmp(conf->algorithm, "MD5-sess")) && (resp->client = gen_client(r)) != NULL) opaque = ltox(r->pool, resp->client->key); else opaque = ""; /* opaque not needed */ } else if (resp->client == NULL) { /* client info was gc'd */ resp->client = gen_client(r); if (resp->client != NULL) { opaque = ltox(r->pool, resp->client->key); stale = 1; client_list->num_renewed++; } else opaque = ""; /* ??? */ } else { opaque = resp->opaque; /* we're generating a new nonce, so reset the nonce-count */ resp->client->nonce_count = 0; } if (opaque[0]) opaque_param = ap_pstrcat(r->pool, ", opaque=\"", opaque, "\"", NULL); else opaque_param = NULL; /* Setup nonce */ nonce = gen_nonce(r->pool, r->request_time, opaque, r->server, conf); if (resp->client && conf->nonce_lifetime == 0) memcpy(resp->client->last_nonce, nonce, NONCE_LEN+1); /* Setup MD5-sess stuff. Note that we just clear out the session * info here, since we can't generate a new session until the request * from the client comes in with the cnonce. */ if (!strcasecmp(conf->algorithm, "MD5-sess")) clear_session(resp); /* setup domain attribute. We want to send this attribute wherever * possible so that the client won't send the Authorization header * unneccessarily (it's usually > 200 bytes!). */ /* don't send domain * - for proxy requests * - if it's no specified */ if (r->proxyreq || !conf->uri_list) { domain = NULL; } else { domain = conf->uri_list; } ap_table_mergen(r->err_headers_out, r->proxyreq == STD_PROXY ? "Proxy-Authenticate" : "WWW-Authenticate", ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%s\", " "algorithm=%s%s%s%s%s", ap_auth_name(r), nonce, conf->algorithm, opaque_param ? opaque_param : "", domain ? domain : "", stale ? ", stale=true" : "", qop));}/* * Authorization header verification code */static const char *get_hash(request_rec *r, const char *user, const char *realm, const char *auth_pwfile){ configfile_t *f; char l[MAX_STRING_LEN]; const char *rpw; char *w, *x; if (!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "Digest: Could not open password file: %s", auth_pwfile); return NULL; } while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { if ((l[0] == '#') || (!l[0])) continue; rpw = l; w = ap_getword(r->pool, &rpw, ':'); x = ap_getword(r->pool, &rpw, ':'); if (x && w && !strcmp(user, w) && !strcmp(realm, x)) { ap_cfg_closefile(f); return ap_pstrdup(r->pool, rpw); } } ap_cfg_closefile(f); return NULL;}static int check_nc(const request_rec *r, const digest_header_rec *resp, const digest_config_rec *conf){ unsigned long nc; const char *snc = resp->nonce_count; char *endptr; if (!conf->check_nc || !client_mm) return OK; nc = ap_strtol(snc, &endptr, 16); if (endptr < (snc+strlen(snc)) && !ap_isspace(*endptr)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: invalid nc %s received - not a number", snc); return !OK; } if (!resp->client) return !OK; if (nc != resp->client->nonce_count) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: Warning, possible replay attack: nonce-count " "check failed: %lu != %lu", nc, resp->client->nonce_count); return !OK; } return OK;}static int check_nonce(request_rec *r, digest_header_rec *resp, const digest_config_rec *conf){ double dt; time_rec nonce_time; char tmp, hash[NONCE_HASH_LEN+1]; if (strlen(resp->nonce) != NONCE_LEN) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: invalid nonce %s received - length is not %d", resp->nonce, NONCE_LEN); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } tmp = resp->nonce[NONCE_TIME_LEN]; resp->nonce[NONCE_TIME_LEN] = '\0'; ap_base64decode_binary(nonce_time.arr, resp->nonce); gen_nonce_hash(hash, resp->nonce, resp->opaque, r->server, conf); resp->nonce[NONCE_TIME_LEN] = tmp; resp->nonce_time = nonce_time.time; if (strcmp(hash, resp->nonce+NONCE_TIME_LEN)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: invalid nonce %s received - hash is not %s", resp->nonce, hash); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } dt = difftime(r->request_time, nonce_time.time); if (conf->nonce_lifetime > 0 && dt < 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: invalid nonce %s received - user attempted " "time travel", resp->nonce); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } if (conf->nonce_lifetime > 0) { if (dt > conf->nonce_lifetime) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, "Digest: user %s: nonce expired - sending new nonce", r->connection->user); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } } else if (conf->nonce_lifetime == 0 && resp->client) { if (memcmp(resp->client->last_nonce, resp->nonce, NONCE_LEN)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r, "Digest: user %s: one-time-nonce mismatch - sending " "new nonce", r->connection->user); note_digest_auth_failure(r, conf, resp, 1); return AUTH_REQUIRED; } } /* else (lifetime < 0) => never expires */ return OK;}/* The actual MD5 code... whee *//* RFC-2069 */static const char *old_digest(const request_rec *r, const digest_header_rec *resp, const char *ha1){ const char *ha2; ha2 = ap_md5(r->pool, (unsigned char *)ap_pstrcat(r->pool, r->method, ":", resp->uri, NULL)); return ap_md5(r->pool, (unsigned char *)ap_pstrcat(r->pool, ha1, ":", resp->nonce, ":", ha2, NULL));}/* RFC-2617 */static const char *new_digest(const request_rec *r, digest_header_rec *resp, const digest_config_rec *conf){ const char *ha1, *ha2, *a2; if (resp->algorithm && !strcasecmp(resp->algorithm, "MD5-sess")) { ha1 = get_session_HA1(r, resp, conf, 1); if (!ha1) return NULL; } else ha1 = conf->ha1; if (resp->message_qop && !strcasecmp(resp->message_qop, "auth-int")) a2 = ap_pstrcat(r->pool, r->method, ":", resp->uri, ":", ap_md5(r->pool, (const unsigned char*) ""), NULL); /* TBD */ else a2 = ap_pstrcat(r->pool, r->method, ":", resp->uri, NULL); ha2 = ap_md5(r->pool, (const unsigned char *)a2); return ap_md5(r->pool, (unsigned char *)ap_pstrcat(r->pool, ha1, ":", resp->nonce, ":", resp->nonce_count, ":", resp->cnonce, ":", resp->message_qop, ":", ha2, NULL));}static void copy_uri_components(uri_components *dst, uri_components *src, request_rec *r){ if (src->scheme && src->scheme[0] != '\0') dst->scheme = src->scheme; else dst->scheme = (char *) "http"; if (src->hostname && src->hostname[0] != '\0') { dst->hostname = ap_pstrdup(r->pool, src->hostname); ap_unescape_url(dst->hostname); } else dst->hostname = (char *) ap_get_server_name(r); if (src->port_str && src->port_str[0] != '\0') dst->port = src->port; else dst->port = ap_get_server_port(r); if (src->path && src->path[0] != '\0') { dst->path = ap_pstrdup(r->pool, src->path); ap_unescape_url(dst->path); } else dst->path = src->path; if (src->query && src->query[0] != '\0') { dst->query = ap_pstrdup(r->pool, src->query); ap_unescape_url(dst->query); } else dst->query = src->query;}/* This handles non-FQDN's. If h1 is empty, the comparison succeeds. Else * if h1 is a FQDN (i.e. contains a '.') then normal strcasecmp() is done. * Else only the first part of h2 (up to the first '.') is compared. */static int compare_hostnames(const char *h1, const char *h2){ const char *dot; /* if no hostname given, then ok */ if (!h1 || h1[0] == '\0') return 1; /* handle FQDN's in h1 */ dot = strchr(h1, '.'); if (dot != NULL) return !strcasecmp(h1, h2); /* handle non-FQDN's in h1 */ dot = strchr(h2, '.'); if (dot == NULL) return !strcasecmp(h1, h2); else return (strlen(h1) == (size_t) (dot - h2)) && !strncasecmp(h1, h2, dot-h2);}/* These functions return 0 if client is OK, and proper error status * if not... either AUTH_REQUIRED, if we made a check, and it failed, or * SERVER_ERROR, if things are so totally confused that we couldn't * figure out how to tell if the client is authorized or not. * * If they return DECLINED, and all other modules also decline, that's * treated by the server core as a configuration error, logged and * reported as such. *//* Determine user ID, and check if the attributes are correct, if it * really is that user, if the nonce is correct, etc. */static int authenticate_digest_user(request_rec *r){ digest_config_rec *conf; digest_header_rec *resp; request_rec *mainreq; conn_rec *conn = r->connection; const char *t; int res; /* do we require Digest auth for this URI? */ if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest")) return DECLINED; if (!ap_auth_name(r)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: need AuthName: %s", r->uri); return SERVER_ERROR; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -