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