1 |
|
|
/* $OpenBSD: net.c,v 1.23 2015/12/12 20:04:23 mmcc Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2005 Håkan Olsson. All rights reserved. |
5 |
|
|
* |
6 |
|
|
* Redistribution and use in source and binary forms, with or without |
7 |
|
|
* modification, are permitted provided that the following conditions |
8 |
|
|
* are met: |
9 |
|
|
* |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
17 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
18 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
19 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
20 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
21 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
22 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
23 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
25 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
/* |
29 |
|
|
* This code was written under funding by Multicom Security AB. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
#include <sys/types.h> |
33 |
|
|
#include <sys/socket.h> |
34 |
|
|
#include <sys/time.h> |
35 |
|
|
#include <netinet/in.h> |
36 |
|
|
#include <arpa/inet.h> |
37 |
|
|
#include <ifaddrs.h> |
38 |
|
|
#include <netdb.h> |
39 |
|
|
#include <signal.h> |
40 |
|
|
|
41 |
|
|
#include <openssl/aes.h> |
42 |
|
|
#include <openssl/sha.h> |
43 |
|
|
|
44 |
|
|
#include <errno.h> |
45 |
|
|
#include <stdio.h> |
46 |
|
|
#include <stdlib.h> |
47 |
|
|
#include <string.h> |
48 |
|
|
#include <unistd.h> |
49 |
|
|
|
50 |
|
|
#include "sasyncd.h" |
51 |
|
|
#include "net.h" |
52 |
|
|
|
53 |
|
|
struct msg { |
54 |
|
|
u_int8_t *buf; |
55 |
|
|
u_int32_t len; |
56 |
|
|
int refcnt; |
57 |
|
|
}; |
58 |
|
|
|
59 |
|
|
struct qmsg { |
60 |
|
|
SIMPLEQ_ENTRY(qmsg) next; |
61 |
|
|
struct msg *msg; |
62 |
|
|
}; |
63 |
|
|
|
64 |
|
|
int *listeners; |
65 |
|
|
AES_KEY aes_key[2]; |
66 |
|
|
#define AES_IV_LEN AES_BLOCK_SIZE |
67 |
|
|
|
68 |
|
|
/* We never send (or expect to receive) messages smaller/larger than this. */ |
69 |
|
|
#define MSG_MINLEN 12 |
70 |
|
|
#define MSG_MAXLEN 4096 |
71 |
|
|
|
72 |
|
|
/* Local prototypes. */ |
73 |
|
|
static u_int8_t *net_read(struct syncpeer *, u_int32_t *, u_int32_t *); |
74 |
|
|
static int net_set_sa(struct sockaddr *, char *, in_port_t); |
75 |
|
|
static void net_check_peers(void *); |
76 |
|
|
|
77 |
|
|
/* Pretty-print a buffer. */ |
78 |
|
|
void |
79 |
|
|
dump_buf(int lvl, u_int8_t *b, u_int32_t len, char *title) |
80 |
|
|
{ |
81 |
|
|
u_int32_t i, off, blen; |
82 |
|
|
u_int8_t *buf; |
83 |
|
|
const char def[] = "Buffer:"; |
84 |
|
|
|
85 |
|
|
if (cfgstate.verboselevel < lvl) |
86 |
|
|
return; |
87 |
|
|
|
88 |
|
|
blen = 2 * (len + len / 36) + 3 + (title ? strlen(title) : sizeof def); |
89 |
|
|
if (!(buf = calloc(1, blen))) |
90 |
|
|
return; |
91 |
|
|
|
92 |
|
|
snprintf(buf, blen, "%s\n ", title ? title : def); |
93 |
|
|
off = strlen(buf); |
94 |
|
|
for (i = 0; i < len; i++, off+=2) { |
95 |
|
|
snprintf(buf + off, blen - off, "%02x", b[i]); |
96 |
|
|
if ((i+1) % 36 == 0) { |
97 |
|
|
off += 2; |
98 |
|
|
snprintf(buf + off, blen - off, "\n "); |
99 |
|
|
} |
100 |
|
|
} |
101 |
|
|
log_msg(lvl, "%s", buf); |
102 |
|
|
free(buf); |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
/* Add a listening socket. */ |
106 |
|
|
static int |
107 |
|
|
net_add_listener(struct sockaddr *sa) |
108 |
|
|
{ |
109 |
|
|
char host[NI_MAXHOST], port[NI_MAXSERV]; |
110 |
|
|
int r, s; |
111 |
|
|
|
112 |
|
|
s = socket(sa->sa_family, SOCK_STREAM, 0); |
113 |
|
|
if (s < 0) { |
114 |
|
|
perror("net_add_listener: socket()"); |
115 |
|
|
close(s); |
116 |
|
|
return -1; |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
r = 1; |
120 |
|
|
if (setsockopt(s, SOL_SOCKET, |
121 |
|
|
cfgstate.listen_on ? SO_REUSEADDR : SO_REUSEPORT, (void *)&r, |
122 |
|
|
sizeof r)) { |
123 |
|
|
perror("net_add_listener: setsockopt()"); |
124 |
|
|
close(s); |
125 |
|
|
return -1; |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
if (bind(s, sa, sa->sa_family == AF_INET ? sizeof(struct sockaddr_in) : |
129 |
|
|
sizeof (struct sockaddr_in6))) { |
130 |
|
|
perror("net_add_listener: bind()"); |
131 |
|
|
close(s); |
132 |
|
|
return -1; |
133 |
|
|
} |
134 |
|
|
|
135 |
|
|
if (listen(s, 3)) { |
136 |
|
|
perror("net_add_listener: listen()"); |
137 |
|
|
close(s); |
138 |
|
|
return -1; |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
if (getnameinfo(sa, sa->sa_len, host, sizeof host, port, sizeof port, |
142 |
|
|
NI_NUMERICHOST | NI_NUMERICSERV)) |
143 |
|
|
log_msg(2, "listening on port %u fd %d", cfgstate.listen_port, |
144 |
|
|
s); |
145 |
|
|
else |
146 |
|
|
log_msg(2, "listening on %s port %s fd %d", host, port, s); |
147 |
|
|
|
148 |
|
|
return s; |
149 |
|
|
} |
150 |
|
|
|
151 |
|
|
/* Allocate and fill in listeners array. */ |
152 |
|
|
static int |
153 |
|
|
net_setup_listeners(void) |
154 |
|
|
{ |
155 |
|
|
struct sockaddr_storage sa_storage; |
156 |
|
|
struct sockaddr *sa = (struct sockaddr *)&sa_storage; |
157 |
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
158 |
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
159 |
|
|
struct ifaddrs *ifap = 0, *ifa; |
160 |
|
|
int i, count; |
161 |
|
|
|
162 |
|
|
/* Setup listening sockets. */ |
163 |
|
|
memset(&sa_storage, 0, sizeof sa_storage); |
164 |
|
|
if (net_set_sa(sa, cfgstate.listen_on, cfgstate.listen_port) == 0) { |
165 |
|
|
listeners = calloc(2, sizeof(int)); |
166 |
|
|
if (!listeners) { |
167 |
|
|
perror("net_setup_listeners: calloc()"); |
168 |
|
|
goto errout; |
169 |
|
|
} |
170 |
|
|
listeners[1] = -1; |
171 |
|
|
listeners[0] = net_add_listener(sa); |
172 |
|
|
if (listeners[0] == -1) { |
173 |
|
|
log_msg(0, "net_setup_listeners: could not find " |
174 |
|
|
"listen address (%s)", cfgstate.listen_on); |
175 |
|
|
goto errout; |
176 |
|
|
} |
177 |
|
|
return 0; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
/* |
181 |
|
|
* If net_set_sa() failed, cfgstate.listen_on is probably an |
182 |
|
|
* interface name, so we should listen on all it's addresses. |
183 |
|
|
*/ |
184 |
|
|
|
185 |
|
|
if (getifaddrs(&ifap) != 0) { |
186 |
|
|
perror("net_setup_listeners: getifaddrs()"); |
187 |
|
|
goto errout; |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
/* How many addresses matches? */ |
191 |
|
|
for (count = 0, ifa = ifap; ifa; ifa = ifa->ifa_next) { |
192 |
|
|
if (!ifa->ifa_name || !ifa->ifa_addr || |
193 |
|
|
(ifa->ifa_addr->sa_family != AF_INET && |
194 |
|
|
ifa->ifa_addr->sa_family != AF_INET6)) |
195 |
|
|
continue; |
196 |
|
|
if (cfgstate.listen_family && |
197 |
|
|
cfgstate.listen_family != ifa->ifa_addr->sa_family) |
198 |
|
|
continue; |
199 |
|
|
if (strcmp(ifa->ifa_name, cfgstate.listen_on) != 0) |
200 |
|
|
continue; |
201 |
|
|
count++; |
202 |
|
|
} |
203 |
|
|
|
204 |
|
|
if (!count) { |
205 |
|
|
log_msg(0, "net_setup_listeners: no listeners found for %s", |
206 |
|
|
cfgstate.listen_on); |
207 |
|
|
goto errout; |
208 |
|
|
} |
209 |
|
|
|
210 |
|
|
/* Allocate one extra slot and set to -1, marking end of array. */ |
211 |
|
|
listeners = calloc(count + 1, sizeof(int)); |
212 |
|
|
if (!listeners) { |
213 |
|
|
perror("net_setup_listeners: calloc()"); |
214 |
|
|
goto errout; |
215 |
|
|
} |
216 |
|
|
for (i = 0; i <= count; i++) |
217 |
|
|
listeners[i] = -1; |
218 |
|
|
|
219 |
|
|
/* Create listening sockets */ |
220 |
|
|
for (count = 0, ifa = ifap; ifa; ifa = ifa->ifa_next) { |
221 |
|
|
if (!ifa->ifa_name || !ifa->ifa_addr || |
222 |
|
|
(ifa->ifa_addr->sa_family != AF_INET && |
223 |
|
|
ifa->ifa_addr->sa_family != AF_INET6)) |
224 |
|
|
continue; |
225 |
|
|
if (cfgstate.listen_family && |
226 |
|
|
cfgstate.listen_family != ifa->ifa_addr->sa_family) |
227 |
|
|
continue; |
228 |
|
|
if (strcmp(ifa->ifa_name, cfgstate.listen_on) != 0) |
229 |
|
|
continue; |
230 |
|
|
|
231 |
|
|
memset(&sa_storage, 0, sizeof sa_storage); |
232 |
|
|
sa->sa_family = ifa->ifa_addr->sa_family; |
233 |
|
|
switch (sa->sa_family) { |
234 |
|
|
case AF_INET: |
235 |
|
|
sin->sin_port = htons(cfgstate.listen_port); |
236 |
|
|
sin->sin_len = sizeof *sin; |
237 |
|
|
memcpy(&sin->sin_addr, |
238 |
|
|
&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, |
239 |
|
|
sizeof sin->sin_addr); |
240 |
|
|
break; |
241 |
|
|
case AF_INET6: |
242 |
|
|
sin6->sin6_port = htons(cfgstate.listen_port); |
243 |
|
|
sin6->sin6_len = sizeof *sin6; |
244 |
|
|
memcpy(&sin6->sin6_addr, |
245 |
|
|
&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, |
246 |
|
|
sizeof sin6->sin6_addr); |
247 |
|
|
break; |
248 |
|
|
} |
249 |
|
|
|
250 |
|
|
listeners[count] = net_add_listener(sa); |
251 |
|
|
if (listeners[count] == -1) { |
252 |
|
|
log_msg(2, "net_setup_listeners(setup): failed to " |
253 |
|
|
"add listener, count = %d", count); |
254 |
|
|
goto errout; |
255 |
|
|
} |
256 |
|
|
count++; |
257 |
|
|
} |
258 |
|
|
freeifaddrs(ifap); |
259 |
|
|
return 0; |
260 |
|
|
|
261 |
|
|
errout: |
262 |
|
|
if (ifap) |
263 |
|
|
freeifaddrs(ifap); |
264 |
|
|
if (listeners) { |
265 |
|
|
for (i = 0; listeners[i] != -1; i++) |
266 |
|
|
close(listeners[i]); |
267 |
|
|
free(listeners); |
268 |
|
|
} |
269 |
|
|
return -1; |
270 |
|
|
} |
271 |
|
|
|
272 |
|
|
int |
273 |
|
|
net_init(void) |
274 |
|
|
{ |
275 |
|
|
struct syncpeer *p; |
276 |
|
|
|
277 |
|
|
if (AES_set_encrypt_key(cfgstate.sharedkey, cfgstate.sharedkey_len, |
278 |
|
|
&aes_key[0]) || |
279 |
|
|
AES_set_decrypt_key(cfgstate.sharedkey, cfgstate.sharedkey_len, |
280 |
|
|
&aes_key[1])) { |
281 |
|
|
fprintf(stderr, "Bad AES shared key\n"); |
282 |
|
|
return -1; |
283 |
|
|
} |
284 |
|
|
|
285 |
|
|
if (net_setup_listeners()) |
286 |
|
|
return -1; |
287 |
|
|
|
288 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { |
289 |
|
|
p->socket = -1; |
290 |
|
|
SIMPLEQ_INIT(&p->msgs); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
net_check_peers(0); |
294 |
|
|
return 0; |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
static void |
298 |
|
|
net_enqueue(struct syncpeer *p, struct msg *m) |
299 |
|
|
{ |
300 |
|
|
struct qmsg *qm; |
301 |
|
|
|
302 |
|
|
if (p->socket < 0) |
303 |
|
|
return; |
304 |
|
|
|
305 |
|
|
qm = calloc(1, sizeof *qm); |
306 |
|
|
if (!qm) { |
307 |
|
|
log_err("net_enqueue: calloc()"); |
308 |
|
|
return; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
qm->msg = m; |
312 |
|
|
m->refcnt++; |
313 |
|
|
|
314 |
|
|
SIMPLEQ_INSERT_TAIL(&p->msgs, qm, next); |
315 |
|
|
return; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
/* |
319 |
|
|
* Queue a message for transmission to a particular peer, |
320 |
|
|
* or to all peers if no peer is specified. |
321 |
|
|
*/ |
322 |
|
|
int |
323 |
|
|
net_queue(struct syncpeer *p0, u_int32_t msgtype, u_int8_t *buf, u_int32_t len) |
324 |
|
|
{ |
325 |
|
|
struct syncpeer *p = p0; |
326 |
|
|
struct msg *m; |
327 |
|
|
SHA_CTX ctx; |
328 |
|
|
u_int8_t hash[SHA_DIGEST_LENGTH]; |
329 |
|
|
u_int8_t iv[AES_IV_LEN], tmp_iv[AES_IV_LEN]; |
330 |
|
|
u_int32_t v, padlen = 0; |
331 |
|
|
int i, offset; |
332 |
|
|
|
333 |
|
|
m = calloc(1, sizeof *m); |
334 |
|
|
if (!m) { |
335 |
|
|
log_err("net_queue: calloc()"); |
336 |
|
|
free(buf); |
337 |
|
|
return -1; |
338 |
|
|
} |
339 |
|
|
|
340 |
|
|
/* Generate hash */ |
341 |
|
|
SHA1_Init(&ctx); |
342 |
|
|
SHA1_Update(&ctx, buf, len); |
343 |
|
|
SHA1_Final(hash, &ctx); |
344 |
|
|
dump_buf(2, hash, sizeof hash, "net_queue: computed hash"); |
345 |
|
|
|
346 |
|
|
/* Padding required? */ |
347 |
|
|
i = len % AES_IV_LEN; |
348 |
|
|
if (i) { |
349 |
|
|
u_int8_t *pbuf; |
350 |
|
|
i = AES_IV_LEN - i; |
351 |
|
|
pbuf = realloc(buf, len + i); |
352 |
|
|
if (!pbuf) { |
353 |
|
|
log_err("net_queue: realloc()"); |
354 |
|
|
free(buf); |
355 |
|
|
free(m); |
356 |
|
|
return -1; |
357 |
|
|
} |
358 |
|
|
padlen = i; |
359 |
|
|
while (i > 0) |
360 |
|
|
pbuf[len++] = (u_int8_t)i--; |
361 |
|
|
buf = pbuf; |
362 |
|
|
} |
363 |
|
|
|
364 |
|
|
/* Get random IV */ |
365 |
|
|
for (i = 0; (size_t)i <= sizeof iv - sizeof v; i += sizeof v) { |
366 |
|
|
v = arc4random(); |
367 |
|
|
memcpy(&iv[i], &v, sizeof v); |
368 |
|
|
} |
369 |
|
|
dump_buf(2, iv, sizeof iv, "net_queue: IV"); |
370 |
|
|
memcpy(tmp_iv, iv, sizeof tmp_iv); |
371 |
|
|
|
372 |
|
|
/* Encrypt */ |
373 |
|
|
dump_buf(2, buf, len, "net_queue: pre encrypt"); |
374 |
|
|
AES_cbc_encrypt(buf, buf, len, &aes_key[0], tmp_iv, AES_ENCRYPT); |
375 |
|
|
dump_buf(2, buf, len, "net_queue: post encrypt"); |
376 |
|
|
|
377 |
|
|
/* Allocate send buffer */ |
378 |
|
|
m->len = len + sizeof iv + sizeof hash + 3 * sizeof(u_int32_t); |
379 |
|
|
m->buf = malloc(m->len); |
380 |
|
|
if (!m->buf) { |
381 |
|
|
free(m); |
382 |
|
|
free(buf); |
383 |
|
|
log_err("net_queue: calloc()"); |
384 |
|
|
return -1; |
385 |
|
|
} |
386 |
|
|
offset = 0; |
387 |
|
|
|
388 |
|
|
/* Fill it (order must match parsing code in net_read()) */ |
389 |
|
|
v = htonl(m->len - sizeof(u_int32_t)); |
390 |
|
|
memcpy(m->buf + offset, &v, sizeof v); |
391 |
|
|
offset += sizeof v; |
392 |
|
|
v = htonl(msgtype); |
393 |
|
|
memcpy(m->buf + offset, &v, sizeof v); |
394 |
|
|
offset += sizeof v; |
395 |
|
|
v = htonl(padlen); |
396 |
|
|
memcpy(m->buf + offset, &v, sizeof v); |
397 |
|
|
offset += sizeof v; |
398 |
|
|
memcpy(m->buf + offset, hash, sizeof hash); |
399 |
|
|
offset += sizeof hash; |
400 |
|
|
memcpy(m->buf + offset, iv, sizeof iv); |
401 |
|
|
offset += sizeof iv; |
402 |
|
|
memcpy(m->buf + offset, buf, len); |
403 |
|
|
free(buf); |
404 |
|
|
|
405 |
|
|
if (p) |
406 |
|
|
net_enqueue(p, m); |
407 |
|
|
else |
408 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; |
409 |
|
|
p = LIST_NEXT(p, link)) |
410 |
|
|
net_enqueue(p, m); |
411 |
|
|
|
412 |
|
|
if (!m->refcnt) { |
413 |
|
|
free(m->buf); |
414 |
|
|
free(m); |
415 |
|
|
} |
416 |
|
|
|
417 |
|
|
return 0; |
418 |
|
|
} |
419 |
|
|
|
420 |
|
|
/* Set all write pending filedescriptors. */ |
421 |
|
|
int |
422 |
|
|
net_set_pending_wfds(fd_set *fds) |
423 |
|
|
{ |
424 |
|
|
struct syncpeer *p; |
425 |
|
|
int max_fd = -1; |
426 |
|
|
|
427 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) |
428 |
|
|
if (p->socket > -1 && SIMPLEQ_FIRST(&p->msgs)) { |
429 |
|
|
FD_SET(p->socket, fds); |
430 |
|
|
if (p->socket > max_fd) |
431 |
|
|
max_fd = p->socket; |
432 |
|
|
} |
433 |
|
|
return max_fd + 1; |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
/* |
437 |
|
|
* Set readable filedescriptors. They are basically the same as for write, |
438 |
|
|
* plus the listening socket. |
439 |
|
|
*/ |
440 |
|
|
int |
441 |
|
|
net_set_rfds(fd_set *fds) |
442 |
|
|
{ |
443 |
|
|
struct syncpeer *p; |
444 |
|
|
int i, max_fd = -1; |
445 |
|
|
|
446 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { |
447 |
|
|
if (p->socket > -1) |
448 |
|
|
FD_SET(p->socket, fds); |
449 |
|
|
if (p->socket > max_fd) |
450 |
|
|
max_fd = p->socket; |
451 |
|
|
} |
452 |
|
|
for (i = 0; listeners[i] != -1; i++) { |
453 |
|
|
FD_SET(listeners[i], fds); |
454 |
|
|
if (listeners[i] > max_fd) |
455 |
|
|
max_fd = listeners[i]; |
456 |
|
|
} |
457 |
|
|
return max_fd + 1; |
458 |
|
|
} |
459 |
|
|
|
460 |
|
|
static void |
461 |
|
|
net_accept(int accept_socket) |
462 |
|
|
{ |
463 |
|
|
struct sockaddr_storage sa_storage, sa_storage2; |
464 |
|
|
struct sockaddr *sa = (struct sockaddr *)&sa_storage; |
465 |
|
|
struct sockaddr *sa2 = (struct sockaddr *)&sa_storage2; |
466 |
|
|
struct sockaddr_in *sin, *sin2; |
467 |
|
|
struct sockaddr_in6 *sin6, *sin62; |
468 |
|
|
struct syncpeer *p; |
469 |
|
|
socklen_t socklen; |
470 |
|
|
int s, found; |
471 |
|
|
|
472 |
|
|
/* Accept a new incoming connection */ |
473 |
|
|
socklen = sizeof sa_storage; |
474 |
|
|
memset(&sa_storage, 0, socklen); |
475 |
|
|
memset(&sa_storage2, 0, socklen); |
476 |
|
|
s = accept(accept_socket, sa, &socklen); |
477 |
|
|
if (s > -1) { |
478 |
|
|
/* Setup the syncpeer structure */ |
479 |
|
|
found = 0; |
480 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p && !found; |
481 |
|
|
p = LIST_NEXT(p, link)) { |
482 |
|
|
|
483 |
|
|
/* Match? */ |
484 |
|
|
if (net_set_sa(sa2, p->name, 0)) |
485 |
|
|
continue; |
486 |
|
|
if (sa->sa_family != sa2->sa_family) |
487 |
|
|
continue; |
488 |
|
|
if (sa->sa_family == AF_INET) { |
489 |
|
|
sin = (struct sockaddr_in *)sa; |
490 |
|
|
sin2 = (struct sockaddr_in *)sa2; |
491 |
|
|
if (memcmp(&sin->sin_addr, &sin2->sin_addr, |
492 |
|
|
sizeof(struct in_addr))) |
493 |
|
|
continue; |
494 |
|
|
} else { |
495 |
|
|
sin6 = (struct sockaddr_in6 *)sa; |
496 |
|
|
sin62 = (struct sockaddr_in6 *)sa2; |
497 |
|
|
if (memcmp(&sin6->sin6_addr, &sin62->sin6_addr, |
498 |
|
|
sizeof(struct in6_addr))) |
499 |
|
|
continue; |
500 |
|
|
} |
501 |
|
|
/* Match! */ |
502 |
|
|
found++; |
503 |
|
|
p->socket = s; |
504 |
|
|
log_msg(1, "net: peer \"%s\" connected", p->name); |
505 |
|
|
if (cfgstate.runstate == MASTER) |
506 |
|
|
timer_add("pfkey_snap", 2, pfkey_snapshot, p); |
507 |
|
|
} |
508 |
|
|
if (!found) { |
509 |
|
|
log_msg(1, "net: found no matching peer for accepted " |
510 |
|
|
"socket, closing."); |
511 |
|
|
close(s); |
512 |
|
|
} |
513 |
|
|
} else if (errno != EWOULDBLOCK && errno != EINTR && |
514 |
|
|
errno != ECONNABORTED) |
515 |
|
|
log_err("net: accept()"); |
516 |
|
|
} |
517 |
|
|
|
518 |
|
|
void |
519 |
|
|
net_handle_messages(fd_set *fds) |
520 |
|
|
{ |
521 |
|
|
struct syncpeer *p; |
522 |
|
|
u_int8_t *msg; |
523 |
|
|
u_int32_t msgtype, msglen; |
524 |
|
|
int i; |
525 |
|
|
|
526 |
|
|
for (i = 0; listeners[i] != -1; i++) |
527 |
|
|
if (FD_ISSET(listeners[i], fds)) |
528 |
|
|
net_accept(listeners[i]); |
529 |
|
|
|
530 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { |
531 |
|
|
if (p->socket < 0 || !FD_ISSET(p->socket, fds)) |
532 |
|
|
continue; |
533 |
|
|
msg = net_read(p, &msgtype, &msglen); |
534 |
|
|
if (!msg) |
535 |
|
|
continue; |
536 |
|
|
|
537 |
|
|
log_msg(2, "net_handle_messages: got msg type %u len %u from " |
538 |
|
|
"peer %s", msgtype, msglen, p->name); |
539 |
|
|
|
540 |
|
|
switch (msgtype) { |
541 |
|
|
case MSG_SYNCCTL: |
542 |
|
|
net_ctl_handle_msg(p, msg, msglen); |
543 |
|
|
free(msg); |
544 |
|
|
break; |
545 |
|
|
|
546 |
|
|
case MSG_PFKEYDATA: |
547 |
|
|
if (p->runstate != MASTER || |
548 |
|
|
cfgstate.runstate == MASTER) { |
549 |
|
|
log_msg(1, "net: got PFKEY message from " |
550 |
|
|
"non-MASTER peer"); |
551 |
|
|
free(msg); |
552 |
|
|
if (cfgstate.runstate == MASTER) |
553 |
|
|
net_ctl_send_state(p); |
554 |
|
|
else |
555 |
|
|
net_ctl_send_error(p, 0); |
556 |
|
|
} else if (pfkey_queue_message(msg, msglen)) |
557 |
|
|
free(msg); |
558 |
|
|
break; |
559 |
|
|
|
560 |
|
|
default: |
561 |
|
|
log_msg(0, "net: got unknown message type %u len %u " |
562 |
|
|
"from peer %s", msgtype, msglen, p->name); |
563 |
|
|
free(msg); |
564 |
|
|
net_ctl_send_error(p, 0); |
565 |
|
|
} |
566 |
|
|
} |
567 |
|
|
} |
568 |
|
|
|
569 |
|
|
void |
570 |
|
|
net_send_messages(fd_set *fds) |
571 |
|
|
{ |
572 |
|
|
struct syncpeer *p; |
573 |
|
|
struct qmsg *qm; |
574 |
|
|
struct msg *m; |
575 |
|
|
ssize_t r; |
576 |
|
|
|
577 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { |
578 |
|
|
if (p->socket < 0 || !FD_ISSET(p->socket, fds)) |
579 |
|
|
continue; |
580 |
|
|
qm = SIMPLEQ_FIRST(&p->msgs); |
581 |
|
|
if (!qm) { |
582 |
|
|
/* XXX Log */ |
583 |
|
|
continue; |
584 |
|
|
} |
585 |
|
|
m = qm->msg; |
586 |
|
|
|
587 |
|
|
log_msg(2, "net_send_messages: msg %p len %u ref %d " |
588 |
|
|
"to peer %s", m, m->len, m->refcnt, p->name); |
589 |
|
|
|
590 |
|
|
/* write message */ |
591 |
|
|
r = write(p->socket, m->buf, m->len); |
592 |
|
|
if (r == -1) { |
593 |
|
|
net_disconnect_peer(p); |
594 |
|
|
log_msg(0, "net_send_messages: write() failed, " |
595 |
|
|
"peer disconnected"); |
596 |
|
|
} else if (r < (ssize_t)m->len) { |
597 |
|
|
/* retransmit later */ |
598 |
|
|
continue; |
599 |
|
|
} |
600 |
|
|
|
601 |
|
|
/* cleanup */ |
602 |
|
|
SIMPLEQ_REMOVE_HEAD(&p->msgs, next); |
603 |
|
|
free(qm); |
604 |
|
|
|
605 |
|
|
if (--m->refcnt < 1) { |
606 |
|
|
log_msg(2, "net_send_messages: freeing msg %p", m); |
607 |
|
|
free(m->buf); |
608 |
|
|
free(m); |
609 |
|
|
} |
610 |
|
|
} |
611 |
|
|
return; |
612 |
|
|
} |
613 |
|
|
|
614 |
|
|
void |
615 |
|
|
net_disconnect_peer(struct syncpeer *p) |
616 |
|
|
{ |
617 |
|
|
if (p->socket > -1) { |
618 |
|
|
log_msg(1, "net_disconnect_peer: peer \"%s\" removed", |
619 |
|
|
p->name); |
620 |
|
|
close(p->socket); |
621 |
|
|
} |
622 |
|
|
p->socket = -1; |
623 |
|
|
} |
624 |
|
|
|
625 |
|
|
void |
626 |
|
|
net_shutdown(void) |
627 |
|
|
{ |
628 |
|
|
struct syncpeer *p; |
629 |
|
|
struct qmsg *qm; |
630 |
|
|
struct msg *m; |
631 |
|
|
int i; |
632 |
|
|
|
633 |
|
|
while ((p = LIST_FIRST(&cfgstate.peerlist))) { |
634 |
|
|
while ((qm = SIMPLEQ_FIRST(&p->msgs))) { |
635 |
|
|
SIMPLEQ_REMOVE_HEAD(&p->msgs, next); |
636 |
|
|
m = qm->msg; |
637 |
|
|
if (--m->refcnt < 1) { |
638 |
|
|
free(m->buf); |
639 |
|
|
free(m); |
640 |
|
|
} |
641 |
|
|
free(qm); |
642 |
|
|
} |
643 |
|
|
net_disconnect_peer(p); |
644 |
|
|
free(p->sa); |
645 |
|
|
free(p->name); |
646 |
|
|
LIST_REMOVE(p, link); |
647 |
|
|
cfgstate.peercnt--; |
648 |
|
|
free(p); |
649 |
|
|
} |
650 |
|
|
|
651 |
|
|
if (listeners) { |
652 |
|
|
for (i = 0; listeners[i] != -1; i++) |
653 |
|
|
close(listeners[i]); |
654 |
|
|
free(listeners); |
655 |
|
|
listeners = 0; |
656 |
|
|
} |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
/* |
660 |
|
|
* Helper functions (local) below here. |
661 |
|
|
*/ |
662 |
|
|
|
663 |
|
|
static u_int8_t * |
664 |
|
|
net_read(struct syncpeer *p, u_int32_t *msgtype, u_int32_t *msglen) |
665 |
|
|
{ |
666 |
|
|
u_int8_t *msg, *blob, *rhash, *iv, hash[SHA_DIGEST_LENGTH]; |
667 |
|
|
u_int32_t v, blob_len, pos = 0; |
668 |
|
|
int padlen = 0, offset = 0; |
669 |
|
|
ssize_t r; |
670 |
|
|
SHA_CTX ctx; |
671 |
|
|
|
672 |
|
|
/* Read blob length */ |
673 |
|
|
r = read(p->socket, &v, sizeof v); |
674 |
|
|
if (r != (ssize_t)sizeof v) { |
675 |
|
|
if (r < 1) |
676 |
|
|
net_disconnect_peer(p); |
677 |
|
|
return NULL; |
678 |
|
|
} |
679 |
|
|
|
680 |
|
|
blob_len = ntohl(v); |
681 |
|
|
if (blob_len < sizeof hash + AES_IV_LEN + 2 * sizeof(u_int32_t)) |
682 |
|
|
return NULL; |
683 |
|
|
*msglen = blob_len - sizeof hash - AES_IV_LEN - 2 * sizeof(u_int32_t); |
684 |
|
|
if (*msglen < MSG_MINLEN || *msglen > MSG_MAXLEN) |
685 |
|
|
return NULL; |
686 |
|
|
|
687 |
|
|
/* Read message blob */ |
688 |
|
|
blob = malloc(blob_len); |
689 |
|
|
if (!blob) { |
690 |
|
|
log_err("net_read: malloc()"); |
691 |
|
|
return NULL; |
692 |
|
|
} |
693 |
|
|
|
694 |
|
|
while (blob_len > pos) { |
695 |
|
|
switch (r = read(p->socket, blob + pos, blob_len - pos)) { |
696 |
|
|
case -1: |
697 |
|
|
if (errno == EINTR || errno == EAGAIN) |
698 |
|
|
continue; |
699 |
|
|
/* FALLTHROUGH */ |
700 |
|
|
case 0: |
701 |
|
|
net_disconnect_peer(p); |
702 |
|
|
free(blob); |
703 |
|
|
return NULL; |
704 |
|
|
/* NOTREACHED */ |
705 |
|
|
default: |
706 |
|
|
pos += r; |
707 |
|
|
} |
708 |
|
|
} |
709 |
|
|
|
710 |
|
|
offset = 0; |
711 |
|
|
memcpy(&v, blob + offset, sizeof v); |
712 |
|
|
*msgtype = ntohl(v); |
713 |
|
|
offset += sizeof v; |
714 |
|
|
|
715 |
|
|
if (*msgtype > MSG_MAXTYPE) { |
716 |
|
|
free(blob); |
717 |
|
|
return NULL; |
718 |
|
|
} |
719 |
|
|
|
720 |
|
|
memcpy(&v, blob + offset, sizeof v); |
721 |
|
|
padlen = ntohl(v); |
722 |
|
|
offset += sizeof v; |
723 |
|
|
|
724 |
|
|
rhash = blob + offset; |
725 |
|
|
iv = rhash + sizeof hash; |
726 |
|
|
msg = malloc(*msglen); |
727 |
|
|
if (!msg) { |
728 |
|
|
free(blob); |
729 |
|
|
return NULL; |
730 |
|
|
} |
731 |
|
|
memcpy(msg, iv + AES_IV_LEN, *msglen); |
732 |
|
|
|
733 |
|
|
dump_buf(2, rhash, sizeof hash, "net_read: got hash"); |
734 |
|
|
dump_buf(2, iv, AES_IV_LEN, "net_read: got IV"); |
735 |
|
|
dump_buf(2, msg, *msglen, "net_read: pre decrypt"); |
736 |
|
|
AES_cbc_encrypt(msg, msg, *msglen, &aes_key[1], iv, AES_DECRYPT); |
737 |
|
|
dump_buf(2, msg, *msglen, "net_read: post decrypt"); |
738 |
|
|
*msglen -= padlen; |
739 |
|
|
|
740 |
|
|
SHA1_Init(&ctx); |
741 |
|
|
SHA1_Update(&ctx, msg, *msglen); |
742 |
|
|
SHA1_Final(hash, &ctx); |
743 |
|
|
dump_buf(2, hash, sizeof hash, "net_read: computed hash"); |
744 |
|
|
|
745 |
|
|
if (memcmp(hash, rhash, sizeof hash) != 0) { |
746 |
|
|
free(blob); |
747 |
|
|
free(msg); |
748 |
|
|
log_msg(0, "net_read: got bad message (typo in shared key?)"); |
749 |
|
|
return NULL; |
750 |
|
|
} |
751 |
|
|
free(blob); |
752 |
|
|
return msg; |
753 |
|
|
} |
754 |
|
|
|
755 |
|
|
static int |
756 |
|
|
net_set_sa(struct sockaddr *sa, char *name, in_port_t port) |
757 |
|
|
{ |
758 |
|
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa; |
759 |
|
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; |
760 |
|
|
|
761 |
|
|
if (!name) { |
762 |
|
|
/* XXX Assume IPv4 */ |
763 |
|
|
sa->sa_family = AF_INET; |
764 |
|
|
sin->sin_port = htons(port); |
765 |
|
|
sin->sin_len = sizeof *sin; |
766 |
|
|
return 0; |
767 |
|
|
} |
768 |
|
|
|
769 |
|
|
if (inet_pton(AF_INET, name, &sin->sin_addr) == 1) { |
770 |
|
|
sa->sa_family = AF_INET; |
771 |
|
|
sin->sin_port = htons(port); |
772 |
|
|
sin->sin_len = sizeof *sin; |
773 |
|
|
return 0; |
774 |
|
|
} |
775 |
|
|
|
776 |
|
|
if (inet_pton(AF_INET6, name, &sin6->sin6_addr) == 1) { |
777 |
|
|
sa->sa_family = AF_INET6; |
778 |
|
|
sin6->sin6_port = htons(port); |
779 |
|
|
sin6->sin6_len = sizeof *sin6; |
780 |
|
|
return 0; |
781 |
|
|
} |
782 |
|
|
|
783 |
|
|
return -1; |
784 |
|
|
} |
785 |
|
|
|
786 |
|
|
static void |
787 |
|
|
got_sigalrm(int s) |
788 |
|
|
{ |
789 |
|
|
return; |
790 |
|
|
} |
791 |
|
|
|
792 |
|
|
void |
793 |
|
|
net_connect(void) |
794 |
|
|
{ |
795 |
|
|
struct itimerval iv; |
796 |
|
|
struct syncpeer *p; |
797 |
|
|
|
798 |
|
|
signal(SIGALRM, got_sigalrm); |
799 |
|
|
memset(&iv, 0, sizeof iv); |
800 |
|
|
iv.it_value.tv_sec = 5; |
801 |
|
|
iv.it_interval.tv_sec = 5; |
802 |
|
|
setitimer(ITIMER_REAL, &iv, NULL); |
803 |
|
|
|
804 |
|
|
for (p = LIST_FIRST(&cfgstate.peerlist); p; p = LIST_NEXT(p, link)) { |
805 |
|
|
if (p->socket > -1) |
806 |
|
|
continue; |
807 |
|
|
if (!p->sa) { |
808 |
|
|
p->sa = calloc(1, sizeof(struct sockaddr_storage)); |
809 |
|
|
if (!p->sa) |
810 |
|
|
return; |
811 |
|
|
if (net_set_sa(p->sa, p->name, cfgstate.listen_port)) |
812 |
|
|
continue; |
813 |
|
|
} |
814 |
|
|
p->socket = socket(p->sa->sa_family, SOCK_STREAM, 0); |
815 |
|
|
if (p->socket < 0) { |
816 |
|
|
log_err("peer \"%s\": socket()", p->name); |
817 |
|
|
continue; |
818 |
|
|
} |
819 |
|
|
if (connect(p->socket, p->sa, p->sa->sa_len)) { |
820 |
|
|
log_msg(1, "net_connect: peer \"%s\" not ready yet", |
821 |
|
|
p->name); |
822 |
|
|
net_disconnect_peer(p); |
823 |
|
|
continue; |
824 |
|
|
} |
825 |
|
|
if (net_ctl_send_state(p)) { |
826 |
|
|
log_msg(0, "net_connect: peer \"%s\" failed", p->name); |
827 |
|
|
net_disconnect_peer(p); |
828 |
|
|
continue; |
829 |
|
|
} |
830 |
|
|
log_msg(1, "net_connect: peer \"%s\" connected, fd %d", |
831 |
|
|
p->name, p->socket); |
832 |
|
|
|
833 |
|
|
/* Schedule a pfkey sync to the newly connected peer. */ |
834 |
|
|
if (cfgstate.runstate == MASTER) |
835 |
|
|
timer_add("pfkey_snapshot", 2, pfkey_snapshot, p); |
836 |
|
|
} |
837 |
|
|
|
838 |
|
|
timerclear(&iv.it_value); |
839 |
|
|
timerclear(&iv.it_interval); |
840 |
|
|
setitimer(ITIMER_REAL, &iv, NULL); |
841 |
|
|
signal(SIGALRM, SIG_IGN); |
842 |
|
|
|
843 |
|
|
return; |
844 |
|
|
} |
845 |
|
|
|
846 |
|
|
static void |
847 |
|
|
net_check_peers(void *arg) |
848 |
|
|
{ |
849 |
|
|
net_connect(); |
850 |
|
|
(void)timer_add("peer recheck", 600, net_check_peers, 0); |
851 |
|
|
} |