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