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
21static void rc_random_vector (unsigned char *);
22static 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
33static 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
192int 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
395static 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
483static 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