1 /*
2 * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
3 *
4 * Copyright (C) 1995,1996,1997 Lars Fenneberg
5 *
6 * Copyright 1992 Livingston Enterprises, Inc.
7 *
8 * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
9 * and Merit Network, Inc. All Rights Reserved
10 *
11 * See the file COPYRIGHT for the respective terms and conditions.
12 * If the file is missing contact me at lf@elemental.net
13 * and I'll send you a copy.
14 *
15 */
16
17 #include <includes.h>
18 #include <radiusclient.h>
19 #include <pathnames.h>
20
21 static void rc_random_vector (unsigned char *);
22 static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char);
23
24 /*
25 * Function: rc_pack_list
26 *
27 * Purpose: Packs an attribute value pair list into a buffer.
28 *
29 * Returns: Number of octets packed.
30 *
31 */
32
rc_pack_list(VALUE_PAIR * vp,char * secret,AUTH_HDR * auth)33 static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth)
34 {
35 int length, i, pc, secretlen, padded_length;
36 int total_length = 0;
37 UINT4 lvalue;
38 unsigned char passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)];
39 unsigned char md5buf[256];
40 unsigned char *buf, *vector, *lenptr;
41
42 buf = auth->data;
43
44 while (vp != (VALUE_PAIR *) NULL)
45 {
46
47 if (vp->vendorcode != VENDOR_NONE) {
48 *buf++ = PW_VENDOR_SPECIFIC;
49
50 /* Place-holder for where to put length */
51 lenptr = buf++;
52
53 /* Insert vendor code */
54 *buf++ = 0;
55 *buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255;
56 *buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255;
57 *buf++ = ((unsigned int) vp->vendorcode) & 255;
58
59 /* Insert vendor-type */
60 *buf++ = vp->attribute;
61
62 /* Insert value */
63 switch(vp->type) {
64 case PW_TYPE_STRING:
65 length = vp->lvalue;
66 *lenptr = length + 8;
67 *buf++ = length+2;
68 memcpy(buf, vp->strvalue, (size_t) length);
69 buf += length;
70 total_length += length+8;
71 break;
72 case PW_TYPE_INTEGER:
73 case PW_TYPE_IPADDR:
74 length = sizeof(UINT4);
75 *lenptr = length + 8;
76 *buf++ = length+2;
77 lvalue = htonl(vp->lvalue);
78 memcpy(buf, (char *) &lvalue, sizeof(UINT4));
79 buf += length;
80 total_length += length+8;
81 break;
82 default:
83 break;
84 }
85 } else {
86 *buf++ = vp->attribute;
87 switch (vp->attribute) {
88 case PW_USER_PASSWORD:
89
90 /* Encrypt the password */
91
92 /* Chop off password at AUTH_PASS_LEN */
93 length = vp->lvalue;
94 if (length > AUTH_PASS_LEN) length = AUTH_PASS_LEN;
95
96 /* Calculate the padded length */
97 padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1);
98
99 /* Record the attribute length */
100 *buf++ = padded_length + 2;
101
102 /* Pad the password with zeros */
103 memset ((char *) passbuf, '\0', AUTH_PASS_LEN);
104 memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
105
106 secretlen = strlen (secret);
107 vector = (char *)auth->vector;
108 for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN) {
109 /* Calculate the MD5 digest*/
110 strcpy ((char *) md5buf, secret);
111 memcpy ((char *) md5buf + secretlen, vector,
112 AUTH_VECTOR_LEN);
113 rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
114
115 /* Remeber the start of the digest */
116 vector = buf;
117
118 /* Xor the password into the MD5 digest */
119 for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++) {
120 *buf++ ^= passbuf[pc];
121 }
122 }
123
124 total_length += padded_length + 2;
125
126 break;
127 #if 0
128 case PW_CHAP_PASSWORD:
129
130 *buf++ = CHAP_VALUE_LENGTH + 2;
131
132 /* Encrypt the Password */
133 length = vp->lvalue;
134 if (length > CHAP_VALUE_LENGTH) {
135 length = CHAP_VALUE_LENGTH;
136 }
137 memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH);
138 memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
139
140 /* Calculate the MD5 Digest */
141 secretlen = strlen (secret);
142 strcpy ((char *) md5buf, secret);
143 memcpy ((char *) md5buf + secretlen, (char *) auth->vector,
144 AUTH_VECTOR_LEN);
145 rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
146
147 /* Xor the password into the MD5 digest */
148 for (i = 0; i < CHAP_VALUE_LENGTH; i++) {
149 *buf++ ^= passbuf[i];
150 }
151 total_length += CHAP_VALUE_LENGTH + 2;
152
153 break;
154 #endif
155 default:
156 switch (vp->type) {
157 case PW_TYPE_STRING:
158 length = vp->lvalue;
159 *buf++ = length + 2;
160 memcpy (buf, vp->strvalue, (size_t) length);
161 buf += length;
162 total_length += length + 2;
163 break;
164
165 case PW_TYPE_INTEGER:
166 case PW_TYPE_IPADDR:
167 *buf++ = sizeof (UINT4) + 2;
168 lvalue = htonl (vp->lvalue);
169 memcpy (buf, (char *) &lvalue, sizeof (UINT4));
170 buf += sizeof (UINT4);
171 total_length += sizeof (UINT4) + 2;
172 break;
173
174 default:
175 break;
176 }
177 break;
178 }
179 }
180 vp = vp->next;
181 }
182 return total_length;
183 }
184
185 /*
186 * Function: rc_send_server
187 *
188 * Purpose: send a request to a RADIUS server and wait for the reply
189 *
190 */
191
rc_send_server(SEND_DATA * data,char * msg,REQUEST_INFO * info)192 int rc_send_server (SEND_DATA *data, char *msg, REQUEST_INFO *info)
193 {
194 int sockfd;
195 struct sockaddr salocal;
196 struct sockaddr saremote;
197 struct sockaddr_in *sin;
198 struct timeval authtime;
199 fd_set readfds;
200 AUTH_HDR *auth, *recv_auth;
201 UINT4 auth_ipaddr;
202 char *server_name; /* Name of server to query */
203 int salen;
204 int result;
205 int total_length;
206 int length;
207 int retry_max;
208 int secretlen;
209 char secret[MAX_SECRET_LENGTH + 1];
210 unsigned char vector[AUTH_VECTOR_LEN];
211 char recv_buffer[BUFFER_LEN];
212 char send_buffer[BUFFER_LEN];
213 int retries;
214 VALUE_PAIR *vp;
215
216 server_name = data->server;
217 if (server_name == (char *) NULL || server_name[0] == '\0')
218 return (ERROR_RC);
219
220 if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \
221 (vp->lvalue == PW_ADMINISTRATIVE))
222 {
223 strcpy(secret, MGMT_POLL_SECRET);
224 if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0)
225 return (ERROR_RC);
226 }
227 else
228 {
229 if (rc_find_server (server_name, &auth_ipaddr, secret) != 0)
230 {
231 return (ERROR_RC);
232 }
233 }
234
235 sockfd = socket (AF_INET, SOCK_DGRAM, 0);
236 if (sockfd < 0)
237 {
238 memset (secret, '\0', sizeof (secret));
239 error("rc_send_server: socket: %s", strerror(errno));
240 return (ERROR_RC);
241 }
242
243 length = sizeof (salocal);
244 sin = (struct sockaddr_in *) & salocal;
245 memset ((char *) sin, '\0', (size_t) length);
246 sin->sin_family = AF_INET;
247 sin->sin_addr.s_addr = htonl(INADDR_ANY);
248 sin->sin_port = htons ((unsigned short) 0);
249 if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
250 getsockname (sockfd, (struct sockaddr *) sin, &length) < 0)
251 {
252 close (sockfd);
253 memset (secret, '\0', sizeof (secret));
254 error("rc_send_server: bind: %s: %m", server_name);
255 return (ERROR_RC);
256 }
257
258 retry_max = data->retries; /* Max. numbers to try for reply */
259 retries = 0; /* Init retry cnt for blocking call */
260
261 /* Build a request */
262 auth = (AUTH_HDR *) send_buffer;
263 auth->code = data->code;
264 auth->id = data->seq_nbr;
265
266 if (data->code == PW_ACCOUNTING_REQUEST)
267 {
268 total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
269
270 auth->length = htons ((unsigned short) total_length);
271
272 memset((char *) auth->vector, 0, AUTH_VECTOR_LEN);
273 secretlen = strlen (secret);
274 memcpy ((char *) auth + total_length, secret, secretlen);
275 rc_md5_calc (vector, (char *) auth, total_length + secretlen);
276 memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
277 }
278 else
279 {
280 rc_random_vector (vector);
281 memcpy (auth->vector, vector, AUTH_VECTOR_LEN);
282
283 total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
284
285 auth->length = htons ((unsigned short) total_length);
286 }
287
288 sin = (struct sockaddr_in *) & saremote;
289 memset ((char *) sin, '\0', sizeof (saremote));
290 sin->sin_family = AF_INET;
291 sin->sin_addr.s_addr = htonl (auth_ipaddr);
292 sin->sin_port = htons ((unsigned short) data->svc_port);
293
294 for (;;)
295 {
296 sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0,
297 (struct sockaddr *) sin, sizeof (struct sockaddr_in));
298
299 authtime.tv_usec = 0L;
300 authtime.tv_sec = (long) data->timeout;
301 FD_ZERO (&readfds);
302 FD_SET (sockfd, &readfds);
303 if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0)
304 {
305 if (errno == EINTR)
306 continue;
307 error("rc_send_server: select: %m");
308 memset (secret, '\0', sizeof (secret));
309 close (sockfd);
310 return (ERROR_RC);
311 }
312 if (FD_ISSET (sockfd, &readfds))
313 break;
314
315 /*
316 * Timed out waiting for response. Retry "retry_max" times
317 * before giving up. If retry_max = 0, don't retry at all.
318 */
319 if (++retries >= retry_max)
320 {
321 error("rc_send_server: no reply from RADIUS server %s:%u",
322 rc_ip_hostname (auth_ipaddr), data->svc_port);
323 close (sockfd);
324 memset (secret, '\0', sizeof (secret));
325 return (TIMEOUT_RC);
326 }
327 }
328 salen = sizeof (saremote);
329 length = recvfrom (sockfd, (char *) recv_buffer,
330 (int) sizeof (recv_buffer),
331 (int) 0, &saremote, &salen);
332
333 if (length <= 0)
334 {
335 error("rc_send_server: recvfrom: %s:%d: %m", server_name,\
336 data->svc_port);
337 close (sockfd);
338 memset (secret, '\0', sizeof (secret));
339 return (ERROR_RC);
340 }
341
342 recv_auth = (AUTH_HDR *)recv_buffer;
343
344 result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr);
345
346 data->receive_pairs = rc_avpair_gen(recv_auth);
347
348 close (sockfd);
349 if (info)
350 {
351 memcpy(info->secret, secret, sizeof(info->secret));
352 memcpy(info->request_vector, vector,
353 sizeof(info->request_vector));
354 }
355 memset (secret, '\0', sizeof (secret));
356
357 if (result != OK_RC) return (result);
358
359 *msg = '\0';
360 vp = data->receive_pairs;
361 while (vp)
362 {
363 if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE)))
364 {
365 strcat(msg, vp->strvalue);
366 strcat(msg, "\n");
367 vp = vp->next;
368 }
369 }
370
371 if ((recv_auth->code == PW_ACCESS_ACCEPT) ||
372 (recv_auth->code == PW_PASSWORD_ACK) ||
373 (recv_auth->code == PW_ACCOUNTING_RESPONSE))
374 {
375 result = OK_RC;
376 }
377 else
378 {
379 result = BADRESP_RC;
380 }
381
382 return (result);
383 }
384
385 /*
386 * Function: rc_check_reply
387 *
388 * Purpose: verify items in returned packet.
389 *
390 * Returns: OK_RC -- upon success,
391 * BADRESP_RC -- if anything looks funny.
392 *
393 */
394
rc_check_reply(AUTH_HDR * auth,int bufferlen,char * secret,unsigned char * vector,unsigned char seq_nbr)395 static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret,
396 unsigned char *vector, unsigned char seq_nbr)
397 {
398 int secretlen;
399 int totallen;
400 unsigned char calc_digest[AUTH_VECTOR_LEN];
401 unsigned char reply_digest[AUTH_VECTOR_LEN];
402
403 totallen = ntohs (auth->length);
404
405 secretlen = strlen (secret);
406
407 /* Do sanity checks on packet length */
408 if ((totallen < 20) || (totallen > 4096))
409 {
410 error("rc_check_reply: received RADIUS server response with invalid length");
411 return (BADRESP_RC);
412 }
413
414 /* Verify buffer space, should never trigger with current buffer size and check above */
415 if ((totallen + secretlen) > bufferlen)
416 {
417 error("rc_check_reply: not enough buffer space to verify RADIUS server response");
418 return (BADRESP_RC);
419 }
420 /* Verify that id (seq. number) matches what we sent */
421 if (auth->id != seq_nbr)
422 {
423 error("rc_check_reply: received non-matching id in RADIUS server response");
424 return (BADRESP_RC);
425 }
426
427 /* Verify the reply digest */
428 memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN);
429 memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
430 memcpy ((char *) auth + totallen, secret, secretlen);
431 rc_md5_calc (calc_digest, (char *) auth, totallen + secretlen);
432
433 #ifdef DIGEST_DEBUG
434 {
435 int i;
436
437 fputs("reply_digest: ", stderr);
438 for (i = 0; i < AUTH_VECTOR_LEN; i++)
439 {
440 fprintf(stderr,"%.2x ", (int) reply_digest[i]);
441 }
442 fputs("\ncalc_digest: ", stderr);
443 for (i = 0; i < AUTH_VECTOR_LEN; i++)
444 {
445 fprintf(stderr,"%.2x ", (int) calc_digest[i]);
446 }
447 fputs("\n", stderr);
448 }
449 #endif
450
451 if (memcmp ((char *) reply_digest, (char *) calc_digest,
452 AUTH_VECTOR_LEN) != 0)
453 {
454 #ifdef RADIUS_116
455 /* the original Livingston radiusd v1.16 seems to have
456 a bug in digest calculation with accounting requests,
457 authentication request are ok. i looked at the code
458 but couldn't find any bugs. any help to get this
459 kludge out are welcome. preferably i want to
460 reproduce the calculation bug here to be compatible
461 to stock Livingston radiusd v1.16. -lf, 03/14/96
462 */
463 if (auth->code == PW_ACCOUNTING_RESPONSE)
464 return (OK_RC);
465 #endif
466 error("rc_check_reply: received invalid reply digest from RADIUS server");
467 return (BADRESP_RC);
468 }
469
470 return (OK_RC);
471
472 }
473
474 /*
475 * Function: rc_random_vector
476 *
477 * Purpose: generates a random vector of AUTH_VECTOR_LEN octets.
478 *
479 * Returns: the vector (call by reference)
480 *
481 */
482
rc_random_vector(unsigned char * vector)483 static void rc_random_vector (unsigned char *vector)
484 {
485 int randno;
486 int i;
487 int fd;
488
489 /* well, I added this to increase the security for user passwords.
490 we use /dev/urandom here, as /dev/random might block and we don't
491 need that much randomness. BTW, great idea, Ted! -lf, 03/18/95 */
492
493 if ((fd = open(_PATH_DEV_URANDOM, O_RDONLY)) >= 0)
494 {
495 unsigned char *pos;
496 int readcount;
497
498 i = AUTH_VECTOR_LEN;
499 pos = vector;
500 while (i > 0)
501 {
502 readcount = read(fd, (char *)pos, i);
503 pos += readcount;
504 i -= readcount;
505 }
506
507 close(fd);
508 return;
509 } /* else fall through */
510
511 for (i = 0; i < AUTH_VECTOR_LEN;)
512 {
513 randno = magic();
514 memcpy ((char *) vector, (char *) &randno, sizeof (int));
515 vector += sizeof (int);
516 i += sizeof (int);
517 }
518
519 return;
520 }
521