]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - nhrpd/vici.c
New upstream version 1.2.4
[quagga-debian.git] / nhrpd / vici.c
1 /* strongSwan VICI protocol implementation for NHRP
2  * Copyright (c) 2014-2015 Timo Teräs
3  *
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.
8  */
9
10 #include <string.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13
14 #include "thread.h"
15 #include "zbuf.h"
16 #include "log.h"
17 #include "nhrpd.h"
18
19 #include "vici.h"
20
21 #define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR))
22
23 struct blob {
24         char *ptr;
25         int len;
26 };
27
28 static int blob_equal(const struct blob *b, const char *str)
29 {
30         if (b->len != (int) strlen(str)) return 0;
31         return memcmp(b->ptr, str, b->len) == 0;
32 }
33
34 static int blob2buf(const struct blob *b, char *buf, size_t n)
35 {
36         if (b->len >= (int) n) return 0;
37         memcpy(buf, b->ptr, b->len);
38         buf[b->len] = 0;
39         return 1;
40 }
41
42 struct vici_conn {
43         struct thread *t_reconnect, *t_read, *t_write;
44         struct zbuf ibuf;
45         struct zbuf_queue obuf;
46         int fd;
47         uint8_t ibuf_data[VICI_MAX_MSGLEN];
48 };
49
50 struct vici_message_ctx {
51         const char *sections[8];
52         int nsections;
53 };
54
55 static int vici_reconnect(struct thread *t);
56 static void vici_submit_request(struct vici_conn *vici, const char *name, ...);
57
58 static void vici_zbuf_puts(struct zbuf *obuf, const char *str)
59 {
60         size_t len = strlen(str);
61         zbuf_put8(obuf, len);
62         zbuf_put(obuf, str, len);
63 }
64
65 static void vici_connection_error(struct vici_conn *vici)
66 {
67         nhrp_vc_reset();
68
69         THREAD_OFF(vici->t_read);
70         THREAD_OFF(vici->t_write);
71         zbuf_reset(&vici->ibuf);
72         zbufq_reset(&vici->obuf);
73
74         close(vici->fd);
75         vici->fd = -1;
76         THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
77 }
78
79 static void vici_parse_message(
80         struct vici_conn *vici, struct zbuf *msg,
81         void (*parser)(struct vici_message_ctx *ctx, enum vici_type_t msgtype, const struct blob *key, const struct blob *val),
82         struct vici_message_ctx *ctx)
83 {
84         uint8_t *type;
85         struct blob key;
86         struct blob val;
87
88         while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) {
89                 switch (*type) {
90                 case VICI_SECTION_START:
91                         key.len = zbuf_get8(msg);
92                         key.ptr = zbuf_pulln(msg, key.len);
93                         debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'", key.len, key.ptr);
94                         parser(ctx, *type, &key, NULL);
95                         ctx->nsections++;
96                         break;
97                 case VICI_SECTION_END:
98                         debugf(NHRP_DEBUG_VICI, "VICI: Section end");
99                         parser(ctx, *type, NULL, NULL);
100                         ctx->nsections--;
101                         break;
102                 case VICI_KEY_VALUE:
103                         key.len = zbuf_get8(msg);
104                         key.ptr = zbuf_pulln(msg, key.len);
105                         val.len = zbuf_get_be16(msg);
106                         val.ptr = zbuf_pulln(msg, val.len);
107                         debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'", key.len, key.ptr, val.len, val.ptr);
108                         parser(ctx, *type, &key, &val);
109                         break;
110                 case VICI_LIST_START:
111                         key.len = zbuf_get8(msg);
112                         key.ptr = zbuf_pulln(msg, key.len);
113                         debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'", key.len, key.ptr);
114                         break;
115                 case VICI_LIST_ITEM:
116                         val.len = zbuf_get_be16(msg);
117                         val.ptr = zbuf_pulln(msg, val.len);
118                         debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'", val.len, val.ptr);
119                         parser(ctx, *type, &key, &val);
120                         break;
121                 case VICI_LIST_END:
122                         debugf(NHRP_DEBUG_VICI, "VICI: List end");
123                         break;
124                 default:
125                         debugf(NHRP_DEBUG_VICI, "VICI: Unsupported message component type %d", *type);
126                         return;
127                 }
128         }
129 }
130
131 struct handle_sa_ctx {
132         struct vici_message_ctx msgctx;
133         int event;
134         int child_ok;
135         int kill_ikesa;
136         uint32_t child_uniqueid, ike_uniqueid;
137         struct {
138                 union sockunion host;
139                 struct blob id, cert;
140         } local, remote;
141 };
142
143 static void parse_sa_message(
144         struct vici_message_ctx *ctx,
145         enum vici_type_t msgtype,
146         const struct blob *key, const struct blob *val)
147 {
148         struct handle_sa_ctx *sactx = container_of(ctx, struct handle_sa_ctx, msgctx);
149         struct nhrp_vc *vc;
150         char buf[512];
151
152         switch (msgtype) {
153         case VICI_SECTION_START:
154                 if (ctx->nsections == 3) {
155                         /* Begin of child-sa section, reset child vars */
156                         sactx->child_uniqueid = 0;
157                         sactx->child_ok = 0;
158                 }
159                 break;
160         case VICI_SECTION_END:
161                 if (ctx->nsections == 3) {
162                         /* End of child-sa section, update nhrp_vc */
163                         int up = sactx->child_ok || sactx->event == 1;
164                         if (up) {
165                                 vc = nhrp_vc_get(&sactx->local.host, &sactx->remote.host, up);
166                                 if (vc) {
167                                         blob2buf(&sactx->local.id, vc->local.id, sizeof(vc->local.id));
168                                         if (blob2buf(&sactx->local.cert, (char*)vc->local.cert, sizeof(vc->local.cert)))
169                                                 vc->local.certlen = sactx->local.cert.len;
170                                         blob2buf(&sactx->remote.id, vc->remote.id, sizeof(vc->remote.id));
171                                         if (blob2buf(&sactx->remote.cert, (char*)vc->remote.cert, sizeof(vc->remote.cert)))
172                                                 vc->remote.certlen = sactx->remote.cert.len;
173                                         sactx->kill_ikesa |= nhrp_vc_ipsec_updown(sactx->child_uniqueid, vc);
174                                 }
175                         } else {
176                                 nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0);
177                         }
178                 }
179                 break;
180         default:
181                 switch (key->ptr[0]) {
182                 case 'l':
183                         if (blob_equal(key, "local-host") && ctx->nsections == 1) {
184                                 if (blob2buf(val, buf, sizeof(buf)))
185                                         str2sockunion(buf, &sactx->local.host);
186                         } else if (blob_equal(key, "local-id") && ctx->nsections == 1) {
187                                 sactx->local.id = *val;
188                         } else if (blob_equal(key, "local-cert-data") && ctx->nsections == 1) {
189                                 sactx->local.cert = *val;
190                         }
191                         break;
192                 case 'r':
193                         if (blob_equal(key, "remote-host") && ctx->nsections == 1) {
194                                 if (blob2buf(val, buf, sizeof(buf)))
195                                         str2sockunion(buf, &sactx->remote.host);
196                         } else if (blob_equal(key, "remote-id") && ctx->nsections == 1) {
197                                 sactx->remote.id = *val;
198                         } else if (blob_equal(key, "remote-cert-data") && ctx->nsections == 1) {
199                                 sactx->remote.cert = *val;
200                         }
201                         break;
202                 case 'u':
203                         if (blob_equal(key, "uniqueid") && blob2buf(val, buf, sizeof(buf))) {
204                                 if (ctx->nsections == 3)
205                                         sactx->child_uniqueid = strtoul(buf, NULL, 0);
206                                 else if (ctx->nsections == 1)
207                                         sactx->ike_uniqueid = strtoul(buf, NULL, 0);
208                         }
209                         break;
210                 case 's':
211                         if (blob_equal(key, "state") && ctx->nsections == 3) {
212                                 sactx->child_ok =
213                                         (sactx->event == 0 &&
214                                          (blob_equal(val, "INSTALLED") ||
215                                           blob_equal(val, "REKEYED")));
216                         }
217                         break;
218                 }
219                 break;
220         }
221 }
222
223 static void parse_cmd_response(
224         struct vici_message_ctx *ctx,
225         enum vici_type_t msgtype,
226         const struct blob *key, const struct blob *val)
227 {
228         char buf[512];
229
230         switch (msgtype) {
231         case VICI_KEY_VALUE:
232                 if (blob_equal(key, "errmsg") && blob2buf(val, buf, sizeof(buf)))
233                         zlog_err("VICI: strongSwan: %s", buf);
234                 break;
235         default:
236                 break;
237         }
238 }
239
240 static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event)
241 {
242         char buf[32];
243         struct handle_sa_ctx ctx = {
244                 .event = event,
245         };
246
247         vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx);
248
249         if (ctx.kill_ikesa && ctx.ike_uniqueid) {
250                 debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid);
251                 snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid);
252                 vici_submit_request(
253                         vici, "terminate",
254                         VICI_KEY_VALUE, "ike-id", strlen(buf), buf,
255                         VICI_END);
256         }
257 }
258
259 static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg)
260 {
261         uint32_t msglen;
262         uint8_t msgtype;
263         struct blob name;
264
265         msglen = zbuf_get_be32(msg);
266         msgtype = zbuf_get8(msg);
267         debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen);
268
269         switch (msgtype) {
270         case VICI_EVENT:
271                 name.len = zbuf_get8(msg);
272                 name.ptr = zbuf_pulln(msg, name.len);
273
274                 debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len, name.ptr);
275                 if (blob_equal(&name, "list-sa") ||
276                     blob_equal(&name, "child-updown") ||
277                     blob_equal(&name, "child-rekey"))
278                         vici_recv_sa(vici, msg, 0);
279                 else if (blob_equal(&name, "child-state-installed") ||
280                          blob_equal(&name, "child-state-rekeyed"))
281                         vici_recv_sa(vici, msg, 1);
282                 else if (blob_equal(&name, "child-state-destroying"))
283                         vici_recv_sa(vici, msg, 2);
284                 break;
285         case VICI_CMD_RESPONSE:
286                 vici_parse_message(vici, msg, parse_cmd_response, 0);
287                 break;
288         case VICI_EVENT_UNKNOWN:
289         case VICI_CMD_UNKNOWN:
290                 zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)");
291                 break;
292         case VICI_EVENT_CONFIRM:
293                 break;
294         default:
295                 zlog_notice("VICI: Unrecognized message type %d", msgtype);
296                 break;
297         }
298 }
299
300 static int vici_read(struct thread *t)
301 {
302         struct vici_conn *vici = THREAD_ARG(t);
303         struct zbuf *ibuf = &vici->ibuf;
304         struct zbuf pktbuf;
305
306         vici->t_read = NULL;
307         if (zbuf_read(ibuf, vici->fd, (size_t) -1) < 0) {
308                 vici_connection_error(vici);
309                 return 0;
310         }
311
312         /* Process all messages in buffer */
313         do {
314                 uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t);
315                 if (!hdrlen)
316                         break;
317                 if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) {
318                         zbuf_reset_head(ibuf, hdrlen);
319                         break;
320                 }
321
322                 /* Handle packet */
323                 zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen)+4, htonl(*hdrlen)+4);
324                 vici_recv_message(vici, &pktbuf);
325         } while (1);
326
327         THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
328         return 0;
329 }
330
331 static int vici_write(struct thread *t)
332 {
333         struct vici_conn *vici = THREAD_ARG(t);
334         int r;
335
336         vici->t_write = NULL;
337         r = zbufq_write(&vici->obuf, vici->fd);
338         if (r > 0) {
339                 THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
340         } else if (r < 0) {
341                 vici_connection_error(vici);
342         }
343
344         return 0;
345 }
346
347 static void vici_submit(struct vici_conn *vici, struct zbuf *obuf)
348 {
349         if (vici->fd < 0) {
350                 zbuf_free(obuf);
351                 return;
352         }
353
354         zbufq_queue(&vici->obuf, obuf);
355         THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd);
356 }
357
358 static void vici_submit_request(struct vici_conn *vici, const char *name, ...)
359 {
360         struct zbuf *obuf;
361         uint32_t *hdrlen;
362         va_list va;
363         size_t len;
364         int type;
365
366         obuf = zbuf_alloc(256);
367         if (!obuf) return;
368
369         hdrlen = zbuf_push(obuf, uint32_t);
370         zbuf_put8(obuf, VICI_CMD_REQUEST);
371         vici_zbuf_puts(obuf, name);
372
373         va_start(va, name);
374         for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) {
375                 zbuf_put8(obuf, type);
376                 switch (type) {
377                 case VICI_KEY_VALUE:
378                         vici_zbuf_puts(obuf, va_arg(va, const char *));
379                         len = va_arg(va, size_t);
380                         zbuf_put_be16(obuf, len);
381                         zbuf_put(obuf, va_arg(va, void *), len);
382                         break;
383                 case VICI_END:
384                         break;
385                 default:
386                         break;
387                 }
388         }
389         va_end(va);
390         *hdrlen = htonl(zbuf_used(obuf) - 4);
391         vici_submit(vici, obuf);
392 }
393
394 static void vici_register_event(struct vici_conn *vici, const char *name)
395 {
396         struct zbuf *obuf;
397         uint32_t *hdrlen;
398         uint8_t namelen;
399
400         namelen = strlen(name);
401         obuf = zbuf_alloc(4 + 1 + 1 + namelen);
402         if (!obuf) return;
403
404         hdrlen = zbuf_push(obuf, uint32_t);
405         zbuf_put8(obuf, VICI_EVENT_REGISTER);
406         zbuf_put8(obuf, namelen);
407         zbuf_put(obuf, name, namelen);
408         *hdrlen = htonl(zbuf_used(obuf) - 4);
409
410         vici_submit(vici, obuf);
411 }
412
413 static int vici_reconnect(struct thread *t)
414 {
415         struct vici_conn *vici = THREAD_ARG(t);
416         int fd;
417
418         vici->t_reconnect = NULL;
419         if (vici->fd >= 0) return 0;
420
421         fd = sock_open_unix("/var/run/charon.vici");
422         if (fd < 0) {
423                 zlog_warn("%s: failure connecting VICI socket: %s",
424                         __PRETTY_FUNCTION__, strerror(errno));
425                 THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2);
426                 return 0;
427         }
428
429         debugf(NHRP_DEBUG_COMMON, "VICI: Connected");
430         vici->fd = fd;
431         THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd);
432
433         /* Send event subscribtions */
434         //vici_register_event(vici, "child-updown");
435         //vici_register_event(vici, "child-rekey");
436         vici_register_event(vici, "child-state-installed");
437         vici_register_event(vici, "child-state-rekeyed");
438         vici_register_event(vici, "child-state-destroying");
439         vici_register_event(vici, "list-sa");
440         vici_submit_request(vici, "list-sas", VICI_END);
441
442         return 0;
443 }
444
445 static struct vici_conn vici_connection;
446
447 void vici_init(void)
448 {
449         struct vici_conn *vici = &vici_connection;
450
451         vici->fd = -1;
452         zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0);
453         zbufq_init(&vici->obuf);
454         THREAD_TIMER_MSEC_ON(master, vici->t_reconnect, vici_reconnect, vici, 10);
455 }
456
457 void vici_terminate(void)
458 {
459 }
460
461 void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio)
462 {
463         struct vici_conn *vici = &vici_connection;
464         char buf[2][SU_ADDRSTRLEN];
465
466         sockunion2str(src, buf[0], sizeof buf[0]);
467         sockunion2str(dst, buf[1], sizeof buf[1]);
468
469         vici_submit_request(
470                 vici, "initiate",
471                 VICI_KEY_VALUE, "child", strlen(profile), profile,
472                 VICI_KEY_VALUE, "timeout", (size_t) 2, "-1",
473                 VICI_KEY_VALUE, "async", (size_t) 1, "1",
474                 VICI_KEY_VALUE, "init-limits", (size_t) 1, prio ? "0" : "1",
475                 VICI_KEY_VALUE, "my-host", strlen(buf[0]), buf[0],
476                 VICI_KEY_VALUE, "other-host", strlen(buf[1]), buf[1],
477                 VICI_END);
478 }
479
480 int sock_open_unix(const char *path)
481 {
482         int ret, fd;
483         struct sockaddr_un addr;
484
485         fd = socket(AF_UNIX, SOCK_STREAM, 0);
486         if (fd < 0)
487                 return -1;
488
489         memset(&addr, 0, sizeof (struct sockaddr_un));
490         addr.sun_family = AF_UNIX;
491         strncpy(addr.sun_path, path, strlen (path));
492
493         ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path));
494         if (ret < 0) {
495                 close(fd);
496                 return -1;
497         }
498
499         fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
500
501         return fd;
502 }