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