141118Sjdp/*-
241118Sjdp * Copyright 1998 Juniper Networks, Inc.
341118Sjdp * All rights reserved.
441118Sjdp *
541118Sjdp * Redistribution and use in source and binary forms, with or without
641118Sjdp * modification, are permitted provided that the following conditions
741118Sjdp * are met:
841118Sjdp * 1. Redistributions of source code must retain the above copyright
941118Sjdp *    notice, this list of conditions and the following disclaimer.
1041118Sjdp * 2. Redistributions in binary form must reproduce the above copyright
1141118Sjdp *    notice, this list of conditions and the following disclaimer in the
1241118Sjdp *    documentation and/or other materials provided with the distribution.
1341118Sjdp *
1441118Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1541118Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1641118Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1741118Sjdp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1841118Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1941118Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2041118Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2141118Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2241118Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2341118Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2441118Sjdp * SUCH DAMAGE.
2541118Sjdp */
2641118Sjdp
2784219Sdillon#include <sys/cdefs.h>
2884219Sdillon__FBSDID("$FreeBSD$");
2984219Sdillon
3041118Sjdp#include <sys/types.h>
3141118Sjdp#include <sys/socket.h>
3241118Sjdp#include <sys/time.h>
3341118Sjdp#include <netinet/in.h>
3441118Sjdp#include <arpa/inet.h>
35128684Sru#ifdef WITH_SSL
36128684Sru#include <openssl/hmac.h>
37128684Sru#include <openssl/md5.h>
38128684Sru#define MD5Init MD5_Init
39128684Sru#define MD5Update MD5_Update
40128684Sru#define MD5Final MD5_Final
41128684Sru#else
42128684Sru#define MD5_DIGEST_LENGTH 16
43128684Sru#include <md5.h>
44128684Sru#endif
4541118Sjdp
46243956Ssem#define	MAX_FIELDS	7
47243956Ssem
48128684Sru/* We need the MPPE_KEY_LEN define */
49128684Sru#include <netgraph/ng_mppc.h>
50128684Sru
5141118Sjdp#include <errno.h>
5241118Sjdp#include <netdb.h>
5341118Sjdp#include <stdarg.h>
5441118Sjdp#include <stddef.h>
5541118Sjdp#include <stdio.h>
5641118Sjdp#include <stdlib.h>
5741118Sjdp#include <string.h>
5841118Sjdp#include <unistd.h>
5941118Sjdp
6041118Sjdp#include "radlib_private.h"
6141118Sjdp
6241118Sjdpstatic void	 clear_password(struct rad_handle *);
6341118Sjdpstatic void	 generr(struct rad_handle *, const char *, ...)
6441118Sjdp		    __printflike(2, 3);
6541118Sjdpstatic void	 insert_scrambled_password(struct rad_handle *, int);
6652709Sjdpstatic void	 insert_request_authenticator(struct rad_handle *, int);
67128684Srustatic void	 insert_message_authenticator(struct rad_handle *, int);
6841118Sjdpstatic int	 is_valid_response(struct rad_handle *, int,
6941118Sjdp		    const struct sockaddr_in *);
7041118Sjdpstatic int	 put_password_attr(struct rad_handle *, int,
7141118Sjdp		    const void *, size_t);
7241118Sjdpstatic int	 put_raw_attr(struct rad_handle *, int,
7341118Sjdp		    const void *, size_t);
7441118Sjdpstatic int	 split(char *, char *[], int, char *, size_t);
7541118Sjdp
7641118Sjdpstatic void
7741118Sjdpclear_password(struct rad_handle *h)
7841118Sjdp{
7941118Sjdp	if (h->pass_len != 0) {
8041118Sjdp		memset(h->pass, 0, h->pass_len);
8141118Sjdp		h->pass_len = 0;
8241118Sjdp	}
8368499Seivind	h->pass_pos = 0;
8441118Sjdp}
8541118Sjdp
8641118Sjdpstatic void
8741118Sjdpgenerr(struct rad_handle *h, const char *format, ...)
8841118Sjdp{
8941118Sjdp	va_list		 ap;
9041118Sjdp
9141118Sjdp	va_start(ap, format);
9241118Sjdp	vsnprintf(h->errmsg, ERRSIZE, format, ap);
9341118Sjdp	va_end(ap);
9441118Sjdp}
9541118Sjdp
9641118Sjdpstatic void
9741118Sjdpinsert_scrambled_password(struct rad_handle *h, int srv)
9841118Sjdp{
9941118Sjdp	MD5_CTX ctx;
100128684Sru	unsigned char md5[MD5_DIGEST_LENGTH];
10141118Sjdp	const struct rad_server *srvp;
10241118Sjdp	int padded_len;
10341118Sjdp	int pos;
10441118Sjdp
10541118Sjdp	srvp = &h->servers[srv];
10641118Sjdp	padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
10741118Sjdp
108197086Smav	memcpy(md5, &h->out[POS_AUTH], LEN_AUTH);
10941118Sjdp	for (pos = 0;  pos < padded_len;  pos += 16) {
11041118Sjdp		int i;
11141118Sjdp
11241118Sjdp		/* Calculate the new scrambler */
11341118Sjdp		MD5Init(&ctx);
11441118Sjdp		MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
11541118Sjdp		MD5Update(&ctx, md5, 16);
11641118Sjdp		MD5Final(md5, &ctx);
11741118Sjdp
11841118Sjdp		/*
11941118Sjdp		 * Mix in the current chunk of the password, and copy
12041118Sjdp		 * the result into the right place in the request.  Also
12141118Sjdp		 * modify the scrambler in place, since we will use this
12241118Sjdp		 * in calculating the scrambler for next time.
12341118Sjdp		 */
12441118Sjdp		for (i = 0;  i < 16;  i++)
125197086Smav			h->out[h->pass_pos + pos + i] =
12641118Sjdp			    md5[i] ^= h->pass[pos + i];
12741118Sjdp	}
12841118Sjdp}
12941118Sjdp
13052709Sjdpstatic void
131197086Smavinsert_request_authenticator(struct rad_handle *h, int resp)
13252709Sjdp{
13352709Sjdp	MD5_CTX ctx;
13452709Sjdp	const struct rad_server *srvp;
13552709Sjdp
136197086Smav	srvp = &h->servers[h->srv];
13752709Sjdp
13852709Sjdp	/* Create the request authenticator */
13952709Sjdp	MD5Init(&ctx);
140197086Smav	MD5Update(&ctx, &h->out[POS_CODE], POS_AUTH - POS_CODE);
141197086Smav	if (resp)
142197086Smav	    MD5Update(&ctx, &h->in[POS_AUTH], LEN_AUTH);
143197086Smav	else
144197086Smav	    MD5Update(&ctx, &h->out[POS_AUTH], LEN_AUTH);
145197086Smav	MD5Update(&ctx, &h->out[POS_ATTRS], h->out_len - POS_ATTRS);
14652709Sjdp	MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
147197086Smav	MD5Final(&h->out[POS_AUTH], &ctx);
14852709Sjdp}
14952709Sjdp
150128684Srustatic void
151197086Smavinsert_message_authenticator(struct rad_handle *h, int resp)
152128684Sru{
153128684Sru#ifdef WITH_SSL
154128684Sru	u_char md[EVP_MAX_MD_SIZE];
155128684Sru	u_int md_len;
156128684Sru	const struct rad_server *srvp;
157128684Sru	HMAC_CTX ctx;
158197086Smav	srvp = &h->servers[h->srv];
159128684Sru
160128684Sru	if (h->authentic_pos != 0) {
161128684Sru		HMAC_CTX_init(&ctx);
162128684Sru		HMAC_Init(&ctx, srvp->secret, strlen(srvp->secret), EVP_md5());
163197086Smav		HMAC_Update(&ctx, &h->out[POS_CODE], POS_AUTH - POS_CODE);
164197086Smav		if (resp)
165197086Smav		    HMAC_Update(&ctx, &h->in[POS_AUTH], LEN_AUTH);
166197086Smav		else
167197086Smav		    HMAC_Update(&ctx, &h->out[POS_AUTH], LEN_AUTH);
168197086Smav		HMAC_Update(&ctx, &h->out[POS_ATTRS],
169197086Smav		    h->out_len - POS_ATTRS);
170128684Sru		HMAC_Final(&ctx, md, &md_len);
171128684Sru		HMAC_CTX_cleanup(&ctx);
172128684Sru		HMAC_cleanup(&ctx);
173197086Smav		memcpy(&h->out[h->authentic_pos + 2], md, md_len);
174128684Sru	}
175128684Sru#endif
176128684Sru}
177128684Sru
17841118Sjdp/*
17941118Sjdp * Return true if the current response is valid for a request to the
18041118Sjdp * specified server.
18141118Sjdp */
18241118Sjdpstatic int
18341118Sjdpis_valid_response(struct rad_handle *h, int srv,
18441118Sjdp    const struct sockaddr_in *from)
18541118Sjdp{
18641118Sjdp	MD5_CTX ctx;
187128684Sru	unsigned char md5[MD5_DIGEST_LENGTH];
18841118Sjdp	const struct rad_server *srvp;
18941118Sjdp	int len;
190128684Sru#ifdef WITH_SSL
191128684Sru	HMAC_CTX hctx;
192128684Sru	u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE];
193168341Skan	u_int md_len;
194168341Skan	int pos;
195128684Sru#endif
19641118Sjdp
19741118Sjdp	srvp = &h->servers[srv];
19841118Sjdp
19941118Sjdp	/* Check the source address */
20041118Sjdp	if (from->sin_family != srvp->addr.sin_family ||
20141118Sjdp	    from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
20241118Sjdp	    from->sin_port != srvp->addr.sin_port)
20341118Sjdp		return 0;
20441118Sjdp
20541118Sjdp	/* Check the message length */
206197086Smav	if (h->in_len < POS_ATTRS)
20741118Sjdp		return 0;
208197086Smav	len = h->in[POS_LENGTH] << 8 | h->in[POS_LENGTH+1];
209197086Smav	if (len > h->in_len)
21041118Sjdp		return 0;
21141118Sjdp
21241118Sjdp	/* Check the response authenticator */
21341118Sjdp	MD5Init(&ctx);
214197086Smav	MD5Update(&ctx, &h->in[POS_CODE], POS_AUTH - POS_CODE);
215197086Smav	MD5Update(&ctx, &h->out[POS_AUTH], LEN_AUTH);
216197086Smav	MD5Update(&ctx, &h->in[POS_ATTRS], len - POS_ATTRS);
21741118Sjdp	MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
21841118Sjdp	MD5Final(md5, &ctx);
219197086Smav	if (memcmp(&h->in[POS_AUTH], md5, sizeof md5) != 0)
22041118Sjdp		return 0;
22141118Sjdp
222128684Sru#ifdef WITH_SSL
223128684Sru	/*
224128684Sru	 * For non accounting responses check the message authenticator,
225128684Sru	 * if any.
226128684Sru	 */
227197086Smav	if (h->in[POS_CODE] != RAD_ACCOUNTING_RESPONSE) {
228128684Sru
229197086Smav		memcpy(resp, h->in, MSGSIZE);
230128684Sru		pos = POS_ATTRS;
231128684Sru
232128684Sru		/* Search and verify the Message-Authenticator */
233128684Sru		while (pos < len - 2) {
234128684Sru
235197086Smav			if (h->in[pos] == RAD_MESSAGE_AUTHENTIC) {
236128684Sru				/* zero fill the Message-Authenticator */
237128684Sru				memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH);
238128684Sru
239128684Sru				HMAC_CTX_init(&hctx);
240128684Sru				HMAC_Init(&hctx, srvp->secret,
241128684Sru				    strlen(srvp->secret), EVP_md5());
242197086Smav				HMAC_Update(&hctx, &h->in[POS_CODE],
243128684Sru				    POS_AUTH - POS_CODE);
244197086Smav				HMAC_Update(&hctx, &h->out[POS_AUTH],
245128684Sru				    LEN_AUTH);
246128684Sru				HMAC_Update(&hctx, &resp[POS_ATTRS],
247197086Smav				    h->in_len - POS_ATTRS);
248128684Sru				HMAC_Final(&hctx, md, &md_len);
249128684Sru				HMAC_CTX_cleanup(&hctx);
250128684Sru				HMAC_cleanup(&hctx);
251197086Smav				if (memcmp(md, &h->in[pos + 2],
252128684Sru				    MD5_DIGEST_LENGTH) != 0)
253128684Sru					return 0;
254128684Sru				break;
255128684Sru			}
256197086Smav			pos += h->in[pos + 1];
257128684Sru		}
258128684Sru	}
259128684Sru#endif
26041118Sjdp	return 1;
26141118Sjdp}
26241118Sjdp
263197086Smav/*
264197086Smav * Return true if the current request is valid for the specified server.
265197086Smav */
26641118Sjdpstatic int
267197086Smavis_valid_request(struct rad_handle *h)
268197086Smav{
269197086Smav	MD5_CTX ctx;
270197086Smav	unsigned char md5[MD5_DIGEST_LENGTH];
271197086Smav	const struct rad_server *srvp;
272197086Smav	int len;
273197086Smav#ifdef WITH_SSL
274197086Smav	HMAC_CTX hctx;
275197086Smav	u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE];
276197086Smav	u_int md_len;
277197086Smav	int pos;
278197086Smav#endif
279197086Smav
280197086Smav	srvp = &h->servers[h->srv];
281197086Smav
282197086Smav	/* Check the message length */
283197086Smav	if (h->in_len < POS_ATTRS)
284197086Smav		return (0);
285197086Smav	len = h->in[POS_LENGTH] << 8 | h->in[POS_LENGTH+1];
286197086Smav	if (len > h->in_len)
287197086Smav		return (0);
288197086Smav
289197086Smav	if (h->in[POS_CODE] != RAD_ACCESS_REQUEST) {
290197086Smav		uint32_t zeroes[4] = { 0, 0, 0, 0 };
291197086Smav		/* Check the request authenticator */
292197086Smav		MD5Init(&ctx);
293197086Smav		MD5Update(&ctx, &h->in[POS_CODE], POS_AUTH - POS_CODE);
294197086Smav		MD5Update(&ctx, zeroes, LEN_AUTH);
295197086Smav		MD5Update(&ctx, &h->in[POS_ATTRS], len - POS_ATTRS);
296197086Smav		MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
297197086Smav		MD5Final(md5, &ctx);
298197086Smav		if (memcmp(&h->in[POS_AUTH], md5, sizeof md5) != 0)
299197086Smav			return (0);
300197086Smav	}
301197086Smav
302197086Smav#ifdef WITH_SSL
303197086Smav	/* Search and verify the Message-Authenticator */
304197086Smav	pos = POS_ATTRS;
305197086Smav	while (pos < len - 2) {
306197086Smav		if (h->in[pos] == RAD_MESSAGE_AUTHENTIC) {
307197086Smav			memcpy(resp, h->in, MSGSIZE);
308197086Smav			/* zero fill the Request-Authenticator */
309197086Smav			if (h->in[POS_CODE] != RAD_ACCESS_REQUEST)
310197086Smav				memset(&resp[POS_AUTH], 0, LEN_AUTH);
311197086Smav			/* zero fill the Message-Authenticator */
312197086Smav			memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH);
313197086Smav
314197086Smav			HMAC_CTX_init(&hctx);
315197086Smav			HMAC_Init(&hctx, srvp->secret,
316197086Smav			    strlen(srvp->secret), EVP_md5());
317197086Smav			HMAC_Update(&hctx, resp, h->in_len);
318197086Smav			HMAC_Final(&hctx, md, &md_len);
319197086Smav			HMAC_CTX_cleanup(&hctx);
320197086Smav			HMAC_cleanup(&hctx);
321197086Smav			if (memcmp(md, &h->in[pos + 2],
322197086Smav			    MD5_DIGEST_LENGTH) != 0)
323197086Smav				return (0);
324197086Smav			break;
325197086Smav		}
326197086Smav		pos += h->in[pos + 1];
327197086Smav	}
328197086Smav#endif
329197086Smav	return (1);
330197086Smav}
331197086Smav
332197086Smavstatic int
33341118Sjdpput_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
33441118Sjdp{
33541118Sjdp	int padded_len;
33641118Sjdp	int pad_len;
33741118Sjdp
33841118Sjdp	if (h->pass_pos != 0) {
33941118Sjdp		generr(h, "Multiple User-Password attributes specified");
34041118Sjdp		return -1;
34141118Sjdp	}
34241118Sjdp	if (len > PASSSIZE)
34341118Sjdp		len = PASSSIZE;
34441118Sjdp	padded_len = len == 0 ? 16 : (len+15) & ~0xf;
34541118Sjdp	pad_len = padded_len - len;
34641118Sjdp
34741118Sjdp	/*
34841118Sjdp	 * Put in a place-holder attribute containing all zeros, and
34941118Sjdp	 * remember where it is so we can fill it in later.
35041118Sjdp	 */
35141118Sjdp	clear_password(h);
35241118Sjdp	put_raw_attr(h, type, h->pass, padded_len);
353197086Smav	h->pass_pos = h->out_len - padded_len;
35441118Sjdp
35541118Sjdp	/* Save the cleartext password, padded as necessary */
35641118Sjdp	memcpy(h->pass, value, len);
35741118Sjdp	h->pass_len = len;
35841118Sjdp	memset(h->pass + len, 0, pad_len);
35941118Sjdp	return 0;
36041118Sjdp}
36141118Sjdp
36241118Sjdpstatic int
36341118Sjdpput_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
36441118Sjdp{
36541118Sjdp	if (len > 253) {
36641118Sjdp		generr(h, "Attribute too long");
36741118Sjdp		return -1;
36841118Sjdp	}
369197086Smav	if (h->out_len + 2 + len > MSGSIZE) {
37041118Sjdp		generr(h, "Maximum message length exceeded");
37141118Sjdp		return -1;
37241118Sjdp	}
373197086Smav	h->out[h->out_len++] = type;
374197086Smav	h->out[h->out_len++] = len + 2;
375197086Smav	memcpy(&h->out[h->out_len], value, len);
376197086Smav	h->out_len += len;
37741118Sjdp	return 0;
37841118Sjdp}
37941118Sjdp
38041118Sjdpint
38141118Sjdprad_add_server(struct rad_handle *h, const char *host, int port,
38241118Sjdp    const char *secret, int timeout, int tries)
38341118Sjdp{
384243956Ssem    	struct in_addr bindto;
385243956Ssem	bindto.s_addr = INADDR_ANY;
386243956Ssem
387243956Ssem	return rad_add_server_ex(h, host, port, secret, timeout, tries,
388243956Ssem		DEAD_TIME, &bindto);
389243956Ssem}
390243956Ssem
391243956Ssemint
392243956Ssemrad_add_server_ex(struct rad_handle *h, const char *host, int port,
393243956Ssem    const char *secret, int timeout, int tries, int dead_time,
394243956Ssem    struct in_addr *bindto)
395243956Ssem{
39641118Sjdp	struct rad_server *srvp;
39741118Sjdp
39841118Sjdp	if (h->num_servers >= MAXSERVERS) {
39941118Sjdp		generr(h, "Too many RADIUS servers specified");
40041118Sjdp		return -1;
40141118Sjdp	}
40241118Sjdp	srvp = &h->servers[h->num_servers];
40341118Sjdp
40441118Sjdp	memset(&srvp->addr, 0, sizeof srvp->addr);
40541118Sjdp	srvp->addr.sin_len = sizeof srvp->addr;
40641118Sjdp	srvp->addr.sin_family = AF_INET;
40741118Sjdp	if (!inet_aton(host, &srvp->addr.sin_addr)) {
40841118Sjdp		struct hostent *hent;
40941118Sjdp
41041118Sjdp		if ((hent = gethostbyname(host)) == NULL) {
41141118Sjdp			generr(h, "%s: host not found", host);
41241118Sjdp			return -1;
41341118Sjdp		}
41441118Sjdp		memcpy(&srvp->addr.sin_addr, hent->h_addr,
41541118Sjdp		    sizeof srvp->addr.sin_addr);
41641118Sjdp	}
41741118Sjdp	if (port != 0)
418128684Sru		srvp->addr.sin_port = htons((u_short)port);
41941118Sjdp	else {
42041118Sjdp		struct servent *sent;
42141118Sjdp
42252709Sjdp		if (h->type == RADIUS_AUTH)
42352709Sjdp			srvp->addr.sin_port =
42452709Sjdp			    (sent = getservbyname("radius", "udp")) != NULL ?
42552709Sjdp				sent->s_port : htons(RADIUS_PORT);
42652709Sjdp		else
42752709Sjdp			srvp->addr.sin_port =
42852709Sjdp			    (sent = getservbyname("radacct", "udp")) != NULL ?
42952709Sjdp				sent->s_port : htons(RADACCT_PORT);
43041118Sjdp	}
43141118Sjdp	if ((srvp->secret = strdup(secret)) == NULL) {
43241118Sjdp		generr(h, "Out of memory");
43341118Sjdp		return -1;
43441118Sjdp	}
43541118Sjdp	srvp->timeout = timeout;
43641118Sjdp	srvp->max_tries = tries;
43741118Sjdp	srvp->num_tries = 0;
438243956Ssem	srvp->is_dead = 0;
439243956Ssem	srvp->dead_time = dead_time;
440243956Ssem	srvp->next_probe = 0;
441243956Ssem	srvp->bindto = bindto->s_addr;
44241118Sjdp	h->num_servers++;
44341118Sjdp	return 0;
44441118Sjdp}
44541118Sjdp
44641118Sjdpvoid
44741118Sjdprad_close(struct rad_handle *h)
44841118Sjdp{
44941118Sjdp	int srv;
45041118Sjdp
45141118Sjdp	if (h->fd != -1)
45241118Sjdp		close(h->fd);
45341118Sjdp	for (srv = 0;  srv < h->num_servers;  srv++) {
45441118Sjdp		memset(h->servers[srv].secret, 0,
45541118Sjdp		    strlen(h->servers[srv].secret));
45641118Sjdp		free(h->servers[srv].secret);
45741118Sjdp	}
45841118Sjdp	clear_password(h);
45941118Sjdp	free(h);
46041118Sjdp}
46141118Sjdp
462243956Ssemvoid
463243956Ssemrad_bind_to(struct rad_handle *h, in_addr_t addr)
464243956Ssem{
465243956Ssem
466243956Ssem	h->bindto = addr;
467243956Ssem}
468243956Ssem
46941118Sjdpint
47041118Sjdprad_config(struct rad_handle *h, const char *path)
47141118Sjdp{
47241118Sjdp	FILE *fp;
47341118Sjdp	char buf[MAXCONFLINE];
47441118Sjdp	int linenum;
47541118Sjdp	int retval;
47641118Sjdp
47741118Sjdp	if (path == NULL)
47841118Sjdp		path = PATH_RADIUS_CONF;
47941118Sjdp	if ((fp = fopen(path, "r")) == NULL) {
48041118Sjdp		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
48141118Sjdp		return -1;
48241118Sjdp	}
48341118Sjdp	retval = 0;
48441118Sjdp	linenum = 0;
48541118Sjdp	while (fgets(buf, sizeof buf, fp) != NULL) {
48641118Sjdp		int len;
487243964Sdelphij		char *fields[MAX_FIELDS];
48841118Sjdp		int nfields;
48941118Sjdp		char msg[ERRSIZE];
49052709Sjdp		char *type;
49165222Sache		char *host, *res;
49241118Sjdp		char *port_str;
49341118Sjdp		char *secret;
49441118Sjdp		char *timeout_str;
49541118Sjdp		char *maxtries_str;
496243956Ssem		char *dead_time_str;
497243956Ssem		char *bindto_str;
49841118Sjdp		char *end;
49952709Sjdp		char *wanttype;
50041118Sjdp		unsigned long timeout;
50141118Sjdp		unsigned long maxtries;
502243956Ssem		unsigned long dead_time;
50341118Sjdp		int port;
504243956Ssem		struct in_addr bindto;
50552709Sjdp		int i;
50641118Sjdp
50741118Sjdp		linenum++;
50841118Sjdp		len = strlen(buf);
50941118Sjdp		/* We know len > 0, else fgets would have returned NULL. */
51041118Sjdp		if (buf[len - 1] != '\n') {
51141118Sjdp			if (len == sizeof buf - 1)
51241118Sjdp				generr(h, "%s:%d: line too long", path,
51341118Sjdp				    linenum);
51441118Sjdp			else
51541118Sjdp				generr(h, "%s:%d: missing newline", path,
51641118Sjdp				    linenum);
51741118Sjdp			retval = -1;
51841118Sjdp			break;
51941118Sjdp		}
52041118Sjdp		buf[len - 1] = '\0';
52141118Sjdp
52241118Sjdp		/* Extract the fields from the line. */
523243956Ssem		nfields = split(buf, fields, MAX_FIELDS, msg, sizeof msg);
52441118Sjdp		if (nfields == -1) {
52541118Sjdp			generr(h, "%s:%d: %s", path, linenum, msg);
52641118Sjdp			retval = -1;
52741118Sjdp			break;
52841118Sjdp		}
52941118Sjdp		if (nfields == 0)
53041118Sjdp			continue;
53152709Sjdp		/*
53252709Sjdp		 * The first field should contain "auth" or "acct" for
53352709Sjdp		 * authentication or accounting, respectively.  But older
53452709Sjdp		 * versions of the file didn't have that field.  Default
53552709Sjdp		 * it to "auth" for backward compatibility.
53652709Sjdp		 */
53752709Sjdp		if (strcmp(fields[0], "auth") != 0 &&
53852709Sjdp		    strcmp(fields[0], "acct") != 0) {
539243956Ssem			if (nfields >= MAX_FIELDS) {
54052709Sjdp				generr(h, "%s:%d: invalid service type", path,
54152709Sjdp				    linenum);
54252709Sjdp				retval = -1;
54352709Sjdp				break;
54452709Sjdp			}
54552709Sjdp			nfields++;
54652709Sjdp			for (i = nfields;  --i > 0;  )
54752709Sjdp				fields[i] = fields[i - 1];
54852709Sjdp			fields[0] = "auth";
54952709Sjdp		}
55052709Sjdp		if (nfields < 3) {
55141118Sjdp			generr(h, "%s:%d: missing shared secret", path,
55241118Sjdp			    linenum);
55341118Sjdp			retval = -1;
55441118Sjdp			break;
55541118Sjdp		}
55652709Sjdp		type = fields[0];
55752709Sjdp		host = fields[1];
55852709Sjdp		secret = fields[2];
55952709Sjdp		timeout_str = fields[3];
56052709Sjdp		maxtries_str = fields[4];
561243956Ssem		dead_time_str = fields[5];
562243956Ssem		bindto_str = fields[6];
56341118Sjdp
56452709Sjdp		/* Ignore the line if it is for the wrong service type. */
56552709Sjdp		wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
56652709Sjdp		if (strcmp(type, wanttype) != 0)
56752709Sjdp			continue;
56852709Sjdp
56941118Sjdp		/* Parse and validate the fields. */
57065222Sache		res = host;
57165222Sache		host = strsep(&res, ":");
57265222Sache		port_str = strsep(&res, ":");
57341118Sjdp		if (port_str != NULL) {
57441118Sjdp			port = strtoul(port_str, &end, 10);
57541118Sjdp			if (*end != '\0') {
57641118Sjdp				generr(h, "%s:%d: invalid port", path,
57741118Sjdp				    linenum);
57841118Sjdp				retval = -1;
57941118Sjdp				break;
58041118Sjdp			}
58141118Sjdp		} else
58241118Sjdp			port = 0;
58341118Sjdp		if (timeout_str != NULL) {
58441118Sjdp			timeout = strtoul(timeout_str, &end, 10);
58541118Sjdp			if (*end != '\0') {
58641118Sjdp				generr(h, "%s:%d: invalid timeout", path,
58741118Sjdp				    linenum);
58841118Sjdp				retval = -1;
58941118Sjdp				break;
59041118Sjdp			}
59141118Sjdp		} else
59241118Sjdp			timeout = TIMEOUT;
59341118Sjdp		if (maxtries_str != NULL) {
59441118Sjdp			maxtries = strtoul(maxtries_str, &end, 10);
59541118Sjdp			if (*end != '\0') {
59641118Sjdp				generr(h, "%s:%d: invalid maxtries", path,
59741118Sjdp				    linenum);
59841118Sjdp				retval = -1;
59941118Sjdp				break;
60041118Sjdp			}
60141118Sjdp		} else
60241118Sjdp			maxtries = MAXTRIES;
60341118Sjdp
604243956Ssem		if (dead_time_str != NULL) {
605243956Ssem			dead_time = strtoul(dead_time_str, &end, 10);
606243956Ssem			if (*end != '\0') {
607243956Ssem				generr(h, "%s:%d: invalid dead_time", path,
608243956Ssem				    linenum);
609243956Ssem				retval = -1;
610243956Ssem				break;
611243956Ssem			}
612243956Ssem		} else
613243956Ssem		    	dead_time = DEAD_TIME;
614243956Ssem
615243956Ssem		if (bindto_str != NULL) {
616243956Ssem		    	bindto.s_addr = inet_addr(bindto_str);
617243956Ssem			if (bindto.s_addr == INADDR_NONE) {
618243956Ssem				generr(h, "%s:%d: invalid bindto", path,
619243956Ssem				    linenum);
620243956Ssem				retval = -1;
621243956Ssem				break;
622243956Ssem			}
623243956Ssem		} else
624243956Ssem		    	bindto.s_addr = INADDR_ANY;
625243956Ssem
626243956Ssem		if (rad_add_server_ex(h, host, port, secret, timeout, maxtries,
627243956Ssem			    dead_time, &bindto) == -1) {
62841118Sjdp			strcpy(msg, h->errmsg);
62941118Sjdp			generr(h, "%s:%d: %s", path, linenum, msg);
63041118Sjdp			retval = -1;
63141118Sjdp			break;
63241118Sjdp		}
63341118Sjdp	}
63441118Sjdp	/* Clear out the buffer to wipe a possible copy of a shared secret */
63541118Sjdp	memset(buf, 0, sizeof buf);
63641118Sjdp	fclose(fp);
63741118Sjdp	return retval;
63841118Sjdp}
63941118Sjdp
64043662Sbrian/*
64143662Sbrian * rad_init_send_request() must have previously been called.
64243662Sbrian * Returns:
64343662Sbrian *   0     The application should select on *fd with a timeout of tv before
64443662Sbrian *         calling rad_continue_send_request again.
64543662Sbrian *   < 0   Failure
64643662Sbrian *   > 0   Success
64743662Sbrian */
64841118Sjdpint
64943662Sbrianrad_continue_send_request(struct rad_handle *h, int selected, int *fd,
65043662Sbrian                          struct timeval *tv)
65143662Sbrian{
652243956Ssem	int n, cur_srv;
653243956Ssem	time_t now;
654243956Ssem	struct sockaddr_in sin;
65543662Sbrian
656197086Smav	if (h->type == RADIUS_SERVER) {
657197086Smav		generr(h, "denied function call");
658197086Smav		return (-1);
659197086Smav	}
66043662Sbrian	if (selected) {
66143662Sbrian		struct sockaddr_in from;
662168341Skan		socklen_t fromlen;
66343662Sbrian
66443662Sbrian		fromlen = sizeof from;
665197086Smav		h->in_len = recvfrom(h->fd, h->in,
66643662Sbrian		    MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
667197086Smav		if (h->in_len == -1) {
66843662Sbrian			generr(h, "recvfrom: %s", strerror(errno));
66943662Sbrian			return -1;
67043662Sbrian		}
67143662Sbrian		if (is_valid_response(h, h->srv, &from)) {
672197086Smav			h->in_len = h->in[POS_LENGTH] << 8 |
673197086Smav			    h->in[POS_LENGTH+1];
674197086Smav			h->in_pos = POS_ATTRS;
675197086Smav			return h->in[POS_CODE];
67643662Sbrian		}
67743662Sbrian	}
67843662Sbrian
67943662Sbrian	/*
68043662Sbrian         * Scan round-robin to the next server that has some
68143662Sbrian         * tries left.  There is guaranteed to be one, or we
68243662Sbrian         * would have exited this loop by now.
68343662Sbrian	 */
684243956Ssem	cur_srv = h->srv;
685243956Ssem	now = time(NULL);
686243956Ssem	if (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries) {
687243956Ssem		/* Set next probe time for this server */
688243956Ssem		if (h->servers[h->srv].dead_time) {
689243956Ssem			h->servers[h->srv].is_dead = 1;
690243956Ssem			h->servers[h->srv].next_probe = now +
691243956Ssem			    h->servers[h->srv].dead_time;
692243956Ssem		}
693243956Ssem		do {
694243956Ssem		    	h->srv++;
695243956Ssem			if (h->srv >= h->num_servers)
696243956Ssem				h->srv = 0;
697243956Ssem			if (h->servers[h->srv].is_dead == 0)
698243956Ssem			    	break;
699243956Ssem			if (h->servers[h->srv].dead_time &&
700243956Ssem			    h->servers[h->srv].next_probe <= now) {
701243956Ssem			    	h->servers[h->srv].is_dead = 0;
702243956Ssem				h->servers[h->srv].num_tries = 0;
703243956Ssem				break;
704243956Ssem			}
705243956Ssem		} while (h->srv != cur_srv);
70643662Sbrian
707243956Ssem		if (h->srv == cur_srv) {
708243956Ssem			generr(h, "No valid RADIUS responses received");
709243956Ssem			return (-1);
710243956Ssem		}
711243956Ssem	}
712243956Ssem
713243956Ssem	/* Rebind */
714243956Ssem	if (h->bindto != h->servers[h->srv].bindto) {
715243956Ssem	    	h->bindto = h->servers[h->srv].bindto;
716243956Ssem		close(h->fd);
717243956Ssem		if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
718243956Ssem			generr(h, "Cannot create socket: %s", strerror(errno));
719243956Ssem			return -1;
720243956Ssem		}
721243956Ssem		memset(&sin, 0, sizeof sin);
722243956Ssem		sin.sin_len = sizeof sin;
723243956Ssem		sin.sin_family = AF_INET;
724243956Ssem		sin.sin_addr.s_addr = h->bindto;
725243956Ssem		sin.sin_port = 0;
726243956Ssem		if (bind(h->fd, (const struct sockaddr *)&sin,
727243956Ssem		    sizeof sin) == -1) {
728243956Ssem			generr(h, "bind: %s", strerror(errno));
729243956Ssem			close(h->fd);
730243956Ssem			h->fd = -1;
731243956Ssem			return (-1);
732243956Ssem		}
733243956Ssem	}
734243956Ssem
735197086Smav	if (h->out[POS_CODE] == RAD_ACCESS_REQUEST) {
73652709Sjdp		/* Insert the scrambled password into the request */
73752709Sjdp		if (h->pass_pos != 0)
73852709Sjdp			insert_scrambled_password(h, h->srv);
739197086Smav	}
740197086Smav	insert_message_authenticator(h, 0);
741243956Ssem
742197086Smav	if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) {
743197086Smav		/* Insert the request authenticator into the request */
744243956Ssem		memset(&h->out[POS_AUTH], 0, LEN_AUTH);
745243956Ssem		insert_request_authenticator(h, 0);
746197086Smav	}
74743662Sbrian
74843662Sbrian	/* Send the request */
749197086Smav	n = sendto(h->fd, h->out, h->out_len, 0,
75043662Sbrian	    (const struct sockaddr *)&h->servers[h->srv].addr,
75143662Sbrian	    sizeof h->servers[h->srv].addr);
752197621Smav	if (n != h->out_len)
753197621Smav		tv->tv_sec = 1; /* Do not wait full timeout if send failed. */
754197621Smav	else
755197621Smav		tv->tv_sec = h->servers[h->srv].timeout;
75643662Sbrian	h->servers[h->srv].num_tries++;
75743662Sbrian	tv->tv_usec = 0;
75843662Sbrian	*fd = h->fd;
75943662Sbrian
76043662Sbrian	return 0;
76143662Sbrian}
76243662Sbrian
76343662Sbrianint
764197086Smavrad_receive_request(struct rad_handle *h)
765197086Smav{
766197086Smav	struct sockaddr_in from;
767197086Smav	socklen_t fromlen;
768197086Smav	int n;
769197086Smav
770197086Smav	if (h->type != RADIUS_SERVER) {
771197086Smav		generr(h, "denied function call");
772197086Smav		return (-1);
773197086Smav	}
774197086Smav	h->srv = -1;
775197086Smav	fromlen = sizeof(from);
776197086Smav	h->in_len = recvfrom(h->fd, h->in,
777197086Smav	    MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
778197086Smav	if (h->in_len == -1) {
779197086Smav		generr(h, "recvfrom: %s", strerror(errno));
780197086Smav		return (-1);
781197086Smav	}
782197086Smav	for (n = 0; n < h->num_servers; n++) {
783197086Smav		if (h->servers[n].addr.sin_addr.s_addr == from.sin_addr.s_addr) {
784197086Smav			h->servers[n].addr.sin_port = from.sin_port;
785197086Smav			h->srv = n;
786197086Smav			break;
787197086Smav		}
788197086Smav	}
789197086Smav	if (h->srv == -1)
790197086Smav		return (-2);
791197086Smav	if (is_valid_request(h)) {
792197086Smav		h->in_len = h->in[POS_LENGTH] << 8 |
793197086Smav		    h->in[POS_LENGTH+1];
794197086Smav		h->in_pos = POS_ATTRS;
795197086Smav		return (h->in[POS_CODE]);
796197086Smav	}
797197086Smav	return (-3);
798197086Smav}
799197086Smav
800197086Smavint
801197086Smavrad_send_response(struct rad_handle *h)
802197086Smav{
803197086Smav	int n;
804197086Smav
805197086Smav	if (h->type != RADIUS_SERVER) {
806197086Smav		generr(h, "denied function call");
807197086Smav		return (-1);
808197086Smav	}
809197086Smav	/* Fill in the length field in the message */
810197086Smav	h->out[POS_LENGTH] = h->out_len >> 8;
811197086Smav	h->out[POS_LENGTH+1] = h->out_len;
812197086Smav
813197086Smav	insert_message_authenticator(h,
814197086Smav	    (h->in[POS_CODE] == RAD_ACCESS_REQUEST) ? 1 : 0);
815197086Smav	insert_request_authenticator(h, 1);
816197086Smav
817197086Smav	/* Send the request */
818197086Smav	n = sendto(h->fd, h->out, h->out_len, 0,
819197086Smav	    (const struct sockaddr *)&h->servers[h->srv].addr,
820197086Smav	    sizeof h->servers[h->srv].addr);
821197086Smav	if (n != h->out_len) {
822197086Smav		if (n == -1)
823197086Smav			generr(h, "sendto: %s", strerror(errno));
824197086Smav		else
825197086Smav			generr(h, "sendto: short write");
826197086Smav		return -1;
827197086Smav	}
828197086Smav
829197086Smav	return 0;
830197086Smav}
831197086Smav
832197086Smavint
83341118Sjdprad_create_request(struct rad_handle *h, int code)
83441118Sjdp{
83541118Sjdp	int i;
83641118Sjdp
837197086Smav	if (h->type == RADIUS_SERVER) {
838197086Smav		generr(h, "denied function call");
839197086Smav		return (-1);
84041118Sjdp	}
841243956Ssem	if (h->num_servers == 0) {
842243956Ssem	    	generr(h, "No RADIUS servers specified");
843243956Ssem		return (-1);
844243956Ssem	}
845197086Smav	h->out[POS_CODE] = code;
846197086Smav	h->out[POS_IDENT] = ++h->ident;
847197086Smav	if (code == RAD_ACCESS_REQUEST) {
848197086Smav		/* Create a random authenticator */
849197086Smav		for (i = 0;  i < LEN_AUTH;  i += 2) {
850197086Smav			long r;
851197086Smav			r = random();
852197086Smav			h->out[POS_AUTH+i] = (u_char)r;
853197086Smav			h->out[POS_AUTH+i+1] = (u_char)(r >> 8);
854197086Smav		}
855197086Smav	} else
856197086Smav		memset(&h->out[POS_AUTH], 0, LEN_AUTH);
857197086Smav	h->out_len = POS_ATTRS;
85841118Sjdp	clear_password(h);
859197086Smav	h->authentic_pos = 0;
860197086Smav	h->out_created = 1;
86141118Sjdp	return 0;
86241118Sjdp}
86341118Sjdp
864197086Smavint
865197086Smavrad_create_response(struct rad_handle *h, int code)
866197086Smav{
867197086Smav
868197086Smav	if (h->type != RADIUS_SERVER) {
869197086Smav		generr(h, "denied function call");
870197086Smav		return (-1);
871197086Smav	}
872197086Smav	h->out[POS_CODE] = code;
873197086Smav	h->out[POS_IDENT] = h->in[POS_IDENT];
874197086Smav	memset(&h->out[POS_AUTH], 0, LEN_AUTH);
875197086Smav	h->out_len = POS_ATTRS;
876197086Smav	clear_password(h);
877197086Smav	h->authentic_pos = 0;
878197086Smav	h->out_created = 1;
879197086Smav	return 0;
880197086Smav}
881197086Smav
88241118Sjdpstruct in_addr
88341118Sjdprad_cvt_addr(const void *data)
88441118Sjdp{
88541118Sjdp	struct in_addr value;
88641118Sjdp
88741118Sjdp	memcpy(&value.s_addr, data, sizeof value.s_addr);
88841118Sjdp	return value;
88941118Sjdp}
89041118Sjdp
891243956Ssemstruct in6_addr
892243956Ssemrad_cvt_addr6(const void *data)
893243956Ssem{
894243956Ssem	struct in6_addr value;
895243956Ssem
896243956Ssem	memcpy(&value.s6_addr, data, sizeof value.s6_addr);
897243956Ssem	return value;
898243956Ssem}
899243956Ssem
90041118Sjdpu_int32_t
90141118Sjdprad_cvt_int(const void *data)
90241118Sjdp{
90341118Sjdp	u_int32_t value;
90441118Sjdp
90541118Sjdp	memcpy(&value, data, sizeof value);
90641118Sjdp	return ntohl(value);
90741118Sjdp}
90841118Sjdp
90941118Sjdpchar *
91041118Sjdprad_cvt_string(const void *data, size_t len)
91141118Sjdp{
91241118Sjdp	char *s;
91341118Sjdp
91441118Sjdp	s = malloc(len + 1);
91541118Sjdp	if (s != NULL) {
91641118Sjdp		memcpy(s, data, len);
91741118Sjdp		s[len] = '\0';
91841118Sjdp	}
91941118Sjdp	return s;
92041118Sjdp}
92141118Sjdp
92241118Sjdp/*
92341118Sjdp * Returns the attribute type.  If none are left, returns 0.  On failure,
92441118Sjdp * returns -1.
92541118Sjdp */
92641118Sjdpint
92741118Sjdprad_get_attr(struct rad_handle *h, const void **value, size_t *len)
92841118Sjdp{
92941118Sjdp	int type;
93041118Sjdp
931197086Smav	if (h->in_pos >= h->in_len)
93241118Sjdp		return 0;
933197086Smav	if (h->in_pos + 2 > h->in_len) {
93441118Sjdp		generr(h, "Malformed attribute in response");
93541118Sjdp		return -1;
93641118Sjdp	}
937197086Smav	type = h->in[h->in_pos++];
938197086Smav	*len = h->in[h->in_pos++] - 2;
939197086Smav	if (h->in_pos + (int)*len > h->in_len) {
94041118Sjdp		generr(h, "Malformed attribute in response");
94141118Sjdp		return -1;
94241118Sjdp	}
943197086Smav	*value = &h->in[h->in_pos];
944197086Smav	h->in_pos += *len;
94541118Sjdp	return type;
94641118Sjdp}
94741118Sjdp
94841118Sjdp/*
94943662Sbrian * Returns -1 on error, 0 to indicate no event and >0 for success
95043662Sbrian */
95143662Sbrianint
95243662Sbrianrad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
95343662Sbrian{
95443662Sbrian	int srv;
955243956Ssem	time_t now;
956243956Ssem	struct sockaddr_in sin;
95743662Sbrian
958197086Smav	if (h->type == RADIUS_SERVER) {
959197086Smav		generr(h, "denied function call");
960197086Smav		return (-1);
961197086Smav	}
96243662Sbrian	/* Make sure we have a socket to use */
96343662Sbrian	if (h->fd == -1) {
96443662Sbrian		if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
96543662Sbrian			generr(h, "Cannot create socket: %s", strerror(errno));
96643662Sbrian			return -1;
96743662Sbrian		}
96843662Sbrian		memset(&sin, 0, sizeof sin);
96943662Sbrian		sin.sin_len = sizeof sin;
97043662Sbrian		sin.sin_family = AF_INET;
971228730Smelifaro		sin.sin_addr.s_addr = h->bindto;
97243662Sbrian		sin.sin_port = htons(0);
97343662Sbrian		if (bind(h->fd, (const struct sockaddr *)&sin,
97443662Sbrian		    sizeof sin) == -1) {
97543662Sbrian			generr(h, "bind: %s", strerror(errno));
97643662Sbrian			close(h->fd);
97743662Sbrian			h->fd = -1;
97843662Sbrian			return -1;
97943662Sbrian		}
98043662Sbrian	}
98143662Sbrian
982197086Smav	if (h->out[POS_CODE] != RAD_ACCESS_REQUEST) {
98352709Sjdp		/* Make sure no password given */
98452709Sjdp		if (h->pass_pos || h->chap_pass) {
985128684Sru			generr(h, "User or Chap Password"
986128684Sru			    " in accounting request");
98752709Sjdp			return -1;
98852709Sjdp		}
98952709Sjdp	} else {
990128684Sru		if (h->eap_msg == 0) {
991128684Sru			/* Make sure the user gave us a password */
992128684Sru			if (h->pass_pos == 0 && !h->chap_pass) {
993128684Sru				generr(h, "No User or Chap Password"
994128684Sru				    " attributes given");
995128684Sru				return -1;
996128684Sru			}
997128684Sru			if (h->pass_pos != 0 && h->chap_pass) {
998128684Sru				generr(h, "Both User and Chap Password"
999128684Sru				    " attributes given");
1000128684Sru				return -1;
1001128684Sru			}
100252709Sjdp		}
100343662Sbrian	}
100443662Sbrian
100543662Sbrian	/* Fill in the length field in the message */
1006197086Smav	h->out[POS_LENGTH] = h->out_len >> 8;
1007197086Smav	h->out[POS_LENGTH+1] = h->out_len;
100843662Sbrian
1009243956Ssem	h->srv = 0;
1010243956Ssem	now = time(NULL);
1011243956Ssem	for (srv = 0;  srv < h->num_servers;  srv++)
1012243956Ssem		h->servers[srv].num_tries = 0;
1013243956Ssem	/* Find a first good server. */
101443662Sbrian	for (srv = 0;  srv < h->num_servers;  srv++) {
1015243956Ssem		if (h->servers[srv].is_dead == 0)
1016243956Ssem			break;
1017243956Ssem		if (h->servers[srv].dead_time &&
1018243956Ssem		    h->servers[srv].next_probe <= now) {
1019243956Ssem		    	h->servers[srv].is_dead = 0;
1020243956Ssem			break;
1021243956Ssem		}
1022243956Ssem		h->srv++;
102343662Sbrian	}
1024243956Ssem
1025243956Ssem	/* If all servers was dead on the last probe, try from beginning */
1026243956Ssem	if (h->srv == h->num_servers) {
1027243956Ssem		for (srv = 0;  srv < h->num_servers;  srv++) {
1028243956Ssem		    	h->servers[srv].is_dead = 0;
1029243956Ssem			h->servers[srv].next_probe = 0;
1030243956Ssem		}
1031243956Ssem		h->srv = 0;
103243662Sbrian	}
103343662Sbrian
103443662Sbrian	return rad_continue_send_request(h, 0, fd, tv);
103543662Sbrian}
103643662Sbrian
103743662Sbrian/*
103841118Sjdp * Create and initialize a rad_handle structure, and return it to the
103941118Sjdp * caller.  Can fail only if the necessary memory cannot be allocated.
104041118Sjdp * In that case, it returns NULL.
104141118Sjdp */
104241118Sjdpstruct rad_handle *
104352709Sjdprad_auth_open(void)
104441118Sjdp{
104541118Sjdp	struct rad_handle *h;
104641118Sjdp
104741118Sjdp	h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
104841118Sjdp	if (h != NULL) {
104941118Sjdp		srandomdev();
105041118Sjdp		h->fd = -1;
105141118Sjdp		h->num_servers = 0;
105241118Sjdp		h->ident = random();
105341118Sjdp		h->errmsg[0] = '\0';
105441118Sjdp		memset(h->pass, 0, sizeof h->pass);
105541118Sjdp		h->pass_len = 0;
105641118Sjdp		h->pass_pos = 0;
105743400Sbrian		h->chap_pass = 0;
1058128684Sru		h->authentic_pos = 0;
105952709Sjdp		h->type = RADIUS_AUTH;
1060197086Smav		h->out_created = 0;
1061128684Sru		h->eap_msg = 0;
1062243956Ssem		h->bindto = INADDR_ANY;
106341118Sjdp	}
106441118Sjdp	return h;
106541118Sjdp}
106641118Sjdp
106752709Sjdpstruct rad_handle *
106852709Sjdprad_acct_open(void)
106952709Sjdp{
107052709Sjdp	struct rad_handle *h;
107152709Sjdp
107252709Sjdp	h = rad_open();
107352709Sjdp	if (h != NULL)
107452709Sjdp	        h->type = RADIUS_ACCT;
107552709Sjdp	return h;
107652709Sjdp}
107752709Sjdp
107852709Sjdpstruct rad_handle *
1079197086Smavrad_server_open(int fd)
1080197086Smav{
1081197086Smav	struct rad_handle *h;
1082197086Smav
1083197086Smav	h = rad_open();
1084197086Smav	if (h != NULL) {
1085197086Smav	        h->type = RADIUS_SERVER;
1086197086Smav	        h->fd = fd;
1087197086Smav	}
1088197086Smav	return h;
1089197086Smav}
1090197086Smav
1091197086Smavstruct rad_handle *
109252709Sjdprad_open(void)
109352709Sjdp{
109452709Sjdp    return rad_auth_open();
109552709Sjdp}
109652709Sjdp
109741118Sjdpint
109841118Sjdprad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
109941118Sjdp{
110041118Sjdp	return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
110141118Sjdp}
110241118Sjdp
110341118Sjdpint
1104243956Ssemrad_put_addr6(struct rad_handle *h, int type, struct in6_addr addr)
1105243956Ssem{
1106243956Ssem
1107243956Ssem	return rad_put_attr(h, type, &addr.s6_addr, sizeof addr.s6_addr);
1108243956Ssem}
1109243956Ssem
1110243956Ssemint
111141118Sjdprad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
111241118Sjdp{
111343400Sbrian	int result;
111443400Sbrian
1115197086Smav	if (!h->out_created) {
1116128684Sru		generr(h, "Please call rad_create_request()"
1117128684Sru		    " before putting attributes");
1118128684Sru		return -1;
1119128684Sru	}
1120128684Sru
1121197086Smav	if (h->out[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
1122128684Sru		if (type == RAD_EAP_MESSAGE) {
1123128684Sru			generr(h, "EAP-Message attribute is not valid"
1124128684Sru			    " in accounting requests");
1125128684Sru			return -1;
1126128684Sru		}
1127128684Sru	}
1128128684Sru
1129128684Sru	/*
1130128684Sru	 * When proxying EAP Messages, the Message Authenticator
1131128684Sru	 * MUST be present; see RFC 3579.
1132128684Sru	 */
1133128684Sru	if (type == RAD_EAP_MESSAGE) {
1134128684Sru		if (rad_put_message_authentic(h) == -1)
1135128684Sru			return -1;
1136128684Sru	}
1137128684Sru
1138128684Sru	if (type == RAD_USER_PASSWORD) {
113943400Sbrian		result = put_password_attr(h, type, value, len);
1140128684Sru	} else if (type == RAD_MESSAGE_AUTHENTIC) {
1141128684Sru		result = rad_put_message_authentic(h);
1142128684Sru	} else {
114343400Sbrian		result = put_raw_attr(h, type, value, len);
1144128684Sru		if (result == 0) {
1145128684Sru			if (type == RAD_CHAP_PASSWORD)
1146128684Sru				h->chap_pass = 1;
1147128684Sru			else if (type == RAD_EAP_MESSAGE)
1148128684Sru				h->eap_msg = 1;
1149128684Sru		}
115043400Sbrian	}
115143400Sbrian
115243400Sbrian	return result;
115341118Sjdp}
115441118Sjdp
115541118Sjdpint
115641118Sjdprad_put_int(struct rad_handle *h, int type, u_int32_t value)
115741118Sjdp{
115841118Sjdp	u_int32_t nvalue;
115941118Sjdp
116041118Sjdp	nvalue = htonl(value);
116141118Sjdp	return rad_put_attr(h, type, &nvalue, sizeof nvalue);
116241118Sjdp}
116341118Sjdp
116441118Sjdpint
116541118Sjdprad_put_string(struct rad_handle *h, int type, const char *str)
116641118Sjdp{
116741118Sjdp	return rad_put_attr(h, type, str, strlen(str));
116841118Sjdp}
116941118Sjdp
1170128684Sruint
1171128684Srurad_put_message_authentic(struct rad_handle *h)
1172128684Sru{
1173128684Sru#ifdef WITH_SSL
1174128684Sru	u_char md_zero[MD5_DIGEST_LENGTH];
1175128684Sru
1176197086Smav	if (h->out[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
1177128684Sru		generr(h, "Message-Authenticator is not valid"
1178128684Sru		    " in accounting requests");
1179128684Sru		return -1;
1180128684Sru	}
1181128684Sru
1182128684Sru	if (h->authentic_pos == 0) {
1183197086Smav		h->authentic_pos = h->out_len;
1184128684Sru		memset(md_zero, 0, sizeof(md_zero));
1185128684Sru		return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero,
1186128684Sru		    sizeof(md_zero)));
1187128684Sru	}
1188128684Sru	return 0;
1189128684Sru#else
1190128684Sru	generr(h, "Message Authenticator not supported,"
1191128684Sru	    " please recompile libradius with SSL support");
1192128684Sru	return -1;
1193128684Sru#endif
1194128684Sru}
1195128684Sru
119641118Sjdp/*
119741118Sjdp * Returns the response type code on success, or -1 on failure.
119841118Sjdp */
119941118Sjdpint
120041118Sjdprad_send_request(struct rad_handle *h)
120141118Sjdp{
120243662Sbrian	struct timeval timelimit;
120343662Sbrian	struct timeval tv;
120443662Sbrian	int fd;
120541118Sjdp	int n;
120641118Sjdp
120743662Sbrian	n = rad_init_send_request(h, &fd, &tv);
120841118Sjdp
120943662Sbrian	if (n != 0)
121043662Sbrian		return n;
121141118Sjdp
121243662Sbrian	gettimeofday(&timelimit, NULL);
121343662Sbrian	timeradd(&tv, &timelimit, &timelimit);
121441118Sjdp
121543662Sbrian	for ( ; ; ) {
121643662Sbrian		fd_set readfds;
121743400Sbrian
121843662Sbrian		FD_ZERO(&readfds);
121943662Sbrian		FD_SET(fd, &readfds);
122041118Sjdp
122143662Sbrian		n = select(fd + 1, &readfds, NULL, NULL, &tv);
122241118Sjdp
122343662Sbrian		if (n == -1) {
122443662Sbrian			generr(h, "select: %s", strerror(errno));
122541118Sjdp			return -1;
122641118Sjdp		}
122741118Sjdp
122843662Sbrian		if (!FD_ISSET(fd, &readfds)) {
122941118Sjdp			/* Compute a new timeout */
123041118Sjdp			gettimeofday(&tv, NULL);
123141118Sjdp			timersub(&timelimit, &tv, &tv);
123243662Sbrian			if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
123343662Sbrian				/* Continue the select */
123443662Sbrian				continue;
123541118Sjdp		}
123643662Sbrian
123743662Sbrian		n = rad_continue_send_request(h, n, &fd, &tv);
123843662Sbrian
123943662Sbrian		if (n != 0)
124043662Sbrian			return n;
124143662Sbrian
124243662Sbrian		gettimeofday(&timelimit, NULL);
124343662Sbrian		timeradd(&tv, &timelimit, &timelimit);
124441118Sjdp	}
124541118Sjdp}
124641118Sjdp
124741118Sjdpconst char *
124841118Sjdprad_strerror(struct rad_handle *h)
124941118Sjdp{
125041118Sjdp	return h->errmsg;
125141118Sjdp}
125241118Sjdp
125341118Sjdp/*
125441118Sjdp * Destructively split a string into fields separated by white space.
125541118Sjdp * `#' at the beginning of a field begins a comment that extends to the
125641118Sjdp * end of the string.  Fields may be quoted with `"'.  Inside quoted
125741118Sjdp * strings, the backslash escapes `\"' and `\\' are honored.
125841118Sjdp *
125941118Sjdp * Pointers to up to the first maxfields fields are stored in the fields
126041118Sjdp * array.  Missing fields get NULL pointers.
126141118Sjdp *
126241118Sjdp * The return value is the actual number of fields parsed, and is always
126341118Sjdp * <= maxfields.
126441118Sjdp *
126541118Sjdp * On a syntax error, places a message in the msg string, and returns -1.
126641118Sjdp */
126741118Sjdpstatic int
126841118Sjdpsplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
126941118Sjdp{
127041118Sjdp	char *p;
127141118Sjdp	int i;
127241118Sjdp	static const char ws[] = " \t";
127341118Sjdp
127441118Sjdp	for (i = 0;  i < maxfields;  i++)
127541118Sjdp		fields[i] = NULL;
127641118Sjdp	p = str;
127741118Sjdp	i = 0;
127841118Sjdp	while (*p != '\0') {
127941118Sjdp		p += strspn(p, ws);
128041118Sjdp		if (*p == '#' || *p == '\0')
128141118Sjdp			break;
128241118Sjdp		if (i >= maxfields) {
128341118Sjdp			snprintf(msg, msglen, "line has too many fields");
128441118Sjdp			return -1;
128541118Sjdp		}
128641118Sjdp		if (*p == '"') {
128741118Sjdp			char *dst;
128841118Sjdp
128941118Sjdp			dst = ++p;
129041118Sjdp			fields[i] = dst;
129141118Sjdp			while (*p != '"') {
129241118Sjdp				if (*p == '\\') {
129341118Sjdp					p++;
129441118Sjdp					if (*p != '"' && *p != '\\' &&
129541118Sjdp					    *p != '\0') {
129641118Sjdp						snprintf(msg, msglen,
129741118Sjdp						    "invalid `\\' escape");
129841118Sjdp						return -1;
129941118Sjdp					}
130041118Sjdp				}
130141118Sjdp				if (*p == '\0') {
130241118Sjdp					snprintf(msg, msglen,
130341118Sjdp					    "unterminated quoted string");
130441118Sjdp					return -1;
130541118Sjdp				}
130641118Sjdp				*dst++ = *p++;
130741118Sjdp			}
130841118Sjdp			*dst = '\0';
130941118Sjdp			p++;
131041118Sjdp			if (*fields[i] == '\0') {
131141118Sjdp				snprintf(msg, msglen,
131241118Sjdp				    "empty quoted string not permitted");
131341118Sjdp				return -1;
131441118Sjdp			}
131541118Sjdp			if (*p != '\0' && strspn(p, ws) == 0) {
131641118Sjdp				snprintf(msg, msglen, "quoted string not"
131741118Sjdp				    " followed by white space");
131841118Sjdp				return -1;
131941118Sjdp			}
132041118Sjdp		} else {
132141118Sjdp			fields[i] = p;
132241118Sjdp			p += strcspn(p, ws);
132341118Sjdp			if (*p != '\0')
132441118Sjdp				*p++ = '\0';
132541118Sjdp		}
132641118Sjdp		i++;
132741118Sjdp	}
132841118Sjdp	return i;
132941118Sjdp}
133096154Sbrian
133196154Sbrianint
133296322Sbrianrad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len)
133396322Sbrian{
133496322Sbrian	struct vendor_attribute *attr;
133596322Sbrian
133696322Sbrian	attr = (struct vendor_attribute *)*data;
133796322Sbrian	*vendor = ntohl(attr->vendor_value);
133896322Sbrian	*data = attr->attrib_data;
133996322Sbrian	*len = attr->attrib_len - 2;
134096322Sbrian
134196322Sbrian	return (attr->attrib_type);
134296322Sbrian}
134396322Sbrian
134496322Sbrianint
134596154Sbrianrad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
134696154Sbrian    struct in_addr addr)
134796154Sbrian{
134896154Sbrian	return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr,
134996154Sbrian	    sizeof addr.s_addr));
135096154Sbrian}
135196154Sbrian
135296154Sbrianint
1353243956Ssemrad_put_vendor_addr6(struct rad_handle *h, int vendor, int type,
1354243956Ssem    struct in6_addr addr)
1355243956Ssem{
1356243956Ssem
1357243956Ssem	return (rad_put_vendor_attr(h, vendor, type, &addr.s6_addr,
1358243956Ssem	    sizeof addr.s6_addr));
1359243956Ssem}
1360243956Ssem
1361243956Ssemint
136296154Sbrianrad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
136396154Sbrian    const void *value, size_t len)
136496154Sbrian{
136596154Sbrian	struct vendor_attribute *attr;
136696154Sbrian	int res;
136796154Sbrian
1368197086Smav	if (!h->out_created) {
1369128684Sru		generr(h, "Please call rad_create_request()"
1370128684Sru		    " before putting attributes");
1371128684Sru		return -1;
1372128684Sru	}
1373128684Sru
137496154Sbrian	if ((attr = malloc(len + 6)) == NULL) {
1375130490Sstefanf		generr(h, "malloc failure (%zu bytes)", len + 6);
137696154Sbrian		return -1;
137796154Sbrian	}
137896154Sbrian
137996154Sbrian	attr->vendor_value = htonl(vendor);
138096154Sbrian	attr->attrib_type = type;
138196154Sbrian	attr->attrib_len = len + 2;
138296154Sbrian	memcpy(attr->attrib_data, value, len);
138396154Sbrian
138496154Sbrian	res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6);
138596154Sbrian	free(attr);
138696154Sbrian	if (res == 0 && vendor == RAD_VENDOR_MICROSOFT
138796154Sbrian	    && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE
138896154Sbrian	    || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) {
138996154Sbrian		h->chap_pass = 1;
139096154Sbrian	}
139196154Sbrian	return (res);
139296154Sbrian}
139396154Sbrian
139496154Sbrianint
139596154Sbrianrad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i)
139696154Sbrian{
139796154Sbrian	u_int32_t value;
139896154Sbrian
139996154Sbrian	value = htonl(i);
140096154Sbrian	return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value));
140196154Sbrian}
140296154Sbrian
140396154Sbrianint
140496154Sbrianrad_put_vendor_string(struct rad_handle *h, int vendor, int type,
140596154Sbrian    const char *str)
140696154Sbrian{
140796154Sbrian	return (rad_put_vendor_attr(h, vendor, type, str, strlen(str)));
140896154Sbrian}
140998131Sbrian
141098131Sbrianssize_t
141198131Sbrianrad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
141298131Sbrian{
141398131Sbrian	if (len < LEN_AUTH)
141498131Sbrian		return (-1);
1415197086Smav	memcpy(buf, h->out + POS_AUTH, LEN_AUTH);
141698131Sbrian	if (len > LEN_AUTH)
141798131Sbrian		buf[LEN_AUTH] = '\0';
141898131Sbrian	return (LEN_AUTH);
141998131Sbrian}
142098131Sbrian
1421128684Sruu_char *
1422128684Srurad_demangle(struct rad_handle *h, const void *mangled, size_t mlen)
1423128684Sru{
1424128684Sru	char R[LEN_AUTH];
1425128684Sru	const char *S;
1426128684Sru	int i, Ppos;
1427128684Sru	MD5_CTX Context;
1428128684Sru	u_char b[MD5_DIGEST_LENGTH], *C, *demangled;
1429128684Sru
1430128684Sru	if ((mlen % 16 != 0) || mlen > 128) {
1431128684Sru		generr(h, "Cannot interpret mangled data of length %lu",
1432128684Sru		    (u_long)mlen);
1433128684Sru		return NULL;
1434128684Sru	}
1435128684Sru
1436128684Sru	C = (u_char *)mangled;
1437128684Sru
1438128684Sru	/* We need the shared secret as Salt */
1439128684Sru	S = rad_server_secret(h);
1440128684Sru
1441128684Sru	/* We need the request authenticator */
1442128684Sru	if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
1443128684Sru		generr(h, "Cannot obtain the RADIUS request authenticator");
1444128684Sru		return NULL;
1445128684Sru	}
1446128684Sru
1447128684Sru	demangled = malloc(mlen);
1448128684Sru	if (!demangled)
1449128684Sru		return NULL;
1450128684Sru
1451128684Sru	MD5Init(&Context);
1452128684Sru	MD5Update(&Context, S, strlen(S));
1453128684Sru	MD5Update(&Context, R, LEN_AUTH);
1454128684Sru	MD5Final(b, &Context);
1455128684Sru	Ppos = 0;
1456128684Sru	while (mlen) {
1457128684Sru
1458128684Sru		mlen -= 16;
1459128684Sru		for (i = 0; i < 16; i++)
1460128684Sru			demangled[Ppos++] = C[i] ^ b[i];
1461128684Sru
1462128684Sru		if (mlen) {
1463128684Sru			MD5Init(&Context);
1464128684Sru			MD5Update(&Context, S, strlen(S));
1465128684Sru			MD5Update(&Context, C, 16);
1466128684Sru			MD5Final(b, &Context);
1467128684Sru		}
1468128684Sru
1469128684Sru		C += 16;
1470128684Sru	}
1471128684Sru
1472128684Sru	return demangled;
1473128684Sru}
1474128684Sru
1475128684Sruu_char *
1476128684Srurad_demangle_mppe_key(struct rad_handle *h, const void *mangled,
1477128684Sru    size_t mlen, size_t *len)
1478128684Sru{
1479128684Sru	char R[LEN_AUTH];    /* variable names as per rfc2548 */
1480128684Sru	const char *S;
1481128684Sru	u_char b[MD5_DIGEST_LENGTH], *demangled;
1482128684Sru	const u_char *A, *C;
1483128684Sru	MD5_CTX Context;
1484128684Sru	int Slen, i, Clen, Ppos;
1485128684Sru	u_char *P;
1486128684Sru
1487128684Sru	if (mlen % 16 != SALT_LEN) {
1488128684Sru		generr(h, "Cannot interpret mangled data of length %lu",
1489128684Sru		    (u_long)mlen);
1490128684Sru		return NULL;
1491128684Sru	}
1492128684Sru
1493128684Sru	/* We need the RADIUS Request-Authenticator */
1494128684Sru	if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
1495128684Sru		generr(h, "Cannot obtain the RADIUS request authenticator");
1496128684Sru		return NULL;
1497128684Sru	}
1498128684Sru
1499128684Sru	A = (const u_char *)mangled;      /* Salt comes first */
1500128684Sru	C = (const u_char *)mangled + SALT_LEN;  /* Then the ciphertext */
1501128684Sru	Clen = mlen - SALT_LEN;
1502128684Sru	S = rad_server_secret(h);    /* We need the RADIUS secret */
1503128684Sru	Slen = strlen(S);
1504128684Sru	P = alloca(Clen);        /* We derive our plaintext */
1505128684Sru
1506128684Sru	MD5Init(&Context);
1507128684Sru	MD5Update(&Context, S, Slen);
1508128684Sru	MD5Update(&Context, R, LEN_AUTH);
1509128684Sru	MD5Update(&Context, A, SALT_LEN);
1510128684Sru	MD5Final(b, &Context);
1511128684Sru	Ppos = 0;
1512128684Sru
1513128684Sru	while (Clen) {
1514128684Sru		Clen -= 16;
1515128684Sru
1516128684Sru		for (i = 0; i < 16; i++)
1517128684Sru		    P[Ppos++] = C[i] ^ b[i];
1518128684Sru
1519128684Sru		if (Clen) {
1520128684Sru			MD5Init(&Context);
1521128684Sru			MD5Update(&Context, S, Slen);
1522128684Sru			MD5Update(&Context, C, 16);
1523128684Sru			MD5Final(b, &Context);
1524128684Sru		}
1525128684Sru
1526128684Sru		C += 16;
1527128684Sru	}
1528128684Sru
1529128684Sru	/*
1530128684Sru	* The resulting plain text consists of a one-byte length, the text and
1531128684Sru	* maybe some padding.
1532128684Sru	*/
1533128684Sru	*len = *P;
1534128684Sru	if (*len > mlen - 1) {
1535130490Sstefanf		generr(h, "Mangled data seems to be garbage %zu %zu",
1536128684Sru		    *len, mlen-1);
1537128684Sru		return NULL;
1538128684Sru	}
1539128684Sru
1540128684Sru	if (*len > MPPE_KEY_LEN * 2) {
1541130490Sstefanf		generr(h, "Key to long (%zu) for me max. %d",
1542128684Sru		    *len, MPPE_KEY_LEN * 2);
1543128684Sru		return NULL;
1544128684Sru	}
1545128684Sru	demangled = malloc(*len);
1546128684Sru	if (!demangled)
1547128684Sru		return NULL;
1548128684Sru
1549128684Sru	memcpy(demangled, P + 1, *len);
1550128684Sru	return demangled;
1551128684Sru}
1552128684Sru
155398131Sbrianconst char *
155498131Sbrianrad_server_secret(struct rad_handle *h)
155598131Sbrian{
155698131Sbrian	return (h->servers[h->srv].secret);
155798131Sbrian}
1558