]> git.ipfire.org Git - thirdparty/squid.git/blame - helpers/basic_auth/squid_radius_auth/squid_rad_auth.c
Change priority of proxy auth and extacl provided username in login=*:pass
[thirdparty/squid.git] / helpers / basic_auth / squid_radius_auth / squid_rad_auth.c
CommitLineData
d80aac12 1/*
2 * RADIUS
3 * Remote Authentication Dial In User Service
4 *
5 *
6 * Livingston Enterprises, Inc.
7 * 6920 Koll Center Parkway
8 * Pleasanton, CA 94566
9 *
10 * Copyright 1992 Livingston Enterprises, Inc.
11 *
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.
20 *
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.
24 *
25 * The new parts of the code is Copyright (C) 1998 R.M. van Selm <selm@cistron.nl>
26 * with modifications
27 * Copyright (C) 2004 Henrik Nordstrom <hno@squid-cache.org>
28 * Copyright (C) 2006 Henrik Nordstrom <hno@squid-cache.org>
29 */
30
31/* Squid_rad_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.
34 *
35 * Squid_rad_auth-1.0 is based on modules from the Cistron-radiusd-1.5.4.
36 *
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)
41 *
42 * Marc van Selm <selm@cistron.nl>
43 * with contributions from
44 * Henrik Nordstrom <hno@squid-cache.org>
45 * and many others
46 */
47
48#include <sys/types.h>
49#include <sys/socket.h>
50#include <netinet/in.h>
51#include <sys/time.h>
52#include <unistd.h>
53#include <fcntl.h>
54
55#include <ctype.h>
56#include <stdio.h>
57#include <unistd.h>
58#include <netdb.h>
59#include <pwd.h>
60#include <stdlib.h>
61#include <time.h>
62#include <string.h>
63
64#include "md5.h"
65#include "radius.h"
66#include "util.h"
67
68#define MAXPWNAM 254
69#define MAXPASS 254
70#define MAXLINE 254
71
72
73static int i_send_buffer[2048];
74static int i_recv_buffer[2048];
75static char *send_buffer = (char *) i_send_buffer;
76static char *recv_buffer = (char *) i_recv_buffer;
77static int sockfd;
78static int request_id;
79static char vector[AUTH_VECTOR_LEN];
80static char secretkey[MAXPASS + 1] = "";
81static char server[MAXLINE] = "";
82static char identifier[MAXLINE] = "";
83static char svc_name[MAXLINE] = "radius";
84static int nasport = 111;
85static int nasporttype = 0;
86static UINT4 nas_ipaddr;
87static UINT4 auth_ipaddr;
88static int retries = 30;
89
90char *progname = "squid_rad_auth";
91int debug_flag = 0;
92
93/*
94 * Diff two timeval, b - a
95 */
96static int
97timeval_diff(const struct timeval *a, const struct timeval *b)
98{
99 return (b->tv_sec - a->tv_sec) * 1000000 + (b->tv_usec - a->tv_usec);
100}
101
102/*
103 * Time since a timeval
104 */
105static int
106time_since(const struct timeval *when)
107{
108 struct timeval now;
109 gettimeofday(&now, NULL);
110 return timeval_diff(when, &now);
111}
112
113/*
114 * Receive and verify the result.
115 */
116static int
117result_recv(UINT4 host, u_short udp_port, char *buffer, int length)
118{
119 AUTH_HDR *auth;
120 int totallen;
121 unsigned char reply_digest[AUTH_VECTOR_LEN];
122 unsigned char calc_digest[AUTH_VECTOR_LEN];
123 int secretlen;
124 /* VALUE_PAIR *req; */
125
126 auth = (AUTH_HDR *) buffer;
127 totallen = ntohs(auth->length);
128
129 if (totallen != length) {
130 fprintf(stderr,
131 "Squid_rad_auth: Received invalid reply length from server (want %d/ got %d)\n",
132 totallen, length);
133 return -1;
134 }
135
136 if (auth->id != request_id) {
137 /* Duplicate response of an earlier query, ignore */
138 return -1;
139 }
140
141 /* Verify the reply digest */
142 memcpy(reply_digest, auth->vector, AUTH_VECTOR_LEN);
143 memcpy(auth->vector, vector, AUTH_VECTOR_LEN);
144 secretlen = strlen(secretkey);
145 memcpy(buffer + length, secretkey, secretlen);
146 md5_calc(calc_digest, (unsigned char *) auth, length + secretlen);
147
148 if (memcmp(reply_digest, calc_digest, AUTH_VECTOR_LEN) != 0) {
149 fprintf(stderr, "Warning: Received invalid reply digest from server\n");
150 return -1;
151 }
152
153 if (auth->code != PW_AUTHENTICATION_ACK)
154 return 1;
155
156 return 0;
157}
158
159
160/*
161 * Generate a random vector.
162 */
163static void
164random_vector(char *vector)
165{
166 int randno;
167 int i;
168
169 srand((time(0) ^ rand()) + rand());
170 for (i = 0; i < AUTH_VECTOR_LEN;) {
171 randno = rand();
172 memcpy(vector, &randno, sizeof(int));
173 vector += sizeof(int);
174 i += sizeof(int);
175 }
176}
177
178/* read the config file
179 * The format should be something like:
180 * # squid_rad_auth configuration file
181 * # MvS: 28-10-1998
182 * server suncone.cistron.nl
183 * secret testje
184 */
185static int
186rad_auth_config(const char *cfname)
187{
188 FILE *cf;
189 char line[MAXLINE];
190 int srv = 0, crt = 0;
191
192 if ((cf = fopen(cfname, "r")) == NULL) {
193 perror(cfname);
194 return -1;
195 }
196 while (fgets(line, MAXLINE, cf) != NULL) {
197 if (!memcmp(line, "server", 6))
198 srv = sscanf(line, "server %s", server);
199 if (!memcmp(line, "secret", 6))
200 crt = sscanf(line, "secret %s", secretkey);
201 if (!memcmp(line, "identifier", 10))
202 sscanf(line, "identifier %s", identifier);
203 if (!memcmp(line, "service", 7))
204 sscanf(line, "service %s", svc_name);
205 if (!memcmp(line, "port", 4))
206 sscanf(line, "port %s", svc_name);
207 }
208 if (srv && crt)
209 return 0;
210 return -1;
211}
212
213static void
214urldecode(char *dst, const char *src, int size)
215{
216 char tmp[3];
217 tmp[2] = '\0';
218 while (*src && size > 1) {
219 if (*src == '%' && src[1] != '\0' && src[2] != '\0') {
220 src++;
221 tmp[0] = *src++;
222 tmp[1] = *src++;
223 *dst++ = strtol(tmp, NULL, 16);
224 } else {
225 *dst++ = *src++;
226 }
227 size--;
228 }
229 *dst++ = '\0';
230}
231
232static int
233authenticate(int sockfd, const char *username, const char *passwd)
234{
235 AUTH_HDR *auth;
236 u_short total_length;
237 u_char *ptr;
238 int length;
239 char passbuf[MAXPASS];
240 u_char md5buf[256];
241 int secretlen;
242 u_char cbc[AUTH_VECTOR_LEN];
243 int i, j;
244 UINT4 ui;
245 struct sockaddr_in saremote;
246 fd_set readfds;
247 socklen_t salen;
248 int retry = retries;
249
250 /*
251 * Build an authentication request
252 */
253 auth = (AUTH_HDR *) send_buffer;
254 auth->code = PW_AUTHENTICATION_REQUEST;
255 auth->id = ++request_id;
256 random_vector(vector);
257 memcpy(auth->vector, vector, AUTH_VECTOR_LEN);
258 total_length = AUTH_HDR_LEN;
259 ptr = auth->data;
260
261 /*
262 * User Name
263 */
264 *ptr++ = PW_USER_NAME;
265 length = strlen(username);
266 if (length > MAXPWNAM) {
267 length = MAXPWNAM;
268 }
269 *ptr++ = length + 2;
270 memcpy(ptr, username, length);
271 ptr += length;
272 total_length += length + 2;
273
274 /*
275 * Password
276 */
277 length = strlen(passwd);
278 if (length > MAXPASS) {
279 length = MAXPASS;
280 }
281 memset(passbuf, 0, MAXPASS);
282 memcpy(passbuf, passwd, length);
283
284 /*
285 * Length is rounded up to multiple of 16,
286 * and the password is encoded in blocks of 16
287 * with cipher block chaining
288 */
289 length = ((length / AUTH_VECTOR_LEN) + 1) * AUTH_VECTOR_LEN;
290
291 *ptr++ = PW_PASSWORD;
292 *ptr++ = length + 2;
293
294 secretlen = strlen(secretkey);
295 /* Set up the Cipher block chain */
296 memcpy(cbc, auth->vector, AUTH_VECTOR_LEN);
297 for (j = 0; j < length; j += AUTH_VECTOR_LEN) {
298 /* Calculate the MD5 Digest */
299 strcpy((char *)md5buf, secretkey);
300 memcpy(md5buf + secretlen, cbc, AUTH_VECTOR_LEN);
301 md5_calc(cbc, md5buf, secretlen + AUTH_VECTOR_LEN);
302
303 /* Xor the password into the MD5 digest */
304 for (i = 0; i < AUTH_VECTOR_LEN; i++) {
305 *ptr++ = (cbc[i] ^= passbuf[j + i]);
306 }
307 }
308 total_length += length + 2;
309
310 *ptr++ = PW_NAS_PORT_ID;
311 *ptr++ = 6;
312
313 ui = htonl(nasport);
314 memcpy(ptr, &ui, 4);
315 ptr += 4;
316 total_length += 6;
317
318 *ptr++ = PW_NAS_PORT_TYPE;
319 *ptr++ = 6;
320
321 ui = htonl(nasporttype);
322 memcpy(ptr, &ui, 4);
323 ptr += 4;
324 total_length += 6;
325
326 if (*identifier) {
327 int len = strlen(identifier);
328 *ptr++ = PW_NAS_ID;
329 *ptr++ = len + 2;
330 memcpy(ptr, identifier, len);
331 ptr += len;
332 } else {
333 *ptr++ = PW_NAS_IP_ADDRESS;
334 *ptr++ = 6;
335
336 ui = htonl(nas_ipaddr);
337 memcpy(ptr, &ui, 4);
338 ptr += 4;
339 total_length += 6;
340 }
341
342 /* Klaus Weidner <kw@w-m-p.com> changed this
343 * from htonl to htons. It might have caused
344 * you trouble or not. That depends on the byte
345 * order of your system.
346 * The symptom was that the radius server
347 * ignored the requests, because they had zero
348 * length according to the data header.
349 */
350 auth->length = htons(total_length);
351
352 while(retry--) {
353 int time_spent;
354 struct timeval sent;
355 /*
356 * Send the request we've built.
357 */
358 gettimeofday(&sent, NULL);
359 send(sockfd, (char *) auth, total_length, 0);
360 while ((time_spent = time_since(&sent)) < 1000000) {
361 struct timeval tv;
362 int rc, len;
363 if (!time_spent) {
364 tv.tv_sec = 1;
365 tv.tv_usec = 0;
366 } else {
367 tv.tv_sec = 0;
368 tv.tv_usec = 1000000 - time_spent;
369 }
370 FD_ZERO(&readfds);
371 FD_SET(sockfd, &readfds);
372 if (select(sockfd + 1, &readfds, NULL, NULL, &tv) == 0) /* Select timeout */
373 break;
374 salen = sizeof(saremote);
375 len = recvfrom(sockfd, recv_buffer, sizeof(i_recv_buffer),
376 0, (struct sockaddr *) &saremote, &salen);
377
378 if (len < 0)
379 continue;
380
381 rc = result_recv(saremote.sin_addr.s_addr, saremote.sin_port, recv_buffer, len);
382 if (rc == 0)
383 return 1;
384 if (rc == 1)
385 return 0;
386 }
387 }
388
389 fprintf(stderr, "%s: No response from RADIUS server\n", progname);
390
391 return 0;
392}
393
394int
395main(int argc, char **argv)
396{
397 struct sockaddr_in salocal;
398 struct sockaddr_in saremote;
399 struct servent *svp;
400 u_short svc_port;
401 char username[MAXPWNAM];
402 char passwd[MAXPASS];
403 char *ptr;
404 char authstring[MAXLINE];
405 const char *cfname = NULL;
406 int err = 0;
407 socklen_t salen;
408 int c;
409
410 while ((c = getopt(argc, argv, "h:p:f:w:i:t:")) != -1) {
411 switch(c) {
412 case 'f':
413 cfname = optarg;
414 break;
415 case 'h':
416 strcpy(server, optarg);
417 break;
418 case 'p':
419 strcpy(svc_name, optarg);
420 break;
421 case 'w':
422 strcpy(secretkey, optarg);
423 break;
424 case 'i':
425 strcpy(identifier, optarg);
426 break;
427 case 't':
428 retries = atoi(optarg);
429 break;
430 }
431 }
432 /* make standard output line buffered */
433 if (setvbuf(stdout, NULL, _IOLBF, 0) != 0)
434 return 1;
435
436 if (cfname) {
437 if (rad_auth_config(cfname) < 0) {
438 fprintf(stderr, "%s: can't open configuration file '%s'.\n", argv[0], cfname);
439 exit(1);
440 }
441 }
442
443 if (!*server) {
444 fprintf(stderr, "%s: Server not specified\n", argv[0]);
445 exit(1);
446 }
447
448 if (!*secretkey) {
449 fprintf(stderr, "%s: Shared secret not specified\n", argv[0]);
450 exit(1);
451 }
452
453 /*
454 * Open a connection to the server.
455 */
456 svp = getservbyname(svc_name, "udp");
457 if (svp != NULL)
458 svc_port = ntohs((u_short) svp->s_port);
459 else
460 svc_port = atoi(svc_name);
461 if (svc_port == 0)
462 svc_port = PW_AUTH_UDP_PORT;
463
464 /* Get the IP address of the authentication server */
465 if ((auth_ipaddr = get_ipaddr(server)) == 0) {
466 fprintf(stderr, "Couldn't find host %s\n", server);
467 exit(1);
468 }
469 sockfd = socket(AF_INET, SOCK_DGRAM, 0);
470 if (sockfd < 0) {
471 perror("socket");
472 exit(1);
473 }
474 memset(&saremote, 0, sizeof(saremote));
475 saremote.sin_family = AF_INET;
476 saremote.sin_addr.s_addr = htonl(auth_ipaddr);
477 saremote.sin_port = htons(svc_port);
478
479 if (connect(sockfd, (struct sockaddr *) &saremote, sizeof(saremote)) < 0) {
480 perror("connect");
481 exit(1);
482 }
483 salen = sizeof(salocal);
484 if (getsockname(sockfd, (struct sockaddr *) &salocal, &salen) < 0) {
485 perror("getsockname");
486 exit(1);
487 }
488#ifdef O_NONBLOCK
489 fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
490#endif
491 nas_ipaddr = ntohl(salocal.sin_addr.s_addr);
492 while (fgets(authstring, MAXLINE, stdin) != NULL) {
493 char *end;
494 /* protect me form to long lines */
495 if ((end = strchr(authstring, '\n')) == NULL) {
496 err = 1;
497 continue;
498 }
499 if (err) {
500 printf("ERR\n");
501 err = 0;
502 continue;
503 }
504 if (strlen(authstring) > MAXLINE) {
505 printf("ERR\n");
506 continue;
507 }
508 /* Strip off the trailing newline */
509 *end = '\0';
510
511 /* Parse out the username and password */
512 ptr = authstring;
513 while (isspace(*ptr))
514 ptr++;
515 if ((end = strchr(ptr, ' ')) == NULL) {
516 printf("ERR\n"); /* No password */
517 continue;
518 }
519 *end = '\0';
520 urldecode(username, ptr, MAXPWNAM);
521 ptr = end + 1;
522 while (isspace(*ptr))
523 ptr++;
524 urldecode(passwd, ptr, MAXPASS);
525
526 if (authenticate(sockfd, username, passwd))
527 printf("OK\n");
528 else
529 printf("ERR\n");
530 }
531 close(sockfd);
532 exit(1);
533}