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