12c195535SJohn Polstra /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
35e53a4f9SPedro F. Giffuni *
41a61aeb8SPaul Traina * Copyright (c) 1998, 2001, 2002, Juniper Networks, Inc.
52c195535SJohn Polstra * All rights reserved.
62c195535SJohn Polstra *
72c195535SJohn Polstra * Redistribution and use in source and binary forms, with or without
82c195535SJohn Polstra * modification, are permitted provided that the following conditions
92c195535SJohn Polstra * are met:
102c195535SJohn Polstra * 1. Redistributions of source code must retain the above copyright
112c195535SJohn Polstra * notice, this list of conditions and the following disclaimer.
122c195535SJohn Polstra * 2. Redistributions in binary form must reproduce the above copyright
132c195535SJohn Polstra * notice, this list of conditions and the following disclaimer in the
142c195535SJohn Polstra * documentation and/or other materials provided with the distribution.
152c195535SJohn Polstra *
162c195535SJohn Polstra * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
172c195535SJohn Polstra * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
182c195535SJohn Polstra * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
192c195535SJohn Polstra * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
202c195535SJohn Polstra * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
212c195535SJohn Polstra * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
222c195535SJohn Polstra * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
232c195535SJohn Polstra * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
242c195535SJohn Polstra * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
252c195535SJohn Polstra * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
262c195535SJohn Polstra * SUCH DAMAGE.
272c195535SJohn Polstra */
282c195535SJohn Polstra
292c195535SJohn Polstra #include <sys/types.h>
302c195535SJohn Polstra #include <sys/socket.h>
312c195535SJohn Polstra #include <sys/time.h>
322c195535SJohn Polstra #include <netinet/in.h>
332c195535SJohn Polstra #include <arpa/inet.h>
342c195535SJohn Polstra
352c195535SJohn Polstra #include <assert.h>
36*21850106SDag-Erling Smørgrav #include <ctype.h>
372c195535SJohn Polstra #include <errno.h>
382c195535SJohn Polstra #include <fcntl.h>
392c195535SJohn Polstra #include <md5.h>
402c195535SJohn Polstra #include <netdb.h>
412c195535SJohn Polstra #include <stdarg.h>
422c195535SJohn Polstra #include <stddef.h>
432c195535SJohn Polstra #include <stdio.h>
442c195535SJohn Polstra #include <stdlib.h>
452c195535SJohn Polstra #include <string.h>
462c195535SJohn Polstra #include <unistd.h>
472c195535SJohn Polstra
48*21850106SDag-Erling Smørgrav #include <security/pam_appl.h>
49*21850106SDag-Erling Smørgrav #include <security/openpam.h>
50*21850106SDag-Erling Smørgrav
512c195535SJohn Polstra #include "taclib_private.h"
522c195535SJohn Polstra
532c195535SJohn Polstra static int add_str_8(struct tac_handle *, u_int8_t *,
54*21850106SDag-Erling Smørgrav struct tac_str *);
552c195535SJohn Polstra static int add_str_16(struct tac_handle *, u_int16_t *,
56*21850106SDag-Erling Smørgrav struct tac_str *);
571a61aeb8SPaul Traina static int protocol_version(int, int, int);
582c195535SJohn Polstra static void close_connection(struct tac_handle *);
592c195535SJohn Polstra static int conn_server(struct tac_handle *);
602c195535SJohn Polstra static void crypt_msg(struct tac_handle *, struct tac_msg *);
61*21850106SDag-Erling Smørgrav static void *dup_str(struct tac_handle *, const struct tac_str *,
622c195535SJohn Polstra size_t *);
632c195535SJohn Polstra static int establish_connection(struct tac_handle *);
64*21850106SDag-Erling Smørgrav static void free_str(struct tac_str *);
652c195535SJohn Polstra static void generr(struct tac_handle *, const char *, ...)
662c195535SJohn Polstra __printflike(2, 3);
672c195535SJohn Polstra static void gen_session_id(struct tac_msg *);
682c195535SJohn Polstra static int get_srvr_end(struct tac_handle *);
69*21850106SDag-Erling Smørgrav static int get_str(struct tac_handle *, const char *,
70*21850106SDag-Erling Smørgrav struct tac_str *, size_t);
71*21850106SDag-Erling Smørgrav static void init_str(struct tac_str *);
722c195535SJohn Polstra static int read_timed(struct tac_handle *, void *, size_t,
732c195535SJohn Polstra const struct timeval *);
742c195535SJohn Polstra static int recv_msg(struct tac_handle *);
75*21850106SDag-Erling Smørgrav static int save_str(struct tac_handle *, struct tac_str *,
762c195535SJohn Polstra const void *, size_t);
772c195535SJohn Polstra static int send_msg(struct tac_handle *);
782c195535SJohn Polstra static int split(char *, char *[], int, char *, size_t);
792c195535SJohn Polstra static void *xmalloc(struct tac_handle *, size_t);
802c195535SJohn Polstra static char *xstrdup(struct tac_handle *, const char *);
811a61aeb8SPaul Traina static void clear_srvr_avs(struct tac_handle *);
821a61aeb8SPaul Traina static void create_msg(struct tac_handle *, int, int, int);
832c195535SJohn Polstra
842c195535SJohn Polstra /*
852c195535SJohn Polstra * Append some optional data to the current request, and store its
862c195535SJohn Polstra * length into the 8-bit field referenced by "fld". Returns 0 on
872c195535SJohn Polstra * success, or -1 on failure.
882c195535SJohn Polstra *
892c195535SJohn Polstra * This function also frees the "cs" string data and initializes it
902c195535SJohn Polstra * for the next time.
912c195535SJohn Polstra */
922c195535SJohn Polstra static int
add_str_8(struct tac_handle * h,u_int8_t * fld,struct tac_str * cs)93*21850106SDag-Erling Smørgrav add_str_8(struct tac_handle *h, u_int8_t *fld, struct tac_str *cs)
942c195535SJohn Polstra {
952c195535SJohn Polstra u_int16_t len;
962c195535SJohn Polstra
972c195535SJohn Polstra if (add_str_16(h, &len, cs) == -1)
982c195535SJohn Polstra return -1;
992c195535SJohn Polstra len = ntohs(len);
1002c195535SJohn Polstra if (len > 0xff) {
1012c195535SJohn Polstra generr(h, "Field too long");
1022c195535SJohn Polstra return -1;
1032c195535SJohn Polstra }
1042c195535SJohn Polstra *fld = len;
1052c195535SJohn Polstra return 0;
1062c195535SJohn Polstra }
1072c195535SJohn Polstra
1082c195535SJohn Polstra /*
1092c195535SJohn Polstra * Append some optional data to the current request, and store its
1102c195535SJohn Polstra * length into the 16-bit field (network byte order) referenced by
1112c195535SJohn Polstra * "fld". Returns 0 on success, or -1 on failure.
1122c195535SJohn Polstra *
1132c195535SJohn Polstra * This function also frees the "cs" string data and initializes it
1142c195535SJohn Polstra * for the next time.
1152c195535SJohn Polstra */
1162c195535SJohn Polstra static int
add_str_16(struct tac_handle * h,u_int16_t * fld,struct tac_str * cs)117*21850106SDag-Erling Smørgrav add_str_16(struct tac_handle *h, u_int16_t *fld, struct tac_str *cs)
1182c195535SJohn Polstra {
1192c195535SJohn Polstra size_t len;
1202c195535SJohn Polstra
1212c195535SJohn Polstra len = cs->len;
1222c195535SJohn Polstra if (cs->data == NULL)
1232c195535SJohn Polstra len = 0;
1242c195535SJohn Polstra if (len != 0) {
1252c195535SJohn Polstra int offset;
1262c195535SJohn Polstra
1272c195535SJohn Polstra if (len > 0xffff) {
1282c195535SJohn Polstra generr(h, "Field too long");
1292c195535SJohn Polstra return -1;
1302c195535SJohn Polstra }
1312c195535SJohn Polstra offset = ntohl(h->request.length);
1322c195535SJohn Polstra if (offset + len > BODYSIZE) {
1332c195535SJohn Polstra generr(h, "Message too long");
1342c195535SJohn Polstra return -1;
1352c195535SJohn Polstra }
1362c195535SJohn Polstra memcpy(h->request.u.body + offset, cs->data, len);
1372c195535SJohn Polstra h->request.length = htonl(offset + len);
1382c195535SJohn Polstra }
1392c195535SJohn Polstra *fld = htons(len);
1402c195535SJohn Polstra free_str(cs);
1412c195535SJohn Polstra return 0;
1422c195535SJohn Polstra }
1432c195535SJohn Polstra
1442c195535SJohn Polstra static int
protocol_version(int msg_type,int var,int type)1451a61aeb8SPaul Traina protocol_version(int msg_type, int var, int type)
1462c195535SJohn Polstra {
1472c195535SJohn Polstra int minor;
1482c195535SJohn Polstra
1491a61aeb8SPaul Traina switch (msg_type) {
1501a61aeb8SPaul Traina case TAC_AUTHEN:
1511a61aeb8SPaul Traina /* 'var' represents the 'action' */
1521a61aeb8SPaul Traina switch (var) {
1532c195535SJohn Polstra case TAC_AUTHEN_LOGIN:
1542c195535SJohn Polstra switch (type) {
1552c195535SJohn Polstra
1562c195535SJohn Polstra case TAC_AUTHEN_TYPE_PAP:
1572c195535SJohn Polstra case TAC_AUTHEN_TYPE_CHAP:
1582c195535SJohn Polstra case TAC_AUTHEN_TYPE_MSCHAP:
1592c195535SJohn Polstra case TAC_AUTHEN_TYPE_ARAP:
1602c195535SJohn Polstra minor = 1;
1612c195535SJohn Polstra break;
1622c195535SJohn Polstra
1632c195535SJohn Polstra default:
1642c195535SJohn Polstra minor = 0;
1652c195535SJohn Polstra break;
1662c195535SJohn Polstra }
1672c195535SJohn Polstra break;
1682c195535SJohn Polstra
1692c195535SJohn Polstra case TAC_AUTHEN_SENDAUTH:
1702c195535SJohn Polstra minor = 1;
1712c195535SJohn Polstra break;
1722c195535SJohn Polstra
1732c195535SJohn Polstra default:
1742c195535SJohn Polstra minor = 0;
1752c195535SJohn Polstra break;
1762c195535SJohn Polstra };
1771a61aeb8SPaul Traina break;
1781a61aeb8SPaul Traina
1791a61aeb8SPaul Traina case TAC_AUTHOR:
1801a61aeb8SPaul Traina /* 'var' represents the 'method' */
1811a61aeb8SPaul Traina switch (var) {
1821a61aeb8SPaul Traina /*
1831a61aeb8SPaul Traina * When new authentication methods are added, include 'method'
1841a61aeb8SPaul Traina * in determining the value of 'minor'. At this point, all
1851a61aeb8SPaul Traina * methods defined in this implementation (see "Authorization
1861a61aeb8SPaul Traina * authentication methods" in taclib.h) are minor version 0
1871a61aeb8SPaul Traina * Not all types, however, indicate minor version 0.
1881a61aeb8SPaul Traina */
1891a61aeb8SPaul Traina case TAC_AUTHEN_METH_NOT_SET:
1901a61aeb8SPaul Traina case TAC_AUTHEN_METH_NONE:
1911a61aeb8SPaul Traina case TAC_AUTHEN_METH_KRB5:
1921a61aeb8SPaul Traina case TAC_AUTHEN_METH_LINE:
1931a61aeb8SPaul Traina case TAC_AUTHEN_METH_ENABLE:
1941a61aeb8SPaul Traina case TAC_AUTHEN_METH_LOCAL:
1951a61aeb8SPaul Traina case TAC_AUTHEN_METH_TACACSPLUS:
1961a61aeb8SPaul Traina case TAC_AUTHEN_METH_RCMD:
1971a61aeb8SPaul Traina switch (type) {
1981a61aeb8SPaul Traina case TAC_AUTHEN_TYPE_PAP:
1991a61aeb8SPaul Traina case TAC_AUTHEN_TYPE_CHAP:
2001a61aeb8SPaul Traina case TAC_AUTHEN_TYPE_MSCHAP:
2011a61aeb8SPaul Traina case TAC_AUTHEN_TYPE_ARAP:
2021a61aeb8SPaul Traina minor = 1;
2031a61aeb8SPaul Traina break;
2041a61aeb8SPaul Traina
2051a61aeb8SPaul Traina default:
2061a61aeb8SPaul Traina minor = 0;
2071a61aeb8SPaul Traina break;
2081a61aeb8SPaul Traina }
2091a61aeb8SPaul Traina break;
2101a61aeb8SPaul Traina default:
2111a61aeb8SPaul Traina minor = 0;
2121a61aeb8SPaul Traina break;
2131a61aeb8SPaul Traina }
2141a61aeb8SPaul Traina break;
2151a61aeb8SPaul Traina
216db3a20a5SShteryana Shopova case TAC_ACCT:
217db3a20a5SShteryana Shopova
2181a61aeb8SPaul Traina default:
2191a61aeb8SPaul Traina minor = 0;
2201a61aeb8SPaul Traina break;
2211a61aeb8SPaul Traina }
2222c195535SJohn Polstra
2232c195535SJohn Polstra return TAC_VER_MAJOR << 4 | minor;
2242c195535SJohn Polstra }
2252c195535SJohn Polstra
2261a61aeb8SPaul Traina
2272c195535SJohn Polstra static void
close_connection(struct tac_handle * h)2282c195535SJohn Polstra close_connection(struct tac_handle *h)
2292c195535SJohn Polstra {
2302c195535SJohn Polstra if (h->fd != -1) {
2312c195535SJohn Polstra close(h->fd);
2322c195535SJohn Polstra h->fd = -1;
2332c195535SJohn Polstra }
2342c195535SJohn Polstra }
2352c195535SJohn Polstra
2362c195535SJohn Polstra static int
conn_server(struct tac_handle * h)2372c195535SJohn Polstra conn_server(struct tac_handle *h)
2382c195535SJohn Polstra {
239*21850106SDag-Erling Smørgrav struct tac_server *srvp = &h->servers[h->cur_server];
2402c195535SJohn Polstra int flags;
2412c195535SJohn Polstra
2422c195535SJohn Polstra if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
2432c195535SJohn Polstra generr(h, "Cannot create socket: %s", strerror(errno));
2442c195535SJohn Polstra return -1;
2452c195535SJohn Polstra }
2462c195535SJohn Polstra if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
2472c195535SJohn Polstra fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
2482c195535SJohn Polstra generr(h, "Cannot set non-blocking mode on socket: %s",
2492c195535SJohn Polstra strerror(errno));
2502c195535SJohn Polstra close(h->fd);
2512c195535SJohn Polstra h->fd = -1;
2522c195535SJohn Polstra return -1;
2532c195535SJohn Polstra }
2542c195535SJohn Polstra if (connect(h->fd, (struct sockaddr *)&srvp->addr,
2552c195535SJohn Polstra sizeof srvp->addr) == 0)
2562c195535SJohn Polstra return 0;
2572c195535SJohn Polstra
2582c195535SJohn Polstra if (errno == EINPROGRESS) {
2592c195535SJohn Polstra fd_set wfds;
2602c195535SJohn Polstra struct timeval tv;
2612c195535SJohn Polstra int nfds;
2622c195535SJohn Polstra struct sockaddr peer;
26378e3eed0SStefan Farfeleder socklen_t errlen, peerlen;
2642c195535SJohn Polstra int err;
2652c195535SJohn Polstra
2662c195535SJohn Polstra /* Wait for the connection to complete. */
2672c195535SJohn Polstra FD_ZERO(&wfds);
2682c195535SJohn Polstra FD_SET(h->fd, &wfds);
2692c195535SJohn Polstra tv.tv_sec = srvp->timeout;
2702c195535SJohn Polstra tv.tv_usec = 0;
2712c195535SJohn Polstra nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
2722c195535SJohn Polstra if (nfds == -1) {
2732c195535SJohn Polstra generr(h, "select: %s", strerror(errno));
2742c195535SJohn Polstra close(h->fd);
2752c195535SJohn Polstra h->fd = -1;
2762c195535SJohn Polstra return -1;
2772c195535SJohn Polstra }
2782c195535SJohn Polstra if (nfds == 0) {
2792c195535SJohn Polstra generr(h, "connect: timed out");
2802c195535SJohn Polstra close(h->fd);
2812c195535SJohn Polstra h->fd = -1;
2822c195535SJohn Polstra return -1;
2832c195535SJohn Polstra }
2842c195535SJohn Polstra
2852c195535SJohn Polstra /* See whether we are connected now. */
2862c195535SJohn Polstra peerlen = sizeof peer;
2872c195535SJohn Polstra if (getpeername(h->fd, &peer, &peerlen) == 0)
2882c195535SJohn Polstra return 0;
2892c195535SJohn Polstra
2902c195535SJohn Polstra if (errno != ENOTCONN) {
2912c195535SJohn Polstra generr(h, "getpeername: %s", strerror(errno));
2922c195535SJohn Polstra close(h->fd);
2932c195535SJohn Polstra h->fd = -1;
2942c195535SJohn Polstra return -1;
2952c195535SJohn Polstra }
2962c195535SJohn Polstra
2972c195535SJohn Polstra /* Find out why the connect failed. */
2982c195535SJohn Polstra errlen = sizeof err;
2992c195535SJohn Polstra getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
3002c195535SJohn Polstra errno = err;
3012c195535SJohn Polstra }
3022c195535SJohn Polstra generr(h, "connect: %s", strerror(errno));
3032c195535SJohn Polstra close(h->fd);
3042c195535SJohn Polstra h->fd = -1;
3052c195535SJohn Polstra return -1;
3062c195535SJohn Polstra }
3072c195535SJohn Polstra
3082c195535SJohn Polstra /*
3092c195535SJohn Polstra * Encrypt or decrypt a message. The operations are symmetrical.
3102c195535SJohn Polstra */
3112c195535SJohn Polstra static void
crypt_msg(struct tac_handle * h,struct tac_msg * msg)3122c195535SJohn Polstra crypt_msg(struct tac_handle *h, struct tac_msg *msg)
3132c195535SJohn Polstra {
3142c195535SJohn Polstra const char *secret;
3152c195535SJohn Polstra MD5_CTX base_ctx;
3162c195535SJohn Polstra MD5_CTX ctx;
3172c195535SJohn Polstra unsigned char md5[16];
3182c195535SJohn Polstra int chunk;
3192c195535SJohn Polstra int msg_len;
3202c195535SJohn Polstra
3212c195535SJohn Polstra secret = h->servers[h->cur_server].secret;
3222c195535SJohn Polstra if (secret[0] == '\0')
3232c195535SJohn Polstra msg->flags |= TAC_UNENCRYPTED;
3242c195535SJohn Polstra if (msg->flags & TAC_UNENCRYPTED)
3252c195535SJohn Polstra return;
3262c195535SJohn Polstra
3272c195535SJohn Polstra msg_len = ntohl(msg->length);
3282c195535SJohn Polstra
3292c195535SJohn Polstra MD5Init(&base_ctx);
3302c195535SJohn Polstra MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
3312c195535SJohn Polstra MD5Update(&base_ctx, secret, strlen(secret));
3322c195535SJohn Polstra MD5Update(&base_ctx, &msg->version, sizeof msg->version);
3332c195535SJohn Polstra MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
3342c195535SJohn Polstra
3352c195535SJohn Polstra ctx = base_ctx;
3362c195535SJohn Polstra for (chunk = 0; chunk < msg_len; chunk += sizeof md5) {
3372c195535SJohn Polstra int chunk_len;
3382c195535SJohn Polstra int i;
3392c195535SJohn Polstra
3402c195535SJohn Polstra MD5Final(md5, &ctx);
3412c195535SJohn Polstra
3422c195535SJohn Polstra if ((chunk_len = msg_len - chunk) > sizeof md5)
3432c195535SJohn Polstra chunk_len = sizeof md5;
3442c195535SJohn Polstra for (i = 0; i < chunk_len; i++)
3452c195535SJohn Polstra msg->u.body[chunk + i] ^= md5[i];
3462c195535SJohn Polstra
3472c195535SJohn Polstra ctx = base_ctx;
3482c195535SJohn Polstra MD5Update(&ctx, md5, sizeof md5);
3492c195535SJohn Polstra }
3502c195535SJohn Polstra }
3512c195535SJohn Polstra
3522c195535SJohn Polstra /*
3532c195535SJohn Polstra * Return a dynamically allocated copy of the given server string.
3542c195535SJohn Polstra * The copy is null-terminated. If "len" is non-NULL, the length of
3552c195535SJohn Polstra * the string (excluding the terminating null byte) is stored via it.
3562c195535SJohn Polstra * Returns NULL on failure. Empty strings are still allocated even
3572c195535SJohn Polstra * though they have no content.
3582c195535SJohn Polstra */
3592c195535SJohn Polstra static void *
dup_str(struct tac_handle * h,const struct tac_str * ss,size_t * len)360*21850106SDag-Erling Smørgrav dup_str(struct tac_handle *h, const struct tac_str *ss, size_t *len)
3612c195535SJohn Polstra {
3622c195535SJohn Polstra unsigned char *p;
3632c195535SJohn Polstra
3642c195535SJohn Polstra if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
3652c195535SJohn Polstra return NULL;
3662c195535SJohn Polstra if (ss->data != NULL && ss->len != 0)
3672c195535SJohn Polstra memcpy(p, ss->data, ss->len);
3682c195535SJohn Polstra p[ss->len] = '\0';
3692c195535SJohn Polstra if (len != NULL)
3702c195535SJohn Polstra *len = ss->len;
3712c195535SJohn Polstra return p;
3722c195535SJohn Polstra }
3732c195535SJohn Polstra
3742c195535SJohn Polstra static int
establish_connection(struct tac_handle * h)3752c195535SJohn Polstra establish_connection(struct tac_handle *h)
3762c195535SJohn Polstra {
3772c195535SJohn Polstra int i;
3782c195535SJohn Polstra
3792c195535SJohn Polstra if (h->fd >= 0) /* Already connected. */
3802c195535SJohn Polstra return 0;
3812c195535SJohn Polstra if (h->num_servers == 0) {
3822c195535SJohn Polstra generr(h, "No TACACS+ servers specified");
3832c195535SJohn Polstra return -1;
3842c195535SJohn Polstra }
3852c195535SJohn Polstra /*
3862c195535SJohn Polstra * Try the servers round-robin. We begin with the one that
3872c195535SJohn Polstra * worked for us the last time. That way, once we find a good
3882c195535SJohn Polstra * server, we won't waste any more time trying the bad ones.
3892c195535SJohn Polstra */
3902c195535SJohn Polstra for (i = 0; i < h->num_servers; i++) {
3912c195535SJohn Polstra if (conn_server(h) == 0) {
3922c195535SJohn Polstra h->single_connect = (h->servers[h->cur_server].flags &
3932c195535SJohn Polstra TAC_SRVR_SINGLE_CONNECT) != 0;
3942c195535SJohn Polstra return 0;
3952c195535SJohn Polstra }
3962c195535SJohn Polstra if (++h->cur_server >= h->num_servers) /* Wrap around */
3972c195535SJohn Polstra h->cur_server = 0;
3982c195535SJohn Polstra }
3992c195535SJohn Polstra /* Just return whatever error was last reported by conn_server(). */
4002c195535SJohn Polstra return -1;
4012c195535SJohn Polstra }
4022c195535SJohn Polstra
4032c195535SJohn Polstra /*
4042c195535SJohn Polstra * Free a client string, obliterating its contents first for security.
4052c195535SJohn Polstra */
4062c195535SJohn Polstra static void
free_str(struct tac_str * cs)407*21850106SDag-Erling Smørgrav free_str(struct tac_str *cs)
4082c195535SJohn Polstra {
4092c195535SJohn Polstra if (cs->data != NULL) {
410*21850106SDag-Erling Smørgrav memset_s(cs->data, cs->len, 0, cs->len);
4112c195535SJohn Polstra free(cs->data);
4122c195535SJohn Polstra cs->data = NULL;
4132c195535SJohn Polstra cs->len = 0;
4142c195535SJohn Polstra }
4152c195535SJohn Polstra }
4162c195535SJohn Polstra
4172c195535SJohn Polstra static void
generr(struct tac_handle * h,const char * format,...)4182c195535SJohn Polstra generr(struct tac_handle *h, const char *format, ...)
4192c195535SJohn Polstra {
4202c195535SJohn Polstra va_list ap;
4212c195535SJohn Polstra
4222c195535SJohn Polstra va_start(ap, format);
4232c195535SJohn Polstra vsnprintf(h->errmsg, ERRSIZE, format, ap);
4242c195535SJohn Polstra va_end(ap);
4252c195535SJohn Polstra }
4262c195535SJohn Polstra
4272c195535SJohn Polstra static void
gen_session_id(struct tac_msg * msg)4282c195535SJohn Polstra gen_session_id(struct tac_msg *msg)
4292c195535SJohn Polstra {
4302c195535SJohn Polstra int r;
4312c195535SJohn Polstra
432d05c99c5SConrad Meyer r = arc4random();
4332c195535SJohn Polstra msg->session_id[0] = r >> 8;
4342c195535SJohn Polstra msg->session_id[1] = r;
435d05c99c5SConrad Meyer r = arc4random();
4362c195535SJohn Polstra msg->session_id[2] = r >> 8;
4372c195535SJohn Polstra msg->session_id[3] = r;
4382c195535SJohn Polstra }
4392c195535SJohn Polstra
4402c195535SJohn Polstra /*
4412c195535SJohn Polstra * Verify that we are exactly at the end of the response message.
4422c195535SJohn Polstra * Returns 0 on success, -1 on failure.
4432c195535SJohn Polstra */
4442c195535SJohn Polstra static int
get_srvr_end(struct tac_handle * h)4452c195535SJohn Polstra get_srvr_end(struct tac_handle *h)
4462c195535SJohn Polstra {
4471a61aeb8SPaul Traina int len;
4481a61aeb8SPaul Traina
4491a61aeb8SPaul Traina len = ntohl(h->response.length);
4501a61aeb8SPaul Traina
4511a61aeb8SPaul Traina if (h->srvr_pos != len) {
4521a61aeb8SPaul Traina generr(h, "Invalid length field in response "
4531a61aeb8SPaul Traina "from server: end expected at %u, response length %u",
4541a61aeb8SPaul Traina h->srvr_pos, len);
4552c195535SJohn Polstra return -1;
4562c195535SJohn Polstra }
4572c195535SJohn Polstra return 0;
4582c195535SJohn Polstra }
4592c195535SJohn Polstra
4602c195535SJohn Polstra static int
get_str(struct tac_handle * h,const char * field,struct tac_str * ss,size_t len)461*21850106SDag-Erling Smørgrav get_str(struct tac_handle *h, const char *field,
462*21850106SDag-Erling Smørgrav struct tac_str *ss, size_t len)
4632c195535SJohn Polstra {
4642c195535SJohn Polstra if (h->srvr_pos + len > ntohl(h->response.length)) {
4651a61aeb8SPaul Traina generr(h, "Invalid length field in %s response from server "
4661a61aeb8SPaul Traina "(%lu > %lu)", field, (u_long)(h->srvr_pos + len),
4671a61aeb8SPaul Traina (u_long)ntohl(h->response.length));
4682c195535SJohn Polstra return -1;
4692c195535SJohn Polstra }
4702c195535SJohn Polstra ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
4712c195535SJohn Polstra ss->len = len;
4722c195535SJohn Polstra h->srvr_pos += len;
4732c195535SJohn Polstra return 0;
4742c195535SJohn Polstra }
4752c195535SJohn Polstra
4762c195535SJohn Polstra static void
init_str(struct tac_str * cs)477*21850106SDag-Erling Smørgrav init_str(struct tac_str *cs)
4782c195535SJohn Polstra {
4792c195535SJohn Polstra cs->data = NULL;
4802c195535SJohn Polstra cs->len = 0;
4812c195535SJohn Polstra }
4822c195535SJohn Polstra
4832c195535SJohn Polstra static int
read_timed(struct tac_handle * h,void * buf,size_t len,const struct timeval * deadline)4842c195535SJohn Polstra read_timed(struct tac_handle *h, void *buf, size_t len,
4852c195535SJohn Polstra const struct timeval *deadline)
4862c195535SJohn Polstra {
4872c195535SJohn Polstra char *ptr;
4882c195535SJohn Polstra
4892c195535SJohn Polstra ptr = (char *)buf;
4902c195535SJohn Polstra while (len > 0) {
4912c195535SJohn Polstra int n;
4922c195535SJohn Polstra
4932c195535SJohn Polstra n = read(h->fd, ptr, len);
4942c195535SJohn Polstra if (n == -1) {
4952c195535SJohn Polstra struct timeval tv;
4962c195535SJohn Polstra int nfds;
4972c195535SJohn Polstra
4982c195535SJohn Polstra if (errno != EAGAIN) {
4992c195535SJohn Polstra generr(h, "Network read error: %s",
5002c195535SJohn Polstra strerror(errno));
5012c195535SJohn Polstra return -1;
5022c195535SJohn Polstra }
5032c195535SJohn Polstra
5042c195535SJohn Polstra /* Wait until we can read more data. */
5052c195535SJohn Polstra gettimeofday(&tv, NULL);
5062c195535SJohn Polstra timersub(deadline, &tv, &tv);
5072c195535SJohn Polstra if (tv.tv_sec >= 0) {
5082c195535SJohn Polstra fd_set rfds;
5092c195535SJohn Polstra
5102c195535SJohn Polstra FD_ZERO(&rfds);
5112c195535SJohn Polstra FD_SET(h->fd, &rfds);
5122c195535SJohn Polstra nfds =
5132c195535SJohn Polstra select(h->fd + 1, &rfds, NULL, NULL, &tv);
5142c195535SJohn Polstra if (nfds == -1) {
5152c195535SJohn Polstra generr(h, "select: %s",
5162c195535SJohn Polstra strerror(errno));
5172c195535SJohn Polstra return -1;
5182c195535SJohn Polstra }
5192c195535SJohn Polstra } else
5202c195535SJohn Polstra nfds = 0;
5212c195535SJohn Polstra if (nfds == 0) {
5222c195535SJohn Polstra generr(h, "Network read timed out");
5232c195535SJohn Polstra return -1;
5242c195535SJohn Polstra }
5252c195535SJohn Polstra } else if (n == 0) {
5262c195535SJohn Polstra generr(h, "unexpected EOF from server");
5272c195535SJohn Polstra return -1;
5282c195535SJohn Polstra } else {
5292c195535SJohn Polstra ptr += n;
5302c195535SJohn Polstra len -= n;
5312c195535SJohn Polstra }
5322c195535SJohn Polstra }
5332c195535SJohn Polstra return 0;
5342c195535SJohn Polstra }
5352c195535SJohn Polstra
5362c195535SJohn Polstra /*
5372c195535SJohn Polstra * Receive a response from the server and decrypt it. Returns 0 on
5382c195535SJohn Polstra * success, or -1 on failure.
5392c195535SJohn Polstra */
5402c195535SJohn Polstra static int
recv_msg(struct tac_handle * h)5412c195535SJohn Polstra recv_msg(struct tac_handle *h)
5422c195535SJohn Polstra {
5432c195535SJohn Polstra struct timeval deadline;
5442c195535SJohn Polstra struct tac_msg *msg;
5451a61aeb8SPaul Traina u_int32_t len;
5462c195535SJohn Polstra
5472c195535SJohn Polstra msg = &h->response;
5482c195535SJohn Polstra gettimeofday(&deadline, NULL);
5492c195535SJohn Polstra deadline.tv_sec += h->servers[h->cur_server].timeout;
5502c195535SJohn Polstra
5512c195535SJohn Polstra /* Read the message header and make sure it is reasonable. */
5522c195535SJohn Polstra if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
5532c195535SJohn Polstra return -1;
5542c195535SJohn Polstra if (memcmp(msg->session_id, h->request.session_id,
5552c195535SJohn Polstra sizeof msg->session_id) != 0) {
5562c195535SJohn Polstra generr(h, "Invalid session ID in received message");
5572c195535SJohn Polstra return -1;
5582c195535SJohn Polstra }
5592c195535SJohn Polstra if (msg->type != h->request.type) {
5601a61aeb8SPaul Traina generr(h, "Invalid type in received message"
5611a61aeb8SPaul Traina " (got %u, expected %u)",
5621a61aeb8SPaul Traina msg->type, h->request.type);
5632c195535SJohn Polstra return -1;
5642c195535SJohn Polstra }
5652c195535SJohn Polstra len = ntohl(msg->length);
5662c195535SJohn Polstra if (len > BODYSIZE) {
5671a61aeb8SPaul Traina generr(h, "Received message too large (%u > %u)",
5681a61aeb8SPaul Traina len, BODYSIZE);
5692c195535SJohn Polstra return -1;
5702c195535SJohn Polstra }
5712c195535SJohn Polstra if (msg->seq_no != ++h->last_seq_no) {
5721a61aeb8SPaul Traina generr(h, "Invalid sequence number in received message"
5731a61aeb8SPaul Traina " (got %u, expected %u)",
5741a61aeb8SPaul Traina msg->seq_no, h->last_seq_no);
5752c195535SJohn Polstra return -1;
5762c195535SJohn Polstra }
5772c195535SJohn Polstra
5782c195535SJohn Polstra /* Read the message body. */
5792c195535SJohn Polstra if (read_timed(h, msg->u.body, len, &deadline) == -1)
5802c195535SJohn Polstra return -1;
5812c195535SJohn Polstra
5822c195535SJohn Polstra /* Decrypt it. */
5832c195535SJohn Polstra crypt_msg(h, msg);
5842c195535SJohn Polstra
5852c195535SJohn Polstra /*
5862c195535SJohn Polstra * Turn off single-connection mode if the server isn't amenable
5872c195535SJohn Polstra * to it.
5882c195535SJohn Polstra */
5892c195535SJohn Polstra if (!(msg->flags & TAC_SINGLE_CONNECT))
5902c195535SJohn Polstra h->single_connect = 0;
5912c195535SJohn Polstra return 0;
5922c195535SJohn Polstra }
5932c195535SJohn Polstra
5942c195535SJohn Polstra static int
save_str(struct tac_handle * h,struct tac_str * cs,const void * data,size_t len)595*21850106SDag-Erling Smørgrav save_str(struct tac_handle *h, struct tac_str *cs, const void *data,
5962c195535SJohn Polstra size_t len)
5972c195535SJohn Polstra {
5982c195535SJohn Polstra free_str(cs);
5992c195535SJohn Polstra if (data != NULL && len != 0) {
6002c195535SJohn Polstra if ((cs->data = xmalloc(h, len)) == NULL)
6012c195535SJohn Polstra return -1;
6022c195535SJohn Polstra cs->len = len;
6032c195535SJohn Polstra memcpy(cs->data, data, len);
6042c195535SJohn Polstra }
6052c195535SJohn Polstra return 0;
6062c195535SJohn Polstra }
6072c195535SJohn Polstra
6082c195535SJohn Polstra /*
6092c195535SJohn Polstra * Send the current request, after encrypting it. Returns 0 on success,
6102c195535SJohn Polstra * or -1 on failure.
6112c195535SJohn Polstra */
6122c195535SJohn Polstra static int
send_msg(struct tac_handle * h)6132c195535SJohn Polstra send_msg(struct tac_handle *h)
6142c195535SJohn Polstra {
6152c195535SJohn Polstra struct timeval deadline;
6162c195535SJohn Polstra struct tac_msg *msg;
6172c195535SJohn Polstra char *ptr;
6182c195535SJohn Polstra int len;
6192c195535SJohn Polstra
6202c195535SJohn Polstra if (h->last_seq_no & 1) {
6212c195535SJohn Polstra generr(h, "Attempt to send message out of sequence");
6222c195535SJohn Polstra return -1;
6232c195535SJohn Polstra }
6242c195535SJohn Polstra
6251a61aeb8SPaul Traina if (establish_connection(h) == -1)
6261a61aeb8SPaul Traina return -1;
6271a61aeb8SPaul Traina
6282c195535SJohn Polstra msg = &h->request;
6292c195535SJohn Polstra msg->seq_no = ++h->last_seq_no;
6302c195535SJohn Polstra if (msg->seq_no == 1)
6312c195535SJohn Polstra gen_session_id(msg);
6322c195535SJohn Polstra crypt_msg(h, msg);
6332c195535SJohn Polstra
6342c195535SJohn Polstra if (h->single_connect)
6352c195535SJohn Polstra msg->flags |= TAC_SINGLE_CONNECT;
6362c195535SJohn Polstra else
6372c195535SJohn Polstra msg->flags &= ~TAC_SINGLE_CONNECT;
6382c195535SJohn Polstra gettimeofday(&deadline, NULL);
6392c195535SJohn Polstra deadline.tv_sec += h->servers[h->cur_server].timeout;
6402c195535SJohn Polstra len = HDRSIZE + ntohl(msg->length);
6412c195535SJohn Polstra ptr = (char *)msg;
6422c195535SJohn Polstra while (len > 0) {
6432c195535SJohn Polstra int n;
6442c195535SJohn Polstra
6452c195535SJohn Polstra n = write(h->fd, ptr, len);
6462c195535SJohn Polstra if (n == -1) {
6472c195535SJohn Polstra struct timeval tv;
6482c195535SJohn Polstra int nfds;
6492c195535SJohn Polstra
6502c195535SJohn Polstra if (errno != EAGAIN) {
6512c195535SJohn Polstra generr(h, "Network write error: %s",
6522c195535SJohn Polstra strerror(errno));
6532c195535SJohn Polstra return -1;
6542c195535SJohn Polstra }
6552c195535SJohn Polstra
6562c195535SJohn Polstra /* Wait until we can write more data. */
6572c195535SJohn Polstra gettimeofday(&tv, NULL);
6582c195535SJohn Polstra timersub(&deadline, &tv, &tv);
6592c195535SJohn Polstra if (tv.tv_sec >= 0) {
6602c195535SJohn Polstra fd_set wfds;
6612c195535SJohn Polstra
6622c195535SJohn Polstra FD_ZERO(&wfds);
6632c195535SJohn Polstra FD_SET(h->fd, &wfds);
6642c195535SJohn Polstra nfds =
6652c195535SJohn Polstra select(h->fd + 1, NULL, &wfds, NULL, &tv);
6662c195535SJohn Polstra if (nfds == -1) {
6672c195535SJohn Polstra generr(h, "select: %s",
6682c195535SJohn Polstra strerror(errno));
6692c195535SJohn Polstra return -1;
6702c195535SJohn Polstra }
6712c195535SJohn Polstra } else
6722c195535SJohn Polstra nfds = 0;
6732c195535SJohn Polstra if (nfds == 0) {
6742c195535SJohn Polstra generr(h, "Network write timed out");
6752c195535SJohn Polstra return -1;
6762c195535SJohn Polstra }
6772c195535SJohn Polstra } else {
6782c195535SJohn Polstra ptr += n;
6792c195535SJohn Polstra len -= n;
6802c195535SJohn Polstra }
6812c195535SJohn Polstra }
6822c195535SJohn Polstra return 0;
6832c195535SJohn Polstra }
6842c195535SJohn Polstra
6852c195535SJohn Polstra static int
tac_add_server_av(struct tac_handle * h,const char * host,int port,const char * secret,int timeout,int flags,const char * const * avs)686*21850106SDag-Erling Smørgrav tac_add_server_av(struct tac_handle *h, const char *host, int port,
687*21850106SDag-Erling Smørgrav const char *secret, int timeout, int flags, const char *const *avs)
6882c195535SJohn Polstra {
6892c195535SJohn Polstra struct tac_server *srvp;
690*21850106SDag-Erling Smørgrav const char *p;
691*21850106SDag-Erling Smørgrav size_t len;
692*21850106SDag-Erling Smørgrav int i;
6932c195535SJohn Polstra
6942c195535SJohn Polstra if (h->num_servers >= MAXSERVERS) {
695343ce585SJohn Polstra generr(h, "Too many TACACS+ servers specified");
6962c195535SJohn Polstra return -1;
6972c195535SJohn Polstra }
6982c195535SJohn Polstra srvp = &h->servers[h->num_servers];
6992c195535SJohn Polstra
7002c195535SJohn Polstra memset(&srvp->addr, 0, sizeof srvp->addr);
7012c195535SJohn Polstra srvp->addr.sin_len = sizeof srvp->addr;
7022c195535SJohn Polstra srvp->addr.sin_family = AF_INET;
7032c195535SJohn Polstra if (!inet_aton(host, &srvp->addr.sin_addr)) {
7042c195535SJohn Polstra struct hostent *hent;
7052c195535SJohn Polstra
7062c195535SJohn Polstra if ((hent = gethostbyname(host)) == NULL) {
7072c195535SJohn Polstra generr(h, "%s: host not found", host);
7082c195535SJohn Polstra return -1;
7092c195535SJohn Polstra }
7102c195535SJohn Polstra memcpy(&srvp->addr.sin_addr, hent->h_addr,
7112c195535SJohn Polstra sizeof srvp->addr.sin_addr);
7122c195535SJohn Polstra }
7132c195535SJohn Polstra srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
7142c195535SJohn Polstra if ((srvp->secret = xstrdup(h, secret)) == NULL)
7152c195535SJohn Polstra return -1;
7162c195535SJohn Polstra srvp->timeout = timeout;
7172c195535SJohn Polstra srvp->flags = flags;
718*21850106SDag-Erling Smørgrav srvp->navs = 0;
719*21850106SDag-Erling Smørgrav for (i = 0; avs[i] != NULL; i++) {
720*21850106SDag-Erling Smørgrav if (i >= MAXAVPAIRS) {
721*21850106SDag-Erling Smørgrav generr(h, "too many AV pairs");
722*21850106SDag-Erling Smørgrav goto fail;
723*21850106SDag-Erling Smørgrav }
724*21850106SDag-Erling Smørgrav for (p = avs[i], len = 0; is_arg(*p); p++)
725*21850106SDag-Erling Smørgrav len++;
726*21850106SDag-Erling Smørgrav if (p == avs[i] || *p != '=') {
727*21850106SDag-Erling Smørgrav generr(h, "invalid AV pair %d", i);
728*21850106SDag-Erling Smørgrav goto fail;
729*21850106SDag-Erling Smørgrav }
730*21850106SDag-Erling Smørgrav while (*p++ != '\0')
731*21850106SDag-Erling Smørgrav len++;
732*21850106SDag-Erling Smørgrav if ((srvp->avs[i].data = xstrdup(h, avs[i])) == NULL)
733*21850106SDag-Erling Smørgrav goto fail;
734*21850106SDag-Erling Smørgrav srvp->avs[i].len = len;
735*21850106SDag-Erling Smørgrav srvp->navs++;
736*21850106SDag-Erling Smørgrav }
7372c195535SJohn Polstra h->num_servers++;
7382c195535SJohn Polstra return 0;
739*21850106SDag-Erling Smørgrav fail:
740*21850106SDag-Erling Smørgrav memset_s(srvp->secret, strlen(srvp->secret), 0, strlen(srvp->secret));
741*21850106SDag-Erling Smørgrav free(srvp->secret);
742*21850106SDag-Erling Smørgrav srvp->secret = NULL;
743*21850106SDag-Erling Smørgrav for (i = 0; i < srvp->navs; i++) {
744*21850106SDag-Erling Smørgrav free(srvp->avs[i].data);
745*21850106SDag-Erling Smørgrav srvp->avs[i].data = NULL;
746*21850106SDag-Erling Smørgrav srvp->avs[i].len = 0;
747*21850106SDag-Erling Smørgrav }
748*21850106SDag-Erling Smørgrav return -1;
749*21850106SDag-Erling Smørgrav }
750*21850106SDag-Erling Smørgrav
751*21850106SDag-Erling Smørgrav int
tac_add_server(struct tac_handle * h,const char * host,int port,const char * secret,int timeout,int flags)752*21850106SDag-Erling Smørgrav tac_add_server(struct tac_handle *h, const char *host, int port,
753*21850106SDag-Erling Smørgrav const char *secret, int timeout, int flags)
754*21850106SDag-Erling Smørgrav {
755*21850106SDag-Erling Smørgrav const char *const *avs = { NULL };
756*21850106SDag-Erling Smørgrav
757*21850106SDag-Erling Smørgrav return tac_add_server_av(h, host, port, secret, timeout, flags, avs);
7582c195535SJohn Polstra }
7592c195535SJohn Polstra
7602c195535SJohn Polstra void
tac_close(struct tac_handle * h)7612c195535SJohn Polstra tac_close(struct tac_handle *h)
7622c195535SJohn Polstra {
7631a61aeb8SPaul Traina int i, srv;
7642c195535SJohn Polstra
7652c195535SJohn Polstra if (h->fd != -1)
7662c195535SJohn Polstra close(h->fd);
7672c195535SJohn Polstra for (srv = 0; srv < h->num_servers; srv++) {
7682c195535SJohn Polstra memset(h->servers[srv].secret, 0,
7692c195535SJohn Polstra strlen(h->servers[srv].secret));
7702c195535SJohn Polstra free(h->servers[srv].secret);
7712c195535SJohn Polstra }
7722c195535SJohn Polstra free_str(&h->user);
7732c195535SJohn Polstra free_str(&h->port);
7742c195535SJohn Polstra free_str(&h->rem_addr);
7752c195535SJohn Polstra free_str(&h->data);
7762c195535SJohn Polstra free_str(&h->user_msg);
7771a61aeb8SPaul Traina for (i=0; i<MAXAVPAIRS; i++)
7781a61aeb8SPaul Traina free_str(&(h->avs[i]));
7791a61aeb8SPaul Traina
7801a61aeb8SPaul Traina /* Clear everything else before freeing memory */
7811a61aeb8SPaul Traina memset(h, 0, sizeof(struct tac_handle));
7822c195535SJohn Polstra free(h);
7832c195535SJohn Polstra }
7842c195535SJohn Polstra
785*21850106SDag-Erling Smørgrav static void
freev(char ** fields,int nfields)786*21850106SDag-Erling Smørgrav freev(char **fields, int nfields)
787*21850106SDag-Erling Smørgrav {
788*21850106SDag-Erling Smørgrav if (fields != NULL) {
789*21850106SDag-Erling Smørgrav while (nfields-- > 0)
790*21850106SDag-Erling Smørgrav free(fields[nfields]);
791*21850106SDag-Erling Smørgrav free(fields);
792*21850106SDag-Erling Smørgrav }
793*21850106SDag-Erling Smørgrav }
794*21850106SDag-Erling Smørgrav
7952c195535SJohn Polstra int
tac_config(struct tac_handle * h,const char * path)7962c195535SJohn Polstra tac_config(struct tac_handle *h, const char *path)
7972c195535SJohn Polstra {
7982c195535SJohn Polstra FILE *fp;
799*21850106SDag-Erling Smørgrav char **fields;
800*21850106SDag-Erling Smørgrav int linenum, nfields;
8012c195535SJohn Polstra int retval;
8022c195535SJohn Polstra
8032c195535SJohn Polstra if (path == NULL)
8042c195535SJohn Polstra path = PATH_TACPLUS_CONF;
8052c195535SJohn Polstra if ((fp = fopen(path, "r")) == NULL) {
8062c195535SJohn Polstra generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
8072c195535SJohn Polstra return -1;
8082c195535SJohn Polstra }
8092c195535SJohn Polstra retval = 0;
810*21850106SDag-Erling Smørgrav linenum = nfields = 0;
811*21850106SDag-Erling Smørgrav fields = NULL;
812*21850106SDag-Erling Smørgrav while ((fields = openpam_readlinev(fp, &linenum, &nfields)) != NULL) {
813c5159910SAndrey A. Chernov char *host, *res;
8142c195535SJohn Polstra char *port_str;
8152c195535SJohn Polstra char *secret;
8162c195535SJohn Polstra char *timeout_str;
8172c195535SJohn Polstra char *end;
8182c195535SJohn Polstra unsigned long timeout;
8192c195535SJohn Polstra int port;
8202c195535SJohn Polstra int options;
821*21850106SDag-Erling Smørgrav int i;
8222c195535SJohn Polstra
823*21850106SDag-Erling Smørgrav if (nfields == 0) {
824*21850106SDag-Erling Smørgrav freev(fields, nfields);
8252c195535SJohn Polstra continue;
826*21850106SDag-Erling Smørgrav }
8272c195535SJohn Polstra if (nfields < 2) {
8282c195535SJohn Polstra generr(h, "%s:%d: missing shared secret", path,
8292c195535SJohn Polstra linenum);
8302c195535SJohn Polstra retval = -1;
8312c195535SJohn Polstra break;
8322c195535SJohn Polstra }
8332c195535SJohn Polstra host = fields[0];
8342c195535SJohn Polstra secret = fields[1];
8352c195535SJohn Polstra
8362c195535SJohn Polstra /* Parse and validate the fields. */
837c5159910SAndrey A. Chernov res = host;
838c5159910SAndrey A. Chernov host = strsep(&res, ":");
839c5159910SAndrey A. Chernov port_str = strsep(&res, ":");
8402c195535SJohn Polstra if (port_str != NULL) {
8412c195535SJohn Polstra port = strtoul(port_str, &end, 10);
8422c195535SJohn Polstra if (port_str[0] == '\0' || *end != '\0') {
8432c195535SJohn Polstra generr(h, "%s:%d: invalid port", path,
8442c195535SJohn Polstra linenum);
8452c195535SJohn Polstra retval = -1;
8462c195535SJohn Polstra break;
8472c195535SJohn Polstra }
8482c195535SJohn Polstra } else
8492c195535SJohn Polstra port = 0;
850*21850106SDag-Erling Smørgrav i = 2;
851*21850106SDag-Erling Smørgrav if (nfields > i && strlen(fields[i]) > 0 &&
852*21850106SDag-Erling Smørgrav strspn(fields[i], "0123456789") == strlen(fields[i])) {
853*21850106SDag-Erling Smørgrav timeout_str = fields[i];
8542c195535SJohn Polstra timeout = strtoul(timeout_str, &end, 10);
8552c195535SJohn Polstra if (timeout_str[0] == '\0' || *end != '\0') {
8562c195535SJohn Polstra generr(h, "%s:%d: invalid timeout", path,
8572c195535SJohn Polstra linenum);
8582c195535SJohn Polstra retval = -1;
8592c195535SJohn Polstra break;
8602c195535SJohn Polstra }
861*21850106SDag-Erling Smørgrav i++;
8622c195535SJohn Polstra } else
8632c195535SJohn Polstra timeout = TIMEOUT;
8642c195535SJohn Polstra options = 0;
865*21850106SDag-Erling Smørgrav if (nfields > i &&
866*21850106SDag-Erling Smørgrav strcmp(fields[i], "single-connection") == 0) {
8672c195535SJohn Polstra options |= TAC_SRVR_SINGLE_CONNECT;
868*21850106SDag-Erling Smørgrav i++;
8692c195535SJohn Polstra }
870*21850106SDag-Erling Smørgrav if (tac_add_server_av(h, host, port, secret, timeout,
871*21850106SDag-Erling Smørgrav options, (const char *const *)(fields + i)) == -1) {
8722c195535SJohn Polstra char msg[ERRSIZE];
8732c195535SJohn Polstra
8742c195535SJohn Polstra strcpy(msg, h->errmsg);
8752c195535SJohn Polstra generr(h, "%s:%d: %s", path, linenum, msg);
8762c195535SJohn Polstra retval = -1;
8772c195535SJohn Polstra break;
8782c195535SJohn Polstra }
879*21850106SDag-Erling Smørgrav memset_s(secret, strlen(secret), 0, strlen(secret));
880*21850106SDag-Erling Smørgrav freev(fields, nfields);
8812c195535SJohn Polstra }
882*21850106SDag-Erling Smørgrav freev(fields, nfields);
8832c195535SJohn Polstra fclose(fp);
8842c195535SJohn Polstra return retval;
8852c195535SJohn Polstra }
8862c195535SJohn Polstra
8872c195535SJohn Polstra int
tac_create_authen(struct tac_handle * h,int action,int type,int service)8882c195535SJohn Polstra tac_create_authen(struct tac_handle *h, int action, int type, int service)
8892c195535SJohn Polstra {
8902c195535SJohn Polstra struct tac_authen_start *as;
8912c195535SJohn Polstra
8921a61aeb8SPaul Traina create_msg(h, TAC_AUTHEN, action, type);
8932c195535SJohn Polstra
8941a61aeb8SPaul Traina as = &h->request.u.authen_start;
8952c195535SJohn Polstra as->action = action;
8962c195535SJohn Polstra as->priv_lvl = TAC_PRIV_LVL_USER;
8972c195535SJohn Polstra as->authen_type = type;
8982c195535SJohn Polstra as->service = service;
8992c195535SJohn Polstra
9001a61aeb8SPaul Traina return 0;
9011a61aeb8SPaul Traina }
9021a61aeb8SPaul Traina
9031a61aeb8SPaul Traina int
tac_create_author(struct tac_handle * h,int method,int type,int service)9041a61aeb8SPaul Traina tac_create_author(struct tac_handle *h, int method, int type, int service)
9051a61aeb8SPaul Traina {
9061a61aeb8SPaul Traina struct tac_author_request *areq;
9071a61aeb8SPaul Traina
9081a61aeb8SPaul Traina create_msg(h, TAC_AUTHOR, method, type);
9091a61aeb8SPaul Traina
9101a61aeb8SPaul Traina areq = &h->request.u.author_request;
9111a61aeb8SPaul Traina areq->authen_meth = method;
9121a61aeb8SPaul Traina areq->priv_lvl = TAC_PRIV_LVL_USER;
9131a61aeb8SPaul Traina areq->authen_type = type;
9141a61aeb8SPaul Traina areq->service = service;
9151a61aeb8SPaul Traina
9161a61aeb8SPaul Traina return 0;
9171a61aeb8SPaul Traina }
9181a61aeb8SPaul Traina
919db3a20a5SShteryana Shopova int
tac_create_acct(struct tac_handle * h,int acct,int action,int type,int service)920db3a20a5SShteryana Shopova tac_create_acct(struct tac_handle *h, int acct, int action, int type, int service)
921db3a20a5SShteryana Shopova {
922db3a20a5SShteryana Shopova struct tac_acct_start *as;
923db3a20a5SShteryana Shopova
924db3a20a5SShteryana Shopova create_msg(h, TAC_ACCT, action, type);
925db3a20a5SShteryana Shopova
926db3a20a5SShteryana Shopova as = &h->request.u.acct_start;
927db3a20a5SShteryana Shopova as->action = acct;
928db3a20a5SShteryana Shopova as->authen_action = action;
929db3a20a5SShteryana Shopova as->priv_lvl = TAC_PRIV_LVL_USER;
930db3a20a5SShteryana Shopova as->authen_type = type;
931db3a20a5SShteryana Shopova as->authen_service = service;
932db3a20a5SShteryana Shopova
933db3a20a5SShteryana Shopova return 0;
934db3a20a5SShteryana Shopova }
935db3a20a5SShteryana Shopova
9361a61aeb8SPaul Traina static void
create_msg(struct tac_handle * h,int msg_type,int var,int type)9371a61aeb8SPaul Traina create_msg(struct tac_handle *h, int msg_type, int var, int type)
9381a61aeb8SPaul Traina {
9391a61aeb8SPaul Traina struct tac_msg *msg;
9401a61aeb8SPaul Traina int i;
9411a61aeb8SPaul Traina
9421a61aeb8SPaul Traina h->last_seq_no = 0;
9431a61aeb8SPaul Traina
9441a61aeb8SPaul Traina msg = &h->request;
9451a61aeb8SPaul Traina msg->type = msg_type;
9461a61aeb8SPaul Traina msg->version = protocol_version(msg_type, var, type);
9471a61aeb8SPaul Traina msg->flags = 0; /* encrypted packet body */
9481a61aeb8SPaul Traina
9492c195535SJohn Polstra free_str(&h->user);
9502c195535SJohn Polstra free_str(&h->port);
9512c195535SJohn Polstra free_str(&h->rem_addr);
9522c195535SJohn Polstra free_str(&h->data);
9532c195535SJohn Polstra free_str(&h->user_msg);
9542c195535SJohn Polstra
9551a61aeb8SPaul Traina for (i=0; i<MAXAVPAIRS; i++)
9561a61aeb8SPaul Traina free_str(&(h->avs[i]));
9572c195535SJohn Polstra }
9582c195535SJohn Polstra
9592c195535SJohn Polstra void *
tac_get_data(struct tac_handle * h,size_t * len)9602c195535SJohn Polstra tac_get_data(struct tac_handle *h, size_t *len)
9612c195535SJohn Polstra {
9622c195535SJohn Polstra return dup_str(h, &h->srvr_data, len);
9632c195535SJohn Polstra }
9642c195535SJohn Polstra
9652c195535SJohn Polstra char *
tac_get_msg(struct tac_handle * h)9662c195535SJohn Polstra tac_get_msg(struct tac_handle *h)
9672c195535SJohn Polstra {
9681a61aeb8SPaul Traina return dup_str(h, &h->srvr_msg, NULL);
9692c195535SJohn Polstra }
9702c195535SJohn Polstra
9712c195535SJohn Polstra /*
9722c195535SJohn Polstra * Create and initialize a tac_handle structure, and return it to the
9732c195535SJohn Polstra * caller. Can fail only if the necessary memory cannot be allocated.
9742c195535SJohn Polstra * In that case, it returns NULL.
9752c195535SJohn Polstra */
9762c195535SJohn Polstra struct tac_handle *
tac_open(void)9772c195535SJohn Polstra tac_open(void)
9782c195535SJohn Polstra {
9791a61aeb8SPaul Traina int i;
9802c195535SJohn Polstra struct tac_handle *h;
9812c195535SJohn Polstra
9822c195535SJohn Polstra h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
9832c195535SJohn Polstra if (h != NULL) {
9842c195535SJohn Polstra h->fd = -1;
9852c195535SJohn Polstra h->num_servers = 0;
9862c195535SJohn Polstra h->cur_server = 0;
9872c195535SJohn Polstra h->errmsg[0] = '\0';
988*21850106SDag-Erling Smørgrav init_str(&h->user);
989*21850106SDag-Erling Smørgrav init_str(&h->port);
990*21850106SDag-Erling Smørgrav init_str(&h->rem_addr);
991*21850106SDag-Erling Smørgrav init_str(&h->data);
992*21850106SDag-Erling Smørgrav init_str(&h->user_msg);
9931a61aeb8SPaul Traina for (i=0; i<MAXAVPAIRS; i++) {
994*21850106SDag-Erling Smørgrav init_str(&(h->avs[i]));
995*21850106SDag-Erling Smørgrav init_str(&(h->srvr_avs[i]));
9961a61aeb8SPaul Traina }
997*21850106SDag-Erling Smørgrav init_str(&h->srvr_msg);
998*21850106SDag-Erling Smørgrav init_str(&h->srvr_data);
9992c195535SJohn Polstra }
10002c195535SJohn Polstra return h;
10012c195535SJohn Polstra }
10022c195535SJohn Polstra
10032c195535SJohn Polstra int
tac_send_authen(struct tac_handle * h)10042c195535SJohn Polstra tac_send_authen(struct tac_handle *h)
10052c195535SJohn Polstra {
10062c195535SJohn Polstra struct tac_authen_reply *ar;
10072c195535SJohn Polstra
10081a61aeb8SPaul Traina if (h->num_servers == 0)
10091a61aeb8SPaul Traina return -1;
10101a61aeb8SPaul Traina
10112c195535SJohn Polstra if (h->last_seq_no == 0) { /* Authentication START packet */
10122c195535SJohn Polstra struct tac_authen_start *as;
10132c195535SJohn Polstra
10142c195535SJohn Polstra as = &h->request.u.authen_start;
10152c195535SJohn Polstra h->request.length =
10162c195535SJohn Polstra htonl(offsetof(struct tac_authen_start, rest[0]));
10172c195535SJohn Polstra if (add_str_8(h, &as->user_len, &h->user) == -1 ||
10182c195535SJohn Polstra add_str_8(h, &as->port_len, &h->port) == -1 ||
10192c195535SJohn Polstra add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
10202c195535SJohn Polstra add_str_8(h, &as->data_len, &h->data) == -1)
10212c195535SJohn Polstra return -1;
10222c195535SJohn Polstra } else { /* Authentication CONTINUE packet */
10232c195535SJohn Polstra struct tac_authen_cont *ac;
10242c195535SJohn Polstra
10252c195535SJohn Polstra ac = &h->request.u.authen_cont;
10262c195535SJohn Polstra ac->flags = 0;
10272c195535SJohn Polstra h->request.length =
10282c195535SJohn Polstra htonl(offsetof(struct tac_authen_cont, rest[0]));
10292c195535SJohn Polstra if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
10302c195535SJohn Polstra add_str_16(h, &ac->data_len, &h->data) == -1)
10312c195535SJohn Polstra return -1;
10322c195535SJohn Polstra }
10332c195535SJohn Polstra
10342c195535SJohn Polstra /* Send the message and retrieve the reply. */
10352c195535SJohn Polstra if (send_msg(h) == -1 || recv_msg(h) == -1)
10362c195535SJohn Polstra return -1;
10372c195535SJohn Polstra
10382c195535SJohn Polstra /* Scan the optional fields in the reply. */
10392c195535SJohn Polstra ar = &h->response.u.authen_reply;
10402c195535SJohn Polstra h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
1041*21850106SDag-Erling Smørgrav if (get_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1042*21850106SDag-Erling Smørgrav get_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
10432c195535SJohn Polstra get_srvr_end(h) == -1)
10442c195535SJohn Polstra return -1;
10452c195535SJohn Polstra
10462c195535SJohn Polstra if (!h->single_connect &&
10472c195535SJohn Polstra ar->status != TAC_AUTHEN_STATUS_GETDATA &&
10482c195535SJohn Polstra ar->status != TAC_AUTHEN_STATUS_GETUSER &&
10492c195535SJohn Polstra ar->status != TAC_AUTHEN_STATUS_GETPASS)
10502c195535SJohn Polstra close_connection(h);
10512c195535SJohn Polstra
10522c195535SJohn Polstra return ar->flags << 8 | ar->status;
10532c195535SJohn Polstra }
10542c195535SJohn Polstra
10552c195535SJohn Polstra int
tac_send_author(struct tac_handle * h)10561a61aeb8SPaul Traina tac_send_author(struct tac_handle *h)
10571a61aeb8SPaul Traina {
10581a61aeb8SPaul Traina int i, current;
10591a61aeb8SPaul Traina char dbgstr[64];
10601a61aeb8SPaul Traina struct tac_author_request *areq = &h->request.u.author_request;
10611a61aeb8SPaul Traina struct tac_author_response *ares = &h->response.u.author_response;
1062*21850106SDag-Erling Smørgrav struct tac_server *srvp;
10631a61aeb8SPaul Traina
10641a61aeb8SPaul Traina h->request.length =
10651a61aeb8SPaul Traina htonl(offsetof(struct tac_author_request, rest[0]));
10661a61aeb8SPaul Traina
10671a61aeb8SPaul Traina /* Count each specified AV pair */
10681a61aeb8SPaul Traina for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
10691a61aeb8SPaul Traina if (h->avs[i].len && h->avs[i].data)
10701a61aeb8SPaul Traina areq->av_cnt++;
10711a61aeb8SPaul Traina
10721a61aeb8SPaul Traina /*
10731a61aeb8SPaul Traina * Each AV size is a byte starting right after 'av_cnt'. Update the
10741a61aeb8SPaul Traina * offset to include these AV sizes.
10751a61aeb8SPaul Traina */
10761a61aeb8SPaul Traina h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
10771a61aeb8SPaul Traina
10781a61aeb8SPaul Traina /* Now add the string arguments from 'h' */
10791a61aeb8SPaul Traina if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
10801a61aeb8SPaul Traina add_str_8(h, &areq->port_len, &h->port) == -1 ||
10811a61aeb8SPaul Traina add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
10821a61aeb8SPaul Traina return -1;
10831a61aeb8SPaul Traina
10841a61aeb8SPaul Traina /* Add each AV pair, the size of each placed in areq->rest[current] */
10851a61aeb8SPaul Traina for (current=0, i=0; i<MAXAVPAIRS; i++) {
10861a61aeb8SPaul Traina if (h->avs[i].len && h->avs[i].data) {
10871a61aeb8SPaul Traina if (add_str_8(h, &areq->rest[current++],
10881a61aeb8SPaul Traina &(h->avs[i])) == -1)
10891a61aeb8SPaul Traina return -1;
10901a61aeb8SPaul Traina }
10911a61aeb8SPaul Traina }
10921a61aeb8SPaul Traina
10931a61aeb8SPaul Traina /* Send the message and retrieve the reply. */
10941a61aeb8SPaul Traina if (send_msg(h) == -1 || recv_msg(h) == -1)
10951a61aeb8SPaul Traina return -1;
1096*21850106SDag-Erling Smørgrav srvp = &h->servers[h->cur_server];
10971a61aeb8SPaul Traina
10981a61aeb8SPaul Traina /* Update the offset in the response packet based on av pairs count */
10991a61aeb8SPaul Traina h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
11001a61aeb8SPaul Traina ares->av_cnt;
11011a61aeb8SPaul Traina
11021a61aeb8SPaul Traina /* Scan the optional fields in the response. */
1103*21850106SDag-Erling Smørgrav if (get_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
1104*21850106SDag-Erling Smørgrav get_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
11051a61aeb8SPaul Traina return -1;
11061a61aeb8SPaul Traina
11071a61aeb8SPaul Traina /* Get each AV pair (just setting pointers, not malloc'ing) */
11081a61aeb8SPaul Traina clear_srvr_avs(h);
11091a61aeb8SPaul Traina for (i=0; i<ares->av_cnt; i++) {
11101a61aeb8SPaul Traina snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
1111*21850106SDag-Erling Smørgrav if (get_str(h, dbgstr, &(h->srvr_avs[i]),
11121a61aeb8SPaul Traina ares->rest[i]) == -1)
11131a61aeb8SPaul Traina return -1;
1114*21850106SDag-Erling Smørgrav h->srvr_navs++;
11151a61aeb8SPaul Traina }
11161a61aeb8SPaul Traina
11171a61aeb8SPaul Traina /* Should have ended up at the end */
11181a61aeb8SPaul Traina if (get_srvr_end(h) == -1)
11191a61aeb8SPaul Traina return -1;
11201a61aeb8SPaul Traina
11211a61aeb8SPaul Traina /* Sanity checks */
11221a61aeb8SPaul Traina if (!h->single_connect)
11231a61aeb8SPaul Traina close_connection(h);
11241a61aeb8SPaul Traina
1125*21850106SDag-Erling Smørgrav return (h->srvr_navs + srvp->navs) << 8 | ares->status;
11261a61aeb8SPaul Traina }
11271a61aeb8SPaul Traina
11281a61aeb8SPaul Traina int
tac_send_acct(struct tac_handle * h)1129db3a20a5SShteryana Shopova tac_send_acct(struct tac_handle *h)
1130db3a20a5SShteryana Shopova {
1131db3a20a5SShteryana Shopova register int i, current;
1132db3a20a5SShteryana Shopova struct tac_acct_start *as = &h->request.u.acct_start;
1133db3a20a5SShteryana Shopova struct tac_acct_reply *ar = &h->response.u.acct_reply;
1134db3a20a5SShteryana Shopova
1135db3a20a5SShteryana Shopova /* start */
1136db3a20a5SShteryana Shopova as = &h->request.u.acct_start;
1137db3a20a5SShteryana Shopova h->request.length = htonl(offsetof(struct tac_acct_start, rest[0]));
1138db3a20a5SShteryana Shopova for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++)
1139db3a20a5SShteryana Shopova if (h->avs[i].len && h->avs[i].data)
1140db3a20a5SShteryana Shopova as->av_cnt++;
1141db3a20a5SShteryana Shopova h->request.length = ntohl(htonl(h->request.length) + as->av_cnt);
1142db3a20a5SShteryana Shopova
1143db3a20a5SShteryana Shopova if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1144db3a20a5SShteryana Shopova add_str_8(h, &as->port_len, &h->port) == -1 ||
1145db3a20a5SShteryana Shopova add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1)
1146db3a20a5SShteryana Shopova return -1;
1147db3a20a5SShteryana Shopova
1148db3a20a5SShteryana Shopova for (i = current = 0; i < MAXAVPAIRS; i++)
1149db3a20a5SShteryana Shopova if (h->avs[i].len && h->avs[i].data)
1150db3a20a5SShteryana Shopova if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1)
1151db3a20a5SShteryana Shopova return -1;
1152db3a20a5SShteryana Shopova
1153db3a20a5SShteryana Shopova /* send */
1154db3a20a5SShteryana Shopova if (send_msg(h) == -1 || recv_msg(h) == -1)
1155db3a20a5SShteryana Shopova return -1;
1156db3a20a5SShteryana Shopova
1157db3a20a5SShteryana Shopova /* reply */
1158db3a20a5SShteryana Shopova h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]);
1159*21850106SDag-Erling Smørgrav if (get_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1160*21850106SDag-Erling Smørgrav get_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1161db3a20a5SShteryana Shopova get_srvr_end(h) == -1)
1162db3a20a5SShteryana Shopova return -1;
1163db3a20a5SShteryana Shopova
1164db3a20a5SShteryana Shopova /* Sanity checks */
1165db3a20a5SShteryana Shopova if (!h->single_connect)
1166db3a20a5SShteryana Shopova close_connection(h);
1167db3a20a5SShteryana Shopova
1168db3a20a5SShteryana Shopova return ar->status;
1169db3a20a5SShteryana Shopova }
1170db3a20a5SShteryana Shopova
1171db3a20a5SShteryana Shopova int
tac_set_rem_addr(struct tac_handle * h,const char * addr)11722c195535SJohn Polstra tac_set_rem_addr(struct tac_handle *h, const char *addr)
11732c195535SJohn Polstra {
11742c195535SJohn Polstra return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
11752c195535SJohn Polstra }
11762c195535SJohn Polstra
11772c195535SJohn Polstra int
tac_set_data(struct tac_handle * h,const void * data,size_t data_len)11782c195535SJohn Polstra tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
11792c195535SJohn Polstra {
11802c195535SJohn Polstra return save_str(h, &h->data, data, data_len);
11812c195535SJohn Polstra }
11822c195535SJohn Polstra
11832c195535SJohn Polstra int
tac_set_msg(struct tac_handle * h,const char * msg)11842c195535SJohn Polstra tac_set_msg(struct tac_handle *h, const char *msg)
11852c195535SJohn Polstra {
11862c195535SJohn Polstra return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
11872c195535SJohn Polstra }
11882c195535SJohn Polstra
11892c195535SJohn Polstra int
tac_set_port(struct tac_handle * h,const char * port)11902c195535SJohn Polstra tac_set_port(struct tac_handle *h, const char *port)
11912c195535SJohn Polstra {
11922c195535SJohn Polstra return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
11932c195535SJohn Polstra }
11942c195535SJohn Polstra
11952c195535SJohn Polstra int
tac_set_priv(struct tac_handle * h,int priv)11962c195535SJohn Polstra tac_set_priv(struct tac_handle *h, int priv)
11972c195535SJohn Polstra {
11982c195535SJohn Polstra if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
11992c195535SJohn Polstra generr(h, "Attempt to set invalid privilege level");
12002c195535SJohn Polstra return -1;
12012c195535SJohn Polstra }
12022c195535SJohn Polstra h->request.u.authen_start.priv_lvl = priv;
12032c195535SJohn Polstra return 0;
12042c195535SJohn Polstra }
12052c195535SJohn Polstra
12062c195535SJohn Polstra int
tac_set_user(struct tac_handle * h,const char * user)12072c195535SJohn Polstra tac_set_user(struct tac_handle *h, const char *user)
12082c195535SJohn Polstra {
12092c195535SJohn Polstra return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
12102c195535SJohn Polstra }
12112c195535SJohn Polstra
12121a61aeb8SPaul Traina int
tac_set_av(struct tac_handle * h,u_int index,const char * av)12131a61aeb8SPaul Traina tac_set_av(struct tac_handle *h, u_int index, const char *av)
12141a61aeb8SPaul Traina {
12151a61aeb8SPaul Traina if (index >= MAXAVPAIRS)
12161a61aeb8SPaul Traina return -1;
12171a61aeb8SPaul Traina return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
12181a61aeb8SPaul Traina }
12191a61aeb8SPaul Traina
12201a61aeb8SPaul Traina char *
tac_get_av(struct tac_handle * h,u_int index)12211a61aeb8SPaul Traina tac_get_av(struct tac_handle *h, u_int index)
12221a61aeb8SPaul Traina {
1223*21850106SDag-Erling Smørgrav struct tac_server *srvp;
1224*21850106SDag-Erling Smørgrav
1225*21850106SDag-Erling Smørgrav if (index < h->srvr_navs)
1226*21850106SDag-Erling Smørgrav return dup_str(h, &h->srvr_avs[index], NULL);
1227*21850106SDag-Erling Smørgrav index -= h->srvr_navs;
1228*21850106SDag-Erling Smørgrav srvp = &h->servers[h->cur_server];
1229*21850106SDag-Erling Smørgrav if (index < srvp->navs)
1230*21850106SDag-Erling Smørgrav return xstrdup(h, srvp->avs[index].data);
12311a61aeb8SPaul Traina return NULL;
12321a61aeb8SPaul Traina }
12331a61aeb8SPaul Traina
12341a61aeb8SPaul Traina char *
tac_get_av_value(struct tac_handle * h,const char * attribute)12351a61aeb8SPaul Traina tac_get_av_value(struct tac_handle *h, const char *attribute)
12361a61aeb8SPaul Traina {
1237*21850106SDag-Erling Smørgrav int i, attr_len;
12381a61aeb8SPaul Traina int found_seperator;
1239*21850106SDag-Erling Smørgrav char *ch, *end;
1240*21850106SDag-Erling Smørgrav struct tac_str *candidate;
1241*21850106SDag-Erling Smørgrav struct tac_str value;
1242*21850106SDag-Erling Smørgrav struct tac_server *srvp = &h->servers[h->cur_server];
12431a61aeb8SPaul Traina
1244*21850106SDag-Erling Smørgrav if (attribute == NULL || (attr_len = strlen(attribute)) == 0)
12451a61aeb8SPaul Traina return NULL;
12461a61aeb8SPaul Traina
1247*21850106SDag-Erling Smørgrav for (i = 0; i < h->srvr_navs + srvp->navs; i++) {
1248*21850106SDag-Erling Smørgrav if (i < h->srvr_navs)
1249*21850106SDag-Erling Smørgrav candidate = &h->srvr_avs[i];
1250*21850106SDag-Erling Smørgrav else
1251*21850106SDag-Erling Smørgrav candidate = &srvp->avs[i - h->srvr_navs];
12521a61aeb8SPaul Traina
1253*21850106SDag-Erling Smørgrav if (attr_len < candidate->len &&
1254*21850106SDag-Erling Smørgrav strncmp(candidate->data, attribute, attr_len) == 0) {
12551a61aeb8SPaul Traina
1256*21850106SDag-Erling Smørgrav ch = candidate->data + attr_len;
1257*21850106SDag-Erling Smørgrav end = candidate->data + candidate->len;
12581a61aeb8SPaul Traina
12591a61aeb8SPaul Traina /*
12601a61aeb8SPaul Traina * Sift out the white space between A and V (should not
12611a61aeb8SPaul Traina * be any, but don't trust implementation of server...)
12621a61aeb8SPaul Traina */
12631a61aeb8SPaul Traina found_seperator = 0;
12641a61aeb8SPaul Traina while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
12651a61aeb8SPaul Traina *ch == '\t') && ch != end) {
12661a61aeb8SPaul Traina if (*ch == '=' || *ch == '*')
12671a61aeb8SPaul Traina found_seperator++;
12681a61aeb8SPaul Traina ch++;
12691a61aeb8SPaul Traina }
12701a61aeb8SPaul Traina
12711a61aeb8SPaul Traina /*
12721a61aeb8SPaul Traina * Note:
12731a61aeb8SPaul Traina * The case of 'attribute' == "foo" and
12741a61aeb8SPaul Traina * h->srvr_avs[0] = "foobie=var1"
12751a61aeb8SPaul Traina * h->srvr_avs[1] = "foo=var2"
12761a61aeb8SPaul Traina * is handled.
12775e3d7b09SAttilio Rao *
12785e3d7b09SAttilio Rao * Note that for empty string attribute values a
12795e3d7b09SAttilio Rao * 0-length string is returned in order to distinguish
12805e3d7b09SAttilio Rao * against unset values.
1281bc7622b7SEd Maste * dup_str() will handle srvr.len == 0 correctly.
12821a61aeb8SPaul Traina */
12835e3d7b09SAttilio Rao if (found_seperator == 1) {
1284*21850106SDag-Erling Smørgrav value.len = end - ch;
1285*21850106SDag-Erling Smørgrav value.data = ch;
1286*21850106SDag-Erling Smørgrav return dup_str(h, &value, NULL);
12871a61aeb8SPaul Traina }
12881a61aeb8SPaul Traina }
12891a61aeb8SPaul Traina }
12901a61aeb8SPaul Traina return NULL;
12911a61aeb8SPaul Traina }
12921a61aeb8SPaul Traina
12931a61aeb8SPaul Traina void
tac_clear_avs(struct tac_handle * h)12941a61aeb8SPaul Traina tac_clear_avs(struct tac_handle *h)
12951a61aeb8SPaul Traina {
12961a61aeb8SPaul Traina int i;
12971a61aeb8SPaul Traina for (i=0; i<MAXAVPAIRS; i++)
12981a61aeb8SPaul Traina save_str(h, &(h->avs[i]), NULL, 0);
12991a61aeb8SPaul Traina }
13001a61aeb8SPaul Traina
13011a61aeb8SPaul Traina static void
clear_srvr_avs(struct tac_handle * h)13021a61aeb8SPaul Traina clear_srvr_avs(struct tac_handle *h)
13031a61aeb8SPaul Traina {
13041a61aeb8SPaul Traina int i;
1305*21850106SDag-Erling Smørgrav
1306*21850106SDag-Erling Smørgrav for (i = 0; i < h->srvr_navs; i++)
1307*21850106SDag-Erling Smørgrav init_str(&(h->srvr_avs[i]));
1308*21850106SDag-Erling Smørgrav h->srvr_navs = 0;
13091a61aeb8SPaul Traina }
13101a61aeb8SPaul Traina
13111a61aeb8SPaul Traina
13122c195535SJohn Polstra const char *
tac_strerror(struct tac_handle * h)13132c195535SJohn Polstra tac_strerror(struct tac_handle *h)
13142c195535SJohn Polstra {
13152c195535SJohn Polstra return h->errmsg;
13162c195535SJohn Polstra }
13172c195535SJohn Polstra
13182c195535SJohn Polstra static void *
xmalloc(struct tac_handle * h,size_t size)13192c195535SJohn Polstra xmalloc(struct tac_handle *h, size_t size)
13202c195535SJohn Polstra {
13212c195535SJohn Polstra void *r;
13222c195535SJohn Polstra
13232c195535SJohn Polstra if ((r = malloc(size)) == NULL)
13242c195535SJohn Polstra generr(h, "Out of memory");
13252c195535SJohn Polstra return r;
13262c195535SJohn Polstra }
13272c195535SJohn Polstra
13282c195535SJohn Polstra static char *
xstrdup(struct tac_handle * h,const char * s)13292c195535SJohn Polstra xstrdup(struct tac_handle *h, const char *s)
13302c195535SJohn Polstra {
13312c195535SJohn Polstra char *r;
13322c195535SJohn Polstra
13332c195535SJohn Polstra if ((r = strdup(s)) == NULL)
13342c195535SJohn Polstra generr(h, "Out of memory");
13352c195535SJohn Polstra return r;
13362c195535SJohn Polstra }
1337