1 /*- 2 * Copyright (c) 1998, 2001, 2002, 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 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <sys/time.h> 33 #include <netinet/in.h> 34 #include <arpa/inet.h> 35 36 #include <assert.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <md5.h> 40 #include <netdb.h> 41 #include <stdarg.h> 42 #include <stddef.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 48 #include "taclib_private.h" 49 50 static int add_str_8(struct tac_handle *, u_int8_t *, 51 struct clnt_str *); 52 static int add_str_16(struct tac_handle *, u_int16_t *, 53 struct clnt_str *); 54 static int protocol_version(int, int, int); 55 static void close_connection(struct tac_handle *); 56 static int conn_server(struct tac_handle *); 57 static void crypt_msg(struct tac_handle *, struct tac_msg *); 58 static void *dup_str(struct tac_handle *, const struct srvr_str *, 59 size_t *); 60 static int establish_connection(struct tac_handle *); 61 static void free_str(struct clnt_str *); 62 static void generr(struct tac_handle *, const char *, ...) 63 __printflike(2, 3); 64 static void gen_session_id(struct tac_msg *); 65 static int get_srvr_end(struct tac_handle *); 66 static int get_srvr_str(struct tac_handle *, const char *, 67 struct srvr_str *, size_t); 68 static void init_clnt_str(struct clnt_str *); 69 static void init_srvr_str(struct srvr_str *); 70 static int read_timed(struct tac_handle *, void *, size_t, 71 const struct timeval *); 72 static int recv_msg(struct tac_handle *); 73 static int save_str(struct tac_handle *, struct clnt_str *, 74 const void *, size_t); 75 static int send_msg(struct tac_handle *); 76 static int split(char *, char *[], int, char *, size_t); 77 static void *xmalloc(struct tac_handle *, size_t); 78 static char *xstrdup(struct tac_handle *, const char *); 79 static void clear_srvr_avs(struct tac_handle *); 80 static void create_msg(struct tac_handle *, int, int, int); 81 82 /* 83 * Append some optional data to the current request, and store its 84 * length into the 8-bit field referenced by "fld". Returns 0 on 85 * success, or -1 on failure. 86 * 87 * This function also frees the "cs" string data and initializes it 88 * for the next time. 89 */ 90 static int 91 add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs) 92 { 93 u_int16_t len; 94 95 if (add_str_16(h, &len, cs) == -1) 96 return -1; 97 len = ntohs(len); 98 if (len > 0xff) { 99 generr(h, "Field too long"); 100 return -1; 101 } 102 *fld = len; 103 return 0; 104 } 105 106 /* 107 * Append some optional data to the current request, and store its 108 * length into the 16-bit field (network byte order) referenced by 109 * "fld". Returns 0 on success, or -1 on failure. 110 * 111 * This function also frees the "cs" string data and initializes it 112 * for the next time. 113 */ 114 static int 115 add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs) 116 { 117 size_t len; 118 119 len = cs->len; 120 if (cs->data == NULL) 121 len = 0; 122 if (len != 0) { 123 int offset; 124 125 if (len > 0xffff) { 126 generr(h, "Field too long"); 127 return -1; 128 } 129 offset = ntohl(h->request.length); 130 if (offset + len > BODYSIZE) { 131 generr(h, "Message too long"); 132 return -1; 133 } 134 memcpy(h->request.u.body + offset, cs->data, len); 135 h->request.length = htonl(offset + len); 136 } 137 *fld = htons(len); 138 free_str(cs); 139 return 0; 140 } 141 142 static int 143 protocol_version(int msg_type, int var, int type) 144 { 145 int minor; 146 147 switch (msg_type) { 148 case TAC_AUTHEN: 149 /* 'var' represents the 'action' */ 150 switch (var) { 151 case TAC_AUTHEN_LOGIN: 152 switch (type) { 153 154 case TAC_AUTHEN_TYPE_PAP: 155 case TAC_AUTHEN_TYPE_CHAP: 156 case TAC_AUTHEN_TYPE_MSCHAP: 157 case TAC_AUTHEN_TYPE_ARAP: 158 minor = 1; 159 break; 160 161 default: 162 minor = 0; 163 break; 164 } 165 break; 166 167 case TAC_AUTHEN_SENDAUTH: 168 minor = 1; 169 break; 170 171 default: 172 minor = 0; 173 break; 174 }; 175 break; 176 177 case TAC_AUTHOR: 178 /* 'var' represents the 'method' */ 179 switch (var) { 180 /* 181 * When new authentication methods are added, include 'method' 182 * in determining the value of 'minor'. At this point, all 183 * methods defined in this implementation (see "Authorization 184 * authentication methods" in taclib.h) are minor version 0 185 * Not all types, however, indicate minor version 0. 186 */ 187 case TAC_AUTHEN_METH_NOT_SET: 188 case TAC_AUTHEN_METH_NONE: 189 case TAC_AUTHEN_METH_KRB5: 190 case TAC_AUTHEN_METH_LINE: 191 case TAC_AUTHEN_METH_ENABLE: 192 case TAC_AUTHEN_METH_LOCAL: 193 case TAC_AUTHEN_METH_TACACSPLUS: 194 case TAC_AUTHEN_METH_RCMD: 195 switch (type) { 196 case TAC_AUTHEN_TYPE_PAP: 197 case TAC_AUTHEN_TYPE_CHAP: 198 case TAC_AUTHEN_TYPE_MSCHAP: 199 case TAC_AUTHEN_TYPE_ARAP: 200 minor = 1; 201 break; 202 203 default: 204 minor = 0; 205 break; 206 } 207 break; 208 default: 209 minor = 0; 210 break; 211 } 212 break; 213 214 case TAC_ACCT: 215 216 default: 217 minor = 0; 218 break; 219 } 220 221 return TAC_VER_MAJOR << 4 | minor; 222 } 223 224 225 static void 226 close_connection(struct tac_handle *h) 227 { 228 if (h->fd != -1) { 229 close(h->fd); 230 h->fd = -1; 231 } 232 } 233 234 static int 235 conn_server(struct tac_handle *h) 236 { 237 const struct tac_server *srvp = &h->servers[h->cur_server]; 238 int flags; 239 240 if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 241 generr(h, "Cannot create socket: %s", strerror(errno)); 242 return -1; 243 } 244 if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 || 245 fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) { 246 generr(h, "Cannot set non-blocking mode on socket: %s", 247 strerror(errno)); 248 close(h->fd); 249 h->fd = -1; 250 return -1; 251 } 252 if (connect(h->fd, (struct sockaddr *)&srvp->addr, 253 sizeof srvp->addr) == 0) 254 return 0; 255 256 if (errno == EINPROGRESS) { 257 fd_set wfds; 258 struct timeval tv; 259 int nfds; 260 struct sockaddr peer; 261 socklen_t errlen, peerlen; 262 int err; 263 264 /* Wait for the connection to complete. */ 265 FD_ZERO(&wfds); 266 FD_SET(h->fd, &wfds); 267 tv.tv_sec = srvp->timeout; 268 tv.tv_usec = 0; 269 nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv); 270 if (nfds == -1) { 271 generr(h, "select: %s", strerror(errno)); 272 close(h->fd); 273 h->fd = -1; 274 return -1; 275 } 276 if (nfds == 0) { 277 generr(h, "connect: timed out"); 278 close(h->fd); 279 h->fd = -1; 280 return -1; 281 } 282 283 /* See whether we are connected now. */ 284 peerlen = sizeof peer; 285 if (getpeername(h->fd, &peer, &peerlen) == 0) 286 return 0; 287 288 if (errno != ENOTCONN) { 289 generr(h, "getpeername: %s", strerror(errno)); 290 close(h->fd); 291 h->fd = -1; 292 return -1; 293 } 294 295 /* Find out why the connect failed. */ 296 errlen = sizeof err; 297 getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); 298 errno = err; 299 } 300 generr(h, "connect: %s", strerror(errno)); 301 close(h->fd); 302 h->fd = -1; 303 return -1; 304 } 305 306 /* 307 * Encrypt or decrypt a message. The operations are symmetrical. 308 */ 309 static void 310 crypt_msg(struct tac_handle *h, struct tac_msg *msg) 311 { 312 const char *secret; 313 MD5_CTX base_ctx; 314 MD5_CTX ctx; 315 unsigned char md5[16]; 316 int chunk; 317 int msg_len; 318 319 secret = h->servers[h->cur_server].secret; 320 if (secret[0] == '\0') 321 msg->flags |= TAC_UNENCRYPTED; 322 if (msg->flags & TAC_UNENCRYPTED) 323 return; 324 325 msg_len = ntohl(msg->length); 326 327 MD5Init(&base_ctx); 328 MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id); 329 MD5Update(&base_ctx, secret, strlen(secret)); 330 MD5Update(&base_ctx, &msg->version, sizeof msg->version); 331 MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no); 332 333 ctx = base_ctx; 334 for (chunk = 0; chunk < msg_len; chunk += sizeof md5) { 335 int chunk_len; 336 int i; 337 338 MD5Final(md5, &ctx); 339 340 if ((chunk_len = msg_len - chunk) > sizeof md5) 341 chunk_len = sizeof md5; 342 for (i = 0; i < chunk_len; i++) 343 msg->u.body[chunk + i] ^= md5[i]; 344 345 ctx = base_ctx; 346 MD5Update(&ctx, md5, sizeof md5); 347 } 348 } 349 350 /* 351 * Return a dynamically allocated copy of the given server string. 352 * The copy is null-terminated. If "len" is non-NULL, the length of 353 * the string (excluding the terminating null byte) is stored via it. 354 * Returns NULL on failure. Empty strings are still allocated even 355 * though they have no content. 356 */ 357 static void * 358 dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len) 359 { 360 unsigned char *p; 361 362 if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL) 363 return NULL; 364 if (ss->data != NULL && ss->len != 0) 365 memcpy(p, ss->data, ss->len); 366 p[ss->len] = '\0'; 367 if (len != NULL) 368 *len = ss->len; 369 return p; 370 } 371 372 static int 373 establish_connection(struct tac_handle *h) 374 { 375 int i; 376 377 if (h->fd >= 0) /* Already connected. */ 378 return 0; 379 if (h->num_servers == 0) { 380 generr(h, "No TACACS+ servers specified"); 381 return -1; 382 } 383 /* 384 * Try the servers round-robin. We begin with the one that 385 * worked for us the last time. That way, once we find a good 386 * server, we won't waste any more time trying the bad ones. 387 */ 388 for (i = 0; i < h->num_servers; i++) { 389 if (conn_server(h) == 0) { 390 h->single_connect = (h->servers[h->cur_server].flags & 391 TAC_SRVR_SINGLE_CONNECT) != 0; 392 return 0; 393 } 394 if (++h->cur_server >= h->num_servers) /* Wrap around */ 395 h->cur_server = 0; 396 } 397 /* Just return whatever error was last reported by conn_server(). */ 398 return -1; 399 } 400 401 /* 402 * Free a client string, obliterating its contents first for security. 403 */ 404 static void 405 free_str(struct clnt_str *cs) 406 { 407 if (cs->data != NULL) { 408 memset(cs->data, 0, cs->len); 409 free(cs->data); 410 cs->data = NULL; 411 cs->len = 0; 412 } 413 } 414 415 static void 416 generr(struct tac_handle *h, const char *format, ...) 417 { 418 va_list ap; 419 420 va_start(ap, format); 421 vsnprintf(h->errmsg, ERRSIZE, format, ap); 422 va_end(ap); 423 } 424 425 static void 426 gen_session_id(struct tac_msg *msg) 427 { 428 int r; 429 430 r = random(); 431 msg->session_id[0] = r >> 8; 432 msg->session_id[1] = r; 433 r = random(); 434 msg->session_id[2] = r >> 8; 435 msg->session_id[3] = r; 436 } 437 438 /* 439 * Verify that we are exactly at the end of the response message. 440 * Returns 0 on success, -1 on failure. 441 */ 442 static int 443 get_srvr_end(struct tac_handle *h) 444 { 445 int len; 446 447 len = ntohl(h->response.length); 448 449 if (h->srvr_pos != len) { 450 generr(h, "Invalid length field in response " 451 "from server: end expected at %u, response length %u", 452 h->srvr_pos, len); 453 return -1; 454 } 455 return 0; 456 } 457 458 static int 459 get_srvr_str(struct tac_handle *h, const char *field, 460 struct srvr_str *ss, size_t len) 461 { 462 if (h->srvr_pos + len > ntohl(h->response.length)) { 463 generr(h, "Invalid length field in %s response from server " 464 "(%lu > %lu)", field, (u_long)(h->srvr_pos + len), 465 (u_long)ntohl(h->response.length)); 466 return -1; 467 } 468 ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL; 469 ss->len = len; 470 h->srvr_pos += len; 471 return 0; 472 } 473 474 static void 475 init_clnt_str(struct clnt_str *cs) 476 { 477 cs->data = NULL; 478 cs->len = 0; 479 } 480 481 static void 482 init_srvr_str(struct srvr_str *ss) 483 { 484 ss->data = NULL; 485 ss->len = 0; 486 } 487 488 static int 489 read_timed(struct tac_handle *h, void *buf, size_t len, 490 const struct timeval *deadline) 491 { 492 char *ptr; 493 494 ptr = (char *)buf; 495 while (len > 0) { 496 int n; 497 498 n = read(h->fd, ptr, len); 499 if (n == -1) { 500 struct timeval tv; 501 int nfds; 502 503 if (errno != EAGAIN) { 504 generr(h, "Network read error: %s", 505 strerror(errno)); 506 return -1; 507 } 508 509 /* Wait until we can read more data. */ 510 gettimeofday(&tv, NULL); 511 timersub(deadline, &tv, &tv); 512 if (tv.tv_sec >= 0) { 513 fd_set rfds; 514 515 FD_ZERO(&rfds); 516 FD_SET(h->fd, &rfds); 517 nfds = 518 select(h->fd + 1, &rfds, NULL, NULL, &tv); 519 if (nfds == -1) { 520 generr(h, "select: %s", 521 strerror(errno)); 522 return -1; 523 } 524 } else 525 nfds = 0; 526 if (nfds == 0) { 527 generr(h, "Network read timed out"); 528 return -1; 529 } 530 } else if (n == 0) { 531 generr(h, "unexpected EOF from server"); 532 return -1; 533 } else { 534 ptr += n; 535 len -= n; 536 } 537 } 538 return 0; 539 } 540 541 /* 542 * Receive a response from the server and decrypt it. Returns 0 on 543 * success, or -1 on failure. 544 */ 545 static int 546 recv_msg(struct tac_handle *h) 547 { 548 struct timeval deadline; 549 struct tac_msg *msg; 550 u_int32_t len; 551 552 msg = &h->response; 553 gettimeofday(&deadline, NULL); 554 deadline.tv_sec += h->servers[h->cur_server].timeout; 555 556 /* Read the message header and make sure it is reasonable. */ 557 if (read_timed(h, msg, HDRSIZE, &deadline) == -1) 558 return -1; 559 if (memcmp(msg->session_id, h->request.session_id, 560 sizeof msg->session_id) != 0) { 561 generr(h, "Invalid session ID in received message"); 562 return -1; 563 } 564 if (msg->type != h->request.type) { 565 generr(h, "Invalid type in received message" 566 " (got %u, expected %u)", 567 msg->type, h->request.type); 568 return -1; 569 } 570 len = ntohl(msg->length); 571 if (len > BODYSIZE) { 572 generr(h, "Received message too large (%u > %u)", 573 len, BODYSIZE); 574 return -1; 575 } 576 if (msg->seq_no != ++h->last_seq_no) { 577 generr(h, "Invalid sequence number in received message" 578 " (got %u, expected %u)", 579 msg->seq_no, h->last_seq_no); 580 return -1; 581 } 582 583 /* Read the message body. */ 584 if (read_timed(h, msg->u.body, len, &deadline) == -1) 585 return -1; 586 587 /* Decrypt it. */ 588 crypt_msg(h, msg); 589 590 /* 591 * Turn off single-connection mode if the server isn't amenable 592 * to it. 593 */ 594 if (!(msg->flags & TAC_SINGLE_CONNECT)) 595 h->single_connect = 0; 596 return 0; 597 } 598 599 static int 600 save_str(struct tac_handle *h, struct clnt_str *cs, const void *data, 601 size_t len) 602 { 603 free_str(cs); 604 if (data != NULL && len != 0) { 605 if ((cs->data = xmalloc(h, len)) == NULL) 606 return -1; 607 cs->len = len; 608 memcpy(cs->data, data, len); 609 } 610 return 0; 611 } 612 613 /* 614 * Send the current request, after encrypting it. Returns 0 on success, 615 * or -1 on failure. 616 */ 617 static int 618 send_msg(struct tac_handle *h) 619 { 620 struct timeval deadline; 621 struct tac_msg *msg; 622 char *ptr; 623 int len; 624 625 if (h->last_seq_no & 1) { 626 generr(h, "Attempt to send message out of sequence"); 627 return -1; 628 } 629 630 if (establish_connection(h) == -1) 631 return -1; 632 633 msg = &h->request; 634 msg->seq_no = ++h->last_seq_no; 635 if (msg->seq_no == 1) 636 gen_session_id(msg); 637 crypt_msg(h, msg); 638 639 if (h->single_connect) 640 msg->flags |= TAC_SINGLE_CONNECT; 641 else 642 msg->flags &= ~TAC_SINGLE_CONNECT; 643 gettimeofday(&deadline, NULL); 644 deadline.tv_sec += h->servers[h->cur_server].timeout; 645 len = HDRSIZE + ntohl(msg->length); 646 ptr = (char *)msg; 647 while (len > 0) { 648 int n; 649 650 n = write(h->fd, ptr, len); 651 if (n == -1) { 652 struct timeval tv; 653 int nfds; 654 655 if (errno != EAGAIN) { 656 generr(h, "Network write error: %s", 657 strerror(errno)); 658 return -1; 659 } 660 661 /* Wait until we can write more data. */ 662 gettimeofday(&tv, NULL); 663 timersub(&deadline, &tv, &tv); 664 if (tv.tv_sec >= 0) { 665 fd_set wfds; 666 667 FD_ZERO(&wfds); 668 FD_SET(h->fd, &wfds); 669 nfds = 670 select(h->fd + 1, NULL, &wfds, NULL, &tv); 671 if (nfds == -1) { 672 generr(h, "select: %s", 673 strerror(errno)); 674 return -1; 675 } 676 } else 677 nfds = 0; 678 if (nfds == 0) { 679 generr(h, "Network write timed out"); 680 return -1; 681 } 682 } else { 683 ptr += n; 684 len -= n; 685 } 686 } 687 return 0; 688 } 689 690 /* 691 * Destructively split a string into fields separated by white space. 692 * `#' at the beginning of a field begins a comment that extends to the 693 * end of the string. Fields may be quoted with `"'. Inside quoted 694 * strings, the backslash escapes `\"' and `\\' are honored. 695 * 696 * Pointers to up to the first maxfields fields are stored in the fields 697 * array. Missing fields get NULL pointers. 698 * 699 * The return value is the actual number of fields parsed, and is always 700 * <= maxfields. 701 * 702 * On a syntax error, places a message in the msg string, and returns -1. 703 */ 704 static int 705 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen) 706 { 707 char *p; 708 int i; 709 static const char ws[] = " \t"; 710 711 for (i = 0; i < maxfields; i++) 712 fields[i] = NULL; 713 p = str; 714 i = 0; 715 while (*p != '\0') { 716 p += strspn(p, ws); 717 if (*p == '#' || *p == '\0') 718 break; 719 if (i >= maxfields) { 720 snprintf(msg, msglen, "line has too many fields"); 721 return -1; 722 } 723 if (*p == '"') { 724 char *dst; 725 726 dst = ++p; 727 fields[i] = dst; 728 while (*p != '"') { 729 if (*p == '\\') { 730 p++; 731 if (*p != '"' && *p != '\\' && 732 *p != '\0') { 733 snprintf(msg, msglen, 734 "invalid `\\' escape"); 735 return -1; 736 } 737 } 738 if (*p == '\0') { 739 snprintf(msg, msglen, 740 "unterminated quoted string"); 741 return -1; 742 } 743 *dst++ = *p++; 744 } 745 *dst = '\0'; 746 p++; 747 if (*p != '\0' && strspn(p, ws) == 0) { 748 snprintf(msg, msglen, "quoted string not" 749 " followed by white space"); 750 return -1; 751 } 752 } else { 753 fields[i] = p; 754 p += strcspn(p, ws); 755 if (*p != '\0') 756 *p++ = '\0'; 757 } 758 i++; 759 } 760 return i; 761 } 762 763 int 764 tac_add_server(struct tac_handle *h, const char *host, int port, 765 const char *secret, int timeout, int flags) 766 { 767 struct tac_server *srvp; 768 769 if (h->num_servers >= MAXSERVERS) { 770 generr(h, "Too many TACACS+ servers specified"); 771 return -1; 772 } 773 srvp = &h->servers[h->num_servers]; 774 775 memset(&srvp->addr, 0, sizeof srvp->addr); 776 srvp->addr.sin_len = sizeof srvp->addr; 777 srvp->addr.sin_family = AF_INET; 778 if (!inet_aton(host, &srvp->addr.sin_addr)) { 779 struct hostent *hent; 780 781 if ((hent = gethostbyname(host)) == NULL) { 782 generr(h, "%s: host not found", host); 783 return -1; 784 } 785 memcpy(&srvp->addr.sin_addr, hent->h_addr, 786 sizeof srvp->addr.sin_addr); 787 } 788 srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT); 789 if ((srvp->secret = xstrdup(h, secret)) == NULL) 790 return -1; 791 srvp->timeout = timeout; 792 srvp->flags = flags; 793 h->num_servers++; 794 return 0; 795 } 796 797 void 798 tac_close(struct tac_handle *h) 799 { 800 int i, srv; 801 802 if (h->fd != -1) 803 close(h->fd); 804 for (srv = 0; srv < h->num_servers; srv++) { 805 memset(h->servers[srv].secret, 0, 806 strlen(h->servers[srv].secret)); 807 free(h->servers[srv].secret); 808 } 809 free_str(&h->user); 810 free_str(&h->port); 811 free_str(&h->rem_addr); 812 free_str(&h->data); 813 free_str(&h->user_msg); 814 for (i=0; i<MAXAVPAIRS; i++) 815 free_str(&(h->avs[i])); 816 817 /* Clear everything else before freeing memory */ 818 memset(h, 0, sizeof(struct tac_handle)); 819 free(h); 820 } 821 822 int 823 tac_config(struct tac_handle *h, const char *path) 824 { 825 FILE *fp; 826 char buf[MAXCONFLINE]; 827 int linenum; 828 int retval; 829 830 if (path == NULL) 831 path = PATH_TACPLUS_CONF; 832 if ((fp = fopen(path, "r")) == NULL) { 833 generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); 834 return -1; 835 } 836 retval = 0; 837 linenum = 0; 838 while (fgets(buf, sizeof buf, fp) != NULL) { 839 int len; 840 char *fields[4]; 841 int nfields; 842 char msg[ERRSIZE]; 843 char *host, *res; 844 char *port_str; 845 char *secret; 846 char *timeout_str; 847 char *options_str; 848 char *end; 849 unsigned long timeout; 850 int port; 851 int options; 852 853 linenum++; 854 len = strlen(buf); 855 /* We know len > 0, else fgets would have returned NULL. */ 856 if (buf[len - 1] != '\n') { 857 if (len >= sizeof buf - 1) 858 generr(h, "%s:%d: line too long", path, 859 linenum); 860 else 861 generr(h, "%s:%d: missing newline", path, 862 linenum); 863 retval = -1; 864 break; 865 } 866 buf[len - 1] = '\0'; 867 868 /* Extract the fields from the line. */ 869 nfields = split(buf, fields, 4, msg, sizeof msg); 870 if (nfields == -1) { 871 generr(h, "%s:%d: %s", path, linenum, msg); 872 retval = -1; 873 break; 874 } 875 if (nfields == 0) 876 continue; 877 if (nfields < 2) { 878 generr(h, "%s:%d: missing shared secret", path, 879 linenum); 880 retval = -1; 881 break; 882 } 883 host = fields[0]; 884 secret = fields[1]; 885 timeout_str = fields[2]; 886 options_str = fields[3]; 887 888 /* Parse and validate the fields. */ 889 res = host; 890 host = strsep(&res, ":"); 891 port_str = strsep(&res, ":"); 892 if (port_str != NULL) { 893 port = strtoul(port_str, &end, 10); 894 if (port_str[0] == '\0' || *end != '\0') { 895 generr(h, "%s:%d: invalid port", path, 896 linenum); 897 retval = -1; 898 break; 899 } 900 } else 901 port = 0; 902 if (timeout_str != NULL) { 903 timeout = strtoul(timeout_str, &end, 10); 904 if (timeout_str[0] == '\0' || *end != '\0') { 905 generr(h, "%s:%d: invalid timeout", path, 906 linenum); 907 retval = -1; 908 break; 909 } 910 } else 911 timeout = TIMEOUT; 912 options = 0; 913 if (options_str != NULL) { 914 if (strcmp(options_str, "single-connection") == 0) 915 options |= TAC_SRVR_SINGLE_CONNECT; 916 else { 917 generr(h, "%s:%d: invalid option \"%s\"", 918 path, linenum, options_str); 919 retval = -1; 920 break; 921 } 922 }; 923 924 if (tac_add_server(h, host, port, secret, timeout, 925 options) == -1) { 926 char msg[ERRSIZE]; 927 928 strcpy(msg, h->errmsg); 929 generr(h, "%s:%d: %s", path, linenum, msg); 930 retval = -1; 931 break; 932 } 933 } 934 /* Clear out the buffer to wipe a possible copy of a shared secret */ 935 memset(buf, 0, sizeof buf); 936 fclose(fp); 937 return retval; 938 } 939 940 int 941 tac_create_authen(struct tac_handle *h, int action, int type, int service) 942 { 943 struct tac_authen_start *as; 944 945 create_msg(h, TAC_AUTHEN, action, type); 946 947 as = &h->request.u.authen_start; 948 as->action = action; 949 as->priv_lvl = TAC_PRIV_LVL_USER; 950 as->authen_type = type; 951 as->service = service; 952 953 return 0; 954 } 955 956 int 957 tac_create_author(struct tac_handle *h, int method, int type, int service) 958 { 959 struct tac_author_request *areq; 960 961 create_msg(h, TAC_AUTHOR, method, type); 962 963 areq = &h->request.u.author_request; 964 areq->authen_meth = method; 965 areq->priv_lvl = TAC_PRIV_LVL_USER; 966 areq->authen_type = type; 967 areq->service = service; 968 969 return 0; 970 } 971 972 int 973 tac_create_acct(struct tac_handle *h, int acct, int action, int type, int service) 974 { 975 struct tac_acct_start *as; 976 977 create_msg(h, TAC_ACCT, action, type); 978 979 as = &h->request.u.acct_start; 980 as->action = acct; 981 as->authen_action = action; 982 as->priv_lvl = TAC_PRIV_LVL_USER; 983 as->authen_type = type; 984 as->authen_service = service; 985 986 return 0; 987 } 988 989 static void 990 create_msg(struct tac_handle *h, int msg_type, int var, int type) 991 { 992 struct tac_msg *msg; 993 int i; 994 995 h->last_seq_no = 0; 996 997 msg = &h->request; 998 msg->type = msg_type; 999 msg->version = protocol_version(msg_type, var, type); 1000 msg->flags = 0; /* encrypted packet body */ 1001 1002 free_str(&h->user); 1003 free_str(&h->port); 1004 free_str(&h->rem_addr); 1005 free_str(&h->data); 1006 free_str(&h->user_msg); 1007 1008 for (i=0; i<MAXAVPAIRS; i++) 1009 free_str(&(h->avs[i])); 1010 } 1011 1012 void * 1013 tac_get_data(struct tac_handle *h, size_t *len) 1014 { 1015 return dup_str(h, &h->srvr_data, len); 1016 } 1017 1018 char * 1019 tac_get_msg(struct tac_handle *h) 1020 { 1021 return dup_str(h, &h->srvr_msg, NULL); 1022 } 1023 1024 /* 1025 * Create and initialize a tac_handle structure, and return it to the 1026 * caller. Can fail only if the necessary memory cannot be allocated. 1027 * In that case, it returns NULL. 1028 */ 1029 struct tac_handle * 1030 tac_open(void) 1031 { 1032 int i; 1033 struct tac_handle *h; 1034 1035 h = (struct tac_handle *)malloc(sizeof(struct tac_handle)); 1036 if (h != NULL) { 1037 h->fd = -1; 1038 h->num_servers = 0; 1039 h->cur_server = 0; 1040 h->errmsg[0] = '\0'; 1041 init_clnt_str(&h->user); 1042 init_clnt_str(&h->port); 1043 init_clnt_str(&h->rem_addr); 1044 init_clnt_str(&h->data); 1045 init_clnt_str(&h->user_msg); 1046 for (i=0; i<MAXAVPAIRS; i++) { 1047 init_clnt_str(&(h->avs[i])); 1048 init_srvr_str(&(h->srvr_avs[i])); 1049 } 1050 init_srvr_str(&h->srvr_msg); 1051 init_srvr_str(&h->srvr_data); 1052 srandomdev(); 1053 } 1054 return h; 1055 } 1056 1057 int 1058 tac_send_authen(struct tac_handle *h) 1059 { 1060 struct tac_authen_reply *ar; 1061 1062 if (h->num_servers == 0) 1063 return -1; 1064 1065 if (h->last_seq_no == 0) { /* Authentication START packet */ 1066 struct tac_authen_start *as; 1067 1068 as = &h->request.u.authen_start; 1069 h->request.length = 1070 htonl(offsetof(struct tac_authen_start, rest[0])); 1071 if (add_str_8(h, &as->user_len, &h->user) == -1 || 1072 add_str_8(h, &as->port_len, &h->port) == -1 || 1073 add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 || 1074 add_str_8(h, &as->data_len, &h->data) == -1) 1075 return -1; 1076 } else { /* Authentication CONTINUE packet */ 1077 struct tac_authen_cont *ac; 1078 1079 ac = &h->request.u.authen_cont; 1080 ac->flags = 0; 1081 h->request.length = 1082 htonl(offsetof(struct tac_authen_cont, rest[0])); 1083 if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 || 1084 add_str_16(h, &ac->data_len, &h->data) == -1) 1085 return -1; 1086 } 1087 1088 /* Send the message and retrieve the reply. */ 1089 if (send_msg(h) == -1 || recv_msg(h) == -1) 1090 return -1; 1091 1092 /* Scan the optional fields in the reply. */ 1093 ar = &h->response.u.authen_reply; 1094 h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]); 1095 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 || 1096 get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 || 1097 get_srvr_end(h) == -1) 1098 return -1; 1099 1100 if (!h->single_connect && 1101 ar->status != TAC_AUTHEN_STATUS_GETDATA && 1102 ar->status != TAC_AUTHEN_STATUS_GETUSER && 1103 ar->status != TAC_AUTHEN_STATUS_GETPASS) 1104 close_connection(h); 1105 1106 return ar->flags << 8 | ar->status; 1107 } 1108 1109 int 1110 tac_send_author(struct tac_handle *h) 1111 { 1112 int i, current; 1113 char dbgstr[64]; 1114 struct tac_author_request *areq = &h->request.u.author_request; 1115 struct tac_author_response *ares = &h->response.u.author_response; 1116 1117 h->request.length = 1118 htonl(offsetof(struct tac_author_request, rest[0])); 1119 1120 /* Count each specified AV pair */ 1121 for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++) 1122 if (h->avs[i].len && h->avs[i].data) 1123 areq->av_cnt++; 1124 1125 /* 1126 * Each AV size is a byte starting right after 'av_cnt'. Update the 1127 * offset to include these AV sizes. 1128 */ 1129 h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt); 1130 1131 /* Now add the string arguments from 'h' */ 1132 if (add_str_8(h, &areq->user_len, &h->user) == -1 || 1133 add_str_8(h, &areq->port_len, &h->port) == -1 || 1134 add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1) 1135 return -1; 1136 1137 /* Add each AV pair, the size of each placed in areq->rest[current] */ 1138 for (current=0, i=0; i<MAXAVPAIRS; i++) { 1139 if (h->avs[i].len && h->avs[i].data) { 1140 if (add_str_8(h, &areq->rest[current++], 1141 &(h->avs[i])) == -1) 1142 return -1; 1143 } 1144 } 1145 1146 /* Send the message and retrieve the reply. */ 1147 if (send_msg(h) == -1 || recv_msg(h) == -1) 1148 return -1; 1149 1150 /* Update the offset in the response packet based on av pairs count */ 1151 h->srvr_pos = offsetof(struct tac_author_response, rest[0]) + 1152 ares->av_cnt; 1153 1154 /* Scan the optional fields in the response. */ 1155 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 || 1156 get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1) 1157 return -1; 1158 1159 /* Get each AV pair (just setting pointers, not malloc'ing) */ 1160 clear_srvr_avs(h); 1161 for (i=0; i<ares->av_cnt; i++) { 1162 snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i); 1163 if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]), 1164 ares->rest[i]) == -1) 1165 return -1; 1166 } 1167 1168 /* Should have ended up at the end */ 1169 if (get_srvr_end(h) == -1) 1170 return -1; 1171 1172 /* Sanity checks */ 1173 if (!h->single_connect) 1174 close_connection(h); 1175 1176 return ares->av_cnt << 8 | ares->status; 1177 } 1178 1179 int 1180 tac_send_acct(struct tac_handle *h) 1181 { 1182 register int i, current; 1183 struct tac_acct_start *as = &h->request.u.acct_start; 1184 struct tac_acct_reply *ar = &h->response.u.acct_reply; 1185 1186 /* start */ 1187 as = &h->request.u.acct_start; 1188 h->request.length = htonl(offsetof(struct tac_acct_start, rest[0])); 1189 for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++) 1190 if (h->avs[i].len && h->avs[i].data) 1191 as->av_cnt++; 1192 h->request.length = ntohl(htonl(h->request.length) + as->av_cnt); 1193 1194 if (add_str_8(h, &as->user_len, &h->user) == -1 || 1195 add_str_8(h, &as->port_len, &h->port) == -1 || 1196 add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1) 1197 return -1; 1198 1199 for (i = current = 0; i < MAXAVPAIRS; i++) 1200 if (h->avs[i].len && h->avs[i].data) 1201 if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1) 1202 return -1; 1203 1204 /* send */ 1205 if (send_msg(h) == -1 || recv_msg(h) == -1) 1206 return -1; 1207 1208 /* reply */ 1209 h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]); 1210 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 || 1211 get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 || 1212 get_srvr_end(h) == -1) 1213 return -1; 1214 1215 /* Sanity checks */ 1216 if (!h->single_connect) 1217 close_connection(h); 1218 1219 return ar->status; 1220 } 1221 1222 int 1223 tac_set_rem_addr(struct tac_handle *h, const char *addr) 1224 { 1225 return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0); 1226 } 1227 1228 int 1229 tac_set_data(struct tac_handle *h, const void *data, size_t data_len) 1230 { 1231 return save_str(h, &h->data, data, data_len); 1232 } 1233 1234 int 1235 tac_set_msg(struct tac_handle *h, const char *msg) 1236 { 1237 return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0); 1238 } 1239 1240 int 1241 tac_set_port(struct tac_handle *h, const char *port) 1242 { 1243 return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0); 1244 } 1245 1246 int 1247 tac_set_priv(struct tac_handle *h, int priv) 1248 { 1249 if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) { 1250 generr(h, "Attempt to set invalid privilege level"); 1251 return -1; 1252 } 1253 h->request.u.authen_start.priv_lvl = priv; 1254 return 0; 1255 } 1256 1257 int 1258 tac_set_user(struct tac_handle *h, const char *user) 1259 { 1260 return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0); 1261 } 1262 1263 int 1264 tac_set_av(struct tac_handle *h, u_int index, const char *av) 1265 { 1266 if (index >= MAXAVPAIRS) 1267 return -1; 1268 return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0); 1269 } 1270 1271 char * 1272 tac_get_av(struct tac_handle *h, u_int index) 1273 { 1274 if (index >= MAXAVPAIRS) 1275 return NULL; 1276 return dup_str(h, &(h->srvr_avs[index]), NULL); 1277 } 1278 1279 char * 1280 tac_get_av_value(struct tac_handle *h, const char *attribute) 1281 { 1282 int i, len; 1283 const char *ch, *end; 1284 const char *candidate; 1285 int candidate_len; 1286 int found_seperator; 1287 struct srvr_str srvr; 1288 1289 if (attribute == NULL || ((len = strlen(attribute)) == 0)) 1290 return NULL; 1291 1292 for (i=0; i<MAXAVPAIRS; i++) { 1293 candidate = h->srvr_avs[i].data; 1294 candidate_len = h->srvr_avs[i].len; 1295 1296 /* 1297 * Valid 'srvr_avs' guaranteed to be contiguous starting at 1298 * index 0 (not necessarily the case with 'avs'). Break out 1299 * when the "end" of the list has been reached. 1300 */ 1301 if (!candidate) 1302 break; 1303 1304 if (len < candidate_len && 1305 !strncmp(candidate, attribute, len)) { 1306 1307 ch = candidate + len; 1308 end = candidate + candidate_len; 1309 1310 /* 1311 * Sift out the white space between A and V (should not 1312 * be any, but don't trust implementation of server...) 1313 */ 1314 found_seperator = 0; 1315 while ((*ch == '=' || *ch == '*' || *ch == ' ' || 1316 *ch == '\t') && ch != end) { 1317 if (*ch == '=' || *ch == '*') 1318 found_seperator++; 1319 ch++; 1320 } 1321 1322 /* 1323 * Note: 1324 * The case of 'attribute' == "foo" and 1325 * h->srvr_avs[0] = "foobie=var1" 1326 * h->srvr_avs[1] = "foo=var2" 1327 * is handled. 1328 * 1329 * Note that for empty string attribute values a 1330 * 0-length string is returned in order to distinguish 1331 * against unset values. 1332 * dup_str() will handle srvr.len == 0 correctly. 1333 */ 1334 if (found_seperator == 1) { 1335 srvr.len = end - ch; 1336 srvr.data = ch; 1337 return dup_str(h, &srvr, NULL); 1338 } 1339 } 1340 } 1341 return NULL; 1342 } 1343 1344 void 1345 tac_clear_avs(struct tac_handle *h) 1346 { 1347 int i; 1348 for (i=0; i<MAXAVPAIRS; i++) 1349 save_str(h, &(h->avs[i]), NULL, 0); 1350 } 1351 1352 static void 1353 clear_srvr_avs(struct tac_handle *h) 1354 { 1355 int i; 1356 for (i=0; i<MAXAVPAIRS; i++) 1357 init_srvr_str(&(h->srvr_avs[i])); 1358 } 1359 1360 1361 const char * 1362 tac_strerror(struct tac_handle *h) 1363 { 1364 return h->errmsg; 1365 } 1366 1367 static void * 1368 xmalloc(struct tac_handle *h, size_t size) 1369 { 1370 void *r; 1371 1372 if ((r = malloc(size)) == NULL) 1373 generr(h, "Out of memory"); 1374 return r; 1375 } 1376 1377 static char * 1378 xstrdup(struct tac_handle *h, const char *s) 1379 { 1380 char *r; 1381 1382 if ((r = strdup(s)) == NULL) 1383 generr(h, "Out of memory"); 1384 return r; 1385 } 1386