summaryrefslogtreecommitdiffstats
path: root/lib/libtacplus/taclib.c
diff options
context:
space:
mode:
authorpst <pst@FreeBSD.org>2002-09-25 23:18:51 +0000
committerpst <pst@FreeBSD.org>2002-09-25 23:18:51 +0000
commit91648a15549c3af714c84517495e7f1093c53a07 (patch)
treec44b602a3373c774dcba3c1d92b44d5c442d3c92 /lib/libtacplus/taclib.c
parent436cfa19831cbf8a0e28bcc3fba9b09701e8c0ae (diff)
downloadFreeBSD-src-91648a15549c3af714c84517495e7f1093c53a07.zip
FreeBSD-src-91648a15549c3af714c84517495e7f1093c53a07.tar.gz
Enhance TACACS+ library to fully support authorization requests in
addition to existing authentication. No change to the existing APIs to preseve both binary and API compatibility, so I am not inclined to bump the library version number unless someone thinks this is necessary. Submitted by: Paul Fraley <fraley@juniper.net> MFC after: 2 weeks
Diffstat (limited to 'lib/libtacplus/taclib.c')
-rw-r--r--lib/libtacplus/taclib.c366
1 files changed, 315 insertions, 51 deletions
diff --git a/lib/libtacplus/taclib.c b/lib/libtacplus/taclib.c
index 861d2d0..d99995d 100644
--- a/lib/libtacplus/taclib.c
+++ b/lib/libtacplus/taclib.c
@@ -1,5 +1,5 @@
/*-
- * Copyright 1998 Juniper Networks, Inc.
+ * Copyright (c) 1998, 2001, 2002, Juniper Networks, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -51,7 +51,7 @@ 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 int protocol_version(int, 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 *);
@@ -63,8 +63,8 @@ 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 int get_srvr_str(struct tac_handle *, const char *,
+ 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,
@@ -76,6 +76,8 @@ 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 *);
+static void clear_srvr_avs(struct tac_handle *);
+static void create_msg(struct tac_handle *, int, int, int);
/*
* Append some optional data to the current request, and store its
@@ -138,40 +140,86 @@ add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
}
static int
-authen_version(int action, int type)
+protocol_version(int msg_type, int var, int type)
{
- int minor;
-
- switch (action) {
-
- case TAC_AUTHEN_LOGIN:
- switch (type) {
+ int minor;
+
+ switch (msg_type) {
+ case TAC_AUTHEN:
+ /* 'var' represents the 'action' */
+ switch (var) {
+ 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;
- case TAC_AUTHEN_TYPE_PAP:
- case TAC_AUTHEN_TYPE_CHAP:
- case TAC_AUTHEN_TYPE_MSCHAP:
- case TAC_AUTHEN_TYPE_ARAP:
- minor = 1;
+ default:
+ minor = 0;
break;
+ }
+ break;
+
+ case TAC_AUTHEN_SENDAUTH:
+ minor = 1;
+ break;
default:
- minor = 0;
- break;
- }
+ minor = 0;
break;
+ };
+ break;
+
+ case TAC_AUTHOR:
+ /* 'var' represents the 'method' */
+ switch (var) {
+ /*
+ * When new authentication methods are added, include 'method'
+ * in determining the value of 'minor'. At this point, all
+ * methods defined in this implementation (see "Authorization
+ * authentication methods" in taclib.h) are minor version 0
+ * Not all types, however, indicate minor version 0.
+ */
+ case TAC_AUTHEN_METH_NOT_SET:
+ case TAC_AUTHEN_METH_NONE:
+ case TAC_AUTHEN_METH_KRB5:
+ case TAC_AUTHEN_METH_LINE:
+ case TAC_AUTHEN_METH_ENABLE:
+ case TAC_AUTHEN_METH_LOCAL:
+ case TAC_AUTHEN_METH_TACACSPLUS:
+ case TAC_AUTHEN_METH_RCMD:
+ 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;
- case TAC_AUTHEN_SENDAUTH:
- minor = 1;
+ default:
+ minor = 0;
+ break;
+ }
+ break;
+ default:
+ minor = 0;
break;
+ }
+ break;
default:
- minor = 0;
- break;
- };
+ minor = 0;
+ break;
+ }
- return TAC_VER_MAJOR << 4 | minor;
+ return TAC_VER_MAJOR << 4 | minor;
}
+
static void
close_connection(struct tac_handle *h)
{
@@ -393,18 +441,27 @@ gen_session_id(struct tac_msg *msg)
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");
+ int len;
+
+ len = ntohl(h->response.length);
+
+ if (h->srvr_pos != len) {
+ generr(h, "Invalid length field in response "
+ "from server: end expected at %u, response length %u",
+ h->srvr_pos, len);
return -1;
}
return 0;
}
static int
-get_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len)
+get_srvr_str(struct tac_handle *h, const char *field,
+ 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");
+ generr(h, "Invalid length field in %s response from server "
+ "(%lu > %lu)", field, (u_long)(h->srvr_pos + len),
+ (u_long)ntohl(h->response.length));
return -1;
}
ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
@@ -489,7 +546,7 @@ recv_msg(struct tac_handle *h)
{
struct timeval deadline;
struct tac_msg *msg;
- size_t len;
+ u_int32_t len;
msg = &h->response;
gettimeofday(&deadline, NULL);
@@ -504,16 +561,21 @@ recv_msg(struct tac_handle *h)
return -1;
}
if (msg->type != h->request.type) {
- generr(h, "Invalid type in received message");
+ generr(h, "Invalid type in received message"
+ " (got %u, expected %u)",
+ msg->type, h->request.type);
return -1;
}
len = ntohl(msg->length);
if (len > BODYSIZE) {
- generr(h, "Received message too large");
+ generr(h, "Received message too large (%u > %u)",
+ len, BODYSIZE);
return -1;
}
if (msg->seq_no != ++h->last_seq_no) {
- generr(h, "Invalid sequence number in received message");
+ generr(h, "Invalid sequence number in received message"
+ " (got %u, expected %u)",
+ msg->seq_no, h->last_seq_no);
return -1;
}
@@ -564,15 +626,15 @@ send_msg(struct tac_handle *h)
return -1;
}
+ if (establish_connection(h) == -1)
+ 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
@@ -734,7 +796,7 @@ tac_add_server(struct tac_handle *h, const char *host, int port,
void
tac_close(struct tac_handle *h)
{
- int srv;
+ int i, srv;
if (h->fd != -1)
close(h->fd);
@@ -748,6 +810,11 @@ tac_close(struct tac_handle *h)
free_str(&h->rem_addr);
free_str(&h->data);
free_str(&h->user_msg);
+ for (i=0; i<MAXAVPAIRS; i++)
+ free_str(&(h->avs[i]));
+
+ /* Clear everything else before freeing memory */
+ memset(h, 0, sizeof(struct tac_handle));
free(h);
}
@@ -786,7 +853,7 @@ tac_config(struct tac_handle *h, const char *path)
len = strlen(buf);
/* We know len > 0, else fgets would have returned NULL. */
if (buf[len - 1] != '\n') {
- if (len == sizeof buf - 1)
+ if (len >= sizeof buf - 1)
generr(h, "%s:%d: line too long", path,
linenum);
else
@@ -872,30 +939,56 @@ tac_config(struct tac_handle *h, const char *path)
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;
+ create_msg(h, TAC_AUTHEN, action, type);
- msg = &h->request;
- msg->type = TAC_AUTHEN;
- msg->version = authen_version(action, type);
- msg->flags = 0;
-
- as = &msg->u.authen_start;
+ as = &h->request.u.authen_start;
as->action = action;
as->priv_lvl = TAC_PRIV_LVL_USER;
as->authen_type = type;
as->service = service;
+ return 0;
+}
+
+int
+tac_create_author(struct tac_handle *h, int method, int type, int service)
+{
+ struct tac_author_request *areq;
+
+ create_msg(h, TAC_AUTHOR, method, type);
+
+ areq = &h->request.u.author_request;
+ areq->authen_meth = method;
+ areq->priv_lvl = TAC_PRIV_LVL_USER;
+ areq->authen_type = type;
+ areq->service = service;
+
+ return 0;
+}
+
+static void
+create_msg(struct tac_handle *h, int msg_type, int var, int type)
+{
+ struct tac_msg *msg;
+ int i;
+
+ h->last_seq_no = 0;
+
+ msg = &h->request;
+ msg->type = msg_type;
+ msg->version = protocol_version(msg_type, var, type);
+ msg->flags = 0; /* encrypted packet body */
+
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;
+ for (i=0; i<MAXAVPAIRS; i++)
+ free_str(&(h->avs[i]));
}
void *
@@ -907,7 +1000,7 @@ tac_get_data(struct tac_handle *h, size_t *len)
char *
tac_get_msg(struct tac_handle *h)
{
- return (char *)dup_str(h, &h->srvr_msg, NULL);
+ return dup_str(h, &h->srvr_msg, NULL);
}
/*
@@ -918,6 +1011,7 @@ tac_get_msg(struct tac_handle *h)
struct tac_handle *
tac_open(void)
{
+ int i;
struct tac_handle *h;
h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
@@ -931,6 +1025,10 @@ tac_open(void)
init_clnt_str(&h->rem_addr);
init_clnt_str(&h->data);
init_clnt_str(&h->user_msg);
+ for (i=0; i<MAXAVPAIRS; i++) {
+ init_clnt_str(&(h->avs[i]));
+ init_srvr_str(&(h->srvr_avs[i]));
+ }
init_srvr_str(&h->srvr_msg);
init_srvr_str(&h->srvr_data);
srandomdev();
@@ -943,6 +1041,9 @@ tac_send_authen(struct tac_handle *h)
{
struct tac_authen_reply *ar;
+ if (h->num_servers == 0)
+ return -1;
+
if (h->last_seq_no == 0) { /* Authentication START packet */
struct tac_authen_start *as;
@@ -973,8 +1074,8 @@ tac_send_authen(struct tac_handle *h)
/* 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 ||
+ if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
+ get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
get_srvr_end(h) == -1)
return -1;
@@ -988,6 +1089,76 @@ tac_send_authen(struct tac_handle *h)
}
int
+tac_send_author(struct tac_handle *h)
+{
+ int i, current;
+ char dbgstr[64];
+ struct tac_author_request *areq = &h->request.u.author_request;
+ struct tac_author_response *ares = &h->response.u.author_response;
+
+ h->request.length =
+ htonl(offsetof(struct tac_author_request, rest[0]));
+
+ /* Count each specified AV pair */
+ for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
+ if (h->avs[i].len && h->avs[i].data)
+ areq->av_cnt++;
+
+ /*
+ * Each AV size is a byte starting right after 'av_cnt'. Update the
+ * offset to include these AV sizes.
+ */
+ h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
+
+ /* Now add the string arguments from 'h' */
+ if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
+ add_str_8(h, &areq->port_len, &h->port) == -1 ||
+ add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
+ return -1;
+
+ /* Add each AV pair, the size of each placed in areq->rest[current] */
+ for (current=0, i=0; i<MAXAVPAIRS; i++) {
+ if (h->avs[i].len && h->avs[i].data) {
+ if (add_str_8(h, &areq->rest[current++],
+ &(h->avs[i])) == -1)
+ return -1;
+ }
+ }
+
+ /* Send the message and retrieve the reply. */
+ if (send_msg(h) == -1 || recv_msg(h) == -1)
+ return -1;
+
+ /* Update the offset in the response packet based on av pairs count */
+ h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
+ ares->av_cnt;
+
+ /* Scan the optional fields in the response. */
+ if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
+ get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
+ return -1;
+
+ /* Get each AV pair (just setting pointers, not malloc'ing) */
+ clear_srvr_avs(h);
+ for (i=0; i<ares->av_cnt; i++) {
+ snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
+ if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]),
+ ares->rest[i]) == -1)
+ return -1;
+ }
+
+ /* Should have ended up at the end */
+ if (get_srvr_end(h) == -1)
+ return -1;
+
+ /* Sanity checks */
+ if (!h->single_connect)
+ close_connection(h);
+
+ return ares->av_cnt << 8 | ares->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);
@@ -1028,6 +1199,99 @@ tac_set_user(struct tac_handle *h, const char *user)
return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
}
+int
+tac_set_av(struct tac_handle *h, u_int index, const char *av)
+{
+ if (index >= MAXAVPAIRS)
+ return -1;
+ return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
+}
+
+char *
+tac_get_av(struct tac_handle *h, u_int index)
+{
+ if (index >= MAXAVPAIRS)
+ return NULL;
+ return dup_str(h, &(h->srvr_avs[index]), NULL);
+}
+
+char *
+tac_get_av_value(struct tac_handle *h, const char *attribute)
+{
+ int i, len;
+ const char *ch, *end;
+ const char *candidate;
+ int candidate_len;
+ int found_seperator;
+ struct srvr_str srvr;
+
+ if (attribute == NULL || ((len = strlen(attribute)) == 0))
+ return NULL;
+
+ for (i=0; i<MAXAVPAIRS; i++) {
+ candidate = h->srvr_avs[i].data;
+ candidate_len = h->srvr_avs[i].len;
+
+ /*
+ * Valid 'srvr_avs' guaranteed to be contiguous starting at
+ * index 0 (not necessarily the case with 'avs'). Break out
+ * when the "end" of the list has been reached.
+ */
+ if (!candidate)
+ break;
+
+ if (len < candidate_len &&
+ !strncmp(candidate, attribute, len)) {
+
+ ch = candidate + len;
+ end = candidate + candidate_len;
+
+ /*
+ * Sift out the white space between A and V (should not
+ * be any, but don't trust implementation of server...)
+ */
+ found_seperator = 0;
+ while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
+ *ch == '\t') && ch != end) {
+ if (*ch == '=' || *ch == '*')
+ found_seperator++;
+ ch++;
+ }
+
+ /*
+ * Note:
+ * The case of 'attribute' == "foo" and
+ * h->srvr_avs[0] = "foobie=var1"
+ * h->srvr_avs[1] = "foo=var2"
+ * is handled.
+ */
+ if (found_seperator == 1 && ch != end) {
+ srvr.len = end - ch;
+ srvr.data = ch;
+ return dup_str(h, &srvr, NULL);
+ }
+ }
+ }
+ return NULL;
+}
+
+void
+tac_clear_avs(struct tac_handle *h)
+{
+ int i;
+ for (i=0; i<MAXAVPAIRS; i++)
+ save_str(h, &(h->avs[i]), NULL, 0);
+}
+
+static void
+clear_srvr_avs(struct tac_handle *h)
+{
+ int i;
+ for (i=0; i<MAXAVPAIRS; i++)
+ init_srvr_str(&(h->srvr_avs[i]));
+}
+
+
const char *
tac_strerror(struct tac_handle *h)
{
OpenPOWER on IntegriCloud