• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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