Initial import from FreeBSD RELENG_4:
[games.git] / lib / libradius / radlib.c
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: src/lib/libradius/radlib.c,v 1.4.2.3 2002/06/17 02:24:57 brian Exp $
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
867 int
868 rad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len)
869 {
870         struct vendor_attribute *attr;
871
872         attr = (struct vendor_attribute *)*data;
873         *vendor = ntohl(attr->vendor_value);
874         *data = attr->attrib_data;
875         *len = attr->attrib_len - 2;
876
877         return (attr->attrib_type);
878 }
879
880 int
881 rad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
882     struct in_addr addr)
883 {
884         return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr,
885             sizeof addr.s_addr));
886 }
887
888 int
889 rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
890     const void *value, size_t len)
891 {
892         struct vendor_attribute *attr;
893         int res;
894
895         if ((attr = malloc(len + 6)) == NULL) {
896                 generr(h, "malloc failure (%d bytes)", len + 6);
897                 return -1;
898         }
899
900         attr->vendor_value = htonl(vendor);
901         attr->attrib_type = type;
902         attr->attrib_len = len + 2;
903         memcpy(attr->attrib_data, value, len);
904
905         res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6);
906         free(attr);
907         if (res == 0 && vendor == RAD_VENDOR_MICROSOFT
908             && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE
909             || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) {
910                 h->chap_pass = 1;
911         }
912         return (res);
913 }
914
915 int
916 rad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i)
917 {
918         u_int32_t value;
919
920         value = htonl(i);
921         return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value));
922 }
923
924 int
925 rad_put_vendor_string(struct rad_handle *h, int vendor, int type,
926     const char *str)
927 {
928         return (rad_put_vendor_attr(h, vendor, type, str, strlen(str)));
929 }
930
931 ssize_t
932 rad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
933 {
934         if (len < LEN_AUTH)
935                 return (-1);
936         memcpy(buf, h->request + POS_AUTH, LEN_AUTH);
937         if (len > LEN_AUTH)
938                 buf[LEN_AUTH] = '\0';
939         return (LEN_AUTH);
940 }
941
942 const char *
943 rad_server_secret(struct rad_handle *h)
944 {
945         return (h->servers[h->srv].secret);
946 }