📄 mod_auth_digest.c
字号:
/* get the client response and mark */ mainreq = r; while (mainreq->main != NULL) mainreq = mainreq->main; while (mainreq->prev != NULL) mainreq = mainreq->prev; resp = (digest_header_rec *) ap_get_module_config(mainreq->request_config, &digest_auth_module); resp->needed_auth = 1; /* get our conf */ conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config, &digest_auth_module); /* check for existence and syntax of Auth header */ if (resp->auth_hdr_sts != VALID) { if (resp->auth_hdr_sts == NOT_DIGEST) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: client used wrong authentication scheme " "`%s': %s", resp->scheme, r->uri); else if (resp->auth_hdr_sts == INVALID) ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: missing user, realm, nonce, uri, digest, " "cnonce, or nonce_count in authorization header: %s", r->uri); /* else (resp->auth_hdr_sts == NO_HEADER) */ note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } r->connection->user = (char *) resp->username; r->connection->ap_auth_type = (char *) "Digest"; /* check the auth attributes */ if (strcmp(resp->uri, resp->raw_request_uri)) { /* Hmm, the simple match didn't work (probably a proxy modified the * request-uri), so lets do a more sophisticated match */ uri_components r_uri, d_uri; copy_uri_components(&r_uri, resp->psd_request_uri, r); if (ap_parse_uri_components(r->pool, resp->uri, &d_uri) != HTTP_OK) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: invalid uri <%s> in Authorization header", resp->uri); return BAD_REQUEST; } if (d_uri.hostname) ap_unescape_url(d_uri.hostname); if (d_uri.path) ap_unescape_url(d_uri.path); if (d_uri.query) ap_unescape_url(d_uri.query); if (r->method_number == M_CONNECT) { if (strcmp(resp->uri, r_uri.hostinfo)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: uri mismatch - <%s> does not match " "request-uri <%s>", resp->uri, r_uri.hostinfo); return BAD_REQUEST; } } else if ( /* check hostname matches, if present */ !compare_hostnames(d_uri.hostname, r_uri.hostname) /* check port matches, if present */ || (d_uri.port_str && d_uri.port != r_uri.port) /* check that server-port is default port if no port present */ || (d_uri.hostname && d_uri.hostname[0] != '\0' && !d_uri.port_str && r_uri.port != ap_default_port(r)) /* check that path matches */ || (d_uri.path != r_uri.path /* either exact match */ && (!d_uri.path || !r_uri.path || strcmp(d_uri.path, r_uri.path)) /* or '*' matches empty path in scheme://host */ && !(d_uri.path && !r_uri.path && resp->psd_request_uri->hostname && d_uri.path[0] == '*' && d_uri.path[1] == '\0')) /* check that query matches */ || (d_uri.query != r_uri.query && (!d_uri.query || !r_uri.query || strcmp(d_uri.query, r_uri.query))) ) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: uri mismatch - <%s> does not match " "request-uri <%s>", resp->uri, resp->raw_request_uri); return BAD_REQUEST; } } if (resp->opaque && resp->opaque_num == 0) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: received invalid opaque - got `%s'", resp->opaque); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } if (strcmp(resp->realm, conf->realm)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: realm mismatch - got `%s' but expected `%s'", resp->realm, conf->realm); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } if (resp->algorithm != NULL && strcasecmp(resp->algorithm, "MD5") && strcasecmp(resp->algorithm, "MD5-sess")) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: unknown algorithm `%s' received: %s", resp->algorithm, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } if (!conf->pwfile) return DECLINED; if (!(conf->ha1 = get_hash(r, conn->user, conf->realm, conf->pwfile))) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: user `%s' in realm `%s' not found: %s", conn->user, conf->realm, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } if (resp->message_qop == NULL) { /* old (rfc-2069) style digest */ if (strcmp(resp->digest, old_digest(r, resp, conf->ha1))) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: user %s: password mismatch: %s", conn->user, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } } else { const char *exp_digest; int match = 0, idx; for (idx=0; conf->qop_list[idx] != NULL; idx++) { if (!strcasecmp(conf->qop_list[idx], resp->message_qop)) { match = 1; break; } } if (!match && !(conf->qop_list[0] == NULL && !strcasecmp(resp->message_qop, "auth"))) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: invalid qop `%s' received: %s", resp->message_qop, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } if (check_nc(r, resp, conf) != OK) { note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } exp_digest = new_digest(r, resp, conf); if (!exp_digest) { /* we failed to allocate a client struct */ return SERVER_ERROR; } if (strcmp(resp->digest, exp_digest)) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: user %s: password mismatch: %s", conn->user, r->uri); note_digest_auth_failure(r, conf, resp, 0); return AUTH_REQUIRED; } } /* Note: this check is done last so that a "stale=true" can be generated if the nonce is old */ if ((res = check_nonce(r, resp, conf))) return res; return OK;}/* * Checking ID */static table *groups_for_user(request_rec *r, const char *user, const char *grpfile){ configfile_t *f; table *grps = ap_make_table(r->pool, 15); pool *sp; char l[MAX_STRING_LEN]; const char *group_name, *ll, *w; if (!(f = ap_pcfg_openfile(r->pool, grpfile))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "Digest: Could not open group file: %s", grpfile); return NULL; } sp = ap_make_sub_pool(r->pool); while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) { if ((l[0] == '#') || (!l[0])) continue; ll = l; ap_clear_pool(sp); group_name = ap_getword(sp, &ll, ':'); while (ll[0]) { w = ap_getword_conf(sp, &ll); if (!strcmp(w, user)) { ap_table_setn(grps, ap_pstrdup(r->pool, group_name), "in"); break; } } } ap_cfg_closefile(f); ap_destroy_pool(sp); return grps;}static int digest_check_auth(request_rec *r){ const digest_config_rec *conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config, &digest_auth_module); const char *user = r->connection->user; int m = r->method_number; int method_restricted = 0; register int x; const char *t, *w; table *grpstatus; const array_header *reqs_arr; require_line *reqs; if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest")) return DECLINED; reqs_arr = ap_requires(r); /* If there is no "requires" directive, then any user will do. */ if (!reqs_arr) return OK; reqs = (require_line *) reqs_arr->elts; if (conf->grpfile) grpstatus = groups_for_user(r, user, conf->grpfile); else grpstatus = NULL; for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (1 << m))) continue; method_restricted = 1; t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (!strcasecmp(w, "valid-user")) return OK; else if (!strcasecmp(w, "user")) { while (t[0]) { w = ap_getword_conf(r->pool, &t); if (!strcmp(user, w)) return OK; } } else if (!strcasecmp(w, "group")) { if (!grpstatus) return DECLINED; while (t[0]) { w = ap_getword_conf(r->pool, &t); if (ap_table_get(grpstatus, w)) return OK; } } else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: access to %s failed, reason: unknown require " "directive \"%s\"", r->uri, reqs[x].requirement); return DECLINED; } } if (!method_restricted) return OK; ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: access to %s failed, reason: user %s not allowed access", r->uri, user); note_digest_auth_failure(r, conf, (digest_header_rec *) ap_get_module_config(r->request_config, &digest_auth_module), 0); return AUTH_REQUIRED;}/* * Authorization-Info header code */#ifdef SEND_DIGESTstatic const char *hdr(const table *tbl, const char *name){ const char *val = ap_table_get(tbl, name); if (val) return val; else return "";}#endifstatic int add_auth_info(request_rec *r){ const digest_config_rec *conf = (digest_config_rec *) ap_get_module_config(r->per_dir_config, &digest_auth_module); digest_header_rec *resp = (digest_header_rec *) ap_get_module_config(r->request_config, &digest_auth_module); const char *ai = NULL, *digest = NULL, *nextnonce = ""; if (resp == NULL || !resp->needed_auth || conf == NULL) return OK; /* rfc-2069 digest */ if (resp->message_qop == NULL) { /* old client, so calc rfc-2069 digest */#ifdef SEND_DIGEST /* most of this totally bogus because the handlers don't set the * headers until the final handler phase (I wonder why this phase * is called fixup when there's almost nothing you can fix up...) * * Because it's basically impossible to get this right (e.g. the * Content-length is never set yet when we get here, and we can't * calc the entity hash) it's best to just leave this #def'd out. */ char *entity_info = ap_md5(r->pool, (unsigned char *) ap_pstrcat(r->pool, resp->raw_request_uri, ":", r->content_type ? r->content_type : ap_default_type(r), ":", hdr(r->headers_out, "Content-Length"), ":", r->content_encoding ? r->content_encoding : "", ":", hdr(r->headers_out, "Last-Modified"), ":", r->no_cache && !ap_table_get(r->headers_out, "Expires") ? ap_gm_timestr_822(r->pool, r->request_time) : hdr(r->headers_out, "Expires"), NULL)); digest = ap_md5(r->pool, (unsigned char *)ap_pstrcat(r->pool, conf->ha1, ":", resp->nonce, ":", r->method, ":", ap_gm_timestr_822(r->pool, r->request_time), ":", entity_info, ":", ap_md5(r->pool, (unsigned char *) ""), /* H(entity) - TBD */ NULL));#endif } /* setup nextnonce */ if (conf->nonce_lifetime > 0) { /* send nextnonce if current nonce will expire in less than 30 secs */ if (difftime(r->request_time, resp->nonce_time) > (conf->nonce_lifetime-NEXTNONCE_DELTA)) { nextnonce = ap_pstrcat(r->pool, ", nextnonce=\"", gen_nonce(r->pool, r->request_time, resp->opaque, r->server, conf), "\"", NULL); if (resp->client) resp->client->nonce_count = 0; } } else if (conf->nonce_lifetime == 0 && resp->client) { const char *nonce = gen_nonce(r->pool, 0, resp->opaque, r->server, conf); nextnonce = ap_pstrcat(r->pool, ", nextnonce=\"", nonce, "\"", NULL); memcpy(resp->client->last_nonce, nonce, NONCE_LEN+1); } /* else nonce never expires, hence no nextnonce */ /* do rfc-2069 digest */ if (conf->qop_list[0] && !strcasecmp(conf->qop_list[0], "none") && resp->message_qop == NULL) { /* use only RFC-2069 format */ if (digest) ai = ap_pstrcat(r->pool, "digest=\"", digest, "\"", nextnonce,NULL); else ai = nextnonce; } else { const char *resp_dig, *ha1, *a2, *ha2; /* calculate rspauth attribute */ if (resp->algorithm && !strcasecmp(resp->algorithm, "MD5-sess")) { ha1 = get_session_HA1(r, resp, conf, 0); if (!ha1) { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r, "Digest: internal error: couldn't find session " "info for user %s", resp->username); return !OK; } } else ha1 = conf->ha1; if (resp->message_qop && !strcasecmp(resp->message_qop, "auth-int")) a2 = ap_pstrcat(r->pool, ":", resp->uri, ":", ap_md5(r->pool, (const unsigned char *) ""), NULL); /* TBD */ else a2 = ap_pstrcat(r->pool, ":", resp->uri, NULL); ha2 = ap_md5(r->pool, (const unsigned char *)a2); resp_dig = ap_md5(r->pool, (unsigned char *)ap_pstrcat(r->pool, ha1, ":", resp->nonce, ":", resp->nonce_count, ":", resp->cnonce, ":", resp->message_qop ? resp->message_qop : "", ":", ha2, NULL)); /* assemble Authentication-Info header */ ai = ap_pstrcat(r->pool, "rspauth=\"", resp_dig, "\"", nextnonce, resp->cnonce ? ", cnonce=\"" : "", resp->cnonce ? ap_escape_quotes(r->pool, resp->cnonce) : "", resp->cnonce ? "\"" : "", resp->nonce_count ? ", nc=" : "", resp->nonce_count ? resp->nonce_count : "", resp->message_qop ? ", qop=" : "", resp->message_qop ? resp->message_qop : "", digest ? "digest=\"" : "", digest ? digest : "", digest ? "\"" : "", NULL); } if (ai && ai[0]) ap_table_mergen(r->headers_out, r->proxyreq == STD_PROXY ? "Proxy-Authentication-Info" : "Authentication-Info", ai); return OK;}module MODULE_VAR_EXPORT digest_auth_module ={ STANDARD_MODULE_STUFF, initialize_module, /* initializer */ create_digest_dir_config, /* dir config creater */ NULL, /* dir merger --- default is to override */ NULL, /* server config */ NULL, /* merge server config */ digest_cmds, /* command table */ NULL, /* handlers */ NULL, /* filename translation */ authenticate_digest_user, /* check_user_id */ digest_check_auth, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ add_auth_info, /* fixups */ NULL, /* logger */ NULL, /* header parser */ NULL, /* child_init */ NULL, /* child_exit */ update_nonce_count /* post read-request */};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -