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

📄 mod_auth_digest.c

📁 apache 安装教程 apache 安装教程
💻 C
📖 第 1 页 / 共 4 页
字号:
	 */	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 + -