"Fossies" - the Fresh Open Source Software Archive

Member "mod_auth_radius-1.5.7/mod_auth_radius.c" (24 Mar 2003, 43230 Bytes) of package /linux/www/apache_httpd_modules/old/mod_auth_radius-1.5.7.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /* ====================================================================
    2  * Copyright (c) 1997-2002 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.
   24  *
   25  * 5. Redistributions of any form whatsoever must retain the following
   26  *    acknowledgment:
   27  *    "This product includes software developed by the Apache Group
   28  *    for use in the Apache HTTP server project (http://www.apache.org/)."
   29  *
   30  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
   31  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   32  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   33  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
   34  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   35  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   36  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   37  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   38  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
   39  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   40  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
   41  * OF THE POSSIBILITY OF SUCH DAMAGE.
   42  * ====================================================================
   43  *
   44  * This software consists of voluntary contributions made by many
   45  * individuals on behalf of the Apache Group and was originally based
   46  * on public domain software written at the National Center for
   47  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
   48  * For more information on the Apache Group and the Apache HTTP server
   49  * project, please see <http://www.apache.org/>.
   50  *
   51  *
   52  *  CVS $Id: mod_auth_radius.c,v 1.15 2003/03/24 19:16:15 aland Exp $
   53  */
   54 
   55 /*
   56   Everyone wants strong authentication over the web.  For us, this means
   57   RADIUS.
   58   
   59   Using static passwords & RADIUS authentication over the web is a BAD IDEA.
   60   Everyone can sniff the passwords, as they're sent over the net in the clear.
   61   RADIUS web authentication is a REALLY BAD IDEA if you use the same RADIUS
   62   server for web and NAS (dial-up) or firewall users.  Then ANYONE can
   63   pretend to be you, and break through your firewall with minimal effort.
   64 
   65   PLEASE use a different RADIUS server for web authentication and dial-up
   66   or firewall users!  If you must use the same server, go for one-time
   67   passwords.  They're ever so much more secure.
   68 
   69   Also, do NOT have your RADIUS server visible to the external world.
   70   Doing so makes all kinds of attacks possible.
   71 
   72   **************************************************
   73   
   74   Add to Configuration file BEFORE mod_auth.o:
   75   Module radius_auth_module    mod_auth_radius.o
   76   
   77   Add to server configuration file
   78   AddRadiusAuth <server>[:port] <secret> [<seconds>[:<retries>]]
   79   AddRadiusCookieValid <minutes>
   80   AddModule modules/extra/mod_auth_radius.o              (for 1.3.x)
   81   
   82   Add to directory configuration
   83   AddRadiusAuth <server>[:port] <secret> [<seconds>]
   84   AuthRadiusBindAddress <local address/interface>
   85   AuthRadiusAuthoritative on
   86   AuthRadiusActive on
   87   AuthRadiusCookieValid <minutes>
   88 
   89   **************************************************
   90 
   91   Adding mod_auth_radius to the Configuration file before mod_auth
   92   allows you to have mod_auth_radius authoritative by default, but NOT
   93   have it interfere with the rest of your configuration.  The authentication
   94   methods are tried from the bottom of the list, on up.
   95 
   96   You must have at least one authentication method as authoritative.  If
   97   they all return "DECLINED", you get "server configuration error" message.
   98 
   99   AddRadiusAuth configures the RADIUS server name (and optional port).
  100   You must also specify the shared secret, and tell the RADIUS server that
  101   the web host machine is a valid RADIUS client.  The optional <seconds> field
  102   specifies how long Apache waits before giving up, and deciding that the
  103   RADIUS server is down.  It then returns a "DENIED" error.
  104 
  105   If you want, you can specify how long the returned cookies are valid.
  106   The time is in minutes, with the magic value of '0' meaning forever.
  107 
  108 
  109   The per-dir configuration Cookie Valid time does NOT over-ride the server
  110   configuration.  mod_auth_radius choose the most restrictive of the two to
  111   use.  This way, a site administrator can say all cookies are valid forever,
  112   and then make some directories a bit more secure, by forcing
  113   re-authentication every hour.
  114 
  115   If you want logging, use the standard Apache access log.  A log message
  116   is generated ONLY when a user has authenticated, and their name & file
  117   accessed is put in the log file.
  118 
  119   How it works
  120   ============
  121 
  122   The browser requests a page: http://www.example.com/index.html
  123   
  124   Apache notes that the directory is access controlled, and sends a
  125   "Authorization Required".
  126 
  127   The browser asks for a username & password, which it then sends to Apache,
  128   along with a request for the page again.
  129 
  130   Apache calls mod_auth_radius, which notes that there is no RADIUS cookie
  131   in the request.
  132 
  133   mod_auth_radius packages up the username/password into a RADIUS request,
  134   and sends it to the RADIUS server.
  135 
  136   The RADIUS server does its magic, and decides yes/no for authentication.
  137 
  138   If no, mod_auth_radius returns DENIED.
  139 
  140   If yes, mod_auth_radius returns a cookie containing MD5'd public+private
  141   information.
  142 
  143   The web browser uses this cookie on all subsequent requests, and
  144   mod_auth_radius verifies the cookie is valid, and doesn't contact the
  145   RADIUS server again.
  146 
  147   Some caveats
  148   ============
  149 
  150   This works fine for static passwords (i.e. "user", "password"), but needs
  151   a bit more attention for one-time passwords.  All of the browsers I've
  152   tested don't use the cookie immediately if you're accessing a directory
  153   as:
  154 
  155   http://www.example.com/
  156 
  157   What's hidden here is that the following files are checked for:
  158 
  159   http://www.example.com/
  160   http://www.example.com/home.html
  161   http://www.example.com/home.cgi
  162   http://www.example.com/index.cgi
  163   http://www.example.com/index.html
  164 
  165   etc., all in sequence.  This module does a 'stat', and returns "NOT FOUND"
  166   when anyone tries to access a file which doesn't exist.  However,
  167   it WILL authenticate for a file which does exists, but the browser may
  168   not use the returned cookie when accessing a different page.
  169 
  170   The way to fix this is to point the browser at a specific page. i.e.
  171 
  172   http://www.example.com/
  173          says "connect to our _secure_ site",  where _secure_ is a link to 
  174 
  175   http://www.example.com/secure/index.html
  176 
  177 
  178   People using static passwords don't need to do this, but if they don't,
  179   they'll notice that their RADIUS server is getting 1-4 hits for every web
  180   authentication request.
  181 
  182 
  183   Some browsers (I.E.) have a problem with sending cookies on initial
  184   requests. If you have a file index.html which includes img/foo.gif
  185   in the same directory.  The user authenticates, reads index.html
  186   (with the cookie in the request header), BUT on reading the gifs,
  187   the cookie is NOT included.
  188   
  189   This problem can be avoided by EITHER putting the gifs in the same
  190   directory as the index.html file, or putting moving the entire tree
  191   down a node, and having a NEW index.html which points to ./moved/index.html
  192   This is ridiculously ugly, but it seems to work.
  193 
  194   
  195   About the cookies
  196   =================
  197 
  198   The cookies are valid for a specified time, or until the browser dies.
  199   mod_auth_radius will forcibly try to expire cookies that it thinks are
  200   too old.  If your browser doesn't expire the cookie, you'll see an
  201   authorization required message over and over.  You must then exit the
  202   browser, and re-load the web page.
  203 
  204   Any questions or comments can be sent to me at: aland@freeradius.org
  205 
  206 
  207   Challenge-Response support
  208   ==========================
  209 
  210   As of 1.2.1, this module supports the full RADIUS challenge-response
  211   mechanism.  From the user's perspective, on authenticatation, type
  212   in username & garbage (or NUL) password.  Click <OK>, and you'll get
  213   an authentication failure.  This is fine, as mod_auth_radius has secretly
  214   set a cookie, and modified the Basic-Authentication-Realm.
  215 
  216   When the authentication fails, click <OK> to continue, and you'll get
  217   another username/password authentication window.  This time, however,
  218   you'll see your username displayed, along with the RADIUS Reply-Message
  219   at the top of the authentication window.  This message usually includes
  220   a challenge.
  221 
  222   Type in your username, and put the response to the challenge in the password
  223   field.  Click <OK> again, and you should be authenticated.
  224 
  225   The secret is that cookies are being magically set back and forth, and these
  226   cookies include the RADIUS state variable.
  227 
  228   The challenge-response works on Netscape 3.x and 4.x, HotJava, but NOT
  229   on Internet Explorer.  I.E. does not appear to follow the relevant RFCs
  230   properly.
  231 
  232 
  233   Version History
  234   ===============
  235 
  236   1.5.4  Support for retries from John Lines <john.lines@integris.co.uk>
  237 
  238   1.5.3 Bug fix from Bryan Stansell <bryan@stansell.org>, to set
  239         the right data element for the AddRadiusCookieValid configuration
  240     item.
  241 
  242   1.5.2 Updates for NAS-Identifier and NAS-IP-Address, based on ideas
  243         from Adrian Hosey <ahosey@systhug.com>.  The NAS-Identifier is
  244     the virtual server host name, and the NAS-IP-Address is the
  245     IP address of the base server.
  246 
  247     Also integrated code from http://www.wede.de/sw/mod_auth_radius/
  248         which had forked form this one after v1.3.3.
  249 
  250   1.5.1 Quick release, for bug found by f.garosi@usl7.toscana.it.
  251 
  252   1.5.0 Don't stat() proxy requests.
  253 
  254   1.3.3 Another minor bug fix and configuration hints for Apache 1.3.x
  255         Thanks to Hiroshi MIZOGUCHI <mizoguti@screen.co.jp>.
  256 
  257   1.3.2 Fixed a bug which sometimes caused a SEGV in debugging mode.
  258         Thanks to Tomi Leppikangas <tomilepp@ousrvr2.oulu.fi> for
  259         pointing it out.
  260 
  261   1.3.1 (minor) Added more error output on failed response
  262 
  263   1.3.0 Fixed for Apache 1.3.0
  264 
  265   1.2.5 Corrected typo in sscanf
  266 
  267   1.2.4 Added support for debugging, so people can see what's going
  268         on during the authentication process.  Define DEBUG_RADIUS
  269         in the code below to enable debugging.
  270 
  271   1.2.3 Corrected some problems with normal username/password
  272         authentication and re-loads.
  273 
  274   1.2.2: Cleaned up usage of IP addresses
  275          return failure on unknown RADIUS response code.
  276 
  277   1.2.1: Finalized challenge/response & tested it
  278 
  279   1.2 : Cookies are expired on authentication failure.
  280         Add to r->err_headers_out, NOT r->headers_out.
  281 
  282   1.1 : Bug fixes ("forever" is one month, not 12 minutes)
  283         Added proper error outputs
  284 
  285   1.0 : Initial version.
  286 
  287  */
  288 
  289 #include "httpd.h"
  290 #include "http_config.h"
  291 #include "http_core.h"
  292 #include "http_log.h"
  293 #include "http_protocol.h"
  294 #include "util_md5.h"
  295 #include "ap_compat.h"
  296 
  297 module radius_auth_module;
  298 
  299 /* define DEBUG_RADIUS for lots of status messages */
  300 #define DEBUG_RADIUS
  301 #ifdef DEBUG_RADIUS
  302 #define DPRINTF printf
  303 #else
  304 #define DPRINTF
  305 #endif DEBUG_RADIUS
  306 
  307 /*
  308   RFC 2138 says that this port number is wrong, but everyone's using it.
  309   Use " AddRadiusAuth server:port secret " to change the port manually.
  310   */
  311 #define RADIUS_AUTH_UDP_PORT         1645
  312 
  313 #define RADIUS_PASSWORD_LEN      16
  314 #define RADIUS_RANDOM_VECTOR_LEN     16
  315 
  316 /* Per-attribute structure */
  317 typedef struct attribute_t {
  318   unsigned char attribute;
  319   unsigned char length;
  320   unsigned char data[1];
  321 } attribute_t;
  322 
  323 /* Packet header structure */
  324 typedef struct radius_packet_t {
  325   unsigned char code;
  326   unsigned char id;
  327   unsigned short length;
  328   unsigned char vector[RADIUS_RANDOM_VECTOR_LEN];
  329   attribute_t first;
  330 } radius_packet_t;
  331 
  332 #define RADIUS_HEADER_LEN             20
  333 
  334 /* RADIUS ID definitions. See RFC 2138 */
  335 #define RADIUS_ACCESS_REQUEST 1
  336 #define RADIUS_ACCESS_ACCEPT  2
  337 #define RADIUS_ACCESS_REJECT  3
  338 #define RADIUS_ACCESS_CHALLENGE 11
  339 
  340 /* RADIUS attribute definitions. Also from RFC 2138 */
  341 #define RADIUS_USER_NAME          1
  342 #define RADIUS_PASSWORD           2
  343 #define RADIUS_NAS_IP_ADDRESS         4
  344 #define RADIUS_SERVICE_TYPE           6
  345 #define RADIUS_REPLY_MESSAGE          18
  346 #define RADIUS_STATE              24
  347 #define RADIUS_SESSION_TIMEOUT        27
  348 #define RADIUS_NAS_IDENTIFIER         32
  349 
  350 /* service types : authenticate only for now */
  351 #define RADIUS_AUTHENTICATE_ONLY      8
  352 
  353 /* How large the packets may be */
  354 #define RADIUS_PACKET_RECV_SIZE       1024
  355 #define RADIUS_PACKET_SEND_SIZE       1024
  356 #define APACHE_RADIUS_MAGIC_STATE     "f36809ad"
  357 
  358 #ifndef FALSE
  359 #define FALSE 0
  360 #endif
  361 
  362 #ifndef TRUE
  363 #define TRUE !FALSE
  364 #endif
  365 
  366 /* per-server configuration structure */
  367 typedef struct radius_server_config_struct {
  368   struct in_addr *radius_ip;    /* server IP address */
  369   unsigned char *secret;    /* server shared secret */
  370   int secret_len;       /* length of the secret (to save time later) */
  371   int timeout;          /* cookie valid time */
  372   int wait;         /* wait for RADIUS server responses */
  373   int retries;          /*  number of retries on timeout */
  374   unsigned short port;      /* RADIUS port number */
  375   unsigned long bind_address;   /* bind socket to this local address */
  376   struct radius_server_config_struct *next; /* fallback server(s) */
  377 } radius_server_config_rec;
  378 
  379 /* per-server configuration create */
  380 static void *
  381 create_radius_server_config(pool *p, server_rec *s)
  382 {
  383   radius_server_config_rec *scr = (radius_server_config_rec *)
  384     palloc(p, sizeof(radius_server_config_rec) );
  385 
  386   scr->radius_ip = NULL;    /* no server yet */
  387   scr->port = RADIUS_AUTH_UDP_PORT; /* set the default port */
  388   scr->secret = NULL;       /* no secret yet */
  389   scr->secret_len = 0;
  390   scr->wait = 5;        /* wait 5 sec before giving up on the packet */
  391   scr->retries = 0;     /* no additional retries */
  392   scr->timeout = 60;        /* valid for one hour by default */
  393   scr->bind_address = INADDR_ANY;
  394   scr->next = NULL;
  395   return scr;
  396 }
  397 
  398 /* RADIUS utility functions */
  399 static struct in_addr *
  400 get_ip_addr(pool *p, char *hostname)
  401 {
  402   struct hostent *hp;
  403 
  404   if ((hp = gethostbyname(hostname)) != (struct hostent *) NULL) {
  405     struct in_addr *ipaddr = palloc(p, sizeof(struct in_addr));
  406     *ipaddr = *(struct in_addr *) hp->h_addr; /* make a local copy */
  407     return ipaddr;
  408   } else {
  409     return NULL;
  410   }
  411 }
  412 
  413 /* get a random vector */
  414 static void
  415 get_random_vector(unsigned char vector[RADIUS_RANDOM_VECTOR_LEN])
  416 {
  417   struct timeval tv;
  418   struct timezone tz;
  419   static unsigned int session = 1; /* make the random number harder to guess */
  420   AP_MD5_CTX my_md5;
  421   
  422   /* Use the time of day with the best resolution the system can
  423      give us -- often close to microsecond accuracy. */
  424   gettimeofday(&tv,&tz);
  425 
  426   tv.tv_sec ^= getpid() * session++; /* add some secret information: session */
  427 
  428   /* Hash things to get some cryptographically strong pseudo-random numbers */
  429   MD5Init(&my_md5);
  430   MD5Update(&my_md5, (unsigned char *) &tv, sizeof(tv));
  431   MD5Update(&my_md5, (unsigned char *) &tz, sizeof(tz));
  432   MD5Final(vector, &my_md5);          /* set the final vector */
  433 }
  434 
  435 /* Per-dir configuration structure */
  436 typedef struct radius_dir_config_struct {
  437   radius_server_config_rec* server;
  438   int active;                   /* Are we doing RADIUS in this dir? */
  439   int authoritative;        /* is RADIUS authentication authoritative? */
  440   int timeout;          /* cookie time valid */
  441 } radius_dir_config_rec;
  442 
  443 /* Per-dir configuration create */
  444 static void *
  445 create_radius_dir_config (pool *p, char *d)
  446 {
  447   radius_dir_config_rec *rec =
  448     (radius_dir_config_rec *) pcalloc (p, sizeof(radius_dir_config_rec));
  449 
  450   rec->server = NULL;       /* no valid server by default */
  451   rec->active = 1;              /* active by default */  
  452   rec->authoritative = 1;   /* authoritative by default */
  453   rec->timeout = 0;     /* let the server config decide timeouts */
  454   return rec;
  455 }
  456 
  457 /* per-server set configuration */
  458 static const char *
  459 add_auth_radius(cmd_parms *cmd, void *mconfig,
  460         char *server, char *secret, char *wait)
  461 {
  462   radius_server_config_rec *scr;
  463   unsigned int port;
  464   char *p;
  465 
  466   scr = get_module_config(cmd->server->module_config, &radius_auth_module);
  467 
  468   /* allocate and look up the RADIUS server's IP address */
  469   scr->radius_ip = palloc(cmd->pool, sizeof(struct in_addr));
  470 
  471   /* Check to see if there's a port in the server name */
  472   if ((p = strchr(server, ':')) != NULL) {
  473     *(p++) = 0;         /* hammer a zero in it */
  474     port = atoi(p);
  475     if (port < 1024) {
  476       return "AddRadiusAuth: server port number must be 1024 or greater for security reasons";
  477     }
  478     scr->port = (unsigned short) port;
  479   }
  480 
  481   if ((scr->radius_ip = get_ip_addr(cmd->pool, server)) == NULL) {
  482     return "AddRadiusAuth: Failed looking up RADIUS server IP address";
  483   }
  484 
  485   scr->secret = pstrdup(cmd->pool, secret);
  486   scr->secret_len = strlen(scr->secret);
  487   if (wait != NULL) {
  488     if ((p = strchr(wait,':')) != NULL) {
  489       *(p++) = 0;   /* null terminate the wait part of the string */
  490       scr->retries = atoi(p);
  491     } 
  492     scr->wait = atoi(wait);
  493   } /* else it's already initialized */
  494   scr->bind_address = INADDR_ANY;
  495 
  496   return NULL;          /* everything's OK */
  497 }
  498 
  499 /*
  500  *  Set the local address to which this client is bound.
  501  */
  502 static const char *
  503 set_bind_address (cmd_parms *cmd, void *mconfig, char *arg)
  504 {
  505   radius_server_config_rec *scr;
  506   struct in_addr *a;
  507 
  508   scr = get_module_config(cmd->server->module_config,
  509                                 &radius_auth_module);
  510   if ((a = get_ip_addr(cmd->pool, arg)) == NULL)
  511       return "AuthRadiusBindAddress: invalid IP address";
  512   scr->bind_address = a->s_addr;
  513   return NULL;
  514 }
  515 
  516 /*
  517  *  Set the cookie valid time.
  518  */
  519 static const char *
  520 set_cookie_valid(cmd_parms *cmd, void *mconfig, char *arg)
  521 {
  522   radius_server_config_rec *scr;
  523 
  524   scr = get_module_config(cmd->server->module_config,
  525               &radius_auth_module);
  526   scr->timeout = atoi(arg);
  527   return NULL;          /* everything's OK */
  528 }
  529 
  530 static const char *
  531 set_int_slot (cmd_parms *cmd, char *struct_ptr, char *arg)
  532 {
  533   int offset = (int)cmd->info; 
  534   *(int *)(struct_ptr + offset) = atoi(arg);
  535   return NULL;
  536 }
  537 
  538 /* Table of which command does what */
  539 static command_rec auth_cmds[] = {
  540   { "AddRadiusAuth", add_auth_radius, NULL, RSRC_CONF, TAKE23,
  541     "per-server configuration for RADIUS server name:port,  shared secret, and optional timeout:retries" },
  542   
  543   { "AuthRadiusBindAddress", set_bind_address, NULL, RSRC_CONF, TAKE1,
  544     "per-server binding local socket to this local IP address.  RADIUS requests will be sent *from* this IP address." },
  545 
  546   { "AddRadiusCookieValid", set_cookie_valid, NULL, RSRC_CONF, TAKE1,
  547     "per-server time in minutes for which the returned cookie is valid.  After this time, authentication will be requested again.  Use '0' for forever." }, 
  548 
  549   { "AuthRadiusAuthoritative", set_flag_slot,
  550     (void*)XtOffsetOf(radius_dir_config_rec, authoritative), 
  551     OR_AUTHCFG, FLAG, 
  552     "per-directory access on failed authentication.  If set to 'no', then access control is passed along to lower modules on failed authentication." },
  553 
  554   { "AuthRadiusCookieValid", set_int_slot, 
  555     (void*)XtOffsetOf(radius_dir_config_rec, timeout), 
  556     OR_AUTHCFG, TAKE1,
  557     "per-directory time in minutes for which the returned cookie is valid.  After this time, authentication will be requested again.  Use '0' for forever." }, 
  558   
  559   { "AuthRadiusActive", set_flag_slot,
  560     (void*)XtOffsetOf(radius_dir_config_rec, active),
  561     OR_AUTHCFG, FLAG,
  562     "per-directory toggle the use of RADIUS authentication." },
  563 
  564   { NULL }
  565 };
  566 
  567 static unsigned char *
  568 xor(unsigned char *p, unsigned char *q, int length)
  569 {
  570   int i;
  571   unsigned char *response = p;
  572   
  573   for (i = 0; i < length; i++)
  574     *(p++) ^= *(q++);
  575   return response;
  576 }
  577 
  578 static int
  579 verify_packet(request_rec *r, radius_packet_t *packet,
  580           unsigned char vector[RADIUS_RANDOM_VECTOR_LEN])
  581 {
  582   server_rec *s = r->server; 
  583   radius_server_config_rec *scr = (radius_server_config_rec *)
  584     get_module_config (s->module_config, &radius_auth_module);
  585   AP_MD5_CTX my_md5;
  586   unsigned char calculated[RADIUS_RANDOM_VECTOR_LEN];
  587   unsigned char reply[RADIUS_RANDOM_VECTOR_LEN];
  588   
  589   /*
  590    * We could dispense with the memcpy, and do MD5's of the packet
  591    * + vector piece by piece.  This is easier understand, and probably faster.
  592    */
  593   memcpy(reply, packet->vector, RADIUS_RANDOM_VECTOR_LEN); /* save the reply */
  594   memcpy(packet->vector, vector, RADIUS_RANDOM_VECTOR_LEN); /* sent vector */
  595    
  596   /* MD5(packet header + vector + packet data + secret) */
  597   MD5Init(&my_md5);
  598   MD5Update(&my_md5, (unsigned char *) packet, ntohs(packet->length));
  599   MD5Update(&my_md5, scr->secret, scr->secret_len);
  600   MD5Final(calculated, &my_md5);      /* set the final vector */
  601 
  602   /* Did he use the same random vector + shared secret? */
  603   if(memcmp(calculated, reply, RADIUS_RANDOM_VECTOR_LEN) != 0) {
  604     return -1;
  605   }
  606   return 0;
  607 }
  608 static void
  609 add_attribute(radius_packet_t *packet, int type, const unsigned char *data, int length)
  610 {
  611   attribute_t *p;
  612 
  613   p = (attribute_t *) ((unsigned char *)packet + packet->length);
  614   p->attribute = type;
  615   p->length = length + 2;       /* the total size of the attribute */
  616   packet->length += p->length;
  617   memcpy(p->data, data, length);
  618 }
  619 
  620 #define COOKIE_SIZE 1024
  621 /* make a cookie based on secret + public information */
  622 static char *
  623 make_cookie(request_rec *r, time_t expires, const char *passwd, const char *string)
  624 {
  625   char one[COOKIE_SIZE], two[COOKIE_SIZE];
  626   char *cookie = palloc(r->pool, COOKIE_SIZE);
  627   conn_rec *c = r->connection;
  628   server_rec *s = r->server;
  629   radius_server_config_rec *scr = (radius_server_config_rec *)
  630     get_module_config (s->module_config, &radius_auth_module);
  631   const char *hostname;
  632   
  633   if ((hostname = get_remote_host(c, r->per_dir_config, REMOTE_NAME)) == NULL)
  634     hostname = "no.one@example.com";
  635 
  636   /*
  637    * Arg! We can't use 'ntohs(c->remote_addr.sin_port)', because I.E.
  638    * ignores keepalives, and opens a new connection on EVERY request!
  639    * This is a BAD security problem!  It allows multiple users on the
  640    * same machine to access the data.
  641    *
  642    * A derivative security problem is users authenticating from
  643    * behind a firewall.
  644    * All users appear to be coming from the firewall.  A malicious
  645    * agent working in the same company as the authorized user can sniff
  646    * the cookie, and and use it themselves.  Since they appear to be
  647    * coming from the same IP address (firewall), they're let in.
  648    * Oh well, at least the connection is traceable to a particular machine.
  649    */
  650 
  651   /*
  652    *  Piotr Klaban <makler@oryl.man.torun.pl> says:
  653    *
  654    *  > The "squid" proxy set HTTP_X_FORWARDED_FOR variable - the
  655    *  > original IP of the client.  We can use HTTP_X_FORWARDED_FOR
  656    *  > variable besides REMOTE_ADDR.
  657    *
  658    *  > If cookie is stolen, then atacker could use the same proxy as
  659    *  > the client, to validate the cookie. If we would use
  660    *  > HTTP_X_FORWARDED_FOR, then useing the proxy would not be
  661    *  > sufficient.
  662    *
  663    *  We don't do this, mainly because I haven't gotten around to
  664    *  writing the code...
  665    */
  666 
  667   /*
  668    * Make a cookie based on secret + public information.
  669    *
  670    * cookie = MAC(M) = MD5(secret, MD5(secret, M))
  671    *
  672    * See Scheier, B, "Applied Cryptography" 2nd Ed., p.458
  673    * Also, RFC 2104.  I don't know if the HMAC gives any additional
  674    * benefit here.
  675    */  
  676   ap_snprintf(one, COOKIE_SIZE, "%s%s%s%s%s%08x", scr->secret,
  677           c->user, passwd, c->remote_ip, hostname, expires);
  678 
  679   /* if you're REALLY worried about what's going on */
  680 #if 0
  681   DPRINTF("secret     = %s\n", scr->secret);
  682   DPRINTF("user       = %s\n", c->user);
  683   DPRINTF("passwd     = %s\n", passwd);
  684   DPRINTF("remote ip  = %s\n", c->remote_ip);
  685   DPRINTF("hostname   = %s\n", hostname);
  686   DPRINTF("expiry     = %08x\n", expires);
  687 #endif
  688 
  689   /* MD5 the cookie to make it secure, and add more secret information */
  690   ap_snprintf(two, COOKIE_SIZE, "%s%s", scr->secret, ap_md5(r->pool, one));
  691   if (string == NULL) {
  692     ap_snprintf(cookie, COOKIE_SIZE, "%s%08x",
  693         ap_md5(r->pool, two), expires);
  694   } else {
  695     ap_snprintf(cookie, COOKIE_SIZE, "%s%08x%s",
  696         ap_md5(r->pool, two), expires, string);
  697   }
  698   return cookie;
  699 }
  700 static int
  701 valid_cookie(request_rec *r, const char *cookie, const char *passwd)
  702 {
  703   time_t expires, now;
  704 
  705   if (strlen(cookie) < (16 + 4)*2) { /* MD5 is 16 bytes, and expiry date is 4*/
  706     return FALSE;       /* invalid */
  707   }
  708     
  709   sscanf(&cookie[32], "%8lx", &expires);
  710 
  711   now = time(NULL);
  712   if (expires < now) {  /* valid only for a short window of time */
  713     return FALSE;       /* invalid: expired */
  714   }
  715 
  716   /* Is the returned cookie identical to one made from our secret? */
  717   if (strcmp(cookie, make_cookie(r, expires, passwd, NULL)) == 0)
  718     return TRUE;
  719   
  720   return FALSE;         /* cookie doesn't match: re-validate */
  721 }
  722 /* Add a cookie to an outgoing request */
  723 static const char *cookie_name = "RADIUS";
  724 
  725 static void
  726 add_cookie(request_rec *r, table *header, char *cookie, time_t expires)
  727 {
  728   char *new_cookie = palloc(r->pool, COOKIE_SIZE); /* so it'll stick around */
  729 
  730   if (expires != 0) {
  731     ap_snprintf(new_cookie, 1024, "%s=%s; path=/;", cookie_name, cookie);
  732   } else {
  733     ap_snprintf(new_cookie, 1024,
  734         "%s=%s; path=/; expires=Wed, 01-Oct-97 01:01:01 GMT;",
  735         cookie_name, cookie);
  736   }
  737     
  738   table_set(header,"Set-Cookie", new_cookie);
  739 }
  740 /* Spot a cookie in an incoming request */
  741 static char *
  742 spot_cookie(request_rec *r)
  743 {
  744   const char *cookie;
  745   char *value;
  746 
  747   if ((cookie = table_get(r->headers_in, "Cookie"))) {
  748     if ((value=strstr(cookie, cookie_name))) {
  749       char *cookiebuf, *cookieend;
  750       
  751       value += strlen(cookie_name); /* skip the name */
  752 
  753       /*
  754        *  Ensure there's an '=' after the name.
  755        */
  756       if (*value != '=') {
  757     return NULL;
  758       } else {
  759     value++;
  760       }
  761       
  762       cookiebuf = pstrdup( r->pool, value );
  763       cookieend = strchr(cookiebuf,';');
  764       if (cookieend) *cookieend = '\0'; /* Ignore anything after a ; */
  765       
  766       /* Set the cookie in a note, for logging */
  767       return cookiebuf;          /* Theres already a cookie, no new one */
  768     }
  769   }
  770   return NULL;                        /* no cookie was found */
  771 }
  772 
  773 /* There's a lot of parameters to this function, but it does a lot of work */
  774 static int
  775 radius_authenticate(request_rec *r, radius_server_config_rec *scr, 
  776             int sockfd, int code, char *recv_buffer,
  777             const char *user, const char *passwd_in, const char *state, 
  778             unsigned char *vector, char *errstr)
  779 {
  780   struct sockaddr_in *sin;
  781   struct sockaddr saremote;
  782   int salen, total_length;
  783   fd_set set;
  784   int retries = scr->retries;
  785   struct timeval tv;
  786   int rcode;
  787   struct in_addr *ip_addr;
  788   
  789   unsigned char misc[RADIUS_RANDOM_VECTOR_LEN];
  790   int password_len, i;
  791   unsigned char password[128];
  792   AP_MD5_CTX md5_secret, my_md5;
  793   UINT4 service;
  794 
  795   unsigned char send_buffer[RADIUS_PACKET_SEND_SIZE];
  796   radius_packet_t *packet = (radius_packet_t *) send_buffer;
  797 
  798   i = strlen(passwd_in);
  799   password_len = (i + 0x0f) & 0xfffffff0; /* round off to 16 */
  800   if (password_len == 0) {
  801     password_len = 16;      /* it's at least 15 bytes long */
  802   } else if (password_len > 128) { /* password too long, from RFC2138, p.22 */
  803     ap_snprintf(errstr, MAX_STRING_LEN, "password given by user %s is too long for RADIUS", user);
  804     return FALSE;
  805   }
  806   
  807   memset(password, 0, password_len);
  808   memcpy(password, passwd_in, i); /* don't use strcpy! */
  809   
  810   /* ************************************************************ */
  811   /* generate a random authentication vector */
  812   get_random_vector(vector);
  813 
  814   /* ************************************************************ */
  815   /* Fill in the packet header */
  816   memset(send_buffer, 0, sizeof(send_buffer));
  817 
  818   packet->code = code;
  819   packet->id = vector[0];   /* make a random request id */
  820   packet->length = RADIUS_HEADER_LEN;
  821   memcpy(packet->vector, vector, RADIUS_RANDOM_VECTOR_LEN);
  822 
  823   /* Fill in the user name attribute */
  824   add_attribute(packet, RADIUS_USER_NAME, user, strlen(user));
  825 
  826   /* ************************************************************ */
  827   /* encrypt the password */
  828   /* password : e[0] = p[0] ^ MD5(secret + vector) */
  829   MD5Init(&md5_secret);
  830   MD5Update(&md5_secret, scr->secret, scr->secret_len);
  831   my_md5 = md5_secret;      /* so we won't re-do the hash later */
  832   MD5Update(&my_md5, vector, RADIUS_RANDOM_VECTOR_LEN);
  833   MD5Final(misc, &my_md5);      /* set the final vector */
  834   xor(password, misc, RADIUS_PASSWORD_LEN);
  835 
  836   /* For each step through, e[i] = p[i] ^ MD5(secret + e[i-1]) */
  837   for (i = 1; i < (password_len >> 4); i++) {
  838     my_md5 = md5_secret;    /* grab old value of the hash */
  839     MD5Update(&my_md5, &password[(i-1) * RADIUS_PASSWORD_LEN], RADIUS_PASSWORD_LEN);
  840     MD5Final(misc, &my_md5);      /* set the final vector */
  841     xor(&password[i * RADIUS_PASSWORD_LEN], misc, RADIUS_PASSWORD_LEN);
  842   }
  843   add_attribute(packet, RADIUS_PASSWORD, password, password_len);
  844 
  845   /* ************************************************************ */
  846   /* Tell the RADIUS server that we only want to authenticate */
  847   service = htonl(RADIUS_AUTHENTICATE_ONLY);
  848   add_attribute(packet, RADIUS_SERVICE_TYPE, (unsigned char *) &service,
  849         sizeof(service));
  850   
  851   /* ************************************************************ */
  852   /* Tell the RADIUS server which virtual server we're coming from */
  853   add_attribute(packet, RADIUS_NAS_IDENTIFIER, r->server->server_hostname,
  854         strlen(r->server->server_hostname));
  855 
  856   /* ************************************************************ */
  857   /* Tell the RADIUS server which IP address we're coming from */
  858   if (scr->radius_ip->s_addr == htonl(0x7f000001)) {
  859     ip_addr = scr->radius_ip; /* go to localhost through localhost */
  860   } else {
  861     ip_addr = get_ip_addr(r->pool, r->connection->base_server->server_hostname);
  862     if (ip_addr == NULL) {
  863       ap_snprintf(errstr, MAX_STRING_LEN, "cannot look up server hostname %s",
  864           r->connection->base_server->server_hostname);
  865       return FALSE;
  866     }
  867   }
  868 
  869   add_attribute(packet, RADIUS_NAS_IP_ADDRESS, (unsigned char *)&ip_addr->s_addr,
  870         sizeof(ip_addr->s_addr));
  871   
  872   
  873   /* ************************************************************ */
  874   /* add state, if requested */
  875   if (state != NULL) {
  876     add_attribute(packet, RADIUS_STATE, state, strlen(state));
  877   }
  878 
  879   /* ************************************************************ */
  880   /* Now that we're done building the packet, we can send it */
  881   total_length = packet->length;
  882   packet->length = htons(packet->length);
  883   
  884   sin = (struct sockaddr_in *) &saremote;
  885   memset ((char *) sin, '\0', sizeof(saremote));
  886   sin->sin_family = AF_INET;
  887   sin->sin_addr.s_addr = scr->radius_ip->s_addr;
  888   sin->sin_port = htons(scr->port);
  889 
  890   while (retries >= 0) {
  891     if (sendto(sockfd, (char *) packet, total_length, 0,
  892            &saremote, sizeof(struct sockaddr_in)) < 0) {
  893       ap_snprintf(errstr, MAX_STRING_LEN, "error sending RADIUS packet for user %s: %s", user, strerror(errno));
  894       return FALSE;
  895     }
  896 
  897   wait_again:
  898     /* ************************************************************ */
  899     /* Wait for the response, and verify it. */
  900     salen = sizeof (saremote);
  901     tv.tv_sec = scr->wait;  /* wait for the specified time */
  902     tv.tv_usec = 0;
  903     FD_ZERO(&set);      /* clear out the set */
  904     FD_SET(sockfd, &set);   /* wait only for the RADIUS UDP socket */
  905     
  906     rcode = select(sockfd + 1, &set, NULL, NULL, &tv);
  907     if ((rcode < 0) && (errno == EINTR)) {
  908       goto wait_again;      /* signal, ignore it */
  909     }
  910 
  911     if (rcode == 0) {       /* done the select, with no data ready */
  912       retries--;
  913     } else {
  914       break;            /* exit from the 'while retries' loop */
  915     }
  916   } /* loop over the retries */
  917 
  918   /*
  919    *  Error.  Die.
  920    */
  921   if (rcode < 0) {
  922     ap_snprintf(errstr, MAX_STRING_LEN, "error waiting for RADIUS response: %s", strerror(errno));
  923     return FALSE;
  924   }
  925   
  926   /*
  927    *  Time out.
  928    */
  929   if (rcode == 0) {
  930     ap_snprintf(errstr, MAX_STRING_LEN, "RADIUS server %s failed to respond within %d seconds after each of %d retries",
  931         inet_ntoa(*scr->radius_ip), scr->wait, scr->retries);
  932     return FALSE;
  933   }
  934 
  935   if ((total_length = recvfrom(sockfd, (char *) recv_buffer,
  936                    RADIUS_PACKET_RECV_SIZE,
  937                    0, &saremote, &salen)) < 0) {
  938     ap_snprintf(errstr, MAX_STRING_LEN, "error reading RADIUS packet: %s", strerror(errno));
  939     return FALSE;
  940   } else {
  941 
  942     packet = (radius_packet_t *) recv_buffer; /* we have a new packet */
  943     if ((ntohs(packet->length) > total_length) ||
  944          (ntohs(packet->length) > RADIUS_PACKET_RECV_SIZE)) {
  945     ap_snprintf(errstr, MAX_STRING_LEN, "RADIUS packet corrupted");
  946     return FALSE;
  947     }
  948   }
  949   
  950   /* Check if we've got everything OK.  We should also check packet->id...*/
  951   if (verify_packet(r, packet, vector)) {
  952     ap_snprintf(errstr, MAX_STRING_LEN, "RADIUS packet fails verification");
  953     return FALSE;
  954   }
  955   
  956   return TRUE;
  957 }
  958 
  959 /* Find a particular attribute.  All we really care about is STATE */
  960 static attribute_t *
  961 find_attribute(radius_packet_t *packet, unsigned char type)
  962 {
  963   attribute_t *attr = &packet->first;
  964   int len = ntohs(packet->length) - RADIUS_HEADER_LEN;
  965 
  966   while (attr->attribute != type) {
  967     if ((len -= attr->length) <= 0) {
  968       return NULL;      /* not found */
  969     }
  970     attr = (attribute_t *) ((char *) attr + attr->length);
  971   }
  972   return attr;
  973 }
  974 #define radcpy(STRING, ATTR) {memcpy(STRING, ATTR->data, ATTR->length - 2); \
  975                               (STRING)[ATTR->length - 2] = 0;}
  976 
  977 
  978 /* authentication module utility functions */
  979 static int
  980 check_pw(request_rec *r, radius_server_config_rec *scr, const char *user, const char *passwd_in, const char *state, char *message, char *errstr)
  981 {
  982   struct sockaddr_in *sin;
  983   struct sockaddr salocal;
  984   int sockfd;
  985   unsigned short local_port;
  986 
  987   unsigned char vector[RADIUS_RANDOM_VECTOR_LEN];
  988   unsigned char recv_buffer[RADIUS_PACKET_RECV_SIZE];
  989   radius_packet_t *packet;
  990 
  991   int rcode;
  992 
  993   /* ************************************************************ */
  994   /* connect to a port */
  995   if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
  996     ap_snprintf(errstr, MAX_STRING_LEN, "error opening RADIUS socket for user %s: %s", user, strerror(errno));
  997     return FALSE;
  998   }
  999 
 1000   sin = (struct sockaddr_in *) &salocal;
 1001   memset((char *) sin, '\0', sizeof(salocal));
 1002   sin->sin_family = AF_INET;
 1003   sin->sin_addr.s_addr = scr->bind_address;
 1004   
 1005   local_port = 1025;
 1006   do {
 1007     local_port++;
 1008     sin->sin_port = htons((unsigned short) local_port);
 1009   } while((bind(sockfd, &salocal, sizeof(struct sockaddr_in)) < 0) &&
 1010       (local_port < 64000));
 1011   if(local_port >= 64000) {
 1012     close(sockfd);
 1013     ap_snprintf(errstr, MAX_STRING_LEN, "cannot bind to RADIUS socket for user %s", user);
 1014     return FALSE;
 1015   }
 1016 
 1017   rcode = radius_authenticate(r, scr, sockfd, RADIUS_ACCESS_REQUEST, recv_buffer, user, passwd_in, state, vector, errstr);
 1018 
 1019   close(sockfd);        /* we're done with it */
 1020 
 1021   if (rcode == FALSE) {
 1022     return FALSE;       /* error out */
 1023   }
 1024   
 1025   packet = (radius_packet_t *) recv_buffer;
 1026 
 1027   switch (packet->code)
 1028     {
 1029       
 1030     case RADIUS_ACCESS_ACCEPT:
 1031       {
 1032     attribute_t *a_timeout;
 1033     int i;
 1034 
 1035     a_timeout = find_attribute(packet, RADIUS_SESSION_TIMEOUT);
 1036     if (a_timeout) {
 1037       memcpy(&i, a_timeout->data, 4);
 1038       i = ntohl(i);
 1039     }
 1040       }
 1041       *message = 0;     /* no message */
 1042       return TRUE;      /* he likes you! */
 1043       break;
 1044       
 1045     case RADIUS_ACCESS_REJECT:
 1046       ap_snprintf(errstr, MAX_STRING_LEN, "RADIUS authentication failed for user %s", user);
 1047       break;
 1048       
 1049     case RADIUS_ACCESS_CHALLENGE:
 1050       {
 1051     attribute_t *a_state, *a_reply;
 1052     time_t expires = time(NULL) + 120; /* state expires in two minutes */
 1053     char server_state[256];
 1054 
 1055     if (((a_state = find_attribute(packet, RADIUS_STATE)) == NULL) ||
 1056         ((a_reply = find_attribute(packet, RADIUS_REPLY_MESSAGE)) == NULL)) {
 1057       ap_snprintf(errstr, MAX_STRING_LEN, "RADIUS access-challenge received with State or Reply-Message missing");
 1058     } else {
 1059       char *p;
 1060 
 1061       /* Copy magic state message to the state */
 1062       strcpy(server_state, APACHE_RADIUS_MAGIC_STATE);
 1063       radcpy(server_state + sizeof(APACHE_RADIUS_MAGIC_STATE) - 1,
 1064          a_state);
 1065 
 1066       /* Copy the Reply-Message back to the caller : do CR/LF smashing */
 1067       radcpy(message, a_reply);
 1068 
 1069       p = message;      /* strip any control characters */
 1070       while (*p) {
 1071         if (*p < ' ')
 1072           *p = ' ';
 1073         p++;
 1074       }
 1075       
 1076       /* set the magic cookie */
 1077       add_cookie(r, r->err_headers_out,
 1078              make_cookie(r, expires, "", server_state), expires);
 1079 
 1080       /* log the challenge, as it IS an error returned to the user */
 1081       ap_snprintf(errstr, MAX_STRING_LEN,
 1082               "RADIUS server requested challenge for user %s", user);
 1083 
 1084     }
 1085       }
 1086       break;
 1087       
 1088     default:            /* don't know what else to do */
 1089       ap_snprintf(errstr, MAX_STRING_LEN,
 1090           "RADIUS server returned unknown response %02x",
 1091           packet->code);
 1092       break;
 1093     }
 1094   
 1095   return FALSE;         /* default to failing authentication */
 1096 }
 1097 
 1098 void
 1099 note_challenge_auth_failure(request_rec *r, char *user, char *message)
 1100 {
 1101     if (!*message) {        /* no message to print */
 1102       note_basic_auth_failure(r);
 1103     } else {            /* print our magic message */
 1104       table_set (r->err_headers_out, "WWW-Authenticate",
 1105          pstrcat(r->pool, "Basic realm=\"", auth_name(r), " for ", user, " '", message, "'", NULL));
 1106     }
 1107 }
 1108 /* These functions return 0 if client is OK, and proper error status
 1109  * if not... either AUTH_REQUIRED, if we made a check, and it failed, or
 1110  * SERVER_ERROR, if things are so totally confused that we couldn't
 1111  * figure out how to tell if the client is authorized or not.
 1112  *
 1113  * If they return DECLINED, and all other modules also decline, that's
 1114  * treated by the server core as a configuration error, logged and
 1115  * reported as such.
 1116  */
 1117 
 1118 /* Determine user ID, and check if it really is that user, for HTTP
 1119  * basic authentication...
 1120  */
 1121 
 1122 static int
 1123 authenticate_basic_user(request_rec *r)
 1124 {
 1125   radius_dir_config_rec *rec =
 1126     (radius_dir_config_rec *)get_module_config (r->per_dir_config, &radius_auth_module);
 1127   server_rec *s = r->server; 
 1128   radius_server_config_rec *scr = (radius_server_config_rec *)
 1129     get_module_config (s->module_config, &radius_auth_module);
 1130   conn_rec *c = r->connection;
 1131   const char *sent_pw;
 1132   char errstr[MAX_STRING_LEN];
 1133   int res, min;
 1134   char *cookie;
 1135   char *state = NULL;
 1136   char message[256];
 1137   time_t expires;
 1138   struct stat buf;
 1139   
 1140   if (!rec->active || !scr->radius_ip)  /*  not active here, or no radius */
 1141     return DECLINED;                    /*  server declared, decline      */
 1142   
 1143   if ((res = get_basic_auth_pw(r, &sent_pw)))
 1144     return res;
 1145 
 1146   if (c->user[0] == 0)      /* NUL users can never be let in */
 1147     return HTTP_UNAUTHORIZED;
 1148 
 1149   message[0] = 0;       /* no message for now */
 1150 
 1151   DPRINTF("###### %s requests %s : file=%s ######\n",
 1152       r->server->server_hostname, r->uri, r->filename);
 1153 
 1154   /* check for the existence of a cookie: do weak authentication if so */
 1155   if ((cookie = spot_cookie(r)) != NULL) {
 1156     DPRINTF("Found cookie=%s for user=%s : ", cookie, c->user);
 1157     /* are we in a Challenge-Response intermediate state? */
 1158     if (((state = strstr(cookie, APACHE_RADIUS_MAGIC_STATE)) != NULL) &&
 1159     ((state - cookie) == 40)) { /* it's in the right place */
 1160       DPRINTF("with RADIUS challenge state set.\n");
 1161       /*
 1162        * If there's an authentication failure, ensure we delete the state.
 1163        * If authentication succeeds, the new cookie will supersede the old.
 1164        * (RFC 2109, 4.3.3)
 1165        */
 1166       add_cookie(r, r->err_headers_out, cookie, 0);
 1167       state += sizeof(APACHE_RADIUS_MAGIC_STATE) -1; /* skip state string */
 1168 
 1169       /* valid username, passwd, and expiry date: don't do RADIUS */
 1170     } else if (valid_cookie(r, cookie, sent_pw)) {
 1171       DPRINTF("still valid.  Serving page.\n");
 1172       return OK;
 1173     } else {            /* the cookie has probably expired */
 1174       /* don't bother logging the fact: we probably don't care */
 1175       add_cookie(r, r->err_headers_out, cookie, 0);
 1176       note_challenge_auth_failure(r, c->user, message);
 1177       DPRINTF("invalid or expired. telling browser to delete cookie\n");
 1178       return AUTH_REQUIRED;
 1179     }
 1180   } else {
 1181     DPRINTF("No cookie found.  Trying RADIUS authentication.\n");
 1182   }
 1183 
 1184   /*
 1185    *  This is for one-time passwords, so we don't get too badly out of sync .
 1186    *  Also, don't bother doing the stat for requests we're proxying.
 1187    */
 1188   if ((strstr(r->filename, "proxy:") != r->filename) &&
 1189       (stat(r->filename, &buf) < 0)) {
 1190     return HTTP_NOT_FOUND; /* can't stat it, so we can't authenticate it */
 1191   }
 1192 
 1193   /* Check the password, and fill in the error string if an error happens */
 1194   if (!(check_pw(r, scr, c->user, sent_pw, state, message, errstr))) {
 1195     DPRINTF("RADIUS authentication for user=%s password=%s failed\n",
 1196         c->user, sent_pw);
 1197     if (!(rec->authoritative)) {
 1198       DPRINTF("We're not authoritative.  Never mind.\n");
 1199       return DECLINED;      /* never mind */
 1200     }
 1201     log_reason (errstr, r->uri, r);
 1202     note_challenge_auth_failure(r, c->user, message);
 1203     DPRINTF("Sending failure message to user=%s : '%s'\n", c->user, message);
 1204     return AUTH_REQUIRED;
 1205   }
 1206 
 1207   min = scr->timeout;       /* the server config is authoritative */
 1208   if (scr->timeout == 0) {  /* except that zero means forever */
 1209     min = 24*30*60;     /* expire in one month (that's forever!) */
 1210   }
 1211 
 1212   if ((rec->timeout != 0) && /* if we don't let the server choose */
 1213       (rec->timeout < min)) { /* and we're more restrictive than the server */
 1214     min = rec->timeout;     /* use the directory config */
 1215   }
 1216 
 1217   expires = time(NULL) + (min * 60);
 1218   cookie = make_cookie(r, expires, sent_pw, NULL);
 1219 
 1220   DPRINTF("RADIUS Authentication for user=%s password=%s OK.  Cookie expiry in %d minutes\n",
 1221       c->user, sent_pw, min);
 1222   DPRINTF("Adding cookie %s\n", cookie);
 1223 
 1224   add_cookie(r, r->headers_out, cookie, expires);
 1225   return OK;
 1226 }
 1227     
 1228 module radius_auth_module = {
 1229    STANDARD_MODULE_STUFF,
 1230    NULL,            /* initializer */
 1231    create_radius_dir_config,    /* dir config creater */
 1232    NULL,            /* dir merger --- default is to override */
 1233    create_radius_server_config, /* server config */
 1234    NULL,            /* merge server config */
 1235    auth_cmds,           /* command table */
 1236    NULL,            /* handlers */
 1237    NULL,            /* filename translation */
 1238    authenticate_basic_user, /* check_user_id */
 1239    NULL,            /* check auth */
 1240    NULL,            /* check access */
 1241    NULL,            /* type_checker */
 1242    NULL,            /* fixups */
 1243    NULL,            /* logger */
 1244    NULL             /* header parser */
 1245 };