1 /* strongSwan VICI protocol implementation for NHRP
2 * Copyright (c) 2014-2015 Timo Teräs
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.
15 #include <sys/socket.h>
21 #include "lib_errors.h"
25 #include "nhrp_errors.h"
27 #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
34 static int blob_equal(const struct blob
*b
, const char *str
)
36 if (!b
|| b
->len
!= (int)strlen(str
))
38 return memcmp(b
->ptr
, str
, b
->len
) == 0;
41 static int blob2buf(const struct blob
*b
, char *buf
, size_t n
)
43 if (!b
|| b
->len
>= (int)n
)
45 memcpy(buf
, b
->ptr
, b
->len
);
51 struct thread
*t_reconnect
, *t_read
, *t_write
;
53 struct zbuf_queue obuf
;
55 uint8_t ibuf_data
[VICI_MAX_MSGLEN
];
58 struct vici_message_ctx
{
59 const char *sections
[8];
63 static int vici_reconnect(struct thread
*t
);
64 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...);
66 static void vici_zbuf_puts(struct zbuf
*obuf
, const char *str
)
68 size_t len
= strlen(str
);
70 zbuf_put(obuf
, str
, len
);
73 static void vici_connection_error(struct vici_conn
*vici
)
77 THREAD_OFF(vici
->t_read
);
78 THREAD_OFF(vici
->t_write
);
79 zbuf_reset(&vici
->ibuf
);
80 zbufq_reset(&vici
->obuf
);
84 thread_add_timer(master
, vici_reconnect
, vici
, 2, &vici
->t_reconnect
);
87 static void vici_parse_message(struct vici_conn
*vici
, struct zbuf
*msg
,
88 void (*parser
)(struct vici_message_ctx
*ctx
,
89 enum vici_type_t msgtype
,
90 const struct blob
*key
,
91 const struct blob
*val
),
92 struct vici_message_ctx
*ctx
)
95 struct blob key
= {0};
96 struct blob val
= {0};
98 while ((type
= zbuf_may_pull(msg
, uint8_t)) != NULL
) {
100 case VICI_SECTION_START
:
101 key
.len
= zbuf_get8(msg
);
102 key
.ptr
= zbuf_pulln(msg
, key
.len
);
103 debugf(NHRP_DEBUG_VICI
, "VICI: Section start '%.*s'",
105 parser(ctx
, *type
, &key
, NULL
);
108 case VICI_SECTION_END
:
109 debugf(NHRP_DEBUG_VICI
, "VICI: Section end");
110 parser(ctx
, *type
, NULL
, NULL
);
114 key
.len
= zbuf_get8(msg
);
115 key
.ptr
= zbuf_pulln(msg
, key
.len
);
116 val
.len
= zbuf_get_be16(msg
);
117 val
.ptr
= zbuf_pulln(msg
, val
.len
);
118 debugf(NHRP_DEBUG_VICI
, "VICI: Key '%.*s'='%.*s'",
119 key
.len
, key
.ptr
, val
.len
, val
.ptr
);
120 parser(ctx
, *type
, &key
, &val
);
122 case VICI_LIST_START
:
123 key
.len
= zbuf_get8(msg
);
124 key
.ptr
= zbuf_pulln(msg
, key
.len
);
125 debugf(NHRP_DEBUG_VICI
, "VICI: List start '%.*s'",
129 val
.len
= zbuf_get_be16(msg
);
130 val
.ptr
= zbuf_pulln(msg
, val
.len
);
131 debugf(NHRP_DEBUG_VICI
, "VICI: List item: '%.*s'",
133 parser(ctx
, *type
, &key
, &val
);
136 debugf(NHRP_DEBUG_VICI
, "VICI: List end");
139 debugf(NHRP_DEBUG_VICI
,
140 "VICI: Unsupported message component type %d",
147 struct handle_sa_ctx
{
148 struct vici_message_ctx msgctx
;
152 uint32_t child_uniqueid
, ike_uniqueid
;
154 union sockunion host
;
155 struct blob id
, cert
;
159 static void parse_sa_message(struct vici_message_ctx
*ctx
,
160 enum vici_type_t msgtype
, const struct blob
*key
,
161 const struct blob
*val
)
163 struct handle_sa_ctx
*sactx
=
164 container_of(ctx
, struct handle_sa_ctx
, msgctx
);
169 case VICI_SECTION_START
:
170 if (ctx
->nsections
== 3) {
171 /* Begin of child-sa section, reset child vars */
172 sactx
->child_uniqueid
= 0;
176 case VICI_SECTION_END
:
177 if (ctx
->nsections
== 3) {
178 /* End of child-sa section, update nhrp_vc */
179 int up
= sactx
->child_ok
|| sactx
->event
== 1;
181 vc
= nhrp_vc_get(&sactx
->local
.host
,
182 &sactx
->remote
.host
, up
);
184 blob2buf(&sactx
->local
.id
, vc
->local
.id
,
185 sizeof(vc
->local
.id
));
186 if (blob2buf(&sactx
->local
.cert
,
187 (char *)vc
->local
.cert
,
188 sizeof(vc
->local
.cert
)))
190 sactx
->local
.cert
.len
;
191 blob2buf(&sactx
->remote
.id
,
193 sizeof(vc
->remote
.id
));
194 if (blob2buf(&sactx
->remote
.cert
,
195 (char *)vc
->remote
.cert
,
196 sizeof(vc
->remote
.cert
)))
198 sactx
->remote
.cert
.len
;
200 nhrp_vc_ipsec_updown(
201 sactx
->child_uniqueid
,
205 nhrp_vc_ipsec_updown(sactx
->child_uniqueid
, 0);
213 switch (key
->ptr
[0]) {
215 if (blob_equal(key
, "local-host")
216 && ctx
->nsections
== 1) {
217 if (blob2buf(val
, buf
, sizeof(buf
)))
218 if (str2sockunion(buf
,
223 "VICI: bad strongSwan local-host: %s",
225 } else if (blob_equal(key
, "local-id")
226 && ctx
->nsections
== 1) {
227 sactx
->local
.id
= *val
;
228 } else if (blob_equal(key
, "local-cert-data")
229 && ctx
->nsections
== 1) {
230 sactx
->local
.cert
= *val
;
234 if (blob_equal(key
, "remote-host")
235 && ctx
->nsections
== 1) {
236 if (blob2buf(val
, buf
, sizeof(buf
)))
237 if (str2sockunion(buf
,
242 "VICI: bad strongSwan remote-host: %s",
244 } else if (blob_equal(key
, "remote-id")
245 && ctx
->nsections
== 1) {
246 sactx
->remote
.id
= *val
;
247 } else if (blob_equal(key
, "remote-cert-data")
248 && ctx
->nsections
== 1) {
249 sactx
->remote
.cert
= *val
;
253 if (blob_equal(key
, "uniqueid")
254 && blob2buf(val
, buf
, sizeof(buf
))) {
255 if (ctx
->nsections
== 3)
256 sactx
->child_uniqueid
=
257 strtoul(buf
, NULL
, 0);
258 else if (ctx
->nsections
== 1)
259 sactx
->ike_uniqueid
=
260 strtoul(buf
, NULL
, 0);
264 if (blob_equal(key
, "state") && ctx
->nsections
== 3) {
267 && (blob_equal(val
, "INSTALLED")
268 || blob_equal(val
, "REKEYED")));
276 static void parse_cmd_response(struct vici_message_ctx
*ctx
,
277 enum vici_type_t msgtype
, const struct blob
*key
,
278 const struct blob
*val
)
284 if (blob_equal(key
, "errmsg")
285 && blob2buf(val
, buf
, sizeof(buf
)))
286 flog_err(EC_NHRP_SWAN
, "VICI: strongSwan: %s", buf
);
293 static void vici_recv_sa(struct vici_conn
*vici
, struct zbuf
*msg
, int event
)
296 struct handle_sa_ctx ctx
= {
298 .msgctx
.nsections
= 0
301 vici_parse_message(vici
, msg
, parse_sa_message
, &ctx
.msgctx
);
303 if (ctx
.kill_ikesa
&& ctx
.ike_uniqueid
) {
304 debugf(NHRP_DEBUG_COMMON
, "VICI: Deleting IKE_SA %u",
306 snprintf(buf
, sizeof buf
, "%u", ctx
.ike_uniqueid
);
307 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
308 strlen(buf
), buf
, VICI_END
);
312 static void vici_recv_message(struct vici_conn
*vici
, struct zbuf
*msg
)
317 struct vici_message_ctx ctx
= { .nsections
= 0 };
319 msglen
= zbuf_get_be32(msg
);
320 msgtype
= zbuf_get8(msg
);
321 debugf(NHRP_DEBUG_VICI
, "VICI: Message %d, %d bytes", msgtype
, msglen
);
325 name
.len
= zbuf_get8(msg
);
326 name
.ptr
= zbuf_pulln(msg
, name
.len
);
328 debugf(NHRP_DEBUG_VICI
, "VICI: Event '%.*s'", name
.len
,
330 if (blob_equal(&name
, "list-sa")
331 || blob_equal(&name
, "child-updown")
332 || blob_equal(&name
, "child-rekey"))
333 vici_recv_sa(vici
, msg
, 0);
334 else if (blob_equal(&name
, "child-state-installed")
335 || blob_equal(&name
, "child-state-rekeyed"))
336 vici_recv_sa(vici
, msg
, 1);
337 else if (blob_equal(&name
, "child-state-destroying"))
338 vici_recv_sa(vici
, msg
, 2);
340 case VICI_CMD_RESPONSE
:
341 vici_parse_message(vici
, msg
, parse_cmd_response
, &ctx
);
343 case VICI_EVENT_UNKNOWN
:
344 case VICI_CMD_UNKNOWN
:
347 "VICI: StrongSwan does not support mandatory events (unpatched?)");
349 case VICI_EVENT_CONFIRM
:
352 zlog_notice("VICI: Unrecognized message type %d", msgtype
);
357 static int vici_read(struct thread
*t
)
359 struct vici_conn
*vici
= THREAD_ARG(t
);
360 struct zbuf
*ibuf
= &vici
->ibuf
;
364 if (zbuf_read(ibuf
, vici
->fd
, (size_t)-1) < 0) {
365 vici_connection_error(vici
);
369 /* Process all messages in buffer */
371 uint32_t *hdrlen
= zbuf_may_pull(ibuf
, uint32_t);
374 if (!zbuf_may_pulln(ibuf
, ntohl(*hdrlen
))) {
375 zbuf_reset_head(ibuf
, hdrlen
);
380 zbuf_init(&pktbuf
, hdrlen
, htonl(*hdrlen
) + 4,
382 vici_recv_message(vici
, &pktbuf
);
385 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
389 static int vici_write(struct thread
*t
)
391 struct vici_conn
*vici
= THREAD_ARG(t
);
394 vici
->t_write
= NULL
;
395 r
= zbufq_write(&vici
->obuf
, vici
->fd
);
397 thread_add_write(master
, vici_write
, vici
, vici
->fd
,
400 vici_connection_error(vici
);
406 static void vici_submit(struct vici_conn
*vici
, struct zbuf
*obuf
)
413 zbufq_queue(&vici
->obuf
, obuf
);
414 thread_add_write(master
, vici_write
, vici
, vici
->fd
, &vici
->t_write
);
417 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...)
425 obuf
= zbuf_alloc(256);
429 hdrlen
= zbuf_push(obuf
, uint32_t);
430 zbuf_put8(obuf
, VICI_CMD_REQUEST
);
431 vici_zbuf_puts(obuf
, name
);
434 for (type
= va_arg(va
, int); type
!= VICI_END
; type
= va_arg(va
, int)) {
435 zbuf_put8(obuf
, type
);
438 vici_zbuf_puts(obuf
, va_arg(va
, const char *));
439 len
= va_arg(va
, size_t);
440 zbuf_put_be16(obuf
, len
);
441 zbuf_put(obuf
, va_arg(va
, void *), len
);
448 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
449 vici_submit(vici
, obuf
);
452 static void vici_register_event(struct vici_conn
*vici
, const char *name
)
458 namelen
= strlen(name
);
459 obuf
= zbuf_alloc(4 + 1 + 1 + namelen
);
463 hdrlen
= zbuf_push(obuf
, uint32_t);
464 zbuf_put8(obuf
, VICI_EVENT_REGISTER
);
465 zbuf_put8(obuf
, namelen
);
466 zbuf_put(obuf
, name
, namelen
);
467 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
469 vici_submit(vici
, obuf
);
472 static int vici_reconnect(struct thread
*t
)
474 struct vici_conn
*vici
= THREAD_ARG(t
);
477 vici
->t_reconnect
= NULL
;
481 fd
= sock_open_unix("/var/run/charon.vici");
483 debugf(NHRP_DEBUG_VICI
,
484 "%s: failure connecting VICI socket: %s",
485 __PRETTY_FUNCTION__
, strerror(errno
));
486 thread_add_timer(master
, vici_reconnect
, vici
, 2,
491 debugf(NHRP_DEBUG_COMMON
, "VICI: Connected");
493 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
495 /* Send event subscribtions */
496 // vici_register_event(vici, "child-updown");
497 // vici_register_event(vici, "child-rekey");
498 vici_register_event(vici
, "child-state-installed");
499 vici_register_event(vici
, "child-state-rekeyed");
500 vici_register_event(vici
, "child-state-destroying");
501 vici_register_event(vici
, "list-sa");
502 vici_submit_request(vici
, "list-sas", VICI_END
);
507 static struct vici_conn vici_connection
;
511 struct vici_conn
*vici
= &vici_connection
;
514 zbuf_init(&vici
->ibuf
, vici
->ibuf_data
, sizeof(vici
->ibuf_data
), 0);
515 zbufq_init(&vici
->obuf
);
516 thread_add_timer_msec(master
, vici_reconnect
, vici
, 10,
520 void vici_terminate(void)
524 void vici_request_vc(const char *profile
, union sockunion
*src
,
525 union sockunion
*dst
, int prio
)
527 struct vici_conn
*vici
= &vici_connection
;
528 char buf
[2][SU_ADDRSTRLEN
];
530 sockunion2str(src
, buf
[0], sizeof buf
[0]);
531 sockunion2str(dst
, buf
[1], sizeof buf
[1]);
533 vici_submit_request(vici
, "initiate", VICI_KEY_VALUE
, "child",
534 strlen(profile
), profile
, VICI_KEY_VALUE
, "timeout",
535 (size_t)2, "-1", VICI_KEY_VALUE
, "async", (size_t)1,
536 "1", VICI_KEY_VALUE
, "init-limits", (size_t)1,
537 prio
? "0" : "1", VICI_KEY_VALUE
, "my-host",
538 strlen(buf
[0]), buf
[0], VICI_KEY_VALUE
,
539 "other-host", strlen(buf
[1]), buf
[1], VICI_END
);
542 int sock_open_unix(const char *path
)
545 struct sockaddr_un addr
;
547 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
551 memset(&addr
, 0, sizeof(struct sockaddr_un
));
552 addr
.sun_family
= AF_UNIX
;
553 strncpy(addr
.sun_path
, path
, sizeof(addr
.sun_path
) - 1);
555 ret
= connect(fd
, (struct sockaddr
*)&addr
,
556 sizeof(addr
.sun_family
) + strlen(addr
.sun_path
));
562 ret
= fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
);