btools: Strip libmd dep for usr.bin/sort.
[dragonfly.git] / lib / libtacplus / taclib.c
blobcf18dd562f68fa77567d3c647c8ad6542b10dfec
1 /*-
2 * Copyright (c) 1998, 2001, 2002, Juniper Networks, Inc.
3 * All rights reserved.
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.
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.
26 * $FreeBSD: src/lib/libtacplus/taclib.c,v 1.2.2.2 2002/10/09 08:50:42 pst Exp $
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/time.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
35 #include <assert.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <md5.h>
39 #include <netdb.h>
40 #include <stdarg.h>
41 #include <stddef.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
47 #include "taclib_private.h"
49 static int add_str_8(struct tac_handle *, u_int8_t *,
50 struct clnt_str *);
51 static int add_str_16(struct tac_handle *, u_int16_t *,
52 struct clnt_str *);
53 static int protocol_version(int, int, int);
54 static void close_connection(struct tac_handle *);
55 static int conn_server(struct tac_handle *);
56 static void crypt_msg(struct tac_handle *, struct tac_msg *);
57 static void *dup_str(struct tac_handle *, const struct srvr_str *,
58 size_t *);
59 static int establish_connection(struct tac_handle *);
60 static void free_str(struct clnt_str *);
61 static void generr(struct tac_handle *, const char *, ...)
62 __printflike(2, 3);
63 static void gen_session_id(struct tac_msg *);
64 static int get_srvr_end(struct tac_handle *);
65 static int get_srvr_str(struct tac_handle *, const char *,
66 struct srvr_str *, size_t);
67 static void init_clnt_str(struct clnt_str *);
68 static void init_srvr_str(struct srvr_str *);
69 static int read_timed(struct tac_handle *, void *, size_t,
70 const struct timeval *);
71 static int recv_msg(struct tac_handle *);
72 static int save_str(struct tac_handle *, struct clnt_str *,
73 const void *, size_t);
74 static int send_msg(struct tac_handle *);
75 static int split(char *, char *[], int, char *, size_t);
76 static void *xmalloc(struct tac_handle *, size_t);
77 static char *xstrdup(struct tac_handle *, const char *);
78 static void clear_srvr_avs(struct tac_handle *);
79 static void create_msg(struct tac_handle *, int, int, int);
82 * Append some optional data to the current request, and store its
83 * length into the 8-bit field referenced by "fld". Returns 0 on
84 * success, or -1 on failure.
86 * This function also frees the "cs" string data and initializes it
87 * for the next time.
89 static int
90 add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
92 u_int16_t len;
94 if (add_str_16(h, &len, cs) == -1)
95 return -1;
96 len = ntohs(len);
97 if (len > 0xff) {
98 generr(h, "Field too long");
99 return -1;
101 *fld = len;
102 return 0;
106 * Append some optional data to the current request, and store its
107 * length into the 16-bit field (network byte order) referenced by
108 * "fld". Returns 0 on success, or -1 on failure.
110 * This function also frees the "cs" string data and initializes it
111 * for the next time.
113 static int
114 add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
116 size_t len;
118 len = cs->len;
119 if (cs->data == NULL)
120 len = 0;
121 if (len != 0) {
122 int offset;
124 if (len > 0xffff) {
125 generr(h, "Field too long");
126 return -1;
128 offset = ntohl(h->request.length);
129 if (offset + len > BODYSIZE) {
130 generr(h, "Message too long");
131 return -1;
133 memcpy(h->request.u.body + offset, cs->data, len);
134 h->request.length = htonl(offset + len);
136 *fld = htons(len);
137 free_str(cs);
138 return 0;
141 static int
142 protocol_version(int msg_type, int var, int type)
144 int minor;
146 switch (msg_type) {
147 case TAC_AUTHEN:
148 /* 'var' represents the 'action' */
149 switch (var) {
150 case TAC_AUTHEN_LOGIN:
151 switch (type) {
153 case TAC_AUTHEN_TYPE_PAP:
154 case TAC_AUTHEN_TYPE_CHAP:
155 case TAC_AUTHEN_TYPE_MSCHAP:
156 case TAC_AUTHEN_TYPE_ARAP:
157 minor = 1;
158 break;
160 default:
161 minor = 0;
162 break;
164 break;
166 case TAC_AUTHEN_SENDAUTH:
167 minor = 1;
168 break;
170 default:
171 minor = 0;
172 break;
174 break;
176 case TAC_AUTHOR:
177 /* 'var' represents the 'method' */
178 switch (var) {
180 * When new authentication methods are added, include 'method'
181 * in determining the value of 'minor'. At this point, all
182 * methods defined in this implementation (see "Authorization
183 * authentication methods" in taclib.h) are minor version 0
184 * Not all types, however, indicate minor version 0.
186 case TAC_AUTHEN_METH_NOT_SET:
187 case TAC_AUTHEN_METH_NONE:
188 case TAC_AUTHEN_METH_KRB5:
189 case TAC_AUTHEN_METH_LINE:
190 case TAC_AUTHEN_METH_ENABLE:
191 case TAC_AUTHEN_METH_LOCAL:
192 case TAC_AUTHEN_METH_TACACSPLUS:
193 case TAC_AUTHEN_METH_RCMD:
194 switch (type) {
195 case TAC_AUTHEN_TYPE_PAP:
196 case TAC_AUTHEN_TYPE_CHAP:
197 case TAC_AUTHEN_TYPE_MSCHAP:
198 case TAC_AUTHEN_TYPE_ARAP:
199 minor = 1;
200 break;
202 default:
203 minor = 0;
204 break;
206 break;
207 default:
208 minor = 0;
209 break;
211 break;
213 default:
214 minor = 0;
215 break;
218 return TAC_VER_MAJOR << 4 | minor;
222 static void
223 close_connection(struct tac_handle *h)
225 if (h->fd != -1) {
226 close(h->fd);
227 h->fd = -1;
231 static int
232 conn_server(struct tac_handle *h)
234 const struct tac_server *srvp = &h->servers[h->cur_server];
235 int flags;
237 if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
238 generr(h, "Cannot create socket: %s", strerror(errno));
239 return -1;
241 if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
242 fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
243 generr(h, "Cannot set non-blocking mode on socket: %s",
244 strerror(errno));
245 close(h->fd);
246 h->fd = -1;
247 return -1;
249 if (connect(h->fd, (struct sockaddr *)&srvp->addr,
250 sizeof srvp->addr) == 0)
251 return 0;
253 if (errno == EINPROGRESS) {
254 fd_set wfds;
255 struct timeval tv;
256 int nfds;
257 struct sockaddr peer;
258 int peerlen;
259 int err;
260 int errlen;
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;
274 if (nfds == 0) {
275 generr(h, "connect: timed out");
276 close(h->fd);
277 h->fd = -1;
278 return -1;
281 /* See whether we are connected now. */
282 peerlen = sizeof peer;
283 if (getpeername(h->fd, &peer, &peerlen) == 0)
284 return 0;
286 if (errno != ENOTCONN) {
287 generr(h, "getpeername: %s", strerror(errno));
288 close(h->fd);
289 h->fd = -1;
290 return -1;
293 /* Find out why the connect failed. */
294 errlen = sizeof err;
295 getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
296 errno = err;
298 generr(h, "connect: %s", strerror(errno));
299 close(h->fd);
300 h->fd = -1;
301 return -1;
305 * Encrypt or decrypt a message. The operations are symmetrical.
307 static void
308 crypt_msg(struct tac_handle *h, struct tac_msg *msg)
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;
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;
323 msg_len = ntohl(msg->length);
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);
331 ctx = base_ctx;
332 for (chunk = 0; chunk < msg_len; chunk += sizeof md5) {
333 int chunk_len;
334 int i;
336 MD5Final(md5, &ctx);
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];
343 ctx = base_ctx;
344 MD5Update(&ctx, md5, sizeof md5);
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.
355 static void *
356 dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
358 unsigned char *p;
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;
370 static int
371 establish_connection(struct tac_handle *h)
373 int i;
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;
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.
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;
392 if (++h->cur_server >= h->num_servers) /* Wrap around */
393 h->cur_server = 0;
395 /* Just return whatever error was last reported by conn_server(). */
396 return -1;
400 * Free a client string, obliterating its contents first for security.
402 static void
403 free_str(struct clnt_str *cs)
405 if (cs->data != NULL) {
406 memset(cs->data, 0, cs->len);
407 free(cs->data);
408 cs->data = NULL;
409 cs->len = 0;
413 static void
414 generr(struct tac_handle *h, const char *format, ...)
416 va_list ap;
418 va_start(ap, format);
419 vsnprintf(h->errmsg, ERRSIZE, format, ap);
420 va_end(ap);
423 static void
424 gen_session_id(struct tac_msg *msg)
426 int r;
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;
437 * Verify that we are exactly at the end of the response message.
438 * Returns 0 on success, -1 on failure.
440 static int
441 get_srvr_end(struct tac_handle *h)
443 int len;
445 len = ntohl(h->response.length);
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;
453 return 0;
456 static int
457 get_srvr_str(struct tac_handle *h, const char *field,
458 struct srvr_str *ss, size_t len)
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;
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;
472 static void
473 init_clnt_str(struct clnt_str *cs)
475 cs->data = NULL;
476 cs->len = 0;
479 static void
480 init_srvr_str(struct srvr_str *ss)
482 ss->data = NULL;
483 ss->len = 0;
486 static int
487 read_timed(struct tac_handle *h, void *buf, size_t len,
488 const struct timeval *deadline)
490 char *ptr;
492 ptr = (char *)buf;
493 while (len > 0) {
494 int n;
496 n = read(h->fd, ptr, len);
497 if (n == -1) {
498 struct timeval tv;
499 int nfds;
501 if (errno != EAGAIN) {
502 generr(h, "Network read error: %s",
503 strerror(errno));
504 return -1;
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;
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;
522 } else
523 nfds = 0;
524 if (nfds == 0) {
525 generr(h, "Network read timed out");
526 return -1;
528 } else if (n == 0) {
529 generr(h, "unexpected EOF from server");
530 return -1;
531 } else {
532 ptr += n;
533 len -= n;
536 return 0;
540 * Receive a response from the server and decrypt it. Returns 0 on
541 * success, or -1 on failure.
543 static int
544 recv_msg(struct tac_handle *h)
546 struct timeval deadline;
547 struct tac_msg *msg;
548 u_int32_t len;
550 msg = &h->response;
551 gettimeofday(&deadline, NULL);
552 deadline.tv_sec += h->servers[h->cur_server].timeout;
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;
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;
568 len = ntohl(msg->length);
569 if (len > BODYSIZE) {
570 generr(h, "Received message too large (%u > %u)",
571 len, BODYSIZE);
572 return -1;
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;
581 /* Read the message body. */
582 if (read_timed(h, msg->u.body, len, &deadline) == -1)
583 return -1;
585 /* Decrypt it. */
586 crypt_msg(h, msg);
589 * Turn off single-connection mode if the server isn't amenable
590 * to it.
592 if (!(msg->flags & TAC_SINGLE_CONNECT))
593 h->single_connect = 0;
594 return 0;
597 static int
598 save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
599 size_t len)
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);
608 return 0;
612 * Send the current request, after encrypting it. Returns 0 on success,
613 * or -1 on failure.
615 static int
616 send_msg(struct tac_handle *h)
618 struct timeval deadline;
619 struct tac_msg *msg;
620 char *ptr;
621 int len;
623 if (h->last_seq_no & 1) {
624 generr(h, "Attempt to send message out of sequence");
625 return -1;
628 if (establish_connection(h) == -1)
629 return -1;
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);
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;
648 n = write(h->fd, ptr, len);
649 if (n == -1) {
650 struct timeval tv;
651 int nfds;
653 if (errno != EAGAIN) {
654 generr(h, "Network write error: %s",
655 strerror(errno));
656 return -1;
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;
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;
674 } else
675 nfds = 0;
676 if (nfds == 0) {
677 generr(h, "Network write timed out");
678 return -1;
680 } else {
681 ptr += n;
682 len -= n;
685 return 0;
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.
694 * Pointers to up to the first maxfields fields are stored in the fields
695 * array. Missing fields get NULL pointers.
697 * The return value is the actual number of fields parsed, and is always
698 * <= maxfields.
700 * On a syntax error, places a message in the msg string, and returns -1.
702 static int
703 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
705 char *p;
706 int i;
707 static const char ws[] = " \t";
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;
721 if (*p == '"') {
722 char *dst;
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;
736 if (*p == '\0') {
737 snprintf(msg, msglen,
738 "unterminated quoted string");
739 return -1;
741 *dst++ = *p++;
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;
750 } else {
751 fields[i] = p;
752 p += strcspn(p, ws);
753 if (*p != '\0')
754 *p++ = '\0';
756 i++;
758 return i;
762 tac_add_server(struct tac_handle *h, const char *host, int port,
763 const char *secret, int timeout, int flags)
765 struct tac_server *srvp;
767 if (h->num_servers >= MAXSERVERS) {
768 generr(h, "Too many TACACS+ servers specified");
769 return -1;
771 srvp = &h->servers[h->num_servers];
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;
779 if ((hent = gethostbyname(host)) == NULL) {
780 generr(h, "%s: host not found", host);
781 return -1;
783 memcpy(&srvp->addr.sin_addr, hent->h_addr,
784 sizeof srvp->addr.sin_addr);
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;
795 void
796 tac_close(struct tac_handle *h)
798 int i, srv;
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);
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]));
815 /* Clear everything else before freeing memory */
816 memset(h, 0, sizeof(struct tac_handle));
817 free(h);
821 tac_config(struct tac_handle *h, const char *path)
823 FILE *fp;
824 char buf[MAXCONFLINE];
825 int linenum;
826 int retval;
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;
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;
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;
864 buf[len - 1] = '\0';
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;
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;
881 host = fields[0];
882 secret = fields[1];
883 timeout_str = fields[2];
884 options_str = fields[3];
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;
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;
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;
922 if (tac_add_server(h, host, port, secret, timeout,
923 options) == -1) {
924 char msg[ERRSIZE];
926 strcpy(msg, h->errmsg);
927 generr(h, "%s:%d: %s", path, linenum, msg);
928 retval = -1;
929 break;
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;
939 tac_create_authen(struct tac_handle *h, int action, int type, int service)
941 struct tac_authen_start *as;
943 create_msg(h, TAC_AUTHEN, action, type);
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;
951 return 0;
955 tac_create_author(struct tac_handle *h, int method, int type, int service)
957 struct tac_author_request *areq;
959 create_msg(h, TAC_AUTHOR, method, type);
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;
967 return 0;
970 static void
971 create_msg(struct tac_handle *h, int msg_type, int var, int type)
973 struct tac_msg *msg;
974 int i;
976 h->last_seq_no = 0;
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 */
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);
989 for (i=0; i<MAXAVPAIRS; i++)
990 free_str(&(h->avs[i]));
993 void *
994 tac_get_data(struct tac_handle *h, size_t *len)
996 return dup_str(h, &h->srvr_data, len);
999 char *
1000 tac_get_msg(struct tac_handle *h)
1002 return dup_str(h, &h->srvr_msg, NULL);
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.
1010 struct tac_handle *
1011 tac_open(void)
1013 int i;
1014 struct tac_handle *h;
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]));
1031 init_srvr_str(&h->srvr_msg);
1032 init_srvr_str(&h->srvr_data);
1033 srandomdev();
1035 return h;
1039 tac_send_authen(struct tac_handle *h)
1041 struct tac_authen_reply *ar;
1043 if (h->num_servers == 0)
1044 return -1;
1046 if (h->last_seq_no == 0) { /* Authentication START packet */
1047 struct tac_authen_start *as;
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;
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;
1069 /* Send the message and retrieve the reply. */
1070 if (send_msg(h) == -1 || recv_msg(h) == -1)
1071 return -1;
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;
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);
1087 return ar->flags << 8 | ar->status;
1091 tac_send_author(struct tac_handle *h)
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;
1098 h->request.length =
1099 htonl(offsetof(struct tac_author_request, rest[0]));
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++;
1107 * Each AV size is a byte starting right after 'av_cnt'. Update the
1108 * offset to include these AV sizes.
1110 h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
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;
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;
1127 /* Send the message and retrieve the reply. */
1128 if (send_msg(h) == -1 || recv_msg(h) == -1)
1129 return -1;
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;
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;
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;
1149 /* Should have ended up at the end */
1150 if (get_srvr_end(h) == -1)
1151 return -1;
1153 /* Sanity checks */
1154 if (!h->single_connect)
1155 close_connection(h);
1157 return ares->av_cnt << 8 | ares->status;
1161 tac_set_rem_addr(struct tac_handle *h, const char *addr)
1163 return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
1167 tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
1169 return save_str(h, &h->data, data, data_len);
1173 tac_set_msg(struct tac_handle *h, const char *msg)
1175 return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1179 tac_set_port(struct tac_handle *h, const char *port)
1181 return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1185 tac_set_priv(struct tac_handle *h, int priv)
1187 if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1188 generr(h, "Attempt to set invalid privilege level");
1189 return -1;
1191 h->request.u.authen_start.priv_lvl = priv;
1192 return 0;
1196 tac_set_user(struct tac_handle *h, const char *user)
1198 return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1202 tac_set_av(struct tac_handle *h, u_int index, const char *av)
1204 if (index >= MAXAVPAIRS)
1205 return -1;
1206 return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
1209 char *
1210 tac_get_av(struct tac_handle *h, u_int index)
1212 if (index >= MAXAVPAIRS)
1213 return NULL;
1214 return dup_str(h, &(h->srvr_avs[index]), NULL);
1217 char *
1218 tac_get_av_value(struct tac_handle *h, const char *attribute)
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;
1227 if (attribute == NULL || ((len = strlen(attribute)) == 0))
1228 return NULL;
1230 for (i=0; i<MAXAVPAIRS; i++) {
1231 candidate = h->srvr_avs[i].data;
1232 candidate_len = h->srvr_avs[i].len;
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.
1239 if (!candidate)
1240 break;
1242 if (len < candidate_len &&
1243 !strncmp(candidate, attribute, len)) {
1245 ch = candidate + len;
1246 end = candidate + candidate_len;
1249 * Sift out the white space between A and V (should not
1250 * be any, but don't trust implementation of server...)
1252 found_seperator = 0;
1253 while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
1254 *ch == '\t') && ch != end) {
1255 if (*ch == '=' || *ch == '*')
1256 found_seperator++;
1257 ch++;
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.
1267 if (found_seperator == 1 && ch != end) {
1268 srvr.len = end - ch;
1269 srvr.data = ch;
1270 return dup_str(h, &srvr, NULL);
1274 return NULL;
1277 void
1278 tac_clear_avs(struct tac_handle *h)
1280 int i;
1281 for (i=0; i<MAXAVPAIRS; i++)
1282 save_str(h, &(h->avs[i]), NULL, 0);
1285 static void
1286 clear_srvr_avs(struct tac_handle *h)
1288 int i;
1289 for (i=0; i<MAXAVPAIRS; i++)
1290 init_srvr_str(&(h->srvr_avs[i]));
1294 const char *
1295 tac_strerror(struct tac_handle *h)
1297 return h->errmsg;
1300 static void *
1301 xmalloc(struct tac_handle *h, size_t size)
1303 void *r;
1305 if ((r = malloc(size)) == NULL)
1306 generr(h, "Out of memory");
1307 return r;
1310 static char *
1311 xstrdup(struct tac_handle *h, const char *s)
1313 char *r;
1315 if ((r = strdup(s)) == NULL)
1316 generr(h, "Out of memory");
1317 return r;