1/*-
2 * Copyright (c) 1998, 2001, 2002, 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$");
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		 protocol_version(int, 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 *, const char *,
67				      struct srvr_str *, 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 *);
79static void              clear_srvr_avs(struct tac_handle *);
80static void              create_msg(struct tac_handle *, int, int, int);
81
82/*
83 * Append some optional data to the current request, and store its
84 * length into the 8-bit field referenced by "fld".  Returns 0 on
85 * success, or -1 on failure.
86 *
87 * This function also frees the "cs" string data and initializes it
88 * for the next time.
89 */
90static int
91add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
92{
93	u_int16_t len;
94
95	if (add_str_16(h, &len, cs) == -1)
96		return -1;
97	len = ntohs(len);
98	if (len > 0xff) {
99		generr(h, "Field too long");
100		return -1;
101	}
102	*fld = len;
103	return 0;
104}
105
106/*
107 * Append some optional data to the current request, and store its
108 * length into the 16-bit field (network byte order) referenced by
109 * "fld".  Returns 0 on success, or -1 on failure.
110 *
111 * This function also frees the "cs" string data and initializes it
112 * for the next time.
113 */
114static int
115add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
116{
117	size_t len;
118
119	len = cs->len;
120	if (cs->data == NULL)
121		len = 0;
122	if (len != 0) {
123		int offset;
124
125		if (len > 0xffff) {
126			generr(h, "Field too long");
127			return -1;
128		}
129		offset = ntohl(h->request.length);
130		if (offset + len > BODYSIZE) {
131			generr(h, "Message too long");
132			return -1;
133		}
134		memcpy(h->request.u.body + offset, cs->data, len);
135		h->request.length = htonl(offset + len);
136	}
137	*fld = htons(len);
138	free_str(cs);
139	return 0;
140}
141
142static int
143protocol_version(int msg_type, int var, int type)
144{
145    int minor;
146
147    switch (msg_type) {
148        case TAC_AUTHEN:
149	    /* 'var' represents the 'action' */
150	    switch (var) {
151	        case TAC_AUTHEN_LOGIN:
152		    switch (type) {
153
154		        case TAC_AUTHEN_TYPE_PAP:
155			case TAC_AUTHEN_TYPE_CHAP:
156			case TAC_AUTHEN_TYPE_MSCHAP:
157			case TAC_AUTHEN_TYPE_ARAP:
158			    minor = 1;
159			break;
160
161			default:
162			    minor = 0;
163			break;
164		     }
165		break;
166
167		case TAC_AUTHEN_SENDAUTH:
168		    minor = 1;
169		break;
170
171		default:
172		    minor = 0;
173		break;
174	    };
175	break;
176
177	case TAC_AUTHOR:
178	    /* 'var' represents the 'method' */
179	    switch (var) {
180	        /*
181		 * When new authentication methods are added, include 'method'
182		 * in determining the value of 'minor'.  At this point, all
183                 * methods defined in this implementation (see "Authorization
184                 * authentication methods" in taclib.h) are minor version 0
185		 * Not all types, however, indicate minor version 0.
186		 */
187                case TAC_AUTHEN_METH_NOT_SET:
188                case TAC_AUTHEN_METH_NONE:
189                case TAC_AUTHEN_METH_KRB5:
190                case TAC_AUTHEN_METH_LINE:
191                case TAC_AUTHEN_METH_ENABLE:
192                case TAC_AUTHEN_METH_LOCAL:
193                case TAC_AUTHEN_METH_TACACSPLUS:
194                case TAC_AUTHEN_METH_RCMD:
195		    switch (type) {
196		        case TAC_AUTHEN_TYPE_PAP:
197			case TAC_AUTHEN_TYPE_CHAP:
198			case TAC_AUTHEN_TYPE_MSCHAP:
199			case TAC_AUTHEN_TYPE_ARAP:
200			    minor = 1;
201			break;
202
203			default:
204			    minor = 0;
205			break;
206		     }
207	        break;
208	        default:
209		    minor = 0;
210		break;
211	    }
212        break;
213
214	case TAC_ACCT:
215
216	default:
217	    minor = 0;
218        break;
219    }
220
221    return TAC_VER_MAJOR << 4 | minor;
222}
223
224
225static void
226close_connection(struct tac_handle *h)
227{
228	if (h->fd != -1) {
229		close(h->fd);
230		h->fd = -1;
231	}
232}
233
234static int
235conn_server(struct tac_handle *h)
236{
237	const struct tac_server *srvp = &h->servers[h->cur_server];
238	int flags;
239
240	if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
241		generr(h, "Cannot create socket: %s", strerror(errno));
242		return -1;
243	}
244	if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
245	    fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
246		generr(h, "Cannot set non-blocking mode on socket: %s",
247		    strerror(errno));
248		close(h->fd);
249		h->fd = -1;
250		return -1;
251	}
252	if (connect(h->fd, (struct sockaddr *)&srvp->addr,
253	    sizeof srvp->addr) == 0)
254		return 0;
255
256	if (errno == EINPROGRESS) {
257		fd_set wfds;
258		struct timeval tv;
259		int nfds;
260		struct sockaddr peer;
261		socklen_t errlen, peerlen;
262		int err;
263
264		/* Wait for the connection to complete. */
265		FD_ZERO(&wfds);
266		FD_SET(h->fd, &wfds);
267		tv.tv_sec = srvp->timeout;
268		tv.tv_usec = 0;
269		nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
270		if (nfds == -1) {
271			generr(h, "select: %s", strerror(errno));
272			close(h->fd);
273			h->fd = -1;
274			return -1;
275		}
276		if (nfds == 0) {
277			generr(h, "connect: timed out");
278			close(h->fd);
279			h->fd = -1;
280			return -1;
281		}
282
283		/* See whether we are connected now. */
284		peerlen = sizeof peer;
285		if (getpeername(h->fd, &peer, &peerlen) == 0)
286			return 0;
287
288		if (errno != ENOTCONN) {
289			generr(h, "getpeername: %s", strerror(errno));
290			close(h->fd);
291			h->fd = -1;
292			return -1;
293		}
294
295		/* Find out why the connect failed. */
296		errlen = sizeof err;
297		getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
298		errno = err;
299	}
300	generr(h, "connect: %s", strerror(errno));
301	close(h->fd);
302	h->fd = -1;
303	return -1;
304}
305
306/*
307 * Encrypt or decrypt a message.  The operations are symmetrical.
308 */
309static void
310crypt_msg(struct tac_handle *h, struct tac_msg *msg)
311{
312	const char *secret;
313	MD5_CTX base_ctx;
314	MD5_CTX ctx;
315	unsigned char md5[16];
316	int chunk;
317	int msg_len;
318
319	secret = h->servers[h->cur_server].secret;
320	if (secret[0] == '\0')
321		msg->flags |= TAC_UNENCRYPTED;
322	if (msg->flags & TAC_UNENCRYPTED)
323		return;
324
325	msg_len = ntohl(msg->length);
326
327	MD5Init(&base_ctx);
328	MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
329	MD5Update(&base_ctx, secret, strlen(secret));
330	MD5Update(&base_ctx, &msg->version, sizeof msg->version);
331	MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
332
333	ctx = base_ctx;
334	for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
335		int chunk_len;
336		int i;
337
338		MD5Final(md5, &ctx);
339
340		if ((chunk_len = msg_len - chunk) > sizeof md5)
341			chunk_len = sizeof md5;
342		for (i = 0;  i < chunk_len;  i++)
343			msg->u.body[chunk + i] ^= md5[i];
344
345		ctx = base_ctx;
346		MD5Update(&ctx, md5, sizeof md5);
347	}
348}
349
350/*
351 * Return a dynamically allocated copy of the given server string.
352 * The copy is null-terminated.  If "len" is non-NULL, the length of
353 * the string (excluding the terminating null byte) is stored via it.
354 * Returns NULL on failure.  Empty strings are still allocated even
355 * though they have no content.
356 */
357static void *
358dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
359{
360	unsigned char *p;
361
362	if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
363		return NULL;
364	if (ss->data != NULL && ss->len != 0)
365		memcpy(p, ss->data, ss->len);
366	p[ss->len] = '\0';
367	if (len != NULL)
368		*len = ss->len;
369	return p;
370}
371
372static int
373establish_connection(struct tac_handle *h)
374{
375	int i;
376
377	if (h->fd >= 0)		/* Already connected. */
378		return 0;
379	if (h->num_servers == 0) {
380		generr(h, "No TACACS+ servers specified");
381		return -1;
382	}
383	/*
384         * Try the servers round-robin.  We begin with the one that
385         * worked for us the last time.  That way, once we find a good
386         * server, we won't waste any more time trying the bad ones.
387	 */
388	for (i = 0;  i < h->num_servers;  i++) {
389		if (conn_server(h) == 0) {
390			h->single_connect = (h->servers[h->cur_server].flags &
391			    TAC_SRVR_SINGLE_CONNECT) != 0;
392			return 0;
393		}
394		if (++h->cur_server >= h->num_servers)	/* Wrap around */
395			h->cur_server = 0;
396	}
397	/* Just return whatever error was last reported by conn_server(). */
398	return -1;
399}
400
401/*
402 * Free a client string, obliterating its contents first for security.
403 */
404static void
405free_str(struct clnt_str *cs)
406{
407	if (cs->data != NULL) {
408		memset(cs->data, 0, cs->len);
409		free(cs->data);
410		cs->data = NULL;
411		cs->len = 0;
412	}
413}
414
415static void
416generr(struct tac_handle *h, const char *format, ...)
417{
418	va_list		 ap;
419
420	va_start(ap, format);
421	vsnprintf(h->errmsg, ERRSIZE, format, ap);
422	va_end(ap);
423}
424
425static void
426gen_session_id(struct tac_msg *msg)
427{
428	int r;
429
430	r = random();
431	msg->session_id[0] = r >> 8;
432	msg->session_id[1] = r;
433	r = random();
434	msg->session_id[2] = r >> 8;
435	msg->session_id[3] = r;
436}
437
438/*
439 * Verify that we are exactly at the end of the response message.
440 * Returns 0 on success, -1 on failure.
441 */
442static int
443get_srvr_end(struct tac_handle *h)
444{
445	int len;
446
447	len = ntohl(h->response.length);
448
449	if (h->srvr_pos != len) {
450		generr(h, "Invalid length field in response "
451		       "from server: end expected at %u, response length %u",
452		       h->srvr_pos, len);
453		return -1;
454	}
455	return 0;
456}
457
458static int
459get_srvr_str(struct tac_handle *h, const char *field,
460	     struct srvr_str *ss, size_t len)
461{
462	if (h->srvr_pos + len > ntohl(h->response.length)) {
463		generr(h, "Invalid length field in %s response from server "
464		       "(%lu > %lu)", field, (u_long)(h->srvr_pos + len),
465		       (u_long)ntohl(h->response.length));
466		return -1;
467	}
468	ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
469	ss->len = len;
470	h->srvr_pos += len;
471	return 0;
472}
473
474static void
475init_clnt_str(struct clnt_str *cs)
476{
477	cs->data = NULL;
478	cs->len = 0;
479}
480
481static void
482init_srvr_str(struct srvr_str *ss)
483{
484	ss->data = NULL;
485	ss->len = 0;
486}
487
488static int
489read_timed(struct tac_handle *h, void *buf, size_t len,
490    const struct timeval *deadline)
491{
492	char *ptr;
493
494	ptr = (char *)buf;
495	while (len > 0) {
496		int n;
497
498		n = read(h->fd, ptr, len);
499		if (n == -1) {
500			struct timeval tv;
501			int nfds;
502
503			if (errno != EAGAIN) {
504				generr(h, "Network read error: %s",
505				    strerror(errno));
506				return -1;
507			}
508
509			/* Wait until we can read more data. */
510			gettimeofday(&tv, NULL);
511			timersub(deadline, &tv, &tv);
512			if (tv.tv_sec >= 0) {
513				fd_set rfds;
514
515				FD_ZERO(&rfds);
516				FD_SET(h->fd, &rfds);
517				nfds =
518				    select(h->fd + 1, &rfds, NULL, NULL, &tv);
519				if (nfds == -1) {
520					generr(h, "select: %s",
521					    strerror(errno));
522					return -1;
523				}
524			} else
525				nfds = 0;
526			if (nfds == 0) {
527				generr(h, "Network read timed out");
528				return -1;
529			}
530		} else if (n == 0) {
531			generr(h, "unexpected EOF from server");
532			return -1;
533		} else {
534			ptr += n;
535			len -= n;
536		}
537	}
538	return 0;
539}
540
541/*
542 * Receive a response from the server and decrypt it.  Returns 0 on
543 * success, or -1 on failure.
544 */
545static int
546recv_msg(struct tac_handle *h)
547{
548	struct timeval deadline;
549	struct tac_msg *msg;
550	u_int32_t len;
551
552	msg = &h->response;
553	gettimeofday(&deadline, NULL);
554	deadline.tv_sec += h->servers[h->cur_server].timeout;
555
556	/* Read the message header and make sure it is reasonable. */
557	if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
558		return -1;
559	if (memcmp(msg->session_id, h->request.session_id,
560	    sizeof msg->session_id) != 0) {
561		generr(h, "Invalid session ID in received message");
562		return -1;
563	}
564	if (msg->type != h->request.type) {
565		generr(h, "Invalid type in received message"
566			  " (got %u, expected %u)",
567			  msg->type, h->request.type);
568		return -1;
569	}
570	len = ntohl(msg->length);
571	if (len > BODYSIZE) {
572		generr(h, "Received message too large (%u > %u)",
573			  len, BODYSIZE);
574		return -1;
575	}
576	if (msg->seq_no != ++h->last_seq_no) {
577		generr(h, "Invalid sequence number in received message"
578			  " (got %u, expected %u)",
579			  msg->seq_no, h->last_seq_no);
580		return -1;
581	}
582
583	/* Read the message body. */
584	if (read_timed(h, msg->u.body, len, &deadline) == -1)
585		return -1;
586
587	/* Decrypt it. */
588	crypt_msg(h, msg);
589
590	/*
591	 * Turn off single-connection mode if the server isn't amenable
592	 * to it.
593	 */
594	if (!(msg->flags & TAC_SINGLE_CONNECT))
595		h->single_connect = 0;
596	return 0;
597}
598
599static int
600save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
601    size_t len)
602{
603	free_str(cs);
604	if (data != NULL && len != 0) {
605		if ((cs->data = xmalloc(h, len)) == NULL)
606			return -1;
607		cs->len = len;
608		memcpy(cs->data, data, len);
609	}
610	return 0;
611}
612
613/*
614 * Send the current request, after encrypting it.  Returns 0 on success,
615 * or -1 on failure.
616 */
617static int
618send_msg(struct tac_handle *h)
619{
620	struct timeval deadline;
621	struct tac_msg *msg;
622	char *ptr;
623	int len;
624
625	if (h->last_seq_no & 1) {
626		generr(h, "Attempt to send message out of sequence");
627		return -1;
628	}
629
630	if (establish_connection(h) == -1)
631		return -1;
632
633	msg = &h->request;
634	msg->seq_no = ++h->last_seq_no;
635	if (msg->seq_no == 1)
636		gen_session_id(msg);
637	crypt_msg(h, msg);
638
639	if (h->single_connect)
640		msg->flags |= TAC_SINGLE_CONNECT;
641	else
642		msg->flags &= ~TAC_SINGLE_CONNECT;
643	gettimeofday(&deadline, NULL);
644	deadline.tv_sec += h->servers[h->cur_server].timeout;
645	len = HDRSIZE + ntohl(msg->length);
646	ptr = (char *)msg;
647	while (len > 0) {
648		int n;
649
650		n = write(h->fd, ptr, len);
651		if (n == -1) {
652			struct timeval tv;
653			int nfds;
654
655			if (errno != EAGAIN) {
656				generr(h, "Network write error: %s",
657				    strerror(errno));
658				return -1;
659			}
660
661			/* Wait until we can write more data. */
662			gettimeofday(&tv, NULL);
663			timersub(&deadline, &tv, &tv);
664			if (tv.tv_sec >= 0) {
665				fd_set wfds;
666
667				FD_ZERO(&wfds);
668				FD_SET(h->fd, &wfds);
669				nfds =
670				    select(h->fd + 1, NULL, &wfds, NULL, &tv);
671				if (nfds == -1) {
672					generr(h, "select: %s",
673					    strerror(errno));
674					return -1;
675				}
676			} else
677				nfds = 0;
678			if (nfds == 0) {
679				generr(h, "Network write timed out");
680				return -1;
681			}
682		} else {
683			ptr += n;
684			len -= n;
685		}
686	}
687	return 0;
688}
689
690/*
691 * Destructively split a string into fields separated by white space.
692 * `#' at the beginning of a field begins a comment that extends to the
693 * end of the string.  Fields may be quoted with `"'.  Inside quoted
694 * strings, the backslash escapes `\"' and `\\' are honored.
695 *
696 * Pointers to up to the first maxfields fields are stored in the fields
697 * array.  Missing fields get NULL pointers.
698 *
699 * The return value is the actual number of fields parsed, and is always
700 * <= maxfields.
701 *
702 * On a syntax error, places a message in the msg string, and returns -1.
703 */
704static int
705split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
706{
707	char *p;
708	int i;
709	static const char ws[] = " \t";
710
711	for (i = 0;  i < maxfields;  i++)
712		fields[i] = NULL;
713	p = str;
714	i = 0;
715	while (*p != '\0') {
716		p += strspn(p, ws);
717		if (*p == '#' || *p == '\0')
718			break;
719		if (i >= maxfields) {
720			snprintf(msg, msglen, "line has too many fields");
721			return -1;
722		}
723		if (*p == '"') {
724			char *dst;
725
726			dst = ++p;
727			fields[i] = dst;
728			while (*p != '"') {
729				if (*p == '\\') {
730					p++;
731					if (*p != '"' && *p != '\\' &&
732					    *p != '\0') {
733						snprintf(msg, msglen,
734						    "invalid `\\' escape");
735						return -1;
736					}
737				}
738				if (*p == '\0') {
739					snprintf(msg, msglen,
740					    "unterminated quoted string");
741					return -1;
742				}
743				*dst++ = *p++;
744			}
745			*dst = '\0';
746			p++;
747			if (*p != '\0' && strspn(p, ws) == 0) {
748				snprintf(msg, msglen, "quoted string not"
749				    " followed by white space");
750				return -1;
751			}
752		} else {
753			fields[i] = p;
754			p += strcspn(p, ws);
755			if (*p != '\0')
756				*p++ = '\0';
757		}
758		i++;
759	}
760	return i;
761}
762
763int
764tac_add_server(struct tac_handle *h, const char *host, int port,
765    const char *secret, int timeout, int flags)
766{
767	struct tac_server *srvp;
768
769	if (h->num_servers >= MAXSERVERS) {
770		generr(h, "Too many TACACS+ servers specified");
771		return -1;
772	}
773	srvp = &h->servers[h->num_servers];
774
775	memset(&srvp->addr, 0, sizeof srvp->addr);
776	srvp->addr.sin_len = sizeof srvp->addr;
777	srvp->addr.sin_family = AF_INET;
778	if (!inet_aton(host, &srvp->addr.sin_addr)) {
779		struct hostent *hent;
780
781		if ((hent = gethostbyname(host)) == NULL) {
782			generr(h, "%s: host not found", host);
783			return -1;
784		}
785		memcpy(&srvp->addr.sin_addr, hent->h_addr,
786		    sizeof srvp->addr.sin_addr);
787	}
788	srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
789	if ((srvp->secret = xstrdup(h, secret)) == NULL)
790		return -1;
791	srvp->timeout = timeout;
792	srvp->flags = flags;
793	h->num_servers++;
794	return 0;
795}
796
797void
798tac_close(struct tac_handle *h)
799{
800	int i, srv;
801
802	if (h->fd != -1)
803		close(h->fd);
804	for (srv = 0;  srv < h->num_servers;  srv++) {
805		memset(h->servers[srv].secret, 0,
806		    strlen(h->servers[srv].secret));
807		free(h->servers[srv].secret);
808	}
809	free_str(&h->user);
810	free_str(&h->port);
811	free_str(&h->rem_addr);
812	free_str(&h->data);
813	free_str(&h->user_msg);
814	for (i=0; i<MAXAVPAIRS; i++)
815		free_str(&(h->avs[i]));
816
817	/* Clear everything else before freeing memory */
818	memset(h, 0, sizeof(struct tac_handle));
819	free(h);
820}
821
822int
823tac_config(struct tac_handle *h, const char *path)
824{
825	FILE *fp;
826	char buf[MAXCONFLINE];
827	int linenum;
828	int retval;
829
830	if (path == NULL)
831		path = PATH_TACPLUS_CONF;
832	if ((fp = fopen(path, "r")) == NULL) {
833		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
834		return -1;
835	}
836	retval = 0;
837	linenum = 0;
838	while (fgets(buf, sizeof buf, fp) != NULL) {
839		int len;
840		char *fields[4];
841		int nfields;
842		char msg[ERRSIZE];
843		char *host, *res;
844		char *port_str;
845		char *secret;
846		char *timeout_str;
847		char *options_str;
848		char *end;
849		unsigned long timeout;
850		int port;
851		int options;
852
853		linenum++;
854		len = strlen(buf);
855		/* We know len > 0, else fgets would have returned NULL. */
856		if (buf[len - 1] != '\n') {
857			if (len >= sizeof buf - 1)
858				generr(h, "%s:%d: line too long", path,
859				    linenum);
860			else
861				generr(h, "%s:%d: missing newline", path,
862				    linenum);
863			retval = -1;
864			break;
865		}
866		buf[len - 1] = '\0';
867
868		/* Extract the fields from the line. */
869		nfields = split(buf, fields, 4, msg, sizeof msg);
870		if (nfields == -1) {
871			generr(h, "%s:%d: %s", path, linenum, msg);
872			retval = -1;
873			break;
874		}
875		if (nfields == 0)
876			continue;
877		if (nfields < 2) {
878			generr(h, "%s:%d: missing shared secret", path,
879			    linenum);
880			retval = -1;
881			break;
882		}
883		host = fields[0];
884		secret = fields[1];
885		timeout_str = fields[2];
886		options_str = fields[3];
887
888		/* Parse and validate the fields. */
889		res = host;
890		host = strsep(&res, ":");
891		port_str = strsep(&res, ":");
892		if (port_str != NULL) {
893			port = strtoul(port_str, &end, 10);
894			if (port_str[0] == '\0' || *end != '\0') {
895				generr(h, "%s:%d: invalid port", path,
896				    linenum);
897				retval = -1;
898				break;
899			}
900		} else
901			port = 0;
902		if (timeout_str != NULL) {
903			timeout = strtoul(timeout_str, &end, 10);
904			if (timeout_str[0] == '\0' || *end != '\0') {
905				generr(h, "%s:%d: invalid timeout", path,
906				    linenum);
907				retval = -1;
908				break;
909			}
910		} else
911			timeout = TIMEOUT;
912		options = 0;
913		if (options_str != NULL) {
914			if (strcmp(options_str, "single-connection") == 0)
915				options |= TAC_SRVR_SINGLE_CONNECT;
916			else {
917				generr(h, "%s:%d: invalid option \"%s\"",
918				    path, linenum, options_str);
919				retval = -1;
920				break;
921			}
922		};
923
924		if (tac_add_server(h, host, port, secret, timeout,
925		    options) == -1) {
926			char msg[ERRSIZE];
927
928			strcpy(msg, h->errmsg);
929			generr(h, "%s:%d: %s", path, linenum, msg);
930			retval = -1;
931			break;
932		}
933	}
934	/* Clear out the buffer to wipe a possible copy of a shared secret */
935	memset(buf, 0, sizeof buf);
936	fclose(fp);
937	return retval;
938}
939
940int
941tac_create_authen(struct tac_handle *h, int action, int type, int service)
942{
943	struct tac_authen_start *as;
944
945	create_msg(h, TAC_AUTHEN, action, type);
946
947	as = &h->request.u.authen_start;
948	as->action = action;
949	as->priv_lvl = TAC_PRIV_LVL_USER;
950	as->authen_type = type;
951	as->service = service;
952
953	return 0;
954}
955
956int
957tac_create_author(struct tac_handle *h, int method, int type, int service)
958{
959	struct tac_author_request *areq;
960
961	create_msg(h, TAC_AUTHOR, method, type);
962
963	areq = &h->request.u.author_request;
964	areq->authen_meth = method;
965	areq->priv_lvl = TAC_PRIV_LVL_USER;
966	areq->authen_type = type;
967	areq->service = service;
968
969	return 0;
970}
971
972int
973tac_create_acct(struct tac_handle *h, int acct, int action, int type, int service)
974{
975	struct tac_acct_start *as;
976
977	create_msg(h, TAC_ACCT, action, type);
978
979	as = &h->request.u.acct_start;
980	as->action = acct;
981	as->authen_action = action;
982	as->priv_lvl = TAC_PRIV_LVL_USER;
983	as->authen_type = type;
984	as->authen_service = service;
985
986	return 0;
987}
988
989static void
990create_msg(struct tac_handle *h, int msg_type, int var, int type)
991{
992	struct tac_msg *msg;
993	int i;
994
995	h->last_seq_no = 0;
996
997	msg = &h->request;
998	msg->type = msg_type;
999	msg->version = protocol_version(msg_type, var, type);
1000	msg->flags = 0; /* encrypted packet body */
1001
1002	free_str(&h->user);
1003	free_str(&h->port);
1004	free_str(&h->rem_addr);
1005	free_str(&h->data);
1006	free_str(&h->user_msg);
1007
1008	for (i=0; i<MAXAVPAIRS; i++)
1009		free_str(&(h->avs[i]));
1010}
1011
1012void *
1013tac_get_data(struct tac_handle *h, size_t *len)
1014{
1015	return dup_str(h, &h->srvr_data, len);
1016}
1017
1018char *
1019tac_get_msg(struct tac_handle *h)
1020{
1021	return dup_str(h, &h->srvr_msg, NULL);
1022}
1023
1024/*
1025 * Create and initialize a tac_handle structure, and return it to the
1026 * caller.  Can fail only if the necessary memory cannot be allocated.
1027 * In that case, it returns NULL.
1028 */
1029struct tac_handle *
1030tac_open(void)
1031{
1032	int i;
1033	struct tac_handle *h;
1034
1035	h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
1036	if (h != NULL) {
1037		h->fd = -1;
1038		h->num_servers = 0;
1039		h->cur_server = 0;
1040		h->errmsg[0] = '\0';
1041		init_clnt_str(&h->user);
1042		init_clnt_str(&h->port);
1043		init_clnt_str(&h->rem_addr);
1044		init_clnt_str(&h->data);
1045		init_clnt_str(&h->user_msg);
1046		for (i=0; i<MAXAVPAIRS; i++) {
1047			init_clnt_str(&(h->avs[i]));
1048			init_srvr_str(&(h->srvr_avs[i]));
1049		}
1050		init_srvr_str(&h->srvr_msg);
1051		init_srvr_str(&h->srvr_data);
1052		srandomdev();
1053	}
1054	return h;
1055}
1056
1057int
1058tac_send_authen(struct tac_handle *h)
1059{
1060	struct tac_authen_reply *ar;
1061
1062	if (h->num_servers == 0)
1063	    return -1;
1064
1065	if (h->last_seq_no == 0) {	/* Authentication START packet */
1066		struct tac_authen_start *as;
1067
1068		as = &h->request.u.authen_start;
1069		h->request.length =
1070		    htonl(offsetof(struct tac_authen_start, rest[0]));
1071		if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1072		    add_str_8(h, &as->port_len, &h->port) == -1 ||
1073		    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
1074		    add_str_8(h, &as->data_len, &h->data) == -1)
1075			return -1;
1076	} else {			/* Authentication CONTINUE packet */
1077		struct tac_authen_cont *ac;
1078
1079		ac = &h->request.u.authen_cont;
1080		ac->flags = 0;
1081		h->request.length =
1082		    htonl(offsetof(struct tac_authen_cont, rest[0]));
1083		if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
1084		    add_str_16(h, &ac->data_len, &h->data) == -1)
1085			return -1;
1086	}
1087
1088	/* Send the message and retrieve the reply. */
1089	if (send_msg(h) == -1 || recv_msg(h) == -1)
1090		return -1;
1091
1092	/* Scan the optional fields in the reply. */
1093	ar = &h->response.u.authen_reply;
1094	h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
1095	if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1096	    get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1097	    get_srvr_end(h) == -1)
1098		return -1;
1099
1100	if (!h->single_connect &&
1101	    ar->status != TAC_AUTHEN_STATUS_GETDATA &&
1102	    ar->status != TAC_AUTHEN_STATUS_GETUSER &&
1103	    ar->status != TAC_AUTHEN_STATUS_GETPASS)
1104		close_connection(h);
1105
1106	return ar->flags << 8 | ar->status;
1107}
1108
1109int
1110tac_send_author(struct tac_handle *h)
1111{
1112	int i, current;
1113	char dbgstr[64];
1114	struct tac_author_request *areq = &h->request.u.author_request;
1115	struct tac_author_response *ares = &h->response.u.author_response;
1116
1117	h->request.length =
1118		htonl(offsetof(struct tac_author_request, rest[0]));
1119
1120	/* Count each specified AV pair */
1121	for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
1122		if (h->avs[i].len && h->avs[i].data)
1123			areq->av_cnt++;
1124
1125	/*
1126	 * Each AV size is a byte starting right after 'av_cnt'.  Update the
1127	 * offset to include these AV sizes.
1128	 */
1129	h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
1130
1131	/* Now add the string arguments from 'h' */
1132	if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
1133	    add_str_8(h, &areq->port_len, &h->port) == -1 ||
1134	    add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
1135		return -1;
1136
1137	/* Add each AV pair, the size of each placed in areq->rest[current] */
1138	for (current=0, i=0; i<MAXAVPAIRS; i++) {
1139		if (h->avs[i].len && h->avs[i].data) {
1140			if (add_str_8(h, &areq->rest[current++],
1141				      &(h->avs[i])) == -1)
1142				return -1;
1143		}
1144	}
1145
1146	/* Send the message and retrieve the reply. */
1147	if (send_msg(h) == -1 || recv_msg(h) == -1)
1148		return -1;
1149
1150	/* Update the offset in the response packet based on av pairs count */
1151	h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
1152		ares->av_cnt;
1153
1154	/* Scan the optional fields in the response. */
1155	if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
1156	    get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
1157		return -1;
1158
1159	/* Get each AV pair (just setting pointers, not malloc'ing) */
1160	clear_srvr_avs(h);
1161	for (i=0; i<ares->av_cnt; i++) {
1162		snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
1163		if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]),
1164				 ares->rest[i]) == -1)
1165			return -1;
1166	}
1167
1168	/* Should have ended up at the end */
1169	if (get_srvr_end(h) == -1)
1170		return -1;
1171
1172	/* Sanity checks */
1173	if (!h->single_connect)
1174		close_connection(h);
1175
1176	return ares->av_cnt << 8 | ares->status;
1177}
1178
1179int
1180tac_send_acct(struct tac_handle *h)
1181{
1182	register int i, current;
1183	struct tac_acct_start *as = &h->request.u.acct_start;
1184	struct tac_acct_reply *ar = &h->response.u.acct_reply;
1185
1186	/* start */
1187	as = &h->request.u.acct_start;
1188	h->request.length = htonl(offsetof(struct tac_acct_start, rest[0]));
1189	for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++)
1190		if (h->avs[i].len && h->avs[i].data)
1191			as->av_cnt++;
1192	h->request.length = ntohl(htonl(h->request.length) + as->av_cnt);
1193
1194	if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1195	    add_str_8(h, &as->port_len, &h->port) == -1 ||
1196	    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1)
1197		return -1;
1198
1199	for (i = current = 0; i < MAXAVPAIRS; i++)
1200		if (h->avs[i].len && h->avs[i].data)
1201			if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1)
1202				return -1;
1203
1204	/* send */
1205	if (send_msg(h) == -1 || recv_msg(h) == -1)
1206		return -1;
1207
1208	/* reply */
1209	h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]);
1210	if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1211	    get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1212	    get_srvr_end(h) == -1)
1213		return -1;
1214
1215	/* Sanity checks */
1216	if (!h->single_connect)
1217		close_connection(h);
1218
1219	return ar->status;
1220}
1221
1222int
1223tac_set_rem_addr(struct tac_handle *h, const char *addr)
1224{
1225	return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
1226}
1227
1228int
1229tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
1230{
1231	return save_str(h, &h->data, data, data_len);
1232}
1233
1234int
1235tac_set_msg(struct tac_handle *h, const char *msg)
1236{
1237	return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1238}
1239
1240int
1241tac_set_port(struct tac_handle *h, const char *port)
1242{
1243	return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1244}
1245
1246int
1247tac_set_priv(struct tac_handle *h, int priv)
1248{
1249	if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1250		generr(h, "Attempt to set invalid privilege level");
1251		return -1;
1252	}
1253	h->request.u.authen_start.priv_lvl = priv;
1254	return 0;
1255}
1256
1257int
1258tac_set_user(struct tac_handle *h, const char *user)
1259{
1260	return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1261}
1262
1263int
1264tac_set_av(struct tac_handle *h, u_int index, const char *av)
1265{
1266	if (index >= MAXAVPAIRS)
1267		return -1;
1268	return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
1269}
1270
1271char *
1272tac_get_av(struct tac_handle *h, u_int index)
1273{
1274	if (index >= MAXAVPAIRS)
1275		return NULL;
1276	return dup_str(h, &(h->srvr_avs[index]), NULL);
1277}
1278
1279char *
1280tac_get_av_value(struct tac_handle *h, const char *attribute)
1281{
1282	int i, len;
1283	const char *ch, *end;
1284	const char *candidate;
1285	int   candidate_len;
1286	int   found_seperator;
1287	struct srvr_str srvr;
1288
1289	if (attribute == NULL || ((len = strlen(attribute)) == 0))
1290		return NULL;
1291
1292	for (i=0; i<MAXAVPAIRS; i++) {
1293		candidate = h->srvr_avs[i].data;
1294		candidate_len = h->srvr_avs[i].len;
1295
1296		/*
1297		 * Valid 'srvr_avs' guaranteed to be contiguous starting at
1298		 * index 0 (not necessarily the case with 'avs').  Break out
1299		 * when the "end" of the list has been reached.
1300		 */
1301		if (!candidate)
1302			break;
1303
1304		if (len < candidate_len &&
1305		    !strncmp(candidate, attribute, len)) {
1306
1307			ch = candidate + len;
1308			end = candidate + candidate_len;
1309
1310			/*
1311			 * Sift out the white space between A and V (should not
1312			 * be any, but don't trust implementation of server...)
1313			 */
1314			found_seperator = 0;
1315			while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
1316				*ch == '\t') && ch != end) {
1317				if (*ch == '=' || *ch == '*')
1318					found_seperator++;
1319				ch++;
1320			}
1321
1322			/*
1323			 * Note:
1324			 *     The case of 'attribute' == "foo" and
1325			 *     h->srvr_avs[0] = "foobie=var1"
1326			 *     h->srvr_avs[1] = "foo=var2"
1327			 * is handled.
1328			 *
1329			 * Note that for empty string attribute values a
1330			 * 0-length string is returned in order to distinguish
1331			 * against unset values.
1332			 * dup_str() will handle srvr.len == 0 correctly.
1333			 */
1334			if (found_seperator == 1) {
1335				srvr.len = end - ch;
1336				srvr.data = ch;
1337				return dup_str(h, &srvr, NULL);
1338			}
1339		}
1340	}
1341	return NULL;
1342}
1343
1344void
1345tac_clear_avs(struct tac_handle *h)
1346{
1347	int i;
1348	for (i=0; i<MAXAVPAIRS; i++)
1349		save_str(h, &(h->avs[i]), NULL, 0);
1350}
1351
1352static void
1353clear_srvr_avs(struct tac_handle *h)
1354{
1355	int i;
1356	for (i=0; i<MAXAVPAIRS; i++)
1357		init_srvr_str(&(h->srvr_avs[i]));
1358}
1359
1360
1361const char *
1362tac_strerror(struct tac_handle *h)
1363{
1364	return h->errmsg;
1365}
1366
1367static void *
1368xmalloc(struct tac_handle *h, size_t size)
1369{
1370	void *r;
1371
1372	if ((r = malloc(size)) == NULL)
1373		generr(h, "Out of memory");
1374	return r;
1375}
1376
1377static char *
1378xstrdup(struct tac_handle *h, const char *s)
1379{
1380	char *r;
1381
1382	if ((r = strdup(s)) == NULL)
1383		generr(h, "Out of memory");
1384	return r;
1385}
1386