]> git.proxmox.com Git - mirror_frr.git/blob - nhrpd/vici.c
ospfd zebra: array overrun fixes (PVS-Studio)
[mirror_frr.git] / nhrpd / vici.c
1 /* strongSwan VICI protocol implementation for NHRP
2 * Copyright (c) 2014-2015 Timo Teräs
3 *
4 * This file is free software: you may copy, redistribute and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10 #include <string.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13
14 #include "thread.h"
15 #include "zbuf.h"
16 #include "log.h"
17 #include "nhrpd.h"
18
19 #include "vici.h"
20
21 #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
22
23 struct blob {
24 char *ptr;
25 int len;
26 };
27
28 static int blob_equal(const struct blob *b, const char *str)
29 {
30 if (!b || b->len != (int)strlen(str))
31 return 0;
32 return memcmp(b->ptr, str, b->len) == 0;
33 }
34
35 static int blob2buf(const struct blob *b, char *buf, size_t n)
36 {
37 if (!b || b->len >= (int)n)
38 return 0;
39 memcpy(buf, b->ptr, b->len);
40 buf[b->len] = 0;
41 return 1;
42 }
43
44 struct vici_conn {
45 struct thread *t_reconnect, *t_read, *t_write;
46 struct zbuf ibuf;
47 struct zbuf_queue obuf;
48 int fd;
49 uint8_t ibuf_data[VICI_MAX_MSGLEN];
50 };
51
52 struct vici_message_ctx {
53 const char *sections[8];
54 int nsections;
55 };
56
57 static int vici_reconnect(struct thread *t);
58 static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
59
60 static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
61 {
62 size_t len = strlen(str);
63 zbuf_put8(obuf, len);
64 zbuf_put(obuf, str, len);
65 }
66
67 static void vici_connection_error(struct vici_conn *vici)
68 {
69 nhrp_vc_reset();
70
71 THREAD_OFF(vici->t_read);
72 THREAD_OFF(vici->t_write);
73 zbuf_reset(&vici->ibuf);
74 zbufq_reset(&vici->obuf);
75
76 close(vici->fd);
77 vici->fd = -1;
78 thread_add_timer(master, vici_reconnect, vici, 2, &vici->t_reconnect);
79 }
80
81 static void vici_parse_message(struct vici_conn *vici, struct zbuf *msg,
82 void (*parser)(struct vici_message_ctx *ctx,
83 enum vici_type_t msgtype,
84 const struct blob *key,
85 const struct blob *val),
86 struct vici_message_ctx *ctx)
87 {
88 uint8_t *type;
89 struct blob key = {0};
90 struct blob val = {0};
91
92 while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
93 switch (*type) {
94 case VICI_SECTION_START:
95 key.len = zbuf_get8(msg);
96 key.ptr = zbuf_pulln(msg, key.len);
97 debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'",
98 key.len, key.ptr);
99 parser(ctx, *type, &key, NULL);
100 ctx->nsections++;
101 break;
102 case VICI_SECTION_END:
103 debugf(NHRP_DEBUG_VICI, "VICI: Section end");
104 parser(ctx, *type, NULL, NULL);
105 ctx->nsections--;
106 break;
107 case VICI_KEY_VALUE:
108 key.len = zbuf_get8(msg);
109 key.ptr = zbuf_pulln(msg, key.len);
110 val.len = zbuf_get_be16(msg);
111 val.ptr = zbuf_pulln(msg, val.len);
112 debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'",
113 key.len, key.ptr, val.len, val.ptr);
114 parser(ctx, *type, &key, &val);
115 break;
116 case VICI_LIST_START:
117 key.len = zbuf_get8(msg);
118 key.ptr = zbuf_pulln(msg, key.len);
119 debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'",
120 key.len, key.ptr);
121 break;
122 case VICI_LIST_ITEM:
123 val.len = zbuf_get_be16(msg);
124 val.ptr = zbuf_pulln(msg, val.len);
125 debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'",
126 val.len, val.ptr);
127 parser(ctx, *type, &key, &val);
128 break;
129 case VICI_LIST_END:
130 debugf(NHRP_DEBUG_VICI, "VICI: List end");
131 break;
132 default:
133 debugf(NHRP_DEBUG_VICI,
134 "VICI: Unsupported message component type %d",
135 *type);
136 return;
137 }
138 }
139 }
140
141 struct handle_sa_ctx {
142 struct vici_message_ctx msgctx;
143 int event;
144 int child_ok;
145 int kill_ikesa;
146 uint32_t child_uniqueid, ike_uniqueid;
147 struct {
148 union sockunion host;
149 struct blob id, cert;
150 } local, remote;
151 };
152
153 static void parse_sa_message(struct vici_message_ctx *ctx,
154 enum vici_type_t msgtype, const struct blob *key,
155 const struct blob *val)
156 {
157 struct handle_sa_ctx *sactx =
158 container_of(ctx, struct handle_sa_ctx, msgctx);
159 struct nhrp_vc *vc;
160 char buf[512];
161
162 switch (msgtype) {
163 case VICI_SECTION_START:
164 if (ctx->nsections == 3) {
165 /* Begin of child-sa section, reset child vars */
166 sactx->child_uniqueid = 0;
167 sactx->child_ok = 0;
168 }
169 break;
170 case VICI_SECTION_END:
171 if (ctx->nsections == 3) {
172 /* End of child-sa section, update nhrp_vc */
173 int up = sactx->child_ok || sactx->event == 1;
174 if (up) {
175 vc = nhrp_vc_get(&sactx->local.host,
176 &sactx->remote.host, up);
177 if (vc) {
178 blob2buf(&sactx->local.id, vc->local.id,
179 sizeof(vc->local.id));
180 if (blob2buf(&sactx->local.cert,
181 (char *)vc->local.cert,
182 sizeof(vc->local.cert)))
183 vc->local.certlen =
184 sactx->local.cert.len;
185 blob2buf(&sactx->remote.id,
186 vc->remote.id,
187 sizeof(vc->remote.id));
188 if (blob2buf(&sactx->remote.cert,
189 (char *)vc->remote.cert,
190 sizeof(vc->remote.cert)))
191 vc->remote.certlen =
192 sactx->remote.cert.len;
193 sactx->kill_ikesa |=
194 nhrp_vc_ipsec_updown(
195 sactx->child_uniqueid,
196 vc);
197 }
198 } else {
199 nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
200 }
201 }
202 break;
203 default:
204 if (!key)
205 break;
206
207 switch (key->ptr[0]) {
208 case 'l':
209 if (blob_equal(key, "local-host")
210 && ctx->nsections == 1) {
211 if (blob2buf(val, buf, sizeof(buf)))
212 if (str2sockunion(buf,
213 &sactx->local.host)
214 < 0)
215 zlog_err(
216 "VICI: bad strongSwan local-host: %s",
217 buf);
218 } else if (blob_equal(key, "local-id")
219 && ctx->nsections == 1) {
220 sactx->local.id = *val;
221 } else if (blob_equal(key, "local-cert-data")
222 && ctx->nsections == 1) {
223 sactx->local.cert = *val;
224 }
225 break;
226 case 'r':
227 if (blob_equal(key, "remote-host")
228 && ctx->nsections == 1) {
229 if (blob2buf(val, buf, sizeof(buf)))
230 if (str2sockunion(buf,
231 &sactx->remote.host)
232 < 0)
233 zlog_err(
234 "VICI: bad strongSwan remote-host: %s",
235 buf);
236 } else if (blob_equal(key, "remote-id")
237 && ctx->nsections == 1) {
238 sactx->remote.id = *val;
239 } else if (blob_equal(key, "remote-cert-data")
240 && ctx->nsections == 1) {
241 sactx->remote.cert = *val;
242 }
243 break;
244 case 'u':
245 if (blob_equal(key, "uniqueid")
246 && blob2buf(val, buf, sizeof(buf))) {
247 if (ctx->nsections == 3)
248 sactx->child_uniqueid =
249 strtoul(buf, NULL, 0);
250 else if (ctx->nsections == 1)
251 sactx->ike_uniqueid =
252 strtoul(buf, NULL, 0);
253 }
254 break;
255 case 's':
256 if (blob_equal(key, "state") && ctx->nsections == 3) {
257 sactx->child_ok =
258 (sactx->event == 0
259 && (blob_equal(val, "INSTALLED")
260 || blob_equal(val, "REKEYED")));
261 }
262 break;
263 }
264 break;
265 }
266 }
267
268 static void parse_cmd_response(struct vici_message_ctx *ctx,
269 enum vici_type_t msgtype, const struct blob *key,
270 const struct blob *val)
271 {
272 char buf[512];
273
274 switch (msgtype) {
275 case VICI_KEY_VALUE:
276 if (blob_equal(key, "errmsg")
277 && blob2buf(val, buf, sizeof(buf)))
278 zlog_err("VICI: strongSwan: %s", buf);
279 break;
280 default:
281 break;
282 }
283 }
284
285 static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
286 {
287 char buf[32];
288 struct handle_sa_ctx ctx = {
289 .event = event,
290 .msgctx.nsections = 0
291 };
292
293 vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
294
295 if (ctx.kill_ikesa && ctx.ike_uniqueid) {
296 debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u",
297 ctx.ike_uniqueid);
298 snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
299 vici_submit_request(vici, "terminate", VICI_KEY_VALUE, "ike-id",
300 strlen(buf), buf, VICI_END);
301 }
302 }
303
304 static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
305 {
306 uint32_t msglen;
307 uint8_t msgtype;
308 struct blob name;
309 struct vici_message_ctx ctx;
310
311 msglen = zbuf_get_be32(msg);
312 msgtype = zbuf_get8(msg);
313 debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
314
315 switch (msgtype) {
316 case VICI_EVENT:
317 name.len = zbuf_get8(msg);
318 name.ptr = zbuf_pulln(msg, name.len);
319
320 debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len,
321 name.ptr);
322 if (blob_equal(&name, "list-sa")
323 || blob_equal(&name, "child-updown")
324 || blob_equal(&name, "child-rekey"))
325 vici_recv_sa(vici, msg, 0);
326 else if (blob_equal(&name, "child-state-installed")
327 || blob_equal(&name, "child-state-rekeyed"))
328 vici_recv_sa(vici, msg, 1);
329 else if (blob_equal(&name, "child-state-destroying"))
330 vici_recv_sa(vici, msg, 2);
331 break;
332 case VICI_CMD_RESPONSE:
333 vici_parse_message(vici, msg, parse_cmd_response, &ctx);
334 break;
335 case VICI_EVENT_UNKNOWN:
336 case VICI_CMD_UNKNOWN:
337 zlog_err(
338 "VICI: StrongSwan does not support mandatory events (unpatched?)");
339 break;
340 case VICI_EVENT_CONFIRM:
341 break;
342 default:
343 zlog_notice("VICI: Unrecognized message type %d", msgtype);
344 break;
345 }
346 }
347
348 static int vici_read(struct thread *t)
349 {
350 struct vici_conn *vici = THREAD_ARG(t);
351 struct zbuf *ibuf = &vici->ibuf;
352 struct zbuf pktbuf;
353
354 vici->t_read = NULL;
355 if (zbuf_read(ibuf, vici->fd, (size_t)-1) < 0) {
356 vici_connection_error(vici);
357 return 0;
358 }
359
360 /* Process all messages in buffer */
361 do {
362 uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
363 if (!hdrlen)
364 break;
365 if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
366 zbuf_reset_head(ibuf, hdrlen);
367 break;
368 }
369
370 /* Handle packet */
371 zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen) + 4,
372 htonl(*hdrlen) + 4);
373 vici_recv_message(vici, &pktbuf);
374 } while (1);
375
376 thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read);
377 return 0;
378 }
379
380 static int vici_write(struct thread *t)
381 {
382 struct vici_conn *vici = THREAD_ARG(t);
383 int r;
384
385 vici->t_write = NULL;
386 r = zbufq_write(&vici->obuf, vici->fd);
387 if (r > 0) {
388 thread_add_write(master, vici_write, vici, vici->fd,
389 &vici->t_write);
390 } else if (r < 0) {
391 vici_connection_error(vici);
392 }
393
394 return 0;
395 }
396
397 static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
398 {
399 if (vici->fd < 0) {
400 zbuf_free(obuf);
401 return;
402 }
403
404 zbufq_queue(&vici->obuf, obuf);
405 thread_add_write(master, vici_write, vici, vici->fd, &vici->t_write);
406 }
407
408 static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
409 {
410 struct zbuf *obuf;
411 uint32_t *hdrlen;
412 va_list va;
413 size_t len;
414 int type;
415
416 obuf = zbuf_alloc(256);
417 if (!obuf)
418 return;
419
420 hdrlen = zbuf_push(obuf, uint32_t);
421 zbuf_put8(obuf, VICI_CMD_REQUEST);
422 vici_zbuf_puts(obuf, name);
423
424 va_start(va, name);
425 for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
426 zbuf_put8(obuf, type);
427 switch (type) {
428 case VICI_KEY_VALUE:
429 vici_zbuf_puts(obuf, va_arg(va, const char *));
430 len = va_arg(va, size_t);
431 zbuf_put_be16(obuf, len);
432 zbuf_put(obuf, va_arg(va, void *), len);
433 break;
434 default:
435 break;
436 }
437 }
438 va_end(va);
439 *hdrlen = htonl(zbuf_used(obuf) - 4);
440 vici_submit(vici, obuf);
441 }
442
443 static void vici_register_event(struct vici_conn *vici, const char *name)
444 {
445 struct zbuf *obuf;
446 uint32_t *hdrlen;
447 uint8_t namelen;
448
449 namelen = strlen(name);
450 obuf = zbuf_alloc(4 + 1 + 1 + namelen);
451 if (!obuf)
452 return;
453
454 hdrlen = zbuf_push(obuf, uint32_t);
455 zbuf_put8(obuf, VICI_EVENT_REGISTER);
456 zbuf_put8(obuf, namelen);
457 zbuf_put(obuf, name, namelen);
458 *hdrlen = htonl(zbuf_used(obuf) - 4);
459
460 vici_submit(vici, obuf);
461 }
462
463 static int vici_reconnect(struct thread *t)
464 {
465 struct vici_conn *vici = THREAD_ARG(t);
466 int fd;
467
468 vici->t_reconnect = NULL;
469 if (vici->fd >= 0)
470 return 0;
471
472 fd = sock_open_unix("/var/run/charon.vici");
473 if (fd < 0) {
474 debugf(NHRP_DEBUG_VICI,
475 "%s: failure connecting VICI socket: %s",
476 __PRETTY_FUNCTION__, strerror(errno));
477 thread_add_timer(master, vici_reconnect, vici, 2,
478 &vici->t_reconnect);
479 return 0;
480 }
481
482 debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
483 vici->fd = fd;
484 thread_add_read(master, vici_read, vici, vici->fd, &vici->t_read);
485
486 /* Send event subscribtions */
487 // vici_register_event(vici, "child-updown");
488 // vici_register_event(vici, "child-rekey");
489 vici_register_event(vici, "child-state-installed");
490 vici_register_event(vici, "child-state-rekeyed");
491 vici_register_event(vici, "child-state-destroying");
492 vici_register_event(vici, "list-sa");
493 vici_submit_request(vici, "list-sas", VICI_END);
494
495 return 0;
496 }
497
498 static struct vici_conn vici_connection;
499
500 void vici_init(void)
501 {
502 struct vici_conn *vici = &vici_connection;
503
504 vici->fd = -1;
505 zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
506 zbufq_init(&vici->obuf);
507 thread_add_timer_msec(master, vici_reconnect, vici, 10,
508 &vici->t_reconnect);
509 }
510
511 void vici_terminate(void)
512 {
513 }
514
515 void vici_request_vc(const char *profile, union sockunion *src,
516 union sockunion *dst, int prio)
517 {
518 struct vici_conn *vici = &vici_connection;
519 char buf[2][SU_ADDRSTRLEN];
520
521 sockunion2str(src, buf[0], sizeof buf[0]);
522 sockunion2str(dst, buf[1], sizeof buf[1]);
523
524 vici_submit_request(vici, "initiate", VICI_KEY_VALUE, "child",
525 strlen(profile), profile, VICI_KEY_VALUE, "timeout",
526 (size_t)2, "-1", VICI_KEY_VALUE, "async", (size_t)1,
527 "1", VICI_KEY_VALUE, "init-limits", (size_t)1,
528 prio ? "0" : "1", VICI_KEY_VALUE, "my-host",
529 strlen(buf[0]), buf[0], VICI_KEY_VALUE,
530 "other-host", strlen(buf[1]), buf[1], VICI_END);
531 }
532
533 int sock_open_unix(const char *path)
534 {
535 int ret, fd;
536 struct sockaddr_un addr;
537
538 fd = socket(AF_UNIX, SOCK_STREAM, 0);
539 if (fd < 0)
540 return -1;
541
542 memset(&addr, 0, sizeof(struct sockaddr_un));
543 addr.sun_family = AF_UNIX;
544 strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
545
546 ret = connect(fd, (struct sockaddr *)&addr,
547 sizeof(addr.sun_family) + strlen(addr.sun_path));
548 if (ret < 0) {
549 close(fd);
550 return -1;
551 }
552
553 ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
554 if (ret < 0) {
555 close(fd);
556 return -1;
557 }
558
559 return fd;
560 }