Fix buildworld: add -I${.CURDIR} so aliases_parse.c can find dma.h.
[dragonfly.git] / lib / libradius / radlib.c
blob2f5eeba9ecd4cd446e50d3f49706124848badb34
1 /*-
2 * Copyright 1998 Juniper Networks, Inc.
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/lib/libradius/radlib.c,v 1.4.2.3 2002/06/17 02:24:57 brian Exp $
27 * $DragonFly: src/lib/libradius/radlib.c,v 1.4 2005/04/20 20:45:57 joerg Exp $
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/time.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
36 #include <errno.h>
37 #include <md5.h>
38 #include <netdb.h>
39 #include <stdarg.h>
40 #include <stddef.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
46 #include "radlib_private.h"
48 static void clear_password(struct rad_handle *);
49 static void generr(struct rad_handle *, const char *, ...)
50 __printflike(2, 3);
51 static void insert_scrambled_password(struct rad_handle *, int);
52 static void insert_request_authenticator(struct rad_handle *, int);
53 static int is_valid_response(struct rad_handle *, int,
54 const struct sockaddr_in *);
55 static int put_password_attr(struct rad_handle *, int,
56 const void *, size_t);
57 static int put_raw_attr(struct rad_handle *, int,
58 const void *, size_t);
59 static int split(char *, const char *[], int, char *, size_t);
61 static void
62 clear_password(struct rad_handle *h)
64 if (h->pass_len != 0) {
65 memset(h->pass, 0, h->pass_len);
66 h->pass_len = 0;
68 h->pass_pos = 0;
71 static void
72 generr(struct rad_handle *h, const char *format, ...)
74 va_list ap;
76 va_start(ap, format);
77 vsnprintf(h->errmsg, ERRSIZE, format, ap);
78 va_end(ap);
81 static void
82 insert_scrambled_password(struct rad_handle *h, int srv)
84 MD5_CTX ctx;
85 unsigned char md5[16];
86 const struct rad_server *srvp;
87 int padded_len;
88 int pos;
90 srvp = &h->servers[srv];
91 padded_len = h->pass_len == 0 ? 16 : (h->pass_len+15) & ~0xf;
93 memcpy(md5, &h->request[POS_AUTH], LEN_AUTH);
94 for (pos = 0; pos < padded_len; pos += 16) {
95 int i;
97 /* Calculate the new scrambler */
98 MD5Init(&ctx);
99 MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
100 MD5Update(&ctx, md5, 16);
101 MD5Final(md5, &ctx);
104 * Mix in the current chunk of the password, and copy
105 * the result into the right place in the request. Also
106 * modify the scrambler in place, since we will use this
107 * in calculating the scrambler for next time.
109 for (i = 0; i < 16; i++)
110 h->request[h->pass_pos + pos + i] =
111 md5[i] ^= h->pass[pos + i];
115 static void
116 insert_request_authenticator(struct rad_handle *h, int srv)
118 MD5_CTX ctx;
119 const struct rad_server *srvp;
121 srvp = &h->servers[srv];
123 /* Create the request authenticator */
124 MD5Init(&ctx);
125 MD5Update(&ctx, &h->request[POS_CODE], POS_AUTH - POS_CODE);
126 MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, LEN_AUTH), LEN_AUTH);
127 MD5Update(&ctx, &h->request[POS_ATTRS], h->req_len - POS_ATTRS);
128 MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
129 MD5Final(&h->request[POS_AUTH], &ctx);
133 * Return true if the current response is valid for a request to the
134 * specified server.
136 static int
137 is_valid_response(struct rad_handle *h, int srv,
138 const struct sockaddr_in *from)
140 MD5_CTX ctx;
141 unsigned char md5[16];
142 const struct rad_server *srvp;
143 int len;
145 srvp = &h->servers[srv];
147 /* Check the source address */
148 if (from->sin_family != srvp->addr.sin_family ||
149 from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
150 from->sin_port != srvp->addr.sin_port)
151 return 0;
153 /* Check the message length */
154 if (h->resp_len < POS_ATTRS)
155 return 0;
156 len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
157 if (len > h->resp_len)
158 return 0;
160 /* Check the response authenticator */
161 MD5Init(&ctx);
162 MD5Update(&ctx, &h->response[POS_CODE], POS_AUTH - POS_CODE);
163 MD5Update(&ctx, &h->request[POS_AUTH], LEN_AUTH);
164 MD5Update(&ctx, &h->response[POS_ATTRS], len - POS_ATTRS);
165 MD5Update(&ctx, srvp->secret, strlen(srvp->secret));
166 MD5Final(md5, &ctx);
167 if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
168 return 0;
170 return 1;
173 static int
174 put_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
176 int padded_len;
177 int pad_len;
179 if (h->pass_pos != 0) {
180 generr(h, "Multiple User-Password attributes specified");
181 return -1;
183 if (len > PASSSIZE)
184 len = PASSSIZE;
185 padded_len = len == 0 ? 16 : (len+15) & ~0xf;
186 pad_len = padded_len - len;
189 * Put in a place-holder attribute containing all zeros, and
190 * remember where it is so we can fill it in later.
192 clear_password(h);
193 put_raw_attr(h, type, h->pass, padded_len);
194 h->pass_pos = h->req_len - padded_len;
196 /* Save the cleartext password, padded as necessary */
197 memcpy(h->pass, value, len);
198 h->pass_len = len;
199 memset(h->pass + len, 0, pad_len);
200 return 0;
203 static int
204 put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
206 if (len > 253) {
207 generr(h, "Attribute too long");
208 return -1;
210 if (h->req_len + 2 + len > MSGSIZE) {
211 generr(h, "Maximum message length exceeded");
212 return -1;
214 h->request[h->req_len++] = type;
215 h->request[h->req_len++] = len + 2;
216 memcpy(&h->request[h->req_len], value, len);
217 h->req_len += len;
218 return 0;
222 rad_add_server(struct rad_handle *h, const char *host, int port,
223 const char *secret, int timeout, int tries)
225 struct rad_server *srvp;
227 if (h->num_servers >= MAXSERVERS) {
228 generr(h, "Too many RADIUS servers specified");
229 return -1;
231 srvp = &h->servers[h->num_servers];
233 memset(&srvp->addr, 0, sizeof srvp->addr);
234 srvp->addr.sin_len = sizeof srvp->addr;
235 srvp->addr.sin_family = AF_INET;
236 if (!inet_aton(host, &srvp->addr.sin_addr)) {
237 struct hostent *hent;
239 if ((hent = gethostbyname(host)) == NULL) {
240 generr(h, "%s: host not found", host);
241 return -1;
243 memcpy(&srvp->addr.sin_addr, hent->h_addr,
244 sizeof srvp->addr.sin_addr);
246 if (port != 0)
247 srvp->addr.sin_port = htons(port);
248 else {
249 struct servent *sent;
251 if (h->type == RADIUS_AUTH)
252 srvp->addr.sin_port =
253 (sent = getservbyname("radius", "udp")) != NULL ?
254 sent->s_port : htons(RADIUS_PORT);
255 else
256 srvp->addr.sin_port =
257 (sent = getservbyname("radacct", "udp")) != NULL ?
258 sent->s_port : htons(RADACCT_PORT);
260 if ((srvp->secret = strdup(secret)) == NULL) {
261 generr(h, "Out of memory");
262 return -1;
264 srvp->timeout = timeout;
265 srvp->max_tries = tries;
266 srvp->num_tries = 0;
267 h->num_servers++;
268 return 0;
271 void
272 rad_close(struct rad_handle *h)
274 int srv;
276 if (h->fd != -1)
277 close(h->fd);
278 for (srv = 0; srv < h->num_servers; srv++) {
279 memset(h->servers[srv].secret, 0,
280 strlen(h->servers[srv].secret));
281 free(h->servers[srv].secret);
283 clear_password(h);
284 free(h);
288 rad_config(struct rad_handle *h, const char *path)
290 FILE *fp;
291 char buf[MAXCONFLINE];
292 int linenum;
293 int retval;
295 if (path == NULL)
296 path = PATH_RADIUS_CONF;
297 if ((fp = fopen(path, "r")) == NULL) {
298 generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
299 return -1;
301 retval = 0;
302 linenum = 0;
303 while (fgets(buf, sizeof buf, fp) != NULL) {
304 int len;
305 const char *fields[5];
306 int nfields;
307 char msg[ERRSIZE];
308 const char *ohost, *secret, *timeout_str, *maxtries_str;
309 const char *type, *wanttype;
310 char *host;
311 char *res;
312 char *port_str;
313 char *end;
314 unsigned long timeout;
315 unsigned long maxtries;
316 int port;
317 int i;
319 linenum++;
320 len = strlen(buf);
321 /* We know len > 0, else fgets would have returned NULL. */
322 if (buf[len - 1] != '\n') {
323 if (len == sizeof buf - 1)
324 generr(h, "%s:%d: line too long", path,
325 linenum);
326 else
327 generr(h, "%s:%d: missing newline", path,
328 linenum);
329 retval = -1;
330 break;
332 buf[len - 1] = '\0';
334 /* Extract the fields from the line. */
335 nfields = split(buf, fields, 5, msg, sizeof msg);
336 if (nfields == -1) {
337 generr(h, "%s:%d: %s", path, linenum, msg);
338 retval = -1;
339 break;
341 if (nfields == 0)
342 continue;
344 * The first field should contain "auth" or "acct" for
345 * authentication or accounting, respectively. But older
346 * versions of the file didn't have that field. Default
347 * it to "auth" for backward compatibility.
349 if (strcmp(fields[0], "auth") != 0 &&
350 strcmp(fields[0], "acct") != 0) {
351 if (nfields >= 5) {
352 generr(h, "%s:%d: invalid service type", path,
353 linenum);
354 retval = -1;
355 break;
357 nfields++;
358 for (i = nfields; --i > 0; )
359 fields[i] = fields[i - 1];
360 fields[0] = "auth";
362 if (nfields < 3) {
363 generr(h, "%s:%d: missing shared secret", path,
364 linenum);
365 retval = -1;
366 break;
368 type = fields[0];
369 ohost = fields[1];
370 secret = fields[2];
371 timeout_str = fields[3];
372 maxtries_str = fields[4];
374 /* Ignore the line if it is for the wrong service type. */
375 wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
376 if (strcmp(type, wanttype) != 0)
377 continue;
379 /* Parse and validate the fields. */
380 if ((res = strdup(ohost)) == NULL) {
381 generr(h, "%s:%d: malloc failed", path, linenum);
382 retval = -1;
383 break;
385 host = strsep(&res, ":");
386 port_str = strsep(&res, ":");
387 if (port_str != NULL) {
388 port = strtoul(port_str, &end, 10);
389 if (*end != '\0') {
390 free(host);
391 generr(h, "%s:%d: invalid port", path,
392 linenum);
393 retval = -1;
394 break;
396 } else
397 port = 0;
398 if (timeout_str != NULL) {
399 timeout = strtoul(timeout_str, &end, 10);
400 if (*end != '\0') {
401 free(host);
402 generr(h, "%s:%d: invalid timeout", path,
403 linenum);
404 retval = -1;
405 break;
407 } else
408 timeout = TIMEOUT;
409 if (maxtries_str != NULL) {
410 maxtries = strtoul(maxtries_str, &end, 10);
411 if (*end != '\0') {
412 free(host);
413 generr(h, "%s:%d: invalid maxtries", path,
414 linenum);
415 retval = -1;
416 break;
418 } else
419 maxtries = MAXTRIES;
421 if (rad_add_server(h, host, port, secret, timeout, maxtries) ==
422 -1) {
423 free(host);
424 strcpy(msg, h->errmsg);
425 generr(h, "%s:%d: %s", path, linenum, msg);
426 retval = -1;
427 break;
429 free(host);
431 /* Clear out the buffer to wipe a possible copy of a shared secret */
432 memset(buf, 0, sizeof buf);
433 fclose(fp);
434 return retval;
438 * rad_init_send_request() must have previously been called.
439 * Returns:
440 * 0 The application should select on *fd with a timeout of tv before
441 * calling rad_continue_send_request again.
442 * < 0 Failure
443 * > 0 Success
446 rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
447 struct timeval *tv)
449 int n;
451 if (selected) {
452 struct sockaddr_in from;
453 int fromlen;
455 fromlen = sizeof from;
456 h->resp_len = recvfrom(h->fd, h->response,
457 MSGSIZE, MSG_WAITALL, (struct sockaddr *)&from, &fromlen);
458 if (h->resp_len == -1) {
459 generr(h, "recvfrom: %s", strerror(errno));
460 return -1;
462 if (is_valid_response(h, h->srv, &from)) {
463 h->resp_len = h->response[POS_LENGTH] << 8 |
464 h->response[POS_LENGTH+1];
465 h->resp_pos = POS_ATTRS;
466 return h->response[POS_CODE];
470 if (h->try == h->total_tries) {
471 generr(h, "No valid RADIUS responses received");
472 return -1;
476 * Scan round-robin to the next server that has some
477 * tries left. There is guaranteed to be one, or we
478 * would have exited this loop by now.
480 while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
481 if (++h->srv >= h->num_servers)
482 h->srv = 0;
484 if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
485 /* Insert the request authenticator into the request */
486 insert_request_authenticator(h, h->srv);
487 else
488 /* Insert the scrambled password into the request */
489 if (h->pass_pos != 0)
490 insert_scrambled_password(h, h->srv);
492 /* Send the request */
493 n = sendto(h->fd, h->request, h->req_len, 0,
494 (const struct sockaddr *)&h->servers[h->srv].addr,
495 sizeof h->servers[h->srv].addr);
496 if (n != h->req_len) {
497 if (n == -1)
498 generr(h, "sendto: %s", strerror(errno));
499 else
500 generr(h, "sendto: short write");
501 return -1;
504 h->try++;
505 h->servers[h->srv].num_tries++;
506 tv->tv_sec = h->servers[h->srv].timeout;
507 tv->tv_usec = 0;
508 *fd = h->fd;
510 return 0;
514 rad_create_request(struct rad_handle *h, int code)
516 int i;
518 h->request[POS_CODE] = code;
519 h->request[POS_IDENT] = ++h->ident;
520 /* Create a random authenticator */
521 for (i = 0; i < LEN_AUTH; i += 2) {
522 long r;
523 r = random();
524 h->request[POS_AUTH+i] = r;
525 h->request[POS_AUTH+i+1] = r >> 8;
527 h->req_len = POS_ATTRS;
528 clear_password(h);
529 return 0;
532 struct in_addr
533 rad_cvt_addr(const void *data)
535 struct in_addr value;
537 memcpy(&value.s_addr, data, sizeof value.s_addr);
538 return value;
541 u_int32_t
542 rad_cvt_int(const void *data)
544 u_int32_t value;
546 memcpy(&value, data, sizeof value);
547 return ntohl(value);
550 char *
551 rad_cvt_string(const void *data, size_t len)
553 char *s;
555 s = malloc(len + 1);
556 if (s != NULL) {
557 memcpy(s, data, len);
558 s[len] = '\0';
560 return s;
564 * Returns the attribute type. If none are left, returns 0. On failure,
565 * returns -1.
568 rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
570 int type;
572 if (h->resp_pos >= h->resp_len)
573 return 0;
574 if (h->resp_pos + 2 > h->resp_len) {
575 generr(h, "Malformed attribute in response");
576 return -1;
578 type = h->response[h->resp_pos++];
579 *len = h->response[h->resp_pos++] - 2;
580 if (h->resp_pos + (int)*len > h->resp_len) {
581 generr(h, "Malformed attribute in response");
582 return -1;
584 *value = &h->response[h->resp_pos];
585 h->resp_pos += *len;
586 return type;
590 * Returns -1 on error, 0 to indicate no event and >0 for success
593 rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
595 int srv;
597 /* Make sure we have a socket to use */
598 if (h->fd == -1) {
599 struct sockaddr_in sin;
601 if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
602 generr(h, "Cannot create socket: %s", strerror(errno));
603 return -1;
605 memset(&sin, 0, sizeof sin);
606 sin.sin_len = sizeof sin;
607 sin.sin_family = AF_INET;
608 sin.sin_addr.s_addr = INADDR_ANY;
609 sin.sin_port = htons(0);
610 if (bind(h->fd, (const struct sockaddr *)&sin,
611 sizeof sin) == -1) {
612 generr(h, "bind: %s", strerror(errno));
613 close(h->fd);
614 h->fd = -1;
615 return -1;
619 if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
620 /* Make sure no password given */
621 if (h->pass_pos || h->chap_pass) {
622 generr(h, "User or Chap Password in accounting request");
623 return -1;
625 } else {
626 /* Make sure the user gave us a password */
627 if (h->pass_pos == 0 && !h->chap_pass) {
628 generr(h, "No User or Chap Password attributes given");
629 return -1;
631 if (h->pass_pos != 0 && h->chap_pass) {
632 generr(h, "Both User and Chap Password attributes given");
633 return -1;
637 /* Fill in the length field in the message */
638 h->request[POS_LENGTH] = h->req_len >> 8;
639 h->request[POS_LENGTH+1] = h->req_len;
642 * Count the total number of tries we will make, and zero the
643 * counter for each server.
645 h->total_tries = 0;
646 for (srv = 0; srv < h->num_servers; srv++) {
647 h->total_tries += h->servers[srv].max_tries;
648 h->servers[srv].num_tries = 0;
650 if (h->total_tries == 0) {
651 generr(h, "No RADIUS servers specified");
652 return -1;
655 h->try = h->srv = 0;
657 return rad_continue_send_request(h, 0, fd, tv);
661 * Create and initialize a rad_handle structure, and return it to the
662 * caller. Can fail only if the necessary memory cannot be allocated.
663 * In that case, it returns NULL.
665 struct rad_handle *
666 rad_auth_open(void)
668 struct rad_handle *h;
670 h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
671 if (h != NULL) {
672 srandomdev();
673 h->fd = -1;
674 h->num_servers = 0;
675 h->ident = random();
676 h->errmsg[0] = '\0';
677 memset(h->pass, 0, sizeof h->pass);
678 h->pass_len = 0;
679 h->pass_pos = 0;
680 h->chap_pass = 0;
681 h->type = RADIUS_AUTH;
683 return h;
686 struct rad_handle *
687 rad_acct_open(void)
689 struct rad_handle *h;
691 h = rad_open();
692 if (h != NULL)
693 h->type = RADIUS_ACCT;
694 return h;
697 struct rad_handle *
698 rad_open(void)
700 return rad_auth_open();
704 rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
706 return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
710 rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
712 int result;
714 if (type == RAD_USER_PASSWORD)
715 result = put_password_attr(h, type, value, len);
716 else {
717 result = put_raw_attr(h, type, value, len);
718 if (result == 0 && type == RAD_CHAP_PASSWORD)
719 h->chap_pass = 1;
722 return result;
726 rad_put_int(struct rad_handle *h, int type, u_int32_t value)
728 u_int32_t nvalue;
730 nvalue = htonl(value);
731 return rad_put_attr(h, type, &nvalue, sizeof nvalue);
735 rad_put_string(struct rad_handle *h, int type, const char *str)
737 return rad_put_attr(h, type, str, strlen(str));
741 * Returns the response type code on success, or -1 on failure.
744 rad_send_request(struct rad_handle *h)
746 struct timeval timelimit;
747 struct timeval tv;
748 int fd;
749 int n;
751 n = rad_init_send_request(h, &fd, &tv);
753 if (n != 0)
754 return n;
756 gettimeofday(&timelimit, NULL);
757 timeradd(&tv, &timelimit, &timelimit);
759 for ( ; ; ) {
760 fd_set readfds;
762 FD_ZERO(&readfds);
763 FD_SET(fd, &readfds);
765 n = select(fd + 1, &readfds, NULL, NULL, &tv);
767 if (n == -1) {
768 generr(h, "select: %s", strerror(errno));
769 return -1;
772 if (!FD_ISSET(fd, &readfds)) {
773 /* Compute a new timeout */
774 gettimeofday(&tv, NULL);
775 timersub(&timelimit, &tv, &tv);
776 if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
777 /* Continue the select */
778 continue;
781 n = rad_continue_send_request(h, n, &fd, &tv);
783 if (n != 0)
784 return n;
786 gettimeofday(&timelimit, NULL);
787 timeradd(&tv, &timelimit, &timelimit);
791 const char *
792 rad_strerror(struct rad_handle *h)
794 return h->errmsg;
798 * Destructively split a string into fields separated by white space.
799 * `#' at the beginning of a field begins a comment that extends to the
800 * end of the string. Fields may be quoted with `"'. Inside quoted
801 * strings, the backslash escapes `\"' and `\\' are honored.
803 * Pointers to up to the first maxfields fields are stored in the fields
804 * array. Missing fields get NULL pointers.
806 * The return value is the actual number of fields parsed, and is always
807 * <= maxfields.
809 * On a syntax error, places a message in the msg string, and returns -1.
811 static int
812 split(char *str, const char *fields[], int maxfields, char *msg, size_t msglen)
814 char *p;
815 int i;
816 static const char ws[] = " \t";
818 for (i = 0; i < maxfields; i++)
819 fields[i] = NULL;
820 p = str;
821 i = 0;
822 while (*p != '\0') {
823 p += strspn(p, ws);
824 if (*p == '#' || *p == '\0')
825 break;
826 if (i >= maxfields) {
827 snprintf(msg, msglen, "line has too many fields");
828 return -1;
830 if (*p == '"') {
831 char *dst;
833 dst = ++p;
834 fields[i] = dst;
835 while (*p != '"') {
836 if (*p == '\\') {
837 p++;
838 if (*p != '"' && *p != '\\' &&
839 *p != '\0') {
840 snprintf(msg, msglen,
841 "invalid `\\' escape");
842 return -1;
845 if (*p == '\0') {
846 snprintf(msg, msglen,
847 "unterminated quoted string");
848 return -1;
850 *dst++ = *p++;
852 *dst = '\0';
853 p++;
854 if (*fields[i] == '\0') {
855 snprintf(msg, msglen,
856 "empty quoted string not permitted");
857 return -1;
859 if (*p != '\0' && strspn(p, ws) == 0) {
860 snprintf(msg, msglen, "quoted string not"
861 " followed by white space");
862 return -1;
864 } else {
865 fields[i] = p;
866 p += strcspn(p, ws);
867 if (*p != '\0')
868 *p++ = '\0';
870 i++;
872 return i;
876 rad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len)
878 const struct vendor_attribute *attr;
880 attr = (const struct vendor_attribute *)*data;
881 *vendor = ntohl(attr->vendor_value);
882 *data = attr->attrib_data;
883 *len = attr->attrib_len - 2;
885 return (attr->attrib_type);
889 rad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
890 struct in_addr addr)
892 return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr,
893 sizeof addr.s_addr));
897 rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
898 const void *value, size_t len)
900 struct vendor_attribute *attr;
901 int res;
903 if ((attr = malloc(len + 6)) == NULL) {
904 generr(h, "malloc failure (%d bytes)", len + 6);
905 return -1;
908 attr->vendor_value = htonl(vendor);
909 attr->attrib_type = type;
910 attr->attrib_len = len + 2;
911 memcpy(attr->attrib_data, value, len);
913 res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6);
914 free(attr);
915 if (res == 0 && vendor == RAD_VENDOR_MICROSOFT
916 && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE
917 || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) {
918 h->chap_pass = 1;
920 return (res);
924 rad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i)
926 u_int32_t value;
928 value = htonl(i);
929 return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value));
933 rad_put_vendor_string(struct rad_handle *h, int vendor, int type,
934 const char *str)
936 return (rad_put_vendor_attr(h, vendor, type, str, strlen(str)));
939 ssize_t
940 rad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
942 if (len < LEN_AUTH)
943 return (-1);
944 memcpy(buf, h->request + POS_AUTH, LEN_AUTH);
945 if (len > LEN_AUTH)
946 buf[LEN_AUTH] = '\0';
947 return (LEN_AUTH);
950 const char *
951 rad_server_secret(struct rad_handle *h)
953 return (h->servers[h->srv].secret);