xref: /freebsd-src/lib/libtacplus/taclib.c (revision a2f733abcff64628b7771a47089628b7327a88bd)
12c195535SJohn Polstra /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni  *
41a61aeb8SPaul Traina  * Copyright (c) 1998, 2001, 2002, Juniper Networks, Inc.
52c195535SJohn Polstra  * All rights reserved.
62c195535SJohn Polstra  *
72c195535SJohn Polstra  * Redistribution and use in source and binary forms, with or without
82c195535SJohn Polstra  * modification, are permitted provided that the following conditions
92c195535SJohn Polstra  * are met:
102c195535SJohn Polstra  * 1. Redistributions of source code must retain the above copyright
112c195535SJohn Polstra  *    notice, this list of conditions and the following disclaimer.
122c195535SJohn Polstra  * 2. Redistributions in binary form must reproduce the above copyright
132c195535SJohn Polstra  *    notice, this list of conditions and the following disclaimer in the
142c195535SJohn Polstra  *    documentation and/or other materials provided with the distribution.
152c195535SJohn Polstra  *
162c195535SJohn Polstra  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172c195535SJohn Polstra  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182c195535SJohn Polstra  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192c195535SJohn Polstra  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202c195535SJohn Polstra  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212c195535SJohn Polstra  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222c195535SJohn Polstra  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232c195535SJohn Polstra  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242c195535SJohn Polstra  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252c195535SJohn Polstra  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262c195535SJohn Polstra  * SUCH DAMAGE.
272c195535SJohn Polstra  */
282c195535SJohn Polstra 
292c195535SJohn Polstra #include <sys/types.h>
302c195535SJohn Polstra #include <sys/socket.h>
312c195535SJohn Polstra #include <sys/time.h>
322c195535SJohn Polstra #include <netinet/in.h>
332c195535SJohn Polstra #include <arpa/inet.h>
342c195535SJohn Polstra 
352c195535SJohn Polstra #include <assert.h>
36*21850106SDag-Erling Smørgrav #include <ctype.h>
372c195535SJohn Polstra #include <errno.h>
382c195535SJohn Polstra #include <fcntl.h>
392c195535SJohn Polstra #include <md5.h>
402c195535SJohn Polstra #include <netdb.h>
412c195535SJohn Polstra #include <stdarg.h>
422c195535SJohn Polstra #include <stddef.h>
432c195535SJohn Polstra #include <stdio.h>
442c195535SJohn Polstra #include <stdlib.h>
452c195535SJohn Polstra #include <string.h>
462c195535SJohn Polstra #include <unistd.h>
472c195535SJohn Polstra 
48*21850106SDag-Erling Smørgrav #include <security/pam_appl.h>
49*21850106SDag-Erling Smørgrav #include <security/openpam.h>
50*21850106SDag-Erling Smørgrav 
512c195535SJohn Polstra #include "taclib_private.h"
522c195535SJohn Polstra 
532c195535SJohn Polstra static int		 add_str_8(struct tac_handle *, u_int8_t *,
54*21850106SDag-Erling Smørgrav 			    struct tac_str *);
552c195535SJohn Polstra static int		 add_str_16(struct tac_handle *, u_int16_t *,
56*21850106SDag-Erling Smørgrav 			    struct tac_str *);
571a61aeb8SPaul Traina static int		 protocol_version(int, int, int);
582c195535SJohn Polstra static void		 close_connection(struct tac_handle *);
592c195535SJohn Polstra static int		 conn_server(struct tac_handle *);
602c195535SJohn Polstra static void		 crypt_msg(struct tac_handle *, struct tac_msg *);
61*21850106SDag-Erling Smørgrav static void		*dup_str(struct tac_handle *, const struct tac_str *,
622c195535SJohn Polstra 			    size_t *);
632c195535SJohn Polstra static int		 establish_connection(struct tac_handle *);
64*21850106SDag-Erling Smørgrav static void		 free_str(struct tac_str *);
652c195535SJohn Polstra static void		 generr(struct tac_handle *, const char *, ...)
662c195535SJohn Polstra 			    __printflike(2, 3);
672c195535SJohn Polstra static void		 gen_session_id(struct tac_msg *);
682c195535SJohn Polstra static int		 get_srvr_end(struct tac_handle *);
69*21850106SDag-Erling Smørgrav static int		 get_str(struct tac_handle *, const char *,
70*21850106SDag-Erling Smørgrav 				      struct tac_str *, size_t);
71*21850106SDag-Erling Smørgrav static void		 init_str(struct tac_str *);
722c195535SJohn Polstra static int		 read_timed(struct tac_handle *, void *, size_t,
732c195535SJohn Polstra 			    const struct timeval *);
742c195535SJohn Polstra static int		 recv_msg(struct tac_handle *);
75*21850106SDag-Erling Smørgrav static int		 save_str(struct tac_handle *, struct tac_str *,
762c195535SJohn Polstra 			    const void *, size_t);
772c195535SJohn Polstra static int		 send_msg(struct tac_handle *);
782c195535SJohn Polstra static int		 split(char *, char *[], int, char *, size_t);
792c195535SJohn Polstra static void		*xmalloc(struct tac_handle *, size_t);
802c195535SJohn Polstra static char		*xstrdup(struct tac_handle *, const char *);
811a61aeb8SPaul Traina static void              clear_srvr_avs(struct tac_handle *);
821a61aeb8SPaul Traina static void              create_msg(struct tac_handle *, int, int, int);
832c195535SJohn Polstra 
842c195535SJohn Polstra /*
852c195535SJohn Polstra  * Append some optional data to the current request, and store its
862c195535SJohn Polstra  * length into the 8-bit field referenced by "fld".  Returns 0 on
872c195535SJohn Polstra  * success, or -1 on failure.
882c195535SJohn Polstra  *
892c195535SJohn Polstra  * This function also frees the "cs" string data and initializes it
902c195535SJohn Polstra  * for the next time.
912c195535SJohn Polstra  */
922c195535SJohn Polstra static int
add_str_8(struct tac_handle * h,u_int8_t * fld,struct tac_str * cs)93*21850106SDag-Erling Smørgrav add_str_8(struct tac_handle *h, u_int8_t *fld, struct tac_str *cs)
942c195535SJohn Polstra {
952c195535SJohn Polstra 	u_int16_t len;
962c195535SJohn Polstra 
972c195535SJohn Polstra 	if (add_str_16(h, &len, cs) == -1)
982c195535SJohn Polstra 		return -1;
992c195535SJohn Polstra 	len = ntohs(len);
1002c195535SJohn Polstra 	if (len > 0xff) {
1012c195535SJohn Polstra 		generr(h, "Field too long");
1022c195535SJohn Polstra 		return -1;
1032c195535SJohn Polstra 	}
1042c195535SJohn Polstra 	*fld = len;
1052c195535SJohn Polstra 	return 0;
1062c195535SJohn Polstra }
1072c195535SJohn Polstra 
1082c195535SJohn Polstra /*
1092c195535SJohn Polstra  * Append some optional data to the current request, and store its
1102c195535SJohn Polstra  * length into the 16-bit field (network byte order) referenced by
1112c195535SJohn Polstra  * "fld".  Returns 0 on success, or -1 on failure.
1122c195535SJohn Polstra  *
1132c195535SJohn Polstra  * This function also frees the "cs" string data and initializes it
1142c195535SJohn Polstra  * for the next time.
1152c195535SJohn Polstra  */
1162c195535SJohn Polstra static int
add_str_16(struct tac_handle * h,u_int16_t * fld,struct tac_str * cs)117*21850106SDag-Erling Smørgrav add_str_16(struct tac_handle *h, u_int16_t *fld, struct tac_str *cs)
1182c195535SJohn Polstra {
1192c195535SJohn Polstra 	size_t len;
1202c195535SJohn Polstra 
1212c195535SJohn Polstra 	len = cs->len;
1222c195535SJohn Polstra 	if (cs->data == NULL)
1232c195535SJohn Polstra 		len = 0;
1242c195535SJohn Polstra 	if (len != 0) {
1252c195535SJohn Polstra 		int offset;
1262c195535SJohn Polstra 
1272c195535SJohn Polstra 		if (len > 0xffff) {
1282c195535SJohn Polstra 			generr(h, "Field too long");
1292c195535SJohn Polstra 			return -1;
1302c195535SJohn Polstra 		}
1312c195535SJohn Polstra 		offset = ntohl(h->request.length);
1322c195535SJohn Polstra 		if (offset + len > BODYSIZE) {
1332c195535SJohn Polstra 			generr(h, "Message too long");
1342c195535SJohn Polstra 			return -1;
1352c195535SJohn Polstra 		}
1362c195535SJohn Polstra 		memcpy(h->request.u.body + offset, cs->data, len);
1372c195535SJohn Polstra 		h->request.length = htonl(offset + len);
1382c195535SJohn Polstra 	}
1392c195535SJohn Polstra 	*fld = htons(len);
1402c195535SJohn Polstra 	free_str(cs);
1412c195535SJohn Polstra 	return 0;
1422c195535SJohn Polstra }
1432c195535SJohn Polstra 
1442c195535SJohn Polstra static int
protocol_version(int msg_type,int var,int type)1451a61aeb8SPaul Traina protocol_version(int msg_type, int var, int type)
1462c195535SJohn Polstra {
1472c195535SJohn Polstra     int minor;
1482c195535SJohn Polstra 
1491a61aeb8SPaul Traina     switch (msg_type) {
1501a61aeb8SPaul Traina         case TAC_AUTHEN:
1511a61aeb8SPaul Traina 	    /* 'var' represents the 'action' */
1521a61aeb8SPaul Traina 	    switch (var) {
1532c195535SJohn Polstra 	        case TAC_AUTHEN_LOGIN:
1542c195535SJohn Polstra 		    switch (type) {
1552c195535SJohn Polstra 
1562c195535SJohn Polstra 		        case TAC_AUTHEN_TYPE_PAP:
1572c195535SJohn Polstra 			case TAC_AUTHEN_TYPE_CHAP:
1582c195535SJohn Polstra 			case TAC_AUTHEN_TYPE_MSCHAP:
1592c195535SJohn Polstra 			case TAC_AUTHEN_TYPE_ARAP:
1602c195535SJohn Polstra 			    minor = 1;
1612c195535SJohn Polstra 			break;
1622c195535SJohn Polstra 
1632c195535SJohn Polstra 			default:
1642c195535SJohn Polstra 			    minor = 0;
1652c195535SJohn Polstra 			break;
1662c195535SJohn Polstra 		     }
1672c195535SJohn Polstra 		break;
1682c195535SJohn Polstra 
1692c195535SJohn Polstra 		case TAC_AUTHEN_SENDAUTH:
1702c195535SJohn Polstra 		    minor = 1;
1712c195535SJohn Polstra 		break;
1722c195535SJohn Polstra 
1732c195535SJohn Polstra 		default:
1742c195535SJohn Polstra 		    minor = 0;
1752c195535SJohn Polstra 		break;
1762c195535SJohn Polstra 	    };
1771a61aeb8SPaul Traina 	break;
1781a61aeb8SPaul Traina 
1791a61aeb8SPaul Traina 	case TAC_AUTHOR:
1801a61aeb8SPaul Traina 	    /* 'var' represents the 'method' */
1811a61aeb8SPaul Traina 	    switch (var) {
1821a61aeb8SPaul Traina 	        /*
1831a61aeb8SPaul Traina 		 * When new authentication methods are added, include 'method'
1841a61aeb8SPaul Traina 		 * in determining the value of 'minor'.  At this point, all
1851a61aeb8SPaul Traina                  * methods defined in this implementation (see "Authorization
1861a61aeb8SPaul Traina                  * authentication methods" in taclib.h) are minor version 0
1871a61aeb8SPaul Traina 		 * Not all types, however, indicate minor version 0.
1881a61aeb8SPaul Traina 		 */
1891a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_NOT_SET:
1901a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_NONE:
1911a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_KRB5:
1921a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_LINE:
1931a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_ENABLE:
1941a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_LOCAL:
1951a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_TACACSPLUS:
1961a61aeb8SPaul Traina                 case TAC_AUTHEN_METH_RCMD:
1971a61aeb8SPaul Traina 		    switch (type) {
1981a61aeb8SPaul Traina 		        case TAC_AUTHEN_TYPE_PAP:
1991a61aeb8SPaul Traina 			case TAC_AUTHEN_TYPE_CHAP:
2001a61aeb8SPaul Traina 			case TAC_AUTHEN_TYPE_MSCHAP:
2011a61aeb8SPaul Traina 			case TAC_AUTHEN_TYPE_ARAP:
2021a61aeb8SPaul Traina 			    minor = 1;
2031a61aeb8SPaul Traina 			break;
2041a61aeb8SPaul Traina 
2051a61aeb8SPaul Traina 			default:
2061a61aeb8SPaul Traina 			    minor = 0;
2071a61aeb8SPaul Traina 			break;
2081a61aeb8SPaul Traina 		     }
2091a61aeb8SPaul Traina 	        break;
2101a61aeb8SPaul Traina 	        default:
2111a61aeb8SPaul Traina 		    minor = 0;
2121a61aeb8SPaul Traina 		break;
2131a61aeb8SPaul Traina 	    }
2141a61aeb8SPaul Traina         break;
2151a61aeb8SPaul Traina 
216db3a20a5SShteryana Shopova 	case TAC_ACCT:
217db3a20a5SShteryana Shopova 
2181a61aeb8SPaul Traina 	default:
2191a61aeb8SPaul Traina 	    minor = 0;
2201a61aeb8SPaul Traina         break;
2211a61aeb8SPaul Traina     }
2222c195535SJohn Polstra 
2232c195535SJohn Polstra     return TAC_VER_MAJOR << 4 | minor;
2242c195535SJohn Polstra }
2252c195535SJohn Polstra 
2261a61aeb8SPaul Traina 
2272c195535SJohn Polstra static void
close_connection(struct tac_handle * h)2282c195535SJohn Polstra close_connection(struct tac_handle *h)
2292c195535SJohn Polstra {
2302c195535SJohn Polstra 	if (h->fd != -1) {
2312c195535SJohn Polstra 		close(h->fd);
2322c195535SJohn Polstra 		h->fd = -1;
2332c195535SJohn Polstra 	}
2342c195535SJohn Polstra }
2352c195535SJohn Polstra 
2362c195535SJohn Polstra static int
conn_server(struct tac_handle * h)2372c195535SJohn Polstra conn_server(struct tac_handle *h)
2382c195535SJohn Polstra {
239*21850106SDag-Erling Smørgrav 	struct tac_server *srvp = &h->servers[h->cur_server];
2402c195535SJohn Polstra 	int flags;
2412c195535SJohn Polstra 
2422c195535SJohn Polstra 	if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
2432c195535SJohn Polstra 		generr(h, "Cannot create socket: %s", strerror(errno));
2442c195535SJohn Polstra 		return -1;
2452c195535SJohn Polstra 	}
2462c195535SJohn Polstra 	if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
2472c195535SJohn Polstra 	    fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
2482c195535SJohn Polstra 		generr(h, "Cannot set non-blocking mode on socket: %s",
2492c195535SJohn Polstra 		    strerror(errno));
2502c195535SJohn Polstra 		close(h->fd);
2512c195535SJohn Polstra 		h->fd = -1;
2522c195535SJohn Polstra 		return -1;
2532c195535SJohn Polstra 	}
2542c195535SJohn Polstra 	if (connect(h->fd, (struct sockaddr *)&srvp->addr,
2552c195535SJohn Polstra 	    sizeof srvp->addr) == 0)
2562c195535SJohn Polstra 		return 0;
2572c195535SJohn Polstra 
2582c195535SJohn Polstra 	if (errno == EINPROGRESS) {
2592c195535SJohn Polstra 		fd_set wfds;
2602c195535SJohn Polstra 		struct timeval tv;
2612c195535SJohn Polstra 		int nfds;
2622c195535SJohn Polstra 		struct sockaddr peer;
26378e3eed0SStefan Farfeleder 		socklen_t errlen, peerlen;
2642c195535SJohn Polstra 		int err;
2652c195535SJohn Polstra 
2662c195535SJohn Polstra 		/* Wait for the connection to complete. */
2672c195535SJohn Polstra 		FD_ZERO(&wfds);
2682c195535SJohn Polstra 		FD_SET(h->fd, &wfds);
2692c195535SJohn Polstra 		tv.tv_sec = srvp->timeout;
2702c195535SJohn Polstra 		tv.tv_usec = 0;
2712c195535SJohn Polstra 		nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
2722c195535SJohn Polstra 		if (nfds == -1) {
2732c195535SJohn Polstra 			generr(h, "select: %s", strerror(errno));
2742c195535SJohn Polstra 			close(h->fd);
2752c195535SJohn Polstra 			h->fd = -1;
2762c195535SJohn Polstra 			return -1;
2772c195535SJohn Polstra 		}
2782c195535SJohn Polstra 		if (nfds == 0) {
2792c195535SJohn Polstra 			generr(h, "connect: timed out");
2802c195535SJohn Polstra 			close(h->fd);
2812c195535SJohn Polstra 			h->fd = -1;
2822c195535SJohn Polstra 			return -1;
2832c195535SJohn Polstra 		}
2842c195535SJohn Polstra 
2852c195535SJohn Polstra 		/* See whether we are connected now. */
2862c195535SJohn Polstra 		peerlen = sizeof peer;
2872c195535SJohn Polstra 		if (getpeername(h->fd, &peer, &peerlen) == 0)
2882c195535SJohn Polstra 			return 0;
2892c195535SJohn Polstra 
2902c195535SJohn Polstra 		if (errno != ENOTCONN) {
2912c195535SJohn Polstra 			generr(h, "getpeername: %s", strerror(errno));
2922c195535SJohn Polstra 			close(h->fd);
2932c195535SJohn Polstra 			h->fd = -1;
2942c195535SJohn Polstra 			return -1;
2952c195535SJohn Polstra 		}
2962c195535SJohn Polstra 
2972c195535SJohn Polstra 		/* Find out why the connect failed. */
2982c195535SJohn Polstra 		errlen = sizeof err;
2992c195535SJohn Polstra 		getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
3002c195535SJohn Polstra 		errno = err;
3012c195535SJohn Polstra 	}
3022c195535SJohn Polstra 	generr(h, "connect: %s", strerror(errno));
3032c195535SJohn Polstra 	close(h->fd);
3042c195535SJohn Polstra 	h->fd = -1;
3052c195535SJohn Polstra 	return -1;
3062c195535SJohn Polstra }
3072c195535SJohn Polstra 
3082c195535SJohn Polstra /*
3092c195535SJohn Polstra  * Encrypt or decrypt a message.  The operations are symmetrical.
3102c195535SJohn Polstra  */
3112c195535SJohn Polstra static void
crypt_msg(struct tac_handle * h,struct tac_msg * msg)3122c195535SJohn Polstra crypt_msg(struct tac_handle *h, struct tac_msg *msg)
3132c195535SJohn Polstra {
3142c195535SJohn Polstra 	const char *secret;
3152c195535SJohn Polstra 	MD5_CTX base_ctx;
3162c195535SJohn Polstra 	MD5_CTX ctx;
3172c195535SJohn Polstra 	unsigned char md5[16];
3182c195535SJohn Polstra 	int chunk;
3192c195535SJohn Polstra 	int msg_len;
3202c195535SJohn Polstra 
3212c195535SJohn Polstra 	secret = h->servers[h->cur_server].secret;
3222c195535SJohn Polstra 	if (secret[0] == '\0')
3232c195535SJohn Polstra 		msg->flags |= TAC_UNENCRYPTED;
3242c195535SJohn Polstra 	if (msg->flags & TAC_UNENCRYPTED)
3252c195535SJohn Polstra 		return;
3262c195535SJohn Polstra 
3272c195535SJohn Polstra 	msg_len = ntohl(msg->length);
3282c195535SJohn Polstra 
3292c195535SJohn Polstra 	MD5Init(&base_ctx);
3302c195535SJohn Polstra 	MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
3312c195535SJohn Polstra 	MD5Update(&base_ctx, secret, strlen(secret));
3322c195535SJohn Polstra 	MD5Update(&base_ctx, &msg->version, sizeof msg->version);
3332c195535SJohn Polstra 	MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
3342c195535SJohn Polstra 
3352c195535SJohn Polstra 	ctx = base_ctx;
3362c195535SJohn Polstra 	for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
3372c195535SJohn Polstra 		int chunk_len;
3382c195535SJohn Polstra 		int i;
3392c195535SJohn Polstra 
3402c195535SJohn Polstra 		MD5Final(md5, &ctx);
3412c195535SJohn Polstra 
3422c195535SJohn Polstra 		if ((chunk_len = msg_len - chunk) > sizeof md5)
3432c195535SJohn Polstra 			chunk_len = sizeof md5;
3442c195535SJohn Polstra 		for (i = 0;  i < chunk_len;  i++)
3452c195535SJohn Polstra 			msg->u.body[chunk + i] ^= md5[i];
3462c195535SJohn Polstra 
3472c195535SJohn Polstra 		ctx = base_ctx;
3482c195535SJohn Polstra 		MD5Update(&ctx, md5, sizeof md5);
3492c195535SJohn Polstra 	}
3502c195535SJohn Polstra }
3512c195535SJohn Polstra 
3522c195535SJohn Polstra /*
3532c195535SJohn Polstra  * Return a dynamically allocated copy of the given server string.
3542c195535SJohn Polstra  * The copy is null-terminated.  If "len" is non-NULL, the length of
3552c195535SJohn Polstra  * the string (excluding the terminating null byte) is stored via it.
3562c195535SJohn Polstra  * Returns NULL on failure.  Empty strings are still allocated even
3572c195535SJohn Polstra  * though they have no content.
3582c195535SJohn Polstra  */
3592c195535SJohn Polstra static void *
dup_str(struct tac_handle * h,const struct tac_str * ss,size_t * len)360*21850106SDag-Erling Smørgrav dup_str(struct tac_handle *h, const struct tac_str *ss, size_t *len)
3612c195535SJohn Polstra {
3622c195535SJohn Polstra 	unsigned char *p;
3632c195535SJohn Polstra 
3642c195535SJohn Polstra 	if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
3652c195535SJohn Polstra 		return NULL;
3662c195535SJohn Polstra 	if (ss->data != NULL && ss->len != 0)
3672c195535SJohn Polstra 		memcpy(p, ss->data, ss->len);
3682c195535SJohn Polstra 	p[ss->len] = '\0';
3692c195535SJohn Polstra 	if (len != NULL)
3702c195535SJohn Polstra 		*len = ss->len;
3712c195535SJohn Polstra 	return p;
3722c195535SJohn Polstra }
3732c195535SJohn Polstra 
3742c195535SJohn Polstra static int
establish_connection(struct tac_handle * h)3752c195535SJohn Polstra establish_connection(struct tac_handle *h)
3762c195535SJohn Polstra {
3772c195535SJohn Polstra 	int i;
3782c195535SJohn Polstra 
3792c195535SJohn Polstra 	if (h->fd >= 0)		/* Already connected. */
3802c195535SJohn Polstra 		return 0;
3812c195535SJohn Polstra 	if (h->num_servers == 0) {
3822c195535SJohn Polstra 		generr(h, "No TACACS+ servers specified");
3832c195535SJohn Polstra 		return -1;
3842c195535SJohn Polstra 	}
3852c195535SJohn Polstra 	/*
3862c195535SJohn Polstra          * Try the servers round-robin.  We begin with the one that
3872c195535SJohn Polstra          * worked for us the last time.  That way, once we find a good
3882c195535SJohn Polstra          * server, we won't waste any more time trying the bad ones.
3892c195535SJohn Polstra 	 */
3902c195535SJohn Polstra 	for (i = 0;  i < h->num_servers;  i++) {
3912c195535SJohn Polstra 		if (conn_server(h) == 0) {
3922c195535SJohn Polstra 			h->single_connect = (h->servers[h->cur_server].flags &
3932c195535SJohn Polstra 			    TAC_SRVR_SINGLE_CONNECT) != 0;
3942c195535SJohn Polstra 			return 0;
3952c195535SJohn Polstra 		}
3962c195535SJohn Polstra 		if (++h->cur_server >= h->num_servers)	/* Wrap around */
3972c195535SJohn Polstra 			h->cur_server = 0;
3982c195535SJohn Polstra 	}
3992c195535SJohn Polstra 	/* Just return whatever error was last reported by conn_server(). */
4002c195535SJohn Polstra 	return -1;
4012c195535SJohn Polstra }
4022c195535SJohn Polstra 
4032c195535SJohn Polstra /*
4042c195535SJohn Polstra  * Free a client string, obliterating its contents first for security.
4052c195535SJohn Polstra  */
4062c195535SJohn Polstra static void
free_str(struct tac_str * cs)407*21850106SDag-Erling Smørgrav free_str(struct tac_str *cs)
4082c195535SJohn Polstra {
4092c195535SJohn Polstra 	if (cs->data != NULL) {
410*21850106SDag-Erling Smørgrav 		memset_s(cs->data, cs->len, 0, cs->len);
4112c195535SJohn Polstra 		free(cs->data);
4122c195535SJohn Polstra 		cs->data = NULL;
4132c195535SJohn Polstra 		cs->len = 0;
4142c195535SJohn Polstra 	}
4152c195535SJohn Polstra }
4162c195535SJohn Polstra 
4172c195535SJohn Polstra static void
generr(struct tac_handle * h,const char * format,...)4182c195535SJohn Polstra generr(struct tac_handle *h, const char *format, ...)
4192c195535SJohn Polstra {
4202c195535SJohn Polstra 	va_list		 ap;
4212c195535SJohn Polstra 
4222c195535SJohn Polstra 	va_start(ap, format);
4232c195535SJohn Polstra 	vsnprintf(h->errmsg, ERRSIZE, format, ap);
4242c195535SJohn Polstra 	va_end(ap);
4252c195535SJohn Polstra }
4262c195535SJohn Polstra 
4272c195535SJohn Polstra static void
gen_session_id(struct tac_msg * msg)4282c195535SJohn Polstra gen_session_id(struct tac_msg *msg)
4292c195535SJohn Polstra {
4302c195535SJohn Polstra 	int r;
4312c195535SJohn Polstra 
432d05c99c5SConrad Meyer 	r = arc4random();
4332c195535SJohn Polstra 	msg->session_id[0] = r >> 8;
4342c195535SJohn Polstra 	msg->session_id[1] = r;
435d05c99c5SConrad Meyer 	r = arc4random();
4362c195535SJohn Polstra 	msg->session_id[2] = r >> 8;
4372c195535SJohn Polstra 	msg->session_id[3] = r;
4382c195535SJohn Polstra }
4392c195535SJohn Polstra 
4402c195535SJohn Polstra /*
4412c195535SJohn Polstra  * Verify that we are exactly at the end of the response message.
4422c195535SJohn Polstra  * Returns 0 on success, -1 on failure.
4432c195535SJohn Polstra  */
4442c195535SJohn Polstra static int
get_srvr_end(struct tac_handle * h)4452c195535SJohn Polstra get_srvr_end(struct tac_handle *h)
4462c195535SJohn Polstra {
4471a61aeb8SPaul Traina 	int len;
4481a61aeb8SPaul Traina 
4491a61aeb8SPaul Traina 	len = ntohl(h->response.length);
4501a61aeb8SPaul Traina 
4511a61aeb8SPaul Traina 	if (h->srvr_pos != len) {
4521a61aeb8SPaul Traina 		generr(h, "Invalid length field in response "
4531a61aeb8SPaul Traina 		       "from server: end expected at %u, response length %u",
4541a61aeb8SPaul Traina 		       h->srvr_pos, len);
4552c195535SJohn Polstra 		return -1;
4562c195535SJohn Polstra 	}
4572c195535SJohn Polstra 	return 0;
4582c195535SJohn Polstra }
4592c195535SJohn Polstra 
4602c195535SJohn Polstra static int
get_str(struct tac_handle * h,const char * field,struct tac_str * ss,size_t len)461*21850106SDag-Erling Smørgrav get_str(struct tac_handle *h, const char *field,
462*21850106SDag-Erling Smørgrav     struct tac_str *ss, size_t len)
4632c195535SJohn Polstra {
4642c195535SJohn Polstra 	if (h->srvr_pos + len > ntohl(h->response.length)) {
4651a61aeb8SPaul Traina 		generr(h, "Invalid length field in %s response from server "
4661a61aeb8SPaul Traina 		       "(%lu > %lu)", field, (u_long)(h->srvr_pos + len),
4671a61aeb8SPaul Traina 		       (u_long)ntohl(h->response.length));
4682c195535SJohn Polstra 		return -1;
4692c195535SJohn Polstra 	}
4702c195535SJohn Polstra 	ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
4712c195535SJohn Polstra 	ss->len = len;
4722c195535SJohn Polstra 	h->srvr_pos += len;
4732c195535SJohn Polstra 	return 0;
4742c195535SJohn Polstra }
4752c195535SJohn Polstra 
4762c195535SJohn Polstra static void
init_str(struct tac_str * cs)477*21850106SDag-Erling Smørgrav init_str(struct tac_str *cs)
4782c195535SJohn Polstra {
4792c195535SJohn Polstra 	cs->data = NULL;
4802c195535SJohn Polstra 	cs->len = 0;
4812c195535SJohn Polstra }
4822c195535SJohn Polstra 
4832c195535SJohn Polstra static int
read_timed(struct tac_handle * h,void * buf,size_t len,const struct timeval * deadline)4842c195535SJohn Polstra read_timed(struct tac_handle *h, void *buf, size_t len,
4852c195535SJohn Polstra     const struct timeval *deadline)
4862c195535SJohn Polstra {
4872c195535SJohn Polstra 	char *ptr;
4882c195535SJohn Polstra 
4892c195535SJohn Polstra 	ptr = (char *)buf;
4902c195535SJohn Polstra 	while (len > 0) {
4912c195535SJohn Polstra 		int n;
4922c195535SJohn Polstra 
4932c195535SJohn Polstra 		n = read(h->fd, ptr, len);
4942c195535SJohn Polstra 		if (n == -1) {
4952c195535SJohn Polstra 			struct timeval tv;
4962c195535SJohn Polstra 			int nfds;
4972c195535SJohn Polstra 
4982c195535SJohn Polstra 			if (errno != EAGAIN) {
4992c195535SJohn Polstra 				generr(h, "Network read error: %s",
5002c195535SJohn Polstra 				    strerror(errno));
5012c195535SJohn Polstra 				return -1;
5022c195535SJohn Polstra 			}
5032c195535SJohn Polstra 
5042c195535SJohn Polstra 			/* Wait until we can read more data. */
5052c195535SJohn Polstra 			gettimeofday(&tv, NULL);
5062c195535SJohn Polstra 			timersub(deadline, &tv, &tv);
5072c195535SJohn Polstra 			if (tv.tv_sec >= 0) {
5082c195535SJohn Polstra 				fd_set rfds;
5092c195535SJohn Polstra 
5102c195535SJohn Polstra 				FD_ZERO(&rfds);
5112c195535SJohn Polstra 				FD_SET(h->fd, &rfds);
5122c195535SJohn Polstra 				nfds =
5132c195535SJohn Polstra 				    select(h->fd + 1, &rfds, NULL, NULL, &tv);
5142c195535SJohn Polstra 				if (nfds == -1) {
5152c195535SJohn Polstra 					generr(h, "select: %s",
5162c195535SJohn Polstra 					    strerror(errno));
5172c195535SJohn Polstra 					return -1;
5182c195535SJohn Polstra 				}
5192c195535SJohn Polstra 			} else
5202c195535SJohn Polstra 				nfds = 0;
5212c195535SJohn Polstra 			if (nfds == 0) {
5222c195535SJohn Polstra 				generr(h, "Network read timed out");
5232c195535SJohn Polstra 				return -1;
5242c195535SJohn Polstra 			}
5252c195535SJohn Polstra 		} else if (n == 0) {
5262c195535SJohn Polstra 			generr(h, "unexpected EOF from server");
5272c195535SJohn Polstra 			return -1;
5282c195535SJohn Polstra 		} else {
5292c195535SJohn Polstra 			ptr += n;
5302c195535SJohn Polstra 			len -= n;
5312c195535SJohn Polstra 		}
5322c195535SJohn Polstra 	}
5332c195535SJohn Polstra 	return 0;
5342c195535SJohn Polstra }
5352c195535SJohn Polstra 
5362c195535SJohn Polstra /*
5372c195535SJohn Polstra  * Receive a response from the server and decrypt it.  Returns 0 on
5382c195535SJohn Polstra  * success, or -1 on failure.
5392c195535SJohn Polstra  */
5402c195535SJohn Polstra static int
recv_msg(struct tac_handle * h)5412c195535SJohn Polstra recv_msg(struct tac_handle *h)
5422c195535SJohn Polstra {
5432c195535SJohn Polstra 	struct timeval deadline;
5442c195535SJohn Polstra 	struct tac_msg *msg;
5451a61aeb8SPaul Traina 	u_int32_t len;
5462c195535SJohn Polstra 
5472c195535SJohn Polstra 	msg = &h->response;
5482c195535SJohn Polstra 	gettimeofday(&deadline, NULL);
5492c195535SJohn Polstra 	deadline.tv_sec += h->servers[h->cur_server].timeout;
5502c195535SJohn Polstra 
5512c195535SJohn Polstra 	/* Read the message header and make sure it is reasonable. */
5522c195535SJohn Polstra 	if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
5532c195535SJohn Polstra 		return -1;
5542c195535SJohn Polstra 	if (memcmp(msg->session_id, h->request.session_id,
5552c195535SJohn Polstra 	    sizeof msg->session_id) != 0) {
5562c195535SJohn Polstra 		generr(h, "Invalid session ID in received message");
5572c195535SJohn Polstra 		return -1;
5582c195535SJohn Polstra 	}
5592c195535SJohn Polstra 	if (msg->type != h->request.type) {
5601a61aeb8SPaul Traina 		generr(h, "Invalid type in received message"
5611a61aeb8SPaul Traina 			  " (got %u, expected %u)",
5621a61aeb8SPaul Traina 			  msg->type, h->request.type);
5632c195535SJohn Polstra 		return -1;
5642c195535SJohn Polstra 	}
5652c195535SJohn Polstra 	len = ntohl(msg->length);
5662c195535SJohn Polstra 	if (len > BODYSIZE) {
5671a61aeb8SPaul Traina 		generr(h, "Received message too large (%u > %u)",
5681a61aeb8SPaul Traina 			  len, BODYSIZE);
5692c195535SJohn Polstra 		return -1;
5702c195535SJohn Polstra 	}
5712c195535SJohn Polstra 	if (msg->seq_no != ++h->last_seq_no) {
5721a61aeb8SPaul Traina 		generr(h, "Invalid sequence number in received message"
5731a61aeb8SPaul Traina 			  " (got %u, expected %u)",
5741a61aeb8SPaul Traina 			  msg->seq_no, h->last_seq_no);
5752c195535SJohn Polstra 		return -1;
5762c195535SJohn Polstra 	}
5772c195535SJohn Polstra 
5782c195535SJohn Polstra 	/* Read the message body. */
5792c195535SJohn Polstra 	if (read_timed(h, msg->u.body, len, &deadline) == -1)
5802c195535SJohn Polstra 		return -1;
5812c195535SJohn Polstra 
5822c195535SJohn Polstra 	/* Decrypt it. */
5832c195535SJohn Polstra 	crypt_msg(h, msg);
5842c195535SJohn Polstra 
5852c195535SJohn Polstra 	/*
5862c195535SJohn Polstra 	 * Turn off single-connection mode if the server isn't amenable
5872c195535SJohn Polstra 	 * to it.
5882c195535SJohn Polstra 	 */
5892c195535SJohn Polstra 	if (!(msg->flags & TAC_SINGLE_CONNECT))
5902c195535SJohn Polstra 		h->single_connect = 0;
5912c195535SJohn Polstra 	return 0;
5922c195535SJohn Polstra }
5932c195535SJohn Polstra 
5942c195535SJohn Polstra static int
save_str(struct tac_handle * h,struct tac_str * cs,const void * data,size_t len)595*21850106SDag-Erling Smørgrav save_str(struct tac_handle *h, struct tac_str *cs, const void *data,
5962c195535SJohn Polstra     size_t len)
5972c195535SJohn Polstra {
5982c195535SJohn Polstra 	free_str(cs);
5992c195535SJohn Polstra 	if (data != NULL && len != 0) {
6002c195535SJohn Polstra 		if ((cs->data = xmalloc(h, len)) == NULL)
6012c195535SJohn Polstra 			return -1;
6022c195535SJohn Polstra 		cs->len = len;
6032c195535SJohn Polstra 		memcpy(cs->data, data, len);
6042c195535SJohn Polstra 	}
6052c195535SJohn Polstra 	return 0;
6062c195535SJohn Polstra }
6072c195535SJohn Polstra 
6082c195535SJohn Polstra /*
6092c195535SJohn Polstra  * Send the current request, after encrypting it.  Returns 0 on success,
6102c195535SJohn Polstra  * or -1 on failure.
6112c195535SJohn Polstra  */
6122c195535SJohn Polstra static int
send_msg(struct tac_handle * h)6132c195535SJohn Polstra send_msg(struct tac_handle *h)
6142c195535SJohn Polstra {
6152c195535SJohn Polstra 	struct timeval deadline;
6162c195535SJohn Polstra 	struct tac_msg *msg;
6172c195535SJohn Polstra 	char *ptr;
6182c195535SJohn Polstra 	int len;
6192c195535SJohn Polstra 
6202c195535SJohn Polstra 	if (h->last_seq_no & 1) {
6212c195535SJohn Polstra 		generr(h, "Attempt to send message out of sequence");
6222c195535SJohn Polstra 		return -1;
6232c195535SJohn Polstra 	}
6242c195535SJohn Polstra 
6251a61aeb8SPaul Traina 	if (establish_connection(h) == -1)
6261a61aeb8SPaul Traina 		return -1;
6271a61aeb8SPaul Traina 
6282c195535SJohn Polstra 	msg = &h->request;
6292c195535SJohn Polstra 	msg->seq_no = ++h->last_seq_no;
6302c195535SJohn Polstra 	if (msg->seq_no == 1)
6312c195535SJohn Polstra 		gen_session_id(msg);
6322c195535SJohn Polstra 	crypt_msg(h, msg);
6332c195535SJohn Polstra 
6342c195535SJohn Polstra 	if (h->single_connect)
6352c195535SJohn Polstra 		msg->flags |= TAC_SINGLE_CONNECT;
6362c195535SJohn Polstra 	else
6372c195535SJohn Polstra 		msg->flags &= ~TAC_SINGLE_CONNECT;
6382c195535SJohn Polstra 	gettimeofday(&deadline, NULL);
6392c195535SJohn Polstra 	deadline.tv_sec += h->servers[h->cur_server].timeout;
6402c195535SJohn Polstra 	len = HDRSIZE + ntohl(msg->length);
6412c195535SJohn Polstra 	ptr = (char *)msg;
6422c195535SJohn Polstra 	while (len > 0) {
6432c195535SJohn Polstra 		int n;
6442c195535SJohn Polstra 
6452c195535SJohn Polstra 		n = write(h->fd, ptr, len);
6462c195535SJohn Polstra 		if (n == -1) {
6472c195535SJohn Polstra 			struct timeval tv;
6482c195535SJohn Polstra 			int nfds;
6492c195535SJohn Polstra 
6502c195535SJohn Polstra 			if (errno != EAGAIN) {
6512c195535SJohn Polstra 				generr(h, "Network write error: %s",
6522c195535SJohn Polstra 				    strerror(errno));
6532c195535SJohn Polstra 				return -1;
6542c195535SJohn Polstra 			}
6552c195535SJohn Polstra 
6562c195535SJohn Polstra 			/* Wait until we can write more data. */
6572c195535SJohn Polstra 			gettimeofday(&tv, NULL);
6582c195535SJohn Polstra 			timersub(&deadline, &tv, &tv);
6592c195535SJohn Polstra 			if (tv.tv_sec >= 0) {
6602c195535SJohn Polstra 				fd_set wfds;
6612c195535SJohn Polstra 
6622c195535SJohn Polstra 				FD_ZERO(&wfds);
6632c195535SJohn Polstra 				FD_SET(h->fd, &wfds);
6642c195535SJohn Polstra 				nfds =
6652c195535SJohn Polstra 				    select(h->fd + 1, NULL, &wfds, NULL, &tv);
6662c195535SJohn Polstra 				if (nfds == -1) {
6672c195535SJohn Polstra 					generr(h, "select: %s",
6682c195535SJohn Polstra 					    strerror(errno));
6692c195535SJohn Polstra 					return -1;
6702c195535SJohn Polstra 				}
6712c195535SJohn Polstra 			} else
6722c195535SJohn Polstra 				nfds = 0;
6732c195535SJohn Polstra 			if (nfds == 0) {
6742c195535SJohn Polstra 				generr(h, "Network write timed out");
6752c195535SJohn Polstra 				return -1;
6762c195535SJohn Polstra 			}
6772c195535SJohn Polstra 		} else {
6782c195535SJohn Polstra 			ptr += n;
6792c195535SJohn Polstra 			len -= n;
6802c195535SJohn Polstra 		}
6812c195535SJohn Polstra 	}
6822c195535SJohn Polstra 	return 0;
6832c195535SJohn Polstra }
6842c195535SJohn Polstra 
6852c195535SJohn Polstra static int
tac_add_server_av(struct tac_handle * h,const char * host,int port,const char * secret,int timeout,int flags,const char * const * avs)686*21850106SDag-Erling Smørgrav tac_add_server_av(struct tac_handle *h, const char *host, int port,
687*21850106SDag-Erling Smørgrav     const char *secret, int timeout, int flags, const char *const *avs)
6882c195535SJohn Polstra {
6892c195535SJohn Polstra 	struct tac_server *srvp;
690*21850106SDag-Erling Smørgrav 	const char *p;
691*21850106SDag-Erling Smørgrav 	size_t len;
692*21850106SDag-Erling Smørgrav 	int i;
6932c195535SJohn Polstra 
6942c195535SJohn Polstra 	if (h->num_servers >= MAXSERVERS) {
695343ce585SJohn Polstra 		generr(h, "Too many TACACS+ servers specified");
6962c195535SJohn Polstra 		return -1;
6972c195535SJohn Polstra 	}
6982c195535SJohn Polstra 	srvp = &h->servers[h->num_servers];
6992c195535SJohn Polstra 
7002c195535SJohn Polstra 	memset(&srvp->addr, 0, sizeof srvp->addr);
7012c195535SJohn Polstra 	srvp->addr.sin_len = sizeof srvp->addr;
7022c195535SJohn Polstra 	srvp->addr.sin_family = AF_INET;
7032c195535SJohn Polstra 	if (!inet_aton(host, &srvp->addr.sin_addr)) {
7042c195535SJohn Polstra 		struct hostent *hent;
7052c195535SJohn Polstra 
7062c195535SJohn Polstra 		if ((hent = gethostbyname(host)) == NULL) {
7072c195535SJohn Polstra 			generr(h, "%s: host not found", host);
7082c195535SJohn Polstra 			return -1;
7092c195535SJohn Polstra 		}
7102c195535SJohn Polstra 		memcpy(&srvp->addr.sin_addr, hent->h_addr,
7112c195535SJohn Polstra 		    sizeof srvp->addr.sin_addr);
7122c195535SJohn Polstra 	}
7132c195535SJohn Polstra 	srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
7142c195535SJohn Polstra 	if ((srvp->secret = xstrdup(h, secret)) == NULL)
7152c195535SJohn Polstra 		return -1;
7162c195535SJohn Polstra 	srvp->timeout = timeout;
7172c195535SJohn Polstra 	srvp->flags = flags;
718*21850106SDag-Erling Smørgrav 	srvp->navs = 0;
719*21850106SDag-Erling Smørgrav 	for (i = 0; avs[i] != NULL; i++) {
720*21850106SDag-Erling Smørgrav 		if (i >= MAXAVPAIRS) {
721*21850106SDag-Erling Smørgrav 			generr(h, "too many AV pairs");
722*21850106SDag-Erling Smørgrav 			goto fail;
723*21850106SDag-Erling Smørgrav 		}
724*21850106SDag-Erling Smørgrav 		for (p = avs[i], len = 0; is_arg(*p); p++)
725*21850106SDag-Erling Smørgrav 			len++;
726*21850106SDag-Erling Smørgrav 		if (p == avs[i] || *p != '=') {
727*21850106SDag-Erling Smørgrav 			generr(h, "invalid AV pair %d", i);
728*21850106SDag-Erling Smørgrav 			goto fail;
729*21850106SDag-Erling Smørgrav 		}
730*21850106SDag-Erling Smørgrav 		while (*p++ != '\0')
731*21850106SDag-Erling Smørgrav 			len++;
732*21850106SDag-Erling Smørgrav 		if ((srvp->avs[i].data = xstrdup(h, avs[i])) == NULL)
733*21850106SDag-Erling Smørgrav 			goto fail;
734*21850106SDag-Erling Smørgrav 		srvp->avs[i].len = len;
735*21850106SDag-Erling Smørgrav 		srvp->navs++;
736*21850106SDag-Erling Smørgrav 	}
7372c195535SJohn Polstra 	h->num_servers++;
7382c195535SJohn Polstra 	return 0;
739*21850106SDag-Erling Smørgrav fail:
740*21850106SDag-Erling Smørgrav 	memset_s(srvp->secret, strlen(srvp->secret), 0, strlen(srvp->secret));
741*21850106SDag-Erling Smørgrav 	free(srvp->secret);
742*21850106SDag-Erling Smørgrav 	srvp->secret = NULL;
743*21850106SDag-Erling Smørgrav 	for (i = 0; i < srvp->navs; i++) {
744*21850106SDag-Erling Smørgrav 		free(srvp->avs[i].data);
745*21850106SDag-Erling Smørgrav 		srvp->avs[i].data = NULL;
746*21850106SDag-Erling Smørgrav 		srvp->avs[i].len = 0;
747*21850106SDag-Erling Smørgrav 	}
748*21850106SDag-Erling Smørgrav 	return -1;
749*21850106SDag-Erling Smørgrav }
750*21850106SDag-Erling Smørgrav 
751*21850106SDag-Erling Smørgrav int
tac_add_server(struct tac_handle * h,const char * host,int port,const char * secret,int timeout,int flags)752*21850106SDag-Erling Smørgrav tac_add_server(struct tac_handle *h, const char *host, int port,
753*21850106SDag-Erling Smørgrav     const char *secret, int timeout, int flags)
754*21850106SDag-Erling Smørgrav {
755*21850106SDag-Erling Smørgrav 	const char *const *avs = { NULL };
756*21850106SDag-Erling Smørgrav 
757*21850106SDag-Erling Smørgrav 	return tac_add_server_av(h, host, port, secret, timeout, flags, avs);
7582c195535SJohn Polstra }
7592c195535SJohn Polstra 
7602c195535SJohn Polstra void
tac_close(struct tac_handle * h)7612c195535SJohn Polstra tac_close(struct tac_handle *h)
7622c195535SJohn Polstra {
7631a61aeb8SPaul Traina 	int i, srv;
7642c195535SJohn Polstra 
7652c195535SJohn Polstra 	if (h->fd != -1)
7662c195535SJohn Polstra 		close(h->fd);
7672c195535SJohn Polstra 	for (srv = 0;  srv < h->num_servers;  srv++) {
7682c195535SJohn Polstra 		memset(h->servers[srv].secret, 0,
7692c195535SJohn Polstra 		    strlen(h->servers[srv].secret));
7702c195535SJohn Polstra 		free(h->servers[srv].secret);
7712c195535SJohn Polstra 	}
7722c195535SJohn Polstra 	free_str(&h->user);
7732c195535SJohn Polstra 	free_str(&h->port);
7742c195535SJohn Polstra 	free_str(&h->rem_addr);
7752c195535SJohn Polstra 	free_str(&h->data);
7762c195535SJohn Polstra 	free_str(&h->user_msg);
7771a61aeb8SPaul Traina 	for (i=0; i<MAXAVPAIRS; i++)
7781a61aeb8SPaul Traina 		free_str(&(h->avs[i]));
7791a61aeb8SPaul Traina 
7801a61aeb8SPaul Traina 	/* Clear everything else before freeing memory */
7811a61aeb8SPaul Traina 	memset(h, 0, sizeof(struct tac_handle));
7822c195535SJohn Polstra 	free(h);
7832c195535SJohn Polstra }
7842c195535SJohn Polstra 
785*21850106SDag-Erling Smørgrav static void
freev(char ** fields,int nfields)786*21850106SDag-Erling Smørgrav freev(char **fields, int nfields)
787*21850106SDag-Erling Smørgrav {
788*21850106SDag-Erling Smørgrav 	if (fields != NULL) {
789*21850106SDag-Erling Smørgrav 		while (nfields-- > 0)
790*21850106SDag-Erling Smørgrav 			free(fields[nfields]);
791*21850106SDag-Erling Smørgrav 		free(fields);
792*21850106SDag-Erling Smørgrav 	}
793*21850106SDag-Erling Smørgrav }
794*21850106SDag-Erling Smørgrav 
7952c195535SJohn Polstra int
tac_config(struct tac_handle * h,const char * path)7962c195535SJohn Polstra tac_config(struct tac_handle *h, const char *path)
7972c195535SJohn Polstra {
7982c195535SJohn Polstra 	FILE *fp;
799*21850106SDag-Erling Smørgrav 	char **fields;
800*21850106SDag-Erling Smørgrav 	int linenum, nfields;
8012c195535SJohn Polstra 	int retval;
8022c195535SJohn Polstra 
8032c195535SJohn Polstra 	if (path == NULL)
8042c195535SJohn Polstra 		path = PATH_TACPLUS_CONF;
8052c195535SJohn Polstra 	if ((fp = fopen(path, "r")) == NULL) {
8062c195535SJohn Polstra 		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
8072c195535SJohn Polstra 		return -1;
8082c195535SJohn Polstra 	}
8092c195535SJohn Polstra 	retval = 0;
810*21850106SDag-Erling Smørgrav 	linenum = nfields = 0;
811*21850106SDag-Erling Smørgrav 	fields = NULL;
812*21850106SDag-Erling Smørgrav 	while ((fields = openpam_readlinev(fp, &linenum, &nfields)) != NULL) {
813c5159910SAndrey A. Chernov 		char *host, *res;
8142c195535SJohn Polstra 		char *port_str;
8152c195535SJohn Polstra 		char *secret;
8162c195535SJohn Polstra 		char *timeout_str;
8172c195535SJohn Polstra 		char *end;
8182c195535SJohn Polstra 		unsigned long timeout;
8192c195535SJohn Polstra 		int port;
8202c195535SJohn Polstra 		int options;
821*21850106SDag-Erling Smørgrav 		int i;
8222c195535SJohn Polstra 
823*21850106SDag-Erling Smørgrav 		if (nfields == 0) {
824*21850106SDag-Erling Smørgrav 			freev(fields, nfields);
8252c195535SJohn Polstra 			continue;
826*21850106SDag-Erling Smørgrav 		}
8272c195535SJohn Polstra 		if (nfields < 2) {
8282c195535SJohn Polstra 			generr(h, "%s:%d: missing shared secret", path,
8292c195535SJohn Polstra 			    linenum);
8302c195535SJohn Polstra 			retval = -1;
8312c195535SJohn Polstra 			break;
8322c195535SJohn Polstra 		}
8332c195535SJohn Polstra 		host = fields[0];
8342c195535SJohn Polstra 		secret = fields[1];
8352c195535SJohn Polstra 
8362c195535SJohn Polstra 		/* Parse and validate the fields. */
837c5159910SAndrey A. Chernov 		res = host;
838c5159910SAndrey A. Chernov 		host = strsep(&res, ":");
839c5159910SAndrey A. Chernov 		port_str = strsep(&res, ":");
8402c195535SJohn Polstra 		if (port_str != NULL) {
8412c195535SJohn Polstra 			port = strtoul(port_str, &end, 10);
8422c195535SJohn Polstra 			if (port_str[0] == '\0' || *end != '\0') {
8432c195535SJohn Polstra 				generr(h, "%s:%d: invalid port", path,
8442c195535SJohn Polstra 				    linenum);
8452c195535SJohn Polstra 				retval = -1;
8462c195535SJohn Polstra 				break;
8472c195535SJohn Polstra 			}
8482c195535SJohn Polstra 		} else
8492c195535SJohn Polstra 			port = 0;
850*21850106SDag-Erling Smørgrav 		i = 2;
851*21850106SDag-Erling Smørgrav 		if (nfields > i && strlen(fields[i]) > 0 &&
852*21850106SDag-Erling Smørgrav 		    strspn(fields[i], "0123456789") == strlen(fields[i])) {
853*21850106SDag-Erling Smørgrav 			timeout_str = fields[i];
8542c195535SJohn Polstra 			timeout = strtoul(timeout_str, &end, 10);
8552c195535SJohn Polstra 			if (timeout_str[0] == '\0' || *end != '\0') {
8562c195535SJohn Polstra 				generr(h, "%s:%d: invalid timeout", path,
8572c195535SJohn Polstra 				    linenum);
8582c195535SJohn Polstra 				retval = -1;
8592c195535SJohn Polstra 				break;
8602c195535SJohn Polstra 			}
861*21850106SDag-Erling Smørgrav 			i++;
8622c195535SJohn Polstra 		} else
8632c195535SJohn Polstra 			timeout = TIMEOUT;
8642c195535SJohn Polstra 		options = 0;
865*21850106SDag-Erling Smørgrav 		if (nfields > i &&
866*21850106SDag-Erling Smørgrav 		    strcmp(fields[i], "single-connection") == 0) {
8672c195535SJohn Polstra 			options |= TAC_SRVR_SINGLE_CONNECT;
868*21850106SDag-Erling Smørgrav 			i++;
8692c195535SJohn Polstra 		}
870*21850106SDag-Erling Smørgrav 		if (tac_add_server_av(h, host, port, secret, timeout,
871*21850106SDag-Erling Smørgrav 		    options, (const char *const *)(fields + i)) == -1) {
8722c195535SJohn Polstra 			char msg[ERRSIZE];
8732c195535SJohn Polstra 
8742c195535SJohn Polstra 			strcpy(msg, h->errmsg);
8752c195535SJohn Polstra 			generr(h, "%s:%d: %s", path, linenum, msg);
8762c195535SJohn Polstra 			retval = -1;
8772c195535SJohn Polstra 			break;
8782c195535SJohn Polstra 		}
879*21850106SDag-Erling Smørgrav 		memset_s(secret, strlen(secret), 0, strlen(secret));
880*21850106SDag-Erling Smørgrav 		freev(fields, nfields);
8812c195535SJohn Polstra 	}
882*21850106SDag-Erling Smørgrav 	freev(fields, nfields);
8832c195535SJohn Polstra 	fclose(fp);
8842c195535SJohn Polstra 	return retval;
8852c195535SJohn Polstra }
8862c195535SJohn Polstra 
8872c195535SJohn Polstra int
tac_create_authen(struct tac_handle * h,int action,int type,int service)8882c195535SJohn Polstra tac_create_authen(struct tac_handle *h, int action, int type, int service)
8892c195535SJohn Polstra {
8902c195535SJohn Polstra 	struct tac_authen_start *as;
8912c195535SJohn Polstra 
8921a61aeb8SPaul Traina 	create_msg(h, TAC_AUTHEN, action, type);
8932c195535SJohn Polstra 
8941a61aeb8SPaul Traina 	as = &h->request.u.authen_start;
8952c195535SJohn Polstra 	as->action = action;
8962c195535SJohn Polstra 	as->priv_lvl = TAC_PRIV_LVL_USER;
8972c195535SJohn Polstra 	as->authen_type = type;
8982c195535SJohn Polstra 	as->service = service;
8992c195535SJohn Polstra 
9001a61aeb8SPaul Traina 	return 0;
9011a61aeb8SPaul Traina }
9021a61aeb8SPaul Traina 
9031a61aeb8SPaul Traina int
tac_create_author(struct tac_handle * h,int method,int type,int service)9041a61aeb8SPaul Traina tac_create_author(struct tac_handle *h, int method, int type, int service)
9051a61aeb8SPaul Traina {
9061a61aeb8SPaul Traina 	struct tac_author_request *areq;
9071a61aeb8SPaul Traina 
9081a61aeb8SPaul Traina 	create_msg(h, TAC_AUTHOR, method, type);
9091a61aeb8SPaul Traina 
9101a61aeb8SPaul Traina 	areq = &h->request.u.author_request;
9111a61aeb8SPaul Traina 	areq->authen_meth = method;
9121a61aeb8SPaul Traina 	areq->priv_lvl = TAC_PRIV_LVL_USER;
9131a61aeb8SPaul Traina 	areq->authen_type = type;
9141a61aeb8SPaul Traina 	areq->service = service;
9151a61aeb8SPaul Traina 
9161a61aeb8SPaul Traina 	return 0;
9171a61aeb8SPaul Traina }
9181a61aeb8SPaul Traina 
919db3a20a5SShteryana Shopova int
tac_create_acct(struct tac_handle * h,int acct,int action,int type,int service)920db3a20a5SShteryana Shopova tac_create_acct(struct tac_handle *h, int acct, int action, int type, int service)
921db3a20a5SShteryana Shopova {
922db3a20a5SShteryana Shopova 	struct tac_acct_start *as;
923db3a20a5SShteryana Shopova 
924db3a20a5SShteryana Shopova 	create_msg(h, TAC_ACCT, action, type);
925db3a20a5SShteryana Shopova 
926db3a20a5SShteryana Shopova 	as = &h->request.u.acct_start;
927db3a20a5SShteryana Shopova 	as->action = acct;
928db3a20a5SShteryana Shopova 	as->authen_action = action;
929db3a20a5SShteryana Shopova 	as->priv_lvl = TAC_PRIV_LVL_USER;
930db3a20a5SShteryana Shopova 	as->authen_type = type;
931db3a20a5SShteryana Shopova 	as->authen_service = service;
932db3a20a5SShteryana Shopova 
933db3a20a5SShteryana Shopova 	return 0;
934db3a20a5SShteryana Shopova }
935db3a20a5SShteryana Shopova 
9361a61aeb8SPaul Traina static void
create_msg(struct tac_handle * h,int msg_type,int var,int type)9371a61aeb8SPaul Traina create_msg(struct tac_handle *h, int msg_type, int var, int type)
9381a61aeb8SPaul Traina {
9391a61aeb8SPaul Traina 	struct tac_msg *msg;
9401a61aeb8SPaul Traina 	int i;
9411a61aeb8SPaul Traina 
9421a61aeb8SPaul Traina 	h->last_seq_no = 0;
9431a61aeb8SPaul Traina 
9441a61aeb8SPaul Traina 	msg = &h->request;
9451a61aeb8SPaul Traina 	msg->type = msg_type;
9461a61aeb8SPaul Traina 	msg->version = protocol_version(msg_type, var, type);
9471a61aeb8SPaul Traina 	msg->flags = 0; /* encrypted packet body */
9481a61aeb8SPaul Traina 
9492c195535SJohn Polstra 	free_str(&h->user);
9502c195535SJohn Polstra 	free_str(&h->port);
9512c195535SJohn Polstra 	free_str(&h->rem_addr);
9522c195535SJohn Polstra 	free_str(&h->data);
9532c195535SJohn Polstra 	free_str(&h->user_msg);
9542c195535SJohn Polstra 
9551a61aeb8SPaul Traina 	for (i=0; i<MAXAVPAIRS; i++)
9561a61aeb8SPaul Traina 		free_str(&(h->avs[i]));
9572c195535SJohn Polstra }
9582c195535SJohn Polstra 
9592c195535SJohn Polstra void *
tac_get_data(struct tac_handle * h,size_t * len)9602c195535SJohn Polstra tac_get_data(struct tac_handle *h, size_t *len)
9612c195535SJohn Polstra {
9622c195535SJohn Polstra 	return dup_str(h, &h->srvr_data, len);
9632c195535SJohn Polstra }
9642c195535SJohn Polstra 
9652c195535SJohn Polstra char *
tac_get_msg(struct tac_handle * h)9662c195535SJohn Polstra tac_get_msg(struct tac_handle *h)
9672c195535SJohn Polstra {
9681a61aeb8SPaul Traina 	return dup_str(h, &h->srvr_msg, NULL);
9692c195535SJohn Polstra }
9702c195535SJohn Polstra 
9712c195535SJohn Polstra /*
9722c195535SJohn Polstra  * Create and initialize a tac_handle structure, and return it to the
9732c195535SJohn Polstra  * caller.  Can fail only if the necessary memory cannot be allocated.
9742c195535SJohn Polstra  * In that case, it returns NULL.
9752c195535SJohn Polstra  */
9762c195535SJohn Polstra struct tac_handle *
tac_open(void)9772c195535SJohn Polstra tac_open(void)
9782c195535SJohn Polstra {
9791a61aeb8SPaul Traina 	int i;
9802c195535SJohn Polstra 	struct tac_handle *h;
9812c195535SJohn Polstra 
9822c195535SJohn Polstra 	h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
9832c195535SJohn Polstra 	if (h != NULL) {
9842c195535SJohn Polstra 		h->fd = -1;
9852c195535SJohn Polstra 		h->num_servers = 0;
9862c195535SJohn Polstra 		h->cur_server = 0;
9872c195535SJohn Polstra 		h->errmsg[0] = '\0';
988*21850106SDag-Erling Smørgrav 		init_str(&h->user);
989*21850106SDag-Erling Smørgrav 		init_str(&h->port);
990*21850106SDag-Erling Smørgrav 		init_str(&h->rem_addr);
991*21850106SDag-Erling Smørgrav 		init_str(&h->data);
992*21850106SDag-Erling Smørgrav 		init_str(&h->user_msg);
9931a61aeb8SPaul Traina 		for (i=0; i<MAXAVPAIRS; i++) {
994*21850106SDag-Erling Smørgrav 			init_str(&(h->avs[i]));
995*21850106SDag-Erling Smørgrav 			init_str(&(h->srvr_avs[i]));
9961a61aeb8SPaul Traina 		}
997*21850106SDag-Erling Smørgrav 		init_str(&h->srvr_msg);
998*21850106SDag-Erling Smørgrav 		init_str(&h->srvr_data);
9992c195535SJohn Polstra 	}
10002c195535SJohn Polstra 	return h;
10012c195535SJohn Polstra }
10022c195535SJohn Polstra 
10032c195535SJohn Polstra int
tac_send_authen(struct tac_handle * h)10042c195535SJohn Polstra tac_send_authen(struct tac_handle *h)
10052c195535SJohn Polstra {
10062c195535SJohn Polstra 	struct tac_authen_reply *ar;
10072c195535SJohn Polstra 
10081a61aeb8SPaul Traina 	if (h->num_servers == 0)
10091a61aeb8SPaul Traina 	    return -1;
10101a61aeb8SPaul Traina 
10112c195535SJohn Polstra 	if (h->last_seq_no == 0) {	/* Authentication START packet */
10122c195535SJohn Polstra 		struct tac_authen_start *as;
10132c195535SJohn Polstra 
10142c195535SJohn Polstra 		as = &h->request.u.authen_start;
10152c195535SJohn Polstra 		h->request.length =
10162c195535SJohn Polstra 		    htonl(offsetof(struct tac_authen_start, rest[0]));
10172c195535SJohn Polstra 		if (add_str_8(h, &as->user_len, &h->user) == -1 ||
10182c195535SJohn Polstra 		    add_str_8(h, &as->port_len, &h->port) == -1 ||
10192c195535SJohn Polstra 		    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
10202c195535SJohn Polstra 		    add_str_8(h, &as->data_len, &h->data) == -1)
10212c195535SJohn Polstra 			return -1;
10222c195535SJohn Polstra 	} else {			/* Authentication CONTINUE packet */
10232c195535SJohn Polstra 		struct tac_authen_cont *ac;
10242c195535SJohn Polstra 
10252c195535SJohn Polstra 		ac = &h->request.u.authen_cont;
10262c195535SJohn Polstra 		ac->flags = 0;
10272c195535SJohn Polstra 		h->request.length =
10282c195535SJohn Polstra 		    htonl(offsetof(struct tac_authen_cont, rest[0]));
10292c195535SJohn Polstra 		if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
10302c195535SJohn Polstra 		    add_str_16(h, &ac->data_len, &h->data) == -1)
10312c195535SJohn Polstra 			return -1;
10322c195535SJohn Polstra 	}
10332c195535SJohn Polstra 
10342c195535SJohn Polstra 	/* Send the message and retrieve the reply. */
10352c195535SJohn Polstra 	if (send_msg(h) == -1 || recv_msg(h) == -1)
10362c195535SJohn Polstra 		return -1;
10372c195535SJohn Polstra 
10382c195535SJohn Polstra 	/* Scan the optional fields in the reply. */
10392c195535SJohn Polstra 	ar = &h->response.u.authen_reply;
10402c195535SJohn Polstra 	h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
1041*21850106SDag-Erling Smørgrav 	if (get_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1042*21850106SDag-Erling Smørgrav 	    get_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
10432c195535SJohn Polstra 	    get_srvr_end(h) == -1)
10442c195535SJohn Polstra 		return -1;
10452c195535SJohn Polstra 
10462c195535SJohn Polstra 	if (!h->single_connect &&
10472c195535SJohn Polstra 	    ar->status != TAC_AUTHEN_STATUS_GETDATA &&
10482c195535SJohn Polstra 	    ar->status != TAC_AUTHEN_STATUS_GETUSER &&
10492c195535SJohn Polstra 	    ar->status != TAC_AUTHEN_STATUS_GETPASS)
10502c195535SJohn Polstra 		close_connection(h);
10512c195535SJohn Polstra 
10522c195535SJohn Polstra 	return ar->flags << 8 | ar->status;
10532c195535SJohn Polstra }
10542c195535SJohn Polstra 
10552c195535SJohn Polstra int
tac_send_author(struct tac_handle * h)10561a61aeb8SPaul Traina tac_send_author(struct tac_handle *h)
10571a61aeb8SPaul Traina {
10581a61aeb8SPaul Traina 	int i, current;
10591a61aeb8SPaul Traina 	char dbgstr[64];
10601a61aeb8SPaul Traina 	struct tac_author_request *areq = &h->request.u.author_request;
10611a61aeb8SPaul Traina 	struct tac_author_response *ares = &h->response.u.author_response;
1062*21850106SDag-Erling Smørgrav 	struct tac_server *srvp;
10631a61aeb8SPaul Traina 
10641a61aeb8SPaul Traina 	h->request.length =
10651a61aeb8SPaul Traina 		htonl(offsetof(struct tac_author_request, rest[0]));
10661a61aeb8SPaul Traina 
10671a61aeb8SPaul Traina 	/* Count each specified AV pair */
10681a61aeb8SPaul Traina 	for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
10691a61aeb8SPaul Traina 		if (h->avs[i].len && h->avs[i].data)
10701a61aeb8SPaul Traina 			areq->av_cnt++;
10711a61aeb8SPaul Traina 
10721a61aeb8SPaul Traina 	/*
10731a61aeb8SPaul Traina 	 * Each AV size is a byte starting right after 'av_cnt'.  Update the
10741a61aeb8SPaul Traina 	 * offset to include these AV sizes.
10751a61aeb8SPaul Traina 	 */
10761a61aeb8SPaul Traina 	h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
10771a61aeb8SPaul Traina 
10781a61aeb8SPaul Traina 	/* Now add the string arguments from 'h' */
10791a61aeb8SPaul Traina 	if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
10801a61aeb8SPaul Traina 	    add_str_8(h, &areq->port_len, &h->port) == -1 ||
10811a61aeb8SPaul Traina 	    add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
10821a61aeb8SPaul Traina 		return -1;
10831a61aeb8SPaul Traina 
10841a61aeb8SPaul Traina 	/* Add each AV pair, the size of each placed in areq->rest[current] */
10851a61aeb8SPaul Traina 	for (current=0, i=0; i<MAXAVPAIRS; i++) {
10861a61aeb8SPaul Traina 		if (h->avs[i].len && h->avs[i].data) {
10871a61aeb8SPaul Traina 			if (add_str_8(h, &areq->rest[current++],
10881a61aeb8SPaul Traina 				      &(h->avs[i])) == -1)
10891a61aeb8SPaul Traina 				return -1;
10901a61aeb8SPaul Traina 		}
10911a61aeb8SPaul Traina 	}
10921a61aeb8SPaul Traina 
10931a61aeb8SPaul Traina 	/* Send the message and retrieve the reply. */
10941a61aeb8SPaul Traina 	if (send_msg(h) == -1 || recv_msg(h) == -1)
10951a61aeb8SPaul Traina 		return -1;
1096*21850106SDag-Erling Smørgrav 	srvp = &h->servers[h->cur_server];
10971a61aeb8SPaul Traina 
10981a61aeb8SPaul Traina 	/* Update the offset in the response packet based on av pairs count */
10991a61aeb8SPaul Traina 	h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
11001a61aeb8SPaul Traina 		ares->av_cnt;
11011a61aeb8SPaul Traina 
11021a61aeb8SPaul Traina 	/* Scan the optional fields in the response. */
1103*21850106SDag-Erling Smørgrav 	if (get_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
1104*21850106SDag-Erling Smørgrav 	    get_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
11051a61aeb8SPaul Traina 		return -1;
11061a61aeb8SPaul Traina 
11071a61aeb8SPaul Traina 	/* Get each AV pair (just setting pointers, not malloc'ing) */
11081a61aeb8SPaul Traina 	clear_srvr_avs(h);
11091a61aeb8SPaul Traina 	for (i=0; i<ares->av_cnt; i++) {
11101a61aeb8SPaul Traina 		snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
1111*21850106SDag-Erling Smørgrav 		if (get_str(h, dbgstr, &(h->srvr_avs[i]),
11121a61aeb8SPaul Traina 				 ares->rest[i]) == -1)
11131a61aeb8SPaul Traina 			return -1;
1114*21850106SDag-Erling Smørgrav 		h->srvr_navs++;
11151a61aeb8SPaul Traina 	}
11161a61aeb8SPaul Traina 
11171a61aeb8SPaul Traina 	/* Should have ended up at the end */
11181a61aeb8SPaul Traina 	if (get_srvr_end(h) == -1)
11191a61aeb8SPaul Traina 		return -1;
11201a61aeb8SPaul Traina 
11211a61aeb8SPaul Traina 	/* Sanity checks */
11221a61aeb8SPaul Traina 	if (!h->single_connect)
11231a61aeb8SPaul Traina 		close_connection(h);
11241a61aeb8SPaul Traina 
1125*21850106SDag-Erling Smørgrav 	return (h->srvr_navs + srvp->navs) << 8 | ares->status;
11261a61aeb8SPaul Traina }
11271a61aeb8SPaul Traina 
11281a61aeb8SPaul Traina int
tac_send_acct(struct tac_handle * h)1129db3a20a5SShteryana Shopova tac_send_acct(struct tac_handle *h)
1130db3a20a5SShteryana Shopova {
1131db3a20a5SShteryana Shopova 	register int i, current;
1132db3a20a5SShteryana Shopova 	struct tac_acct_start *as = &h->request.u.acct_start;
1133db3a20a5SShteryana Shopova 	struct tac_acct_reply *ar = &h->response.u.acct_reply;
1134db3a20a5SShteryana Shopova 
1135db3a20a5SShteryana Shopova 	/* start */
1136db3a20a5SShteryana Shopova 	as = &h->request.u.acct_start;
1137db3a20a5SShteryana Shopova 	h->request.length = htonl(offsetof(struct tac_acct_start, rest[0]));
1138db3a20a5SShteryana Shopova 	for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++)
1139db3a20a5SShteryana Shopova 		if (h->avs[i].len && h->avs[i].data)
1140db3a20a5SShteryana Shopova 			as->av_cnt++;
1141db3a20a5SShteryana Shopova 	h->request.length = ntohl(htonl(h->request.length) + as->av_cnt);
1142db3a20a5SShteryana Shopova 
1143db3a20a5SShteryana Shopova 	if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1144db3a20a5SShteryana Shopova 	    add_str_8(h, &as->port_len, &h->port) == -1 ||
1145db3a20a5SShteryana Shopova 	    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1)
1146db3a20a5SShteryana Shopova 		return -1;
1147db3a20a5SShteryana Shopova 
1148db3a20a5SShteryana Shopova 	for (i = current = 0; i < MAXAVPAIRS; i++)
1149db3a20a5SShteryana Shopova 		if (h->avs[i].len && h->avs[i].data)
1150db3a20a5SShteryana Shopova 			if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1)
1151db3a20a5SShteryana Shopova 				return -1;
1152db3a20a5SShteryana Shopova 
1153db3a20a5SShteryana Shopova 	/* send */
1154db3a20a5SShteryana Shopova 	if (send_msg(h) == -1 || recv_msg(h) == -1)
1155db3a20a5SShteryana Shopova 		return -1;
1156db3a20a5SShteryana Shopova 
1157db3a20a5SShteryana Shopova 	/* reply */
1158db3a20a5SShteryana Shopova 	h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]);
1159*21850106SDag-Erling Smørgrav 	if (get_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1160*21850106SDag-Erling Smørgrav 	    get_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1161db3a20a5SShteryana Shopova 	    get_srvr_end(h) == -1)
1162db3a20a5SShteryana Shopova 		return -1;
1163db3a20a5SShteryana Shopova 
1164db3a20a5SShteryana Shopova 	/* Sanity checks */
1165db3a20a5SShteryana Shopova 	if (!h->single_connect)
1166db3a20a5SShteryana Shopova 		close_connection(h);
1167db3a20a5SShteryana Shopova 
1168db3a20a5SShteryana Shopova 	return ar->status;
1169db3a20a5SShteryana Shopova }
1170db3a20a5SShteryana Shopova 
1171db3a20a5SShteryana Shopova int
tac_set_rem_addr(struct tac_handle * h,const char * addr)11722c195535SJohn Polstra tac_set_rem_addr(struct tac_handle *h, const char *addr)
11732c195535SJohn Polstra {
11742c195535SJohn Polstra 	return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
11752c195535SJohn Polstra }
11762c195535SJohn Polstra 
11772c195535SJohn Polstra int
tac_set_data(struct tac_handle * h,const void * data,size_t data_len)11782c195535SJohn Polstra tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
11792c195535SJohn Polstra {
11802c195535SJohn Polstra 	return save_str(h, &h->data, data, data_len);
11812c195535SJohn Polstra }
11822c195535SJohn Polstra 
11832c195535SJohn Polstra int
tac_set_msg(struct tac_handle * h,const char * msg)11842c195535SJohn Polstra tac_set_msg(struct tac_handle *h, const char *msg)
11852c195535SJohn Polstra {
11862c195535SJohn Polstra 	return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
11872c195535SJohn Polstra }
11882c195535SJohn Polstra 
11892c195535SJohn Polstra int
tac_set_port(struct tac_handle * h,const char * port)11902c195535SJohn Polstra tac_set_port(struct tac_handle *h, const char *port)
11912c195535SJohn Polstra {
11922c195535SJohn Polstra 	return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
11932c195535SJohn Polstra }
11942c195535SJohn Polstra 
11952c195535SJohn Polstra int
tac_set_priv(struct tac_handle * h,int priv)11962c195535SJohn Polstra tac_set_priv(struct tac_handle *h, int priv)
11972c195535SJohn Polstra {
11982c195535SJohn Polstra 	if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
11992c195535SJohn Polstra 		generr(h, "Attempt to set invalid privilege level");
12002c195535SJohn Polstra 		return -1;
12012c195535SJohn Polstra 	}
12022c195535SJohn Polstra 	h->request.u.authen_start.priv_lvl = priv;
12032c195535SJohn Polstra 	return 0;
12042c195535SJohn Polstra }
12052c195535SJohn Polstra 
12062c195535SJohn Polstra int
tac_set_user(struct tac_handle * h,const char * user)12072c195535SJohn Polstra tac_set_user(struct tac_handle *h, const char *user)
12082c195535SJohn Polstra {
12092c195535SJohn Polstra 	return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
12102c195535SJohn Polstra }
12112c195535SJohn Polstra 
12121a61aeb8SPaul Traina int
tac_set_av(struct tac_handle * h,u_int index,const char * av)12131a61aeb8SPaul Traina tac_set_av(struct tac_handle *h, u_int index, const char *av)
12141a61aeb8SPaul Traina {
12151a61aeb8SPaul Traina 	if (index >= MAXAVPAIRS)
12161a61aeb8SPaul Traina 		return -1;
12171a61aeb8SPaul Traina 	return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
12181a61aeb8SPaul Traina }
12191a61aeb8SPaul Traina 
12201a61aeb8SPaul Traina char *
tac_get_av(struct tac_handle * h,u_int index)12211a61aeb8SPaul Traina tac_get_av(struct tac_handle *h, u_int index)
12221a61aeb8SPaul Traina {
1223*21850106SDag-Erling Smørgrav 	struct tac_server *srvp;
1224*21850106SDag-Erling Smørgrav 
1225*21850106SDag-Erling Smørgrav 	if (index < h->srvr_navs)
1226*21850106SDag-Erling Smørgrav 		return dup_str(h, &h->srvr_avs[index], NULL);
1227*21850106SDag-Erling Smørgrav 	index -= h->srvr_navs;
1228*21850106SDag-Erling Smørgrav 	srvp = &h->servers[h->cur_server];
1229*21850106SDag-Erling Smørgrav 	if (index < srvp->navs)
1230*21850106SDag-Erling Smørgrav 		return xstrdup(h, srvp->avs[index].data);
12311a61aeb8SPaul Traina 	return NULL;
12321a61aeb8SPaul Traina }
12331a61aeb8SPaul Traina 
12341a61aeb8SPaul Traina char *
tac_get_av_value(struct tac_handle * h,const char * attribute)12351a61aeb8SPaul Traina tac_get_av_value(struct tac_handle *h, const char *attribute)
12361a61aeb8SPaul Traina {
1237*21850106SDag-Erling Smørgrav 	int i, attr_len;
12381a61aeb8SPaul Traina 	int   found_seperator;
1239*21850106SDag-Erling Smørgrav 	char *ch, *end;
1240*21850106SDag-Erling Smørgrav 	struct tac_str *candidate;
1241*21850106SDag-Erling Smørgrav 	struct tac_str value;
1242*21850106SDag-Erling Smørgrav 	struct tac_server *srvp = &h->servers[h->cur_server];
12431a61aeb8SPaul Traina 
1244*21850106SDag-Erling Smørgrav 	if (attribute == NULL || (attr_len = strlen(attribute)) == 0)
12451a61aeb8SPaul Traina 		return NULL;
12461a61aeb8SPaul Traina 
1247*21850106SDag-Erling Smørgrav 	for (i = 0; i < h->srvr_navs + srvp->navs; i++) {
1248*21850106SDag-Erling Smørgrav 		if (i < h->srvr_navs)
1249*21850106SDag-Erling Smørgrav 			candidate = &h->srvr_avs[i];
1250*21850106SDag-Erling Smørgrav 		else
1251*21850106SDag-Erling Smørgrav 			candidate = &srvp->avs[i - h->srvr_navs];
12521a61aeb8SPaul Traina 
1253*21850106SDag-Erling Smørgrav 		if (attr_len < candidate->len &&
1254*21850106SDag-Erling Smørgrav 		    strncmp(candidate->data, attribute, attr_len) == 0) {
12551a61aeb8SPaul Traina 
1256*21850106SDag-Erling Smørgrav 			ch = candidate->data + attr_len;
1257*21850106SDag-Erling Smørgrav 			end = candidate->data + candidate->len;
12581a61aeb8SPaul Traina 
12591a61aeb8SPaul Traina 			/*
12601a61aeb8SPaul Traina 			 * Sift out the white space between A and V (should not
12611a61aeb8SPaul Traina 			 * be any, but don't trust implementation of server...)
12621a61aeb8SPaul Traina 			 */
12631a61aeb8SPaul Traina 			found_seperator = 0;
12641a61aeb8SPaul Traina 			while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
12651a61aeb8SPaul Traina 				*ch == '\t') && ch != end) {
12661a61aeb8SPaul Traina 				if (*ch == '=' || *ch == '*')
12671a61aeb8SPaul Traina 					found_seperator++;
12681a61aeb8SPaul Traina 				ch++;
12691a61aeb8SPaul Traina 			}
12701a61aeb8SPaul Traina 
12711a61aeb8SPaul Traina 			/*
12721a61aeb8SPaul Traina 			 * Note:
12731a61aeb8SPaul Traina 			 *     The case of 'attribute' == "foo" and
12741a61aeb8SPaul Traina 			 *     h->srvr_avs[0] = "foobie=var1"
12751a61aeb8SPaul Traina 			 *     h->srvr_avs[1] = "foo=var2"
12761a61aeb8SPaul Traina 			 * is handled.
12775e3d7b09SAttilio Rao 			 *
12785e3d7b09SAttilio Rao 			 * Note that for empty string attribute values a
12795e3d7b09SAttilio Rao 			 * 0-length string is returned in order to distinguish
12805e3d7b09SAttilio Rao 			 * against unset values.
1281bc7622b7SEd Maste 			 * dup_str() will handle srvr.len == 0 correctly.
12821a61aeb8SPaul Traina 			 */
12835e3d7b09SAttilio Rao 			if (found_seperator == 1) {
1284*21850106SDag-Erling Smørgrav 				value.len = end - ch;
1285*21850106SDag-Erling Smørgrav 				value.data = ch;
1286*21850106SDag-Erling Smørgrav 				return dup_str(h, &value, NULL);
12871a61aeb8SPaul Traina 			}
12881a61aeb8SPaul Traina 		}
12891a61aeb8SPaul Traina 	}
12901a61aeb8SPaul Traina 	return NULL;
12911a61aeb8SPaul Traina }
12921a61aeb8SPaul Traina 
12931a61aeb8SPaul Traina void
tac_clear_avs(struct tac_handle * h)12941a61aeb8SPaul Traina tac_clear_avs(struct tac_handle *h)
12951a61aeb8SPaul Traina {
12961a61aeb8SPaul Traina 	int i;
12971a61aeb8SPaul Traina 	for (i=0; i<MAXAVPAIRS; i++)
12981a61aeb8SPaul Traina 		save_str(h, &(h->avs[i]), NULL, 0);
12991a61aeb8SPaul Traina }
13001a61aeb8SPaul Traina 
13011a61aeb8SPaul Traina static void
clear_srvr_avs(struct tac_handle * h)13021a61aeb8SPaul Traina clear_srvr_avs(struct tac_handle *h)
13031a61aeb8SPaul Traina {
13041a61aeb8SPaul Traina 	int i;
1305*21850106SDag-Erling Smørgrav 
1306*21850106SDag-Erling Smørgrav 	for (i = 0; i < h->srvr_navs; i++)
1307*21850106SDag-Erling Smørgrav 		init_str(&(h->srvr_avs[i]));
1308*21850106SDag-Erling Smørgrav 	h->srvr_navs = 0;
13091a61aeb8SPaul Traina }
13101a61aeb8SPaul Traina 
13111a61aeb8SPaul Traina 
13122c195535SJohn Polstra const char *
tac_strerror(struct tac_handle * h)13132c195535SJohn Polstra tac_strerror(struct tac_handle *h)
13142c195535SJohn Polstra {
13152c195535SJohn Polstra 	return h->errmsg;
13162c195535SJohn Polstra }
13172c195535SJohn Polstra 
13182c195535SJohn Polstra static void *
xmalloc(struct tac_handle * h,size_t size)13192c195535SJohn Polstra xmalloc(struct tac_handle *h, size_t size)
13202c195535SJohn Polstra {
13212c195535SJohn Polstra 	void *r;
13222c195535SJohn Polstra 
13232c195535SJohn Polstra 	if ((r = malloc(size)) == NULL)
13242c195535SJohn Polstra 		generr(h, "Out of memory");
13252c195535SJohn Polstra 	return r;
13262c195535SJohn Polstra }
13272c195535SJohn Polstra 
13282c195535SJohn Polstra static char *
xstrdup(struct tac_handle * h,const char * s)13292c195535SJohn Polstra xstrdup(struct tac_handle *h, const char *s)
13302c195535SJohn Polstra {
13312c195535SJohn Polstra 	char *r;
13322c195535SJohn Polstra 
13332c195535SJohn Polstra 	if ((r = strdup(s)) == NULL)
13342c195535SJohn Polstra 		generr(h, "Out of memory");
13352c195535SJohn Polstra 	return r;
13362c195535SJohn Polstra }
1337