taclib.c revision 65222
1132720Skan/*-
2132720Skan * Copyright 1998 Juniper Networks, Inc.
3132720Skan * All rights reserved.
4132720Skan *
5132720Skan * Redistribution and use in source and binary forms, with or without
6132720Skan * modification, are permitted provided that the following conditions
7132720Skan * are met:
8132720Skan * 1. Redistributions of source code must retain the above copyright
9132720Skan *    notice, this list of conditions and the following disclaimer.
10132720Skan * 2. Redistributions in binary form must reproduce the above copyright
11132720Skan *    notice, this list of conditions and the following disclaimer in the
12132720Skan *    documentation and/or other materials provided with the distribution.
13132720Skan *
14132720Skan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15132720Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16132720Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132720Skan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18132720Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19132720Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20132720Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21132720Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22132720Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23132720Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24132720Skan * SUCH DAMAGE.
25132720Skan *
26132720Skan *	$FreeBSD: head/lib/libtacplus/taclib.c 65222 2000-08-29 21:49:11Z ache $
27132720Skan */
28132720Skan
29132720Skan#include <sys/types.h>
30169691Skan#include <sys/socket.h>
31169691Skan#include <sys/time.h>
32169691Skan#include <netinet/in.h>
33169691Skan#include <arpa/inet.h>
34169691Skan
35132720Skan#include <assert.h>
36169691Skan#include <errno.h>
37169691Skan#include <fcntl.h>
38169691Skan#include <md5.h>
39169691Skan#include <netdb.h>
40169691Skan#include <stdarg.h>
41169691Skan#include <stddef.h>
42169691Skan#include <stdio.h>
43169691Skan#include <stdlib.h>
44169691Skan#include <string.h>
45169691Skan#include <unistd.h>
46169691Skan
47169691Skan#include "taclib_private.h"
48132720Skan
49132720Skanstatic int		 add_str_8(struct tac_handle *, u_int8_t *,
50132720Skan			    struct clnt_str *);
51132720Skanstatic int		 add_str_16(struct tac_handle *, u_int16_t *,
52132720Skan			    struct clnt_str *);
53132720Skanstatic int		 authen_version(int, int);
54132720Skanstatic void		 close_connection(struct tac_handle *);
55132720Skanstatic int		 conn_server(struct tac_handle *);
56132720Skanstatic void		 crypt_msg(struct tac_handle *, struct tac_msg *);
57132720Skanstatic void		*dup_str(struct tac_handle *, const struct srvr_str *,
58132720Skan			    size_t *);
59132720Skanstatic int		 establish_connection(struct tac_handle *);
60132720Skanstatic void		 free_str(struct clnt_str *);
61132720Skanstatic void		 generr(struct tac_handle *, const char *, ...)
62132720Skan			    __printflike(2, 3);
63132720Skanstatic void		 gen_session_id(struct tac_msg *);
64132720Skanstatic int		 get_srvr_end(struct tac_handle *);
65132720Skanstatic int		 get_srvr_str(struct tac_handle *, struct srvr_str *,
66132720Skan			    size_t);
67132720Skanstatic void		 init_clnt_str(struct clnt_str *);
68132720Skanstatic void		 init_srvr_str(struct srvr_str *);
69132720Skanstatic int		 read_timed(struct tac_handle *, void *, size_t,
70169691Skan			    const struct timeval *);
71132720Skanstatic int		 recv_msg(struct tac_handle *);
72132720Skanstatic int		 save_str(struct tac_handle *, struct clnt_str *,
73132720Skan			    const void *, size_t);
74132720Skanstatic int		 send_msg(struct tac_handle *);
75132720Skanstatic int		 split(char *, char *[], int, char *, size_t);
76132720Skanstatic void		*xmalloc(struct tac_handle *, size_t);
77132720Skanstatic char		*xstrdup(struct tac_handle *, const char *);
78132720Skan
79132720Skan/*
80169691Skan * Append some optional data to the current request, and store its
81132720Skan * length into the 8-bit field referenced by "fld".  Returns 0 on
82132720Skan * success, or -1 on failure.
83132720Skan *
84132720Skan * This function also frees the "cs" string data and initializes it
85132720Skan * for the next time.
86169691Skan */
87132720Skanstatic int
88132720Skanadd_str_8(struct tac_handle *h, u_int8_t *fld, struct clnt_str *cs)
89132720Skan{
90132720Skan	u_int16_t len;
91132720Skan
92169691Skan	if (add_str_16(h, &len, cs) == -1)
93169691Skan		return -1;
94169691Skan	len = ntohs(len);
95169691Skan	if (len > 0xff) {
96169691Skan		generr(h, "Field too long");
97169691Skan		return -1;
98169691Skan	}
99169691Skan	*fld = len;
100169691Skan	return 0;
101169691Skan}
102169691Skan
103169691Skan/*
104169691Skan * Append some optional data to the current request, and store its
105169691Skan * length into the 16-bit field (network byte order) referenced by
106169691Skan * "fld".  Returns 0 on success, or -1 on failure.
107132720Skan *
108169691Skan * This function also frees the "cs" string data and initializes it
109169691Skan * for the next time.
110132720Skan */
111132720Skanstatic int
112132720Skanadd_str_16(struct tac_handle *h, u_int16_t *fld, struct clnt_str *cs)
113132720Skan{
114132720Skan	size_t len;
115132720Skan
116132720Skan	len = cs->len;
117132720Skan	if (cs->data == NULL)
118132720Skan		len = 0;
119132720Skan	if (len != 0) {
120132720Skan		int offset;
121132720Skan
122132720Skan		if (len > 0xffff) {
123132720Skan			generr(h, "Field too long");
124132720Skan			return -1;
125132720Skan		}
126132720Skan		offset = ntohl(h->request.length);
127132720Skan		if (offset + len > BODYSIZE) {
128132720Skan			generr(h, "Message too long");
129132720Skan			return -1;
130132720Skan		}
131132720Skan		memcpy(h->request.u.body + offset, cs->data, len);
132132720Skan		h->request.length = htonl(offset + len);
133132720Skan	}
134132720Skan	*fld = htons(len);
135132720Skan	free_str(cs);
136132720Skan	return 0;
137132720Skan}
138132720Skan
139132720Skanstatic int
140132720Skanauthen_version(int action, int type)
141132720Skan{
142132720Skan	int minor;
143132720Skan
144132720Skan	switch (action) {
145132720Skan
146132720Skan	case TAC_AUTHEN_LOGIN:
147132720Skan		switch (type) {
148132720Skan
149132720Skan		case TAC_AUTHEN_TYPE_PAP:
150132720Skan		case TAC_AUTHEN_TYPE_CHAP:
151132720Skan		case TAC_AUTHEN_TYPE_MSCHAP:
152132720Skan		case TAC_AUTHEN_TYPE_ARAP:
153132720Skan			minor = 1;
154132720Skan			break;
155169691Skan
156132720Skan		default:
157132720Skan			minor = 0;
158132720Skan			break;
159132720Skan		}
160132720Skan		break;
161132720Skan
162132720Skan	case TAC_AUTHEN_SENDAUTH:
163132720Skan		minor = 1;
164132720Skan		break;
165132720Skan
166132720Skan	default:
167132720Skan		minor = 0;
168132720Skan		break;
169132720Skan	};
170132720Skan
171132720Skan	return TAC_VER_MAJOR << 4 | minor;
172132720Skan}
173132720Skan
174132720Skanstatic void
175132720Skanclose_connection(struct tac_handle *h)
176132720Skan{
177132720Skan	if (h->fd != -1) {
178132720Skan		close(h->fd);
179132720Skan		h->fd = -1;
180132720Skan	}
181132720Skan}
182132720Skan
183132720Skanstatic int
184132720Skanconn_server(struct tac_handle *h)
185132720Skan{
186132720Skan	const struct tac_server *srvp = &h->servers[h->cur_server];
187132720Skan	int flags;
188132720Skan
189132720Skan	if ((h->fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
190132720Skan		generr(h, "Cannot create socket: %s", strerror(errno));
191132720Skan		return -1;
192132720Skan	}
193132720Skan	if ((flags = fcntl(h->fd, F_GETFL, 0)) == -1 ||
194132720Skan	    fcntl(h->fd, F_SETFL, flags | O_NONBLOCK) == -1) {
195132720Skan		generr(h, "Cannot set non-blocking mode on socket: %s",
196132720Skan		    strerror(errno));
197132720Skan		close(h->fd);
198132720Skan		h->fd = -1;
199132720Skan		return -1;
200132720Skan	}
201132720Skan	if (connect(h->fd, (struct sockaddr *)&srvp->addr,
202169691Skan	    sizeof srvp->addr) == 0)
203132720Skan		return 0;
204132720Skan
205132720Skan	if (errno == EINPROGRESS) {
206132720Skan		fd_set wfds;
207132720Skan		struct timeval tv;
208132720Skan		int nfds;
209132720Skan		struct sockaddr peer;
210132720Skan		int peerlen;
211132720Skan		int err;
212132720Skan		int errlen;
213132720Skan
214132720Skan		/* Wait for the connection to complete. */
215132720Skan		FD_ZERO(&wfds);
216132720Skan		FD_SET(h->fd, &wfds);
217132720Skan		tv.tv_sec = srvp->timeout;
218132720Skan		tv.tv_usec = 0;
219132720Skan		nfds = select(h->fd + 1, NULL, &wfds, NULL, &tv);
220132720Skan		if (nfds == -1) {
221132720Skan			generr(h, "select: %s", strerror(errno));
222132720Skan			close(h->fd);
223132720Skan			h->fd = -1;
224132720Skan			return -1;
225132720Skan		}
226132720Skan		if (nfds == 0) {
227132720Skan			generr(h, "connect: timed out");
228132720Skan			close(h->fd);
229132720Skan			h->fd = -1;
230132720Skan			return -1;
231132720Skan		}
232132720Skan
233132720Skan		/* See whether we are connected now. */
234132720Skan		peerlen = sizeof peer;
235132720Skan		if (getpeername(h->fd, &peer, &peerlen) == 0)
236132720Skan			return 0;
237132720Skan
238132720Skan		if (errno != ENOTCONN) {
239132720Skan			generr(h, "getpeername: %s", strerror(errno));
240132720Skan			close(h->fd);
241132720Skan			h->fd = -1;
242146897Skan			return -1;
243132720Skan		}
244132720Skan
245132720Skan		/* Find out why the connect failed. */
246132720Skan		errlen = sizeof err;
247132720Skan		getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &err, &errlen);
248132720Skan		errno = err;
249132720Skan	}
250132720Skan	generr(h, "connect: %s", strerror(errno));
251132720Skan	close(h->fd);
252132720Skan	h->fd = -1;
253132720Skan	return -1;
254132720Skan}
255132720Skan
256132720Skan/*
257132720Skan * Encrypt or decrypt a message.  The operations are symmetrical.
258132720Skan */
259132720Skanstatic void
260132720Skancrypt_msg(struct tac_handle *h, struct tac_msg *msg)
261169691Skan{
262132720Skan	const char *secret;
263132720Skan	MD5_CTX base_ctx;
264132720Skan	MD5_CTX ctx;
265132720Skan	unsigned char md5[16];
266132720Skan	int chunk;
267132720Skan	int msg_len;
268132720Skan
269132720Skan	secret = h->servers[h->cur_server].secret;
270132720Skan	if (secret[0] == '\0')
271132720Skan		msg->flags |= TAC_UNENCRYPTED;
272146897Skan	if (msg->flags & TAC_UNENCRYPTED)
273132720Skan		return;
274132720Skan
275132720Skan	msg_len = ntohl(msg->length);
276132720Skan
277132720Skan	MD5Init(&base_ctx);
278132720Skan	MD5Update(&base_ctx, msg->session_id, sizeof msg->session_id);
279132720Skan	MD5Update(&base_ctx, secret, strlen(secret));
280132720Skan	MD5Update(&base_ctx, &msg->version, sizeof msg->version);
281132720Skan	MD5Update(&base_ctx, &msg->seq_no, sizeof msg->seq_no);
282132720Skan
283132720Skan	ctx = base_ctx;
284132720Skan	for (chunk = 0;  chunk < msg_len;  chunk += sizeof md5) {
285132720Skan		int chunk_len;
286132720Skan		int i;
287132720Skan
288132720Skan		MD5Final(md5, &ctx);
289132720Skan
290132720Skan		if ((chunk_len = msg_len - chunk) > sizeof md5)
291132720Skan			chunk_len = sizeof md5;
292132720Skan		for (i = 0;  i < chunk_len;  i++)
293132720Skan			msg->u.body[chunk + i] ^= md5[i];
294132720Skan
295132720Skan		ctx = base_ctx;
296132720Skan		MD5Update(&ctx, md5, sizeof md5);
297132720Skan	}
298132720Skan}
299132720Skan
300132720Skan/*
301132720Skan * Return a dynamically allocated copy of the given server string.
302132720Skan * The copy is null-terminated.  If "len" is non-NULL, the length of
303132720Skan * the string (excluding the terminating null byte) is stored via it.
304132720Skan * Returns NULL on failure.  Empty strings are still allocated even
305132720Skan * though they have no content.
306132720Skan */
307132720Skanstatic void *
308132720Skandup_str(struct tac_handle *h, const struct srvr_str *ss, size_t *len)
309132720Skan{
310132720Skan	unsigned char *p;
311132720Skan
312132720Skan	if ((p = (unsigned char *)xmalloc(h, ss->len + 1)) == NULL)
313132720Skan		return NULL;
314132720Skan	if (ss->data != NULL && ss->len != 0)
315132720Skan		memcpy(p, ss->data, ss->len);
316132720Skan	p[ss->len] = '\0';
317132720Skan	if (len != NULL)
318132720Skan		*len = ss->len;
319132720Skan	return p;
320132720Skan}
321132720Skan
322132720Skanstatic int
323132720Skanestablish_connection(struct tac_handle *h)
324132720Skan{
325132720Skan	int i;
326132720Skan
327132720Skan	if (h->fd >= 0)		/* Already connected. */
328132720Skan		return 0;
329132720Skan	if (h->num_servers == 0) {
330169691Skan		generr(h, "No TACACS+ servers specified");
331169691Skan		return -1;
332132720Skan	}
333169691Skan	/*
334132720Skan         * Try the servers round-robin.  We begin with the one that
335169691Skan         * worked for us the last time.  That way, once we find a good
336132720Skan         * server, we won't waste any more time trying the bad ones.
337132720Skan	 */
338132720Skan	for (i = 0;  i < h->num_servers;  i++) {
339132720Skan		if (conn_server(h) == 0) {
340132720Skan			h->single_connect = (h->servers[h->cur_server].flags &
341132720Skan			    TAC_SRVR_SINGLE_CONNECT) != 0;
342132720Skan			return 0;
343132720Skan		}
344132720Skan		if (++h->cur_server >= h->num_servers)	/* Wrap around */
345132720Skan			h->cur_server = 0;
346132720Skan	}
347132720Skan	/* Just return whatever error was last reported by conn_server(). */
348132720Skan	return -1;
349132720Skan}
350132720Skan
351132720Skan/*
352132720Skan * Free a client string, obliterating its contents first for security.
353132720Skan */
354132720Skanstatic void
355132720Skanfree_str(struct clnt_str *cs)
356132720Skan{
357132720Skan	if (cs->data != NULL) {
358132720Skan		memset(cs->data, 0, cs->len);
359132720Skan		free(cs->data);
360132720Skan		cs->data = NULL;
361132720Skan		cs->len = 0;
362132720Skan	}
363132720Skan}
364132720Skan
365132720Skanstatic void
366132720Skangenerr(struct tac_handle *h, const char *format, ...)
367132720Skan{
368132720Skan	va_list		 ap;
369132720Skan
370132720Skan	va_start(ap, format);
371132720Skan	vsnprintf(h->errmsg, ERRSIZE, format, ap);
372132720Skan	va_end(ap);
373132720Skan}
374132720Skan
375132720Skanstatic void
376132720Skangen_session_id(struct tac_msg *msg)
377132720Skan{
378132720Skan	int r;
379132720Skan
380132720Skan	r = random();
381132720Skan	msg->session_id[0] = r >> 8;
382132720Skan	msg->session_id[1] = r;
383132720Skan	r = random();
384132720Skan	msg->session_id[2] = r >> 8;
385132720Skan	msg->session_id[3] = r;
386132720Skan}
387132720Skan
388132720Skan/*
389132720Skan * Verify that we are exactly at the end of the response message.
390132720Skan * Returns 0 on success, -1 on failure.
391132720Skan */
392132720Skanstatic int
393132720Skanget_srvr_end(struct tac_handle *h)
394132720Skan{
395132720Skan	if (h->srvr_pos != ntohl(h->response.length)) {
396132720Skan		generr(h, "Invalid length field in response from server");
397132720Skan		return -1;
398132720Skan	}
399132720Skan	return 0;
400132720Skan}
401132720Skan
402132720Skanstatic int
403132720Skanget_srvr_str(struct tac_handle *h, struct srvr_str *ss, size_t len)
404132720Skan{
405132720Skan	if (h->srvr_pos + len > ntohl(h->response.length)) {
406132720Skan		generr(h, "Invalid length field in response from server");
407132720Skan		return -1;
408132720Skan	}
409132720Skan	ss->data = len != 0 ? h->response.u.body + h->srvr_pos : NULL;
410132720Skan	ss->len = len;
411132720Skan	h->srvr_pos += len;
412132720Skan	return 0;
413132720Skan}
414132720Skan
415132720Skanstatic void
416132720Skaninit_clnt_str(struct clnt_str *cs)
417132720Skan{
418132720Skan	cs->data = NULL;
419132720Skan	cs->len = 0;
420132720Skan}
421132720Skan
422132720Skanstatic void
423132720Skaninit_srvr_str(struct srvr_str *ss)
424132720Skan{
425132720Skan	ss->data = NULL;
426132720Skan	ss->len = 0;
427132720Skan}
428132720Skan
429132720Skanstatic int
430132720Skanread_timed(struct tac_handle *h, void *buf, size_t len,
431132720Skan    const struct timeval *deadline)
432132720Skan{
433132720Skan	char *ptr;
434132720Skan
435132720Skan	ptr = (char *)buf;
436132720Skan	while (len > 0) {
437132720Skan		int n;
438132720Skan
439132720Skan		n = read(h->fd, ptr, len);
440132720Skan		if (n == -1) {
441132720Skan			struct timeval tv;
442132720Skan			int nfds;
443132720Skan
444132720Skan			if (errno != EAGAIN) {
445132720Skan				generr(h, "Network read error: %s",
446132720Skan				    strerror(errno));
447132720Skan				return -1;
448132720Skan			}
449132720Skan
450132720Skan			/* Wait until we can read more data. */
451132720Skan			gettimeofday(&tv, NULL);
452132720Skan			timersub(deadline, &tv, &tv);
453132720Skan			if (tv.tv_sec >= 0) {
454169691Skan				fd_set rfds;
455132720Skan
456132720Skan				FD_ZERO(&rfds);
457132720Skan				FD_SET(h->fd, &rfds);
458132720Skan				nfds =
459132720Skan				    select(h->fd + 1, &rfds, NULL, NULL, &tv);
460132720Skan				if (nfds == -1) {
461132720Skan					generr(h, "select: %s",
462132720Skan					    strerror(errno));
463132720Skan					return -1;
464132720Skan				}
465132720Skan			} else
466132720Skan				nfds = 0;
467132720Skan			if (nfds == 0) {
468132720Skan				generr(h, "Network read timed out");
469132720Skan				return -1;
470132720Skan			}
471132720Skan		} else if (n == 0) {
472132720Skan			generr(h, "unexpected EOF from server");
473132720Skan			return -1;
474132720Skan		} else {
475132720Skan			ptr += n;
476132720Skan			len -= n;
477132720Skan		}
478132720Skan	}
479132720Skan	return 0;
480132720Skan}
481132720Skan
482132720Skan/*
483132720Skan * Receive a response from the server and decrypt it.  Returns 0 on
484132720Skan * success, or -1 on failure.
485132720Skan */
486132720Skanstatic int
487132720Skanrecv_msg(struct tac_handle *h)
488132720Skan{
489132720Skan	struct timeval deadline;
490132720Skan	struct tac_msg *msg;
491132720Skan	size_t len;
492132720Skan
493132720Skan	msg = &h->response;
494132720Skan	gettimeofday(&deadline, NULL);
495132720Skan	deadline.tv_sec += h->servers[h->cur_server].timeout;
496132720Skan
497132720Skan	/* Read the message header and make sure it is reasonable. */
498132720Skan	if (read_timed(h, msg, HDRSIZE, &deadline) == -1)
499132720Skan		return -1;
500132720Skan	if (memcmp(msg->session_id, h->request.session_id,
501132720Skan	    sizeof msg->session_id) != 0) {
502132720Skan		generr(h, "Invalid session ID in received message");
503132720Skan		return -1;
504132720Skan	}
505132720Skan	if (msg->type != h->request.type) {
506132720Skan		generr(h, "Invalid type in received message");
507132720Skan		return -1;
508132720Skan	}
509132720Skan	len = ntohl(msg->length);
510132720Skan	if (len > BODYSIZE) {
511132720Skan		generr(h, "Received message too large");
512132720Skan		return -1;
513132720Skan	}
514132720Skan	if (msg->seq_no != ++h->last_seq_no) {
515132720Skan		generr(h, "Invalid sequence number in received message");
516132720Skan		return -1;
517132720Skan	}
518132720Skan
519132720Skan	/* Read the message body. */
520132720Skan	if (read_timed(h, msg->u.body, len, &deadline) == -1)
521132720Skan		return -1;
522132720Skan
523132720Skan	/* Decrypt it. */
524132720Skan	crypt_msg(h, msg);
525132720Skan
526132720Skan	/*
527132720Skan	 * Turn off single-connection mode if the server isn't amenable
528132720Skan	 * to it.
529132720Skan	 */
530132720Skan	if (!(msg->flags & TAC_SINGLE_CONNECT))
531132720Skan		h->single_connect = 0;
532132720Skan	return 0;
533132720Skan}
534132720Skan
535132720Skanstatic int
536132720Skansave_str(struct tac_handle *h, struct clnt_str *cs, const void *data,
537132720Skan    size_t len)
538132720Skan{
539132720Skan	free_str(cs);
540132720Skan	if (data != NULL && len != 0) {
541132720Skan		if ((cs->data = xmalloc(h, len)) == NULL)
542132720Skan			return -1;
543		cs->len = len;
544		memcpy(cs->data, data, len);
545	}
546	return 0;
547}
548
549/*
550 * Send the current request, after encrypting it.  Returns 0 on success,
551 * or -1 on failure.
552 */
553static int
554send_msg(struct tac_handle *h)
555{
556	struct timeval deadline;
557	struct tac_msg *msg;
558	char *ptr;
559	int len;
560
561	if (h->last_seq_no & 1) {
562		generr(h, "Attempt to send message out of sequence");
563		return -1;
564	}
565
566	msg = &h->request;
567	msg->seq_no = ++h->last_seq_no;
568	if (msg->seq_no == 1)
569		gen_session_id(msg);
570	crypt_msg(h, msg);
571
572	if (establish_connection(h) == -1)
573		return -1;
574
575	if (h->single_connect)
576		msg->flags |= TAC_SINGLE_CONNECT;
577	else
578		msg->flags &= ~TAC_SINGLE_CONNECT;
579	gettimeofday(&deadline, NULL);
580	deadline.tv_sec += h->servers[h->cur_server].timeout;
581	len = HDRSIZE + ntohl(msg->length);
582	ptr = (char *)msg;
583	while (len > 0) {
584		int n;
585
586		n = write(h->fd, ptr, len);
587		if (n == -1) {
588			struct timeval tv;
589			int nfds;
590
591			if (errno != EAGAIN) {
592				generr(h, "Network write error: %s",
593				    strerror(errno));
594				return -1;
595			}
596
597			/* Wait until we can write more data. */
598			gettimeofday(&tv, NULL);
599			timersub(&deadline, &tv, &tv);
600			if (tv.tv_sec >= 0) {
601				fd_set wfds;
602
603				FD_ZERO(&wfds);
604				FD_SET(h->fd, &wfds);
605				nfds =
606				    select(h->fd + 1, NULL, &wfds, NULL, &tv);
607				if (nfds == -1) {
608					generr(h, "select: %s",
609					    strerror(errno));
610					return -1;
611				}
612			} else
613				nfds = 0;
614			if (nfds == 0) {
615				generr(h, "Network write timed out");
616				return -1;
617			}
618		} else {
619			ptr += n;
620			len -= n;
621		}
622	}
623	return 0;
624}
625
626/*
627 * Destructively split a string into fields separated by white space.
628 * `#' at the beginning of a field begins a comment that extends to the
629 * end of the string.  Fields may be quoted with `"'.  Inside quoted
630 * strings, the backslash escapes `\"' and `\\' are honored.
631 *
632 * Pointers to up to the first maxfields fields are stored in the fields
633 * array.  Missing fields get NULL pointers.
634 *
635 * The return value is the actual number of fields parsed, and is always
636 * <= maxfields.
637 *
638 * On a syntax error, places a message in the msg string, and returns -1.
639 */
640static int
641split(char *str, char *fields[], int maxfields, char *msg, size_t msglen)
642{
643	char *p;
644	int i;
645	static const char ws[] = " \t";
646
647	for (i = 0;  i < maxfields;  i++)
648		fields[i] = NULL;
649	p = str;
650	i = 0;
651	while (*p != '\0') {
652		p += strspn(p, ws);
653		if (*p == '#' || *p == '\0')
654			break;
655		if (i >= maxfields) {
656			snprintf(msg, msglen, "line has too many fields");
657			return -1;
658		}
659		if (*p == '"') {
660			char *dst;
661
662			dst = ++p;
663			fields[i] = dst;
664			while (*p != '"') {
665				if (*p == '\\') {
666					p++;
667					if (*p != '"' && *p != '\\' &&
668					    *p != '\0') {
669						snprintf(msg, msglen,
670						    "invalid `\\' escape");
671						return -1;
672					}
673				}
674				if (*p == '\0') {
675					snprintf(msg, msglen,
676					    "unterminated quoted string");
677					return -1;
678				}
679				*dst++ = *p++;
680			}
681			*dst = '\0';
682			p++;
683			if (*p != '\0' && strspn(p, ws) == 0) {
684				snprintf(msg, msglen, "quoted string not"
685				    " followed by white space");
686				return -1;
687			}
688		} else {
689			fields[i] = p;
690			p += strcspn(p, ws);
691			if (*p != '\0')
692				*p++ = '\0';
693		}
694		i++;
695	}
696	return i;
697}
698
699int
700tac_add_server(struct tac_handle *h, const char *host, int port,
701    const char *secret, int timeout, int flags)
702{
703	struct tac_server *srvp;
704
705	if (h->num_servers >= MAXSERVERS) {
706		generr(h, "Too many TACACS+ servers specified");
707		return -1;
708	}
709	srvp = &h->servers[h->num_servers];
710
711	memset(&srvp->addr, 0, sizeof srvp->addr);
712	srvp->addr.sin_len = sizeof srvp->addr;
713	srvp->addr.sin_family = AF_INET;
714	if (!inet_aton(host, &srvp->addr.sin_addr)) {
715		struct hostent *hent;
716
717		if ((hent = gethostbyname(host)) == NULL) {
718			generr(h, "%s: host not found", host);
719			return -1;
720		}
721		memcpy(&srvp->addr.sin_addr, hent->h_addr,
722		    sizeof srvp->addr.sin_addr);
723	}
724	srvp->addr.sin_port = htons(port != 0 ? port : TACPLUS_PORT);
725	if ((srvp->secret = xstrdup(h, secret)) == NULL)
726		return -1;
727	srvp->timeout = timeout;
728	srvp->flags = flags;
729	h->num_servers++;
730	return 0;
731}
732
733void
734tac_close(struct tac_handle *h)
735{
736	int srv;
737
738	if (h->fd != -1)
739		close(h->fd);
740	for (srv = 0;  srv < h->num_servers;  srv++) {
741		memset(h->servers[srv].secret, 0,
742		    strlen(h->servers[srv].secret));
743		free(h->servers[srv].secret);
744	}
745	free_str(&h->user);
746	free_str(&h->port);
747	free_str(&h->rem_addr);
748	free_str(&h->data);
749	free_str(&h->user_msg);
750	free(h);
751}
752
753int
754tac_config(struct tac_handle *h, const char *path)
755{
756	FILE *fp;
757	char buf[MAXCONFLINE];
758	int linenum;
759	int retval;
760
761	if (path == NULL)
762		path = PATH_TACPLUS_CONF;
763	if ((fp = fopen(path, "r")) == NULL) {
764		generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
765		return -1;
766	}
767	retval = 0;
768	linenum = 0;
769	while (fgets(buf, sizeof buf, fp) != NULL) {
770		int len;
771		char *fields[4];
772		int nfields;
773		char msg[ERRSIZE];
774		char *host, *res;
775		char *port_str;
776		char *secret;
777		char *timeout_str;
778		char *options_str;
779		char *end;
780		unsigned long timeout;
781		int port;
782		int options;
783
784		linenum++;
785		len = strlen(buf);
786		/* We know len > 0, else fgets would have returned NULL. */
787		if (buf[len - 1] != '\n') {
788			if (len == sizeof buf - 1)
789				generr(h, "%s:%d: line too long", path,
790				    linenum);
791			else
792				generr(h, "%s:%d: missing newline", path,
793				    linenum);
794			retval = -1;
795			break;
796		}
797		buf[len - 1] = '\0';
798
799		/* Extract the fields from the line. */
800		nfields = split(buf, fields, 4, msg, sizeof msg);
801		if (nfields == -1) {
802			generr(h, "%s:%d: %s", path, linenum, msg);
803			retval = -1;
804			break;
805		}
806		if (nfields == 0)
807			continue;
808		if (nfields < 2) {
809			generr(h, "%s:%d: missing shared secret", path,
810			    linenum);
811			retval = -1;
812			break;
813		}
814		host = fields[0];
815		secret = fields[1];
816		timeout_str = fields[2];
817		options_str = fields[3];
818
819		/* Parse and validate the fields. */
820		res = host;
821		host = strsep(&res, ":");
822		port_str = strsep(&res, ":");
823		if (port_str != NULL) {
824			port = strtoul(port_str, &end, 10);
825			if (port_str[0] == '\0' || *end != '\0') {
826				generr(h, "%s:%d: invalid port", path,
827				    linenum);
828				retval = -1;
829				break;
830			}
831		} else
832			port = 0;
833		if (timeout_str != NULL) {
834			timeout = strtoul(timeout_str, &end, 10);
835			if (timeout_str[0] == '\0' || *end != '\0') {
836				generr(h, "%s:%d: invalid timeout", path,
837				    linenum);
838				retval = -1;
839				break;
840			}
841		} else
842			timeout = TIMEOUT;
843		options = 0;
844		if (options_str != NULL) {
845			if (strcmp(options_str, "single-connection") == 0)
846				options |= TAC_SRVR_SINGLE_CONNECT;
847			else {
848				generr(h, "%s:%d: invalid option \"%s\"",
849				    path, linenum, options_str);
850				retval = -1;
851				break;
852			}
853		};
854
855		if (tac_add_server(h, host, port, secret, timeout,
856		    options) == -1) {
857			char msg[ERRSIZE];
858
859			strcpy(msg, h->errmsg);
860			generr(h, "%s:%d: %s", path, linenum, msg);
861			retval = -1;
862			break;
863		}
864	}
865	/* Clear out the buffer to wipe a possible copy of a shared secret */
866	memset(buf, 0, sizeof buf);
867	fclose(fp);
868	return retval;
869}
870
871int
872tac_create_authen(struct tac_handle *h, int action, int type, int service)
873{
874	struct tac_msg *msg;
875	struct tac_authen_start *as;
876
877	h->last_seq_no = 0;
878
879	msg = &h->request;
880	msg->type = TAC_AUTHEN;
881	msg->version = authen_version(action, type);
882	msg->flags = 0;
883
884	as = &msg->u.authen_start;
885	as->action = action;
886	as->priv_lvl = TAC_PRIV_LVL_USER;
887	as->authen_type = type;
888	as->service = service;
889
890	free_str(&h->user);
891	free_str(&h->port);
892	free_str(&h->rem_addr);
893	free_str(&h->data);
894	free_str(&h->user_msg);
895
896	/* XXX - more to do */
897	return 0;
898}
899
900void *
901tac_get_data(struct tac_handle *h, size_t *len)
902{
903	return dup_str(h, &h->srvr_data, len);
904}
905
906char *
907tac_get_msg(struct tac_handle *h)
908{
909	return (char *)dup_str(h, &h->srvr_msg, NULL);
910}
911
912/*
913 * Create and initialize a tac_handle structure, and return it to the
914 * caller.  Can fail only if the necessary memory cannot be allocated.
915 * In that case, it returns NULL.
916 */
917struct tac_handle *
918tac_open(void)
919{
920	struct tac_handle *h;
921
922	h = (struct tac_handle *)malloc(sizeof(struct tac_handle));
923	if (h != NULL) {
924		h->fd = -1;
925		h->num_servers = 0;
926		h->cur_server = 0;
927		h->errmsg[0] = '\0';
928		init_clnt_str(&h->user);
929		init_clnt_str(&h->port);
930		init_clnt_str(&h->rem_addr);
931		init_clnt_str(&h->data);
932		init_clnt_str(&h->user_msg);
933		init_srvr_str(&h->srvr_msg);
934		init_srvr_str(&h->srvr_data);
935		srandomdev();
936	}
937	return h;
938}
939
940int
941tac_send_authen(struct tac_handle *h)
942{
943	struct tac_authen_reply *ar;
944
945	if (h->last_seq_no == 0) {	/* Authentication START packet */
946		struct tac_authen_start *as;
947
948		as = &h->request.u.authen_start;
949		h->request.length =
950		    htonl(offsetof(struct tac_authen_start, rest[0]));
951		if (add_str_8(h, &as->user_len, &h->user) == -1 ||
952		    add_str_8(h, &as->port_len, &h->port) == -1 ||
953		    add_str_8(h, &as->rem_addr_len, &h->rem_addr) == -1 ||
954		    add_str_8(h, &as->data_len, &h->data) == -1)
955			return -1;
956	} else {			/* Authentication CONTINUE packet */
957		struct tac_authen_cont *ac;
958
959		ac = &h->request.u.authen_cont;
960		ac->flags = 0;
961		h->request.length =
962		    htonl(offsetof(struct tac_authen_cont, rest[0]));
963		if (add_str_16(h, &ac->user_msg_len, &h->user_msg) == -1 ||
964		    add_str_16(h, &ac->data_len, &h->data) == -1)
965			return -1;
966	}
967
968	/* Send the message and retrieve the reply. */
969	if (send_msg(h) == -1 || recv_msg(h) == -1)
970		return -1;
971
972	/* Scan the optional fields in the reply. */
973	ar = &h->response.u.authen_reply;
974	h->srvr_pos = offsetof(struct tac_authen_reply, rest[0]);
975	if (get_srvr_str(h, &h->srvr_msg, ntohs(ar->msg_len)) == -1 ||
976	    get_srvr_str(h, &h->srvr_data, ntohs(ar->data_len)) == -1 ||
977	    get_srvr_end(h) == -1)
978		return -1;
979
980	if (!h->single_connect &&
981	    ar->status != TAC_AUTHEN_STATUS_GETDATA &&
982	    ar->status != TAC_AUTHEN_STATUS_GETUSER &&
983	    ar->status != TAC_AUTHEN_STATUS_GETPASS)
984		close_connection(h);
985
986	return ar->flags << 8 | ar->status;
987}
988
989int
990tac_set_rem_addr(struct tac_handle *h, const char *addr)
991{
992	return save_str(h, &h->rem_addr, addr, addr != NULL ? strlen(addr) : 0);
993}
994
995int
996tac_set_data(struct tac_handle *h, const void *data, size_t data_len)
997{
998	return save_str(h, &h->data, data, data_len);
999}
1000
1001int
1002tac_set_msg(struct tac_handle *h, const char *msg)
1003{
1004	return save_str(h, &h->user_msg, msg, msg != NULL ? strlen(msg) : 0);
1005}
1006
1007int
1008tac_set_port(struct tac_handle *h, const char *port)
1009{
1010	return save_str(h, &h->port, port, port != NULL ? strlen(port) : 0);
1011}
1012
1013int
1014tac_set_priv(struct tac_handle *h, int priv)
1015{
1016	if (!(TAC_PRIV_LVL_MIN <= priv && priv <= TAC_PRIV_LVL_MAX)) {
1017		generr(h, "Attempt to set invalid privilege level");
1018		return -1;
1019	}
1020	h->request.u.authen_start.priv_lvl = priv;
1021	return 0;
1022}
1023
1024int
1025tac_set_user(struct tac_handle *h, const char *user)
1026{
1027	return save_str(h, &h->user, user, user != NULL ? strlen(user) : 0);
1028}
1029
1030const char *
1031tac_strerror(struct tac_handle *h)
1032{
1033	return h->errmsg;
1034}
1035
1036static void *
1037xmalloc(struct tac_handle *h, size_t size)
1038{
1039	void *r;
1040
1041	if ((r = malloc(size)) == NULL)
1042		generr(h, "Out of memory");
1043	return r;
1044}
1045
1046static char *
1047xstrdup(struct tac_handle *h, const char *s)
1048{
1049	char *r;
1050
1051	if ((r = strdup(s)) == NULL)
1052		generr(h, "Out of memory");
1053	return r;
1054}
1055