home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_digest.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-01  |  11.0 KB  |  391 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /*
  59.  * mod_digest: MD5 digest authentication
  60.  * 
  61.  * by Alexei Kosut <akosut@nueva.pvt.k12.ca.us>
  62.  * based on mod_auth, by Rob McCool and Robert S. Thau
  63.  *
  64.  */
  65.  
  66. #include "httpd.h"
  67. #include "http_config.h"
  68. #include "http_core.h"
  69. #include "http_log.h"
  70. #include "http_protocol.h"
  71. #include "util_md5.h"
  72.  
  73. typedef struct digest_config_struct {
  74.     char *pwfile;
  75. } digest_config_rec;
  76.  
  77. typedef struct digest_header_struct {
  78.     char *username;
  79.     char *realm;
  80.     char *nonce;
  81.     char *requested_uri;
  82.     char *digest;
  83. } digest_header_rec;
  84.  
  85. static void *create_digest_dir_config(pool *p, char *d)
  86. {
  87.     return ap_pcalloc(p, sizeof(digest_config_rec));
  88. }
  89.  
  90. static const char *set_digest_slot(cmd_parms *cmd, void *offset, char *f, char *t)
  91. {
  92.     if (t && strcmp(t, "standard"))
  93.     return ap_pstrcat(cmd->pool, "Invalid auth file type: ", t, NULL);
  94.  
  95.     return ap_set_string_slot(cmd, offset, f);
  96. }
  97.  
  98. static const command_rec digest_cmds[] =
  99. {
  100.     {"AuthDigestFile", set_digest_slot,
  101.   (void *) XtOffsetOf(digest_config_rec, pwfile), OR_AUTHCFG, TAKE12, NULL},
  102.     {NULL}
  103. };
  104.  
  105. module MODULE_VAR_EXPORT digest_module;
  106.  
  107. static char *get_hash(request_rec *r, char *user, char *auth_pwfile)
  108. {
  109.     configfile_t *f;
  110.     char l[MAX_STRING_LEN];
  111.     const char *rpw;
  112.     char *w, *x;
  113.  
  114.     if (!(f = ap_pcfg_openfile(r->pool, auth_pwfile))) {
  115.     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  116.             "Could not open password file: %s", auth_pwfile);
  117.     return NULL;
  118.     }
  119.     while (!(ap_cfg_getline(l, MAX_STRING_LEN, f))) {
  120.     if ((l[0] == '#') || (!l[0]))
  121.         continue;
  122.     rpw = l;
  123.     w = ap_getword(r->pool, &rpw, ':');
  124.     x = ap_getword(r->pool, &rpw, ':');
  125.  
  126.     if (x && w && !strcmp(user, w) && !strcmp(ap_auth_name(r), x)) {
  127.         ap_cfg_closefile(f);
  128.         return ap_pstrdup(r->pool, rpw);
  129.     }
  130.     }
  131.     ap_cfg_closefile(f);
  132.     return NULL;
  133. }
  134.  
  135. /* Parse the Authorization header, if it exists */
  136.  
  137. static int get_digest_rec(request_rec *r, digest_header_rec * response)
  138. {
  139.     const char *auth_line = ap_table_get(r->headers_in,
  140.                                     r->proxyreq ? "Proxy-Authorization"
  141.                                     : "Authorization");
  142.     int l;
  143.     int s, vk = 0, vv = 0;
  144.     const char *t;
  145.     char *key, *value;
  146.     const char *scheme;
  147.  
  148.     if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest"))
  149.     return DECLINED;
  150.  
  151.     if (!ap_auth_name(r)) {
  152.     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  153.             "need AuthName: %s", r->uri);
  154.     return SERVER_ERROR;
  155.     }
  156.  
  157.     if (!auth_line) {
  158.     ap_note_digest_auth_failure(r);
  159.     return AUTH_REQUIRED;
  160.     }
  161.  
  162.     if (strcasecmp(scheme = ap_getword_white(r->pool, &auth_line), "Digest")) {
  163.     /* Client tried to authenticate using wrong auth scheme */
  164.     ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server,
  165.             "client used wrong authentication scheme: %s for %s", 
  166.             scheme, r->uri);
  167.     ap_note_digest_auth_failure(r);
  168.     return AUTH_REQUIRED;
  169.     }
  170.  
  171.     l = strlen(auth_line);
  172.  
  173.     /* Note we don't allocate l + 1 bytes for these deliberately, because
  174.      * there has to be at least one '=' character for either of these two
  175.      * new strings to be terminated.  That takes care of the need for +1.
  176.      */
  177.     key = ap_palloc(r->pool, l);
  178.     value = ap_palloc(r->pool, l);
  179.  
  180.     /* There's probably a better way to do this, but for the time being... */
  181.  
  182. #define D_KEY 0
  183. #define D_VALUE 1
  184. #define D_STRING 2
  185. #define D_EXIT -1
  186.  
  187.     s = D_KEY;
  188.     while (s != D_EXIT) {
  189.     switch (s) {
  190.     case D_STRING:
  191.         if (auth_line[0] == '\"') {
  192.         s = D_VALUE;
  193.         }
  194.         else {
  195.         value[vv] = auth_line[0];
  196.         vv++;
  197.         }
  198.         auth_line++;
  199.         break;
  200.  
  201.     case D_VALUE:
  202.         if (ap_isalnum(auth_line[0])) {
  203.         value[vv] = auth_line[0];
  204.         vv++;
  205.         }
  206.         else if (auth_line[0] == '\"') {
  207.         s = D_STRING;
  208.         }
  209.         else {
  210.         value[vv] = '\0';
  211.  
  212.         if (!strcasecmp(key, "username"))
  213.             response->username = ap_pstrdup(r->pool, value);
  214.         else if (!strcasecmp(key, "realm"))
  215.             response->realm = ap_pstrdup(r->pool, value);
  216.         else if (!strcasecmp(key, "nonce"))
  217.             response->nonce = ap_pstrdup(r->pool, value);
  218.         else if (!strcasecmp(key, "uri"))
  219.             response->requested_uri = ap_pstrdup(r->pool, value);
  220.         else if (!strcasecmp(key, "response"))
  221.             response->digest = ap_pstrdup(r->pool, value);
  222.  
  223.         vv = 0;
  224.         s = D_KEY;
  225.         }
  226.         auth_line++;
  227.         break;
  228.  
  229.     case D_KEY:
  230.         if (ap_isalnum(auth_line[0])) {
  231.         key[vk] = auth_line[0];
  232.         vk++;
  233.         }
  234.         else if (auth_line[0] == '=') {
  235.         key[vk] = '\0';
  236.         vk = 0;
  237.         s = D_VALUE;
  238.         }
  239.         auth_line++;
  240.         break;
  241.     }
  242.  
  243.     if (auth_line[-1] == '\0')
  244.         s = D_EXIT;
  245.     }
  246.  
  247.     if (!response->username || !response->realm || !response->nonce ||
  248.     !response->requested_uri || !response->digest) {
  249.     ap_note_digest_auth_failure(r);
  250.     return AUTH_REQUIRED;
  251.     }
  252.  
  253.     r->connection->user = response->username;
  254.     r->connection->ap_auth_type = "Digest";
  255.  
  256.     return OK;
  257. }
  258.  
  259. /* The actual MD5 code... whee */
  260.  
  261. static char *find_digest(request_rec *r, digest_header_rec * h, char *a1)
  262. {
  263.     return ap_md5(r->pool,
  264.           (unsigned char *)ap_pstrcat(r->pool, a1, ":", h->nonce, ":",
  265.                        ap_md5(r->pool,
  266.                    (unsigned char *)ap_pstrcat(r->pool, r->method, ":",
  267.                             h->requested_uri, NULL)),
  268.                        NULL));
  269. }
  270.  
  271. /* These functions return 0 if client is OK, and proper error status
  272.  * if not... either AUTH_REQUIRED, if we made a check, and it failed, or
  273.  * SERVER_ERROR, if things are so totally confused that we couldn't
  274.  * figure out how to tell if the client is authorized or not.
  275.  *
  276.  * If they return DECLINED, and all other modules also decline, that's
  277.  * treated by the server core as a configuration error, logged and
  278.  * reported as such.
  279.  */
  280.  
  281. /* Determine user ID, and check if it really is that user, for HTTP
  282.  * basic authentication...
  283.  */
  284.  
  285. static int authenticate_digest_user(request_rec *r)
  286. {
  287.     digest_config_rec *sec =
  288.     (digest_config_rec *) ap_get_module_config(r->per_dir_config,
  289.                         &digest_module);
  290.     digest_header_rec *response = ap_pcalloc(r->pool, sizeof(digest_header_rec));
  291.     conn_rec *c = r->connection;
  292.     char *a1;
  293.     int res;
  294.  
  295.     if ((res = get_digest_rec(r, response)))
  296.     return res;
  297.  
  298.     if (!sec->pwfile)
  299.     return DECLINED;
  300.  
  301.     if (!(a1 = get_hash(r, c->user, sec->pwfile))) {
  302.     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  303.             "user %s not found: %s", c->user, r->uri);
  304.     ap_note_digest_auth_failure(r);
  305.     return AUTH_REQUIRED;
  306.     }
  307.     if (strcmp(response->digest, find_digest(r, response, a1))) {
  308.     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  309.             "user %s: password mismatch: %s", c->user, r->uri);
  310.     ap_note_digest_auth_failure(r);
  311.     return AUTH_REQUIRED;
  312.     }
  313.     return OK;
  314. }
  315.  
  316. /* Checking ID */
  317.  
  318. static int digest_check_auth(request_rec *r)
  319. {
  320.     char *user = r->connection->user;
  321.     int m = r->method_number;
  322.     int method_restricted = 0;
  323.     register int x;
  324.     const char *t;
  325.     char *w;
  326.     const array_header *reqs_arr;
  327.     require_line *reqs;
  328.  
  329.     if (!(t = ap_auth_type(r)) || strcasecmp(t, "Digest"))
  330.     return DECLINED;
  331.  
  332.     reqs_arr = ap_requires(r);
  333.     /* If there is no "requires" directive, 
  334.      * then any user will do.
  335.      */
  336.     if (!reqs_arr)
  337.     return OK;
  338.     reqs = (require_line *) reqs_arr->elts;
  339.  
  340.     for (x = 0; x < reqs_arr->nelts; x++) {
  341.  
  342.     if (!(reqs[x].method_mask & (1 << m)))
  343.         continue;
  344.  
  345.     method_restricted = 1;
  346.  
  347.     t = reqs[x].requirement;
  348.     w = ap_getword_white(r->pool, &t);
  349.     if (!strcmp(w, "valid-user"))
  350.         return OK;
  351.     else if (!strcmp(w, "user")) {
  352.         while (t[0]) {
  353.         w = ap_getword_conf(r->pool, &t);
  354.         if (!strcmp(user, w))
  355.             return OK;
  356.         }
  357.     }
  358.     else
  359.         return DECLINED;
  360.     }
  361.  
  362.     if (!method_restricted)
  363.     return OK;
  364.  
  365.     ap_note_digest_auth_failure(r);
  366.     return AUTH_REQUIRED;
  367. }
  368.  
  369. module MODULE_VAR_EXPORT digest_module =
  370. {
  371.     STANDARD_MODULE_STUFF,
  372.     NULL,            /* initializer */
  373.     create_digest_dir_config,    /* dir config creater */
  374.     NULL,            /* dir merger --- default is to override */
  375.     NULL,            /* server config */
  376.     NULL,            /* merge server config */
  377.     digest_cmds,        /* command table */
  378.     NULL,            /* handlers */
  379.     NULL,            /* filename translation */
  380.     authenticate_digest_user,    /* check_user_id */
  381.     digest_check_auth,        /* check auth */
  382.     NULL,            /* check access */
  383.     NULL,            /* type_checker */
  384.     NULL,            /* fixups */
  385.     NULL,            /* logger */
  386.     NULL,            /* header parser */
  387.     NULL,            /* child_init */
  388.     NULL,            /* child_exit */
  389.     NULL            /* post read-request */
  390. };
  391.