taclib.c revision 84222
141120Sjdp/*-
241120Sjdp * Copyright 1998 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: head/lib/libtacplus/taclib.c 84222 2001-09-30 22:29:19Z dillon $");
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 *);
5441120Sjdpstatic int		 authen_version(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 *);
6641120Sjdpstatic int		 get_srvr_str(struct tac_handle *, struct srvr_str *,
6741120Sjdp			    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 *);
7941120Sjdp
8041120Sjdp/*
8141120Sjdp * Append some optional data to the current request, and store its
8241120Sjdp * length into the 8-bit field referenced by "fld".  Returns 0 on
8341120Sjdp * success, or -1 on failure.
8441120Sjdp *
8541120Sjdp * This function also frees the "cs" string data and initializes it
8641120Sjdp * for the next time.
8741120Sjdp */
8841120Sjdpstatic int
8941120Sjdpadd_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
9041120Sjdp{
9141120Sjdp	u_int16_t len;
9241120Sjdp
9341120Sjdp	if (add_str_16(h, &len, cs) == -1)
9441120Sjdp		return -1;
9541120Sjdp	len = ntohs(len);
9641120Sjdp	if (len > 0xff) {
9741120Sjdp		generr(h, "Field too long");
9841120Sjdp		return -1;
9941120Sjdp	}
10041120Sjdp	*fld = len;
10141120Sjdp	return 0;
10241120Sjdp}
10341120Sjdp
10441120Sjdp/*
10541120Sjdp * Append some optional data to the current request, and store its
10641120Sjdp * length into the 16-bit field (network byte order) referenced by
10741120Sjdp * "fld".  Returns 0 on success, or -1 on failure.
10841120Sjdp *
10941120Sjdp * This function also frees the "cs" string data and initializes it
11041120Sjdp * for the next time.
11141120Sjdp */
11241120Sjdpstatic int
11341120Sjdpadd_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
11441120Sjdp{
11541120Sjdp	size_t len;
11641120Sjdp
11741120Sjdp	len = cs->len;
11841120Sjdp	if (cs->data == NULL)
11941120Sjdp		len = 0;
12041120Sjdp	if (len != 0) {
12141120Sjdp		int offset;
12241120Sjdp
12341120Sjdp		if (len > 0xffff) {
12441120Sjdp			generr(h, "Field too long");
12541120Sjdp			return -1;
12641120Sjdp		}
12741120Sjdp		offset = ntohl(h->request.length);
12841120Sjdp		if (offset + len > BODYSIZE) {
12941120Sjdp			generr(h, "Message too long");
13041120Sjdp			return -1;
13141120Sjdp		}
13241120Sjdp		memcpy(h->request.u.body + offset, cs->data, len);
13341120Sjdp		h->request.length = htonl(offset + len);
13441120Sjdp	}
13541120Sjdp	*fld = htons(len);
13641120Sjdp	free_str(cs);
13741120Sjdp	return 0;
13841120Sjdp}
13941120Sjdp
14041120Sjdpstatic int
14141120Sjdpauthen_version(int action, int type)
14241120Sjdp{
14341120Sjdp	int minor;
14441120Sjdp
14541120Sjdp	switch (action) {
14641120Sjdp
14741120Sjdp	case TAC_AUTHEN_LOGIN:
14841120Sjdp		switch (type) {
14941120Sjdp
15041120Sjdp		case TAC_AUTHEN_TYPE_PAP:
15141120Sjdp		case TAC_AUTHEN_TYPE_CHAP:
15241120Sjdp		case TAC_AUTHEN_TYPE_MSCHAP:
15341120Sjdp		case TAC_AUTHEN_TYPE_ARAP:
15441120Sjdp			minor = 1;
15541120Sjdp			break;
15641120Sjdp
15741120Sjdp		default:
15841120Sjdp			minor = 0;
15941120Sjdp			break;
16041120Sjdp		}
16141120Sjdp		break;
16241120Sjdp
16341120Sjdp	case TAC_AUTHEN_SENDAUTH:
16441120Sjdp		minor = 1;
16541120Sjdp		break;
16641120Sjdp
16741120Sjdp	default:
16841120Sjdp		minor = 0;
16941120Sjdp		break;
17041120Sjdp	};
17141120Sjdp
17241120Sjdp	return TAC_VER_MAJOR << 4 | minor;
17341120Sjdp}
17441120Sjdp
17541120Sjdpstatic void
17641120Sjdpclose_connection(struct tac_handle *h)
17741120Sjdp{
17841120Sjdp	if (h->fd != -1) {
17941120Sjdp		close(h->fd);
18041120Sjdp		h->fd = -1;
18141120Sjdp	}
18241120Sjdp}
18341120Sjdp
18441120Sjdpstatic int
18541120Sjdpconn_server(struct tac_handle *h)
18641120Sjdp{
18741120Sjdp	const struct tac_server *srvp = &h->servers[h->cur_server];
18841120Sjdp	int flags;
18941120Sjdp
19041120Sjdp	if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
19141120Sjdp		generr(h, "Cannot create socket: %s", strerror(errno));
19241120Sjdp		return -1;
19341120Sjdp	}
19441120Sjdp	if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
19541120Sjdp	    fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
19641120Sjdp		generr(h, "Cannot set non-blocking mode on socket: %s",
19741120Sjdp		    strerror(errno));
19841120Sjdp		close(h->fd);
19941120Sjdp		h->fd = -1;
20041120Sjdp		return -1;
20141120Sjdp	}
20241120Sjdp	if (connect(h->fd, (struct sockaddr *)&srvp->addr,
20341120Sjdp	    sizeof srvp->addr) == 0)
20441120Sjdp		return 0;
20541120Sjdp
20641120Sjdp	if (errno == EINPROGRESS) {
20741120Sjdp		fd_set wfds;
20841120Sjdp		struct timeval tv;
20941120Sjdp		int nfds;
21041120Sjdp		struct sockaddr peer;
21141120Sjdp		int peerlen;
21241120Sjdp		int err;
21341120Sjdp		int errlen;
21441120Sjdp
21541120Sjdp		/* Wait for the connection to complete. */
21641120Sjdp		FD_ZERO(&wfds);
21741120Sjdp		FD_SET(h->fd, &wfds);
21841120Sjdp		tv.tv_sec = srvp->timeout;
21941120Sjdp		tv.tv_usec = 0;
22041120Sjdp		nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
22141120Sjdp		if (nfds == -1) {
22241120Sjdp			generr(h, "select: %s", strerror(errno));
22341120Sjdp			close(h->fd);
22441120Sjdp			h->fd = -1;
22541120Sjdp			return -1;
22641120Sjdp		}
22741120Sjdp		if (nfds == 0) {
22841120Sjdp			generr(h, "connect: timed out");
22941120Sjdp			close(h->fd);
23041120Sjdp			h->fd = -1;
23141120Sjdp			return -1;
23241120Sjdp		}
23341120Sjdp
23441120Sjdp		/* See whether we are connected now. */
23541120Sjdp		peerlen = sizeof peer;
23641120Sjdp		if (getpeername(h->fd, &peer, &peerlen) == 0)
23741120Sjdp			return 0;
23841120Sjdp
23941120Sjdp		if (errno != ENOTCONN) {
24041120Sjdp			generr(h, "getpeername: %s", strerror(errno));
24141120Sjdp			close(h->fd);
24241120Sjdp			h->fd = -1;
24341120Sjdp			return -1;
24441120Sjdp		}
24541120Sjdp
24641120Sjdp		/* Find out why the connect failed. */
24741120Sjdp		errlen = sizeof err;
24841120Sjdp		getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
24941120Sjdp		errno = err;
25041120Sjdp	}
25141120Sjdp	generr(h, "connect: %s", strerror(errno));
25241120Sjdp	close(h->fd);
25341120Sjdp	h->fd = -1;
25441120Sjdp	return -1;
25541120Sjdp}
25641120Sjdp
25741120Sjdp/*
25841120Sjdp * Encrypt or decrypt a message.  The operations are symmetrical.
25941120Sjdp */
26041120Sjdpstatic void
26141120Sjdpcrypt_msg(struct tac_handle *h, struct tac_msg *msg)
26241120Sjdp{
26341120Sjdp	const char *secret;
26441120Sjdp	MD5_CTX base_ctx;
26541120Sjdp	MD5_CTX ctx;
26641120Sjdp	unsigned char md5[16];
26741120Sjdp	int chunk;
26841120Sjdp	int msg_len;
26941120Sjdp
27041120Sjdp	secret = h->servers[h->cur_server].secret;
27141120Sjdp	if (secret[0] == '\0')
27241120Sjdp		msg->flags |= TAC_UNENCRYPTED;
27341120Sjdp	if (msg->flags & TAC_UNENCRYPTED)
27441120Sjdp		return;
27541120Sjdp
27641120Sjdp	msg_len = ntohl(msg->length);
27741120Sjdp
27841120Sjdp	MD5Init(&base_ctx);
27941120Sjdp	MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
28041120Sjdp	MD5Update(&base_ctx, secret, strlen(secret));
28141120Sjdp	MD5Update(&base_ctx, &msg->version, sizeof msg->version);
28241120Sjdp	MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
28341120Sjdp
28441120Sjdp	ctx = base_ctx;
28541120Sjdp	for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
28641120Sjdp		int chunk_len;
28741120Sjdp		int i;
28841120Sjdp
28941120Sjdp		MD5Final(md5, &ctx);
29041120Sjdp
29141120Sjdp		if ((chunk_len = msg_len - chunk) > sizeof md5)
29241120Sjdp			chunk_len = sizeof md5;
29341120Sjdp		for (i = 0;  i < chunk_len;  i++)
29441120Sjdp			msg->u.body[chunk + i] ^= md5[i];
29541120Sjdp
29641120Sjdp		ctx = base_ctx;
29741120Sjdp		MD5Update(&ctx, md5, sizeof md5);
29841120Sjdp	}
29941120Sjdp}
30041120Sjdp
30141120Sjdp/*
30241120Sjdp * Return a dynamically allocated copy of the given server string.
30341120Sjdp * The copy is null-terminated.  If "len" is non-NULL, the length of
30441120Sjdp * the string (excluding the terminating null byte) is stored via it.
30541120Sjdp * Returns NULL on failure.  Empty strings are still allocated even
30641120Sjdp * though they have no content.
30741120Sjdp */
30841120Sjdpstatic void *
30941120Sjdpdup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
31041120Sjdp{
31141120Sjdp	unsigned char *p;
31241120Sjdp
31341120Sjdp	if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
31441120Sjdp		return NULL;
31541120Sjdp	if (ss->data != NULL && ss->len != 0)
31641120Sjdp		memcpy(p, ss->data, ss->len);
31741120Sjdp	p[ss->len] = '\0';
31841120Sjdp	if (len != NULL)
31941120Sjdp		*len = ss->len;
32041120Sjdp	return p;
32141120Sjdp}
32241120Sjdp
32341120Sjdpstatic int
32441120Sjdpestablish_connection(struct tac_handle *h)
32541120Sjdp{
32641120Sjdp	int i;
32741120Sjdp
32841120Sjdp	if (h->fd >= 0)		/* Already connected. */
32941120Sjdp		return 0;
33041120Sjdp	if (h->num_servers == 0) {
33141120Sjdp		generr(h, "No TACACS+ servers specified");
33241120Sjdp		return -1;
33341120Sjdp	}
33441120Sjdp	/*
33541120Sjdp         * Try the servers round-robin.  We begin with the one that
33641120Sjdp         * worked for us the last time.  That way, once we find a good
33741120Sjdp         * server, we won't waste any more time trying the bad ones.
33841120Sjdp	 */
33941120Sjdp	for (i = 0;  i < h->num_servers;  i++) {
34041120Sjdp		if (conn_server(h) == 0) {
34141120Sjdp			h->single_connect = (h->servers[h->cur_server].flags &
34241120Sjdp			    TAC_SRVR_SINGLE_CONNECT) != 0;
34341120Sjdp			return 0;
34441120Sjdp		}
34541120Sjdp		if (++h->cur_server >= h->num_servers)	/* Wrap around */
34641120Sjdp			h->cur_server = 0;
34741120Sjdp	}
34841120Sjdp	/* Just return whatever error was last reported by conn_server(). */
34941120Sjdp	return -1;
35041120Sjdp}
35141120Sjdp
35241120Sjdp/*
35341120Sjdp * Free a client string, obliterating its contents first for security.
35441120Sjdp */
35541120Sjdpstatic void
35641120Sjdpfree_str(struct clnt_str *cs)
35741120Sjdp{
35841120Sjdp	if (cs->data != NULL) {
35941120Sjdp		memset(cs->data, 0, cs->len);
36041120Sjdp		free(cs->data);
36141120Sjdp		cs->data = NULL;
36241120Sjdp		cs->len = 0;
36341120Sjdp	}
36441120Sjdp}
36541120Sjdp
36641120Sjdpstatic void
36741120Sjdpgenerr(struct tac_handle *h, const char *format, ...)
36841120Sjdp{
36941120Sjdp	va_list		 ap;
37041120Sjdp
37141120Sjdp	va_start(ap, format);
37241120Sjdp	vsnprintf(h->errmsg, ERRSIZE, format, ap);
37341120Sjdp	va_end(ap);
37441120Sjdp}
37541120Sjdp
37641120Sjdpstatic void
37741120Sjdpgen_session_id(struct tac_msg *msg)
37841120Sjdp{
37941120Sjdp	int r;
38041120Sjdp
38141120Sjdp	r = random();
38241120Sjdp	msg->session_id[0] = r >> 8;
38341120Sjdp	msg->session_id[1] = r;
38441120Sjdp	r = random();
38541120Sjdp	msg->session_id[2] = r >> 8;
38641120Sjdp	msg->session_id[3] = r;
38741120Sjdp}
38841120Sjdp
38941120Sjdp/*
39041120Sjdp * Verify that we are exactly at the end of the response message.
39141120Sjdp * Returns 0 on success, -1 on failure.
39241120Sjdp */
39341120Sjdpstatic int
39441120Sjdpget_srvr_end(struct tac_handle *h)
39541120Sjdp{
39641120Sjdp	if (h->srvr_pos != ntohl(h->response.length)) {
39741120Sjdp		generr(h, "Invalid length field in response from server");
39841120Sjdp		return -1;
39941120Sjdp	}
40041120Sjdp	return 0;
40141120Sjdp}
40241120Sjdp
40341120Sjdpstatic int
40441120Sjdpget_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len)
40541120Sjdp{
40641120Sjdp	if (h->srvr_pos + len > ntohl(h->response.length)) {
40741120Sjdp		generr(h, "Invalid length field in response from server");
40841120Sjdp		return -1;
40941120Sjdp	}
41041120Sjdp	ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
41141120Sjdp	ss->len = len;
41241120Sjdp	h->srvr_pos += len;
41341120Sjdp	return 0;
41441120Sjdp}
41541120Sjdp
41641120Sjdpstatic void
41741120Sjdpinit_clnt_str(struct clnt_str *cs)
41841120Sjdp{
41941120Sjdp	cs->data = NULL;
42041120Sjdp	cs->len = 0;
42141120Sjdp}
42241120Sjdp
42341120Sjdpstatic void
42441120Sjdpinit_srvr_str(struct srvr_str *ss)
42541120Sjdp{
42641120Sjdp	ss->data = NULL;
42741120Sjdp	ss->len = 0;
42841120Sjdp}
42941120Sjdp
43041120Sjdpstatic int
43141120Sjdpread_timed(struct tac_handle *h, void *buf, size_t len,
43241120Sjdp    const struct timeval *deadline)
43341120Sjdp{
43441120Sjdp	char *ptr;
43541120Sjdp
43641120Sjdp	ptr = (char *)buf;
43741120Sjdp	while (len > 0) {
43841120Sjdp		int n;
43941120Sjdp
44041120Sjdp		n = read(h->fd, ptr, len);
44141120Sjdp		if (n == -1) {
44241120Sjdp			struct timeval tv;
44341120Sjdp			int nfds;
44441120Sjdp
44541120Sjdp			if (errno != EAGAIN) {
44641120Sjdp				generr(h, "Network read error: %s",
44741120Sjdp				    strerror(errno));
44841120Sjdp				return -1;
44941120Sjdp			}
45041120Sjdp
45141120Sjdp			/* Wait until we can read more data. */
45241120Sjdp			gettimeofday(&tv, NULL);
45341120Sjdp			timersub(deadline, &tv, &tv);
45441120Sjdp			if (tv.tv_sec >= 0) {
45541120Sjdp				fd_set rfds;
45641120Sjdp
45741120Sjdp				FD_ZERO(&rfds);
45841120Sjdp				FD_SET(h->fd, &rfds);
45941120Sjdp				nfds =
46041120Sjdp				    select(h->fd + 1, &rfds, NULL, NULL, &tv);
46141120Sjdp				if (nfds == -1) {
46241120Sjdp					generr(h, "select: %s",
46341120Sjdp					    strerror(errno));
46441120Sjdp					return -1;
46541120Sjdp				}
46641120Sjdp			} else
46741120Sjdp				nfds = 0;
46841120Sjdp			if (nfds == 0) {
46941120Sjdp				generr(h, "Network read timed out");
47041120Sjdp				return -1;
47141120Sjdp			}
47241120Sjdp		} else if (n == 0) {
47341120Sjdp			generr(h, "unexpected EOF from server");
47441120Sjdp			return -1;
47541120Sjdp		} else {
47641120Sjdp			ptr += n;
47741120Sjdp			len -= n;
47841120Sjdp		}
47941120Sjdp	}
48041120Sjdp	return 0;
48141120Sjdp}
48241120Sjdp
48341120Sjdp/*
48441120Sjdp * Receive a response from the server and decrypt it.  Returns 0 on
48541120Sjdp * success, or -1 on failure.
48641120Sjdp */
48741120Sjdpstatic int
48841120Sjdprecv_msg(struct tac_handle *h)
48941120Sjdp{
49041120Sjdp	struct timeval deadline;
49141120Sjdp	struct tac_msg *msg;
49241120Sjdp	size_t len;
49341120Sjdp
49441120Sjdp	msg = &h->response;
49541120Sjdp	gettimeofday(&deadline, NULL);
49641120Sjdp	deadline.tv_sec += h->servers[h->cur_server].timeout;
49741120Sjdp
49841120Sjdp	/* Read the message header and make sure it is reasonable. */
49941120Sjdp	if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
50041120Sjdp		return -1;
50141120Sjdp	if (memcmp(msg->session_id, h->request.session_id,
50241120Sjdp	    sizeof msg->session_id) != 0) {
50341120Sjdp		generr(h, "Invalid session ID in received message");
50441120Sjdp		return -1;
50541120Sjdp	}
50641120Sjdp	if (msg->type != h->request.type) {
50741120Sjdp		generr(h, "Invalid type in received message");
50841120Sjdp		return -1;
50941120Sjdp	}
51041120Sjdp	len = ntohl(msg->length);
51141120Sjdp	if (len > BODYSIZE) {
51241120Sjdp		generr(h, "Received message too large");
51341120Sjdp		return -1;
51441120Sjdp	}
51541120Sjdp	if (msg->seq_no != ++h->last_seq_no) {
51641120Sjdp		generr(h, "Invalid sequence number in received message");
51741120Sjdp		return -1;
51841120Sjdp	}
51941120Sjdp
52041120Sjdp	/* Read the message body. */
52141120Sjdp	if (read_timed(h, msg->u.body, len, &deadline) == -1)
52241120Sjdp		return -1;
52341120Sjdp
52441120Sjdp	/* Decrypt it. */
52541120Sjdp	crypt_msg(h, msg);
52641120Sjdp
52741120Sjdp	/*
52841120Sjdp	 * Turn off single-connection mode if the server isn't amenable
52941120Sjdp	 * to it.
53041120Sjdp	 */
53141120Sjdp	if (!(msg->flags & TAC_SINGLE_CONNECT))
53241120Sjdp		h->single_connect = 0;
53341120Sjdp	return 0;
53441120Sjdp}
53541120Sjdp
53641120Sjdpstatic int
53741120Sjdpsave_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
53841120Sjdp    size_t len)
53941120Sjdp{
54041120Sjdp	free_str(cs);
54141120Sjdp	if (data != NULL && len != 0) {
54241120Sjdp		if ((cs->data = xmalloc(h, len)) == NULL)
54341120Sjdp			return -1;
54441120Sjdp		cs->len = len;
54541120Sjdp		memcpy(cs->data, data, len);
54641120Sjdp	}
54741120Sjdp	return 0;
54841120Sjdp}
54941120Sjdp
55041120Sjdp/*
55141120Sjdp * Send the current request, after encrypting it.  Returns 0 on success,
55241120Sjdp * or -1 on failure.
55341120Sjdp */
55441120Sjdpstatic int
55541120Sjdpsend_msg(struct tac_handle *h)
55641120Sjdp{
55741120Sjdp	struct timeval deadline;
55841120Sjdp	struct tac_msg *msg;
55941120Sjdp	char *ptr;
56041120Sjdp	int len;
56141120Sjdp
56241120Sjdp	if (h->last_seq_no & 1) {
56341120Sjdp		generr(h, "Attempt to send message out of sequence");
56441120Sjdp		return -1;
56541120Sjdp	}
56641120Sjdp
56741120Sjdp	msg = &h->request;
56841120Sjdp	msg->seq_no = ++h->last_seq_no;
56941120Sjdp	if (msg->seq_no == 1)
57041120Sjdp		gen_session_id(msg);
57141120Sjdp	crypt_msg(h, msg);
57241120Sjdp
57341120Sjdp	if (establish_connection(h) == -1)
57441120Sjdp		return -1;
57541120Sjdp
57641120Sjdp	if (h->single_connect)
57741120Sjdp		msg->flags |= TAC_SINGLE_CONNECT;
57841120Sjdp	else
57941120Sjdp		msg->flags &= ~TAC_SINGLE_CONNECT;
58041120Sjdp	gettimeofday(&deadline, NULL);
58141120Sjdp	deadline.tv_sec += h->servers[h->cur_server].timeout;
58241120Sjdp	len = HDRSIZE + ntohl(msg->length);
58341120Sjdp	ptr = (char *)msg;
58441120Sjdp	while (len > 0) {
58541120Sjdp		int n;
58641120Sjdp
58741120Sjdp		n = write(h->fd, ptr, len);
58841120Sjdp		if (n == -1) {
58941120Sjdp			struct timeval tv;
59041120Sjdp			int nfds;
59141120Sjdp
59241120Sjdp			if (errno != EAGAIN) {
59341120Sjdp				generr(h, "Network write error: %s",
59441120Sjdp				    strerror(errno));
59541120Sjdp				return -1;
59641120Sjdp			}
59741120Sjdp
59841120Sjdp			/* Wait until we can write more data. */
59941120Sjdp			gettimeofday(&tv, NULL);
60041120Sjdp			timersub(&deadline, &tv, &tv);
60141120Sjdp			if (tv.tv_sec >= 0) {
60241120Sjdp				fd_set wfds;
60341120Sjdp
60441120Sjdp				FD_ZERO(&wfds);
60541120Sjdp				FD_SET(h->fd, &wfds);
60641120Sjdp				nfds =
60741120Sjdp				    select(h->fd + 1, NULL, &wfds, NULL, &tv);
60841120Sjdp				if (nfds == -1) {
60941120Sjdp					generr(h, "select: %s",
61041120Sjdp					    strerror(errno));
61141120Sjdp					return -1;
61241120Sjdp				}
61341120Sjdp			} else
61441120Sjdp				nfds = 0;
61541120Sjdp			if (nfds == 0) {
61641120Sjdp				generr(h, "Network write timed out");
61741120Sjdp				return -1;
61841120Sjdp			}
61941120Sjdp		} else {
62041120Sjdp			ptr += n;
62141120Sjdp			len -= n;
62241120Sjdp		}
62341120Sjdp	}
62441120Sjdp	return 0;
62541120Sjdp}
62641120Sjdp
62741120Sjdp/*
62841120Sjdp * Destructively split a string into fields separated by white space.
62941120Sjdp * `#' at the beginning of a field begins a comment that extends to the
63041120Sjdp * end of the string.  Fields may be quoted with `"'.  Inside quoted
63141120Sjdp * strings, the backslash escapes `\"' and `\\' are honored.
63241120Sjdp *
63341120Sjdp * Pointers to up to the first maxfields fields are stored in the fields
63441120Sjdp * array.  Missing fields get NULL pointers.
63541120Sjdp *
63641120Sjdp * The return value is the actual number of fields parsed, and is always
63741120Sjdp * <= maxfields.
63841120Sjdp *
63941120Sjdp * On a syntax error, places a message in the msg string, and returns -1.
64041120Sjdp */
64141120Sjdpstatic int
64241120Sjdpsplit(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
64341120Sjdp{
64441120Sjdp	char *p;
64541120Sjdp	int i;
64641120Sjdp	static const char ws[] = " \t";
64741120Sjdp
64841120Sjdp	for (i = 0;  i < maxfields;  i++)
64941120Sjdp		fields[i] = NULL;
65041120Sjdp	p = str;
65141120Sjdp	i = 0;
65241120Sjdp	while (*p != '\0') {
65341120Sjdp		p += strspn(p, ws);
65441120Sjdp		if (*p == '#' || *p == '\0')
65541120Sjdp			break;
65641120Sjdp		if (i >= maxfields) {
65741120Sjdp			snprintf(msg, msglen, "line has too many fields");
65841120Sjdp			return -1;
65941120Sjdp		}
66041120Sjdp		if (*p == '"') {
66141120Sjdp			char *dst;
66241120Sjdp
66341120Sjdp			dst = ++p;
66441120Sjdp			fields[i] = dst;
66541120Sjdp			while (*p != '"') {
66641120Sjdp				if (*p == '\\') {
66741120Sjdp					p++;
66841120Sjdp					if (*p != '"' && *p != '\\' &&
66941120Sjdp					    *p != '\0') {
67041120Sjdp						snprintf(msg, msglen,
67141120Sjdp						    "invalid `\\' escape");
67241120Sjdp						return -1;
67341120Sjdp					}
67441120Sjdp				}
67541120Sjdp				if (*p == '\0') {
67641120Sjdp					snprintf(msg, msglen,
67741120Sjdp					    "unterminated quoted string");
67841120Sjdp					return -1;
67941120Sjdp				}
68041120Sjdp				*dst++ = *p++;
68141120Sjdp			}
68241120Sjdp			*dst = '\0';
68341120Sjdp			p++;
68441120Sjdp			if (*p != '\0' && strspn(p, ws) == 0) {
68541120Sjdp				snprintf(msg, msglen, "quoted string not"
68641120Sjdp				    " followed by white space");
68741120Sjdp				return -1;
68841120Sjdp			}
68941120Sjdp		} else {
69041120Sjdp			fields[i] = p;
69141120Sjdp			p += strcspn(p, ws);
69241120Sjdp			if (*p != '\0')
69341120Sjdp				*p++ = '\0';
69441120Sjdp		}
69541120Sjdp		i++;
69641120Sjdp	}
69741120Sjdp	return i;
69841120Sjdp}
69941120Sjdp
70041120Sjdpint
70141120Sjdptac_add_server(struct tac_handle *h, const char *host, int port,
70241120Sjdp    const char *secret, int timeout, int flags)
70341120Sjdp{
70441120Sjdp	struct tac_server *srvp;
70541120Sjdp
70641120Sjdp	if (h->num_servers >= MAXSERVERS) {
70756141Sjdp		generr(h, "Too many TACACS+ servers specified");
70841120Sjdp		return -1;
70941120Sjdp	}
71041120Sjdp	srvp = &h->servers[h->num_servers];
71141120Sjdp
71241120Sjdp	memset(&srvp->addr, 0, sizeof srvp->addr);
71341120Sjdp	srvp->addr.sin_len = sizeof srvp->addr;
71441120Sjdp	srvp->addr.sin_family = AF_INET;
71541120Sjdp	if (!inet_aton(host, &srvp->addr.sin_addr)) {
71641120Sjdp		struct hostent *hent;
71741120Sjdp
71841120Sjdp		if ((hent = gethostbyname(host)) == NULL) {
71941120Sjdp			generr(h, "%s: host not found", host);
72041120Sjdp			return -1;
72141120Sjdp		}
72241120Sjdp		memcpy(&srvp->addr.sin_addr, hent->h_addr,
72341120Sjdp		    sizeof srvp->addr.sin_addr);
72441120Sjdp	}
72541120Sjdp	srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
72641120Sjdp	if ((srvp->secret = xstrdup(h, secret)) == NULL)
72741120Sjdp		return -1;
72841120Sjdp	srvp->timeout = timeout;
72941120Sjdp	srvp->flags = flags;
73041120Sjdp	h->num_servers++;
73141120Sjdp	return 0;
73241120Sjdp}
73341120Sjdp
73441120Sjdpvoid
73541120Sjdptac_close(struct tac_handle *h)
73641120Sjdp{
73741120Sjdp	int srv;
73841120Sjdp
73941120Sjdp	if (h->fd != -1)
74041120Sjdp		close(h->fd);
74141120Sjdp	for (srv = 0;  srv < h->num_servers;  srv++) {
74241120Sjdp		memset(h->servers[srv].secret, 0,
74341120Sjdp		    strlen(h->servers[srv].secret));
74441120Sjdp		free(h->servers[srv].secret);
74541120Sjdp	}
74641120Sjdp	free_str(&h->user);
74741120Sjdp	free_str(&h->port);
74841120Sjdp	free_str(&h->rem_addr);
74941120Sjdp	free_str(&h->data);
75041120Sjdp	free_str(&h->user_msg);
75141120Sjdp	free(h);
75241120Sjdp}
75341120Sjdp
75441120Sjdpint
75541120Sjdptac_config(struct tac_handle *h, const char *path)
75641120Sjdp{
75741120Sjdp	FILE *fp;
75841120Sjdp	char buf[MAXCONFLINE];
75941120Sjdp	int linenum;
76041120Sjdp	int retval;
76141120Sjdp
76241120Sjdp	if (path == NULL)
76341120Sjdp		path = PATH_TACPLUS_CONF;
76441120Sjdp	if ((fp = fopen(path, "r")) == NULL) {
76541120Sjdp		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
76641120Sjdp		return -1;
76741120Sjdp	}
76841120Sjdp	retval = 0;
76941120Sjdp	linenum = 0;
77041120Sjdp	while (fgets(buf, sizeof buf, fp) != NULL) {
77141120Sjdp		int len;
77241120Sjdp		char *fields[4];
77341120Sjdp		int nfields;
77441120Sjdp		char msg[ERRSIZE];
77565222Sache		char *host, *res;
77641120Sjdp		char *port_str;
77741120Sjdp		char *secret;
77841120Sjdp		char *timeout_str;
77941120Sjdp		char *options_str;
78041120Sjdp		char *end;
78141120Sjdp		unsigned long timeout;
78241120Sjdp		int port;
78341120Sjdp		int options;
78441120Sjdp
78541120Sjdp		linenum++;
78641120Sjdp		len = strlen(buf);
78741120Sjdp		/* We know len > 0, else fgets would have returned NULL. */
78841120Sjdp		if (buf[len - 1] != '\n') {
78941120Sjdp			if (len == sizeof buf - 1)
79041120Sjdp				generr(h, "%s:%d: line too long", path,
79141120Sjdp				    linenum);
79241120Sjdp			else
79341120Sjdp				generr(h, "%s:%d: missing newline", path,
79441120Sjdp				    linenum);
79541120Sjdp			retval = -1;
79641120Sjdp			break;
79741120Sjdp		}
79841120Sjdp		buf[len - 1] = '\0';
79941120Sjdp
80041120Sjdp		/* Extract the fields from the line. */
80141120Sjdp		nfields = split(buf, fields, 4, msg, sizeof msg);
80241120Sjdp		if (nfields == -1) {
80341120Sjdp			generr(h, "%s:%d: %s", path, linenum, msg);
80441120Sjdp			retval = -1;
80541120Sjdp			break;
80641120Sjdp		}
80741120Sjdp		if (nfields == 0)
80841120Sjdp			continue;
80941120Sjdp		if (nfields < 2) {
81041120Sjdp			generr(h, "%s:%d: missing shared secret", path,
81141120Sjdp			    linenum);
81241120Sjdp			retval = -1;
81341120Sjdp			break;
81441120Sjdp		}
81541120Sjdp		host = fields[0];
81641120Sjdp		secret = fields[1];
81741120Sjdp		timeout_str = fields[2];
81841120Sjdp		options_str = fields[3];
81941120Sjdp
82041120Sjdp		/* Parse and validate the fields. */
82165222Sache		res = host;
82265222Sache		host = strsep(&res, ":");
82365222Sache		port_str = strsep(&res, ":");
82441120Sjdp		if (port_str != NULL) {
82541120Sjdp			port = strtoul(port_str, &end, 10);
82641120Sjdp			if (port_str[0] == '\0' || *end != '\0') {
82741120Sjdp				generr(h, "%s:%d: invalid port", path,
82841120Sjdp				    linenum);
82941120Sjdp				retval = -1;
83041120Sjdp				break;
83141120Sjdp			}
83241120Sjdp		} else
83341120Sjdp			port = 0;
83441120Sjdp		if (timeout_str != NULL) {
83541120Sjdp			timeout = strtoul(timeout_str, &end, 10);
83641120Sjdp			if (timeout_str[0] == '\0' || *end != '\0') {
83741120Sjdp				generr(h, "%s:%d: invalid timeout", path,
83841120Sjdp				    linenum);
83941120Sjdp				retval = -1;
84041120Sjdp				break;
84141120Sjdp			}
84241120Sjdp		} else
84341120Sjdp			timeout = TIMEOUT;
84441120Sjdp		options = 0;
84541120Sjdp		if (options_str != NULL) {
84641120Sjdp			if (strcmp(options_str, "single-connection") == 0)
84741120Sjdp				options |= TAC_SRVR_SINGLE_CONNECT;
84841120Sjdp			else {
84941120Sjdp				generr(h, "%s:%d: invalid option \"%s\"",
85041120Sjdp				    path, linenum, options_str);
85141120Sjdp				retval = -1;
85241120Sjdp				break;
85341120Sjdp			}
85441120Sjdp		};
85541120Sjdp
85641120Sjdp		if (tac_add_server(h, host, port, secret, timeout,
85741120Sjdp		    options) == -1) {
85841120Sjdp			char msg[ERRSIZE];
85941120Sjdp
86041120Sjdp			strcpy(msg, h->errmsg);
86141120Sjdp			generr(h, "%s:%d: %s", path, linenum, msg);
86241120Sjdp			retval = -1;
86341120Sjdp			break;
86441120Sjdp		}
86541120Sjdp	}
86641120Sjdp	/* Clear out the buffer to wipe a possible copy of a shared secret */
86741120Sjdp	memset(buf, 0, sizeof buf);
86841120Sjdp	fclose(fp);
86941120Sjdp	return retval;
87041120Sjdp}
87141120Sjdp
87241120Sjdpint
87341120Sjdptac_create_authen(struct tac_handle *h, int action, int type, int service)
87441120Sjdp{
87541120Sjdp	struct tac_msg *msg;
87641120Sjdp	struct tac_authen_start *as;
87741120Sjdp
87841120Sjdp	h->last_seq_no = 0;
87941120Sjdp
88041120Sjdp	msg = &h->request;
88141120Sjdp	msg->type = TAC_AUTHEN;
88241120Sjdp	msg->version = authen_version(action, type);
88341120Sjdp	msg->flags = 0;
88441120Sjdp
88541120Sjdp	as = &msg->u.authen_start;
88641120Sjdp	as->action = action;
88741120Sjdp	as->priv_lvl = TAC_PRIV_LVL_USER;
88841120Sjdp	as->authen_type = type;
88941120Sjdp	as->service = service;
89041120Sjdp
89141120Sjdp	free_str(&h->user);
89241120Sjdp	free_str(&h->port);
89341120Sjdp	free_str(&h->rem_addr);
89441120Sjdp	free_str(&h->data);
89541120Sjdp	free_str(&h->user_msg);
89641120Sjdp
89741120Sjdp	/* XXX - more to do */
89841120Sjdp	return 0;
89941120Sjdp}
90041120Sjdp
90141120Sjdpvoid *
90241120Sjdptac_get_data(struct tac_handle *h, size_t *len)
90341120Sjdp{
90441120Sjdp	return dup_str(h, &h->srvr_data, len);
90541120Sjdp}
90641120Sjdp
90741120Sjdpchar *
90841120Sjdptac_get_msg(struct tac_handle *h)
90941120Sjdp{
91041120Sjdp	return (char *)dup_str(h, &h->srvr_msg, NULL);
91141120Sjdp}
91241120Sjdp
91341120Sjdp/*
91441120Sjdp * Create and initialize a tac_handle structure, and return it to the
91541120Sjdp * caller.  Can fail only if the necessary memory cannot be allocated.
91641120Sjdp * In that case, it returns NULL.
91741120Sjdp */
91841120Sjdpstruct tac_handle *
91941120Sjdptac_open(void)
92041120Sjdp{
92141120Sjdp	struct tac_handle *h;
92241120Sjdp
92341120Sjdp	h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
92441120Sjdp	if (h != NULL) {
92541120Sjdp		h->fd = -1;
92641120Sjdp		h->num_servers = 0;
92741120Sjdp		h->cur_server = 0;
92841120Sjdp		h->errmsg[0] = '\0';
92941120Sjdp		init_clnt_str(&h->user);
93041120Sjdp		init_clnt_str(&h->port);
93141120Sjdp		init_clnt_str(&h->rem_addr);
93241120Sjdp		init_clnt_str(&h->data);
93341120Sjdp		init_clnt_str(&h->user_msg);
93441120Sjdp		init_srvr_str(&h->srvr_msg);
93541120Sjdp		init_srvr_str(&h->srvr_data);
93641120Sjdp		srandomdev();
93741120Sjdp	}
93841120Sjdp	return h;
93941120Sjdp}
94041120Sjdp
94141120Sjdpint
94241120Sjdptac_send_authen(struct tac_handle *h)
94341120Sjdp{
94441120Sjdp	struct tac_authen_reply *ar;
94541120Sjdp
94641120Sjdp	if (h->last_seq_no == 0) {	/* Authentication START packet */
94741120Sjdp		struct tac_authen_start *as;
94841120Sjdp
94941120Sjdp		as = &h->request.u.authen_start;
95041120Sjdp		h->request.length =
95141120Sjdp		    htonl(offsetof(struct tac_authen_start, rest[0]));
95241120Sjdp		if (add_str_8(h, &as->user_len, &h->user) == -1 ||
95341120Sjdp		    add_str_8(h, &as->port_len, &h->port) == -1 ||
95441120Sjdp		    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
95541120Sjdp		    add_str_8(h, &as->data_len, &h->data) == -1)
95641120Sjdp			return -1;
95741120Sjdp	} else {			/* Authentication CONTINUE packet */
95841120Sjdp		struct tac_authen_cont *ac;
95941120Sjdp
96041120Sjdp		ac = &h->request.u.authen_cont;
96141120Sjdp		ac->flags = 0;
96241120Sjdp		h->request.length =
96341120Sjdp		    htonl(offsetof(struct tac_authen_cont, rest[0]));
96441120Sjdp		if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
96541120Sjdp		    add_str_16(h, &ac->data_len, &h->data) == -1)
96641120Sjdp			return -1;
96741120Sjdp	}
96841120Sjdp
96941120Sjdp	/* Send the message and retrieve the reply. */
97041120Sjdp	if (send_msg(h) == -1 || recv_msg(h) == -1)
97141120Sjdp		return -1;
97241120Sjdp
97341120Sjdp	/* Scan the optional fields in the reply. */
97441120Sjdp	ar = &h->response.u.authen_reply;
97541120Sjdp	h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
97641120Sjdp	if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
97741120Sjdp	    get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 ||
97841120Sjdp	    get_srvr_end(h) == -1)
97941120Sjdp		return -1;
98041120Sjdp
98141120Sjdp	if (!h->single_connect &&
98241120Sjdp	    ar->status != TAC_AUTHEN_STATUS_GETDATA &&
98341120Sjdp	    ar->status != TAC_AUTHEN_STATUS_GETUSER &&
98441120Sjdp	    ar->status != TAC_AUTHEN_STATUS_GETPASS)
98541120Sjdp		close_connection(h);
98641120Sjdp
98741120Sjdp	return ar->flags << 8 | ar->status;
98841120Sjdp}
98941120Sjdp
99041120Sjdpint
99141120Sjdptac_set_rem_addr(struct tac_handle *h, const char *addr)
99241120Sjdp{
99341120Sjdp	return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
99441120Sjdp}
99541120Sjdp
99641120Sjdpint
99741120Sjdptac_set_data(struct tac_handle *h, const void *data, size_t data_len)
99841120Sjdp{
99941120Sjdp	return save_str(h, &h->data, data, data_len);
100041120Sjdp}
100141120Sjdp
100241120Sjdpint
100341120Sjdptac_set_msg(struct tac_handle *h, const char *msg)
100441120Sjdp{
100541120Sjdp	return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
100641120Sjdp}
100741120Sjdp
100841120Sjdpint
100941120Sjdptac_set_port(struct tac_handle *h, const char *port)
101041120Sjdp{
101141120Sjdp	return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
101241120Sjdp}
101341120Sjdp
101441120Sjdpint
101541120Sjdptac_set_priv(struct tac_handle *h, int priv)
101641120Sjdp{
101741120Sjdp	if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
101841120Sjdp		generr(h, "Attempt to set invalid privilege level");
101941120Sjdp		return -1;
102041120Sjdp	}
102141120Sjdp	h->request.u.authen_start.priv_lvl = priv;
102241120Sjdp	return 0;
102341120Sjdp}
102441120Sjdp
102541120Sjdpint
102641120Sjdptac_set_user(struct tac_handle *h, const char *user)
102741120Sjdp{
102841120Sjdp	return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
102941120Sjdp}
103041120Sjdp
103141120Sjdpconst char *
103241120Sjdptac_strerror(struct tac_handle *h)
103341120Sjdp{
103441120Sjdp	return h->errmsg;
103541120Sjdp}
103641120Sjdp
103741120Sjdpstatic void *
103841120Sjdpxmalloc(struct tac_handle *h, size_t size)
103941120Sjdp{
104041120Sjdp	void *r;
104141120Sjdp
104241120Sjdp	if ((r = malloc(size)) == NULL)
104341120Sjdp		generr(h, "Out of memory");
104441120Sjdp	return r;
104541120Sjdp}
104641120Sjdp
104741120Sjdpstatic char *
104841120Sjdpxstrdup(struct tac_handle *h, const char *s)
104941120Sjdp{
105041120Sjdp	char *r;
105141120Sjdp
105241120Sjdp	if ((r = strdup(s)) == NULL)
105341120Sjdp		generr(h, "Out of memory");
105441120Sjdp	return r;
105541120Sjdp}
1056