xref: /freebsd/lib/libtacplus/taclib.c (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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/types.h>
30 #include <sys/socket.h>
31 #include <sys/time.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
34 
35 #include <assert.h>
36 #include <ctype.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <md5.h>
40 #include <netdb.h>
41 #include <stdarg.h>
42 #include <stddef.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 
48 #include <security/pam_appl.h>
49 #include <security/openpam.h>
50 
51 #include "taclib_private.h"
52 
53 static int		 add_str_8(struct tac_handle *, u_int8_t *,
54 			    struct tac_str *);
55 static int		 add_str_16(struct tac_handle *, u_int16_t *,
56 			    struct tac_str *);
57 static int		 protocol_version(int, int, int);
58 static void		 close_connection(struct tac_handle *);
59 static int		 conn_server(struct tac_handle *);
60 static void		 crypt_msg(struct tac_handle *, struct tac_msg *);
61 static void		*dup_str(struct tac_handle *, const struct tac_str *,
62 			    size_t *);
63 static int		 establish_connection(struct tac_handle *);
64 static void		 free_str(struct tac_str *);
65 static void		 generr(struct tac_handle *, const char *, ...)
66 			    __printflike(2, 3);
67 static void		 gen_session_id(struct tac_msg *);
68 static int		 get_srvr_end(struct tac_handle *);
69 static int		 get_str(struct tac_handle *, const char *,
70 				      struct tac_str *, size_t);
71 static void		 init_str(struct tac_str *);
72 static int		 read_timed(struct tac_handle *, void *, size_t,
73 			    const struct timeval *);
74 static int		 recv_msg(struct tac_handle *);
75 static int		 save_str(struct tac_handle *, struct tac_str *,
76 			    const void *, size_t);
77 static int		 send_msg(struct tac_handle *);
78 static int		 split(char *, char *[], int, char *, size_t);
79 static void		*xmalloc(struct tac_handle *, size_t);
80 static char		*xstrdup(struct tac_handle *, const char *);
81 static void              clear_srvr_avs(struct tac_handle *);
82 static 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  */
92 static int
93 add_str_8(struct tac_handle *h, u_int8_t *fld, struct tac_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  */
116 static int
117 add_str_16(struct tac_handle *h, u_int16_t *fld, struct tac_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 
144 static int
145 protocol_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 
227 static void
228 close_connection(struct tac_handle *h)
229 {
230 	if (h->fd != -1) {
231 		close(h->fd);
232 		h->fd = -1;
233 	}
234 }
235 
236 static int
237 conn_server(struct tac_handle *h)
238 {
239 	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  */
311 static void
312 crypt_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  */
359 static void *
360 dup_str(struct tac_handle *h, const struct tac_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 
374 static int
375 establish_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  */
406 static void
407 free_str(struct tac_str *cs)
408 {
409 	if (cs->data != NULL) {
410 		memset_s(cs->data, cs->len, 0, cs->len);
411 		free(cs->data);
412 		cs->data = NULL;
413 		cs->len = 0;
414 	}
415 }
416 
417 static void
418 generr(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 
427 static void
428 gen_session_id(struct tac_msg *msg)
429 {
430 	int r;
431 
432 	r = arc4random();
433 	msg->session_id[0] = r >> 8;
434 	msg->session_id[1] = r;
435 	r = arc4random();
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  */
444 static int
445 get_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 
460 static int
461 get_str(struct tac_handle *h, const char *field,
462     struct tac_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 
476 static void
477 init_str(struct tac_str *cs)
478 {
479 	cs->data = NULL;
480 	cs->len = 0;
481 }
482 
483 static int
484 read_timed(struct tac_handle *h, void *buf, size_t len,
485     const struct timeval *deadline)
486 {
487 	char *ptr;
488 
489 	ptr = (char *)buf;
490 	while (len > 0) {
491 		int n;
492 
493 		n = read(h->fd, ptr, len);
494 		if (n == -1) {
495 			struct timeval tv;
496 			int nfds;
497 
498 			if (errno != EAGAIN) {
499 				generr(h, "Network read error: %s",
500 				    strerror(errno));
501 				return -1;
502 			}
503 
504 			/* Wait until we can read more data. */
505 			gettimeofday(&tv, NULL);
506 			timersub(deadline, &tv, &tv);
507 			if (tv.tv_sec >= 0) {
508 				fd_set rfds;
509 
510 				FD_ZERO(&rfds);
511 				FD_SET(h->fd, &rfds);
512 				nfds =
513 				    select(h->fd + 1, &rfds, NULL, NULL, &tv);
514 				if (nfds == -1) {
515 					generr(h, "select: %s",
516 					    strerror(errno));
517 					return -1;
518 				}
519 			} else
520 				nfds = 0;
521 			if (nfds == 0) {
522 				generr(h, "Network read timed out");
523 				return -1;
524 			}
525 		} else if (n == 0) {
526 			generr(h, "unexpected EOF from server");
527 			return -1;
528 		} else {
529 			ptr += n;
530 			len -= n;
531 		}
532 	}
533 	return 0;
534 }
535 
536 /*
537  * Receive a response from the server and decrypt it.  Returns 0 on
538  * success, or -1 on failure.
539  */
540 static int
541 recv_msg(struct tac_handle *h)
542 {
543 	struct timeval deadline;
544 	struct tac_msg *msg;
545 	u_int32_t len;
546 
547 	msg = &h->response;
548 	gettimeofday(&deadline, NULL);
549 	deadline.tv_sec += h->servers[h->cur_server].timeout;
550 
551 	/* Read the message header and make sure it is reasonable. */
552 	if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
553 		return -1;
554 	if (memcmp(msg->session_id, h->request.session_id,
555 	    sizeof msg->session_id) != 0) {
556 		generr(h, "Invalid session ID in received message");
557 		return -1;
558 	}
559 	if (msg->type != h->request.type) {
560 		generr(h, "Invalid type in received message"
561 			  " (got %u, expected %u)",
562 			  msg->type, h->request.type);
563 		return -1;
564 	}
565 	len = ntohl(msg->length);
566 	if (len > BODYSIZE) {
567 		generr(h, "Received message too large (%u > %u)",
568 			  len, BODYSIZE);
569 		return -1;
570 	}
571 	if (msg->seq_no != ++h->last_seq_no) {
572 		generr(h, "Invalid sequence number in received message"
573 			  " (got %u, expected %u)",
574 			  msg->seq_no, h->last_seq_no);
575 		return -1;
576 	}
577 
578 	/* Read the message body. */
579 	if (read_timed(h, msg->u.body, len, &deadline) == -1)
580 		return -1;
581 
582 	/* Decrypt it. */
583 	crypt_msg(h, msg);
584 
585 	/*
586 	 * Turn off single-connection mode if the server isn't amenable
587 	 * to it.
588 	 */
589 	if (!(msg->flags & TAC_SINGLE_CONNECT))
590 		h->single_connect = 0;
591 	return 0;
592 }
593 
594 static int
595 save_str(struct tac_handle *h, struct tac_str *cs, const void *data,
596     size_t len)
597 {
598 	free_str(cs);
599 	if (data != NULL && len != 0) {
600 		if ((cs->data = xmalloc(h, len)) == NULL)
601 			return -1;
602 		cs->len = len;
603 		memcpy(cs->data, data, len);
604 	}
605 	return 0;
606 }
607 
608 /*
609  * Send the current request, after encrypting it.  Returns 0 on success,
610  * or -1 on failure.
611  */
612 static int
613 send_msg(struct tac_handle *h)
614 {
615 	struct timeval deadline;
616 	struct tac_msg *msg;
617 	char *ptr;
618 	int len;
619 
620 	if (h->last_seq_no & 1) {
621 		generr(h, "Attempt to send message out of sequence");
622 		return -1;
623 	}
624 
625 	if (establish_connection(h) == -1)
626 		return -1;
627 
628 	msg = &h->request;
629 	msg->seq_no = ++h->last_seq_no;
630 	if (msg->seq_no == 1)
631 		gen_session_id(msg);
632 	crypt_msg(h, msg);
633 
634 	if (h->single_connect)
635 		msg->flags |= TAC_SINGLE_CONNECT;
636 	else
637 		msg->flags &= ~TAC_SINGLE_CONNECT;
638 	gettimeofday(&deadline, NULL);
639 	deadline.tv_sec += h->servers[h->cur_server].timeout;
640 	len = HDRSIZE + ntohl(msg->length);
641 	ptr = (char *)msg;
642 	while (len > 0) {
643 		int n;
644 
645 		n = write(h->fd, ptr, len);
646 		if (n == -1) {
647 			struct timeval tv;
648 			int nfds;
649 
650 			if (errno != EAGAIN) {
651 				generr(h, "Network write error: %s",
652 				    strerror(errno));
653 				return -1;
654 			}
655 
656 			/* Wait until we can write more data. */
657 			gettimeofday(&tv, NULL);
658 			timersub(&deadline, &tv, &tv);
659 			if (tv.tv_sec >= 0) {
660 				fd_set wfds;
661 
662 				FD_ZERO(&wfds);
663 				FD_SET(h->fd, &wfds);
664 				nfds =
665 				    select(h->fd + 1, NULL, &wfds, NULL, &tv);
666 				if (nfds == -1) {
667 					generr(h, "select: %s",
668 					    strerror(errno));
669 					return -1;
670 				}
671 			} else
672 				nfds = 0;
673 			if (nfds == 0) {
674 				generr(h, "Network write timed out");
675 				return -1;
676 			}
677 		} else {
678 			ptr += n;
679 			len -= n;
680 		}
681 	}
682 	return 0;
683 }
684 
685 static int
686 tac_add_server_av(struct tac_handle *h, const char *host, int port,
687     const char *secret, int timeout, int flags, const char *const *avs)
688 {
689 	struct tac_server *srvp;
690 	const char *p;
691 	size_t len;
692 	int i;
693 
694 	if (h->num_servers >= MAXSERVERS) {
695 		generr(h, "Too many TACACS+ servers specified");
696 		return -1;
697 	}
698 	srvp = &h->servers[h->num_servers];
699 
700 	memset(&srvp->addr, 0, sizeof srvp->addr);
701 	srvp->addr.sin_len = sizeof srvp->addr;
702 	srvp->addr.sin_family = AF_INET;
703 	if (!inet_aton(host, &srvp->addr.sin_addr)) {
704 		struct hostent *hent;
705 
706 		if ((hent = gethostbyname(host)) == NULL) {
707 			generr(h, "%s: host not found", host);
708 			return -1;
709 		}
710 		memcpy(&srvp->addr.sin_addr, hent->h_addr,
711 		    sizeof srvp->addr.sin_addr);
712 	}
713 	srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
714 	if ((srvp->secret = xstrdup(h, secret)) == NULL)
715 		return -1;
716 	srvp->timeout = timeout;
717 	srvp->flags = flags;
718 	srvp->navs = 0;
719 	for (i = 0; avs[i] != NULL; i++) {
720 		if (i >= MAXAVPAIRS) {
721 			generr(h, "too many AV pairs");
722 			goto fail;
723 		}
724 		for (p = avs[i], len = 0; is_arg(*p); p++)
725 			len++;
726 		if (p == avs[i] || *p != '=') {
727 			generr(h, "invalid AV pair %d", i);
728 			goto fail;
729 		}
730 		while (*p++ != '\0')
731 			len++;
732 		if ((srvp->avs[i].data = xstrdup(h, avs[i])) == NULL)
733 			goto fail;
734 		srvp->avs[i].len = len;
735 		srvp->navs++;
736 	}
737 	h->num_servers++;
738 	return 0;
739 fail:
740 	memset_s(srvp->secret, strlen(srvp->secret), 0, strlen(srvp->secret));
741 	free(srvp->secret);
742 	srvp->secret = NULL;
743 	for (i = 0; i < srvp->navs; i++) {
744 		free(srvp->avs[i].data);
745 		srvp->avs[i].data = NULL;
746 		srvp->avs[i].len = 0;
747 	}
748 	return -1;
749 }
750 
751 int
752 tac_add_server(struct tac_handle *h, const char *host, int port,
753     const char *secret, int timeout, int flags)
754 {
755 	const char *const *avs = { NULL };
756 
757 	return tac_add_server_av(h, host, port, secret, timeout, flags, avs);
758 }
759 
760 void
761 tac_close(struct tac_handle *h)
762 {
763 	int i, srv;
764 
765 	if (h->fd != -1)
766 		close(h->fd);
767 	for (srv = 0;  srv < h->num_servers;  srv++) {
768 		memset(h->servers[srv].secret, 0,
769 		    strlen(h->servers[srv].secret));
770 		free(h->servers[srv].secret);
771 	}
772 	free_str(&h->user);
773 	free_str(&h->port);
774 	free_str(&h->rem_addr);
775 	free_str(&h->data);
776 	free_str(&h->user_msg);
777 	for (i=0; i<MAXAVPAIRS; i++)
778 		free_str(&(h->avs[i]));
779 
780 	/* Clear everything else before freeing memory */
781 	memset(h, 0, sizeof(struct tac_handle));
782 	free(h);
783 }
784 
785 static void
786 freev(char **fields, int nfields)
787 {
788 	if (fields != NULL) {
789 		while (nfields-- > 0)
790 			free(fields[nfields]);
791 		free(fields);
792 	}
793 }
794 
795 int
796 tac_config(struct tac_handle *h, const char *path)
797 {
798 	FILE *fp;
799 	char **fields;
800 	int linenum, nfields;
801 	int retval;
802 
803 	if (path == NULL)
804 		path = PATH_TACPLUS_CONF;
805 	if ((fp = fopen(path, "r")) == NULL) {
806 		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
807 		return -1;
808 	}
809 	retval = 0;
810 	linenum = nfields = 0;
811 	fields = NULL;
812 	while ((fields = openpam_readlinev(fp, &linenum, &nfields)) != NULL) {
813 		char *host, *res;
814 		char *port_str;
815 		char *secret;
816 		char *timeout_str;
817 		char *end;
818 		unsigned long timeout;
819 		int port;
820 		int options;
821 		int i;
822 
823 		if (nfields == 0) {
824 			freev(fields, nfields);
825 			continue;
826 		}
827 		if (nfields < 2) {
828 			generr(h, "%s:%d: missing shared secret", path,
829 			    linenum);
830 			retval = -1;
831 			break;
832 		}
833 		host = fields[0];
834 		secret = fields[1];
835 
836 		/* Parse and validate the fields. */
837 		res = host;
838 		host = strsep(&res, ":");
839 		port_str = strsep(&res, ":");
840 		if (port_str != NULL) {
841 			port = strtoul(port_str, &end, 10);
842 			if (port_str[0] == '\0' || *end != '\0') {
843 				generr(h, "%s:%d: invalid port", path,
844 				    linenum);
845 				retval = -1;
846 				break;
847 			}
848 		} else
849 			port = 0;
850 		i = 2;
851 		if (nfields > i && strlen(fields[i]) > 0 &&
852 		    strspn(fields[i], "0123456789") == strlen(fields[i])) {
853 			timeout_str = fields[i];
854 			timeout = strtoul(timeout_str, &end, 10);
855 			if (timeout_str[0] == '\0' || *end != '\0') {
856 				generr(h, "%s:%d: invalid timeout", path,
857 				    linenum);
858 				retval = -1;
859 				break;
860 			}
861 			i++;
862 		} else
863 			timeout = TIMEOUT;
864 		options = 0;
865 		if (nfields > i &&
866 		    strcmp(fields[i], "single-connection") == 0) {
867 			options |= TAC_SRVR_SINGLE_CONNECT;
868 			i++;
869 		}
870 		if (tac_add_server_av(h, host, port, secret, timeout,
871 		    options, (const char *const *)(fields + i)) == -1) {
872 			char msg[ERRSIZE];
873 
874 			strcpy(msg, h->errmsg);
875 			generr(h, "%s:%d: %s", path, linenum, msg);
876 			retval = -1;
877 			break;
878 		}
879 		memset_s(secret, strlen(secret), 0, strlen(secret));
880 		freev(fields, nfields);
881 	}
882 	freev(fields, nfields);
883 	fclose(fp);
884 	return retval;
885 }
886 
887 int
888 tac_create_authen(struct tac_handle *h, int action, int type, int service)
889 {
890 	struct tac_authen_start *as;
891 
892 	create_msg(h, TAC_AUTHEN, action, type);
893 
894 	as = &h->request.u.authen_start;
895 	as->action = action;
896 	as->priv_lvl = TAC_PRIV_LVL_USER;
897 	as->authen_type = type;
898 	as->service = service;
899 
900 	return 0;
901 }
902 
903 int
904 tac_create_author(struct tac_handle *h, int method, int type, int service)
905 {
906 	struct tac_author_request *areq;
907 
908 	create_msg(h, TAC_AUTHOR, method, type);
909 
910 	areq = &h->request.u.author_request;
911 	areq->authen_meth = method;
912 	areq->priv_lvl = TAC_PRIV_LVL_USER;
913 	areq->authen_type = type;
914 	areq->service = service;
915 
916 	return 0;
917 }
918 
919 int
920 tac_create_acct(struct tac_handle *h, int acct, int action, int type, int service)
921 {
922 	struct tac_acct_start *as;
923 
924 	create_msg(h, TAC_ACCT, action, type);
925 
926 	as = &h->request.u.acct_start;
927 	as->action = acct;
928 	as->authen_action = action;
929 	as->priv_lvl = TAC_PRIV_LVL_USER;
930 	as->authen_type = type;
931 	as->authen_service = service;
932 
933 	return 0;
934 }
935 
936 static void
937 create_msg(struct tac_handle *h, int msg_type, int var, int type)
938 {
939 	struct tac_msg *msg;
940 	int i;
941 
942 	h->last_seq_no = 0;
943 
944 	msg = &h->request;
945 	msg->type = msg_type;
946 	msg->version = protocol_version(msg_type, var, type);
947 	msg->flags = 0; /* encrypted packet body */
948 
949 	free_str(&h->user);
950 	free_str(&h->port);
951 	free_str(&h->rem_addr);
952 	free_str(&h->data);
953 	free_str(&h->user_msg);
954 
955 	for (i=0; i<MAXAVPAIRS; i++)
956 		free_str(&(h->avs[i]));
957 }
958 
959 void *
960 tac_get_data(struct tac_handle *h, size_t *len)
961 {
962 	return dup_str(h, &h->srvr_data, len);
963 }
964 
965 char *
966 tac_get_msg(struct tac_handle *h)
967 {
968 	return dup_str(h, &h->srvr_msg, NULL);
969 }
970 
971 /*
972  * Create and initialize a tac_handle structure, and return it to the
973  * caller.  Can fail only if the necessary memory cannot be allocated.
974  * In that case, it returns NULL.
975  */
976 struct tac_handle *
977 tac_open(void)
978 {
979 	int i;
980 	struct tac_handle *h;
981 
982 	h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
983 	if (h != NULL) {
984 		h->fd = -1;
985 		h->num_servers = 0;
986 		h->cur_server = 0;
987 		h->errmsg[0] = '\0';
988 		init_str(&h->user);
989 		init_str(&h->port);
990 		init_str(&h->rem_addr);
991 		init_str(&h->data);
992 		init_str(&h->user_msg);
993 		for (i=0; i<MAXAVPAIRS; i++) {
994 			init_str(&(h->avs[i]));
995 			init_str(&(h->srvr_avs[i]));
996 		}
997 		init_str(&h->srvr_msg);
998 		init_str(&h->srvr_data);
999 	}
1000 	return h;
1001 }
1002 
1003 int
1004 tac_send_authen(struct tac_handle *h)
1005 {
1006 	struct tac_authen_reply *ar;
1007 
1008 	if (h->num_servers == 0)
1009 	    return -1;
1010 
1011 	if (h->last_seq_no == 0) {	/* Authentication START packet */
1012 		struct tac_authen_start *as;
1013 
1014 		as = &h->request.u.authen_start;
1015 		h->request.length =
1016 		    htonl(offsetof(struct tac_authen_start, rest[0]));
1017 		if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1018 		    add_str_8(h, &as->port_len, &h->port) == -1 ||
1019 		    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
1020 		    add_str_8(h, &as->data_len, &h->data) == -1)
1021 			return -1;
1022 	} else {			/* Authentication CONTINUE packet */
1023 		struct tac_authen_cont *ac;
1024 
1025 		ac = &h->request.u.authen_cont;
1026 		ac->flags = 0;
1027 		h->request.length =
1028 		    htonl(offsetof(struct tac_authen_cont, rest[0]));
1029 		if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
1030 		    add_str_16(h, &ac->data_len, &h->data) == -1)
1031 			return -1;
1032 	}
1033 
1034 	/* Send the message and retrieve the reply. */
1035 	if (send_msg(h) == -1 || recv_msg(h) == -1)
1036 		return -1;
1037 
1038 	/* Scan the optional fields in the reply. */
1039 	ar = &h->response.u.authen_reply;
1040 	h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
1041 	if (get_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1042 	    get_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1043 	    get_srvr_end(h) == -1)
1044 		return -1;
1045 
1046 	if (!h->single_connect &&
1047 	    ar->status != TAC_AUTHEN_STATUS_GETDATA &&
1048 	    ar->status != TAC_AUTHEN_STATUS_GETUSER &&
1049 	    ar->status != TAC_AUTHEN_STATUS_GETPASS)
1050 		close_connection(h);
1051 
1052 	return ar->flags << 8 | ar->status;
1053 }
1054 
1055 int
1056 tac_send_author(struct tac_handle *h)
1057 {
1058 	int i, current;
1059 	char dbgstr[64];
1060 	struct tac_author_request *areq = &h->request.u.author_request;
1061 	struct tac_author_response *ares = &h->response.u.author_response;
1062 	struct tac_server *srvp;
1063 
1064 	h->request.length =
1065 		htonl(offsetof(struct tac_author_request, rest[0]));
1066 
1067 	/* Count each specified AV pair */
1068 	for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
1069 		if (h->avs[i].len && h->avs[i].data)
1070 			areq->av_cnt++;
1071 
1072 	/*
1073 	 * Each AV size is a byte starting right after 'av_cnt'.  Update the
1074 	 * offset to include these AV sizes.
1075 	 */
1076 	h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
1077 
1078 	/* Now add the string arguments from 'h' */
1079 	if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
1080 	    add_str_8(h, &areq->port_len, &h->port) == -1 ||
1081 	    add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
1082 		return -1;
1083 
1084 	/* Add each AV pair, the size of each placed in areq->rest[current] */
1085 	for (current=0, i=0; i<MAXAVPAIRS; i++) {
1086 		if (h->avs[i].len && h->avs[i].data) {
1087 			if (add_str_8(h, &areq->rest[current++],
1088 				      &(h->avs[i])) == -1)
1089 				return -1;
1090 		}
1091 	}
1092 
1093 	/* Send the message and retrieve the reply. */
1094 	if (send_msg(h) == -1 || recv_msg(h) == -1)
1095 		return -1;
1096 	srvp = &h->servers[h->cur_server];
1097 
1098 	/* Update the offset in the response packet based on av pairs count */
1099 	h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
1100 		ares->av_cnt;
1101 
1102 	/* Scan the optional fields in the response. */
1103 	if (get_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
1104 	    get_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
1105 		return -1;
1106 
1107 	/* Get each AV pair (just setting pointers, not malloc'ing) */
1108 	clear_srvr_avs(h);
1109 	for (i=0; i<ares->av_cnt; i++) {
1110 		snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
1111 		if (get_str(h, dbgstr, &(h->srvr_avs[i]),
1112 				 ares->rest[i]) == -1)
1113 			return -1;
1114 		h->srvr_navs++;
1115 	}
1116 
1117 	/* Should have ended up at the end */
1118 	if (get_srvr_end(h) == -1)
1119 		return -1;
1120 
1121 	/* Sanity checks */
1122 	if (!h->single_connect)
1123 		close_connection(h);
1124 
1125 	return (h->srvr_navs + srvp->navs) << 8 | ares->status;
1126 }
1127 
1128 int
1129 tac_send_acct(struct tac_handle *h)
1130 {
1131 	register int i, current;
1132 	struct tac_acct_start *as = &h->request.u.acct_start;
1133 	struct tac_acct_reply *ar = &h->response.u.acct_reply;
1134 
1135 	/* start */
1136 	as = &h->request.u.acct_start;
1137 	h->request.length = htonl(offsetof(struct tac_acct_start, rest[0]));
1138 	for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++)
1139 		if (h->avs[i].len && h->avs[i].data)
1140 			as->av_cnt++;
1141 	h->request.length = ntohl(htonl(h->request.length) + as->av_cnt);
1142 
1143 	if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1144 	    add_str_8(h, &as->port_len, &h->port) == -1 ||
1145 	    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1)
1146 		return -1;
1147 
1148 	for (i = current = 0; i < MAXAVPAIRS; i++)
1149 		if (h->avs[i].len && h->avs[i].data)
1150 			if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1)
1151 				return -1;
1152 
1153 	/* send */
1154 	if (send_msg(h) == -1 || recv_msg(h) == -1)
1155 		return -1;
1156 
1157 	/* reply */
1158 	h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]);
1159 	if (get_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1160 	    get_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1161 	    get_srvr_end(h) == -1)
1162 		return -1;
1163 
1164 	/* Sanity checks */
1165 	if (!h->single_connect)
1166 		close_connection(h);
1167 
1168 	return ar->status;
1169 }
1170 
1171 int
1172 tac_set_rem_addr(struct tac_handle *h, const char *addr)
1173 {
1174 	return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
1175 }
1176 
1177 int
1178 tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
1179 {
1180 	return save_str(h, &h->data, data, data_len);
1181 }
1182 
1183 int
1184 tac_set_msg(struct tac_handle *h, const char *msg)
1185 {
1186 	return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1187 }
1188 
1189 int
1190 tac_set_port(struct tac_handle *h, const char *port)
1191 {
1192 	return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1193 }
1194 
1195 int
1196 tac_set_priv(struct tac_handle *h, int priv)
1197 {
1198 	if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1199 		generr(h, "Attempt to set invalid privilege level");
1200 		return -1;
1201 	}
1202 	h->request.u.authen_start.priv_lvl = priv;
1203 	return 0;
1204 }
1205 
1206 int
1207 tac_set_user(struct tac_handle *h, const char *user)
1208 {
1209 	return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1210 }
1211 
1212 int
1213 tac_set_av(struct tac_handle *h, u_int index, const char *av)
1214 {
1215 	if (index >= MAXAVPAIRS)
1216 		return -1;
1217 	return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
1218 }
1219 
1220 char *
1221 tac_get_av(struct tac_handle *h, u_int index)
1222 {
1223 	struct tac_server *srvp;
1224 
1225 	if (index < h->srvr_navs)
1226 		return dup_str(h, &h->srvr_avs[index], NULL);
1227 	index -= h->srvr_navs;
1228 	srvp = &h->servers[h->cur_server];
1229 	if (index < srvp->navs)
1230 		return xstrdup(h, srvp->avs[index].data);
1231 	return NULL;
1232 }
1233 
1234 char *
1235 tac_get_av_value(struct tac_handle *h, const char *attribute)
1236 {
1237 	int i, attr_len;
1238 	int   found_seperator;
1239 	char *ch, *end;
1240 	struct tac_str *candidate;
1241 	struct tac_str value;
1242 	struct tac_server *srvp = &h->servers[h->cur_server];
1243 
1244 	if (attribute == NULL || (attr_len = strlen(attribute)) == 0)
1245 		return NULL;
1246 
1247 	for (i = 0; i < h->srvr_navs + srvp->navs; i++) {
1248 		if (i < h->srvr_navs)
1249 			candidate = &h->srvr_avs[i];
1250 		else
1251 			candidate = &srvp->avs[i - h->srvr_navs];
1252 
1253 		if (attr_len < candidate->len &&
1254 		    strncmp(candidate->data, attribute, attr_len) == 0) {
1255 
1256 			ch = candidate->data + attr_len;
1257 			end = candidate->data + candidate->len;
1258 
1259 			/*
1260 			 * Sift out the white space between A and V (should not
1261 			 * be any, but don't trust implementation of server...)
1262 			 */
1263 			found_seperator = 0;
1264 			while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
1265 				*ch == '\t') && ch != end) {
1266 				if (*ch == '=' || *ch == '*')
1267 					found_seperator++;
1268 				ch++;
1269 			}
1270 
1271 			/*
1272 			 * Note:
1273 			 *     The case of 'attribute' == "foo" and
1274 			 *     h->srvr_avs[0] = "foobie=var1"
1275 			 *     h->srvr_avs[1] = "foo=var2"
1276 			 * is handled.
1277 			 *
1278 			 * Note that for empty string attribute values a
1279 			 * 0-length string is returned in order to distinguish
1280 			 * against unset values.
1281 			 * dup_str() will handle srvr.len == 0 correctly.
1282 			 */
1283 			if (found_seperator == 1) {
1284 				value.len = end - ch;
1285 				value.data = ch;
1286 				return dup_str(h, &value, NULL);
1287 			}
1288 		}
1289 	}
1290 	return NULL;
1291 }
1292 
1293 void
1294 tac_clear_avs(struct tac_handle *h)
1295 {
1296 	int i;
1297 	for (i=0; i<MAXAVPAIRS; i++)
1298 		save_str(h, &(h->avs[i]), NULL, 0);
1299 }
1300 
1301 static void
1302 clear_srvr_avs(struct tac_handle *h)
1303 {
1304 	int i;
1305 
1306 	for (i = 0; i < h->srvr_navs; i++)
1307 		init_str(&(h->srvr_avs[i]));
1308 	h->srvr_navs = 0;
1309 }
1310 
1311 
1312 const char *
1313 tac_strerror(struct tac_handle *h)
1314 {
1315 	return h->errmsg;
1316 }
1317 
1318 static void *
1319 xmalloc(struct tac_handle *h, size_t size)
1320 {
1321 	void *r;
1322 
1323 	if ((r = malloc(size)) == NULL)
1324 		generr(h, "Out of memory");
1325 	return r;
1326 }
1327 
1328 static char *
1329 xstrdup(struct tac_handle *h, const char *s)
1330 {
1331 	char *r;
1332 
1333 	if ((r = strdup(s)) == NULL)
1334 		generr(h, "Out of memory");
1335 	return r;
1336 }
1337