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
,
203 vc
->ike_uniqueid
= sactx
->ike_uniqueid
;
206 nhrp_vc_ipsec_updown(sactx
->child_uniqueid
, 0);
211 if (!key
|| !key
->ptr
)
214 switch (key
->ptr
[0]) {
216 if (blob_equal(key
, "local-host")
217 && ctx
->nsections
== 1) {
218 if (blob2buf(val
, buf
, sizeof(buf
)))
219 if (str2sockunion(buf
,
224 "VICI: bad strongSwan local-host: %s",
226 } else if (blob_equal(key
, "local-id")
227 && ctx
->nsections
== 1) {
228 sactx
->local
.id
= *val
;
229 } else if (blob_equal(key
, "local-cert-data")
230 && ctx
->nsections
== 1) {
231 sactx
->local
.cert
= *val
;
235 if (blob_equal(key
, "remote-host")
236 && ctx
->nsections
== 1) {
237 if (blob2buf(val
, buf
, sizeof(buf
)))
238 if (str2sockunion(buf
,
243 "VICI: bad strongSwan remote-host: %s",
245 } else if (blob_equal(key
, "remote-id")
246 && ctx
->nsections
== 1) {
247 sactx
->remote
.id
= *val
;
248 } else if (blob_equal(key
, "remote-cert-data")
249 && ctx
->nsections
== 1) {
250 sactx
->remote
.cert
= *val
;
254 if (blob_equal(key
, "uniqueid")
255 && blob2buf(val
, buf
, sizeof(buf
))) {
256 if (ctx
->nsections
== 3)
257 sactx
->child_uniqueid
=
258 strtoul(buf
, NULL
, 0);
259 else if (ctx
->nsections
== 1)
260 sactx
->ike_uniqueid
=
261 strtoul(buf
, NULL
, 0);
265 if (blob_equal(key
, "state") && ctx
->nsections
== 3) {
268 && (blob_equal(val
, "INSTALLED")
269 || blob_equal(val
, "REKEYED")));
277 static void parse_cmd_response(struct vici_message_ctx
*ctx
,
278 enum vici_type_t msgtype
, const struct blob
*key
,
279 const struct blob
*val
)
285 if (blob_equal(key
, "errmsg")
286 && blob2buf(val
, buf
, sizeof(buf
)))
287 flog_err(EC_NHRP_SWAN
, "VICI: strongSwan: %s", buf
);
294 static void vici_recv_sa(struct vici_conn
*vici
, struct zbuf
*msg
, int event
)
297 struct handle_sa_ctx ctx
= {
299 .msgctx
.nsections
= 0
302 vici_parse_message(vici
, msg
, parse_sa_message
, &ctx
.msgctx
);
304 if (ctx
.kill_ikesa
&& ctx
.ike_uniqueid
) {
305 debugf(NHRP_DEBUG_COMMON
, "VICI: Deleting IKE_SA %u",
307 snprintf(buf
, sizeof(buf
), "%u", ctx
.ike_uniqueid
);
308 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
309 strlen(buf
), buf
, VICI_END
);
313 static void vici_recv_message(struct vici_conn
*vici
, struct zbuf
*msg
)
318 struct vici_message_ctx ctx
= { .nsections
= 0 };
320 msglen
= zbuf_get_be32(msg
);
321 msgtype
= zbuf_get8(msg
);
322 debugf(NHRP_DEBUG_VICI
, "VICI: Message %d, %d bytes", msgtype
, msglen
);
326 name
.len
= zbuf_get8(msg
);
327 name
.ptr
= zbuf_pulln(msg
, name
.len
);
329 debugf(NHRP_DEBUG_VICI
, "VICI: Event '%.*s'", name
.len
,
331 if (blob_equal(&name
, "list-sa")
332 || blob_equal(&name
, "child-updown")
333 || blob_equal(&name
, "child-rekey"))
334 vici_recv_sa(vici
, msg
, 0);
335 else if (blob_equal(&name
, "child-state-installed")
336 || blob_equal(&name
, "child-state-rekeyed"))
337 vici_recv_sa(vici
, msg
, 1);
338 else if (blob_equal(&name
, "child-state-destroying"))
339 vici_recv_sa(vici
, msg
, 2);
341 case VICI_CMD_RESPONSE
:
342 vici_parse_message(vici
, msg
, parse_cmd_response
, &ctx
);
344 case VICI_EVENT_UNKNOWN
:
345 case VICI_CMD_UNKNOWN
:
348 "VICI: StrongSwan does not support mandatory events (unpatched?)");
350 case VICI_EVENT_CONFIRM
:
353 zlog_notice("VICI: Unrecognized message type %d", msgtype
);
358 static int vici_read(struct thread
*t
)
360 struct vici_conn
*vici
= THREAD_ARG(t
);
361 struct zbuf
*ibuf
= &vici
->ibuf
;
365 if (zbuf_read(ibuf
, vici
->fd
, (size_t)-1) < 0) {
366 vici_connection_error(vici
);
370 /* Process all messages in buffer */
372 uint32_t *hdrlen
= zbuf_may_pull(ibuf
, uint32_t);
375 if (!zbuf_may_pulln(ibuf
, ntohl(*hdrlen
))) {
376 zbuf_reset_head(ibuf
, hdrlen
);
381 zbuf_init(&pktbuf
, hdrlen
, htonl(*hdrlen
) + 4,
383 vici_recv_message(vici
, &pktbuf
);
386 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
390 static int vici_write(struct thread
*t
)
392 struct vici_conn
*vici
= THREAD_ARG(t
);
395 vici
->t_write
= NULL
;
396 r
= zbufq_write(&vici
->obuf
, vici
->fd
);
398 thread_add_write(master
, vici_write
, vici
, vici
->fd
,
401 vici_connection_error(vici
);
407 static void vici_submit(struct vici_conn
*vici
, struct zbuf
*obuf
)
414 zbufq_queue(&vici
->obuf
, obuf
);
415 thread_add_write(master
, vici_write
, vici
, vici
->fd
, &vici
->t_write
);
418 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...)
426 obuf
= zbuf_alloc(256);
430 hdrlen
= zbuf_push(obuf
, uint32_t);
431 zbuf_put8(obuf
, VICI_CMD_REQUEST
);
432 vici_zbuf_puts(obuf
, name
);
435 for (type
= va_arg(va
, int); type
!= VICI_END
; type
= va_arg(va
, int)) {
436 zbuf_put8(obuf
, type
);
439 vici_zbuf_puts(obuf
, va_arg(va
, const char *));
440 len
= va_arg(va
, size_t);
441 zbuf_put_be16(obuf
, len
);
442 zbuf_put(obuf
, va_arg(va
, void *), len
);
449 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
450 vici_submit(vici
, obuf
);
453 static void vici_register_event(struct vici_conn
*vici
, const char *name
)
459 namelen
= strlen(name
);
460 obuf
= zbuf_alloc(4 + 1 + 1 + namelen
);
464 hdrlen
= zbuf_push(obuf
, uint32_t);
465 zbuf_put8(obuf
, VICI_EVENT_REGISTER
);
466 zbuf_put8(obuf
, namelen
);
467 zbuf_put(obuf
, name
, namelen
);
468 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
470 vici_submit(vici
, obuf
);
473 static bool vici_charon_filepath_done
;
474 static bool vici_charon_not_found
;
476 static char *vici_get_charon_filepath(void)
478 static char buff
[1200];
483 if (vici_charon_filepath_done
)
485 fp
= popen("ipsec --piddir", "r");
487 if (!vici_charon_not_found
) {
488 flog_err(EC_NHRP_SWAN
,
489 "VICI: Failed to retrieve charon file path");
490 vici_charon_not_found
= true;
494 /* last line of output is used to get vici path */
495 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
496 ptr
= strchr(line
, '\n');
499 snprintf(buff
, sizeof(buff
), "%s/charon.vici", line
);
502 vici_charon_filepath_done
= true;
506 static int vici_reconnect(struct thread
*t
)
508 struct vici_conn
*vici
= THREAD_ARG(t
);
512 vici
->t_reconnect
= NULL
;
516 fd
= sock_open_unix(VICI_SOCKET
);
518 file_path
= vici_get_charon_filepath();
520 fd
= sock_open_unix(file_path
);
523 debugf(NHRP_DEBUG_VICI
,
524 "%s: failure connecting VICI socket: %s", __func__
,
526 thread_add_timer(master
, vici_reconnect
, vici
, 2,
531 debugf(NHRP_DEBUG_COMMON
, "VICI: Connected");
533 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
535 /* Send event subscribtions */
536 // vici_register_event(vici, "child-updown");
537 // vici_register_event(vici, "child-rekey");
538 vici_register_event(vici
, "child-state-installed");
539 vici_register_event(vici
, "child-state-rekeyed");
540 vici_register_event(vici
, "child-state-destroying");
541 vici_register_event(vici
, "list-sa");
542 vici_submit_request(vici
, "list-sas", VICI_END
);
547 static struct vici_conn vici_connection
;
551 struct vici_conn
*vici
= &vici_connection
;
554 zbuf_init(&vici
->ibuf
, vici
->ibuf_data
, sizeof(vici
->ibuf_data
), 0);
555 zbufq_init(&vici
->obuf
);
556 thread_add_timer_msec(master
, vici_reconnect
, vici
, 10,
560 void vici_terminate(void)
564 void vici_terminate_vc_by_profile_name(char *profile_name
)
566 struct vici_conn
*vici
= &vici_connection
;
568 debugf(NHRP_DEBUG_VICI
, "Terminate profile = %s", profile_name
);
569 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike",
570 strlen(profile_name
), profile_name
, VICI_END
);
573 void vici_terminate_vc_by_ike_id(unsigned int ike_id
)
575 struct vici_conn
*vici
= &vici_connection
;
578 snprintf(ike_id_str
, sizeof(ike_id_str
), "%d", ike_id
);
579 debugf(NHRP_DEBUG_VICI
, "Terminate ike_id_str = %s", ike_id_str
);
580 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
581 strlen(ike_id_str
), ike_id_str
, VICI_END
);
584 void vici_request_vc(const char *profile
, union sockunion
*src
,
585 union sockunion
*dst
, int prio
)
587 struct vici_conn
*vici
= &vici_connection
;
588 char buf
[2][SU_ADDRSTRLEN
];
590 sockunion2str(src
, buf
[0], sizeof(buf
[0]));
591 sockunion2str(dst
, buf
[1], sizeof(buf
[1]));
593 vici_submit_request(vici
, "initiate", VICI_KEY_VALUE
, "child",
594 strlen(profile
), profile
, VICI_KEY_VALUE
, "timeout",
595 (size_t)2, "-1", VICI_KEY_VALUE
, "async", (size_t)1,
596 "1", VICI_KEY_VALUE
, "init-limits", (size_t)1,
597 prio
? "0" : "1", VICI_KEY_VALUE
, "my-host",
598 strlen(buf
[0]), buf
[0], VICI_KEY_VALUE
,
599 "other-host", strlen(buf
[1]), buf
[1], VICI_END
);
602 int sock_open_unix(const char *path
)
605 struct sockaddr_un addr
;
607 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
611 memset(&addr
, 0, sizeof(struct sockaddr_un
));
612 addr
.sun_family
= AF_UNIX
;
613 strlcpy(addr
.sun_path
, path
, sizeof(addr
.sun_path
));
615 ret
= connect(fd
, (struct sockaddr
*)&addr
,
616 sizeof(addr
.sun_family
) + strlen(addr
.sun_path
));
622 ret
= fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
);