xref: /freebsd/lib/libradius/radlib.c (revision a14a0223ae1b172e96dd2a1d849e22026a98b692)
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 		h->pass_pos = 0;
67 	}
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;
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 		host = strtok(host, ":");
382 		port_str = strtok(NULL, ":");
383 		if (port_str != NULL) {
384 			port = strtoul(port_str, &end, 10);
385 			if (*end != '\0') {
386 				generr(h, "%s:%d: invalid port", path,
387 				    linenum);
388 				retval = -1;
389 				break;
390 			}
391 		} else
392 			port = 0;
393 		if (timeout_str != NULL) {
394 			timeout = strtoul(timeout_str, &end, 10);
395 			if (*end != '\0') {
396 				generr(h, "%s:%d: invalid timeout", path,
397 				    linenum);
398 				retval = -1;
399 				break;
400 			}
401 		} else
402 			timeout = TIMEOUT;
403 		if (maxtries_str != NULL) {
404 			maxtries = strtoul(maxtries_str, &end, 10);
405 			if (*end != '\0') {
406 				generr(h, "%s:%d: invalid maxtries", path,
407 				    linenum);
408 				retval = -1;
409 				break;
410 			}
411 		} else
412 			maxtries = MAXTRIES;
413 
414 		if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
415 		    -1) {
416 			strcpy(msg, h->errmsg);
417 			generr(h, "%s:%d: %s", path, linenum, msg);
418 			retval = -1;
419 			break;
420 		}
421 	}
422 	/* Clear out the buffer to wipe a possible copy of a shared secret */
423 	memset(buf, 0, sizeof buf);
424 	fclose(fp);
425 	return retval;
426 }
427 
428 /*
429  * rad_init_send_request() must have previously been called.
430  * Returns:
431  *   0     The application should select on *fd with a timeout of tv before
432  *         calling rad_continue_send_request again.
433  *   < 0   Failure
434  *   > 0   Success
435  */
436 int
437 rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
438                           struct timeval *tv)
439 {
440 	int n;
441 
442 	if (selected) {
443 		struct sockaddr_in from;
444 		int fromlen;
445 
446 		fromlen = sizeof from;
447 		h->resp_len = recvfrom(h->fd, h->response,
448 		    MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
449 		if (h->resp_len == -1) {
450 			generr(h, "recvfrom: %s", strerror(errno));
451 			return -1;
452 		}
453 		if (is_valid_response(h, h->srv, &from)) {
454 			h->resp_len = h->response[POS_LENGTH] << 8 |
455 			    h->response[POS_LENGTH+1];
456 			h->resp_pos = POS_ATTRS;
457 			return h->response[POS_CODE];
458 		}
459 	}
460 
461 	if (h->try == h->total_tries) {
462 		generr(h, "No valid RADIUS responses received");
463 		return -1;
464 	}
465 
466 	/*
467          * Scan round-robin to the next server that has some
468          * tries left.  There is guaranteed to be one, or we
469          * would have exited this loop by now.
470 	 */
471 	while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
472 		if (++h->srv >= h->num_servers)
473 			h->srv = 0;
474 
475 	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
476 		/* Insert the request authenticator into the request */
477 		insert_request_authenticator(h, h->srv);
478 	else
479 		/* Insert the scrambled password into the request */
480 		if (h->pass_pos != 0)
481 			insert_scrambled_password(h, h->srv);
482 
483 	/* Send the request */
484 	n = sendto(h->fd, h->request, h->req_len, 0,
485 	    (const struct sockaddr *)&h->servers[h->srv].addr,
486 	    sizeof h->servers[h->srv].addr);
487 	if (n != h->req_len) {
488 		if (n == -1)
489 			generr(h, "sendto: %s", strerror(errno));
490 		else
491 			generr(h, "sendto: short write");
492 		return -1;
493 	}
494 
495 	h->try++;
496 	h->servers[h->srv].num_tries++;
497 	tv->tv_sec = h->servers[h->srv].timeout;
498 	tv->tv_usec = 0;
499 	*fd = h->fd;
500 
501 	return 0;
502 }
503 
504 int
505 rad_create_request(struct rad_handle *h, int code)
506 {
507 	int i;
508 
509 	h->request[POS_CODE] = code;
510 	h->request[POS_IDENT] = ++h->ident;
511 	/* Create a random authenticator */
512 	for (i = 0;  i < LEN_AUTH;  i += 2) {
513 		long r;
514 		r = random();
515 		h->request[POS_AUTH+i] = r;
516 		h->request[POS_AUTH+i+1] = r >> 8;
517 	}
518 	h->req_len = POS_ATTRS;
519 	clear_password(h);
520 	return 0;
521 }
522 
523 struct in_addr
524 rad_cvt_addr(const void *data)
525 {
526 	struct in_addr value;
527 
528 	memcpy(&value.s_addr, data, sizeof value.s_addr);
529 	return value;
530 }
531 
532 u_int32_t
533 rad_cvt_int(const void *data)
534 {
535 	u_int32_t value;
536 
537 	memcpy(&value, data, sizeof value);
538 	return ntohl(value);
539 }
540 
541 char *
542 rad_cvt_string(const void *data, size_t len)
543 {
544 	char *s;
545 
546 	s = malloc(len + 1);
547 	if (s != NULL) {
548 		memcpy(s, data, len);
549 		s[len] = '\0';
550 	}
551 	return s;
552 }
553 
554 /*
555  * Returns the attribute type.  If none are left, returns 0.  On failure,
556  * returns -1.
557  */
558 int
559 rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
560 {
561 	int type;
562 
563 	if (h->resp_pos >= h->resp_len)
564 		return 0;
565 	if (h->resp_pos + 2 > h->resp_len) {
566 		generr(h, "Malformed attribute in response");
567 		return -1;
568 	}
569 	type = h->response[h->resp_pos++];
570 	*len = h->response[h->resp_pos++] - 2;
571 	if (h->resp_pos + *len > h->resp_len) {
572 		generr(h, "Malformed attribute in response");
573 		return -1;
574 	}
575 	*value = &h->response[h->resp_pos];
576 	h->resp_pos += *len;
577 	return type;
578 }
579 
580 /*
581  * Returns -1 on error, 0 to indicate no event and >0 for success
582  */
583 int
584 rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
585 {
586 	int srv;
587 
588 	/* Make sure we have a socket to use */
589 	if (h->fd == -1) {
590 		struct sockaddr_in sin;
591 
592 		if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
593 			generr(h, "Cannot create socket: %s", strerror(errno));
594 			return -1;
595 		}
596 		memset(&sin, 0, sizeof sin);
597 		sin.sin_len = sizeof sin;
598 		sin.sin_family = AF_INET;
599 		sin.sin_addr.s_addr = INADDR_ANY;
600 		sin.sin_port = htons(0);
601 		if (bind(h->fd, (const struct sockaddr *)&sin,
602 		    sizeof sin) == -1) {
603 			generr(h, "bind: %s", strerror(errno));
604 			close(h->fd);
605 			h->fd = -1;
606 			return -1;
607 		}
608 	}
609 
610 	if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
611 		/* Make sure no password given */
612 		if (h->pass_pos || h->chap_pass) {
613 			generr(h, "User or Chap Password in accounting request");
614 			return -1;
615 		}
616 	} else {
617 		/* Make sure the user gave us a password */
618 		if (h->pass_pos == 0 && !h->chap_pass) {
619 			generr(h, "No User or Chap Password attributes given");
620 			return -1;
621 		}
622 		if (h->pass_pos != 0 && h->chap_pass) {
623 			generr(h, "Both User and Chap Password attributes given");
624 			return -1;
625 		}
626 	}
627 
628 	/* Fill in the length field in the message */
629 	h->request[POS_LENGTH] = h->req_len >> 8;
630 	h->request[POS_LENGTH+1] = h->req_len;
631 
632 	/*
633 	 * Count the total number of tries we will make, and zero the
634 	 * counter for each server.
635 	 */
636 	h->total_tries = 0;
637 	for (srv = 0;  srv < h->num_servers;  srv++) {
638 		h->total_tries += h->servers[srv].max_tries;
639 		h->servers[srv].num_tries = 0;
640 	}
641 	if (h->total_tries == 0) {
642 		generr(h, "No RADIUS servers specified");
643 		return -1;
644 	}
645 
646 	h->try = h->srv = 0;
647 
648 	return rad_continue_send_request(h, 0, fd, tv);
649 }
650 
651 /*
652  * Create and initialize a rad_handle structure, and return it to the
653  * caller.  Can fail only if the necessary memory cannot be allocated.
654  * In that case, it returns NULL.
655  */
656 struct rad_handle *
657 rad_auth_open(void)
658 {
659 	struct rad_handle *h;
660 
661 	h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
662 	if (h != NULL) {
663 		srandomdev();
664 		h->fd = -1;
665 		h->num_servers = 0;
666 		h->ident = random();
667 		h->errmsg[0] = '\0';
668 		memset(h->pass, 0, sizeof h->pass);
669 		h->pass_len = 0;
670 		h->pass_pos = 0;
671 		h->chap_pass = 0;
672 		h->type = RADIUS_AUTH;
673 	}
674 	return h;
675 }
676 
677 struct rad_handle *
678 rad_acct_open(void)
679 {
680 	struct rad_handle *h;
681 
682 	h = rad_open();
683 	if (h != NULL)
684 	        h->type = RADIUS_ACCT;
685 	return h;
686 }
687 
688 struct rad_handle *
689 rad_open(void)
690 {
691     return rad_auth_open();
692 }
693 
694 int
695 rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
696 {
697 	return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
698 }
699 
700 int
701 rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
702 {
703 	int result;
704 
705 	if (type == RAD_USER_PASSWORD)
706 		result = put_password_attr(h, type, value, len);
707 	else {
708 		result = put_raw_attr(h, type, value, len);
709 		if (result == 0 && type == RAD_CHAP_PASSWORD)
710 			h->chap_pass = 1;
711 	}
712 
713 	return result;
714 }
715 
716 int
717 rad_put_int(struct rad_handle *h, int type, u_int32_t value)
718 {
719 	u_int32_t nvalue;
720 
721 	nvalue = htonl(value);
722 	return rad_put_attr(h, type, &nvalue, sizeof nvalue);
723 }
724 
725 int
726 rad_put_string(struct rad_handle *h, int type, const char *str)
727 {
728 	return rad_put_attr(h, type, str, strlen(str));
729 }
730 
731 /*
732  * Returns the response type code on success, or -1 on failure.
733  */
734 int
735 rad_send_request(struct rad_handle *h)
736 {
737 	struct timeval timelimit;
738 	struct timeval tv;
739 	int fd;
740 	int n;
741 
742 	n = rad_init_send_request(h, &fd, &tv);
743 
744 	if (n != 0)
745 		return n;
746 
747 	gettimeofday(&timelimit, NULL);
748 	timeradd(&tv, &timelimit, &timelimit);
749 
750 	for ( ; ; ) {
751 		fd_set readfds;
752 
753 		FD_ZERO(&readfds);
754 		FD_SET(fd, &readfds);
755 
756 		n = select(fd + 1, &readfds, NULL, NULL, &tv);
757 
758 		if (n == -1) {
759 			generr(h, "select: %s", strerror(errno));
760 			return -1;
761 		}
762 
763 		if (!FD_ISSET(fd, &readfds)) {
764 			/* Compute a new timeout */
765 			gettimeofday(&tv, NULL);
766 			timersub(&timelimit, &tv, &tv);
767 			if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
768 				/* Continue the select */
769 				continue;
770 		}
771 
772 		n = rad_continue_send_request(h, n, &fd, &tv);
773 
774 		if (n != 0)
775 			return n;
776 
777 		gettimeofday(&timelimit, NULL);
778 		timeradd(&tv, &timelimit, &timelimit);
779 	}
780 }
781 
782 const char *
783 rad_strerror(struct rad_handle *h)
784 {
785 	return h->errmsg;
786 }
787 
788 /*
789  * Destructively split a string into fields separated by white space.
790  * `#' at the beginning of a field begins a comment that extends to the
791  * end of the string.  Fields may be quoted with `"'.  Inside quoted
792  * strings, the backslash escapes `\"' and `\\' are honored.
793  *
794  * Pointers to up to the first maxfields fields are stored in the fields
795  * array.  Missing fields get NULL pointers.
796  *
797  * The return value is the actual number of fields parsed, and is always
798  * <= maxfields.
799  *
800  * On a syntax error, places a message in the msg string, and returns -1.
801  */
802 static int
803 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
804 {
805 	char *p;
806 	int i;
807 	static const char ws[] = " \t";
808 
809 	for (i = 0;  i < maxfields;  i++)
810 		fields[i] = NULL;
811 	p = str;
812 	i = 0;
813 	while (*p != '\0') {
814 		p += strspn(p, ws);
815 		if (*p == '#' || *p == '\0')
816 			break;
817 		if (i >= maxfields) {
818 			snprintf(msg, msglen, "line has too many fields");
819 			return -1;
820 		}
821 		if (*p == '"') {
822 			char *dst;
823 
824 			dst = ++p;
825 			fields[i] = dst;
826 			while (*p != '"') {
827 				if (*p == '\\') {
828 					p++;
829 					if (*p != '"' && *p != '\\' &&
830 					    *p != '\0') {
831 						snprintf(msg, msglen,
832 						    "invalid `\\' escape");
833 						return -1;
834 					}
835 				}
836 				if (*p == '\0') {
837 					snprintf(msg, msglen,
838 					    "unterminated quoted string");
839 					return -1;
840 				}
841 				*dst++ = *p++;
842 			}
843 			*dst = '\0';
844 			p++;
845 			if (*fields[i] == '\0') {
846 				snprintf(msg, msglen,
847 				    "empty quoted string not permitted");
848 				return -1;
849 			}
850 			if (*p != '\0' && strspn(p, ws) == 0) {
851 				snprintf(msg, msglen, "quoted string not"
852 				    " followed by white space");
853 				return -1;
854 			}
855 		} else {
856 			fields[i] = p;
857 			p += strcspn(p, ws);
858 			if (*p != '\0')
859 				*p++ = '\0';
860 		}
861 		i++;
862 	}
863 	return i;
864 }
865