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
26 * $FreeBSD: src/lib/libtacplus/taclib.c,v 1.2.2.2 2002/10/09 08:50:42 pst Exp $
29 #include <sys/param.h>
30 #include <sys/socket.h>
32 #include <netinet/in.h>
33 #include <arpa/inet.h>
47 #include "taclib_private.h"
49 static int add_str_8(struct tac_handle
*, u_int8_t
*,
51 static int add_str_16(struct tac_handle
*, u_int16_t
*,
53 static int protocol_version(int, int, int);
54 static void close_connection(struct tac_handle
*);
55 static int conn_server(struct tac_handle
*);
56 static void crypt_msg(struct tac_handle
*, struct tac_msg
*);
57 static void *dup_str(struct tac_handle
*, const struct srvr_str
*,
59 static int establish_connection(struct tac_handle
*);
60 static void free_str(struct clnt_str
*);
61 static void generr(struct tac_handle
*, const char *, ...)
63 static void gen_session_id(struct tac_msg
*);
64 static int get_srvr_end(struct tac_handle
*);
65 static int get_srvr_str(struct tac_handle
*, const char *,
66 struct srvr_str
*, size_t);
67 static void init_clnt_str(struct clnt_str
*);
68 static void init_srvr_str(struct srvr_str
*);
69 static int read_timed(struct tac_handle
*, void *, size_t,
70 const struct timeval
*);
71 static int recv_msg(struct tac_handle
*);
72 static int save_str(struct tac_handle
*, struct clnt_str
*,
73 const void *, size_t);
74 static int send_msg(struct tac_handle
*);
75 static int split(char *, char *[], int, char *, size_t);
76 static void *xmalloc(struct tac_handle
*, size_t);
77 static char *xstrdup(struct tac_handle
*, const char *);
78 static void clear_srvr_avs(struct tac_handle
*);
79 static void create_msg(struct tac_handle
*, int, int, int);
82 * Append some optional data to the current request, and store its
83 * length into the 8-bit field referenced by "fld". Returns 0 on
84 * success, or -1 on failure.
86 * This function also frees the "cs" string data and initializes it
90 add_str_8(struct tac_handle
*h
, u_int8_t
*fld
, struct clnt_str
*cs
)
94 if (add_str_16(h
, &len
, cs
) == -1)
98 generr(h
, "Field too long");
106 * Append some optional data to the current request, and store its
107 * length into the 16-bit field (network byte order) referenced by
108 * "fld". Returns 0 on success, or -1 on failure.
110 * This function also frees the "cs" string data and initializes it
114 add_str_16(struct tac_handle
*h
, u_int16_t
*fld
, struct clnt_str
*cs
)
119 if (cs
->data
== NULL
)
125 generr(h
, "Field too long");
128 offset
= ntohl(h
->request
.length
);
129 if (offset
+ len
> BODYSIZE
) {
130 generr(h
, "Message too long");
133 memcpy(h
->request
.u
.body
+ offset
, cs
->data
, len
);
134 h
->request
.length
= htonl(offset
+ len
);
142 protocol_version(int msg_type
, int var
, int type
)
148 /* 'var' represents the 'action' */
150 case TAC_AUTHEN_LOGIN
:
153 case TAC_AUTHEN_TYPE_PAP
:
154 case TAC_AUTHEN_TYPE_CHAP
:
155 case TAC_AUTHEN_TYPE_MSCHAP
:
156 case TAC_AUTHEN_TYPE_ARAP
:
166 case TAC_AUTHEN_SENDAUTH
:
177 /* 'var' represents the 'method' */
180 * When new authentication methods are added, include 'method'
181 * in determining the value of 'minor'. At this point, all
182 * methods defined in this implementation (see "Authorization
183 * authentication methods" in taclib.h) are minor version 0
184 * Not all types, however, indicate minor version 0.
186 case TAC_AUTHEN_METH_NOT_SET
:
187 case TAC_AUTHEN_METH_NONE
:
188 case TAC_AUTHEN_METH_KRB5
:
189 case TAC_AUTHEN_METH_LINE
:
190 case TAC_AUTHEN_METH_ENABLE
:
191 case TAC_AUTHEN_METH_LOCAL
:
192 case TAC_AUTHEN_METH_TACACSPLUS
:
193 case TAC_AUTHEN_METH_RCMD
:
195 case TAC_AUTHEN_TYPE_PAP
:
196 case TAC_AUTHEN_TYPE_CHAP
:
197 case TAC_AUTHEN_TYPE_MSCHAP
:
198 case TAC_AUTHEN_TYPE_ARAP
:
218 return TAC_VER_MAJOR
<< 4 | minor
;
223 close_connection(struct tac_handle
*h
)
232 conn_server(struct tac_handle
*h
)
234 const struct tac_server
*srvp
= &h
->servers
[h
->cur_server
];
237 if ((h
->fd
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
)) == -1) {
238 generr(h
, "Cannot create socket: %s", strerror(errno
));
241 if ((flags
= fcntl(h
->fd
, F_GETFL
, 0)) == -1 ||
242 fcntl(h
->fd
, F_SETFL
, flags
| O_NONBLOCK
) == -1) {
243 generr(h
, "Cannot set non-blocking mode on socket: %s",
249 if (connect(h
->fd
, (struct sockaddr
*)&srvp
->addr
,
250 sizeof srvp
->addr
) == 0)
253 if (errno
== EINPROGRESS
) {
257 struct sockaddr peer
;
262 /* Wait for the connection to complete. */
264 FD_SET(h
->fd
, &wfds
);
265 tv
.tv_sec
= srvp
->timeout
;
267 nfds
= select(h
->fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
269 generr(h
, "select: %s", strerror(errno
));
275 generr(h
, "connect: timed out");
281 /* See whether we are connected now. */
282 peerlen
= sizeof peer
;
283 if (getpeername(h
->fd
, &peer
, &peerlen
) == 0)
286 if (errno
!= ENOTCONN
) {
287 generr(h
, "getpeername: %s", strerror(errno
));
293 /* Find out why the connect failed. */
295 getsockopt(h
->fd
, SOL_SOCKET
, SO_ERROR
, &err
, &errlen
);
298 generr(h
, "connect: %s", strerror(errno
));
305 * Encrypt or decrypt a message. The operations are symmetrical.
308 crypt_msg(struct tac_handle
*h
, struct tac_msg
*msg
)
313 unsigned char md5
[16];
317 secret
= h
->servers
[h
->cur_server
].secret
;
318 if (secret
[0] == '\0')
319 msg
->flags
|= TAC_UNENCRYPTED
;
320 if (msg
->flags
& TAC_UNENCRYPTED
)
323 msg_len
= ntohl(msg
->length
);
326 MD5Update(&base_ctx
, msg
->session_id
, sizeof msg
->session_id
);
327 MD5Update(&base_ctx
, secret
, strlen(secret
));
328 MD5Update(&base_ctx
, &msg
->version
, sizeof msg
->version
);
329 MD5Update(&base_ctx
, &msg
->seq_no
, sizeof msg
->seq_no
);
332 for (chunk
= 0; chunk
< msg_len
; chunk
+= sizeof md5
) {
338 if ((chunk_len
= msg_len
- chunk
) > sizeof md5
)
339 chunk_len
= sizeof md5
;
340 for (i
= 0; i
< chunk_len
; i
++)
341 msg
->u
.body
[chunk
+ i
] ^= md5
[i
];
344 MD5Update(&ctx
, md5
, sizeof md5
);
349 * Return a dynamically allocated copy of the given server string.
350 * The copy is null-terminated. If "len" is non-NULL, the length of
351 * the string (excluding the terminating null byte) is stored via it.
352 * Returns NULL on failure. Empty strings are still allocated even
353 * though they have no content.
356 dup_str(struct tac_handle
*h
, const struct srvr_str
*ss
, size_t *len
)
360 if ((p
= (unsigned char *)xmalloc(h
, ss
->len
+ 1)) == NULL
)
362 if (ss
->data
!= NULL
&& ss
->len
!= 0)
363 memcpy(p
, ss
->data
, ss
->len
);
371 establish_connection(struct tac_handle
*h
)
375 if (h
->fd
>= 0) /* Already connected. */
377 if (h
->num_servers
== 0) {
378 generr(h
, "No TACACS+ servers specified");
382 * Try the servers round-robin. We begin with the one that
383 * worked for us the last time. That way, once we find a good
384 * server, we won't waste any more time trying the bad ones.
386 for (i
= 0; i
< h
->num_servers
; i
++) {
387 if (conn_server(h
) == 0) {
388 h
->single_connect
= (h
->servers
[h
->cur_server
].flags
&
389 TAC_SRVR_SINGLE_CONNECT
) != 0;
392 if (++h
->cur_server
>= h
->num_servers
) /* Wrap around */
395 /* Just return whatever error was last reported by conn_server(). */
400 * Free a client string, obliterating its contents first for security.
403 free_str(struct clnt_str
*cs
)
405 if (cs
->data
!= NULL
) {
406 memset(cs
->data
, 0, cs
->len
);
414 generr(struct tac_handle
*h
, const char *format
, ...)
418 va_start(ap
, format
);
419 vsnprintf(h
->errmsg
, ERRSIZE
, format
, ap
);
424 gen_session_id(struct tac_msg
*msg
)
429 msg
->session_id
[0] = r
>> 8;
430 msg
->session_id
[1] = r
;
432 msg
->session_id
[2] = r
>> 8;
433 msg
->session_id
[3] = r
;
437 * Verify that we are exactly at the end of the response message.
438 * Returns 0 on success, -1 on failure.
441 get_srvr_end(struct tac_handle
*h
)
445 len
= ntohl(h
->response
.length
);
447 if (h
->srvr_pos
!= len
) {
448 generr(h
, "Invalid length field in response "
449 "from server: end expected at %u, response length %u",
457 get_srvr_str(struct tac_handle
*h
, const char *field
,
458 struct srvr_str
*ss
, size_t len
)
460 if (h
->srvr_pos
+ len
> ntohl(h
->response
.length
)) {
461 generr(h
, "Invalid length field in %s response from server "
462 "(%lu > %lu)", field
, (u_long
)(h
->srvr_pos
+ len
),
463 (u_long
)ntohl(h
->response
.length
));
466 ss
->data
= len
!= 0 ? h
->response
.u
.body
+ h
->srvr_pos
: NULL
;
473 init_clnt_str(struct clnt_str
*cs
)
480 init_srvr_str(struct srvr_str
*ss
)
487 read_timed(struct tac_handle
*h
, void *buf
, size_t len
,
488 const struct timeval
*deadline
)
496 n
= read(h
->fd
, ptr
, len
);
501 if (errno
!= EAGAIN
) {
502 generr(h
, "Network read error: %s",
507 /* Wait until we can read more data. */
508 gettimeofday(&tv
, NULL
);
509 timersub(deadline
, &tv
, &tv
);
510 if (tv
.tv_sec
>= 0) {
514 FD_SET(h
->fd
, &rfds
);
516 select(h
->fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
518 generr(h
, "select: %s",
525 generr(h
, "Network read timed out");
529 generr(h
, "unexpected EOF from server");
540 * Receive a response from the server and decrypt it. Returns 0 on
541 * success, or -1 on failure.
544 recv_msg(struct tac_handle
*h
)
546 struct timeval deadline
;
551 gettimeofday(&deadline
, NULL
);
552 deadline
.tv_sec
+= h
->servers
[h
->cur_server
].timeout
;
554 /* Read the message header and make sure it is reasonable. */
555 if (read_timed(h
, msg
, HDRSIZE
, &deadline
) == -1)
557 if (memcmp(msg
->session_id
, h
->request
.session_id
,
558 sizeof msg
->session_id
) != 0) {
559 generr(h
, "Invalid session ID in received message");
562 if (msg
->type
!= h
->request
.type
) {
563 generr(h
, "Invalid type in received message"
564 " (got %u, expected %u)",
565 msg
->type
, h
->request
.type
);
568 len
= ntohl(msg
->length
);
569 if (len
> BODYSIZE
) {
570 generr(h
, "Received message too large (%u > %u)",
574 if (msg
->seq_no
!= ++h
->last_seq_no
) {
575 generr(h
, "Invalid sequence number in received message"
576 " (got %u, expected %u)",
577 msg
->seq_no
, h
->last_seq_no
);
581 /* Read the message body. */
582 if (read_timed(h
, msg
->u
.body
, len
, &deadline
) == -1)
589 * Turn off single-connection mode if the server isn't amenable
592 if (!(msg
->flags
& TAC_SINGLE_CONNECT
))
593 h
->single_connect
= 0;
598 save_str(struct tac_handle
*h
, struct clnt_str
*cs
, const void *data
,
602 if (data
!= NULL
&& len
!= 0) {
603 if ((cs
->data
= xmalloc(h
, len
)) == NULL
)
606 memcpy(cs
->data
, data
, len
);
612 * Send the current request, after encrypting it. Returns 0 on success,
616 send_msg(struct tac_handle
*h
)
618 struct timeval deadline
;
623 if (h
->last_seq_no
& 1) {
624 generr(h
, "Attempt to send message out of sequence");
628 if (establish_connection(h
) == -1)
632 msg
->seq_no
= ++h
->last_seq_no
;
633 if (msg
->seq_no
== 1)
637 if (h
->single_connect
)
638 msg
->flags
|= TAC_SINGLE_CONNECT
;
640 msg
->flags
&= ~TAC_SINGLE_CONNECT
;
641 gettimeofday(&deadline
, NULL
);
642 deadline
.tv_sec
+= h
->servers
[h
->cur_server
].timeout
;
643 len
= HDRSIZE
+ ntohl(msg
->length
);
648 n
= write(h
->fd
, ptr
, len
);
653 if (errno
!= EAGAIN
) {
654 generr(h
, "Network write error: %s",
659 /* Wait until we can write more data. */
660 gettimeofday(&tv
, NULL
);
661 timersub(&deadline
, &tv
, &tv
);
662 if (tv
.tv_sec
>= 0) {
666 FD_SET(h
->fd
, &wfds
);
668 select(h
->fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
670 generr(h
, "select: %s",
677 generr(h
, "Network write timed out");
689 * Destructively split a string into fields separated by white space.
690 * `#' at the beginning of a field begins a comment that extends to the
691 * end of the string. Fields may be quoted with `"'. Inside quoted
692 * strings, the backslash escapes `\"' and `\\' are honored.
694 * Pointers to up to the first maxfields fields are stored in the fields
695 * array. Missing fields get NULL pointers.
697 * The return value is the actual number of fields parsed, and is always
700 * On a syntax error, places a message in the msg string, and returns -1.
703 split(char *str
, char *fields
[], int maxfields
, char *msg
, size_t msglen
)
707 static const char ws
[] = " \t";
709 for (i
= 0; i
< maxfields
; i
++)
715 if (*p
== '#' || *p
== '\0')
717 if (i
>= maxfields
) {
718 snprintf(msg
, msglen
, "line has too many fields");
729 if (*p
!= '"' && *p
!= '\\' &&
731 snprintf(msg
, msglen
,
732 "invalid `\\' escape");
737 snprintf(msg
, msglen
,
738 "unterminated quoted string");
745 if (*p
!= '\0' && strspn(p
, ws
) == 0) {
746 snprintf(msg
, msglen
, "quoted string not"
747 " followed by white space");
762 tac_add_server(struct tac_handle
*h
, const char *host
, int port
,
763 const char *secret
, int timeout
, int flags
)
765 struct tac_server
*srvp
;
767 if (h
->num_servers
>= MAXSERVERS
) {
768 generr(h
, "Too many TACACS+ servers specified");
771 srvp
= &h
->servers
[h
->num_servers
];
773 memset(&srvp
->addr
, 0, sizeof srvp
->addr
);
774 srvp
->addr
.sin_len
= sizeof srvp
->addr
;
775 srvp
->addr
.sin_family
= AF_INET
;
776 if (!inet_aton(host
, &srvp
->addr
.sin_addr
)) {
777 struct hostent
*hent
;
779 if ((hent
= gethostbyname(host
)) == NULL
) {
780 generr(h
, "%s: host not found", host
);
783 memcpy(&srvp
->addr
.sin_addr
, hent
->h_addr
,
784 sizeof srvp
->addr
.sin_addr
);
786 srvp
->addr
.sin_port
= htons(port
!= 0 ? port
: TACPLUS_PORT
);
787 if ((srvp
->secret
= xstrdup(h
, secret
)) == NULL
)
789 srvp
->timeout
= timeout
;
796 tac_close(struct tac_handle
*h
)
802 for (srv
= 0; srv
< h
->num_servers
; srv
++) {
803 memset(h
->servers
[srv
].secret
, 0,
804 strlen(h
->servers
[srv
].secret
));
805 free(h
->servers
[srv
].secret
);
809 free_str(&h
->rem_addr
);
811 free_str(&h
->user_msg
);
812 for (i
=0; i
<MAXAVPAIRS
; i
++)
813 free_str(&(h
->avs
[i
]));
815 /* Clear everything else before freeing memory */
816 memset(h
, 0, sizeof(struct tac_handle
));
821 tac_config(struct tac_handle
*h
, const char *path
)
824 char buf
[MAXCONFLINE
];
829 path
= PATH_TACPLUS_CONF
;
830 if ((fp
= fopen(path
, "r")) == NULL
) {
831 generr(h
, "Cannot open \"%s\": %s", path
, strerror(errno
));
836 while (fgets(buf
, sizeof buf
, fp
) != NULL
) {
847 unsigned long timeout
;
853 /* We know len > 0, else fgets would have returned NULL. */
854 if (buf
[len
- 1] != '\n') {
855 if (len
>= sizeof buf
- 1)
856 generr(h
, "%s:%d: line too long", path
,
859 generr(h
, "%s:%d: missing newline", path
,
866 /* Extract the fields from the line. */
867 nfields
= split(buf
, fields
, 4, msg
, sizeof msg
);
869 generr(h
, "%s:%d: %s", path
, linenum
, msg
);
876 generr(h
, "%s:%d: missing shared secret", path
,
883 timeout_str
= fields
[2];
884 options_str
= fields
[3];
886 /* Parse and validate the fields. */
888 host
= strsep(&res
, ":");
889 port_str
= strsep(&res
, ":");
890 if (port_str
!= NULL
) {
891 port
= strtoul(port_str
, &end
, 10);
892 if (port_str
[0] == '\0' || *end
!= '\0') {
893 generr(h
, "%s:%d: invalid port", path
,
900 if (timeout_str
!= NULL
) {
901 timeout
= strtoul(timeout_str
, &end
, 10);
902 if (timeout_str
[0] == '\0' || *end
!= '\0') {
903 generr(h
, "%s:%d: invalid timeout", path
,
911 if (options_str
!= NULL
) {
912 if (strcmp(options_str
, "single-connection") == 0)
913 options
|= TAC_SRVR_SINGLE_CONNECT
;
915 generr(h
, "%s:%d: invalid option \"%s\"",
916 path
, linenum
, options_str
);
922 if (tac_add_server(h
, host
, port
, secret
, timeout
,
926 strcpy(msg
, h
->errmsg
);
927 generr(h
, "%s:%d: %s", path
, linenum
, msg
);
932 /* Clear out the buffer to wipe a possible copy of a shared secret */
933 memset(buf
, 0, sizeof buf
);
939 tac_create_authen(struct tac_handle
*h
, int action
, int type
, int service
)
941 struct tac_authen_start
*as
;
943 create_msg(h
, TAC_AUTHEN
, action
, type
);
945 as
= &h
->request
.u
.authen_start
;
947 as
->priv_lvl
= TAC_PRIV_LVL_USER
;
948 as
->authen_type
= type
;
949 as
->service
= service
;
955 tac_create_author(struct tac_handle
*h
, int method
, int type
, int service
)
957 struct tac_author_request
*areq
;
959 create_msg(h
, TAC_AUTHOR
, method
, type
);
961 areq
= &h
->request
.u
.author_request
;
962 areq
->authen_meth
= method
;
963 areq
->priv_lvl
= TAC_PRIV_LVL_USER
;
964 areq
->authen_type
= type
;
965 areq
->service
= service
;
971 create_msg(struct tac_handle
*h
, int msg_type
, int var
, int type
)
979 msg
->type
= msg_type
;
980 msg
->version
= protocol_version(msg_type
, var
, type
);
981 msg
->flags
= 0; /* encrypted packet body */
985 free_str(&h
->rem_addr
);
987 free_str(&h
->user_msg
);
989 for (i
=0; i
<MAXAVPAIRS
; i
++)
990 free_str(&(h
->avs
[i
]));
994 tac_get_data(struct tac_handle
*h
, size_t *len
)
996 return dup_str(h
, &h
->srvr_data
, len
);
1000 tac_get_msg(struct tac_handle
*h
)
1002 return dup_str(h
, &h
->srvr_msg
, NULL
);
1006 * Create and initialize a tac_handle structure, and return it to the
1007 * caller. Can fail only if the necessary memory cannot be allocated.
1008 * In that case, it returns NULL.
1014 struct tac_handle
*h
;
1016 h
= (struct tac_handle
*)malloc(sizeof(struct tac_handle
));
1021 h
->errmsg
[0] = '\0';
1022 init_clnt_str(&h
->user
);
1023 init_clnt_str(&h
->port
);
1024 init_clnt_str(&h
->rem_addr
);
1025 init_clnt_str(&h
->data
);
1026 init_clnt_str(&h
->user_msg
);
1027 for (i
=0; i
<MAXAVPAIRS
; i
++) {
1028 init_clnt_str(&(h
->avs
[i
]));
1029 init_srvr_str(&(h
->srvr_avs
[i
]));
1031 init_srvr_str(&h
->srvr_msg
);
1032 init_srvr_str(&h
->srvr_data
);
1039 tac_send_authen(struct tac_handle
*h
)
1041 struct tac_authen_reply
*ar
;
1043 if (h
->num_servers
== 0)
1046 if (h
->last_seq_no
== 0) { /* Authentication START packet */
1047 struct tac_authen_start
*as
;
1049 as
= &h
->request
.u
.authen_start
;
1051 htonl(offsetof(struct tac_authen_start
, rest
[0]));
1052 if (add_str_8(h
, &as
->user_len
, &h
->user
) == -1 ||
1053 add_str_8(h
, &as
->port_len
, &h
->port
) == -1 ||
1054 add_str_8(h
, &as
->rem_addr_len
, &h
->rem_addr
) == -1 ||
1055 add_str_8(h
, &as
->data_len
, &h
->data
) == -1)
1057 } else { /* Authentication CONTINUE packet */
1058 struct tac_authen_cont
*ac
;
1060 ac
= &h
->request
.u
.authen_cont
;
1063 htonl(offsetof(struct tac_authen_cont
, rest
[0]));
1064 if (add_str_16(h
, &ac
->user_msg_len
, &h
->user_msg
) == -1 ||
1065 add_str_16(h
, &ac
->data_len
, &h
->data
) == -1)
1069 /* Send the message and retrieve the reply. */
1070 if (send_msg(h
) == -1 || recv_msg(h
) == -1)
1073 /* Scan the optional fields in the reply. */
1074 ar
= &h
->response
.u
.authen_reply
;
1075 h
->srvr_pos
= offsetof(struct tac_authen_reply
, rest
[0]);
1076 if (get_srvr_str(h
, "msg", &h
->srvr_msg
, ntohs(ar
->msg_len
)) == -1 ||
1077 get_srvr_str(h
, "data", &h
->srvr_data
, ntohs(ar
->data_len
)) == -1 ||
1078 get_srvr_end(h
) == -1)
1081 if (!h
->single_connect
&&
1082 ar
->status
!= TAC_AUTHEN_STATUS_GETDATA
&&
1083 ar
->status
!= TAC_AUTHEN_STATUS_GETUSER
&&
1084 ar
->status
!= TAC_AUTHEN_STATUS_GETPASS
)
1085 close_connection(h
);
1087 return ar
->flags
<< 8 | ar
->status
;
1091 tac_send_author(struct tac_handle
*h
)
1095 struct tac_author_request
*areq
= &h
->request
.u
.author_request
;
1096 struct tac_author_response
*ares
= &h
->response
.u
.author_response
;
1099 htonl(offsetof(struct tac_author_request
, rest
[0]));
1101 /* Count each specified AV pair */
1102 for (areq
->av_cnt
=0, i
=0; i
<MAXAVPAIRS
; i
++)
1103 if (h
->avs
[i
].len
&& h
->avs
[i
].data
)
1107 * Each AV size is a byte starting right after 'av_cnt'. Update the
1108 * offset to include these AV sizes.
1110 h
->request
.length
= ntohl(htonl(h
->request
.length
) + areq
->av_cnt
);
1112 /* Now add the string arguments from 'h' */
1113 if (add_str_8(h
, &areq
->user_len
, &h
->user
) == -1 ||
1114 add_str_8(h
, &areq
->port_len
, &h
->port
) == -1 ||
1115 add_str_8(h
, &areq
->rem_addr_len
, &h
->rem_addr
) == -1)
1118 /* Add each AV pair, the size of each placed in areq->rest[current] */
1119 for (current
=0, i
=0; i
<MAXAVPAIRS
; i
++) {
1120 if (h
->avs
[i
].len
&& h
->avs
[i
].data
) {
1121 if (add_str_8(h
, &areq
->rest
[current
++],
1122 &(h
->avs
[i
])) == -1)
1127 /* Send the message and retrieve the reply. */
1128 if (send_msg(h
) == -1 || recv_msg(h
) == -1)
1131 /* Update the offset in the response packet based on av pairs count */
1132 h
->srvr_pos
= offsetof(struct tac_author_response
, rest
[0]) +
1135 /* Scan the optional fields in the response. */
1136 if (get_srvr_str(h
, "msg", &h
->srvr_msg
, ntohs(ares
->msg_len
)) == -1 ||
1137 get_srvr_str(h
, "data", &h
->srvr_data
, ntohs(ares
->data_len
)) ==-1)
1140 /* Get each AV pair (just setting pointers, not malloc'ing) */
1142 for (i
=0; i
<ares
->av_cnt
; i
++) {
1143 snprintf(dbgstr
, sizeof dbgstr
, "av-pair-%d", i
);
1144 if (get_srvr_str(h
, dbgstr
, &(h
->srvr_avs
[i
]),
1145 ares
->rest
[i
]) == -1)
1149 /* Should have ended up at the end */
1150 if (get_srvr_end(h
) == -1)
1154 if (!h
->single_connect
)
1155 close_connection(h
);
1157 return ares
->av_cnt
<< 8 | ares
->status
;
1161 tac_set_rem_addr(struct tac_handle
*h
, const char *addr
)
1163 return save_str(h
, &h
->rem_addr
, addr
, addr
!= NULL
? strlen(addr
) : 0);
1167 tac_set_data(struct tac_handle
*h
, const void *data
, size_t data_len
)
1169 return save_str(h
, &h
->data
, data
, data_len
);
1173 tac_set_msg(struct tac_handle
*h
, const char *msg
)
1175 return save_str(h
, &h
->user_msg
, msg
, msg
!= NULL
? strlen(msg
) : 0);
1179 tac_set_port(struct tac_handle
*h
, const char *port
)
1181 return save_str(h
, &h
->port
, port
, port
!= NULL
? strlen(port
) : 0);
1185 tac_set_priv(struct tac_handle
*h
, int priv
)
1187 if (!(TAC_PRIV_LVL_MIN
<= priv
&& priv
<= TAC_PRIV_LVL_MAX
)) {
1188 generr(h
, "Attempt to set invalid privilege level");
1191 h
->request
.u
.authen_start
.priv_lvl
= priv
;
1196 tac_set_user(struct tac_handle
*h
, const char *user
)
1198 return save_str(h
, &h
->user
, user
, user
!= NULL
? strlen(user
) : 0);
1202 tac_set_av(struct tac_handle
*h
, u_int index
, const char *av
)
1204 if (index
>= MAXAVPAIRS
)
1206 return save_str(h
, &(h
->avs
[index
]), av
, av
!= NULL
? strlen(av
) : 0);
1210 tac_get_av(struct tac_handle
*h
, u_int index
)
1212 if (index
>= MAXAVPAIRS
)
1214 return dup_str(h
, &(h
->srvr_avs
[index
]), NULL
);
1218 tac_get_av_value(struct tac_handle
*h
, const char *attribute
)
1221 const char *ch
, *end
;
1222 const char *candidate
;
1224 int found_seperator
;
1225 struct srvr_str srvr
;
1227 if (attribute
== NULL
|| ((len
= strlen(attribute
)) == 0))
1230 for (i
=0; i
<MAXAVPAIRS
; i
++) {
1231 candidate
= h
->srvr_avs
[i
].data
;
1232 candidate_len
= h
->srvr_avs
[i
].len
;
1235 * Valid 'srvr_avs' guaranteed to be contiguous starting at
1236 * index 0 (not necessarily the case with 'avs'). Break out
1237 * when the "end" of the list has been reached.
1242 if (len
< candidate_len
&&
1243 !strncmp(candidate
, attribute
, len
)) {
1245 ch
= candidate
+ len
;
1246 end
= candidate
+ candidate_len
;
1249 * Sift out the white space between A and V (should not
1250 * be any, but don't trust implementation of server...)
1252 found_seperator
= 0;
1253 while ((*ch
== '=' || *ch
== '*' || *ch
== ' ' ||
1254 *ch
== '\t') && ch
!= end
) {
1255 if (*ch
== '=' || *ch
== '*')
1262 * The case of 'attribute' == "foo" and
1263 * h->srvr_avs[0] = "foobie=var1"
1264 * h->srvr_avs[1] = "foo=var2"
1267 if (found_seperator
== 1 && ch
!= end
) {
1268 srvr
.len
= end
- ch
;
1270 return dup_str(h
, &srvr
, NULL
);
1278 tac_clear_avs(struct tac_handle
*h
)
1281 for (i
=0; i
<MAXAVPAIRS
; i
++)
1282 save_str(h
, &(h
->avs
[i
]), NULL
, 0);
1286 clear_srvr_avs(struct tac_handle
*h
)
1289 for (i
=0; i
<MAXAVPAIRS
; i
++)
1290 init_srvr_str(&(h
->srvr_avs
[i
]));
1295 tac_strerror(struct tac_handle
*h
)
1301 xmalloc(struct tac_handle
*h
, size_t size
)
1305 if ((r
= malloc(size
)) == NULL
)
1306 generr(h
, "Out of memory");
1311 xstrdup(struct tac_handle
*h
, const char *s
)
1315 if ((r
= strdup(s
)) == NULL
)
1316 generr(h
, "Out of memory");