2 * Copyright (c) 1998, 2001, 2002, Juniper Networks, Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
48 #include "taclib_private.h"
50 static int add_str_8(struct tac_handle *, u_int8_t *,
52 static int add_str_16(struct tac_handle *, u_int16_t *,
54 static int protocol_version(int, int, int);
55 static void close_connection(struct tac_handle *);
56 static int conn_server(struct tac_handle *);
57 static void crypt_msg(struct tac_handle *, struct tac_msg *);
58 static void *dup_str(struct tac_handle *, const struct srvr_str *,
60 static int establish_connection(struct tac_handle *);
61 static void free_str(struct clnt_str *);
62 static void generr(struct tac_handle *, const char *, ...)
64 static void gen_session_id(struct tac_msg *);
65 static int get_srvr_end(struct tac_handle *);
66 static int get_srvr_str(struct tac_handle *, const char *,
67 struct srvr_str *, size_t);
68 static void init_clnt_str(struct clnt_str *);
69 static void init_srvr_str(struct srvr_str *);
70 static int read_timed(struct tac_handle *, void *, size_t,
71 const struct timeval *);
72 static int recv_msg(struct tac_handle *);
73 static int save_str(struct tac_handle *, struct clnt_str *,
74 const void *, size_t);
75 static int send_msg(struct tac_handle *);
76 static int split(char *, char *[], int, char *, size_t);
77 static void *xmalloc(struct tac_handle *, size_t);
78 static char *xstrdup(struct tac_handle *, const char *);
79 static void clear_srvr_avs(struct tac_handle *);
80 static void create_msg(struct tac_handle *, int, int, int);
83 * Append some optional data to the current request, and store its
84 * length into the 8-bit field referenced by "fld". Returns 0 on
85 * success, or -1 on failure.
87 * This function also frees the "cs" string data and initializes it
91 add_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
95 if (add_str_16(h, &len, cs) == -1)
99 generr(h, "Field too long");
107 * Append some optional data to the current request, and store its
108 * length into the 16-bit field (network byte order) referenced by
109 * "fld". Returns 0 on success, or -1 on failure.
111 * This function also frees the "cs" string data and initializes it
115 add_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
120 if (cs->data == NULL)
126 generr(h, "Field too long");
129 offset = ntohl(h->request.length);
130 if (offset + len > BODYSIZE) {
131 generr(h, "Message too long");
134 memcpy(h->request.u.body + offset, cs->data, len);
135 h->request.length = htonl(offset + len);
143 protocol_version(int msg_type, int var, int type)
149 /* 'var' represents the 'action' */
151 case TAC_AUTHEN_LOGIN:
154 case TAC_AUTHEN_TYPE_PAP:
155 case TAC_AUTHEN_TYPE_CHAP:
156 case TAC_AUTHEN_TYPE_MSCHAP:
157 case TAC_AUTHEN_TYPE_ARAP:
167 case TAC_AUTHEN_SENDAUTH:
178 /* 'var' represents the 'method' */
181 * When new authentication methods are added, include 'method'
182 * in determining the value of 'minor'. At this point, all
183 * methods defined in this implementation (see "Authorization
184 * authentication methods" in taclib.h) are minor version 0
185 * Not all types, however, indicate minor version 0.
187 case TAC_AUTHEN_METH_NOT_SET:
188 case TAC_AUTHEN_METH_NONE:
189 case TAC_AUTHEN_METH_KRB5:
190 case TAC_AUTHEN_METH_LINE:
191 case TAC_AUTHEN_METH_ENABLE:
192 case TAC_AUTHEN_METH_LOCAL:
193 case TAC_AUTHEN_METH_TACACSPLUS:
194 case TAC_AUTHEN_METH_RCMD:
196 case TAC_AUTHEN_TYPE_PAP:
197 case TAC_AUTHEN_TYPE_CHAP:
198 case TAC_AUTHEN_TYPE_MSCHAP:
199 case TAC_AUTHEN_TYPE_ARAP:
221 return TAC_VER_MAJOR << 4 | minor;
226 close_connection(struct tac_handle *h)
235 conn_server(struct tac_handle *h)
237 const struct tac_server *srvp = &h->servers[h->cur_server];
240 if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
241 generr(h, "Cannot create socket: %s", strerror(errno));
244 if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
245 fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
246 generr(h, "Cannot set non-blocking mode on socket: %s",
252 if (connect(h->fd, (struct sockaddr *)&srvp->addr,
253 sizeof srvp->addr) == 0)
256 if (errno == EINPROGRESS) {
260 struct sockaddr peer;
261 socklen_t errlen, peerlen;
264 /* Wait for the connection to complete. */
266 FD_SET(h->fd, &wfds);
267 tv.tv_sec = srvp->timeout;
269 nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
271 generr(h, "select: %s", strerror(errno));
277 generr(h, "connect: timed out");
283 /* See whether we are connected now. */
284 peerlen = sizeof peer;
285 if (getpeername(h->fd, &peer, &peerlen) == 0)
288 if (errno != ENOTCONN) {
289 generr(h, "getpeername: %s", strerror(errno));
295 /* Find out why the connect failed. */
297 getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
300 generr(h, "connect: %s", strerror(errno));
307 * Encrypt or decrypt a message. The operations are symmetrical.
310 crypt_msg(struct tac_handle *h, struct tac_msg *msg)
315 unsigned char md5[16];
319 secret = h->servers[h->cur_server].secret;
320 if (secret[0] == '\0')
321 msg->flags |= TAC_UNENCRYPTED;
322 if (msg->flags & TAC_UNENCRYPTED)
325 msg_len = ntohl(msg->length);
328 MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
329 MD5Update(&base_ctx, secret, strlen(secret));
330 MD5Update(&base_ctx, &msg->version, sizeof msg->version);
331 MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
334 for (chunk = 0; chunk < msg_len; chunk += sizeof md5) {
340 if ((chunk_len = msg_len - chunk) > sizeof md5)
341 chunk_len = sizeof md5;
342 for (i = 0; i < chunk_len; i++)
343 msg->u.body[chunk + i] ^= md5[i];
346 MD5Update(&ctx, md5, sizeof md5);
351 * Return a dynamically allocated copy of the given server string.
352 * The copy is null-terminated. If "len" is non-NULL, the length of
353 * the string (excluding the terminating null byte) is stored via it.
354 * Returns NULL on failure. Empty strings are still allocated even
355 * though they have no content.
358 dup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
362 if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
364 if (ss->data != NULL && ss->len != 0)
365 memcpy(p, ss->data, ss->len);
373 establish_connection(struct tac_handle *h)
377 if (h->fd >= 0) /* Already connected. */
379 if (h->num_servers == 0) {
380 generr(h, "No TACACS+ servers specified");
384 * Try the servers round-robin. We begin with the one that
385 * worked for us the last time. That way, once we find a good
386 * server, we won't waste any more time trying the bad ones.
388 for (i = 0; i < h->num_servers; i++) {
389 if (conn_server(h) == 0) {
390 h->single_connect = (h->servers[h->cur_server].flags &
391 TAC_SRVR_SINGLE_CONNECT) != 0;
394 if (++h->cur_server >= h->num_servers) /* Wrap around */
397 /* Just return whatever error was last reported by conn_server(). */
402 * Free a client string, obliterating its contents first for security.
405 free_str(struct clnt_str *cs)
407 if (cs->data != NULL) {
408 memset(cs->data, 0, cs->len);
416 generr(struct tac_handle *h, const char *format, ...)
420 va_start(ap, format);
421 vsnprintf(h->errmsg, ERRSIZE, format, ap);
426 gen_session_id(struct tac_msg *msg)
431 msg->session_id[0] = r >> 8;
432 msg->session_id[1] = r;
434 msg->session_id[2] = r >> 8;
435 msg->session_id[3] = r;
439 * Verify that we are exactly at the end of the response message.
440 * Returns 0 on success, -1 on failure.
443 get_srvr_end(struct tac_handle *h)
447 len = ntohl(h->response.length);
449 if (h->srvr_pos != len) {
450 generr(h, "Invalid length field in response "
451 "from server: end expected at %u, response length %u",
459 get_srvr_str(struct tac_handle *h, const char *field,
460 struct srvr_str *ss, size_t len)
462 if (h->srvr_pos + len > ntohl(h->response.length)) {
463 generr(h, "Invalid length field in %s response from server "
464 "(%lu > %lu)", field, (u_long)(h->srvr_pos + len),
465 (u_long)ntohl(h->response.length));
468 ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
475 init_clnt_str(struct clnt_str *cs)
482 init_srvr_str(struct srvr_str *ss)
489 read_timed(struct tac_handle *h, void *buf, size_t len,
490 const struct timeval *deadline)
498 n = read(h->fd, ptr, len);
503 if (errno != EAGAIN) {
504 generr(h, "Network read error: %s",
509 /* Wait until we can read more data. */
510 gettimeofday(&tv, NULL);
511 timersub(deadline, &tv, &tv);
512 if (tv.tv_sec >= 0) {
516 FD_SET(h->fd, &rfds);
518 select(h->fd + 1, &rfds, NULL, NULL, &tv);
520 generr(h, "select: %s",
527 generr(h, "Network read timed out");
531 generr(h, "unexpected EOF from server");
542 * Receive a response from the server and decrypt it. Returns 0 on
543 * success, or -1 on failure.
546 recv_msg(struct tac_handle *h)
548 struct timeval deadline;
553 gettimeofday(&deadline, NULL);
554 deadline.tv_sec += h->servers[h->cur_server].timeout;
556 /* Read the message header and make sure it is reasonable. */
557 if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
559 if (memcmp(msg->session_id, h->request.session_id,
560 sizeof msg->session_id) != 0) {
561 generr(h, "Invalid session ID in received message");
564 if (msg->type != h->request.type) {
565 generr(h, "Invalid type in received message"
566 " (got %u, expected %u)",
567 msg->type, h->request.type);
570 len = ntohl(msg->length);
571 if (len > BODYSIZE) {
572 generr(h, "Received message too large (%u > %u)",
576 if (msg->seq_no != ++h->last_seq_no) {
577 generr(h, "Invalid sequence number in received message"
578 " (got %u, expected %u)",
579 msg->seq_no, h->last_seq_no);
583 /* Read the message body. */
584 if (read_timed(h, msg->u.body, len, &deadline) == -1)
591 * Turn off single-connection mode if the server isn't amenable
594 if (!(msg->flags & TAC_SINGLE_CONNECT))
595 h->single_connect = 0;
600 save_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
604 if (data != NULL && len != 0) {
605 if ((cs->data = xmalloc(h, len)) == NULL)
608 memcpy(cs->data, data, len);
614 * Send the current request, after encrypting it. Returns 0 on success,
618 send_msg(struct tac_handle *h)
620 struct timeval deadline;
625 if (h->last_seq_no & 1) {
626 generr(h, "Attempt to send message out of sequence");
630 if (establish_connection(h) == -1)
634 msg->seq_no = ++h->last_seq_no;
635 if (msg->seq_no == 1)
639 if (h->single_connect)
640 msg->flags |= TAC_SINGLE_CONNECT;
642 msg->flags &= ~TAC_SINGLE_CONNECT;
643 gettimeofday(&deadline, NULL);
644 deadline.tv_sec += h->servers[h->cur_server].timeout;
645 len = HDRSIZE + ntohl(msg->length);
650 n = write(h->fd, ptr, len);
655 if (errno != EAGAIN) {
656 generr(h, "Network write error: %s",
661 /* Wait until we can write more data. */
662 gettimeofday(&tv, NULL);
663 timersub(&deadline, &tv, &tv);
664 if (tv.tv_sec >= 0) {
668 FD_SET(h->fd, &wfds);
670 select(h->fd + 1, NULL, &wfds, NULL, &tv);
672 generr(h, "select: %s",
679 generr(h, "Network write timed out");
691 * Destructively split a string into fields separated by white space.
692 * `#' at the beginning of a field begins a comment that extends to the
693 * end of the string. Fields may be quoted with `"'. Inside quoted
694 * strings, the backslash escapes `\"' and `\\' are honored.
696 * Pointers to up to the first maxfields fields are stored in the fields
697 * array. Missing fields get NULL pointers.
699 * The return value is the actual number of fields parsed, and is always
702 * On a syntax error, places a message in the msg string, and returns -1.
705 split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
709 static const char ws[] = " \t";
711 for (i = 0; i < maxfields; i++)
717 if (*p == '#' || *p == '\0')
719 if (i >= maxfields) {
720 snprintf(msg, msglen, "line has too many fields");
731 if (*p != '"' && *p != '\\' &&
733 snprintf(msg, msglen,
734 "invalid `\\' escape");
739 snprintf(msg, msglen,
740 "unterminated quoted string");
747 if (*p != '\0' && strspn(p, ws) == 0) {
748 snprintf(msg, msglen, "quoted string not"
749 " followed by white space");
764 tac_add_server(struct tac_handle *h, const char *host, int port,
765 const char *secret, int timeout, int flags)
767 struct tac_server *srvp;
769 if (h->num_servers >= MAXSERVERS) {
770 generr(h, "Too many TACACS+ servers specified");
773 srvp = &h->servers[h->num_servers];
775 memset(&srvp->addr, 0, sizeof srvp->addr);
776 srvp->addr.sin_len = sizeof srvp->addr;
777 srvp->addr.sin_family = AF_INET;
778 if (!inet_aton(host, &srvp->addr.sin_addr)) {
779 struct hostent *hent;
781 if ((hent = gethostbyname(host)) == NULL) {
782 generr(h, "%s: host not found", host);
785 memcpy(&srvp->addr.sin_addr, hent->h_addr,
786 sizeof srvp->addr.sin_addr);
788 srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
789 if ((srvp->secret = xstrdup(h, secret)) == NULL)
791 srvp->timeout = timeout;
798 tac_close(struct tac_handle *h)
804 for (srv = 0; srv < h->num_servers; srv++) {
805 memset(h->servers[srv].secret, 0,
806 strlen(h->servers[srv].secret));
807 free(h->servers[srv].secret);
811 free_str(&h->rem_addr);
813 free_str(&h->user_msg);
814 for (i=0; i<MAXAVPAIRS; i++)
815 free_str(&(h->avs[i]));
817 /* Clear everything else before freeing memory */
818 memset(h, 0, sizeof(struct tac_handle));
823 tac_config(struct tac_handle *h, const char *path)
826 char buf[MAXCONFLINE];
831 path = PATH_TACPLUS_CONF;
832 if ((fp = fopen(path, "r")) == NULL) {
833 generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
838 while (fgets(buf, sizeof buf, fp) != NULL) {
849 unsigned long timeout;
855 /* We know len > 0, else fgets would have returned NULL. */
856 if (buf[len - 1] != '\n') {
857 if (len >= sizeof buf - 1)
858 generr(h, "%s:%d: line too long", path,
861 generr(h, "%s:%d: missing newline", path,
868 /* Extract the fields from the line. */
869 nfields = split(buf, fields, 4, msg, sizeof msg);
871 generr(h, "%s:%d: %s", path, linenum, msg);
878 generr(h, "%s:%d: missing shared secret", path,
885 timeout_str = fields[2];
886 options_str = fields[3];
888 /* Parse and validate the fields. */
890 host = strsep(&res, ":");
891 port_str = strsep(&res, ":");
892 if (port_str != NULL) {
893 port = strtoul(port_str, &end, 10);
894 if (port_str[0] == '\0' || *end != '\0') {
895 generr(h, "%s:%d: invalid port", path,
902 if (timeout_str != NULL) {
903 timeout = strtoul(timeout_str, &end, 10);
904 if (timeout_str[0] == '\0' || *end != '\0') {
905 generr(h, "%s:%d: invalid timeout", path,
913 if (options_str != NULL) {
914 if (strcmp(options_str, "single-connection") == 0)
915 options |= TAC_SRVR_SINGLE_CONNECT;
917 generr(h, "%s:%d: invalid option \"%s\"",
918 path, linenum, options_str);
924 if (tac_add_server(h, host, port, secret, timeout,
928 strcpy(msg, h->errmsg);
929 generr(h, "%s:%d: %s", path, linenum, msg);
934 /* Clear out the buffer to wipe a possible copy of a shared secret */
935 memset(buf, 0, sizeof buf);
941 tac_create_authen(struct tac_handle *h, int action, int type, int service)
943 struct tac_authen_start *as;
945 create_msg(h, TAC_AUTHEN, action, type);
947 as = &h->request.u.authen_start;
949 as->priv_lvl = TAC_PRIV_LVL_USER;
950 as->authen_type = type;
951 as->service = service;
957 tac_create_author(struct tac_handle *h, int method, int type, int service)
959 struct tac_author_request *areq;
961 create_msg(h, TAC_AUTHOR, method, type);
963 areq = &h->request.u.author_request;
964 areq->authen_meth = method;
965 areq->priv_lvl = TAC_PRIV_LVL_USER;
966 areq->authen_type = type;
967 areq->service = service;
973 tac_create_acct(struct tac_handle *h, int acct, int action, int type, int service)
975 struct tac_acct_start *as;
977 create_msg(h, TAC_ACCT, action, type);
979 as = &h->request.u.acct_start;
981 as->authen_action = action;
982 as->priv_lvl = TAC_PRIV_LVL_USER;
983 as->authen_type = type;
984 as->authen_service = service;
990 create_msg(struct tac_handle *h, int msg_type, int var, int type)
998 msg->type = msg_type;
999 msg->version = protocol_version(msg_type, var, type);
1000 msg->flags = 0; /* encrypted packet body */
1004 free_str(&h->rem_addr);
1006 free_str(&h->user_msg);
1008 for (i=0; i<MAXAVPAIRS; i++)
1009 free_str(&(h->avs[i]));
1013 tac_get_data(struct tac_handle *h, size_t *len)
1015 return dup_str(h, &h->srvr_data, len);
1019 tac_get_msg(struct tac_handle *h)
1021 return dup_str(h, &h->srvr_msg, NULL);
1025 * Create and initialize a tac_handle structure, and return it to the
1026 * caller. Can fail only if the necessary memory cannot be allocated.
1027 * In that case, it returns NULL.
1033 struct tac_handle *h;
1035 h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
1040 h->errmsg[0] = '\0';
1041 init_clnt_str(&h->user);
1042 init_clnt_str(&h->port);
1043 init_clnt_str(&h->rem_addr);
1044 init_clnt_str(&h->data);
1045 init_clnt_str(&h->user_msg);
1046 for (i=0; i<MAXAVPAIRS; i++) {
1047 init_clnt_str(&(h->avs[i]));
1048 init_srvr_str(&(h->srvr_avs[i]));
1050 init_srvr_str(&h->srvr_msg);
1051 init_srvr_str(&h->srvr_data);
1058 tac_send_authen(struct tac_handle *h)
1060 struct tac_authen_reply *ar;
1062 if (h->num_servers == 0)
1065 if (h->last_seq_no == 0) { /* Authentication START packet */
1066 struct tac_authen_start *as;
1068 as = &h->request.u.authen_start;
1070 htonl(offsetof(struct tac_authen_start, rest[0]));
1071 if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1072 add_str_8(h, &as->port_len, &h->port) == -1 ||
1073 add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
1074 add_str_8(h, &as->data_len, &h->data) == -1)
1076 } else { /* Authentication CONTINUE packet */
1077 struct tac_authen_cont *ac;
1079 ac = &h->request.u.authen_cont;
1082 htonl(offsetof(struct tac_authen_cont, rest[0]));
1083 if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
1084 add_str_16(h, &ac->data_len, &h->data) == -1)
1088 /* Send the message and retrieve the reply. */
1089 if (send_msg(h) == -1 || recv_msg(h) == -1)
1092 /* Scan the optional fields in the reply. */
1093 ar = &h->response.u.authen_reply;
1094 h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
1095 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1096 get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1097 get_srvr_end(h) == -1)
1100 if (!h->single_connect &&
1101 ar->status != TAC_AUTHEN_STATUS_GETDATA &&
1102 ar->status != TAC_AUTHEN_STATUS_GETUSER &&
1103 ar->status != TAC_AUTHEN_STATUS_GETPASS)
1104 close_connection(h);
1106 return ar->flags << 8 | ar->status;
1110 tac_send_author(struct tac_handle *h)
1114 struct tac_author_request *areq = &h->request.u.author_request;
1115 struct tac_author_response *ares = &h->response.u.author_response;
1118 htonl(offsetof(struct tac_author_request, rest[0]));
1120 /* Count each specified AV pair */
1121 for (areq->av_cnt=0, i=0; i<MAXAVPAIRS; i++)
1122 if (h->avs[i].len && h->avs[i].data)
1126 * Each AV size is a byte starting right after 'av_cnt'. Update the
1127 * offset to include these AV sizes.
1129 h->request.length = ntohl(htonl(h->request.length) + areq->av_cnt);
1131 /* Now add the string arguments from 'h' */
1132 if (add_str_8(h, &areq->user_len, &h->user) == -1 ||
1133 add_str_8(h, &areq->port_len, &h->port) == -1 ||
1134 add_str_8(h, &areq->rem_addr_len, &h->rem_addr) == -1)
1137 /* Add each AV pair, the size of each placed in areq->rest[current] */
1138 for (current=0, i=0; i<MAXAVPAIRS; i++) {
1139 if (h->avs[i].len && h->avs[i].data) {
1140 if (add_str_8(h, &areq->rest[current++],
1141 &(h->avs[i])) == -1)
1146 /* Send the message and retrieve the reply. */
1147 if (send_msg(h) == -1 || recv_msg(h) == -1)
1150 /* Update the offset in the response packet based on av pairs count */
1151 h->srvr_pos = offsetof(struct tac_author_response, rest[0]) +
1154 /* Scan the optional fields in the response. */
1155 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ares->msg_len)) == -1 ||
1156 get_srvr_str(h, "data", &h->srvr_data, ntohs(ares->data_len)) ==-1)
1159 /* Get each AV pair (just setting pointers, not malloc'ing) */
1161 for (i=0; i<ares->av_cnt; i++) {
1162 snprintf(dbgstr, sizeof dbgstr, "av-pair-%d", i);
1163 if (get_srvr_str(h, dbgstr, &(h->srvr_avs[i]),
1164 ares->rest[i]) == -1)
1168 /* Should have ended up at the end */
1169 if (get_srvr_end(h) == -1)
1173 if (!h->single_connect)
1174 close_connection(h);
1176 return ares->av_cnt << 8 | ares->status;
1180 tac_send_acct(struct tac_handle *h)
1182 register int i, current;
1183 struct tac_acct_start *as = &h->request.u.acct_start;
1184 struct tac_acct_reply *ar = &h->response.u.acct_reply;
1187 as = &h->request.u.acct_start;
1188 h->request.length = htonl(offsetof(struct tac_acct_start, rest[0]));
1189 for (as->av_cnt = 0, i = 0; i < MAXAVPAIRS; i++)
1190 if (h->avs[i].len && h->avs[i].data)
1192 h->request.length = ntohl(htonl(h->request.length) + as->av_cnt);
1194 if (add_str_8(h, &as->user_len, &h->user) == -1 ||
1195 add_str_8(h, &as->port_len, &h->port) == -1 ||
1196 add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1)
1199 for (i = current = 0; i < MAXAVPAIRS; i++)
1200 if (h->avs[i].len && h->avs[i].data)
1201 if (add_str_8(h, &as->rest[current++], &(h->avs[i])) == -1)
1205 if (send_msg(h) == -1 || recv_msg(h) == -1)
1209 h->srvr_pos = offsetof(struct tac_acct_reply, rest[0]);
1210 if (get_srvr_str(h, "msg", &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
1211 get_srvr_str(h, "data", &h->srvr_data, ntohs(ar->data_len)) == -1 ||
1212 get_srvr_end(h) == -1)
1216 if (!h->single_connect)
1217 close_connection(h);
1223 tac_set_rem_addr(struct tac_handle *h, const char *addr)
1225 return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
1229 tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
1231 return save_str(h, &h->data, data, data_len);
1235 tac_set_msg(struct tac_handle *h, const char *msg)
1237 return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1241 tac_set_port(struct tac_handle *h, const char *port)
1243 return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1247 tac_set_priv(struct tac_handle *h, int priv)
1249 if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1250 generr(h, "Attempt to set invalid privilege level");
1253 h->request.u.authen_start.priv_lvl = priv;
1258 tac_set_user(struct tac_handle *h, const char *user)
1260 return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1264 tac_set_av(struct tac_handle *h, u_int index, const char *av)
1266 if (index >= MAXAVPAIRS)
1268 return save_str(h, &(h->avs[index]), av, av != NULL ? strlen(av) : 0);
1272 tac_get_av(struct tac_handle *h, u_int index)
1274 if (index >= MAXAVPAIRS)
1276 return dup_str(h, &(h->srvr_avs[index]), NULL);
1280 tac_get_av_value(struct tac_handle *h, const char *attribute)
1283 const char *ch, *end;
1284 const char *candidate;
1286 int found_seperator;
1287 struct srvr_str srvr;
1289 if (attribute == NULL || ((len = strlen(attribute)) == 0))
1292 for (i=0; i<MAXAVPAIRS; i++) {
1293 candidate = h->srvr_avs[i].data;
1294 candidate_len = h->srvr_avs[i].len;
1297 * Valid 'srvr_avs' guaranteed to be contiguous starting at
1298 * index 0 (not necessarily the case with 'avs'). Break out
1299 * when the "end" of the list has been reached.
1304 if (len < candidate_len &&
1305 !strncmp(candidate, attribute, len)) {
1307 ch = candidate + len;
1308 end = candidate + candidate_len;
1311 * Sift out the white space between A and V (should not
1312 * be any, but don't trust implementation of server...)
1314 found_seperator = 0;
1315 while ((*ch == '=' || *ch == '*' || *ch == ' ' ||
1316 *ch == '\t') && ch != end) {
1317 if (*ch == '=' || *ch == '*')
1324 * The case of 'attribute' == "foo" and
1325 * h->srvr_avs[0] = "foobie=var1"
1326 * h->srvr_avs[1] = "foo=var2"
1329 * Note that for empty string attribute values a
1330 * 0-length string is returned in order to distinguish
1331 * against unset values.
1332 * dup_str() will handle srvr.len == 0 correctly.
1334 if (found_seperator == 1) {
1335 srvr.len = end - ch;
1337 return dup_str(h, &srvr, NULL);
1345 tac_clear_avs(struct tac_handle *h)
1348 for (i=0; i<MAXAVPAIRS; i++)
1349 save_str(h, &(h->avs[i]), NULL, 0);
1353 clear_srvr_avs(struct tac_handle *h)
1356 for (i=0; i<MAXAVPAIRS; i++)
1357 init_srvr_str(&(h->srvr_avs[i]));
1362 tac_strerror(struct tac_handle *h)
1368 xmalloc(struct tac_handle *h, size_t size)
1372 if ((r = malloc(size)) == NULL)
1373 generr(h, "Out of memory");
1378 xstrdup(struct tac_handle *h, const char *s)
1382 if ((r = strdup(s)) == NULL)
1383 generr(h, "Out of memory");