This is mod_digest.c in view mode; [Download] [Up]
/* ==================================================================== * Copyright (c) 1995-1997 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see <http://www.apache.org/>. * */ /* * mod_digest: MD5 digest authentication * * by Alexei Kosut <akosut@nueva.pvt.k12.ca.us> * based on mod_auth, by Rob McCool and Robert S. Thau * */ #include "httpd.h" #include "http_config.h" #include "http_core.h" #include "http_log.h" #include "http_protocol.h" #include "util_md5.h" typedef struct digest_config_struct { char *pwfile; } digest_config_rec; typedef struct digest_header_struct { char *username; char *realm; char *nonce; char *requested_uri; char *digest; } digest_header_rec; void *create_digest_dir_config (pool *p, char *d) { return pcalloc (p, sizeof(digest_config_rec)); } const char *set_digest_slot (cmd_parms *cmd, void *offset, char *f, char *t) { if (t && strcmp(t, "standard")) return pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL); return set_string_slot(cmd, offset, f); } command_rec digest_cmds[] = { { "AuthDigestFile", set_digest_slot, (void*)XtOffsetOf(digest_config_rec,pwfile), OR_AUTHCFG, TAKE12, NULL }, { NULL } }; module MODULE_VAR_EXPORT digest_module; char *get_hash(request_rec *r, char *user, char *auth_pwfile) { FILE *f; char l[MAX_STRING_LEN]; const char *rpw; char *w, *x; if(!(f=pfopen(r->pool, auth_pwfile, "r"))) { log_reason ("Could not open password file", auth_pwfile, r); return NULL; } while(!(cfg_getline(l,MAX_STRING_LEN,f))) { if((l[0] == '#') || (!l[0])) continue; rpw = l; w = getword(r->pool, &rpw, ':'); x = getword(r->pool, &rpw, ':'); if(x && w && !strcmp(user,w) && !strcmp(auth_name(r), x)) { pfclose(r->pool, f); return pstrdup (r->pool, rpw); } } pfclose(r->pool, f); return NULL; } /* Parse the Authorization header, if it exists */ int get_digest_rec(request_rec *r, digest_header_rec *response) { const char *auth_line = table_get(r->headers_in, "Authorization"); int l; int s = 0, vk = 0, vv = 0; char *t, *key, *value; if (!(t = auth_type(r)) || strcasecmp(t, "Digest")) return DECLINED; if (!auth_name (r)) { log_reason ("need AuthName", r->uri, r); return SERVER_ERROR; } if (!auth_line) { note_digest_auth_failure (r); return AUTH_REQUIRED; } if (strcmp(getword (r->pool, &auth_line, ' '), "Digest")) { /* Client tried to authenticate using wrong auth scheme */ log_reason ("client used wrong authentication scheme", r->uri, r); note_digest_auth_failure (r); return AUTH_REQUIRED; } l = strlen(auth_line); key=palloc(r->pool,l); value=palloc(r->pool,l); /* There's probably a better way to do this, but for the time being... */ #define D_KEY 0 #define D_VALUE 1 #define D_STRING 2 #define D_EXIT -1 while (s != D_EXIT) { switch (s) { case D_STRING: if (auth_line[0] == '\"') { s = D_VALUE; } else { value[vv] = auth_line[0]; vv++; } auth_line++; break; case D_VALUE: if (isalnum(auth_line[0])) { value[vv] = auth_line[0]; vv++; } else if (auth_line[0] == '\"') { s = D_STRING; } else { value[vv] = '\0'; if (!strcasecmp(key, "username")) response->username = pstrdup(r->pool, value); else if (!strcasecmp(key, "realm")) response->realm = pstrdup(r->pool, value); else if (!strcasecmp(key, "nonce")) response->nonce = pstrdup(r->pool, value); else if (!strcasecmp(key, "uri")) response->requested_uri = pstrdup(r->pool, value); else if (!strcasecmp(key, "response")) response->digest = pstrdup(r->pool, value); vv = 0; s = D_KEY; } auth_line++; break; case D_KEY: if (isalnum(auth_line[0])) { key[vk] = auth_line[0]; vk++; } else if (auth_line[0] == '=') { key[vk] = '\0'; vk = 0; s = D_VALUE; } auth_line++; break; } if (auth_line[-1] == '\0') s = D_EXIT; } if (!response->username || !response->realm || !response->nonce || !response->requested_uri || !response->digest) { note_digest_auth_failure (r); return AUTH_REQUIRED; } r->connection->user = response->username; r->connection->auth_type = "Digest"; return OK; } /* The actual MD5 code... whee */ char *find_digest(request_rec *r, digest_header_rec *h, char *a1) { return md5(r->pool, (unsigned char *)pstrcat(r->pool, a1, ":", h->nonce, ":", md5(r->pool, (unsigned char *)pstrcat(r->pool,r->method,":", h->requested_uri,NULL)), NULL)); } /* 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 it really is that user, for HTTP * basic authentication... */ int authenticate_digest_user (request_rec *r) { digest_config_rec *sec = (digest_config_rec *)get_module_config (r->per_dir_config, &digest_module); digest_header_rec *response = pcalloc (r->pool, sizeof(digest_header_rec)); conn_rec *c = r->connection; char *a1; char errstr[MAX_STRING_LEN]; int res; if ((res = get_digest_rec (r, response))) return res; if(!sec->pwfile) return DECLINED; if (!(a1 = get_hash(r, c->user, sec->pwfile))) { ap_snprintf(errstr, sizeof(errstr), "user %s not found",c->user); log_reason (errstr, r->uri, r); note_digest_auth_failure (r); return AUTH_REQUIRED; } /* anyone know where the prototype for crypt is? */ if(strcmp(response->digest, find_digest(r, response, a1))) { ap_snprintf(errstr, sizeof(errstr), "user %s: password mismatch",c->user); log_reason (errstr, r->uri, r); note_digest_auth_failure (r); return AUTH_REQUIRED; } return OK; } /* Checking ID */ int digest_check_auth (request_rec *r) { char *user = r->connection->user; int m = r->method_number; int method_restricted = 0; register int x; const char *t; char *w; array_header *reqs_arr; require_line *reqs; if (!(t = auth_type(r)) || strcasecmp(t, "Digest")) return DECLINED; reqs_arr = requires (r); /* If there is no "requires" directive, * then any user will do. */ if (!reqs_arr) return OK; reqs = (require_line *)reqs_arr->elts; for(x=0; x < reqs_arr->nelts; x++) { if (! (reqs[x].method_mask & (1 << m))) continue; method_restricted = 1; t = reqs[x].requirement; w = getword(r->pool, &t, ' '); if(!strcmp(w,"valid-user")) return OK; else if(!strcmp(w,"user")) { while(t[0]) { w = getword_conf (r->pool, &t); if(!strcmp(user,w)) return OK; } } else return DECLINED; } if (!method_restricted) return OK; note_digest_auth_failure(r); return AUTH_REQUIRED; } module MODULE_VAR_EXPORT digest_module = { STANDARD_MODULE_STUFF, NULL, /* 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 */ NULL, /* fixups */ NULL, /* logger */ NULL, /* header parser */ NULL /* child_init */ };
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.