1 /* $NetBSD: ip_ipsec_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $ */ 2 3 /* 4 * Copyright (C) 2012 by Darren Reed. 5 * 6 * See the IPFILTER.LICENCE file for details on licencing. 7 * 8 * Simple ISAKMP transparent proxy for in-kernel use. For use with the NAT 9 * code. 10 * 11 * Id: ip_ipsec_pxy.c,v 1.1.1.2 2012/07/22 13:45:19 darrenr Exp 12 * 13 */ 14 15 #include <sys/cdefs.h> 16 __KERNEL_RCSID(1, "$NetBSD: ip_ipsec_pxy.c,v 1.3 2012/07/22 14:27:51 darrenr Exp $"); 17 18 #define IPF_IPSEC_PROXY 19 20 21 /* 22 * IPSec proxy 23 */ 24 typedef struct ipf_ipsec_softc_s { 25 frentry_t ipsec_fr; 26 int ipsec_proxy_init; 27 int ipsec_proxy_ttl; 28 ipftq_t *ipsec_nat_tqe; 29 ipftq_t *ipsec_state_tqe; 30 char ipsec_buffer[1500]; 31 } ipf_ipsec_softc_t; 32 33 34 void *ipf_p_ipsec_soft_create(ipf_main_softc_t *); 35 void ipf_p_ipsec_soft_destroy(ipf_main_softc_t *, void *); 36 int ipf_p_ipsec_soft_init(ipf_main_softc_t *, void *); 37 void ipf_p_ipsec_soft_fini(ipf_main_softc_t *, void *); 38 int ipf_p_ipsec_init(void); 39 void ipf_p_ipsec_fini(void); 40 int ipf_p_ipsec_new(void *, fr_info_t *, ap_session_t *, nat_t *); 41 void ipf_p_ipsec_del(ipf_main_softc_t *, ap_session_t *); 42 int ipf_p_ipsec_inout(void *, fr_info_t *, ap_session_t *, nat_t *); 43 int ipf_p_ipsec_match(fr_info_t *, ap_session_t *, nat_t *); 44 45 46 /* 47 * IPSec application proxy initialization. 48 */ 49 void * 50 ipf_p_ipsec_soft_create(ipf_main_softc_t *softc) 51 { 52 ipf_ipsec_softc_t *softi; 53 54 KMALLOC(softi, ipf_ipsec_softc_t *); 55 if (softi == NULL) 56 return NULL; 57 58 bzero((char *)softi, sizeof(*softi)); 59 softi->ipsec_fr.fr_ref = 1; 60 softi->ipsec_fr.fr_flags = FR_OUTQUE|FR_PASS|FR_QUICK|FR_KEEPSTATE; 61 MUTEX_INIT(&softi->ipsec_fr.fr_lock, "IPsec proxy rule lock"); 62 softi->ipsec_proxy_init = 1; 63 softi->ipsec_proxy_ttl = 60; 64 65 return softi; 66 } 67 68 69 int 70 ipf_p_ipsec_soft_init(ipf_main_softc_t *softc, void *arg) 71 { 72 ipf_ipsec_softc_t *softi = arg; 73 74 softi->ipsec_nat_tqe = ipf_state_add_tq(softc, softi->ipsec_proxy_ttl); 75 if (softi->ipsec_nat_tqe == NULL) 76 return -1; 77 softi->ipsec_state_tqe = ipf_nat_add_tq(softc, softi->ipsec_proxy_ttl); 78 if (softi->ipsec_state_tqe == NULL) { 79 if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) 80 ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); 81 softi->ipsec_nat_tqe = NULL; 82 return -1; 83 } 84 85 softi->ipsec_nat_tqe->ifq_flags |= IFQF_PROXY; 86 softi->ipsec_state_tqe->ifq_flags |= IFQF_PROXY; 87 softi->ipsec_fr.fr_age[0] = softi->ipsec_proxy_ttl; 88 softi->ipsec_fr.fr_age[1] = softi->ipsec_proxy_ttl; 89 return 0; 90 } 91 92 93 void 94 ipf_p_ipsec_soft_fini(ipf_main_softc_t *softc, void *arg) 95 { 96 ipf_ipsec_softc_t *softi = arg; 97 98 if (arg == NULL) 99 return; 100 101 if (softi->ipsec_nat_tqe != NULL) { 102 if (ipf_deletetimeoutqueue(softi->ipsec_nat_tqe) == 0) 103 ipf_freetimeoutqueue(softc, softi->ipsec_nat_tqe); 104 } 105 softi->ipsec_nat_tqe = NULL; 106 if (softi->ipsec_state_tqe != NULL) { 107 if (ipf_deletetimeoutqueue(softi->ipsec_state_tqe) == 0) 108 ipf_freetimeoutqueue(softc, softi->ipsec_state_tqe); 109 } 110 softi->ipsec_state_tqe = NULL; 111 } 112 113 114 void 115 ipf_p_ipsec_soft_destroy(ipf_main_softc_t *softc, void *arg) 116 { 117 ipf_ipsec_softc_t *softi = arg; 118 119 if (softi->ipsec_proxy_init == 1) { 120 MUTEX_DESTROY(&softi->ipsec_fr.fr_lock); 121 softi->ipsec_proxy_init = 0; 122 } 123 124 KFREE(softi); 125 } 126 127 128 /* 129 * Setup for a new IPSEC proxy. 130 */ 131 int 132 ipf_p_ipsec_new(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) 133 { 134 ipf_ipsec_softc_t *softi = arg; 135 ipf_main_softc_t *softc = fin->fin_main_soft; 136 #ifdef USE_MUTEXES 137 ipf_nat_softc_t *softn = softc->ipf_nat_soft; 138 #endif 139 int p, off, dlen, ttl; 140 ipsec_pxy_t *ipsec; 141 ipnat_t *ipn, *np; 142 fr_info_t fi; 143 char *ptr; 144 int size; 145 ip_t *ip; 146 mb_t *m; 147 148 if (fin->fin_v != 4) 149 return -1; 150 151 off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; 152 bzero(softi->ipsec_buffer, sizeof(softi->ipsec_buffer)); 153 ip = fin->fin_ip; 154 m = fin->fin_m; 155 156 dlen = M_LEN(m) - off; 157 if (dlen < 16) 158 return -1; 159 COPYDATA(m, off, MIN(sizeof(softi->ipsec_buffer), dlen), 160 softi->ipsec_buffer); 161 162 if (ipf_nat_outlookup(fin, 0, IPPROTO_ESP, nat->nat_nsrcip, 163 ip->ip_dst) != NULL) 164 return -1; 165 166 np = nat->nat_ptr; 167 size = np->in_size; 168 KMALLOC(ipsec, ipsec_pxy_t *); 169 if (ipsec == NULL) 170 return -1; 171 172 KMALLOCS(ipn, ipnat_t *, size); 173 if (ipn == NULL) { 174 KFREE(ipsec); 175 return -1; 176 } 177 178 aps->aps_data = ipsec; 179 aps->aps_psiz = sizeof(*ipsec); 180 bzero((char *)ipsec, sizeof(*ipsec)); 181 bzero((char *)ipn, size); 182 ipsec->ipsc_rule = ipn; 183 184 /* 185 * Create NAT rule against which the tunnel/transport mapping is 186 * created. This is required because the current NAT rule does not 187 * describe ESP but UDP instead. 188 */ 189 ipn->in_size = size; 190 ttl = IPF_TTLVAL(softi->ipsec_nat_tqe->ifq_ttl); 191 ipn->in_tqehead[0] = ipf_nat_add_tq(softc, ttl); 192 ipn->in_tqehead[1] = ipf_nat_add_tq(softc, ttl); 193 ipn->in_ifps[0] = fin->fin_ifp; 194 ipn->in_apr = NULL; 195 ipn->in_use = 1; 196 ipn->in_hits = 1; 197 ipn->in_snip = ntohl(nat->nat_nsrcaddr); 198 ipn->in_ippip = 1; 199 ipn->in_osrcip = nat->nat_osrcip; 200 ipn->in_osrcmsk = 0xffffffff; 201 ipn->in_nsrcip = nat->nat_nsrcip; 202 ipn->in_nsrcmsk = 0xffffffff; 203 ipn->in_odstip = nat->nat_odstip; 204 ipn->in_odstmsk = 0xffffffff; 205 ipn->in_ndstip = nat->nat_ndstip; 206 ipn->in_ndstmsk = 0xffffffff; 207 ipn->in_redir = NAT_MAP; 208 ipn->in_pr[0] = IPPROTO_ESP; 209 ipn->in_pr[1] = IPPROTO_ESP; 210 ipn->in_flags = (np->in_flags | IPN_PROXYRULE); 211 MUTEX_INIT(&ipn->in_lock, "IPSec proxy NAT rule"); 212 213 ipn->in_namelen = np->in_namelen; 214 bcopy(np->in_names, ipn->in_ifnames, ipn->in_namelen); 215 ipn->in_ifnames[0] = np->in_ifnames[0]; 216 ipn->in_ifnames[1] = np->in_ifnames[1]; 217 218 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 219 fi.fin_fi.fi_p = IPPROTO_ESP; 220 fi.fin_fr = &softi->ipsec_fr; 221 fi.fin_data[0] = 0; 222 fi.fin_data[1] = 0; 223 p = ip->ip_p; 224 ip->ip_p = IPPROTO_ESP; 225 fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); 226 fi.fin_flx |= FI_IGNORE; 227 228 ptr = softi->ipsec_buffer; 229 bcopy(ptr, (char *)ipsec->ipsc_icookie, sizeof(ipsec_cookie_t)); 230 ptr += sizeof(ipsec_cookie_t); 231 bcopy(ptr, (char *)ipsec->ipsc_rcookie, sizeof(ipsec_cookie_t)); 232 /* 233 * The responder cookie should only be non-zero if the initiator 234 * cookie is non-zero. Therefore, it is safe to assume(!) that the 235 * cookies are both set after copying if the responder is non-zero. 236 */ 237 if ((ipsec->ipsc_rcookie[0]|ipsec->ipsc_rcookie[1]) != 0) 238 ipsec->ipsc_rckset = 1; 239 240 MUTEX_ENTER(&softn->ipf_nat_new); 241 ipsec->ipsc_nat = ipf_nat_add(&fi, ipn, &ipsec->ipsc_nat, 242 NAT_SLAVE|SI_WILDP, NAT_OUTBOUND); 243 MUTEX_EXIT(&softn->ipf_nat_new); 244 if (ipsec->ipsc_nat != NULL) { 245 (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); 246 MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); 247 ipf_nat_update(&fi, ipsec->ipsc_nat); 248 MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); 249 250 fi.fin_data[0] = 0; 251 fi.fin_data[1] = 0; 252 (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, SI_WILDP); 253 } 254 ip->ip_p = p & 0xff; 255 return 0; 256 } 257 258 259 /* 260 * For outgoing IKE packets. refresh timeouts for NAT & state entries, if 261 * we can. If they have disappeared, recreate them. 262 */ 263 int 264 ipf_p_ipsec_inout(void *arg, fr_info_t *fin, ap_session_t *aps, nat_t *nat) 265 { 266 ipf_ipsec_softc_t *softi = arg; 267 ipf_main_softc_t *softc = fin->fin_main_soft; 268 ipsec_pxy_t *ipsec; 269 fr_info_t fi; 270 ip_t *ip; 271 int p; 272 273 if ((fin->fin_out == 1) && (nat->nat_dir == NAT_INBOUND)) 274 return 0; 275 276 if ((fin->fin_out == 0) && (nat->nat_dir == NAT_OUTBOUND)) 277 return 0; 278 279 ipsec = aps->aps_data; 280 281 if (ipsec != NULL) { 282 ip = fin->fin_ip; 283 p = ip->ip_p; 284 285 if ((ipsec->ipsc_nat == NULL) || (ipsec->ipsc_state == NULL)) { 286 bcopy((char *)fin, (char *)&fi, sizeof(fi)); 287 fi.fin_fi.fi_p = IPPROTO_ESP; 288 fi.fin_fr = &softi->ipsec_fr; 289 fi.fin_data[0] = 0; 290 fi.fin_data[1] = 0; 291 ip->ip_p = IPPROTO_ESP; 292 fi.fin_flx &= ~(FI_TCPUDP|FI_STATE|FI_FRAG); 293 fi.fin_flx |= FI_IGNORE; 294 } 295 296 /* 297 * Update NAT timeout/create NAT if missing. 298 */ 299 if (ipsec->ipsc_nat != NULL) 300 ipf_queueback(softc->ipf_ticks, 301 &ipsec->ipsc_nat->nat_tqe); 302 else { 303 #ifdef USE_MUTEXES 304 ipf_nat_softc_t *softn = softc->ipf_nat_soft; 305 #endif 306 307 MUTEX_ENTER(&softn->ipf_nat_new); 308 ipsec->ipsc_nat = ipf_nat_add(&fi, ipsec->ipsc_rule, 309 &ipsec->ipsc_nat, 310 NAT_SLAVE|SI_WILDP, 311 nat->nat_dir); 312 MUTEX_EXIT(&softn->ipf_nat_new); 313 if (ipsec->ipsc_nat != NULL) { 314 (void) ipf_nat_proto(&fi, ipsec->ipsc_nat, 0); 315 MUTEX_ENTER(&ipsec->ipsc_nat->nat_lock); 316 ipf_nat_update(&fi, ipsec->ipsc_nat); 317 MUTEX_EXIT(&ipsec->ipsc_nat->nat_lock); 318 } 319 } 320 321 /* 322 * Update state timeout/create state if missing. 323 */ 324 READ_ENTER(&softc->ipf_state); 325 if (ipsec->ipsc_state != NULL) { 326 ipf_queueback(softc->ipf_ticks, 327 &ipsec->ipsc_state->is_sti); 328 ipsec->ipsc_state->is_die = nat->nat_age; 329 RWLOCK_EXIT(&softc->ipf_state); 330 } else { 331 RWLOCK_EXIT(&softc->ipf_state); 332 fi.fin_data[0] = 0; 333 fi.fin_data[1] = 0; 334 (void) ipf_state_add(softc, &fi, &ipsec->ipsc_state, 335 SI_WILDP); 336 } 337 ip->ip_p = p; 338 } 339 return 0; 340 } 341 342 343 /* 344 * This extends the NAT matching to be based on the cookies associated with 345 * a session and found at the front of IKE packets. The cookies are always 346 * in the same order (not reversed depending on packet flow direction as with 347 * UDP/TCP port numbers). 348 */ 349 int 350 ipf_p_ipsec_match(fr_info_t *fin, ap_session_t *aps, nat_t *nat) 351 { 352 ipsec_pxy_t *ipsec; 353 u_32_t cookies[4]; 354 mb_t *m; 355 int off; 356 357 nat = nat; /* LINT */ 358 359 if ((fin->fin_dlen < sizeof(cookies)) || (fin->fin_flx & FI_FRAG)) 360 return -1; 361 362 off = fin->fin_plen - fin->fin_dlen + fin->fin_ipoff; 363 ipsec = aps->aps_data; 364 m = fin->fin_m; 365 COPYDATA(m, off, sizeof(cookies), (char *)cookies); 366 367 if ((cookies[0] != ipsec->ipsc_icookie[0]) || 368 (cookies[1] != ipsec->ipsc_icookie[1])) 369 return -1; 370 371 if (ipsec->ipsc_rckset == 0) { 372 if ((cookies[2]|cookies[3]) == 0) { 373 return 0; 374 } 375 ipsec->ipsc_rckset = 1; 376 ipsec->ipsc_rcookie[0] = cookies[2]; 377 ipsec->ipsc_rcookie[1] = cookies[3]; 378 return 0; 379 } 380 381 if ((cookies[2] != ipsec->ipsc_rcookie[0]) || 382 (cookies[3] != ipsec->ipsc_rcookie[1])) 383 return -1; 384 return 0; 385 } 386 387 388 /* 389 * clean up after ourselves. 390 */ 391 void 392 ipf_p_ipsec_del(ipf_main_softc_t *softc, ap_session_t *aps) 393 { 394 ipsec_pxy_t *ipsec; 395 396 ipsec = aps->aps_data; 397 398 if (ipsec != NULL) { 399 /* 400 * Don't bother changing any of the NAT structure details, 401 * *_del() is on a callback from aps_free(), from nat_delete() 402 */ 403 404 READ_ENTER(&softc->ipf_state); 405 if (ipsec->ipsc_state != NULL) { 406 ipsec->ipsc_state->is_die = softc->ipf_ticks + 1; 407 ipsec->ipsc_state->is_me = NULL; 408 ipf_queuefront(&ipsec->ipsc_state->is_sti); 409 } 410 RWLOCK_EXIT(&softc->ipf_state); 411 412 ipsec->ipsc_state = NULL; 413 ipsec->ipsc_nat = NULL; 414 ipsec->ipsc_rule->in_flags |= IPN_DELETE; 415 ipf_nat_rule_deref(softc, &ipsec->ipsc_rule); 416 } 417 } 418