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 void 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 void vici_read(struct thread
*t
)
360 struct vici_conn
*vici
= THREAD_ARG(t
);
361 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
);
388 static void vici_write(struct thread
*t
)
390 struct vici_conn
*vici
= THREAD_ARG(t
);
393 r
= zbufq_write(&vici
->obuf
, vici
->fd
);
395 thread_add_write(master
, vici_write
, vici
, vici
->fd
,
398 vici_connection_error(vici
);
402 static void vici_submit(struct vici_conn
*vici
, struct zbuf
*obuf
)
409 zbufq_queue(&vici
->obuf
, obuf
);
410 thread_add_write(master
, vici_write
, vici
, vici
->fd
, &vici
->t_write
);
413 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...)
421 obuf
= zbuf_alloc(256);
425 hdrlen
= zbuf_push(obuf
, uint32_t);
426 zbuf_put8(obuf
, VICI_CMD_REQUEST
);
427 vici_zbuf_puts(obuf
, name
);
430 for (type
= va_arg(va
, int); type
!= VICI_END
; type
= va_arg(va
, int)) {
431 zbuf_put8(obuf
, type
);
434 vici_zbuf_puts(obuf
, va_arg(va
, const char *));
435 len
= va_arg(va
, size_t);
436 zbuf_put_be16(obuf
, len
);
437 zbuf_put(obuf
, va_arg(va
, void *), len
);
444 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
445 vici_submit(vici
, obuf
);
448 static void vici_register_event(struct vici_conn
*vici
, const char *name
)
454 namelen
= strlen(name
);
455 obuf
= zbuf_alloc(4 + 1 + 1 + namelen
);
459 hdrlen
= zbuf_push(obuf
, uint32_t);
460 zbuf_put8(obuf
, VICI_EVENT_REGISTER
);
461 zbuf_put8(obuf
, namelen
);
462 zbuf_put(obuf
, name
, namelen
);
463 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
465 vici_submit(vici
, obuf
);
468 static bool vici_charon_filepath_done
;
469 static bool vici_charon_not_found
;
471 static char *vici_get_charon_filepath(void)
473 static char buff
[1200];
478 if (vici_charon_filepath_done
)
480 fp
= popen("ipsec --piddir", "r");
482 if (!vici_charon_not_found
) {
483 flog_err(EC_NHRP_SWAN
,
484 "VICI: Failed to retrieve charon file path");
485 vici_charon_not_found
= true;
489 /* last line of output is used to get vici path */
490 while (fgets(line
, sizeof(line
), fp
) != NULL
) {
491 ptr
= strchr(line
, '\n');
494 snprintf(buff
, sizeof(buff
), "%s/charon.vici", line
);
497 vici_charon_filepath_done
= true;
501 static void vici_reconnect(struct thread
*t
)
503 struct vici_conn
*vici
= THREAD_ARG(t
);
510 fd
= sock_open_unix(VICI_SOCKET
);
512 file_path
= vici_get_charon_filepath();
514 fd
= sock_open_unix(file_path
);
517 debugf(NHRP_DEBUG_VICI
,
518 "%s: failure connecting VICI socket: %s", __func__
,
520 thread_add_timer(master
, vici_reconnect
, vici
, 2,
525 debugf(NHRP_DEBUG_COMMON
, "VICI: Connected");
527 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
529 /* Send event subscribtions */
530 // vici_register_event(vici, "child-updown");
531 // vici_register_event(vici, "child-rekey");
532 vici_register_event(vici
, "child-state-installed");
533 vici_register_event(vici
, "child-state-rekeyed");
534 vici_register_event(vici
, "child-state-destroying");
535 vici_register_event(vici
, "list-sa");
536 vici_submit_request(vici
, "list-sas", VICI_END
);
539 static struct vici_conn vici_connection
;
543 struct vici_conn
*vici
= &vici_connection
;
546 zbuf_init(&vici
->ibuf
, vici
->ibuf_data
, sizeof(vici
->ibuf_data
), 0);
547 zbufq_init(&vici
->obuf
);
548 thread_add_timer_msec(master
, vici_reconnect
, vici
, 10,
552 void vici_terminate(void)
556 void vici_terminate_vc_by_profile_name(char *profile_name
)
558 struct vici_conn
*vici
= &vici_connection
;
560 debugf(NHRP_DEBUG_VICI
, "Terminate profile = %s", profile_name
);
561 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike",
562 strlen(profile_name
), profile_name
, VICI_END
);
565 void vici_terminate_vc_by_ike_id(unsigned int ike_id
)
567 struct vici_conn
*vici
= &vici_connection
;
570 snprintf(ike_id_str
, sizeof(ike_id_str
), "%d", ike_id
);
571 debugf(NHRP_DEBUG_VICI
, "Terminate ike_id_str = %s", ike_id_str
);
572 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
573 strlen(ike_id_str
), ike_id_str
, VICI_END
);
576 void vici_request_vc(const char *profile
, union sockunion
*src
,
577 union sockunion
*dst
, int prio
)
579 struct vici_conn
*vici
= &vici_connection
;
580 char buf
[2][SU_ADDRSTRLEN
];
582 sockunion2str(src
, buf
[0], sizeof(buf
[0]));
583 sockunion2str(dst
, buf
[1], sizeof(buf
[1]));
585 vici_submit_request(vici
, "initiate", VICI_KEY_VALUE
, "child",
586 strlen(profile
), profile
, VICI_KEY_VALUE
, "timeout",
587 (size_t)2, "-1", VICI_KEY_VALUE
, "async", (size_t)1,
588 "1", VICI_KEY_VALUE
, "init-limits", (size_t)1,
589 prio
? "0" : "1", VICI_KEY_VALUE
, "my-host",
590 strlen(buf
[0]), buf
[0], VICI_KEY_VALUE
,
591 "other-host", strlen(buf
[1]), buf
[1], VICI_END
);
594 int sock_open_unix(const char *path
)
597 struct sockaddr_un addr
;
599 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
603 memset(&addr
, 0, sizeof(addr
));
604 addr
.sun_family
= AF_UNIX
;
605 strlcpy(addr
.sun_path
, path
, sizeof(addr
.sun_path
));
607 ret
= connect(fd
, (struct sockaddr
*)&addr
,
608 sizeof(addr
.sun_family
) + strlen(addr
.sun_path
));
614 ret
= fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
);