radlib.c revision 128684
11556Srgrimes/*-
21556Srgrimes * Copyright 1998 Juniper Networks, Inc.
31556Srgrimes * All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes *
141556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
151556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
161556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
171556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
181556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
191556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
201556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
211556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
221556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
231556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
241556Srgrimes * SUCH DAMAGE.
251556Srgrimes */
261556Srgrimes
271556Srgrimes#include <sys/cdefs.h>
281556Srgrimes__FBSDID("$FreeBSD: head/lib/libradius/radlib.c 128684 2004-04-27 15:00:29Z ru $");
291556Srgrimes
301556Srgrimes#include <sys/types.h>
311556Srgrimes#include <sys/socket.h>
321556Srgrimes#include <sys/time.h>
331556Srgrimes#include <netinet/in.h>
3436150Scharnier#include <arpa/inet.h>
3536150Scharnier#ifdef WITH_SSL
3636150Scharnier#include <openssl/hmac.h>
371556Srgrimes#include <openssl/md5.h>
3899110Sobrien#define MD5Init MD5_Init
3999110Sobrien#define MD5Update MD5_Update
401556Srgrimes#define MD5Final MD5_Final
41100437Stjr#else
4217987Speter#define MD5_DIGEST_LENGTH 16
43102576Skeramida#include <md5.h>
4417987Speter#endif
45153091Sstefanf
4645266Scracauer/* We need the MPPE_KEY_LEN define */
4753891Scracauer#include <netgraph/ng_mppc.h>
4817987Speter
491556Srgrimes#include <errno.h>
501556Srgrimes#include <netdb.h>
511556Srgrimes#include <stdarg.h>
521556Srgrimes#include <stddef.h>
531556Srgrimes#include <stdio.h>
541556Srgrimes#include <stdlib.h>
551556Srgrimes#include <string.h>
561556Srgrimes#include <unistd.h>
571556Srgrimes
581556Srgrimes#include "radlib_private.h"
591556Srgrimes
601556Srgrimesstatic void	 clear_password(struct rad_handle *);
611556Srgrimesstatic void	 generr(struct rad_handle *, const char *, ...)
621556Srgrimes		    __printflike(2, 3);
631556Srgrimesstatic void	 insert_scrambled_password(struct rad_handle *, int);
641556Srgrimesstatic void	 insert_request_authenticator(struct rad_handle *, int);
651556Srgrimesstatic void	 insert_message_authenticator(struct rad_handle *, int);
661556Srgrimesstatic int	 is_valid_response(struct rad_handle *, int,
671556Srgrimes		    const struct sockaddr_in *);
681556Srgrimesstatic int	 put_password_attr(struct rad_handle *, int,
691556Srgrimes		    const void *, size_t);
7017987Speterstatic int	 put_raw_attr(struct rad_handle *, int,
711556Srgrimes		    const void *, size_t);
7217987Speterstatic int	 split(char *, char *[], int, char *, size_t);
731556Srgrimes
7417987Speterstatic void
751556Srgrimesclear_password(struct rad_handle *h)
761556Srgrimes{
771556Srgrimes	if (h->pass_len != 0) {
781556Srgrimes		memset(h->pass, 0, h->pass_len);
791556Srgrimes		h->pass_len = 0;
801556Srgrimes	}
811556Srgrimes	h->pass_pos = 0;
821556Srgrimes}
831556Srgrimes
841556Srgrimesstatic void
851556Srgrimesgenerr(struct rad_handle *h, const char *format, ...)
86193169Sstefanf{
871556Srgrimes	va_list		 ap;
881556Srgrimes
891556Srgrimes	va_start(ap, format);
901556Srgrimes	vsnprintf(h->errmsg, ERRSIZE, format, ap);
911556Srgrimes	va_end(ap);
9217987Speter}
931556Srgrimes
941556Srgrimesstatic void
95149933Sstefanfinsert_scrambled_password(struct rad_handle *h, int srv)
96149933Sstefanf{
9790111Simp	MD5_CTX ctx;
9890111Simp	unsigned char md5[MD5_DIGEST_LENGTH];
9990111Simp	const struct rad_server *srvp;
10090111Simp	int padded_len;
10190111Simp	int pos;
10290111Simp
1031556Srgrimes	srvp = &h->servers[srv];
1041556Srgrimes	padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
1051556Srgrimes
1061556Srgrimes	memcpy(md5, &h->request[POS_AUTH], LEN_AUTH);
1071556Srgrimes	for (pos = 0;  pos < padded_len;  pos += 16) {
1081556Srgrimes		int i;
1091556Srgrimes
1101556Srgrimes		/* Calculate the new scrambler */
1111556Srgrimes		MD5Init(&ctx);
1121556Srgrimes		MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
1131556Srgrimes		MD5Update(&ctx, md5, 16);
1141556Srgrimes		MD5Final(md5, &ctx);
1151556Srgrimes
1161556Srgrimes		/*
1171556Srgrimes		 * Mix in the current chunk of the password, and copy
1181556Srgrimes		 * the result into the right place in the request.  Also
1191556Srgrimes		 * modify the scrambler in place, since we will use this
1201556Srgrimes		 * in calculating the scrambler for next time.
1211556Srgrimes		 */
1221556Srgrimes		for (i = 0;  i < 16;  i++)
1231556Srgrimes			h->request[h->pass_pos + pos + i] =
1241556Srgrimes			    md5[i] ^= h->pass[pos + i];
1251556Srgrimes	}
12646684Skris}
1271556Srgrimes
1281556Srgrimesstatic void
12917987Speterinsert_request_authenticator(struct rad_handle *h, int srv)
13090111Simp{
1311556Srgrimes	MD5_CTX ctx;
1321556Srgrimes	const struct rad_server *srvp;
1331556Srgrimes
1341556Srgrimes	srvp = &h->servers[srv];
1351556Srgrimes
1361556Srgrimes	/* Create the request authenticator */
1371556Srgrimes	MD5Init(&ctx);
1381556Srgrimes	MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
1391556Srgrimes	MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH);
1401556Srgrimes	MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS);
1411556Srgrimes	MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
1421556Srgrimes	MD5Final(&h->request[POS_AUTH], &ctx);
1431556Srgrimes}
1441556Srgrimes
1451556Srgrimesstatic void
1461556Srgrimesinsert_message_authenticator(struct rad_handle *h, int srv)
1471556Srgrimes{
1481556Srgrimes#ifdef WITH_SSL
1491556Srgrimes	u_char md[EVP_MAX_MD_SIZE];
1501556Srgrimes	u_int md_len;
151193169Sstefanf	const struct rad_server *srvp;
1521556Srgrimes	HMAC_CTX ctx;
1531556Srgrimes	srvp = &h->servers[srv];
1541556Srgrimes
1551556Srgrimes	if (h->authentic_pos != 0) {
1561556Srgrimes		HMAC_CTX_init(&ctx);
1571556Srgrimes		HMAC_Init(&ctx, srvp->secret, strlen(srvp->secret), EVP_md5());
1581556Srgrimes		HMAC_Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
1591556Srgrimes		HMAC_Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
1601556Srgrimes		HMAC_Update(&ctx, &h->request[POS_ATTRS],
1611556Srgrimes		    h->req_len - POS_ATTRS);
162193169Sstefanf		HMAC_Final(&ctx, md, &md_len);
16390111Simp		HMAC_CTX_cleanup(&ctx);
1641556Srgrimes		HMAC_cleanup(&ctx);
1651556Srgrimes		memcpy(&h->request[h->authentic_pos + 2], md, md_len);
1661556Srgrimes	}
1671556Srgrimes#endif
1681556Srgrimes}
1691556Srgrimes
170190698Sstefanf/*
171193169Sstefanf * Return true if the current response is valid for a request to the
1721556Srgrimes * specified server.
1731556Srgrimes */
1741556Srgrimesstatic int
1751556Srgrimesis_valid_response(struct rad_handle *h, int srv,
1761556Srgrimes    const struct sockaddr_in *from)
1771556Srgrimes{
1781556Srgrimes	MD5_CTX ctx;
1791556Srgrimes	unsigned char md5[MD5_DIGEST_LENGTH];
1801556Srgrimes	const struct rad_server *srvp;
1811556Srgrimes	int len;
1821556Srgrimes#ifdef WITH_SSL
1831556Srgrimes	HMAC_CTX hctx;
1841556Srgrimes	u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE];
1851556Srgrimes	int pos, md_len;
18690111Simp#endif
18717987Speter
188149927Sstefanf	srvp = &h->servers[srv];
189149927Sstefanf
190149927Sstefanf	/* Check the source address */
1911556Srgrimes	if (from->sin_family != srvp->addr.sin_family ||
1921556Srgrimes	    from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
1931556Srgrimes	    from->sin_port != srvp->addr.sin_port)
1941556Srgrimes		return 0;
1951556Srgrimes
19617987Speter	/* Check the message length */
1971556Srgrimes	if (h->resp_len < POS_ATTRS)
19817987Speter		return 0;
199149802Sstefanf	len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
2001556Srgrimes	if (len > h->resp_len)
2011556Srgrimes		return 0;
202149932Sstefanf
2031556Srgrimes	/* Check the response authenticator */
2041556Srgrimes	MD5Init(&ctx);
2051556Srgrimes	MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE);
2061556Srgrimes	MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
2071556Srgrimes	MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS);
2081556Srgrimes	MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
20918754Ssteve	MD5Final(md5, &ctx);
2101556Srgrimes	if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
21118754Ssteve		return 0;
2121556Srgrimes
2131556Srgrimes#ifdef WITH_SSL
2141556Srgrimes	/*
2151556Srgrimes	 * For non accounting responses check the message authenticator,
2161556Srgrimes	 * if any.
2171556Srgrimes	 */
2181556Srgrimes	if (h->response[POS_CODE] != RAD_ACCOUNTING_RESPONSE) {
2191556Srgrimes
2201556Srgrimes		memcpy(resp, h->response, MSGSIZE);
2211556Srgrimes		pos = POS_ATTRS;
2221556Srgrimes
2231556Srgrimes		/* Search and verify the Message-Authenticator */
2241556Srgrimes		while (pos < len - 2) {
2251556Srgrimes
2261556Srgrimes			if (h->response[pos] == RAD_MESSAGE_AUTHENTIC) {
2271556Srgrimes				/* zero fill the Message-Authenticator */
228149927Sstefanf				memset(&resp[pos + 2], 0, MD5_DIGEST_LENGTH);
2291556Srgrimes
2301556Srgrimes				HMAC_CTX_init(&hctx);
2311556Srgrimes				HMAC_Init(&hctx, srvp->secret,
2321556Srgrimes				    strlen(srvp->secret), EVP_md5());
2331556Srgrimes				HMAC_Update(&hctx, &h->response[POS_CODE],
2341556Srgrimes				    POS_AUTH - POS_CODE);
2351556Srgrimes				HMAC_Update(&hctx, &h->request[POS_AUTH],
2361556Srgrimes				    LEN_AUTH);
23720425Ssteve				HMAC_Update(&hctx, &resp[POS_ATTRS],
2381556Srgrimes				    h->resp_len - POS_ATTRS);
23917987Speter				HMAC_Final(&hctx, md, &md_len);
2401556Srgrimes				HMAC_CTX_cleanup(&hctx);
24120425Ssteve				HMAC_cleanup(&hctx);
24220425Ssteve				if (memcmp(md, &h->response[pos + 2],
2431556Srgrimes				    MD5_DIGEST_LENGTH) != 0)
2441556Srgrimes					return 0;
2451556Srgrimes				break;
2461556Srgrimes			}
247149933Sstefanf			pos += h->response[pos + 1];
2481556Srgrimes		}
2491556Srgrimes	}
250149933Sstefanf#endif
2511556Srgrimes	return 1;
2521556Srgrimes}
2531556Srgrimes
2541556Srgrimesstatic int
2551556Srgrimesput_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
2561556Srgrimes{
2571556Srgrimes	int padded_len;
2581556Srgrimes	int pad_len;
2591556Srgrimes
2601556Srgrimes	if (h->pass_pos != 0) {
2611556Srgrimes		generr(h, "Multiple User-Password attributes specified");
2621556Srgrimes		return -1;
2631556Srgrimes	}
2641556Srgrimes	if (len > PASSSIZE)
2651556Srgrimes		len = PASSSIZE;
266149927Sstefanf	padded_len = len == 0 ? 16 : (len+15) & ~0xf;
2671556Srgrimes	pad_len = padded_len - len;
2681556Srgrimes
2691556Srgrimes	/*
270149927Sstefanf	 * Put in a place-holder attribute containing all zeros, and
2711556Srgrimes	 * remember where it is so we can fill it in later.
2721556Srgrimes	 */
2731556Srgrimes	clear_password(h);
2741556Srgrimes	put_raw_attr(h, type, h->pass, padded_len);
2751556Srgrimes	h->pass_pos = h->req_len - padded_len;
2761556Srgrimes
2771556Srgrimes	/* Save the cleartext password, padded as necessary */
2781556Srgrimes	memcpy(h->pass, value, len);
2791556Srgrimes	h->pass_len = len;
280149927Sstefanf	memset(h->pass + len, 0, pad_len);
2811556Srgrimes	return 0;
2821556Srgrimes}
2831556Srgrimes
2841556Srgrimesstatic int
2851556Srgrimesput_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
286149933Sstefanf{
28717987Speter	if (len > 253) {
2881556Srgrimes		generr(h, "Attribute too long");
2891556Srgrimes		return -1;
2901556Srgrimes	}
2911556Srgrimes	if (h->req_len + 2 + len > MSGSIZE) {
2921556Srgrimes		generr(h, "Maximum message length exceeded");
2931556Srgrimes		return -1;
2941556Srgrimes	}
2951556Srgrimes	h->request[h->req_len++] = type;
2961556Srgrimes	h->request[h->req_len++] = len + 2;
2971556Srgrimes	memcpy(&h->request[h->req_len], value, len);
2981556Srgrimes	h->req_len += len;
2991556Srgrimes	return 0;
3001556Srgrimes}
3011556Srgrimes
3021556Srgrimesint
3031556Srgrimesrad_add_server(struct rad_handle *h, const char *host, int port,
3041556Srgrimes    const char *secret, int timeout, int tries)
3051556Srgrimes{
3061556Srgrimes	struct rad_server *srvp;
3071556Srgrimes
3081556Srgrimes	if (h->num_servers >= MAXSERVERS) {
3091556Srgrimes		generr(h, "Too many RADIUS servers specified");
310149933Sstefanf		return -1;
3111556Srgrimes	}
3121556Srgrimes	srvp = &h->servers[h->num_servers];
3131556Srgrimes
3141556Srgrimes	memset(&srvp->addr, 0, sizeof srvp->addr);
3151556Srgrimes	srvp->addr.sin_len = sizeof srvp->addr;
3161556Srgrimes	srvp->addr.sin_family = AF_INET;
3171556Srgrimes	if (!inet_aton(host, &srvp->addr.sin_addr)) {
3181556Srgrimes		struct hostent *hent;
3191556Srgrimes
3201556Srgrimes		if ((hent = gethostbyname(host)) == NULL) {
3211556Srgrimes			generr(h, "%s: host not found", host);
322149933Sstefanf			return -1;
32317987Speter		}
3241556Srgrimes		memcpy(&srvp->addr.sin_addr, hent->h_addr,
3251556Srgrimes		    sizeof srvp->addr.sin_addr);
3261556Srgrimes	}
3271556Srgrimes	if (port != 0)
3281556Srgrimes		srvp->addr.sin_port = htons((u_short)port);
3291556Srgrimes	else {
3301556Srgrimes		struct servent *sent;
3311556Srgrimes
33217987Speter		if (h->type == RADIUS_AUTH)
3331556Srgrimes			srvp->addr.sin_port =
3341556Srgrimes			    (sent = getservbyname("radius", "udp")) != NULL ?
3351556Srgrimes				sent->s_port : htons(RADIUS_PORT);
3361556Srgrimes		else
3371556Srgrimes			srvp->addr.sin_port =
3381556Srgrimes			    (sent = getservbyname("radacct", "udp")) != NULL ?
3391556Srgrimes				sent->s_port : htons(RADACCT_PORT);
3401556Srgrimes	}
3411556Srgrimes	if ((srvp->secret = strdup(secret)) == NULL) {
3421556Srgrimes		generr(h, "Out of memory");
343149933Sstefanf		return -1;
3441556Srgrimes	}
3451556Srgrimes	srvp->timeout = timeout;
3461556Srgrimes	srvp->max_tries = tries;
3471556Srgrimes	srvp->num_tries = 0;
3481556Srgrimes	h->num_servers++;
3491556Srgrimes	return 0;
3501556Srgrimes}
3511556Srgrimes
3521556Srgrimesvoid
3531556Srgrimesrad_close(struct rad_handle *h)
3541556Srgrimes{
3551556Srgrimes	int srv;
3561556Srgrimes
3571556Srgrimes	if (h->fd != -1)
3581556Srgrimes		close(h->fd);
3591556Srgrimes	for (srv = 0;  srv < h->num_servers;  srv++) {
3601556Srgrimes		memset(h->servers[srv].secret, 0,
3611556Srgrimes		    strlen(h->servers[srv].secret));
36290111Simp		free(h->servers[srv].secret);
36317987Speter	}
3641556Srgrimes	clear_password(h);
3651556Srgrimes	free(h);
3661556Srgrimes}
3671556Srgrimes
3681556Srgrimesint
3691556Srgrimesrad_config(struct rad_handle *h, const char *path)
3701556Srgrimes{
37117987Speter	FILE *fp;
372172440Sstefanf	char buf[MAXCONFLINE];
3731556Srgrimes	int linenum;
3741556Srgrimes	int retval;
3751556Srgrimes
3761556Srgrimes	if (path == NULL)
3771556Srgrimes		path = PATH_RADIUS_CONF;
3781556Srgrimes	if ((fp = fopen(path, "r")) == NULL) {
3791556Srgrimes		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
3801556Srgrimes		return -1;
3811556Srgrimes	}
3821556Srgrimes	retval = 0;
3831556Srgrimes	linenum = 0;
3841556Srgrimes	while (fgets(buf, sizeof buf, fp) != NULL) {
3851556Srgrimes		int len;
3861556Srgrimes		char *fields[5];
3871556Srgrimes		int nfields;
3881556Srgrimes		char msg[ERRSIZE];
3891556Srgrimes		char *type;
3901556Srgrimes		char *host, *res;
3911556Srgrimes		char *port_str;
3921556Srgrimes		char *secret;
3931556Srgrimes		char *timeout_str;
3941556Srgrimes		char *maxtries_str;
39590111Simp		char *end;
39617987Speter		char *wanttype;
3971556Srgrimes		unsigned long timeout;
3981556Srgrimes		unsigned long maxtries;
3991556Srgrimes		int port;
4001556Srgrimes		int i;
4011556Srgrimes
4021556Srgrimes		linenum++;
4031556Srgrimes		len = strlen(buf);
4041556Srgrimes		/* We know len > 0, else fgets would have returned NULL. */
4051556Srgrimes		if (buf[len - 1] != '\n') {
4061556Srgrimes			if (len == sizeof buf - 1)
4071556Srgrimes				generr(h, "%s:%d: line too long", path,
4081556Srgrimes				    linenum);
4091556Srgrimes			else
41045916Scracauer				generr(h, "%s:%d: missing newline", path,
4111556Srgrimes				    linenum);
4121556Srgrimes			retval = -1;
4131556Srgrimes			break;
4141556Srgrimes		}
4151556Srgrimes		buf[len - 1] = '\0';
4161556Srgrimes
4171556Srgrimes		/* Extract the fields from the line. */
4181556Srgrimes		nfields = split(buf, fields, 5, msg, sizeof msg);
4191556Srgrimes		if (nfields == -1) {
4201556Srgrimes			generr(h, "%s:%d: %s", path, linenum, msg);
4211556Srgrimes			retval = -1;
42290111Simp			break;
42317987Speter		}
42425222Ssteve		if (nfields == 0)
4251556Srgrimes			continue;
4261556Srgrimes		/*
42717987Speter		 * The first field should contain "auth" or "acct" for
42817987Speter		 * authentication or accounting, respectively.  But older
42917987Speter		 * versions of the file didn't have that field.  Default
43017987Speter		 * it to "auth" for backward compatibility.
43117987Speter		 */
43217987Speter		if (strcmp(fields[0], "auth") != 0 &&
43366612Sbrian		    strcmp(fields[0], "acct") != 0) {
43417987Speter			if (nfields >= 5) {
43596922Stjr				generr(h, "%s:%d: invalid service type", path,
4361556Srgrimes				    linenum);
4371556Srgrimes				retval = -1;
43817987Speter				break;
43917987Speter			}
44017987Speter			nfields++;
44117987Speter			for (i = nfields;  --i > 0;  )
442181017Sstefanf				fields[i] = fields[i - 1];
44317987Speter			fields[0] = "auth";
44417987Speter		}
44517987Speter		if (nfields < 3) {
4461556Srgrimes			generr(h, "%s:%d: missing shared secret", path,
4471556Srgrimes			    linenum);
4481556Srgrimes			retval = -1;
4491556Srgrimes			break;
4501556Srgrimes		}
4511556Srgrimes		type = fields[0];
4521556Srgrimes		host = fields[1];
4531556Srgrimes		secret = fields[2];
4541556Srgrimes		timeout_str = fields[3];
4551556Srgrimes		maxtries_str = fields[4];
4561556Srgrimes
4571556Srgrimes		/* Ignore the line if it is for the wrong service type. */
4581556Srgrimes		wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
4591556Srgrimes		if (strcmp(type, wanttype) != 0)
46090111Simp			continue;
46117987Speter
4621556Srgrimes		/* Parse and validate the fields. */
4631556Srgrimes		res = host;
4641556Srgrimes		host = strsep(&res, ":");
4651556Srgrimes		port_str = strsep(&res, ":");
4661556Srgrimes		if (port_str != NULL) {
4671556Srgrimes			port = strtoul(port_str, &end, 10);
468149802Sstefanf			if (*end != '\0') {
4691556Srgrimes				generr(h, "%s:%d: invalid port", path,
4701556Srgrimes				    linenum);
4711556Srgrimes				retval = -1;
4721556Srgrimes				break;
4731556Srgrimes			}
4741556Srgrimes		} else
4751556Srgrimes			port = 0;
4761556Srgrimes		if (timeout_str != NULL) {
4771556Srgrimes			timeout = strtoul(timeout_str, &end, 10);
4781556Srgrimes			if (*end != '\0') {
4791556Srgrimes				generr(h, "%s:%d: invalid timeout", path,
4801556Srgrimes				    linenum);
48153891Scracauer				retval = -1;
4821556Srgrimes				break;
4831556Srgrimes			}
4841556Srgrimes		} else
4851556Srgrimes			timeout = TIMEOUT;
4861556Srgrimes		if (maxtries_str != NULL) {
487124780Sdes			maxtries = strtoul(maxtries_str, &end, 10);
4881556Srgrimes			if (*end != '\0') {
4891556Srgrimes				generr(h, "%s:%d: invalid maxtries", path,
4901556Srgrimes				    linenum);
49153282Scracauer				retval = -1;
49252900Scracauer				break;
4931556Srgrimes			}
494124780Sdes		} else
4951556Srgrimes			maxtries = MAXTRIES;
4961556Srgrimes
4971556Srgrimes		if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
4981556Srgrimes		    -1) {
4991556Srgrimes			strcpy(msg, h->errmsg);
5001556Srgrimes			generr(h, "%s:%d: %s", path, linenum, msg);
5011556Srgrimes			retval = -1;
5021556Srgrimes			break;
5031556Srgrimes		}
5041556Srgrimes	}
5051556Srgrimes	/* Clear out the buffer to wipe a possible copy of a shared secret */
5061556Srgrimes	memset(buf, 0, sizeof buf);
5071556Srgrimes	fclose(fp);
50845916Scracauer	return retval;
5091556Srgrimes}
5101556Srgrimes
5111556Srgrimes/*
5121556Srgrimes * rad_init_send_request() must have previously been called.
5131556Srgrimes * Returns:
5141556Srgrimes *   0     The application should select on *fd with a timeout of tv before
5151556Srgrimes *         calling rad_continue_send_request again.
5161556Srgrimes *   < 0   Failure
5171556Srgrimes *   > 0   Success
5181556Srgrimes */
5191556Srgrimesint
5201556Srgrimesrad_continue_send_request(struct rad_handle *h, int selected, int *fd,
5211556Srgrimes                          struct timeval *tv)
5221556Srgrimes{
5231556Srgrimes	int n;
52490111Simp
52517987Speter	if (selected) {
5261556Srgrimes		struct sockaddr_in from;
5271556Srgrimes		int fromlen;
5281556Srgrimes
5291556Srgrimes		fromlen = sizeof from;
5301556Srgrimes		h->resp_len = recvfrom(h->fd, h->response,
5311556Srgrimes		    MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
5321556Srgrimes		if (h->resp_len == -1) {
5331556Srgrimes			generr(h, "recvfrom: %s", strerror(errno));
5341556Srgrimes			return -1;
53517987Speter		}
53617987Speter		if (is_valid_response(h, h->srv, &from)) {
5371556Srgrimes			h->resp_len = h->response[POS_LENGTH] << 8 |
53817987Speter			    h->response[POS_LENGTH+1];
5391556Srgrimes			h->resp_pos = POS_ATTRS;
54017987Speter			return h->response[POS_CODE];
5411556Srgrimes		}
5421556Srgrimes	}
54317987Speter
5441556Srgrimes	if (h->try == h->total_tries) {
54553891Scracauer		generr(h, "No valid RADIUS responses received");
5461556Srgrimes		return -1;
5471556Srgrimes	}
5481556Srgrimes
5491556Srgrimes	/*
5501556Srgrimes         * Scan round-robin to the next server that has some
551124780Sdes         * tries left.  There is guaranteed to be one, or we
5521556Srgrimes         * would have exited this loop by now.
5531556Srgrimes	 */
5541556Srgrimes	while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
5551556Srgrimes		if (++h->srv >= h->num_servers)
5561556Srgrimes			h->srv = 0;
5571556Srgrimes
5581556Srgrimes	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
5591556Srgrimes		/* Insert the request authenticator into the request */
5601556Srgrimes		insert_request_authenticator(h, h->srv);
5611556Srgrimes	else
562109627Stjr		/* Insert the scrambled password into the request */
5631556Srgrimes		if (h->pass_pos != 0)
5641556Srgrimes			insert_scrambled_password(h, h->srv);
5651556Srgrimes
5661556Srgrimes	insert_message_authenticator(h, h->srv);
5671556Srgrimes
5681556Srgrimes	/* Send the request */
5691556Srgrimes	n = sendto(h->fd, h->request, h->req_len, 0,
5701556Srgrimes	    (const struct sockaddr *)&h->servers[h->srv].addr,
5711556Srgrimes	    sizeof h->servers[h->srv].addr);
5721556Srgrimes	if (n != h->req_len) {
57390111Simp		if (n == -1)
57417987Speter			generr(h, "sendto: %s", strerror(errno));
5751556Srgrimes		else
5761556Srgrimes			generr(h, "sendto: short write");
5771556Srgrimes		return -1;
5781556Srgrimes	}
5791556Srgrimes
5801556Srgrimes	h->try++;
5811556Srgrimes	h->servers[h->srv].num_tries++;
5821556Srgrimes	tv->tv_sec = h->servers[h->srv].timeout;
5831556Srgrimes	tv->tv_usec = 0;
5841556Srgrimes	*fd = h->fd;
5851556Srgrimes
5861556Srgrimes	return 0;
5871556Srgrimes}
5881556Srgrimes
5891556Srgrimesint
5901556Srgrimesrad_create_request(struct rad_handle *h, int code)
5911556Srgrimes{
5921556Srgrimes	int i;
5931556Srgrimes
5941556Srgrimes	h->request[POS_CODE] = code;
59545916Scracauer	h->request[POS_IDENT] = ++h->ident;
59654884Scracauer	/* Create a random authenticator */
597193222Srse	for (i = 0;  i < LEN_AUTH;  i += 2) {
59817987Speter		long r;
59917987Speter		r = random();
60017987Speter		h->request[POS_AUTH+i] = (u_char)r;
60117987Speter		h->request[POS_AUTH+i+1] = (u_char)(r >> 8);
60217987Speter	}
60354884Scracauer	h->req_len = POS_ATTRS;
60417987Speter	clear_password(h);
6051556Srgrimes	h->request_created = 1;
6061556Srgrimes	return 0;
607149802Sstefanf}
6081556Srgrimes
6091556Srgrimesstruct in_addr
6101556Srgrimesrad_cvt_addr(const void *data)
6111556Srgrimes{
61254884Scracauer	struct in_addr value;
61317987Speter
61417987Speter	memcpy(&value.s_addr, data, sizeof value.s_addr);
6151556Srgrimes	return value;
61617987Speter}
6171556Srgrimes
6181556Srgrimesu_int32_t
6191556Srgrimesrad_cvt_int(const void *data)
6201556Srgrimes{
6211556Srgrimes	u_int32_t value;
6221556Srgrimes
6231556Srgrimes	memcpy(&value, data, sizeof value);
6241556Srgrimes	return ntohl(value);
6251556Srgrimes}
6261556Srgrimes
6271556Srgrimeschar *
6281556Srgrimesrad_cvt_string(const void *data, size_t len)
6291556Srgrimes{
6301556Srgrimes	char *s;
6311556Srgrimes
6321556Srgrimes	s = malloc(len + 1);
6331556Srgrimes	if (s != NULL) {
6341556Srgrimes		memcpy(s, data, len);
6351556Srgrimes		s[len] = '\0';
6361556Srgrimes	}
6371556Srgrimes	return s;
6381556Srgrimes}
6391556Srgrimes
6401556Srgrimes/*
6411556Srgrimes * Returns the attribute type.  If none are left, returns 0.  On failure,
6421556Srgrimes * returns -1.
6431556Srgrimes */
6441556Srgrimesint
6451556Srgrimesrad_get_attr(struct rad_handle *h, const void **value, size_t *len)
6461556Srgrimes{
6471556Srgrimes	int type;
6481556Srgrimes
649159632Sstefanf	if (h->resp_pos >= h->resp_len)
650159632Sstefanf		return 0;
6511556Srgrimes	if (h->resp_pos + 2 > h->resp_len) {
652159632Sstefanf		generr(h, "Malformed attribute in response");
653159632Sstefanf		return -1;
6541556Srgrimes	}
655159632Sstefanf	type = h->response[h->resp_pos++];
6561556Srgrimes	*len = h->response[h->resp_pos++] - 2;
6571556Srgrimes	if (h->resp_pos + (int)*len > h->resp_len) {
658159632Sstefanf		generr(h, "Malformed attribute in response");
659159632Sstefanf		return -1;
6601556Srgrimes	}
661159632Sstefanf	*value = &h->response[h->resp_pos];
6621556Srgrimes	h->resp_pos += *len;
6631556Srgrimes	return type;
6641556Srgrimes}
6651556Srgrimes
6661556Srgrimes/*
6671556Srgrimes * Returns -1 on error, 0 to indicate no event and >0 for success
6681556Srgrimes */
669157601Sstefanfint
6701556Srgrimesrad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
6711556Srgrimes{
672157601Sstefanf	int srv;
6731556Srgrimes
67417987Speter	/* Make sure we have a socket to use */
67517987Speter	if (h->fd == -1) {
67617987Speter		struct sockaddr_in sin;
67717987Speter
67817987Speter		if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
67917987Speter			generr(h, "Cannot create socket: %s", strerror(errno));
68017987Speter			return -1;
68117987Speter		}
68254884Scracauer		memset(&sin, 0, sizeof sin);
68317987Speter		sin.sin_len = sizeof sin;
684155301Sschweikh		sin.sin_family = AF_INET;
68554884Scracauer		sin.sin_addr.s_addr = INADDR_ANY;
68654884Scracauer		sin.sin_port = htons(0);
68754884Scracauer		if (bind(h->fd, (const struct sockaddr *)&sin,
688155301Sschweikh		    sizeof sin) == -1) {
68954884Scracauer			generr(h, "bind: %s", strerror(errno));
69054884Scracauer			close(h->fd);
69154884Scracauer			h->fd = -1;
692155301Sschweikh			return -1;
69354884Scracauer		}
69454884Scracauer	}
69554884Scracauer
69654884Scracauer	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
69754884Scracauer		/* Make sure no password given */
69854884Scracauer		if (h->pass_pos || h->chap_pass) {
69954884Scracauer			generr(h, "User or Chap Password"
700123996Smaxim			    " in accounting request");
70154884Scracauer			return -1;
70254884Scracauer		}
70354884Scracauer	} else {
70454884Scracauer		if (h->eap_msg == 0) {
70517987Speter			/* Make sure the user gave us a password */
70617987Speter			if (h->pass_pos == 0 && !h->chap_pass) {
7071556Srgrimes				generr(h, "No User or Chap Password"
70820425Ssteve				    " attributes given");
7091556Srgrimes				return -1;
7101556Srgrimes			}
7111556Srgrimes			if (h->pass_pos != 0 && h->chap_pass) {
7121556Srgrimes				generr(h, "Both User and Chap Password"
7131556Srgrimes				    " attributes given");
7141556Srgrimes				return -1;
7151556Srgrimes			}
7161556Srgrimes		}
7171556Srgrimes	}
718157601Sstefanf
719157601Sstefanf	/* Fill in the length field in the message */
7201556Srgrimes	h->request[POS_LENGTH] = h->req_len >> 8;
72120425Ssteve	h->request[POS_LENGTH+1] = h->req_len;
7221556Srgrimes
7231556Srgrimes	/*
7241556Srgrimes	 * Count the total number of tries we will make, and zero the
7251556Srgrimes	 * counter for each server.
7261556Srgrimes	 */
7271556Srgrimes	h->total_tries = 0;
7281556Srgrimes	for (srv = 0;  srv < h->num_servers;  srv++) {
7291556Srgrimes		h->total_tries += h->servers[srv].max_tries;
7301556Srgrimes		h->servers[srv].num_tries = 0;
7311556Srgrimes	}
7321556Srgrimes	if (h->total_tries == 0) {
73345221Scracauer		generr(h, "No RADIUS servers specified");
734194127Sjilles		return -1;
73517987Speter	}
7361556Srgrimes
73748896Ssheldonh	h->try = h->srv = 0;
7381556Srgrimes
739100437Stjr	return rad_continue_send_request(h, 0, fd, tv);
740100437Stjr}
741100437Stjr
7421556Srgrimes/*
7431556Srgrimes * Create and initialize a rad_handle structure, and return it to the
7441556Srgrimes * caller.  Can fail only if the necessary memory cannot be allocated.
7451556Srgrimes * In that case, it returns NULL.
7461556Srgrimes */
74753891Scracauerstruct rad_handle *
7481556Srgrimesrad_auth_open(void)
7491556Srgrimes{
7501556Srgrimes	struct rad_handle *h;
7511556Srgrimes
7521556Srgrimes	h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
7531556Srgrimes	if (h != NULL) {
7541556Srgrimes		srandomdev();
755124780Sdes		h->fd = -1;
7561556Srgrimes		h->num_servers = 0;
7571556Srgrimes		h->ident = random();
7581556Srgrimes		h->errmsg[0] = '\0';
7591556Srgrimes		memset(h->pass, 0, sizeof h->pass);
7601556Srgrimes		h->pass_len = 0;
7611556Srgrimes		h->pass_pos = 0;
7621556Srgrimes		h->chap_pass = 0;
7631556Srgrimes		h->authentic_pos = 0;
7641556Srgrimes		h->type = RADIUS_AUTH;
76520425Ssteve		h->request_created = 0;
7661556Srgrimes		h->eap_msg = 0;
76720425Ssteve	}
7681556Srgrimes	return h;
7691556Srgrimes}
7701556Srgrimes
77120425Sstevestruct rad_handle *
7721556Srgrimesrad_acct_open(void)
7731556Srgrimes{
7741556Srgrimes	struct rad_handle *h;
7751556Srgrimes
7761556Srgrimes	h = rad_open();
7771556Srgrimes	if (h != NULL)
7781556Srgrimes	        h->type = RADIUS_ACCT;
7791556Srgrimes	return h;
7801556Srgrimes}
7811556Srgrimes
7821556Srgrimesstruct rad_handle *
7831556Srgrimesrad_open(void)
7841556Srgrimes{
7851556Srgrimes    return rad_auth_open();
7861556Srgrimes}
7871556Srgrimes
7881556Srgrimesint
7891556Srgrimesrad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
7901556Srgrimes{
7911556Srgrimes	return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
7921556Srgrimes}
7931556Srgrimes
7941556Srgrimesint
7951556Srgrimesrad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
796185231Sstefanf{
79735675Scracauer	int result;
79835675Scracauer
79935675Scracauer	if (!h->request_created) {
80035675Scracauer		generr(h, "Please call rad_create_request()"
8011556Srgrimes		    " before putting attributes");
8021556Srgrimes		return -1;
8031556Srgrimes	}
8041556Srgrimes
8051556Srgrimes	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
8061556Srgrimes		if (type == RAD_EAP_MESSAGE) {
8071556Srgrimes			generr(h, "EAP-Message attribute is not valid"
8081556Srgrimes			    " in accounting requests");
8091556Srgrimes			return -1;
8101556Srgrimes		}
8111556Srgrimes	}
8121556Srgrimes
8131556Srgrimes	/*
8141556Srgrimes	 * When proxying EAP Messages, the Message Authenticator
8151556Srgrimes	 * MUST be present; see RFC 3579.
8161556Srgrimes	 */
81720425Ssteve	if (type == RAD_EAP_MESSAGE) {
8181556Srgrimes		if (rad_put_message_authentic(h) == -1)
81920425Ssteve			return -1;
8201556Srgrimes	}
8211556Srgrimes
8221556Srgrimes	if (type == RAD_USER_PASSWORD) {
8231556Srgrimes		result = put_password_attr(h, type, value, len);
8241556Srgrimes	} else if (type == RAD_MESSAGE_AUTHENTIC) {
8251556Srgrimes		result = rad_put_message_authentic(h);
8261556Srgrimes	} else {
8271556Srgrimes		result = put_raw_attr(h, type, value, len);
8281556Srgrimes		if (result == 0) {
8291556Srgrimes			if (type == RAD_CHAP_PASSWORD)
8301556Srgrimes				h->chap_pass = 1;
8311556Srgrimes			else if (type == RAD_EAP_MESSAGE)
8321556Srgrimes				h->eap_msg = 1;
8331556Srgrimes		}
8341556Srgrimes	}
8351556Srgrimes
8361556Srgrimes	return result;
837157601Sstefanf}
838157601Sstefanf
839157601Sstefanfint
8401556Srgrimesrad_put_int(struct rad_handle *h, int type, u_int32_t value)
8411556Srgrimes{
8421556Srgrimes	u_int32_t nvalue;
843193169Sstefanf
8441556Srgrimes	nvalue = htonl(value);
8451556Srgrimes	return rad_put_attr(h, type, &nvalue, sizeof nvalue);
8461556Srgrimes}
84760592Scracauer
8481556Srgrimesint
8491556Srgrimesrad_put_string(struct rad_handle *h, int type, const char *str)
8501556Srgrimes{
8511556Srgrimes	return rad_put_attr(h, type, str, strlen(str));
8521556Srgrimes}
8531556Srgrimes
8541556Srgrimesint
8551556Srgrimesrad_put_message_authentic(struct rad_handle *h)
8561556Srgrimes{
8571556Srgrimes#ifdef WITH_SSL
8581556Srgrimes	u_char md_zero[MD5_DIGEST_LENGTH];
85920425Ssteve
860157601Sstefanf	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
8611556Srgrimes		generr(h, "Message-Authenticator is not valid"
8621556Srgrimes		    " in accounting requests");
8631556Srgrimes		return -1;
8641556Srgrimes	}
8651556Srgrimes
8661556Srgrimes	if (h->authentic_pos == 0) {
8671556Srgrimes		h->authentic_pos = h->req_len;
8681556Srgrimes		memset(md_zero, 0, sizeof(md_zero));
8691556Srgrimes		return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero,
8701556Srgrimes		    sizeof(md_zero)));
8711556Srgrimes	}
87220425Ssteve	return 0;
8731556Srgrimes#else
87420425Ssteve	generr(h, "Message Authenticator not supported,"
8751556Srgrimes	    " please recompile libradius with SSL support");
8761556Srgrimes	return -1;
8771556Srgrimes#endif
8781556Srgrimes}
8791556Srgrimes
88017987Speter/*
8811556Srgrimes * Returns the response type code on success, or -1 on failure.
8821556Srgrimes */
8831556Srgrimesint
8841556Srgrimesrad_send_request(struct rad_handle *h)
8851556Srgrimes{
8861556Srgrimes	struct timeval timelimit;
8871556Srgrimes	struct timeval tv;
88845916Scracauer	int fd;
8891556Srgrimes	int n;
89045916Scracauer
89145266Scracauer	n = rad_init_send_request(h, &fd, &tv);
89245266Scracauer
89345266Scracauer	if (n != 0)
8941556Srgrimes		return n;
8951556Srgrimes
8961556Srgrimes	gettimeofday(&timelimit, NULL);
8971556Srgrimes	timeradd(&tv, &timelimit, &timelimit);
8981556Srgrimes
8991556Srgrimes	for ( ; ; ) {
9001556Srgrimes		fd_set readfds;
9011556Srgrimes
9021556Srgrimes		FD_ZERO(&readfds);
90354884Scracauer		FD_SET(fd, &readfds);
90454884Scracauer
9051556Srgrimes		n = select(fd + 1, &readfds, NULL, NULL, &tv);
9061556Srgrimes
9071556Srgrimes		if (n == -1) {
9081556Srgrimes			generr(h, "select: %s", strerror(errno));
9091556Srgrimes			return -1;
9101556Srgrimes		}
9111556Srgrimes
9121556Srgrimes		if (!FD_ISSET(fd, &readfds)) {
9131556Srgrimes			/* Compute a new timeout */
9141556Srgrimes			gettimeofday(&tv, NULL);
9151556Srgrimes			timersub(&timelimit, &tv, &tv);
9161556Srgrimes			if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
9171556Srgrimes				/* Continue the select */
91890111Simp				continue;
91917987Speter		}
9201556Srgrimes
9211556Srgrimes		n = rad_continue_send_request(h, n, &fd, &tv);
922159633Sstefanf
92317987Speter		if (n != 0)
92417987Speter			return n;
92517987Speter
9261556Srgrimes		gettimeofday(&timelimit, NULL);
9271556Srgrimes		timeradd(&tv, &timelimit, &timelimit);
9281556Srgrimes	}
9291556Srgrimes}
9301556Srgrimes
9311556Srgrimesconst char *
9321556Srgrimesrad_strerror(struct rad_handle *h)
9331556Srgrimes{
9341556Srgrimes	return h->errmsg;
9351556Srgrimes}
936157601Sstefanf
9371556Srgrimes/*
9381556Srgrimes * Destructively split a string into fields separated by white space.
93917987Speter * `#' at the beginning of a field begins a comment that extends to the
94090111Simp * end of the string.  Fields may be quoted with `"'.  Inside quoted
94117987Speter * strings, the backslash escapes `\"' and `\\' are honored.
94220425Ssteve *
94317987Speter * Pointers to up to the first maxfields fields are stored in the fields
94420425Ssteve * array.  Missing fields get NULL pointers.
94517987Speter *
9461556Srgrimes * The return value is the actual number of fields parsed, and is always
9471556Srgrimes * <= maxfields.
9481556Srgrimes *
9491556Srgrimes * On a syntax error, places a message in the msg string, and returns -1.
9501556Srgrimes */
9511556Srgrimesstatic int
9521556Srgrimessplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
9531556Srgrimes{
9541556Srgrimes	char *p;
9551556Srgrimes	int i;
9561556Srgrimes	static const char ws[] = " \t";
9571556Srgrimes
9581556Srgrimes	for (i = 0;  i < maxfields;  i++)
9591556Srgrimes		fields[i] = NULL;
9601556Srgrimes	p = str;
96117987Speter	i = 0;
96290111Simp	while (*p != '\0') {
96317987Speter		p += strspn(p, ws);
96420425Ssteve		if (*p == '#' || *p == '\0')
9651556Srgrimes			break;
9661556Srgrimes		if (i >= maxfields) {
9671556Srgrimes			snprintf(msg, msglen, "line has too many fields");
9681556Srgrimes			return -1;
9691556Srgrimes		}
9701556Srgrimes		if (*p == '"') {
9711556Srgrimes			char *dst;
9721556Srgrimes
9731556Srgrimes			dst = ++p;
9741556Srgrimes			fields[i] = dst;
975100437Stjr			while (*p != '"') {
976100437Stjr				if (*p == '\\') {
977100437Stjr					p++;
978100437Stjr					if (*p != '"' && *p != '\\' &&
979100437Stjr					    *p != '\0') {
980100437Stjr						snprintf(msg, msglen,
981100437Stjr						    "invalid `\\' escape");
982100437Stjr						return -1;
983100437Stjr					}
984100437Stjr				}
985100437Stjr				if (*p == '\0') {
986151810Sstefanf					snprintf(msg, msglen,
9871556Srgrimes					    "unterminated quoted string");
988100437Stjr					return -1;
989100437Stjr				}
990100437Stjr				*dst++ = *p++;
991100437Stjr			}
992100437Stjr			*dst = '\0';
993100663Stjr			p++;
994151810Sstefanf			if (*fields[i] == '\0') {
995100437Stjr				snprintf(msg, msglen,
996100437Stjr				    "empty quoted string not permitted");
997100437Stjr				return -1;
998100437Stjr			}
999151810Sstefanf			if (*p != '\0' && strspn(p, ws) == 0) {
1000151810Sstefanf				snprintf(msg, msglen, "quoted string not"
1001151810Sstefanf				    " followed by white space");
1002151810Sstefanf				return -1;
1003151810Sstefanf			}
1004151810Sstefanf		} else {
1005100437Stjr			fields[i] = p;
1006100437Stjr			p += strcspn(p, ws);
1007100437Stjr			if (*p != '\0')
1008100437Stjr				*p++ = '\0';
1009100437Stjr		}
1010100437Stjr		i++;
1011100437Stjr	}
1012100437Stjr	return i;
1013151810Sstefanf}
1014151810Sstefanf
1015151810Sstefanfint
1016151810Sstefanfrad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len)
1017151810Sstefanf{
1018100437Stjr	struct vendor_attribute *attr;
1019100437Stjr
1020100437Stjr	attr = (struct vendor_attribute *)*data;
1021100437Stjr	*vendor = ntohl(attr->vendor_value);
1022100437Stjr	*data = attr->attrib_data;
1023100437Stjr	*len = attr->attrib_len - 2;
1024100437Stjr
1025100437Stjr	return (attr->attrib_type);
1026100437Stjr}
1027100437Stjr
1028100437Stjrint
1029100437Stjrrad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
1030100437Stjr    struct in_addr addr)
1031100437Stjr{
1032100437Stjr	return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr,
1033100437Stjr	    sizeof addr.s_addr));
1034100437Stjr}
1035100437Stjr
1036100437Stjrint
10371556Srgrimesrad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
10381556Srgrimes    const void *value, size_t len)
10391556Srgrimes{
10401556Srgrimes	struct vendor_attribute *attr;
104117987Speter	int res;
104290111Simp
104317987Speter	if (!h->request_created) {
104420425Ssteve		generr(h, "Please call rad_create_request()"
10451556Srgrimes		    " before putting attributes");
10461556Srgrimes		return -1;
10471556Srgrimes	}
10481556Srgrimes
104920425Ssteve	if ((attr = malloc(len + 6)) == NULL) {
105020425Ssteve		generr(h, "malloc failure (%d bytes)", len + 6);
105120425Ssteve		return -1;
105220425Ssteve	}
10531556Srgrimes
10541556Srgrimes	attr->vendor_value = htonl(vendor);
10551556Srgrimes	attr->attrib_type = type;
10561556Srgrimes	attr->attrib_len = len + 2;
10571556Srgrimes	memcpy(attr->attrib_data, value, len);
105817987Speter
105990111Simp	res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6);
106017987Speter	free(attr);
106117987Speter	if (res == 0 && vendor == RAD_VENDOR_MICROSOFT
106217987Speter	    && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE
106317987Speter	    || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) {
106417987Speter		h->chap_pass = 1;
106517987Speter	}
106690111Simp	return (res);
106717987Speter}
10681556Srgrimes
10691556Srgrimesint
10701556Srgrimesrad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i)
10711556Srgrimes{
107217987Speter	u_int32_t value;
107390111Simp
107417987Speter	value = htonl(i);
10751556Srgrimes	return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value));
107617987Speter}
107717987Speter
10781556Srgrimesint
10791556Srgrimesrad_put_vendor_string(struct rad_handle *h, int vendor, int type,
10801556Srgrimes    const char *str)
108117987Speter{
108217987Speter	return (rad_put_vendor_attr(h, vendor, type, str, strlen(str)));
10831556Srgrimes}
10841556Srgrimes
10851556Srgrimesssize_t
10861556Srgrimesrad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
10871556Srgrimes{
1088153091Sstefanf	if (len < LEN_AUTH)
1089153091Sstefanf		return (-1);
1090153091Sstefanf	memcpy(buf, h->request + POS_AUTH, LEN_AUTH);
1091153091Sstefanf	if (len > LEN_AUTH)
1092153091Sstefanf		buf[LEN_AUTH] = '\0';
1093153091Sstefanf	return (LEN_AUTH);
1094153091Sstefanf}
1095153091Sstefanf
1096153091Sstefanfu_char *
1097153091Sstefanfrad_demangle(struct rad_handle *h, const void *mangled, size_t mlen)
1098153091Sstefanf{
1099153091Sstefanf	char R[LEN_AUTH];
1100153091Sstefanf	const char *S;
1101153091Sstefanf	int i, Ppos;
1102153091Sstefanf	MD5_CTX Context;
1103153091Sstefanf	u_char b[MD5_DIGEST_LENGTH], *C, *demangled;
1104153091Sstefanf
1105153091Sstefanf	if ((mlen % 16 != 0) || mlen > 128) {
1106153091Sstefanf		generr(h, "Cannot interpret mangled data of length %lu",
1107153091Sstefanf		    (u_long)mlen);
1108153091Sstefanf		return NULL;
1109153091Sstefanf	}
1110153091Sstefanf
1111153091Sstefanf	C = (u_char *)mangled;
1112153091Sstefanf
1113	/* We need the shared secret as Salt */
1114	S = rad_server_secret(h);
1115
1116	/* We need the request authenticator */
1117	if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
1118		generr(h, "Cannot obtain the RADIUS request authenticator");
1119		return NULL;
1120	}
1121
1122	demangled = malloc(mlen);
1123	if (!demangled)
1124		return NULL;
1125
1126	MD5Init(&Context);
1127	MD5Update(&Context, S, strlen(S));
1128	MD5Update(&Context, R, LEN_AUTH);
1129	MD5Final(b, &Context);
1130	Ppos = 0;
1131	while (mlen) {
1132
1133		mlen -= 16;
1134		for (i = 0; i < 16; i++)
1135			demangled[Ppos++] = C[i] ^ b[i];
1136
1137		if (mlen) {
1138			MD5Init(&Context);
1139			MD5Update(&Context, S, strlen(S));
1140			MD5Update(&Context, C, 16);
1141			MD5Final(b, &Context);
1142		}
1143
1144		C += 16;
1145	}
1146
1147	return demangled;
1148}
1149
1150u_char *
1151rad_demangle_mppe_key(struct rad_handle *h, const void *mangled,
1152    size_t mlen, size_t *len)
1153{
1154	char R[LEN_AUTH];    /* variable names as per rfc2548 */
1155	const char *S;
1156	u_char b[MD5_DIGEST_LENGTH], *demangled;
1157	const u_char *A, *C;
1158	MD5_CTX Context;
1159	int Slen, i, Clen, Ppos;
1160	u_char *P;
1161
1162	if (mlen % 16 != SALT_LEN) {
1163		generr(h, "Cannot interpret mangled data of length %lu",
1164		    (u_long)mlen);
1165		return NULL;
1166	}
1167
1168	/* We need the RADIUS Request-Authenticator */
1169	if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
1170		generr(h, "Cannot obtain the RADIUS request authenticator");
1171		return NULL;
1172	}
1173
1174	A = (const u_char *)mangled;      /* Salt comes first */
1175	C = (const u_char *)mangled + SALT_LEN;  /* Then the ciphertext */
1176	Clen = mlen - SALT_LEN;
1177	S = rad_server_secret(h);    /* We need the RADIUS secret */
1178	Slen = strlen(S);
1179	P = alloca(Clen);        /* We derive our plaintext */
1180
1181	MD5Init(&Context);
1182	MD5Update(&Context, S, Slen);
1183	MD5Update(&Context, R, LEN_AUTH);
1184	MD5Update(&Context, A, SALT_LEN);
1185	MD5Final(b, &Context);
1186	Ppos = 0;
1187
1188	while (Clen) {
1189		Clen -= 16;
1190
1191		for (i = 0; i < 16; i++)
1192		    P[Ppos++] = C[i] ^ b[i];
1193
1194		if (Clen) {
1195			MD5Init(&Context);
1196			MD5Update(&Context, S, Slen);
1197			MD5Update(&Context, C, 16);
1198			MD5Final(b, &Context);
1199		}
1200
1201		C += 16;
1202	}
1203
1204	/*
1205	* The resulting plain text consists of a one-byte length, the text and
1206	* maybe some padding.
1207	*/
1208	*len = *P;
1209	if (*len > mlen - 1) {
1210		generr(h, "Mangled data seems to be garbage %d %d",
1211		    *len, mlen-1);
1212		return NULL;
1213	}
1214
1215	if (*len > MPPE_KEY_LEN * 2) {
1216		generr(h, "Key to long (%d) for me max. %d",
1217		    *len, MPPE_KEY_LEN * 2);
1218		return NULL;
1219	}
1220	demangled = malloc(*len);
1221	if (!demangled)
1222		return NULL;
1223
1224	memcpy(demangled, P + 1, *len);
1225	return demangled;
1226}
1227
1228const char *
1229rad_server_secret(struct rad_handle *h)
1230{
1231	return (h->servers[h->srv].secret);
1232}
1233