Make setthetime() static per the prototype.
[dragonfly.git] / lib / libtacplus / taclib.c
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  *      $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.2 2003/06/17 04:26:51 dillon Exp $
28  */
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                 int peerlen;
260                 int err;
261                 int errlen;
262
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;
274                 }
275                 if (nfds == 0) {
276                         generr(h, "connect: timed out");
277                         close(h->fd);
278                         h->fd = -1;
279                         return -1;
280                 }
281
282                 /* See whether we are connected now. */
283                 peerlen = sizeof peer;
284                 if (getpeername(h->fd, &peer, &peerlen) == 0)
285                         return 0;
286
287                 if (errno != ENOTCONN) {
288                         generr(h, "getpeername: %s", strerror(errno));
289                         close(h->fd);
290                         h->fd = -1;
291                         return -1;
292                 }
293
294                 /* Find out why the connect failed. */
295                 errlen = sizeof err;
296                 getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
297                 errno = err;
298         }
299         generr(h, "connect: %s", strerror(errno));
300         close(h->fd);
301         h->fd = -1;
302         return -1;
303 }
304
305 /*
306  * Encrypt or decrypt a message.  The operations are symmetrical.
307  */
308 static void
309 crypt_msg(struct tac_handle *h, struct tac_msg *msg)
310 {
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;
317
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;
323
324         msg_len = ntohl(msg->length);
325
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);
331
332         ctx = base_ctx;
333         for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
334                 int chunk_len;
335                 int i;
336
337                 MD5Final(md5, &ctx);
338
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];
343
344                 ctx = base_ctx;
345                 MD5Update(&ctx, md5, sizeof md5);
346         }
347 }
348
349 /*
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.
355  */
356 static void *
357 dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
358 {
359         unsigned char *p;
360
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;
369 }
370
371 static int
372 establish_connection(struct tac_handle *h)
373 {
374         int i;
375
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;
381         }
382         /*
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.
386          */
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;
392                 }
393                 if (++h->cur_server >= h->num_servers)  /* Wrap around */
394                         h->cur_server = 0;
395         }
396         /* Just return whatever error was last reported by conn_server(). */
397         return -1;
398 }
399
400 /*
401  * Free a client string, obliterating its contents first for security.
402  */
403 static void
404 free_str(struct clnt_str *cs)
405 {
406         if (cs->data != NULL) {
407                 memset(cs->data, 0, cs->len);
408                 free(cs->data);
409                 cs->data = NULL;
410                 cs->len = 0;
411         }
412 }
413
414 static void
415 generr(struct tac_handle *h, const char *format, ...)
416 {
417         va_list          ap;
418
419         va_start(ap, format);
420         vsnprintf(h->errmsg, ERRSIZE, format, ap);
421         va_end(ap);
422 }
423
424 static void
425 gen_session_id(struct tac_msg *msg)
426 {
427         int r;
428
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;
435 }
436
437 /*
438  * Verify that we are exactly at the end of the response message.
439  * Returns 0 on success, -1 on failure.
440  */
441 static int
442 get_srvr_end(struct tac_handle *h)
443 {
444         int len;
445
446         len = ntohl(h->response.length);
447
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;
453         }
454         return 0;
455 }
456
457 static int
458 get_srvr_str(struct tac_handle *h, const char *field,
459              struct srvr_str *ss, size_t len)
460 {
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;
466         }
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;
471 }
472
473 static void
474 init_clnt_str(struct clnt_str *cs)
475 {
476         cs->data = NULL;
477         cs->len = 0;
478 }
479
480 static void
481 init_srvr_str(struct srvr_str *ss)
482 {
483         ss->data = NULL;
484         ss->len = 0;
485 }
486
487 static int
488 read_timed(struct tac_handle *h, void *buf, size_t len,
489     const struct timeval *deadline)
490 {
491         char *ptr;
492
493         ptr = (char *)buf;
494         while (len > 0) {
495                 int n;
496
497                 n = read(h->fd, ptr, len);
498                 if (n == -1) {
499                         struct timeval tv;
500                         int nfds;
501
502                         if (errno != EAGAIN) {
503                                 generr(h, "Network read error: %s",
504                                     strerror(errno));
505                                 return -1;
506                         }
507
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;
513
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;
522                                 }
523                         } else
524                                 nfds = 0;
525                         if (nfds == 0) {
526                                 generr(h, "Network read timed out");
527                                 return -1;
528                         }
529                 } else if (n == 0) {
530                         generr(h, "unexpected EOF from server");
531                         return -1;
532                 } else {
533                         ptr += n;
534                         len -= n;
535                 }
536         }
537         return 0;
538 }
539
540 /*
541  * Receive a response from the server and decrypt it.  Returns 0 on
542  * success, or -1 on failure.
543  */
544 static int
545 recv_msg(struct tac_handle *h)
546 {
547         struct timeval deadline;
548         struct tac_msg *msg;
549         u_int32_t len;
550
551         msg = &h->response;
552         gettimeofday(&deadline, NULL);
553         deadline.tv_sec += h->servers[h->cur_server].timeout;
554
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;
562         }
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;
568         }
569         len = ntohl(msg->length);
570         if (len > BODYSIZE) {
571                 generr(h, "Received message too large (%u > %u)",
572                           len, BODYSIZE);
573                 return -1;
574         }
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;
580         }
581
582         /* Read the message body. */
583         if (read_timed(h, msg->u.body, len, &deadline) == -1)
584                 return -1;
585
586         /* Decrypt it. */
587         crypt_msg(h, msg);
588
589         /*
590          * Turn off single-connection mode if the server isn't amenable
591          * to it.
592          */
593         if (!(msg->flags & TAC_SINGLE_CONNECT))
594                 h->single_connect = 0;
595         return 0;
596 }
597
598 static int
599 save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
600     size_t len)
601 {
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);
608         }
609         return 0;
610 }
611
612 /*
613  * Send the current request, after encrypting it.  Returns 0 on success,
614  * or -1 on failure.
615  */
616 static int
617 send_msg(struct tac_handle *h)
618 {
619         struct timeval deadline;
620         struct tac_msg *msg;
621         char *ptr;
622         int len;
623
624         if (h->last_seq_no & 1) {
625                 generr(h, "Attempt to send message out of sequence");
626                 return -1;
627         }
628
629         if (establish_connection(h) == -1)
630                 return -1;
631
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);
637
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;
648
649                 n = write(h->fd, ptr, len);
650                 if (n == -1) {
651                         struct timeval tv;
652                         int nfds;
653
654                         if (errno != EAGAIN) {
655                                 generr(h, "Network write error: %s",
656                                     strerror(errno));
657                                 return -1;
658                         }
659
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;
665
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;
674                                 }
675                         } else
676                                 nfds = 0;
677                         if (nfds == 0) {
678                                 generr(h, "Network write timed out");
679                                 return -1;
680                         }
681                 } else {
682                         ptr += n;
683                         len -= n;
684                 }
685         }
686         return 0;
687 }
688
689 /*
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.
694  *
695  * Pointers to up to the first maxfields fields are stored in the fields
696  * array.  Missing fields get NULL pointers.
697  *
698  * The return value is the actual number of fields parsed, and is always
699  * <= maxfields.
700  *
701  * On a syntax error, places a message in the msg string, and returns -1.
702  */
703 static int
704 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
705 {
706         char *p;
707         int i;
708         static const char ws[] = " \t";
709
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;
721                 }
722                 if (*p == '"') {
723                         char *dst;
724
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;
735                                         }
736                                 }
737                                 if (*p == '\0') {
738                                         snprintf(msg, msglen,
739                                             "unterminated quoted string");
740                                         return -1;
741                                 }
742                                 *dst++ = *p++;
743                         }
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;
750                         }
751                 } else {
752                         fields[i] = p;
753                         p += strcspn(p, ws);
754                         if (*p != '\0')
755                                 *p++ = '\0';
756                 }
757                 i++;
758         }
759         return i;
760 }
761
762 int
763 tac_add_server(struct tac_handle *h, const char *host, int port,
764     const char *secret, int timeout, int flags)
765 {
766         struct tac_server *srvp;
767
768         if (h->num_servers >= MAXSERVERS) {
769                 generr(h, "Too many TACACS+ servers specified");
770                 return -1;
771         }
772         srvp = &h->servers[h->num_servers];
773
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;
779
780                 if ((hent = gethostbyname(host)) == NULL) {
781                         generr(h, "%s: host not found", host);
782                         return -1;
783                 }
784                 memcpy(&srvp->addr.sin_addr, hent->h_addr,
785                     sizeof srvp->addr.sin_addr);
786         }
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;
794 }
795
796 void
797 tac_close(struct tac_handle *h)
798 {
799         int i, srv;
800
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);
807         }
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]));
815
816         /* Clear everything else before freeing memory */
817         memset(h, 0, sizeof(struct tac_handle));
818         free(h);
819 }
820
821 int
822 tac_config(struct tac_handle *h, const char *path)
823 {
824         FILE *fp;
825         char buf[MAXCONFLINE];
826         int linenum;
827         int retval;
828
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;
834         }
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;
851
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;
864                 }
865                 buf[len - 1] = '\0';
866
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;
873                 }
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;
881                 }
882                 host = fields[0];
883                 secret = fields[1];
884                 timeout_str = fields[2];
885                 options_str = fields[3];
886
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;
898                         }
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;
908                         }
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;
920                         }
921                 };
922
923                 if (tac_add_server(h, host, port, secret, timeout,
924                     options) == -1) {
925                         char msg[ERRSIZE];
926
927                         strcpy(msg, h->errmsg);
928                         generr(h, "%s:%d: %s", path, linenum, msg);
929                         retval = -1;
930                         break;
931                 }
932         }
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;
937 }
938
939 int
940 tac_create_authen(struct tac_handle *h, int action, int type, int service)
941 {
942         struct tac_authen_start *as;
943
944         create_msg(h, TAC_AUTHEN, action, type);
945
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;
951
952         return 0;
953 }
954
955 int
956 tac_create_author(struct tac_handle *h, int method, int type, int service)
957 {
958         struct tac_author_request *areq;
959
960         create_msg(h, TAC_AUTHOR, method, type);
961
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;
967
968         return 0;
969 }
970
971 static void
972 create_msg(struct tac_handle *h, int msg_type, int var, int type)
973 {
974         struct tac_msg *msg;
975         int i;
976
977         h->last_seq_no = 0;
978
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 */
983
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);
989
990         for (i=0; i<MAXAVPAIRS; i++)
991                 free_str(&(h->avs[i]));
992 }
993
994 void *
995 tac_get_data(struct tac_handle *h, size_t *len)
996 {
997         return dup_str(h, &h->srvr_data, len);
998 }
999
1000 char *
1001 tac_get_msg(struct tac_handle *h)
1002 {
1003         return dup_str(h, &h->srvr_msg, NULL);
1004 }
1005
1006 /*
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.
1010  */
1011 struct tac_handle *
1012 tac_open(void)
1013 {
1014         int i;
1015         struct tac_handle *h;
1016
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]));
1031                 }
1032                 init_srvr_str(&h->srvr_msg);
1033                 init_srvr_str(&h->srvr_data);
1034                 srandomdev();
1035         }
1036         return h;
1037 }
1038
1039 int
1040 tac_send_authen(struct tac_handle *h)
1041 {
1042         struct tac_authen_reply *ar;
1043
1044         if (h->num_servers == 0)
1045             return -1;
1046
1047         if (h->last_seq_no == 0) {      /* Authentication START packet */
1048                 struct tac_authen_start *as;
1049
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;
1060
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;
1068         }
1069
1070         /* Send the message and retrieve the reply. */
1071         if (send_msg(h) == -1 || recv_msg(h) == -1)
1072                 return -1;
1073
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;
1081
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);
1087
1088         return ar->flags << 8 | ar->status;
1089 }
1090
1091 int
1092 tac_send_author(struct tac_handle *h)
1093 {
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;
1098
1099         h->request.length =
1100                 htonl(offsetof(struct tac_author_request, rest[0]));
1101
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++;
1106
1107         /*
1108          * Each AV size is a byte starting right after 'av_cnt'.  Update the
1109          * offset to include these AV sizes.
1110          */
1111         h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
1112
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;
1118
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;
1125                 }
1126         }
1127
1128         /* Send the message and retrieve the reply. */
1129         if (send_msg(h) == -1 || recv_msg(h) == -1)
1130                 return -1;
1131
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;
1135
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;
1140
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;
1148         }
1149
1150         /* Should have ended up at the end */
1151         if (get_srvr_end(h) == -1)
1152                 return -1;
1153
1154         /* Sanity checks */
1155         if (!h->single_connect)
1156                 close_connection(h);
1157
1158         return ares->av_cnt << 8 | ares->status;
1159 }
1160
1161 int
1162 tac_set_rem_addr(struct tac_handle *h, const char *addr)
1163 {
1164         return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
1165 }
1166
1167 int
1168 tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
1169 {
1170         return save_str(h, &h->data, data, data_len);
1171 }
1172
1173 int
1174 tac_set_msg(struct tac_handle *h, const char *msg)
1175 {
1176         return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1177 }
1178
1179 int
1180 tac_set_port(struct tac_handle *h, const char *port)
1181 {
1182         return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1183 }
1184
1185 int
1186 tac_set_priv(struct tac_handle *h, int priv)
1187 {
1188         if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1189                 generr(h, "Attempt to set invalid privilege level");
1190                 return -1;
1191         }
1192         h->request.u.authen_start.priv_lvl = priv;
1193         return 0;
1194 }
1195
1196 int
1197 tac_set_user(struct tac_handle *h, const char *user)
1198 {
1199         return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1200 }
1201
1202 int
1203 tac_set_av(struct tac_handle *h, u_int index, const char *av)
1204 {
1205         if (index >= MAXAVPAIRS)
1206                 return -1;
1207         return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
1208 }
1209
1210 char *
1211 tac_get_av(struct tac_handle *h, u_int index)
1212 {
1213         if (index >= MAXAVPAIRS)
1214                 return NULL;
1215         return dup_str(h, &(h->srvr_avs[index]), NULL);
1216 }
1217
1218 char *
1219 tac_get_av_value(struct tac_handle *h, const char *attribute)
1220 {
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;
1227
1228         if (attribute == NULL || ((len = strlen(attribute)) == 0))
1229                 return NULL;
1230
1231         for (i=0; i<MAXAVPAIRS; i++) {
1232                 candidate = h->srvr_avs[i].data;
1233                 candidate_len = h->srvr_avs[i].len;
1234
1235                 /*
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.
1239                  */
1240                 if (!candidate)
1241                         break;
1242
1243                 if (len < candidate_len && 
1244                     !strncmp(candidate, attribute, len)) {
1245
1246                         ch = candidate + len;
1247                         end = candidate + candidate_len;
1248
1249                         /*
1250                          * Sift out the white space between A and V (should not
1251                          * be any, but don't trust implementation of server...)
1252                          */
1253                         found_seperator = 0;
1254                         while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
1255                                 *ch == '\t') && ch != end) {
1256                                 if (*ch == '=' || *ch == '*')
1257                                         found_seperator++;
1258                                 ch++;
1259                         }
1260
1261                         /*
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.
1267                          */
1268                         if (found_seperator == 1 && ch != end) {
1269                                 srvr.len = end - ch;
1270                                 srvr.data = ch;
1271                                 return dup_str(h, &srvr, NULL);
1272                         }
1273                 }
1274         }
1275         return NULL;
1276 }
1277
1278 void
1279 tac_clear_avs(struct tac_handle *h)
1280 {
1281         int i;
1282         for (i=0; i<MAXAVPAIRS; i++)
1283                 save_str(h, &(h->avs[i]), NULL, 0);
1284 }
1285
1286 static void
1287 clear_srvr_avs(struct tac_handle *h)
1288 {
1289         int i;
1290         for (i=0; i<MAXAVPAIRS; i++)
1291                 init_srvr_str(&(h->srvr_avs[i]));
1292 }
1293
1294
1295 const char *
1296 tac_strerror(struct tac_handle *h)
1297 {
1298         return h->errmsg;
1299 }
1300
1301 static void *
1302 xmalloc(struct tac_handle *h, size_t size)
1303 {
1304         void *r;
1305
1306         if ((r = malloc(size)) == NULL)
1307                 generr(h, "Out of memory");
1308         return r;
1309 }
1310
1311 static char *
1312 xstrdup(struct tac_handle *h, const char *s)
1313 {
1314         char *r;
1315
1316         if ((r = strdup(s)) == NULL)
1317                 generr(h, "Out of memory");
1318         return r;
1319 }