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