]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libtacplus/taclib.c
This commit was generated by cvs2svn to compensate for changes in r95504,
[FreeBSD/FreeBSD.git] / lib / libtacplus / taclib.c
1 /*-
2  * Copyright 1998 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               authen_version(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 *, struct srvr_str *,
67                             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
80 /*
81  * Append some optional data to the current request, and store its
82  * length into the 8-bit field referenced by "fld".  Returns 0 on
83  * success, or -1 on failure.
84  *
85  * This function also frees the "cs" string data and initializes it
86  * for the next time.
87  */
88 static int
89 add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
90 {
91         u_int16_t len;
92
93         if (add_str_16(h, &len, cs) == -1)
94                 return -1;
95         len = ntohs(len);
96         if (len > 0xff) {
97                 generr(h, "Field too long");
98                 return -1;
99         }
100         *fld = len;
101         return 0;
102 }
103
104 /*
105  * Append some optional data to the current request, and store its
106  * length into the 16-bit field (network byte order) referenced by
107  * "fld".  Returns 0 on success, or -1 on failure.
108  *
109  * This function also frees the "cs" string data and initializes it
110  * for the next time.
111  */
112 static int
113 add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
114 {
115         size_t len;
116
117         len = cs->len;
118         if (cs->data == NULL)
119                 len = 0;
120         if (len != 0) {
121                 int offset;
122
123                 if (len > 0xffff) {
124                         generr(h, "Field too long");
125                         return -1;
126                 }
127                 offset = ntohl(h->request.length);
128                 if (offset + len > BODYSIZE) {
129                         generr(h, "Message too long");
130                         return -1;
131                 }
132                 memcpy(h->request.u.body + offset, cs->data, len);
133                 h->request.length = htonl(offset + len);
134         }
135         *fld = htons(len);
136         free_str(cs);
137         return 0;
138 }
139
140 static int
141 authen_version(int action, int type)
142 {
143         int minor;
144
145         switch (action) {
146
147         case TAC_AUTHEN_LOGIN:
148                 switch (type) {
149
150                 case TAC_AUTHEN_TYPE_PAP:
151                 case TAC_AUTHEN_TYPE_CHAP:
152                 case TAC_AUTHEN_TYPE_MSCHAP:
153                 case TAC_AUTHEN_TYPE_ARAP:
154                         minor = 1;
155                         break;
156
157                 default:
158                         minor = 0;
159                         break;
160                 }
161                 break;
162
163         case TAC_AUTHEN_SENDAUTH:
164                 minor = 1;
165                 break;
166
167         default:
168                 minor = 0;
169                 break;
170         };
171
172         return TAC_VER_MAJOR << 4 | minor;
173 }
174
175 static void
176 close_connection(struct tac_handle *h)
177 {
178         if (h->fd != -1) {
179                 close(h->fd);
180                 h->fd = -1;
181         }
182 }
183
184 static int
185 conn_server(struct tac_handle *h)
186 {
187         const struct tac_server *srvp = &h->servers[h->cur_server];
188         int flags;
189
190         if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
191                 generr(h, "Cannot create socket: %s", strerror(errno));
192                 return -1;
193         }
194         if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
195             fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
196                 generr(h, "Cannot set non-blocking mode on socket: %s",
197                     strerror(errno));
198                 close(h->fd);
199                 h->fd = -1;
200                 return -1;
201         }
202         if (connect(h->fd, (struct sockaddr *)&srvp->addr,
203             sizeof srvp->addr) == 0)
204                 return 0;
205
206         if (errno == EINPROGRESS) {
207                 fd_set wfds;
208                 struct timeval tv;
209                 int nfds;
210                 struct sockaddr peer;
211                 int peerlen;
212                 int err;
213                 int errlen;
214
215                 /* Wait for the connection to complete. */
216                 FD_ZERO(&wfds);
217                 FD_SET(h->fd, &wfds);
218                 tv.tv_sec = srvp->timeout;
219                 tv.tv_usec = 0;
220                 nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
221                 if (nfds == -1) {
222                         generr(h, "select: %s", strerror(errno));
223                         close(h->fd);
224                         h->fd = -1;
225                         return -1;
226                 }
227                 if (nfds == 0) {
228                         generr(h, "connect: timed out");
229                         close(h->fd);
230                         h->fd = -1;
231                         return -1;
232                 }
233
234                 /* See whether we are connected now. */
235                 peerlen = sizeof peer;
236                 if (getpeername(h->fd, &peer, &peerlen) == 0)
237                         return 0;
238
239                 if (errno != ENOTCONN) {
240                         generr(h, "getpeername: %s", strerror(errno));
241                         close(h->fd);
242                         h->fd = -1;
243                         return -1;
244                 }
245
246                 /* Find out why the connect failed. */
247                 errlen = sizeof err;
248                 getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
249                 errno = err;
250         }
251         generr(h, "connect: %s", strerror(errno));
252         close(h->fd);
253         h->fd = -1;
254         return -1;
255 }
256
257 /*
258  * Encrypt or decrypt a message.  The operations are symmetrical.
259  */
260 static void
261 crypt_msg(struct tac_handle *h, struct tac_msg *msg)
262 {
263         const char *secret;
264         MD5_CTX base_ctx;
265         MD5_CTX ctx;
266         unsigned char md5[16];
267         int chunk;
268         int msg_len;
269
270         secret = h->servers[h->cur_server].secret;
271         if (secret[0] == '\0')
272                 msg->flags |= TAC_UNENCRYPTED;
273         if (msg->flags & TAC_UNENCRYPTED)
274                 return;
275
276         msg_len = ntohl(msg->length);
277
278         MD5Init(&base_ctx);
279         MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
280         MD5Update(&base_ctx, secret, strlen(secret));
281         MD5Update(&base_ctx, &msg->version, sizeof msg->version);
282         MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
283
284         ctx = base_ctx;
285         for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
286                 int chunk_len;
287                 int i;
288
289                 MD5Final(md5, &ctx);
290
291                 if ((chunk_len = msg_len - chunk) > sizeof md5)
292                         chunk_len = sizeof md5;
293                 for (i = 0;  i < chunk_len;  i++)
294                         msg->u.body[chunk + i] ^= md5[i];
295
296                 ctx = base_ctx;
297                 MD5Update(&ctx, md5, sizeof md5);
298         }
299 }
300
301 /*
302  * Return a dynamically allocated copy of the given server string.
303  * The copy is null-terminated.  If "len" is non-NULL, the length of
304  * the string (excluding the terminating null byte) is stored via it.
305  * Returns NULL on failure.  Empty strings are still allocated even
306  * though they have no content.
307  */
308 static void *
309 dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
310 {
311         unsigned char *p;
312
313         if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
314                 return NULL;
315         if (ss->data != NULL && ss->len != 0)
316                 memcpy(p, ss->data, ss->len);
317         p[ss->len] = '\0';
318         if (len != NULL)
319                 *len = ss->len;
320         return p;
321 }
322
323 static int
324 establish_connection(struct tac_handle *h)
325 {
326         int i;
327
328         if (h->fd >= 0)         /* Already connected. */
329                 return 0;
330         if (h->num_servers == 0) {
331                 generr(h, "No TACACS+ servers specified");
332                 return -1;
333         }
334         /*
335          * Try the servers round-robin.  We begin with the one that
336          * worked for us the last time.  That way, once we find a good
337          * server, we won't waste any more time trying the bad ones.
338          */
339         for (i = 0;  i < h->num_servers;  i++) {
340                 if (conn_server(h) == 0) {
341                         h->single_connect = (h->servers[h->cur_server].flags &
342                             TAC_SRVR_SINGLE_CONNECT) != 0;
343                         return 0;
344                 }
345                 if (++h->cur_server >= h->num_servers)  /* Wrap around */
346                         h->cur_server = 0;
347         }
348         /* Just return whatever error was last reported by conn_server(). */
349         return -1;
350 }
351
352 /*
353  * Free a client string, obliterating its contents first for security.
354  */
355 static void
356 free_str(struct clnt_str *cs)
357 {
358         if (cs->data != NULL) {
359                 memset(cs->data, 0, cs->len);
360                 free(cs->data);
361                 cs->data = NULL;
362                 cs->len = 0;
363         }
364 }
365
366 static void
367 generr(struct tac_handle *h, const char *format, ...)
368 {
369         va_list          ap;
370
371         va_start(ap, format);
372         vsnprintf(h->errmsg, ERRSIZE, format, ap);
373         va_end(ap);
374 }
375
376 static void
377 gen_session_id(struct tac_msg *msg)
378 {
379         int r;
380
381         r = random();
382         msg->session_id[0] = r >> 8;
383         msg->session_id[1] = r;
384         r = random();
385         msg->session_id[2] = r >> 8;
386         msg->session_id[3] = r;
387 }
388
389 /*
390  * Verify that we are exactly at the end of the response message.
391  * Returns 0 on success, -1 on failure.
392  */
393 static int
394 get_srvr_end(struct tac_handle *h)
395 {
396         if (h->srvr_pos != ntohl(h->response.length)) {
397                 generr(h, "Invalid length field in response from server");
398                 return -1;
399         }
400         return 0;
401 }
402
403 static int
404 get_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len)
405 {
406         if (h->srvr_pos + len > ntohl(h->response.length)) {
407                 generr(h, "Invalid length field in response from server");
408                 return -1;
409         }
410         ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
411         ss->len = len;
412         h->srvr_pos += len;
413         return 0;
414 }
415
416 static void
417 init_clnt_str(struct clnt_str *cs)
418 {
419         cs->data = NULL;
420         cs->len = 0;
421 }
422
423 static void
424 init_srvr_str(struct srvr_str *ss)
425 {
426         ss->data = NULL;
427         ss->len = 0;
428 }
429
430 static int
431 read_timed(struct tac_handle *h, void *buf, size_t len,
432     const struct timeval *deadline)
433 {
434         char *ptr;
435
436         ptr = (char *)buf;
437         while (len > 0) {
438                 int n;
439
440                 n = read(h->fd, ptr, len);
441                 if (n == -1) {
442                         struct timeval tv;
443                         int nfds;
444
445                         if (errno != EAGAIN) {
446                                 generr(h, "Network read error: %s",
447                                     strerror(errno));
448                                 return -1;
449                         }
450
451                         /* Wait until we can read more data. */
452                         gettimeofday(&tv, NULL);
453                         timersub(deadline, &tv, &tv);
454                         if (tv.tv_sec >= 0) {
455                                 fd_set rfds;
456
457                                 FD_ZERO(&rfds);
458                                 FD_SET(h->fd, &rfds);
459                                 nfds =
460                                     select(h->fd + 1, &rfds, NULL, NULL, &tv);
461                                 if (nfds == -1) {
462                                         generr(h, "select: %s",
463                                             strerror(errno));
464                                         return -1;
465                                 }
466                         } else
467                                 nfds = 0;
468                         if (nfds == 0) {
469                                 generr(h, "Network read timed out");
470                                 return -1;
471                         }
472                 } else if (n == 0) {
473                         generr(h, "unexpected EOF from server");
474                         return -1;
475                 } else {
476                         ptr += n;
477                         len -= n;
478                 }
479         }
480         return 0;
481 }
482
483 /*
484  * Receive a response from the server and decrypt it.  Returns 0 on
485  * success, or -1 on failure.
486  */
487 static int
488 recv_msg(struct tac_handle *h)
489 {
490         struct timeval deadline;
491         struct tac_msg *msg;
492         size_t len;
493
494         msg = &h->response;
495         gettimeofday(&deadline, NULL);
496         deadline.tv_sec += h->servers[h->cur_server].timeout;
497
498         /* Read the message header and make sure it is reasonable. */
499         if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
500                 return -1;
501         if (memcmp(msg->session_id, h->request.session_id,
502             sizeof msg->session_id) != 0) {
503                 generr(h, "Invalid session ID in received message");
504                 return -1;
505         }
506         if (msg->type != h->request.type) {
507                 generr(h, "Invalid type in received message");
508                 return -1;
509         }
510         len = ntohl(msg->length);
511         if (len > BODYSIZE) {
512                 generr(h, "Received message too large");
513                 return -1;
514         }
515         if (msg->seq_no != ++h->last_seq_no) {
516                 generr(h, "Invalid sequence number in received message");
517                 return -1;
518         }
519
520         /* Read the message body. */
521         if (read_timed(h, msg->u.body, len, &deadline) == -1)
522                 return -1;
523
524         /* Decrypt it. */
525         crypt_msg(h, msg);
526
527         /*
528          * Turn off single-connection mode if the server isn't amenable
529          * to it.
530          */
531         if (!(msg->flags & TAC_SINGLE_CONNECT))
532                 h->single_connect = 0;
533         return 0;
534 }
535
536 static int
537 save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
538     size_t len)
539 {
540         free_str(cs);
541         if (data != NULL && len != 0) {
542                 if ((cs->data = xmalloc(h, len)) == NULL)
543                         return -1;
544                 cs->len = len;
545                 memcpy(cs->data, data, len);
546         }
547         return 0;
548 }
549
550 /*
551  * Send the current request, after encrypting it.  Returns 0 on success,
552  * or -1 on failure.
553  */
554 static int
555 send_msg(struct tac_handle *h)
556 {
557         struct timeval deadline;
558         struct tac_msg *msg;
559         char *ptr;
560         int len;
561
562         if (h->last_seq_no & 1) {
563                 generr(h, "Attempt to send message out of sequence");
564                 return -1;
565         }
566
567         msg = &h->request;
568         msg->seq_no = ++h->last_seq_no;
569         if (msg->seq_no == 1)
570                 gen_session_id(msg);
571         crypt_msg(h, msg);
572
573         if (establish_connection(h) == -1)
574                 return -1;
575
576         if (h->single_connect)
577                 msg->flags |= TAC_SINGLE_CONNECT;
578         else
579                 msg->flags &= ~TAC_SINGLE_CONNECT;
580         gettimeofday(&deadline, NULL);
581         deadline.tv_sec += h->servers[h->cur_server].timeout;
582         len = HDRSIZE + ntohl(msg->length);
583         ptr = (char *)msg;
584         while (len > 0) {
585                 int n;
586
587                 n = write(h->fd, ptr, len);
588                 if (n == -1) {
589                         struct timeval tv;
590                         int nfds;
591
592                         if (errno != EAGAIN) {
593                                 generr(h, "Network write error: %s",
594                                     strerror(errno));
595                                 return -1;
596                         }
597
598                         /* Wait until we can write more data. */
599                         gettimeofday(&tv, NULL);
600                         timersub(&deadline, &tv, &tv);
601                         if (tv.tv_sec >= 0) {
602                                 fd_set wfds;
603
604                                 FD_ZERO(&wfds);
605                                 FD_SET(h->fd, &wfds);
606                                 nfds =
607                                     select(h->fd + 1, NULL, &wfds, NULL, &tv);
608                                 if (nfds == -1) {
609                                         generr(h, "select: %s",
610                                             strerror(errno));
611                                         return -1;
612                                 }
613                         } else
614                                 nfds = 0;
615                         if (nfds == 0) {
616                                 generr(h, "Network write timed out");
617                                 return -1;
618                         }
619                 } else {
620                         ptr += n;
621                         len -= n;
622                 }
623         }
624         return 0;
625 }
626
627 /*
628  * Destructively split a string into fields separated by white space.
629  * `#' at the beginning of a field begins a comment that extends to the
630  * end of the string.  Fields may be quoted with `"'.  Inside quoted
631  * strings, the backslash escapes `\"' and `\\' are honored.
632  *
633  * Pointers to up to the first maxfields fields are stored in the fields
634  * array.  Missing fields get NULL pointers.
635  *
636  * The return value is the actual number of fields parsed, and is always
637  * <= maxfields.
638  *
639  * On a syntax error, places a message in the msg string, and returns -1.
640  */
641 static int
642 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
643 {
644         char *p;
645         int i;
646         static const char ws[] = " \t";
647
648         for (i = 0;  i < maxfields;  i++)
649                 fields[i] = NULL;
650         p = str;
651         i = 0;
652         while (*p != '\0') {
653                 p += strspn(p, ws);
654                 if (*p == '#' || *p == '\0')
655                         break;
656                 if (i >= maxfields) {
657                         snprintf(msg, msglen, "line has too many fields");
658                         return -1;
659                 }
660                 if (*p == '"') {
661                         char *dst;
662
663                         dst = ++p;
664                         fields[i] = dst;
665                         while (*p != '"') {
666                                 if (*p == '\\') {
667                                         p++;
668                                         if (*p != '"' && *p != '\\' &&
669                                             *p != '\0') {
670                                                 snprintf(msg, msglen,
671                                                     "invalid `\\' escape");
672                                                 return -1;
673                                         }
674                                 }
675                                 if (*p == '\0') {
676                                         snprintf(msg, msglen,
677                                             "unterminated quoted string");
678                                         return -1;
679                                 }
680                                 *dst++ = *p++;
681                         }
682                         *dst = '\0';
683                         p++;
684                         if (*p != '\0' && strspn(p, ws) == 0) {
685                                 snprintf(msg, msglen, "quoted string not"
686                                     " followed by white space");
687                                 return -1;
688                         }
689                 } else {
690                         fields[i] = p;
691                         p += strcspn(p, ws);
692                         if (*p != '\0')
693                                 *p++ = '\0';
694                 }
695                 i++;
696         }
697         return i;
698 }
699
700 int
701 tac_add_server(struct tac_handle *h, const char *host, int port,
702     const char *secret, int timeout, int flags)
703 {
704         struct tac_server *srvp;
705
706         if (h->num_servers >= MAXSERVERS) {
707                 generr(h, "Too many TACACS+ servers specified");
708                 return -1;
709         }
710         srvp = &h->servers[h->num_servers];
711
712         memset(&srvp->addr, 0, sizeof srvp->addr);
713         srvp->addr.sin_len = sizeof srvp->addr;
714         srvp->addr.sin_family = AF_INET;
715         if (!inet_aton(host, &srvp->addr.sin_addr)) {
716                 struct hostent *hent;
717
718                 if ((hent = gethostbyname(host)) == NULL) {
719                         generr(h, "%s: host not found", host);
720                         return -1;
721                 }
722                 memcpy(&srvp->addr.sin_addr, hent->h_addr,
723                     sizeof srvp->addr.sin_addr);
724         }
725         srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
726         if ((srvp->secret = xstrdup(h, secret)) == NULL)
727                 return -1;
728         srvp->timeout = timeout;
729         srvp->flags = flags;
730         h->num_servers++;
731         return 0;
732 }
733
734 void
735 tac_close(struct tac_handle *h)
736 {
737         int srv;
738
739         if (h->fd != -1)
740                 close(h->fd);
741         for (srv = 0;  srv < h->num_servers;  srv++) {
742                 memset(h->servers[srv].secret, 0,
743                     strlen(h->servers[srv].secret));
744                 free(h->servers[srv].secret);
745         }
746         free_str(&h->user);
747         free_str(&h->port);
748         free_str(&h->rem_addr);
749         free_str(&h->data);
750         free_str(&h->user_msg);
751         free(h);
752 }
753
754 int
755 tac_config(struct tac_handle *h, const char *path)
756 {
757         FILE *fp;
758         char buf[MAXCONFLINE];
759         int linenum;
760         int retval;
761
762         if (path == NULL)
763                 path = PATH_TACPLUS_CONF;
764         if ((fp = fopen(path, "r")) == NULL) {
765                 generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
766                 return -1;
767         }
768         retval = 0;
769         linenum = 0;
770         while (fgets(buf, sizeof buf, fp) != NULL) {
771                 int len;
772                 char *fields[4];
773                 int nfields;
774                 char msg[ERRSIZE];
775                 char *host, *res;
776                 char *port_str;
777                 char *secret;
778                 char *timeout_str;
779                 char *options_str;
780                 char *end;
781                 unsigned long timeout;
782                 int port;
783                 int options;
784
785                 linenum++;
786                 len = strlen(buf);
787                 /* We know len > 0, else fgets would have returned NULL. */
788                 if (buf[len - 1] != '\n') {
789                         if (len == sizeof buf - 1)
790                                 generr(h, "%s:%d: line too long", path,
791                                     linenum);
792                         else
793                                 generr(h, "%s:%d: missing newline", path,
794                                     linenum);
795                         retval = -1;
796                         break;
797                 }
798                 buf[len - 1] = '\0';
799
800                 /* Extract the fields from the line. */
801                 nfields = split(buf, fields, 4, msg, sizeof msg);
802                 if (nfields == -1) {
803                         generr(h, "%s:%d: %s", path, linenum, msg);
804                         retval = -1;
805                         break;
806                 }
807                 if (nfields == 0)
808                         continue;
809                 if (nfields < 2) {
810                         generr(h, "%s:%d: missing shared secret", path,
811                             linenum);
812                         retval = -1;
813                         break;
814                 }
815                 host = fields[0];
816                 secret = fields[1];
817                 timeout_str = fields[2];
818                 options_str = fields[3];
819
820                 /* Parse and validate the fields. */
821                 res = host;
822                 host = strsep(&res, ":");
823                 port_str = strsep(&res, ":");
824                 if (port_str != NULL) {
825                         port = strtoul(port_str, &end, 10);
826                         if (port_str[0] == '\0' || *end != '\0') {
827                                 generr(h, "%s:%d: invalid port", path,
828                                     linenum);
829                                 retval = -1;
830                                 break;
831                         }
832                 } else
833                         port = 0;
834                 if (timeout_str != NULL) {
835                         timeout = strtoul(timeout_str, &end, 10);
836                         if (timeout_str[0] == '\0' || *end != '\0') {
837                                 generr(h, "%s:%d: invalid timeout", path,
838                                     linenum);
839                                 retval = -1;
840                                 break;
841                         }
842                 } else
843                         timeout = TIMEOUT;
844                 options = 0;
845                 if (options_str != NULL) {
846                         if (strcmp(options_str, "single-connection") == 0)
847                                 options |= TAC_SRVR_SINGLE_CONNECT;
848                         else {
849                                 generr(h, "%s:%d: invalid option \"%s\"",
850                                     path, linenum, options_str);
851                                 retval = -1;
852                                 break;
853                         }
854                 };
855
856                 if (tac_add_server(h, host, port, secret, timeout,
857                     options) == -1) {
858                         char msg[ERRSIZE];
859
860                         strcpy(msg, h->errmsg);
861                         generr(h, "%s:%d: %s", path, linenum, msg);
862                         retval = -1;
863                         break;
864                 }
865         }
866         /* Clear out the buffer to wipe a possible copy of a shared secret */
867         memset(buf, 0, sizeof buf);
868         fclose(fp);
869         return retval;
870 }
871
872 int
873 tac_create_authen(struct tac_handle *h, int action, int type, int service)
874 {
875         struct tac_msg *msg;
876         struct tac_authen_start *as;
877
878         h->last_seq_no = 0;
879
880         msg = &h->request;
881         msg->type = TAC_AUTHEN;
882         msg->version = authen_version(action, type);
883         msg->flags = 0;
884
885         as = &msg->u.authen_start;
886         as->action = action;
887         as->priv_lvl = TAC_PRIV_LVL_USER;
888         as->authen_type = type;
889         as->service = service;
890
891         free_str(&h->user);
892         free_str(&h->port);
893         free_str(&h->rem_addr);
894         free_str(&h->data);
895         free_str(&h->user_msg);
896
897         /* XXX - more to do */
898         return 0;
899 }
900
901 void *
902 tac_get_data(struct tac_handle *h, size_t *len)
903 {
904         return dup_str(h, &h->srvr_data, len);
905 }
906
907 char *
908 tac_get_msg(struct tac_handle *h)
909 {
910         return (char *)dup_str(h, &h->srvr_msg, NULL);
911 }
912
913 /*
914  * Create and initialize a tac_handle structure, and return it to the
915  * caller.  Can fail only if the necessary memory cannot be allocated.
916  * In that case, it returns NULL.
917  */
918 struct tac_handle *
919 tac_open(void)
920 {
921         struct tac_handle *h;
922
923         h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
924         if (h != NULL) {
925                 h->fd = -1;
926                 h->num_servers = 0;
927                 h->cur_server = 0;
928                 h->errmsg[0] = '\0';
929                 init_clnt_str(&h->user);
930                 init_clnt_str(&h->port);
931                 init_clnt_str(&h->rem_addr);
932                 init_clnt_str(&h->data);
933                 init_clnt_str(&h->user_msg);
934                 init_srvr_str(&h->srvr_msg);
935                 init_srvr_str(&h->srvr_data);
936                 srandomdev();
937         }
938         return h;
939 }
940
941 int
942 tac_send_authen(struct tac_handle *h)
943 {
944         struct tac_authen_reply *ar;
945
946         if (h->last_seq_no == 0) {      /* Authentication START packet */
947                 struct tac_authen_start *as;
948
949                 as = &h->request.u.authen_start;
950                 h->request.length =
951                     htonl(offsetof(struct tac_authen_start, rest[0]));
952                 if (add_str_8(h, &as->user_len, &h->user) == -1 ||
953                     add_str_8(h, &as->port_len, &h->port) == -1 ||
954                     add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
955                     add_str_8(h, &as->data_len, &h->data) == -1)
956                         return -1;
957         } else {                        /* Authentication CONTINUE packet */
958                 struct tac_authen_cont *ac;
959
960                 ac = &h->request.u.authen_cont;
961                 ac->flags = 0;
962                 h->request.length =
963                     htonl(offsetof(struct tac_authen_cont, rest[0]));
964                 if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
965                     add_str_16(h, &ac->data_len, &h->data) == -1)
966                         return -1;
967         }
968
969         /* Send the message and retrieve the reply. */
970         if (send_msg(h) == -1 || recv_msg(h) == -1)
971                 return -1;
972
973         /* Scan the optional fields in the reply. */
974         ar = &h->response.u.authen_reply;
975         h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
976         if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
977             get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 ||
978             get_srvr_end(h) == -1)
979                 return -1;
980
981         if (!h->single_connect &&
982             ar->status != TAC_AUTHEN_STATUS_GETDATA &&
983             ar->status != TAC_AUTHEN_STATUS_GETUSER &&
984             ar->status != TAC_AUTHEN_STATUS_GETPASS)
985                 close_connection(h);
986
987         return ar->flags << 8 | ar->status;
988 }
989
990 int
991 tac_set_rem_addr(struct tac_handle *h, const char *addr)
992 {
993         return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
994 }
995
996 int
997 tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
998 {
999         return save_str(h, &h->data, data, data_len);
1000 }
1001
1002 int
1003 tac_set_msg(struct tac_handle *h, const char *msg)
1004 {
1005         return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1006 }
1007
1008 int
1009 tac_set_port(struct tac_handle *h, const char *port)
1010 {
1011         return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1012 }
1013
1014 int
1015 tac_set_priv(struct tac_handle *h, int priv)
1016 {
1017         if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1018                 generr(h, "Attempt to set invalid privilege level");
1019                 return -1;
1020         }
1021         h->request.u.authen_start.priv_lvl = priv;
1022         return 0;
1023 }
1024
1025 int
1026 tac_set_user(struct tac_handle *h, const char *user)
1027 {
1028         return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1029 }
1030
1031 const char *
1032 tac_strerror(struct tac_handle *h)
1033 {
1034         return h->errmsg;
1035 }
1036
1037 static void *
1038 xmalloc(struct tac_handle *h, size_t size)
1039 {
1040         void *r;
1041
1042         if ((r = malloc(size)) == NULL)
1043                 generr(h, "Out of memory");
1044         return r;
1045 }
1046
1047 static char *
1048 xstrdup(struct tac_handle *h, const char *s)
1049 {
1050         char *r;
1051
1052         if ((r = strdup(s)) == NULL)
1053                 generr(h, "Out of memory");
1054         return r;
1055 }