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