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