1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* strongSwan VICI protocol implementation for NHRP
3 * Copyright (c) 2014-2015 Timo Teräs
11 #include <sys/socket.h>
17 #include "lib_errors.h"
21 #include "nhrp_errors.h"
23 #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
30 static int blob_equal(const struct blob
*b
, const char *str
)
32 if (!b
|| b
->len
!= (int)strlen(str
))
34 return memcmp(b
->ptr
, str
, b
->len
) == 0;
37 static int blob2buf(const struct blob
*b
, char *buf
, size_t n
)
39 if (!b
|| b
->len
>= (int)n
)
41 memcpy(buf
, b
->ptr
, b
->len
);
47 struct event
*t_reconnect
, *t_read
, *t_write
;
49 struct zbuf_queue obuf
;
51 uint8_t ibuf_data
[VICI_MAX_MSGLEN
];
54 struct vici_message_ctx
{
55 const char *sections
[8];
59 static void vici_reconnect(struct event
*t
);
60 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...);
62 static void vici_zbuf_puts(struct zbuf
*obuf
, const char *str
)
64 size_t len
= strlen(str
);
66 zbuf_put(obuf
, str
, len
);
69 static void vici_connection_error(struct vici_conn
*vici
)
73 EVENT_OFF(vici
->t_read
);
74 EVENT_OFF(vici
->t_write
);
75 zbuf_reset(&vici
->ibuf
);
76 zbufq_reset(&vici
->obuf
);
80 event_add_timer(master
, vici_reconnect
, vici
, 2, &vici
->t_reconnect
);
83 static void vici_parse_message(struct vici_conn
*vici
, struct zbuf
*msg
,
84 void (*parser
)(struct vici_message_ctx
*ctx
,
85 enum vici_type_t msgtype
,
86 const struct blob
*key
,
87 const struct blob
*val
),
88 struct vici_message_ctx
*ctx
)
91 struct blob key
= {0};
92 struct blob val
= {0};
94 while ((type
= zbuf_may_pull(msg
, uint8_t)) != NULL
) {
96 case VICI_SECTION_START
:
97 key
.len
= zbuf_get8(msg
);
98 key
.ptr
= zbuf_pulln(msg
, key
.len
);
99 debugf(NHRP_DEBUG_VICI
, "VICI: Section start '%.*s'",
101 parser(ctx
, *type
, &key
, NULL
);
104 case VICI_SECTION_END
:
105 debugf(NHRP_DEBUG_VICI
, "VICI: Section end");
106 parser(ctx
, *type
, NULL
, NULL
);
110 key
.len
= zbuf_get8(msg
);
111 key
.ptr
= zbuf_pulln(msg
, key
.len
);
112 val
.len
= zbuf_get_be16(msg
);
113 val
.ptr
= zbuf_pulln(msg
, val
.len
);
114 debugf(NHRP_DEBUG_VICI
, "VICI: Key '%.*s'='%.*s'",
115 key
.len
, key
.ptr
, val
.len
, val
.ptr
);
116 parser(ctx
, *type
, &key
, &val
);
118 case VICI_LIST_START
:
119 key
.len
= zbuf_get8(msg
);
120 key
.ptr
= zbuf_pulln(msg
, key
.len
);
121 debugf(NHRP_DEBUG_VICI
, "VICI: List start '%.*s'",
125 val
.len
= zbuf_get_be16(msg
);
126 val
.ptr
= zbuf_pulln(msg
, val
.len
);
127 debugf(NHRP_DEBUG_VICI
, "VICI: List item: '%.*s'",
129 parser(ctx
, *type
, &key
, &val
);
132 debugf(NHRP_DEBUG_VICI
, "VICI: List end");
138 struct handle_sa_ctx
{
139 struct vici_message_ctx msgctx
;
143 uint32_t child_uniqueid
, ike_uniqueid
;
145 union sockunion host
;
146 struct blob id
, cert
;
150 static void parse_sa_message(struct vici_message_ctx
*ctx
,
151 enum vici_type_t msgtype
, const struct blob
*key
,
152 const struct blob
*val
)
154 struct handle_sa_ctx
*sactx
=
155 container_of(ctx
, struct handle_sa_ctx
, msgctx
);
160 case VICI_SECTION_START
:
161 if (ctx
->nsections
== 3) {
162 /* Begin of child-sa section, reset child vars */
163 sactx
->child_uniqueid
= 0;
167 case VICI_SECTION_END
:
168 if (ctx
->nsections
== 3) {
169 /* End of child-sa section, update nhrp_vc */
170 int up
= sactx
->child_ok
|| sactx
->event
== 1;
172 vc
= nhrp_vc_get(&sactx
->local
.host
,
173 &sactx
->remote
.host
, up
);
175 blob2buf(&sactx
->local
.id
, vc
->local
.id
,
176 sizeof(vc
->local
.id
));
177 if (blob2buf(&sactx
->local
.cert
,
178 (char *)vc
->local
.cert
,
179 sizeof(vc
->local
.cert
)))
181 sactx
->local
.cert
.len
;
182 blob2buf(&sactx
->remote
.id
,
184 sizeof(vc
->remote
.id
));
185 if (blob2buf(&sactx
->remote
.cert
,
186 (char *)vc
->remote
.cert
,
187 sizeof(vc
->remote
.cert
)))
189 sactx
->remote
.cert
.len
;
191 nhrp_vc_ipsec_updown(
192 sactx
->child_uniqueid
,
194 vc
->ike_uniqueid
= sactx
->ike_uniqueid
;
197 nhrp_vc_ipsec_updown(sactx
->child_uniqueid
, 0);
203 case VICI_LIST_START
:
207 if (!key
|| !key
->ptr
)
210 switch (key
->ptr
[0]) {
212 if (blob_equal(key
, "local-host")
213 && ctx
->nsections
== 1) {
214 if (blob2buf(val
, buf
, sizeof(buf
)))
215 if (str2sockunion(buf
,
220 "VICI: bad strongSwan local-host: %s",
222 } else if (blob_equal(key
, "local-id")
223 && ctx
->nsections
== 1) {
224 sactx
->local
.id
= *val
;
225 } else if (blob_equal(key
, "local-cert-data")
226 && ctx
->nsections
== 1) {
227 sactx
->local
.cert
= *val
;
231 if (blob_equal(key
, "remote-host")
232 && ctx
->nsections
== 1) {
233 if (blob2buf(val
, buf
, sizeof(buf
)))
234 if (str2sockunion(buf
,
239 "VICI: bad strongSwan remote-host: %s",
241 } else if (blob_equal(key
, "remote-id")
242 && ctx
->nsections
== 1) {
243 sactx
->remote
.id
= *val
;
244 } else if (blob_equal(key
, "remote-cert-data")
245 && ctx
->nsections
== 1) {
246 sactx
->remote
.cert
= *val
;
250 if (blob_equal(key
, "uniqueid")
251 && blob2buf(val
, buf
, sizeof(buf
))) {
252 if (ctx
->nsections
== 3)
253 sactx
->child_uniqueid
=
254 strtoul(buf
, NULL
, 0);
255 else if (ctx
->nsections
== 1)
256 sactx
->ike_uniqueid
=
257 strtoul(buf
, NULL
, 0);
261 if (blob_equal(key
, "state") && ctx
->nsections
== 3) {
264 && (blob_equal(val
, "INSTALLED")
265 || blob_equal(val
, "REKEYED")));
273 static void parse_cmd_response(struct vici_message_ctx
*ctx
,
274 enum vici_type_t msgtype
, const struct blob
*key
,
275 const struct blob
*val
)
281 if (blob_equal(key
, "errmsg")
282 && blob2buf(val
, buf
, sizeof(buf
)))
283 flog_err(EC_NHRP_SWAN
, "VICI: strongSwan: %s", buf
);
286 case VICI_SECTION_START
:
287 case VICI_SECTION_END
:
288 case VICI_LIST_START
:
296 static void vici_recv_sa(struct vici_conn
*vici
, struct zbuf
*msg
, int event
)
299 struct handle_sa_ctx ctx
= {
301 .msgctx
.nsections
= 0
304 vici_parse_message(vici
, msg
, parse_sa_message
, &ctx
.msgctx
);
306 if (ctx
.kill_ikesa
&& ctx
.ike_uniqueid
) {
307 debugf(NHRP_DEBUG_COMMON
, "VICI: Deleting IKE_SA %u",
309 snprintf(buf
, sizeof(buf
), "%u", ctx
.ike_uniqueid
);
310 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
311 strlen(buf
), buf
, VICI_END
);
315 static void vici_recv_message(struct vici_conn
*vici
, struct zbuf
*msg
)
320 struct vici_message_ctx ctx
= { .nsections
= 0 };
322 msglen
= zbuf_get_be32(msg
);
323 msgtype
= zbuf_get8(msg
);
324 debugf(NHRP_DEBUG_VICI
, "VICI: Message %d, %d bytes", msgtype
, msglen
);
328 name
.len
= zbuf_get8(msg
);
329 name
.ptr
= zbuf_pulln(msg
, name
.len
);
331 debugf(NHRP_DEBUG_VICI
, "VICI: Event '%.*s'", name
.len
,
333 if (blob_equal(&name
, "list-sa")
334 || blob_equal(&name
, "child-updown")
335 || blob_equal(&name
, "child-rekey"))
336 vici_recv_sa(vici
, msg
, 0);
337 else if (blob_equal(&name
, "child-state-installed")
338 || blob_equal(&name
, "child-state-rekeyed"))
339 vici_recv_sa(vici
, msg
, 1);
340 else if (blob_equal(&name
, "child-state-destroying"))
341 vici_recv_sa(vici
, msg
, 2);
343 case VICI_CMD_RESPONSE
:
344 vici_parse_message(vici
, msg
, parse_cmd_response
, &ctx
);
346 case VICI_EVENT_UNKNOWN
:
347 case VICI_CMD_UNKNOWN
:
350 "VICI: StrongSwan does not support mandatory events (unpatched?)");
352 case VICI_EVENT_CONFIRM
:
355 zlog_notice("VICI: Unrecognized message type %d", msgtype
);
360 static void vici_read(struct event
*t
)
362 struct vici_conn
*vici
= EVENT_ARG(t
);
363 struct zbuf
*ibuf
= &vici
->ibuf
;
366 if (zbuf_read(ibuf
, vici
->fd
, (size_t)-1) < 0) {
367 vici_connection_error(vici
);
371 /* Process all messages in buffer */
373 uint32_t *hdrlen
= zbuf_may_pull(ibuf
, uint32_t);
376 if (!zbuf_may_pulln(ibuf
, ntohl(*hdrlen
))) {
377 zbuf_reset_head(ibuf
, hdrlen
);
382 zbuf_init(&pktbuf
, hdrlen
, htonl(*hdrlen
) + 4,
384 vici_recv_message(vici
, &pktbuf
);
387 event_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
390 static void vici_write(struct event
*t
)
392 struct vici_conn
*vici
= EVENT_ARG(t
);
395 r
= zbufq_write(&vici
->obuf
, vici
->fd
);
397 event_add_write(master
, vici_write
, vici
, vici
->fd
,
400 vici_connection_error(vici
);
404 static void vici_submit(struct vici_conn
*vici
, struct zbuf
*obuf
)
411 zbufq_queue(&vici
->obuf
, obuf
);
412 event_add_write(master
, vici_write
, vici
, vici
->fd
, &vici
->t_write
);
415 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...)
423 obuf
= zbuf_alloc(256);
427 hdrlen
= zbuf_push(obuf
, uint32_t);
428 zbuf_put8(obuf
, VICI_CMD_REQUEST
);
429 vici_zbuf_puts(obuf
, name
);
432 for (type
= va_arg(va
, int); type
!= VICI_END
; type
= va_arg(va
, int)) {
433 zbuf_put8(obuf
, type
);
436 vici_zbuf_puts(obuf
, va_arg(va
, const char *));
437 len
= va_arg(va
, size_t);
438 zbuf_put_be16(obuf
, len
);
439 zbuf_put(obuf
, va_arg(va
, void *), len
);
446 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
447 vici_submit(vici
, obuf
);
450 static void vici_register_event(struct vici_conn
*vici
, const char *name
)
456 namelen
= strlen(name
);
457 obuf
= zbuf_alloc(4 + 1 + 1 + namelen
);
461 hdrlen
= zbuf_push(obuf
, uint32_t);
462 zbuf_put8(obuf
, VICI_EVENT_REGISTER
);
463 zbuf_put8(obuf
, namelen
);
464 zbuf_put(obuf
, name
, namelen
);
465 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
467 vici_submit(vici
, obuf
);
470 static bool vici_charon_filepath_done
;
471 static bool vici_charon_not_found
;
473 static char *vici_get_charon_filepath(void)
475 static char buff
[1200];
480 if (vici_charon_filepath_done
)
482 fp
= popen("ipsec --piddir", "r");
484 if (!vici_charon_not_found
) {
485 flog_err(EC_NHRP_SWAN
,
486 "VICI: Failed to retrieve charon file path");
487 vici_charon_not_found
= true;
491 /* last line of output is used to get vici path */
492 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
493 ptr
= strchr(line
, '\n');
496 snprintf(buff
, sizeof(buff
), "%s/charon.vici", line
);
499 vici_charon_filepath_done
= true;
503 static void vici_reconnect(struct event
*t
)
505 struct vici_conn
*vici
= EVENT_ARG(t
);
512 fd
= sock_open_unix(VICI_SOCKET
);
514 file_path
= vici_get_charon_filepath();
516 fd
= sock_open_unix(file_path
);
519 debugf(NHRP_DEBUG_VICI
,
520 "%s: failure connecting VICI socket: %s", __func__
,
522 event_add_timer(master
, vici_reconnect
, vici
, 2,
527 debugf(NHRP_DEBUG_COMMON
, "VICI: Connected");
529 event_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
531 /* Send event subscribtions */
532 // vici_register_event(vici, "child-updown");
533 // vici_register_event(vici, "child-rekey");
534 vici_register_event(vici
, "child-state-installed");
535 vici_register_event(vici
, "child-state-rekeyed");
536 vici_register_event(vici
, "child-state-destroying");
537 vici_register_event(vici
, "list-sa");
538 vici_submit_request(vici
, "list-sas", VICI_END
);
541 static struct vici_conn vici_connection
;
545 struct vici_conn
*vici
= &vici_connection
;
548 zbuf_init(&vici
->ibuf
, vici
->ibuf_data
, sizeof(vici
->ibuf_data
), 0);
549 zbufq_init(&vici
->obuf
);
550 event_add_timer_msec(master
, vici_reconnect
, vici
, 10,
554 void vici_terminate(void)
558 void vici_terminate_vc_by_profile_name(char *profile_name
)
560 struct vici_conn
*vici
= &vici_connection
;
562 debugf(NHRP_DEBUG_VICI
, "Terminate profile = %s", profile_name
);
563 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike",
564 strlen(profile_name
), profile_name
, VICI_END
);
567 void vici_terminate_vc_by_ike_id(unsigned int ike_id
)
569 struct vici_conn
*vici
= &vici_connection
;
572 snprintf(ike_id_str
, sizeof(ike_id_str
), "%d", ike_id
);
573 debugf(NHRP_DEBUG_VICI
, "Terminate ike_id_str = %s", ike_id_str
);
574 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
575 strlen(ike_id_str
), ike_id_str
, VICI_END
);
578 void vici_request_vc(const char *profile
, union sockunion
*src
,
579 union sockunion
*dst
, int prio
)
581 struct vici_conn
*vici
= &vici_connection
;
582 char buf
[2][SU_ADDRSTRLEN
];
584 sockunion2str(src
, buf
[0], sizeof(buf
[0]));
585 sockunion2str(dst
, buf
[1], sizeof(buf
[1]));
587 vici_submit_request(vici
, "initiate", VICI_KEY_VALUE
, "child",
588 strlen(profile
), profile
, VICI_KEY_VALUE
, "timeout",
589 (size_t)2, "-1", VICI_KEY_VALUE
, "async", (size_t)1,
590 "1", VICI_KEY_VALUE
, "init-limits", (size_t)1,
591 prio
? "0" : "1", VICI_KEY_VALUE
, "my-host",
592 strlen(buf
[0]), buf
[0], VICI_KEY_VALUE
,
593 "other-host", strlen(buf
[1]), buf
[1], VICI_END
);
596 int sock_open_unix(const char *path
)
599 struct sockaddr_un addr
;
601 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
605 memset(&addr
, 0, sizeof(addr
));
606 addr
.sun_family
= AF_UNIX
;
607 strlcpy(addr
.sun_path
, path
, sizeof(addr
.sun_path
));
609 ret
= connect(fd
, (struct sockaddr
*)&addr
,
610 sizeof(addr
.sun_family
) + strlen(addr
.sun_path
));
616 ret
= fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
);