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.
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 thread
*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 int vici_reconnect(struct thread
*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 THREAD_OFF(vici
->t_read
);
74 THREAD_OFF(vici
->t_write
);
75 zbuf_reset(&vici
->ibuf
);
76 zbufq_reset(&vici
->obuf
);
80 thread_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");
135 debugf(NHRP_DEBUG_VICI
,
136 "VICI: Unsupported message component type %d",
143 struct handle_sa_ctx
{
144 struct vici_message_ctx msgctx
;
148 uint32_t child_uniqueid
, ike_uniqueid
;
150 union sockunion host
;
151 struct blob id
, cert
;
155 static void parse_sa_message(struct vici_message_ctx
*ctx
,
156 enum vici_type_t msgtype
, const struct blob
*key
,
157 const struct blob
*val
)
159 struct handle_sa_ctx
*sactx
=
160 container_of(ctx
, struct handle_sa_ctx
, msgctx
);
165 case VICI_SECTION_START
:
166 if (ctx
->nsections
== 3) {
167 /* Begin of child-sa section, reset child vars */
168 sactx
->child_uniqueid
= 0;
172 case VICI_SECTION_END
:
173 if (ctx
->nsections
== 3) {
174 /* End of child-sa section, update nhrp_vc */
175 int up
= sactx
->child_ok
|| sactx
->event
== 1;
177 vc
= nhrp_vc_get(&sactx
->local
.host
,
178 &sactx
->remote
.host
, up
);
180 blob2buf(&sactx
->local
.id
, vc
->local
.id
,
181 sizeof(vc
->local
.id
));
182 if (blob2buf(&sactx
->local
.cert
,
183 (char *)vc
->local
.cert
,
184 sizeof(vc
->local
.cert
)))
186 sactx
->local
.cert
.len
;
187 blob2buf(&sactx
->remote
.id
,
189 sizeof(vc
->remote
.id
));
190 if (blob2buf(&sactx
->remote
.cert
,
191 (char *)vc
->remote
.cert
,
192 sizeof(vc
->remote
.cert
)))
194 sactx
->remote
.cert
.len
;
196 nhrp_vc_ipsec_updown(
197 sactx
->child_uniqueid
,
201 nhrp_vc_ipsec_updown(sactx
->child_uniqueid
, 0);
209 switch (key
->ptr
[0]) {
211 if (blob_equal(key
, "local-host")
212 && ctx
->nsections
== 1) {
213 if (blob2buf(val
, buf
, sizeof(buf
)))
214 if (str2sockunion(buf
,
217 flog_err(NHRP_ERR_SWAN
,
218 "VICI: bad strongSwan local-host: %s",
220 } else if (blob_equal(key
, "local-id")
221 && ctx
->nsections
== 1) {
222 sactx
->local
.id
= *val
;
223 } else if (blob_equal(key
, "local-cert-data")
224 && ctx
->nsections
== 1) {
225 sactx
->local
.cert
= *val
;
229 if (blob_equal(key
, "remote-host")
230 && ctx
->nsections
== 1) {
231 if (blob2buf(val
, buf
, sizeof(buf
)))
232 if (str2sockunion(buf
,
235 flog_err(NHRP_ERR_SWAN
,
236 "VICI: bad strongSwan remote-host: %s",
238 } else if (blob_equal(key
, "remote-id")
239 && ctx
->nsections
== 1) {
240 sactx
->remote
.id
= *val
;
241 } else if (blob_equal(key
, "remote-cert-data")
242 && ctx
->nsections
== 1) {
243 sactx
->remote
.cert
= *val
;
247 if (blob_equal(key
, "uniqueid")
248 && blob2buf(val
, buf
, sizeof(buf
))) {
249 if (ctx
->nsections
== 3)
250 sactx
->child_uniqueid
=
251 strtoul(buf
, NULL
, 0);
252 else if (ctx
->nsections
== 1)
253 sactx
->ike_uniqueid
=
254 strtoul(buf
, NULL
, 0);
258 if (blob_equal(key
, "state") && ctx
->nsections
== 3) {
261 && (blob_equal(val
, "INSTALLED")
262 || blob_equal(val
, "REKEYED")));
270 static void parse_cmd_response(struct vici_message_ctx
*ctx
,
271 enum vici_type_t msgtype
, const struct blob
*key
,
272 const struct blob
*val
)
278 if (blob_equal(key
, "errmsg")
279 && blob2buf(val
, buf
, sizeof(buf
)))
280 flog_err(NHRP_ERR_SWAN
, "VICI: strongSwan: %s", buf
);
287 static void vici_recv_sa(struct vici_conn
*vici
, struct zbuf
*msg
, int event
)
290 struct handle_sa_ctx ctx
= {
292 .msgctx
.nsections
= 0
295 vici_parse_message(vici
, msg
, parse_sa_message
, &ctx
.msgctx
);
297 if (ctx
.kill_ikesa
&& ctx
.ike_uniqueid
) {
298 debugf(NHRP_DEBUG_COMMON
, "VICI: Deleting IKE_SA %u",
300 snprintf(buf
, sizeof buf
, "%u", ctx
.ike_uniqueid
);
301 vici_submit_request(vici
, "terminate", VICI_KEY_VALUE
, "ike-id",
302 strlen(buf
), buf
, VICI_END
);
306 static void vici_recv_message(struct vici_conn
*vici
, struct zbuf
*msg
)
311 struct vici_message_ctx ctx
= { .nsections
= 0 };
313 msglen
= zbuf_get_be32(msg
);
314 msgtype
= zbuf_get8(msg
);
315 debugf(NHRP_DEBUG_VICI
, "VICI: Message %d, %d bytes", msgtype
, msglen
);
319 name
.len
= zbuf_get8(msg
);
320 name
.ptr
= zbuf_pulln(msg
, name
.len
);
322 debugf(NHRP_DEBUG_VICI
, "VICI: Event '%.*s'", name
.len
,
324 if (blob_equal(&name
, "list-sa")
325 || blob_equal(&name
, "child-updown")
326 || blob_equal(&name
, "child-rekey"))
327 vici_recv_sa(vici
, msg
, 0);
328 else if (blob_equal(&name
, "child-state-installed")
329 || blob_equal(&name
, "child-state-rekeyed"))
330 vici_recv_sa(vici
, msg
, 1);
331 else if (blob_equal(&name
, "child-state-destroying"))
332 vici_recv_sa(vici
, msg
, 2);
334 case VICI_CMD_RESPONSE
:
335 vici_parse_message(vici
, msg
, parse_cmd_response
, &ctx
);
337 case VICI_EVENT_UNKNOWN
:
338 case VICI_CMD_UNKNOWN
:
339 flog_err(NHRP_ERR_SWAN
,
340 "VICI: StrongSwan does not support mandatory events (unpatched?)");
342 case VICI_EVENT_CONFIRM
:
345 zlog_notice("VICI: Unrecognized message type %d", msgtype
);
350 static int vici_read(struct thread
*t
)
352 struct vici_conn
*vici
= THREAD_ARG(t
);
353 struct zbuf
*ibuf
= &vici
->ibuf
;
357 if (zbuf_read(ibuf
, vici
->fd
, (size_t)-1) < 0) {
358 vici_connection_error(vici
);
362 /* Process all messages in buffer */
364 uint32_t *hdrlen
= zbuf_may_pull(ibuf
, uint32_t);
367 if (!zbuf_may_pulln(ibuf
, ntohl(*hdrlen
))) {
368 zbuf_reset_head(ibuf
, hdrlen
);
373 zbuf_init(&pktbuf
, hdrlen
, htonl(*hdrlen
) + 4,
375 vici_recv_message(vici
, &pktbuf
);
378 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
382 static int vici_write(struct thread
*t
)
384 struct vici_conn
*vici
= THREAD_ARG(t
);
387 vici
->t_write
= NULL
;
388 r
= zbufq_write(&vici
->obuf
, vici
->fd
);
390 thread_add_write(master
, vici_write
, vici
, vici
->fd
,
393 vici_connection_error(vici
);
399 static void vici_submit(struct vici_conn
*vici
, struct zbuf
*obuf
)
406 zbufq_queue(&vici
->obuf
, obuf
);
407 thread_add_write(master
, vici_write
, vici
, vici
->fd
, &vici
->t_write
);
410 static void vici_submit_request(struct vici_conn
*vici
, const char *name
, ...)
418 obuf
= zbuf_alloc(256);
422 hdrlen
= zbuf_push(obuf
, uint32_t);
423 zbuf_put8(obuf
, VICI_CMD_REQUEST
);
424 vici_zbuf_puts(obuf
, name
);
427 for (type
= va_arg(va
, int); type
!= VICI_END
; type
= va_arg(va
, int)) {
428 zbuf_put8(obuf
, type
);
431 vici_zbuf_puts(obuf
, va_arg(va
, const char *));
432 len
= va_arg(va
, size_t);
433 zbuf_put_be16(obuf
, len
);
434 zbuf_put(obuf
, va_arg(va
, void *), len
);
441 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
442 vici_submit(vici
, obuf
);
445 static void vici_register_event(struct vici_conn
*vici
, const char *name
)
451 namelen
= strlen(name
);
452 obuf
= zbuf_alloc(4 + 1 + 1 + namelen
);
456 hdrlen
= zbuf_push(obuf
, uint32_t);
457 zbuf_put8(obuf
, VICI_EVENT_REGISTER
);
458 zbuf_put8(obuf
, namelen
);
459 zbuf_put(obuf
, name
, namelen
);
460 *hdrlen
= htonl(zbuf_used(obuf
) - 4);
462 vici_submit(vici
, obuf
);
465 static int vici_reconnect(struct thread
*t
)
467 struct vici_conn
*vici
= THREAD_ARG(t
);
470 vici
->t_reconnect
= NULL
;
474 fd
= sock_open_unix("/var/run/charon.vici");
476 debugf(NHRP_DEBUG_VICI
,
477 "%s: failure connecting VICI socket: %s",
478 __PRETTY_FUNCTION__
, strerror(errno
));
479 thread_add_timer(master
, vici_reconnect
, vici
, 2,
484 debugf(NHRP_DEBUG_COMMON
, "VICI: Connected");
486 thread_add_read(master
, vici_read
, vici
, vici
->fd
, &vici
->t_read
);
488 /* Send event subscribtions */
489 // vici_register_event(vici, "child-updown");
490 // vici_register_event(vici, "child-rekey");
491 vici_register_event(vici
, "child-state-installed");
492 vici_register_event(vici
, "child-state-rekeyed");
493 vici_register_event(vici
, "child-state-destroying");
494 vici_register_event(vici
, "list-sa");
495 vici_submit_request(vici
, "list-sas", VICI_END
);
500 static struct vici_conn vici_connection
;
504 struct vici_conn
*vici
= &vici_connection
;
507 zbuf_init(&vici
->ibuf
, vici
->ibuf_data
, sizeof(vici
->ibuf_data
), 0);
508 zbufq_init(&vici
->obuf
);
509 thread_add_timer_msec(master
, vici_reconnect
, vici
, 10,
513 void vici_terminate(void)
517 void vici_request_vc(const char *profile
, union sockunion
*src
,
518 union sockunion
*dst
, int prio
)
520 struct vici_conn
*vici
= &vici_connection
;
521 char buf
[2][SU_ADDRSTRLEN
];
523 sockunion2str(src
, buf
[0], sizeof buf
[0]);
524 sockunion2str(dst
, buf
[1], sizeof buf
[1]);
526 vici_submit_request(vici
, "initiate", VICI_KEY_VALUE
, "child",
527 strlen(profile
), profile
, VICI_KEY_VALUE
, "timeout",
528 (size_t)2, "-1", VICI_KEY_VALUE
, "async", (size_t)1,
529 "1", VICI_KEY_VALUE
, "init-limits", (size_t)1,
530 prio
? "0" : "1", VICI_KEY_VALUE
, "my-host",
531 strlen(buf
[0]), buf
[0], VICI_KEY_VALUE
,
532 "other-host", strlen(buf
[1]), buf
[1], VICI_END
);
535 int sock_open_unix(const char *path
)
538 struct sockaddr_un addr
;
540 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
544 memset(&addr
, 0, sizeof(struct sockaddr_un
));
545 addr
.sun_family
= AF_UNIX
;
546 strncpy(addr
.sun_path
, path
, sizeof(addr
.sun_path
) - 1);
548 ret
= connect(fd
, (struct sockaddr
*)&addr
,
549 sizeof(addr
.sun_family
) + strlen(addr
.sun_path
));
555 ret
= fcntl(fd
, F_SETFL
, fcntl(fd
, F_GETFL
, 0) | O_NONBLOCK
);