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