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