141120Sjdp/*- 2103976Spst * Copyright (c) 1998, 2001, 2002, Juniper Networks, Inc. 341120Sjdp * All rights reserved. 441120Sjdp * 541120Sjdp * Redistribution and use in source and binary forms, with or without 641120Sjdp * modification, are permitted provided that the following conditions 741120Sjdp * are met: 841120Sjdp * 1. Redistributions of source code must retain the above copyright 941120Sjdp * notice, this list of conditions and the following disclaimer. 1041120Sjdp * 2. Redistributions in binary form must reproduce the above copyright 1141120Sjdp * notice, this list of conditions and the following disclaimer in the 1241120Sjdp * documentation and/or other materials provided with the distribution. 1341120Sjdp * 1441120Sjdp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1541120Sjdp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1641120Sjdp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1741120Sjdp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1841120Sjdp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1941120Sjdp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2041120Sjdp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2141120Sjdp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2241120Sjdp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2341120Sjdp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2441120Sjdp * SUCH DAMAGE. 2541120Sjdp */ 2641120Sjdp 2784222Sdillon#include <sys/cdefs.h> 2884222Sdillon__FBSDID("$FreeBSD$"); 2984222Sdillon 3041120Sjdp#include <sys/types.h> 3141120Sjdp#include <sys/socket.h> 3241120Sjdp#include <sys/time.h> 3341120Sjdp#include <netinet/in.h> 3441120Sjdp#include <arpa/inet.h> 3541120Sjdp 3641120Sjdp#include <assert.h> 3741120Sjdp#include <errno.h> 3841120Sjdp#include <fcntl.h> 3941120Sjdp#include <md5.h> 4041120Sjdp#include <netdb.h> 4141120Sjdp#include <stdarg.h> 4241120Sjdp#include <stddef.h> 4341120Sjdp#include <stdio.h> 4441120Sjdp#include <stdlib.h> 4541120Sjdp#include <string.h> 4641120Sjdp#include <unistd.h> 4741120Sjdp 4841120Sjdp#include "taclib_private.h" 4941120Sjdp 5041120Sjdpstatic int add_str_8(struct tac_handle *, u_int8_t *, 5141120Sjdp struct clnt_str *); 5241120Sjdpstatic int add_str_16(struct tac_handle *, u_int16_t *, 5341120Sjdp struct clnt_str *); 54103976Spststatic int protocol_version(int, int, int); 5541120Sjdpstatic void close_connection(struct tac_handle *); 5641120Sjdpstatic int conn_server(struct tac_handle *); 5741120Sjdpstatic void crypt_msg(struct tac_handle *, struct tac_msg *); 5841120Sjdpstatic void *dup_str(struct tac_handle *, const struct srvr_str *, 5941120Sjdp size_t *); 6041120Sjdpstatic int establish_connection(struct tac_handle *); 6141120Sjdpstatic void free_str(struct clnt_str *); 6241120Sjdpstatic void generr(struct tac_handle *, const char *, ...) 6341120Sjdp __printflike(2, 3); 6441120Sjdpstatic void gen_session_id(struct tac_msg *); 6541120Sjdpstatic int get_srvr_end(struct tac_handle *); 66103976Spststatic int get_srvr_str(struct tac_handle *, const char *, 67103976Spst struct srvr_str *, size_t); 6841120Sjdpstatic void init_clnt_str(struct clnt_str *); 6941120Sjdpstatic void init_srvr_str(struct srvr_str *); 7041120Sjdpstatic int read_timed(struct tac_handle *, void *, size_t, 7141120Sjdp const struct timeval *); 7241120Sjdpstatic int recv_msg(struct tac_handle *); 7341120Sjdpstatic int save_str(struct tac_handle *, struct clnt_str *, 7441120Sjdp const void *, size_t); 7541120Sjdpstatic int send_msg(struct tac_handle *); 7641120Sjdpstatic int split(char *, char *[], int, char *, size_t); 7741120Sjdpstatic void *xmalloc(struct tac_handle *, size_t); 7841120Sjdpstatic char *xstrdup(struct tac_handle *, const char *); 79103976Spststatic void clear_srvr_avs(struct tac_handle *); 80103976Spststatic void create_msg(struct tac_handle *, int, int, int); 8141120Sjdp 8241120Sjdp/* 8341120Sjdp * Append some optional data to the current request, and store its 8441120Sjdp * length into the 8-bit field referenced by "fld". Returns 0 on 8541120Sjdp * success, or -1 on failure. 8641120Sjdp * 8741120Sjdp * This function also frees the "cs" string data and initializes it 8841120Sjdp * for the next time. 8941120Sjdp */ 9041120Sjdpstatic int 9141120Sjdpadd_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs) 9241120Sjdp{ 9341120Sjdp u_int16_t len; 9441120Sjdp 9541120Sjdp if (add_str_16(h, &len, cs) == -1) 9641120Sjdp return -1; 9741120Sjdp len = ntohs(len); 9841120Sjdp if (len > 0xff) { 9941120Sjdp generr(h, "Field too long"); 10041120Sjdp return -1; 10141120Sjdp } 10241120Sjdp *fld = len; 10341120Sjdp return 0; 10441120Sjdp} 10541120Sjdp 10641120Sjdp/* 10741120Sjdp * Append some optional data to the current request, and store its 10841120Sjdp * length into the 16-bit field (network byte order) referenced by 10941120Sjdp * "fld". Returns 0 on success, or -1 on failure. 11041120Sjdp * 11141120Sjdp * This function also frees the "cs" string data and initializes it 11241120Sjdp * for the next time. 11341120Sjdp */ 11441120Sjdpstatic int 11541120Sjdpadd_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs) 11641120Sjdp{ 11741120Sjdp size_t len; 11841120Sjdp 11941120Sjdp len = cs->len; 12041120Sjdp if (cs->data == NULL) 12141120Sjdp len = 0; 12241120Sjdp if (len != 0) { 12341120Sjdp int offset; 12441120Sjdp 12541120Sjdp if (len > 0xffff) { 12641120Sjdp generr(h, "Field too long"); 12741120Sjdp return -1; 12841120Sjdp } 12941120Sjdp offset = ntohl(h->request.length); 13041120Sjdp if (offset + len > BODYSIZE) { 13141120Sjdp generr(h, "Message too long"); 13241120Sjdp return -1; 13341120Sjdp } 13441120Sjdp memcpy(h->request.u.body + offset, cs->data, len); 13541120Sjdp h->request.length = htonl(offset + len); 13641120Sjdp } 13741120Sjdp *fld = htons(len); 13841120Sjdp free_str(cs); 13941120Sjdp return 0; 14041120Sjdp} 14141120Sjdp 14241120Sjdpstatic int 143103976Spstprotocol_version(int msg_type, int var, int type) 14441120Sjdp{ 145103976Spst int minor; 14641120Sjdp 147103976Spst switch (msg_type) { 148103976Spst case TAC_AUTHEN: 149103976Spst /* 'var' represents the 'action' */ 150103976Spst switch (var) { 151103976Spst case TAC_AUTHEN_LOGIN: 152103976Spst switch (type) { 15341120Sjdp 154103976Spst case TAC_AUTHEN_TYPE_PAP: 155103976Spst case TAC_AUTHEN_TYPE_CHAP: 156103976Spst case TAC_AUTHEN_TYPE_MSCHAP: 157103976Spst case TAC_AUTHEN_TYPE_ARAP: 158103976Spst minor = 1; 159103976Spst break; 16041120Sjdp 161103976Spst default: 162103976Spst minor = 0; 16341120Sjdp break; 164103976Spst } 165103976Spst break; 16641120Sjdp 167103976Spst case TAC_AUTHEN_SENDAUTH: 168103976Spst minor = 1; 169103976Spst break; 170103976Spst 17141120Sjdp default: 172103976Spst minor = 0; 17341120Sjdp break; 174103976Spst }; 175103976Spst break; 17641120Sjdp 177103976Spst case TAC_AUTHOR: 178103976Spst /* 'var' represents the 'method' */ 179103976Spst switch (var) { 180103976Spst /* 181103976Spst * When new authentication methods are added, include 'method' 182103976Spst * in determining the value of 'minor'. At this point, all 183103976Spst * methods defined in this implementation (see "Authorization 184103976Spst * authentication methods" in taclib.h) are minor version 0 185103976Spst * Not all types, however, indicate minor version 0. 186103976Spst */ 187103976Spst case TAC_AUTHEN_METH_NOT_SET: 188103976Spst case TAC_AUTHEN_METH_NONE: 189103976Spst case TAC_AUTHEN_METH_KRB5: 190103976Spst case TAC_AUTHEN_METH_LINE: 191103976Spst case TAC_AUTHEN_METH_ENABLE: 192103976Spst case TAC_AUTHEN_METH_LOCAL: 193103976Spst case TAC_AUTHEN_METH_TACACSPLUS: 194103976Spst case TAC_AUTHEN_METH_RCMD: 195103976Spst switch (type) { 196103976Spst case TAC_AUTHEN_TYPE_PAP: 197103976Spst case TAC_AUTHEN_TYPE_CHAP: 198103976Spst case TAC_AUTHEN_TYPE_MSCHAP: 199103976Spst case TAC_AUTHEN_TYPE_ARAP: 200103976Spst minor = 1; 201103976Spst break; 202103976Spst 203103976Spst default: 204103976Spst minor = 0; 205103976Spst break; 206103976Spst } 207103976Spst break; 208103976Spst default: 209103976Spst minor = 0; 21041120Sjdp break; 211103976Spst } 212103976Spst break; 21341120Sjdp 214200399Ssyrinx case TAC_ACCT: 215200399Ssyrinx 21641120Sjdp default: 217103976Spst minor = 0; 218103976Spst break; 219103976Spst } 22041120Sjdp 221103976Spst return TAC_VER_MAJOR << 4 | minor; 22241120Sjdp} 22341120Sjdp 224103976Spst 22541120Sjdpstatic void 22641120Sjdpclose_connection(struct tac_handle *h) 22741120Sjdp{ 22841120Sjdp if (h->fd != -1) { 22941120Sjdp close(h->fd); 23041120Sjdp h->fd = -1; 23141120Sjdp } 23241120Sjdp} 23341120Sjdp 23441120Sjdpstatic int 23541120Sjdpconn_server(struct tac_handle *h) 23641120Sjdp{ 23741120Sjdp const struct tac_server *srvp = &h->servers[h->cur_server]; 23841120Sjdp int flags; 23941120Sjdp 24041120Sjdp if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 24141120Sjdp generr(h, "Cannot create socket: %s", strerror(errno)); 24241120Sjdp return -1; 24341120Sjdp } 24441120Sjdp if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 || 24541120Sjdp fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) { 24641120Sjdp generr(h, "Cannot set non-blocking mode on socket: %s", 24741120Sjdp strerror(errno)); 24841120Sjdp close(h->fd); 24941120Sjdp h->fd = -1; 25041120Sjdp return -1; 25141120Sjdp } 25241120Sjdp if (connect(h->fd, (struct sockaddr *)&srvp->addr, 25341120Sjdp sizeof srvp->addr) == 0) 25441120Sjdp return 0; 25541120Sjdp 25641120Sjdp if (errno == EINPROGRESS) { 25741120Sjdp fd_set wfds; 25841120Sjdp struct timeval tv; 25941120Sjdp int nfds; 26041120Sjdp struct sockaddr peer; 261141918Sstefanf socklen_t errlen, peerlen; 26241120Sjdp int err; 26341120Sjdp 26441120Sjdp /* Wait for the connection to complete. */ 26541120Sjdp FD_ZERO(&wfds); 26641120Sjdp FD_SET(h->fd, &wfds); 26741120Sjdp tv.tv_sec = srvp->timeout; 26841120Sjdp tv.tv_usec = 0; 26941120Sjdp nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv); 27041120Sjdp if (nfds == -1) { 27141120Sjdp generr(h, "select: %s", strerror(errno)); 27241120Sjdp close(h->fd); 27341120Sjdp h->fd = -1; 27441120Sjdp return -1; 27541120Sjdp } 27641120Sjdp if (nfds == 0) { 27741120Sjdp generr(h, "connect: timed out"); 27841120Sjdp close(h->fd); 27941120Sjdp h->fd = -1; 28041120Sjdp return -1; 28141120Sjdp } 28241120Sjdp 28341120Sjdp /* See whether we are connected now. */ 28441120Sjdp peerlen = sizeof peer; 28541120Sjdp if (getpeername(h->fd, &peer, &peerlen) == 0) 28641120Sjdp return 0; 28741120Sjdp 28841120Sjdp if (errno != ENOTCONN) { 28941120Sjdp generr(h, "getpeername: %s", strerror(errno)); 29041120Sjdp close(h->fd); 29141120Sjdp h->fd = -1; 29241120Sjdp return -1; 29341120Sjdp } 29441120Sjdp 29541120Sjdp /* Find out why the connect failed. */ 29641120Sjdp errlen = sizeof err; 29741120Sjdp getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen); 29841120Sjdp errno = err; 29941120Sjdp } 30041120Sjdp generr(h, "connect: %s", strerror(errno)); 30141120Sjdp close(h->fd); 30241120Sjdp h->fd = -1; 30341120Sjdp return -1; 30441120Sjdp} 30541120Sjdp 30641120Sjdp/* 30741120Sjdp * Encrypt or decrypt a message. The operations are symmetrical. 30841120Sjdp */ 30941120Sjdpstatic void 31041120Sjdpcrypt_msg(struct tac_handle *h, struct tac_msg *msg) 31141120Sjdp{ 31241120Sjdp const char *secret; 31341120Sjdp MD5_CTX base_ctx; 31441120Sjdp MD5_CTX ctx; 31541120Sjdp unsigned char md5[16]; 31641120Sjdp int chunk; 31741120Sjdp int msg_len; 31841120Sjdp 31941120Sjdp secret = h->servers[h->cur_server].secret; 32041120Sjdp if (secret[0] == '\0') 32141120Sjdp msg->flags |= TAC_UNENCRYPTED; 32241120Sjdp if (msg->flags & TAC_UNENCRYPTED) 32341120Sjdp return; 32441120Sjdp 32541120Sjdp msg_len = ntohl(msg->length); 32641120Sjdp 32741120Sjdp MD5Init(&base_ctx); 32841120Sjdp MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id); 32941120Sjdp MD5Update(&base_ctx, secret, strlen(secret)); 33041120Sjdp MD5Update(&base_ctx, &msg->version, sizeof msg->version); 33141120Sjdp MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no); 33241120Sjdp 33341120Sjdp ctx = base_ctx; 33441120Sjdp for (chunk = 0; chunk < msg_len; chunk += sizeof md5) { 33541120Sjdp int chunk_len; 33641120Sjdp int i; 33741120Sjdp 33841120Sjdp MD5Final(md5, &ctx); 33941120Sjdp 34041120Sjdp if ((chunk_len = msg_len - chunk) > sizeof md5) 34141120Sjdp chunk_len = sizeof md5; 34241120Sjdp for (i = 0; i < chunk_len; i++) 34341120Sjdp msg->u.body[chunk + i] ^= md5[i]; 34441120Sjdp 34541120Sjdp ctx = base_ctx; 34641120Sjdp MD5Update(&ctx, md5, sizeof md5); 34741120Sjdp } 34841120Sjdp} 34941120Sjdp 35041120Sjdp/* 35141120Sjdp * Return a dynamically allocated copy of the given server string. 35241120Sjdp * The copy is null-terminated. If "len" is non-NULL, the length of 35341120Sjdp * the string (excluding the terminating null byte) is stored via it. 35441120Sjdp * Returns NULL on failure. Empty strings are still allocated even 35541120Sjdp * though they have no content. 35641120Sjdp */ 35741120Sjdpstatic void * 35841120Sjdpdup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len) 35941120Sjdp{ 36041120Sjdp unsigned char *p; 36141120Sjdp 36241120Sjdp if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL) 36341120Sjdp return NULL; 36441120Sjdp if (ss->data != NULL && ss->len != 0) 36541120Sjdp memcpy(p, ss->data, ss->len); 36641120Sjdp p[ss->len] = '\0'; 36741120Sjdp if (len != NULL) 36841120Sjdp *len = ss->len; 36941120Sjdp return p; 37041120Sjdp} 37141120Sjdp 37241120Sjdpstatic int 37341120Sjdpestablish_connection(struct tac_handle *h) 37441120Sjdp{ 37541120Sjdp int i; 37641120Sjdp 37741120Sjdp if (h->fd >= 0) /* Already connected. */ 37841120Sjdp return 0; 37941120Sjdp if (h->num_servers == 0) { 38041120Sjdp generr(h, "No TACACS+ servers specified"); 38141120Sjdp return -1; 38241120Sjdp } 38341120Sjdp /* 38441120Sjdp * Try the servers round-robin. We begin with the one that 38541120Sjdp * worked for us the last time. That way, once we find a good 38641120Sjdp * server, we won't waste any more time trying the bad ones. 38741120Sjdp */ 38841120Sjdp for (i = 0; i < h->num_servers; i++) { 38941120Sjdp if (conn_server(h) == 0) { 39041120Sjdp h->single_connect = (h->servers[h->cur_server].flags & 39141120Sjdp TAC_SRVR_SINGLE_CONNECT) != 0; 39241120Sjdp return 0; 39341120Sjdp } 39441120Sjdp if (++h->cur_server >= h->num_servers) /* Wrap around */ 39541120Sjdp h->cur_server = 0; 39641120Sjdp } 39741120Sjdp /* Just return whatever error was last reported by conn_server(). */ 39841120Sjdp return -1; 39941120Sjdp} 40041120Sjdp 40141120Sjdp/* 40241120Sjdp * Free a client string, obliterating its contents first for security. 40341120Sjdp */ 40441120Sjdpstatic void 40541120Sjdpfree_str(struct clnt_str *cs) 40641120Sjdp{ 40741120Sjdp if (cs->data != NULL) { 40841120Sjdp memset(cs->data, 0, cs->len); 40941120Sjdp free(cs->data); 41041120Sjdp cs->data = NULL; 41141120Sjdp cs->len = 0; 41241120Sjdp } 41341120Sjdp} 41441120Sjdp 41541120Sjdpstatic void 41641120Sjdpgenerr(struct tac_handle *h, const char *format, ...) 41741120Sjdp{ 41841120Sjdp va_list ap; 41941120Sjdp 42041120Sjdp va_start(ap, format); 42141120Sjdp vsnprintf(h->errmsg, ERRSIZE, format, ap); 42241120Sjdp va_end(ap); 42341120Sjdp} 42441120Sjdp 42541120Sjdpstatic void 42641120Sjdpgen_session_id(struct tac_msg *msg) 42741120Sjdp{ 42841120Sjdp int r; 42941120Sjdp 43041120Sjdp r = random(); 43141120Sjdp msg->session_id[0] = r >> 8; 43241120Sjdp msg->session_id[1] = r; 43341120Sjdp r = random(); 43441120Sjdp msg->session_id[2] = r >> 8; 43541120Sjdp msg->session_id[3] = r; 43641120Sjdp} 43741120Sjdp 43841120Sjdp/* 43941120Sjdp * Verify that we are exactly at the end of the response message. 44041120Sjdp * Returns 0 on success, -1 on failure. 44141120Sjdp */ 44241120Sjdpstatic int 44341120Sjdpget_srvr_end(struct tac_handle *h) 44441120Sjdp{ 445103976Spst int len; 446103976Spst 447103976Spst len = ntohl(h->response.length); 448103976Spst 449103976Spst if (h->srvr_pos != len) { 450103976Spst generr(h, "Invalid length field in response " 451103976Spst "from server: end expected at %u, response length %u", 452103976Spst h->srvr_pos, len); 45341120Sjdp return -1; 45441120Sjdp } 45541120Sjdp return 0; 45641120Sjdp} 45741120Sjdp 45841120Sjdpstatic int 459103976Spstget_srvr_str(struct tac_handle *h, const char *field, 460103976Spst struct srvr_str *ss, size_t len) 46141120Sjdp{ 46241120Sjdp if (h->srvr_pos + len > ntohl(h->response.length)) { 463103976Spst generr(h, "Invalid length field in %s response from server " 464103976Spst "(%lu > %lu)", field, (u_long)(h->srvr_pos + len), 465103976Spst (u_long)ntohl(h->response.length)); 46641120Sjdp return -1; 46741120Sjdp } 46841120Sjdp ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL; 46941120Sjdp ss->len = len; 47041120Sjdp h->srvr_pos += len; 47141120Sjdp return 0; 47241120Sjdp} 47341120Sjdp 47441120Sjdpstatic void 47541120Sjdpinit_clnt_str(struct clnt_str *cs) 47641120Sjdp{ 47741120Sjdp cs->data = NULL; 47841120Sjdp cs->len = 0; 47941120Sjdp} 48041120Sjdp 48141120Sjdpstatic void 48241120Sjdpinit_srvr_str(struct srvr_str *ss) 48341120Sjdp{ 48441120Sjdp ss->data = NULL; 48541120Sjdp ss->len = 0; 48641120Sjdp} 48741120Sjdp 48841120Sjdpstatic int 48941120Sjdpread_timed(struct tac_handle *h, void *buf, size_t len, 49041120Sjdp const struct timeval *deadline) 49141120Sjdp{ 49241120Sjdp char *ptr; 49341120Sjdp 49441120Sjdp ptr = (char *)buf; 49541120Sjdp while (len > 0) { 49641120Sjdp int n; 49741120Sjdp 49841120Sjdp n = read(h->fd, ptr, len); 49941120Sjdp if (n == -1) { 50041120Sjdp struct timeval tv; 50141120Sjdp int nfds; 50241120Sjdp 50341120Sjdp if (errno != EAGAIN) { 50441120Sjdp generr(h, "Network read error: %s", 50541120Sjdp strerror(errno)); 50641120Sjdp return -1; 50741120Sjdp } 50841120Sjdp 50941120Sjdp /* Wait until we can read more data. */ 51041120Sjdp gettimeofday(&tv, NULL); 51141120Sjdp timersub(deadline, &tv, &tv); 51241120Sjdp if (tv.tv_sec >= 0) { 51341120Sjdp fd_set rfds; 51441120Sjdp 51541120Sjdp FD_ZERO(&rfds); 51641120Sjdp FD_SET(h->fd, &rfds); 51741120Sjdp nfds = 51841120Sjdp select(h->fd + 1, &rfds, NULL, NULL, &tv); 51941120Sjdp if (nfds == -1) { 52041120Sjdp generr(h, "select: %s", 52141120Sjdp strerror(errno)); 52241120Sjdp return -1; 52341120Sjdp } 52441120Sjdp } else 52541120Sjdp nfds = 0; 52641120Sjdp if (nfds == 0) { 52741120Sjdp generr(h, "Network read timed out"); 52841120Sjdp return -1; 52941120Sjdp } 53041120Sjdp } else if (n == 0) { 53141120Sjdp generr(h, "unexpected EOF from server"); 53241120Sjdp return -1; 53341120Sjdp } else { 53441120Sjdp ptr += n; 53541120Sjdp len -= n; 53641120Sjdp } 53741120Sjdp } 53841120Sjdp return 0; 53941120Sjdp} 54041120Sjdp 54141120Sjdp/* 54241120Sjdp * Receive a response from the server and decrypt it. Returns 0 on 54341120Sjdp * success, or -1 on failure. 54441120Sjdp */ 54541120Sjdpstatic int 54641120Sjdprecv_msg(struct tac_handle *h) 54741120Sjdp{ 54841120Sjdp struct timeval deadline; 54941120Sjdp struct tac_msg *msg; 550103976Spst u_int32_t len; 55141120Sjdp 55241120Sjdp msg = &h->response; 55341120Sjdp gettimeofday(&deadline, NULL); 55441120Sjdp deadline.tv_sec += h->servers[h->cur_server].timeout; 55541120Sjdp 55641120Sjdp /* Read the message header and make sure it is reasonable. */ 55741120Sjdp if (read_timed(h, msg, HDRSIZE, &deadline) == -1) 55841120Sjdp return -1; 55941120Sjdp if (memcmp(msg->session_id, h->request.session_id, 56041120Sjdp sizeof msg->session_id) != 0) { 56141120Sjdp generr(h, "Invalid session ID in received message"); 56241120Sjdp return -1; 56341120Sjdp } 56441120Sjdp if (msg->type != h->request.type) { 565103976Spst generr(h, "Invalid type in received message" 566103976Spst " (got %u, expected %u)", 567103976Spst msg->type, h->request.type); 56841120Sjdp return -1; 56941120Sjdp } 57041120Sjdp len = ntohl(msg->length); 57141120Sjdp if (len > BODYSIZE) { 572103976Spst generr(h, "Received message too large (%u > %u)", 573103976Spst len, BODYSIZE); 57441120Sjdp return -1; 57541120Sjdp } 57641120Sjdp if (msg->seq_no != ++h->last_seq_no) { 577103976Spst generr(h, "Invalid sequence number in received message" 578103976Spst " (got %u, expected %u)", 579103976Spst msg->seq_no, h->last_seq_no); 58041120Sjdp return -1; 58141120Sjdp } 58241120Sjdp 58341120Sjdp /* Read the message body. */ 58441120Sjdp if (read_timed(h, msg->u.body, len, &deadline) == -1) 58541120Sjdp return -1; 58641120Sjdp 58741120Sjdp /* Decrypt it. */ 58841120Sjdp crypt_msg(h, msg); 58941120Sjdp 59041120Sjdp /* 59141120Sjdp * Turn off single-connection mode if the server isn't amenable 59241120Sjdp * to it. 59341120Sjdp */ 59441120Sjdp if (!(msg->flags & TAC_SINGLE_CONNECT)) 59541120Sjdp h->single_connect = 0; 59641120Sjdp return 0; 59741120Sjdp} 59841120Sjdp 59941120Sjdpstatic int 60041120Sjdpsave_str(struct tac_handle *h, struct clnt_str *cs, const void *data, 60141120Sjdp size_t len) 60241120Sjdp{ 60341120Sjdp free_str(cs); 60441120Sjdp if (data != NULL && len != 0) { 60541120Sjdp if ((cs->data = xmalloc(h, len)) == NULL) 60641120Sjdp return -1; 60741120Sjdp cs->len = len; 60841120Sjdp memcpy(cs->data, data, len); 60941120Sjdp } 61041120Sjdp return 0; 61141120Sjdp} 61241120Sjdp 61341120Sjdp/* 61441120Sjdp * Send the current request, after encrypting it. Returns 0 on success, 61541120Sjdp * or -1 on failure. 61641120Sjdp */ 61741120Sjdpstatic int 61841120Sjdpsend_msg(struct tac_handle *h) 61941120Sjdp{ 62041120Sjdp struct timeval deadline; 62141120Sjdp struct tac_msg *msg; 62241120Sjdp char *ptr; 62341120Sjdp int len; 62441120Sjdp 62541120Sjdp if (h->last_seq_no & 1) { 62641120Sjdp generr(h, "Attempt to send message out of sequence"); 62741120Sjdp return -1; 62841120Sjdp } 62941120Sjdp 630103976Spst if (establish_connection(h) == -1) 631103976Spst return -1; 632103976Spst 63341120Sjdp msg = &h->request; 63441120Sjdp msg->seq_no = ++h->last_seq_no; 63541120Sjdp if (msg->seq_no == 1) 63641120Sjdp gen_session_id(msg); 63741120Sjdp crypt_msg(h, msg); 63841120Sjdp 63941120Sjdp if (h->single_connect) 64041120Sjdp msg->flags |= TAC_SINGLE_CONNECT; 64141120Sjdp else 64241120Sjdp msg->flags &= ~TAC_SINGLE_CONNECT; 64341120Sjdp gettimeofday(&deadline, NULL); 64441120Sjdp deadline.tv_sec += h->servers[h->cur_server].timeout; 64541120Sjdp len = HDRSIZE + ntohl(msg->length); 64641120Sjdp ptr = (char *)msg; 64741120Sjdp while (len > 0) { 64841120Sjdp int n; 64941120Sjdp 65041120Sjdp n = write(h->fd, ptr, len); 65141120Sjdp if (n == -1) { 65241120Sjdp struct timeval tv; 65341120Sjdp int nfds; 65441120Sjdp 65541120Sjdp if (errno != EAGAIN) { 65641120Sjdp generr(h, "Network write error: %s", 65741120Sjdp strerror(errno)); 65841120Sjdp return -1; 65941120Sjdp } 66041120Sjdp 66141120Sjdp /* Wait until we can write more data. */ 66241120Sjdp gettimeofday(&tv, NULL); 66341120Sjdp timersub(&deadline, &tv, &tv); 66441120Sjdp if (tv.tv_sec >= 0) { 66541120Sjdp fd_set wfds; 66641120Sjdp 66741120Sjdp FD_ZERO(&wfds); 66841120Sjdp FD_SET(h->fd, &wfds); 66941120Sjdp nfds = 67041120Sjdp select(h->fd + 1, NULL, &wfds, NULL, &tv); 67141120Sjdp if (nfds == -1) { 67241120Sjdp generr(h, "select: %s", 67341120Sjdp strerror(errno)); 67441120Sjdp return -1; 67541120Sjdp } 67641120Sjdp } else 67741120Sjdp nfds = 0; 67841120Sjdp if (nfds == 0) { 67941120Sjdp generr(h, "Network write timed out"); 68041120Sjdp return -1; 68141120Sjdp } 68241120Sjdp } else { 68341120Sjdp ptr += n; 68441120Sjdp len -= n; 68541120Sjdp } 68641120Sjdp } 68741120Sjdp return 0; 68841120Sjdp} 68941120Sjdp 69041120Sjdp/* 69141120Sjdp * Destructively split a string into fields separated by white space. 69241120Sjdp * `#' at the beginning of a field begins a comment that extends to the 69341120Sjdp * end of the string. Fields may be quoted with `"'. Inside quoted 69441120Sjdp * strings, the backslash escapes `\"' and `\\' are honored. 69541120Sjdp * 69641120Sjdp * Pointers to up to the first maxfields fields are stored in the fields 69741120Sjdp * array. Missing fields get NULL pointers. 69841120Sjdp * 69941120Sjdp * The return value is the actual number of fields parsed, and is always 70041120Sjdp * <= maxfields. 70141120Sjdp * 70241120Sjdp * On a syntax error, places a message in the msg string, and returns -1. 70341120Sjdp */ 70441120Sjdpstatic int 70541120Sjdpsplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen) 70641120Sjdp{ 70741120Sjdp char *p; 70841120Sjdp int i; 70941120Sjdp static const char ws[] = " \t"; 71041120Sjdp 71141120Sjdp for (i = 0; i < maxfields; i++) 71241120Sjdp fields[i] = NULL; 71341120Sjdp p = str; 71441120Sjdp i = 0; 71541120Sjdp while (*p != '\0') { 71641120Sjdp p += strspn(p, ws); 71741120Sjdp if (*p == '#' || *p == '\0') 71841120Sjdp break; 71941120Sjdp if (i >= maxfields) { 72041120Sjdp snprintf(msg, msglen, "line has too many fields"); 72141120Sjdp return -1; 72241120Sjdp } 72341120Sjdp if (*p == '"') { 72441120Sjdp char *dst; 72541120Sjdp 72641120Sjdp dst = ++p; 72741120Sjdp fields[i] = dst; 72841120Sjdp while (*p != '"') { 72941120Sjdp if (*p == '\\') { 73041120Sjdp p++; 73141120Sjdp if (*p != '"' && *p != '\\' && 73241120Sjdp *p != '\0') { 73341120Sjdp snprintf(msg, msglen, 73441120Sjdp "invalid `\\' escape"); 73541120Sjdp return -1; 73641120Sjdp } 73741120Sjdp } 73841120Sjdp if (*p == '\0') { 73941120Sjdp snprintf(msg, msglen, 74041120Sjdp "unterminated quoted string"); 74141120Sjdp return -1; 74241120Sjdp } 74341120Sjdp *dst++ = *p++; 74441120Sjdp } 74541120Sjdp *dst = '\0'; 74641120Sjdp p++; 74741120Sjdp if (*p != '\0' && strspn(p, ws) == 0) { 74841120Sjdp snprintf(msg, msglen, "quoted string not" 74941120Sjdp " followed by white space"); 75041120Sjdp return -1; 75141120Sjdp } 75241120Sjdp } else { 75341120Sjdp fields[i] = p; 75441120Sjdp p += strcspn(p, ws); 75541120Sjdp if (*p != '\0') 75641120Sjdp *p++ = '\0'; 75741120Sjdp } 75841120Sjdp i++; 75941120Sjdp } 76041120Sjdp return i; 76141120Sjdp} 76241120Sjdp 76341120Sjdpint 76441120Sjdptac_add_server(struct tac_handle *h, const char *host, int port, 76541120Sjdp const char *secret, int timeout, int flags) 76641120Sjdp{ 76741120Sjdp struct tac_server *srvp; 76841120Sjdp 76941120Sjdp if (h->num_servers >= MAXSERVERS) { 77056141Sjdp generr(h, "Too many TACACS+ servers specified"); 77141120Sjdp return -1; 77241120Sjdp } 77341120Sjdp srvp = &h->servers[h->num_servers]; 77441120Sjdp 77541120Sjdp memset(&srvp->addr, 0, sizeof srvp->addr); 77641120Sjdp srvp->addr.sin_len = sizeof srvp->addr; 77741120Sjdp srvp->addr.sin_family = AF_INET; 77841120Sjdp if (!inet_aton(host, &srvp->addr.sin_addr)) { 77941120Sjdp struct hostent *hent; 78041120Sjdp 78141120Sjdp if ((hent = gethostbyname(host)) == NULL) { 78241120Sjdp generr(h, "%s: host not found", host); 78341120Sjdp return -1; 78441120Sjdp } 78541120Sjdp memcpy(&srvp->addr.sin_addr, hent->h_addr, 78641120Sjdp sizeof srvp->addr.sin_addr); 78741120Sjdp } 78841120Sjdp srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT); 78941120Sjdp if ((srvp->secret = xstrdup(h, secret)) == NULL) 79041120Sjdp return -1; 79141120Sjdp srvp->timeout = timeout; 79241120Sjdp srvp->flags = flags; 79341120Sjdp h->num_servers++; 79441120Sjdp return 0; 79541120Sjdp} 79641120Sjdp 79741120Sjdpvoid 79841120Sjdptac_close(struct tac_handle *h) 79941120Sjdp{ 800103976Spst int i, srv; 80141120Sjdp 80241120Sjdp if (h->fd != -1) 80341120Sjdp close(h->fd); 80441120Sjdp for (srv = 0; srv < h->num_servers; srv++) { 80541120Sjdp memset(h->servers[srv].secret, 0, 80641120Sjdp strlen(h->servers[srv].secret)); 80741120Sjdp free(h->servers[srv].secret); 80841120Sjdp } 80941120Sjdp free_str(&h->user); 81041120Sjdp free_str(&h->port); 81141120Sjdp free_str(&h->rem_addr); 81241120Sjdp free_str(&h->data); 81341120Sjdp free_str(&h->user_msg); 814103976Spst for (i=0; i<MAXAVPAIRS; i++) 815103976Spst free_str(&(h->avs[i])); 816103976Spst 817103976Spst /* Clear everything else before freeing memory */ 818103976Spst memset(h, 0, sizeof(struct tac_handle)); 81941120Sjdp free(h); 82041120Sjdp} 82141120Sjdp 82241120Sjdpint 82341120Sjdptac_config(struct tac_handle *h, const char *path) 82441120Sjdp{ 82541120Sjdp FILE *fp; 82641120Sjdp char buf[MAXCONFLINE]; 82741120Sjdp int linenum; 82841120Sjdp int retval; 82941120Sjdp 83041120Sjdp if (path == NULL) 83141120Sjdp path = PATH_TACPLUS_CONF; 83241120Sjdp if ((fp = fopen(path, "r")) == NULL) { 83341120Sjdp generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); 83441120Sjdp return -1; 83541120Sjdp } 83641120Sjdp retval = 0; 83741120Sjdp linenum = 0; 83841120Sjdp while (fgets(buf, sizeof buf, fp) != NULL) { 83941120Sjdp int len; 84041120Sjdp char *fields[4]; 84141120Sjdp int nfields; 84241120Sjdp char msg[ERRSIZE]; 84365222Sache char *host, *res; 84441120Sjdp char *port_str; 84541120Sjdp char *secret; 84641120Sjdp char *timeout_str; 84741120Sjdp char *options_str; 84841120Sjdp char *end; 84941120Sjdp unsigned long timeout; 85041120Sjdp int port; 85141120Sjdp int options; 85241120Sjdp 85341120Sjdp linenum++; 85441120Sjdp len = strlen(buf); 85541120Sjdp /* We know len > 0, else fgets would have returned NULL. */ 85641120Sjdp if (buf[len - 1] != '\n') { 857103976Spst if (len >= sizeof buf - 1) 85841120Sjdp generr(h, "%s:%d: line too long", path, 85941120Sjdp linenum); 86041120Sjdp else 86141120Sjdp generr(h, "%s:%d: missing newline", path, 86241120Sjdp linenum); 86341120Sjdp retval = -1; 86441120Sjdp break; 86541120Sjdp } 86641120Sjdp buf[len - 1] = '\0'; 86741120Sjdp 86841120Sjdp /* Extract the fields from the line. */ 86941120Sjdp nfields = split(buf, fields, 4, msg, sizeof msg); 87041120Sjdp if (nfields == -1) { 87141120Sjdp generr(h, "%s:%d: %s", path, linenum, msg); 87241120Sjdp retval = -1; 87341120Sjdp break; 87441120Sjdp } 87541120Sjdp if (nfields == 0) 87641120Sjdp continue; 87741120Sjdp if (nfields < 2) { 87841120Sjdp generr(h, "%s:%d: missing shared secret", path, 87941120Sjdp linenum); 88041120Sjdp retval = -1; 88141120Sjdp break; 88241120Sjdp } 88341120Sjdp host = fields[0]; 88441120Sjdp secret = fields[1]; 88541120Sjdp timeout_str = fields[2]; 88641120Sjdp options_str = fields[3]; 88741120Sjdp 88841120Sjdp /* Parse and validate the fields. */ 88965222Sache res = host; 89065222Sache host = strsep(&res, ":"); 89165222Sache port_str = strsep(&res, ":"); 89241120Sjdp if (port_str != NULL) { 89341120Sjdp port = strtoul(port_str, &end, 10); 89441120Sjdp if (port_str[0] == '\0' || *end != '\0') { 89541120Sjdp generr(h, "%s:%d: invalid port", path, 89641120Sjdp linenum); 89741120Sjdp retval = -1; 89841120Sjdp break; 89941120Sjdp } 90041120Sjdp } else 90141120Sjdp port = 0; 90241120Sjdp if (timeout_str != NULL) { 90341120Sjdp timeout = strtoul(timeout_str, &end, 10); 90441120Sjdp if (timeout_str[0] == '\0' || *end != '\0') { 90541120Sjdp generr(h, "%s:%d: invalid timeout", path, 90641120Sjdp linenum); 90741120Sjdp retval = -1; 90841120Sjdp break; 90941120Sjdp } 91041120Sjdp } else 91141120Sjdp timeout = TIMEOUT; 91241120Sjdp options = 0; 91341120Sjdp if (options_str != NULL) { 91441120Sjdp if (strcmp(options_str, "single-connection") == 0) 91541120Sjdp options |= TAC_SRVR_SINGLE_CONNECT; 91641120Sjdp else { 91741120Sjdp generr(h, "%s:%d: invalid option \"%s\"", 91841120Sjdp path, linenum, options_str); 91941120Sjdp retval = -1; 92041120Sjdp break; 92141120Sjdp } 92241120Sjdp }; 92341120Sjdp 92441120Sjdp if (tac_add_server(h, host, port, secret, timeout, 92541120Sjdp options) == -1) { 92641120Sjdp char msg[ERRSIZE]; 92741120Sjdp 92841120Sjdp strcpy(msg, h->errmsg); 92941120Sjdp generr(h, "%s:%d: %s", path, linenum, msg); 93041120Sjdp retval = -1; 93141120Sjdp break; 93241120Sjdp } 93341120Sjdp } 93441120Sjdp /* Clear out the buffer to wipe a possible copy of a shared secret */ 93541120Sjdp memset(buf, 0, sizeof buf); 93641120Sjdp fclose(fp); 93741120Sjdp return retval; 93841120Sjdp} 93941120Sjdp 94041120Sjdpint 94141120Sjdptac_create_authen(struct tac_handle *h, int action, int type, int service) 94241120Sjdp{ 94341120Sjdp struct tac_authen_start *as; 94441120Sjdp 945103976Spst create_msg(h, TAC_AUTHEN, action, type); 94641120Sjdp 947103976Spst as = &h->request.u.authen_start; 94841120Sjdp as->action = action; 94941120Sjdp as->priv_lvl = TAC_PRIV_LVL_USER; 95041120Sjdp as->authen_type = type; 95141120Sjdp as->service = service; 95241120Sjdp 953103976Spst return 0; 954103976Spst} 955103976Spst 956103976Spstint 957103976Spsttac_create_author(struct tac_handle *h, int method, int type, int service) 958103976Spst{ 959103976Spst struct tac_author_request *areq; 960103976Spst 961103976Spst create_msg(h, TAC_AUTHOR, method, type); 962103976Spst 963103976Spst areq = &h->request.u.author_request; 964103976Spst areq->authen_meth = method; 965103976Spst areq->priv_lvl = TAC_PRIV_LVL_USER; 966103976Spst areq->authen_type = type; 967103976Spst areq->service = service; 968103976Spst 969103976Spst return 0; 970103976Spst} 971103976Spst 972200399Ssyrinxint 973200399Ssyrinxtac_create_acct(struct tac_handle *h, int acct, int action, int type, int service) 974200399Ssyrinx{ 975200399Ssyrinx struct tac_acct_start *as; 976200399Ssyrinx 977200399Ssyrinx create_msg(h, TAC_ACCT, action, type); 978200399Ssyrinx 979200399Ssyrinx as = &h->request.u.acct_start; 980200399Ssyrinx as->action = acct; 981200399Ssyrinx as->authen_action = action; 982200399Ssyrinx as->priv_lvl = TAC_PRIV_LVL_USER; 983200399Ssyrinx as->authen_type = type; 984200399Ssyrinx as->authen_service = service; 985200399Ssyrinx 986200399Ssyrinx return 0; 987200399Ssyrinx} 988200399Ssyrinx 989103976Spststatic void 990103976Spstcreate_msg(struct tac_handle *h, int msg_type, int var, int type) 991103976Spst{ 992103976Spst struct tac_msg *msg; 993103976Spst int i; 994103976Spst 995103976Spst h->last_seq_no = 0; 996103976Spst 997103976Spst msg = &h->request; 998103976Spst msg->type = msg_type; 999103976Spst msg->version = protocol_version(msg_type, var, type); 1000103976Spst msg->flags = 0; /* encrypted packet body */ 1001103976Spst 100241120Sjdp free_str(&h->user); 100341120Sjdp free_str(&h->port); 100441120Sjdp free_str(&h->rem_addr); 100541120Sjdp free_str(&h->data); 100641120Sjdp free_str(&h->user_msg); 100741120Sjdp 1008103976Spst for (i=0; i<MAXAVPAIRS; i++) 1009103976Spst free_str(&(h->avs[i])); 101041120Sjdp} 101141120Sjdp 101241120Sjdpvoid * 101341120Sjdptac_get_data(struct tac_handle *h, size_t *len) 101441120Sjdp{ 101541120Sjdp return dup_str(h, &h->srvr_data, len); 101641120Sjdp} 101741120Sjdp 101841120Sjdpchar * 101941120Sjdptac_get_msg(struct tac_handle *h) 102041120Sjdp{ 1021103976Spst return dup_str(h, &h->srvr_msg, NULL); 102241120Sjdp} 102341120Sjdp 102441120Sjdp/* 102541120Sjdp * Create and initialize a tac_handle structure, and return it to the 102641120Sjdp * caller. Can fail only if the necessary memory cannot be allocated. 102741120Sjdp * In that case, it returns NULL. 102841120Sjdp */ 102941120Sjdpstruct tac_handle * 103041120Sjdptac_open(void) 103141120Sjdp{ 1032103976Spst int i; 103341120Sjdp struct tac_handle *h; 103441120Sjdp 103541120Sjdp h = (struct tac_handle *)malloc(sizeof(struct tac_handle)); 103641120Sjdp if (h != NULL) { 103741120Sjdp h->fd = -1; 103841120Sjdp h->num_servers = 0; 103941120Sjdp h->cur_server = 0; 104041120Sjdp h->errmsg[0] = '\0'; 104141120Sjdp init_clnt_str(&h->user); 104241120Sjdp init_clnt_str(&h->port); 104341120Sjdp init_clnt_str(&h->rem_addr); 104441120Sjdp init_clnt_str(&h->data); 104541120Sjdp init_clnt_str(&h->user_msg); 1046103976Spst for (i=0; i<MAXAVPAIRS; i++) { 1047103976Spst init_clnt_str(&(h->avs[i])); 1048103976Spst init_srvr_str(&(h->srvr_avs[i])); 1049103976Spst } 105041120Sjdp init_srvr_str(&h->srvr_msg); 105141120Sjdp init_srvr_str(&h->srvr_data); 105241120Sjdp srandomdev(); 105341120Sjdp } 105441120Sjdp return h; 105541120Sjdp} 105641120Sjdp 105741120Sjdpint 105841120Sjdptac_send_authen(struct tac_handle *h) 105941120Sjdp{ 106041120Sjdp struct tac_authen_reply *ar; 106141120Sjdp 1062103976Spst if (h->num_servers == 0) 1063103976Spst return -1; 1064103976Spst 106541120Sjdp if (h->last_seq_no == 0) { /* Authentication START packet */ 106641120Sjdp struct tac_authen_start *as; 106741120Sjdp 106841120Sjdp as = &h->request.u.authen_start; 106941120Sjdp h->request.length = 107041120Sjdp htonl(offsetof(struct tac_authen_start, rest[0])); 107141120Sjdp if (add_str_8(h, &as->user_len, &h->user) == -1 || 107241120Sjdp add_str_8(h, &as->port_len, &h->port) == -1 || 107341120Sjdp add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 || 107441120Sjdp add_str_8(h, &as->data_len, &h->data) == -1) 107541120Sjdp return -1; 107641120Sjdp } else { /* Authentication CONTINUE packet */ 107741120Sjdp struct tac_authen_cont *ac; 107841120Sjdp 107941120Sjdp ac = &h->request.u.authen_cont; 108041120Sjdp ac->flags = 0; 108141120Sjdp h->request.length = 108241120Sjdp htonl(offsetof(struct tac_authen_cont, rest[0])); 108341120Sjdp if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 || 108441120Sjdp add_str_16(h, &ac->data_len, &h->data) == -1) 108541120Sjdp return -1; 108641120Sjdp } 108741120Sjdp 108841120Sjdp /* Send the message and retrieve the reply. */ 108941120Sjdp if (send_msg(h) == -1 || recv_msg(h) == -1) 109041120Sjdp return -1; 109141120Sjdp 109241120Sjdp /* Scan the optional fields in the reply. */ 109341120Sjdp ar = &h->response.u.authen_reply; 109441120Sjdp h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]); 1095103976Spst if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 || 1096103976Spst get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 || 109741120Sjdp get_srvr_end(h) == -1) 109841120Sjdp return -1; 109941120Sjdp 110041120Sjdp if (!h->single_connect && 110141120Sjdp ar->status != TAC_AUTHEN_STATUS_GETDATA && 110241120Sjdp ar->status != TAC_AUTHEN_STATUS_GETUSER && 110341120Sjdp ar->status != TAC_AUTHEN_STATUS_GETPASS) 110441120Sjdp close_connection(h); 110541120Sjdp 110641120Sjdp return ar->flags << 8 | ar->status; 110741120Sjdp} 110841120Sjdp 110941120Sjdpint 1110103976Spsttac_send_author(struct tac_handle *h) 1111103976Spst{ 1112103976Spst int i, current; 1113103976Spst char dbgstr[64]; 1114103976Spst struct tac_author_request *areq = &h->request.u.author_request; 1115103976Spst struct tac_author_response *ares = &h->response.u.author_response; 1116103976Spst 1117103976Spst h->request.length = 1118103976Spst htonl(offsetof(struct tac_author_request, rest[0])); 1119103976Spst 1120103976Spst /* Count each specified AV pair */ 1121103976Spst for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++) 1122103976Spst if (h->avs[i].len && h->avs[i].data) 1123103976Spst areq->av_cnt++; 1124103976Spst 1125103976Spst /* 1126103976Spst * Each AV size is a byte starting right after 'av_cnt'. Update the 1127103976Spst * offset to include these AV sizes. 1128103976Spst */ 1129103976Spst h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt); 1130103976Spst 1131103976Spst /* Now add the string arguments from 'h' */ 1132103976Spst if (add_str_8(h, &areq->user_len, &h->user) == -1 || 1133103976Spst add_str_8(h, &areq->port_len, &h->port) == -1 || 1134103976Spst add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1) 1135103976Spst return -1; 1136103976Spst 1137103976Spst /* Add each AV pair, the size of each placed in areq->rest[current] */ 1138103976Spst for (current=0, i=0; i<MAXAVPAIRS; i++) { 1139103976Spst if (h->avs[i].len && h->avs[i].data) { 1140103976Spst if (add_str_8(h, &areq->rest[current++], 1141103976Spst &(h->avs[i])) == -1) 1142103976Spst return -1; 1143103976Spst } 1144103976Spst } 1145103976Spst 1146103976Spst /* Send the message and retrieve the reply. */ 1147103976Spst if (send_msg(h) == -1 || recv_msg(h) == -1) 1148103976Spst return -1; 1149103976Spst 1150103976Spst /* Update the offset in the response packet based on av pairs count */ 1151103976Spst h->srvr_pos = offsetof(struct tac_author_response, rest[0]) + 1152103976Spst ares->av_cnt; 1153103976Spst 1154103976Spst /* Scan the optional fields in the response. */ 1155103976Spst if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 || 1156103976Spst get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1) 1157103976Spst return -1; 1158103976Spst 1159103976Spst /* Get each AV pair (just setting pointers, not malloc'ing) */ 1160103976Spst clear_srvr_avs(h); 1161103976Spst for (i=0; i<ares->av_cnt; i++) { 1162103976Spst snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i); 1163103976Spst if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]), 1164103976Spst ares->rest[i]) == -1) 1165103976Spst return -1; 1166103976Spst } 1167103976Spst 1168103976Spst /* Should have ended up at the end */ 1169103976Spst if (get_srvr_end(h) == -1) 1170103976Spst return -1; 1171103976Spst 1172103976Spst /* Sanity checks */ 1173103976Spst if (!h->single_connect) 1174103976Spst close_connection(h); 1175103976Spst 1176103976Spst return ares->av_cnt << 8 | ares->status; 1177103976Spst} 1178103976Spst 1179103976Spstint 1180200399Ssyrinxtac_send_acct(struct tac_handle *h) 1181200399Ssyrinx{ 1182200399Ssyrinx register int i, current; 1183200399Ssyrinx struct tac_acct_start *as = &h->request.u.acct_start; 1184200399Ssyrinx struct tac_acct_reply *ar = &h->response.u.acct_reply; 1185200399Ssyrinx 1186200399Ssyrinx /* start */ 1187200399Ssyrinx as = &h->request.u.acct_start; 1188200399Ssyrinx h->request.length = htonl(offsetof(struct tac_acct_start, rest[0])); 1189200399Ssyrinx for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++) 1190200399Ssyrinx if (h->avs[i].len && h->avs[i].data) 1191200399Ssyrinx as->av_cnt++; 1192200399Ssyrinx h->request.length = ntohl(htonl(h->request.length) + as->av_cnt); 1193200399Ssyrinx 1194200399Ssyrinx if (add_str_8(h, &as->user_len, &h->user) == -1 || 1195200399Ssyrinx add_str_8(h, &as->port_len, &h->port) == -1 || 1196200399Ssyrinx add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1) 1197200399Ssyrinx return -1; 1198200399Ssyrinx 1199200399Ssyrinx for (i = current = 0; i < MAXAVPAIRS; i++) 1200200399Ssyrinx if (h->avs[i].len && h->avs[i].data) 1201200399Ssyrinx if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1) 1202200399Ssyrinx return -1; 1203200399Ssyrinx 1204200399Ssyrinx /* send */ 1205200399Ssyrinx if (send_msg(h) == -1 || recv_msg(h) == -1) 1206200399Ssyrinx return -1; 1207200399Ssyrinx 1208200399Ssyrinx /* reply */ 1209200399Ssyrinx h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]); 1210200399Ssyrinx if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 || 1211200399Ssyrinx get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 || 1212200399Ssyrinx get_srvr_end(h) == -1) 1213200399Ssyrinx return -1; 1214200399Ssyrinx 1215200399Ssyrinx /* Sanity checks */ 1216200399Ssyrinx if (!h->single_connect) 1217200399Ssyrinx close_connection(h); 1218200399Ssyrinx 1219200399Ssyrinx return ar->status; 1220200399Ssyrinx} 1221200399Ssyrinx 1222200399Ssyrinxint 122341120Sjdptac_set_rem_addr(struct tac_handle *h, const char *addr) 122441120Sjdp{ 122541120Sjdp return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0); 122641120Sjdp} 122741120Sjdp 122841120Sjdpint 122941120Sjdptac_set_data(struct tac_handle *h, const void *data, size_t data_len) 123041120Sjdp{ 123141120Sjdp return save_str(h, &h->data, data, data_len); 123241120Sjdp} 123341120Sjdp 123441120Sjdpint 123541120Sjdptac_set_msg(struct tac_handle *h, const char *msg) 123641120Sjdp{ 123741120Sjdp return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0); 123841120Sjdp} 123941120Sjdp 124041120Sjdpint 124141120Sjdptac_set_port(struct tac_handle *h, const char *port) 124241120Sjdp{ 124341120Sjdp return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0); 124441120Sjdp} 124541120Sjdp 124641120Sjdpint 124741120Sjdptac_set_priv(struct tac_handle *h, int priv) 124841120Sjdp{ 124941120Sjdp if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) { 125041120Sjdp generr(h, "Attempt to set invalid privilege level"); 125141120Sjdp return -1; 125241120Sjdp } 125341120Sjdp h->request.u.authen_start.priv_lvl = priv; 125441120Sjdp return 0; 125541120Sjdp} 125641120Sjdp 125741120Sjdpint 125841120Sjdptac_set_user(struct tac_handle *h, const char *user) 125941120Sjdp{ 126041120Sjdp return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0); 126141120Sjdp} 126241120Sjdp 1263103976Spstint 1264103976Spsttac_set_av(struct tac_handle *h, u_int index, const char *av) 1265103976Spst{ 1266103976Spst if (index >= MAXAVPAIRS) 1267103976Spst return -1; 1268103976Spst return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0); 1269103976Spst} 1270103976Spst 1271103976Spstchar * 1272103976Spsttac_get_av(struct tac_handle *h, u_int index) 1273103976Spst{ 1274103976Spst if (index >= MAXAVPAIRS) 1275103976Spst return NULL; 1276103976Spst return dup_str(h, &(h->srvr_avs[index]), NULL); 1277103976Spst} 1278103976Spst 1279103976Spstchar * 1280103976Spsttac_get_av_value(struct tac_handle *h, const char *attribute) 1281103976Spst{ 1282103976Spst int i, len; 1283103976Spst const char *ch, *end; 1284103976Spst const char *candidate; 1285103976Spst int candidate_len; 1286103976Spst int found_seperator; 1287103976Spst struct srvr_str srvr; 1288103976Spst 1289103976Spst if (attribute == NULL || ((len = strlen(attribute)) == 0)) 1290103976Spst return NULL; 1291103976Spst 1292103976Spst for (i=0; i<MAXAVPAIRS; i++) { 1293103976Spst candidate = h->srvr_avs[i].data; 1294103976Spst candidate_len = h->srvr_avs[i].len; 1295103976Spst 1296103976Spst /* 1297103976Spst * Valid 'srvr_avs' guaranteed to be contiguous starting at 1298103976Spst * index 0 (not necessarily the case with 'avs'). Break out 1299103976Spst * when the "end" of the list has been reached. 1300103976Spst */ 1301103976Spst if (!candidate) 1302103976Spst break; 1303103976Spst 1304103976Spst if (len < candidate_len && 1305103976Spst !strncmp(candidate, attribute, len)) { 1306103976Spst 1307103976Spst ch = candidate + len; 1308103976Spst end = candidate + candidate_len; 1309103976Spst 1310103976Spst /* 1311103976Spst * Sift out the white space between A and V (should not 1312103976Spst * be any, but don't trust implementation of server...) 1313103976Spst */ 1314103976Spst found_seperator = 0; 1315103976Spst while ((*ch == '=' || *ch == '*' || *ch == ' ' || 1316103976Spst *ch == '\t') && ch != end) { 1317103976Spst if (*ch == '=' || *ch == '*') 1318103976Spst found_seperator++; 1319103976Spst ch++; 1320103976Spst } 1321103976Spst 1322103976Spst /* 1323103976Spst * Note: 1324103976Spst * The case of 'attribute' == "foo" and 1325103976Spst * h->srvr_avs[0] = "foobie=var1" 1326103976Spst * h->srvr_avs[1] = "foo=var2" 1327103976Spst * is handled. 1328199802Sattilio * 1329199802Sattilio * Note that for empty string attribute values a 1330199802Sattilio * 0-length string is returned in order to distinguish 1331199802Sattilio * against unset values. 1332202751Semaste * dup_str() will handle srvr.len == 0 correctly. 1333103976Spst */ 1334199802Sattilio if (found_seperator == 1) { 1335103976Spst srvr.len = end - ch; 1336103976Spst srvr.data = ch; 1337103976Spst return dup_str(h, &srvr, NULL); 1338103976Spst } 1339103976Spst } 1340103976Spst } 1341103976Spst return NULL; 1342103976Spst} 1343103976Spst 1344103976Spstvoid 1345103976Spsttac_clear_avs(struct tac_handle *h) 1346103976Spst{ 1347103976Spst int i; 1348103976Spst for (i=0; i<MAXAVPAIRS; i++) 1349103976Spst save_str(h, &(h->avs[i]), NULL, 0); 1350103976Spst} 1351103976Spst 1352103976Spststatic void 1353103976Spstclear_srvr_avs(struct tac_handle *h) 1354103976Spst{ 1355103976Spst int i; 1356103976Spst for (i=0; i<MAXAVPAIRS; i++) 1357103976Spst init_srvr_str(&(h->srvr_avs[i])); 1358103976Spst} 1359103976Spst 1360103976Spst 136141120Sjdpconst char * 136241120Sjdptac_strerror(struct tac_handle *h) 136341120Sjdp{ 136441120Sjdp return h->errmsg; 136541120Sjdp} 136641120Sjdp 136741120Sjdpstatic void * 136841120Sjdpxmalloc(struct tac_handle *h, size_t size) 136941120Sjdp{ 137041120Sjdp void *r; 137141120Sjdp 137241120Sjdp if ((r = malloc(size)) == NULL) 137341120Sjdp generr(h, "Out of memory"); 137441120Sjdp return r; 137541120Sjdp} 137641120Sjdp 137741120Sjdpstatic char * 137841120Sjdpxstrdup(struct tac_handle *h, const char *s) 137941120Sjdp{ 138041120Sjdp char *r; 138141120Sjdp 138241120Sjdp if ((r = strdup(s)) == NULL) 138341120Sjdp generr(h, "Out of memory"); 138441120Sjdp return r; 138541120Sjdp} 1386