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