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