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