]> git.proxmox.com Git - mirror_frr.git/blame - ldpd/pfkey.c
Merge pull request #2932 from donaldsharp/ferr_fix
[mirror_frr.git] / ldpd / pfkey.c
CommitLineData
8429abe0
RW
1/* $OpenBSD$ */
2
3/*
4 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5 * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
eac6e3f0 20#ifdef __OpenBSD__
8429abe0 21#include <sys/types.h>
eac6e3f0 22#include <sys/socket.h>
8429abe0
RW
23#include <errno.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
28#include "ldpd.h"
29#include "ldpe.h"
30#include "log.h"
31
32static int pfkey_send(int, uint8_t, uint8_t, uint8_t,
33 int, union ldpd_addr *, union ldpd_addr *,
34 uint32_t, uint8_t, int, char *, uint8_t, int, char *,
35 uint16_t, uint16_t);
36static int pfkey_reply(int, uint32_t *);
37static int pfkey_sa_add(int, union ldpd_addr *, union ldpd_addr *,
38 uint8_t, char *, uint32_t *);
39static int pfkey_sa_remove(int, union ldpd_addr *, union ldpd_addr *,
40 uint32_t *);
41static int pfkey_md5sig_establish(struct nbr *, struct nbr_params *nbrp);
42static int pfkey_md5sig_remove(struct nbr *);
43
44#define PFKEY2_CHUNK sizeof(uint64_t)
45#define ROUNDUP(x) (((x) + (PFKEY2_CHUNK - 1)) & ~(PFKEY2_CHUNK - 1))
46#define IOV_CNT 20
47
48static uint32_t sadb_msg_seq;
49static uint32_t pid; /* should pid_t but pfkey needs uint32_t */
50static int fd;
51
52static int
53pfkey_send(int sd, uint8_t satype, uint8_t mtype, uint8_t dir,
54 int af, union ldpd_addr *src, union ldpd_addr *dst, uint32_t spi,
55 uint8_t aalg, int alen, char *akey, uint8_t ealg, int elen, char *ekey,
56 uint16_t sport, uint16_t dport)
57{
58 struct sadb_msg smsg;
59 struct sadb_sa sa;
60 struct sadb_address sa_src, sa_dst;
61 struct sadb_key sa_akey, sa_ekey;
62 struct sadb_spirange sa_spirange;
63 struct iovec iov[IOV_CNT];
64 ssize_t n;
65 int len = 0;
66 int iov_cnt;
4149ef7c
A
67 struct sockaddr_storage smask, dmask;
68 union sockunion su_src, su_dst;
8429abe0
RW
69
70 if (!pid)
71 pid = getpid();
72
73 /* we need clean sockaddr... no ports set */
8429abe0 74 memset(&smask, 0, sizeof(smask));
4149ef7c
A
75
76 addr2sa(af, src, 0, &su_src);
77
8429abe0
RW
78 switch (af) {
79 case AF_INET:
80 memset(&((struct sockaddr_in *)&smask)->sin_addr, 0xff, 32/8);
81 break;
82 case AF_INET6:
83 memset(&((struct sockaddr_in6 *)&smask)->sin6_addr, 0xff,
84 128/8);
85 break;
86 default:
87 return (-1);
88 }
4149ef7c
A
89 smask.ss_family = su_src.sa.sa_family;
90 smask.ss_len = sockaddr_len(&su_src.sa);
8429abe0 91
8429abe0 92 memset(&dmask, 0, sizeof(dmask));
4149ef7c
A
93
94 addr2sa(af, dst, 0, &su_dst);
95
8429abe0
RW
96 switch (af) {
97 case AF_INET:
98 memset(&((struct sockaddr_in *)&dmask)->sin_addr, 0xff, 32/8);
99 break;
100 case AF_INET6:
101 memset(&((struct sockaddr_in6 *)&dmask)->sin6_addr, 0xff,
102 128/8);
103 break;
104 default:
105 return (-1);
106 }
4149ef7c
A
107 dmask.ss_family = su_dst.sa.sa_family;
108 dmask.ss_len = sockaddr_len(&su_dst.sa);
8429abe0
RW
109
110 memset(&smsg, 0, sizeof(smsg));
111 smsg.sadb_msg_version = PF_KEY_V2;
112 smsg.sadb_msg_seq = ++sadb_msg_seq;
113 smsg.sadb_msg_pid = pid;
114 smsg.sadb_msg_len = sizeof(smsg) / 8;
115 smsg.sadb_msg_type = mtype;
116 smsg.sadb_msg_satype = satype;
117
118 switch (mtype) {
119 case SADB_GETSPI:
120 memset(&sa_spirange, 0, sizeof(sa_spirange));
121 sa_spirange.sadb_spirange_exttype = SADB_EXT_SPIRANGE;
122 sa_spirange.sadb_spirange_len = sizeof(sa_spirange) / 8;
123 sa_spirange.sadb_spirange_min = 0x100;
124 sa_spirange.sadb_spirange_max = 0xffffffff;
125 sa_spirange.sadb_spirange_reserved = 0;
126 break;
127 case SADB_ADD:
128 case SADB_UPDATE:
129 case SADB_DELETE:
130 memset(&sa, 0, sizeof(sa));
131 sa.sadb_sa_exttype = SADB_EXT_SA;
132 sa.sadb_sa_len = sizeof(sa) / 8;
133 sa.sadb_sa_replay = 0;
ffdc293b 134 sa.sadb_sa_spi = htonl(spi);
8429abe0
RW
135 sa.sadb_sa_state = SADB_SASTATE_MATURE;
136 break;
137 }
138
139 memset(&sa_src, 0, sizeof(sa_src));
140 sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
4149ef7c
A
141 sa_src.sadb_address_len =
142 (sizeof(sa_src) + ROUNDUP(sockaddr_len(&su_src.sa))) / 8;
8429abe0
RW
143
144 memset(&sa_dst, 0, sizeof(sa_dst));
145 sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST;
4149ef7c
A
146 sa_dst.sadb_address_len =
147 (sizeof(sa_dst) + ROUNDUP(sockaddr_len(&su_dst.sa))) / 8;
8429abe0
RW
148
149 sa.sadb_sa_auth = aalg;
150 sa.sadb_sa_encrypt = SADB_X_EALG_AES; /* XXX */
151
152 switch (mtype) {
153 case SADB_ADD:
154 case SADB_UPDATE:
155 memset(&sa_akey, 0, sizeof(sa_akey));
156 sa_akey.sadb_key_exttype = SADB_EXT_KEY_AUTH;
157 sa_akey.sadb_key_len = (sizeof(sa_akey) +
158 ((alen + 7) / 8) * 8) / 8;
159 sa_akey.sadb_key_bits = 8 * alen;
160
161 memset(&sa_ekey, 0, sizeof(sa_ekey));
162 sa_ekey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT;
163 sa_ekey.sadb_key_len = (sizeof(sa_ekey) +
164 ((elen + 7) / 8) * 8) / 8;
165 sa_ekey.sadb_key_bits = 8 * elen;
166
167 break;
168 }
169
170 iov_cnt = 0;
171
172 /* msghdr */
173 iov[iov_cnt].iov_base = &smsg;
174 iov[iov_cnt].iov_len = sizeof(smsg);
175 iov_cnt++;
176
177 switch (mtype) {
178 case SADB_ADD:
179 case SADB_UPDATE:
180 case SADB_DELETE:
181 /* SA hdr */
182 iov[iov_cnt].iov_base = &sa;
183 iov[iov_cnt].iov_len = sizeof(sa);
184 smsg.sadb_msg_len += sa.sadb_sa_len;
185 iov_cnt++;
186 break;
187 case SADB_GETSPI:
188 /* SPI range */
189 iov[iov_cnt].iov_base = &sa_spirange;
190 iov[iov_cnt].iov_len = sizeof(sa_spirange);
191 smsg.sadb_msg_len += sa_spirange.sadb_spirange_len;
192 iov_cnt++;
193 break;
194 }
195
196 /* dest addr */
197 iov[iov_cnt].iov_base = &sa_dst;
198 iov[iov_cnt].iov_len = sizeof(sa_dst);
199 iov_cnt++;
4149ef7c
A
200 iov[iov_cnt].iov_base = &su_dst;
201 iov[iov_cnt].iov_len = ROUNDUP(sockaddr_len(&su_dst.sa));
8429abe0
RW
202 smsg.sadb_msg_len += sa_dst.sadb_address_len;
203 iov_cnt++;
204
205 /* src addr */
206 iov[iov_cnt].iov_base = &sa_src;
207 iov[iov_cnt].iov_len = sizeof(sa_src);
208 iov_cnt++;
4149ef7c
A
209 iov[iov_cnt].iov_base = &su_src;
210 iov[iov_cnt].iov_len = ROUNDUP(sockaddr_len(&su_src.sa));
8429abe0
RW
211 smsg.sadb_msg_len += sa_src.sadb_address_len;
212 iov_cnt++;
213
214 switch (mtype) {
215 case SADB_ADD:
216 case SADB_UPDATE:
217 if (alen) {
218 /* auth key */
219 iov[iov_cnt].iov_base = &sa_akey;
220 iov[iov_cnt].iov_len = sizeof(sa_akey);
221 iov_cnt++;
222 iov[iov_cnt].iov_base = akey;
223 iov[iov_cnt].iov_len = ((alen + 7) / 8) * 8;
224 smsg.sadb_msg_len += sa_akey.sadb_key_len;
225 iov_cnt++;
226 }
227 if (elen) {
228 /* encryption key */
229 iov[iov_cnt].iov_base = &sa_ekey;
230 iov[iov_cnt].iov_len = sizeof(sa_ekey);
231 iov_cnt++;
232 iov[iov_cnt].iov_base = ekey;
233 iov[iov_cnt].iov_len = ((elen + 7) / 8) * 8;
234 smsg.sadb_msg_len += sa_ekey.sadb_key_len;
235 iov_cnt++;
236 }
237 break;
238 }
239
240 len = smsg.sadb_msg_len * 8;
241 do {
242 n = writev(sd, iov, iov_cnt);
243 } while (n == -1 && (errno == EAGAIN || errno == EINTR));
244
245 if (n == -1) {
246 log_warn("writev (%d/%d)", iov_cnt, len);
247 return (-1);
248 }
249
250 return (0);
251}
252
253int
254pfkey_read(int sd, struct sadb_msg *h)
255{
256 struct sadb_msg hdr;
257
258 if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) {
259 if (errno == EAGAIN || errno == EINTR)
260 return (0);
261 log_warn("pfkey peek");
262 return (-1);
263 }
264
265 /* XXX: Only one message can be outstanding. */
266 if (hdr.sadb_msg_seq == sadb_msg_seq &&
267 hdr.sadb_msg_pid == pid) {
268 if (h)
269 *h = hdr;
270 return (0);
271 }
272
273 /* not ours, discard */
274 if (read(sd, &hdr, sizeof(hdr)) == -1) {
275 if (errno == EAGAIN || errno == EINTR)
276 return (0);
277 log_warn("pfkey read");
278 return (-1);
279 }
280
281 return (1);
282}
283
284static int
ffdc293b 285pfkey_reply(int sd, uint32_t *spi)
8429abe0
RW
286{
287 struct sadb_msg hdr, *msg;
288 struct sadb_ext *ext;
289 struct sadb_sa *sa;
290 uint8_t *data;
291 ssize_t len;
292 int rv;
293
294 do {
295 rv = pfkey_read(sd, &hdr);
296 if (rv == -1)
297 return (-1);
298 } while (rv);
299
300 if (hdr.sadb_msg_errno != 0) {
301 errno = hdr.sadb_msg_errno;
302 if (errno == ESRCH)
303 return (0);
304 else {
305 log_warn("pfkey");
306 return (-1);
307 }
308 }
309 if ((data = reallocarray(NULL, hdr.sadb_msg_len, PFKEY2_CHUNK)) == NULL) {
310 log_warn("pfkey malloc");
311 return (-1);
312 }
313 len = hdr.sadb_msg_len * PFKEY2_CHUNK;
314 if (read(sd, data, len) != len) {
315 log_warn("pfkey read");
316 explicit_bzero(data, len);
317 free(data);
318 return (-1);
319 }
320
321 if (hdr.sadb_msg_type == SADB_GETSPI) {
ffdc293b 322 if (spi == NULL) {
8429abe0
RW
323 explicit_bzero(data, len);
324 free(data);
325 return (0);
326 }
327
328 msg = (struct sadb_msg *)data;
329 for (ext = (struct sadb_ext *)(msg + 1);
330 (size_t)((uint8_t *)ext - (uint8_t *)msg) <
331 msg->sadb_msg_len * PFKEY2_CHUNK;
332 ext = (struct sadb_ext *)((uint8_t *)ext +
333 ext->sadb_ext_len * PFKEY2_CHUNK)) {
334 if (ext->sadb_ext_type == SADB_EXT_SA) {
335 sa = (struct sadb_sa *) ext;
ffdc293b 336 *spi = ntohl(sa->sadb_sa_spi);
8429abe0
RW
337 break;
338 }
339 }
340 }
341 explicit_bzero(data, len);
342 free(data);
343 return (0);
344}
345
346static int
347pfkey_sa_add(int af, union ldpd_addr *src, union ldpd_addr *dst, uint8_t keylen,
348 char *key, uint32_t *spi)
349{
350 if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_GETSPI, 0,
351 af, src, dst, 0, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0)
352 return (-1);
353 if (pfkey_reply(fd, spi) < 0)
354 return (-1);
355 if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_UPDATE, 0,
356 af, src, dst, *spi, 0, keylen, key, 0, 0, NULL, 0, 0) < 0)
357 return (-1);
358 if (pfkey_reply(fd, NULL) < 0)
359 return (-1);
360 return (0);
361}
362
363static int
364pfkey_sa_remove(int af, union ldpd_addr *src, union ldpd_addr *dst,
365 uint32_t *spi)
366{
367 if (pfkey_send(fd, SADB_X_SATYPE_TCPSIGNATURE, SADB_DELETE, 0,
368 af, src, dst, *spi, 0, 0, NULL, 0, 0, NULL, 0, 0) < 0)
369 return (-1);
370 if (pfkey_reply(fd, NULL) < 0)
371 return (-1);
372 *spi = 0;
373 return (0);
374}
375
376static int
377pfkey_md5sig_establish(struct nbr *nbr, struct nbr_params *nbrp)
378{
379 sleep(1);
380
381 if (!nbr->auth.spi_out)
382 if (pfkey_sa_add(nbr->af, &nbr->laddr, &nbr->raddr,
383 nbrp->auth.md5key_len, nbrp->auth.md5key,
384 &nbr->auth.spi_out) == -1)
385 return (-1);
386 if (!nbr->auth.spi_in)
387 if (pfkey_sa_add(nbr->af, &nbr->raddr, &nbr->laddr,
388 nbrp->auth.md5key_len, nbrp->auth.md5key,
389 &nbr->auth.spi_in) == -1)
390 return (-1);
391
392 nbr->auth.established = 1;
393 return (0);
394}
395
396static int
397pfkey_md5sig_remove(struct nbr *nbr)
398{
399 if (nbr->auth.spi_out)
400 if (pfkey_sa_remove(nbr->af, &nbr->laddr, &nbr->raddr,
401 &nbr->auth.spi_out) == -1)
402 return (-1);
403 if (nbr->auth.spi_in)
404 if (pfkey_sa_remove(nbr->af, &nbr->raddr, &nbr->laddr,
405 &nbr->auth.spi_in) == -1)
406 return (-1);
407
408 nbr->auth.established = 0;
409 nbr->auth.spi_in = 0;
410 nbr->auth.spi_out = 0;
411 nbr->auth.method = AUTH_NONE;
412 memset(nbr->auth.md5key, 0, sizeof(nbr->auth.md5key));
413
414 return (0);
415}
416
417int
418pfkey_establish(struct nbr *nbr, struct nbr_params *nbrp)
419{
420 if (nbrp->auth.method == AUTH_NONE)
421 return (0);
422
8429abe0
RW
423 switch (nbr->auth.method) {
424 case AUTH_MD5SIG:
425 strlcpy(nbr->auth.md5key, nbrp->auth.md5key,
426 sizeof(nbr->auth.md5key));
427 return (pfkey_md5sig_establish(nbr, nbrp));
428 default:
429 break;
430 }
431
432 return (0);
433}
434
435int
436pfkey_remove(struct nbr *nbr)
437{
438 if (nbr->auth.method == AUTH_NONE || !nbr->auth.established)
439 return (0);
440
441 switch (nbr->auth.method) {
442 case AUTH_MD5SIG:
443 return (pfkey_md5sig_remove(nbr));
444 default:
445 break;
446 }
447
448 return (0);
449}
450
451int
452pfkey_init(void)
453{
454 if ((fd = socket(PF_KEY, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
455 PF_KEY_V2)) == -1) {
456 if (errno == EPROTONOSUPPORT) {
457 log_warnx("PF_KEY not available");
458 sysdep.no_pfkey = 1;
459 return (-1);
460 } else
461 fatal("pfkey setup failed");
462 }
463 return (fd);
464}
eac6e3f0 465#endif /* __OpenBSD__ */