From 3119595d44dc868d838d64af9c12dc876c059ab9 Mon Sep 17 00:00:00 2001 From: jdp Date: Fri, 13 Nov 1998 00:54:26 +0000 Subject: Initial import of TACACS+ client library donated by Juniper Networks, Inc. --- lib/libtacplus/Makefile | 41 ++ lib/libtacplus/libtacplus.3 | 347 +++++++++++++ lib/libtacplus/taclib.c | 1053 +++++++++++++++++++++++++++++++++++++++ lib/libtacplus/taclib.h | 105 ++++ lib/libtacplus/taclib_private.h | 152 ++++++ lib/libtacplus/tacplus.conf.5 | 114 +++++ 6 files changed, 1812 insertions(+) create mode 100644 lib/libtacplus/Makefile create mode 100644 lib/libtacplus/libtacplus.3 create mode 100644 lib/libtacplus/taclib.c create mode 100644 lib/libtacplus/taclib.h create mode 100644 lib/libtacplus/taclib_private.h create mode 100644 lib/libtacplus/tacplus.conf.5 (limited to 'lib') 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 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 +.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 +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +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. -- cgit v1.1