xref: /freebsd/lib/libradius/radlib.c (revision 6990ffd8a95caaba6858ad44ff1b3157d1efba8f)
1 /*-
2  * Copyright 1998 Juniper Networks, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/time.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 
35 #include <errno.h>
36 #include <md5.h>
37 #include <netdb.h>
38 #include <stdarg.h>
39 #include <stddef.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 #include "radlib_private.h"
46 
47 static void	 clear_password(struct rad_handle *);
48 static void	 generr(struct rad_handle *, const char *, ...)
49 		    __printflike(2, 3);
50 static void	 insert_scrambled_password(struct rad_handle *, int);
51 static void	 insert_request_authenticator(struct rad_handle *, int);
52 static int	 is_valid_response(struct rad_handle *, int,
53 		    const struct sockaddr_in *);
54 static int	 put_password_attr(struct rad_handle *, int,
55 		    const void *, size_t);
56 static int	 put_raw_attr(struct rad_handle *, int,
57 		    const void *, size_t);
58 static int	 split(char *, char *[], int, char *, size_t);
59 
60 static void
61 clear_password(struct rad_handle *h)
62 {
63 	if (h->pass_len != 0) {
64 		memset(h->pass, 0, h->pass_len);
65 		h->pass_len = 0;
66 	}
67 	h->pass_pos = 0;
68 }
69 
70 static void
71 generr(struct rad_handle *h, const char *format, ...)
72 {
73 	va_list		 ap;
74 
75 	va_start(ap, format);
76 	vsnprintf(h->errmsg, ERRSIZE, format, ap);
77 	va_end(ap);
78 }
79 
80 static void
81 insert_scrambled_password(struct rad_handle *h, int srv)
82 {
83 	MD5_CTX ctx;
84 	unsigned char md5[16];
85 	const struct rad_server *srvp;
86 	int padded_len;
87 	int pos;
88 
89 	srvp = &h->servers[srv];
90 	padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
91 
92 	memcpy(md5, &h->request[POS_AUTH], LEN_AUTH);
93 	for (pos = 0;  pos < padded_len;  pos += 16) {
94 		int i;
95 
96 		/* Calculate the new scrambler */
97 		MD5Init(&ctx);
98 		MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
99 		MD5Update(&ctx, md5, 16);
100 		MD5Final(md5, &ctx);
101 
102 		/*
103 		 * Mix in the current chunk of the password, and copy
104 		 * the result into the right place in the request.  Also
105 		 * modify the scrambler in place, since we will use this
106 		 * in calculating the scrambler for next time.
107 		 */
108 		for (i = 0;  i < 16;  i++)
109 			h->request[h->pass_pos + pos + i] =
110 			    md5[i] ^= h->pass[pos + i];
111 	}
112 }
113 
114 static void
115 insert_request_authenticator(struct rad_handle *h, int srv)
116 {
117 	MD5_CTX ctx;
118 	const struct rad_server *srvp;
119 
120 	srvp = &h->servers[srv];
121 
122 	/* Create the request authenticator */
123 	MD5Init(&ctx);
124 	MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
125 	MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH);
126 	MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS);
127 	MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
128 	MD5Final(&h->request[POS_AUTH], &ctx);
129 }
130 
131 /*
132  * Return true if the current response is valid for a request to the
133  * specified server.
134  */
135 static int
136 is_valid_response(struct rad_handle *h, int srv,
137     const struct sockaddr_in *from)
138 {
139 	MD5_CTX ctx;
140 	unsigned char md5[16];
141 	const struct rad_server *srvp;
142 	int len;
143 
144 	srvp = &h->servers[srv];
145 
146 	/* Check the source address */
147 	if (from->sin_family != srvp->addr.sin_family ||
148 	    from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
149 	    from->sin_port != srvp->addr.sin_port)
150 		return 0;
151 
152 	/* Check the message length */
153 	if (h->resp_len < POS_ATTRS)
154 		return 0;
155 	len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
156 	if (len > h->resp_len)
157 		return 0;
158 
159 	/* Check the response authenticator */
160 	MD5Init(&ctx);
161 	MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE);
162 	MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
163 	MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS);
164 	MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
165 	MD5Final(md5, &ctx);
166 	if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
167 		return 0;
168 
169 	return 1;
170 }
171 
172 static int
173 put_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
174 {
175 	int padded_len;
176 	int pad_len;
177 
178 	if (h->pass_pos != 0) {
179 		generr(h, "Multiple User-Password attributes specified");
180 		return -1;
181 	}
182 	if (len > PASSSIZE)
183 		len = PASSSIZE;
184 	padded_len = len == 0 ? 16 : (len+15) & ~0xf;
185 	pad_len = padded_len - len;
186 
187 	/*
188 	 * Put in a place-holder attribute containing all zeros, and
189 	 * remember where it is so we can fill it in later.
190 	 */
191 	clear_password(h);
192 	put_raw_attr(h, type, h->pass, padded_len);
193 	h->pass_pos = h->req_len - padded_len;
194 
195 	/* Save the cleartext password, padded as necessary */
196 	memcpy(h->pass, value, len);
197 	h->pass_len = len;
198 	memset(h->pass + len, 0, pad_len);
199 	return 0;
200 }
201 
202 static int
203 put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
204 {
205 	if (len > 253) {
206 		generr(h, "Attribute too long");
207 		return -1;
208 	}
209 	if (h->req_len + 2 + len > MSGSIZE) {
210 		generr(h, "Maximum message length exceeded");
211 		return -1;
212 	}
213 	h->request[h->req_len++] = type;
214 	h->request[h->req_len++] = len + 2;
215 	memcpy(&h->request[h->req_len], value, len);
216 	h->req_len += len;
217 	return 0;
218 }
219 
220 int
221 rad_add_server(struct rad_handle *h, const char *host, int port,
222     const char *secret, int timeout, int tries)
223 {
224 	struct rad_server *srvp;
225 
226 	if (h->num_servers >= MAXSERVERS) {
227 		generr(h, "Too many RADIUS servers specified");
228 		return -1;
229 	}
230 	srvp = &h->servers[h->num_servers];
231 
232 	memset(&srvp->addr, 0, sizeof srvp->addr);
233 	srvp->addr.sin_len = sizeof srvp->addr;
234 	srvp->addr.sin_family = AF_INET;
235 	if (!inet_aton(host, &srvp->addr.sin_addr)) {
236 		struct hostent *hent;
237 
238 		if ((hent = gethostbyname(host)) == NULL) {
239 			generr(h, "%s: host not found", host);
240 			return -1;
241 		}
242 		memcpy(&srvp->addr.sin_addr, hent->h_addr,
243 		    sizeof srvp->addr.sin_addr);
244 	}
245 	if (port != 0)
246 		srvp->addr.sin_port = htons(port);
247 	else {
248 		struct servent *sent;
249 
250 		if (h->type == RADIUS_AUTH)
251 			srvp->addr.sin_port =
252 			    (sent = getservbyname("radius", "udp")) != NULL ?
253 				sent->s_port : htons(RADIUS_PORT);
254 		else
255 			srvp->addr.sin_port =
256 			    (sent = getservbyname("radacct", "udp")) != NULL ?
257 				sent->s_port : htons(RADACCT_PORT);
258 	}
259 	if ((srvp->secret = strdup(secret)) == NULL) {
260 		generr(h, "Out of memory");
261 		return -1;
262 	}
263 	srvp->timeout = timeout;
264 	srvp->max_tries = tries;
265 	srvp->num_tries = 0;
266 	h->num_servers++;
267 	return 0;
268 }
269 
270 void
271 rad_close(struct rad_handle *h)
272 {
273 	int srv;
274 
275 	if (h->fd != -1)
276 		close(h->fd);
277 	for (srv = 0;  srv < h->num_servers;  srv++) {
278 		memset(h->servers[srv].secret, 0,
279 		    strlen(h->servers[srv].secret));
280 		free(h->servers[srv].secret);
281 	}
282 	clear_password(h);
283 	free(h);
284 }
285 
286 int
287 rad_config(struct rad_handle *h, const char *path)
288 {
289 	FILE *fp;
290 	char buf[MAXCONFLINE];
291 	int linenum;
292 	int retval;
293 
294 	if (path == NULL)
295 		path = PATH_RADIUS_CONF;
296 	if ((fp = fopen(path, "r")) == NULL) {
297 		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
298 		return -1;
299 	}
300 	retval = 0;
301 	linenum = 0;
302 	while (fgets(buf, sizeof buf, fp) != NULL) {
303 		int len;
304 		char *fields[5];
305 		int nfields;
306 		char msg[ERRSIZE];
307 		char *type;
308 		char *host, *res;
309 		char *port_str;
310 		char *secret;
311 		char *timeout_str;
312 		char *maxtries_str;
313 		char *end;
314 		char *wanttype;
315 		unsigned long timeout;
316 		unsigned long maxtries;
317 		int port;
318 		int i;
319 
320 		linenum++;
321 		len = strlen(buf);
322 		/* We know len > 0, else fgets would have returned NULL. */
323 		if (buf[len - 1] != '\n') {
324 			if (len == sizeof buf - 1)
325 				generr(h, "%s:%d: line too long", path,
326 				    linenum);
327 			else
328 				generr(h, "%s:%d: missing newline", path,
329 				    linenum);
330 			retval = -1;
331 			break;
332 		}
333 		buf[len - 1] = '\0';
334 
335 		/* Extract the fields from the line. */
336 		nfields = split(buf, fields, 5, msg, sizeof msg);
337 		if (nfields == -1) {
338 			generr(h, "%s:%d: %s", path, linenum, msg);
339 			retval = -1;
340 			break;
341 		}
342 		if (nfields == 0)
343 			continue;
344 		/*
345 		 * The first field should contain "auth" or "acct" for
346 		 * authentication or accounting, respectively.  But older
347 		 * versions of the file didn't have that field.  Default
348 		 * it to "auth" for backward compatibility.
349 		 */
350 		if (strcmp(fields[0], "auth") != 0 &&
351 		    strcmp(fields[0], "acct") != 0) {
352 			if (nfields >= 5) {
353 				generr(h, "%s:%d: invalid service type", path,
354 				    linenum);
355 				retval = -1;
356 				break;
357 			}
358 			nfields++;
359 			for (i = nfields;  --i > 0;  )
360 				fields[i] = fields[i - 1];
361 			fields[0] = "auth";
362 		}
363 		if (nfields < 3) {
364 			generr(h, "%s:%d: missing shared secret", path,
365 			    linenum);
366 			retval = -1;
367 			break;
368 		}
369 		type = fields[0];
370 		host = fields[1];
371 		secret = fields[2];
372 		timeout_str = fields[3];
373 		maxtries_str = fields[4];
374 
375 		/* Ignore the line if it is for the wrong service type. */
376 		wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
377 		if (strcmp(type, wanttype) != 0)
378 			continue;
379 
380 		/* Parse and validate the fields. */
381 		res = host;
382 		host = strsep(&res, ":");
383 		port_str = strsep(&res, ":");
384 		if (port_str != NULL) {
385 			port = strtoul(port_str, &end, 10);
386 			if (*end != '\0') {
387 				generr(h, "%s:%d: invalid port", path,
388 				    linenum);
389 				retval = -1;
390 				break;
391 			}
392 		} else
393 			port = 0;
394 		if (timeout_str != NULL) {
395 			timeout = strtoul(timeout_str, &end, 10);
396 			if (*end != '\0') {
397 				generr(h, "%s:%d: invalid timeout", path,
398 				    linenum);
399 				retval = -1;
400 				break;
401 			}
402 		} else
403 			timeout = TIMEOUT;
404 		if (maxtries_str != NULL) {
405 			maxtries = strtoul(maxtries_str, &end, 10);
406 			if (*end != '\0') {
407 				generr(h, "%s:%d: invalid maxtries", path,
408 				    linenum);
409 				retval = -1;
410 				break;
411 			}
412 		} else
413 			maxtries = MAXTRIES;
414 
415 		if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
416 		    -1) {
417 			strcpy(msg, h->errmsg);
418 			generr(h, "%s:%d: %s", path, linenum, msg);
419 			retval = -1;
420 			break;
421 		}
422 	}
423 	/* Clear out the buffer to wipe a possible copy of a shared secret */
424 	memset(buf, 0, sizeof buf);
425 	fclose(fp);
426 	return retval;
427 }
428 
429 /*
430  * rad_init_send_request() must have previously been called.
431  * Returns:
432  *   0     The application should select on *fd with a timeout of tv before
433  *         calling rad_continue_send_request again.
434  *   < 0   Failure
435  *   > 0   Success
436  */
437 int
438 rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
439                           struct timeval *tv)
440 {
441 	int n;
442 
443 	if (selected) {
444 		struct sockaddr_in from;
445 		int fromlen;
446 
447 		fromlen = sizeof from;
448 		h->resp_len = recvfrom(h->fd, h->response,
449 		    MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
450 		if (h->resp_len == -1) {
451 			generr(h, "recvfrom: %s", strerror(errno));
452 			return -1;
453 		}
454 		if (is_valid_response(h, h->srv, &from)) {
455 			h->resp_len = h->response[POS_LENGTH] << 8 |
456 			    h->response[POS_LENGTH+1];
457 			h->resp_pos = POS_ATTRS;
458 			return h->response[POS_CODE];
459 		}
460 	}
461 
462 	if (h->try == h->total_tries) {
463 		generr(h, "No valid RADIUS responses received");
464 		return -1;
465 	}
466 
467 	/*
468          * Scan round-robin to the next server that has some
469          * tries left.  There is guaranteed to be one, or we
470          * would have exited this loop by now.
471 	 */
472 	while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
473 		if (++h->srv >= h->num_servers)
474 			h->srv = 0;
475 
476 	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
477 		/* Insert the request authenticator into the request */
478 		insert_request_authenticator(h, h->srv);
479 	else
480 		/* Insert the scrambled password into the request */
481 		if (h->pass_pos != 0)
482 			insert_scrambled_password(h, h->srv);
483 
484 	/* Send the request */
485 	n = sendto(h->fd, h->request, h->req_len, 0,
486 	    (const struct sockaddr *)&h->servers[h->srv].addr,
487 	    sizeof h->servers[h->srv].addr);
488 	if (n != h->req_len) {
489 		if (n == -1)
490 			generr(h, "sendto: %s", strerror(errno));
491 		else
492 			generr(h, "sendto: short write");
493 		return -1;
494 	}
495 
496 	h->try++;
497 	h->servers[h->srv].num_tries++;
498 	tv->tv_sec = h->servers[h->srv].timeout;
499 	tv->tv_usec = 0;
500 	*fd = h->fd;
501 
502 	return 0;
503 }
504 
505 int
506 rad_create_request(struct rad_handle *h, int code)
507 {
508 	int i;
509 
510 	h->request[POS_CODE] = code;
511 	h->request[POS_IDENT] = ++h->ident;
512 	/* Create a random authenticator */
513 	for (i = 0;  i < LEN_AUTH;  i += 2) {
514 		long r;
515 		r = random();
516 		h->request[POS_AUTH+i] = r;
517 		h->request[POS_AUTH+i+1] = r >> 8;
518 	}
519 	h->req_len = POS_ATTRS;
520 	clear_password(h);
521 	return 0;
522 }
523 
524 struct in_addr
525 rad_cvt_addr(const void *data)
526 {
527 	struct in_addr value;
528 
529 	memcpy(&value.s_addr, data, sizeof value.s_addr);
530 	return value;
531 }
532 
533 u_int32_t
534 rad_cvt_int(const void *data)
535 {
536 	u_int32_t value;
537 
538 	memcpy(&value, data, sizeof value);
539 	return ntohl(value);
540 }
541 
542 char *
543 rad_cvt_string(const void *data, size_t len)
544 {
545 	char *s;
546 
547 	s = malloc(len + 1);
548 	if (s != NULL) {
549 		memcpy(s, data, len);
550 		s[len] = '\0';
551 	}
552 	return s;
553 }
554 
555 /*
556  * Returns the attribute type.  If none are left, returns 0.  On failure,
557  * returns -1.
558  */
559 int
560 rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
561 {
562 	int type;
563 
564 	if (h->resp_pos >= h->resp_len)
565 		return 0;
566 	if (h->resp_pos + 2 > h->resp_len) {
567 		generr(h, "Malformed attribute in response");
568 		return -1;
569 	}
570 	type = h->response[h->resp_pos++];
571 	*len = h->response[h->resp_pos++] - 2;
572 	if (h->resp_pos + *len > h->resp_len) {
573 		generr(h, "Malformed attribute in response");
574 		return -1;
575 	}
576 	*value = &h->response[h->resp_pos];
577 	h->resp_pos += *len;
578 	return type;
579 }
580 
581 /*
582  * Returns -1 on error, 0 to indicate no event and >0 for success
583  */
584 int
585 rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
586 {
587 	int srv;
588 
589 	/* Make sure we have a socket to use */
590 	if (h->fd == -1) {
591 		struct sockaddr_in sin;
592 
593 		if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
594 			generr(h, "Cannot create socket: %s", strerror(errno));
595 			return -1;
596 		}
597 		memset(&sin, 0, sizeof sin);
598 		sin.sin_len = sizeof sin;
599 		sin.sin_family = AF_INET;
600 		sin.sin_addr.s_addr = INADDR_ANY;
601 		sin.sin_port = htons(0);
602 		if (bind(h->fd, (const struct sockaddr *)&sin,
603 		    sizeof sin) == -1) {
604 			generr(h, "bind: %s", strerror(errno));
605 			close(h->fd);
606 			h->fd = -1;
607 			return -1;
608 		}
609 	}
610 
611 	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
612 		/* Make sure no password given */
613 		if (h->pass_pos || h->chap_pass) {
614 			generr(h, "User or Chap Password in accounting request");
615 			return -1;
616 		}
617 	} else {
618 		/* Make sure the user gave us a password */
619 		if (h->pass_pos == 0 && !h->chap_pass) {
620 			generr(h, "No User or Chap Password attributes given");
621 			return -1;
622 		}
623 		if (h->pass_pos != 0 && h->chap_pass) {
624 			generr(h, "Both User and Chap Password attributes given");
625 			return -1;
626 		}
627 	}
628 
629 	/* Fill in the length field in the message */
630 	h->request[POS_LENGTH] = h->req_len >> 8;
631 	h->request[POS_LENGTH+1] = h->req_len;
632 
633 	/*
634 	 * Count the total number of tries we will make, and zero the
635 	 * counter for each server.
636 	 */
637 	h->total_tries = 0;
638 	for (srv = 0;  srv < h->num_servers;  srv++) {
639 		h->total_tries += h->servers[srv].max_tries;
640 		h->servers[srv].num_tries = 0;
641 	}
642 	if (h->total_tries == 0) {
643 		generr(h, "No RADIUS servers specified");
644 		return -1;
645 	}
646 
647 	h->try = h->srv = 0;
648 
649 	return rad_continue_send_request(h, 0, fd, tv);
650 }
651 
652 /*
653  * Create and initialize a rad_handle structure, and return it to the
654  * caller.  Can fail only if the necessary memory cannot be allocated.
655  * In that case, it returns NULL.
656  */
657 struct rad_handle *
658 rad_auth_open(void)
659 {
660 	struct rad_handle *h;
661 
662 	h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
663 	if (h != NULL) {
664 		srandomdev();
665 		h->fd = -1;
666 		h->num_servers = 0;
667 		h->ident = random();
668 		h->errmsg[0] = '\0';
669 		memset(h->pass, 0, sizeof h->pass);
670 		h->pass_len = 0;
671 		h->pass_pos = 0;
672 		h->chap_pass = 0;
673 		h->type = RADIUS_AUTH;
674 	}
675 	return h;
676 }
677 
678 struct rad_handle *
679 rad_acct_open(void)
680 {
681 	struct rad_handle *h;
682 
683 	h = rad_open();
684 	if (h != NULL)
685 	        h->type = RADIUS_ACCT;
686 	return h;
687 }
688 
689 struct rad_handle *
690 rad_open(void)
691 {
692     return rad_auth_open();
693 }
694 
695 int
696 rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
697 {
698 	return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
699 }
700 
701 int
702 rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
703 {
704 	int result;
705 
706 	if (type == RAD_USER_PASSWORD)
707 		result = put_password_attr(h, type, value, len);
708 	else {
709 		result = put_raw_attr(h, type, value, len);
710 		if (result == 0 && type == RAD_CHAP_PASSWORD)
711 			h->chap_pass = 1;
712 	}
713 
714 	return result;
715 }
716 
717 int
718 rad_put_int(struct rad_handle *h, int type, u_int32_t value)
719 {
720 	u_int32_t nvalue;
721 
722 	nvalue = htonl(value);
723 	return rad_put_attr(h, type, &nvalue, sizeof nvalue);
724 }
725 
726 int
727 rad_put_string(struct rad_handle *h, int type, const char *str)
728 {
729 	return rad_put_attr(h, type, str, strlen(str));
730 }
731 
732 /*
733  * Returns the response type code on success, or -1 on failure.
734  */
735 int
736 rad_send_request(struct rad_handle *h)
737 {
738 	struct timeval timelimit;
739 	struct timeval tv;
740 	int fd;
741 	int n;
742 
743 	n = rad_init_send_request(h, &fd, &tv);
744 
745 	if (n != 0)
746 		return n;
747 
748 	gettimeofday(&timelimit, NULL);
749 	timeradd(&tv, &timelimit, &timelimit);
750 
751 	for ( ; ; ) {
752 		fd_set readfds;
753 
754 		FD_ZERO(&readfds);
755 		FD_SET(fd, &readfds);
756 
757 		n = select(fd + 1, &readfds, NULL, NULL, &tv);
758 
759 		if (n == -1) {
760 			generr(h, "select: %s", strerror(errno));
761 			return -1;
762 		}
763 
764 		if (!FD_ISSET(fd, &readfds)) {
765 			/* Compute a new timeout */
766 			gettimeofday(&tv, NULL);
767 			timersub(&timelimit, &tv, &tv);
768 			if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
769 				/* Continue the select */
770 				continue;
771 		}
772 
773 		n = rad_continue_send_request(h, n, &fd, &tv);
774 
775 		if (n != 0)
776 			return n;
777 
778 		gettimeofday(&timelimit, NULL);
779 		timeradd(&tv, &timelimit, &timelimit);
780 	}
781 }
782 
783 const char *
784 rad_strerror(struct rad_handle *h)
785 {
786 	return h->errmsg;
787 }
788 
789 /*
790  * Destructively split a string into fields separated by white space.
791  * `#' at the beginning of a field begins a comment that extends to the
792  * end of the string.  Fields may be quoted with `"'.  Inside quoted
793  * strings, the backslash escapes `\"' and `\\' are honored.
794  *
795  * Pointers to up to the first maxfields fields are stored in the fields
796  * array.  Missing fields get NULL pointers.
797  *
798  * The return value is the actual number of fields parsed, and is always
799  * <= maxfields.
800  *
801  * On a syntax error, places a message in the msg string, and returns -1.
802  */
803 static int
804 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
805 {
806 	char *p;
807 	int i;
808 	static const char ws[] = " \t";
809 
810 	for (i = 0;  i < maxfields;  i++)
811 		fields[i] = NULL;
812 	p = str;
813 	i = 0;
814 	while (*p != '\0') {
815 		p += strspn(p, ws);
816 		if (*p == '#' || *p == '\0')
817 			break;
818 		if (i >= maxfields) {
819 			snprintf(msg, msglen, "line has too many fields");
820 			return -1;
821 		}
822 		if (*p == '"') {
823 			char *dst;
824 
825 			dst = ++p;
826 			fields[i] = dst;
827 			while (*p != '"') {
828 				if (*p == '\\') {
829 					p++;
830 					if (*p != '"' && *p != '\\' &&
831 					    *p != '\0') {
832 						snprintf(msg, msglen,
833 						    "invalid `\\' escape");
834 						return -1;
835 					}
836 				}
837 				if (*p == '\0') {
838 					snprintf(msg, msglen,
839 					    "unterminated quoted string");
840 					return -1;
841 				}
842 				*dst++ = *p++;
843 			}
844 			*dst = '\0';
845 			p++;
846 			if (*fields[i] == '\0') {
847 				snprintf(msg, msglen,
848 				    "empty quoted string not permitted");
849 				return -1;
850 			}
851 			if (*p != '\0' && strspn(p, ws) == 0) {
852 				snprintf(msg, msglen, "quoted string not"
853 				    " followed by white space");
854 				return -1;
855 			}
856 		} else {
857 			fields[i] = p;
858 			p += strcspn(p, ws);
859 			if (*p != '\0')
860 				*p++ = '\0';
861 		}
862 		i++;
863 	}
864 	return i;
865 }
866