2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
11 * Remote Authentication Dial In User Service
14 * Livingston Enterprises, Inc.
15 * 6920 Koll Center Parkway
16 * Pleasanton, CA 94566
18 * Copyright 1992 Livingston Enterprises, Inc.
20 * Permission to use, copy, modify, and distribute this software for any
21 * purpose and without fee is hereby granted, provided that this
22 * copyright and permission notice appear on all copies and supporting
23 * documentation, the name of Livingston Enterprises, Inc. not be used
24 * in advertising or publicity pertaining to distribution of the
25 * program without specific prior permission, and notice be given
26 * in supporting documentation that copying and distribution is by
27 * permission of Livingston Enterprises, Inc.
29 * Livingston Enterprises, Inc. makes no representations about
30 * the suitability of this software for any purpose. It is
31 * provided "as is" without express or implied warranty.
33 * The new parts of the code is Copyright (C) 1998 R.M. van Selm <selm@cistron.nl>
35 * Copyright (C) 2004 Henrik Nordstrom <hno@squid-cache.org>
36 * Copyright (C) 2006 Henrik Nordstrom <hno@squid-cache.org>
39 /* basic_radius_auth is a RADIUS authenticator for Squid-2.5 and later.
40 * The authenticator reads a line with a user and password combination.
41 * If access is granted OK is returned. Else ERR.
43 * basic_radius_auth-1.0 is based on modules from the Cistron-radiusd-1.5.4.
45 * Currently you should only start 1 authentificator at a time because the
46 * the ID's of the different programs can start to conflict. I'm not sure it
47 * would help anyway. I think the RADIUS server is close by and I don't think
48 * it will handle requests in parallel anyway (correct me if I'm wrong here)
50 * Marc van Selm <selm@cistron.nl>
51 * with contributions from
52 * Henrik Nordstrom <hno@squid-cache.org>
57 #include "helpers/defines.h"
59 #include "radius-util.h"
67 #include <sys/socket.h>
70 #include <netinet/in.h>
94 /* AYJ: helper input buffer may be a lot larger than this used to expect... */
99 static void md5_calc(uint8_t out
[16], void *in
, size_t len
);
101 static int i_send_buffer
[2048];
102 static int i_recv_buffer
[2048];
103 static char *send_buffer
= (char *) i_send_buffer
;
104 static char *recv_buffer
= (char *) i_recv_buffer
;
106 static u_char request_id
;
107 static char vector
[AUTH_VECTOR_LEN
];
108 static char secretkey
[MAXPASS
+ 1] = "";
109 static char server
[MAXLINE
] = "";
110 static char identifier
[MAXLINE
] = "";
111 static char svc_name
[MAXLINE
] = "radius";
112 static int nasport
= 111;
113 static int nasporttype
= 0;
114 static uint32_t nas_ipaddr
;
115 static uint32_t auth_ipaddr
;
116 static int retries
= 10;
118 char progname
[] = "basic_radius_auth";
122 Win32SockCleanup(void)
130 * Diff two timeval, b - a
133 timeval_diff(const struct timeval
*a
, const struct timeval
*b
)
135 return (b
->tv_sec
- a
->tv_sec
) * 1000000 + (b
->tv_usec
- a
->tv_usec
);
139 * Time since a timeval
142 time_since(const struct timeval
*when
)
145 gettimeofday(&now
, NULL
);
146 return timeval_diff(when
, &now
);
153 md5_calc(uint8_t out
[16], void *in
, size_t len
)
157 SquidMD5Update(&ctx
, in
, len
);
158 SquidMD5Final(out
, &ctx
);
162 * Receive and verify the result.
165 result_recv(uint32_t host
, unsigned short udp_port
, char *buffer
, int length
)
169 unsigned char reply_digest
[AUTH_VECTOR_LEN
];
170 unsigned char calc_digest
[AUTH_VECTOR_LEN
];
172 /* VALUE_PAIR *req; */
174 auth
= (AUTH_HDR
*) buffer
;
175 totallen
= ntohs(auth
->length
);
177 if (totallen
!= length
) {
178 debug("Received invalid reply length from server (want %d/ got %d)\n", totallen
, length
);
181 if (auth
->id
!= request_id
) {
182 /* Duplicate response of an earlier query, ignore */
185 /* Verify the reply digest */
186 memcpy(reply_digest
, auth
->vector
, AUTH_VECTOR_LEN
);
187 memcpy(auth
->vector
, vector
, AUTH_VECTOR_LEN
);
188 secretlen
= strlen(secretkey
);
189 memcpy(buffer
+ length
, secretkey
, secretlen
);
190 md5_calc(calc_digest
, (unsigned char *) auth
, length
+ secretlen
);
192 if (memcmp(reply_digest
, calc_digest
, AUTH_VECTOR_LEN
) != 0) {
193 debug("WARNING: Received invalid reply digest from server\n");
196 if (auth
->code
!= PW_AUTHENTICATION_ACK
)
203 * Generate a random vector.
206 random_vector(char *aVector
)
211 srand((time(0) ^ rand()) + rand());
212 for (i
= 0; i
< AUTH_VECTOR_LEN
;) {
214 memcpy(aVector
, &randno
, sizeof(int));
215 aVector
+= sizeof(int);
220 /* read the config file
221 * The format should be something like:
222 * # basic_radius_auth configuration file
224 * server suncone.cistron.nl
228 rad_auth_config(const char *cfname
)
232 int srv
= 0, crt
= 0;
234 if ((cf
= fopen(cfname
, "r")) == NULL
) {
238 while (fgets(line
, MAXLINE
, cf
) != NULL
) {
239 if (!memcmp(line
, "server", 6))
240 srv
= sscanf(line
, "server %s", server
);
241 if (!memcmp(line
, "secret", 6))
242 crt
= sscanf(line
, "secret %s", secretkey
);
243 if (!memcmp(line
, "identifier", 10))
244 sscanf(line
, "identifier %s", identifier
);
245 if (!memcmp(line
, "service", 7))
246 sscanf(line
, "service %s", svc_name
);
247 if (!memcmp(line
, "port", 4))
248 sscanf(line
, "port %s", svc_name
);
249 if (!memcmp(line
, "timeout", 7))
250 sscanf(line
, "timeout %d", &retries
);
259 urldecode(char *dst
, const char *src
, int size
)
263 while (*src
&& size
> 1) {
264 if (*src
== '%' && src
[1] != '\0' && src
[2] != '\0') {
270 *dst
= strtol(tmp
, NULL
, 16);
283 authenticate(int socket_fd
, const char *username
, const char *passwd
)
286 unsigned short total_length
;
289 char passbuf
[MAXPASS
];
292 u_char cbc
[AUTH_VECTOR_LEN
];
295 struct sockaddr_in saremote
;
301 * Build an authentication request
303 auth
= (AUTH_HDR
*) send_buffer
;
304 auth
->code
= PW_AUTHENTICATION_REQUEST
;
305 auth
->id
= ++request_id
;
306 random_vector(vector
);
307 memcpy(auth
->vector
, vector
, AUTH_VECTOR_LEN
);
308 total_length
= AUTH_HDR_LEN
;
316 length
= strlen(username
);
317 if (length
> MAXPWNAM
) {
321 ptr
= (unsigned char*)send_buffer
+ sizeof(AUTH_HDR
);
322 memcpy(ptr
, username
, length
);
324 total_length
+= length
+ 2;
329 length
= strlen(passwd
);
330 if (length
> MAXPASS
) {
333 memset(passbuf
, 0, MAXPASS
);
334 memcpy(passbuf
, passwd
, length
);
337 * Length is rounded up to multiple of 16,
338 * and the password is encoded in blocks of 16
339 * with cipher block chaining
341 length
= ((length
/ AUTH_VECTOR_LEN
) + 1) * AUTH_VECTOR_LEN
;
348 secretlen
= strlen(secretkey
);
349 /* Set up the Cipher block chain */
350 memcpy(cbc
, auth
->vector
, AUTH_VECTOR_LEN
);
351 for (j
= 0; j
< length
; j
+= AUTH_VECTOR_LEN
) {
352 /* Calculate the MD5 Digest */
353 strcpy((char *) md5buf
, secretkey
);
354 memcpy(md5buf
+ secretlen
, cbc
, AUTH_VECTOR_LEN
);
355 md5_calc(cbc
, md5buf
, secretlen
+ AUTH_VECTOR_LEN
);
357 /* Xor the password into the MD5 digest */
358 for (i
= 0; i
< AUTH_VECTOR_LEN
; ++i
) {
359 *ptr
= (cbc
[i
] ^= passbuf
[j
+ i
]);
363 total_length
+= length
+ 2;
365 *ptr
= PW_NAS_PORT_ID
;
375 *ptr
= PW_NAS_PORT_TYPE
;
380 ui
= htonl(nasporttype
);
386 int len
= strlen(identifier
);
391 memcpy(ptr
, identifier
, len
);
393 total_length
+= len
+ 2;
395 *ptr
= PW_NAS_IP_ADDRESS
;
400 ui
= htonl(nas_ipaddr
);
406 /* Klaus Weidner <kw@w-m-p.com> changed this
407 * from htonl to htons. It might have caused
408 * you trouble or not. That depends on the byte
409 * order of your system.
410 * The symptom was that the radius server
411 * ignored the requests, because they had zero
412 * length according to the data header.
414 auth
->length
= htons(total_length
);
421 * Send the request we've built.
423 gettimeofday(&sent
, NULL
);
424 if (send(socket_fd
, (char *) auth
, total_length
, 0) < 0) {
425 // EAGAIN is expected at high traffic, just retry
426 // TODO: block/sleep a few ms to let the apparently full buffer drain ?
427 if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
)
428 fprintf(stderr
,"ERROR: RADIUS send() failure: %s\n", xstrerror());
431 while ((time_spent
= time_since(&sent
)) < 1000000) {
439 tv
.tv_usec
= 1000000 - time_spent
;
442 FD_SET(socket_fd
, &readfds
);
443 if (select(socket_fd
+ 1, &readfds
, NULL
, NULL
, &tv
) == 0) /* Select timeout */
445 salen
= sizeof(saremote
);
446 len
= recvfrom(socket_fd
, recv_buffer
, sizeof(i_recv_buffer
),
447 0, (struct sockaddr
*) &saremote
, &salen
);
452 rc
= result_recv(saremote
.sin_addr
.s_addr
, saremote
.sin_port
, recv_buffer
, len
);
464 fprintf(stderr
, "%s: No response from RADIUS server\n", progname
);
465 SEND_ERR("No response from RADIUS server");
470 main(int argc
, char **argv
)
472 struct sockaddr_in salocal
;
473 struct sockaddr_in saremote
;
475 unsigned short svc_port
;
476 char username
[MAXPWNAM
];
477 char passwd
[MAXPASS
];
479 char buf
[HELPER_INPUT_BUFFER
];
480 const char *cfname
= NULL
;
485 while ((c
= getopt(argc
, argv
, "h:p:f:w:i:t:")) != -1) {
494 strncpy(server
, optarg
, sizeof(server
)-1);
495 server
[sizeof(server
)-1] = '\0';
498 strncpy(svc_name
, optarg
, sizeof(svc_name
)-1);
499 svc_name
[sizeof(svc_name
)-1] = '\0';
502 strncpy(secretkey
, optarg
, sizeof(secretkey
)-1);
503 secretkey
[sizeof(secretkey
)-1] = '\0';
506 strncpy(identifier
, optarg
, sizeof(identifier
)-1);
507 identifier
[sizeof(identifier
)-1] = '\0';
510 retries
= atoi(optarg
);
514 /* make standard output line buffered */
515 if (setvbuf(stdout
, NULL
, _IOLBF
, 0) != 0)
519 if (rad_auth_config(cfname
) < 0) {
520 fprintf(stderr
, "FATAL: %s: can't open configuration file '%s'.\n", argv
[0], cfname
);
525 fprintf(stderr
, "FATAL: %s: Server not specified\n", argv
[0]);
529 fprintf(stderr
, "FATAL: %s: Shared secret not specified\n", argv
[0]);
535 WSAStartup(2, &wsaData
);
536 atexit(Win32SockCleanup
);
540 * Open a connection to the server.
542 svp
= getservbyname(svc_name
, "udp");
544 svc_port
= ntohs((unsigned short) svp
->s_port
);
546 svc_port
= atoi(svc_name
);
548 svc_port
= PW_AUTH_UDP_PORT
;
550 /* Get the IP address of the authentication server */
551 if ((auth_ipaddr
= get_ipaddr(server
)) == 0) {
552 fprintf(stderr
, "FATAL: %s: Couldn't find host %s\n", argv
[0], server
);
555 sockfd
= socket(AF_INET
, SOCK_DGRAM
, 0);
560 memset(&saremote
, 0, sizeof(saremote
));
561 saremote
.sin_family
= AF_INET
;
562 saremote
.sin_addr
.s_addr
= htonl(auth_ipaddr
);
563 saremote
.sin_port
= htons(svc_port
);
565 if (connect(sockfd
, (struct sockaddr
*) &saremote
, sizeof(saremote
)) < 0) {
569 salen
= sizeof(salocal
);
570 if (getsockname(sockfd
, (struct sockaddr
*) &salocal
, &salen
) < 0) {
571 perror("getsockname");
575 if (fcntl(sockfd
, F_SETFL
, fcntl(sockfd
, F_GETFL
, 0) | O_NONBLOCK
) < 0) {
576 fprintf(stderr
,"%s| ERROR: fcntl() failure: %s\n", argv
[0], xstrerror());
580 nas_ipaddr
= ntohl(salocal
.sin_addr
.s_addr
);
581 while (fgets(buf
, HELPER_INPUT_BUFFER
, stdin
) != NULL
) {
583 /* protect me form to long lines */
584 if ((end
= strchr(buf
, '\n')) == NULL
) {
593 if (strlen(buf
) > HELPER_INPUT_BUFFER
) {
597 /* Strip off the trailing newline */
600 /* Parse out the username and password */
602 while (isspace(*ptr
))
604 if ((end
= strchr(ptr
, ' ')) == NULL
) {
605 SEND_ERR("No password");
609 urldecode(username
, ptr
, MAXPWNAM
);
611 while (isspace(*ptr
))
613 urldecode(passwd
, ptr
, MAXPASS
);
615 authenticate(sockfd
, username
, passwd
);