taclib.c revision 84222
1/*-
2 * Copyright 1998 Juniper Networks, Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/lib/libtacplus/taclib.c 84222 2001-09-30 22:29:19Z dillon $");
29
30#include <sys/types.h>
31#include <sys/socket.h>
32#include <sys/time.h>
33#include <netinet/in.h>
34#include <arpa/inet.h>
35
36#include <assert.h>
37#include <errno.h>
38#include <fcntl.h>
39#include <md5.h>
40#include <netdb.h>
41#include <stdarg.h>
42#include <stddef.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
47
48#include "taclib_private.h"
49
50static int		 add_str_8(struct tac_handle *, u_int8_t *,
51			    struct clnt_str *);
52static int		 add_str_16(struct tac_handle *, u_int16_t *,
53			    struct clnt_str *);
54static int		 authen_version(int, int);
55static void		 close_connection(struct tac_handle *);
56static int		 conn_server(struct tac_handle *);
57static void		 crypt_msg(struct tac_handle *, struct tac_msg *);
58static void		*dup_str(struct tac_handle *, const struct srvr_str *,
59			    size_t *);
60static int		 establish_connection(struct tac_handle *);
61static void		 free_str(struct clnt_str *);
62static void		 generr(struct tac_handle *, const char *, ...)
63			    __printflike(2, 3);
64static void		 gen_session_id(struct tac_msg *);
65static int		 get_srvr_end(struct tac_handle *);
66static int		 get_srvr_str(struct tac_handle *, struct srvr_str *,
67			    size_t);
68static void		 init_clnt_str(struct clnt_str *);
69static void		 init_srvr_str(struct srvr_str *);
70static int		 read_timed(struct tac_handle *, void *, size_t,
71			    const struct timeval *);
72static int		 recv_msg(struct tac_handle *);
73static int		 save_str(struct tac_handle *, struct clnt_str *,
74			    const void *, size_t);
75static int		 send_msg(struct tac_handle *);
76static int		 split(char *, char *[], int, char *, size_t);
77static void		*xmalloc(struct tac_handle *, size_t);
78static char		*xstrdup(struct tac_handle *, const char *);
79
80/*
81 * Append some optional data to the current request, and store its
82 * length into the 8-bit field referenced by "fld".  Returns 0 on
83 * success, or -1 on failure.
84 *
85 * This function also frees the "cs" string data and initializes it
86 * for the next time.
87 */
88static int
89add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
90{
91	u_int16_t len;
92
93	if (add_str_16(h, &len, cs) == -1)
94		return -1;
95	len = ntohs(len);
96	if (len > 0xff) {
97		generr(h, "Field too long");
98		return -1;
99	}
100	*fld = len;
101	return 0;
102}
103
104/*
105 * Append some optional data to the current request, and store its
106 * length into the 16-bit field (network byte order) referenced by
107 * "fld".  Returns 0 on success, or -1 on failure.
108 *
109 * This function also frees the "cs" string data and initializes it
110 * for the next time.
111 */
112static int
113add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
114{
115	size_t len;
116
117	len = cs->len;
118	if (cs->data == NULL)
119		len = 0;
120	if (len != 0) {
121		int offset;
122
123		if (len > 0xffff) {
124			generr(h, "Field too long");
125			return -1;
126		}
127		offset = ntohl(h->request.length);
128		if (offset + len > BODYSIZE) {
129			generr(h, "Message too long");
130			return -1;
131		}
132		memcpy(h->request.u.body + offset, cs->data, len);
133		h->request.length = htonl(offset + len);
134	}
135	*fld = htons(len);
136	free_str(cs);
137	return 0;
138}
139
140static int
141authen_version(int action, int type)
142{
143	int minor;
144
145	switch (action) {
146
147	case TAC_AUTHEN_LOGIN:
148		switch (type) {
149
150		case TAC_AUTHEN_TYPE_PAP:
151		case TAC_AUTHEN_TYPE_CHAP:
152		case TAC_AUTHEN_TYPE_MSCHAP:
153		case TAC_AUTHEN_TYPE_ARAP:
154			minor = 1;
155			break;
156
157		default:
158			minor = 0;
159			break;
160		}
161		break;
162
163	case TAC_AUTHEN_SENDAUTH:
164		minor = 1;
165		break;
166
167	default:
168		minor = 0;
169		break;
170	};
171
172	return TAC_VER_MAJOR << 4 | minor;
173}
174
175static void
176close_connection(struct tac_handle *h)
177{
178	if (h->fd != -1) {
179		close(h->fd);
180		h->fd = -1;
181	}
182}
183
184static int
185conn_server(struct tac_handle *h)
186{
187	const struct tac_server *srvp = &h->servers[h->cur_server];
188	int flags;
189
190	if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
191		generr(h, "Cannot create socket: %s", strerror(errno));
192		return -1;
193	}
194	if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
195	    fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
196		generr(h, "Cannot set non-blocking mode on socket: %s",
197		    strerror(errno));
198		close(h->fd);
199		h->fd = -1;
200		return -1;
201	}
202	if (connect(h->fd, (struct sockaddr *)&srvp->addr,
203	    sizeof srvp->addr) == 0)
204		return 0;
205
206	if (errno == EINPROGRESS) {
207		fd_set wfds;
208		struct timeval tv;
209		int nfds;
210		struct sockaddr peer;
211		int peerlen;
212		int err;
213		int errlen;
214
215		/* Wait for the connection to complete. */
216		FD_ZERO(&wfds);
217		FD_SET(h->fd, &wfds);
218		tv.tv_sec = srvp->timeout;
219		tv.tv_usec = 0;
220		nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
221		if (nfds == -1) {
222			generr(h, "select: %s", strerror(errno));
223			close(h->fd);
224			h->fd = -1;
225			return -1;
226		}
227		if (nfds == 0) {
228			generr(h, "connect: timed out");
229			close(h->fd);
230			h->fd = -1;
231			return -1;
232		}
233
234		/* See whether we are connected now. */
235		peerlen = sizeof peer;
236		if (getpeername(h->fd, &peer, &peerlen) == 0)
237			return 0;
238
239		if (errno != ENOTCONN) {
240			generr(h, "getpeername: %s", strerror(errno));
241			close(h->fd);
242			h->fd = -1;
243			return -1;
244		}
245
246		/* Find out why the connect failed. */
247		errlen = sizeof err;
248		getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
249		errno = err;
250	}
251	generr(h, "connect: %s", strerror(errno));
252	close(h->fd);
253	h->fd = -1;
254	return -1;
255}
256
257/*
258 * Encrypt or decrypt a message.  The operations are symmetrical.
259 */
260static void
261crypt_msg(struct tac_handle *h, struct tac_msg *msg)
262{
263	const char *secret;
264	MD5_CTX base_ctx;
265	MD5_CTX ctx;
266	unsigned char md5[16];
267	int chunk;
268	int msg_len;
269
270	secret = h->servers[h->cur_server].secret;
271	if (secret[0] == '\0')
272		msg->flags |= TAC_UNENCRYPTED;
273	if (msg->flags & TAC_UNENCRYPTED)
274		return;
275
276	msg_len = ntohl(msg->length);
277
278	MD5Init(&base_ctx);
279	MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
280	MD5Update(&base_ctx, secret, strlen(secret));
281	MD5Update(&base_ctx, &msg->version, sizeof msg->version);
282	MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
283
284	ctx = base_ctx;
285	for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
286		int chunk_len;
287		int i;
288
289		MD5Final(md5, &ctx);
290
291		if ((chunk_len = msg_len - chunk) > sizeof md5)
292			chunk_len = sizeof md5;
293		for (i = 0;  i < chunk_len;  i++)
294			msg->u.body[chunk + i] ^= md5[i];
295
296		ctx = base_ctx;
297		MD5Update(&ctx, md5, sizeof md5);
298	}
299}
300
301/*
302 * Return a dynamically allocated copy of the given server string.
303 * The copy is null-terminated.  If "len" is non-NULL, the length of
304 * the string (excluding the terminating null byte) is stored via it.
305 * Returns NULL on failure.  Empty strings are still allocated even
306 * though they have no content.
307 */
308static void *
309dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
310{
311	unsigned char *p;
312
313	if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
314		return NULL;
315	if (ss->data != NULL && ss->len != 0)
316		memcpy(p, ss->data, ss->len);
317	p[ss->len] = '\0';
318	if (len != NULL)
319		*len = ss->len;
320	return p;
321}
322
323static int
324establish_connection(struct tac_handle *h)
325{
326	int i;
327
328	if (h->fd >= 0)		/* Already connected. */
329		return 0;
330	if (h->num_servers == 0) {
331		generr(h, "No TACACS+ servers specified");
332		return -1;
333	}
334	/*
335         * Try the servers round-robin.  We begin with the one that
336         * worked for us the last time.  That way, once we find a good
337         * server, we won't waste any more time trying the bad ones.
338	 */
339	for (i = 0;  i < h->num_servers;  i++) {
340		if (conn_server(h) == 0) {
341			h->single_connect = (h->servers[h->cur_server].flags &
342			    TAC_SRVR_SINGLE_CONNECT) != 0;
343			return 0;
344		}
345		if (++h->cur_server >= h->num_servers)	/* Wrap around */
346			h->cur_server = 0;
347	}
348	/* Just return whatever error was last reported by conn_server(). */
349	return -1;
350}
351
352/*
353 * Free a client string, obliterating its contents first for security.
354 */
355static void
356free_str(struct clnt_str *cs)
357{
358	if (cs->data != NULL) {
359		memset(cs->data, 0, cs->len);
360		free(cs->data);
361		cs->data = NULL;
362		cs->len = 0;
363	}
364}
365
366static void
367generr(struct tac_handle *h, const char *format, ...)
368{
369	va_list		 ap;
370
371	va_start(ap, format);
372	vsnprintf(h->errmsg, ERRSIZE, format, ap);
373	va_end(ap);
374}
375
376static void
377gen_session_id(struct tac_msg *msg)
378{
379	int r;
380
381	r = random();
382	msg->session_id[0] = r >> 8;
383	msg->session_id[1] = r;
384	r = random();
385	msg->session_id[2] = r >> 8;
386	msg->session_id[3] = r;
387}
388
389/*
390 * Verify that we are exactly at the end of the response message.
391 * Returns 0 on success, -1 on failure.
392 */
393static int
394get_srvr_end(struct tac_handle *h)
395{
396	if (h->srvr_pos != ntohl(h->response.length)) {
397		generr(h, "Invalid length field in response from server");
398		return -1;
399	}
400	return 0;
401}
402
403static int
404get_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len)
405{
406	if (h->srvr_pos + len > ntohl(h->response.length)) {
407		generr(h, "Invalid length field in response from server");
408		return -1;
409	}
410	ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
411	ss->len = len;
412	h->srvr_pos += len;
413	return 0;
414}
415
416static void
417init_clnt_str(struct clnt_str *cs)
418{
419	cs->data = NULL;
420	cs->len = 0;
421}
422
423static void
424init_srvr_str(struct srvr_str *ss)
425{
426	ss->data = NULL;
427	ss->len = 0;
428}
429
430static int
431read_timed(struct tac_handle *h, void *buf, size_t len,
432    const struct timeval *deadline)
433{
434	char *ptr;
435
436	ptr = (char *)buf;
437	while (len > 0) {
438		int n;
439
440		n = read(h->fd, ptr, len);
441		if (n == -1) {
442			struct timeval tv;
443			int nfds;
444
445			if (errno != EAGAIN) {
446				generr(h, "Network read error: %s",
447				    strerror(errno));
448				return -1;
449			}
450
451			/* Wait until we can read more data. */
452			gettimeofday(&tv, NULL);
453			timersub(deadline, &tv, &tv);
454			if (tv.tv_sec >= 0) {
455				fd_set rfds;
456
457				FD_ZERO(&rfds);
458				FD_SET(h->fd, &rfds);
459				nfds =
460				    select(h->fd + 1, &rfds, NULL, NULL, &tv);
461				if (nfds == -1) {
462					generr(h, "select: %s",
463					    strerror(errno));
464					return -1;
465				}
466			} else
467				nfds = 0;
468			if (nfds == 0) {
469				generr(h, "Network read timed out");
470				return -1;
471			}
472		} else if (n == 0) {
473			generr(h, "unexpected EOF from server");
474			return -1;
475		} else {
476			ptr += n;
477			len -= n;
478		}
479	}
480	return 0;
481}
482
483/*
484 * Receive a response from the server and decrypt it.  Returns 0 on
485 * success, or -1 on failure.
486 */
487static int
488recv_msg(struct tac_handle *h)
489{
490	struct timeval deadline;
491	struct tac_msg *msg;
492	size_t len;
493
494	msg = &h->response;
495	gettimeofday(&deadline, NULL);
496	deadline.tv_sec += h->servers[h->cur_server].timeout;
497
498	/* Read the message header and make sure it is reasonable. */
499	if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
500		return -1;
501	if (memcmp(msg->session_id, h->request.session_id,
502	    sizeof msg->session_id) != 0) {
503		generr(h, "Invalid session ID in received message");
504		return -1;
505	}
506	if (msg->type != h->request.type) {
507		generr(h, "Invalid type in received message");
508		return -1;
509	}
510	len = ntohl(msg->length);
511	if (len > BODYSIZE) {
512		generr(h, "Received message too large");
513		return -1;
514	}
515	if (msg->seq_no != ++h->last_seq_no) {
516		generr(h, "Invalid sequence number in received message");
517		return -1;
518	}
519
520	/* Read the message body. */
521	if (read_timed(h, msg->u.body, len, &deadline) == -1)
522		return -1;
523
524	/* Decrypt it. */
525	crypt_msg(h, msg);
526
527	/*
528	 * Turn off single-connection mode if the server isn't amenable
529	 * to it.
530	 */
531	if (!(msg->flags & TAC_SINGLE_CONNECT))
532		h->single_connect = 0;
533	return 0;
534}
535
536static int
537save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
538    size_t len)
539{
540	free_str(cs);
541	if (data != NULL && len != 0) {
542		if ((cs->data = xmalloc(h, len)) == NULL)
543			return -1;
544		cs->len = len;
545		memcpy(cs->data, data, len);
546	}
547	return 0;
548}
549
550/*
551 * Send the current request, after encrypting it.  Returns 0 on success,
552 * or -1 on failure.
553 */
554static int
555send_msg(struct tac_handle *h)
556{
557	struct timeval deadline;
558	struct tac_msg *msg;
559	char *ptr;
560	int len;
561
562	if (h->last_seq_no & 1) {
563		generr(h, "Attempt to send message out of sequence");
564		return -1;
565	}
566
567	msg = &h->request;
568	msg->seq_no = ++h->last_seq_no;
569	if (msg->seq_no == 1)
570		gen_session_id(msg);
571	crypt_msg(h, msg);
572
573	if (establish_connection(h) == -1)
574		return -1;
575
576	if (h->single_connect)
577		msg->flags |= TAC_SINGLE_CONNECT;
578	else
579		msg->flags &= ~TAC_SINGLE_CONNECT;
580	gettimeofday(&deadline, NULL);
581	deadline.tv_sec += h->servers[h->cur_server].timeout;
582	len = HDRSIZE + ntohl(msg->length);
583	ptr = (char *)msg;
584	while (len > 0) {
585		int n;
586
587		n = write(h->fd, ptr, len);
588		if (n == -1) {
589			struct timeval tv;
590			int nfds;
591
592			if (errno != EAGAIN) {
593				generr(h, "Network write error: %s",
594				    strerror(errno));
595				return -1;
596			}
597
598			/* Wait until we can write more data. */
599			gettimeofday(&tv, NULL);
600			timersub(&deadline, &tv, &tv);
601			if (tv.tv_sec >= 0) {
602				fd_set wfds;
603
604				FD_ZERO(&wfds);
605				FD_SET(h->fd, &wfds);
606				nfds =
607				    select(h->fd + 1, NULL, &wfds, NULL, &tv);
608				if (nfds == -1) {
609					generr(h, "select: %s",
610					    strerror(errno));
611					return -1;
612				}
613			} else
614				nfds = 0;
615			if (nfds == 0) {
616				generr(h, "Network write timed out");
617				return -1;
618			}
619		} else {
620			ptr += n;
621			len -= n;
622		}
623	}
624	return 0;
625}
626
627/*
628 * Destructively split a string into fields separated by white space.
629 * `#' at the beginning of a field begins a comment that extends to the
630 * end of the string.  Fields may be quoted with `"'.  Inside quoted
631 * strings, the backslash escapes `\"' and `\\' are honored.
632 *
633 * Pointers to up to the first maxfields fields are stored in the fields
634 * array.  Missing fields get NULL pointers.
635 *
636 * The return value is the actual number of fields parsed, and is always
637 * <= maxfields.
638 *
639 * On a syntax error, places a message in the msg string, and returns -1.
640 */
641static int
642split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
643{
644	char *p;
645	int i;
646	static const char ws[] = " \t";
647
648	for (i = 0;  i < maxfields;  i++)
649		fields[i] = NULL;
650	p = str;
651	i = 0;
652	while (*p != '\0') {
653		p += strspn(p, ws);
654		if (*p == '#' || *p == '\0')
655			break;
656		if (i >= maxfields) {
657			snprintf(msg, msglen, "line has too many fields");
658			return -1;
659		}
660		if (*p == '"') {
661			char *dst;
662
663			dst = ++p;
664			fields[i] = dst;
665			while (*p != '"') {
666				if (*p == '\\') {
667					p++;
668					if (*p != '"' && *p != '\\' &&
669					    *p != '\0') {
670						snprintf(msg, msglen,
671						    "invalid `\\' escape");
672						return -1;
673					}
674				}
675				if (*p == '\0') {
676					snprintf(msg, msglen,
677					    "unterminated quoted string");
678					return -1;
679				}
680				*dst++ = *p++;
681			}
682			*dst = '\0';
683			p++;
684			if (*p != '\0' && strspn(p, ws) == 0) {
685				snprintf(msg, msglen, "quoted string not"
686				    " followed by white space");
687				return -1;
688			}
689		} else {
690			fields[i] = p;
691			p += strcspn(p, ws);
692			if (*p != '\0')
693				*p++ = '\0';
694		}
695		i++;
696	}
697	return i;
698}
699
700int
701tac_add_server(struct tac_handle *h, const char *host, int port,
702    const char *secret, int timeout, int flags)
703{
704	struct tac_server *srvp;
705
706	if (h->num_servers >= MAXSERVERS) {
707		generr(h, "Too many TACACS+ servers specified");
708		return -1;
709	}
710	srvp = &h->servers[h->num_servers];
711
712	memset(&srvp->addr, 0, sizeof srvp->addr);
713	srvp->addr.sin_len = sizeof srvp->addr;
714	srvp->addr.sin_family = AF_INET;
715	if (!inet_aton(host, &srvp->addr.sin_addr)) {
716		struct hostent *hent;
717
718		if ((hent = gethostbyname(host)) == NULL) {
719			generr(h, "%s: host not found", host);
720			return -1;
721		}
722		memcpy(&srvp->addr.sin_addr, hent->h_addr,
723		    sizeof srvp->addr.sin_addr);
724	}
725	srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
726	if ((srvp->secret = xstrdup(h, secret)) == NULL)
727		return -1;
728	srvp->timeout = timeout;
729	srvp->flags = flags;
730	h->num_servers++;
731	return 0;
732}
733
734void
735tac_close(struct tac_handle *h)
736{
737	int srv;
738
739	if (h->fd != -1)
740		close(h->fd);
741	for (srv = 0;  srv < h->num_servers;  srv++) {
742		memset(h->servers[srv].secret, 0,
743		    strlen(h->servers[srv].secret));
744		free(h->servers[srv].secret);
745	}
746	free_str(&h->user);
747	free_str(&h->port);
748	free_str(&h->rem_addr);
749	free_str(&h->data);
750	free_str(&h->user_msg);
751	free(h);
752}
753
754int
755tac_config(struct tac_handle *h, const char *path)
756{
757	FILE *fp;
758	char buf[MAXCONFLINE];
759	int linenum;
760	int retval;
761
762	if (path == NULL)
763		path = PATH_TACPLUS_CONF;
764	if ((fp = fopen(path, "r")) == NULL) {
765		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
766		return -1;
767	}
768	retval = 0;
769	linenum = 0;
770	while (fgets(buf, sizeof buf, fp) != NULL) {
771		int len;
772		char *fields[4];
773		int nfields;
774		char msg[ERRSIZE];
775		char *host, *res;
776		char *port_str;
777		char *secret;
778		char *timeout_str;
779		char *options_str;
780		char *end;
781		unsigned long timeout;
782		int port;
783		int options;
784
785		linenum++;
786		len = strlen(buf);
787		/* We know len > 0, else fgets would have returned NULL. */
788		if (buf[len - 1] != '\n') {
789			if (len == sizeof buf - 1)
790				generr(h, "%s:%d: line too long", path,
791				    linenum);
792			else
793				generr(h, "%s:%d: missing newline", path,
794				    linenum);
795			retval = -1;
796			break;
797		}
798		buf[len - 1] = '\0';
799
800		/* Extract the fields from the line. */
801		nfields = split(buf, fields, 4, msg, sizeof msg);
802		if (nfields == -1) {
803			generr(h, "%s:%d: %s", path, linenum, msg);
804			retval = -1;
805			break;
806		}
807		if (nfields == 0)
808			continue;
809		if (nfields < 2) {
810			generr(h, "%s:%d: missing shared secret", path,
811			    linenum);
812			retval = -1;
813			break;
814		}
815		host = fields[0];
816		secret = fields[1];
817		timeout_str = fields[2];
818		options_str = fields[3];
819
820		/* Parse and validate the fields. */
821		res = host;
822		host = strsep(&res, ":");
823		port_str = strsep(&res, ":");
824		if (port_str != NULL) {
825			port = strtoul(port_str, &end, 10);
826			if (port_str[0] == '\0' || *end != '\0') {
827				generr(h, "%s:%d: invalid port", path,
828				    linenum);
829				retval = -1;
830				break;
831			}
832		} else
833			port = 0;
834		if (timeout_str != NULL) {
835			timeout = strtoul(timeout_str, &end, 10);
836			if (timeout_str[0] == '\0' || *end != '\0') {
837				generr(h, "%s:%d: invalid timeout", path,
838				    linenum);
839				retval = -1;
840				break;
841			}
842		} else
843			timeout = TIMEOUT;
844		options = 0;
845		if (options_str != NULL) {
846			if (strcmp(options_str, "single-connection") == 0)
847				options |= TAC_SRVR_SINGLE_CONNECT;
848			else {
849				generr(h, "%s:%d: invalid option \"%s\"",
850				    path, linenum, options_str);
851				retval = -1;
852				break;
853			}
854		};
855
856		if (tac_add_server(h, host, port, secret, timeout,
857		    options) == -1) {
858			char msg[ERRSIZE];
859
860			strcpy(msg, h->errmsg);
861			generr(h, "%s:%d: %s", path, linenum, msg);
862			retval = -1;
863			break;
864		}
865	}
866	/* Clear out the buffer to wipe a possible copy of a shared secret */
867	memset(buf, 0, sizeof buf);
868	fclose(fp);
869	return retval;
870}
871
872int
873tac_create_authen(struct tac_handle *h, int action, int type, int service)
874{
875	struct tac_msg *msg;
876	struct tac_authen_start *as;
877
878	h->last_seq_no = 0;
879
880	msg = &h->request;
881	msg->type = TAC_AUTHEN;
882	msg->version = authen_version(action, type);
883	msg->flags = 0;
884
885	as = &msg->u.authen_start;
886	as->action = action;
887	as->priv_lvl = TAC_PRIV_LVL_USER;
888	as->authen_type = type;
889	as->service = service;
890
891	free_str(&h->user);
892	free_str(&h->port);
893	free_str(&h->rem_addr);
894	free_str(&h->data);
895	free_str(&h->user_msg);
896
897	/* XXX - more to do */
898	return 0;
899}
900
901void *
902tac_get_data(struct tac_handle *h, size_t *len)
903{
904	return dup_str(h, &h->srvr_data, len);
905}
906
907char *
908tac_get_msg(struct tac_handle *h)
909{
910	return (char *)dup_str(h, &h->srvr_msg, NULL);
911}
912
913/*
914 * Create and initialize a tac_handle structure, and return it to the
915 * caller.  Can fail only if the necessary memory cannot be allocated.
916 * In that case, it returns NULL.
917 */
918struct tac_handle *
919tac_open(void)
920{
921	struct tac_handle *h;
922
923	h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
924	if (h != NULL) {
925		h->fd = -1;
926		h->num_servers = 0;
927		h->cur_server = 0;
928		h->errmsg[0] = '\0';
929		init_clnt_str(&h->user);
930		init_clnt_str(&h->port);
931		init_clnt_str(&h->rem_addr);
932		init_clnt_str(&h->data);
933		init_clnt_str(&h->user_msg);
934		init_srvr_str(&h->srvr_msg);
935		init_srvr_str(&h->srvr_data);
936		srandomdev();
937	}
938	return h;
939}
940
941int
942tac_send_authen(struct tac_handle *h)
943{
944	struct tac_authen_reply *ar;
945
946	if (h->last_seq_no == 0) {	/* Authentication START packet */
947		struct tac_authen_start *as;
948
949		as = &h->request.u.authen_start;
950		h->request.length =
951		    htonl(offsetof(struct tac_authen_start, rest[0]));
952		if (add_str_8(h, &as->user_len, &h->user) == -1 ||
953		    add_str_8(h, &as->port_len, &h->port) == -1 ||
954		    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
955		    add_str_8(h, &as->data_len, &h->data) == -1)
956			return -1;
957	} else {			/* Authentication CONTINUE packet */
958		struct tac_authen_cont *ac;
959
960		ac = &h->request.u.authen_cont;
961		ac->flags = 0;
962		h->request.length =
963		    htonl(offsetof(struct tac_authen_cont, rest[0]));
964		if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
965		    add_str_16(h, &ac->data_len, &h->data) == -1)
966			return -1;
967	}
968
969	/* Send the message and retrieve the reply. */
970	if (send_msg(h) == -1 || recv_msg(h) == -1)
971		return -1;
972
973	/* Scan the optional fields in the reply. */
974	ar = &h->response.u.authen_reply;
975	h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
976	if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
977	    get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 ||
978	    get_srvr_end(h) == -1)
979		return -1;
980
981	if (!h->single_connect &&
982	    ar->status != TAC_AUTHEN_STATUS_GETDATA &&
983	    ar->status != TAC_AUTHEN_STATUS_GETUSER &&
984	    ar->status != TAC_AUTHEN_STATUS_GETPASS)
985		close_connection(h);
986
987	return ar->flags << 8 | ar->status;
988}
989
990int
991tac_set_rem_addr(struct tac_handle *h, const char *addr)
992{
993	return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
994}
995
996int
997tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
998{
999	return save_str(h, &h->data, data, data_len);
1000}
1001
1002int
1003tac_set_msg(struct tac_handle *h, const char *msg)
1004{
1005	return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1006}
1007
1008int
1009tac_set_port(struct tac_handle *h, const char *port)
1010{
1011	return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1012}
1013
1014int
1015tac_set_priv(struct tac_handle *h, int priv)
1016{
1017	if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1018		generr(h, "Attempt to set invalid privilege level");
1019		return -1;
1020	}
1021	h->request.u.authen_start.priv_lvl = priv;
1022	return 0;
1023}
1024
1025int
1026tac_set_user(struct tac_handle *h, const char *user)
1027{
1028	return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1029}
1030
1031const char *
1032tac_strerror(struct tac_handle *h)
1033{
1034	return h->errmsg;
1035}
1036
1037static void *
1038xmalloc(struct tac_handle *h, size_t size)
1039{
1040	void *r;
1041
1042	if ((r = malloc(size)) == NULL)
1043		generr(h, "Out of memory");
1044	return r;
1045}
1046
1047static char *
1048xstrdup(struct tac_handle *h, const char *s)
1049{
1050	char *r;
1051
1052	if ((r = strdup(s)) == NULL)
1053		generr(h, "Out of memory");
1054	return r;
1055}
1056