3 * Remote Authentication Dial In User Service
6 * Livingston Enterprises, Inc.
7 * 6920 Koll Center Parkway
10 * Copyright 1992 Livingston Enterprises, Inc.
12 * Permission to use, copy, modify, and distribute this software for any
13 * purpose and without fee is hereby granted, provided that this
14 * copyright and permission notice appear on all copies and supporting
15 * documentation, the name of Livingston Enterprises, Inc. not be used
16 * in advertising or publicity pertaining to distribution of the
17 * program without specific prior permission, and notice be given
18 * in supporting documentation that copying and distribution is by
19 * permission of Livingston Enterprises, Inc.
21 * Livingston Enterprises, Inc. makes no representations about
22 * the suitability of this software for any purpose. It is
23 * provided "as is" without express or implied warranty.
25 * The new parts of the code is Copyright (C) 1998 R.M. van Selm <selm@cistron.nl>
27 * Copyright (C) 2004 Henrik Nordstrom <hno@squid-cache.org>
28 * Copyright (C) 2006 Henrik Nordstrom <hno@squid-cache.org>
31 /* basic_radius_auth is a RADIUS authenticator for Squid-2.5 and later.
32 * The authenticator reads a line with a user and password combination.
33 * If access is granted OK is returned. Else ERR.
35 * basic_radius_auth-1.0 is based on modules from the Cistron-radiusd-1.5.4.
37 * Currently you should only start 1 authentificator at a time because the
38 * the ID's of the different programs can start to conflict. I'm not sure it
39 * would help anyway. I think the RADIUS server is close by and I don't think
40 * it will handle requests in parallel anyway (correct me if I'm wrong here)
42 * Marc van Selm <selm@cistron.nl>
43 * with contributions from
44 * Henrik Nordstrom <hno@squid-cache.org>
49 #include "helpers/defines.h"
52 #include "radius-util.h"
55 #include <sys/socket.h>
58 #include <netinet/in.h>
97 /* AYJ: helper input buffer may be a lot larger than this used to expect... */
102 static void md5_calc(uint8_t out
[16], void *in
, size_t len
);
104 static int i_send_buffer
[2048];
105 static int i_recv_buffer
[2048];
106 static char *send_buffer
= (char *) i_send_buffer
;
107 static char *recv_buffer
= (char *) i_recv_buffer
;
109 static u_char request_id
;
110 static char vector
[AUTH_VECTOR_LEN
];
111 static char secretkey
[MAXPASS
+ 1] = "";
112 static char server
[MAXLINE
] = "";
113 static char identifier
[MAXLINE
] = "";
114 static char svc_name
[MAXLINE
] = "radius";
115 static int nasport
= 111;
116 static int nasporttype
= 0;
117 static uint32_t nas_ipaddr
;
118 static uint32_t auth_ipaddr
;
119 static int retries
= 30;
121 char progname
[] = "basic_radius_auth";
125 Win32SockCleanup(void)
133 * Diff two timeval, b - a
136 timeval_diff(const struct timeval
*a
, const struct timeval
*b
)
138 return (b
->tv_sec
- a
->tv_sec
) * 1000000 + (b
->tv_usec
- a
->tv_usec
);
142 * Time since a timeval
145 time_since(const struct timeval
*when
)
148 gettimeofday(&now
, NULL
);
149 return timeval_diff(when
, &now
);
156 md5_calc(uint8_t out
[16], void *in
, size_t len
)
160 SquidMD5Update(&ctx
, in
, len
);
161 SquidMD5Final(out
, &ctx
);
165 * Receive and verify the result.
168 result_recv(uint32_t host
, unsigned short udp_port
, char *buffer
, int length
)
172 unsigned char reply_digest
[AUTH_VECTOR_LEN
];
173 unsigned char calc_digest
[AUTH_VECTOR_LEN
];
175 /* VALUE_PAIR *req; */
177 auth
= (AUTH_HDR
*) buffer
;
178 totallen
= ntohs(auth
->length
);
180 if (totallen
!= length
) {
181 debug("Received invalid reply length from server (want %d/ got %d)\n", totallen
, length
);
184 if (auth
->id
!= request_id
) {
185 /* Duplicate response of an earlier query, ignore */
188 /* Verify the reply digest */
189 memcpy(reply_digest
, auth
->vector
, AUTH_VECTOR_LEN
);
190 memcpy(auth
->vector
, vector
, AUTH_VECTOR_LEN
);
191 secretlen
= strlen(secretkey
);
192 memcpy(buffer
+ length
, secretkey
, secretlen
);
193 md5_calc(calc_digest
, (unsigned char *) auth
, length
+ secretlen
);
195 if (memcmp(reply_digest
, calc_digest
, AUTH_VECTOR_LEN
) != 0) {
196 debug("WARNING: Received invalid reply digest from server\n");
199 if (auth
->code
!= PW_AUTHENTICATION_ACK
)
207 * Generate a random vector.
210 random_vector(char *aVector
)
215 srand((time(0) ^ rand()) + rand());
216 for (i
= 0; i
< AUTH_VECTOR_LEN
;) {
218 memcpy(aVector
, &randno
, sizeof(int));
219 aVector
+= sizeof(int);
224 /* read the config file
225 * The format should be something like:
226 * # basic_radius_auth configuration file
228 * server suncone.cistron.nl
232 rad_auth_config(const char *cfname
)
236 int srv
= 0, crt
= 0;
238 if ((cf
= fopen(cfname
, "r")) == NULL
) {
242 while (fgets(line
, MAXLINE
, cf
) != NULL
) {
243 if (!memcmp(line
, "server", 6))
244 srv
= sscanf(line
, "server %s", server
);
245 if (!memcmp(line
, "secret", 6))
246 crt
= sscanf(line
, "secret %s", secretkey
);
247 if (!memcmp(line
, "identifier", 10))
248 sscanf(line
, "identifier %s", identifier
);
249 if (!memcmp(line
, "service", 7))
250 sscanf(line
, "service %s", svc_name
);
251 if (!memcmp(line
, "port", 4))
252 sscanf(line
, "port %s", svc_name
);
261 urldecode(char *dst
, const char *src
, int size
)
265 while (*src
&& size
> 1) {
266 if (*src
== '%' && src
[1] != '\0' && src
[2] != '\0') {
272 *dst
= strtol(tmp
, NULL
, 16);
285 authenticate(int socket_fd
, const char *username
, const char *passwd
)
288 unsigned short total_length
;
291 char passbuf
[MAXPASS
];
294 u_char cbc
[AUTH_VECTOR_LEN
];
297 struct sockaddr_in saremote
;
303 * Build an authentication request
305 auth
= (AUTH_HDR
*) send_buffer
;
306 auth
->code
= PW_AUTHENTICATION_REQUEST
;
307 auth
->id
= ++request_id
;
308 random_vector(vector
);
309 memcpy(auth
->vector
, vector
, AUTH_VECTOR_LEN
);
310 total_length
= AUTH_HDR_LEN
;
318 length
= strlen(username
);
319 if (length
> MAXPWNAM
) {
324 memcpy(ptr
, username
, length
);
326 total_length
+= length
+ 2;
331 length
= strlen(passwd
);
332 if (length
> MAXPASS
) {
335 memset(passbuf
, 0, MAXPASS
);
336 memcpy(passbuf
, passwd
, length
);
339 * Length is rounded up to multiple of 16,
340 * and the password is encoded in blocks of 16
341 * with cipher block chaining
343 length
= ((length
/ AUTH_VECTOR_LEN
) + 1) * AUTH_VECTOR_LEN
;
350 secretlen
= strlen(secretkey
);
351 /* Set up the Cipher block chain */
352 memcpy(cbc
, auth
->vector
, AUTH_VECTOR_LEN
);
353 for (j
= 0; j
< length
; j
+= AUTH_VECTOR_LEN
) {
354 /* Calculate the MD5 Digest */
355 strcpy((char *) md5buf
, secretkey
);
356 memcpy(md5buf
+ secretlen
, cbc
, AUTH_VECTOR_LEN
);
357 md5_calc(cbc
, md5buf
, secretlen
+ AUTH_VECTOR_LEN
);
359 /* Xor the password into the MD5 digest */
360 for (i
= 0; i
< AUTH_VECTOR_LEN
; ++i
) {
361 *ptr
= (cbc
[i
] ^= passbuf
[j
+ i
]);
365 total_length
+= length
+ 2;
367 *ptr
= PW_NAS_PORT_ID
;
377 *ptr
= PW_NAS_PORT_TYPE
;
382 ui
= htonl(nasporttype
);
388 int len
= strlen(identifier
);
393 memcpy(ptr
, identifier
, len
);
395 total_length
+= len
+ 2;
397 *ptr
= PW_NAS_IP_ADDRESS
;
402 ui
= htonl(nas_ipaddr
);
408 /* Klaus Weidner <kw@w-m-p.com> changed this
409 * from htonl to htons. It might have caused
410 * you trouble or not. That depends on the byte
411 * order of your system.
412 * The symptom was that the radius server
413 * ignored the requests, because they had zero
414 * length according to the data header.
416 auth
->length
= htons(total_length
);
423 * Send the request we've built.
425 gettimeofday(&sent
, NULL
);
426 send(socket_fd
, (char *) auth
, total_length
, 0);
427 while ((time_spent
= time_since(&sent
)) < 1000000) {
435 tv
.tv_usec
= 1000000 - time_spent
;
438 FD_SET(socket_fd
, &readfds
);
439 if (select(socket_fd
+ 1, &readfds
, NULL
, NULL
, &tv
) == 0) /* Select timeout */
441 salen
= sizeof(saremote
);
442 len
= recvfrom(socket_fd
, recv_buffer
, sizeof(i_recv_buffer
),
443 0, (struct sockaddr
*) &saremote
, &salen
);
448 rc
= result_recv(saremote
.sin_addr
.s_addr
, saremote
.sin_port
, recv_buffer
, len
);
456 fprintf(stderr
, "%s: No response from RADIUS server\n", progname
);
462 main(int argc
, char **argv
)
464 struct sockaddr_in salocal
;
465 struct sockaddr_in saremote
;
467 unsigned short svc_port
;
468 char username
[MAXPWNAM
];
469 char passwd
[MAXPASS
];
471 char buf
[HELPER_INPUT_BUFFER
];
472 const char *cfname
= NULL
;
477 while ((c
= getopt(argc
, argv
, "h:p:f:w:i:t:")) != -1) {
486 strcpy(server
, optarg
);
489 strcpy(svc_name
, optarg
);
492 strcpy(secretkey
, optarg
);
495 strcpy(identifier
, optarg
);
498 retries
= atoi(optarg
);
502 /* make standard output line buffered */
503 if (setvbuf(stdout
, NULL
, _IOLBF
, 0) != 0)
507 if (rad_auth_config(cfname
) < 0) {
508 fprintf(stderr
, "FATAL: %s: can't open configuration file '%s'.\n", argv
[0], cfname
);
513 fprintf(stderr
, "FATAL: %s: Server not specified\n", argv
[0]);
517 fprintf(stderr
, "FATAL: %s: Shared secret not specified\n", argv
[0]);
523 WSAStartup(2, &wsaData
);
524 atexit(Win32SockCleanup
);
528 * Open a connection to the server.
530 svp
= getservbyname(svc_name
, "udp");
532 svc_port
= ntohs((unsigned short) svp
->s_port
);
534 svc_port
= atoi(svc_name
);
536 svc_port
= PW_AUTH_UDP_PORT
;
538 /* Get the IP address of the authentication server */
539 if ((auth_ipaddr
= get_ipaddr(server
)) == 0) {
540 fprintf(stderr
, "FATAL: %s: Couldn't find host %s\n", argv
[0], server
);
543 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
548 memset(&saremote
, 0, sizeof(saremote
));
549 saremote
.sin_family
= AF_INET
;
550 saremote
.sin_addr
.s_addr
= htonl(auth_ipaddr
);
551 saremote
.sin_port
= htons(svc_port
);
553 if (connect(sockfd
, (struct sockaddr
*) &saremote
, sizeof(saremote
)) < 0) {
557 salen
= sizeof(salocal
);
558 if (getsockname(sockfd
, (struct sockaddr
*) &salocal
, &salen
) < 0) {
559 perror("getsockname");
563 fcntl(sockfd
, F_SETFL
, fcntl(sockfd
, F_GETFL
, 0) | O_NONBLOCK
);
565 nas_ipaddr
= ntohl(salocal
.sin_addr
.s_addr
);
566 while (fgets(buf
, HELPER_INPUT_BUFFER
, stdin
) != NULL
) {
568 /* protect me form to long lines */
569 if ((end
= strchr(buf
, '\n')) == NULL
) {
578 if (strlen(buf
) > HELPER_INPUT_BUFFER
) {
582 /* Strip off the trailing newline */
585 /* Parse out the username and password */
587 while (isspace(*ptr
))
589 if ((end
= strchr(ptr
, ' ')) == NULL
) {
590 SEND_ERR("No password");
594 urldecode(username
, ptr
, MAXPWNAM
);
596 while (isspace(*ptr
))
598 urldecode(passwd
, ptr
, MAXPASS
);
600 if (authenticate(sockfd
, username
, passwd
))