Changeset View
Standalone View
lib/libtacplus/taclib.c
Show All 30 Lines | |||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <arpa/inet.h> | #include <arpa/inet.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <ctype.h> | |||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <md5.h> | #include <md5.h> | ||||
#include <netdb.h> | #include <netdb.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <stddef.h> | #include <stddef.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <security/pam_appl.h> | |||||
#include <security/openpam.h> | |||||
#include "taclib_private.h" | #include "taclib_private.h" | ||||
static int add_str_8(struct tac_handle *, u_int8_t *, | static int add_str_8(struct tac_handle *, u_int8_t *, | ||||
struct clnt_str *); | struct tac_str *); | ||||
static int add_str_16(struct tac_handle *, u_int16_t *, | static int add_str_16(struct tac_handle *, u_int16_t *, | ||||
struct clnt_str *); | struct tac_str *); | ||||
static int protocol_version(int, int, int); | static int protocol_version(int, int, int); | ||||
static void close_connection(struct tac_handle *); | static void close_connection(struct tac_handle *); | ||||
static int conn_server(struct tac_handle *); | static int conn_server(struct tac_handle *); | ||||
static void crypt_msg(struct tac_handle *, struct tac_msg *); | static void crypt_msg(struct tac_handle *, struct tac_msg *); | ||||
static void *dup_str(struct tac_handle *, const struct srvr_str *, | static void *dup_str(struct tac_handle *, const struct tac_str *, | ||||
size_t *); | size_t *); | ||||
static int establish_connection(struct tac_handle *); | static int establish_connection(struct tac_handle *); | ||||
static void free_str(struct clnt_str *); | static void free_str(struct tac_str *); | ||||
static void generr(struct tac_handle *, const char *, ...) | static void generr(struct tac_handle *, const char *, ...) | ||||
__printflike(2, 3); | __printflike(2, 3); | ||||
static void gen_session_id(struct tac_msg *); | static void gen_session_id(struct tac_msg *); | ||||
static int get_srvr_end(struct tac_handle *); | static int get_srvr_end(struct tac_handle *); | ||||
static int get_srvr_str(struct tac_handle *, const char *, | static int get_str(struct tac_handle *, const char *, | ||||
struct srvr_str *, size_t); | struct tac_str *, size_t); | ||||
static void init_clnt_str(struct clnt_str *); | static void init_str(struct tac_str *); | ||||
static void init_srvr_str(struct srvr_str *); | |||||
static int read_timed(struct tac_handle *, void *, size_t, | static int read_timed(struct tac_handle *, void *, size_t, | ||||
const struct timeval *); | const struct timeval *); | ||||
static int recv_msg(struct tac_handle *); | static int recv_msg(struct tac_handle *); | ||||
static int save_str(struct tac_handle *, struct clnt_str *, | static int save_str(struct tac_handle *, struct tac_str *, | ||||
const void *, size_t); | const void *, size_t); | ||||
static int send_msg(struct tac_handle *); | static int send_msg(struct tac_handle *); | ||||
static int split(char *, char *[], int, char *, size_t); | static int split(char *, char *[], int, char *, size_t); | ||||
static void *xmalloc(struct tac_handle *, size_t); | static void *xmalloc(struct tac_handle *, size_t); | ||||
static char *xstrdup(struct tac_handle *, const char *); | static char *xstrdup(struct tac_handle *, const char *); | ||||
static void clear_srvr_avs(struct tac_handle *); | static void clear_srvr_avs(struct tac_handle *); | ||||
static void create_msg(struct tac_handle *, int, int, int); | static void create_msg(struct tac_handle *, int, int, int); | ||||
/* | /* | ||||
* Append some optional data to the current request, and store its | * Append some optional data to the current request, and store its | ||||
* length into the 8-bit field referenced by "fld". Returns 0 on | * length into the 8-bit field referenced by "fld". Returns 0 on | ||||
* success, or -1 on failure. | * success, or -1 on failure. | ||||
* | * | ||||
* This function also frees the "cs" string data and initializes it | * This function also frees the "cs" string data and initializes it | ||||
* for the next time. | * for the next time. | ||||
*/ | */ | ||||
static int | static int | ||||
add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs) | add_str_8(struct tac_handle *h, u_int8_t *fld, struct tac_str *cs) | ||||
{ | { | ||||
u_int16_t len; | u_int16_t len; | ||||
if (add_str_16(h, &len, cs) == -1) | if (add_str_16(h, &len, cs) == -1) | ||||
return -1; | return -1; | ||||
len = ntohs(len); | len = ntohs(len); | ||||
if (len > 0xff) { | if (len > 0xff) { | ||||
generr(h, "Field too long"); | generr(h, "Field too long"); | ||||
return -1; | return -1; | ||||
} | } | ||||
*fld = len; | *fld = len; | ||||
return 0; | return 0; | ||||
} | } | ||||
/* | /* | ||||
* Append some optional data to the current request, and store its | * Append some optional data to the current request, and store its | ||||
* length into the 16-bit field (network byte order) referenced by | * length into the 16-bit field (network byte order) referenced by | ||||
* "fld". Returns 0 on success, or -1 on failure. | * "fld". Returns 0 on success, or -1 on failure. | ||||
* | * | ||||
* This function also frees the "cs" string data and initializes it | * This function also frees the "cs" string data and initializes it | ||||
* for the next time. | * for the next time. | ||||
*/ | */ | ||||
static int | static int | ||||
add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs) | add_str_16(struct tac_handle *h, u_int16_t *fld, struct tac_str *cs) | ||||
{ | { | ||||
size_t len; | size_t len; | ||||
len = cs->len; | len = cs->len; | ||||
if (cs->data == NULL) | if (cs->data == NULL) | ||||
len = 0; | len = 0; | ||||
if (len != 0) { | if (len != 0) { | ||||
int offset; | int offset; | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | if (h->fd != -1) { | ||||
close(h->fd); | close(h->fd); | ||||
h->fd = -1; | h->fd = -1; | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
conn_server(struct tac_handle *h) | conn_server(struct tac_handle *h) | ||||
{ | { | ||||
const struct tac_server *srvp = &h->servers[h->cur_server]; | struct tac_server *srvp = &h->servers[h->cur_server]; | ||||
int flags; | int flags; | ||||
if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { | if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { | ||||
generr(h, "Cannot create socket: %s", strerror(errno)); | generr(h, "Cannot create socket: %s", strerror(errno)); | ||||
return -1; | return -1; | ||||
} | } | ||||
if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 || | if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 || | ||||
fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) { | fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) { | ||||
▲ Show 20 Lines • Show All 104 Lines • ▼ Show 20 Lines | |||||
/* | /* | ||||
* Return a dynamically allocated copy of the given server string. | * Return a dynamically allocated copy of the given server string. | ||||
* The copy is null-terminated. If "len" is non-NULL, the length of | * The copy is null-terminated. If "len" is non-NULL, the length of | ||||
* the string (excluding the terminating null byte) is stored via it. | * the string (excluding the terminating null byte) is stored via it. | ||||
* Returns NULL on failure. Empty strings are still allocated even | * Returns NULL on failure. Empty strings are still allocated even | ||||
* though they have no content. | * though they have no content. | ||||
*/ | */ | ||||
static void * | static void * | ||||
dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len) | dup_str(struct tac_handle *h, const struct tac_str *ss, size_t *len) | ||||
{ | { | ||||
unsigned char *p; | unsigned char *p; | ||||
if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL) | if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL) | ||||
return NULL; | return NULL; | ||||
if (ss->data != NULL && ss->len != 0) | if (ss->data != NULL && ss->len != 0) | ||||
memcpy(p, ss->data, ss->len); | memcpy(p, ss->data, ss->len); | ||||
p[ss->len] = '\0'; | p[ss->len] = '\0'; | ||||
Show All 30 Lines | establish_connection(struct tac_handle *h) | ||||
/* Just return whatever error was last reported by conn_server(). */ | /* Just return whatever error was last reported by conn_server(). */ | ||||
return -1; | return -1; | ||||
} | } | ||||
/* | /* | ||||
* Free a client string, obliterating its contents first for security. | * Free a client string, obliterating its contents first for security. | ||||
*/ | */ | ||||
static void | static void | ||||
free_str(struct clnt_str *cs) | free_str(struct tac_str *cs) | ||||
{ | { | ||||
if (cs->data != NULL) { | if (cs->data != NULL) { | ||||
memset(cs->data, 0, cs->len); | memset_s(cs->data, cs->len, 0, cs->len); | ||||
markj: Presumably this should be explicit_bzero() or memset_s()? | |||||
free(cs->data); | free(cs->data); | ||||
cs->data = NULL; | cs->data = NULL; | ||||
cs->len = 0; | cs->len = 0; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
generr(struct tac_handle *h, const char *format, ...) | generr(struct tac_handle *h, const char *format, ...) | ||||
Show All 34 Lines | generr(h, "Invalid length field in response " | ||||
"from server: end expected at %u, response length %u", | "from server: end expected at %u, response length %u", | ||||
h->srvr_pos, len); | h->srvr_pos, len); | ||||
return -1; | return -1; | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
get_srvr_str(struct tac_handle *h, const char *field, | get_str(struct tac_handle *h, const char *field, | ||||
struct srvr_str *ss, size_t len) | struct tac_str *ss, size_t len) | ||||
{ | { | ||||
if (h->srvr_pos + len > ntohl(h->response.length)) { | if (h->srvr_pos + len > ntohl(h->response.length)) { | ||||
generr(h, "Invalid length field in %s response from server " | generr(h, "Invalid length field in %s response from server " | ||||
"(%lu > %lu)", field, (u_long)(h->srvr_pos + len), | "(%lu > %lu)", field, (u_long)(h->srvr_pos + len), | ||||
(u_long)ntohl(h->response.length)); | (u_long)ntohl(h->response.length)); | ||||
return -1; | return -1; | ||||
} | } | ||||
ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL; | ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL; | ||||
ss->len = len; | ss->len = len; | ||||
h->srvr_pos += len; | h->srvr_pos += len; | ||||
return 0; | return 0; | ||||
} | } | ||||
static void | static void | ||||
init_clnt_str(struct clnt_str *cs) | init_str(struct tac_str *cs) | ||||
{ | { | ||||
cs->data = NULL; | cs->data = NULL; | ||||
cs->len = 0; | cs->len = 0; | ||||
} | } | ||||
static void | |||||
init_srvr_str(struct srvr_str *ss) | |||||
{ | |||||
ss->data = NULL; | |||||
ss->len = 0; | |||||
} | |||||
static int | static int | ||||
read_timed(struct tac_handle *h, void *buf, size_t len, | read_timed(struct tac_handle *h, void *buf, size_t len, | ||||
const struct timeval *deadline) | const struct timeval *deadline) | ||||
{ | { | ||||
char *ptr; | char *ptr; | ||||
ptr = (char *)buf; | ptr = (char *)buf; | ||||
while (len > 0) { | while (len > 0) { | ||||
▲ Show 20 Lines • Show All 96 Lines • ▼ Show 20 Lines | recv_msg(struct tac_handle *h) | ||||
* to it. | * to it. | ||||
*/ | */ | ||||
if (!(msg->flags & TAC_SINGLE_CONNECT)) | if (!(msg->flags & TAC_SINGLE_CONNECT)) | ||||
h->single_connect = 0; | h->single_connect = 0; | ||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
save_str(struct tac_handle *h, struct clnt_str *cs, const void *data, | save_str(struct tac_handle *h, struct tac_str *cs, const void *data, | ||||
size_t len) | size_t len) | ||||
{ | { | ||||
free_str(cs); | free_str(cs); | ||||
if (data != NULL && len != 0) { | if (data != NULL && len != 0) { | ||||
if ((cs->data = xmalloc(h, len)) == NULL) | if ((cs->data = xmalloc(h, len)) == NULL) | ||||
return -1; | return -1; | ||||
cs->len = len; | cs->len = len; | ||||
memcpy(cs->data, data, len); | memcpy(cs->data, data, len); | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | while (len > 0) { | ||||
} else { | } else { | ||||
ptr += n; | ptr += n; | ||||
len -= n; | len -= n; | ||||
} | } | ||||
} | } | ||||
return 0; | 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 | static int | ||||
split(char *str, char *fields[], int maxfields, char *msg, size_t msglen) | tac_add_server_av(struct tac_handle *h, const char *host, int port, | ||||
const char *secret, int timeout, int flags, const char *const *avs) | |||||
{ | { | ||||
char *p; | struct tac_server *srvp; | ||||
const char *p; | |||||
size_t len; | |||||
int i; | 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) { | if (h->num_servers >= MAXSERVERS) { | ||||
generr(h, "Too many TACACS+ servers specified"); | generr(h, "Too many TACACS+ servers specified"); | ||||
return -1; | return -1; | ||||
} | } | ||||
srvp = &h->servers[h->num_servers]; | srvp = &h->servers[h->num_servers]; | ||||
memset(&srvp->addr, 0, sizeof srvp->addr); | memset(&srvp->addr, 0, sizeof srvp->addr); | ||||
srvp->addr.sin_len = sizeof srvp->addr; | srvp->addr.sin_len = sizeof srvp->addr; | ||||
srvp->addr.sin_family = AF_INET; | srvp->addr.sin_family = AF_INET; | ||||
if (!inet_aton(host, &srvp->addr.sin_addr)) { | if (!inet_aton(host, &srvp->addr.sin_addr)) { | ||||
struct hostent *hent; | struct hostent *hent; | ||||
if ((hent = gethostbyname(host)) == NULL) { | if ((hent = gethostbyname(host)) == NULL) { | ||||
generr(h, "%s: host not found", host); | generr(h, "%s: host not found", host); | ||||
return -1; | return -1; | ||||
} | } | ||||
memcpy(&srvp->addr.sin_addr, hent->h_addr, | memcpy(&srvp->addr.sin_addr, hent->h_addr, | ||||
sizeof srvp->addr.sin_addr); | sizeof srvp->addr.sin_addr); | ||||
} | } | ||||
srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT); | srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT); | ||||
if ((srvp->secret = xstrdup(h, secret)) == NULL) | if ((srvp->secret = xstrdup(h, secret)) == NULL) | ||||
return -1; | return -1; | ||||
srvp->timeout = timeout; | srvp->timeout = timeout; | ||||
srvp->flags = flags; | srvp->flags = flags; | ||||
srvp->navs = 0; | |||||
for (i = 0; avs[i] != NULL; i++) { | |||||
if (i >= MAXAVPAIRS) { | |||||
generr(h, "too many AV pairs"); | |||||
goto fail; | |||||
} | |||||
for (p = avs[i], len = 0; is_arg(*p); p++) | |||||
len++; | |||||
if (p == avs[i] || *p != '=') { | |||||
generr(h, "invalid AV pair %d", i); | |||||
goto fail; | |||||
} | |||||
while (*p++ != '\0') | |||||
Done Inline ActionsCan't you write len += strlen(p); instead of having this loop? markj: Can't you write `len += strlen(p);` instead of having this loop? | |||||
Done Inline ActionsThat's a matter of taste. des: That's a matter of taste. | |||||
len++; | |||||
Done Inline Actionsxstrdup() does not fail. markj: xstrdup() does not fail. | |||||
Done Inline ActionsYes it does. des: Yes it does. | |||||
if ((srvp->avs[i].data = xstrdup(h, avs[i])) == NULL) | |||||
goto fail; | |||||
Done Inline ActionsIs len supposed to include the =? It looks like it does not. markj: Is `len` supposed to include the `=`? It looks like it does not. | |||||
Done Inline ActionsIt is and it does. des: It is and it does. | |||||
srvp->avs[i].len = len; | |||||
Done Inline ActionsIs srvp->navs guaranteed to be initialized? It might be nice to reset it at the beginning of the loop regardless. markj: Is `srvp->navs` guaranteed to be initialized? It might be nice to reset it at the beginning of… | |||||
srvp->navs++; | |||||
} | |||||
h->num_servers++; | h->num_servers++; | ||||
return 0; | return 0; | ||||
fail: | |||||
memset_s(srvp->secret, strlen(srvp->secret), 0, strlen(srvp->secret)); | |||||
free(srvp->secret); | |||||
srvp->secret = NULL; | |||||
for (i = 0; i < srvp->navs; i++) { | |||||
free(srvp->avs[i].data); | |||||
srvp->avs[i].data = NULL; | |||||
srvp->avs[i].len = 0; | |||||
} | } | ||||
return -1; | |||||
} | |||||
int | |||||
tac_add_server(struct tac_handle *h, const char *host, int port, | |||||
const char *secret, int timeout, int flags) | |||||
{ | |||||
const char *const *avs = { NULL }; | |||||
return tac_add_server_av(h, host, port, secret, timeout, flags, avs); | |||||
} | |||||
void | void | ||||
tac_close(struct tac_handle *h) | tac_close(struct tac_handle *h) | ||||
{ | { | ||||
int i, srv; | int i, srv; | ||||
if (h->fd != -1) | if (h->fd != -1) | ||||
close(h->fd); | close(h->fd); | ||||
for (srv = 0; srv < h->num_servers; srv++) { | for (srv = 0; srv < h->num_servers; srv++) { | ||||
Show All 9 Lines | tac_close(struct tac_handle *h) | ||||
for (i=0; i<MAXAVPAIRS; i++) | for (i=0; i<MAXAVPAIRS; i++) | ||||
free_str(&(h->avs[i])); | free_str(&(h->avs[i])); | ||||
/* Clear everything else before freeing memory */ | /* Clear everything else before freeing memory */ | ||||
memset(h, 0, sizeof(struct tac_handle)); | memset(h, 0, sizeof(struct tac_handle)); | ||||
free(h); | free(h); | ||||
} | } | ||||
static void | |||||
freev(char **fields, int nfields) | |||||
{ | |||||
if (fields != NULL) { | |||||
while (nfields-- > 0) | |||||
free(fields[nfields]); | |||||
free(fields); | |||||
} | |||||
} | |||||
int | int | ||||
tac_config(struct tac_handle *h, const char *path) | tac_config(struct tac_handle *h, const char *path) | ||||
{ | { | ||||
FILE *fp; | FILE *fp; | ||||
char buf[MAXCONFLINE]; | char **fields; | ||||
int linenum; | int linenum, nfields; | ||||
int retval; | int retval; | ||||
if (path == NULL) | if (path == NULL) | ||||
path = PATH_TACPLUS_CONF; | path = PATH_TACPLUS_CONF; | ||||
if ((fp = fopen(path, "r")) == NULL) { | if ((fp = fopen(path, "r")) == NULL) { | ||||
generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); | generr(h, "Cannot open \"%s\": %s", path, strerror(errno)); | ||||
return -1; | return -1; | ||||
} | } | ||||
retval = 0; | retval = 0; | ||||
linenum = 0; | linenum = nfields = 0; | ||||
while (fgets(buf, sizeof buf, fp) != NULL) { | fields = NULL; | ||||
int len; | while ((fields = openpam_readlinev(fp, &linenum, &nfields)) != NULL) { | ||||
char *fields[4]; | |||||
int nfields; | |||||
char msg[ERRSIZE]; | |||||
char *host, *res; | char *host, *res; | ||||
char *port_str; | char *port_str; | ||||
char *secret; | char *secret; | ||||
char *timeout_str; | char *timeout_str; | ||||
char *options_str; | |||||
char *end; | char *end; | ||||
unsigned long timeout; | unsigned long timeout; | ||||
int port; | int port; | ||||
int options; | int options; | ||||
int i; | |||||
linenum++; | if (nfields == 0) { | ||||
len = strlen(buf); | freev(fields, nfields); | ||||
/* 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; | continue; | ||||
} | |||||
if (nfields < 2) { | if (nfields < 2) { | ||||
generr(h, "%s:%d: missing shared secret", path, | generr(h, "%s:%d: missing shared secret", path, | ||||
linenum); | linenum); | ||||
retval = -1; | retval = -1; | ||||
break; | break; | ||||
} | } | ||||
host = fields[0]; | host = fields[0]; | ||||
secret = fields[1]; | secret = fields[1]; | ||||
timeout_str = fields[2]; | |||||
options_str = fields[3]; | |||||
/* Parse and validate the fields. */ | /* Parse and validate the fields. */ | ||||
res = host; | res = host; | ||||
host = strsep(&res, ":"); | host = strsep(&res, ":"); | ||||
port_str = strsep(&res, ":"); | port_str = strsep(&res, ":"); | ||||
if (port_str != NULL) { | if (port_str != NULL) { | ||||
port = strtoul(port_str, &end, 10); | port = strtoul(port_str, &end, 10); | ||||
if (port_str[0] == '\0' || *end != '\0') { | if (port_str[0] == '\0' || *end != '\0') { | ||||
generr(h, "%s:%d: invalid port", path, | generr(h, "%s:%d: invalid port", path, | ||||
linenum); | linenum); | ||||
retval = -1; | retval = -1; | ||||
break; | break; | ||||
} | } | ||||
} else | } else | ||||
port = 0; | port = 0; | ||||
if (timeout_str != NULL) { | i = 2; | ||||
if (nfields > i && strlen(fields[i]) > 0 && | |||||
Done Inline ActionsSuppose strlen(fields[2]) == 0, i.e., the timeout was specified as "" in the config. Then we don't increment i, so below we'll compare "" with "single-connection", which doesn't seem to be the desired behaviour. In that case, I'd expect the timeout to be the default, and that we continue processing the next field. markj: Suppose `strlen(fields[2]) == 0`, i.e., the timeout was specified as `""` in the config. Then… | |||||
Done Inline ActionsThat is exactly the desired behavior. If you want the default timeout value, don't specify a timeout. des: That is exactly the desired behavior. If you want the default timeout value, don't specify a… | |||||
strspn(fields[i], "0123456789") == strlen(fields[i])) { | |||||
timeout_str = fields[i]; | |||||
timeout = strtoul(timeout_str, &end, 10); | timeout = strtoul(timeout_str, &end, 10); | ||||
if (timeout_str[0] == '\0' || *end != '\0') { | if (timeout_str[0] == '\0' || *end != '\0') { | ||||
generr(h, "%s:%d: invalid timeout", path, | generr(h, "%s:%d: invalid timeout", path, | ||||
linenum); | linenum); | ||||
retval = -1; | retval = -1; | ||||
break; | break; | ||||
} | } | ||||
i++; | |||||
} else | } else | ||||
timeout = TIMEOUT; | timeout = TIMEOUT; | ||||
options = 0; | options = 0; | ||||
if (options_str != NULL) { | if (nfields > i && | ||||
if (strcmp(options_str, "single-connection") == 0) | strcmp(fields[i], "single-connection") == 0) { | ||||
options |= TAC_SRVR_SINGLE_CONNECT; | options |= TAC_SRVR_SINGLE_CONNECT; | ||||
else { | i++; | ||||
generr(h, "%s:%d: invalid option \"%s\"", | |||||
path, linenum, options_str); | |||||
retval = -1; | |||||
break; | |||||
} | } | ||||
}; | if (tac_add_server_av(h, host, port, secret, timeout, | ||||
options, (const char *const *)(fields + i)) == -1) { | |||||
Done Inline ActionsIndentation here is wrong. markj: Indentation here is wrong. | |||||
if (tac_add_server(h, host, port, secret, timeout, | |||||
options) == -1) { | |||||
char msg[ERRSIZE]; | char msg[ERRSIZE]; | ||||
strcpy(msg, h->errmsg); | strcpy(msg, h->errmsg); | ||||
generr(h, "%s:%d: %s", path, linenum, msg); | generr(h, "%s:%d: %s", path, linenum, msg); | ||||
retval = -1; | retval = -1; | ||||
break; | break; | ||||
} | } | ||||
memset_s(secret, strlen(secret), 0, strlen(secret)); | |||||
freev(fields, nfields); | |||||
} | } | ||||
/* Clear out the buffer to wipe a possible copy of a shared secret */ | freev(fields, nfields); | ||||
memset(buf, 0, sizeof buf); | |||||
fclose(fp); | fclose(fp); | ||||
return retval; | return retval; | ||||
} | } | ||||
int | int | ||||
tac_create_authen(struct tac_handle *h, int action, int type, int service) | tac_create_authen(struct tac_handle *h, int action, int type, int service) | ||||
{ | { | ||||
struct tac_authen_start *as; | struct tac_authen_start *as; | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | tac_open(void) | ||||
struct tac_handle *h; | struct tac_handle *h; | ||||
h = (struct tac_handle *)malloc(sizeof(struct tac_handle)); | h = (struct tac_handle *)malloc(sizeof(struct tac_handle)); | ||||
if (h != NULL) { | if (h != NULL) { | ||||
h->fd = -1; | h->fd = -1; | ||||
h->num_servers = 0; | h->num_servers = 0; | ||||
h->cur_server = 0; | h->cur_server = 0; | ||||
h->errmsg[0] = '\0'; | h->errmsg[0] = '\0'; | ||||
init_clnt_str(&h->user); | init_str(&h->user); | ||||
init_clnt_str(&h->port); | init_str(&h->port); | ||||
init_clnt_str(&h->rem_addr); | init_str(&h->rem_addr); | ||||
init_clnt_str(&h->data); | init_str(&h->data); | ||||
init_clnt_str(&h->user_msg); | init_str(&h->user_msg); | ||||
for (i=0; i<MAXAVPAIRS; i++) { | for (i=0; i<MAXAVPAIRS; i++) { | ||||
init_clnt_str(&(h->avs[i])); | init_str(&(h->avs[i])); | ||||
init_srvr_str(&(h->srvr_avs[i])); | init_str(&(h->srvr_avs[i])); | ||||
} | } | ||||
init_srvr_str(&h->srvr_msg); | init_str(&h->srvr_msg); | ||||
init_srvr_str(&h->srvr_data); | init_str(&h->srvr_data); | ||||
} | } | ||||
return h; | return h; | ||||
} | } | ||||
int | int | ||||
tac_send_authen(struct tac_handle *h) | tac_send_authen(struct tac_handle *h) | ||||
{ | { | ||||
struct tac_authen_reply *ar; | struct tac_authen_reply *ar; | ||||
Show All 26 Lines | tac_send_authen(struct tac_handle *h) | ||||
/* Send the message and retrieve the reply. */ | /* Send the message and retrieve the reply. */ | ||||
if (send_msg(h) == -1 || recv_msg(h) == -1) | if (send_msg(h) == -1 || recv_msg(h) == -1) | ||||
return -1; | return -1; | ||||
/* Scan the optional fields in the reply. */ | /* Scan the optional fields in the reply. */ | ||||
ar = &h->response.u.authen_reply; | ar = &h->response.u.authen_reply; | ||||
h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]); | h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]); | ||||
if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 || | if (get_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_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 || | ||||
get_srvr_end(h) == -1) | get_srvr_end(h) == -1) | ||||
return -1; | return -1; | ||||
if (!h->single_connect && | if (!h->single_connect && | ||||
ar->status != TAC_AUTHEN_STATUS_GETDATA && | ar->status != TAC_AUTHEN_STATUS_GETDATA && | ||||
ar->status != TAC_AUTHEN_STATUS_GETUSER && | ar->status != TAC_AUTHEN_STATUS_GETUSER && | ||||
ar->status != TAC_AUTHEN_STATUS_GETPASS) | ar->status != TAC_AUTHEN_STATUS_GETPASS) | ||||
close_connection(h); | close_connection(h); | ||||
return ar->flags << 8 | ar->status; | return ar->flags << 8 | ar->status; | ||||
} | } | ||||
int | int | ||||
tac_send_author(struct tac_handle *h) | tac_send_author(struct tac_handle *h) | ||||
{ | { | ||||
int i, current; | int i, current; | ||||
char dbgstr[64]; | char dbgstr[64]; | ||||
struct tac_author_request *areq = &h->request.u.author_request; | struct tac_author_request *areq = &h->request.u.author_request; | ||||
struct tac_author_response *ares = &h->response.u.author_response; | struct tac_author_response *ares = &h->response.u.author_response; | ||||
struct tac_server *srvp; | |||||
h->request.length = | h->request.length = | ||||
htonl(offsetof(struct tac_author_request, rest[0])); | htonl(offsetof(struct tac_author_request, rest[0])); | ||||
/* Count each specified AV pair */ | /* Count each specified AV pair */ | ||||
for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++) | for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++) | ||||
if (h->avs[i].len && h->avs[i].data) | if (h->avs[i].len && h->avs[i].data) | ||||
areq->av_cnt++; | areq->av_cnt++; | ||||
Show All 17 Lines | if (h->avs[i].len && h->avs[i].data) { | ||||
&(h->avs[i])) == -1) | &(h->avs[i])) == -1) | ||||
return -1; | return -1; | ||||
} | } | ||||
} | } | ||||
/* Send the message and retrieve the reply. */ | /* Send the message and retrieve the reply. */ | ||||
if (send_msg(h) == -1 || recv_msg(h) == -1) | if (send_msg(h) == -1 || recv_msg(h) == -1) | ||||
return -1; | return -1; | ||||
srvp = &h->servers[h->cur_server]; | |||||
/* Update the offset in the response packet based on av pairs count */ | /* Update the offset in the response packet based on av pairs count */ | ||||
h->srvr_pos = offsetof(struct tac_author_response, rest[0]) + | h->srvr_pos = offsetof(struct tac_author_response, rest[0]) + | ||||
ares->av_cnt; | ares->av_cnt; | ||||
/* Scan the optional fields in the response. */ | /* Scan the optional fields in the response. */ | ||||
if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 || | if (get_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 || | ||||
get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1) | get_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1) | ||||
return -1; | return -1; | ||||
/* Get each AV pair (just setting pointers, not malloc'ing) */ | /* Get each AV pair (just setting pointers, not malloc'ing) */ | ||||
clear_srvr_avs(h); | clear_srvr_avs(h); | ||||
for (i=0; i<ares->av_cnt; i++) { | for (i=0; i<ares->av_cnt; i++) { | ||||
snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i); | snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i); | ||||
if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]), | if (get_str(h, dbgstr, &(h->srvr_avs[i]), | ||||
ares->rest[i]) == -1) | ares->rest[i]) == -1) | ||||
return -1; | return -1; | ||||
h->srvr_navs++; | |||||
} | } | ||||
/* Should have ended up at the end */ | /* Should have ended up at the end */ | ||||
if (get_srvr_end(h) == -1) | if (get_srvr_end(h) == -1) | ||||
return -1; | return -1; | ||||
/* Sanity checks */ | /* Sanity checks */ | ||||
if (!h->single_connect) | if (!h->single_connect) | ||||
close_connection(h); | close_connection(h); | ||||
return ares->av_cnt << 8 | ares->status; | return (h->srvr_navs + srvp->navs) << 8 | ares->status; | ||||
} | } | ||||
int | int | ||||
tac_send_acct(struct tac_handle *h) | tac_send_acct(struct tac_handle *h) | ||||
{ | { | ||||
register int i, current; | register int i, current; | ||||
struct tac_acct_start *as = &h->request.u.acct_start; | struct tac_acct_start *as = &h->request.u.acct_start; | ||||
struct tac_acct_reply *ar = &h->response.u.acct_reply; | struct tac_acct_reply *ar = &h->response.u.acct_reply; | ||||
Show All 17 Lines | if (h->avs[i].len && h->avs[i].data) | ||||
return -1; | return -1; | ||||
/* send */ | /* send */ | ||||
if (send_msg(h) == -1 || recv_msg(h) == -1) | if (send_msg(h) == -1 || recv_msg(h) == -1) | ||||
return -1; | return -1; | ||||
/* reply */ | /* reply */ | ||||
h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]); | h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]); | ||||
if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 || | if (get_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_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 || | ||||
get_srvr_end(h) == -1) | get_srvr_end(h) == -1) | ||||
return -1; | return -1; | ||||
/* Sanity checks */ | /* Sanity checks */ | ||||
if (!h->single_connect) | if (!h->single_connect) | ||||
close_connection(h); | close_connection(h); | ||||
return ar->status; | return ar->status; | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | tac_set_av(struct tac_handle *h, u_int index, const char *av) | ||||
if (index >= MAXAVPAIRS) | if (index >= MAXAVPAIRS) | ||||
return -1; | return -1; | ||||
return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0); | return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0); | ||||
} | } | ||||
char * | char * | ||||
tac_get_av(struct tac_handle *h, u_int index) | tac_get_av(struct tac_handle *h, u_int index) | ||||
{ | { | ||||
if (index >= MAXAVPAIRS) | struct tac_server *srvp; | ||||
if (index < h->srvr_navs) | |||||
return dup_str(h, &h->srvr_avs[index], NULL); | |||||
index -= h->srvr_navs; | |||||
srvp = &h->servers[h->cur_server]; | |||||
if (index < srvp->navs) | |||||
return xstrdup(h, srvp->avs[index].data); | |||||
return NULL; | return NULL; | ||||
return dup_str(h, &(h->srvr_avs[index]), NULL); | |||||
} | } | ||||
char * | char * | ||||
tac_get_av_value(struct tac_handle *h, const char *attribute) | tac_get_av_value(struct tac_handle *h, const char *attribute) | ||||
{ | { | ||||
int i, len; | int i, attr_len; | ||||
const char *ch, *end; | |||||
const char *candidate; | |||||
int candidate_len; | |||||
int found_seperator; | int found_seperator; | ||||
struct srvr_str srvr; | char *ch, *end; | ||||
struct tac_str *candidate; | |||||
struct tac_str value; | |||||
struct tac_server *srvp = &h->servers[h->cur_server]; | |||||
if (attribute == NULL || ((len = strlen(attribute)) == 0)) | if (attribute == NULL || (attr_len = strlen(attribute)) == 0) | ||||
return NULL; | return NULL; | ||||
for (i=0; i<MAXAVPAIRS; i++) { | for (i = 0; i < h->srvr_navs + srvp->navs; i++) { | ||||
candidate = h->srvr_avs[i].data; | if (i < h->srvr_navs) | ||||
candidate_len = h->srvr_avs[i].len; | candidate = &h->srvr_avs[i]; | ||||
else | |||||
candidate = &srvp->avs[i - h->srvr_navs]; | |||||
/* | if (attr_len < candidate->len && | ||||
* Valid 'srvr_avs' guaranteed to be contiguous starting at | strncmp(candidate->data, attribute, attr_len) == 0) { | ||||
* 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 && | ch = candidate->data + attr_len; | ||||
!strncmp(candidate, attribute, len)) { | end = candidate->data + candidate->len; | ||||
ch = candidate + len; | |||||
end = candidate + candidate_len; | |||||
/* | /* | ||||
* Sift out the white space between A and V (should not | * Sift out the white space between A and V (should not | ||||
* be any, but don't trust implementation of server...) | * be any, but don't trust implementation of server...) | ||||
*/ | */ | ||||
found_seperator = 0; | found_seperator = 0; | ||||
while ((*ch == '=' || *ch == '*' || *ch == ' ' || | while ((*ch == '=' || *ch == '*' || *ch == ' ' || | ||||
*ch == '\t') && ch != end) { | *ch == '\t') && ch != end) { | ||||
if (*ch == '=' || *ch == '*') | if (*ch == '=' || *ch == '*') | ||||
Show All 9 Lines | if (attr_len < candidate->len && | ||||
* is handled. | * is handled. | ||||
* | * | ||||
* Note that for empty string attribute values a | * Note that for empty string attribute values a | ||||
* 0-length string is returned in order to distinguish | * 0-length string is returned in order to distinguish | ||||
* against unset values. | * against unset values. | ||||
* dup_str() will handle srvr.len == 0 correctly. | * dup_str() will handle srvr.len == 0 correctly. | ||||
*/ | */ | ||||
if (found_seperator == 1) { | if (found_seperator == 1) { | ||||
srvr.len = end - ch; | value.len = end - ch; | ||||
srvr.data = ch; | value.data = ch; | ||||
return dup_str(h, &srvr, NULL); | return dup_str(h, &value, NULL); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return NULL; | return NULL; | ||||
} | } | ||||
void | void | ||||
tac_clear_avs(struct tac_handle *h) | tac_clear_avs(struct tac_handle *h) | ||||
{ | { | ||||
int i; | int i; | ||||
for (i=0; i<MAXAVPAIRS; i++) | for (i=0; i<MAXAVPAIRS; i++) | ||||
save_str(h, &(h->avs[i]), NULL, 0); | save_str(h, &(h->avs[i]), NULL, 0); | ||||
} | } | ||||
static void | static void | ||||
clear_srvr_avs(struct tac_handle *h) | clear_srvr_avs(struct tac_handle *h) | ||||
{ | { | ||||
int i; | int i; | ||||
for (i=0; i<MAXAVPAIRS; i++) | |||||
init_srvr_str(&(h->srvr_avs[i])); | for (i = 0; i < h->srvr_navs; i++) | ||||
init_str(&(h->srvr_avs[i])); | |||||
h->srvr_navs = 0; | |||||
} | } | ||||
const char * | const char * | ||||
tac_strerror(struct tac_handle *h) | tac_strerror(struct tac_handle *h) | ||||
{ | { | ||||
return h->errmsg; | return h->errmsg; | ||||
} | } | ||||
Show All 20 Lines |
Presumably this should be explicit_bzero() or memset_s()?