2 * $Id: sendserver.c,v 1.1 2004/11/14 07:26:26 paulus Exp $
4 * Copyright (C) 1995,1996,1997 Lars Fenneberg
6 * Copyright 1992 Livingston Enterprises, Inc.
8 * Copyright 1992,1993, 1994,1995 The Regents of the University of Michigan
9 * and Merit Network, Inc. All Rights Reserved
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.
18 #include <radiusclient.h>
19 #include <pathnames.h>
22 static void rc_random_vector (unsigned char *);
23 static int rc_check_reply (AUTH_HDR *, int, char *, unsigned char *, unsigned char);
26 * Function: rc_pack_list
28 * Purpose: Packs an attribute value pair list into a buffer.
30 * Returns: Number of octets packed.
34 static int rc_pack_list (VALUE_PAIR *vp, char *secret, AUTH_HDR *auth)
36 int length, i, pc, secretlen, padded_length;
39 unsigned char passbuf[MAX(AUTH_PASS_LEN, CHAP_VALUE_LENGTH)];
40 unsigned char md5buf[256];
41 unsigned char *buf, *vector, *lenptr;
45 while (vp != (VALUE_PAIR *) NULL)
48 if (vp->vendorcode != VENDOR_NONE) {
49 *buf++ = PW_VENDOR_SPECIFIC;
51 /* Place-holder for where to put length */
54 /* Insert vendor code */
56 *buf++ = (((unsigned int) vp->vendorcode) >> 16) & 255;
57 *buf++ = (((unsigned int) vp->vendorcode) >> 8) & 255;
58 *buf++ = ((unsigned int) vp->vendorcode) & 255;
60 /* Insert vendor-type */
61 *buf++ = vp->attribute;
69 memcpy(buf, vp->strvalue, (size_t) length);
71 total_length += length+8;
75 length = sizeof(UINT4);
78 lvalue = htonl(vp->lvalue);
79 memcpy(buf, (char *) &lvalue, sizeof(UINT4));
81 total_length += length+8;
87 *buf++ = vp->attribute;
88 switch (vp->attribute) {
89 case PW_USER_PASSWORD:
91 /* Encrypt the password */
93 /* Chop off password at AUTH_PASS_LEN */
95 if (length > AUTH_PASS_LEN) length = AUTH_PASS_LEN;
97 /* Calculate the padded length */
98 padded_length = (length+(AUTH_VECTOR_LEN-1)) & ~(AUTH_VECTOR_LEN-1);
100 /* Record the attribute length */
101 *buf++ = padded_length + 2;
103 /* Pad the password with zeros */
104 memset ((char *) passbuf, '\0', AUTH_PASS_LEN);
105 memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
107 secretlen = strlen (secret);
108 vector = auth->vector;
109 for(i = 0; i < padded_length; i += AUTH_VECTOR_LEN) {
110 /* Calculate the MD5 digest*/
111 strcpy ((char *) md5buf, secret);
112 memcpy ((char *) md5buf + secretlen, vector,
114 rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
116 /* Remeber the start of the digest */
119 /* Xor the password into the MD5 digest */
120 for (pc = i; pc < (i + AUTH_VECTOR_LEN); pc++) {
121 *buf++ ^= passbuf[pc];
125 total_length += padded_length + 2;
129 case PW_CHAP_PASSWORD:
131 *buf++ = CHAP_VALUE_LENGTH + 2;
133 /* Encrypt the Password */
135 if (length > CHAP_VALUE_LENGTH) {
136 length = CHAP_VALUE_LENGTH;
138 memset ((char *) passbuf, '\0', CHAP_VALUE_LENGTH);
139 memcpy ((char *) passbuf, vp->strvalue, (size_t) length);
141 /* Calculate the MD5 Digest */
142 secretlen = strlen (secret);
143 strcpy ((char *) md5buf, secret);
144 memcpy ((char *) md5buf + secretlen, (char *) auth->vector,
146 rc_md5_calc (buf, md5buf, secretlen + AUTH_VECTOR_LEN);
148 /* Xor the password into the MD5 digest */
149 for (i = 0; i < CHAP_VALUE_LENGTH; i++) {
150 *buf++ ^= passbuf[i];
152 total_length += CHAP_VALUE_LENGTH + 2;
161 memcpy (buf, vp->strvalue, (size_t) length);
163 total_length += length + 2;
166 case PW_TYPE_INTEGER:
168 *buf++ = sizeof (UINT4) + 2;
169 lvalue = htonl (vp->lvalue);
170 memcpy (buf, (char *) &lvalue, sizeof (UINT4));
171 buf += sizeof (UINT4);
172 total_length += sizeof (UINT4) + 2;
187 * Function: rc_send_server
189 * Purpose: send a request to a RADIUS server and wait for the reply
193 int rc_send_server (SEND_DATA *data, char *msg, REQUEST_INFO *info)
196 struct sockaddr salocal;
197 struct sockaddr saremote;
198 struct sockaddr_in *sin;
199 struct timeval authtime;
201 AUTH_HDR *auth, *recv_auth;
203 char *server_name; /* Name of server to query */
210 char secret[MAX_SECRET_LENGTH + 1];
211 unsigned char vector[AUTH_VECTOR_LEN];
212 char recv_buffer[BUFFER_LEN];
213 char send_buffer[BUFFER_LEN];
217 server_name = data->server;
218 if (server_name == (char *) NULL || server_name[0] == '\0')
221 if ((vp = rc_avpair_get(data->send_pairs, PW_SERVICE_TYPE)) && \
222 (vp->lvalue == PW_ADMINISTRATIVE))
224 strcpy(secret, MGMT_POLL_SECRET);
225 if ((auth_ipaddr = rc_get_ipaddr(server_name)) == 0)
230 if (rc_find_server (server_name, &auth_ipaddr, secret) != 0)
232 memset (secret, '\0', sizeof (secret));
237 sockfd = socket (AF_INET, SOCK_DGRAM, 0);
240 memset (secret, '\0', sizeof (secret));
241 error("rc_send_server: socket: %s", strerror(errno));
245 length = sizeof (salocal);
246 sin = (struct sockaddr_in *) & salocal;
247 memset ((char *) sin, '\0', (size_t) length);
248 sin->sin_family = AF_INET;
249 sin->sin_addr.s_addr = htonl(rc_own_bind_ipaddress());
250 sin->sin_port = htons ((unsigned short) 0);
251 if (bind (sockfd, (struct sockaddr *) sin, length) < 0 ||
252 getsockname (sockfd, (struct sockaddr *) sin, &length) < 0)
255 memset (secret, '\0', sizeof (secret));
256 error("rc_send_server: bind: %s: %m", server_name);
260 retry_max = data->retries; /* Max. numbers to try for reply */
261 retries = 0; /* Init retry cnt for blocking call */
263 /* Build a request */
264 auth = (AUTH_HDR *) send_buffer;
265 auth->code = data->code;
266 auth->id = data->seq_nbr;
268 if (data->code == PW_ACCOUNTING_REQUEST)
270 total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
272 auth->length = htons ((unsigned short) total_length);
274 memset((char *) auth->vector, 0, AUTH_VECTOR_LEN);
275 secretlen = strlen (secret);
276 memcpy ((char *) auth + total_length, secret, secretlen);
277 rc_md5_calc (vector, (unsigned char *) auth, total_length + secretlen);
278 memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
282 rc_random_vector (vector);
283 memcpy (auth->vector, vector, AUTH_VECTOR_LEN);
285 total_length = rc_pack_list(data->send_pairs, secret, auth) + AUTH_HDR_LEN;
287 auth->length = htons ((unsigned short) total_length);
290 sin = (struct sockaddr_in *) & saremote;
291 memset ((char *) sin, '\0', sizeof (saremote));
292 sin->sin_family = AF_INET;
293 sin->sin_addr.s_addr = htonl (auth_ipaddr);
294 sin->sin_port = htons ((unsigned short) data->svc_port);
298 sendto (sockfd, (char *) auth, (unsigned int) total_length, (int) 0,
299 (struct sockaddr *) sin, sizeof (struct sockaddr_in));
301 authtime.tv_usec = 0L;
302 authtime.tv_sec = (long) data->timeout;
304 FD_SET (sockfd, &readfds);
305 if (select (sockfd + 1, &readfds, NULL, NULL, &authtime) < 0)
307 if (errno == EINTR && !ppp_signaled(SIGTERM))
309 error("rc_send_server: select: %m");
310 memset (secret, '\0', sizeof (secret));
314 if (FD_ISSET (sockfd, &readfds))
318 * Timed out waiting for response. Retry "retry_max" times
319 * before giving up. If retry_max = 0, don't retry at all.
321 if (++retries >= retry_max)
323 error("rc_send_server: no reply from RADIUS server %s:%u",
324 rc_ip_hostname (auth_ipaddr), data->svc_port);
326 memset (secret, '\0', sizeof (secret));
330 salen = sizeof (saremote);
331 length = recvfrom (sockfd, (char *) recv_buffer,
332 (int) sizeof (recv_buffer),
333 (int) 0, &saremote, &salen);
337 error("rc_send_server: recvfrom: %s:%d: %m", server_name,\
340 memset (secret, '\0', sizeof (secret));
344 recv_auth = (AUTH_HDR *)recv_buffer;
346 result = rc_check_reply (recv_auth, BUFFER_LEN, secret, vector, data->seq_nbr);
348 data->receive_pairs = rc_avpair_gen(recv_auth);
353 memcpy(info->secret, secret, sizeof(info->secret));
354 memcpy(info->request_vector, vector,
355 sizeof(info->request_vector));
357 memset (secret, '\0', sizeof (secret));
359 if (result != OK_RC) return (result);
362 vp = data->receive_pairs;
365 if ((vp = rc_avpair_get(vp, PW_REPLY_MESSAGE)))
367 strcat(msg, (char*) vp->strvalue);
373 if ((recv_auth->code == PW_ACCESS_ACCEPT) ||
374 (recv_auth->code == PW_PASSWORD_ACK) ||
375 (recv_auth->code == PW_ACCOUNTING_RESPONSE))
388 * Function: rc_check_reply
390 * Purpose: verify items in returned packet.
392 * Returns: OK_RC -- upon success,
393 * BADRESP_RC -- if anything looks funny.
397 static int rc_check_reply (AUTH_HDR *auth, int bufferlen, char *secret,
398 unsigned char *vector, unsigned char seq_nbr)
402 unsigned char calc_digest[AUTH_VECTOR_LEN];
403 unsigned char reply_digest[AUTH_VECTOR_LEN];
405 totallen = ntohs (auth->length);
407 secretlen = strlen (secret);
409 /* Do sanity checks on packet length */
410 if ((totallen < 20) || (totallen > 4096))
412 error("rc_check_reply: received RADIUS server response with invalid length");
416 /* Verify buffer space, should never trigger with current buffer size and check above */
417 if ((totallen + secretlen) > bufferlen)
419 error("rc_check_reply: not enough buffer space to verify RADIUS server response");
422 /* Verify that id (seq. number) matches what we sent */
423 if (auth->id != seq_nbr)
425 error("rc_check_reply: received non-matching id in RADIUS server response");
429 /* Verify the reply digest */
430 memcpy ((char *) reply_digest, (char *) auth->vector, AUTH_VECTOR_LEN);
431 memcpy ((char *) auth->vector, (char *) vector, AUTH_VECTOR_LEN);
432 memcpy ((char *) auth + totallen, secret, secretlen);
433 rc_md5_calc (calc_digest, (unsigned char *) auth, totallen + secretlen);
439 fputs("reply_digest: ", stderr);
440 for (i = 0; i < AUTH_VECTOR_LEN; i++)
442 fprintf(stderr,"%.2x ", (int) reply_digest[i]);
444 fputs("\ncalc_digest: ", stderr);
445 for (i = 0; i < AUTH_VECTOR_LEN; i++)
447 fprintf(stderr,"%.2x ", (int) calc_digest[i]);
453 if (memcmp ((char *) reply_digest, (char *) calc_digest,
454 AUTH_VECTOR_LEN) != 0)
457 /* the original Livingston radiusd v1.16 seems to have
458 a bug in digest calculation with accounting requests,
459 authentication request are ok. i looked at the code
460 but couldn't find any bugs. any help to get this
461 kludge out are welcome. preferably i want to
462 reproduce the calculation bug here to be compatible
463 to stock Livingston radiusd v1.16. -lf, 03/14/96
465 if (auth->code == PW_ACCOUNTING_RESPONSE)
468 error("rc_check_reply: received invalid reply digest from RADIUS server");
477 * Function: rc_random_vector
479 * Purpose: generates a random vector of AUTH_VECTOR_LEN octets.
481 * Returns: the vector (call by reference)
485 static void rc_random_vector (unsigned char *vector)
491 /* well, I added this to increase the security for user passwords.
492 we use /dev/urandom here, as /dev/random might block and we don't
493 need that much randomness. BTW, great idea, Ted! -lf, 03/18/95 */
495 if ((fd = open(PPP_PATH_DEV_URANDOM, O_RDONLY)) >= 0)
504 readcount = read(fd, (char *)pos, i);
511 } /* else fall through */
513 for (i = 0; i < AUTH_VECTOR_LEN;)
516 memcpy ((char *) vector, (char *) &randno, sizeof (int));
517 vector += sizeof (int);