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