"Fossies" - the Fresh Open Source Software Archive 
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 };