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