"Fossies" - the Fresh Open Source Software Archive

Member "mod_auth_radius-1.5.7/mod_auth_radius-2.0.c" (24 Mar 2003, 45123 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-2.0.c,v 1.5 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          Port to Apache 2.0 by Harrie Hazewinkel <harrie@mod-snmp.com>
  238 
  239   1.5.3 Bug fix from Bryan Stansell <bryan@stansell.org>, to set
  240         the right data element for the AddRadiusCookieValid configuration
  241     item.
  242 
  243   1.5.2 Updates for NAS-Identifier and NAS-IP-Address, based on ideas
  244         from Adrian Hosey <ahosey@systhug.com>.  The NAS-Identifier is
  245     the virtual server host name, and the NAS-IP-Address is the
  246     IP address of the base server.
  247 
  248     Also integrated code from http://www.wede.de/sw/mod_auth_radius/
  249         which had forked form this one after v1.3.3.
  250 
  251   1.5.1 Quick release, for bug found by f.garosi@usl7.toscana.it.
  252 
  253   1.5.0 Don't stat() proxy requests.
  254 
  255   1.3.3 Another minor bug fix and configuration hints for Apache 1.3.x
  256         Thanks to Hiroshi MIZOGUCHI <mizoguti@screen.co.jp>.
  257 
  258   1.3.2 Fixed a bug which sometimes caused a SEGV in debugging mode.
  259         Thanks to Tomi Leppikangas <tomilepp@ousrvr2.oulu.fi> for
  260         pointing it out.
  261 
  262   1.3.1 (minor) Added more error output on failed response
  263 
  264   1.3.0 Fixed for Apache 1.3.0
  265 
  266   1.2.5 Corrected typo in sscanf
  267 
  268   1.2.4 Added support for debugging, so people can see what's going
  269         on during the authentication process.  Define DEBUG_RADIUS
  270         in the code below to enable debugging.
  271 
  272   1.2.3 Corrected some problems with normal username/password
  273         authentication and re-loads.
  274 
  275   1.2.2: Cleaned up usage of IP addresses
  276          return failure on unknown RADIUS response code.
  277 
  278   1.2.1: Finalized challenge/response & tested it
  279 
  280   1.2 : Cookies are expired on authentication failure.
  281         Add to r->err_headers_out, NOT r->headers_out.
  282 
  283   1.1 : Bug fixes ("forever" is one month, not 12 minutes)
  284         Added proper error outputs
  285 
  286   1.0 : Initial version.
  287 
  288  */
  289 
  290 #include <netdb.h>
  291 #include <openssl/md5.h>
  292 #include <sys/stat.h>
  293 
  294 #include "httpd.h"
  295 #include "http_config.h"
  296 #include "http_core.h"
  297 #include "http_log.h"
  298 #include "http_protocol.h"
  299 #include "util_md5.h"
  300 #include "apr_general.h"
  301 #include "apr_tables.h"
  302 #include "apr_strings.h"
  303 
  304 module AP_MODULE_DECLARE_DATA radius_auth_module;
  305 
  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(apr_pool_t *p, server_rec *s)
  382 {
  383   radius_server_config_rec *scr = (radius_server_config_rec *) apr_pcalloc(p, sizeof(radius_server_config_rec) );
  384 
  385   scr->radius_ip = NULL;    /* no server yet */
  386   scr->port = RADIUS_AUTH_UDP_PORT; /* set the default port */
  387   scr->secret = NULL;       /* no secret yet */
  388   scr->secret_len = 0;
  389   scr->wait = 5;        /* wait 5 sec before giving up on the packet */
  390   scr->retries = 0;     /* no additional retries */
  391   scr->timeout = 60;        /* valid for one hour by default */
  392   scr->bind_address = INADDR_ANY;
  393   scr->next = NULL;
  394 
  395   return scr;
  396 }
  397 
  398 /* RADIUS utility functions */
  399 static struct in_addr *
  400 get_ip_addr(apr_pool_t *p, const char *hostname)
  401 {
  402   struct hostent *hp;
  403 
  404   if ((hp = gethostbyname(hostname)) != NULL) {
  405     struct in_addr *ipaddr = apr_pcalloc(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   apr_md5_ctx_t 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   apr_md5_init(&my_md5);
  430   apr_md5_update(&my_md5, (unsigned char *) &tv, sizeof(tv));
  431   apr_md5_update(&my_md5, (unsigned char *) &tz, sizeof(tz));
  432   apr_md5_final(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 (apr_pool_t *p, char *d)
  446 {
  447   
  448   radius_dir_config_rec *rec =
  449     (radius_dir_config_rec *) apr_pcalloc (p, sizeof(radius_dir_config_rec));
  450 
  451   rec->server = NULL;       /* no valid server by default */
  452   rec->active = 1;              /* active by default */  
  453   rec->authoritative = 1;   /* authoritative by default */
  454   rec->timeout = 0;     /* let the server config decide timeouts */
  455 
  456   return rec;
  457 }
  458 
  459 /* per-server set configuration */
  460 static const char *
  461 add_auth_radius(cmd_parms *cmd, void *mconfig,
  462         const char *server, const char *secret, const char *wait)
  463 {
  464   radius_server_config_rec *scr;
  465   unsigned int port;
  466   char *p;
  467 
  468   scr = ap_get_module_config(cmd->server->module_config, &radius_auth_module);
  469 
  470   /* allocate and look up the RADIUS server's IP address */
  471 
  472   scr->radius_ip = (struct in_addr *)apr_pcalloc(cmd->pool, sizeof(struct in_addr));
  473 
  474   /* Check to see if there's a port in the server name */
  475   if ((p = strchr(server, ':')) != NULL) {
  476     *(p++) = 0;         /* hammer a zero in it */
  477     port = atoi(p);
  478     if (port < 1024) {
  479       return "AddRadiusAuth: server port number must be 1024 or greater for security reasons";
  480     }
  481     scr->port = (unsigned short) port;
  482   }
  483 
  484   if ((scr->radius_ip = get_ip_addr(cmd->pool, server)) == NULL) {
  485     return "AddRadiusAuth: Failed looking up RADIUS server IP address";
  486   }
  487 
  488   scr->secret = apr_pstrdup(cmd->pool, secret);
  489   scr->secret_len = strlen(scr->secret);
  490   if (wait != NULL) {
  491     if ((p = strchr(wait,':')) != NULL) {
  492       *(p++) = 0;   /* null terminate the wait part of the string */
  493       scr->retries = atoi(p);
  494     } 
  495     scr->wait = atoi(wait);
  496   } /* else it's already initialized */
  497   scr->bind_address = INADDR_ANY;
  498 
  499   return NULL;          /* everything's OK */
  500 }
  501 
  502 /*
  503  *  Set the local address to which this client is bound.
  504  */
  505 static const char *
  506 set_bind_address (cmd_parms *cmd, void *mconfig, const char *arg)
  507 {
  508   radius_server_config_rec *scr;
  509   struct in_addr *a;
  510 
  511   scr = ap_get_module_config(cmd->server->module_config,
  512                                 &radius_auth_module);
  513   if ((a = get_ip_addr(cmd->pool, arg)) == NULL)
  514       return "AuthRadiusBindAddress: invalid IP address";
  515   scr->bind_address = a->s_addr;
  516   return NULL;
  517 }
  518 
  519 /*
  520  *  Set the cookie valid time.
  521  */
  522 static const char *
  523 set_cookie_valid(cmd_parms *cmd, void *mconfig, const char *arg)
  524 {
  525   radius_server_config_rec *scr;
  526 
  527   scr = ap_get_module_config(cmd->server->module_config,
  528               &radius_auth_module);
  529   scr->timeout = atoi(arg);
  530   return NULL;          /* everything's OK */
  531 }
  532 
  533 static const char *
  534 set_int_slot(cmd_parms *cmd, char *struct_ptr, const char *arg)
  535 {
  536   int offset = (int)cmd->info; 
  537   *(int *)(struct_ptr + offset) = atoi(arg);
  538   return NULL;
  539 }
  540 
  541 
  542 /* Table of which command does what */
  543 static command_rec auth_cmds[] = {
  544   AP_INIT_TAKE23("AddRadiusAuth", add_auth_radius,
  545     NULL, RSRC_CONF,
  546     "per-server configuration for RADIUS server name:port, shared secret, and optional timeout:retries"),
  547 
  548   AP_INIT_TAKE1("AuthRadiusBindAddress", set_bind_address,
  549     NULL, RSRC_CONF,
  550     "per-server binding local socket to this local IP address. RADIUS requests will be sent *from* this IP address."),
  551 
  552   AP_INIT_TAKE1("AddRadiusCookieValid", set_cookie_valid,
  553     NULL, RSRC_CONF, 
  554     "per-server time in minutes for which the returned cookie is valid. After this time, authentication will be requested again. Use '0' for forever."), 
  555 
  556   AP_INIT_FLAG("AuthRadiusAuthoritative", ap_set_flag_slot,
  557     (void*)APR_OFFSETOF(radius_dir_config_rec, authoritative), OR_AUTHCFG,
  558    "per-directory access on failed authentication. If set to 'no', then access control is passed along to lower modules on failed authentication."),
  559 
  560   AP_INIT_TAKE1("AuthRadiusCookieValid", set_int_slot,
  561     NULL,OR_AUTHCFG,
  562     "per-directory time in minutes for which the returned cookie is valid. After this time, authentication will be requested again .Use 0 for forever."),
  563 
  564   AP_INIT_FLAG("AuthRadiusActive", ap_set_flag_slot,
  565     (void*)APR_OFFSETOF(radius_dir_config_rec, active), OR_AUTHCFG,
  566     "per-directory toggle the use of RADIUS authentication."),
  567   { NULL }
  568 };
  569 
  570 static unsigned char *
  571 xor(unsigned char *p, unsigned char *q, int length)
  572 {
  573   int i;
  574   unsigned char *response = p;
  575   
  576   for (i = 0; i < length; i++)
  577     *(p++) ^= *(q++);
  578   return response;
  579 }
  580 
  581 static int
  582 verify_packet(request_rec *r, radius_packet_t *packet,
  583           unsigned char vector[RADIUS_RANDOM_VECTOR_LEN])
  584 {
  585   server_rec *s = r->server; 
  586   radius_server_config_rec *scr = (radius_server_config_rec *)
  587     ap_get_module_config (s->module_config, &radius_auth_module);
  588   apr_md5_ctx_t my_md5;
  589   unsigned char calculated[RADIUS_RANDOM_VECTOR_LEN];
  590   unsigned char reply[RADIUS_RANDOM_VECTOR_LEN];
  591   
  592   /*
  593    * We could dispense with the memcpy, and do MD5's of the packet
  594    * + vector piece by piece.  This is easier understand, and probably faster.
  595    */
  596   memcpy(reply, packet->vector, RADIUS_RANDOM_VECTOR_LEN); /* save the reply */
  597   memcpy(packet->vector, vector, RADIUS_RANDOM_VECTOR_LEN); /* sent vector */
  598    
  599   /* MD5(packet header + vector + packet data + secret) */
  600   apr_md5_init(&my_md5);
  601   apr_md5_update(&my_md5, (unsigned char *) packet, ntohs(packet->length));
  602   apr_md5_update(&my_md5, scr->secret, scr->secret_len);
  603   apr_md5_final(calculated, &my_md5);      /* set the final vector */
  604 
  605   /* Did he use the same random vector + shared secret? */
  606   if(memcmp(calculated, reply, RADIUS_RANDOM_VECTOR_LEN) != 0) {
  607     return -1;
  608   }
  609   return 0;
  610 }
  611 static void
  612 add_attribute(radius_packet_t *packet, int type, const unsigned char *data, int length)
  613 {
  614   attribute_t *p;
  615 
  616   p = (attribute_t *) ((unsigned char *)packet + packet->length);
  617   p->attribute = type;
  618   p->length = length + 2;       /* the total size of the attribute */
  619   packet->length += p->length;
  620   memcpy(p->data, data, length);
  621 }
  622 
  623 #define COOKIE_SIZE 1024
  624 /* make a cookie based on secret + public information */
  625 static char *
  626 make_cookie(request_rec *r, time_t expires, const char *passwd, const char *string)
  627 {
  628   char one[COOKIE_SIZE], two[COOKIE_SIZE];
  629   char *cookie = apr_pcalloc(r->pool, COOKIE_SIZE);
  630   conn_rec *c = r->connection;
  631   server_rec *s = r->server;
  632   radius_server_config_rec *scr = (radius_server_config_rec *)
  633     ap_get_module_config (s->module_config, &radius_auth_module);
  634   const char *hostname;
  635   
  636   if ((hostname = ap_get_remote_host(c, r->per_dir_config, REMOTE_NAME, NULL)) == NULL)
  637     hostname = "no.one@example.com";
  638 
  639   /*
  640    * Arg! We can't use 'ntohs(c->remote_addr.sin_port)', because I.E.
  641    * ignores keepalives, and opens a new connection on EVERY request!
  642    * This is a BAD security problem!  It allows multiple users on the
  643    * same machine to access the data.
  644    *
  645    * A derivative security problem is users authenticating from
  646    * behind a firewall.
  647    * All users appear to be coming from the firewall.  A malicious
  648    * agent working in the same company as the authorized user can sniff
  649    * the cookie, and and use it themselves.  Since they appear to be
  650    * coming from the same IP address (firewall), they're let in.
  651    * Oh well, at least the connection is traceable to a particular machine.
  652    */
  653 
  654   /*
  655    *  Piotr Klaban <makler@oryl.man.torun.pl> says:
  656    *
  657    *  > The "squid" proxy set HTTP_X_FORWARDED_FOR variable - the
  658    *  > original IP of the client.  We can use HTTP_X_FORWARDED_FOR
  659    *  > variable besides REMOTE_ADDR.
  660    *
  661    *  > If cookie is stolen, then atacker could use the same proxy as
  662    *  > the client, to validate the cookie. If we would use
  663    *  > HTTP_X_FORWARDED_FOR, then useing the proxy would not be
  664    *  > sufficient.
  665    *
  666    *  We don't do this, mainly because I haven't gotten around to
  667    *  writing the code...
  668    */
  669 
  670   /*
  671    * Make a cookie based on secret + public information.
  672    *
  673    * cookie = MAC(M) = apr_md5(secret, MD5(secret, M))
  674    *
  675    * See Scheier, B, "Applied Cryptography" 2nd Ed., p.458
  676    * Also, RFC 2104.  I don't know if the HMAC gives any additional
  677    * benefit here.
  678    */  
  679   apr_snprintf(one, COOKIE_SIZE, "%s%s%s%s%s%08x", scr->secret,
  680           r->user, passwd, c->remote_ip, hostname, expires);
  681 
  682   /* if you're REALLY worried about what's going on */
  683 
  684 #if 0
  685   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," secret     = %s\n", scr->secret);
  686   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," user       = %s\n", r->user);
  687   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," passwd     = %s\n", passwd);
  688   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," remote ip  = %s\n", c->remote_ip);
  689   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," hostname   = %s\n", hostname);
  690   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," expiry     = %08x\n", expires);
  691 #endif
  692 
  693   /* MD5 the cookie to make it secure, and add more secret information */
  694   apr_snprintf(two, COOKIE_SIZE, "%s%s", scr->secret, ap_md5(r->pool, one));
  695   if (string == NULL) {
  696     apr_snprintf(cookie, COOKIE_SIZE, "%s%08x",
  697         ap_md5(r->pool, two), expires);
  698   } else {
  699     apr_snprintf(cookie, COOKIE_SIZE, "%s%08x%s",
  700         ap_md5(r->pool, two), expires, string);
  701   }
  702   return cookie;
  703 }
  704 static int
  705 valid_cookie(request_rec *r, const char *cookie, const char *passwd)
  706 {
  707   time_t expires, now;
  708 
  709   if (strlen(cookie) < (16 + 4)*2) { /* MD5 is 16 bytes, and expiry date is 4*/
  710     return FALSE;       /* invalid */
  711   }
  712     
  713   sscanf(&cookie[32], "%8lx", &expires);
  714 
  715   now = time(NULL);
  716   if (expires < now) {  /* valid only for a short window of time */
  717     return FALSE;       /* invalid: expired */
  718   }
  719 
  720   /* Is the returned cookie identical to one made from our secret? */
  721   if (strcmp(cookie, make_cookie(r, expires, passwd, NULL)) == 0)
  722     return TRUE;
  723   
  724   return FALSE;         /* cookie doesn't match: re-validate */
  725 }
  726 /* Add a cookie to an outgoing request */
  727 static const char *cookie_name = "RADIUS";
  728 
  729 static void
  730 add_cookie(request_rec *r, apr_table_t *header, char *cookie, time_t expires)
  731 {
  732   char *new_cookie = apr_pcalloc(r->pool, COOKIE_SIZE); /* so it'll stick around */
  733 
  734   if (expires != 0) {
  735     apr_snprintf(new_cookie, 1024, "%s=%s; path=/;", cookie_name, cookie);
  736   } else {
  737     apr_snprintf(new_cookie, 1024,
  738         "%s=%s; path=/; expires=Wed, 01-Oct-97 01:01:01 GMT;",
  739         cookie_name, cookie);
  740   }
  741     
  742   apr_table_set(header,"Set-Cookie", new_cookie);
  743 }
  744 /* Spot a cookie in an incoming request */
  745 static char *
  746 spot_cookie(request_rec *r)
  747 {
  748   const char *cookie;
  749   char *value;
  750 
  751   if ((cookie = apr_table_get(r->headers_in, "Cookie"))) {
  752     if ((value=strstr(cookie, cookie_name))) {
  753       char *cookiebuf, *cookieend;
  754       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,r->server,"Found Radius Cookie, now check if it's valid...");
  755       value += strlen(cookie_name); /* skip the name */
  756 
  757       /*
  758        *  Ensure there's an '=' after the name.
  759        */
  760       if (*value != '=') {
  761     return NULL;
  762       } else {
  763     value++;
  764       }
  765       
  766       cookiebuf = apr_pstrdup( r->pool, value );
  767       cookieend = strchr(cookiebuf,';');
  768       if (cookieend) *cookieend = '\0'; /* Ignore anything after a ; */
  769       
  770       /* Set the cookie in a note, for logging */
  771       return cookiebuf;          /* Theres already a cookie, no new one */
  772     }
  773   }
  774   return NULL;                        /* no cookie was found */
  775 }
  776 
  777 /* There's a lot of parameters to this function, but it does a lot of work */
  778 static int
  779 radius_authenticate(request_rec *r, radius_server_config_rec *scr, 
  780             int sockfd, int code, char *recv_buffer,
  781             const char *user, const char *passwd_in, const char *state, 
  782             unsigned char *vector, char *errstr)
  783 {
  784   struct sockaddr_in *sin;
  785   struct sockaddr saremote;
  786   int salen, total_length;
  787   fd_set set;
  788   int retries = scr->retries;
  789   struct timeval tv;
  790   int rcode;
  791   struct in_addr *ip_addr;
  792   
  793   unsigned char misc[RADIUS_RANDOM_VECTOR_LEN];
  794   int password_len, i;
  795   unsigned char password[128];
  796   apr_md5_ctx_t md5_secret, my_md5;
  797   uint32_t service;
  798 
  799   unsigned char send_buffer[RADIUS_PACKET_SEND_SIZE];
  800   radius_packet_t *packet = (radius_packet_t *) send_buffer;
  801 
  802   i = strlen(passwd_in);
  803   password_len = (i + 0x0f) & 0xfffffff0; /* round off to 16 */
  804   if (password_len == 0) {
  805     password_len = 16;      /* it's at least 15 bytes long */
  806   } else if (password_len > 128) { /* password too long, from RFC2138, p.22 */
  807     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,r->server,"password given by user %s is too long for RADIUS", user);
  808     return FALSE;
  809   }
  810   
  811   memset(password, 0, password_len);
  812   memcpy(password, passwd_in, i); /* don't use strcpy! */
  813   
  814   /* ************************************************************ */
  815   /* generate a random authentication vector */
  816   get_random_vector(vector);
  817 
  818   /* ************************************************************ */
  819   /* Fill in the packet header */
  820   memset(send_buffer, 0, sizeof(send_buffer));
  821 
  822   packet->code = code;
  823   packet->id = vector[0];   /* make a random request id */
  824   packet->length = RADIUS_HEADER_LEN;
  825   memcpy(packet->vector, vector, RADIUS_RANDOM_VECTOR_LEN);
  826 
  827   /* Fill in the user name attribute */
  828   add_attribute(packet, RADIUS_USER_NAME, user, strlen(user));
  829 
  830   /* ************************************************************ */
  831   /* encrypt the password */
  832   /* password : e[0] = p[0] ^ MD5(secret + vector) */
  833   apr_md5_init(&md5_secret);
  834   apr_md5_update(&md5_secret, scr->secret, scr->secret_len);
  835   my_md5 = md5_secret;      /* so we won't re-do the hash later */
  836   apr_md5_update(&my_md5, vector, RADIUS_RANDOM_VECTOR_LEN);
  837   apr_md5_final(misc, &my_md5);      /* set the final vector */
  838   xor(password, misc, RADIUS_PASSWORD_LEN);
  839 
  840   /* For each step through, e[i] = p[i] ^ MD5(secret + e[i-1]) */
  841   for (i = 1; i < (password_len >> 4); i++) {
  842     my_md5 = md5_secret;    /* grab old value of the hash */
  843     apr_md5_update(&my_md5, &password[(i-1) * RADIUS_PASSWORD_LEN], RADIUS_PASSWORD_LEN);
  844     apr_md5_final(misc, &my_md5);      /* set the final vector */
  845     xor(&password[i * RADIUS_PASSWORD_LEN], misc, RADIUS_PASSWORD_LEN);
  846   }
  847   add_attribute(packet, RADIUS_PASSWORD, password, password_len);
  848 
  849   /* ************************************************************ */
  850   /* Tell the RADIUS server that we only want to authenticate */
  851   service = htonl(RADIUS_AUTHENTICATE_ONLY);
  852   add_attribute(packet, RADIUS_SERVICE_TYPE, (unsigned char *) &service,
  853         sizeof(service));
  854   
  855   /* ************************************************************ */
  856   /* Tell the RADIUS server which virtual server we're coming from */
  857   add_attribute(packet, RADIUS_NAS_IDENTIFIER, r->server->server_hostname,
  858         strlen(r->server->server_hostname));
  859 
  860   /* ************************************************************ */
  861   /* Tell the RADIUS server which IP address we're coming from */
  862   if (scr->radius_ip->s_addr == htonl(0x7f000001)) {
  863     ip_addr = scr->radius_ip; /* go to localhost through localhost */
  864   } else {
  865     ip_addr = get_ip_addr(r->pool, r->connection->base_server->server_hostname);
  866     if (ip_addr == NULL) {
  867       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,r->server, "cannot look up server hostname %s",
  868           r->connection->base_server->server_hostname);
  869       return FALSE;
  870     }
  871   }
  872 
  873   add_attribute(packet, RADIUS_NAS_IP_ADDRESS, (unsigned char *)&ip_addr->s_addr,
  874         sizeof(ip_addr->s_addr));
  875   
  876   
  877   /* ************************************************************ */
  878   /* add state, if requested */
  879   if (state != NULL) {
  880     add_attribute(packet, RADIUS_STATE, state, strlen(state));
  881   }
  882 
  883   /* ************************************************************ */
  884   /* Now that we're done building the packet, we can send it */
  885   total_length = packet->length;
  886   packet->length = htons(packet->length);
  887   
  888   sin = (struct sockaddr_in *) &saremote;
  889   memset ((char *) sin, '\0', sizeof(saremote));
  890   sin->sin_family = AF_INET;
  891   sin->sin_addr.s_addr = scr->radius_ip->s_addr;
  892   sin->sin_port = htons(scr->port);
  893 
  894   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Sending packet on %s:%i", inet_ntoa(*scr->radius_ip), scr->port);
  895 
  896   while (retries >= 0) {
  897     if (sendto(sockfd, (char *) packet, total_length, 0,
  898            &saremote, sizeof(struct sockaddr_in)) < 0) {
  899       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Error sending RADIUS packet for user %s: %s", user, strerror(errno));
  900       return FALSE;
  901     }
  902 
  903   wait_again:
  904     /* ************************************************************ */
  905     /* Wait for the response, and verify it. */
  906     salen = sizeof (saremote);
  907     tv.tv_sec = scr->wait;  /* wait for the specified time */
  908     tv.tv_usec = 0;
  909     FD_ZERO(&set);      /* clear out the set */
  910     FD_SET(sockfd, &set);   /* wait only for the RADIUS UDP socket */
  911     
  912     rcode = select(sockfd + 1, &set, NULL, NULL, &tv);
  913     if ((rcode < 0) && (errno == EINTR)) {
  914       goto wait_again;      /* signal, ignore it */
  915     }
  916 
  917     if (rcode == 0) {       /* done the select, with no data ready */
  918       retries--;
  919     } else {
  920       break;            /* exit from the 'while retries' loop */
  921     }
  922   } /* loop over the retries */
  923 
  924   /*
  925    *  Error.  Die.
  926    */
  927   if (rcode < 0) {
  928     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Error waiting for RADIUS response: %s", strerror(errno));
  929     return FALSE;
  930   }
  931   
  932   /*
  933    *  Time out.
  934    */
  935   if (rcode == 0) {
  936     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS server %s failed to respond within %d seconds after each of %d retries",
  937         inet_ntoa(*scr->radius_ip), scr->wait, scr->retries);
  938     return FALSE;
  939   }
  940 
  941   if ((total_length = recvfrom(sockfd, (char *) recv_buffer,
  942                    RADIUS_PACKET_RECV_SIZE,
  943                    0, &saremote, &salen)) < 0) {
  944     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Error reading RADIUS packet: %s", strerror(errno));
  945     return FALSE;
  946   } else {
  947 
  948     packet = (radius_packet_t *) recv_buffer; /* we have a new packet */
  949     if ((ntohs(packet->length) > total_length) ||
  950          (ntohs(packet->length) > RADIUS_PACKET_RECV_SIZE)) {
  951     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS packet corrupted");
  952     return FALSE;
  953     }
  954   }
  955   
  956   /* Check if we've got everything OK.  We should also check packet->id...*/
  957   if (verify_packet(r, packet, vector)) {
  958     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS packet fails verification");
  959     return FALSE;
  960   }
  961   
  962   return TRUE;
  963 }
  964 
  965 /* Find a particular attribute.  All we really care about is STATE */
  966 static attribute_t *
  967 find_attribute(radius_packet_t *packet, unsigned char type)
  968 {
  969   attribute_t *attr = &packet->first;
  970   int len = ntohs(packet->length) - RADIUS_HEADER_LEN;
  971 
  972   while (attr->attribute != type) {
  973     if ((len -= attr->length) <= 0) {
  974       return NULL;      /* not found */
  975     }
  976     attr = (attribute_t *) ((char *) attr + attr->length);
  977   }
  978   return attr;
  979 }
  980 #define radcpy(STRING, ATTR) {memcpy(STRING, ATTR->data, ATTR->length - 2); \
  981                               (STRING)[ATTR->length - 2] = 0;}
  982 
  983 
  984 /* authentication module utility functions */
  985 static int
  986 check_pw(request_rec *r, radius_server_config_rec *scr, const char *user, const char *passwd_in, const char *state, char *message, char *errstr)
  987 {
  988   struct sockaddr_in *sin;
  989   struct sockaddr salocal;
  990   int sockfd;
  991   unsigned short local_port;
  992 
  993   unsigned char vector[RADIUS_RANDOM_VECTOR_LEN];
  994   unsigned char recv_buffer[RADIUS_PACKET_RECV_SIZE];
  995   radius_packet_t *packet;
  996 
  997   int rcode;
  998 
  999   /* ************************************************************ */
 1000   /* connect to a port */
 1001   if ((sockfd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) {
 1002     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,r->server, "error opening RADIUS socket for user %s: %s", user, strerror(errno));
 1003     return FALSE;
 1004   }
 1005 
 1006   sin = (struct sockaddr_in *) &salocal;
 1007   memset((char *) sin, '\0', sizeof(salocal));
 1008   sin->sin_family = AF_INET;
 1009   sin->sin_addr.s_addr = scr->bind_address;
 1010   
 1011   local_port = 1025;
 1012   do {
 1013     local_port++;
 1014     sin->sin_port = htons((unsigned short) local_port);
 1015   } while((bind(sockfd, &salocal, sizeof(struct sockaddr_in)) < 0) &&
 1016       (local_port < 64000));
 1017   if(local_port >= 64000) {
 1018     close(sockfd);
 1019     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "cannot bind to RADIUS socket for user %s", user);
 1020     return FALSE;
 1021   }
 1022 
 1023   rcode = radius_authenticate(r, scr, sockfd, RADIUS_ACCESS_REQUEST, recv_buffer, user, passwd_in, state, vector, errstr);
 1024 
 1025   close(sockfd);        /* we're done with it */
 1026 
 1027   if (rcode == FALSE) {
 1028     return FALSE;       /* error out */
 1029   }
 1030   
 1031   packet = (radius_packet_t *) recv_buffer;
 1032 
 1033   switch (packet->code)
 1034     {
 1035       
 1036     case RADIUS_ACCESS_ACCEPT:
 1037       {
 1038     attribute_t *a_timeout;
 1039     int i;
 1040 
 1041     a_timeout = find_attribute(packet, RADIUS_SESSION_TIMEOUT);
 1042     if (a_timeout) {
 1043       memcpy(&i, a_timeout->data, 4);
 1044       i = ntohl(i);
 1045     }
 1046       }
 1047       *message = 0;     /* no message */
 1048       return TRUE;      /* he likes you! */
 1049       break;
 1050       
 1051     case RADIUS_ACCESS_REJECT:
 1052       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,r->server, "RADIUS authentication failed for user %s", user);
 1053       break;
 1054       
 1055     case RADIUS_ACCESS_CHALLENGE:
 1056       {
 1057     attribute_t *a_state, *a_reply;
 1058     time_t expires = time(NULL) + 120; /* state expires in two minutes */
 1059     char server_state[256];
 1060 
 1061     if (((a_state = find_attribute(packet, RADIUS_STATE)) == NULL) ||
 1062         ((a_reply = find_attribute(packet, RADIUS_REPLY_MESSAGE)) == NULL)) {
 1063       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS access-challenge received with State or Reply-Message missing");
 1064     } else {
 1065       char *p;
 1066 
 1067       /* Copy magic state message to the state */
 1068       strcpy(server_state, APACHE_RADIUS_MAGIC_STATE);
 1069       radcpy(server_state + sizeof(APACHE_RADIUS_MAGIC_STATE) - 1,
 1070          a_state);
 1071 
 1072       /* Copy the Reply-Message back to the caller : do CR/LF smashing */
 1073       radcpy(message, a_reply);
 1074 
 1075       p = message;      /* strip any control characters */
 1076       while (*p) {
 1077         if (*p < ' ')
 1078           *p = ' ';
 1079         p++;
 1080       }
 1081       
 1082       /* set the magic cookie */
 1083       add_cookie(r, r->err_headers_out,make_cookie(r, expires, "", server_state), expires);
 1084 
 1085       /* log the challenge, as it IS an error returned to the user */
 1086       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS server requested challenge for user %s", user);
 1087 
 1088     }
 1089       }
 1090       break;
 1091       
 1092     default:            /* don't know what else to do */
 1093       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS server returned unknown response %02x",
 1094           packet->code);
 1095       break;
 1096     }
 1097   
 1098   return FALSE;         /* default to failing authentication */
 1099 }
 1100 
 1101 void
 1102 note_challenge_auth_failure(request_rec *r, char *user, char *message)
 1103 {
 1104     if (!*message) {        /* no message to print */
 1105       /* note_basic_auth_failure(r); */
 1106     } else {            /* print our magic message */
 1107       apr_table_set (r->err_headers_out, "WWW-Authenticate",
 1108          apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), " for ", user, " '", message, "'", NULL));
 1109     }
 1110 }
 1111 /* These functions return 0 if client is OK, and proper error status
 1112  * if not... either HTTP_UNAUTHORIZED, if we made a check, and it failed, or
 1113  * SERVER_ERROR, if things are so totally confused that we couldn't
 1114  * figure out how to tell if the client is authorized or not.
 1115  *
 1116  * If they return DECLINED, and all other modules also decline, that's
 1117  * treated by the server core as a configuration error, logged and
 1118  * reported as such.
 1119  */
 1120 
 1121 /* Determine user ID, and check if it really is that user, for HTTP
 1122  * basic authentication...
 1123  */
 1124 
 1125 static int
 1126 authenticate_basic_user(request_rec *r)
 1127 {
 1128   radius_dir_config_rec *rec =
 1129     (radius_dir_config_rec *)ap_get_module_config (r->per_dir_config, &radius_auth_module);
 1130   server_rec *s = r->server; 
 1131   radius_server_config_rec *scr = (radius_server_config_rec *)
 1132     ap_get_module_config (s->module_config, &radius_auth_module);
 1133   conn_rec *c = r->connection;
 1134   const char *sent_pw;
 1135   char errstr[MAX_STRING_LEN];
 1136   int res, min;
 1137   char *cookie;
 1138   char *state = NULL;
 1139   char message[256];
 1140   time_t expires;
 1141   struct stat buf;
 1142   
 1143   if (!rec->active || !scr->radius_ip)  /*  not active here, or no radius */
 1144     return DECLINED;                    /*  server declared, decline      */
 1145   
 1146   if ((res = ap_get_basic_auth_pw(r, &sent_pw)))
 1147     return res;
 1148 
 1149   if (r->user[0] == 0)      /* NUL users can never be let in */
 1150     return HTTP_UNAUTHORIZED;
 1151 
 1152   message[0] = 0;       /* no message for now */
 1153 
 1154   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Radius Auth for: %s requests %s : file=%s",
 1155       r->server->server_hostname, r->uri, r->filename);
 1156 
 1157   /* check for the existence of a cookie: do weak authentication if so */
 1158   if ((cookie = spot_cookie(r)) != NULL) {
 1159 
 1160     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Found cookie=%s for user=%s : ", cookie, r->user);
 1161     /* are we in a Challenge-Response intermediate state? */
 1162     if (((state = strstr(cookie, APACHE_RADIUS_MAGIC_STATE)) != NULL) &&
 1163     ((state - cookie) == 40)) { /* it's in the right place */
 1164       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "with RADIUS challenge state set.\n");
 1165       /*
 1166        * If there's an authentication failure, ensure we delete the state.
 1167        * If authentication succeeds, the new cookie will supersede the old.
 1168        * (RFC 2109, 4.3.3)
 1169        */
 1170       add_cookie(r, r->err_headers_out, cookie, 0);
 1171       state += sizeof(APACHE_RADIUS_MAGIC_STATE) -1; /* skip state string */
 1172 
 1173       /* valid username, passwd, and expiry date: don't do RADIUS */
 1174     } else if (valid_cookie(r, cookie, sent_pw)) {
 1175       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server,"still valid.  Serving page.\n");
 1176       return OK;
 1177     } else {            /* the cookie has probably expired */
 1178       /* don't bother logging the fact: we probably don't care */
 1179       add_cookie(r, r->err_headers_out, cookie, 0);
 1180       note_challenge_auth_failure(r, r->user, message);
 1181       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," invalid or expired. telling browser to delete cookie\n");
 1182       return HTTP_UNAUTHORIZED;
 1183     }
 1184   } else {
 1185     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," No cookie found.  Trying RADIUS authentication.\n");
 1186   }
 1187 
 1188   /*
 1189    *  This is for one-time passwords, so we don't get too badly out of sync .
 1190    *  Also, don't bother doing the stat for requests we're proxying.
 1191    */
 1192   if ((strstr(r->filename, "proxy:") != r->filename) &&
 1193       (stat(r->filename, &buf) < 0)) {
 1194     return HTTP_NOT_FOUND; /* can't stat it, so we can't authenticate it */
 1195   }
 1196 
 1197   /* Check the password, and fill in the error string if an error happens */
 1198   if (!(check_pw(r, scr, r->user, sent_pw, state, message, errstr))) {
 1199     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "RADIUS authentication for user=%s password=%s failed\n",
 1200         r->user, sent_pw);
 1201     if (!(rec->authoritative)) {
 1202       ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "We're not authoritative.  Never mind.\n");
 1203       return DECLINED;      /* never mind */
 1204     }
 1205     note_challenge_auth_failure(r, r->user, message);
 1206     ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server, "Sending failure message to user=%s\n", r->user);
 1207     return HTTP_UNAUTHORIZED;
 1208   }
 1209 
 1210   min = scr->timeout;       /* the server config is authoritative */
 1211   if (scr->timeout == 0) {  /* except that zero means forever */
 1212     min = 24*30*60;     /* expire in one month (that's forever!) */
 1213   }
 1214 
 1215   if ((rec->timeout != 0) && /* if we don't let the server choose */
 1216       (rec->timeout < min)) { /* and we're more restrictive than the server */
 1217     min = rec->timeout;     /* use the directory config */
 1218   }
 1219 
 1220   expires = time(NULL) + (min * 60);
 1221   cookie = make_cookie(r, expires, sent_pw, NULL);
 1222 
 1223   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," RADIUS Authentication for user=%s password=%s OK.  Cookie expiry in %d minutes\n",
 1224       r->user, sent_pw, min);
 1225   ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0, r->server," Adding cookie %s\n", cookie);
 1226   add_cookie(r, r->headers_out, cookie, expires);
 1227   return OK;
 1228 }
 1229 
 1230 static void register_hooks(apr_pool_t *p)
 1231 {
 1232     ap_hook_check_user_id(authenticate_basic_user,NULL,NULL,APR_HOOK_MIDDLE);
 1233 }
 1234 
 1235 module AP_MODULE_DECLARE_DATA radius_auth_module =
 1236 {
 1237     STANDARD20_MODULE_STUFF,
 1238     create_radius_dir_config,   /* dir config creater */
 1239     NULL,                       /* dir merger --- default is to override */
 1240     create_radius_server_config,/* server config */
 1241     NULL,                       /* merge server config */
 1242     auth_cmds,                  /* command apr_table_t */
 1243     register_hooks              /* register hooks */
 1244 };