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()?