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