xref: /openbsd-src/usr.sbin/sasyncd/net.c (revision 6668a736406d7c2b2dddf4684e79317967d904ab)
1*6668a736Sguenther /*	$OpenBSD: net.c,v 1.24 2022/01/28 06:33:27 guenther Exp $	*/
27aea46c5Sho 
37aea46c5Sho /*
47aea46c5Sho  * Copyright (c) 2005 H�kan Olsson.  All rights reserved.
57aea46c5Sho  *
67aea46c5Sho  * Redistribution and use in source and binary forms, with or without
77aea46c5Sho  * modification, are permitted provided that the following conditions
87aea46c5Sho  * are met:
97aea46c5Sho  *
107aea46c5Sho  * 1. Redistributions of source code must retain the above copyright
117aea46c5Sho  *    notice, this list of conditions and the following disclaimer.
127aea46c5Sho  * 2. Redistributions in binary form must reproduce the above copyright
137aea46c5Sho  *    notice, this list of conditions and the following disclaimer in the
147aea46c5Sho  *    documentation and/or other materials provided with the distribution.
157aea46c5Sho  *
167aea46c5Sho  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
177aea46c5Sho  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
187aea46c5Sho  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
197aea46c5Sho  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
207aea46c5Sho  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
217aea46c5Sho  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
227aea46c5Sho  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
237aea46c5Sho  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
247aea46c5Sho  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
257aea46c5Sho  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
267aea46c5Sho  */
277aea46c5Sho 
287aea46c5Sho /*
297aea46c5Sho  * This code was written under funding by Multicom Security AB.
307aea46c5Sho  */
317aea46c5Sho 
327aea46c5Sho #include <sys/types.h>
337aea46c5Sho #include <sys/socket.h>
347aea46c5Sho #include <sys/time.h>
357aea46c5Sho #include <netinet/in.h>
367aea46c5Sho #include <arpa/inet.h>
376db4730eSho #include <ifaddrs.h>
386db4730eSho #include <netdb.h>
390a4f72c5Smillert #include <signal.h>
407aea46c5Sho 
4154626cc1Sho #include <openssl/aes.h>
4254626cc1Sho #include <openssl/sha.h>
4354626cc1Sho 
447aea46c5Sho #include <errno.h>
4554626cc1Sho #include <stdio.h>
4654626cc1Sho #include <stdlib.h>
477aea46c5Sho #include <string.h>
487aea46c5Sho #include <unistd.h>
497aea46c5Sho 
507aea46c5Sho #include "sasyncd.h"
517aea46c5Sho #include "net.h"
527aea46c5Sho 
537aea46c5Sho struct msg {
547aea46c5Sho 	u_int8_t	*buf;
557aea46c5Sho 	u_int32_t	 len;
567aea46c5Sho 	int		 refcnt;
577aea46c5Sho };
587aea46c5Sho 
597aea46c5Sho struct qmsg {
607aea46c5Sho 	SIMPLEQ_ENTRY(qmsg)	next;
617aea46c5Sho 	struct msg	*msg;
627aea46c5Sho };
637aea46c5Sho 
64bd7abb77Sho int	*listeners;
6554626cc1Sho AES_KEY	aes_key[2];
6654626cc1Sho #define AES_IV_LEN	AES_BLOCK_SIZE
677aea46c5Sho 
681d55a410Sho /* We never send (or expect to receive) messages smaller/larger than this. */
691d55a410Sho #define MSG_MINLEN	12
701d55a410Sho #define MSG_MAXLEN	4096
711d55a410Sho 
727aea46c5Sho /* Local prototypes. */
737aea46c5Sho static u_int8_t *net_read(struct syncpeer *, u_int32_t *, u_int32_t *);
747aea46c5Sho static int	 net_set_sa(struct sockaddr *, char *, in_port_t);
757aea46c5Sho static void	 net_check_peers(void *);
767aea46c5Sho 
771d55a410Sho /* Pretty-print a buffer. */
7894f5df21Sho void
dump_buf(int lvl,u_int8_t * b,u_int32_t len,char * title)7954626cc1Sho dump_buf(int lvl, u_int8_t *b, u_int32_t len, char *title)
8054626cc1Sho {
811d55a410Sho 	u_int32_t	i, off, blen;
821d55a410Sho 	u_int8_t	*buf;
831d55a410Sho 	const char	def[] = "Buffer:";
8454626cc1Sho 
851d55a410Sho 	if (cfgstate.verboselevel < lvl)
8654626cc1Sho 		return;
8754626cc1Sho 
881d55a410Sho 	blen = 2 * (len + len / 36) + 3 + (title ? strlen(title) : sizeof def);
8935de856eSderaadt 	if (!(buf = calloc(1, blen)))
901d55a410Sho 		return;
911d55a410Sho 
921d55a410Sho 	snprintf(buf, blen, "%s\n ", title ? title : def);
9354626cc1Sho 	off = strlen(buf);
941d55a410Sho 	for (i = 0; i < len; i++, off+=2) {
9554626cc1Sho 		snprintf(buf + off, blen - off, "%02x", b[i]);
961d55a410Sho 		if ((i+1) % 36 == 0) {
971d55a410Sho 			off += 2;
981d55a410Sho 			snprintf(buf + off, blen - off, "\n ");
991d55a410Sho 		}
1001d55a410Sho 	}
10154626cc1Sho 	log_msg(lvl, "%s", buf);
10254626cc1Sho 	free(buf);
10354626cc1Sho }
10454626cc1Sho 
105bd7abb77Sho /* Add a listening socket. */
106bd7abb77Sho static int
net_add_listener(struct sockaddr * sa)107bd7abb77Sho net_add_listener(struct sockaddr *sa)
108bd7abb77Sho {
109bd7abb77Sho 	char	host[NI_MAXHOST], port[NI_MAXSERV];
110bd7abb77Sho 	int	r, s;
111bd7abb77Sho 
112bd7abb77Sho 	s = socket(sa->sa_family, SOCK_STREAM, 0);
113bd7abb77Sho 	if (s < 0) {
114bd7abb77Sho 		perror("net_add_listener: socket()");
115bd7abb77Sho 		close(s);
116bd7abb77Sho 		return -1;
117bd7abb77Sho 	}
118bd7abb77Sho 
119bd7abb77Sho 	r = 1;
120bd7abb77Sho 	if (setsockopt(s, SOL_SOCKET,
121bd7abb77Sho 		cfgstate.listen_on ? SO_REUSEADDR : SO_REUSEPORT, (void *)&r,
122bd7abb77Sho 		sizeof r)) {
123bd7abb77Sho 		perror("net_add_listener: setsockopt()");
124bd7abb77Sho 		close(s);
125bd7abb77Sho 		return -1;
126bd7abb77Sho 	}
127bd7abb77Sho 
128bd7abb77Sho 	if (bind(s, sa, sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) :
129bd7abb77Sho 		sizeof (struct sockaddr_in6))) {
130bd7abb77Sho 		perror("net_add_listener: bind()");
131bd7abb77Sho 		close(s);
132bd7abb77Sho 		return -1;
133bd7abb77Sho 	}
134bd7abb77Sho 
135bd7abb77Sho 	if (listen(s, 3)) {
136bd7abb77Sho 		perror("net_add_listener: listen()");
137bd7abb77Sho 		close(s);
138bd7abb77Sho 		return -1;
139bd7abb77Sho 	}
140bd7abb77Sho 
141bd7abb77Sho 	if (getnameinfo(sa, sa->sa_len, host, sizeof host, port, sizeof port,
142bd7abb77Sho 		NI_NUMERICHOST | NI_NUMERICSERV))
1439353ff65Skjell 		log_msg(2, "listening on port %u fd %d", cfgstate.listen_port,
144bd7abb77Sho 		    s);
145bd7abb77Sho 	else
1469353ff65Skjell 		log_msg(2, "listening on %s port %s fd %d", host, port, s);
147bd7abb77Sho 
148bd7abb77Sho 	return s;
149bd7abb77Sho }
150bd7abb77Sho 
151bd7abb77Sho /* Allocate and fill in listeners array. */
152bd7abb77Sho static int
net_setup_listeners(void)153bd7abb77Sho net_setup_listeners(void)
1547aea46c5Sho {
1557aea46c5Sho 	struct sockaddr_storage	 sa_storage;
1567aea46c5Sho 	struct sockaddr		*sa = (struct sockaddr *)&sa_storage;
157bd7abb77Sho 	struct sockaddr_in	*sin = (struct sockaddr_in *)sa;
158bd7abb77Sho 	struct sockaddr_in6	*sin6 = (struct sockaddr_in6 *)sa;
159bd7abb77Sho 	struct ifaddrs		*ifap = 0, *ifa;
160bd7abb77Sho 	int			 i, count;
161bd7abb77Sho 
162bd7abb77Sho 	/* Setup listening sockets.  */
163bd7abb77Sho 	memset(&sa_storage, 0, sizeof sa_storage);
164bd7abb77Sho 	if (net_set_sa(sa, cfgstate.listen_on, cfgstate.listen_port) == 0) {
16535de856eSderaadt 		listeners = calloc(2, sizeof(int));
166bd7abb77Sho 		if (!listeners) {
167bd7abb77Sho 			perror("net_setup_listeners: calloc()");
168f97e363eSmoritz 			goto errout;
169bd7abb77Sho 		}
170bd7abb77Sho 		listeners[1] = -1;
171bd7abb77Sho 		listeners[0] = net_add_listener(sa);
172bd7abb77Sho 		if (listeners[0] == -1) {
173bd7abb77Sho 			log_msg(0, "net_setup_listeners: could not find "
174bd7abb77Sho 			    "listen address (%s)", cfgstate.listen_on);
175bd7abb77Sho 			goto errout;
176bd7abb77Sho 		}
177bd7abb77Sho 		return 0;
178bd7abb77Sho 	}
179bd7abb77Sho 
180bd7abb77Sho 	/*
181bd7abb77Sho 	 * If net_set_sa() failed, cfgstate.listen_on is probably an
182*6668a736Sguenther 	 * interface name, so we should listen on all its addresses.
183bd7abb77Sho 	 */
184bd7abb77Sho 
185bd7abb77Sho 	if (getifaddrs(&ifap) != 0) {
186bd7abb77Sho 		perror("net_setup_listeners: getifaddrs()");
187f97e363eSmoritz 		goto errout;
188bd7abb77Sho 	}
189bd7abb77Sho 
190bd7abb77Sho 	/* How many addresses matches? */
191bd7abb77Sho 	for (count = 0, ifa = ifap; ifa; ifa = ifa->ifa_next) {
192bd7abb77Sho 		if (!ifa->ifa_name || !ifa->ifa_addr ||
193bd7abb77Sho 		    (ifa->ifa_addr->sa_family != AF_INET &&
194bd7abb77Sho 			ifa->ifa_addr->sa_family != AF_INET6))
195bd7abb77Sho 			continue;
196bd7abb77Sho 		if (cfgstate.listen_family &&
197bd7abb77Sho 		    cfgstate.listen_family != ifa->ifa_addr->sa_family)
198bd7abb77Sho 			continue;
199bd7abb77Sho 		if (strcmp(ifa->ifa_name, cfgstate.listen_on) != 0)
200bd7abb77Sho 			continue;
201bd7abb77Sho 		count++;
202bd7abb77Sho 	}
203bd7abb77Sho 
204bd7abb77Sho 	if (!count) {
205bd7abb77Sho 		log_msg(0, "net_setup_listeners: no listeners found for %s",
206bd7abb77Sho 		    cfgstate.listen_on);
207f97e363eSmoritz 		goto errout;
208bd7abb77Sho 	}
209bd7abb77Sho 
210bd7abb77Sho 	/* Allocate one extra slot and set to -1, marking end of array. */
21135de856eSderaadt 	listeners = calloc(count + 1, sizeof(int));
212bd7abb77Sho 	if (!listeners) {
213bd7abb77Sho 		perror("net_setup_listeners: calloc()");
214f97e363eSmoritz 		goto errout;
215bd7abb77Sho 	}
216bd7abb77Sho 	for (i = 0; i <= count; i++)
217bd7abb77Sho 		listeners[i] = -1;
218bd7abb77Sho 
219bd7abb77Sho 	/* Create listening sockets */
220bd7abb77Sho 	for (count = 0, ifa = ifap; ifa; ifa = ifa->ifa_next) {
221bd7abb77Sho 		if (!ifa->ifa_name || !ifa->ifa_addr ||
222bd7abb77Sho 		    (ifa->ifa_addr->sa_family != AF_INET &&
223bd7abb77Sho 			ifa->ifa_addr->sa_family != AF_INET6))
224bd7abb77Sho 			continue;
225bd7abb77Sho 		if (cfgstate.listen_family &&
226bd7abb77Sho 		    cfgstate.listen_family != ifa->ifa_addr->sa_family)
227bd7abb77Sho 			continue;
228bd7abb77Sho 		if (strcmp(ifa->ifa_name, cfgstate.listen_on) != 0)
229bd7abb77Sho 			continue;
230bd7abb77Sho 
231bd7abb77Sho 		memset(&sa_storage, 0, sizeof sa_storage);
232bd7abb77Sho 		sa->sa_family = ifa->ifa_addr->sa_family;
233bd7abb77Sho 		switch (sa->sa_family) {
234bd7abb77Sho 		case AF_INET:
235bd7abb77Sho 			sin->sin_port = htons(cfgstate.listen_port);
236bd7abb77Sho 			sin->sin_len = sizeof *sin;
237bd7abb77Sho 			memcpy(&sin->sin_addr,
238bd7abb77Sho 			    &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
239bd7abb77Sho 			    sizeof sin->sin_addr);
240bd7abb77Sho 			break;
241bd7abb77Sho 		case AF_INET6:
242bd7abb77Sho 			sin6->sin6_port = htons(cfgstate.listen_port);
243bd7abb77Sho 			sin6->sin6_len = sizeof *sin6;
244bd7abb77Sho 			memcpy(&sin6->sin6_addr,
245bd7abb77Sho 			    &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
246bd7abb77Sho 			    sizeof sin6->sin6_addr);
247bd7abb77Sho 			break;
248bd7abb77Sho 		}
249bd7abb77Sho 
250bd7abb77Sho 		listeners[count] = net_add_listener(sa);
251bd7abb77Sho 		if (listeners[count] == -1) {
2529353ff65Skjell 			log_msg(2, "net_setup_listeners(setup): failed to "
253bd7abb77Sho 			    "add listener, count = %d", count);
254bd7abb77Sho 			goto errout;
255bd7abb77Sho 		}
256bd7abb77Sho 		count++;
257bd7abb77Sho 	}
258bd7abb77Sho 	freeifaddrs(ifap);
259bd7abb77Sho 	return 0;
260bd7abb77Sho 
261bd7abb77Sho   errout:
262bd7abb77Sho 	if (ifap)
263bd7abb77Sho 		freeifaddrs(ifap);
264f97e363eSmoritz 	if (listeners) {
265bd7abb77Sho 		for (i = 0; listeners[i] != -1; i++)
266bd7abb77Sho 			close(listeners[i]);
267bd7abb77Sho 		free(listeners);
268f97e363eSmoritz 	}
269bd7abb77Sho 	return -1;
270bd7abb77Sho }
271bd7abb77Sho 
272bd7abb77Sho int
net_init(void)273bd7abb77Sho net_init(void)
274bd7abb77Sho {
2757aea46c5Sho 	struct syncpeer *p;
2767aea46c5Sho 
27750b83ccaSmarkus 	if (AES_set_encrypt_key(cfgstate.sharedkey, cfgstate.sharedkey_len,
27850b83ccaSmarkus 	    &aes_key[0]) ||
27950b83ccaSmarkus 	    AES_set_decrypt_key(cfgstate.sharedkey, cfgstate.sharedkey_len,
28050b83ccaSmarkus 	    &aes_key[1])) {
28154626cc1Sho 		fprintf(stderr, "Bad AES shared key\n");
28254626cc1Sho 		return -1;
28354626cc1Sho 	}
2847aea46c5Sho 
285bd7abb77Sho 	if (net_setup_listeners())
2867aea46c5Sho 		return -1;
2877aea46c5Sho 
2887aea46c5Sho 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
2897aea46c5Sho 		p->socket = -1;
2907aea46c5Sho 		SIMPLEQ_INIT(&p->msgs);
2917aea46c5Sho 	}
2927aea46c5Sho 
2937aea46c5Sho 	net_check_peers(0);
2947aea46c5Sho 	return 0;
2957aea46c5Sho }
2967aea46c5Sho 
2977aea46c5Sho static void
net_enqueue(struct syncpeer * p,struct msg * m)2987aea46c5Sho net_enqueue(struct syncpeer *p, struct msg *m)
2997aea46c5Sho {
3007aea46c5Sho 	struct qmsg	*qm;
3017aea46c5Sho 
3027aea46c5Sho 	if (p->socket < 0)
3037aea46c5Sho 		return;
3047aea46c5Sho 
305cc98d859Sguenther 	qm = calloc(1, sizeof *qm);
3067aea46c5Sho 	if (!qm) {
307cc98d859Sguenther 		log_err("net_enqueue: calloc()");
3087aea46c5Sho 		return;
3097aea46c5Sho 	}
3107aea46c5Sho 
3117aea46c5Sho 	qm->msg = m;
3127aea46c5Sho 	m->refcnt++;
3137aea46c5Sho 
3147aea46c5Sho 	SIMPLEQ_INSERT_TAIL(&p->msgs, qm, next);
3157aea46c5Sho 	return;
3167aea46c5Sho }
3177aea46c5Sho 
3187aea46c5Sho /*
3197aea46c5Sho  * Queue a message for transmission to a particular peer,
3207aea46c5Sho  * or to all peers if no peer is specified.
3217aea46c5Sho  */
3227aea46c5Sho int
net_queue(struct syncpeer * p0,u_int32_t msgtype,u_int8_t * buf,u_int32_t len)32354626cc1Sho net_queue(struct syncpeer *p0, u_int32_t msgtype, u_int8_t *buf, u_int32_t len)
3247aea46c5Sho {
3257aea46c5Sho 	struct syncpeer *p = p0;
3267aea46c5Sho 	struct msg	*m;
32754626cc1Sho 	SHA_CTX		 ctx;
32854626cc1Sho 	u_int8_t	 hash[SHA_DIGEST_LENGTH];
32954626cc1Sho 	u_int8_t	 iv[AES_IV_LEN], tmp_iv[AES_IV_LEN];
33054626cc1Sho 	u_int32_t	 v, padlen = 0;
33154626cc1Sho 	int		 i, offset;
3327aea46c5Sho 
33335de856eSderaadt 	m = calloc(1, sizeof *m);
3347aea46c5Sho 	if (!m) {
335bd7abb77Sho 		log_err("net_queue: calloc()");
3367aea46c5Sho 		free(buf);
3377aea46c5Sho 		return -1;
3387aea46c5Sho 	}
33954626cc1Sho 
34054626cc1Sho 	/* Generate hash */
34154626cc1Sho 	SHA1_Init(&ctx);
34254626cc1Sho 	SHA1_Update(&ctx, buf, len);
34354626cc1Sho 	SHA1_Final(hash, &ctx);
3449353ff65Skjell 	dump_buf(2, hash, sizeof hash, "net_queue: computed hash");
34554626cc1Sho 
34654626cc1Sho 	/* Padding required? */
34754626cc1Sho 	i = len % AES_IV_LEN;
34854626cc1Sho 	if (i) {
34954626cc1Sho 		u_int8_t *pbuf;
35054626cc1Sho 		i = AES_IV_LEN - i;
35154626cc1Sho 		pbuf = realloc(buf, len + i);
35254626cc1Sho 		if (!pbuf) {
35354626cc1Sho 			log_err("net_queue: realloc()");
35454626cc1Sho 			free(buf);
35554626cc1Sho 			free(m);
35654626cc1Sho 			return -1;
35754626cc1Sho 		}
35854626cc1Sho 		padlen = i;
35954626cc1Sho 		while (i > 0)
36054626cc1Sho 			pbuf[len++] = (u_int8_t)i--;
36154626cc1Sho 		buf = pbuf;
36254626cc1Sho 	}
36354626cc1Sho 
36454626cc1Sho 	/* Get random IV */
36522474abcShaesbaert 	for (i = 0; (size_t)i <= sizeof iv - sizeof v; i += sizeof v) {
36654626cc1Sho 		v = arc4random();
36754626cc1Sho 		memcpy(&iv[i], &v, sizeof v);
36854626cc1Sho 	}
3699353ff65Skjell 	dump_buf(2, iv, sizeof iv, "net_queue: IV");
37054626cc1Sho 	memcpy(tmp_iv, iv, sizeof tmp_iv);
37154626cc1Sho 
37254626cc1Sho 	/* Encrypt */
3739353ff65Skjell 	dump_buf(2, buf, len, "net_queue: pre encrypt");
37454626cc1Sho 	AES_cbc_encrypt(buf, buf, len, &aes_key[0], tmp_iv, AES_ENCRYPT);
3759353ff65Skjell 	dump_buf(2, buf, len, "net_queue: post encrypt");
37654626cc1Sho 
37754626cc1Sho 	/* Allocate send buffer */
37854626cc1Sho 	m->len = len + sizeof iv + sizeof hash + 3 * sizeof(u_int32_t);
37935de856eSderaadt 	m->buf = malloc(m->len);
38054626cc1Sho 	if (!m->buf) {
38154626cc1Sho 		free(m);
38254626cc1Sho 		free(buf);
38354626cc1Sho 		log_err("net_queue: calloc()");
38454626cc1Sho 		return -1;
38554626cc1Sho 	}
38654626cc1Sho 	offset = 0;
38754626cc1Sho 
38854626cc1Sho 	/* Fill it (order must match parsing code in net_read()) */
38954626cc1Sho 	v = htonl(m->len - sizeof(u_int32_t));
39054626cc1Sho 	memcpy(m->buf + offset, &v, sizeof v);
39154626cc1Sho 	offset += sizeof v;
39254626cc1Sho 	v = htonl(msgtype);
39354626cc1Sho 	memcpy(m->buf + offset, &v, sizeof v);
39454626cc1Sho 	offset += sizeof v;
39554626cc1Sho 	v = htonl(padlen);
39654626cc1Sho 	memcpy(m->buf + offset, &v, sizeof v);
39754626cc1Sho 	offset += sizeof v;
39854626cc1Sho 	memcpy(m->buf + offset, hash, sizeof hash);
39954626cc1Sho 	offset += sizeof hash;
40054626cc1Sho 	memcpy(m->buf + offset, iv, sizeof iv);
40154626cc1Sho 	offset += sizeof iv;
40254626cc1Sho 	memcpy(m->buf + offset, buf, len);
40354626cc1Sho 	free(buf);
4047aea46c5Sho 
4057aea46c5Sho 	if (p)
4067aea46c5Sho 		net_enqueue(p, m);
4077aea46c5Sho 	else
4087aea46c5Sho 		for (p = LIST_FIRST(&cfgstate.peerlist); p;
4097aea46c5Sho 		     p = LIST_NEXT(p, link))
4107aea46c5Sho 			net_enqueue(p, m);
4117aea46c5Sho 
4127aea46c5Sho 	if (!m->refcnt) {
41354626cc1Sho 		free(m->buf);
4147aea46c5Sho 		free(m);
4157aea46c5Sho 	}
4167aea46c5Sho 
4177aea46c5Sho 	return 0;
4187aea46c5Sho }
4197aea46c5Sho 
4207aea46c5Sho /* Set all write pending filedescriptors. */
4217aea46c5Sho int
net_set_pending_wfds(fd_set * fds)4227aea46c5Sho net_set_pending_wfds(fd_set *fds)
4237aea46c5Sho {
4247aea46c5Sho 	struct syncpeer *p;
4257aea46c5Sho 	int		max_fd = -1;
4267aea46c5Sho 
4277aea46c5Sho 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link))
4287aea46c5Sho 		if (p->socket > -1 && SIMPLEQ_FIRST(&p->msgs)) {
4297aea46c5Sho 			FD_SET(p->socket, fds);
4307aea46c5Sho 			if (p->socket > max_fd)
4317aea46c5Sho 				max_fd = p->socket;
4327aea46c5Sho 		}
4337aea46c5Sho 	return max_fd + 1;
4347aea46c5Sho }
4357aea46c5Sho 
4367aea46c5Sho /*
4377aea46c5Sho  * Set readable filedescriptors. They are basically the same as for write,
4387aea46c5Sho  * plus the listening socket.
4397aea46c5Sho  */
4407aea46c5Sho int
net_set_rfds(fd_set * fds)4417aea46c5Sho net_set_rfds(fd_set *fds)
4427aea46c5Sho {
4437aea46c5Sho 	struct syncpeer *p;
444bd7abb77Sho 	int		i, max_fd = -1;
4457aea46c5Sho 
4467aea46c5Sho 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
4477aea46c5Sho 		if (p->socket > -1)
4487aea46c5Sho 			FD_SET(p->socket, fds);
4497aea46c5Sho 		if (p->socket > max_fd)
4507aea46c5Sho 			max_fd = p->socket;
4517aea46c5Sho 	}
452bd7abb77Sho 	for (i = 0; listeners[i] != -1; i++) {
453bd7abb77Sho 		FD_SET(listeners[i], fds);
454bd7abb77Sho 		if (listeners[i] > max_fd)
455bd7abb77Sho 			max_fd = listeners[i];
456bd7abb77Sho 	}
4577aea46c5Sho 	return max_fd + 1;
4587aea46c5Sho }
4597aea46c5Sho 
460bd7abb77Sho static void
net_accept(int accept_socket)461bd7abb77Sho net_accept(int accept_socket)
4627aea46c5Sho {
4637aea46c5Sho 	struct sockaddr_storage	 sa_storage, sa_storage2;
4647aea46c5Sho 	struct sockaddr		*sa = (struct sockaddr *)&sa_storage;
4657aea46c5Sho 	struct sockaddr		*sa2 = (struct sockaddr *)&sa_storage2;
466bd7abb77Sho 	struct sockaddr_in	*sin, *sin2;
467bd7abb77Sho 	struct sockaddr_in6	*sin6, *sin62;
4687aea46c5Sho 	struct syncpeer		*p;
469bd7abb77Sho 	socklen_t		 socklen;
470bd7abb77Sho 	int			 s, found;
4717aea46c5Sho 
4727aea46c5Sho 	/* Accept a new incoming connection */
4737aea46c5Sho 	socklen = sizeof sa_storage;
474bd7abb77Sho 	memset(&sa_storage, 0, socklen);
475bd7abb77Sho 	memset(&sa_storage2, 0, socklen);
476bd7abb77Sho 	s = accept(accept_socket, sa, &socklen);
477bd7abb77Sho 	if (s > -1) {
4787aea46c5Sho 		/* Setup the syncpeer structure */
4797aea46c5Sho 		found = 0;
4807aea46c5Sho 		for (p = LIST_FIRST(&cfgstate.peerlist); p && !found;
4817aea46c5Sho 		     p = LIST_NEXT(p, link)) {
4827aea46c5Sho 
4837aea46c5Sho 			/* Match? */
4847aea46c5Sho 			if (net_set_sa(sa2, p->name, 0))
4857aea46c5Sho 				continue;
4867aea46c5Sho 			if (sa->sa_family != sa2->sa_family)
4877aea46c5Sho 				continue;
4887aea46c5Sho 			if (sa->sa_family == AF_INET) {
4897aea46c5Sho 				sin = (struct sockaddr_in *)sa;
4907aea46c5Sho 				sin2 = (struct sockaddr_in *)sa2;
491bd7abb77Sho 				if (memcmp(&sin->sin_addr, &sin2->sin_addr,
4927aea46c5Sho 					sizeof(struct in_addr)))
4937aea46c5Sho 					continue;
4947aea46c5Sho 			} else {
4957aea46c5Sho 				sin6 = (struct sockaddr_in6 *)sa;
4967aea46c5Sho 				sin62 = (struct sockaddr_in6 *)sa2;
497bd7abb77Sho 				if (memcmp(&sin6->sin6_addr, &sin62->sin6_addr,
4987aea46c5Sho 					sizeof(struct in6_addr)))
4997aea46c5Sho 					continue;
5007aea46c5Sho 			}
5017aea46c5Sho 			/* Match! */
5027aea46c5Sho 			found++;
503bd7abb77Sho 			p->socket = s;
504bd7abb77Sho 			log_msg(1, "net: peer \"%s\" connected", p->name);
50594f5df21Sho 			if (cfgstate.runstate == MASTER)
506bd7abb77Sho 				timer_add("pfkey_snap", 2, pfkey_snapshot, p);
5077aea46c5Sho 		}
5087aea46c5Sho 		if (!found) {
509bd7abb77Sho 			log_msg(1, "net: found no matching peer for accepted "
510bd7abb77Sho 			    "socket, closing.");
511bd7abb77Sho 			close(s);
5127aea46c5Sho 		}
51362e3c252Sderaadt 	} else if (errno != EWOULDBLOCK && errno != EINTR &&
51462e3c252Sderaadt 	    errno != ECONNABORTED)
515bd7abb77Sho 		log_err("net: accept()");
5167aea46c5Sho }
5177aea46c5Sho 
518bd7abb77Sho void
net_handle_messages(fd_set * fds)519bd7abb77Sho net_handle_messages(fd_set *fds)
520bd7abb77Sho {
521bd7abb77Sho 	struct syncpeer *p;
522bd7abb77Sho 	u_int8_t	*msg;
523bd7abb77Sho 	u_int32_t	 msgtype, msglen;
524bd7abb77Sho 	int		 i;
525bd7abb77Sho 
526bd7abb77Sho 	for (i = 0; listeners[i] != -1; i++)
527bd7abb77Sho 		if (FD_ISSET(listeners[i], fds))
528bd7abb77Sho 			net_accept(listeners[i]);
529bd7abb77Sho 
5307aea46c5Sho 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
5317aea46c5Sho 		if (p->socket < 0 || !FD_ISSET(p->socket, fds))
5327aea46c5Sho 			continue;
5337aea46c5Sho 		msg = net_read(p, &msgtype, &msglen);
5347aea46c5Sho 		if (!msg)
5357aea46c5Sho 			continue;
5367aea46c5Sho 
5379353ff65Skjell 		log_msg(2, "net_handle_messages: got msg type %u len %u from "
5387aea46c5Sho 		    "peer %s", msgtype, msglen, p->name);
5397aea46c5Sho 
5407aea46c5Sho 		switch (msgtype) {
5417aea46c5Sho 		case MSG_SYNCCTL:
5427aea46c5Sho 			net_ctl_handle_msg(p, msg, msglen);
5437aea46c5Sho 			free(msg);
5447aea46c5Sho 			break;
5457aea46c5Sho 
5467aea46c5Sho 		case MSG_PFKEYDATA:
5477aea46c5Sho 			if (p->runstate != MASTER ||
5487aea46c5Sho 			    cfgstate.runstate == MASTER) {
5491d55a410Sho 				log_msg(1, "net: got PFKEY message from "
5501d55a410Sho 				    "non-MASTER peer");
5517aea46c5Sho 				free(msg);
5527aea46c5Sho 				if (cfgstate.runstate == MASTER)
5537aea46c5Sho 					net_ctl_send_state(p);
5547aea46c5Sho 				else
5557aea46c5Sho 					net_ctl_send_error(p, 0);
5567aea46c5Sho 			} else if (pfkey_queue_message(msg, msglen))
5577aea46c5Sho 				free(msg);
5587aea46c5Sho 			break;
5597aea46c5Sho 
5607aea46c5Sho 		default:
5611d55a410Sho 			log_msg(0, "net: got unknown message type %u len %u "
5621d55a410Sho 			    "from peer %s", msgtype, msglen, p->name);
5637aea46c5Sho 			free(msg);
5647aea46c5Sho 			net_ctl_send_error(p, 0);
5657aea46c5Sho 		}
5667aea46c5Sho 	}
5677aea46c5Sho }
5687aea46c5Sho 
5697aea46c5Sho void
net_send_messages(fd_set * fds)5707aea46c5Sho net_send_messages(fd_set *fds)
5717aea46c5Sho {
5727aea46c5Sho 	struct syncpeer *p;
5737aea46c5Sho 	struct qmsg	*qm;
5747aea46c5Sho 	struct msg	*m;
57554626cc1Sho 	ssize_t		 r;
5767aea46c5Sho 
5777aea46c5Sho 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
5787aea46c5Sho 		if (p->socket < 0 || !FD_ISSET(p->socket, fds))
5797aea46c5Sho 			continue;
5807aea46c5Sho 		qm = SIMPLEQ_FIRST(&p->msgs);
5817aea46c5Sho 		if (!qm) {
5827aea46c5Sho 			/* XXX Log */
5837aea46c5Sho 			continue;
5847aea46c5Sho 		}
5857aea46c5Sho 		m = qm->msg;
5867aea46c5Sho 
5879353ff65Skjell 		log_msg(2, "net_send_messages: msg %p len %u ref %d "
5881d55a410Sho 		    "to peer %s", m, m->len, m->refcnt, p->name);
5897aea46c5Sho 
59054626cc1Sho 		/* write message */
59154626cc1Sho 		r = write(p->socket, m->buf, m->len);
5921d55a410Sho 		if (r == -1) {
5931d55a410Sho 			net_disconnect_peer(p);
5941d55a410Sho 			log_msg(0, "net_send_messages: write() failed, "
5951d55a410Sho 			    "peer disconnected");
5961d55a410Sho 		} else if (r < (ssize_t)m->len) {
5971d55a410Sho 			/* retransmit later */
5987aea46c5Sho 			continue;
59954626cc1Sho 		}
6007aea46c5Sho 
60154626cc1Sho 		/* cleanup */
6027aea46c5Sho 		SIMPLEQ_REMOVE_HEAD(&p->msgs, next);
6037aea46c5Sho 		free(qm);
6047aea46c5Sho 
6057aea46c5Sho 		if (--m->refcnt < 1) {
6069353ff65Skjell 			log_msg(2, "net_send_messages: freeing msg %p", m);
60754626cc1Sho 			free(m->buf);
6087aea46c5Sho 			free(m);
6097aea46c5Sho 		}
6107aea46c5Sho 	}
6117aea46c5Sho 	return;
6127aea46c5Sho }
6137aea46c5Sho 
6147aea46c5Sho void
net_disconnect_peer(struct syncpeer * p)6157aea46c5Sho net_disconnect_peer(struct syncpeer *p)
6167aea46c5Sho {
6171d55a410Sho 	if (p->socket > -1) {
6181d55a410Sho 		log_msg(1, "net_disconnect_peer: peer \"%s\" removed",
6191d55a410Sho 		    p->name);
6207aea46c5Sho 		close(p->socket);
6211d55a410Sho 	}
6227aea46c5Sho 	p->socket = -1;
6237aea46c5Sho }
6247aea46c5Sho 
6257aea46c5Sho void
net_shutdown(void)6267aea46c5Sho net_shutdown(void)
6277aea46c5Sho {
6287aea46c5Sho 	struct syncpeer *p;
6297aea46c5Sho 	struct qmsg	*qm;
6307aea46c5Sho 	struct msg	*m;
631bd7abb77Sho 	int		 i;
6327aea46c5Sho 
6337aea46c5Sho 	while ((p = LIST_FIRST(&cfgstate.peerlist))) {
6347aea46c5Sho 		while ((qm = SIMPLEQ_FIRST(&p->msgs))) {
6357aea46c5Sho 			SIMPLEQ_REMOVE_HEAD(&p->msgs, next);
6367aea46c5Sho 			m = qm->msg;
6377aea46c5Sho 			if (--m->refcnt < 1) {
63854626cc1Sho 				free(m->buf);
6397aea46c5Sho 				free(m);
6407aea46c5Sho 			}
6417aea46c5Sho 			free(qm);
6427aea46c5Sho 		}
6437aea46c5Sho 		net_disconnect_peer(p);
6441bf4baafSho 		free(p->sa);
6457aea46c5Sho 		free(p->name);
6467aea46c5Sho 		LIST_REMOVE(p, link);
6479945a12eSmcbride 		cfgstate.peercnt--;
6487aea46c5Sho 		free(p);
6497aea46c5Sho 	}
6507aea46c5Sho 
651bd7abb77Sho 	if (listeners) {
652bd7abb77Sho 		for (i = 0; listeners[i] != -1; i++)
653bd7abb77Sho 			close(listeners[i]);
654bd7abb77Sho 		free(listeners);
655bd7abb77Sho 		listeners = 0;
656bd7abb77Sho 	}
6577aea46c5Sho }
6587aea46c5Sho 
6597aea46c5Sho /*
6607aea46c5Sho  * Helper functions (local) below here.
6617aea46c5Sho  */
6627aea46c5Sho 
6637aea46c5Sho static u_int8_t *
net_read(struct syncpeer * p,u_int32_t * msgtype,u_int32_t * msglen)6647aea46c5Sho net_read(struct syncpeer *p, u_int32_t *msgtype, u_int32_t *msglen)
6657aea46c5Sho {
66654626cc1Sho 	u_int8_t	*msg, *blob, *rhash, *iv, hash[SHA_DIGEST_LENGTH];
667f5fcfc77Smoritz 	u_int32_t	 v, blob_len, pos = 0;
66885fc6b68Shaesbaert 	int		 padlen = 0, offset = 0;
66985fc6b68Shaesbaert 	ssize_t 	 r;
67054626cc1Sho 	SHA_CTX		 ctx;
6717aea46c5Sho 
67254626cc1Sho 	/* Read blob length */
6731d55a410Sho 	r = read(p->socket, &v, sizeof v);
6741d55a410Sho 	if (r != (ssize_t)sizeof v) {
6751d55a410Sho 		if (r < 1)
6761d55a410Sho 			net_disconnect_peer(p);
6777aea46c5Sho 		return NULL;
6781d55a410Sho 	}
6791d55a410Sho 
68054626cc1Sho 	blob_len = ntohl(v);
68154626cc1Sho 	if (blob_len < sizeof hash + AES_IV_LEN + 2 * sizeof(u_int32_t))
6827aea46c5Sho 		return NULL;
68354626cc1Sho 	*msglen = blob_len - sizeof hash - AES_IV_LEN - 2 * sizeof(u_int32_t);
6841d55a410Sho 	if (*msglen < MSG_MINLEN || *msglen > MSG_MAXLEN)
6851d55a410Sho 		return NULL;
6867aea46c5Sho 
68754626cc1Sho 	/* Read message blob */
68835de856eSderaadt 	blob = malloc(blob_len);
68954626cc1Sho 	if (!blob) {
69054626cc1Sho 		log_err("net_read: malloc()");
6917aea46c5Sho 		return NULL;
69254626cc1Sho 	}
693f5fcfc77Smoritz 
694f5fcfc77Smoritz 	while (blob_len > pos) {
695f5fcfc77Smoritz 		switch (r = read(p->socket, blob + pos, blob_len - pos)) {
696f5fcfc77Smoritz 		case -1:
697f5fcfc77Smoritz 			if (errno == EINTR || errno == EAGAIN)
698f5fcfc77Smoritz 				continue;
699f5fcfc77Smoritz                         /* FALLTHROUGH */
700f5fcfc77Smoritz 		case 0:
7011d55a410Sho 			net_disconnect_peer(p);
70254626cc1Sho 			free(blob);
70354626cc1Sho 			return NULL;
704f5fcfc77Smoritz                         /* NOTREACHED */
705f5fcfc77Smoritz 		default:
706f5fcfc77Smoritz 			pos += r;
707f5fcfc77Smoritz 		}
7087aea46c5Sho 	}
7097aea46c5Sho 
71054626cc1Sho 	offset = 0;
71154626cc1Sho 	memcpy(&v, blob + offset, sizeof v);
71254626cc1Sho 	*msgtype = ntohl(v);
71354626cc1Sho 	offset += sizeof v;
71454626cc1Sho 
71554626cc1Sho 	if (*msgtype > MSG_MAXTYPE) {
71654626cc1Sho 		free(blob);
71754626cc1Sho 		return NULL;
71854626cc1Sho 	}
71954626cc1Sho 
72054626cc1Sho 	memcpy(&v, blob + offset, sizeof v);
72154626cc1Sho 	padlen = ntohl(v);
72254626cc1Sho 	offset += sizeof v;
72354626cc1Sho 
72454626cc1Sho 	rhash = blob + offset;
72554626cc1Sho 	iv    = rhash + sizeof hash;
72635de856eSderaadt 	msg = malloc(*msglen);
72754626cc1Sho 	if (!msg) {
72854626cc1Sho 		free(blob);
72954626cc1Sho 		return NULL;
73054626cc1Sho 	}
73154626cc1Sho 	memcpy(msg, iv + AES_IV_LEN, *msglen);
73254626cc1Sho 
7339353ff65Skjell 	dump_buf(2, rhash, sizeof hash, "net_read: got hash");
7349353ff65Skjell 	dump_buf(2, iv, AES_IV_LEN, "net_read: got IV");
7359353ff65Skjell 	dump_buf(2, msg, *msglen, "net_read: pre decrypt");
73654626cc1Sho 	AES_cbc_encrypt(msg, msg, *msglen, &aes_key[1], iv, AES_DECRYPT);
7379353ff65Skjell 	dump_buf(2, msg, *msglen, "net_read: post decrypt");
73854626cc1Sho 	*msglen -= padlen;
73954626cc1Sho 
74054626cc1Sho 	SHA1_Init(&ctx);
74154626cc1Sho 	SHA1_Update(&ctx, msg, *msglen);
74254626cc1Sho 	SHA1_Final(hash, &ctx);
7439353ff65Skjell 	dump_buf(2, hash, sizeof hash, "net_read: computed hash");
74454626cc1Sho 
74554626cc1Sho 	if (memcmp(hash, rhash, sizeof hash) != 0) {
74654626cc1Sho 		free(blob);
7478367b0efSjsg 		free(msg);
7481d55a410Sho 		log_msg(0, "net_read: got bad message (typo in shared key?)");
74954626cc1Sho 		return NULL;
75054626cc1Sho 	}
75154626cc1Sho 	free(blob);
7527aea46c5Sho 	return msg;
7537aea46c5Sho }
7547aea46c5Sho 
7557aea46c5Sho static int
net_set_sa(struct sockaddr * sa,char * name,in_port_t port)7567aea46c5Sho net_set_sa(struct sockaddr *sa, char *name, in_port_t port)
7577aea46c5Sho {
7587aea46c5Sho 	struct sockaddr_in	*sin = (struct sockaddr_in *)sa;
7597aea46c5Sho 	struct sockaddr_in6	*sin6 = (struct sockaddr_in6 *)sa;
7607aea46c5Sho 
7616db4730eSho 	if (!name) {
7626db4730eSho 		/* XXX Assume IPv4 */
7636db4730eSho 		sa->sa_family = AF_INET;
7646db4730eSho 		sin->sin_port = htons(port);
7656db4730eSho 		sin->sin_len = sizeof *sin;
7666db4730eSho 		return 0;
7676db4730eSho 	}
7686db4730eSho 
7697aea46c5Sho 	if (inet_pton(AF_INET, name, &sin->sin_addr) == 1) {
7707aea46c5Sho 		sa->sa_family = AF_INET;
7717aea46c5Sho 		sin->sin_port = htons(port);
7727aea46c5Sho 		sin->sin_len = sizeof *sin;
7737aea46c5Sho 		return 0;
7747aea46c5Sho 	}
7757aea46c5Sho 
7767aea46c5Sho 	if (inet_pton(AF_INET6, name, &sin6->sin6_addr) == 1) {
7777aea46c5Sho 		sa->sa_family = AF_INET6;
7787aea46c5Sho 		sin6->sin6_port = htons(port);
7797aea46c5Sho 		sin6->sin6_len = sizeof *sin6;
7807aea46c5Sho 		return 0;
7817aea46c5Sho 	}
7826db4730eSho 
7836db4730eSho 	return -1;
7846db4730eSho }
7857aea46c5Sho 
7867aea46c5Sho static void
got_sigalrm(int s)7877aea46c5Sho got_sigalrm(int s)
7887aea46c5Sho {
7897aea46c5Sho 	return;
7907aea46c5Sho }
7917aea46c5Sho 
7927aea46c5Sho void
net_connect(void)7931d55a410Sho net_connect(void)
7947aea46c5Sho {
7957aea46c5Sho 	struct itimerval	iv;
7967aea46c5Sho 	struct syncpeer		*p;
7977aea46c5Sho 
7987aea46c5Sho 	signal(SIGALRM, got_sigalrm);
7997aea46c5Sho 	memset(&iv, 0, sizeof iv);
8007aea46c5Sho 	iv.it_value.tv_sec = 5;
8017aea46c5Sho 	iv.it_interval.tv_sec = 5;
8027aea46c5Sho 	setitimer(ITIMER_REAL, &iv, NULL);
8037aea46c5Sho 
8047aea46c5Sho 	for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) {
80554626cc1Sho 		if (p->socket > -1)
8067aea46c5Sho 			continue;
8071bf4baafSho 		if (!p->sa) {
80835de856eSderaadt 			p->sa = calloc(1, sizeof(struct sockaddr_storage));
8091bf4baafSho 			if (!p->sa)
8101bf4baafSho 				return;
8111bf4baafSho 			if (net_set_sa(p->sa, p->name, cfgstate.listen_port))
8127aea46c5Sho 				continue;
8131bf4baafSho 		}
8141bf4baafSho 		p->socket = socket(p->sa->sa_family, SOCK_STREAM, 0);
8157aea46c5Sho 		if (p->socket < 0) {
8167aea46c5Sho 			log_err("peer \"%s\": socket()", p->name);
8177aea46c5Sho 			continue;
8187aea46c5Sho 		}
8191bf4baafSho 		if (connect(p->socket, p->sa, p->sa->sa_len)) {
8201d55a410Sho 			log_msg(1, "net_connect: peer \"%s\" not ready yet",
8211d55a410Sho 			    p->name);
8227aea46c5Sho 			net_disconnect_peer(p);
8237aea46c5Sho 			continue;
8247aea46c5Sho 		}
8257aea46c5Sho 		if (net_ctl_send_state(p)) {
8261d55a410Sho 			log_msg(0, "net_connect: peer \"%s\" failed", p->name);
8277aea46c5Sho 			net_disconnect_peer(p);
8287aea46c5Sho 			continue;
8297aea46c5Sho 		}
8301d55a410Sho 		log_msg(1, "net_connect: peer \"%s\" connected, fd %d",
8311d55a410Sho 		    p->name, p->socket);
83294f5df21Sho 
83394f5df21Sho 		/* Schedule a pfkey sync to the newly connected peer. */
83494f5df21Sho 		if (cfgstate.runstate == MASTER)
83594f5df21Sho 			timer_add("pfkey_snapshot", 2, pfkey_snapshot, p);
8367aea46c5Sho 	}
8377aea46c5Sho 
8387aea46c5Sho 	timerclear(&iv.it_value);
8397aea46c5Sho 	timerclear(&iv.it_interval);
8407aea46c5Sho 	setitimer(ITIMER_REAL, &iv, NULL);
8417aea46c5Sho 	signal(SIGALRM, SIG_IGN);
8427aea46c5Sho 
8437aea46c5Sho 	return;
8447aea46c5Sho }
8457aea46c5Sho 
8467aea46c5Sho static void
net_check_peers(void * arg)8477aea46c5Sho net_check_peers(void *arg)
8487aea46c5Sho {
8491d55a410Sho 	net_connect();
8507aea46c5Sho 	(void)timer_add("peer recheck", 600, net_check_peers, 0);
8517aea46c5Sho }
852