diff options
Diffstat (limited to 'sbin/isakmpd/pf_encap.c')
-rw-r--r-- | sbin/isakmpd/pf_encap.c | 378 |
1 files changed, 295 insertions, 83 deletions
diff --git a/sbin/isakmpd/pf_encap.c b/sbin/isakmpd/pf_encap.c index 476d55f1840..aaf7f9648d5 100644 --- a/sbin/isakmpd/pf_encap.c +++ b/sbin/isakmpd/pf_encap.c @@ -1,5 +1,5 @@ -/* $OpenBSD: pf_encap.c,v 1.4 1998/12/21 01:02:26 niklas Exp $ */ -/* $EOM: pf_encap.c,v 1.38 1998/12/17 07:56:33 niklas Exp $ */ +/* $OpenBSD: pf_encap.c,v 1.5 1999/02/26 03:48:32 niklas Exp $ */ +/* $EOM: pf_encap.c,v 1.44 1999/02/25 14:03:54 niklas Exp $ */ /* * Copyright (c) 1998 Niklas Hallqvist. All rights reserved. @@ -36,7 +36,10 @@ #include <sys/param.h> #include <sys/mbuf.h> +#include <sys/queue.h> +#include <sys/socket.h> #include <sys/time.h> +#include <net/route.h> #include <netinet/in.h> #include <net/encap.h> #include <netinet/ip_ah.h> @@ -48,8 +51,10 @@ #include <string.h> #include <unistd.h> -#include "app.h" +#include "sysdep.h" + #include "conf.h" +#include "exchange.h" #include "hash.h" #include "ipsec.h" #include "ipsec_num.h" @@ -57,11 +62,38 @@ #include "log.h" #include "pf_encap.h" #include "sa.h" -#include "sysdep.h" #include "timer.h" #include "transport.h" -void pf_encap_request_sa (struct encap_msghdr *); +#define ROUNDUP(a) \ + ((a) > 0 ? (1 + (((a) - 1) | (sizeof (long) - 1))) : sizeof (long)) + +static void pf_encap_deregister_on_demand_connection (in_addr_t, char *); +static char *pf_encap_on_demand_connection (in_addr_t); +static int pf_encap_register_on_demand_connection (in_addr_t, char *); +static void pf_encap_request_sa (struct encap_msghdr *); + +struct on_demand_connection { + /* Connections are linked together. */ + LIST_ENTRY (on_demand_connection) link; + + /* The security gateway's IP-address. */ + in_addr_t dst; + + /* The name of a phase 2 connection associated with the security gateway. */ + char *conn; +}; + +static LIST_HEAD (on_demand_connection_list_list, on_demand_connection) + on_demand_connections; + +static int pf_encap_socket; + +void +pf_encap_init () +{ + LIST_INIT (&on_demand_connections); +} int pf_encap_open () @@ -81,9 +113,37 @@ pf_encap_open () static void pf_encap_expire (struct encap_msghdr *emsg) { - /* XXX not implemented yet. */ + struct sa *sa; + + log_debug (LOG_PF_ENCAP, 20, + "pf_encap_handler: NOTIFY_%s_EXPIRE dst %s spi %x sproto %d", + emsg->em_not_type == NOTIFY_SOFT_EXPIRE ? "SOFT" : "HARD", + inet_ntoa (emsg->em_not_dst), emsg->em_not_spi, + emsg->em_not_sproto); - /* Identify the IPsec SA and rekey that one. */ + /* + * Fin the IPsec SA. The IPsec stack has two SAs for every IKE SA, + * one outgoing and one incoming, we regard expirations for any of + * them as an expiration of the full IKE SA. Likewise, in + * protection suites consisitng of more than one protocol, any + * expired individual IPsec stack SA will be seen as an expiration + * of the full suite. + * + * XXX When anything else than AH and ESP is supported this needs to change. + */ + sa = ipsec_sa_lookup (emsg->em_not_dst.s_addr, emsg->em_not_spi, + emsg->em_not_sproto == IPPROTO_ESP + ? IPSEC_PROTO_IPSEC_ESP : IPSEC_PROTO_IPSEC_AH); + + /* If the SA is already gone, don't do anything. */ + if (!sa) + return; + + /* XXX We need to reestablish the on-demand route here. */ + + /* If this was a hard expire, remove the SA. */ + if (emsg->em_not_type == NOTIFY_HARD_EXPIRE) + sa_free (sa); } static void @@ -96,27 +156,20 @@ pf_encap_notify (struct encap_msghdr *emsg) { case NOTIFY_SOFT_EXPIRE: case NOTIFY_HARD_EXPIRE: - log_debug (LOG_PF_ENCAP, 20, - "pf_encap_handler: NOTIFY_%s_EXPIRE dst %s spi %x sproto %d", - emsg->em_not_type == NOTIFY_SOFT_EXPIRE ? "SOFT" : "HARD", - inet_ntoa (emsg->em_not_dst), emsg->em_not_spi, - emsg->em_not_sproto); pf_encap_expire (emsg); + free (emsg); break; case NOTIFY_REQUEST_SA: - log_debug (LOG_PF_ENCAP, 10, - "pf_encap_handler: SA requested for %s type %d", - inet_ntoa (emsg->em_not_dst), emsg->em_not_satype); pf_encap_request_sa (emsg); break; default: log_print ("pf_encap_handler: unknown notify message type (%d)", emsg->em_not_type); + free (emsg); break; } - free (emsg); } void @@ -164,41 +217,6 @@ pf_encap_handler (int fd) pf_encap_notify (emsg); } -void -pf_encap_request_sa (struct encap_msghdr *emsg) -{ - struct transport *transport; - struct sa *isakmp_sa; - char addr[20]; - in_port_t port; - struct sockaddr *taddr; - int taddr_len; - - /* - * XXX I'd really want some more flexible way to map the IPv4 address in - * this message to a general transport endpoint. For now we hardcode - * the ISAKMP peer to be at the same IP and talking UDP. - */ - port = conf_get_num (inet_ntoa (emsg->em_not_dst), "port"); - if (!port) - port = UDP_DEFAULT_PORT; - snprintf (addr, 20, "%s:%d", inet_ntoa (emsg->em_not_dst), port); - transport = transport_create ("udp", addr); - if (!transport) - { - log_print ("pf_encap_request_sa: " - "transport \"udp %s\" could not be created", - transport, addr); - return; - } - - /* Check if we already have an ISAKMP SA setup. */ - transport->vtbl->get_dst (transport, &taddr, &taddr_len); - isakmp_sa = sa_isakmp_lookup_by_peer (taddr, taddr_len); - if (!isakmp_sa) - /* XXX transport_free (transport) */ ; -} - /* Write a PF_ENCAP request down to the kernel. */ static int pf_encap_write (struct encap_msghdr *em) @@ -209,20 +227,55 @@ pf_encap_write (struct encap_msghdr *em) log_debug_buf (LOG_PF_ENCAP, 30, "pf_encap_write: em", (u_int8_t *)em, em->em_msglen); - n = write (app_socket, em, em->em_msglen); + n = write (pf_encap_socket, em, em->em_msglen); if (n == -1) { - log_error ("write (%d, ...) failed", app_socket); + log_error ("write (%d, ...) failed", pf_encap_socket); return -1; } if ((size_t)n != em->em_msglen) { - log_error ("write (%d, ...) returned prematurely", app_socket); + log_error ("write (%d, ...) returned prematurely", pf_encap_socket); return -1; } return 0; } +static void +pf_encap_finalize_request_sa (void *v_emsg) +{ + struct encap_msghdr *emsg = v_emsg; + + pf_encap_write (emsg); + free (emsg); +} + +static void +pf_encap_request_sa (struct encap_msghdr *emsg) +{ + char *conn; + + log_debug (LOG_PF_ENCAP, 10, "pf_encap_handler: SA requested for %s type %d", + inet_ntoa (emsg->em_not_dst), emsg->em_not_satype); + + /* XXX pf_encap_on_demand_connection should return a list of connections. */ + conn = pf_encap_on_demand_connection (emsg->em_not_dst.s_addr); + if (!conn) + /* Not ours. */ + return; + + /* + * If a connection or an SA for this connections already exists, drop it. + * XXX Perhaps this test is better to have in exchange_establish. + * XXX We are leaking emsg here. + */ + if (exchange_lookup_by_name (conn, 2) || sa_lookup_by_name (conn, 2)) + return; + + if (conn) + exchange_establish (conn, pf_encap_finalize_request_sa, emsg); +} + /* * Read a PF_ENCAP non-notify packet (e.g. an answer to a request of ours) * If we see a notify queue it up as a timeout timing out NOW for the main @@ -247,18 +300,17 @@ pf_encap_read () while (1) { - /* XXX Should we have a static pf_encap_socket instead? */ - n = read (app_socket, buf, EMT_NOTIFY_FLEN); + n = read (pf_encap_socket, buf, EMT_NOTIFY_FLEN); if (n == -1) { - log_error ("read (%d, ...) failed", app_socket); + log_error ("read (%d, ...) failed", pf_encap_socket); goto cleanup; } if ((size_t)n < EMT_GENLEN || (size_t)n != emsg->em_msglen) { log_print ("read (%d, ...) returned short packet (%d bytes)", - app_socket, n); + pf_encap_socket, n); goto cleanup; } @@ -293,6 +345,10 @@ pf_encap_read () return 0; } +/* + * Generate a SPI for protocol PROTO and the destination signified by + * ID & ID_SZ. Stash the SPI size in SZ. + */ u_int8_t * pf_encap_get_spi (size_t *sz, u_int8_t proto, void *id, size_t id_sz) { @@ -325,7 +381,7 @@ pf_encap_get_spi (size_t *sz, u_int8_t proto, void *id, size_t id_sz) memcpy (spi, &emsg->em_gen_spi, *sz); free (emsg); - log_debug_buf (LOG_MISC, 50, "pf_encap_get_spi: spi", spi, *sz); + log_debug_buf (LOG_PF_ENCAP, 50, "pf_encap_get_spi: spi", spi, *sz); return spi; @@ -368,7 +424,7 @@ pf_encap_group_spis (struct sa *sa, struct proto *proto1, struct proto *proto2, goto cleanup; free (emsg); - log_debug (LOG_MISC, 50, "pf_encap_group_spis: done"); + log_debug (LOG_PF_ENCAP, 50, "pf_encap_group_spis: done"); return 0; @@ -454,7 +510,7 @@ pf_encap_set_spi (struct sa *sa, struct proto *proto, int role, int initiator) edx->edx_ivlen = 8; edx->edx_confkeylen = keylen; edx->edx_authkeylen = hashlen; - edx->edx_wnd = 16; + edx->edx_wnd = iproto->replay_window; edx->edx_flags = iproto->auth ? ESP_NEW_FLAG_AUTH : 0; memcpy (edx->edx_data + 8, iproto->keymat[role], keylen); if (iproto->auth) @@ -491,7 +547,7 @@ pf_encap_set_spi (struct sa *sa, struct proto *proto, int role, int initiator) } amx->amx_keylen = hashlen; - amx->amx_wnd = 16; + amx->amx_wnd = iproto->replay_window; memcpy (amx->amx_key, iproto->keymat[role], hashlen); break; @@ -586,7 +642,7 @@ pf_encap_delete_spi (struct sa *sa, struct proto *proto, int initiator) goto cleanup; free (emsg); - log_debug (LOG_MISC, 50, "pf_encap_delete_spi: done"); + log_debug (LOG_PF_ENCAP, 50, "pf_encap_delete_spi: done"); return 0; @@ -596,41 +652,54 @@ pf_encap_delete_spi (struct sa *sa, struct proto *proto, int initiator) return -1; } -/* Enable a flow. */ +/* Enable a flow given a SA. */ int -pf_encap_enable_spi (struct sa *sa, int initiator) +pf_encap_enable_sa (struct sa *sa, int initiator) { struct ipsec_sa *isa = sa->data; - struct encap_msghdr *emsg = 0; - struct sockaddr *dst, *src; - int dstlen, srclen; + struct sockaddr *dst; + int dstlen; struct proto *proto = TAILQ_FIRST (&sa->protos); + sa->transport->vtbl->get_dst (sa->transport, &dst, &dstlen); + + /* XXX Check why byte ordering is backwards. */ + return pf_encap_enable_spi (htonl (isa->src_net), htonl (isa->src_mask), + htonl (isa->dst_net), htonl (isa->dst_mask), + proto->spi[!initiator], proto->proto, + ((struct sockaddr_in *)dst)->sin_addr.s_addr); +} + +/* Enable a flow. */ +int +pf_encap_enable_spi (in_addr_t laddr, in_addr_t lmask, in_addr_t raddr, + in_addr_t rmask, u_int8_t *spi, u_int8_t proto, + in_addr_t dst) +{ + struct encap_msghdr *emsg = 0; + emsg = calloc (1, EMT_ENABLESPI_FLEN); if (!emsg) + /* XXX Log? */ return -1; emsg->em_msglen = EMT_ENABLESPI_FLEN; emsg->em_type = EMT_ENABLESPI; - sa->transport->vtbl->get_dst (sa->transport, &dst, &dstlen); - sa->transport->vtbl->get_src (sa->transport, &src, &srclen); - - memcpy (&emsg->em_ena_spi, proto->spi[!initiator], sizeof emsg->em_ena_spi); - emsg->em_ena_dst = ((struct sockaddr_in *)dst)->sin_addr; + memcpy (&emsg->em_ena_spi, spi, sizeof emsg->em_ena_spi); + emsg->em_ena_dst.s_addr = dst; log_debug (LOG_PF_ENCAP, 50, "pf_encap_enable_spi: src %x %x dst %x %x", - isa->src_net, isa->src_mask, isa->dst_net, isa->dst_mask); - /* XXX Check why byte ordering is backwards. */ - emsg->em_ena_isrc.s_addr = htonl (isa->src_net); - emsg->em_ena_ismask.s_addr = htonl (isa->src_mask); - emsg->em_ena_idst.s_addr = htonl (isa->dst_net); - emsg->em_ena_idmask.s_addr = htonl (isa->dst_mask); + laddr, lmask, raddr, rmask); + emsg->em_ena_isrc.s_addr = laddr; + emsg->em_ena_ismask.s_addr = lmask; + emsg->em_ena_idst.s_addr = raddr; + emsg->em_ena_idmask.s_addr = rmask; emsg->em_ena_flags = ENABLE_FLAG_REPLACE; /* XXX What if IPCOMP etc. comes along? */ emsg->em_ena_sproto - = proto->proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; + = proto == IPSEC_PROTO_IPSEC_ESP ? IPPROTO_ESP : IPPROTO_AH; if (pf_encap_write (emsg)) goto cleanup; @@ -650,9 +719,7 @@ pf_encap_enable_spi (struct sa *sa, int initiator) goto cleanup; } free (emsg); - - log_debug (LOG_MISC, 50, "pf_encap_enable_spi: done"); - + log_debug (LOG_PF_ENCAP, 50, "pf_encap_enable_spi: done"); return 0; cleanup: @@ -660,3 +727,148 @@ pf_encap_enable_spi (struct sa *sa, int initiator) free (emsg); return -1; } + +/* + * Establish an encap route. + * XXX We should add delete support here a la ipsecadm/xf_flow.c the day + * we want to clean up after us. + */ +int +pf_encap_route (in_addr_t laddr, in_addr_t lmask, in_addr_t raddr, + in_addr_t rmask, u_int8_t spi, in_addr_t dst, char *conn) +{ + int s = -1; + int off, err; + struct sockaddr_encap *ddst, *msk, *gw; + struct rt_msghdr *rtmsg = 0; + + err = pf_encap_register_on_demand_connection (dst, conn); + if (err) + return -1; + + rtmsg = calloc (1, EMT_ENABLESPI_FLEN); + if (!rtmsg) + /* XXX Log? */ + goto fail; + + s = socket (PF_ROUTE, SOCK_RAW, AF_UNSPEC); + if (s == -1) + { + log_error ("pf_encap_route: " + "socket(PF_ROUTE, SOCK_RAW, AF_UNSPEC) failed"); + goto fail; + } + + off = sizeof *rtmsg; + ddst = (struct sockaddr_encap *)((char *)rtmsg + off); + off = ROUNDUP (off + SENT_IP4_LEN); + gw = (struct sockaddr_encap *)((char *)rtmsg + off); + off = ROUNDUP (off + SENT_IPSP_LEN); + msk = (struct sockaddr_encap *)((char *)rtmsg + off); + bzero (rtmsg, off + SENT_IP4_LEN); + + rtmsg->rtm_version = RTM_VERSION; + rtmsg->rtm_type = RTM_ADD; + rtmsg->rtm_index = 0; + rtmsg->rtm_pid = getpid (); + rtmsg->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; + rtmsg->rtm_errno = 0; + rtmsg->rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC; + rtmsg->rtm_inits = 0; + + ddst->sen_len = SENT_IP4_LEN; + ddst->sen_family = AF_ENCAP; + ddst->sen_type = SENT_IP4; + ddst->sen_ip_src.s_addr = laddr & lmask; + ddst->sen_ip_dst.s_addr = raddr & rmask; + ddst->sen_proto = ddst->sen_sport = ddst->sen_dport = 0; + + gw->sen_len = SENT_IPSP_LEN; + gw->sen_family = AF_ENCAP; + gw->sen_type = SENT_IPSP; + gw->sen_ipsp_dst.s_addr = dst; + gw->sen_ipsp_spi = htonl(spi); + gw->sen_ipsp_sproto = 0; /* XXX Correct? */ + + msk->sen_len = SENT_IP4_LEN; + msk->sen_family = AF_ENCAP; + msk->sen_type = SENT_IP4; + msk->sen_ip_src.s_addr = lmask; + msk->sen_ip_dst.s_addr = rmask; + + rtmsg->rtm_msglen = off + msk->sen_len; + + if (write(s, (caddr_t)rtmsg, rtmsg->rtm_msglen) == -1) + { + log_error("write(%d, ...) failed", s); + goto fail; + } + + /* XXX Local packet route should be setup here. */ + + /* + * Setup a reverse map, address -> name, we can use when getting SA + * requests back from the stack. + */ + + close (s); + free (rtmsg); + return 0; + + fail: + if (s != -1) + close (s); + if (rtmsg) + free (rtmsg); + pf_encap_deregister_on_demand_connection (dst, conn); + return -1; +} + +/* + * Register an IP-address to Phase 2 connection name mapping. + */ +static int +pf_encap_register_on_demand_connection (in_addr_t dst, char *conn) +{ + struct on_demand_connection *node; + + node = malloc (sizeof *node); + if (!node) + return -1; + node->dst = dst; + node->conn = conn; + LIST_INSERT_HEAD (&on_demand_connections, node, link); + return 0; +} + +/* + * Remove an IP-address to Phase 2 connection name mapping. + */ +static void +pf_encap_deregister_on_demand_connection (in_addr_t dst, char *conn) +{ + struct on_demand_connection *node; + + for (node = LIST_FIRST (&on_demand_connections); node; + node = LIST_NEXT (node, link)) + if (dst == node->dst && conn == node->conn) + { + LIST_REMOVE (node, link); + } +} + +/* + * Return a phase 2 connection name given a security gateway's IP-address. + * XXX Does only handle 1-1 mappings so far. + */ +static char * +pf_encap_on_demand_connection (in_addr_t dst) +{ + struct on_demand_connection *node; + + for (node = LIST_FIRST (&on_demand_connections); node; + node = LIST_NEXT (node, link)) + if (dst == node->dst) + return node->conn; + return 0; +} |