summaryrefslogtreecommitdiffstats
path: root/lib/libtacplus
diff options
context:
space:
mode:
authorjdp <jdp@FreeBSD.org>1998-11-13 00:54:26 +0000
committerjdp <jdp@FreeBSD.org>1998-11-13 00:54:26 +0000
commit3119595d44dc868d838d64af9c12dc876c059ab9 (patch)
tree194f1da4d7dcfa2364700e7713e23f39337b035d /lib/libtacplus
parent4900bca2e8b7ca6e6c5c6dd333df4549e8a785d8 (diff)
downloadFreeBSD-src-3119595d44dc868d838d64af9c12dc876c059ab9.zip
FreeBSD-src-3119595d44dc868d838d64af9c12dc876c059ab9.tar.gz
Initial import of TACACS+ client library donated by Juniper Networks, Inc.
Diffstat (limited to 'lib/libtacplus')
-rw-r--r--lib/libtacplus/Makefile41
-rw-r--r--lib/libtacplus/libtacplus.3347
-rw-r--r--lib/libtacplus/taclib.c1053
-rw-r--r--lib/libtacplus/taclib.h105
-rw-r--r--lib/libtacplus/taclib_private.h152
-rw-r--r--lib/libtacplus/tacplus.conf.5114
6 files changed, 1812 insertions, 0 deletions
diff --git a/lib/libtacplus/Makefile b/lib/libtacplus/Makefile
new file mode 100644
index 0000000..798a8e0
--- /dev/null
+++ b/lib/libtacplus/Makefile
@@ -0,0 +1,41 @@
+# Copyright 1998 Juniper Networks, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $FreeBSD$
+
+LIB= tacplus
+SRCS= taclib.c
+CFLAGS+= -Wall
+DPADD+= ${LIBMD}
+LDADD+= -lmd
+SHLIB_MAJOR= 1
+SHLIB_MINOR= 0
+MAN3+= libtacplus.3
+MAN5+= tacplus.conf.5
+
+beforeinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${.CURDIR}/taclib.h ${DESTDIR}/usr/include
+
+.include <bsd.lib.mk>
diff --git a/lib/libtacplus/libtacplus.3 b/lib/libtacplus/libtacplus.3
new file mode 100644
index 0000000..bfbfa35
--- /dev/null
+++ b/lib/libtacplus/libtacplus.3
@@ -0,0 +1,347 @@
+.\" Copyright 1998 Juniper Networks, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 2, 1998
+.Dt LIBTACPLUS 3
+.Os FreeBSD
+.Sh NAME
+.Nm libtacplus
+.Nd TACACS+ client library
+.Sh SYNOPSIS
+.Fd #include <taclib.h>
+.Ft int
+.Fn tac_add_server "struct tac_handle *h" "const char *host" "int port" "const char *secret" "int timeout" "int flags"
+.Ft void
+.Fn tac_close "struct tac_handle *h"
+.Ft int
+.Fn tac_config "struct tac_handle *h" "const char *path"
+.Ft int
+.Fn tac_create_authen "struct tac_handle *h" "int action" "int type" "int service"
+.Ft void *
+.Fn tac_get_data "struct tac_handle *h" "size_t *len"
+.Ft char *
+.Fn tac_get_msg "struct tac_handle *h"
+.Ft struct tac_handle *
+.Fn tac_open "void"
+.Ft int
+.Fn tac_send_authen "struct tac_handle *h"
+.Ft int
+.Fn tac_set_data "struct tac_handle *h" "const void *data" "size_t data_len"
+.Ft int
+.Fn tac_set_msg "struct tac_handle *h" "const char *msg"
+.Ft int
+.Fn tac_set_port "struct tac_handle *h" "const char *port"
+.Ft int
+.Fn tac_set_priv "struct tac_handle *h" "int priv"
+.Ft int
+.Fn tac_set_rem_addr "struct tac_handle *h" "const char *addr"
+.Ft int
+.Fn tac_set_user "struct tac_handle *h" "const char *user"
+.Ft const char *
+.Fn tac_strerror "struct tac_handle *h"
+.Sh DESCRIPTION
+The
+.Nm
+library implements the client side of the TACACS+ network access
+control protocol. TACACS+ allows clients to perform authentication,
+authorization, and accounting by means of network requests to remote
+servers. This library currently supports only the authentication
+portion of the protocol.
+.Sh INITIALIZATION
+To use the library, an application must first call
+.Fn tac_open
+to obtain a
+.Va struct tac_handle * ,
+which provides context for subsequent operations.
+Calls to
+.Fn tac_open
+always succeed unless insufficient virtual memory is available. If
+the necessary memory cannot be allocated,
+.Fn tac_open
+returns
+.Dv NULL .
+.Pp
+Before issuing any TACACS+ requests, the library must be made aware
+of the servers it can contact. The easiest way to configure the
+library is to call
+.Fn tac_config .
+.Fn tac_config
+causes the library to read a configuration file whose format is
+described in
+.Xr tacplus.conf 5 .
+The pathname of the configuration file is passed as the
+.Va file
+argument to
+.Fn tac_config .
+This argument may also be given as
+.Dv NULL ,
+in which case the standard configuration file
+.Pa /etc/tacplus.conf
+is used.
+.Fn tac_config
+returns 0 on success, or -1 if an error occurs.
+.Pp
+The library can also be configured programmatically by calls to
+.Fn tac_add_server .
+The
+.Va host
+parameter specifies the server host, either as a fully qualified
+domain name or as a dotted-quad IP address in text form.
+The
+.Va port
+parameter specifies the TCP port to contact on the server. If
+.Va port
+is given as 0, the library uses port 49, the standard TACACS+ port.
+The shared secret for the server host is passed to the
+.Va secret
+parameter. It may be any null-terminated string of bytes.
+The timeout for receiving replies from the server is passed to the
+.Va timeout
+parameter, in units of seconds.
+The
+.Va flags
+parameter is a bit mask of flags to specify various characteristics of
+the server. It may contain:
+.Pp
+.Bl -tag -width Fl
+.It Dv TAC_SRVR_SINGLE_CONNECT
+Causes the library to attempt to negotiate single connection mode
+when communicating with the server. In single connection mode, the
+original TCP connection is held open for multiple TACACS+ sessions.
+Older servers do not support this mode, and some of them become
+confused if the client attempts to negotiate it.
+.El
+.Pp
+.Fn tac_add_server
+returns 0 on success, or -1 if an error occurs.
+.Pp
+.Fn tac_add_server
+may be called multiple times, and it may be used together with
+.Fn tac_config .
+At most 10 servers may be specified.
+When multiple servers are given, they are tried in round-robin
+fashion until a working, accessible server is found. Once the
+library finds such a server, it continues to use it as long as it
+works.
+.Sh CREATING A TACACS+ AUTHENTICATION REQUEST
+To begin constructing a new authentication request, call
+.Fn tac_create_authen .
+The
+.Va action ,
+.Va type ,
+and
+.Va service
+arguments must be be set to appropriate values as defined in the
+TACACS+ protocol specification. The
+.Aq taclib.h
+header file contains symbolic constants for these values.
+.Pp
+After creating a request with
+.Fn tac_create_authen ,
+various optional parameters may be attached to it through calls to
+.Fn tac_set_data ,
+.Fn tac_set_port ,
+.Fn tac_set_priv ,
+.Fn tac_set_rem_addr ,
+and
+.Fn tac_set_user .
+The library creates its own copies of any strings provided to these
+functions, so that it is not necessary for the caller to preserve
+them. By default, each of these parameters is empty except for the
+privilege level, which defaults to
+.Ql USER
+privilege.
+.Sh SENDING THE AUTHENTICATION REQUEST AND RECEIVING THE RESPONSE
+After the TACACS+ request has been constructed, it is sent by means
+of
+.Fn tac_send_authen .
+This function connects to a server if not already connected, sends
+the request, and waits for a reply. On failure,
+.Fn tac_send_authen
+returns -1. Otherwise, it returns the TACACS+ status code and flags,
+packed into an integer value. The status can be extracted using the
+macro
+.Fn TAC_AUTHEN_STATUS .
+Possible status codes, defined in
+.Aq taclib.h ,
+include:
+.Pp
+.Bl -item -compact -offset indent
+.It
+.Dv TAC_AUTHEN_STATUS_PASS
+.It
+.Dv TAC_AUTHEN_STATUS_FAIL
+.It
+.Dv TAC_AUTHEN_STATUS_GETDATA
+.It
+.Dv TAC_AUTHEN_STATUS_GETUSER
+.It
+.Dv TAC_AUTHEN_STATUS_GETPASS
+.It
+.Dv TAC_AUTHEN_STATUS_RESTART
+.It
+.Dv TAC_AUTHEN_STATUS_ERROR
+.It
+.Dv TAC_AUTHEN_STATUS_FOLLOW
+.El
+.Pp
+The only flag is the no-echo flag, which can be tested using the
+macro
+.Fn TAC_AUTHEN_NOECHO .
+.Sh EXTRACTING INFORMATION FROM THE SERVER'S RESPONSE
+An authentication response packet from the server may contain a
+server message, a data string, or both. After a successful call to
+.Fn tac_send_authen ,
+this information may be retrieved from the response by calling
+.Fn tac_get_msg
+and
+.Fn tac_get_data .
+These functions return dynamically-allocated copies of the
+information from the packet. The caller is responsible for freeing
+the copies when it no longer needs them. The data returned from
+these functions is guaranteed to be terminated by a null byte.
+.Pp
+In the case of
+.Fn tac_get_data ,
+the
+.Va len
+argument points to a location into which the library will store the
+actual length of the received data, not including the null
+terminator. This argument may be given as
+.Dv NULL
+if the caller is not interested in the length.
+.Sh SENDING AUTHENTICATION CONTINUE PACKETS
+If
+.Fn tac_send_authen
+returns a value containing one of the status codes
+.Dv TAC_AUTHEN_STATUS_GETDATA ,
+.Dv TAC_AUTHEN_STATUS_GETUSER ,
+or
+.Dv TAC_AUTHEN_STATUS_GETPASS ,
+then the client must provide additional information to the server by
+means of a TACACS+ CONTINUE packet. To do so, the application must
+first set the packet's user message and/or data fields using
+.Fn tac_set_msg
+and
+.Fn tac_set_data .
+The client then sends the CONTINUE packet with
+.Fn tac_send_authen .
+N.B.,
+.Fn tac_create_authen
+should
+.Em not
+be called to construct a CONTINUE packet; it is used only for the
+initial authentication request.
+.Pp
+When it receives the CONTINUE packet, the server may again request
+more information by returning
+.Dv TAC_AUTHEN_STATUS_GETDATA ,
+.Dv TAC_AUTHEN_STATUS_GETUSER ,
+or
+.Dv TAC_AUTHEN_STATUS_GETPASS .
+The application should send further CONTINUEs until some other
+status is received from the server.
+.Sh OBTAINING ERROR MESSAGES
+Those functions which accept a
+.Va struct tac_handle *
+argument record an error message if they fail. The error message
+can be retrieved by calling
+.Fn tac_strerror .
+The message text is overwritten on each new error for the given
+.Va struct tac_handle * .
+Thus the message must be copied if it is to be preserved through
+subsequent library calls using the same handle.
+.Sh CLEANUP
+To free the resources used by the TACACS+ library, call
+.Fn tac_close .
+.Sh RETURN VALUES
+The following functions return a non-negative value on success. If
+they detect an error, they return -1 and record an error message
+which can be retrieved using
+.Fn tac_strerror .
+.Pp
+.Bl -item -offset indent -compact
+.It
+.Fn tac_add_server
+.It
+.Fn tac_config
+.It
+.Fn tac_create_authen
+.It
+.Fn tac_send_authen
+.It
+.Fn tac_set_data
+.It
+.Fn tac_set_msg
+.It
+.Fn tac_set_port
+.It
+.Fn tac_set_priv
+.It
+.Fn tac_set_rem_addr
+.It
+.Fn tac_set_user
+.El
+.Pp
+The following functions return a
+.No non- Ns Dv NULL
+pointer on success. If they are unable to allocate sufficient
+virtual memory, they return
+.Dv NULL
+and record an error message which can be retrieved using
+.Fn tac_strerror .
+.Pp
+.Bl -item -offset indent -compact
+.It
+.Fn tac_get_data
+.It
+.Fn tac_get_msg
+.El
+.Pp
+The following functions return a
+.No non- Ns Dv NULL
+pointer on success. If they are unable to allocate sufficient
+virtual memory, they return
+.Dv NULL ,
+without recording an error message.
+.Pp
+.Bl -item -offset indent -compact
+.It
+.Fn tac_open
+.El
+.Sh FILES
+.Pa /etc/tacplus.conf
+.Sh SEE ALSO
+.Xr tacplus.conf 5
+.Rs
+.%A D. Carrel and Lol Grant
+.%T The TACACS+ Protocol, Version 1.78
+.%O draft-grant-tacacs-02.txt (Internet Draft)
+.Re
+.Sh AUTHORS
+This software was written by
+.An John Polstra ,
+and donated to the FreeBSD project by Juniper Networks, Inc.
diff --git a/lib/libtacplus/taclib.c b/lib/libtacplus/taclib.c
new file mode 100644
index 0000000..bd2e663
--- /dev/null
+++ b/lib/libtacplus/taclib.c
@@ -0,0 +1,1053 @@
+/*-
+ * Copyright 1998 Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <md5.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "taclib_private.h"
+
+static int add_str_8(struct tac_handle *, u_int8_t *,
+ struct clnt_str *);
+static int add_str_16(struct tac_handle *, u_int16_t *,
+ struct clnt_str *);
+static int authen_version(int, int);
+static void close_connection(struct tac_handle *);
+static int conn_server(struct tac_handle *);
+static void crypt_msg(struct tac_handle *, struct tac_msg *);
+static void *dup_str(struct tac_handle *, const struct srvr_str *,
+ size_t *);
+static int establish_connection(struct tac_handle *);
+static void free_str(struct clnt_str *);
+static void generr(struct tac_handle *, const char *, ...)
+ __printflike(2, 3);
+static void gen_session_id(struct tac_msg *);
+static int get_srvr_end(struct tac_handle *);
+static int get_srvr_str(struct tac_handle *, struct srvr_str *,
+ size_t);
+static void init_clnt_str(struct clnt_str *);
+static void init_srvr_str(struct srvr_str *);
+static int read_timed(struct tac_handle *, void *, size_t,
+ const struct timeval *);
+static int recv_msg(struct tac_handle *);
+static int save_str(struct tac_handle *, struct clnt_str *,
+ const void *, size_t);
+static int send_msg(struct tac_handle *);
+static int split(char *, char *[], int, char *, size_t);
+static void *xmalloc(struct tac_handle *, size_t);
+static char *xstrdup(struct tac_handle *, const char *);
+
+/*
+ * Append some optional data to the current request, and store its
+ * length into the 8-bit field referenced by "fld". Returns 0 on
+ * success, or -1 on failure.
+ *
+ * This function also frees the "cs" string data and initializes it
+ * for the next time.
+ */
+static int
+add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
+{
+ u_int16_t len;
+
+ if (add_str_16(h, &len, cs) == -1)
+ return -1;
+ len = ntohs(len);
+ if (len > 0xff) {
+ generr(h, "Field too long");
+ return -1;
+ }
+ *fld = len;
+ return 0;
+}
+
+/*
+ * Append some optional data to the current request, and store its
+ * length into the 16-bit field (network byte order) referenced by
+ * "fld". Returns 0 on success, or -1 on failure.
+ *
+ * This function also frees the "cs" string data and initializes it
+ * for the next time.
+ */
+static int
+add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
+{
+ size_t len;
+
+ len = cs->len;
+ if (cs->data == NULL)
+ len = 0;
+ if (len != 0) {
+ int offset;
+
+ if (len > 0xffff) {
+ generr(h, "Field too long");
+ return -1;
+ }
+ offset = ntohl(h->request.length);
+ if (offset + len > BODYSIZE) {
+ generr(h, "Message too long");
+ return -1;
+ }
+ memcpy(h->request.u.body + offset, cs->data, len);
+ h->request.length = htonl(offset + len);
+ }
+ *fld = htons(len);
+ free_str(cs);
+ return 0;
+}
+
+static int
+authen_version(int action, int type)
+{
+ int minor;
+
+ switch (action) {
+
+ case TAC_AUTHEN_LOGIN:
+ switch (type) {
+
+ case TAC_AUTHEN_TYPE_PAP:
+ case TAC_AUTHEN_TYPE_CHAP:
+ case TAC_AUTHEN_TYPE_MSCHAP:
+ case TAC_AUTHEN_TYPE_ARAP:
+ minor = 1;
+ break;
+
+ default:
+ minor = 0;
+ break;
+ }
+ break;
+
+ case TAC_AUTHEN_SENDAUTH:
+ minor = 1;
+ break;
+
+ default:
+ minor = 0;
+ break;
+ };
+
+ return TAC_VER_MAJOR << 4 | minor;
+}
+
+static void
+close_connection(struct tac_handle *h)
+{
+ if (h->fd != -1) {
+ close(h->fd);
+ h->fd = -1;
+ }
+}
+
+static int
+conn_server(struct tac_handle *h)
+{
+ const struct tac_server *srvp = &h->servers[h->cur_server];
+ int flags;
+
+ if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ generr(h, "Cannot create socket: %s", strerror(errno));
+ return -1;
+ }
+ if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
+ fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+ generr(h, "Cannot set non-blocking mode on socket: %s",
+ strerror(errno));
+ close(h->fd);
+ h->fd = -1;
+ return -1;
+ }
+ if (connect(h->fd, (struct sockaddr *)&srvp->addr,
+ sizeof srvp->addr) == 0)
+ return 0;
+
+ if (errno == EINPROGRESS) {
+ fd_set wfds;
+ struct timeval tv;
+ int nfds;
+ struct sockaddr peer;
+ int peerlen;
+ int err;
+ int errlen;
+
+ /* Wait for the connection to complete. */
+ FD_ZERO(&wfds);
+ FD_SET(h->fd, &wfds);
+ tv.tv_sec = srvp->timeout;
+ tv.tv_usec = 0;
+ nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
+ if (nfds == -1) {
+ generr(h, "select: %s", strerror(errno));
+ close(h->fd);
+ h->fd = -1;
+ return -1;
+ }
+ if (nfds == 0) {
+ generr(h, "connect: timed out");
+ close(h->fd);
+ h->fd = -1;
+ return -1;
+ }
+
+ /* See whether we are connected now. */
+ peerlen = sizeof peer;
+ if (getpeername(h->fd, &peer, &peerlen) == 0)
+ return 0;
+
+ if (errno != ENOTCONN) {
+ generr(h, "getpeername: %s", strerror(errno));
+ close(h->fd);
+ h->fd = -1;
+ return -1;
+ }
+
+ /* Find out why the connect failed. */
+ errlen = sizeof err;
+ getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
+ errno = err;
+ }
+ generr(h, "connect: %s", strerror(errno));
+ close(h->fd);
+ h->fd = -1;
+ return -1;
+}
+
+/*
+ * Encrypt or decrypt a message. The operations are symmetrical.
+ */
+static void
+crypt_msg(struct tac_handle *h, struct tac_msg *msg)
+{
+ const char *secret;
+ MD5_CTX base_ctx;
+ MD5_CTX ctx;
+ unsigned char md5[16];
+ int chunk;
+ int msg_len;
+
+ secret = h->servers[h->cur_server].secret;
+ if (secret[0] == '\0')
+ msg->flags |= TAC_UNENCRYPTED;
+ if (msg->flags & TAC_UNENCRYPTED)
+ return;
+
+ msg_len = ntohl(msg->length);
+
+ MD5Init(&base_ctx);
+ MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
+ MD5Update(&base_ctx, secret, strlen(secret));
+ MD5Update(&base_ctx, &msg->version, sizeof msg->version);
+ MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
+
+ ctx = base_ctx;
+ for (chunk = 0; chunk < msg_len; chunk += sizeof md5) {
+ int chunk_len;
+ int i;
+
+ MD5Final(md5, &ctx);
+
+ if ((chunk_len = msg_len - chunk) > sizeof md5)
+ chunk_len = sizeof md5;
+ for (i = 0; i < chunk_len; i++)
+ msg->u.body[chunk + i] ^= md5[i];
+
+ ctx = base_ctx;
+ MD5Update(&ctx, md5, sizeof md5);
+ }
+}
+
+/*
+ * Return a dynamically allocated copy of the given server string.
+ * The copy is null-terminated. If "len" is non-NULL, the length of
+ * the string (excluding the terminating null byte) is stored via it.
+ * Returns NULL on failure. Empty strings are still allocated even
+ * though they have no content.
+ */
+static void *
+dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
+{
+ unsigned char *p;
+
+ if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
+ return NULL;
+ if (ss->data != NULL && ss->len != 0)
+ memcpy(p, ss->data, ss->len);
+ p[ss->len] = '\0';
+ if (len != NULL)
+ *len = ss->len;
+ return p;
+}
+
+static int
+establish_connection(struct tac_handle *h)
+{
+ int i;
+
+ if (h->fd >= 0) /* Already connected. */
+ return 0;
+ if (h->num_servers == 0) {
+ generr(h, "No TACACS+ servers specified");
+ return -1;
+ }
+ /*
+ * Try the servers round-robin. We begin with the one that
+ * worked for us the last time. That way, once we find a good
+ * server, we won't waste any more time trying the bad ones.
+ */
+ for (i = 0; i < h->num_servers; i++) {
+ if (conn_server(h) == 0) {
+ h->single_connect = (h->servers[h->cur_server].flags &
+ TAC_SRVR_SINGLE_CONNECT) != 0;
+ return 0;
+ }
+ if (++h->cur_server >= h->num_servers) /* Wrap around */
+ h->cur_server = 0;
+ }
+ /* Just return whatever error was last reported by conn_server(). */
+ return -1;
+}
+
+/*
+ * Free a client string, obliterating its contents first for security.
+ */
+static void
+free_str(struct clnt_str *cs)
+{
+ if (cs->data != NULL) {
+ memset(cs->data, 0, cs->len);
+ free(cs->data);
+ cs->data = NULL;
+ cs->len = 0;
+ }
+}
+
+static void
+generr(struct tac_handle *h, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(h->errmsg, ERRSIZE, format, ap);
+ va_end(ap);
+}
+
+static void
+gen_session_id(struct tac_msg *msg)
+{
+ int r;
+
+ r = random();
+ msg->session_id[0] = r >> 8;
+ msg->session_id[1] = r;
+ r = random();
+ msg->session_id[2] = r >> 8;
+ msg->session_id[3] = r;
+}
+
+/*
+ * Verify that we are exactly at the end of the response message.
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+get_srvr_end(struct tac_handle *h)
+{
+ if (h->srvr_pos != ntohl(h->response.length)) {
+ generr(h, "Invalid length field in response from server");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len)
+{
+ if (h->srvr_pos + len > ntohl(h->response.length)) {
+ generr(h, "Invalid length field in response from server");
+ return -1;
+ }
+ ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
+ ss->len = len;
+ h->srvr_pos += len;
+ return 0;
+}
+
+static void
+init_clnt_str(struct clnt_str *cs)
+{
+ cs->data = NULL;
+ cs->len = 0;
+}
+
+static void
+init_srvr_str(struct srvr_str *ss)
+{
+ ss->data = NULL;
+ ss->len = 0;
+}
+
+static int
+read_timed(struct tac_handle *h, void *buf, size_t len,
+ const struct timeval *deadline)
+{
+ char *ptr;
+
+ ptr = (char *)buf;
+ while (len > 0) {
+ int n;
+
+ n = read(h->fd, ptr, len);
+ if (n == -1) {
+ struct timeval tv;
+ int nfds;
+
+ if (errno != EAGAIN) {
+ generr(h, "Network read error: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Wait until we can read more data. */
+ gettimeofday(&tv, NULL);
+ timersub(deadline, &tv, &tv);
+ if (tv.tv_sec >= 0) {
+ fd_set rfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(h->fd, &rfds);
+ nfds =
+ select(h->fd + 1, &rfds, NULL, NULL, &tv);
+ if (nfds == -1) {
+ generr(h, "select: %s",
+ strerror(errno));
+ return -1;
+ }
+ } else
+ nfds = 0;
+ if (nfds == 0) {
+ generr(h, "Network read timed out");
+ return -1;
+ }
+ } else if (n == 0) {
+ generr(h, "unexpected EOF from server");
+ return -1;
+ } else {
+ ptr += n;
+ len -= n;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Receive a response from the server and decrypt it. Returns 0 on
+ * success, or -1 on failure.
+ */
+static int
+recv_msg(struct tac_handle *h)
+{
+ struct timeval deadline;
+ struct tac_msg *msg;
+ size_t len;
+
+ msg = &h->response;
+ gettimeofday(&deadline, NULL);
+ deadline.tv_sec += h->servers[h->cur_server].timeout;
+
+ /* Read the message header and make sure it is reasonable. */
+ if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
+ return -1;
+ if (memcmp(msg->session_id, h->request.session_id,
+ sizeof msg->session_id) != 0) {
+ generr(h, "Invalid session ID in received message");
+ return -1;
+ }
+ if (msg->type != h->request.type) {
+ generr(h, "Invalid type in received message");
+ return -1;
+ }
+ len = ntohl(msg->length);
+ if (len > BODYSIZE) {
+ generr(h, "Received message too large");
+ return -1;
+ }
+ if (msg->seq_no != ++h->last_seq_no) {
+ generr(h, "Invalid sequence number in received message");
+ return -1;
+ }
+
+ /* Read the message body. */
+ if (read_timed(h, msg->u.body, len, &deadline) == -1)
+ return -1;
+
+ /* Decrypt it. */
+ crypt_msg(h, msg);
+
+ /*
+ * Turn off single-connection mode if the server isn't amenable
+ * to it.
+ */
+ if (!(msg->flags & TAC_SINGLE_CONNECT))
+ h->single_connect = 0;
+ return 0;
+}
+
+static int
+save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
+ size_t len)
+{
+ free_str(cs);
+ if (data != NULL && len != 0) {
+ if ((cs->data = xmalloc(h, len)) == NULL)
+ return -1;
+ cs->len = len;
+ memcpy(cs->data, data, len);
+ }
+ return 0;
+}
+
+/*
+ * Send the current request, after encrypting it. Returns 0 on success,
+ * or -1 on failure.
+ */
+static int
+send_msg(struct tac_handle *h)
+{
+ struct timeval deadline;
+ struct tac_msg *msg;
+ char *ptr;
+ int len;
+
+ if (h->last_seq_no & 1) {
+ generr(h, "Attempt to send message out of sequence");
+ return -1;
+ }
+
+ msg = &h->request;
+ msg->seq_no = ++h->last_seq_no;
+ if (msg->seq_no == 1)
+ gen_session_id(msg);
+ crypt_msg(h, msg);
+
+ if (establish_connection(h) == -1)
+ return -1;
+
+ if (h->single_connect)
+ msg->flags |= TAC_SINGLE_CONNECT;
+ else
+ msg->flags &= ~TAC_SINGLE_CONNECT;
+ gettimeofday(&deadline, NULL);
+ deadline.tv_sec += h->servers[h->cur_server].timeout;
+ len = HDRSIZE + ntohl(msg->length);
+ ptr = (char *)msg;
+ while (len > 0) {
+ int n;
+
+ n = write(h->fd, ptr, len);
+ if (n == -1) {
+ struct timeval tv;
+ int nfds;
+
+ if (errno != EAGAIN) {
+ generr(h, "Network write error: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ /* Wait until we can write more data. */
+ gettimeofday(&tv, NULL);
+ timersub(&deadline, &tv, &tv);
+ if (tv.tv_sec >= 0) {
+ fd_set wfds;
+
+ FD_ZERO(&wfds);
+ FD_SET(h->fd, &wfds);
+ nfds =
+ select(h->fd + 1, NULL, &wfds, NULL, &tv);
+ if (nfds == -1) {
+ generr(h, "select: %s",
+ strerror(errno));
+ return -1;
+ }
+ } else
+ nfds = 0;
+ if (nfds == 0) {
+ generr(h, "Network write timed out");
+ return -1;
+ }
+ } else {
+ ptr += n;
+ len -= n;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Destructively split a string into fields separated by white space.
+ * `#' at the beginning of a field begins a comment that extends to the
+ * end of the string. Fields may be quoted with `"'. Inside quoted
+ * strings, the backslash escapes `\"' and `\\' are honored.
+ *
+ * Pointers to up to the first maxfields fields are stored in the fields
+ * array. Missing fields get NULL pointers.
+ *
+ * The return value is the actual number of fields parsed, and is always
+ * <= maxfields.
+ *
+ * On a syntax error, places a message in the msg string, and returns -1.
+ */
+static int
+split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
+{
+ char *p;
+ int i;
+ static const char ws[] = " \t";
+
+ for (i = 0; i < maxfields; i++)
+ fields[i] = NULL;
+ p = str;
+ i = 0;
+ while (*p != '\0') {
+ p += strspn(p, ws);
+ if (*p == '#' || *p == '\0')
+ break;
+ if (i >= maxfields) {
+ snprintf(msg, msglen, "line has too many fields");
+ return -1;
+ }
+ if (*p == '"') {
+ char *dst;
+
+ dst = ++p;
+ fields[i] = dst;
+ while (*p != '"') {
+ if (*p == '\\') {
+ p++;
+ if (*p != '"' && *p != '\\' &&
+ *p != '\0') {
+ snprintf(msg, msglen,
+ "invalid `\\' escape");
+ return -1;
+ }
+ }
+ if (*p == '\0') {
+ snprintf(msg, msglen,
+ "unterminated quoted string");
+ return -1;
+ }
+ *dst++ = *p++;
+ }
+ *dst = '\0';
+ p++;
+ if (*p != '\0' && strspn(p, ws) == 0) {
+ snprintf(msg, msglen, "quoted string not"
+ " followed by white space");
+ return -1;
+ }
+ } else {
+ fields[i] = p;
+ p += strcspn(p, ws);
+ if (*p != '\0')
+ *p++ = '\0';
+ }
+ i++;
+ }
+ return i;
+}
+
+int
+tac_add_server(struct tac_handle *h, const char *host, int port,
+ const char *secret, int timeout, int flags)
+{
+ struct tac_server *srvp;
+
+ if (h->num_servers >= MAXSERVERS) {
+ generr(h, "Too many RADIUS servers specified");
+ return -1;
+ }
+ srvp = &h->servers[h->num_servers];
+
+ memset(&srvp->addr, 0, sizeof srvp->addr);
+ srvp->addr.sin_len = sizeof srvp->addr;
+ srvp->addr.sin_family = AF_INET;
+ if (!inet_aton(host, &srvp->addr.sin_addr)) {
+ struct hostent *hent;
+
+ if ((hent = gethostbyname(host)) == NULL) {
+ generr(h, "%s: host not found", host);
+ return -1;
+ }
+ memcpy(&srvp->addr.sin_addr, hent->h_addr,
+ sizeof srvp->addr.sin_addr);
+ }
+ srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
+ if ((srvp->secret = xstrdup(h, secret)) == NULL)
+ return -1;
+ srvp->timeout = timeout;
+ srvp->flags = flags;
+ h->num_servers++;
+ return 0;
+}
+
+void
+tac_close(struct tac_handle *h)
+{
+ int srv;
+
+ if (h->fd != -1)
+ close(h->fd);
+ for (srv = 0; srv < h->num_servers; srv++) {
+ memset(h->servers[srv].secret, 0,
+ strlen(h->servers[srv].secret));
+ free(h->servers[srv].secret);
+ }
+ free_str(&h->user);
+ free_str(&h->port);
+ free_str(&h->rem_addr);
+ free_str(&h->data);
+ free_str(&h->user_msg);
+ free(h);
+}
+
+int
+tac_config(struct tac_handle *h, const char *path)
+{
+ FILE *fp;
+ char buf[MAXCONFLINE];
+ int linenum;
+ int retval;
+
+ if (path == NULL)
+ path = PATH_TACPLUS_CONF;
+ if ((fp = fopen(path, "r")) == NULL) {
+ generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
+ return -1;
+ }
+ retval = 0;
+ linenum = 0;
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ int len;
+ char *fields[4];
+ int nfields;
+ char msg[ERRSIZE];
+ char *host;
+ char *port_str;
+ char *secret;
+ char *timeout_str;
+ char *options_str;
+ char *end;
+ unsigned long timeout;
+ int port;
+ int options;
+
+ linenum++;
+ len = strlen(buf);
+ /* We know len > 0, else fgets would have returned NULL. */
+ if (buf[len - 1] != '\n') {
+ if (len == sizeof buf - 1)
+ generr(h, "%s:%d: line too long", path,
+ linenum);
+ else
+ generr(h, "%s:%d: missing newline", path,
+ linenum);
+ retval = -1;
+ break;
+ }
+ buf[len - 1] = '\0';
+
+ /* Extract the fields from the line. */
+ nfields = split(buf, fields, 4, msg, sizeof msg);
+ if (nfields == -1) {
+ generr(h, "%s:%d: %s", path, linenum, msg);
+ retval = -1;
+ break;
+ }
+ if (nfields == 0)
+ continue;
+ if (nfields < 2) {
+ generr(h, "%s:%d: missing shared secret", path,
+ linenum);
+ retval = -1;
+ break;
+ }
+ host = fields[0];
+ secret = fields[1];
+ timeout_str = fields[2];
+ options_str = fields[3];
+
+ /* Parse and validate the fields. */
+ host = strtok(host, ":");
+ port_str = strtok(NULL, ":");
+ if (port_str != NULL) {
+ port = strtoul(port_str, &end, 10);
+ if (port_str[0] == '\0' || *end != '\0') {
+ generr(h, "%s:%d: invalid port", path,
+ linenum);
+ retval = -1;
+ break;
+ }
+ } else
+ port = 0;
+ if (timeout_str != NULL) {
+ timeout = strtoul(timeout_str, &end, 10);
+ if (timeout_str[0] == '\0' || *end != '\0') {
+ generr(h, "%s:%d: invalid timeout", path,
+ linenum);
+ retval = -1;
+ break;
+ }
+ } else
+ timeout = TIMEOUT;
+ options = 0;
+ if (options_str != NULL) {
+ if (strcmp(options_str, "single-connection") == 0)
+ options |= TAC_SRVR_SINGLE_CONNECT;
+ else {
+ generr(h, "%s:%d: invalid option \"%s\"",
+ path, linenum, options_str);
+ retval = -1;
+ break;
+ }
+ };
+
+ if (tac_add_server(h, host, port, secret, timeout,
+ options) == -1) {
+ char msg[ERRSIZE];
+
+ strcpy(msg, h->errmsg);
+ generr(h, "%s:%d: %s", path, linenum, msg);
+ retval = -1;
+ break;
+ }
+ }
+ /* Clear out the buffer to wipe a possible copy of a shared secret */
+ memset(buf, 0, sizeof buf);
+ fclose(fp);
+ return retval;
+}
+
+int
+tac_create_authen(struct tac_handle *h, int action, int type, int service)
+{
+ struct tac_msg *msg;
+ struct tac_authen_start *as;
+
+ h->last_seq_no = 0;
+
+ msg = &h->request;
+ msg->type = TAC_AUTHEN;
+ msg->version = authen_version(action, type);
+ msg->flags = 0;
+
+ as = &msg->u.authen_start;
+ as->action = action;
+ as->priv_lvl = TAC_PRIV_LVL_USER;
+ as->authen_type = type;
+ as->service = service;
+
+ free_str(&h->user);
+ free_str(&h->port);
+ free_str(&h->rem_addr);
+ free_str(&h->data);
+ free_str(&h->user_msg);
+
+ /* XXX - more to do */
+ return 0;
+}
+
+void *
+tac_get_data(struct tac_handle *h, size_t *len)
+{
+ return dup_str(h, &h->srvr_data, len);
+}
+
+char *
+tac_get_msg(struct tac_handle *h)
+{
+ return (char *)dup_str(h, &h->srvr_msg, NULL);
+}
+
+/*
+ * Create and initialize a tac_handle structure, and return it to the
+ * caller. Can fail only if the necessary memory cannot be allocated.
+ * In that case, it returns NULL.
+ */
+struct tac_handle *
+tac_open(void)
+{
+ struct tac_handle *h;
+
+ h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
+ if (h != NULL) {
+ h->fd = -1;
+ h->num_servers = 0;
+ h->cur_server = 0;
+ h->errmsg[0] = '\0';
+ init_clnt_str(&h->user);
+ init_clnt_str(&h->port);
+ init_clnt_str(&h->rem_addr);
+ init_clnt_str(&h->data);
+ init_clnt_str(&h->user_msg);
+ init_srvr_str(&h->srvr_msg);
+ init_srvr_str(&h->srvr_data);
+ srandomdev();
+ }
+ return h;
+}
+
+int
+tac_send_authen(struct tac_handle *h)
+{
+ struct tac_authen_reply *ar;
+
+ if (h->last_seq_no == 0) { /* Authentication START packet */
+ struct tac_authen_start *as;
+
+ as = &h->request.u.authen_start;
+ h->request.length =
+ htonl(offsetof(struct tac_authen_start, rest[0]));
+ if (add_str_8(h, &as->user_len, &h->user) == -1 ||
+ add_str_8(h, &as->port_len, &h->port) == -1 ||
+ add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
+ add_str_8(h, &as->data_len, &h->data) == -1)
+ return -1;
+ } else { /* Authentication CONTINUE packet */
+ struct tac_authen_cont *ac;
+
+ ac = &h->request.u.authen_cont;
+ ac->flags = 0;
+ h->request.length =
+ htonl(offsetof(struct tac_authen_cont, rest[0]));
+ if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
+ add_str_16(h, &ac->data_len, &h->data) == -1)
+ return -1;
+ }
+
+ /* Send the message and retrieve the reply. */
+ if (send_msg(h) == -1 || recv_msg(h) == -1)
+ return -1;
+
+ /* Scan the optional fields in the reply. */
+ ar = &h->response.u.authen_reply;
+ h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
+ if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
+ get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 ||
+ get_srvr_end(h) == -1)
+ return -1;
+
+ if (!h->single_connect &&
+ ar->status != TAC_AUTHEN_STATUS_GETDATA &&
+ ar->status != TAC_AUTHEN_STATUS_GETUSER &&
+ ar->status != TAC_AUTHEN_STATUS_GETPASS)
+ close_connection(h);
+
+ return ar->flags << 8 | ar->status;
+}
+
+int
+tac_set_rem_addr(struct tac_handle *h, const char *addr)
+{
+ return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
+}
+
+int
+tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
+{
+ return save_str(h, &h->data, data, data_len);
+}
+
+int
+tac_set_msg(struct tac_handle *h, const char *msg)
+{
+ return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
+}
+
+int
+tac_set_port(struct tac_handle *h, const char *port)
+{
+ return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
+}
+
+int
+tac_set_priv(struct tac_handle *h, int priv)
+{
+ if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
+ generr(h, "Attempt to set invalid privilege level");
+ return -1;
+ }
+ h->request.u.authen_start.priv_lvl = priv;
+ return 0;
+}
+
+int
+tac_set_user(struct tac_handle *h, const char *user)
+{
+ return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
+}
+
+const char *
+tac_strerror(struct tac_handle *h)
+{
+ return h->errmsg;
+}
+
+static void *
+xmalloc(struct tac_handle *h, size_t size)
+{
+ void *r;
+
+ if ((r = malloc(size)) == NULL)
+ generr(h, "Out of memory");
+ return r;
+}
+
+static char *
+xstrdup(struct tac_handle *h, const char *s)
+{
+ char *r;
+
+ if ((r = strdup(s)) == NULL)
+ generr(h, "Out of memory");
+ return r;
+}
diff --git a/lib/libtacplus/taclib.h b/lib/libtacplus/taclib.h
new file mode 100644
index 0000000..0da1b08
--- /dev/null
+++ b/lib/libtacplus/taclib.h
@@ -0,0 +1,105 @@
+/*-
+ * Copyright 1998 Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _TACLIB_H_
+#define _TACLIB_H_
+
+#include <sys/types.h>
+
+struct tac_handle;
+
+/* Flags for tac_add_server(). */
+#define TAC_SRVR_SINGLE_CONNECT 0x04 /* Keep connection open for multiple
+ sessions. */
+
+/* Disassembly of tac_send_authen() return value. */
+#define TAC_AUTHEN_STATUS(s) ((s) & 0xff)
+#define TAC_AUTHEN_NOECHO(s) ((s) & (1<<8))
+
+/* Privilege levels */
+#define TAC_PRIV_LVL_MIN 0x00
+#define TAC_PRIV_LVL_USER 0x01
+#define TAC_PRIV_LVL_ROOT 0x0f
+#define TAC_PRIV_LVL_MAX 0x0f
+
+/* Authentication actions */
+#define TAC_AUTHEN_LOGIN 0x01
+#define TAC_AUTHEN_CHPASS 0x02
+#define TAC_AUTHEN_SENDPASS 0x03
+#define TAC_AUTHEN_SENDAUTH 0x04
+
+/* Authentication types */
+#define TAC_AUTHEN_TYPE_ASCII 0x01
+#define TAC_AUTHEN_TYPE_PAP 0x02
+#define TAC_AUTHEN_TYPE_CHAP 0x03
+#define TAC_AUTHEN_TYPE_ARAP 0x04
+#define TAC_AUTHEN_TYPE_MSCHAP 0x05
+
+/* Authentication services */
+#define TAC_AUTHEN_SVC_NONE 0x00
+#define TAC_AUTHEN_SVC_LOGIN 0x01
+#define TAC_AUTHEN_SVC_ENABLE 0x02
+#define TAC_AUTHEN_SVC_PPP 0x03
+#define TAC_AUTHEN_SVC_ARAP 0x04
+#define TAC_AUTHEN_SVC_PT 0x05
+#define TAC_AUTHEN_SVC_RCMD 0x06
+#define TAC_AUTHEN_SVC_X25 0x07
+#define TAC_AUTHEN_SVC_NASI 0x08
+#define TAC_AUTHEN_SVC_FWPROXY 0x09
+
+/* Authentication reply status codes */
+#define TAC_AUTHEN_STATUS_PASS 0x01
+#define TAC_AUTHEN_STATUS_FAIL 0x02
+#define TAC_AUTHEN_STATUS_GETDATA 0x03
+#define TAC_AUTHEN_STATUS_GETUSER 0x04
+#define TAC_AUTHEN_STATUS_GETPASS 0x05
+#define TAC_AUTHEN_STATUS_RESTART 0x06
+#define TAC_AUTHEN_STATUS_ERROR 0x07
+#define TAC_AUTHEN_STATUS_FOLLOW 0x21
+
+__BEGIN_DECLS
+int tac_add_server(struct tac_handle *,
+ const char *, int, const char *, int, int);
+void tac_close(struct tac_handle *);
+int tac_config(struct tac_handle *, const char *);
+int tac_create_authen(struct tac_handle *, int, int, int);
+void *tac_get_data(struct tac_handle *, size_t *);
+char *tac_get_msg(struct tac_handle *);
+struct tac_handle *tac_open(void);
+int tac_send_authen(struct tac_handle *);
+int tac_set_data(struct tac_handle *,
+ const void *, size_t);
+int tac_set_msg(struct tac_handle *, const char *);
+int tac_set_port(struct tac_handle *, const char *);
+int tac_set_priv(struct tac_handle *, int);
+int tac_set_rem_addr(struct tac_handle *, const char *);
+int tac_set_user(struct tac_handle *, const char *);
+const char *tac_strerror(struct tac_handle *);
+__END_DECLS
+
+#endif /* _TACLIB_H_ */
diff --git a/lib/libtacplus/taclib_private.h b/lib/libtacplus/taclib_private.h
new file mode 100644
index 0000000..830fc92
--- /dev/null
+++ b/lib/libtacplus/taclib_private.h
@@ -0,0 +1,152 @@
+/*-
+ * Copyright 1998 Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef TACLIB_PRIVATE_H
+#define TACLIB_PRIVATE_H
+
+#include "taclib.h"
+
+/* Defaults */
+#define PATH_TACPLUS_CONF "/etc/tacplus.conf"
+#define TACPLUS_PORT 49
+#define TIMEOUT 3 /* In seconds */
+
+/* Limits */
+#define BODYSIZE 8150 /* Maximum message body size */
+#define ERRSIZE 128 /* Maximum error message length */
+#define MAXCONFLINE 1024 /* Maximum config file line length */
+#define MAXSERVERS 10 /* Maximum number of servers to try */
+
+/* Protocol constants. */
+#define HDRSIZE 12 /* Size of message header */
+
+/* Protocol version number */
+#define TAC_VER_MAJOR 0xc /* Major version number */
+
+/* Protocol packet types */
+#define TAC_AUTHEN 0x01 /* Authentication */
+#define TAC_AUTHOR 0x02 /* Authorization */
+#define TAC_ACCT 0x03 /* Accouting */
+
+/* Protocol header flags */
+#define TAC_UNENCRYPTED 0x01
+#define TAC_SINGLE_CONNECT 0x04
+
+struct tac_server {
+ struct sockaddr_in addr; /* Address of server */
+ char *secret; /* Shared secret */
+ int timeout; /* Timeout in seconds */
+ int flags;
+};
+
+/*
+ * An optional string of bytes specified by the client for inclusion in
+ * a request. The data is always a dynamically allocated copy that
+ * belongs to the library. It is copied into the request packet just
+ * before sending the request.
+ */
+struct clnt_str {
+ void *data;
+ size_t len;
+};
+
+/*
+ * An optional string of bytes from a server response. The data resides
+ * in the response packet itself, and must not be freed.
+ */
+struct srvr_str {
+ const void *data;
+ size_t len;
+};
+
+struct tac_authen_start {
+ u_int8_t action;
+ u_int8_t priv_lvl;
+ u_int8_t authen_type;
+ u_int8_t service;
+ u_int8_t user_len;
+ u_int8_t port_len;
+ u_int8_t rem_addr_len;
+ u_int8_t data_len;
+ unsigned char rest[1];
+};
+
+struct tac_authen_reply {
+ u_int8_t status;
+ u_int8_t flags;
+ u_int16_t msg_len;
+ u_int16_t data_len;
+ unsigned char rest[1];
+};
+
+struct tac_authen_cont {
+ u_int16_t user_msg_len;
+ u_int16_t data_len;
+ u_int8_t flags;
+ unsigned char rest[1];
+};
+
+struct tac_msg {
+ u_int8_t version;
+ u_int8_t type;
+ u_int8_t seq_no;
+ u_int8_t flags;
+ u_int8_t session_id[4];
+ u_int32_t length;
+ union {
+ struct tac_authen_start authen_start;
+ struct tac_authen_reply authen_reply;
+ struct tac_authen_cont authen_cont;
+ unsigned char body[BODYSIZE];
+ } u;
+};
+
+struct tac_handle {
+ int fd; /* Socket file descriptor */
+ struct tac_server servers[MAXSERVERS]; /* Servers to contact */
+ int num_servers; /* Number of valid server entries */
+ int cur_server; /* Server we are currently using */
+ int single_connect; /* Use a single connection */
+ int last_seq_no;
+ char errmsg[ERRSIZE]; /* Most recent error message */
+
+ struct clnt_str user;
+ struct clnt_str port;
+ struct clnt_str rem_addr;
+ struct clnt_str data;
+ struct clnt_str user_msg;
+
+ struct tac_msg request;
+ struct tac_msg response;
+
+ int srvr_pos; /* Scan position in response body */
+ struct srvr_str srvr_msg;
+ struct srvr_str srvr_data;
+};
+
+#endif
diff --git a/lib/libtacplus/tacplus.conf.5 b/lib/libtacplus/tacplus.conf.5
new file mode 100644
index 0000000..a61da84
--- /dev/null
+++ b/lib/libtacplus/tacplus.conf.5
@@ -0,0 +1,114 @@
+.\" Copyright 1998 Juniper Networks, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 29, 1998
+.Dt TACPLUS.CONF 5
+.Os FreeBSD
+.Sh NAME
+.Nm tacplus.conf
+.Nd TACACS+ client configuration file
+.Sh SYNOPSIS
+.Pa /etc/tacplus.conf
+.Sh DESCRIPTION
+.Nm
+contains the information necessary to configure the TACACS+ client
+library. It is parsed by
+.Xr tac_config 3 .
+The file contains one or more lines of text, each describing a
+single TACACS+ server which is to be used by the library. Leading
+white space is ignored, as are empty lines and lines containing
+only comments.
+.Pp
+A TACACS+ server is described by two to four fields on a line. The
+fields are separated by white space. The
+.Ql #
+character at the beginning of a field begins a comment, which extends
+to the end of the line. A field may be enclosed in double quotes,
+in which case it may contain white space and/or begin with the
+.Ql #
+character. Within a quoted string, the double quote character can
+be represented by
+.Ql \e\&" ,
+and the backslash can be represented by
+.Ql \e\e .
+No other escape sequences are supported.
+.Pp
+The first field specifies
+the server host, either as a fully qualified domain name or as a
+dotted-quad IP address. The host may optionally be followed by a
+.Ql \&:
+and a numeric port number, without intervening white space. If the
+port specification is omitted, it defaults to 49, the standard TACACS+
+port.
+.Pp
+The second field contains the shared secret, which should be known
+only to the client and server hosts. It is an arbitrary string
+of characters, though it must be enclosed in double quotes if it
+contains white space or is empty. An empty secret disables the
+normal encryption mechanism, causing all data to cross the network in
+cleartext.
+.Pp
+The third field contains a decimal integer specifying the timeout
+in seconds for communicating with the server. The timeout applies
+separately to each connect, write, and read operation. If this field
+is omitted, it defaults to 3 seconds.
+.Pp
+The optional fourth field may contain the string
+.Ql single-connection .
+If this option is included, the library will attempt to negotiate
+with the server to keep the TCP connection open for multiple
+sessions. Some older TACACS+ servers become confused if this option
+is specified.
+.Pp
+Up to 10 TACACS+ servers may be specified. The servers are tried in
+order, until a valid response is received or the list is exhausted.
+.Pp
+The standard location for this file is
+.Pa /etc/tacplus.conf .
+An alternate pathname may be specified in the call to
+.Xr tac_config 3 .
+Since the file contains sensitive information in the form of the
+shared secrets, it should not be readable except by root.
+.Sh FILES
+.Pa /etc/tacplus.conf
+.Sh EXAMPLES
+.Bd -literal
+# A simple entry using all the defaults:
+tacserver.domain.com OurLittleSecret
+
+# A server using a non-standard port, with an increased timeout and
+# the "single-connection" option.
+auth.domain.com:4333 "Don't tell!!" 15 single-connection
+
+# A server specified by its IP address:
+192.168.27.81 $X*#..38947ax-+=
+.Ed
+.Sh SEE ALSO
+.Xr libtacplus 3
+.Sh AUTHORS
+This documentation was written by
+.An John Polstra ,
+and donated to the FreeBSD project by Juniper Networks, Inc.
OpenPOWER on IntegriCloud