taclib.c revision 65222
1132720Skan/*- 2132720Skan * Copyright 1998 Juniper Networks, Inc. 3132720Skan * All rights reserved. 4132720Skan * 5132720Skan * Redistribution and use in source and binary forms, with or without 6132720Skan * modification, are permitted provided that the following conditions 7132720Skan * are met: 8132720Skan * 1. Redistributions of source code must retain the above copyright 9132720Skan * notice, this list of conditions and the following disclaimer. 10132720Skan * 2. Redistributions in binary form must reproduce the above copyright 11132720Skan * notice, this list of conditions and the following disclaimer in the 12132720Skan * documentation and/or other materials provided with the distribution. 13132720Skan * 14132720Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15132720Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16132720Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17132720Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18132720Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19132720Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20132720Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21132720Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22132720Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23132720Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24132720Skan * SUCH DAMAGE. 25132720Skan * 26132720Skan * $FreeBSD: head/lib/libtacplus/taclib.c 65222 2000-08-29 21:49:11Z ache $ 27132720Skan */ 28132720Skan 29132720Skan#include <sys/types.h> 30169691Skan#include <sys/socket.h> 31169691Skan#include <sys/time.h> 32169691Skan#include <netinet/in.h> 33169691Skan#include <arpa/inet.h> 34169691Skan 35132720Skan#include <assert.h> 36169691Skan#include <errno.h> 37169691Skan#include <fcntl.h> 38169691Skan#include <md5.h> 39169691Skan#include <netdb.h> 40169691Skan#include <stdarg.h> 41169691Skan#include <stddef.h> 42169691Skan#include <stdio.h> 43169691Skan#include <stdlib.h> 44169691Skan#include <string.h> 45169691Skan#include <unistd.h> 46169691Skan 47169691Skan#include "taclib_private.h" 48132720Skan 49132720Skanstatic int add_str_8(struct tac_handle *, u_int8_t *, 50132720Skan struct clnt_str *); 51132720Skanstatic int add_str_16(struct tac_handle *, u_int16_t *, 52132720Skan struct clnt_str *); 53132720Skanstatic int authen_version(int, int); 54132720Skanstatic void close_connection(struct tac_handle *); 55132720Skanstatic int conn_server(struct tac_handle *); 56132720Skanstatic void crypt_msg(struct tac_handle *, struct tac_msg *); 57132720Skanstatic void *dup_str(struct tac_handle *, const struct srvr_str *, 58132720Skan size_t *); 59132720Skanstatic int establish_connection(struct tac_handle *); 60132720Skanstatic void free_str(struct clnt_str *); 61132720Skanstatic void generr(struct tac_handle *, const char *, ...) 62132720Skan __printflike(2, 3); 63132720Skanstatic void gen_session_id(struct tac_msg *); 64132720Skanstatic int get_srvr_end(struct tac_handle *); 65132720Skanstatic int get_srvr_str(struct tac_handle *, struct srvr_str *, 66132720Skan size_t); 67132720Skanstatic void init_clnt_str(struct clnt_str *); 68132720Skanstatic void init_srvr_str(struct srvr_str *); 69132720Skanstatic int read_timed(struct tac_handle *, void *, size_t, 70169691Skan const struct timeval *); 71132720Skanstatic int recv_msg(struct tac_handle *); 72132720Skanstatic int save_str(struct tac_handle *, struct clnt_str *, 73132720Skan const void *, size_t); 74132720Skanstatic int send_msg(struct tac_handle *); 75132720Skanstatic int split(char *, char *[], int, char *, size_t); 76132720Skanstatic void *xmalloc(struct tac_handle *, size_t); 77132720Skanstatic char *xstrdup(struct tac_handle *, const char *); 78132720Skan 79132720Skan/* 80169691Skan * Append some optional data to the current request, and store its 81132720Skan * length into the 8-bit field referenced by "fld". Returns 0 on 82132720Skan * success, or -1 on failure. 83132720Skan * 84132720Skan * This function also frees the "cs" string data and initializes it 85132720Skan * for the next time. 86169691Skan */ 87132720Skanstatic int 88132720Skanadd_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs) 89132720Skan{ 90132720Skan u_int16_t len; 91132720Skan 92169691Skan if (add_str_16(h, &len, cs) == -1) 93169691Skan return -1; 94169691Skan len = ntohs(len); 95169691Skan if (len > 0xff) { 96169691Skan generr(h, "Field too long"); 97169691Skan return -1; 98169691Skan } 99169691Skan *fld = len; 100169691Skan return 0; 101169691Skan} 102169691Skan 103169691Skan/* 104169691Skan * Append some optional data to the current request, and store its 105169691Skan * length into the 16-bit field (network byte order) referenced by 106169691Skan * "fld". Returns 0 on success, or -1 on failure. 107132720Skan * 108169691Skan * This function also frees the "cs" string data and initializes it 109169691Skan * for the next time. 110132720Skan */ 111132720Skanstatic int 112132720Skanadd_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs) 113132720Skan{ 114132720Skan size_t len; 115132720Skan 116132720Skan len = cs->len; 117132720Skan if (cs->data == NULL) 118132720Skan len = 0; 119132720Skan if (len != 0) { 120132720Skan int offset; 121132720Skan 122132720Skan if (len > 0xffff) { 123132720Skan generr(h, "Field too long"); 124132720Skan return -1; 125132720Skan } 126132720Skan offset = ntohl(h->request.length); 127132720Skan if (offset + len > BODYSIZE) { 128132720Skan generr(h, "Message too long"); 129132720Skan return -1; 130132720Skan } 131132720Skan memcpy(h->request.u.body + offset, cs->data, len); 132132720Skan h->request.length = htonl(offset + len); 133132720Skan } 134132720Skan *fld = htons(len); 135132720Skan free_str(cs); 136132720Skan return 0; 137132720Skan} 138132720Skan 139132720Skanstatic int 140132720Skanauthen_version(int action, int type) 141132720Skan{ 142132720Skan int minor; 143132720Skan 144132720Skan switch (action) { 145132720Skan 146132720Skan case TAC_AUTHEN_LOGIN: 147132720Skan switch (type) { 148132720Skan 149132720Skan case TAC_AUTHEN_TYPE_PAP: 150132720Skan case TAC_AUTHEN_TYPE_CHAP: 151132720Skan case TAC_AUTHEN_TYPE_MSCHAP: 152132720Skan case TAC_AUTHEN_TYPE_ARAP: 153132720Skan minor = 1; 154132720Skan break; 155169691Skan 156132720Skan default: 157132720Skan minor = 0; 158132720Skan break; 159132720Skan } 160132720Skan break; 161132720Skan 162132720Skan case TAC_AUTHEN_SENDAUTH: 163132720Skan minor = 1; 164132720Skan break; 165132720Skan 166132720Skan default: 167132720Skan minor = 0; 168132720Skan break; 169132720Skan }; 170132720Skan 171132720Skan return TAC_VER_MAJOR << 4 | minor; 172132720Skan} 173132720Skan 174132720Skanstatic void 175132720Skanclose_connection(struct tac_handle *h) 176132720Skan{ 177132720Skan if (h->fd != -1) { 178132720Skan close(h->fd); 179132720Skan h->fd = -1; 180132720Skan } 181132720Skan} 182132720Skan 183132720Skanstatic int 184132720Skanconn_server(struct tac_handle *h) 185132720Skan{ 186132720Skan const struct tac_server *srvp = &h->servers[h->cur_server]; 187132720Skan int flags; 188132720Skan 189132720Skan if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 190132720Skan generr(h, "Cannot create socket: %s", strerror(errno)); 191132720Skan return -1; 192132720Skan } 193132720Skan if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 || 194132720Skan fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) { 195132720Skan generr(h, "Cannot set non-blocking mode on socket: %s", 196132720Skan strerror(errno)); 197132720Skan close(h->fd); 198132720Skan h->fd = -1; 199132720Skan return -1; 200132720Skan } 201132720Skan if (connect(h->fd, (struct sockaddr *)&srvp->addr, 202169691Skan sizeof srvp->addr) == 0) 203132720Skan return 0; 204132720Skan 205132720Skan if (errno == EINPROGRESS) { 206132720Skan fd_set wfds; 207132720Skan struct timeval tv; 208132720Skan int nfds; 209132720Skan struct sockaddr peer; 210132720Skan int peerlen; 211132720Skan int err; 212132720Skan int errlen; 213132720Skan 214132720Skan /* Wait for the connection to complete. */ 215132720Skan FD_ZERO(&wfds); 216132720Skan FD_SET(h->fd, &wfds); 217132720Skan tv.tv_sec = srvp->timeout; 218132720Skan tv.tv_usec = 0; 219132720Skan nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv); 220132720Skan if (nfds == -1) { 221132720Skan generr(h, "select: %s", strerror(errno)); 222132720Skan close(h->fd); 223132720Skan h->fd = -1; 224132720Skan return -1; 225132720Skan } 226132720Skan if (nfds == 0) { 227132720Skan generr(h, "connect: timed out"); 228132720Skan close(h->fd); 229132720Skan h->fd = -1; 230132720Skan return -1; 231132720Skan } 232132720Skan 233132720Skan /* See whether we are connected now. */ 234132720Skan peerlen = sizeof peer; 235132720Skan if (getpeername(h->fd, &peer, &peerlen) == 0) 236132720Skan return 0; 237132720Skan 238132720Skan if (errno != ENOTCONN) { 239132720Skan generr(h, "getpeername: %s", strerror(errno)); 240132720Skan close(h->fd); 241132720Skan h->fd = -1; 242146897Skan return -1; 243132720Skan } 244132720Skan 245132720Skan /* Find out why the connect failed. */ 246132720Skan errlen = sizeof err; 247132720Skan getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); 248132720Skan errno = err; 249132720Skan } 250132720Skan generr(h, "connect: %s", strerror(errno)); 251132720Skan close(h->fd); 252132720Skan h->fd = -1; 253132720Skan return -1; 254132720Skan} 255132720Skan 256132720Skan/* 257132720Skan * Encrypt or decrypt a message. The operations are symmetrical. 258132720Skan */ 259132720Skanstatic void 260132720Skancrypt_msg(struct tac_handle *h, struct tac_msg *msg) 261169691Skan{ 262132720Skan const char *secret; 263132720Skan MD5_CTX base_ctx; 264132720Skan MD5_CTX ctx; 265132720Skan unsigned char md5[16]; 266132720Skan int chunk; 267132720Skan int msg_len; 268132720Skan 269132720Skan secret = h->servers[h->cur_server].secret; 270132720Skan if (secret[0] == '\0') 271132720Skan msg->flags |= TAC_UNENCRYPTED; 272146897Skan if (msg->flags & TAC_UNENCRYPTED) 273132720Skan return; 274132720Skan 275132720Skan msg_len = ntohl(msg->length); 276132720Skan 277132720Skan MD5Init(&base_ctx); 278132720Skan MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id); 279132720Skan MD5Update(&base_ctx, secret, strlen(secret)); 280132720Skan MD5Update(&base_ctx, &msg->version, sizeof msg->version); 281132720Skan MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no); 282132720Skan 283132720Skan ctx = base_ctx; 284132720Skan for (chunk = 0; chunk < msg_len; chunk += sizeof md5) { 285132720Skan int chunk_len; 286132720Skan int i; 287132720Skan 288132720Skan MD5Final(md5, &ctx); 289132720Skan 290132720Skan if ((chunk_len = msg_len - chunk) > sizeof md5) 291132720Skan chunk_len = sizeof md5; 292132720Skan for (i = 0; i < chunk_len; i++) 293132720Skan msg->u.body[chunk + i] ^= md5[i]; 294132720Skan 295132720Skan ctx = base_ctx; 296132720Skan MD5Update(&ctx, md5, sizeof md5); 297132720Skan } 298132720Skan} 299132720Skan 300132720Skan/* 301132720Skan * Return a dynamically allocated copy of the given server string. 302132720Skan * The copy is null-terminated. If "len" is non-NULL, the length of 303132720Skan * the string (excluding the terminating null byte) is stored via it. 304132720Skan * Returns NULL on failure. Empty strings are still allocated even 305132720Skan * though they have no content. 306132720Skan */ 307132720Skanstatic void * 308132720Skandup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len) 309132720Skan{ 310132720Skan unsigned char *p; 311132720Skan 312132720Skan if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL) 313132720Skan return NULL; 314132720Skan if (ss->data != NULL && ss->len != 0) 315132720Skan memcpy(p, ss->data, ss->len); 316132720Skan p[ss->len] = '\0'; 317132720Skan if (len != NULL) 318132720Skan *len = ss->len; 319132720Skan return p; 320132720Skan} 321132720Skan 322132720Skanstatic int 323132720Skanestablish_connection(struct tac_handle *h) 324132720Skan{ 325132720Skan int i; 326132720Skan 327132720Skan if (h->fd >= 0) /* Already connected. */ 328132720Skan return 0; 329132720Skan if (h->num_servers == 0) { 330169691Skan generr(h, "No TACACS+ servers specified"); 331169691Skan return -1; 332132720Skan } 333169691Skan /* 334132720Skan * Try the servers round-robin. We begin with the one that 335169691Skan * worked for us the last time. That way, once we find a good 336132720Skan * server, we won't waste any more time trying the bad ones. 337132720Skan */ 338132720Skan for (i = 0; i < h->num_servers; i++) { 339132720Skan if (conn_server(h) == 0) { 340132720Skan h->single_connect = (h->servers[h->cur_server].flags & 341132720Skan TAC_SRVR_SINGLE_CONNECT) != 0; 342132720Skan return 0; 343132720Skan } 344132720Skan if (++h->cur_server >= h->num_servers) /* Wrap around */ 345132720Skan h->cur_server = 0; 346132720Skan } 347132720Skan /* Just return whatever error was last reported by conn_server(). */ 348132720Skan return -1; 349132720Skan} 350132720Skan 351132720Skan/* 352132720Skan * Free a client string, obliterating its contents first for security. 353132720Skan */ 354132720Skanstatic void 355132720Skanfree_str(struct clnt_str *cs) 356132720Skan{ 357132720Skan if (cs->data != NULL) { 358132720Skan memset(cs->data, 0, cs->len); 359132720Skan free(cs->data); 360132720Skan cs->data = NULL; 361132720Skan cs->len = 0; 362132720Skan } 363132720Skan} 364132720Skan 365132720Skanstatic void 366132720Skangenerr(struct tac_handle *h, const char *format, ...) 367132720Skan{ 368132720Skan va_list ap; 369132720Skan 370132720Skan va_start(ap, format); 371132720Skan vsnprintf(h->errmsg, ERRSIZE, format, ap); 372132720Skan va_end(ap); 373132720Skan} 374132720Skan 375132720Skanstatic void 376132720Skangen_session_id(struct tac_msg *msg) 377132720Skan{ 378132720Skan int r; 379132720Skan 380132720Skan r = random(); 381132720Skan msg->session_id[0] = r >> 8; 382132720Skan msg->session_id[1] = r; 383132720Skan r = random(); 384132720Skan msg->session_id[2] = r >> 8; 385132720Skan msg->session_id[3] = r; 386132720Skan} 387132720Skan 388132720Skan/* 389132720Skan * Verify that we are exactly at the end of the response message. 390132720Skan * Returns 0 on success, -1 on failure. 391132720Skan */ 392132720Skanstatic int 393132720Skanget_srvr_end(struct tac_handle *h) 394132720Skan{ 395132720Skan if (h->srvr_pos != ntohl(h->response.length)) { 396132720Skan generr(h, "Invalid length field in response from server"); 397132720Skan return -1; 398132720Skan } 399132720Skan return 0; 400132720Skan} 401132720Skan 402132720Skanstatic int 403132720Skanget_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len) 404132720Skan{ 405132720Skan if (h->srvr_pos + len > ntohl(h->response.length)) { 406132720Skan generr(h, "Invalid length field in response from server"); 407132720Skan return -1; 408132720Skan } 409132720Skan ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL; 410132720Skan ss->len = len; 411132720Skan h->srvr_pos += len; 412132720Skan return 0; 413132720Skan} 414132720Skan 415132720Skanstatic void 416132720Skaninit_clnt_str(struct clnt_str *cs) 417132720Skan{ 418132720Skan cs->data = NULL; 419132720Skan cs->len = 0; 420132720Skan} 421132720Skan 422132720Skanstatic void 423132720Skaninit_srvr_str(struct srvr_str *ss) 424132720Skan{ 425132720Skan ss->data = NULL; 426132720Skan ss->len = 0; 427132720Skan} 428132720Skan 429132720Skanstatic int 430132720Skanread_timed(struct tac_handle *h, void *buf, size_t len, 431132720Skan const struct timeval *deadline) 432132720Skan{ 433132720Skan char *ptr; 434132720Skan 435132720Skan ptr = (char *)buf; 436132720Skan while (len > 0) { 437132720Skan int n; 438132720Skan 439132720Skan n = read(h->fd, ptr, len); 440132720Skan if (n == -1) { 441132720Skan struct timeval tv; 442132720Skan int nfds; 443132720Skan 444132720Skan if (errno != EAGAIN) { 445132720Skan generr(h, "Network read error: %s", 446132720Skan strerror(errno)); 447132720Skan return -1; 448132720Skan } 449132720Skan 450132720Skan /* Wait until we can read more data. */ 451132720Skan gettimeofday(&tv, NULL); 452132720Skan timersub(deadline, &tv, &tv); 453132720Skan if (tv.tv_sec >= 0) { 454169691Skan fd_set rfds; 455132720Skan 456132720Skan FD_ZERO(&rfds); 457132720Skan FD_SET(h->fd, &rfds); 458132720Skan nfds = 459132720Skan select(h->fd + 1, &rfds, NULL, NULL, &tv); 460132720Skan if (nfds == -1) { 461132720Skan generr(h, "select: %s", 462132720Skan strerror(errno)); 463132720Skan return -1; 464132720Skan } 465132720Skan } else 466132720Skan nfds = 0; 467132720Skan if (nfds == 0) { 468132720Skan generr(h, "Network read timed out"); 469132720Skan return -1; 470132720Skan } 471132720Skan } else if (n == 0) { 472132720Skan generr(h, "unexpected EOF from server"); 473132720Skan return -1; 474132720Skan } else { 475132720Skan ptr += n; 476132720Skan len -= n; 477132720Skan } 478132720Skan } 479132720Skan return 0; 480132720Skan} 481132720Skan 482132720Skan/* 483132720Skan * Receive a response from the server and decrypt it. Returns 0 on 484132720Skan * success, or -1 on failure. 485132720Skan */ 486132720Skanstatic int 487132720Skanrecv_msg(struct tac_handle *h) 488132720Skan{ 489132720Skan struct timeval deadline; 490132720Skan struct tac_msg *msg; 491132720Skan size_t len; 492132720Skan 493132720Skan msg = &h->response; 494132720Skan gettimeofday(&deadline, NULL); 495132720Skan deadline.tv_sec += h->servers[h->cur_server].timeout; 496132720Skan 497132720Skan /* Read the message header and make sure it is reasonable. */ 498132720Skan if (read_timed(h, msg, HDRSIZE, &deadline) == -1) 499132720Skan return -1; 500132720Skan if (memcmp(msg->session_id, h->request.session_id, 501132720Skan sizeof msg->session_id) != 0) { 502132720Skan generr(h, "Invalid session ID in received message"); 503132720Skan return -1; 504132720Skan } 505132720Skan if (msg->type != h->request.type) { 506132720Skan generr(h, "Invalid type in received message"); 507132720Skan return -1; 508132720Skan } 509132720Skan len = ntohl(msg->length); 510132720Skan if (len > BODYSIZE) { 511132720Skan generr(h, "Received message too large"); 512132720Skan return -1; 513132720Skan } 514132720Skan if (msg->seq_no != ++h->last_seq_no) { 515132720Skan generr(h, "Invalid sequence number in received message"); 516132720Skan return -1; 517132720Skan } 518132720Skan 519132720Skan /* Read the message body. */ 520132720Skan if (read_timed(h, msg->u.body, len, &deadline) == -1) 521132720Skan return -1; 522132720Skan 523132720Skan /* Decrypt it. */ 524132720Skan crypt_msg(h, msg); 525132720Skan 526132720Skan /* 527132720Skan * Turn off single-connection mode if the server isn't amenable 528132720Skan * to it. 529132720Skan */ 530132720Skan if (!(msg->flags & TAC_SINGLE_CONNECT)) 531132720Skan h->single_connect = 0; 532132720Skan return 0; 533132720Skan} 534132720Skan 535132720Skanstatic int 536132720Skansave_str(struct tac_handle *h, struct clnt_str *cs, const void *data, 537132720Skan size_t len) 538132720Skan{ 539132720Skan free_str(cs); 540132720Skan if (data != NULL && len != 0) { 541132720Skan if ((cs->data = xmalloc(h, len)) == NULL) 542132720Skan return -1; 543 cs->len = len; 544 memcpy(cs->data, data, len); 545 } 546 return 0; 547} 548 549/* 550 * Send the current request, after encrypting it. Returns 0 on success, 551 * or -1 on failure. 552 */ 553static int 554send_msg(struct tac_handle *h) 555{ 556 struct timeval deadline; 557 struct tac_msg *msg; 558 char *ptr; 559 int len; 560 561 if (h->last_seq_no & 1) { 562 generr(h, "Attempt to send message out of sequence"); 563 return -1; 564 } 565 566 msg = &h->request; 567 msg->seq_no = ++h->last_seq_no; 568 if (msg->seq_no == 1) 569 gen_session_id(msg); 570 crypt_msg(h, msg); 571 572 if (establish_connection(h) == -1) 573 return -1; 574 575 if (h->single_connect) 576 msg->flags |= TAC_SINGLE_CONNECT; 577 else 578 msg->flags &= ~TAC_SINGLE_CONNECT; 579 gettimeofday(&deadline, NULL); 580 deadline.tv_sec += h->servers[h->cur_server].timeout; 581 len = HDRSIZE + ntohl(msg->length); 582 ptr = (char *)msg; 583 while (len > 0) { 584 int n; 585 586 n = write(h->fd, ptr, len); 587 if (n == -1) { 588 struct timeval tv; 589 int nfds; 590 591 if (errno != EAGAIN) { 592 generr(h, "Network write error: %s", 593 strerror(errno)); 594 return -1; 595 } 596 597 /* Wait until we can write more data. */ 598 gettimeofday(&tv, NULL); 599 timersub(&deadline, &tv, &tv); 600 if (tv.tv_sec >= 0) { 601 fd_set wfds; 602 603 FD_ZERO(&wfds); 604 FD_SET(h->fd, &wfds); 605 nfds = 606 select(h->fd + 1, NULL, &wfds, NULL, &tv); 607 if (nfds == -1) { 608 generr(h, "select: %s", 609 strerror(errno)); 610 return -1; 611 } 612 } else 613 nfds = 0; 614 if (nfds == 0) { 615 generr(h, "Network write timed out"); 616 return -1; 617 } 618 } else { 619 ptr += n; 620 len -= n; 621 } 622 } 623 return 0; 624} 625 626/* 627 * Destructively split a string into fields separated by white space. 628 * `#' at the beginning of a field begins a comment that extends to the 629 * end of the string. Fields may be quoted with `"'. Inside quoted 630 * strings, the backslash escapes `\"' and `\\' are honored. 631 * 632 * Pointers to up to the first maxfields fields are stored in the fields 633 * array. Missing fields get NULL pointers. 634 * 635 * The return value is the actual number of fields parsed, and is always 636 * <= maxfields. 637 * 638 * On a syntax error, places a message in the msg string, and returns -1. 639 */ 640static int 641split(char *str, char *fields[], int maxfields, char *msg, size_t msglen) 642{ 643 char *p; 644 int i; 645 static const char ws[] = " \t"; 646 647 for (i = 0; i < maxfields; i++) 648 fields[i] = NULL; 649 p = str; 650 i = 0; 651 while (*p != '\0') { 652 p += strspn(p, ws); 653 if (*p == '#' || *p == '\0') 654 break; 655 if (i >= maxfields) { 656 snprintf(msg, msglen, "line has too many fields"); 657 return -1; 658 } 659 if (*p == '"') { 660 char *dst; 661 662 dst = ++p; 663 fields[i] = dst; 664 while (*p != '"') { 665 if (*p == '\\') { 666 p++; 667 if (*p != '"' && *p != '\\' && 668 *p != '\0') { 669 snprintf(msg, msglen, 670 "invalid `\\' escape"); 671 return -1; 672 } 673 } 674 if (*p == '\0') { 675 snprintf(msg, msglen, 676 "unterminated quoted string"); 677 return -1; 678 } 679 *dst++ = *p++; 680 } 681 *dst = '\0'; 682 p++; 683 if (*p != '\0' && strspn(p, ws) == 0) { 684 snprintf(msg, msglen, "quoted string not" 685 " followed by white space"); 686 return -1; 687 } 688 } else { 689 fields[i] = p; 690 p += strcspn(p, ws); 691 if (*p != '\0') 692 *p++ = '\0'; 693 } 694 i++; 695 } 696 return i; 697} 698 699int 700tac_add_server(struct tac_handle *h, const char *host, int port, 701 const char *secret, int timeout, int flags) 702{ 703 struct tac_server *srvp; 704 705 if (h->num_servers >= MAXSERVERS) { 706 generr(h, "Too many TACACS+ servers specified"); 707 return -1; 708 } 709 srvp = &h->servers[h->num_servers]; 710 711 memset(&srvp->addr, 0, sizeof srvp->addr); 712 srvp->addr.sin_len = sizeof srvp->addr; 713 srvp->addr.sin_family = AF_INET; 714 if (!inet_aton(host, &srvp->addr.sin_addr)) { 715 struct hostent *hent; 716 717 if ((hent = gethostbyname(host)) == NULL) { 718 generr(h, "%s: host not found", host); 719 return -1; 720 } 721 memcpy(&srvp->addr.sin_addr, hent->h_addr, 722 sizeof srvp->addr.sin_addr); 723 } 724 srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT); 725 if ((srvp->secret = xstrdup(h, secret)) == NULL) 726 return -1; 727 srvp->timeout = timeout; 728 srvp->flags = flags; 729 h->num_servers++; 730 return 0; 731} 732 733void 734tac_close(struct tac_handle *h) 735{ 736 int srv; 737 738 if (h->fd != -1) 739 close(h->fd); 740 for (srv = 0; srv < h->num_servers; srv++) { 741 memset(h->servers[srv].secret, 0, 742 strlen(h->servers[srv].secret)); 743 free(h->servers[srv].secret); 744 } 745 free_str(&h->user); 746 free_str(&h->port); 747 free_str(&h->rem_addr); 748 free_str(&h->data); 749 free_str(&h->user_msg); 750 free(h); 751} 752 753int 754tac_config(struct tac_handle *h, const char *path) 755{ 756 FILE *fp; 757 char buf[MAXCONFLINE]; 758 int linenum; 759 int retval; 760 761 if (path == NULL) 762 path = PATH_TACPLUS_CONF; 763 if ((fp = fopen(path, "r")) == NULL) { 764 generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); 765 return -1; 766 } 767 retval = 0; 768 linenum = 0; 769 while (fgets(buf, sizeof buf, fp) != NULL) { 770 int len; 771 char *fields[4]; 772 int nfields; 773 char msg[ERRSIZE]; 774 char *host, *res; 775 char *port_str; 776 char *secret; 777 char *timeout_str; 778 char *options_str; 779 char *end; 780 unsigned long timeout; 781 int port; 782 int options; 783 784 linenum++; 785 len = strlen(buf); 786 /* We know len > 0, else fgets would have returned NULL. */ 787 if (buf[len - 1] != '\n') { 788 if (len == sizeof buf - 1) 789 generr(h, "%s:%d: line too long", path, 790 linenum); 791 else 792 generr(h, "%s:%d: missing newline", path, 793 linenum); 794 retval = -1; 795 break; 796 } 797 buf[len - 1] = '\0'; 798 799 /* Extract the fields from the line. */ 800 nfields = split(buf, fields, 4, msg, sizeof msg); 801 if (nfields == -1) { 802 generr(h, "%s:%d: %s", path, linenum, msg); 803 retval = -1; 804 break; 805 } 806 if (nfields == 0) 807 continue; 808 if (nfields < 2) { 809 generr(h, "%s:%d: missing shared secret", path, 810 linenum); 811 retval = -1; 812 break; 813 } 814 host = fields[0]; 815 secret = fields[1]; 816 timeout_str = fields[2]; 817 options_str = fields[3]; 818 819 /* Parse and validate the fields. */ 820 res = host; 821 host = strsep(&res, ":"); 822 port_str = strsep(&res, ":"); 823 if (port_str != NULL) { 824 port = strtoul(port_str, &end, 10); 825 if (port_str[0] == '\0' || *end != '\0') { 826 generr(h, "%s:%d: invalid port", path, 827 linenum); 828 retval = -1; 829 break; 830 } 831 } else 832 port = 0; 833 if (timeout_str != NULL) { 834 timeout = strtoul(timeout_str, &end, 10); 835 if (timeout_str[0] == '\0' || *end != '\0') { 836 generr(h, "%s:%d: invalid timeout", path, 837 linenum); 838 retval = -1; 839 break; 840 } 841 } else 842 timeout = TIMEOUT; 843 options = 0; 844 if (options_str != NULL) { 845 if (strcmp(options_str, "single-connection") == 0) 846 options |= TAC_SRVR_SINGLE_CONNECT; 847 else { 848 generr(h, "%s:%d: invalid option \"%s\"", 849 path, linenum, options_str); 850 retval = -1; 851 break; 852 } 853 }; 854 855 if (tac_add_server(h, host, port, secret, timeout, 856 options) == -1) { 857 char msg[ERRSIZE]; 858 859 strcpy(msg, h->errmsg); 860 generr(h, "%s:%d: %s", path, linenum, msg); 861 retval = -1; 862 break; 863 } 864 } 865 /* Clear out the buffer to wipe a possible copy of a shared secret */ 866 memset(buf, 0, sizeof buf); 867 fclose(fp); 868 return retval; 869} 870 871int 872tac_create_authen(struct tac_handle *h, int action, int type, int service) 873{ 874 struct tac_msg *msg; 875 struct tac_authen_start *as; 876 877 h->last_seq_no = 0; 878 879 msg = &h->request; 880 msg->type = TAC_AUTHEN; 881 msg->version = authen_version(action, type); 882 msg->flags = 0; 883 884 as = &msg->u.authen_start; 885 as->action = action; 886 as->priv_lvl = TAC_PRIV_LVL_USER; 887 as->authen_type = type; 888 as->service = service; 889 890 free_str(&h->user); 891 free_str(&h->port); 892 free_str(&h->rem_addr); 893 free_str(&h->data); 894 free_str(&h->user_msg); 895 896 /* XXX - more to do */ 897 return 0; 898} 899 900void * 901tac_get_data(struct tac_handle *h, size_t *len) 902{ 903 return dup_str(h, &h->srvr_data, len); 904} 905 906char * 907tac_get_msg(struct tac_handle *h) 908{ 909 return (char *)dup_str(h, &h->srvr_msg, NULL); 910} 911 912/* 913 * Create and initialize a tac_handle structure, and return it to the 914 * caller. Can fail only if the necessary memory cannot be allocated. 915 * In that case, it returns NULL. 916 */ 917struct tac_handle * 918tac_open(void) 919{ 920 struct tac_handle *h; 921 922 h = (struct tac_handle *)malloc(sizeof(struct tac_handle)); 923 if (h != NULL) { 924 h->fd = -1; 925 h->num_servers = 0; 926 h->cur_server = 0; 927 h->errmsg[0] = '\0'; 928 init_clnt_str(&h->user); 929 init_clnt_str(&h->port); 930 init_clnt_str(&h->rem_addr); 931 init_clnt_str(&h->data); 932 init_clnt_str(&h->user_msg); 933 init_srvr_str(&h->srvr_msg); 934 init_srvr_str(&h->srvr_data); 935 srandomdev(); 936 } 937 return h; 938} 939 940int 941tac_send_authen(struct tac_handle *h) 942{ 943 struct tac_authen_reply *ar; 944 945 if (h->last_seq_no == 0) { /* Authentication START packet */ 946 struct tac_authen_start *as; 947 948 as = &h->request.u.authen_start; 949 h->request.length = 950 htonl(offsetof(struct tac_authen_start, rest[0])); 951 if (add_str_8(h, &as->user_len, &h->user) == -1 || 952 add_str_8(h, &as->port_len, &h->port) == -1 || 953 add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 || 954 add_str_8(h, &as->data_len, &h->data) == -1) 955 return -1; 956 } else { /* Authentication CONTINUE packet */ 957 struct tac_authen_cont *ac; 958 959 ac = &h->request.u.authen_cont; 960 ac->flags = 0; 961 h->request.length = 962 htonl(offsetof(struct tac_authen_cont, rest[0])); 963 if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 || 964 add_str_16(h, &ac->data_len, &h->data) == -1) 965 return -1; 966 } 967 968 /* Send the message and retrieve the reply. */ 969 if (send_msg(h) == -1 || recv_msg(h) == -1) 970 return -1; 971 972 /* Scan the optional fields in the reply. */ 973 ar = &h->response.u.authen_reply; 974 h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]); 975 if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 || 976 get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 || 977 get_srvr_end(h) == -1) 978 return -1; 979 980 if (!h->single_connect && 981 ar->status != TAC_AUTHEN_STATUS_GETDATA && 982 ar->status != TAC_AUTHEN_STATUS_GETUSER && 983 ar->status != TAC_AUTHEN_STATUS_GETPASS) 984 close_connection(h); 985 986 return ar->flags << 8 | ar->status; 987} 988 989int 990tac_set_rem_addr(struct tac_handle *h, const char *addr) 991{ 992 return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0); 993} 994 995int 996tac_set_data(struct tac_handle *h, const void *data, size_t data_len) 997{ 998 return save_str(h, &h->data, data, data_len); 999} 1000 1001int 1002tac_set_msg(struct tac_handle *h, const char *msg) 1003{ 1004 return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0); 1005} 1006 1007int 1008tac_set_port(struct tac_handle *h, const char *port) 1009{ 1010 return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0); 1011} 1012 1013int 1014tac_set_priv(struct tac_handle *h, int priv) 1015{ 1016 if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) { 1017 generr(h, "Attempt to set invalid privilege level"); 1018 return -1; 1019 } 1020 h->request.u.authen_start.priv_lvl = priv; 1021 return 0; 1022} 1023 1024int 1025tac_set_user(struct tac_handle *h, const char *user) 1026{ 1027 return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0); 1028} 1029 1030const char * 1031tac_strerror(struct tac_handle *h) 1032{ 1033 return h->errmsg; 1034} 1035 1036static void * 1037xmalloc(struct tac_handle *h, size_t size) 1038{ 1039 void *r; 1040 1041 if ((r = malloc(size)) == NULL) 1042 generr(h, "Out of memory"); 1043 return r; 1044} 1045 1046static char * 1047xstrdup(struct tac_handle *h, const char *s) 1048{ 1049 char *r; 1050 1051 if ((r = strdup(s)) == NULL) 1052 generr(h, "Out of memory"); 1053 return r; 1054} 1055