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 $
27 * $DragonFly: src/lib/libtacplus/taclib.c,v 1.3 2004/08/19 23:25:58 joerg Exp $
30 #include <sys/param.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
:
219 return TAC_VER_MAJOR
<< 4 | minor
;
224 close_connection(struct tac_handle
*h
)
233 conn_server(struct tac_handle
*h
)
235 const struct tac_server
*srvp
= &h
->servers
[h
->cur_server
];
238 if ((h
->fd
= socket(PF_INET
, SOCK_STREAM
, IPPROTO_TCP
)) == -1) {
239 generr(h
, "Cannot create socket: %s", strerror(errno
));
242 if ((flags
= fcntl(h
->fd
, F_GETFL
, 0)) == -1 ||
243 fcntl(h
->fd
, F_SETFL
, flags
| O_NONBLOCK
) == -1) {
244 generr(h
, "Cannot set non-blocking mode on socket: %s",
250 if (connect(h
->fd
, (struct sockaddr
*)&srvp
->addr
,
251 sizeof srvp
->addr
) == 0)
254 if (errno
== EINPROGRESS
) {
258 struct sockaddr peer
;
263 /* Wait for the connection to complete. */
265 FD_SET(h
->fd
, &wfds
);
266 tv
.tv_sec
= srvp
->timeout
;
268 nfds
= select(h
->fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
270 generr(h
, "select: %s", strerror(errno
));
276 generr(h
, "connect: timed out");
282 /* See whether we are connected now. */
283 peerlen
= sizeof peer
;
284 if (getpeername(h
->fd
, &peer
, &peerlen
) == 0)
287 if (errno
!= ENOTCONN
) {
288 generr(h
, "getpeername: %s", strerror(errno
));
294 /* Find out why the connect failed. */
296 getsockopt(h
->fd
, SOL_SOCKET
, SO_ERROR
, &err
, &errlen
);
299 generr(h
, "connect: %s", strerror(errno
));
306 * Encrypt or decrypt a message. The operations are symmetrical.
309 crypt_msg(struct tac_handle
*h
, struct tac_msg
*msg
)
314 unsigned char md5
[16];
318 secret
= h
->servers
[h
->cur_server
].secret
;
319 if (secret
[0] == '\0')
320 msg
->flags
|= TAC_UNENCRYPTED
;
321 if (msg
->flags
& TAC_UNENCRYPTED
)
324 msg_len
= ntohl(msg
->length
);
327 MD5Update(&base_ctx
, msg
->session_id
, sizeof msg
->session_id
);
328 MD5Update(&base_ctx
, secret
, strlen(secret
));
329 MD5Update(&base_ctx
, &msg
->version
, sizeof msg
->version
);
330 MD5Update(&base_ctx
, &msg
->seq_no
, sizeof msg
->seq_no
);
333 for (chunk
= 0; chunk
< msg_len
; chunk
+= sizeof md5
) {
339 if ((chunk_len
= msg_len
- chunk
) > sizeof md5
)
340 chunk_len
= sizeof md5
;
341 for (i
= 0; i
< chunk_len
; i
++)
342 msg
->u
.body
[chunk
+ i
] ^= md5
[i
];
345 MD5Update(&ctx
, md5
, sizeof md5
);
350 * Return a dynamically allocated copy of the given server string.
351 * The copy is null-terminated. If "len" is non-NULL, the length of
352 * the string (excluding the terminating null byte) is stored via it.
353 * Returns NULL on failure. Empty strings are still allocated even
354 * though they have no content.
357 dup_str(struct tac_handle
*h
, const struct srvr_str
*ss
, size_t *len
)
361 if ((p
= (unsigned char *)xmalloc(h
, ss
->len
+ 1)) == NULL
)
363 if (ss
->data
!= NULL
&& ss
->len
!= 0)
364 memcpy(p
, ss
->data
, ss
->len
);
372 establish_connection(struct tac_handle
*h
)
376 if (h
->fd
>= 0) /* Already connected. */
378 if (h
->num_servers
== 0) {
379 generr(h
, "No TACACS+ servers specified");
383 * Try the servers round-robin. We begin with the one that
384 * worked for us the last time. That way, once we find a good
385 * server, we won't waste any more time trying the bad ones.
387 for (i
= 0; i
< h
->num_servers
; i
++) {
388 if (conn_server(h
) == 0) {
389 h
->single_connect
= (h
->servers
[h
->cur_server
].flags
&
390 TAC_SRVR_SINGLE_CONNECT
) != 0;
393 if (++h
->cur_server
>= h
->num_servers
) /* Wrap around */
396 /* Just return whatever error was last reported by conn_server(). */
401 * Free a client string, obliterating its contents first for security.
404 free_str(struct clnt_str
*cs
)
406 if (cs
->data
!= NULL
) {
407 memset(cs
->data
, 0, cs
->len
);
415 generr(struct tac_handle
*h
, const char *format
, ...)
419 va_start(ap
, format
);
420 vsnprintf(h
->errmsg
, ERRSIZE
, format
, ap
);
425 gen_session_id(struct tac_msg
*msg
)
430 msg
->session_id
[0] = r
>> 8;
431 msg
->session_id
[1] = r
;
433 msg
->session_id
[2] = r
>> 8;
434 msg
->session_id
[3] = r
;
438 * Verify that we are exactly at the end of the response message.
439 * Returns 0 on success, -1 on failure.
442 get_srvr_end(struct tac_handle
*h
)
446 len
= ntohl(h
->response
.length
);
448 if (h
->srvr_pos
!= len
) {
449 generr(h
, "Invalid length field in response "
450 "from server: end expected at %u, response length %u",
458 get_srvr_str(struct tac_handle
*h
, const char *field
,
459 struct srvr_str
*ss
, size_t len
)
461 if (h
->srvr_pos
+ len
> ntohl(h
->response
.length
)) {
462 generr(h
, "Invalid length field in %s response from server "
463 "(%lu > %lu)", field
, (u_long
)(h
->srvr_pos
+ len
),
464 (u_long
)ntohl(h
->response
.length
));
467 ss
->data
= len
!= 0 ? h
->response
.u
.body
+ h
->srvr_pos
: NULL
;
474 init_clnt_str(struct clnt_str
*cs
)
481 init_srvr_str(struct srvr_str
*ss
)
488 read_timed(struct tac_handle
*h
, void *buf
, size_t len
,
489 const struct timeval
*deadline
)
497 n
= read(h
->fd
, ptr
, len
);
502 if (errno
!= EAGAIN
) {
503 generr(h
, "Network read error: %s",
508 /* Wait until we can read more data. */
509 gettimeofday(&tv
, NULL
);
510 timersub(deadline
, &tv
, &tv
);
511 if (tv
.tv_sec
>= 0) {
515 FD_SET(h
->fd
, &rfds
);
517 select(h
->fd
+ 1, &rfds
, NULL
, NULL
, &tv
);
519 generr(h
, "select: %s",
526 generr(h
, "Network read timed out");
530 generr(h
, "unexpected EOF from server");
541 * Receive a response from the server and decrypt it. Returns 0 on
542 * success, or -1 on failure.
545 recv_msg(struct tac_handle
*h
)
547 struct timeval deadline
;
552 gettimeofday(&deadline
, NULL
);
553 deadline
.tv_sec
+= h
->servers
[h
->cur_server
].timeout
;
555 /* Read the message header and make sure it is reasonable. */
556 if (read_timed(h
, msg
, HDRSIZE
, &deadline
) == -1)
558 if (memcmp(msg
->session_id
, h
->request
.session_id
,
559 sizeof msg
->session_id
) != 0) {
560 generr(h
, "Invalid session ID in received message");
563 if (msg
->type
!= h
->request
.type
) {
564 generr(h
, "Invalid type in received message"
565 " (got %u, expected %u)",
566 msg
->type
, h
->request
.type
);
569 len
= ntohl(msg
->length
);
570 if (len
> BODYSIZE
) {
571 generr(h
, "Received message too large (%u > %u)",
575 if (msg
->seq_no
!= ++h
->last_seq_no
) {
576 generr(h
, "Invalid sequence number in received message"
577 " (got %u, expected %u)",
578 msg
->seq_no
, h
->last_seq_no
);
582 /* Read the message body. */
583 if (read_timed(h
, msg
->u
.body
, len
, &deadline
) == -1)
590 * Turn off single-connection mode if the server isn't amenable
593 if (!(msg
->flags
& TAC_SINGLE_CONNECT
))
594 h
->single_connect
= 0;
599 save_str(struct tac_handle
*h
, struct clnt_str
*cs
, const void *data
,
603 if (data
!= NULL
&& len
!= 0) {
604 if ((cs
->data
= xmalloc(h
, len
)) == NULL
)
607 memcpy(cs
->data
, data
, len
);
613 * Send the current request, after encrypting it. Returns 0 on success,
617 send_msg(struct tac_handle
*h
)
619 struct timeval deadline
;
624 if (h
->last_seq_no
& 1) {
625 generr(h
, "Attempt to send message out of sequence");
629 if (establish_connection(h
) == -1)
633 msg
->seq_no
= ++h
->last_seq_no
;
634 if (msg
->seq_no
== 1)
638 if (h
->single_connect
)
639 msg
->flags
|= TAC_SINGLE_CONNECT
;
641 msg
->flags
&= ~TAC_SINGLE_CONNECT
;
642 gettimeofday(&deadline
, NULL
);
643 deadline
.tv_sec
+= h
->servers
[h
->cur_server
].timeout
;
644 len
= HDRSIZE
+ ntohl(msg
->length
);
649 n
= write(h
->fd
, ptr
, len
);
654 if (errno
!= EAGAIN
) {
655 generr(h
, "Network write error: %s",
660 /* Wait until we can write more data. */
661 gettimeofday(&tv
, NULL
);
662 timersub(&deadline
, &tv
, &tv
);
663 if (tv
.tv_sec
>= 0) {
667 FD_SET(h
->fd
, &wfds
);
669 select(h
->fd
+ 1, NULL
, &wfds
, NULL
, &tv
);
671 generr(h
, "select: %s",
678 generr(h
, "Network write timed out");
690 * Destructively split a string into fields separated by white space.
691 * `#' at the beginning of a field begins a comment that extends to the
692 * end of the string. Fields may be quoted with `"'. Inside quoted
693 * strings, the backslash escapes `\"' and `\\' are honored.
695 * Pointers to up to the first maxfields fields are stored in the fields
696 * array. Missing fields get NULL pointers.
698 * The return value is the actual number of fields parsed, and is always
701 * On a syntax error, places a message in the msg string, and returns -1.
704 split(char *str
, char *fields
[], int maxfields
, char *msg
, size_t msglen
)
708 static const char ws
[] = " \t";
710 for (i
= 0; i
< maxfields
; i
++)
716 if (*p
== '#' || *p
== '\0')
718 if (i
>= maxfields
) {
719 snprintf(msg
, msglen
, "line has too many fields");
730 if (*p
!= '"' && *p
!= '\\' &&
732 snprintf(msg
, msglen
,
733 "invalid `\\' escape");
738 snprintf(msg
, msglen
,
739 "unterminated quoted string");
746 if (*p
!= '\0' && strspn(p
, ws
) == 0) {
747 snprintf(msg
, msglen
, "quoted string not"
748 " followed by white space");
763 tac_add_server(struct tac_handle
*h
, const char *host
, int port
,
764 const char *secret
, int timeout
, int flags
)
766 struct tac_server
*srvp
;
768 if (h
->num_servers
>= MAXSERVERS
) {
769 generr(h
, "Too many TACACS+ servers specified");
772 srvp
= &h
->servers
[h
->num_servers
];
774 memset(&srvp
->addr
, 0, sizeof srvp
->addr
);
775 srvp
->addr
.sin_len
= sizeof srvp
->addr
;
776 srvp
->addr
.sin_family
= AF_INET
;
777 if (!inet_aton(host
, &srvp
->addr
.sin_addr
)) {
778 struct hostent
*hent
;
780 if ((hent
= gethostbyname(host
)) == NULL
) {
781 generr(h
, "%s: host not found", host
);
784 memcpy(&srvp
->addr
.sin_addr
, hent
->h_addr
,
785 sizeof srvp
->addr
.sin_addr
);
787 srvp
->addr
.sin_port
= htons(port
!= 0 ? port
: TACPLUS_PORT
);
788 if ((srvp
->secret
= xstrdup(h
, secret
)) == NULL
)
790 srvp
->timeout
= timeout
;
797 tac_close(struct tac_handle
*h
)
803 for (srv
= 0; srv
< h
->num_servers
; srv
++) {
804 memset(h
->servers
[srv
].secret
, 0,
805 strlen(h
->servers
[srv
].secret
));
806 free(h
->servers
[srv
].secret
);
810 free_str(&h
->rem_addr
);
812 free_str(&h
->user_msg
);
813 for (i
=0; i
<MAXAVPAIRS
; i
++)
814 free_str(&(h
->avs
[i
]));
816 /* Clear everything else before freeing memory */
817 memset(h
, 0, sizeof(struct tac_handle
));
822 tac_config(struct tac_handle
*h
, const char *path
)
825 char buf
[MAXCONFLINE
];
830 path
= PATH_TACPLUS_CONF
;
831 if ((fp
= fopen(path
, "r")) == NULL
) {
832 generr(h
, "Cannot open \"%s\": %s", path
, strerror(errno
));
837 while (fgets(buf
, sizeof buf
, fp
) != NULL
) {
848 unsigned long timeout
;
854 /* We know len > 0, else fgets would have returned NULL. */
855 if (buf
[len
- 1] != '\n') {
856 if (len
>= sizeof buf
- 1)
857 generr(h
, "%s:%d: line too long", path
,
860 generr(h
, "%s:%d: missing newline", path
,
867 /* Extract the fields from the line. */
868 nfields
= split(buf
, fields
, 4, msg
, sizeof msg
);
870 generr(h
, "%s:%d: %s", path
, linenum
, msg
);
877 generr(h
, "%s:%d: missing shared secret", path
,
884 timeout_str
= fields
[2];
885 options_str
= fields
[3];
887 /* Parse and validate the fields. */
889 host
= strsep(&res
, ":");
890 port_str
= strsep(&res
, ":");
891 if (port_str
!= NULL
) {
892 port
= strtoul(port_str
, &end
, 10);
893 if (port_str
[0] == '\0' || *end
!= '\0') {
894 generr(h
, "%s:%d: invalid port", path
,
901 if (timeout_str
!= NULL
) {
902 timeout
= strtoul(timeout_str
, &end
, 10);
903 if (timeout_str
[0] == '\0' || *end
!= '\0') {
904 generr(h
, "%s:%d: invalid timeout", path
,
912 if (options_str
!= NULL
) {
913 if (strcmp(options_str
, "single-connection") == 0)
914 options
|= TAC_SRVR_SINGLE_CONNECT
;
916 generr(h
, "%s:%d: invalid option \"%s\"",
917 path
, linenum
, options_str
);
923 if (tac_add_server(h
, host
, port
, secret
, timeout
,
927 strcpy(msg
, h
->errmsg
);
928 generr(h
, "%s:%d: %s", path
, linenum
, msg
);
933 /* Clear out the buffer to wipe a possible copy of a shared secret */
934 memset(buf
, 0, sizeof buf
);
940 tac_create_authen(struct tac_handle
*h
, int action
, int type
, int service
)
942 struct tac_authen_start
*as
;
944 create_msg(h
, TAC_AUTHEN
, action
, type
);
946 as
= &h
->request
.u
.authen_start
;
948 as
->priv_lvl
= TAC_PRIV_LVL_USER
;
949 as
->authen_type
= type
;
950 as
->service
= service
;
956 tac_create_author(struct tac_handle
*h
, int method
, int type
, int service
)
958 struct tac_author_request
*areq
;
960 create_msg(h
, TAC_AUTHOR
, method
, type
);
962 areq
= &h
->request
.u
.author_request
;
963 areq
->authen_meth
= method
;
964 areq
->priv_lvl
= TAC_PRIV_LVL_USER
;
965 areq
->authen_type
= type
;
966 areq
->service
= service
;
972 create_msg(struct tac_handle
*h
, int msg_type
, int var
, int type
)
980 msg
->type
= msg_type
;
981 msg
->version
= protocol_version(msg_type
, var
, type
);
982 msg
->flags
= 0; /* encrypted packet body */
986 free_str(&h
->rem_addr
);
988 free_str(&h
->user_msg
);
990 for (i
=0; i
<MAXAVPAIRS
; i
++)
991 free_str(&(h
->avs
[i
]));
995 tac_get_data(struct tac_handle
*h
, size_t *len
)
997 return dup_str(h
, &h
->srvr_data
, len
);
1001 tac_get_msg(struct tac_handle
*h
)
1003 return dup_str(h
, &h
->srvr_msg
, NULL
);
1007 * Create and initialize a tac_handle structure, and return it to the
1008 * caller. Can fail only if the necessary memory cannot be allocated.
1009 * In that case, it returns NULL.
1015 struct tac_handle
*h
;
1017 h
= (struct tac_handle
*)malloc(sizeof(struct tac_handle
));
1022 h
->errmsg
[0] = '\0';
1023 init_clnt_str(&h
->user
);
1024 init_clnt_str(&h
->port
);
1025 init_clnt_str(&h
->rem_addr
);
1026 init_clnt_str(&h
->data
);
1027 init_clnt_str(&h
->user_msg
);
1028 for (i
=0; i
<MAXAVPAIRS
; i
++) {
1029 init_clnt_str(&(h
->avs
[i
]));
1030 init_srvr_str(&(h
->srvr_avs
[i
]));
1032 init_srvr_str(&h
->srvr_msg
);
1033 init_srvr_str(&h
->srvr_data
);
1040 tac_send_authen(struct tac_handle
*h
)
1042 struct tac_authen_reply
*ar
;
1044 if (h
->num_servers
== 0)
1047 if (h
->last_seq_no
== 0) { /* Authentication START packet */
1048 struct tac_authen_start
*as
;
1050 as
= &h
->request
.u
.authen_start
;
1052 htonl(offsetof(struct tac_authen_start
, rest
[0]));
1053 if (add_str_8(h
, &as
->user_len
, &h
->user
) == -1 ||
1054 add_str_8(h
, &as
->port_len
, &h
->port
) == -1 ||
1055 add_str_8(h
, &as
->rem_addr_len
, &h
->rem_addr
) == -1 ||
1056 add_str_8(h
, &as
->data_len
, &h
->data
) == -1)
1058 } else { /* Authentication CONTINUE packet */
1059 struct tac_authen_cont
*ac
;
1061 ac
= &h
->request
.u
.authen_cont
;
1064 htonl(offsetof(struct tac_authen_cont
, rest
[0]));
1065 if (add_str_16(h
, &ac
->user_msg_len
, &h
->user_msg
) == -1 ||
1066 add_str_16(h
, &ac
->data_len
, &h
->data
) == -1)
1070 /* Send the message and retrieve the reply. */
1071 if (send_msg(h
) == -1 || recv_msg(h
) == -1)
1074 /* Scan the optional fields in the reply. */
1075 ar
= &h
->response
.u
.authen_reply
;
1076 h
->srvr_pos
= offsetof(struct tac_authen_reply
, rest
[0]);
1077 if (get_srvr_str(h
, "msg", &h
->srvr_msg
, ntohs(ar
->msg_len
)) == -1 ||
1078 get_srvr_str(h
, "data", &h
->srvr_data
, ntohs(ar
->data_len
)) == -1 ||
1079 get_srvr_end(h
) == -1)
1082 if (!h
->single_connect
&&
1083 ar
->status
!= TAC_AUTHEN_STATUS_GETDATA
&&
1084 ar
->status
!= TAC_AUTHEN_STATUS_GETUSER
&&
1085 ar
->status
!= TAC_AUTHEN_STATUS_GETPASS
)
1086 close_connection(h
);
1088 return ar
->flags
<< 8 | ar
->status
;
1092 tac_send_author(struct tac_handle
*h
)
1096 struct tac_author_request
*areq
= &h
->request
.u
.author_request
;
1097 struct tac_author_response
*ares
= &h
->response
.u
.author_response
;
1100 htonl(offsetof(struct tac_author_request
, rest
[0]));
1102 /* Count each specified AV pair */
1103 for (areq
->av_cnt
=0, i
=0; i
<MAXAVPAIRS
; i
++)
1104 if (h
->avs
[i
].len
&& h
->avs
[i
].data
)
1108 * Each AV size is a byte starting right after 'av_cnt'. Update the
1109 * offset to include these AV sizes.
1111 h
->request
.length
= ntohl(htonl(h
->request
.length
) + areq
->av_cnt
);
1113 /* Now add the string arguments from 'h' */
1114 if (add_str_8(h
, &areq
->user_len
, &h
->user
) == -1 ||
1115 add_str_8(h
, &areq
->port_len
, &h
->port
) == -1 ||
1116 add_str_8(h
, &areq
->rem_addr_len
, &h
->rem_addr
) == -1)
1119 /* Add each AV pair, the size of each placed in areq->rest[current] */
1120 for (current
=0, i
=0; i
<MAXAVPAIRS
; i
++) {
1121 if (h
->avs
[i
].len
&& h
->avs
[i
].data
) {
1122 if (add_str_8(h
, &areq
->rest
[current
++],
1123 &(h
->avs
[i
])) == -1)
1128 /* Send the message and retrieve the reply. */
1129 if (send_msg(h
) == -1 || recv_msg(h
) == -1)
1132 /* Update the offset in the response packet based on av pairs count */
1133 h
->srvr_pos
= offsetof(struct tac_author_response
, rest
[0]) +
1136 /* Scan the optional fields in the response. */
1137 if (get_srvr_str(h
, "msg", &h
->srvr_msg
, ntohs(ares
->msg_len
)) == -1 ||
1138 get_srvr_str(h
, "data", &h
->srvr_data
, ntohs(ares
->data_len
)) ==-1)
1141 /* Get each AV pair (just setting pointers, not malloc'ing) */
1143 for (i
=0; i
<ares
->av_cnt
; i
++) {
1144 snprintf(dbgstr
, sizeof dbgstr
, "av-pair-%d", i
);
1145 if (get_srvr_str(h
, dbgstr
, &(h
->srvr_avs
[i
]),
1146 ares
->rest
[i
]) == -1)
1150 /* Should have ended up at the end */
1151 if (get_srvr_end(h
) == -1)
1155 if (!h
->single_connect
)
1156 close_connection(h
);
1158 return ares
->av_cnt
<< 8 | ares
->status
;
1162 tac_set_rem_addr(struct tac_handle
*h
, const char *addr
)
1164 return save_str(h
, &h
->rem_addr
, addr
, addr
!= NULL
? strlen(addr
) : 0);
1168 tac_set_data(struct tac_handle
*h
, const void *data
, size_t data_len
)
1170 return save_str(h
, &h
->data
, data
, data_len
);
1174 tac_set_msg(struct tac_handle
*h
, const char *msg
)
1176 return save_str(h
, &h
->user_msg
, msg
, msg
!= NULL
? strlen(msg
) : 0);
1180 tac_set_port(struct tac_handle
*h
, const char *port
)
1182 return save_str(h
, &h
->port
, port
, port
!= NULL
? strlen(port
) : 0);
1186 tac_set_priv(struct tac_handle
*h
, int priv
)
1188 if (!(TAC_PRIV_LVL_MIN
<= priv
&& priv
<= TAC_PRIV_LVL_MAX
)) {
1189 generr(h
, "Attempt to set invalid privilege level");
1192 h
->request
.u
.authen_start
.priv_lvl
= priv
;
1197 tac_set_user(struct tac_handle
*h
, const char *user
)
1199 return save_str(h
, &h
->user
, user
, user
!= NULL
? strlen(user
) : 0);
1203 tac_set_av(struct tac_handle
*h
, u_int index
, const char *av
)
1205 if (index
>= MAXAVPAIRS
)
1207 return save_str(h
, &(h
->avs
[index
]), av
, av
!= NULL
? strlen(av
) : 0);
1211 tac_get_av(struct tac_handle
*h
, u_int index
)
1213 if (index
>= MAXAVPAIRS
)
1215 return dup_str(h
, &(h
->srvr_avs
[index
]), NULL
);
1219 tac_get_av_value(struct tac_handle
*h
, const char *attribute
)
1222 const char *ch
, *end
;
1223 const char *candidate
;
1225 int found_seperator
;
1226 struct srvr_str srvr
;
1228 if (attribute
== NULL
|| ((len
= strlen(attribute
)) == 0))
1231 for (i
=0; i
<MAXAVPAIRS
; i
++) {
1232 candidate
= h
->srvr_avs
[i
].data
;
1233 candidate_len
= h
->srvr_avs
[i
].len
;
1236 * Valid 'srvr_avs' guaranteed to be contiguous starting at
1237 * index 0 (not necessarily the case with 'avs'). Break out
1238 * when the "end" of the list has been reached.
1243 if (len
< candidate_len
&&
1244 !strncmp(candidate
, attribute
, len
)) {
1246 ch
= candidate
+ len
;
1247 end
= candidate
+ candidate_len
;
1250 * Sift out the white space between A and V (should not
1251 * be any, but don't trust implementation of server...)
1253 found_seperator
= 0;
1254 while ((*ch
== '=' || *ch
== '*' || *ch
== ' ' ||
1255 *ch
== '\t') && ch
!= end
) {
1256 if (*ch
== '=' || *ch
== '*')
1263 * The case of 'attribute' == "foo" and
1264 * h->srvr_avs[0] = "foobie=var1"
1265 * h->srvr_avs[1] = "foo=var2"
1268 if (found_seperator
== 1 && ch
!= end
) {
1269 srvr
.len
= end
- ch
;
1271 return dup_str(h
, &srvr
, NULL
);
1279 tac_clear_avs(struct tac_handle
*h
)
1282 for (i
=0; i
<MAXAVPAIRS
; i
++)
1283 save_str(h
, &(h
->avs
[i
]), NULL
, 0);
1287 clear_srvr_avs(struct tac_handle
*h
)
1290 for (i
=0; i
<MAXAVPAIRS
; i
++)
1291 init_srvr_str(&(h
->srvr_avs
[i
]));
1296 tac_strerror(struct tac_handle
*h
)
1302 xmalloc(struct tac_handle
*h
, size_t size
)
1306 if ((r
= malloc(size
)) == NULL
)
1307 generr(h
, "Out of memory");
1312 xstrdup(struct tac_handle
*h
, const char *s
)
1316 if ((r
= strdup(s
)) == NULL
)
1317 generr(h
, "Out of memory");