taclib.c revision 56141
118334Speter/*- 252284Sobrien * Copyright 1998 Juniper Networks, Inc. 318334Speter * All rights reserved. 418334Speter * 518334Speter * Redistribution and use in source and binary forms, with or without 618334Speter * modification, are permitted provided that the following conditions 718334Speter * are met: 818334Speter * 1. Redistributions of source code must retain the above copyright 918334Speter * notice, this list of conditions and the following disclaimer. 1018334Speter * 2. Redistributions in binary form must reproduce the above copyright 1118334Speter * notice, this list of conditions and the following disclaimer in the 1218334Speter * documentation and/or other materials provided with the distribution. 1318334Speter * 1418334Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1518334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1618334Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1718334Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1818334Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1918334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2018334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2118334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2218334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2418334Speter * SUCH DAMAGE. 2518334Speter * 2618334Speter * $FreeBSD: head/lib/libtacplus/taclib.c 56141 2000-01-17 04:26:09Z jdp $ 2718334Speter */ 2818334Speter 2918334Speter#include <sys/types.h> 3018334Speter#include <sys/socket.h> 3118334Speter#include <sys/time.h> 3218334Speter#include <netinet/in.h> 3318334Speter#include <arpa/inet.h> 3418334Speter 3518334Speter#include <assert.h> 3618334Speter#include <errno.h> 3718334Speter#include <fcntl.h> 3818334Speter#include <md5.h> 3918334Speter#include <netdb.h> 4018334Speter#include <stdarg.h> 4118334Speter#include <stddef.h> 4218334Speter#include <stdio.h> 4318334Speter#include <stdlib.h> 4418334Speter#include <string.h> 4518334Speter#include <unistd.h> 4618334Speter 4718334Speter#include "taclib_private.h" 4818334Speter 4918334Speterstatic int add_str_8(struct tac_handle *, u_int8_t *, 5018334Speter struct clnt_str *); 5118334Speterstatic int add_str_16(struct tac_handle *, u_int16_t *, 5218334Speter struct clnt_str *); 5318334Speterstatic int authen_version(int, int); 5418334Speterstatic void close_connection(struct tac_handle *); 5518334Speterstatic int conn_server(struct tac_handle *); 5618334Speterstatic void crypt_msg(struct tac_handle *, struct tac_msg *); 5718334Speterstatic void *dup_str(struct tac_handle *, const struct srvr_str *, 5818334Speter size_t *); 5918334Speterstatic int establish_connection(struct tac_handle *); 6050397Sobrienstatic void free_str(struct clnt_str *); 6152284Sobrienstatic void generr(struct tac_handle *, const char *, ...) 6252284Sobrien __printflike(2, 3); 6352284Sobrienstatic void gen_session_id(struct tac_msg *); 6452284Sobrienstatic int get_srvr_end(struct tac_handle *); 6518334Speterstatic int get_srvr_str(struct tac_handle *, struct srvr_str *, 6618334Speter size_t); 6718334Speterstatic void init_clnt_str(struct clnt_str *); 6818334Speterstatic void init_srvr_str(struct srvr_str *); 6918334Speterstatic int read_timed(struct tac_handle *, void *, size_t, 7018334Speter const struct timeval *); 7118334Speterstatic int recv_msg(struct tac_handle *); 7218334Speterstatic int save_str(struct tac_handle *, struct clnt_str *, 7350397Sobrien const void *, size_t); 7450397Sobrienstatic int send_msg(struct tac_handle *); 7550397Sobrienstatic int split(char *, char *[], int, char *, size_t); 7650397Sobrienstatic void *xmalloc(struct tac_handle *, size_t); 7750397Sobrienstatic char *xstrdup(struct tac_handle *, const char *); 7850397Sobrien 7950397Sobrien/* 8050397Sobrien * Append some optional data to the current request, and store its 8150397Sobrien * length into the 8-bit field referenced by "fld". Returns 0 on 8250397Sobrien * success, or -1 on failure. 8350397Sobrien * 8450397Sobrien * This function also frees the "cs" string data and initializes it 8518334Speter * for the next time. 8618334Speter */ 8718334Speterstatic int 8818334Speteradd_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs) 8918334Speter{ 9018334Speter u_int16_t len; 9118334Speter 9218334Speter if (add_str_16(h, &len, cs) == -1) 9318334Speter return -1; 9418334Speter len = ntohs(len); 9518334Speter if (len > 0xff) { 9618334Speter generr(h, "Field too long"); 9718334Speter return -1; 9818334Speter } 9918334Speter *fld = len; 10018334Speter return 0; 10118334Speter} 10218334Speter 10350397Sobrien/* 10450397Sobrien * Append some optional data to the current request, and store its 10550397Sobrien * length into the 16-bit field (network byte order) referenced by 10618334Speter * "fld". Returns 0 on success, or -1 on failure. 10750397Sobrien * 10818334Speter * This function also frees the "cs" string data and initializes it 10918334Speter * for the next time. 11018334Speter */ 11118334Speterstatic int 11218334Speteradd_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs) 11318334Speter{ 11452284Sobrien size_t len; 11552284Sobrien 11652284Sobrien len = cs->len; 11750397Sobrien if (cs->data == NULL) 11818334Speter len = 0; 11918334Speter if (len != 0) { 12018334Speter int offset; 12118334Speter 12252284Sobrien if (len > 0xffff) { 12352284Sobrien generr(h, "Field too long"); 12418334Speter return -1; 12518334Speter } 12618334Speter offset = ntohl(h->request.length); 12718334Speter if (offset + len > BODYSIZE) { 12818334Speter generr(h, "Message too long"); 12918334Speter return -1; 13018334Speter } 13118334Speter memcpy(h->request.u.body + offset, cs->data, len); 13218334Speter h->request.length = htonl(offset + len); 13318334Speter } 13450397Sobrien *fld = htons(len); 13518334Speter free_str(cs); 13618334Speter return 0; 13718334Speter} 13818334Speter 13918334Speterstatic int 14018334Speterauthen_version(int action, int type) 14150397Sobrien{ 14218334Speter int minor; 14318334Speter 14418334Speter switch (action) { 14518334Speter 14652284Sobrien case TAC_AUTHEN_LOGIN: 14718334Speter switch (type) { 14852284Sobrien 14952284Sobrien case TAC_AUTHEN_TYPE_PAP: 15018334Speter case TAC_AUTHEN_TYPE_CHAP: 15152284Sobrien case TAC_AUTHEN_TYPE_MSCHAP: 15218334Speter case TAC_AUTHEN_TYPE_ARAP: 15318334Speter minor = 1; 15418334Speter break; 15518334Speter 15618334Speter default: 15718334Speter minor = 0; 15818334Speter break; 15918334Speter } 16018334Speter break; 16118334Speter 16218334Speter case TAC_AUTHEN_SENDAUTH: 16318334Speter minor = 1; 16418334Speter break; 16518334Speter 16618334Speter default: 16718334Speter minor = 0; 16818334Speter break; 16918334Speter }; 17018334Speter 17118334Speter return TAC_VER_MAJOR << 4 | minor; 17218334Speter} 17318334Speter 17418334Speterstatic void 17518334Speterclose_connection(struct tac_handle *h) 17618334Speter{ 17718334Speter if (h->fd != -1) { 17818334Speter close(h->fd); 17918334Speter h->fd = -1; 18018334Speter } 18118334Speter} 18218334Speter 18318334Speterstatic int 18418334Speterconn_server(struct tac_handle *h) 18518334Speter{ 18618334Speter const struct tac_server *srvp = &h->servers[h->cur_server]; 18718334Speter int flags; 18818334Speter 18918334Speter if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 19018334Speter generr(h, "Cannot create socket: %s", strerror(errno)); 19118334Speter return -1; 19218334Speter } 19318334Speter if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 || 19418334Speter fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) { 19518334Speter generr(h, "Cannot set non-blocking mode on socket: %s", 19618334Speter strerror(errno)); 19718334Speter close(h->fd); 19818334Speter h->fd = -1; 19950397Sobrien return -1; 20018334Speter } 20118334Speter if (connect(h->fd, (struct sockaddr *)&srvp->addr, 20218334Speter sizeof srvp->addr) == 0) 20318334Speter return 0; 20418334Speter 20518334Speter if (errno == EINPROGRESS) { 20618334Speter fd_set wfds; 20718334Speter struct timeval tv; 20818334Speter int nfds; 20918334Speter struct sockaddr peer; 21018334Speter int peerlen; 21118334Speter int err; 21218334Speter int errlen; 21318334Speter 21418334Speter /* Wait for the connection to complete. */ 21518334Speter FD_ZERO(&wfds); 21618334Speter FD_SET(h->fd, &wfds); 21718334Speter tv.tv_sec = srvp->timeout; 21818334Speter tv.tv_usec = 0; 21918334Speter nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv); 22018334Speter if (nfds == -1) { 22118334Speter generr(h, "select: %s", strerror(errno)); 22218334Speter close(h->fd); 22318334Speter h->fd = -1; 22418334Speter return -1; 22518334Speter } 22618334Speter if (nfds == 0) { 22718334Speter generr(h, "connect: timed out"); 22818334Speter close(h->fd); 22918334Speter h->fd = -1; 23018334Speter return -1; 23118334Speter } 23218334Speter 23318334Speter /* See whether we are connected now. */ 23418334Speter peerlen = sizeof peer; 23518334Speter if (getpeername(h->fd, &peer, &peerlen) == 0) 23618334Speter return 0; 23718334Speter 23818334Speter if (errno != ENOTCONN) { 23918334Speter generr(h, "getpeername: %s", strerror(errno)); 24050397Sobrien close(h->fd); 24150397Sobrien h->fd = -1; 24250397Sobrien return -1; 24318334Speter } 24418334Speter 24518334Speter /* Find out why the connect failed. */ 24618334Speter errlen = sizeof err; 24718334Speter getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); 24850397Sobrien errno = err; 24918334Speter } 25018334Speter generr(h, "connect: %s", strerror(errno)); 25118334Speter close(h->fd); 25250397Sobrien h->fd = -1; 25318334Speter return -1; 25418334Speter} 25550397Sobrien 25618334Speter/* 25750397Sobrien * Encrypt or decrypt a message. The operations are symmetrical. 25818334Speter */ 25918334Speterstatic void 26018334Spetercrypt_msg(struct tac_handle *h, struct tac_msg *msg) 26150397Sobrien{ 26218334Speter const char *secret; 26318334Speter MD5_CTX base_ctx; 26450397Sobrien MD5_CTX ctx; 26518334Speter unsigned char md5[16]; 26618334Speter int chunk; 26750397Sobrien int msg_len; 26818334Speter 26918334Speter secret = h->servers[h->cur_server].secret; 27050397Sobrien if (secret[0] == '\0') 27118334Speter msg->flags |= TAC_UNENCRYPTED; 27250397Sobrien if (msg->flags & TAC_UNENCRYPTED) 27318334Speter return; 27450397Sobrien 27518334Speter msg_len = ntohl(msg->length); 27618334Speter 27718334Speter MD5Init(&base_ctx); 27818334Speter MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id); 27918334Speter MD5Update(&base_ctx, secret, strlen(secret)); 28018334Speter MD5Update(&base_ctx, &msg->version, sizeof msg->version); 28118334Speter MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no); 28218334Speter 28318334Speter ctx = base_ctx; 28418334Speter for (chunk = 0; chunk < msg_len; chunk += sizeof md5) { 28518334Speter int chunk_len; 28618334Speter int i; 28718334Speter 28818334Speter MD5Final(md5, &ctx); 28918334Speter 29018334Speter if ((chunk_len = msg_len - chunk) > sizeof md5) 29118334Speter chunk_len = sizeof md5; 29218334Speter for (i = 0; i < chunk_len; i++) 29318334Speter msg->u.body[chunk + i] ^= md5[i]; 29418334Speter 29518334Speter ctx = base_ctx; 29618334Speter MD5Update(&ctx, md5, sizeof md5); 29718334Speter } 29818334Speter} 29918334Speter 30018334Speter/* 30118334Speter * Return a dynamically allocated copy of the given server string. 30218334Speter * The copy is null-terminated. If "len" is non-NULL, the length of 30318334Speter * the string (excluding the terminating null byte) is stored via it. 30418334Speter * Returns NULL on failure. Empty strings are still allocated even 30518334Speter * though they have no content. 30618334Speter */ 30718334Speterstatic void * 30818334Speterdup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len) 30918334Speter{ 31018334Speter unsigned char *p; 31118334Speter 31218334Speter if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL) 31318334Speter return NULL; 31418334Speter if (ss->data != NULL && ss->len != 0) 31518334Speter memcpy(p, ss->data, ss->len); 31618334Speter p[ss->len] = '\0'; 31718334Speter if (len != NULL) 31818334Speter *len = ss->len; 31918334Speter return p; 32018334Speter} 32118334Speter 32218334Speterstatic int 32318334Speterestablish_connection(struct tac_handle *h) 32418334Speter{ 32518334Speter int i; 32618334Speter 32718334Speter if (h->fd >= 0) /* Already connected. */ 32818334Speter return 0; 32918334Speter if (h->num_servers == 0) { 33018334Speter generr(h, "No TACACS+ servers specified"); 33118334Speter return -1; 33218334Speter } 33318334Speter /* 33418334Speter * Try the servers round-robin. We begin with the one that 33518334Speter * worked for us the last time. That way, once we find a good 33618334Speter * server, we won't waste any more time trying the bad ones. 33718334Speter */ 33818334Speter for (i = 0; i < h->num_servers; i++) { 33918334Speter if (conn_server(h) == 0) { 34018334Speter h->single_connect = (h->servers[h->cur_server].flags & 34118334Speter TAC_SRVR_SINGLE_CONNECT) != 0; 34218334Speter return 0; 34318334Speter } 34418334Speter if (++h->cur_server >= h->num_servers) /* Wrap around */ 34518334Speter h->cur_server = 0; 34618334Speter } 34718334Speter /* Just return whatever error was last reported by conn_server(). */ 34818334Speter return -1; 34918334Speter} 35018334Speter 35118334Speter/* 35218334Speter * Free a client string, obliterating its contents first for security. 35318334Speter */ 35418334Speterstatic void 35518334Speterfree_str(struct clnt_str *cs) 35618334Speter{ 35718334Speter if (cs->data != NULL) { 35818334Speter memset(cs->data, 0, cs->len); 35918334Speter free(cs->data); 36018334Speter cs->data = NULL; 36118334Speter cs->len = 0; 36218334Speter } 36318334Speter} 36418334Speter 36518334Speterstatic void 36618334Spetergenerr(struct tac_handle *h, const char *format, ...) 36718334Speter{ 36818334Speter va_list ap; 36918334Speter 37018334Speter va_start(ap, format); 37118334Speter vsnprintf(h->errmsg, ERRSIZE, format, ap); 37218334Speter va_end(ap); 37318334Speter} 37418334Speter 37518334Speterstatic void 37618334Spetergen_session_id(struct tac_msg *msg) 37718334Speter{ 37818334Speter int r; 37918334Speter 38018334Speter r = random(); 38118334Speter msg->session_id[0] = r >> 8; 38218334Speter msg->session_id[1] = r; 38318334Speter r = random(); 38418334Speter msg->session_id[2] = r >> 8; 38518334Speter msg->session_id[3] = r; 38618334Speter} 38718334Speter 38818334Speter/* 38918334Speter * Verify that we are exactly at the end of the response message. 39018334Speter * Returns 0 on success, -1 on failure. 39118334Speter */ 39218334Speterstatic int 39318334Speterget_srvr_end(struct tac_handle *h) 39418334Speter{ 39518334Speter if (h->srvr_pos != ntohl(h->response.length)) { 39618334Speter generr(h, "Invalid length field in response from server"); 39718334Speter return -1; 39818334Speter } 39918334Speter return 0; 40018334Speter} 40118334Speter 40218334Speterstatic int 40318334Speterget_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len) 40418334Speter{ 40518334Speter if (h->srvr_pos + len > ntohl(h->response.length)) { 40618334Speter generr(h, "Invalid length field in response from server"); 40718334Speter return -1; 40818334Speter } 40918334Speter ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL; 41018334Speter ss->len = len; 41118334Speter h->srvr_pos += len; 41218334Speter return 0; 41318334Speter} 41418334Speter 41518334Speterstatic void 41618334Speterinit_clnt_str(struct clnt_str *cs) 41718334Speter{ 41818334Speter cs->data = NULL; 41918334Speter cs->len = 0; 42018334Speter} 42118334Speter 42218334Speterstatic void 42318334Speterinit_srvr_str(struct srvr_str *ss) 42418334Speter{ 42518334Speter ss->data = NULL; 42618334Speter ss->len = 0; 42718334Speter} 42818334Speter 42918334Speterstatic int 43018334Speterread_timed(struct tac_handle *h, void *buf, size_t len, 43118334Speter const struct timeval *deadline) 43218334Speter{ 43318334Speter char *ptr; 43418334Speter 43518334Speter ptr = (char *)buf; 43618334Speter while (len > 0) { 43718334Speter int n; 43818334Speter 43918334Speter n = read(h->fd, ptr, len); 44018334Speter if (n == -1) { 44118334Speter struct timeval tv; 44218334Speter int nfds; 44318334Speter 44418334Speter if (errno != EAGAIN) { 44518334Speter generr(h, "Network read error: %s", 44618334Speter strerror(errno)); 44718334Speter return -1; 44850397Sobrien } 44918334Speter 45018334Speter /* Wait until we can read more data. */ 45118334Speter gettimeofday(&tv, NULL); 45218334Speter timersub(deadline, &tv, &tv); 45318334Speter if (tv.tv_sec >= 0) { 45418334Speter fd_set rfds; 45518334Speter 45618334Speter FD_ZERO(&rfds); 45718334Speter FD_SET(h->fd, &rfds); 45818334Speter nfds = 45918334Speter select(h->fd + 1, &rfds, NULL, NULL, &tv); 46018334Speter if (nfds == -1) { 46118334Speter generr(h, "select: %s", 46218334Speter strerror(errno)); 46318334Speter return -1; 46418334Speter } 46518334Speter } else 46618334Speter nfds = 0; 46718334Speter if (nfds == 0) { 46818334Speter generr(h, "Network read timed out"); 46918334Speter return -1; 47018334Speter } 47118334Speter } else if (n == 0) { 47218334Speter generr(h, "unexpected EOF from server"); 47318334Speter return -1; 47418334Speter } else { 47518334Speter ptr += n; 47618334Speter len -= n; 47718334Speter } 47818334Speter } 47918334Speter return 0; 48018334Speter} 48118334Speter 48218334Speter/* 48350397Sobrien * Receive a response from the server and decrypt it. Returns 0 on 48418334Speter * success, or -1 on failure. 48518334Speter */ 48618334Speterstatic int 48718334Speterrecv_msg(struct tac_handle *h) 48818334Speter{ 48918334Speter struct timeval deadline; 49018334Speter struct tac_msg *msg; 49118334Speter size_t len; 49218334Speter 49318334Speter msg = &h->response; 49418334Speter gettimeofday(&deadline, NULL); 49518334Speter deadline.tv_sec += h->servers[h->cur_server].timeout; 49618334Speter 49718334Speter /* Read the message header and make sure it is reasonable. */ 49818334Speter if (read_timed(h, msg, HDRSIZE, &deadline) == -1) 49918334Speter return -1; 50018334Speter if (memcmp(msg->session_id, h->request.session_id, 50118334Speter sizeof msg->session_id) != 0) { 50218334Speter generr(h, "Invalid session ID in received message"); 50318334Speter return -1; 50418334Speter } 50518334Speter if (msg->type != h->request.type) { 50618334Speter generr(h, "Invalid type in received message"); 50718334Speter return -1; 50818334Speter } 50918334Speter len = ntohl(msg->length); 51018334Speter if (len > BODYSIZE) { 51118334Speter generr(h, "Received message too large"); 51218334Speter return -1; 51318334Speter } 51418334Speter if (msg->seq_no != ++h->last_seq_no) { 51518334Speter generr(h, "Invalid sequence number in received message"); 51618334Speter return -1; 51718334Speter } 51818334Speter 51918334Speter /* Read the message body. */ 52018334Speter if (read_timed(h, msg->u.body, len, &deadline) == -1) 52118334Speter return -1; 52218334Speter 52318334Speter /* Decrypt it. */ 52418334Speter crypt_msg(h, msg); 52518334Speter 52618334Speter /* 52718334Speter * Turn off single-connection mode if the server isn't amenable 52818334Speter * to it. 52918334Speter */ 53018334Speter if (!(msg->flags & TAC_SINGLE_CONNECT)) 53118334Speter h->single_connect = 0; 53218334Speter return 0; 53318334Speter} 53418334Speter 53518334Speterstatic int 53618334Spetersave_str(struct tac_handle *h, struct clnt_str *cs, const void *data, 53718334Speter size_t len) 53818334Speter{ 53918334Speter free_str(cs); 54018334Speter if (data != NULL && len != 0) { 54118334Speter if ((cs->data = xmalloc(h, len)) == NULL) 54218334Speter return -1; 54318334Speter cs->len = len; 54418334Speter memcpy(cs->data, data, len); 54518334Speter } 54618334Speter return 0; 54718334Speter} 54818334Speter 54918334Speter/* 55018334Speter * Send the current request, after encrypting it. Returns 0 on success, 55118334Speter * or -1 on failure. 55218334Speter */ 55318334Speterstatic int 55418334Spetersend_msg(struct tac_handle *h) 55518334Speter{ 55618334Speter struct timeval deadline; 55718334Speter struct tac_msg *msg; 55818334Speter char *ptr; 55918334Speter int len; 56018334Speter 56118334Speter if (h->last_seq_no & 1) { 56218334Speter generr(h, "Attempt to send message out of sequence"); 56318334Speter return -1; 56418334Speter } 56518334Speter 56618334Speter msg = &h->request; 56718334Speter msg->seq_no = ++h->last_seq_no; 56818334Speter if (msg->seq_no == 1) 56918334Speter gen_session_id(msg); 57018334Speter crypt_msg(h, msg); 57152284Sobrien 57252284Sobrien if (establish_connection(h) == -1) 57352284Sobrien return -1; 57452284Sobrien 57552284Sobrien if (h->single_connect) 57652284Sobrien msg->flags |= TAC_SINGLE_CONNECT; 57752284Sobrien else 57852284Sobrien msg->flags &= ~TAC_SINGLE_CONNECT; 57952284Sobrien gettimeofday(&deadline, NULL); 58052284Sobrien deadline.tv_sec += h->servers[h->cur_server].timeout; 58152284Sobrien len = HDRSIZE + ntohl(msg->length); 58252284Sobrien ptr = (char *)msg; 58352284Sobrien while (len > 0) { 58452284Sobrien int n; 58552284Sobrien 58652284Sobrien n = write(h->fd, ptr, len); 58752284Sobrien if (n == -1) { 58852284Sobrien struct timeval tv; 58952284Sobrien int nfds; 59052284Sobrien 59152284Sobrien if (errno != EAGAIN) { 59252284Sobrien generr(h, "Network write error: %s", 59318334Speter strerror(errno)); 59452284Sobrien return -1; 59552284Sobrien } 59618334Speter 59718334Speter /* Wait until we can write more data. */ 59818334Speter gettimeofday(&tv, NULL); 59918334Speter timersub(&deadline, &tv, &tv); 60018334Speter if (tv.tv_sec >= 0) { 60118334Speter fd_set wfds; 60218334Speter 60318334Speter FD_ZERO(&wfds); 60418334Speter FD_SET(h->fd, &wfds); 60518334Speter nfds = 60618334Speter select(h->fd + 1, NULL, &wfds, NULL, &tv); 60718334Speter if (nfds == -1) { 60852284Sobrien generr(h, "select: %s", 60918334Speter strerror(errno)); 61018334Speter return -1; 61118334Speter } 61218334Speter } else 61318334Speter nfds = 0; 61418334Speter if (nfds == 0) { 61518334Speter generr(h, "Network write timed out"); 61618334Speter return -1; 61752284Sobrien } 61818334Speter } else { 61952284Sobrien ptr += n; 62018334Speter len -= n; 62118334Speter } 62252284Sobrien } 62350397Sobrien return 0; 62418334Speter} 62552284Sobrien 62618334Speter/* 62718334Speter * Destructively split a string into fields separated by white space. 62818334Speter * `#' at the beginning of a field begins a comment that extends to the 62918334Speter * end of the string. Fields may be quoted with `"'. Inside quoted 63018334Speter * strings, the backslash escapes `\"' and `\\' are honored. 63118334Speter * 63218334Speter * Pointers to up to the first maxfields fields are stored in the fields 63318334Speter * array. Missing fields get NULL pointers. 63418334Speter * 63552284Sobrien * The return value is the actual number of fields parsed, and is always 63652284Sobrien * <= maxfields. 63752284Sobrien * 63852284Sobrien * On a syntax error, places a message in the msg string, and returns -1. 63952284Sobrien */ 64018334Speterstatic int 64118334Spetersplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen) 64252284Sobrien{ 64350397Sobrien char *p; 64418334Speter int i; 64552284Sobrien static const char ws[] = " \t"; 64618334Speter 64718334Speter for (i = 0; i < maxfields; i++) 64818334Speter fields[i] = NULL; 64918334Speter p = str; 65018334Speter i = 0; 65118334Speter while (*p != '\0') { 65218334Speter p += strspn(p, ws); 65318334Speter if (*p == '#' || *p == '\0') 65418334Speter break; 65518334Speter if (i >= maxfields) { 65618334Speter snprintf(msg, msglen, "line has too many fields"); 65718334Speter return -1; 65818334Speter } 65918334Speter if (*p == '"') { 66018334Speter char *dst; 66118334Speter 66218334Speter dst = ++p; 66318334Speter fields[i] = dst; 66418334Speter while (*p != '"') { 66518334Speter if (*p == '\\') { 66618334Speter p++; 66718334Speter if (*p != '"' && *p != '\\' && 66818334Speter *p != '\0') { 66918334Speter snprintf(msg, msglen, 67018334Speter "invalid `\\' escape"); 67118334Speter return -1; 67218334Speter } 67318334Speter } 67418334Speter if (*p == '\0') { 67518334Speter snprintf(msg, msglen, 67618334Speter "unterminated quoted string"); 67718334Speter return -1; 67818334Speter } 67918334Speter *dst++ = *p++; 68018334Speter } 68118334Speter *dst = '\0'; 68218334Speter p++; 68318334Speter if (*p != '\0' && strspn(p, ws) == 0) { 68418334Speter snprintf(msg, msglen, "quoted string not" 68518334Speter " followed by white space"); 68618334Speter return -1; 68718334Speter } 68818334Speter } else { 68918334Speter fields[i] = p; 69018334Speter p += strcspn(p, ws); 69118334Speter if (*p != '\0') 69218334Speter *p++ = '\0'; 69352284Sobrien } 69450397Sobrien i++; 69518334Speter } 69618334Speter return i; 69718334Speter} 69818334Speter 69918334Speterint 70018334Spetertac_add_server(struct tac_handle *h, const char *host, int port, 70118334Speter const char *secret, int timeout, int flags) 70218334Speter{ 70318334Speter struct tac_server *srvp; 70418334Speter 70518334Speter if (h->num_servers >= MAXSERVERS) { 70618334Speter generr(h, "Too many TACACS+ servers specified"); 70718334Speter return -1; 70818334Speter } 70918334Speter srvp = &h->servers[h->num_servers]; 71018334Speter 71118334Speter memset(&srvp->addr, 0, sizeof srvp->addr); 71218334Speter srvp->addr.sin_len = sizeof srvp->addr; 71318334Speter srvp->addr.sin_family = AF_INET; 71418334Speter if (!inet_aton(host, &srvp->addr.sin_addr)) { 71518334Speter struct hostent *hent; 71618334Speter 71718334Speter if ((hent = gethostbyname(host)) == NULL) { 71818334Speter generr(h, "%s: host not found", host); 71918334Speter return -1; 72018334Speter } 72118334Speter memcpy(&srvp->addr.sin_addr, hent->h_addr, 72218334Speter sizeof srvp->addr.sin_addr); 72318334Speter } 72418334Speter srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT); 72518334Speter if ((srvp->secret = xstrdup(h, secret)) == NULL) 72650397Sobrien return -1; 72718334Speter srvp->timeout = timeout; 72818334Speter srvp->flags = flags; 72918334Speter h->num_servers++; 73018334Speter return 0; 73118334Speter} 73218334Speter 73318334Spetervoid 73418334Spetertac_close(struct tac_handle *h) 73518334Speter{ 73618334Speter int srv; 73718334Speter 73818334Speter if (h->fd != -1) 73918334Speter close(h->fd); 74018334Speter for (srv = 0; srv < h->num_servers; srv++) { 74118334Speter memset(h->servers[srv].secret, 0, 74218334Speter strlen(h->servers[srv].secret)); 74318334Speter free(h->servers[srv].secret); 74418334Speter } 74518334Speter free_str(&h->user); 74618334Speter free_str(&h->port); 74718334Speter free_str(&h->rem_addr); 74818334Speter free_str(&h->data); 74918334Speter free_str(&h->user_msg); 75018334Speter free(h); 75118334Speter} 75218334Speter 75318334Speterint 75418334Spetertac_config(struct tac_handle *h, const char *path) 75518334Speter{ 75618334Speter FILE *fp; 75718334Speter char buf[MAXCONFLINE]; 75818334Speter int linenum; 75918334Speter int retval; 76018334Speter 76118334Speter if (path == NULL) 76218334Speter path = PATH_TACPLUS_CONF; 76318334Speter if ((fp = fopen(path, "r")) == NULL) { 76418334Speter generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); 76518334Speter return -1; 76618334Speter } 76718334Speter retval = 0; 76818334Speter linenum = 0; 76918334Speter while (fgets(buf, sizeof buf, fp) != NULL) { 77018334Speter int len; 77118334Speter char *fields[4]; 77218334Speter int nfields; 77318334Speter char msg[ERRSIZE]; 77418334Speter char *host; 77518334Speter char *port_str; 77618334Speter char *secret; 77718334Speter char *timeout_str; 77850397Sobrien char *options_str; 77918334Speter char *end; 78050397Sobrien unsigned long timeout; 78118334Speter int port; 78218334Speter int options; 78352284Sobrien 78452284Sobrien linenum++; 78518334Speter len = strlen(buf); 78618334Speter /* We know len > 0, else fgets would have returned NULL. */ 78718334Speter if (buf[len - 1] != '\n') { 78818334Speter if (len == sizeof buf - 1) 78918334Speter generr(h, "%s:%d: line too long", path, 79018334Speter linenum); 79118334Speter else 79218334Speter generr(h, "%s:%d: missing newline", path, 79318334Speter linenum); 79418334Speter retval = -1; 79518334Speter break; 79618334Speter } 79718334Speter buf[len - 1] = '\0'; 79818334Speter 79918334Speter /* Extract the fields from the line. */ 80018334Speter nfields = split(buf, fields, 4, msg, sizeof msg); 80118334Speter if (nfields == -1) { 80218334Speter generr(h, "%s:%d: %s", path, linenum, msg); 80318334Speter retval = -1; 80418334Speter break; 80518334Speter } 80618334Speter if (nfields == 0) 80718334Speter continue; 80818334Speter if (nfields < 2) { 80918334Speter generr(h, "%s:%d: missing shared secret", path, 81018334Speter linenum); 81118334Speter retval = -1; 81218334Speter break; 81318334Speter } 81418334Speter host = fields[0]; 81552284Sobrien secret = fields[1]; 81618334Speter timeout_str = fields[2]; 81750397Sobrien options_str = fields[3]; 81818334Speter 81918334Speter /* Parse and validate the fields. */ 82018334Speter host = strtok(host, ":"); 82118334Speter port_str = strtok(NULL, ":"); 82218334Speter if (port_str != NULL) { 82318334Speter port = strtoul(port_str, &end, 10); 82418334Speter if (port_str[0] == '\0' || *end != '\0') { 82518334Speter generr(h, "%s:%d: invalid port", path, 82618334Speter linenum); 82752284Sobrien retval = -1; 82852284Sobrien break; 82918334Speter } 83052284Sobrien } else 83152284Sobrien port = 0; 83218334Speter if (timeout_str != NULL) { 83350397Sobrien timeout = strtoul(timeout_str, &end, 10); 83418334Speter if (timeout_str[0] == '\0' || *end != '\0') { 83518334Speter generr(h, "%s:%d: invalid timeout", path, 83618334Speter linenum); 83718334Speter retval = -1; 83818334Speter break; 83918334Speter } 84018334Speter } else 84118334Speter timeout = TIMEOUT; 84218334Speter options = 0; 84318334Speter if (options_str != NULL) { 84418334Speter if (strcmp(options_str, "single-connection") == 0) 84518334Speter options |= TAC_SRVR_SINGLE_CONNECT; 84618334Speter else { 84718334Speter generr(h, "%s:%d: invalid option \"%s\"", 84818334Speter path, linenum, options_str); 84918334Speter retval = -1; 85018334Speter break; 85118334Speter } 85218334Speter }; 85318334Speter 85418334Speter if (tac_add_server(h, host, port, secret, timeout, 85518334Speter options) == -1) { 85618334Speter char msg[ERRSIZE]; 85718334Speter 85818334Speter strcpy(msg, h->errmsg); 85918334Speter generr(h, "%s:%d: %s", path, linenum, msg); 86018334Speter retval = -1; 86118334Speter break; 86218334Speter } 86318334Speter } 86418334Speter /* Clear out the buffer to wipe a possible copy of a shared secret */ 86518334Speter memset(buf, 0, sizeof buf); 86618334Speter fclose(fp); 86718334Speter return retval; 86818334Speter} 86918334Speter 87018334Speterint 87118334Spetertac_create_authen(struct tac_handle *h, int action, int type, int service) 87218334Speter{ 87350397Sobrien struct tac_msg *msg; 87418334Speter struct tac_authen_start *as; 87518334Speter 87618334Speter h->last_seq_no = 0; 87718334Speter 87818334Speter msg = &h->request; 87918334Speter msg->type = TAC_AUTHEN; 88018334Speter msg->version = authen_version(action, type); 88118334Speter msg->flags = 0; 88218334Speter 88318334Speter as = &msg->u.authen_start; 88418334Speter as->action = action; 88518334Speter as->priv_lvl = TAC_PRIV_LVL_USER; 88618334Speter as->authen_type = type; 88718334Speter as->service = service; 88818334Speter 88918334Speter free_str(&h->user); 89018334Speter free_str(&h->port); 89118334Speter free_str(&h->rem_addr); 89218334Speter free_str(&h->data); 89318334Speter free_str(&h->user_msg); 89418334Speter 89518334Speter /* XXX - more to do */ 89618334Speter return 0; 89718334Speter} 89818334Speter 89918334Spetervoid * 90018334Spetertac_get_data(struct tac_handle *h, size_t *len) 90118334Speter{ 90218334Speter return dup_str(h, &h->srvr_data, len); 90318334Speter} 90418334Speter 90518334Speterchar * 90618334Spetertac_get_msg(struct tac_handle *h) 90750397Sobrien{ 90818334Speter return (char *)dup_str(h, &h->srvr_msg, NULL); 90918334Speter} 91018334Speter 91118334Speter/* 91218334Speter * Create and initialize a tac_handle structure, and return it to the 91318334Speter * caller. Can fail only if the necessary memory cannot be allocated. 91418334Speter * In that case, it returns NULL. 91518334Speter */ 91618334Speterstruct tac_handle * 91752284Sobrientac_open(void) 91852284Sobrien{ 91918334Speter struct tac_handle *h; 92018334Speter 92118334Speter h = (struct tac_handle *)malloc(sizeof(struct tac_handle)); 92218334Speter if (h != NULL) { 92318334Speter h->fd = -1; 92418334Speter h->num_servers = 0; 92552284Sobrien h->cur_server = 0; 92652284Sobrien h->errmsg[0] = '\0'; 92718334Speter init_clnt_str(&h->user); 92818334Speter init_clnt_str(&h->port); 92918334Speter init_clnt_str(&h->rem_addr); 93018334Speter init_clnt_str(&h->data); 93118334Speter init_clnt_str(&h->user_msg); 93218334Speter init_srvr_str(&h->srvr_msg); 93352284Sobrien init_srvr_str(&h->srvr_data); 93452284Sobrien srandomdev(); 93518334Speter } 93618334Speter return h; 93718334Speter} 93818334Speter 93918334Speterint 94018334Spetertac_send_authen(struct tac_handle *h) 94118334Speter{ 94218334Speter struct tac_authen_reply *ar; 94318334Speter 94418334Speter if (h->last_seq_no == 0) { /* Authentication START packet */ 94518334Speter struct tac_authen_start *as; 94618334Speter 94718334Speter as = &h->request.u.authen_start; 94818334Speter h->request.length = 94918334Speter htonl(offsetof(struct tac_authen_start, rest[0])); 95018334Speter if (add_str_8(h, &as->user_len, &h->user) == -1 || 95118334Speter add_str_8(h, &as->port_len, &h->port) == -1 || 95218334Speter add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 || 95318334Speter add_str_8(h, &as->data_len, &h->data) == -1) 95418334Speter return -1; 95518334Speter } else { /* Authentication CONTINUE packet */ 95618334Speter struct tac_authen_cont *ac; 95718334Speter 95818334Speter ac = &h->request.u.authen_cont; 95918334Speter ac->flags = 0; 96018334Speter h->request.length = 96118334Speter htonl(offsetof(struct tac_authen_cont, rest[0])); 96218334Speter if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 || 96318334Speter add_str_16(h, &ac->data_len, &h->data) == -1) 96418334Speter return -1; 96518334Speter } 96618334Speter 96718334Speter /* Send the message and retrieve the reply. */ 96818334Speter if (send_msg(h) == -1 || recv_msg(h) == -1) 96918334Speter return -1; 97018334Speter 97118334Speter /* Scan the optional fields in the reply. */ 97218334Speter ar = &h->response.u.authen_reply; 97318334Speter h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]); 97418334Speter if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 || 97518334Speter get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 || 97618334Speter get_srvr_end(h) == -1) 97718334Speter return -1; 97818334Speter 97918334Speter if (!h->single_connect && 98018334Speter ar->status != TAC_AUTHEN_STATUS_GETDATA && 98118334Speter ar->status != TAC_AUTHEN_STATUS_GETUSER && 98218334Speter ar->status != TAC_AUTHEN_STATUS_GETPASS) 98318334Speter close_connection(h); 98418334Speter 98518334Speter return ar->flags << 8 | ar->status; 98650397Sobrien} 98718334Speter 98850397Sobrienint 98918334Spetertac_set_rem_addr(struct tac_handle *h, const char *addr) 99018334Speter{ 99118334Speter return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0); 99218334Speter} 99350397Sobrien 99418334Speterint 99518334Spetertac_set_data(struct tac_handle *h, const void *data, size_t data_len) 99618334Speter{ 99718334Speter return save_str(h, &h->data, data, data_len); 99818334Speter} 99918334Speter 100018334Speterint 100118334Spetertac_set_msg(struct tac_handle *h, const char *msg) 100218334Speter{ 100318334Speter return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0); 100418334Speter} 100518334Speter 100618334Speterint 100718334Spetertac_set_port(struct tac_handle *h, const char *port) 100818334Speter{ 100918334Speter return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0); 101018334Speter} 101118334Speter 101218334Speterint 101318334Spetertac_set_priv(struct tac_handle *h, int priv) 101418334Speter{ 101518334Speter if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) { 101618334Speter generr(h, "Attempt to set invalid privilege level"); 101718334Speter return -1; 101818334Speter } 101918334Speter h->request.u.authen_start.priv_lvl = priv; 102018334Speter return 0; 102118334Speter} 102218334Speter 102318334Speterint 102418334Spetertac_set_user(struct tac_handle *h, const char *user) 102518334Speter{ 102618334Speter return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0); 102718334Speter} 102818334Speter 102918334Speterconst char * 103018334Spetertac_strerror(struct tac_handle *h) 103118334Speter{ 103218334Speter return h->errmsg; 103318334Speter} 103418334Speter 103518334Speterstatic void * 103618334Speterxmalloc(struct tac_handle *h, size_t size) 103718334Speter{ 103818334Speter void *r; 103918334Speter 104018334Speter if ((r = malloc(size)) == NULL) 104118334Speter generr(h, "Out of memory"); 104218334Speter return r; 104318334Speter} 104418334Speter 104518334Speterstatic char * 104618334Speterxstrdup(struct tac_handle *h, const char *s) 104718334Speter{ 104818334Speter char *r; 104918334Speter 105018334Speter if ((r = strdup(s)) == NULL) 105118334Speter generr(h, "Out of memory"); 105218334Speter return r; 105318334Speter} 105418334Speter