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