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