1*57f58d0dSnaddy /* $OpenBSD: pfkey.c,v 1.43 2006/06/01 17:32:20 naddy Exp $ */ 2f484f2cfShshoexer /* 3f484f2cfShshoexer * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 4f484f2cfShshoexer * Copyright (c) 2003, 2004 Markus Friedl <markus@openbsd.org> 5f484f2cfShshoexer * Copyright (c) 2004, 2005 Hans-Joerg Hoexer <hshoexer@openbsd.org> 6f484f2cfShshoexer * 7f484f2cfShshoexer * Permission to use, copy, modify, and distribute this software for any 8f484f2cfShshoexer * purpose with or without fee is hereby granted, provided that the above 9f484f2cfShshoexer * copyright notice and this permission notice appear in all copies. 10f484f2cfShshoexer * 11f484f2cfShshoexer * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12f484f2cfShshoexer * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13f484f2cfShshoexer * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14f484f2cfShshoexer * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15f484f2cfShshoexer * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16f484f2cfShshoexer * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17f484f2cfShshoexer * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18f484f2cfShshoexer */ 19f484f2cfShshoexer 20f484f2cfShshoexer #include <sys/types.h> 21f484f2cfShshoexer #include <sys/queue.h> 22f484f2cfShshoexer #include <sys/uio.h> 23f484f2cfShshoexer #include <sys/socket.h> 24f484f2cfShshoexer #include <netinet/in.h> 25f484f2cfShshoexer #include <netinet/ip_ipsp.h> 26f484f2cfShshoexer #include <net/pfkeyv2.h> 27f484f2cfShshoexer 28f484f2cfShshoexer #include <err.h> 29f484f2cfShshoexer #include <errno.h> 30f484f2cfShshoexer #include <stdio.h> 31f484f2cfShshoexer #include <string.h> 32f484f2cfShshoexer #include <stdlib.h> 33f484f2cfShshoexer #include <unistd.h> 34f484f2cfShshoexer 35f484f2cfShshoexer #include "ipsecctl.h" 361edc1b9aShshoexer #include "pfkey.h" 37f484f2cfShshoexer 38f484f2cfShshoexer #define ROUNDUP(x) (((x) + (PFKEYV2_CHUNK - 1)) & ~(PFKEYV2_CHUNK - 1)) 39f484f2cfShshoexer #define IOV_CNT 20 40f484f2cfShshoexer 41f484f2cfShshoexer static int fd; 42f484f2cfShshoexer static u_int32_t sadb_msg_seq = 1; 43f484f2cfShshoexer 449182219dSmarkus static int pfkey_flow(int, u_int8_t, u_int8_t, u_int8_t, u_int8_t, 45*57f58d0dSnaddy struct ipsec_addr_wrap *, u_int16_t, 46*57f58d0dSnaddy struct ipsec_addr_wrap *, u_int16_t, 47435bb41eSmarkus struct ipsec_addr_wrap *, struct ipsec_addr_wrap *, 48435bb41eSmarkus struct ipsec_auth *, u_int8_t); 49f032086dShshoexer static int pfkey_sa(int, u_int8_t, u_int8_t, u_int32_t, 5091f765ddShshoexer struct ipsec_addr_wrap *, struct ipsec_addr_wrap *, 51375db29dShshoexer struct ipsec_transforms *, struct ipsec_key *, 52a38d220fShshoexer struct ipsec_key *, u_int8_t); 538a87fca6Smsf static int pfkey_reply(int, u_int8_t **, ssize_t *); 54e225c210Shshoexer int pfkey_parse(struct sadb_msg *, struct ipsec_rule *); 55f484f2cfShshoexer int pfkey_ipsec_flush(void); 56356121f6Shshoexer int pfkey_ipsec_establish(int, struct ipsec_rule *); 57f484f2cfShshoexer int pfkey_init(void); 58f484f2cfShshoexer 59f484f2cfShshoexer static int 60f484f2cfShshoexer pfkey_flow(int sd, u_int8_t satype, u_int8_t action, u_int8_t direction, 61*57f58d0dSnaddy u_int8_t proto, struct ipsec_addr_wrap *src, u_int16_t sport, 62*57f58d0dSnaddy struct ipsec_addr_wrap *dst, u_int16_t dport, 63435bb41eSmarkus struct ipsec_addr_wrap *local, struct ipsec_addr_wrap *peer, 64435bb41eSmarkus struct ipsec_auth *auth, u_int8_t flowtype) 65f484f2cfShshoexer { 66f484f2cfShshoexer struct sadb_msg smsg; 67435bb41eSmarkus struct sadb_address sa_src, sa_dst, sa_local, sa_peer, sa_smask, 68435bb41eSmarkus sa_dmask; 69f484f2cfShshoexer struct sadb_protocol sa_flowtype, sa_protocol; 70f484f2cfShshoexer struct sadb_ident *sa_srcid, *sa_dstid; 71435bb41eSmarkus struct sockaddr_storage ssrc, sdst, slocal, speer, smask, dmask; 72f484f2cfShshoexer struct iovec iov[IOV_CNT]; 73f484f2cfShshoexer ssize_t n; 74f484f2cfShshoexer int iov_cnt, len, ret = 0; 75f484f2cfShshoexer 76f484f2cfShshoexer sa_srcid = sa_dstid = NULL; 77f484f2cfShshoexer 78f484f2cfShshoexer bzero(&ssrc, sizeof(ssrc)); 79f484f2cfShshoexer bzero(&smask, sizeof(smask)); 802099bcdfStodd ssrc.ss_family = smask.ss_family = src->af; 81f484f2cfShshoexer switch (src->af) { 82f484f2cfShshoexer case AF_INET: 83712e78baShshoexer ((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4; 84f484f2cfShshoexer ssrc.ss_len = sizeof(struct sockaddr_in); 85712e78baShshoexer ((struct sockaddr_in *)&smask)->sin_addr = src->mask.v4; 86*57f58d0dSnaddy if (sport) { 87*57f58d0dSnaddy ((struct sockaddr_in *)&ssrc)->sin_port = sport; 88*57f58d0dSnaddy ((struct sockaddr_in *)&smask)->sin_port = 0xffff; 89*57f58d0dSnaddy } 90f484f2cfShshoexer break; 91f484f2cfShshoexer case AF_INET6: 922099bcdfStodd ((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6; 932099bcdfStodd ssrc.ss_len = sizeof(struct sockaddr_in6); 942099bcdfStodd ((struct sockaddr_in6 *)&smask)->sin6_addr = src->mask.v6; 95*57f58d0dSnaddy if (sport) { 96*57f58d0dSnaddy ((struct sockaddr_in6 *)&ssrc)->sin6_port = sport; 97*57f58d0dSnaddy ((struct sockaddr_in6 *)&smask)->sin6_port = 0xffff; 98*57f58d0dSnaddy } 992099bcdfStodd break; 100f484f2cfShshoexer default: 101f484f2cfShshoexer warnx("unsupported address family %d", src->af); 102f484f2cfShshoexer return -1; 103f484f2cfShshoexer } 104f484f2cfShshoexer smask.ss_len = ssrc.ss_len; 105f484f2cfShshoexer 106f484f2cfShshoexer bzero(&sdst, sizeof(sdst)); 107f484f2cfShshoexer bzero(&dmask, sizeof(dmask)); 1082099bcdfStodd sdst.ss_family = dmask.ss_family = dst->af; 109f484f2cfShshoexer switch (dst->af) { 110f484f2cfShshoexer case AF_INET: 111712e78baShshoexer ((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4; 112f484f2cfShshoexer sdst.ss_len = sizeof(struct sockaddr_in); 113712e78baShshoexer ((struct sockaddr_in *)&dmask)->sin_addr = dst->mask.v4; 114*57f58d0dSnaddy if (dport) { 115*57f58d0dSnaddy ((struct sockaddr_in *)&sdst)->sin_port = dport; 116*57f58d0dSnaddy ((struct sockaddr_in *)&dmask)->sin_port = 0xffff; 117*57f58d0dSnaddy } 118f484f2cfShshoexer break; 119f484f2cfShshoexer case AF_INET6: 1202099bcdfStodd ((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6; 1212099bcdfStodd sdst.ss_len = sizeof(struct sockaddr_in6); 1222099bcdfStodd ((struct sockaddr_in6 *)&dmask)->sin6_addr = dst->mask.v6; 123*57f58d0dSnaddy if (dport) { 124*57f58d0dSnaddy ((struct sockaddr_in6 *)&sdst)->sin6_port = dport; 125*57f58d0dSnaddy ((struct sockaddr_in6 *)&dmask)->sin6_port = 0xffff; 126*57f58d0dSnaddy } 1272099bcdfStodd break; 128f484f2cfShshoexer default: 129f484f2cfShshoexer warnx("unsupported address family %d", dst->af); 130f484f2cfShshoexer return -1; 131f484f2cfShshoexer } 132f484f2cfShshoexer dmask.ss_len = sdst.ss_len; 133f484f2cfShshoexer 134435bb41eSmarkus bzero(&slocal, sizeof(slocal)); 135435bb41eSmarkus if (local) { 1362099bcdfStodd slocal.ss_family = local->af; 137435bb41eSmarkus switch (local->af) { 138435bb41eSmarkus case AF_INET: 139435bb41eSmarkus ((struct sockaddr_in *)&slocal)->sin_addr = 140435bb41eSmarkus local->address.v4; 141435bb41eSmarkus slocal.ss_len = sizeof(struct sockaddr_in); 142435bb41eSmarkus break; 143435bb41eSmarkus case AF_INET6: 1442099bcdfStodd ((struct sockaddr_in6 *)&slocal)->sin6_addr = 1452099bcdfStodd local->address.v6; 1462099bcdfStodd slocal.ss_len = sizeof(struct sockaddr_in6); 1472099bcdfStodd break; 148435bb41eSmarkus default: 149435bb41eSmarkus warnx("unsupported address family %d", local->af); 150435bb41eSmarkus return -1; 151435bb41eSmarkus } 152435bb41eSmarkus } 153435bb41eSmarkus 154f484f2cfShshoexer bzero(&speer, sizeof(speer)); 15522a29ad6Shshoexer if (peer) { 1562099bcdfStodd speer.ss_family = peer->af; 157f484f2cfShshoexer switch (peer->af) { 158f484f2cfShshoexer case AF_INET: 159712e78baShshoexer ((struct sockaddr_in *)&speer)->sin_addr = 160712e78baShshoexer peer->address.v4; 161f484f2cfShshoexer speer.ss_len = sizeof(struct sockaddr_in); 162f484f2cfShshoexer break; 163f484f2cfShshoexer case AF_INET6: 1642099bcdfStodd ((struct sockaddr_in6 *)&speer)->sin6_addr = 1652099bcdfStodd peer->address.v6; 1662099bcdfStodd speer.ss_len = sizeof(struct sockaddr_in6); 1672099bcdfStodd break; 168f484f2cfShshoexer default: 169f484f2cfShshoexer warnx("unsupported address family %d", peer->af); 170f484f2cfShshoexer return -1; 171f484f2cfShshoexer } 17222a29ad6Shshoexer } 173f484f2cfShshoexer 174f484f2cfShshoexer bzero(&smsg, sizeof(smsg)); 175f484f2cfShshoexer smsg.sadb_msg_version = PF_KEY_V2; 176f484f2cfShshoexer smsg.sadb_msg_seq = sadb_msg_seq++; 177f484f2cfShshoexer smsg.sadb_msg_pid = getpid(); 178f484f2cfShshoexer smsg.sadb_msg_len = sizeof(smsg) / 8; 179f484f2cfShshoexer smsg.sadb_msg_type = action; 180f484f2cfShshoexer smsg.sadb_msg_satype = satype; 181f484f2cfShshoexer 182f484f2cfShshoexer bzero(&sa_flowtype, sizeof(sa_flowtype)); 183f484f2cfShshoexer sa_flowtype.sadb_protocol_exttype = SADB_X_EXT_FLOW_TYPE; 184f484f2cfShshoexer sa_flowtype.sadb_protocol_len = sizeof(sa_flowtype) / 8; 185f484f2cfShshoexer sa_flowtype.sadb_protocol_direction = direction; 18626df514dShshoexer 187e1ffbfafShshoexer switch (flowtype) { 188e1ffbfafShshoexer case TYPE_USE: 18926df514dShshoexer sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_USE; 190e1ffbfafShshoexer break; 191b7a16601Shshoexer case TYPE_ACQUIRE: 192b7a16601Shshoexer sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_ACQUIRE; 193b7a16601Shshoexer break; 194e1ffbfafShshoexer case TYPE_REQUIRE: 195f484f2cfShshoexer sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_REQUIRE; 196e1ffbfafShshoexer break; 1977c7fb9e5Sreyk case TYPE_DENY: 1987c7fb9e5Sreyk sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DENY; 1997c7fb9e5Sreyk break; 2007c7fb9e5Sreyk case TYPE_BYPASS: 2017c7fb9e5Sreyk sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_BYPASS; 2027c7fb9e5Sreyk break; 203b7a16601Shshoexer case TYPE_DONTACQ: 204b7a16601Shshoexer sa_flowtype.sadb_protocol_proto = SADB_X_FLOW_TYPE_DONTACQ; 205b7a16601Shshoexer break; 206e1ffbfafShshoexer default: 207e1ffbfafShshoexer warnx("unsupported flowtype %d", flowtype); 208e1ffbfafShshoexer return -1; 209e1ffbfafShshoexer } 210f484f2cfShshoexer 211f484f2cfShshoexer bzero(&sa_protocol, sizeof(sa_protocol)); 212f484f2cfShshoexer sa_protocol.sadb_protocol_exttype = SADB_X_EXT_PROTOCOL; 213f484f2cfShshoexer sa_protocol.sadb_protocol_len = sizeof(sa_protocol) / 8; 214f484f2cfShshoexer sa_protocol.sadb_protocol_direction = 0; 2159182219dSmarkus sa_protocol.sadb_protocol_proto = proto; 216f484f2cfShshoexer 217f484f2cfShshoexer bzero(&sa_src, sizeof(sa_src)); 218f484f2cfShshoexer sa_src.sadb_address_exttype = SADB_X_EXT_SRC_FLOW; 219f484f2cfShshoexer sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8; 220f484f2cfShshoexer 221f484f2cfShshoexer bzero(&sa_smask, sizeof(sa_smask)); 222f484f2cfShshoexer sa_smask.sadb_address_exttype = SADB_X_EXT_SRC_MASK; 223f484f2cfShshoexer sa_smask.sadb_address_len = 224f484f2cfShshoexer (sizeof(sa_smask) + ROUNDUP(smask.ss_len)) / 8; 225f484f2cfShshoexer 226f484f2cfShshoexer bzero(&sa_dst, sizeof(sa_dst)); 227f484f2cfShshoexer sa_dst.sadb_address_exttype = SADB_X_EXT_DST_FLOW; 228f484f2cfShshoexer sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8; 229f484f2cfShshoexer 230f484f2cfShshoexer bzero(&sa_dmask, sizeof(sa_dmask)); 231f484f2cfShshoexer sa_dmask.sadb_address_exttype = SADB_X_EXT_DST_MASK; 232f484f2cfShshoexer sa_dmask.sadb_address_len = 233f484f2cfShshoexer (sizeof(sa_dmask) + ROUNDUP(dmask.ss_len)) / 8; 234f484f2cfShshoexer 235435bb41eSmarkus if (local) { 236435bb41eSmarkus bzero(&sa_local, sizeof(sa_local)); 237435bb41eSmarkus sa_local.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; 238435bb41eSmarkus sa_local.sadb_address_len = 239435bb41eSmarkus (sizeof(sa_local) + ROUNDUP(slocal.ss_len)) / 8; 240435bb41eSmarkus } 241435bb41eSmarkus if (peer) { 242f484f2cfShshoexer bzero(&sa_peer, sizeof(sa_peer)); 243f484f2cfShshoexer sa_peer.sadb_address_exttype = SADB_EXT_ADDRESS_DST; 244f484f2cfShshoexer sa_peer.sadb_address_len = 245f484f2cfShshoexer (sizeof(sa_peer) + ROUNDUP(speer.ss_len)) / 8; 246435bb41eSmarkus } 247f484f2cfShshoexer 248abe65127Shshoexer if (auth && auth->srcid) { 249abe65127Shshoexer len = ROUNDUP(strlen(auth->srcid) + 1) + sizeof(*sa_srcid); 250f484f2cfShshoexer 251f484f2cfShshoexer sa_srcid = calloc(len, sizeof(u_int8_t)); 252f484f2cfShshoexer if (sa_srcid == NULL) 253bd828a90Shshoexer err(1, "pfkey_flow: calloc"); 254f484f2cfShshoexer 255abe65127Shshoexer sa_srcid->sadb_ident_type = auth->idtype; 256f484f2cfShshoexer sa_srcid->sadb_ident_len = len / 8; 257f484f2cfShshoexer sa_srcid->sadb_ident_exttype = SADB_EXT_IDENTITY_SRC; 258f484f2cfShshoexer 259abe65127Shshoexer strlcpy((char *)(sa_srcid + 1), auth->srcid, 260abe65127Shshoexer ROUNDUP(strlen(auth->srcid) + 1)); 261f484f2cfShshoexer } 262abe65127Shshoexer if (auth && auth->dstid) { 263abe65127Shshoexer len = ROUNDUP(strlen(auth->dstid) + 1) + sizeof(*sa_dstid); 264f484f2cfShshoexer 265f484f2cfShshoexer sa_dstid = calloc(len, sizeof(u_int8_t)); 266f484f2cfShshoexer if (sa_dstid == NULL) 267bd828a90Shshoexer err(1, "pfkey_flow: calloc"); 268f484f2cfShshoexer 269abe65127Shshoexer sa_dstid->sadb_ident_type = auth->idtype; 270f484f2cfShshoexer sa_dstid->sadb_ident_len = len / 8; 271f484f2cfShshoexer sa_dstid->sadb_ident_exttype = SADB_EXT_IDENTITY_DST; 272f484f2cfShshoexer 273abe65127Shshoexer strlcpy((char *)(sa_dstid + 1), auth->dstid, 274abe65127Shshoexer ROUNDUP(strlen(auth->dstid) + 1)); 275f484f2cfShshoexer } 276f484f2cfShshoexer 277f484f2cfShshoexer iov_cnt = 0; 278f484f2cfShshoexer 279f484f2cfShshoexer /* header */ 280f484f2cfShshoexer iov[iov_cnt].iov_base = &smsg; 281f484f2cfShshoexer iov[iov_cnt].iov_len = sizeof(smsg); 282f484f2cfShshoexer iov_cnt++; 283f484f2cfShshoexer 28489ad8c34Shshoexer /* add flow type */ 28589ad8c34Shshoexer iov[iov_cnt].iov_base = &sa_flowtype; 28689ad8c34Shshoexer iov[iov_cnt].iov_len = sizeof(sa_flowtype); 28789ad8c34Shshoexer smsg.sadb_msg_len += sa_flowtype.sadb_protocol_len; 28889ad8c34Shshoexer iov_cnt++; 28989ad8c34Shshoexer 290435bb41eSmarkus /* local ip */ 291435bb41eSmarkus if (local) { 292435bb41eSmarkus iov[iov_cnt].iov_base = &sa_local; 293435bb41eSmarkus iov[iov_cnt].iov_len = sizeof(sa_local); 294435bb41eSmarkus iov_cnt++; 295435bb41eSmarkus iov[iov_cnt].iov_base = &slocal; 296435bb41eSmarkus iov[iov_cnt].iov_len = ROUNDUP(slocal.ss_len); 297435bb41eSmarkus smsg.sadb_msg_len += sa_local.sadb_address_len; 298435bb41eSmarkus iov_cnt++; 299435bb41eSmarkus } 300435bb41eSmarkus 301f484f2cfShshoexer /* remote peer */ 30222a29ad6Shshoexer if (peer) { 303f484f2cfShshoexer iov[iov_cnt].iov_base = &sa_peer; 304f484f2cfShshoexer iov[iov_cnt].iov_len = sizeof(sa_peer); 305f484f2cfShshoexer iov_cnt++; 306f484f2cfShshoexer iov[iov_cnt].iov_base = &speer; 307f484f2cfShshoexer iov[iov_cnt].iov_len = ROUNDUP(speer.ss_len); 308f484f2cfShshoexer smsg.sadb_msg_len += sa_peer.sadb_address_len; 309f484f2cfShshoexer iov_cnt++; 31022a29ad6Shshoexer } 311f484f2cfShshoexer 31289ad8c34Shshoexer /* src addr */ 31389ad8c34Shshoexer iov[iov_cnt].iov_base = &sa_src; 31489ad8c34Shshoexer iov[iov_cnt].iov_len = sizeof(sa_src); 31589ad8c34Shshoexer iov_cnt++; 31689ad8c34Shshoexer iov[iov_cnt].iov_base = &ssrc; 31789ad8c34Shshoexer iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len); 31889ad8c34Shshoexer smsg.sadb_msg_len += sa_src.sadb_address_len; 319f484f2cfShshoexer iov_cnt++; 320f484f2cfShshoexer 32189ad8c34Shshoexer /* src mask */ 322f484f2cfShshoexer iov[iov_cnt].iov_base = &sa_smask; 323f484f2cfShshoexer iov[iov_cnt].iov_len = sizeof(sa_smask); 324f484f2cfShshoexer iov_cnt++; 325f484f2cfShshoexer iov[iov_cnt].iov_base = &smask; 326f484f2cfShshoexer iov[iov_cnt].iov_len = ROUNDUP(smask.ss_len); 327f484f2cfShshoexer smsg.sadb_msg_len += sa_smask.sadb_address_len; 328f484f2cfShshoexer iov_cnt++; 329f484f2cfShshoexer 330f484f2cfShshoexer /* dest addr */ 331f484f2cfShshoexer iov[iov_cnt].iov_base = &sa_dst; 332f484f2cfShshoexer iov[iov_cnt].iov_len = sizeof(sa_dst); 333f484f2cfShshoexer iov_cnt++; 334f484f2cfShshoexer iov[iov_cnt].iov_base = &sdst; 335f484f2cfShshoexer iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len); 336f484f2cfShshoexer smsg.sadb_msg_len += sa_dst.sadb_address_len; 337f484f2cfShshoexer iov_cnt++; 338f484f2cfShshoexer 33989ad8c34Shshoexer /* dst mask */ 34089ad8c34Shshoexer iov[iov_cnt].iov_base = &sa_dmask; 34189ad8c34Shshoexer iov[iov_cnt].iov_len = sizeof(sa_dmask); 342f484f2cfShshoexer iov_cnt++; 34389ad8c34Shshoexer iov[iov_cnt].iov_base = &dmask; 34489ad8c34Shshoexer iov[iov_cnt].iov_len = ROUNDUP(dmask.ss_len); 34589ad8c34Shshoexer smsg.sadb_msg_len += sa_dmask.sadb_address_len; 34689ad8c34Shshoexer iov_cnt++; 34789ad8c34Shshoexer 34889ad8c34Shshoexer /* add protocol */ 34989ad8c34Shshoexer iov[iov_cnt].iov_base = &sa_protocol; 35089ad8c34Shshoexer iov[iov_cnt].iov_len = sizeof(sa_protocol); 35189ad8c34Shshoexer smsg.sadb_msg_len += sa_protocol.sadb_protocol_len; 352f484f2cfShshoexer iov_cnt++; 353f484f2cfShshoexer 354f484f2cfShshoexer if (sa_srcid) { 355f484f2cfShshoexer /* src identity */ 356f484f2cfShshoexer iov[iov_cnt].iov_base = sa_srcid; 357f484f2cfShshoexer iov[iov_cnt].iov_len = sa_srcid->sadb_ident_len * 8; 358f484f2cfShshoexer smsg.sadb_msg_len += sa_srcid->sadb_ident_len; 359f484f2cfShshoexer iov_cnt++; 360f484f2cfShshoexer } 361f484f2cfShshoexer if (sa_dstid) { 362f484f2cfShshoexer /* dst identity */ 363f484f2cfShshoexer iov[iov_cnt].iov_base = sa_dstid; 364f484f2cfShshoexer iov[iov_cnt].iov_len = sa_dstid->sadb_ident_len * 8; 365f484f2cfShshoexer smsg.sadb_msg_len += sa_dstid->sadb_ident_len; 366f484f2cfShshoexer iov_cnt++; 367f484f2cfShshoexer } 368f484f2cfShshoexer len = smsg.sadb_msg_len * 8; 369f484f2cfShshoexer if ((n = writev(sd, iov, iov_cnt)) == -1) { 370f484f2cfShshoexer warn("writev failed"); 371f484f2cfShshoexer ret = -1; 372f484f2cfShshoexer goto out; 373f484f2cfShshoexer } 374f484f2cfShshoexer if (n != len) { 375f484f2cfShshoexer warnx("short write"); 376f484f2cfShshoexer ret = -1; 377f484f2cfShshoexer } 378f484f2cfShshoexer 379f484f2cfShshoexer out: 380f484f2cfShshoexer if (sa_srcid) 381f484f2cfShshoexer free(sa_srcid); 382f484f2cfShshoexer if (sa_dstid) 383f484f2cfShshoexer free(sa_dstid); 384f484f2cfShshoexer 385f484f2cfShshoexer return ret; 386f484f2cfShshoexer } 387f484f2cfShshoexer 388f484f2cfShshoexer static int 38991f765ddShshoexer pfkey_sa(int sd, u_int8_t satype, u_int8_t action, u_int32_t spi, 39091f765ddShshoexer struct ipsec_addr_wrap *src, struct ipsec_addr_wrap *dst, 39191f765ddShshoexer struct ipsec_transforms *xfs, struct ipsec_key *authkey, 392a38d220fShshoexer struct ipsec_key *enckey, u_int8_t tmode) 393f032086dShshoexer { 394f032086dShshoexer struct sadb_msg smsg; 395f032086dShshoexer struct sadb_sa sa; 396f032086dShshoexer struct sadb_address sa_src, sa_dst; 397881e2068Shshoexer struct sadb_key sa_authkey, sa_enckey; 398f032086dShshoexer struct sockaddr_storage ssrc, sdst; 399f032086dShshoexer struct iovec iov[IOV_CNT]; 400f032086dShshoexer ssize_t n; 401f032086dShshoexer int iov_cnt, len, ret = 0; 402f032086dShshoexer 403f032086dShshoexer bzero(&ssrc, sizeof(ssrc)); 4042099bcdfStodd ssrc.ss_family = src->af; 405f032086dShshoexer switch (src->af) { 406f032086dShshoexer case AF_INET: 407712e78baShshoexer ((struct sockaddr_in *)&ssrc)->sin_addr = src->address.v4; 408f032086dShshoexer ssrc.ss_len = sizeof(struct sockaddr_in); 409f032086dShshoexer break; 410f032086dShshoexer case AF_INET6: 4112099bcdfStodd ((struct sockaddr_in6 *)&ssrc)->sin6_addr = src->address.v6; 4122099bcdfStodd ssrc.ss_len = sizeof(struct sockaddr_in6); 4132099bcdfStodd break; 414f032086dShshoexer default: 415f032086dShshoexer warnx("unsupported address family %d", src->af); 416f032086dShshoexer return -1; 417f032086dShshoexer } 418f032086dShshoexer 419f032086dShshoexer bzero(&sdst, sizeof(sdst)); 4202099bcdfStodd sdst.ss_family = dst->af; 421f032086dShshoexer switch (dst->af) { 422f032086dShshoexer case AF_INET: 423712e78baShshoexer ((struct sockaddr_in *)&sdst)->sin_addr = dst->address.v4; 424f032086dShshoexer sdst.ss_len = sizeof(struct sockaddr_in); 425f032086dShshoexer break; 426f032086dShshoexer case AF_INET6: 4272099bcdfStodd ((struct sockaddr_in6 *)&sdst)->sin6_addr = dst->address.v6; 4282099bcdfStodd sdst.ss_len = sizeof(struct sockaddr_in6); 4292099bcdfStodd break; 430f032086dShshoexer default: 431f032086dShshoexer warnx("unsupported address family %d", dst->af); 432f032086dShshoexer return -1; 433f032086dShshoexer } 434f032086dShshoexer 435f032086dShshoexer bzero(&smsg, sizeof(smsg)); 436f032086dShshoexer smsg.sadb_msg_version = PF_KEY_V2; 437f032086dShshoexer smsg.sadb_msg_seq = sadb_msg_seq++; 438f032086dShshoexer smsg.sadb_msg_pid = getpid(); 439f032086dShshoexer smsg.sadb_msg_len = sizeof(smsg) / 8; 440f032086dShshoexer smsg.sadb_msg_type = action; 441f032086dShshoexer smsg.sadb_msg_satype = satype; 442f032086dShshoexer 443f032086dShshoexer bzero(&sa, sizeof(sa)); 444f032086dShshoexer sa.sadb_sa_len = sizeof(sa) / 8; 445f032086dShshoexer sa.sadb_sa_exttype = SADB_EXT_SA; 446f032086dShshoexer sa.sadb_sa_spi = htonl(spi); 447f032086dShshoexer sa.sadb_sa_state = SADB_SASTATE_MATURE; 448f032086dShshoexer 44988a8cceeSmarkus if (satype != SADB_X_SATYPE_IPIP && tmode == IPSEC_TUNNEL) 450a38d220fShshoexer sa.sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL; 451a38d220fShshoexer 452375db29dShshoexer if (xfs && xfs->authxf) { 453375db29dShshoexer switch (xfs->authxf->id) { 454881e2068Shshoexer case AUTHXF_NONE: 455881e2068Shshoexer break; 456881e2068Shshoexer case AUTHXF_HMAC_MD5: 457881e2068Shshoexer sa.sadb_sa_auth = SADB_AALG_MD5HMAC; 458881e2068Shshoexer break; 459881e2068Shshoexer case AUTHXF_HMAC_RIPEMD160: 460881e2068Shshoexer sa.sadb_sa_auth = SADB_X_AALG_RIPEMD160HMAC; 461881e2068Shshoexer break; 462881e2068Shshoexer case AUTHXF_HMAC_SHA1: 463881e2068Shshoexer sa.sadb_sa_auth = SADB_AALG_SHA1HMAC; 464881e2068Shshoexer break; 465881e2068Shshoexer case AUTHXF_HMAC_SHA2_256: 466881e2068Shshoexer sa.sadb_sa_auth = SADB_X_AALG_SHA2_256; 467881e2068Shshoexer break; 468881e2068Shshoexer case AUTHXF_HMAC_SHA2_384: 469881e2068Shshoexer sa.sadb_sa_auth = SADB_X_AALG_SHA2_384; 470881e2068Shshoexer break; 471881e2068Shshoexer case AUTHXF_HMAC_SHA2_512: 472881e2068Shshoexer sa.sadb_sa_auth = SADB_X_AALG_SHA2_512; 473881e2068Shshoexer break; 474881e2068Shshoexer default: 475881e2068Shshoexer warnx("unsupported authentication algorithm %d", 476375db29dShshoexer xfs->authxf->id); 477881e2068Shshoexer } 478881e2068Shshoexer } 479375db29dShshoexer if (xfs && xfs->encxf) { 480375db29dShshoexer switch (xfs->encxf->id) { 481881e2068Shshoexer case ENCXF_NONE: 482881e2068Shshoexer break; 483881e2068Shshoexer case ENCXF_3DES_CBC: 484881e2068Shshoexer sa.sadb_sa_encrypt = SADB_EALG_3DESCBC; 485881e2068Shshoexer break; 486881e2068Shshoexer case ENCXF_DES_CBC: 487881e2068Shshoexer sa.sadb_sa_encrypt = SADB_EALG_DESCBC; 488881e2068Shshoexer break; 489881e2068Shshoexer case ENCXF_AES: 490881e2068Shshoexer sa.sadb_sa_encrypt = SADB_X_EALG_AES; 491881e2068Shshoexer break; 492881e2068Shshoexer case ENCXF_AESCTR: 493881e2068Shshoexer sa.sadb_sa_encrypt = SADB_X_EALG_AESCTR; 494881e2068Shshoexer break; 495881e2068Shshoexer case ENCXF_BLOWFISH: 496881e2068Shshoexer sa.sadb_sa_encrypt = SADB_X_EALG_BLF; 497881e2068Shshoexer break; 498881e2068Shshoexer case ENCXF_CAST128: 499881e2068Shshoexer sa.sadb_sa_encrypt = SADB_X_EALG_CAST; 500881e2068Shshoexer break; 501881e2068Shshoexer case ENCXF_NULL: 502881e2068Shshoexer sa.sadb_sa_encrypt = SADB_EALG_NULL; 503881e2068Shshoexer break; 504881e2068Shshoexer case ENCXF_SKIPJACK: 505881e2068Shshoexer sa.sadb_sa_encrypt = SADB_X_EALG_SKIPJACK; 506881e2068Shshoexer break; 507881e2068Shshoexer default: 508375db29dShshoexer warnx("unsupported encryption algorithm %d", 509375db29dShshoexer xfs->encxf->id); 510881e2068Shshoexer } 511881e2068Shshoexer } 51272e25333Shshoexer if (xfs && xfs->compxf) { 51372e25333Shshoexer switch (xfs->compxf->id) { 51472e25333Shshoexer case COMPXF_DEFLATE: 51572e25333Shshoexer sa.sadb_sa_encrypt = SADB_X_CALG_DEFLATE; 51672e25333Shshoexer break; 51772e25333Shshoexer case COMPXF_LZS: 51872e25333Shshoexer sa.sadb_sa_encrypt = SADB_X_CALG_LZS; 51972e25333Shshoexer break; 52072e25333Shshoexer default: 52172e25333Shshoexer warnx("unsupported compression algorithm %d", 52272e25333Shshoexer xfs->compxf->id); 52372e25333Shshoexer } 52472e25333Shshoexer } 525881e2068Shshoexer 526f032086dShshoexer bzero(&sa_src, sizeof(sa_src)); 527f032086dShshoexer sa_src.sadb_address_len = (sizeof(sa_src) + ROUNDUP(ssrc.ss_len)) / 8; 528f032086dShshoexer sa_src.sadb_address_exttype = SADB_EXT_ADDRESS_SRC; 529f032086dShshoexer 530f032086dShshoexer bzero(&sa_dst, sizeof(sa_dst)); 531f032086dShshoexer sa_dst.sadb_address_len = (sizeof(sa_dst) + ROUNDUP(sdst.ss_len)) / 8; 532f032086dShshoexer sa_dst.sadb_address_exttype = SADB_EXT_ADDRESS_DST; 533f032086dShshoexer 53472e25333Shshoexer if (action == SADB_ADD && !authkey && !enckey && satype != 53588a8cceeSmarkus SADB_X_SATYPE_IPCOMP && satype != SADB_X_SATYPE_IPIP) { /* XXX ENCNULL */ 5360813ab45Shshoexer warnx("no key specified"); 5370813ab45Shshoexer return -1; 5380813ab45Shshoexer } 539881e2068Shshoexer if (authkey) { 540881e2068Shshoexer bzero(&sa_authkey, sizeof(sa_authkey)); 541881e2068Shshoexer sa_authkey.sadb_key_len = (sizeof(sa_authkey) + 542881e2068Shshoexer ((authkey->len + 7) / 8) * 8) / 8; 543881e2068Shshoexer sa_authkey.sadb_key_exttype = SADB_EXT_KEY_AUTH; 544881e2068Shshoexer sa_authkey.sadb_key_bits = 8 * authkey->len; 545881e2068Shshoexer } 546881e2068Shshoexer if (enckey) { 547881e2068Shshoexer bzero(&sa_enckey, sizeof(sa_enckey)); 548881e2068Shshoexer sa_enckey.sadb_key_len = (sizeof(sa_enckey) + 549881e2068Shshoexer ((enckey->len + 7) / 8) * 8) / 8; 550881e2068Shshoexer sa_enckey.sadb_key_exttype = SADB_EXT_KEY_ENCRYPT; 551881e2068Shshoexer sa_enckey.sadb_key_bits = 8 * enckey->len; 552f032086dShshoexer } 553f032086dShshoexer 554f032086dShshoexer iov_cnt = 0; 555f032086dShshoexer 556f032086dShshoexer /* header */ 557f032086dShshoexer iov[iov_cnt].iov_base = &smsg; 558f032086dShshoexer iov[iov_cnt].iov_len = sizeof(smsg); 559f032086dShshoexer iov_cnt++; 560f032086dShshoexer 561f032086dShshoexer /* sa */ 562f032086dShshoexer iov[iov_cnt].iov_base = &sa; 563f032086dShshoexer iov[iov_cnt].iov_len = sizeof(sa); 564f032086dShshoexer smsg.sadb_msg_len += sa.sadb_sa_len; 565f032086dShshoexer iov_cnt++; 566f032086dShshoexer 567f032086dShshoexer /* src addr */ 568f032086dShshoexer iov[iov_cnt].iov_base = &sa_src; 569f032086dShshoexer iov[iov_cnt].iov_len = sizeof(sa_src); 570f032086dShshoexer iov_cnt++; 571f032086dShshoexer iov[iov_cnt].iov_base = &ssrc; 572f032086dShshoexer iov[iov_cnt].iov_len = ROUNDUP(ssrc.ss_len); 573f032086dShshoexer smsg.sadb_msg_len += sa_src.sadb_address_len; 574f032086dShshoexer iov_cnt++; 575f032086dShshoexer 576f032086dShshoexer /* dst addr */ 577f032086dShshoexer iov[iov_cnt].iov_base = &sa_dst; 578f032086dShshoexer iov[iov_cnt].iov_len = sizeof(sa_dst); 579f032086dShshoexer iov_cnt++; 580f032086dShshoexer iov[iov_cnt].iov_base = &sdst; 581f032086dShshoexer iov[iov_cnt].iov_len = ROUNDUP(sdst.ss_len); 582f032086dShshoexer smsg.sadb_msg_len += sa_dst.sadb_address_len; 583f032086dShshoexer iov_cnt++; 584f032086dShshoexer 585881e2068Shshoexer if (authkey) { 586881e2068Shshoexer /* authentication key */ 587881e2068Shshoexer iov[iov_cnt].iov_base = &sa_authkey; 588881e2068Shshoexer iov[iov_cnt].iov_len = sizeof(sa_authkey); 589f032086dShshoexer iov_cnt++; 590881e2068Shshoexer iov[iov_cnt].iov_base = authkey->data; 591881e2068Shshoexer iov[iov_cnt].iov_len = ((authkey->len + 7) / 8) * 8; 592881e2068Shshoexer smsg.sadb_msg_len += sa_authkey.sadb_key_len; 593881e2068Shshoexer iov_cnt++; 594881e2068Shshoexer } 595881e2068Shshoexer if (enckey) { 596881e2068Shshoexer /* encryption key */ 597881e2068Shshoexer iov[iov_cnt].iov_base = &sa_enckey; 598881e2068Shshoexer iov[iov_cnt].iov_len = sizeof(sa_enckey); 599881e2068Shshoexer iov_cnt++; 600881e2068Shshoexer iov[iov_cnt].iov_base = enckey->data; 601881e2068Shshoexer iov[iov_cnt].iov_len = ((enckey->len + 7) / 8) * 8; 602881e2068Shshoexer smsg.sadb_msg_len += sa_enckey.sadb_key_len; 603f032086dShshoexer iov_cnt++; 604f032086dShshoexer } 605f032086dShshoexer 606f032086dShshoexer len = smsg.sadb_msg_len * 8; 607f032086dShshoexer if ((n = writev(sd, iov, iov_cnt)) == -1) { 608f032086dShshoexer warn("writev failed"); 609f032086dShshoexer ret = -1; 610f032086dShshoexer } else if (n != len) { 611f032086dShshoexer warnx("short write"); 612f032086dShshoexer ret = -1; 613f032086dShshoexer } 614f032086dShshoexer 615f032086dShshoexer return ret; 616f032086dShshoexer } 617f032086dShshoexer 618f032086dShshoexer static int 6198a87fca6Smsf pfkey_reply(int sd, u_int8_t **datap, ssize_t *lenp) 620f484f2cfShshoexer { 621f484f2cfShshoexer struct sadb_msg hdr; 622f484f2cfShshoexer ssize_t len; 623f484f2cfShshoexer u_int8_t *data; 624f484f2cfShshoexer 625f484f2cfShshoexer if (recv(sd, &hdr, sizeof(hdr), MSG_PEEK) != sizeof(hdr)) { 626f484f2cfShshoexer warnx("short read"); 627f484f2cfShshoexer return -1; 628f484f2cfShshoexer } 629f484f2cfShshoexer len = hdr.sadb_msg_len * PFKEYV2_CHUNK; 630f484f2cfShshoexer if ((data = malloc(len)) == NULL) 631bd828a90Shshoexer err(1, "pfkey_reply: malloc"); 632f484f2cfShshoexer if (read(sd, data, len) != len) { 633f484f2cfShshoexer warn("PF_KEY short read"); 634f484f2cfShshoexer bzero(data, len); 635f484f2cfShshoexer free(data); 636f484f2cfShshoexer return -1; 637f484f2cfShshoexer } 6388a87fca6Smsf if (datap) { 6398a87fca6Smsf *datap = data; 6408a87fca6Smsf if (lenp) 6418a87fca6Smsf *lenp = len; 6428a87fca6Smsf } else { 643f484f2cfShshoexer bzero(data, len); 644f484f2cfShshoexer free(data); 6458a87fca6Smsf } 646011122f7Smarkus if (datap == NULL && hdr.sadb_msg_errno != 0) { 647011122f7Smarkus errno = hdr.sadb_msg_errno; 648011122f7Smarkus warn("PF_KEY failed"); 649011122f7Smarkus return -1; 650011122f7Smarkus } 651f484f2cfShshoexer return 0; 652f484f2cfShshoexer } 653f484f2cfShshoexer 654f484f2cfShshoexer int 6551edc1b9aShshoexer pfkey_parse(struct sadb_msg *msg, struct ipsec_rule *rule) 6561edc1b9aShshoexer { 6571edc1b9aShshoexer struct sadb_ext *ext; 6581edc1b9aShshoexer struct sadb_address *saddr; 6591edc1b9aShshoexer struct sadb_protocol *sproto; 6601edc1b9aShshoexer struct sadb_ident *sident; 6611edc1b9aShshoexer struct sockaddr *sa; 662712e78baShshoexer struct sockaddr_in *sa_in; 6632099bcdfStodd struct sockaddr_in6 *sa_in6; 6641edc1b9aShshoexer int len; 6651edc1b9aShshoexer 6661edc1b9aShshoexer switch (msg->sadb_msg_satype) { 667b26a6cb5Shshoexer case SADB_SATYPE_ESP: 6689182219dSmarkus rule->satype = IPSEC_ESP; 6691edc1b9aShshoexer break; 670b26a6cb5Shshoexer case SADB_SATYPE_AH: 6719182219dSmarkus rule->satype = IPSEC_AH; 6721edc1b9aShshoexer break; 673b26a6cb5Shshoexer case SADB_X_SATYPE_IPCOMP: 6749182219dSmarkus rule->satype = IPSEC_IPCOMP; 675a29da9d0Shshoexer break; 67688a8cceeSmarkus case SADB_X_SATYPE_IPIP: 6779182219dSmarkus rule->satype = IPSEC_IPIP; 67888a8cceeSmarkus break; 6791edc1b9aShshoexer default: 6801edc1b9aShshoexer return (1); 6811edc1b9aShshoexer } 6821edc1b9aShshoexer 6831edc1b9aShshoexer for (ext = (struct sadb_ext *)(msg + 1); 6841edc1b9aShshoexer (size_t)((u_int8_t *)ext - (u_int8_t *)msg) < 685d5d1799eShshoexer msg->sadb_msg_len * PFKEYV2_CHUNK && ext->sadb_ext_len > 0; 6861edc1b9aShshoexer ext = (struct sadb_ext *)((u_int8_t *)ext + 6871edc1b9aShshoexer ext->sadb_ext_len * PFKEYV2_CHUNK)) { 6881edc1b9aShshoexer switch (ext->sadb_ext_type) { 6891edc1b9aShshoexer case SADB_EXT_ADDRESS_SRC: 6901edc1b9aShshoexer saddr = (struct sadb_address *)ext; 6911edc1b9aShshoexer sa = (struct sockaddr *)(saddr + 1); 6921edc1b9aShshoexer 69391f765ddShshoexer rule->local = calloc(1, sizeof(struct ipsec_addr_wrap)); 694d5d1799eShshoexer if (rule->local == NULL) 695a1059f6aStodd err(1, "pfkey_parse: calloc"); 6961edc1b9aShshoexer 6972099bcdfStodd rule->local->af = sa->sa_family; 6981edc1b9aShshoexer switch (sa->sa_family) { 6991edc1b9aShshoexer case AF_INET: 7001edc1b9aShshoexer bcopy(&((struct sockaddr_in *)sa)->sin_addr, 70191f765ddShshoexer &rule->local->address.v4, 702712e78baShshoexer sizeof(struct in_addr)); 7032099bcdfStodd set_ipmask(rule->local, 32); 7042099bcdfStodd break; 7052099bcdfStodd case AF_INET6: 7062099bcdfStodd bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr, 7072099bcdfStodd &rule->local->address.v6, 7082099bcdfStodd sizeof(struct in6_addr)); 7092099bcdfStodd set_ipmask(rule->local, 128); 7101edc1b9aShshoexer break; 7111edc1b9aShshoexer default: 7121edc1b9aShshoexer return (1); 7131edc1b9aShshoexer } 7141edc1b9aShshoexer break; 7151edc1b9aShshoexer 7161edc1b9aShshoexer 7171edc1b9aShshoexer case SADB_EXT_ADDRESS_DST: 7181edc1b9aShshoexer saddr = (struct sadb_address *)ext; 7191edc1b9aShshoexer sa = (struct sockaddr *)(saddr + 1); 7201edc1b9aShshoexer 72191f765ddShshoexer rule->peer = calloc(1, sizeof(struct ipsec_addr_wrap)); 7221edc1b9aShshoexer if (rule->peer == NULL) 723a1059f6aStodd err(1, "pfkey_parse: calloc"); 7241edc1b9aShshoexer 7252099bcdfStodd rule->peer->af = sa->sa_family; 7261edc1b9aShshoexer switch (sa->sa_family) { 7271edc1b9aShshoexer case AF_INET: 7281edc1b9aShshoexer bcopy(&((struct sockaddr_in *)sa)->sin_addr, 729712e78baShshoexer &rule->peer->address.v4, 730712e78baShshoexer sizeof(struct in_addr)); 7312099bcdfStodd set_ipmask(rule->peer, 32); 7322099bcdfStodd break; 7332099bcdfStodd case AF_INET6: 7342099bcdfStodd bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr, 7352099bcdfStodd &rule->peer->address.v6, 7362099bcdfStodd sizeof(struct in6_addr)); 7372099bcdfStodd set_ipmask(rule->peer, 128); 7381edc1b9aShshoexer break; 7391edc1b9aShshoexer default: 7401edc1b9aShshoexer return (1); 7411edc1b9aShshoexer } 7421edc1b9aShshoexer break; 7431edc1b9aShshoexer 7441edc1b9aShshoexer case SADB_EXT_IDENTITY_SRC: 7451edc1b9aShshoexer sident = (struct sadb_ident *)ext; 7461edc1b9aShshoexer len = (sident->sadb_ident_len * sizeof(uint64_t)) - 7471edc1b9aShshoexer sizeof(struct sadb_ident); 7481edc1b9aShshoexer 7499382ed8fShshoexer if (rule->auth == NULL) { 7509382ed8fShshoexer rule->auth = calloc(1, sizeof(struct 7519382ed8fShshoexer ipsec_auth)); 7529382ed8fShshoexer if (rule->auth == NULL) 753bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 7549382ed8fShshoexer } 7559382ed8fShshoexer 756abe65127Shshoexer rule->auth->srcid = calloc(1, len); 757abe65127Shshoexer if (rule->auth->srcid == NULL) 758bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 7591edc1b9aShshoexer 760abe65127Shshoexer strlcpy(rule->auth->srcid, (char *)(sident + 1), len); 7611edc1b9aShshoexer break; 7621edc1b9aShshoexer 7631edc1b9aShshoexer case SADB_EXT_IDENTITY_DST: 7641edc1b9aShshoexer sident = (struct sadb_ident *)ext; 7651edc1b9aShshoexer len = (sident->sadb_ident_len * sizeof(uint64_t)) - 7661edc1b9aShshoexer sizeof(struct sadb_ident); 7671edc1b9aShshoexer 7689382ed8fShshoexer if (rule->auth == NULL) { 7699382ed8fShshoexer rule->auth = calloc(1, sizeof(struct 7709382ed8fShshoexer ipsec_auth)); 7719382ed8fShshoexer if (rule->auth == NULL) 772bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 7739382ed8fShshoexer } 7749382ed8fShshoexer 775abe65127Shshoexer rule->auth->dstid = calloc(1, len); 776abe65127Shshoexer if (rule->auth->dstid == NULL) 777bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 7781edc1b9aShshoexer 779abe65127Shshoexer strlcpy(rule->auth->dstid, (char *)(sident + 1), len); 7801edc1b9aShshoexer break; 7811edc1b9aShshoexer 7821edc1b9aShshoexer case SADB_X_EXT_PROTOCOL: 7839182219dSmarkus sproto = (struct sadb_protocol *)ext; 7849182219dSmarkus if (sproto->sadb_protocol_direction == 0) 7859182219dSmarkus rule->proto = sproto->sadb_protocol_proto; 7861edc1b9aShshoexer break; 7871edc1b9aShshoexer 7881edc1b9aShshoexer case SADB_X_EXT_FLOW_TYPE: 7891edc1b9aShshoexer sproto = (struct sadb_protocol *)ext; 7901edc1b9aShshoexer 7911edc1b9aShshoexer switch (sproto->sadb_protocol_direction) { 7921edc1b9aShshoexer case IPSP_DIRECTION_IN: 7931edc1b9aShshoexer rule->direction = IPSEC_IN; 7941edc1b9aShshoexer break; 7951edc1b9aShshoexer case IPSP_DIRECTION_OUT: 7961edc1b9aShshoexer rule->direction = IPSEC_OUT; 7971edc1b9aShshoexer break; 7981edc1b9aShshoexer default: 7991edc1b9aShshoexer return (1); 8001edc1b9aShshoexer } 8016122c05eShshoexer switch (sproto->sadb_protocol_proto) { 8026122c05eShshoexer case SADB_X_FLOW_TYPE_USE: 8031a3f035aShshoexer rule->flowtype = TYPE_USE; 8046122c05eShshoexer break; 8056122c05eShshoexer case SADB_X_FLOW_TYPE_ACQUIRE: 8061a3f035aShshoexer rule->flowtype = TYPE_ACQUIRE; 8076122c05eShshoexer break; 8086122c05eShshoexer case SADB_X_FLOW_TYPE_REQUIRE: 8091a3f035aShshoexer rule->flowtype = TYPE_REQUIRE; 8106122c05eShshoexer break; 8116122c05eShshoexer case SADB_X_FLOW_TYPE_DENY: 8121a3f035aShshoexer rule->flowtype = TYPE_DENY; 8136122c05eShshoexer break; 8146122c05eShshoexer case SADB_X_FLOW_TYPE_BYPASS: 8151a3f035aShshoexer rule->flowtype = TYPE_BYPASS; 8166122c05eShshoexer break; 8176122c05eShshoexer case SADB_X_FLOW_TYPE_DONTACQ: 8181a3f035aShshoexer rule->flowtype = TYPE_DONTACQ; 8196122c05eShshoexer break; 8206122c05eShshoexer default: 8211a3f035aShshoexer rule->flowtype = TYPE_UNKNOWN; 8226122c05eShshoexer break; 8236122c05eShshoexer } 8241edc1b9aShshoexer break; 8251edc1b9aShshoexer 8261edc1b9aShshoexer case SADB_X_EXT_SRC_FLOW: 8271edc1b9aShshoexer saddr = (struct sadb_address *)ext; 8281edc1b9aShshoexer sa = (struct sockaddr *)(saddr + 1); 8291edc1b9aShshoexer 8301edc1b9aShshoexer if (rule->src == NULL) { 8311edc1b9aShshoexer rule->src = calloc(1, 83291f765ddShshoexer sizeof(struct ipsec_addr_wrap)); 8331edc1b9aShshoexer if (rule->src == NULL) 834bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 8351edc1b9aShshoexer } 8361edc1b9aShshoexer 8372099bcdfStodd rule->src->af = sa->sa_family; 8381edc1b9aShshoexer switch (sa->sa_family) { 8391edc1b9aShshoexer case AF_INET: 8401edc1b9aShshoexer bcopy(&((struct sockaddr_in *)sa)->sin_addr, 841712e78baShshoexer &rule->src->address.v4, 842712e78baShshoexer sizeof(struct in_addr)); 843*57f58d0dSnaddy rule->sport = 844*57f58d0dSnaddy ((struct sockaddr_in *)sa)->sin_port; 8452099bcdfStodd break; 8462099bcdfStodd case AF_INET6: 8472099bcdfStodd bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr, 8482099bcdfStodd &rule->src->address.v6, 8492099bcdfStodd sizeof(struct in6_addr)); 850*57f58d0dSnaddy rule->sport = 851*57f58d0dSnaddy ((struct sockaddr_in6 *)sa)->sin6_port; 8521edc1b9aShshoexer break; 8531edc1b9aShshoexer default: 8541edc1b9aShshoexer return (1); 8551edc1b9aShshoexer } 8561edc1b9aShshoexer break; 8571edc1b9aShshoexer 8581edc1b9aShshoexer case SADB_X_EXT_DST_FLOW: 8591edc1b9aShshoexer saddr = (struct sadb_address *)ext; 8601edc1b9aShshoexer sa = (struct sockaddr *)(saddr + 1); 8611edc1b9aShshoexer 8621edc1b9aShshoexer if (rule->dst == NULL) { 8631edc1b9aShshoexer rule->dst = calloc(1, 86491f765ddShshoexer sizeof(struct ipsec_addr_wrap)); 8651edc1b9aShshoexer if (rule->dst == NULL) 866bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 8671edc1b9aShshoexer } 8681edc1b9aShshoexer 8692099bcdfStodd rule->dst->af = sa->sa_family; 8701edc1b9aShshoexer switch (sa->sa_family) { 8711edc1b9aShshoexer case AF_INET: 8721edc1b9aShshoexer bcopy(&((struct sockaddr_in *)sa)->sin_addr, 873712e78baShshoexer &rule->dst->address.v4, 874712e78baShshoexer sizeof(struct in_addr)); 875*57f58d0dSnaddy rule->dport = 876*57f58d0dSnaddy ((struct sockaddr_in *)sa)->sin_port; 8771edc1b9aShshoexer break; 8782099bcdfStodd case AF_INET6: 8792099bcdfStodd bcopy(&((struct sockaddr_in6 *)sa)->sin6_addr, 8802099bcdfStodd &rule->dst->address.v6, 8812099bcdfStodd sizeof(struct in6_addr)); 882*57f58d0dSnaddy rule->dport = 883*57f58d0dSnaddy ((struct sockaddr_in6 *)sa)->sin6_port; 8842099bcdfStodd break; 8851edc1b9aShshoexer default: 8861edc1b9aShshoexer return (1); 8871edc1b9aShshoexer } 8881edc1b9aShshoexer break; 8891edc1b9aShshoexer 8901edc1b9aShshoexer 8911edc1b9aShshoexer case SADB_X_EXT_SRC_MASK: 8921edc1b9aShshoexer saddr = (struct sadb_address *)ext; 8931edc1b9aShshoexer sa = (struct sockaddr *)(saddr + 1); 8941edc1b9aShshoexer 8951edc1b9aShshoexer if (rule->src == NULL) { 8961edc1b9aShshoexer rule->src = calloc(1, 89791f765ddShshoexer sizeof(struct ipsec_addr_wrap)); 8981edc1b9aShshoexer if (rule->src == NULL) 899bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 9001edc1b9aShshoexer } 9011edc1b9aShshoexer 9022099bcdfStodd rule->src->af = sa->sa_family; 9031edc1b9aShshoexer switch (sa->sa_family) { 9041edc1b9aShshoexer case AF_INET: 905712e78baShshoexer sa_in = (struct sockaddr_in *)sa; 906712e78baShshoexer bcopy(&sa_in->sin_addr, &rule->src->mask.v4, 9071edc1b9aShshoexer sizeof(struct in_addr)); 9082099bcdfStodd break; 9092099bcdfStodd case AF_INET6: 9102099bcdfStodd sa_in6 = (struct sockaddr_in6 *)sa; 9112099bcdfStodd bcopy(&sa_in6->sin6_addr, &rule->src->mask.v6, 9122099bcdfStodd sizeof(struct in6_addr)); 9131edc1b9aShshoexer break; 9141edc1b9aShshoexer 9151edc1b9aShshoexer default: 9161edc1b9aShshoexer return (1); 9171edc1b9aShshoexer } 9181edc1b9aShshoexer break; 9191edc1b9aShshoexer 9201edc1b9aShshoexer case SADB_X_EXT_DST_MASK: 9211edc1b9aShshoexer saddr = (struct sadb_address *)ext; 9221edc1b9aShshoexer sa = (struct sockaddr *)(saddr + 1); 9231edc1b9aShshoexer 9241edc1b9aShshoexer if (rule->dst == NULL) { 9251edc1b9aShshoexer rule->dst = calloc(1, 92691f765ddShshoexer sizeof(struct ipsec_addr_wrap)); 9271edc1b9aShshoexer if (rule->dst == NULL) 928bd828a90Shshoexer err(1, "pfkey_parse: calloc"); 9291edc1b9aShshoexer } 9301edc1b9aShshoexer 9312099bcdfStodd rule->dst->af = sa->sa_family; 9321edc1b9aShshoexer switch (sa->sa_family) { 9331edc1b9aShshoexer case AF_INET: 934712e78baShshoexer sa_in = (struct sockaddr_in *)sa; 935712e78baShshoexer bcopy(&sa_in->sin_addr, &rule->dst->mask.v4, 9361edc1b9aShshoexer sizeof(struct in_addr)); 9371edc1b9aShshoexer break; 9382099bcdfStodd case AF_INET6: 9392099bcdfStodd sa_in6 = (struct sockaddr_in6 *)sa; 9402099bcdfStodd bcopy(&sa_in6->sin6_addr, &rule->dst->mask.v6, 9412099bcdfStodd sizeof(struct in6_addr)); 9422099bcdfStodd break; 9431edc1b9aShshoexer default: 9441edc1b9aShshoexer return (1); 9451edc1b9aShshoexer } 9461edc1b9aShshoexer break; 9471edc1b9aShshoexer 9481edc1b9aShshoexer default: 9491edc1b9aShshoexer return (1); 9501edc1b9aShshoexer } 9511edc1b9aShshoexer } 9521edc1b9aShshoexer 9531edc1b9aShshoexer return (0); 9541edc1b9aShshoexer } 9551edc1b9aShshoexer 9561edc1b9aShshoexer int 957356121f6Shshoexer pfkey_ipsec_establish(int action, struct ipsec_rule *r) 958f484f2cfShshoexer { 95922a29ad6Shshoexer int ret; 96022a29ad6Shshoexer u_int8_t satype, direction; 961f484f2cfShshoexer 962f032086dShshoexer if (r->type == RULE_FLOW) { 9639182219dSmarkus switch (r->satype) { 964f484f2cfShshoexer case IPSEC_ESP: 965f484f2cfShshoexer satype = SADB_SATYPE_ESP; 966f484f2cfShshoexer break; 967f484f2cfShshoexer case IPSEC_AH: 968f484f2cfShshoexer satype = SADB_SATYPE_AH; 969f484f2cfShshoexer break; 97072e25333Shshoexer case IPSEC_IPCOMP: 97172e25333Shshoexer satype = SADB_X_SATYPE_IPCOMP; 97272e25333Shshoexer break; 97388a8cceeSmarkus case IPSEC_IPIP: 97488a8cceeSmarkus satype = SADB_X_SATYPE_IPIP; 97588a8cceeSmarkus break; 976f484f2cfShshoexer default: 977f484f2cfShshoexer return -1; 978f484f2cfShshoexer } 979f484f2cfShshoexer 980f484f2cfShshoexer switch (r->direction) { 981f484f2cfShshoexer case IPSEC_IN: 982f484f2cfShshoexer direction = IPSP_DIRECTION_IN; 983f484f2cfShshoexer break; 984f484f2cfShshoexer case IPSEC_OUT: 985f484f2cfShshoexer direction = IPSP_DIRECTION_OUT; 986f484f2cfShshoexer break; 987f484f2cfShshoexer default: 988f484f2cfShshoexer return -1; 989f484f2cfShshoexer } 990f484f2cfShshoexer 99122a29ad6Shshoexer switch (action) { 99290bd57a7Shshoexer case ACTION_ADD: 993f032086dShshoexer ret = pfkey_flow(fd, satype, SADB_X_ADDFLOW, direction, 994*57f58d0dSnaddy r->proto, r->src, r->sport, r->dst, r->dport, 995*57f58d0dSnaddy r->local, r->peer, r->auth, r->flowtype); 99622a29ad6Shshoexer break; 99790bd57a7Shshoexer case ACTION_DELETE: 99822a29ad6Shshoexer /* No peer for flow deletion. */ 999f032086dShshoexer ret = pfkey_flow(fd, satype, SADB_X_DELFLOW, direction, 1000*57f58d0dSnaddy r->proto, r->src, r->sport, r->dst, r->dport, 1001*57f58d0dSnaddy NULL, NULL, NULL, r->flowtype); 100222a29ad6Shshoexer break; 100322a29ad6Shshoexer default: 100422a29ad6Shshoexer return -1; 100522a29ad6Shshoexer } 1006f032086dShshoexer } else if (r->type == RULE_SA) { 10079182219dSmarkus switch (r->satype) { 1008881e2068Shshoexer case IPSEC_AH: 1009881e2068Shshoexer satype = SADB_SATYPE_AH; 1010881e2068Shshoexer break; 1011881e2068Shshoexer case IPSEC_ESP: 1012881e2068Shshoexer satype = SADB_SATYPE_ESP; 1013881e2068Shshoexer break; 101472e25333Shshoexer case IPSEC_IPCOMP: 101572e25333Shshoexer satype = SADB_X_SATYPE_IPCOMP; 101672e25333Shshoexer break; 1017381a2422Shshoexer case IPSEC_TCPMD5: 1018f032086dShshoexer satype = SADB_X_SATYPE_TCPSIGNATURE; 1019381a2422Shshoexer break; 102088a8cceeSmarkus case IPSEC_IPIP: 102188a8cceeSmarkus satype = SADB_X_SATYPE_IPIP; 102288a8cceeSmarkus break; 1023381a2422Shshoexer default: 1024381a2422Shshoexer return -1; 1025381a2422Shshoexer } 1026f032086dShshoexer switch (action) { 102790bd57a7Shshoexer case ACTION_ADD: 1028f032086dShshoexer ret = pfkey_sa(fd, satype, SADB_ADD, r->spi, 1029a38d220fShshoexer r->src, r->dst, r->xfs, r->authkey, r->enckey, 1030a38d220fShshoexer r->tmode); 1031f032086dShshoexer break; 103290bd57a7Shshoexer case ACTION_DELETE: 1033f032086dShshoexer ret = pfkey_sa(fd, satype, SADB_DELETE, r->spi, 1034a38d220fShshoexer r->src, r->dst, r->xfs, NULL, NULL, r->tmode); 1035f032086dShshoexer break; 1036f032086dShshoexer default: 1037f032086dShshoexer return -1; 1038f032086dShshoexer } 1039f032086dShshoexer } else 1040f032086dShshoexer return -1; 1041f032086dShshoexer 104222a29ad6Shshoexer if (ret < 0) 1043f484f2cfShshoexer return -1; 10448a87fca6Smsf if (pfkey_reply(fd, NULL, NULL) < 0) 1045f484f2cfShshoexer return -1; 1046f484f2cfShshoexer 1047f484f2cfShshoexer return 0; 1048f484f2cfShshoexer } 1049f484f2cfShshoexer 1050f484f2cfShshoexer int 1051f484f2cfShshoexer pfkey_ipsec_flush(void) 1052f484f2cfShshoexer { 1053f484f2cfShshoexer struct sadb_msg smsg; 1054f484f2cfShshoexer struct iovec iov[IOV_CNT]; 1055f484f2cfShshoexer ssize_t n; 1056f484f2cfShshoexer int iov_cnt, len; 1057f484f2cfShshoexer 1058f484f2cfShshoexer bzero(&smsg, sizeof(smsg)); 1059f484f2cfShshoexer smsg.sadb_msg_version = PF_KEY_V2; 1060f484f2cfShshoexer smsg.sadb_msg_seq = sadb_msg_seq++; 1061f484f2cfShshoexer smsg.sadb_msg_pid = getpid(); 1062f484f2cfShshoexer smsg.sadb_msg_len = sizeof(smsg) / 8; 1063f484f2cfShshoexer smsg.sadb_msg_type = SADB_FLUSH; 1064f484f2cfShshoexer smsg.sadb_msg_satype = SADB_SATYPE_UNSPEC; 1065f484f2cfShshoexer 1066f484f2cfShshoexer iov_cnt = 0; 1067f484f2cfShshoexer 1068f484f2cfShshoexer iov[iov_cnt].iov_base = &smsg; 1069f484f2cfShshoexer iov[iov_cnt].iov_len = sizeof(smsg); 1070f484f2cfShshoexer iov_cnt++; 1071f484f2cfShshoexer 1072f484f2cfShshoexer len = smsg.sadb_msg_len * 8; 1073f484f2cfShshoexer if ((n = writev(fd, iov, iov_cnt)) == -1) { 1074f484f2cfShshoexer warn("writev failed"); 1075f484f2cfShshoexer return -1; 1076f484f2cfShshoexer } 1077f484f2cfShshoexer if (n != len) { 1078f484f2cfShshoexer warnx("short write"); 1079f484f2cfShshoexer return -1; 1080f484f2cfShshoexer } 10818a87fca6Smsf if (pfkey_reply(fd, NULL, NULL) < 0) 1082f484f2cfShshoexer return -1; 1083f484f2cfShshoexer 1084f484f2cfShshoexer return 0; 1085f484f2cfShshoexer } 1086f484f2cfShshoexer 10878a87fca6Smsf static int 10888a87fca6Smsf pfkey_promisc(void) 10898a87fca6Smsf { 10908a87fca6Smsf struct sadb_msg msg; 10918a87fca6Smsf 10928a87fca6Smsf memset(&msg, 0, sizeof(msg)); 10938a87fca6Smsf msg.sadb_msg_version = PF_KEY_V2; 10948a87fca6Smsf msg.sadb_msg_seq = sadb_msg_seq++; 10958a87fca6Smsf msg.sadb_msg_pid = getpid(); 10968a87fca6Smsf msg.sadb_msg_len = sizeof(msg) / PFKEYV2_CHUNK; 10978a87fca6Smsf msg.sadb_msg_type = SADB_X_PROMISC; 10988a87fca6Smsf msg.sadb_msg_satype = 1; /* enable */ 10998a87fca6Smsf if (write(fd, &msg, sizeof(msg)) != sizeof(msg)) { 11008a87fca6Smsf warn("pfkey_promisc: write failed"); 11018a87fca6Smsf return -1; 11028a87fca6Smsf } 11038a87fca6Smsf if (pfkey_reply(fd, NULL, NULL) < 0) 11048a87fca6Smsf return -1; 11058a87fca6Smsf return 0; 11068a87fca6Smsf } 11078a87fca6Smsf 11088a87fca6Smsf int 11098a87fca6Smsf pfkey_monitor(int opts) 11108a87fca6Smsf { 11118a87fca6Smsf fd_set *rset; 11128a87fca6Smsf u_int8_t *data; 11138a87fca6Smsf struct sadb_msg *msg; 11148a87fca6Smsf ssize_t len, set_size; 11158a87fca6Smsf int n; 11168a87fca6Smsf 11178a87fca6Smsf if (pfkey_init() < 0) 11188a87fca6Smsf return -1; 11198a87fca6Smsf if (pfkey_promisc() < 0) 11208a87fca6Smsf return -1; 11218a87fca6Smsf 11228a87fca6Smsf set_size = howmany(fd + 1, NFDBITS) * sizeof(fd_mask); 11238a87fca6Smsf if ((rset = malloc(set_size)) == NULL) { 11248a87fca6Smsf warn("malloc"); 11258a87fca6Smsf return -1; 11268a87fca6Smsf } 11278a87fca6Smsf for (;;) { 11288a87fca6Smsf memset(rset, 0, set_size); 11298a87fca6Smsf FD_SET(fd, rset); 11308a87fca6Smsf if ((n = select(fd+1, rset, NULL, NULL, NULL)) < 0) 11318a87fca6Smsf err(2, "select"); 11328a87fca6Smsf if (n == 0) 11338a87fca6Smsf break; 11348a87fca6Smsf if (!FD_ISSET(fd, rset)) 11358a87fca6Smsf continue; 11368a87fca6Smsf if (pfkey_reply(fd, &data, &len) < 0) 11378a87fca6Smsf continue; 11388a87fca6Smsf msg = (struct sadb_msg *)data; 11398a87fca6Smsf if (msg->sadb_msg_type == SADB_X_PROMISC) { 11408a87fca6Smsf /* remove extra header from promisc messages */ 11418a87fca6Smsf if ((msg->sadb_msg_len * PFKEYV2_CHUNK) >= 11428a87fca6Smsf 2 * sizeof(struct sadb_msg)) { 11438a87fca6Smsf msg++; 11448a87fca6Smsf } 11458a87fca6Smsf } 11468a87fca6Smsf pfkey_monitor_sa(msg, opts); 11478a87fca6Smsf if (opts & IPSECCTL_OPT_VERBOSE) 11488a87fca6Smsf pfkey_print_raw(data, len); 11498a87fca6Smsf memset(data, 0, len); 11508a87fca6Smsf free(data); 11518a87fca6Smsf } 11528a87fca6Smsf close(fd); 11538a87fca6Smsf return 0; 11548a87fca6Smsf } 11558a87fca6Smsf 1156f484f2cfShshoexer int 1157f484f2cfShshoexer pfkey_init(void) 1158f484f2cfShshoexer { 1159f484f2cfShshoexer if ((fd = socket(PF_KEY, SOCK_RAW, PF_KEY_V2)) == -1) 1160bd828a90Shshoexer err(1, "pfkey_init: failed to open PF_KEY socket"); 1161f484f2cfShshoexer 1162f484f2cfShshoexer return 0; 1163f484f2cfShshoexer } 1164