sigwait.3: Add missing '.'
[dragonfly.git] / lib / libtacplus / taclib.c
blob600a60185c8687f9e611d17ba5374e3e0847f5ca
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 $
27 * $DragonFly: src/lib/libtacplus/taclib.c,v 1.3 2004/08/19 21:25:58 joerg Exp $
30 #include <sys/param.h>
31 #include <sys/socket.h>
32 #include <sys/time.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
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>
48 #include "taclib_private.h"
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);
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.
87 * This function also frees the "cs" string data and initializes it
88 * for the next time.
90 static int
91 add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
93 u_int16_t len;
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;
102 *fld = len;
103 return 0;
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.
111 * This function also frees the "cs" string data and initializes it
112 * for the next time.
114 static int
115 add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
117 size_t len;
119 len = cs->len;
120 if (cs->data == NULL)
121 len = 0;
122 if (len != 0) {
123 int offset;
125 if (len > 0xffff) {
126 generr(h, "Field too long");
127 return -1;
129 offset = ntohl(h->request.length);
130 if (offset + len > BODYSIZE) {
131 generr(h, "Message too long");
132 return -1;
134 memcpy(h->request.u.body + offset, cs->data, len);
135 h->request.length = htonl(offset + len);
137 *fld = htons(len);
138 free_str(cs);
139 return 0;
142 static int
143 protocol_version(int msg_type, int var, int type)
145 int minor;
147 switch (msg_type) {
148 case TAC_AUTHEN:
149 /* 'var' represents the 'action' */
150 switch (var) {
151 case TAC_AUTHEN_LOGIN:
152 switch (type) {
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;
161 default:
162 minor = 0;
163 break;
165 break;
167 case TAC_AUTHEN_SENDAUTH:
168 minor = 1;
169 break;
171 default:
172 minor = 0;
173 break;
175 break;
177 case TAC_AUTHOR:
178 /* 'var' represents the 'method' */
179 switch (var) {
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.
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;
203 default:
204 minor = 0;
205 break;
207 break;
208 default:
209 minor = 0;
210 break;
212 break;
214 default:
215 minor = 0;
216 break;
219 return TAC_VER_MAJOR << 4 | minor;
223 static void
224 close_connection(struct tac_handle *h)
226 if (h->fd != -1) {
227 close(h->fd);
228 h->fd = -1;
232 static int
233 conn_server(struct tac_handle *h)
235 const struct tac_server *srvp = &h->servers[h->cur_server];
236 int flags;
238 if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
239 generr(h, "Cannot create socket: %s", strerror(errno));
240 return -1;
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;
250 if (connect(h->fd, (struct sockaddr *)&srvp->addr,
251 sizeof srvp->addr) == 0)
252 return 0;
254 if (errno == EINPROGRESS) {
255 fd_set wfds;
256 struct timeval tv;
257 int nfds;
258 struct sockaddr peer;
259 int peerlen;
260 int err;
261 int errlen;
263 /* Wait for the connection to complete. */
264 FD_ZERO(&wfds);
265 FD_SET(h->fd, &wfds);
266 tv.tv_sec = srvp->timeout;
267 tv.tv_usec = 0;
268 nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
269 if (nfds == -1) {
270 generr(h, "select: %s", strerror(errno));
271 close(h->fd);
272 h->fd = -1;
273 return -1;
275 if (nfds == 0) {
276 generr(h, "connect: timed out");
277 close(h->fd);
278 h->fd = -1;
279 return -1;
282 /* See whether we are connected now. */
283 peerlen = sizeof peer;
284 if (getpeername(h->fd, &peer, &peerlen) == 0)
285 return 0;
287 if (errno != ENOTCONN) {
288 generr(h, "getpeername: %s", strerror(errno));
289 close(h->fd);
290 h->fd = -1;
291 return -1;
294 /* Find out why the connect failed. */
295 errlen = sizeof err;
296 getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
297 errno = err;
299 generr(h, "connect: %s", strerror(errno));
300 close(h->fd);
301 h->fd = -1;
302 return -1;
306 * Encrypt or decrypt a message. The operations are symmetrical.
308 static void
309 crypt_msg(struct tac_handle *h, struct tac_msg *msg)
311 const char *secret;
312 MD5_CTX base_ctx;
313 MD5_CTX ctx;
314 unsigned char md5[16];
315 int chunk;
316 int msg_len;
318 secret = h->servers[h->cur_server].secret;
319 if (secret[0] == '\0')
320 msg->flags |= TAC_UNENCRYPTED;
321 if (msg->flags & TAC_UNENCRYPTED)
322 return;
324 msg_len = ntohl(msg->length);
326 MD5Init(&base_ctx);
327 MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
328 MD5Update(&base_ctx, secret, strlen(secret));
329 MD5Update(&base_ctx, &msg->version, sizeof msg->version);
330 MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
332 ctx = base_ctx;
333 for (chunk = 0; chunk < msg_len; chunk += sizeof md5) {
334 int chunk_len;
335 int i;
337 MD5Final(md5, &ctx);
339 if ((chunk_len = msg_len - chunk) > sizeof md5)
340 chunk_len = sizeof md5;
341 for (i = 0; i < chunk_len; i++)
342 msg->u.body[chunk + i] ^= md5[i];
344 ctx = base_ctx;
345 MD5Update(&ctx, md5, sizeof md5);
350 * Return a dynamically allocated copy of the given server string.
351 * The copy is null-terminated. If "len" is non-NULL, the length of
352 * the string (excluding the terminating null byte) is stored via it.
353 * Returns NULL on failure. Empty strings are still allocated even
354 * though they have no content.
356 static void *
357 dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
359 unsigned char *p;
361 if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
362 return NULL;
363 if (ss->data != NULL && ss->len != 0)
364 memcpy(p, ss->data, ss->len);
365 p[ss->len] = '\0';
366 if (len != NULL)
367 *len = ss->len;
368 return p;
371 static int
372 establish_connection(struct tac_handle *h)
374 int i;
376 if (h->fd >= 0) /* Already connected. */
377 return 0;
378 if (h->num_servers == 0) {
379 generr(h, "No TACACS+ servers specified");
380 return -1;
383 * Try the servers round-robin. We begin with the one that
384 * worked for us the last time. That way, once we find a good
385 * server, we won't waste any more time trying the bad ones.
387 for (i = 0; i < h->num_servers; i++) {
388 if (conn_server(h) == 0) {
389 h->single_connect = (h->servers[h->cur_server].flags &
390 TAC_SRVR_SINGLE_CONNECT) != 0;
391 return 0;
393 if (++h->cur_server >= h->num_servers) /* Wrap around */
394 h->cur_server = 0;
396 /* Just return whatever error was last reported by conn_server(). */
397 return -1;
401 * Free a client string, obliterating its contents first for security.
403 static void
404 free_str(struct clnt_str *cs)
406 if (cs->data != NULL) {
407 memset(cs->data, 0, cs->len);
408 free(cs->data);
409 cs->data = NULL;
410 cs->len = 0;
414 static void
415 generr(struct tac_handle *h, const char *format, ...)
417 va_list ap;
419 va_start(ap, format);
420 vsnprintf(h->errmsg, ERRSIZE, format, ap);
421 va_end(ap);
424 static void
425 gen_session_id(struct tac_msg *msg)
427 int r;
429 r = random();
430 msg->session_id[0] = r >> 8;
431 msg->session_id[1] = r;
432 r = random();
433 msg->session_id[2] = r >> 8;
434 msg->session_id[3] = r;
438 * Verify that we are exactly at the end of the response message.
439 * Returns 0 on success, -1 on failure.
441 static int
442 get_srvr_end(struct tac_handle *h)
444 int len;
446 len = ntohl(h->response.length);
448 if (h->srvr_pos != len) {
449 generr(h, "Invalid length field in response "
450 "from server: end expected at %u, response length %u",
451 h->srvr_pos, len);
452 return -1;
454 return 0;
457 static int
458 get_srvr_str(struct tac_handle *h, const char *field,
459 struct srvr_str *ss, size_t len)
461 if (h->srvr_pos + len > ntohl(h->response.length)) {
462 generr(h, "Invalid length field in %s response from server "
463 "(%lu > %lu)", field, (u_long)(h->srvr_pos + len),
464 (u_long)ntohl(h->response.length));
465 return -1;
467 ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
468 ss->len = len;
469 h->srvr_pos += len;
470 return 0;
473 static void
474 init_clnt_str(struct clnt_str *cs)
476 cs->data = NULL;
477 cs->len = 0;
480 static void
481 init_srvr_str(struct srvr_str *ss)
483 ss->data = NULL;
484 ss->len = 0;
487 static int
488 read_timed(struct tac_handle *h, void *buf, size_t len,
489 const struct timeval *deadline)
491 char *ptr;
493 ptr = (char *)buf;
494 while (len > 0) {
495 int n;
497 n = read(h->fd, ptr, len);
498 if (n == -1) {
499 struct timeval tv;
500 int nfds;
502 if (errno != EAGAIN) {
503 generr(h, "Network read error: %s",
504 strerror(errno));
505 return -1;
508 /* Wait until we can read more data. */
509 gettimeofday(&tv, NULL);
510 timersub(deadline, &tv, &tv);
511 if (tv.tv_sec >= 0) {
512 fd_set rfds;
514 FD_ZERO(&rfds);
515 FD_SET(h->fd, &rfds);
516 nfds =
517 select(h->fd + 1, &rfds, NULL, NULL, &tv);
518 if (nfds == -1) {
519 generr(h, "select: %s",
520 strerror(errno));
521 return -1;
523 } else
524 nfds = 0;
525 if (nfds == 0) {
526 generr(h, "Network read timed out");
527 return -1;
529 } else if (n == 0) {
530 generr(h, "unexpected EOF from server");
531 return -1;
532 } else {
533 ptr += n;
534 len -= n;
537 return 0;
541 * Receive a response from the server and decrypt it. Returns 0 on
542 * success, or -1 on failure.
544 static int
545 recv_msg(struct tac_handle *h)
547 struct timeval deadline;
548 struct tac_msg *msg;
549 u_int32_t len;
551 msg = &h->response;
552 gettimeofday(&deadline, NULL);
553 deadline.tv_sec += h->servers[h->cur_server].timeout;
555 /* Read the message header and make sure it is reasonable. */
556 if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
557 return -1;
558 if (memcmp(msg->session_id, h->request.session_id,
559 sizeof msg->session_id) != 0) {
560 generr(h, "Invalid session ID in received message");
561 return -1;
563 if (msg->type != h->request.type) {
564 generr(h, "Invalid type in received message"
565 " (got %u, expected %u)",
566 msg->type, h->request.type);
567 return -1;
569 len = ntohl(msg->length);
570 if (len > BODYSIZE) {
571 generr(h, "Received message too large (%u > %u)",
572 len, BODYSIZE);
573 return -1;
575 if (msg->seq_no != ++h->last_seq_no) {
576 generr(h, "Invalid sequence number in received message"
577 " (got %u, expected %u)",
578 msg->seq_no, h->last_seq_no);
579 return -1;
582 /* Read the message body. */
583 if (read_timed(h, msg->u.body, len, &deadline) == -1)
584 return -1;
586 /* Decrypt it. */
587 crypt_msg(h, msg);
590 * Turn off single-connection mode if the server isn't amenable
591 * to it.
593 if (!(msg->flags & TAC_SINGLE_CONNECT))
594 h->single_connect = 0;
595 return 0;
598 static int
599 save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
600 size_t len)
602 free_str(cs);
603 if (data != NULL && len != 0) {
604 if ((cs->data = xmalloc(h, len)) == NULL)
605 return -1;
606 cs->len = len;
607 memcpy(cs->data, data, len);
609 return 0;
613 * Send the current request, after encrypting it. Returns 0 on success,
614 * or -1 on failure.
616 static int
617 send_msg(struct tac_handle *h)
619 struct timeval deadline;
620 struct tac_msg *msg;
621 char *ptr;
622 int len;
624 if (h->last_seq_no & 1) {
625 generr(h, "Attempt to send message out of sequence");
626 return -1;
629 if (establish_connection(h) == -1)
630 return -1;
632 msg = &h->request;
633 msg->seq_no = ++h->last_seq_no;
634 if (msg->seq_no == 1)
635 gen_session_id(msg);
636 crypt_msg(h, msg);
638 if (h->single_connect)
639 msg->flags |= TAC_SINGLE_CONNECT;
640 else
641 msg->flags &= ~TAC_SINGLE_CONNECT;
642 gettimeofday(&deadline, NULL);
643 deadline.tv_sec += h->servers[h->cur_server].timeout;
644 len = HDRSIZE + ntohl(msg->length);
645 ptr = (char *)msg;
646 while (len > 0) {
647 int n;
649 n = write(h->fd, ptr, len);
650 if (n == -1) {
651 struct timeval tv;
652 int nfds;
654 if (errno != EAGAIN) {
655 generr(h, "Network write error: %s",
656 strerror(errno));
657 return -1;
660 /* Wait until we can write more data. */
661 gettimeofday(&tv, NULL);
662 timersub(&deadline, &tv, &tv);
663 if (tv.tv_sec >= 0) {
664 fd_set wfds;
666 FD_ZERO(&wfds);
667 FD_SET(h->fd, &wfds);
668 nfds =
669 select(h->fd + 1, NULL, &wfds, NULL, &tv);
670 if (nfds == -1) {
671 generr(h, "select: %s",
672 strerror(errno));
673 return -1;
675 } else
676 nfds = 0;
677 if (nfds == 0) {
678 generr(h, "Network write timed out");
679 return -1;
681 } else {
682 ptr += n;
683 len -= n;
686 return 0;
690 * Destructively split a string into fields separated by white space.
691 * `#' at the beginning of a field begins a comment that extends to the
692 * end of the string. Fields may be quoted with `"'. Inside quoted
693 * strings, the backslash escapes `\"' and `\\' are honored.
695 * Pointers to up to the first maxfields fields are stored in the fields
696 * array. Missing fields get NULL pointers.
698 * The return value is the actual number of fields parsed, and is always
699 * <= maxfields.
701 * On a syntax error, places a message in the msg string, and returns -1.
703 static int
704 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
706 char *p;
707 int i;
708 static const char ws[] = " \t";
710 for (i = 0; i < maxfields; i++)
711 fields[i] = NULL;
712 p = str;
713 i = 0;
714 while (*p != '\0') {
715 p += strspn(p, ws);
716 if (*p == '#' || *p == '\0')
717 break;
718 if (i >= maxfields) {
719 snprintf(msg, msglen, "line has too many fields");
720 return -1;
722 if (*p == '"') {
723 char *dst;
725 dst = ++p;
726 fields[i] = dst;
727 while (*p != '"') {
728 if (*p == '\\') {
729 p++;
730 if (*p != '"' && *p != '\\' &&
731 *p != '\0') {
732 snprintf(msg, msglen,
733 "invalid `\\' escape");
734 return -1;
737 if (*p == '\0') {
738 snprintf(msg, msglen,
739 "unterminated quoted string");
740 return -1;
742 *dst++ = *p++;
744 *dst = '\0';
745 p++;
746 if (*p != '\0' && strspn(p, ws) == 0) {
747 snprintf(msg, msglen, "quoted string not"
748 " followed by white space");
749 return -1;
751 } else {
752 fields[i] = p;
753 p += strcspn(p, ws);
754 if (*p != '\0')
755 *p++ = '\0';
757 i++;
759 return i;
763 tac_add_server(struct tac_handle *h, const char *host, int port,
764 const char *secret, int timeout, int flags)
766 struct tac_server *srvp;
768 if (h->num_servers >= MAXSERVERS) {
769 generr(h, "Too many TACACS+ servers specified");
770 return -1;
772 srvp = &h->servers[h->num_servers];
774 memset(&srvp->addr, 0, sizeof srvp->addr);
775 srvp->addr.sin_len = sizeof srvp->addr;
776 srvp->addr.sin_family = AF_INET;
777 if (!inet_aton(host, &srvp->addr.sin_addr)) {
778 struct hostent *hent;
780 if ((hent = gethostbyname(host)) == NULL) {
781 generr(h, "%s: host not found", host);
782 return -1;
784 memcpy(&srvp->addr.sin_addr, hent->h_addr,
785 sizeof srvp->addr.sin_addr);
787 srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
788 if ((srvp->secret = xstrdup(h, secret)) == NULL)
789 return -1;
790 srvp->timeout = timeout;
791 srvp->flags = flags;
792 h->num_servers++;
793 return 0;
796 void
797 tac_close(struct tac_handle *h)
799 int i, srv;
801 if (h->fd != -1)
802 close(h->fd);
803 for (srv = 0; srv < h->num_servers; srv++) {
804 memset(h->servers[srv].secret, 0,
805 strlen(h->servers[srv].secret));
806 free(h->servers[srv].secret);
808 free_str(&h->user);
809 free_str(&h->port);
810 free_str(&h->rem_addr);
811 free_str(&h->data);
812 free_str(&h->user_msg);
813 for (i=0; i<MAXAVPAIRS; i++)
814 free_str(&(h->avs[i]));
816 /* Clear everything else before freeing memory */
817 memset(h, 0, sizeof(struct tac_handle));
818 free(h);
822 tac_config(struct tac_handle *h, const char *path)
824 FILE *fp;
825 char buf[MAXCONFLINE];
826 int linenum;
827 int retval;
829 if (path == NULL)
830 path = PATH_TACPLUS_CONF;
831 if ((fp = fopen(path, "r")) == NULL) {
832 generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
833 return -1;
835 retval = 0;
836 linenum = 0;
837 while (fgets(buf, sizeof buf, fp) != NULL) {
838 int len;
839 char *fields[4];
840 int nfields;
841 char msg[ERRSIZE];
842 char *host, *res;
843 char *port_str;
844 char *secret;
845 char *timeout_str;
846 char *options_str;
847 char *end;
848 unsigned long timeout;
849 int port;
850 int options;
852 linenum++;
853 len = strlen(buf);
854 /* We know len > 0, else fgets would have returned NULL. */
855 if (buf[len - 1] != '\n') {
856 if (len >= sizeof buf - 1)
857 generr(h, "%s:%d: line too long", path,
858 linenum);
859 else
860 generr(h, "%s:%d: missing newline", path,
861 linenum);
862 retval = -1;
863 break;
865 buf[len - 1] = '\0';
867 /* Extract the fields from the line. */
868 nfields = split(buf, fields, 4, msg, sizeof msg);
869 if (nfields == -1) {
870 generr(h, "%s:%d: %s", path, linenum, msg);
871 retval = -1;
872 break;
874 if (nfields == 0)
875 continue;
876 if (nfields < 2) {
877 generr(h, "%s:%d: missing shared secret", path,
878 linenum);
879 retval = -1;
880 break;
882 host = fields[0];
883 secret = fields[1];
884 timeout_str = fields[2];
885 options_str = fields[3];
887 /* Parse and validate the fields. */
888 res = host;
889 host = strsep(&res, ":");
890 port_str = strsep(&res, ":");
891 if (port_str != NULL) {
892 port = strtoul(port_str, &end, 10);
893 if (port_str[0] == '\0' || *end != '\0') {
894 generr(h, "%s:%d: invalid port", path,
895 linenum);
896 retval = -1;
897 break;
899 } else
900 port = 0;
901 if (timeout_str != NULL) {
902 timeout = strtoul(timeout_str, &end, 10);
903 if (timeout_str[0] == '\0' || *end != '\0') {
904 generr(h, "%s:%d: invalid timeout", path,
905 linenum);
906 retval = -1;
907 break;
909 } else
910 timeout = TIMEOUT;
911 options = 0;
912 if (options_str != NULL) {
913 if (strcmp(options_str, "single-connection") == 0)
914 options |= TAC_SRVR_SINGLE_CONNECT;
915 else {
916 generr(h, "%s:%d: invalid option \"%s\"",
917 path, linenum, options_str);
918 retval = -1;
919 break;
923 if (tac_add_server(h, host, port, secret, timeout,
924 options) == -1) {
925 char msg[ERRSIZE];
927 strcpy(msg, h->errmsg);
928 generr(h, "%s:%d: %s", path, linenum, msg);
929 retval = -1;
930 break;
933 /* Clear out the buffer to wipe a possible copy of a shared secret */
934 memset(buf, 0, sizeof buf);
935 fclose(fp);
936 return retval;
940 tac_create_authen(struct tac_handle *h, int action, int type, int service)
942 struct tac_authen_start *as;
944 create_msg(h, TAC_AUTHEN, action, type);
946 as = &h->request.u.authen_start;
947 as->action = action;
948 as->priv_lvl = TAC_PRIV_LVL_USER;
949 as->authen_type = type;
950 as->service = service;
952 return 0;
956 tac_create_author(struct tac_handle *h, int method, int type, int service)
958 struct tac_author_request *areq;
960 create_msg(h, TAC_AUTHOR, method, type);
962 areq = &h->request.u.author_request;
963 areq->authen_meth = method;
964 areq->priv_lvl = TAC_PRIV_LVL_USER;
965 areq->authen_type = type;
966 areq->service = service;
968 return 0;
971 static void
972 create_msg(struct tac_handle *h, int msg_type, int var, int type)
974 struct tac_msg *msg;
975 int i;
977 h->last_seq_no = 0;
979 msg = &h->request;
980 msg->type = msg_type;
981 msg->version = protocol_version(msg_type, var, type);
982 msg->flags = 0; /* encrypted packet body */
984 free_str(&h->user);
985 free_str(&h->port);
986 free_str(&h->rem_addr);
987 free_str(&h->data);
988 free_str(&h->user_msg);
990 for (i=0; i<MAXAVPAIRS; i++)
991 free_str(&(h->avs[i]));
994 void *
995 tac_get_data(struct tac_handle *h, size_t *len)
997 return dup_str(h, &h->srvr_data, len);
1000 char *
1001 tac_get_msg(struct tac_handle *h)
1003 return dup_str(h, &h->srvr_msg, NULL);
1007 * Create and initialize a tac_handle structure, and return it to the
1008 * caller. Can fail only if the necessary memory cannot be allocated.
1009 * In that case, it returns NULL.
1011 struct tac_handle *
1012 tac_open(void)
1014 int i;
1015 struct tac_handle *h;
1017 h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
1018 if (h != NULL) {
1019 h->fd = -1;
1020 h->num_servers = 0;
1021 h->cur_server = 0;
1022 h->errmsg[0] = '\0';
1023 init_clnt_str(&h->user);
1024 init_clnt_str(&h->port);
1025 init_clnt_str(&h->rem_addr);
1026 init_clnt_str(&h->data);
1027 init_clnt_str(&h->user_msg);
1028 for (i=0; i<MAXAVPAIRS; i++) {
1029 init_clnt_str(&(h->avs[i]));
1030 init_srvr_str(&(h->srvr_avs[i]));
1032 init_srvr_str(&h->srvr_msg);
1033 init_srvr_str(&h->srvr_data);
1034 srandomdev();
1036 return h;
1040 tac_send_authen(struct tac_handle *h)
1042 struct tac_authen_reply *ar;
1044 if (h->num_servers == 0)
1045 return -1;
1047 if (h->last_seq_no == 0) { /* Authentication START packet */
1048 struct tac_authen_start *as;
1050 as = &h->request.u.authen_start;
1051 h->request.length =
1052 htonl(offsetof(struct tac_authen_start, rest[0]));
1053 if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1054 add_str_8(h, &as->port_len, &h->port) == -1 ||
1055 add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
1056 add_str_8(h, &as->data_len, &h->data) == -1)
1057 return -1;
1058 } else { /* Authentication CONTINUE packet */
1059 struct tac_authen_cont *ac;
1061 ac = &h->request.u.authen_cont;
1062 ac->flags = 0;
1063 h->request.length =
1064 htonl(offsetof(struct tac_authen_cont, rest[0]));
1065 if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
1066 add_str_16(h, &ac->data_len, &h->data) == -1)
1067 return -1;
1070 /* Send the message and retrieve the reply. */
1071 if (send_msg(h) == -1 || recv_msg(h) == -1)
1072 return -1;
1074 /* Scan the optional fields in the reply. */
1075 ar = &h->response.u.authen_reply;
1076 h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
1077 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1078 get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1079 get_srvr_end(h) == -1)
1080 return -1;
1082 if (!h->single_connect &&
1083 ar->status != TAC_AUTHEN_STATUS_GETDATA &&
1084 ar->status != TAC_AUTHEN_STATUS_GETUSER &&
1085 ar->status != TAC_AUTHEN_STATUS_GETPASS)
1086 close_connection(h);
1088 return ar->flags << 8 | ar->status;
1092 tac_send_author(struct tac_handle *h)
1094 int i, current;
1095 char dbgstr[64];
1096 struct tac_author_request *areq = &h->request.u.author_request;
1097 struct tac_author_response *ares = &h->response.u.author_response;
1099 h->request.length =
1100 htonl(offsetof(struct tac_author_request, rest[0]));
1102 /* Count each specified AV pair */
1103 for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
1104 if (h->avs[i].len && h->avs[i].data)
1105 areq->av_cnt++;
1108 * Each AV size is a byte starting right after 'av_cnt'. Update the
1109 * offset to include these AV sizes.
1111 h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
1113 /* Now add the string arguments from 'h' */
1114 if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
1115 add_str_8(h, &areq->port_len, &h->port) == -1 ||
1116 add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
1117 return -1;
1119 /* Add each AV pair, the size of each placed in areq->rest[current] */
1120 for (current=0, i=0; i<MAXAVPAIRS; i++) {
1121 if (h->avs[i].len && h->avs[i].data) {
1122 if (add_str_8(h, &areq->rest[current++],
1123 &(h->avs[i])) == -1)
1124 return -1;
1128 /* Send the message and retrieve the reply. */
1129 if (send_msg(h) == -1 || recv_msg(h) == -1)
1130 return -1;
1132 /* Update the offset in the response packet based on av pairs count */
1133 h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
1134 ares->av_cnt;
1136 /* Scan the optional fields in the response. */
1137 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
1138 get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
1139 return -1;
1141 /* Get each AV pair (just setting pointers, not malloc'ing) */
1142 clear_srvr_avs(h);
1143 for (i=0; i<ares->av_cnt; i++) {
1144 snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
1145 if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]),
1146 ares->rest[i]) == -1)
1147 return -1;
1150 /* Should have ended up at the end */
1151 if (get_srvr_end(h) == -1)
1152 return -1;
1154 /* Sanity checks */
1155 if (!h->single_connect)
1156 close_connection(h);
1158 return ares->av_cnt << 8 | ares->status;
1162 tac_set_rem_addr(struct tac_handle *h, const char *addr)
1164 return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
1168 tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
1170 return save_str(h, &h->data, data, data_len);
1174 tac_set_msg(struct tac_handle *h, const char *msg)
1176 return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1180 tac_set_port(struct tac_handle *h, const char *port)
1182 return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1186 tac_set_priv(struct tac_handle *h, int priv)
1188 if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1189 generr(h, "Attempt to set invalid privilege level");
1190 return -1;
1192 h->request.u.authen_start.priv_lvl = priv;
1193 return 0;
1197 tac_set_user(struct tac_handle *h, const char *user)
1199 return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1203 tac_set_av(struct tac_handle *h, u_int index, const char *av)
1205 if (index >= MAXAVPAIRS)
1206 return -1;
1207 return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
1210 char *
1211 tac_get_av(struct tac_handle *h, u_int index)
1213 if (index >= MAXAVPAIRS)
1214 return NULL;
1215 return dup_str(h, &(h->srvr_avs[index]), NULL);
1218 char *
1219 tac_get_av_value(struct tac_handle *h, const char *attribute)
1221 int i, len;
1222 const char *ch, *end;
1223 const char *candidate;
1224 int candidate_len;
1225 int found_seperator;
1226 struct srvr_str srvr;
1228 if (attribute == NULL || ((len = strlen(attribute)) == 0))
1229 return NULL;
1231 for (i=0; i<MAXAVPAIRS; i++) {
1232 candidate = h->srvr_avs[i].data;
1233 candidate_len = h->srvr_avs[i].len;
1236 * Valid 'srvr_avs' guaranteed to be contiguous starting at
1237 * index 0 (not necessarily the case with 'avs'). Break out
1238 * when the "end" of the list has been reached.
1240 if (!candidate)
1241 break;
1243 if (len < candidate_len &&
1244 !strncmp(candidate, attribute, len)) {
1246 ch = candidate + len;
1247 end = candidate + candidate_len;
1250 * Sift out the white space between A and V (should not
1251 * be any, but don't trust implementation of server...)
1253 found_seperator = 0;
1254 while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
1255 *ch == '\t') && ch != end) {
1256 if (*ch == '=' || *ch == '*')
1257 found_seperator++;
1258 ch++;
1262 * Note:
1263 * The case of 'attribute' == "foo" and
1264 * h->srvr_avs[0] = "foobie=var1"
1265 * h->srvr_avs[1] = "foo=var2"
1266 * is handled.
1268 if (found_seperator == 1 && ch != end) {
1269 srvr.len = end - ch;
1270 srvr.data = ch;
1271 return dup_str(h, &srvr, NULL);
1275 return NULL;
1278 void
1279 tac_clear_avs(struct tac_handle *h)
1281 int i;
1282 for (i=0; i<MAXAVPAIRS; i++)
1283 save_str(h, &(h->avs[i]), NULL, 0);
1286 static void
1287 clear_srvr_avs(struct tac_handle *h)
1289 int i;
1290 for (i=0; i<MAXAVPAIRS; i++)
1291 init_srvr_str(&(h->srvr_avs[i]));
1295 const char *
1296 tac_strerror(struct tac_handle *h)
1298 return h->errmsg;
1301 static void *
1302 xmalloc(struct tac_handle *h, size_t size)
1304 void *r;
1306 if ((r = malloc(size)) == NULL)
1307 generr(h, "Out of memory");
1308 return r;
1311 static char *
1312 xstrdup(struct tac_handle *h, const char *s)
1314 char *r;
1316 if ((r = strdup(s)) == NULL)
1317 generr(h, "Out of memory");
1318 return r;