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