--- old/iked/ike_conf.c Fri Mar 27 12:09:50 2009 +++ new/iked/ike_conf.c Fri Mar 27 12:09:50 2009 @@ -3266,6 +3266,7 @@ }; const int BITS = 8; + (void) memset(¶m, 0, sizeof (param)); /* * param fields assigned here: * seq, samode, (reqid,) ul_proto, @@ -4282,10 +4283,22 @@ memcpy(ss, config_ipaddr->a.ipaddr, SOCKADDR_LEN(config_ipaddr->a.ipaddr)); addr = (struct sockaddr *)ss; +#ifdef sun + /* + * XXX KEBE SAYS we need the port from the "policy". This, of + * course, goes to hell when we introduce tunnel- mode into + * the mix, and config_ipaddr is != actual_addr. The IKEv1 + * callers of this are restricted to ikev1/pfkey.c, and the + * IKEv2 callers are restricted to ikev2_child.c. Hopefully + * those callers can account for tunnel-mode or + * transport-mode. :) + */ +#else if (!set_port(addr, extract_port(actual_addr))) { plog(PLOG_INTERR, PLOGLOC, 0, "set_port failed\n"); return NULL; } +#endif break; case RCT_ADDR_MACRO: @@ -4309,12 +4322,23 @@ SOCKADDR_LEN(addrlist->a.ipaddr)); rcs_free_addrlist(addrlist); addr = (struct sockaddr *)ss; +#ifdef sun + /* + * XXX KEBE SAYS we need the port from the "policy". This, of + * course, goes to hell when we introduce tunnel- mode into + * the mix, and config_ipaddr is != actual_addr. The IKEv1 + * callers of this are restricted to ikev1/pfkey.c, and the + * IKEv2 callers are restricted to ikev2_child.c. Hopefully + * those callers can account for tunnel-mode or + * transport-mode. :) + */ +#else if (!set_port(addr, extract_port(actual_addr))) { plog(PLOG_INTERR, PLOGLOC, 0, "set_port failed\n"); return NULL; } +#endif break; - default: plog(PLOG_INTERR, PLOGLOC, 0, "my_sa_ipaddr or peers_sa_ipaddr is " --- old/iked/ike_pfkey.c Fri Mar 27 12:09:51 2009 +++ new/iked/ike_pfkey.c Fri Mar 27 12:09:51 2009 @@ -69,6 +69,7 @@ static int sadb_getspi(struct rcpfk_msg *); static int sadb_acquire_error(struct rcpfk_msg *); +static int sadb_inverse_acquire(struct rcpfk_msg *); static int sadb_update(struct rcpfk_msg *); static int sadb_get(struct rcpfk_msg *); static int sadb_add(struct rcpfk_msg *); @@ -89,6 +90,9 @@ sadb_add, sadb_delete, sadb_get, +#ifdef sun + null_proc, +#endif /* sun/OpenSolaris */ }; /* sadb_responder_request_method for use when receiving IKE_SA_INIT packet */ @@ -99,6 +103,9 @@ sadb_add, sadb_delete, sadb_get, +#ifdef sun + sadb_inverse_acquire, +#endif /* sun/OpenSolaris */ }; /* sadb_rekey_request_method for use when rekeying soft-expired IPsec SA */ @@ -114,6 +121,9 @@ /* sadb_null_method for informational exchange SA */ struct sadb_request_method sadb_null_method = { null_proc, null_proc, null_proc, null_proc, null_proc, null_proc +#ifdef sun + , null_proc +#endif /* sun/OpenSolaris */ }; /* sadb_force_initiate_method for use with isakmp_force_initiate() */ @@ -124,12 +134,15 @@ sadb_add, sadb_delete, sadb_get, +#ifdef sun + null_proc, +#endif /* sun/OpenSolaris */ }; static SADB_LIST_HEAD(sadb_request_list_head, sadb_request) sadb_request_list_head; static int pfkey_socket; -static uint32_t sadb_msg_seq; +static uint32_t my_sadb_seqnum; static int sadb_getspi_callback(struct rcpfk_msg *param); static int sadb_update_callback(struct rcpfk_msg *param); @@ -166,6 +179,8 @@ { struct rcpfk_msg param; + (void) memset(¶m, 0, sizeof (param)); + SADB_LIST_INIT(&sadb_request_list_head); if (debug_pfkey) return 0; @@ -207,7 +222,7 @@ uint32_t sadb_new_seq(void) { - return ++sadb_msg_seq; + return ++my_sadb_seqnum; } static void @@ -229,6 +244,7 @@ { struct rcpfk_msg rcpfk_param; + (void) memset(&rcpfk_param, 0, sizeof (rcpfk_param)); rcpfk_param.so = pfkey_socket; rcpfk_param.flags = 0; if (rcpfk_handler(&rcpfk_param) != 0) { @@ -298,15 +314,45 @@ return err; } +#ifdef sun +static int +sadb_inverse_acquire(struct rcpfk_msg *param) +{ + int err; + + TRACE((PLOGLOC, "sadb_inverse_acquire: seq=%d\n", param->seq)); + + /* param: so, satype, seq, eno */ + param->so = pfkey_socket; + param->flags = 0; + err = rcpfk_send_inverse_acquire(param); + if (err) + log_rcpfk_error("sadb_inverse_acquire", param); + return (err); +} +#endif /* sun/OpenSolaris */ + +#define sa2str_chk(sa) (((sa) == NULL) ? "" : rcs_sa2str(sa)) + static void sadb_log_add(char *op, struct rcpfk_msg *param) { if (param->satype == RCT_SATYPE_ESP) { INFO((PLOGLOC, - "%s ul_proto=%d src=%s dst=%s satype=%s samode=%s spi=0x%08x authtype=%s enctype=%s lifetime soft time=%" + "%s ul_proto=%d src=%s dst=%s " +#ifdef sun + "isrc=%s idst=%s nlc=%s nrm=%s" +#endif + "satype=%s samode=%s spi=0x%08x authtype=%s enctype=%s lifetime soft time=%" PRIu64 " bytes=%" PRIu64 " hard time=%" PRIu64 " bytes=%" PRIu64 "\n", op, param->ul_proto, rcs_sa2str(param->sa_src), - rcs_sa2str(param->sa_dst), rct2str(param->satype), + rcs_sa2str(param->sa_dst), +#ifdef sun + sa2str_chk(param->sa_isrc), sa2str_chk(param->sa_idst), + sa2str_chk(param->sa_natlocal), + sa2str_chk(param->sa_natremote), +#endif + rct2str(param->satype), rct2str(param->samode), ntohl(param->spi), rct2str(param->authtype), rct2str(param->enctype), param->lft_soft_time, param->lft_soft_bytes, @@ -313,10 +359,20 @@ param->lft_hard_time, param->lft_hard_bytes)); } else { INFO((PLOGLOC, - "%s ul_proto=%d src=%s dst=%s satype=%s samode=%s spi=0x%08x authtype=%s lifetime soft time=%" + "%s ul_proto=%d src=%s dst=%s " +#ifdef sun + "isrc=%s idst=%s nlc=%s nrm=%s" +#endif + "satype=%s samode=%s spi=0x%08x authtype=%s lifetime soft time=%" PRIu64 " bytes=%" PRIu64 " hard time=%" PRIu64 " bytes=%" PRIu64 "\n", op, param->ul_proto, rcs_sa2str(param->sa_src), - rcs_sa2str(param->sa_dst), rct2str(param->satype), + rcs_sa2str(param->sa_dst), +#ifdef sun + sa2str_chk(param->sa_isrc), sa2str_chk(param->sa_idst), + sa2str_chk(param->sa_natlocal), + sa2str_chk(param->sa_natremote), +#endif + rct2str(param->satype), rct2str(param->samode), ntohl(param->spi), rct2str(param->authtype), param->lft_soft_time, param->lft_soft_bytes, param->lft_hard_time, @@ -612,6 +668,8 @@ static int sadb_acquire_callback(struct rcpfk_msg *param) { + struct sadb_request *req; + invacq_t *invacq; /* param: seq, satype, sa_src, sa_dst, samode, selid */ /* address(P)??? pid?? identity??? proposal??? */ @@ -620,16 +678,20 @@ param->seq, param->satype, rcs_sa2str(param->sa_src), rcs_sa2str(param->sa_dst), param->samode, param->slid)); - if (sadb_find_by_seq(param->seq)) { - TRACE((PLOGLOC, "duplicate seq %u\n", param->seq)); - return 0; + req = sadb_find_by_seq(param->seq); + if (req != NULL) { + /* Inverse-ACQUIRE. */ + invacq = (invacq_t *)req->sa; + invacq->answer = param; + return (invacq->receiver(invacq)); } - isakmp_initiate(&sadb_initiator_request_method, - param->slid, - param->seq, param->satype, - param->sa_src, param->sa_dst, - param->sa2_src); + if (param->eno != 0) { + /* inverse-ACQUIRE error with no outstanding request. Drop. */ + return (-1); + } + + isakmp_initiate(&sadb_initiator_request_method, param); return 0; } --- old/iked/ike_pfkey.h Fri Mar 27 12:09:51 2009 +++ new/iked/ike_pfkey.h Fri Mar 27 12:09:51 2009 @@ -82,6 +82,9 @@ int (*add_outbound) (); int (*delete_sa) (); int (*get) (); +#ifdef sun + int (*inverse_acquire)(); +#endif /* sun/OpenSolaris */ }; struct sadb_response_method { @@ -134,3 +137,35 @@ uint32_t, void *); extern void sadb_request_finish(struct sadb_request *); +#ifdef sun +#ifndef _INVACQ_T +#define _INVACQ_T /* Guard against double-include. */ +typedef struct invacq_s { + /* IKEv1 data */ + struct ph2handle *iph2; /* Referenced */ + + /* IKEv2 data */ + struct ikev2_sa *ikev2_sa; /* Referenced */ + struct sockaddr *local; /* Alloced/copied */ + struct sockaddr *remote; /* Alloced/copied */ + struct ikev2_payload_header *sa_payload;/* Alloced/copied */ + struct ikev2_payload_header *ts_i; /* Alloced/copied */ + struct ikev2_payload_header *ts_r; /* Alloced/copied */ + struct ikev2_payload_header *cfg; /* Alloced/copied */ + rc_vchar_t *g_i; /* Alloced/copied */ + rc_vchar_t *n_i; /* Alloced/copied */ + struct ikev2_child_param *child_param; /* Alloced/copied */ + struct ikev2_child_sa *old_child_sa; /* Referenced. */ + uint32_t message_id; /* Copied */ + boolean_t is_createchild; /* Copied */ + rc_vchar_t *packet; + + /* inverse-ACQUIRE data. */ + struct rcpfk_msg *answer; + int (*receiver)(struct invacq_s *); + struct sadb_request request; +} invacq_t; +#endif +extern int extract_extended_acquire(struct rcpfk_msg *, struct rcf_selector **, + struct rcf_remote **); +#endif --- old/iked/ike_sa.c Fri Mar 27 12:09:52 2009 +++ new/iked/ike_sa.c Fri Mar 27 12:09:52 2009 @@ -297,6 +297,7 @@ { struct rcpfk_msg param; + (void) memset(¶m, 0, sizeof (param)); param.satype = RCT_SATYPE_ESP; /* XXX */ param.seq = child_sa->sadb_request.seqno; param.eno = err; --- old/iked/ikev1/handler.c Fri Mar 27 12:09:52 2009 +++ new/iked/ikev1/handler.c Fri Mar 27 12:09:52 2009 @@ -275,8 +275,10 @@ EVT_PUSH(iph1->local, iph1->remote, EVTT_PHASE1_DOWN, NULL); #ifdef ENABLE_NATT +#ifndef sun /* XXX KEBE SAYS kernel does this in OpenSolaris */ if (iph1->natt_flags & NAT_KA_QUEUED) natt_keepalive_remove (iph1->local, iph1->remote); +#endif if (iph1->natt_options) { racoon_free(iph1->natt_options); @@ -470,8 +472,14 @@ struct ph2handle *p; LIST_FOREACH(p, &ph2tree, chain) { +#ifdef sun + if ((p->selector->sl_index == selector->sl_index || + rc_vmemcmp(p->selector->sl_index, + selector->sl_index) == 0) && +#else if (rc_vmemcmp(p->selector->sl_index, selector->sl_index) == 0 && +#endif CMPSADDR(src, p->src) == 0 && CMPSADDR(dst, p->dst) == 0) return p; @@ -629,6 +637,16 @@ racoon_free(iph2->dst_id); iph2->dst_id = NULL; } +#ifdef ENABLE_NATT + if (iph2->natoa_src) { + racoon_free(iph2->natoa_src); + iph2->natoa_src = NULL; + } + if (iph2->natoa_dst) { + racoon_free(iph2->natoa_dst); + iph2->natoa_dst = NULL; + } +#endif if (iph2->proposal) { flushsaprop(iph2->proposal); @@ -1488,6 +1506,8 @@ int satype; int retval; + (void) memset(¶m, 0, sizeof (param)); + switch (proto) { case IPSECDOI_PROTO_IPSEC_AH: satype = RCT_SATYPE_AH; --- old/iked/ikev1/handler.h Fri Mar 27 12:09:53 2009 +++ new/iked/ikev1/handler.h Fri Mar 27 12:09:53 2009 @@ -232,7 +232,12 @@ */ struct sockaddr *src_id; struct sockaddr *dst_id; +#ifdef ENABLE_NATT + struct sockaddr *natoa_src; /* peer's view of my address */ + struct sockaddr *natoa_dst; /* peer's view of his address */ +#endif + struct sadb_request sadb_request; #if 0 uint32_t spid; /* policy id by kernel */ --- old/iked/ikev1/ikev1.c Fri Mar 27 12:09:53 2009 +++ new/iked/ikev1/ikev1.c Fri Mar 27 12:09:53 2009 @@ -56,10 +56,16 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ +#define IPSEC_ULPROTO_ANY 0 +#endif + #include "racoon.h" #include "isakmp.h" @@ -309,7 +315,11 @@ * (FIXME: should we allow it? E.g. when the NAT gw * is rebooted?) */ +#ifdef sun + iph1->natt_flags |= NAT_PORTS_CHANGED; +#else iph1->natt_flags |= NAT_PORTS_CHANGED | NAT_ADD_NON_ESP_MARKER; +#endif } #endif @@ -835,6 +845,12 @@ if (iph2->scr) SCHED_KILL(iph2->scr); +#ifdef sun + /* Bail now to await inverse-ACQUIRE response. */ + if (iph2->status == PHASE2ST_START && iph2->side == RESPONDER) + return (0); +#endif /* sun/OpenSolaris */ + /* send */ plog(PLOG_DEBUG, PLOGLOC, NULL, "===\n"); if ((ph2exchange[etypesw2(isakmp->etype)] @@ -1136,6 +1152,71 @@ return; } +#ifdef sun +static int +isakmp_ph2_inv_acquire(invacq_t *invacq) +{ + struct ph2handle *iph2 = invacq->iph2; + struct rcpfk_msg *param = invacq->answer; + struct isakmp *isakmp = (struct isakmp *)iph2->msg1->v; + + sadb_request_finish(&invacq->request); + free(invacq); + + /* + * Initialize iph2->selector, iph2->proposal, and iph2-> with the results of an + * inverse-ACQUIRE. + * + * XXX KEBE SAYS -- We need a way to figure out a p2_pfs equivalent + * for racoon2. We store this in Phase I/PAD state in in.iked. + */ + + /* Then send the Quick Mode reply. */ + /* assert(iph2->status == PHASE2ST_STATUS2); */ + + /* change status of isakmp status entry */ + iph2->status = PHASE2ST_STATUS2; + + if (extract_extended_acquire(param, &iph2->selector, NULL) != 0) { + /* XXX KEBE SAYS MORE ERROR HANDLING? */ + return (-1); + } + + /* XXX KEBE SAYS FILL ME IN XXX */ + + if (set_proposal_from_policy(iph2, iph2->ph1->rmconf, + iph2->selector->pl) != 0) { + /* XXX KEBE SAYS MORE ERROR HANDLING? */ + return (-1); + } + + if (ipsecdoi_selectph2proposal(iph2) < 0) { + /* XXX KEBE SAYS MORE ERROR HANDLING? PROPER RETURN? */ + isakmp_info_send_n1(iph2->ph1, ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN, + NULL); + return (-1); + } + + /* The following was moved here from quick_main(). */ + plog(PLOG_DEBUG, PLOGLOC, NULL, "===\n"); + if ((ph2exchange[etypesw2(isakmp->etype)] + [iph2->side] + [iph2->status]) (iph2, iph2->msg1) < 0) { + plog(PLOG_PROTOERR, PLOGLOC, 0, + "failed to process packet.\n"); + /* don't release handler */ + return -1; + } +#ifdef ENABLE_STATS + gettimeofday(&end, NULL); + syslog(LOG_NOTICE, "%s(%s): %8.6f", + "phase2", + s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status), + timedelta(&start, &end)); +#endif +} +#endif + /* new negotiation of phase 2 for responder */ static int isakmp_ph2begin_r(struct ph1handle *iph1, rc_vchar_t *msg) @@ -1206,6 +1287,7 @@ gettimeofday(&start, NULL); #endif + error = (ph2exchange[etypesw2(ISAKMP_ETYPE_QUICK)] [iph2->side] [iph2->status]) (iph2, msg); @@ -1224,6 +1306,33 @@ return -1; } +#ifdef sun + /* XXX KEBE ASKS - how do you insert inverse-ACQUIRE here? */ + + /* Assume iph2->msg1 contains a copy of "msg" we passed-in. */ + { + invacq_t *invacq = malloc(sizeof (*invacq)); + uint32_t newseq = sadb_new_seq(); + + /* + * Use newseq to avoid using iph2's, which already has a + * record via a previous sadb_request_initalize() call. + */ + + if (invacq == NULL) + return (-1); + + invacq->iph2 = iph2; + sadb_request_initialize(&invacq->request, + NULL /* KEBE - reqmethod */, NULL /* KEBE - respmethod */, + newseq, invacq); + invacq->receiver = isakmp_ph2_inv_acquire; + + /* Okay, now we send the inverse-ACQUIRE itself. */ + /* XXX KEBE SAYS CODE ME */ + ikev1_send_inverse_acquire(iph2, newseq); + } +#else /* send */ plog(PLOG_DEBUG, PLOGLOC, NULL, "===\n"); if ((ph2exchange[etypesw2(isakmp->etype)] @@ -1241,6 +1350,7 @@ s_isakmp_state(ISAKMP_ETYPE_QUICK, iph2->side, iph2->status), timedelta(&start, &end)); #endif +#endif /* sun/OpenSolaris */ return 0; } --- old/iked/ikev1/ikev1_natt.c Fri Mar 27 12:09:53 2009 +++ new/iked/ikev1/ikev1_natt.c Fri Mar 27 12:09:53 2009 @@ -46,6 +46,11 @@ #include #include +#ifdef sun /* XXX KEBE SAYS OpenSolaris extras */ +#define UDP_ENCAP_ESPINUDP_NON_IKE 1 +#define UDP_ENCAP_ESPINUDP 2 +#endif + #include "racoon.h" #include "var.h" @@ -296,16 +301,27 @@ if (!(iph1->natt_flags && NAT_DETECTED)) return; if (!iph1->natt_options->float_port) { +#ifndef sun /* XXX KEBE SAYS OpenSolaris does keepalives in-kernel. */ /* Drafts 00 / 01, just schedule keepalive */ natt_keepalive_add_ph1(iph1); +#endif return; } set_port(iph1->local, iph1->natt_options->float_port); set_port(iph1->remote, iph1->natt_options->float_port); - iph1->natt_flags |= NAT_PORTS_CHANGED | NAT_ADD_NON_ESP_MARKER; + iph1->natt_flags |= NAT_PORTS_CHANGED; + +#ifndef sun + /* + * XXX KEBE SAYS OpenSolaris does keepalives in-kernel. + * Also, we have in-kernel zero-spi addition. + */ + iph1->natt_flags |= NAT_ADD_NON_ESP_MARKER; + natt_keepalive_add_ph1(iph1); +#endif } void @@ -326,6 +342,7 @@ iph1->natt_flags |= NAT_ANNOUNCED; } +#ifndef sun /* XXX KEBE SAYS OpenSolaris does keepalives in-kernel. */ /* NAT keepalive functions */ static void natt_keepalive_send(void *param) @@ -447,6 +464,7 @@ } } } +#endif #ifdef notyet static struct remoteconf * --- old/iked/ikev1/ipsec_doi.c Fri Mar 27 12:09:54 2009 +++ new/iked/ikev1/ipsec_doi.c Fri Mar 27 12:09:54 2009 @@ -43,10 +43,17 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ +#define IPSEC_ULPROTO_ANY 0 +#define IPSEC_PORT_ANY 0 +#endif + #include #include #include @@ -3035,7 +3042,8 @@ for (; pp; pp = pp->next) { for (pr = pp->head; pr; pr = pr->next) { - if (pr->encmode != IPSECDOI_ATTR_ENC_MODE_TRNS) + if (pr->encmode != IPSECDOI_ATTR_ENC_MODE_TRNS && + pr->encmode != IPSECDOI_ATTR_ENC_MODE_UDPTRNS_RFC) return 0; } } @@ -3108,8 +3116,10 @@ return IPSECDOI_PROTO_IPSEC_AH; case IPPROTO_ESP: return IPSECDOI_PROTO_IPSEC_ESP; +#ifndef sun /* XXX KEBE SAYS OpenSolaris */ case IPPROTO_IPCOMP: return IPSECDOI_PROTO_IPCOMP; +#endif } return -1; /* XXX */ } @@ -3123,8 +3133,10 @@ return IPPROTO_AH; case IPSECDOI_PROTO_IPSEC_ESP: return IPPROTO_ESP; +#ifndef sun /* XXX KEBE SAYS OpenSolaris */ case IPSECDOI_PROTO_IPCOMP: return IPPROTO_IPCOMP; +#endif } return -1; /* XXX */ } --- old/iked/ikev1/isakmp_inf.c Fri Mar 27 12:09:54 2009 +++ new/iked/ikev1/isakmp_inf.c Fri Mar 27 12:09:54 2009 @@ -44,7 +44,9 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif @@ -886,8 +888,8 @@ iph2 = getph2bymsgid(iph1, msgid); if (iph2 == NULL) { plog(PLOG_PROTOERR, PLOGLOC, 0, - "unknown notify message, " - "no phase2 handle found.\n"); + "unknown notify message (%d), " + "no phase2 handle found.\n", type); } else { /* delete ph2 */ unbindph12(iph2); --- old/iked/ikev1/isakmp_quick.c Fri Mar 27 12:09:55 2009 +++ new/iked/ikev1/isakmp_quick.c Fri Mar 27 12:09:55 2009 @@ -63,10 +63,16 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ +#define IPSEC_ULPROTO_ANY 0 +#endif + #include "racoon.h" #include "var.h" @@ -97,6 +103,11 @@ /* #include "admin.h" */ #include "strnames.h" +#ifdef sun +#include "ikev1_natt.h" +#define IPSECDOI_PREFIX_HOST 32 /* Hack for port of NAT-OA from ipsec-tools. */ +#endif + #include "ike_conf.h" /* quick mode */ @@ -187,7 +198,7 @@ /* * send to responder - * HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ] + * HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ] */ int quick_i1send(struct ph2handle *iph2, rc_vchar_t *msg /* must be null pointer */) @@ -201,6 +212,11 @@ int pfsgroup, idci, idcr; int np; struct ipsecdoi_id_b *id, *id_p; +#ifdef sun + int natoa = ISAKMP_NPTYPE_NONE; + rc_vchar_t *nat_oai = NULL; + rc_vchar_t *nat_oar = NULL; +#endif /* validity check */ if (msg != NULL) { @@ -275,6 +291,44 @@ } else idci = idcr = 1; +#ifdef sun + /* + * RFC3947 5.2. if we propose UDP-Encapsulated-Transport + * we should send NAT-OA + * + * XXX KEBE ASKS if we should send it for tunnel mode anyway, like + * we do with in.iked? + */ + if (ipsecdoi_transportmode(iph2->proposal) + && (iph2->ph1->natt_flags & NAT_DETECTED)) { + natoa = iph2->ph1->natt_options->payload_nat_oa; + + nat_oai = ipsecdoi_sockaddr2id(iph2->src, + IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY); + nat_oar = ipsecdoi_sockaddr2id(iph2->dst, + IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY); + + if (nat_oai == NULL || nat_oar == NULL) { + plog(PLOG_INTERR, PLOGLOC, NULL, + "failed to generate NAT-OA payload.\n"); + goto end; + } + + plog(PLOG_INFO, PLOGLOC, NULL, "Using NAT-OA.\n"); + plog(PLOG_DEBUG, PLOGLOC, NULL, "NAT-OAi:\n"); + plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oai->v, nat_oai->l); + plog(PLOG_DEBUG, PLOGLOC, NULL, "NAT-OAr:\n"); + plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oar->v, nat_oar->l); + } else { + plog(PLOG_INFO, PLOGLOC, NULL, "Not using NAT-OA.\n"); + plog(PLOG_INFO, PLOGLOC, NULL, "transportmode == %d, " + "natt_flags == 0x%x\n", + ipsecdoi_transportmode(iph2->proposal), + iph2->ph1->natt_flags & NAT_DETECTED); + natoa = ISAKMP_NPTYPE_NONE; + } +#endif + /* create SA;NONCE payload, and KE if need, and IDii, IDir. */ tlen = + sizeof(*gen) + iph2->sa->l + sizeof(*gen) + iph2->nonce->l; @@ -284,6 +338,10 @@ tlen += sizeof(*gen) + iph2->id->l; if (idcr) tlen += sizeof(*gen) + iph2->id_p->l; +#ifdef sun + if (natoa != ISAKMP_NPTYPE_NONE) + tlen += 2 * sizeof(*gen) + nat_oai->l + nat_oar->l; +#endif body = rc_vmalloc(tlen); if (body == NULL) { @@ -303,23 +361,31 @@ else if (idci || idcr) np = ISAKMP_NPTYPE_ID; else - np = ISAKMP_NPTYPE_NONE; + np = natoa; p = set_isakmp_payload(p, iph2->nonce, np); /* add KE payload if need. */ - np = (idci || idcr) ? ISAKMP_NPTYPE_ID : ISAKMP_NPTYPE_NONE; + np = (idci || idcr) ? ISAKMP_NPTYPE_ID : natoa; if (pfsgroup) p = set_isakmp_payload(p, iph2->dhpub, np); /* IDci */ - np = (idcr) ? ISAKMP_NPTYPE_ID : ISAKMP_NPTYPE_NONE; + np = (idcr) ? ISAKMP_NPTYPE_ID : natoa; if (idci) p = set_isakmp_payload(p, iph2->id, np); /* IDcr */ if (idcr) - p = set_isakmp_payload(p, iph2->id_p, ISAKMP_NPTYPE_NONE); + p = set_isakmp_payload(p, iph2->id_p, natoa); +#ifdef sun + /* NAT-OA */ + if (natoa != ISAKMP_NPTYPE_NONE) { + p = set_isakmp_payload(p, nat_oai, natoa); + p = set_isakmp_payload(p, nat_oar, ISAKMP_NPTYPE_NONE); + } +#endif + /* generate HASH(1) */ hash = oakley_compute_hash1(iph2->ph1, iph2->msgid, body); if (hash == NULL) @@ -345,6 +411,10 @@ rc_vfree(body); if (hash != NULL) rc_vfree(hash); + if (nat_oai != NULL) + rc_vfree(nat_oai); + if (nat_oar != NULL) + rc_vfree(nat_oar); return error; } @@ -351,7 +421,7 @@ /* * receive from responder - * HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ] + * HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ] */ int quick_i2recv(struct ph2handle *iph2, rc_vchar_t *msg0) @@ -359,10 +429,11 @@ rc_vchar_t *msg = NULL; rc_vchar_t *hbuf = NULL; /* for hash computing. */ rc_vchar_t *pbuf = NULL; /* for payload parsing */ + rc_vchar_t *idci = NULL; + rc_vchar_t *idcr = NULL; struct isakmp_parse_t *pa; struct isakmp *isakmp = (struct isakmp *)msg0->v; struct isakmp_pl_hash *hash = NULL; - int f_id; char *p; int tlen; int error = ISAKMP_INTERNAL_ERROR; @@ -439,7 +510,6 @@ * copy non-HASH payloads into hbuf, so that we can validate HASH. */ iph2->sa_ret = NULL; - f_id = 0; /* flag to use checking ID */ tlen = 0; /* count payload length except of HASH payload. */ for (; pa->type; pa++) { @@ -470,27 +540,14 @@ break; case ISAKMP_NPTYPE_ID: - { - rc_vchar_t *vp; - - /* check ID value */ - if (f_id == 0) { - /* for IDci */ - f_id = 1; - vp = iph2->id; + if (idci == NULL) { + if (isakmp_p2ph(&idci, pa->ptr) < 0) + goto end; + } else if (idcr == NULL) { + if (isakmp_p2ph(&idcr, pa->ptr) < 0) + goto end; } else { - /* for IDcr */ - vp = iph2->id_p; } - - if (memcmp(vp->v, (caddr_t)pa->ptr + sizeof(struct isakmp_gen), vp->l)) { - - plog(PLOG_PROTOERR, PLOGLOC, NULL, - "mismatched ID was returned.\n"); - error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED; - goto end; - } - } break; case ISAKMP_NPTYPE_N: @@ -500,7 +557,43 @@ #ifdef ENABLE_NATT case ISAKMP_NPTYPE_NATOA_DRAFT: case ISAKMP_NPTYPE_NATOA_RFC: +#ifdef sun + /* DON'T ignore original source/destination. */ + { + struct sockaddr_storage addr; + struct sockaddr *daddr; + uint8_t prefix; + uint16_t ul_proto; + rc_vchar_t *vp = NULL; + + if (isakmp_p2ph(&vp, pa->ptr) < 0) + goto end; + + error = ipsecdoi_id2sockaddr(vp, + (struct sockaddr *) &addr, + &prefix, &ul_proto); + + rc_vfree(vp); + + if (error) + goto end; + + daddr = rcs_sadup((struct sockaddr *) &addr); + if (daddr == NULL) + goto end; + + if (iph2->natoa_src == NULL) + iph2->natoa_src = daddr; + else if (iph2->natoa_dst == NULL) + iph2->natoa_dst = daddr; + else { + racoon_free(daddr); + goto end; + } + } +#else /* Ignore original source/destination messages */ +#endif break; #endif @@ -528,6 +621,98 @@ goto end; } + /* identity check */ + if (idci != NULL) { + struct sockaddr_storage proposed_addr, got_addr; + uint8_t proposed_prefix, got_prefix; + uint16_t proposed_ulproto, got_ulproto; + + error = ipsecdoi_id2sockaddr(iph2->id, + (struct sockaddr *) &proposed_addr, + &proposed_prefix, &proposed_ulproto); + if (error) + goto end; + + error = ipsecdoi_id2sockaddr(idci, + (struct sockaddr *) &got_addr, + &got_prefix, &got_ulproto); + if (error) + goto end; + + if (proposed_prefix != got_prefix + || proposed_ulproto != got_ulproto) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "IDci prefix/ulproto does not match proposal.\n"); + error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED; + goto end; + } + + if (rcs_cmpsa((struct sockaddr *) &proposed_addr, + (struct sockaddr *) &got_addr) == 0) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "IDci matches proposal.\n"); +#ifdef ENABLE_NATT + } else if (iph2->natoa_src != NULL + && rcs_cmpsa_wop(iph2->natoa_src, + (struct sockaddr *) &got_addr) == 0 + && extract_port((struct sockaddr *) &proposed_addr) == + extract_port((struct sockaddr *) &got_addr)) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "IDci matches NAT-OAi.\n"); +#endif + } else { + plog(PLOG_INTERR, PLOGLOC, NULL, + "mismatched IDci was returned.\n"); + error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED; + goto end; + } + } + if (idcr != NULL) { + struct sockaddr_storage proposed_addr, got_addr; + uint8_t proposed_prefix, got_prefix; + uint16_t proposed_ulproto, got_ulproto; + + error = ipsecdoi_id2sockaddr(iph2->id_p, + (struct sockaddr *) &proposed_addr, + &proposed_prefix, &proposed_ulproto); + if (error) + goto end; + + error = ipsecdoi_id2sockaddr(idcr, + (struct sockaddr *) &got_addr, + &got_prefix, &got_ulproto); + if (error) + goto end; + + if (proposed_prefix != got_prefix + || proposed_ulproto != got_ulproto) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "IDcr prefix/ulproto does not match proposal.\n"); + error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED; + goto end; + } + + if (rcs_cmpsa((struct sockaddr *) &proposed_addr, + (struct sockaddr *) &got_addr) == 0) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "IDcr matches proposal.\n"); +#ifdef ENABLE_NATT + } else if (iph2->natoa_dst != NULL + && rcs_cmpsa_wop(iph2->natoa_dst, + (struct sockaddr *) &got_addr) == 0 + && extract_port((struct sockaddr *) &proposed_addr) == + extract_port((struct sockaddr *) &got_addr)) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "IDcr matches NAT-OAr.\n"); +#endif + } else { + plog(PLOG_INTERR, PLOGLOC, NULL, + "mismatched IDcr was returned.\n"); + error = ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED; + goto end; + } + } + /* Fixed buffer for calculating HASH */ memcpy(hbuf->v, iph2->nonce->v, iph2->nonce->l); plog(PLOG_DEBUG, PLOGLOC, NULL, @@ -581,6 +766,10 @@ rc_vfree(pbuf); if (msg) rc_vfree(msg); + if (idci) + rc_vfree(idci); + if (idcr) + rc_vfree(idcr); if (error) { VPTRINIT(iph2->sa_ret); @@ -588,11 +777,52 @@ VPTRINIT(iph2->dhpub_p); VPTRINIT(iph2->id); VPTRINIT(iph2->id_p); +#ifdef ENABLE_NATT + if (iph2->natoa_src) { + racoon_free(iph2->natoa_src); + iph2->natoa_src = NULL; + } + if (iph2->natoa_dst) { + racoon_free(iph2->natoa_dst); + iph2->natoa_dst = NULL; + } +#endif } return error; } +static int +fill_in_ipsec_sas(struct ph2handle *iph2) +{ + /* + * NOTE: The OpenSolaris kernel can queue up packets on a larval SA + * such that when the SA is filled-in via SADB_UPDATE, these packets + * can be immediately processed. If the reply packet is generated + * in-kernel (e.g. ICMP_ECHO processing), an additonal ACQUIRE can + * be sent if there is not a corresponding outbound SA waiting in + * the wings. We therefore perform SADB_ADD first so that if an + * outbound SA is needed immediately during SADB_UPDATE processing, + * it is ready. + */ + + plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendadd\n"); + if (pk_sendadd(iph2) < 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n"); + return (-1); + } + plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n"); + + plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n"); + if (pk_sendupdate(iph2) < 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n"); + return (-1); + } + plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n"); + + return (0); +} + /* * send to responder * HDR*, HASH(3) @@ -704,23 +934,9 @@ goto end; } - /* Do UPDATE for initiator */ - plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n"); - if (pk_sendupdate(iph2) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n"); - goto end; - } - plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n"); + plog(PLOG_DEBUG, PLOGLOC, NULL, "call fill_in_ipsec_sas"); + error = fill_in_ipsec_sas(iph2); - /* Do ADD for responder */ - if (pk_sendadd(iph2) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n"); - goto end; - } - plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n"); - - error = 0; - end: if (buf != NULL) rc_vfree(buf); @@ -851,23 +1067,9 @@ } #endif - /* Do UPDATE for initiator */ - plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n"); - if (pk_sendupdate(iph2) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n"); - goto end; - } - plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n"); + plog(PLOG_DEBUG, PLOGLOC, NULL, "call fill_in_ipsec_sas"); + error = fill_in_ipsec_sas(iph2); - /* Do ADD for responder */ - if (pk_sendadd(iph2) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n"); - goto end; - } - plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n"); - - error = 0; - end: if (msg != NULL) rc_vfree(msg); @@ -881,7 +1083,7 @@ /* * receive from initiator - * HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ] + * HDR*, HASH(1), SA, Ni [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ] */ int quick_r1recv(struct ph2handle *iph2, rc_vchar_t *msg0) @@ -1051,7 +1253,42 @@ #ifdef ENABLE_NATT case ISAKMP_NPTYPE_NATOA_DRAFT: case ISAKMP_NPTYPE_NATOA_RFC: +#ifdef sun + { + struct sockaddr_storage addr; + struct sockaddr *daddr; + uint8_t prefix; + uint16_t ul_proto; + rc_vchar_t *vp = NULL; + + if (isakmp_p2ph(&vp, pa->ptr) < 0) + goto end; + + error = ipsecdoi_id2sockaddr(vp, + (struct sockaddr *) &addr, + &prefix, &ul_proto); + + rc_vfree(vp); + + if (error) + goto end; + + daddr = rcs_sadup((struct sockaddr *) &addr); + if (daddr == NULL) + goto end; + + if (iph2->natoa_dst == NULL) + iph2->natoa_dst = daddr; + else if (iph2->natoa_src == NULL) + iph2->natoa_src = daddr; + else { + racoon_free(daddr); + goto end; + } + } +#else /* Ignore original source/destination messages */ +#endif break; #endif @@ -1119,6 +1356,7 @@ } } +#ifndef sun /* get sainfo */ error = get_sainfo_r(iph2); if (error) { @@ -1167,7 +1405,11 @@ error = ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN; goto end; } + /* change status of isakmp status entry */ + iph2->status = PHASE2ST_STATUS2; +#endif /* sun/OpenSolaris */ + /* * save the packet from the initiator in order to resend the * responder's first packet against this packet. @@ -1174,9 +1416,6 @@ */ iph2->msg1 = rc_vdup(msg0); - /* change status of isakmp status entry */ - iph2->status = PHASE2ST_STATUS2; - error = 0; end: @@ -1193,6 +1432,16 @@ VPTRINIT(iph2->dhpub_p); VPTRINIT(iph2->id); VPTRINIT(iph2->id_p); +#ifdef ENABLE_NATT + if (iph2->natoa_src) { + racoon_free(iph2->natoa_src); + iph2->natoa_src = NULL; + } + if (iph2->natoa_dst) { + racoon_free(iph2->natoa_dst); + iph2->natoa_dst = NULL; + } +#endif } return error; @@ -1232,7 +1481,7 @@ /* * send to initiator - * HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ] + * HDR*, HASH(2), SA, Nr [, KE ] [, IDi2, IDr2 ] [, NAT-OAi, NAT-OAr ] */ int quick_r2send(struct ph2handle *iph2, rc_vchar_t *msg) @@ -1243,8 +1492,13 @@ char *p; int tlen; int error = ISAKMP_INTERNAL_ERROR; + int natoa = ISAKMP_NPTYPE_NONE; int pfsgroup; uint8_t *np_p = NULL; +#ifdef ENABLE_NATT + rc_vchar_t *nat_oai = NULL; + rc_vchar_t *nat_oar = NULL; +#endif /* validity check */ if (msg != NULL) { @@ -1285,6 +1539,33 @@ } } +#ifdef ENABLE_NATT + /* + * RFC3947 5.2. if we chose UDP-Encapsulated-Transport + * we should send NAT-OA + */ + if (ipsecdoi_transportmode(iph2->proposal) + && (iph2->ph1->natt_flags & NAT_DETECTED)) { + natoa = iph2->ph1->natt_options->payload_nat_oa; + + nat_oai = ipsecdoi_sockaddr2id(iph2->dst, + IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY); + nat_oar = ipsecdoi_sockaddr2id(iph2->src, + IPSECDOI_PREFIX_HOST, IPSEC_ULPROTO_ANY); + + if (nat_oai == NULL || nat_oar == NULL) { + plog(PLOG_INTERR, PLOGLOC, NULL, + "failed to generate NAT-OA payload.\n"); + goto end; + } + + plog(PLOG_DEBUG, PLOGLOC, NULL, "NAT-OAi:\n"); + plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oai->v, nat_oai->l); + plog(PLOG_DEBUG, PLOGLOC, 0, NULL, "NAT-OAr:\n"); + plogdump(PLOG_DEBUG, PLOGLOC, 0, nat_oar->v, nat_oar->l); + } +#endif + /* create SA;NONCE payload, and KE and ID if need */ tlen = sizeof(*gen) + iph2->sa_ret->l + sizeof(*gen) + iph2->nonce->l; @@ -1294,6 +1575,11 @@ tlen += (sizeof(*gen) + iph2->id_p->l + sizeof(*gen) + iph2->id->l); +#ifdef ENABLE_NATT + if (natoa != ISAKMP_NPTYPE_NONE) + tlen += 2 * sizeof(*gen) + nat_oai->l + nat_oar->l; +#endif + body = rc_vmalloc(tlen); if (body == NULL) { plog(PLOG_INTERR, PLOGLOC, NULL, @@ -1312,7 +1598,7 @@ ? ISAKMP_NPTYPE_KE : (iph2->id_p != NULL ? ISAKMP_NPTYPE_ID - : ISAKMP_NPTYPE_NONE)); + : natoa)); /* add KE payload if need. */ if (iph2->dhpub_p != NULL && pfsgroup != 0) { @@ -1319,7 +1605,7 @@ np_p = &((struct isakmp_gen *)p)->np; /* XXX */ p = set_isakmp_payload(p, iph2->dhpub, (iph2->id_p == NULL) - ? ISAKMP_NPTYPE_NONE + ? natoa : ISAKMP_NPTYPE_ID); } @@ -1329,8 +1615,15 @@ p = set_isakmp_payload(p, iph2->id_p, ISAKMP_NPTYPE_ID); /* IDcr */ np_p = &((struct isakmp_gen *)p)->np; /* XXX */ - p = set_isakmp_payload(p, iph2->id, ISAKMP_NPTYPE_NONE); + p = set_isakmp_payload(p, iph2->id, natoa); } +#ifdef ENABLE_NATT + /* NAT-OA */ + if (natoa != ISAKMP_NPTYPE_NONE) { + p = set_isakmp_payload(p, nat_oai, natoa); + p = set_isakmp_payload(p, nat_oar, ISAKMP_NPTYPE_NONE); + } +#endif /* add a RESPONDER-LIFETIME notify payload if needed */ { @@ -1425,6 +1718,12 @@ rc_vfree(body); if (hash != NULL) rc_vfree(hash); +#ifdef ENABLE_NATT + if (nat_oai != NULL) + rc_vfree(nat_oai); + if (nat_oar != NULL) + rc_vfree(nat_oar); +#endif return error; } @@ -1694,23 +1993,9 @@ } } - /* Do UPDATE as responder */ - plog(PLOG_DEBUG, PLOGLOC, NULL, "call pk_sendupdate\n"); - if (pk_sendupdate(iph2) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey update failed.\n"); - goto end; - } - plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey update sent.\n"); + plog(PLOG_DEBUG, PLOGLOC, NULL, "call fill_in_ipsec_sas"); + error = fill_in_ipsec_sas(iph2); - /* Do ADD for responder */ - if (pk_sendadd(iph2) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, "pfkey add failed.\n"); - goto end; - } - plog(PLOG_DEBUG, PLOGLOC, NULL, "pfkey add sent.\n"); - - error = 0; - end: if (msg != NULL) rc_vfree(msg); @@ -2126,7 +2411,7 @@ spidx2str(&spidx)); iph2->spidx_gen = racoon_malloc(sizeof(spidx)); if (!iph2->spidx_gen) { - plog(LLV_ERROR, LOCATION, NULL, + plog(PLOG_INTERR, PLOGLOC, NULL, "buffer allocation failed.\n"); return ISAKMP_INTERNAL_ERROR; } @@ -2176,7 +2461,7 @@ } return 0; -#endif +#endif /* 0 */ } #ifdef INET6 --- old/iked/ikev1/pfkey.c Fri Mar 27 12:09:55 2009 +++ new/iked/ikev1/pfkey.c Fri Mar 27 12:09:55 2009 @@ -57,7 +57,9 @@ #include #include #include +#ifndef sun /* XXX KEBE SAYS OpenSolaris */ #include +#endif /* #include */ @@ -68,7 +70,9 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif #include @@ -121,7 +125,9 @@ static unsigned int ipsecdoi2rc_calg (unsigned int); static unsigned int keylen_aalg (unsigned int); static unsigned int keylen_ealg (unsigned int, int); +static int pk_sendaddup(struct ph2handle *, int); + /* static int addnewsp (caddr_t *); */ /* callback methods */ @@ -516,6 +522,7 @@ } break; +#ifndef sun /* XXX KEBE SAYS OpenSolaris doesn't do IPCOMP for now. */ case IPSECDOI_PROTO_IPCOMP: if ((*e_type = ipsecdoi2rc_calg(t_id)) == ~0u) goto bad; @@ -533,6 +540,7 @@ goto bad; } break; +#endif default: plog(PLOG_INTERR, PLOGLOC, 0, @@ -610,6 +618,7 @@ plog(PLOG_INTERR, PLOGLOC, NULL, "invalid proto_id %d\n", pr->proto_id); return -1; +#ifndef sun /* XXX KEBE SAYS OpenSolaris */ } /* this works around a bug in Linux kernel where it * allocates 4 byte spi's for IPCOMP */ @@ -616,6 +625,7 @@ else if (satype == SADB_X_SATYPE_IPCOMP) { minspi = 0x100; maxspi = 0xffff; +#endif } else { minspi = 0; maxspi = 0; @@ -629,6 +639,8 @@ plog(PLOG_DEBUG, PLOGLOC, NULL, "call pfkey_send_getspi\n"); + (void) memset(¶m, 0, sizeof (param)); + param.sa_src = dst; /* src of SA */ param.sa_dst = src; /* dst of SA */ param.pref_src = 0; @@ -792,6 +804,7 @@ return -1; } + (void) memset(¶m, 0, sizeof (param)); param.satype = satype; param.seq = iph2->seq; if (dir == 0) { /* inbound */ @@ -869,167 +882,154 @@ return 0; } +#ifdef sun /* - * set inbound SA + * Given a raw SA payload, figure out if it's tunnel-mode or transport-mode. + * + * In practice, a proposal doesn't alternate between tunnel/transport in a + * single payload, so find the first encapsulation attribute we can and + * return based on its value. */ +static boolean_t +sa_payload_says_tunnel(rc_vchar_t *sa) +{ + struct prop_pair **pair; + struct saprop *saprop; + boolean_t rc; + + pair = get_proppair(sa, IPSECDOI_TYPE_PH2); + if (pair == NULL) + return (B_FALSE); /* Assume transport mode... */ + + saprop = aproppair2saprop(*pair); + if (saprop == NULL) { + free_proppair(pair); + return (B_FALSE); + } + + rc = (boolean_t)ipsecdoi_transportmode(saprop); + flushsaprop(saprop); + free_proppair(pair); + return (!rc); +} + +/* + * Given a fully-populated IKEv1 Phase 2 handle (iph2) construct an + * inverse-ACQUIRE. + * + * We need: + * - SRC and DST, including ports/proto if the QM initiator IDs have 'em. + * - iSRC and iDST if the QM initiator IDs don't match the ISAKMP addrs + * and it's not NAT_T encapsulation. + * + * The iph2->{src,dst} should have the IP addresses of the traffic. + * The iph2->{id,id_p} contain initiator's view of my ID and initiator's + * view of his/her ID respectively. + * + * There is an SA payload here, so we can figure out tunnel/transport w/o + * too much pain. + */ int -pk_sendupdate(struct ph2handle *iph2) +ikev1_send_inverse_acquire(struct ph2handle *iph2, uint32_t newseq) { struct sockaddr *src, *dst; - struct sockaddr_storage my_ss, peer_ss; - struct saproto *pr; - unsigned int e_type, a_type; - unsigned int e_keylen, a_keylen, flags; - int satype, mode; - uint64_t lifebyte = 0; - unsigned int wsize = 4; /* XXX static size of window */ -#if 0 - int proxy = 0; -#endif struct rcpfk_msg param; + struct sockaddr_storage my_ss, peer_ss; + struct ipsecdoi_id_b *idptr; + uint16_t holder; - /* sanity check */ - if (iph2->approval == NULL) { - plog(PLOG_INTERR, PLOGLOC, 0, "no approvaled SAs found.\n"); - return -1; - } -#if 0 - if (iph2->side == INITIATOR) - proxy = (ikev1_support_proxy(iph2->ph1->rmconf) != - RCT_BOOL_OFF); - else if (iph2->sainfo && iph2->sainfo->id_i) - proxy = 1; + (void) memset(¶m, 0, sizeof (param)); + param.seq = newseq; - /* for mobile IPv6 */ - if (proxy && iph2->src_id && iph2->dst_id && - ipsecdoi_transportmode(iph2->approval)) { - src = iph2->src_id; - dst = iph2->dst_id; - } else { - src = iph2->src; - dst = iph2->dst; - } -#endif + if (sa_payload_says_tunnel(iph2->sa)) { + int copylen; - src = ike_determine_sa_endpoint(&my_ss, - iph2->selector->pl->my_sa_ipaddr, - iph2->src); + param.sa_src = iph2->src; + param.sa_dst = iph2->dst; - dst = ike_determine_sa_endpoint(&peer_ss, - iph2->selector->pl->peers_sa_ipaddr, - iph2->dst); + param.sa_isrc = (struct sockaddr *)¶m.sa_isrc_storage; + param.sa_idst = (struct sockaddr *)¶m.sa_idst_storage; - if (src == NULL || dst == NULL) - return -1; + if (ipsecdoi_id2sockaddr(iph2->id, param.sa_isrc, + ¶m.pref_isrc, &holder) != 0) + return (-1); + param.ul_iproto = holder; - for (pr = iph2->approval->head; pr != NULL; pr = pr->next) { - /* validity check */ - satype = ipsecdoi2rc_proto(pr->proto_id); - if (satype == 0) { - plog(PLOG_PROTOERR, PLOGLOC, 0, - "invalid proto_id %d\n", pr->proto_id); - return -1; - } else if (satype == RCT_SATYPE_IPCOMP) { - /* IPCOMP has no replay window */ - wsize = 0; - } -#ifdef ENABLE_SAMODE_UNSPECIFIED -#error ENABLE_SAMODE_UNSPECIFIED unsupported -#if 0 - mode = IPSEC_MODE_ANY; -#endif -#else - mode = ipsecdoi2rc_mode(pr->encmode); - if (mode == 0) { - plog(PLOG_PROTOERR, PLOGLOC, 0, - "invalid encmode %d\n", pr->encmode); - return -1; - } -#endif + if (ipsecdoi_id2sockaddr(iph2->id_p, param.sa_idst, + ¶m.pref_idst, &holder) != 0) + return (-1); + if (holder != param.ul_iproto) + /* Inequal inner-protos between addresses! */ + return (-1); - /* set algorithm type and key length */ - e_keylen = pr->head->encklen; - if (rc_convertfromipsecdoi - (pr->proto_id, pr->head->trns_id, pr->head->authtype, - &e_type, &e_keylen, &a_type, &a_keylen, &flags) < 0) - return -1; + param.ul_proto = (param.sa_isrc->sa_family == AF_INET) ? + IPPROTO_ENCAP : IPPROTO_IPV6; + } else { + /* Transport mode stuff... */ + param.sa_src = (struct sockaddr *)¶m.sa_src_storage; + param.sa_dst = (struct sockaddr *)¶m.sa_dst_storage; -#if 0 - lifebyte = iph2->approval->lifebyte * 1024, -#else - lifebyte = 0; -#endif + if (iph2->id != NULL) { + if (ipsecdoi_id2sockaddr(iph2->id, param.sa_src, + ¶m.pref_src, &holder) != 0) + return (-1); + param.ul_proto = holder; - param.satype = satype; - param.seq = iph2->seq; - param.spi = pr->spi; - param.wsize = wsize; - param.authtype = a_type; - param.enctype = e_type; - param.saflags = flags; - param.samode = mode; - param.reqid = pr->reqid_in; - param.lft_hard_time = iph2->approval->lifetime; - param.lft_hard_bytes = lifebyte; - param.lft_soft_time = iph2->approval->lifetime; /* ??? */ - param.lft_soft_bytes = lifebyte; - param.sa_src = dst; /* for inbound */ - param.sa_dst = src; - param.pref_src = 0; - param.pref_dst = 0; - param.ul_proto = RC_PROTO_ANY; /* ??? */ - param.enckey = pr->keymat->v; - param.enckeylen = e_keylen; - param.authkey = pr->keymat->v + e_keylen; - param.authkeylen = a_keylen; - if (iph2->sadb_request.method->update_inbound(¶m)) { - /* (*update_inbound)() logs error message */ - return -1; - } -#if 0 - plog(PLOG_DEBUG, PLOGLOC, NULL, "call pfkey_send_update\n"); - if (pfkey_send_update - (lcconf->sock_pfkey, satype, mode, dst, src, pr->spi, - pr->reqid_in, wsize, pr->keymat->v, e_type, e_keylen, - a_type, a_keylen, flags, 0, lifebyte, - iph2->approval->lifetime, 0, iph2->seq) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, - "libipsec failed send update (%s)\n", - ipsec_strerror()); - return -1; - } -#endif + if (iph2->id_p == NULL) + return (-1); + if (ipsecdoi_id2sockaddr(iph2->id_p, param.sa_dst, + ¶m.pref_dst, &holder) != 0) + return (-1); + if (holder != param.ul_proto) + /* Inequal protos betweeen addresses! */ + return (-1); -#ifdef notyet - if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA]) - continue; - - /* - * It maybe good idea to call backupsa_to_file() after - * racoon will receive the sadb_update messages. - * But it is impossible because there is not key in the - * information from the kernel. - */ - if (backupsa_to_file - (satype, mode, dst, src, pr->spi, pr->reqid_in, 4, - pr->keymat->v, e_type, e_keylen, a_type, a_keylen, flags, - 0, iph2->approval->lifebyte * 1024, - iph2->approval->lifetime, 0, iph2->seq) < 0) { - plog(PLOG_INTERR, PLOGLOC, NULL, - "backuped SA failed: %s\n", sadbsecas2str(dst, src, - satype, - pr->spi, - mode)); + /* Check for NAT zaniness. */ + /* + * Ugggh, rcs_cmpsa*() return 0 if equal, 1 if not + * equal. They resemble str*cmp() more than a boolean + * is_equal() function. + */ + if (iph2->natoa_src != NULL && + rcs_cmpsa_wop(iph2->natoa_src, iph2->src)) { + /* Preserve port, but write external address. */ + holder = extract_port(iph2->src); + memcpy(param.sa_src, iph2->src, + rcs_getsalen(iph2->src)); + rcs_setsaport(param.sa_src, holder); + } + if (iph2->natoa_dst != NULL && + rcs_cmpsa_wop(iph2->natoa_dst, iph2->dst)) { + /* Preserve port, but write external address. */ + holder = extract_port(iph2->dst); + memcpy(param.sa_dst, iph2->dst, + rcs_getsalen(iph2->dst)); + rcs_setsaport(param.sa_dst, holder); + } + } else { + memcpy(param.sa_src, iph2->src, + rcs_getsalen(iph2->src)); + rcs_setsaport(param.sa_src, 0); + memcpy(param.sa_dst, iph2->dst, + rcs_getsalen(iph2->dst)); + rcs_setsaport(param.sa_dst, 0); } - plog(PLOG_DEBUG, PLOGLOC, NULL, - "backuped SA: %s\n", - sadbsecas2str(dst, src, satype, pr->spi, mode)); -#endif } - return 0; + return (iph2->sadb_request.method->inverse_acquire(¶m) == 0 ? + 0 : -1); } +#endif +/* + * set inbound SA + */ +int +pk_sendupdate(struct ph2handle *iph2) +{ + return (pk_sendaddup(iph2, 0)); +} + /* called from scheduler. * this function will call only isakmp_ph2delete(). * phase 2 handler remain forever if kernel doesn't cry a expire of phase 2 SA @@ -1139,12 +1139,19 @@ return 0; } -/* - * set outbound SA - */ + int pk_sendadd(struct ph2handle *iph2) { + return (pk_sendaddup(iph2, 1)); +} + +/* + * common add/update code. + */ +static int +pk_sendaddup(struct ph2handle *iph2, int add) +{ struct sockaddr *src, *dst; struct sockaddr_storage my_ss, peer_ss; struct saproto *pr; @@ -1157,6 +1164,7 @@ int proxy = 0; #endif struct rcpfk_msg param; + int (*pfkeyfunc)(); /* sanity check */ if (iph2->approval == NULL) { @@ -1230,30 +1238,93 @@ lifebyte = 0; #endif + (void) memset(¶m, 0, sizeof (param)); param.satype = satype; param.seq = iph2->seq; - param.spi = pr->spi_p; + /* NAT-OA madness works both ways. */ + if (iph2->natoa_src != NULL && + !rcs_cmpsa_wop(iph2->natoa_src, src)) + param.sa_natlocal = NULL; + else + param.sa_natlocal = iph2->natoa_src; + + param.sa_natremote = iph2->natoa_dst; + if ((iph2->ph1->natt_flags & NAT_DETECTED_PEER) && + extract_port(iph2->dst) != IPPORT_IKE_NATT) { + if (param.sa_natremote == NULL) + param.sa_natremote = rcs_sadup(iph2->dst); + else + set_port(param.sa_natremote, + extract_port(iph2->dst)); + } + + if (add) { + param.spi = pr->spi_p; + param.reqid = pr->reqid_out; + param.sa_src = src; + param.sa_dst = dst; + param.enckey = pr->keymat_p->v; + param.authkey = pr->keymat_p->v + e_keylen; + pfkeyfunc = iph2->sadb_request.method->add_outbound; + } else { + param.spi = pr->spi; + param.reqid = pr->reqid_in; + param.sa_src = dst; + param.sa_dst = src; + param.enckey = pr->keymat->v; + param.authkey = pr->keymat->v + e_keylen; + pfkeyfunc = iph2->sadb_request.method->update_inbound; + } param.wsize = wsize; param.authtype = a_type; param.enctype = e_type; - param.saflags = flags; +#define SADB_X_SAFLAGS_NATTED 0x1000 + param.saflags = flags | + ((iph2->ph1->natt_flags & NAT_DETECTED_ME) ? + SADB_X_SAFLAGS_NATTED : 0); param.samode = mode; - param.reqid = pr->reqid_out; param.lft_hard_time = iph2->approval->lifetime; param.lft_hard_bytes = lifebyte; param.lft_soft_time = iph2->approval->lifetime; /* ??? */ param.lft_soft_bytes = lifebyte; - param.sa_src = src; - param.sa_dst = dst; param.pref_src = 0; param.pref_dst = 0; +#ifdef sun + if (iph2->selector->pl->ipsec_mode == RCT_IPSM_TUNNEL) { + int af = iph2->selector->src->a.ipaddr->sa_family; + + param.ul_iproto = iph2->selector->upper_layer_protocol; + param.ul_proto = (af == AF_INET) ? IPPROTO_ENCAP : + IPPROTO_IPV6; + /* + * XXX KEBE SAYS, NAT-T madness here? + * Also, may need add/update flexibility as well. + */ + if (add) { + param.sa_isrc = iph2->selector->src->a.ipaddr; + param.sa_idst = iph2->selector->dst->a.ipaddr; + param.pref_isrc = + iph2->selector->src->prefixlen; + param.pref_idst = + iph2->selector->dst->prefixlen; + } else { + param.sa_isrc = iph2->selector->dst->a.ipaddr; + param.sa_idst = iph2->selector->src->a.ipaddr; + param.pref_isrc = + iph2->selector->dst->prefixlen; + param.pref_idst = + iph2->selector->src->prefixlen; + } + } else { + param.ul_proto = iph2->selector->upper_layer_protocol; + } +#else param.ul_proto = RC_PROTO_ANY; /* ??? */ - param.enckey = pr->keymat_p->v; +#endif param.enckeylen = e_keylen; - param.authkey = pr->keymat_p->v + e_keylen; param.authkeylen = a_keylen; - if (iph2->sadb_request.method->add_outbound(¶m)) { - /* (*update_outbound)() logs error message */ + if (pfkeyfunc(¶m)) { + /* pfkeyfunc() logs error message */ return -1; } #if 0 @@ -1274,6 +1345,8 @@ if (!lcconf->pathinfo[LC_PATHTYPE_BACKUPSA]) continue; + /* XXX KEBE SAYS, make add/update flexible. */ + /* * It maybe good idea to call backupsa_to_file() after * racoon will receive the sadb_add messages. @@ -2297,6 +2370,7 @@ { struct rcpfk_msg param; + (void) memset(¶m, 0, sizeof (param)); param.seq = iph2->seq; param.satype = iph2->satype; param.eno = ECONNREFUSED; /* ??? */ --- old/iked/ikev1/pfkey.h Fri Mar 27 12:09:56 2009 +++ new/iked/ikev1/pfkey.h Fri Mar 27 12:09:56 2009 @@ -58,6 +58,9 @@ extern int pk_sendspdupdate2 (struct ph2handle *); extern int pk_sendspdadd2 (struct ph2handle *); extern int pk_sendspddelete (struct ph2handle *); +#ifdef sun +extern int ikev1_send_inverse_acquire(struct ph2handle *, uint32_t); +#endif extern void pfkey_timeover_stub (void *); extern void pfkey_timeover (struct ph2handle *); --- old/iked/ikev1/proposal.c Fri Mar 27 12:09:56 2009 +++ new/iked/ikev1/proposal.c Fri Mar 27 12:09:56 2009 @@ -43,7 +43,9 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif --- old/iked/ikev1/strnames.c Fri Mar 27 12:09:57 2009 +++ new/iked/ikev1/strnames.c Fri Mar 27 12:09:57 2009 @@ -42,7 +42,9 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris */ +# include +# endif # endif #endif --- old/iked/ikev2.c Fri Mar 27 12:09:58 2009 +++ new/iked/ikev2.c Fri Mar 27 12:09:58 2009 @@ -760,10 +760,17 @@ if (rcs_getsaport(peer) == 0) rcs_setsaport(peer, isakmp_port_dest); +#ifndef sun if (req->src2 && ike_ipsec_mode(policy) == RCT_IPSM_TRANSPORT) myself = getlocaladdr(peer, req->src2, isakmp_port); else +#endif + /* + * NOTE: In OpenSolaris "src" is always my actual IP + * address. + */ myself = getlocaladdr(peer, req->src, isakmp_port); + if (!myself) { isakmp_log(0, req->src, req->dst, 0, PLOG_INTERR, PLOGLOC, @@ -2194,6 +2201,440 @@ #endif } +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + +/* + * XXX KEBE SAYS maybe move this to ike_pfkey.c? + */ +void +free_invacq(invacq_t *invacq) +{ + /* Unlink sadb_request from its list. */ + sadb_request_finish(&invacq->request); + + /* Free any allocated storage. */ + rc_free(invacq->local); + rc_free(invacq->remote); + + /* We used normal libc alloc for the payloads and child_param. */ + free(invacq->sa_payload); + free(invacq->ts_i); + free(invacq->ts_r); + free(invacq->cfg); + free(invacq->child_param); + + /* DH and nonce are racoon2 bufs. Zeroize them too. */ + rc_vfreez(invacq->g_i); + rc_vfreez(invacq->n_i); + + /* Same withthe packet. */ + rc_vfreez(invacq->packet); + + free(invacq); +} + +/* + * This function will re-inject itself into the flow by calling + * ikev2_create_child_responder(). + */ +static int +ikev2_recv_inverse_acquire(invacq_t *invacq) +{ + int rc; + + /* + * XXX KEBE SAYS - There is a possibility that the inverse-ACQUIRE + * brought back inner-address selectors that are a *superset* of the + * traffic selectors. You might need to make sure that we narrow + * accordingly. + */ + + rc = ikev2_create_child_responder(invacq->ikev2_sa, invacq->local, + invacq->remote, invacq->message_id, invacq->sa_payload, + invacq->ts_i, invacq->ts_r, invacq->cfg, invacq->g_i, invacq->n_i, + invacq->child_param, invacq->is_createchild, + invacq->old_child_sa, invacq->answer); + + if (rc != 0) { + ++isakmpstat.fail_process_packet; /* Orig. code asks ??? */ + if (ikev2_respond_error(invacq->ikev2_sa, + NULL /* invacq->ikev2_pkt */, invacq->remote, + invacq->local, 0, 0, 0, rc, 0, 0) == 0) { + ikev2_update_message_id(invacq->ikev2_sa, + invacq->message_id, invacq->is_createchild); + ikev2_abort(invacq->ikev2_sa, ECONNREFUSED); + } + } else if (invacq->ikev2_sa->state == IKEV2_STATE_RES_IKE_AUTH_RCVD) { + /* Don't call this if not replying to auth... */ + ikev2_update_message_id(invacq->ikev2_sa, invacq->message_id, + invacq->is_createchild); + } + + free_invacq(invacq); + + /* XXX KEBE SAYS REACT ACCORDINGLY TO THE rc/error returned */ + return (rc); +} + +/* + * Duplicate an ikev2_payload_header & contents. + */ +struct ikev2_payload_header * +payload_dup(struct ikev2_payload_header *old) +{ + struct ikev2_payload_header *new; + size_t len; + + if (old == NULL) + return (NULL); + + len = get_payload_length(old); + new = malloc(len); + if (new != NULL) + (void) memcpy(new, old, len); + return (new); +} + +/* + * Return 0 for "any", and any other number for an actual port. Returns in + * host-order, since the result is expected to be fed into rcs_setsaport(). + * For ranged-ports, we will currently select the low port. We could return + * values < 0 or > 65536 for other reportings if need be. + */ +static int +grab_port(struct ikev2_traffic_selector *selector) +{ + uint16_t start_port, end_port; + + start_port = get_uint16(&selector->start_port); + end_port = get_uint16(&selector->end_port); + if (start_port != end_port) { + if (!IKEV2_TS_PORT_IS_ANY(start_port, end_port)) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: " + "ranged ports %d-%d, using low.", start_port, + end_port); + } + if (IKEV2_TS_PORT_IS_OPAQUE(start_port, end_port)) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: " + "OPAQUE ports used."); + /* + * For now, OpenSolaris doesn't differentiate between + * OPAQUE and ANY. + */ + start_port = 0; + } + } + /* exploit start_port == 0 for "any" */ + return (start_port); +} + +/* + * Returns >= 0 if given address-range can be encoded as /len. + * Returns -1 if it cannot. + * Assume "low" points to two addresses of length "len" byte + * Scribbles the previx value into "dst". + */ +static int +range_to_prefixlen(void *lp, void *dp, int len) +{ + uint8_t *dst = (uint8_t *)dp; + uint8_t *low = (uint8_t *)lp; + uint8_t *high = low + len, thisbit; + int i, j, bitcount = 0; + boolean_t prefix = B_TRUE; + + for (i = 0; i < len ; i++) { + /* + * Check for zero-byte in the "low" position in non-prefix + * bytes. + */ + if (!prefix) { + if (high[i] != 0xFF || low[i] != 0) + return (-1); + dst[i] = 0; + continue; + } + + + /* Whole matching prefix byte. */ + if (low[i] == high[i]) { + bitcount += 8; + dst[i] = low[i]; + continue; + } + + dst[i] = 0; + + /* Common-case of byte-boundary prefix shift. */ + if (low[i] == 0 && high[i] == 0xFF) { + prefix = B_FALSE; + continue; + } + + /* Partial-matching prefix byte. End the prefix here. */ + for (j = 7; j >= 0; j--) { + thisbit = 1 << j; + /* non-prefix bits. */ + if (!prefix) { + if ((high[i] & thisbit) != thisbit || + ((low[i] & thisbit) == thisbit)) + return (-1); + /* This bit is already 0 in dst. */ + continue; + } + + /* Prefix bit. */ + if ((low[i] & thisbit) == (high[i] & thisbit)) { + bitcount++; + dst[i] |= (low[i] & thisbit); + } else if ((low[i] & thisbit) == 1) { + return (-1); + } else { + /* Transition from prefix to non-prefix. */ + prefix = B_FALSE; + j++; /* Recheck using non-prefix test. */ + } + } + } + + return (bitcount); +} + +/* + * Convert a single traffic selector to PF_KEY-style address information. + * This includes range-to-prefix conversion, and if the proto pointers + * passed-in point to 0, set them, otherwise, make a comparison. + * + * Return B_FALSE if the range can't be converted/narrowed to a prefix, or + * the non-zero proto passed-in doesn't match the other parameters. + */ +boolean_t +range_to_prefix(struct ikev2_traffic_selector *sel, struct sockaddr *sa, + uint8_t *prefix, uint8_t *proto, uint8_t *iproto) +{ + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + uint16_t lowport, highport, port; + int len; + + /* + * Set the inner-protocol based on what's passed-in as the previous + * value (first traffic selector), plus the current traffic selector. + */ + if (*iproto != 0 && *iproto != sel->protocol_id) + return (B_FALSE); + else + *iproto = sel->protocol_id; + + lowport = sel->start_port; + highport = sel->end_port; + if (IKEV2_TS_PORT_IS_ANY(lowport, highport) || + IKEV2_TS_PORT_IS_OPAQUE(lowport, highport)) + port = 0; + else if (lowport != highport) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: Ranged-port" + "selectors (%d - %d) given. Unsupported currently.\n", + htons(lowport), htons(highport)); + return (B_FALSE); + } else { + port = lowport; + if (port == 0) + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: " + "0-port (high/low) sent."); + } + + switch (htons(sel->selector_length) - sizeof (*sel)) { + case (2 * sizeof (in6_addr_t)): + /* IPv6 */ + *proto = IPPROTO_IPV6; + sa->sa_family = AF_INET6; + sin6 = (struct sockaddr_in6 *)sa; + sin6->sin6_port = port; + if ((len = range_to_prefixlen((sel + 1), &sin6->sin6_addr, + sizeof (in6_addr_t))) == -1) + return (B_FALSE); + break; + case (2 * sizeof (in_addr_t)): + /* IPv4 */ + *proto = IPPROTO_ENCAP; + sa->sa_family = AF_INET; + sin = (struct sockaddr_in *)sa; + sin->sin_port = port; + if ((len = range_to_prefixlen((sel + 1), &sin->sin_addr, + sizeof (in_addr_t))) == -1) + return (B_FALSE); + break; + } + *prefix = len; +} + +/* + * Now that we've save the parameters, construct and send the actual inverse- + * ACQUIRE message via PF_KEY. + */ +static int +ikev2_invacq_to_pfkey(invacq_t *invacq, uint32_t newseq) +{ + struct rcpfk_msg param; + struct ikev2payl_traffic_selector *ts_remote, *ts_local; + struct ikev2_traffic_selector *selector; + int retport; + + (void) memset(¶m, 0, sizeof (param)); + param.seq = newseq; + param.sa_src = (struct sockaddr *)¶m.sa_src_storage; + param.sa_dst = (struct sockaddr *)¶m.sa_dst_storage; + memcpy(param.sa_src, invacq->local, SA_LEN(invacq->local)); + memcpy(param.sa_dst, invacq->remote, SA_LEN(invacq->remote)); + ts_local = (struct ikev2payl_traffic_selector *)invacq->ts_r; + ts_remote = (struct ikev2payl_traffic_selector *)invacq->ts_i; + + if (invacq->child_param->use_transport_mode) { + /* + * Transport Mode. + * + * We don't have to do NAT-OA-ish checking, because the + * IKE traffic addresses is how we deal with internal/external, + * and the traffic-selector ports will fill in the rest. + * + * Perhaps not as much as in Tunnel Mode, but we may have + * to worry about ranges or multiple entries of addresses + * for traffic selectors (think SCTP). + */ + if (ts_local->tsh.num_ts > 1) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: Transport " + "mode local traffic selector count of %d.", + ts_local->tsh.num_ts); + } + if (ts_remote->tsh.num_ts > 1) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: Transport " + "mode remote traffic selector count of %d.", + ts_remote->tsh.num_ts); + } + + /* Grab first selector for local. Don't assume aligned. */ + selector = (struct ikev2_traffic_selector *)(ts_local + 1); + /* Assume protocol ids are local for both... */ + param.ul_proto = selector->protocol_id; + retport = grab_port(selector); + rcs_setsaport(param.sa_src, retport); + + selector = (struct ikev2_traffic_selector *)(ts_remote + 1); + /* Assume protocol ids are local for both... */ + if (param.ul_proto != selector->protocol_id) { + plog(PLOG_INTERR, PLOGLOC, NULL, "Protocol IDs are" + "inequal between local/remote."); + return (-1); + } + retport = grab_port(selector); + rcs_setsaport(param.sa_src, retport); + } else { + /* Clear out ports. */ + rcs_setsaport(param.sa_src, 0); + rcs_setsaport(param.sa_dst, 0); + + if (ts_local->tsh.num_ts > 1) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: Tunnel " + "mode local traffic selector count of %d.", + ts_local->tsh.num_ts); + } + if (ts_remote->tsh.num_ts > 1) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: Tunnel " + "mode remote traffic selector count of %d.", + ts_remote->tsh.num_ts); + } + + selector = (struct ikev2_traffic_selector *)(ts_local + 1); + param.sa_isrc = (struct sockaddr *)¶m.sa_isrc_storage; + /* param.ul_{,i}proto is 0... */ + if (!range_to_prefix(selector, param.sa_isrc, ¶m.pref_isrc, + ¶m.ul_proto, ¶m.ul_iproto)) + return (-1); + selector = (struct ikev2_traffic_selector *)(ts_remote + 1); + param.sa_idst = (struct sockaddr *)¶m.sa_idst_storage; + if (!range_to_prefix(selector, param.sa_idst, ¶m.pref_idst, + ¶m.ul_proto, ¶m.ul_iproto)) + return (-1); + + } + + return (invacq->request.method->inverse_acquire(¶m) == 0 ? + 0 : -1); +} + +/* + * Given a fully-populated IKE SA, and child SA traffic selectors, construct + * an inverse-ACQUIRE for the OpenSolaris kernel. Make sure there's room + * for a *new* ikev2_child_param, as we're going to lose the passed-in one + * thanks to this function's fire-and-return. + * + * This function's signature is nearly-identical to + * ikev2_create_child_responder(), which is what'll be called upon return of + * the inverse-ACQUIRE. + */ +static int +ikev2_send_inverse_acquire(struct ikev2_sa *ike_sa, + struct sockaddr *local, + struct sockaddr *remote, + uint32_t request_message_id, + struct ikev2_payload_header *sa_payload, + struct ikev2_payload_header *proposed_ts_i, + struct ikev2_payload_header *proposed_ts_r, + struct ikev2_payload_header *cfg, + rc_vchar_t *g_i, + rc_vchar_t *n_i, + struct ikev2_child_param *child_param, + boolean_t is_createchild, + struct ikev2_child_sa *old_child_sa, + rc_vchar_t *packet) +{ + invacq_t *invacq; + uint32_t newseq = sadb_new_seq(); + + invacq = calloc(1, sizeof (*invacq)); /* zero-out... */ + if (invacq == NULL) + return (-1); + + + /* Allocate-and-copy those parameters which need it. */ + + /* These are always guaranteed to be non-NULL. */ + if ((invacq->local = rcs_sadup(local)) == NULL || + (invacq->remote = rcs_sadup(remote)) == NULL || + (invacq->sa_payload = payload_dup(sa_payload)) == NULL || + (invacq->ts_i = payload_dup(proposed_ts_i)) == NULL || + (invacq->ts_r = payload_dup(proposed_ts_r)) == NULL || + (invacq->child_param = malloc(sizeof (*child_param))) == NULL || + (invacq->packet = rc_vdup(packet)) == NULL) + goto bail; + + (void) memcpy(invacq->child_param, child_param, sizeof (*child_param)); + + /* Now for the ones that aren't guaranteed... */ + if ((cfg != NULL && (invacq->cfg = payload_dup(cfg)) == NULL) || + (g_i != NULL && (invacq->g_i = rc_vdup(g_i)) == NULL) || + (n_i != NULL && (invacq->n_i = rc_vdup(n_i)) == NULL)) + goto bail; + + /* Then take care of the referenced-or-copied fields. */ + invacq->ikev2_sa = ike_sa; + invacq->old_child_sa = old_child_sa; + invacq->message_id = request_message_id; + invacq->is_createchild = is_createchild; + invacq->receiver = ikev2_recv_inverse_acquire; + + sadb_request_initialize(&invacq->request, + &sadb_responder_request_method, NULL, + newseq, invacq); + + return (ikev2_invacq_to_pfkey(invacq, newseq)); + +bail: + free_invacq(invacq); + return (-1); +} +#endif /* Sun/OpenSolaris */ + static void responder_ike_sa_auth_cont(struct ikev2_sa *ike_sa, int result, rc_vchar_t *msg, struct sockaddr *remote, struct sockaddr *local) @@ -2342,6 +2783,18 @@ ikev2_set_rmconf(ike_sa, conf); #endif +#ifdef sun + /* + * XXX KEBE SAYS start inverse-ACQUIRE proceedings, and have the + * invacq return call ikev2_create_child_responder(). + */ + if (ikev2_send_inverse_acquire(ike_sa, local, remote, message_id, sa_i2, + ts_i, ts_r, cfg, 0, 0, &child_param, FALSE, 0, msg) != 0) { + goto fail_nomem; + } + /* Fall through to "done" below. */ + +#else error = ikev2_create_child_responder(ike_sa, local, remote, message_id, sa_i2, ts_i, ts_r, cfg, 0, 0, &child_param, FALSE, 0); @@ -2358,6 +2811,7 @@ * ikev2_create_child_responder_cont() is called, and it * calls responder_state1_send() */ +#endif /* sun/OpenSolaris */ done: if (id_data) @@ -3546,6 +4000,19 @@ if (!n_i) goto fail_nomem; +#ifdef sun + /* + * XXX KEBE SAYS start inverse-ACQUIRE proceedings, and have the + * invacq return call ikev2_create_child_responder(). + */ + if (ikev2_send_inverse_acquire(ike_sa, local, remote, message_id, + sa, ts_i, ts_r, cfg, g_i, n_i, &child_param, TRUE, + old_child_sa, msg) != 0) { + goto fail_nomem; + } + /* Fall through to "done" below. */ + +#else err = ikev2_create_child_responder(ike_sa, local, remote, message_id, sa, ts_i, ts_r, cfg, g_i, n_i, &child_param, TRUE, @@ -3560,6 +4027,7 @@ * have its state set to GETSPI. When the state transits to * GETSPI_DONE, create_child_responder_send() gets called. */ +#endif /* sun/OpenSolaris */ done: if (g_i) --- old/iked/ikev2_child.c Fri Mar 27 12:09:59 2009 +++ new/iked/ikev2_child.c Fri Mar 27 12:09:59 2009 @@ -1,4 +1,4 @@ -/* $Id: ikev2_child.c,v 1.108 2008/09/10 08:30:58 fukumoto Exp $ */ +/* $Id: ikev2_child.c,v 1.107 2008/08/18 01:51:16 fukumoto Exp $ */ /* * Copyright (C) 2004-2005 WIDE Project. @@ -348,7 +348,261 @@ return child_sa; } +#ifdef sun +static void +assign_ports(uint16_t pstart, uint16_t pend, uint16_t sysport, + uint16_t *rcstart, uint16_t *rcend) +{ + if (IKEV2_TS_PORT_IS_ANY(pstart, pend)) { + if (sysport != 0) { + *rcstart = sysport; + *rcend = sysport; + } else { + /* Set range to ANY. */ + *rcstart = IKEV2_TS_PORT_MIN; + *rcend = IKEV2_TS_PORT_MAX; + } + } else if (IKEV2_TS_PORT_IS_OPAQUE(pstart, pend)) { + if (sysport != 0) { + /* + * Huh? OPAQUE from peer vs. fixed for us?!? + * Set it to OPAQUE anyway, but log something. + */ + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: Peer said" + "OPAQUE vs. kernel's %d.", ntohs(sysport)); + } + *rcstart = IKEV2_TS_PORT_MAX; + *rcend = IKEV2_TS_PORT_MIN; + } else { + assert(pstart == pend); + assert(sysport == 0 || sysport == pstart); + *rcstart = pstart; + *rcend = pend; + } +} + +static void +assign_addrs(int addrlen, uint8_t *src_start, uint8_t *src_end, + uint8_t *dst_pair) +{ + (void) memcpy(dst_pair, src_start, addrlen); + (void) memcpy(dst_pair + addrlen, src_end, addrlen); +} + /* + * Heavy-lifter of responder_child_sa_params. This compares two PF_KEY + * address extensions to two IKEv2 proposed traffic selector and produces a + * responder traffic selector. Given the current 240x-ness of the OpenSolaris + * kernel, exactly one traffic selector on each side (remote/local) will be + * returned. + * + * This function is only on the responder-side, so "local" is the responder, + * and "remote" is the initiator. PF_KEY has "source" and "dest", which + * further confuses things. Briefly: + * + * initiator == remote == dst AND responder == local == src + */ +static boolean_t +extract_ts(struct sockaddr *local, struct sockaddr *remote, + uint8_t local_preflen, uint8_t remote_preflen, uint8_t proto, + struct ikev2_traffic_selector *pts_local, + struct ikev2_traffic_selector *pts_remote, + struct ikev2_traffic_selector *rcts_local, + struct ikev2_traffic_selector *rcts_remote) +{ + uint16_t selector_length; + uint8_t ts_type, *laddr, *raddr; + struct sockaddr_in *local4, *remote4; + struct sockaddr_in6 *local6, *remote6; + boolean_t local_prefix, remote_prefix; + int addrlen, i; + + local4 = (struct sockaddr_in *)local; + remote4 = (struct sockaddr_in *)remote; + local6 = (struct sockaddr_in6 *)local; + remote6 = (struct sockaddr_in6 *)remote; + + /* First, get length-oriented fields all settled. */ + selector_length = sizeof (*rcts_local); + if (local->sa_family == AF_INET) { + addrlen = sizeof (struct in_addr); + ts_type = IKEV2_TS_IPV4_ADDR_RANGE; + local_prefix = (local_preflen != 32); + remote_prefix = (remote_preflen != 32); + laddr = (uint8_t *)&local4->sin_addr; + raddr = (uint8_t *)&remote4->sin_addr; + } else { + assert(local->sa_family == AF_INET6); + addrlen = sizeof (struct in6_addr); + ts_type = IKEV2_TS_IPV6_ADDR_RANGE; + local_prefix = (local_preflen != 128); + remote_prefix = (remote_preflen != 128); + laddr = (uint8_t *)&local6->sin6_addr; + raddr = (uint8_t *)&remote6->sin6_addr; + } + selector_length += 2 * addrlen; + + /* + * Now finish populating the traffic selector. + * Compare vs. the first one from the peer, as that's what we used + * for inverse-ACQUIRE generation. + * + * Always pick more-specific vs. less specific... i.e. NARROW. + */ + rcts_local->selector_length = htons(selector_length); + rcts_remote->selector_length = htons(selector_length); + rcts_local->ts_type = rcts_remote->ts_type = ts_type; + + if (proto != pts_local->protocol_id && + proto != 0 && pts_local->protocol_id != 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, "WARNING: inequal non-zero " + "protocol IDs, favoring invacq's %d over peer's %d.", + proto, pts_local->protocol_id); + } + if (proto == 0) + proto = pts_local->protocol_id; + rcts_local->protocol_id = rcts_remote->protocol_id = proto; + + /* + * We were picky on the inverse-ACQUIRE construction. We know that: + * + * 1.) If the peer gave a port-range that isn't singleton, ANY, or + * OPAQUE, we picked the low one when generating the + * inverse-ACQUIRE. + * 2.) Any non-singleton address range MUST be able to be expressed + * as base-address/prefix-len or we rejected the negotiation. + * + * Exploit those facts, along with the fact that sin6_port and sin_port + * are at the same offset, here. + */ + + assign_ports(pts_local->start_port, pts_local->end_port, + local4->sin_port, &rcts_local->start_port, + &rcts_local->end_port); + assign_ports(pts_remote->start_port, pts_remote->end_port, + remote4->sin_port, &rcts_remote->start_port, + &rcts_remote->end_port); + + + /* + * For now, kernel matches or rejects (no narrowing), so if we + * get this far, we had an exact prefix match. + */ + assign_addrs(addrlen, laddr, laddr, (uint8_t *)(rcts_local + 1)); + if (local_prefix) { + addrmask(addrlen, local_preflen, (uint8_t *)(rcts_local + 1), + ((uint8_t *)(rcts_local + 1)) + addrlen); + } + + assign_addrs(addrlen, raddr, raddr, (uint8_t *)(rcts_remote + 1)); + if (remote_prefix) { + addrmask(addrlen, remote_preflen, (uint8_t *)(rcts_remote + 1), + ((uint8_t *)(rcts_remote + 1)) + addrlen); + } +} + +/* + * Extract traffic-selectors and other goodies from the inverse-ACQUIRE + * results as well as the traffic-selectors proposed by the peer. This is the + * OpenSolaris/inverse-ACQUIRE equivalent to ike_conf_find_ikev2sel_by_ts(). + */ +static boolean_t +responder_child_sa_params(struct rcpfk_msg *invacq, + struct ikev2_payload_header *proposed_ts_i, + struct ikev2_payload_header *proposed_ts_r, + struct ikev2_child_param *param) +{ + struct ikev2payl_traffic_selector *ptsph_remote, *ptsph_local; + struct ikev2payl_ts_h *rctsh_remote, *rctsh_local; + struct ikev2_traffic_selector *pts_local, *pts_remote, *rcts_local, + *rcts_remote; + struct sockaddr *local, *remote; + uint8_t proto, local_preflen, remote_preflen; + int addrlen; + + /* + * Fill in "out" parameters of &child_sa->child_param, + * which include: + * + * single_pair_required (don't touch, zeroed out by default?) + * ts_i + * ts_r + * notify_code (currently unused) + * cfg_payload & friends below. (later/TODO) + * + */ + ptsph_remote = (struct ikev2payl_traffic_selector *)proposed_ts_i; + ptsph_local = (struct ikev2payl_traffic_selector *)proposed_ts_r; + pts_remote = (struct ikev2_traffic_selector *)(ptsph_remote + 1); + pts_local = (struct ikev2_traffic_selector *)(ptsph_local + 1); + + /* Assume *param is zeroed out. */ + + /* + * All other payload fields are dependent on the results of the + * proposed traffic-selectors vs. the inverse-ACQUIRE. Extract + * inverse-acquire results to see what we all have. + */ + if (invacq->samode == RCT_IPSM_TRANSPORT) { + /* Fill in with tranport-mode selectors. */ + local = invacq->sa_src; + remote = invacq->sa_dst; + if (local->sa_family == AF_INET) { + addrlen = sizeof (struct in_addr); + } else { + assert(local->sa_family == AF_INET6); + addrlen = sizeof (struct in6_addr); + } + local_preflen = remote_preflen = (addrlen << 3); + proto = invacq->ul_proto; + } else { + /* Fill in with tunnel-mode selectors. */ + local = invacq->sa_isrc; + remote = invacq->sa_idst; + local_preflen = invacq->pref_isrc; + remote_preflen = invacq->pref_idst; + if (local->sa_family == AF_INET) { + addrlen = sizeof (struct in_addr); + } else { + assert(local->sa_family == AF_INET6); + addrlen = sizeof (struct in6_addr); + } + proto = invacq->ul_iproto; + } + + assert(local->sa_family == remote->sa_family); + + /* + * Allocate and assign our traffic selectors. Due the the 240x-ness + * of the kernel, for now we will always return one (1) traffic + * selector. Lucky us, though, rc_vmalloc() always zeroes the + * allocation. + */ + + param->ts_i = rc_vmalloc(2 * addrlen + sizeof (struct ikev2payl_ts_h) + + sizeof (struct ikev2_traffic_selector)); + if (param->ts_i == NULL) + return (B_FALSE); + param->ts_r = rc_vmalloc(2 * addrlen + sizeof (struct ikev2payl_ts_h) + + sizeof (struct ikev2_traffic_selector)); + if (param->ts_r == NULL) { + rc_vfree(param->ts_i); + return (B_FALSE); + } + + rctsh_remote = (struct ikev2payl_ts_h *)param->ts_i->v; + rctsh_remote->num_ts = 1; + rctsh_local = (struct ikev2payl_ts_h *)param->ts_r->v; + rctsh_local->num_ts = 1; + rcts_remote = (struct ikev2_traffic_selector *)(rctsh_remote + 1); + rcts_local = (struct ikev2_traffic_selector *)(rctsh_local + 1); + + return (extract_ts(local, remote, local_preflen, remote_preflen, proto, + pts_local, pts_remote, rcts_local, rcts_remote)); +} +#endif /* sun/OpenSolaris */ + +/* * creates a responder child_sa * then issues GETSPI */ @@ -365,7 +619,11 @@ rc_vchar_t *n_i, struct ikev2_child_param *child_param, int is_createchild, - struct ikev2_child_sa *old_child_sa) + struct ikev2_child_sa *old_child_sa +#ifdef sun + , struct rcpfk_msg *invacq +#endif + ) { size_t nonce_size; struct prop_pair **parsed_sa = 0; @@ -400,6 +658,14 @@ child_sa->local = rcs_sadup(local); child_sa->remote = rcs_sadup(remote); +#ifdef sun + /* + * OpenSolaris doesn't want to see ISAKMP ports in local/remote + * addresses. + */ + set_port(child_sa->local, 0); + set_port(child_sa->remote, 0); +#endif #ifdef ENABLE_NATT if (child_sa->parent->local) @@ -426,6 +692,26 @@ goto internal_address_failure; } +#ifdef sun + /* + * XXX KEBE SAYS these need to be fed into inverse-ACQUIRE. + * We split here, get an inverse-ACQUIRE, and then create sel + * using extract_extended_acquire(invacq_param, &sel, NULL); + * Once we have that, resume processing where child_sa->selector + * is assigned. If failure, send back ts_unacceptable or some other + * such error. + */ + if (invacq == NULL) { + /* Policy mismatch or other PF_KEY error. */ + goto no_proposal_chosen; + } + + if (extract_extended_acquire(invacq, &sel, NULL) != 0 || + !responder_child_sa_params(invacq, proposed_ts_i, proposed_ts_r, + &child_sa->child_param)) + goto fail_internal; + +#else /* sun/OpenSolaris */ /* choose conf by ts_i and ts_r, use_transport_mode */ /* and obtain matching ts_i and ts_r in child_param */ sel = ike_conf_find_ikev2sel_by_ts(proposed_ts_i, proposed_ts_r, @@ -436,6 +722,7 @@ /* single_pair_required? */ goto ts_unacceptable; } +#endif /* sun/OpenSolaris */ child_sa->selector = sel; if (cfg) { @@ -540,6 +827,7 @@ child_sa->peer_proposal = matching_peer_proposal; matching_peer_proposal = 0; + /* XXX KEBE ASKS --> do we need this? */ /* XXX generate policy */ if (!LIST_EMPTY(&child_sa->lease_list)) { struct rcf_address *a; @@ -924,6 +1212,7 @@ struct sockaddr *peer_addr; struct sockaddr_storage my_ss, peer_ss; + (void) memset(¶m, 0, sizeof (param)); assert(child_sa->state == IKEV2_CHILD_STATE_IDLING); ikev2_child_state_set(child_sa, IKEV2_CHILD_STATE_GETSPI); @@ -1754,6 +2043,7 @@ { struct rcpfk_msg param; + (void) memset(¶m, 0, sizeof (param)); switch (protocol_id) { case IKEV2PROPOSAL_ESP: param.satype = RCT_SATYPE_ESP; --- old/iked/ikev2_impl.h Fri Mar 27 12:10:00 2009 +++ new/iked/ikev2_impl.h Fri Mar 27 12:10:00 2009 @@ -1,4 +1,4 @@ -/* $Id: ikev2_impl.h,v 1.73 2008/09/10 08:30:58 fukumoto Exp $ */ +/* $Id: ikev2_impl.h,v 1.72 2008/02/06 08:09:00 mk Exp $ */ /* * Copyright (C) 2004-2005 WIDE Project. @@ -423,7 +423,11 @@ struct ikev2_payload_header *, struct ikev2_payload_header *, struct ikev2_payload_header *, struct ikev2_payload_header *, rc_vchar_t *, rc_vchar_t *, struct ikev2_child_param *, int, - struct ikev2_child_sa *); + struct ikev2_child_sa * +#ifdef sun + , struct rcpfk_msg * +#endif /* sun/OpenSolaris */ + ); extern int ikev2_set_negotiated_sa(struct ikev2_sa *, struct ikev2_isakmpsa *); extern void ikev2_set_rmconf(struct ikev2_sa *, struct rcf_remote *); extern struct rc_idlist *ikev2_my_id_list(struct ikev2_sa *); @@ -551,6 +555,9 @@ extern const char *ikev2_state_str(int); extern const char *ikev2_child_state_str(int); +extern void addrmask(int, int, uint8_t *, uint8_t *); + + uint32_t ikev2_request_id(struct ikev2_sa *); /* XXX should be in nattraversal.h, need sorting */ --- old/iked/ikev2_payload.c Fri Mar 27 12:10:00 2009 +++ new/iked/ikev2_payload.c Fri Mar 27 12:10:00 2009 @@ -888,7 +888,7 @@ /* * calculate start &= ~((-1) >> prefixlen), end |= ((-1) >> prefixlen) */ -static void +void addrmask(int addrlen, int prefixlen, uint8_t *start, uint8_t *end) { int i; --- old/iked/isakmp.c Fri Mar 27 12:10:00 2009 +++ new/iked/isakmp.c Fri Mar 27 12:10:00 2009 @@ -102,7 +102,8 @@ # define SOL_UDP 17 # endif # endif /* __linux__ */ -# if defined(__NetBSD__) || defined(__FreeBSD__) +/* XXX KEBE SAYS OpenSolaris works like *BSD here, try defined(sun) for now. */ +# if defined(__NetBSD__) || defined(__FreeBSD__) || defined(sun) # include # include # define SOL_UDP IPPROTO_UDP @@ -185,6 +186,7 @@ if (ikev2_init() < 0) goto err; +#ifndef sun /* XXX KEBE SAYS OpenSolaris has the kernel do keepalives. */ #ifdef ENABLE_NATT /* XXX for IKEv1 */ { extern void natt_keepalive_init(void); @@ -191,6 +193,7 @@ natt_keepalive_init(); } #endif +#endif if (isakmp_open() < 0) goto err; @@ -535,10 +538,17 @@ #ifdef ENABLE_NATT if (SOCKADDR_FAMILY(sa) == AF_INET && port == IKEV2_UDP_PORT_NATT) { +#if 1 /* XXX OpenSolaris equivalent... */ + int on = 1; + + if (setsockopt(p->sock, IPPROTO_UDP, UDP_NAT_T_ENDPOINT, &on, + sizeof (on)) < 0) { +#else int option = UDP_ENCAP_ESPINUDP; if (setsockopt(p->sock, SOL_UDP, UDP_ENCAP, &option, sizeof(option)) < 0) { +#endif plog(PLOG_INTWARN, PLOGLOC, NULL, "setsockopt(%s): %s\n", "UDP_ENCAP_ESPINUDP", strerror(errno)); @@ -673,10 +683,13 @@ } goto end; } -#ifdef ENABLE_NATT +#if defined(ENABLE_NATT) && !defined(sun) /* * we don't know about portchange yet, * look for non-esp marker instead + * + * XXX KEBE SAYS OpenSolaris makes this unnecessary with its + * in-kernel zero-SPI stuff. */ if (x.non_esp[0] == 0 && x.non_esp[1] != 0) { extralen = NON_ESP_MARKER_LEN; @@ -840,6 +853,15 @@ return (error); } +#ifdef sun + /* XXX KEBE SAYS OpenSolaris bypass of spmd. */ +typedef struct { + struct rcpfk_msg *param; + struct isakmp_acquire_request *req; +} pfkmsg_use_t; +#endif + + /* * Initiate a negotiation * @@ -848,11 +870,13 @@ */ void isakmp_initiate(struct sadb_request_method *callback_method, - uint32_t spid, - uint32_t request_msg_seq, unsigned int satype, - struct sockaddr *src, struct sockaddr *dst, - struct sockaddr *src2) + struct rcpfk_msg *param) { + uint32_t spid = param->slid; + uint32_t request_msg_seq = param->seq; + unsigned int satype = param->satype; + struct sockaddr *src = param->sa_src, *dst = param->sa_dst, + *src2 = param->sa2_src; struct isakmp_acquire_request *req; int err = ECONNREFUSED; @@ -864,13 +888,37 @@ req->request_msg_seq = request_msg_seq; req->src = rcs_sadup(src); req->dst = rcs_sadup(dst); + + /* + * XXX KEBE SAYS zero-out ports from the ACQUIRE in the + * isakmp_acquire_request, otherwise, we risk having IKE go to the + * remote-port specified in the ACQUIRE message. + */ + ((struct sockaddr_in *)req->src)->sin_port = 0; + ((struct sockaddr_in *)req->dst)->sin_port = 0; if (src2) req->src2 = rcs_sadup(src2); else req->src2 = NULL; - if (!debug_spmif) +#ifdef sun + if (spid == 0) { + /* + * XXX KEBE SAYS OpenSolaris code... + * + * Let's do something to make isakmp_initiate_cont() do + * the right thing and fill up selector, policy, and rm_info + * appropriately. + */ + pfkmsg_use_t arg; + + arg.req = req; + arg.param = param; + isakmp_initiate_cont(&arg, ""); + } else +#endif + if (!debug_spmif) { err = ike_spmif_post_slid(req, spid); - else { + } else { char sel[11]; snprintf(sel, sizeof(sel), "%u", spid); isakmp_initiate_cont(req, sel); @@ -883,6 +931,7 @@ { struct rcpfk_msg param; + (void) memset(¶m, 0, sizeof (param)); param.seq = req->request_msg_seq; param.eno = err; /* rcpfk_send_acquire() requires satype eventhough kernel doesn't use it */ @@ -964,6 +1013,412 @@ return; } +#ifdef sun +static struct rc_addrlist * +generate_addrlist(struct sockaddr *sa, int prefixlen) +{ + struct rc_addrlist *alist; + int salen; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; + + alist = rc_malloc(sizeof (*alist)); + if (alist == NULL) + return (NULL); + + alist->next = NULL; + alist->port = ntohs(sin6->sin6_port); + /* prefixlen == -1 means set the prefix to the address-size. */ + alist->prefixlen = (prefixlen != -1) ? prefixlen : + ((sin6->sin6_family == AF_INET) ? 32 : 128); + alist->type = RCT_ADDR_INET; + salen = (sin6->sin6_family == AF_INET) ? sizeof (struct sockaddr_in) : + sizeof (*sin6); + alist->a.ipaddr = rc_malloc(salen); + if (alist->a.ipaddr == NULL) { + rc_free(alist); + return (NULL); + } + (void) memcpy(alist->a.ipaddr, sin6, salen); + return (alist); +} + +/* XXX KEBE ASKS - should these belong in if_pfkeyv2-solaris.c? */ +typedef struct { + int pfkey; + int rctype; +} algmap_t; +static algmap_t aalgmap[] = { + /* Sigh. Wish there was a way to map with ipsecalgs. */ + {SADB_AALG_MD5HMAC, RCT_ALG_HMAC_MD5}, + {SADB_AALG_SHA1HMAC, RCT_ALG_HMAC_SHA1}, + {SADB_AALG_SHA256HMAC, RCT_ALG_HMAC_SHA2_256}, + {SADB_AALG_SHA384HMAC, RCT_ALG_HMAC_SHA2_384}, + {SADB_AALG_SHA512HMAC, RCT_ALG_HMAC_SHA2_512}, + {-1, -1} +}; +static algmap_t ealgmap[] = { + {SADB_EALG_DESCBC, RCT_ALG_DES_CBC}, + {SADB_EALG_3DESCBC, RCT_ALG_DES3_CBC}, + {SADB_EALG_BLOWFISH, RCT_ALG_BLOWFISH_CBC}, + {SADB_EALG_NULL, RCT_ALG_NULL_ENC}, + {SADB_EALG_AES, -2}, + {-1, -1} +}; + +static struct rc_alglist * +handle_aes_algdesc(sadb_x_algdesc_t *algdesc) +{ + struct rc_alglist *rc = NULL, *newrc; + int keylen = algdesc->sadb_x_algdesc_minbits; + + /* + * SHOOT! Racoon2 treat AES key sizes as different algs. + * Hardwire increment of 64, and return a real LIST in rc_alglist. + */ + + while (keylen <= algdesc->sadb_x_algdesc_maxbits) { + newrc = rc_malloc(sizeof (*rc)); + if (newrc == NULL) { + while (rc != NULL) { + newrc = rc; + rc = newrc->next; + rc_free(newrc); + } + return (NULL); + } + newrc->next = rc; + /* + * Head insert so we get the highest key values up + * front (in case racoon2 only likes one in its + * alglist for a given rcf_sa...). + */ + rc = newrc; + rc->key = NULL; + rc->keylen = keylen; + + switch (keylen) { + case 128: + rc->algtype = RCT_ALG_AES128_CBC; + break; + case 192: + rc->algtype = RCT_ALG_AES192_CBC; + break; + case 256: + rc->algtype = RCT_ALG_AES256_CBC; + break; + } + + keylen += 64; + } + + return (rc); +} + +static struct rc_alglist * +algdesc_to_rc_alglist(sadb_x_algdesc_t *algdesc, boolean_t ikev2) +{ + struct rc_alglist *rc; + algmap_t *mapper; + + switch (algdesc->sadb_x_algdesc_algtype) { + case SADB_X_ALGTYPE_AUTH: + /* Put this into rc_type.c? */ + mapper = aalgmap; + break; + case SADB_X_ALGTYPE_CRYPT: + mapper = ealgmap; + break; + default: + free(rc); + return (NULL); + } + + for ( ; mapper->rctype != -1; mapper++) + if (mapper->pfkey == algdesc->sadb_x_algdesc_alg) + break; + + if (mapper->rctype == -1) + return (NULL); + + if (mapper->rctype == -2) { + assert(mapper->pfkey == SADB_EALG_AES); + return (handle_aes_algdesc(algdesc)); + } + + /* + * If we reach here, we have a fixed-key-size algorithm. + */ + rc = rc_calloc(1, sizeof (*rc)); /* Zero-out for the common case. */ + if (rc == NULL) + return NULL; + rc->algtype = mapper->rctype; + + if (ikev2) { + /* + * Section 3.3.5 of RFC 4306 says: + * + * - Key Length + * + * When using an Encryption Algorithm that has a + * variable-length key, this attribute specifies the key + * length in bits (MUST use network byte order). This + * attribute MUST NOT be used when the specified Encryption + * Algorithm uses a fixed-length key. + */ + /* + * KEBE wonders if we should just do this all the time? + */ + rc->keylen = 0; + } else { + if (mapper->pfkey == SADB_EALG_BLOWFISH) { + /* XXX For now, hardwire to 128-bit keys. :-P */ + rc->keylen = 128; + } else { + assert(algdesc->sadb_x_algdesc_maxbits == + algdesc->sadb_x_algdesc_minbits); + rc->keylen = algdesc->sadb_x_algdesc_maxbits; + } + } + + return (rc); +} + +static struct rcf_sa * +ecomb_to_rcf_sa(sadb_x_ecomb_t *ecomb, sadb_x_algdesc_t *algdesc, + struct rcf_sa *esp_sa, boolean_t ikev2) +{ + struct rcf_sa *sa; + + /* Use calloc() to zero-out everything. */ + sa = rc_calloc(1, sizeof (*sa)); + if (sa == NULL) + return (NULL); + switch (algdesc->sadb_x_algdesc_satype) { + case SADB_SATYPE_ESP: + sa->sa_protocol = RCT_SATYPE_ESP; + if (esp_sa != NULL) { + /* Already hit an ESP algdesc. */ + rc_free(sa); + sa = esp_sa; + } + if (algdesc->sadb_x_algdesc_algtype == SADB_X_ALGTYPE_AUTH) { + assert(sa->auth_alg == NULL); + sa->auth_alg = algdesc_to_rc_alglist(algdesc, ikev2); + if (sa->auth_alg == NULL) + goto midalloc_bail; + } else { + assert(algdesc->sadb_x_algdesc_algtype == + SADB_X_ALGTYPE_CRYPT); + assert(sa->enc_alg == NULL); + sa->enc_alg = algdesc_to_rc_alglist(algdesc, ikev2); + if (sa->enc_alg == NULL) + goto midalloc_bail; + } + break; + case SADB_SATYPE_AH: + sa->sa_protocol = RCT_SATYPE_AH; + sa->auth_alg = algdesc_to_rc_alglist(algdesc, ikev2); + if (sa->auth_alg == NULL) + goto midalloc_bail; + break; + default: + /* + * Should never get here, but eventually log, + * and handle gracefully right now. Clean up + * and free newbie, then return what we have. + */ +midalloc_bail: + if (sa != esp_sa) { + free(sa); + sa = NULL; + } + } + + return (sa); +} + +/* + * Convert extended ACQUIRE proposal into newpol->rcf_ipsec, which is + * annoying, but straightforward. + */ +static struct rcf_ipsec * +eprop_to_rcf_ipsec(sadb_prop_t *eprop, boolean_t ikev2) +{ + sadb_x_ecomb_t *ecomb = (sadb_x_ecomb_t *)(eprop + 1); + sadb_x_algdesc_t *algdesc; + uint64_t *end = (uint64_t *)eprop + eprop->sadb_prop_len; + int i, j; + struct rcf_ipsec *rc = NULL, *last = NULL, *newbie; + struct rcf_sa *sa, *esp_sa = NULL; + uint64_t time, bytes; + + /* + * Go through each ecomb, plucking out what's needed. + */ + for (i = 0; i < eprop->sadb_x_prop_numecombs; i++) { + /* Use calloc() to zero-out everything. */ + newbie = rc_calloc(1, sizeof (*rc)); + if (newbie == NULL) + goto bail; + newbie->ipsec_sa_lifetime_time = + ecomb->sadb_x_ecomb_hard_addtime; + newbie->ipsec_sa_lifetime_byte = /* KB conversion? */ + ecomb->sadb_x_ecomb_hard_bytes; + /* For now, OpenSolaris doesn't support 64-bit sequence nos. */ + newbie->ext_sequence = RCT_BOOL_OFF; + + algdesc = (sadb_x_algdesc_t *)(ecomb + 1); + /* Parse algdescs and put 'em into rcf_sa structures. */ + for (j = 0; j < ecomb->sadb_x_ecomb_numalgs; j++, algdesc++) { + sa = ecomb_to_rcf_sa(ecomb, algdesc, esp_sa, ikev2); + if (sa == NULL) { + rcf_clean_ipsec_list(newbie); + goto bail; + } + switch (sa->sa_protocol) { + case RCT_SATYPE_ESP: + assert(esp_sa == NULL || esp_sa == sa); + esp_sa = sa; + newbie->sa_esp = esp_sa; + break; + case RCT_SATYPE_AH: + newbie->sa_ah = sa; + break; + default: + assert(0); /* Should never reach here. */ + break; + } + } + + /* Tail-insert the newbie. */ + if (rc != NULL) { + last->next = newbie; + last = newbie; + } else { + rc = last = newbie; + } + } + +bail: + return (rc); +} + +/* + * XXX KEBE ASKS - should this belong in if_pfkeyv2-solaris.c or + * ike_pfkey.c? + */ +int +extract_extended_acquire(rcpfk_msg_t *param, + struct rcf_selector **selector, struct rcf_remote **rm_info) +{ + struct rcf_selector *newsel; + struct rcf_addrlist *newaddr; + struct rcf_policy *newpol; + struct rcf_remote *newrm; + uint16_t saveport; + boolean_t ikev2; + + /* + * XXX KEBE SAYS selector must be able to be freed by + * rcf_free_selector(), that policy is not explicitly freed but is + * part of rcf_selector, and that rm_info needs to be freeable by + * rcf_free_remote(). + * + * Return ENOMEM on any allocation errors, freeing all of the garbage + * along the way. + */ + + /* + * First, fill in the rm_info. There is rcf_get_remotebyaddr(), + * so let's use that for now. + * + * XXX KEBE SAYS: This is akin to in.iked's get_phase1()'s + * rule-matching, but here, it's only the remote-peer. This isn't + * great, but we'll need different rcf_get_remote*() functions, most + * likely. + * + * XXX KEBE ALSO SAYS, do IKEv2 first. + */ + if (rm_info != NULL && + rcf_get_remotebyaddr(param->sa_dst, RCT_KMP_IKEV2, &newrm) == -1 && + rcf_get_remotebyaddr(param->sa_dst, RCT_KMP_IKEV1, &newrm) == -1) + return (ESRCH); /* XXX KEBE ASKS, better return code? */ + + ikev2 = (newrm->ikev2 != NULL); + + /* + * Next, the rcf_selector (and all of its substructures, including + * rcf_policy). + */ + newsel = rc_calloc(1, sizeof (*newsel)); /* Free zeroing. */ + if (newsel == NULL) { + if (rm_info != NULL) + rcf_free_remote(newrm); + return (ENOMEM); + } + newsel->direction = RCT_DIR_OUTBOUND; + newpol = rc_calloc(1, sizeof (*newpol)); + if (newpol == NULL) { + if (rm_info != NULL) + rcf_free_remote(newrm); + rc_free(newsel); + return (ENOMEM); + } + newsel->pl = newpol; + + /* + * Let sl_index, next, and tagged be NULL for now. + */ + + /* + * XXX KEBE ASKS - For tunnel mode - where do we put the isrc/idst? + * Policy or selector? Given the structure of what I've seen in KAME, + * say isrc/idst in selector, and src/dst in policy or selector. + * + * While I'm here, WTF is next_header_including used for? + */ + if (param->samode == RCT_IPSM_TRANSPORT) { + /* Fill in the one that needs tunnel with transport. */ + newsel->src = generate_addrlist(param->sa_src, -1); + newsel->dst = generate_addrlist(param->sa_dst, -1); + newsel->upper_layer_protocol = param->ul_proto; + newpol->ipsec_mode = RCT_IPSM_TRANSPORT; + } else { + /* Fill in the one that needs tunnel with tunnel. */ + newsel->src = generate_addrlist(param->sa_isrc, + param->pref_isrc); + newsel->dst = generate_addrlist(param->sa_idst, + param->pref_idst); + newsel->upper_layer_protocol = param->ul_iproto; + newpol->ipsec_mode = RCT_IPSM_TUNNEL; + } + + newpol->action = RCT_ACT_AUTO_IPSEC; + /* + * Ignore newpol->install, and newpol->ipsec_level (see ike_conf.c). + */ + newpol->my_sa_ipaddr = generate_addrlist(param->sa_src, -1); + newpol->peers_sa_ipaddr = generate_addrlist(param->sa_dst, -1); + + /* + * XXX KEBE SAYS Convert extended ACQUIRE proposal into + * newpol->rcf_ipsec, which is non-trivial. + */ + newpol->ips = eprop_to_rcf_ipsec(param->eprop, ikev2); + if (newpol->ips == NULL) { + /* XXX KEBE SAYS FILL ME IN! */ + if (rm_info != NULL) + rcf_free_remote(newrm); + rcf_free_selector(newsel); /* frees newpol as a side-effect */ + return (ENOMEM); + } + *selector = newsel; + if (rm_info != NULL) + *rm_info = newrm; + return (0); +} +#endif + void isakmp_initiate_cont(void *tag, const char *selector_index) { @@ -973,6 +1428,18 @@ struct isakmp_acquire_request *req = 0; int err = ECONNREFUSED; +#ifdef sun + if (selector_index[0] == '\0') { + pfkmsg_use_t *arg = (pfkmsg_use_t *)tag; + + req = arg->req; + err = extract_extended_acquire(arg->param, &selector, &rm_info); + if (err != 0) + goto fail; + policy = selector->pl; + } else { +#endif + req = (struct isakmp_acquire_request *)tag; /* Receiving SADB_ACQUIRE: @@ -1010,6 +1477,9 @@ (int)policy->rm_index->l, policy->rm_index->v); goto fail; } +#ifdef sun + } +#endif switch (ike_initiate_kmp(rm_info)) { case RCT_KMP_IKEV2: @@ -1049,6 +1519,7 @@ { struct rcpfk_msg param; + (void) memset(¶m, 0, sizeof (param)); param.seq = req->request_msg_seq; param.eno = err; /* rcpfk_send_acquire() requires satype eventhough kernel doesn't use it */ @@ -1683,7 +2154,7 @@ "failed to find a socket for transmission\n"); return; } -#ifdef ENABLE_NATT +#if defined(ENABLE_NATT) && !defined(sun) { int do_encap; --- old/iked/isakmp.h Fri Mar 27 12:10:01 2009 +++ new/iked/isakmp.h Fri Mar 27 12:10:01 2009 @@ -44,6 +44,10 @@ #define GENERATE 1 #define VALIDATE 0 +#if 1 /* XXX OpenSolaris - Sun Studio compilers implicitly pack, IIRC. */ +#define __attribute__(x) +#endif + /* 3.1 ISAKMP Header Format 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --- old/iked/isakmp_impl.h Fri Mar 27 12:10:01 2009 +++ new/iked/isakmp_impl.h Fri Mar 27 12:10:01 2009 @@ -197,6 +197,12 @@ struct sockaddr *src; struct sockaddr *dst; struct sockaddr *src2; +#ifdef sun + /* + * XXX KEBE SAYS ACQUIRE or inverse-ACQUIRE for use in IKEv1's + * ph2handle or IKEv2's ??!!??. + */ +#endif }; /* @@ -300,9 +306,7 @@ extern int isakmp_fdset(fd_set *); extern int isakmp_isset(fd_set *); -extern void isakmp_initiate(struct sadb_request_method *, uint32_t, uint32_t, - unsigned int, struct sockaddr *, struct sockaddr *, - struct sockaddr *); +extern void isakmp_initiate(struct sadb_request_method *, struct rcpfk_msg *); extern void isakmp_force_initiate(const char *, const char *); extern void isakmp_initiate_cont(void *, const char *); extern rc_vchar_t *isakmp_p2v(struct isakmp_gen *); --- old/iked/main.c Fri Mar 27 12:10:02 2009 +++ new/iked/main.c Fri Mar 27 12:10:01 2009 @@ -183,6 +183,13 @@ char *dest_selector = 0; extern char *optarg; +#ifdef sun + printf("sizes: payload %d, tsh %d, paylts %d\n", + sizeof (struct ikev2_payload_header), + sizeof (struct ikev2payl_ts_h), + sizeof (struct ikev2payl_traffic_selector)); +#endif + setprogname(argv[0]); for (;;) { @@ -386,11 +393,17 @@ #endif +#ifndef sun + /* + * XXX KEBE SAYS until we prove we need spmd for something, + * don't bother. + */ if (!debug_spmif && ike_spmif_init() == -1) { plog(PLOG_CRITICAL, PLOGLOC, 0, "failed initializing SPMIF interface\n"); iked_exit(IKED_EXIT_FAILURE); } +#endif if (isakmp_init() != 0) { plog(PLOG_CRITICAL, PLOGLOC, NULL, "failed initializing isakmp handling\n"); --- old/iked/ratelimit.c Fri Mar 27 12:10:02 2009 +++ new/iked/ratelimit.c Fri Mar 27 12:10:02 2009 @@ -34,6 +34,7 @@ #include #include +#include "racoon.h" #include "ratelimit.h" #include "plog.h" #include "var.h" --- old/iked/sockmisc.c Fri Mar 27 12:10:02 2009 +++ new/iked/sockmisc.c Fri Mar 27 12:10:02 2009 @@ -30,6 +30,11 @@ * SUCH DAMAGE. */ +#ifdef sun /* XXX KEBE SAYS OpenSolaris - use xnet sockets for this. */ +#define _XOPEN_SOURCE 500 +#define __EXTENSIONS__ +#endif + #include #include @@ -56,7 +61,9 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# ifndef sun /* XXX KEBE SAYS OpenSolaris. */ +# include +# endif # ifndef IP_IPSEC_POLICY /* < usagi in.h rev 1.2 / 1.1.1.4 */ # define IP_IPSEC_POLICY 16 /* */ # endif @@ -274,7 +281,9 @@ sin = (struct sockaddr_in *)to; memset(sin, 0, sizeof(*sin)); sin->sin_family = AF_INET; +#ifndef sun /* XXX OpenSolaris doesn't have sin_len. */ sin->sin_len = sizeof(*sin); +#endif memcpy(&sin->sin_addr, CMSG_DATA(cm), sizeof(sin->sin_addr)); sin->sin_port = ((struct sockaddr_in *)&ss)->sin_port; @@ -522,18 +531,30 @@ int setsockopt_bypass(int fd, int family) { +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + ipsec_req_t policy; +#else struct sadb_x_policy policy; +#endif int level, optname; switch (family) { case AF_INET: level = IPPROTO_IP; +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + optname = IP_SEC_OPT; +#else optname = IP_IPSEC_POLICY; +#endif break; #ifdef INET6 case AF_INET6: level = IPPROTO_IPV6; +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + optname = IPV6_SEC_OPT; +#else optname = IPV6_IPSEC_POLICY; +#endif break; #endif default: @@ -543,6 +564,11 @@ } memset(&policy, 0, sizeof(policy)); +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + policy.ipsr_ah_req = IPSEC_PREF_NEVER; + policy.ipsr_esp_req = IPSEC_PREF_NEVER; + policy.ipsr_self_encap_req = IPSEC_PREF_NEVER; +#else policy.sadb_x_policy_len = PFKEY_UNIT64(sizeof(policy)); policy.sadb_x_policy_exttype = SADB_X_EXT_POLICY; policy.sadb_x_policy_type = IPSEC_POLICY_BYPASS; @@ -553,6 +579,7 @@ return -1; } policy.sadb_x_policy_dir = IPSEC_DIR_OUTBOUND; +#endif if (setsockopt(fd, level, optname, &policy, sizeof(policy)) == -1) { plog(PLOG_INTERR, PLOGLOC, 0, "setsockopt: %s\n", strerror(errno)); --- old/lib/cfsetup.c Fri Mar 27 12:10:03 2009 +++ new/lib/cfsetup.c Fri Mar 27 12:10:03 2009 @@ -242,7 +242,7 @@ static int rcf_fix_ipsec_level (struct cf_list *, void *); /* ipsec */ static int rcf_fix_ipsec (rc_vchar_t *ips_index, struct rcf_ipsec **); -static void rcf_clean_ipsec_list (struct rcf_ipsec *head); +/* static void rcf_clean_ipsec_list (struct rcf_ipsec *head); */ static void rcf_clean_ipsec (struct rcf_ipsec *head); static struct rcf_ipsec *rcf_deepcopy_ipsec (struct rcf_ipsec *); static int rcf_fix_ipsec_sa_lifetime_time (struct cf_list *, void *); @@ -412,7 +412,8 @@ return rcf_tdf[i].tdf; } - return (void *)0; + /* XXX KEBE SAYS return NULL for ALL pointer-return types. */ + return (NULL); } @@ -2300,7 +2301,7 @@ return -1; } -static void +void rcf_clean_ipsec_list(struct rcf_ipsec *head) { struct rcf_ipsec *n, *next; --- old/lib/cftoken.l Fri Mar 27 12:10:03 2009 +++ new/lib/cftoken.l Fri Mar 27 12:10:03 2009 @@ -507,7 +507,11 @@ return -1; } +#if 1 /* KEBE SAYS OpenSolaris has no GLOB_TILDE */ + if (glob(path, 0, NULL, &rcf_istk[rcf_istkp].matches) != 0 || +#else if (glob(path, GLOB_TILDE, NULL, &rcf_istk[rcf_istkp].matches) != 0 || +#endif rcf_istk[rcf_istkp].matches.gl_pathc == 0) { yyerror("glob found no matches for path (%s)", path); return -1; --- /dev/null Fri Mar 27 12:09:21 2009 +++ new/lib/if_pfkeyv2-solaris.c Fri Mar 27 12:10:04 2009 @@ -0,0 +1,1527 @@ +/* + * Copyright (C) 2003 WIDE Project. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Portions Copyright 2008 Sun Microsystems, Inc. All rights reserved. + */ + +/* + * OpenSolaris version of if_pfkeyv2.c. Because OpenSolaris's mods to + * RFC 2367 are so vastly different from the KAME/WIDE stack, it's best to + * reimplement this file from the ground up. Some utility functions from + * the original will survive. + */ + +#ifndef sun /* XXX KEBE SAYS put OpenSolaris check in here */ +#error "This file contains OpenSolaris-specific code." +#endif + +#include +#include +#include +#include +#include +#include + +#include "racoon.h" +#include "pfkeyv2aux.h" +#include + + +/* + * Initialization. + */ + +static pid_t pid; +static boolean_t dont_send; + +static struct rcpfk_cb null_cb = {0}; +static struct rcpfk_cb *cb; + +static struct sadb_supported *supported_map_auth = NULL; +static struct sadb_supported *supported_map_enc = NULL; + +/* + * Utilities from the original if_pfkeyv2.c. These should probably be + * put in their own source file, leaving platform-specific functions to + * separate sources. + */ + +static int rcpfk_recv_getspi(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_update(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_add(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_expire(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_acquire(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_delete(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_get(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_register(caddr_t *, struct rcpfk_msg *); +static int rcpfk_recv_invacq(caddr_t *, struct rcpfk_msg *); + +static struct pfkey_msgtype { + char *name; + int (*recvfunc) (caddr_t *, struct rcpfk_msg *); +} rcpfk_msg[] = { + { "", 0, }, + { "GETSPI", rcpfk_recv_getspi, }, + { "UPDATE", rcpfk_recv_update, }, + { "ADD", rcpfk_recv_add, }, + { "DELETE", rcpfk_recv_delete, }, + { "GET", rcpfk_recv_get, }, + { "ACQUIRE", rcpfk_recv_acquire, }, + { "REGISTER", rcpfk_recv_register, }, + { "EXPIRE", rcpfk_recv_expire, }, + { "FLUSH", 0, }, + { "DUMP", 0, }, + { "X_PROMISC", 0, }, + { "X_INVERSE_ACQUIRE", rcpfk_recv_invacq} +}; + +static struct pfpol_msgtype { + char *name; + int (*recvfunc) (caddr_t *, struct rcpfk_msg *); +} rcpfpol_msg[] = { + { "", 0, }, + { "SPD_FLUSH", 0, }, + { "SPD_ADDRULE", 0, }, + { "SPD_DELETERULE", 0, }, + { "SPD_FLIP", 0, }, + { "SPD_LOOKUP", 0, }, + { "SPD_DUMP", 0, }, + { "SPD_CLONE", 0, }, + { "SPD_ALGLIST", 0, }, + { "SPD_DUMPALGS", 0, }, + { "SPD_UPDATEALGS", 0, }, +}; + +static void +rcpfk_seterror(struct rcpfk_msg *rc, int eno, const char *fmt, ...) +{ + va_list ap; + + rc->eno = eno; + + va_start(ap, fmt); + vsnprintf(rc->estr, sizeof(rc->estr), fmt, ap); + va_end(ap); +} + +static int +rcpfk_close(struct rcpfk_msg *rc) +{ + if (close(rc->so) == -1) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + return 0; +} + +int do_pfkey_dump = 1; + +static int +rcpfk_send(struct rcpfk_msg *rc, rc_vchar_t *buf) +{ + extern void print_samsg(FILE *, uint64_t *, int, int, int); + + /* + * set final message length (XXX here?) + */ + ((struct sadb_msg *)buf->v)->sadb_msg_len = SADB_8TO64(buf->l); + + if (do_pfkey_dump) + print_samsg(stderr, (uint64_t *)(buf->v), 1, 1, 1); + + if (send(rc->so, buf->v, buf->l, 0) == -1) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + return 0; +} + +/* + * append a sadb_spirange to the buffer. + * note that min and max must be in network byte order. + * OUT: total length of the buffer + */ +static int +rcpfk_set_sadbspirange(rc_vchar_t **msg, struct rcpfk_msg *rc, + uint32_t min, uint32_t max) +{ + rc_vchar_t *buf; + struct sadb_spirange *p; + int len, prevlen, extlen; + + extlen = sizeof(struct sadb_spirange); + prevlen = (*msg)->l; + len = prevlen + extlen; + if ((buf = rc_vrealloc(*msg, len)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + p = (struct sadb_spirange *)(buf->v + prevlen); + p->sadb_spirange_len = PFKEY_UNIT64(extlen); + p->sadb_spirange_exttype = SADB_EXT_SPIRANGE; + p->sadb_spirange_min = min; + p->sadb_spirange_max = max; + p->sadb_spirange_reserved = 0; + + *msg = buf; + return 0; +} + +static int +rcpfk_set_sadbmsg(rc_vchar_t **msg, struct rcpfk_msg *rc, int type) +{ + rc_vchar_t *buf; + struct sadb_msg *p; + int len; + + len = sizeof(*p); + if ((buf = rc_vmalloc(len)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + p = (struct sadb_msg *)buf->v; + p->sadb_msg_version = PF_KEY_V2; + p->sadb_msg_type = type; + p->sadb_msg_errno = 0; +#ifndef sun /* XXX KEBE SAYS None of these are in OpenSolaris. */ + switch (type) { + case SADB_X_SPDUPDATE: + case SADB_X_SPDADD: + case SADB_X_SPDDELETE: + case SADB_X_SPDDELETE2: + case SADB_X_SPDDUMP: + /* XXX other SADB_X_SPD* ? */ +#ifdef SADB_X_MIGRATE + case SADB_X_MIGRATE: +#endif + p->sadb_msg_satype = SADB_SATYPE_UNSPEC; + break; + default: + p->sadb_msg_satype = rct2pfk_satype(rc->satype); + break; + } +#else + p->sadb_msg_satype = rct2pfk_satype(rc->satype); +#endif + p->sadb_msg_len = 0; /* must be update before it is sent */ + p->sadb_msg_reserved = 0; + p->sadb_msg_seq = rc->seq; + p->sadb_msg_pid = (uint32_t)pid; + + *msg = buf; + return 0; +} + +/* + * append a sadb_sa to the buffer. + * OUT: total length of the buffer + * spionly == 0 is for SA, spionly == 1 is for SA(*) of RFC 2367. + */ +static int +rcpfk_set_sadbsa(rc_vchar_t **msg, struct rcpfk_msg *rc, int spionly) +{ + rc_vchar_t *buf; + struct sadb_sa *p; + int len, prevlen, extlen; + + extlen = sizeof(struct sadb_sa); + prevlen = (*msg)->l; + len = prevlen + extlen; + if ((buf = rc_vrealloc(*msg, len)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + p = (struct sadb_sa *)(buf->v + prevlen); + p->sadb_sa_len = PFKEY_UNIT64(extlen); + p->sadb_sa_exttype = SADB_EXT_SA; + p->sadb_sa_spi = rc->spi; + if (spionly) { + p->sadb_sa_replay = 0; + p->sadb_sa_state = 0; + p->sadb_sa_auth = 0; + p->sadb_sa_encrypt = 0; + p->sadb_sa_flags = 0; + } else { + p->sadb_sa_replay = rc->wsize; + p->sadb_sa_state = SADB_SASTATE_MATURE; + p->sadb_sa_auth = rct2pfk_authtype(rc->authtype); + if (rc->satype == RCT_SATYPE_AH) + p->sadb_sa_encrypt = SADB_EALG_NONE; + else + p->sadb_sa_encrypt = rct2pfk_enctype(rc->enctype); +#ifdef sun + /* + * Set flags here with NAT-T goodies, tunnel/transport + * goodies, and other flags. + */ + p->sadb_sa_flags = rc->saflags; + if (rc->sa_natlocal != NULL) + p->sadb_sa_flags |= SADB_X_SAFLAGS_NATT_LOC; + if (rc->sa_natremote != NULL) + p->sadb_sa_flags |= SADB_X_SAFLAGS_NATT_REM; + + /* If isrc is NULL, idst must be NULL. */ + if (rc->sa_isrc != NULL) + p->sadb_sa_flags |= SADB_X_SAFLAGS_TUNNEL; +#endif + } + + *msg = buf; + return 0; +} + +/* + * set sadb_lifetime structure after clearing buffer with zero. + * OUT: the pointer of buf + len. + */ +static int +rcpfk_set_sadblifetime(rc_vchar_t **msg, struct rcpfk_msg *rc, int type) +{ + rc_vchar_t *buf; + struct sadb_lifetime *p; + uint64_t lft_time, lft_bytes; + int len, prevlen, extlen; + + switch (type) { + case SADB_EXT_LIFETIME_SOFT: + lft_time = rc->lft_soft_time; + lft_bytes = rc->lft_soft_bytes; + break; + case SADB_EXT_LIFETIME_HARD: + lft_time = rc->lft_hard_time; + lft_bytes = rc->lft_hard_bytes; + break; + default: + rcpfk_seterror(rc, EINVAL, "invalid lifetime type=%d", type); + return -1; + } + extlen = sizeof(*p); + prevlen = (*msg)->l; + len = prevlen + extlen; + if ((buf = rc_vrealloc(*msg, len)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + p = (struct sadb_lifetime *)(buf->v + prevlen); + p->sadb_lifetime_len = PFKEY_UNIT64(extlen); + p->sadb_lifetime_exttype = type & 0xffff; + p->sadb_lifetime_allocations = 0; + p->sadb_lifetime_bytes = lft_bytes; + p->sadb_lifetime_addtime = lft_time; + p->sadb_lifetime_usetime = 0; + + *msg = buf; + return 0; +} + +/* + * append a sadb_address to the buffer. + * OUT: total length of the buffer + */ +static int +rcpfk_set_sadbaddress(rc_vchar_t **msg, struct rcpfk_msg *rc, int type) +{ + rc_vchar_t *buf; + struct sadb_address *p; + struct sockaddr *sa; + int pref = 0, proto; + int len, prevlen, extlen; + + switch (type) { + case SADB_EXT_ADDRESS_SRC: + sa = rc->sa_src; + proto = rc->ul_proto; + break; + case SADB_EXT_ADDRESS_DST: + sa = rc->sa_dst; + proto = rc->ul_proto; + break; + case SADB_X_EXT_ADDRESS_INNER_SRC: + sa = rc->sa_isrc; + pref = rc->pref_isrc; + proto = rc->ul_iproto; + break; + case SADB_X_EXT_ADDRESS_INNER_DST: + sa = rc->sa_idst; + pref = rc->pref_idst; + proto = rc->ul_iproto; + break; + case SADB_X_EXT_ADDRESS_NATT_LOC: + sa = rc->sa_natlocal; + proto = IPPROTO_UDP; + break; + case SADB_X_EXT_ADDRESS_NATT_REM: + sa = rc->sa_natremote; + proto = IPPROTO_UDP; + break; + default: + rcpfk_seterror(rc, EINVAL, "invalid address type=%d", type); + return -1; + } + extlen = sizeof(struct sadb_address) + PFKEY_ALIGN8(SA_LEN(sa)); + prevlen = (*msg)->l; + len = prevlen + extlen; + if ((buf = rc_vrealloc(*msg, len)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + p = (struct sadb_address *)(buf->v + prevlen); + p->sadb_address_len = PFKEY_UNIT64(extlen); + p->sadb_address_exttype = type & 0xffff; + /* + * XXX KEBE SAYS proto was subject to conversion in original racoon2 + * source, but the conversion function was a NOP. + * + * Unfortunately, RCP_PROTO_ANY is 255, which we want to be 0. + */ + p->sadb_address_proto = (proto == RC_PROTO_ANY) ? 0 : proto; + p->sadb_address_prefixlen = pref; + p->sadb_address_reserved = 0; + memcpy(p + 1, sa, SA_LEN(sa)); + +#if 0 + /* Do we need this line? */ + if (rc->flags & PFK_FLAG_NOPORTS) + rcs_setsaport((struct sockaddr *)(p + 1), RC_PORT_IKE); +#else + /* Ewww, this is even grosser. */ + rcs_setsaport((struct sockaddr *)(p + 1), + htons(((struct sockaddr_in *)sa)->sin_port)); +#endif + + *msg = buf; + return 0; +} + +/* + * set sadb_key structure after clearing buffer with zero. + * OUT: the pointer of buf + len. + */ +static int +rcpfk_set_sadbkey(rc_vchar_t **msg, struct rcpfk_msg *rc, int type) +{ + rc_vchar_t *buf; + struct sadb_key *p; + int keytype; + size_t keylen; + caddr_t key; + int len, prevlen, extlen; + + switch (type) { + case SADB_EXT_KEY_AUTH: + keytype = rct2pfk_authtype(rc->authtype); + key = rc->authkey; + keylen = rc->authkeylen; + break; + case SADB_EXT_KEY_ENCRYPT: + keytype = rct2pfk_enctype(rc->enctype); + key = rc->enckey; + keylen = rc->enckeylen; + break; + default: + rcpfk_seterror(rc, EINVAL, "invalid key type=%d", type); + return -1; + } + extlen = sizeof(struct sadb_key) + PFKEY_ALIGN8(keylen); + prevlen = (*msg)->l; + len = prevlen + extlen; + if ((buf = rc_vrealloc(*msg, len)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return -1; + } + + p = (struct sadb_key *)(buf->v + prevlen); + p->sadb_key_len = PFKEY_UNIT64(extlen); + p->sadb_key_exttype = type & 0xffff; + p->sadb_key_bits = keylen << 3; + p->sadb_key_reserved = 0; + memcpy(p + 1, key, keylen); + + *msg = buf; + return 0; +} + + + +/* + * Receiving modules. Copy/paste/modify-if-needed for now. Can revert + * to stock WIDE if mods aren't too bad. + */ + +/* + * receive SADB_GETSPI from kernel. + * OUT: + * rc->spi + * rc->sa_src + * rc->sa_dst + */ +static int +rcpfk_recv_getspi(caddr_t *mhp, struct rcpfk_msg *rc) +{ + struct sadb_msg *base; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + + /* validity check */ + if (mhp[0] == 0 || + mhp[SADB_EXT_SA] == 0 || + mhp[SADB_EXT_ADDRESS_SRC] == 0 || + mhp[SADB_EXT_ADDRESS_DST] == 0) { + rcpfk_seterror(rc, EINVAL, + "inappropriate GETSPI message passed"); + return -1; + } + base = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + + rc->seq = base->sadb_msg_seq; + rc->satype = pfk2rct_satype(base->sadb_msg_satype); + if (rc->satype == 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, "recv_getspi: satype == 0"); + return -1; + } + rc->spi = sa->sadb_sa_spi; + rc->sa_src = (struct sockaddr *)&rc->sa_src_storage; + rc->sa_dst = (struct sockaddr *)&rc->sa_dst_storage; + memcpy(rc->sa_src, src, SA_LEN(src)); + memcpy(rc->sa_dst, dst, SA_LEN(dst)); + + if (cb->cb_getspi != 0 && cb->cb_getspi(rc) < 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, cb->cb_getspi == NULL ? + "NULL cb_getspi" : "cb_getspi returned non-zero."); + return -1; + } + + return 0; +} + +/* + * receive SADB_UPDATE from kernel. + * OUT: + */ +static int +rcpfk_recv_update(caddr_t *mhp, struct rcpfk_msg *rc) +{ + struct sadb_msg *base; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + + /* XXX KEBE ASKS -- Do we need to map isrc/idst for tunnel-mode SAs? */ + + /* validity check */ + if (mhp[0] == 0 || + mhp[SADB_EXT_SA] == 0 || + mhp[SADB_EXT_ADDRESS_SRC] == 0 || + mhp[SADB_EXT_ADDRESS_DST] == 0) { + rcpfk_seterror(rc, EINVAL, + "inappropriate UPDATE message passed"); + return -1; + } + base = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + + rc->seq = base->sadb_msg_seq; + rc->satype = pfk2rct_satype(base->sadb_msg_satype); + if (rc->satype == 0) + return -1; + rc->spi = sa->sadb_sa_spi; + rc->sa_src = (struct sockaddr *)&rc->sa_src_storage; + rc->sa_dst = (struct sockaddr *)&rc->sa_dst_storage; + memcpy(rc->sa_src, src, SA_LEN(src)); + memcpy(rc->sa_dst, dst, SA_LEN(dst)); + + /* + * Set samode by using presence of inner-src/inner-dst. + * We could use SADB_X_SAFLAGS_TUNNEL also, but this isrc/idst is + * easier to pluck for now. Do direct RCT mapping for now also. + */ + rc->samode = mhp[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL ? + RCT_IPSM_TUNNEL : RCT_IPSM_TRANSPORT; + + if (cb->cb_update != 0 && cb->cb_update(rc) < 0) + return -1; + + return 0; +} + +/* + * receive SADB_ADD from kernel. + * OUT: + */ +static int +rcpfk_recv_add(caddr_t *mhp, struct rcpfk_msg *rc) +{ + struct sadb_msg *base; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + + /* XXX KEBE ASKS -- Do we need to map isrc/idst for tunnel-mode SAs? */ + + /* validity check */ + if (mhp[0] == 0 || + mhp[SADB_EXT_SA] == 0 || + mhp[SADB_EXT_ADDRESS_SRC] == 0 || + mhp[SADB_EXT_ADDRESS_DST] == 0) { + rcpfk_seterror(rc, + EINVAL, "inappropriate ADD message passed"); + return -1; + } + base = (struct sadb_msg *)mhp[0]; + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + + rc->seq = base->sadb_msg_seq; + rc->satype = pfk2rct_satype(base->sadb_msg_satype); + if (rc->satype == 0) + return -1; + rc->spi = sa->sadb_sa_spi; + rc->sa_src = (struct sockaddr *)&rc->sa_src_storage; + rc->sa_dst = (struct sockaddr *)&rc->sa_dst_storage; + memcpy(rc->sa_src, src, SA_LEN(src)); + memcpy(rc->sa_dst, dst, SA_LEN(dst)); + + /* + * Set samode by using presence of inner-src/inner-dst. + * We could use SADB_X_SAFLAGS_TUNNEL also, but this isrc/idst is + * easier to pluck for now. Do direct RCT mapping for now also. + */ + rc->samode = mhp[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL ? + RCT_IPSM_TUNNEL : RCT_IPSM_TRANSPORT; + + if (cb->cb_add != 0 && cb->cb_add(rc) < 0) + return -1; + + return 0; +} + +/* + * send a SADB_GETSPI to kernel. + * + */ +int +rcpfk_send_getspi(struct rcpfk_msg *rc) +{ + rc_vchar_t *buf = 0; + + if (rcpfk_set_sadbmsg(&buf, rc, SADB_GETSPI)) { + err: + if (buf) + rc_vfree(buf); + return -1; + } + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_SRC)) + goto err; + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_DST)) + goto err; + + if (rcpfk_set_sadbspirange(&buf, rc, 0, 0xFFFFFFFF)) + goto err; + + if (rcpfk_send(rc, buf)) { + rc_vfree(buf); + return -1; + } + + rc_vfree(buf); + return 0; +} + +/* + * send a SADB_DELETE or SADB_GET to kernel. + * + */ +static int +rcpfk_send_delget(struct rcpfk_msg *rc, int type) +{ + rc_vchar_t *buf = 0; + + assert(type == SADB_DELETE || type == SADB_GET); + + if (rcpfk_set_sadbmsg(&buf, rc, type)) { + err: + if (buf) + rc_vfree(buf); + return -1; + } + + /* + * when it sends a SADB_DELETE without spi to the kernel. This is + * the "delete all" request (an extension also present in Solaris) + */ + if (rc->spi != 0 || type == SADB_GET) { + if (rcpfk_set_sadbsa(&buf, rc, 1)) + goto err; + } + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_SRC)) + goto err; + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_DST)) + goto err; + + if (rcpfk_send(rc, buf)) { + rc_vfree(buf); + return -1; + } + + rc_vfree(buf); + return 0; +} + +/* + * Send an inverse-ACQUIRE to kernel. + * + */ +int +rcpfk_send_inverse_acquire(struct rcpfk_msg *rc) +{ + rc_vchar_t *buf = 0; + + if (rcpfk_set_sadbmsg(&buf, rc, SADB_X_INVERSE_ACQUIRE)) { + err: + if (buf) + rc_vfree(buf); + return -1; + } + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_SRC)) + goto err; + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_DST)) + goto err; + if (rc->sa_isrc != NULL && + rcpfk_set_sadbaddress(&buf, rc, SADB_X_EXT_ADDRESS_INNER_SRC)) + goto err; + + if (rc->sa_idst != NULL && + rcpfk_set_sadbaddress(&buf, rc, SADB_X_EXT_ADDRESS_INNER_DST)) + goto err; + + if (rcpfk_send(rc, buf)) { + rc_vfree(buf); + return -1; + } + + rc_vfree(buf); + return 0; +} + +int +rcpfk_send_delete(struct rcpfk_msg *rc) +{ + return (rcpfk_send_delget(rc, SADB_DELETE)); +} + +int +rcpfk_send_get(struct rcpfk_msg *rc) +{ + return (rcpfk_send_delget(rc, SADB_GET)); +} + +/* + * send an error against ACQUIRE message to kenrel. + * + */ +int +rcpfk_send_acquire(struct rcpfk_msg *rc) +{ + rc_vchar_t *buf = 0; + + if (rcpfk_set_sadbmsg(&buf, rc, SADB_ACQUIRE)) { + if (buf) + rc_vfree(buf); + return -1; + } + + ((struct sadb_msg *)buf->v)->sadb_msg_errno = rc->eno; + + if (rcpfk_send(rc, buf)) { + rc_vfree(buf); + return -1; + } + + rc_vfree(buf); + return 0; +} + +/* + * send a SADB_UPDATE or a SADB_ADD to kernel. + * + */ +static int +rcpfk_send_addx(struct rcpfk_msg *rc, int type) +{ + rc_vchar_t *buf = 0; + + if (rcpfk_set_sadbmsg(&buf, rc, type)) { + err: + if (buf) + rc_vfree(buf); + return -1; + } + + if (rcpfk_set_sadbsa(&buf, rc, 0)) + goto err; + + if (rcpfk_set_sadblifetime(&buf, rc, SADB_EXT_LIFETIME_HARD)) + goto err; + + if (rcpfk_set_sadblifetime(&buf, rc, SADB_EXT_LIFETIME_SOFT)) + goto err; + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_SRC)) + goto err; + + if (rcpfk_set_sadbaddress(&buf, rc, SADB_EXT_ADDRESS_DST)) + goto err; + + if (rc->sa_isrc != NULL && + rcpfk_set_sadbaddress(&buf, rc, SADB_X_EXT_ADDRESS_INNER_SRC)) + goto err; + + if (rc->sa_idst != NULL && + rcpfk_set_sadbaddress(&buf, rc, SADB_X_EXT_ADDRESS_INNER_DST)) + goto err; + + if (rc->sa_natlocal != NULL && + rcpfk_set_sadbaddress(&buf, rc, SADB_X_EXT_ADDRESS_NATT_LOC)) + goto err; + + if (rc->sa_natremote != NULL && + rcpfk_set_sadbaddress(&buf, rc, SADB_X_EXT_ADDRESS_NATT_REM)) + goto err; + + /* + * XXX KEBE ASKS, what about no-encr ESP? Should we check for + * encrkey != NULL? + */ + if (rc->satype != RCT_SATYPE_AH && + rcpfk_set_sadbkey(&buf, rc, SADB_EXT_KEY_ENCRYPT)) + goto err; + + if (rcpfk_set_sadbkey(&buf, rc, SADB_EXT_KEY_AUTH)) + goto err; + + + /* XXX KEBE SAYS -- Add ID extensions here. */ + + if (rcpfk_send(rc, buf)) { + rc_vfreez(buf); + return -1; + } + + rc_vfreez(buf); + return 0; +} + +int +rcpfk_send_update(struct rcpfk_msg *rc) +{ + return rcpfk_send_addx(rc, SADB_UPDATE); +} + +int +rcpfk_send_add(struct rcpfk_msg *rc) +{ + return rcpfk_send_addx(rc, SADB_ADD); +} + +/* + * receive SADB_EXPIRE from kernel. + * OUT: + */ +static int +rcpfk_recv_expire(caddr_t *mhp, struct rcpfk_msg *rc) +{ + struct sadb_msg *base; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + struct sadb_lifetime *lft_hard, *lft_soft, *lft_current; + + /* XXX KEBE ASKS -- Do we need to map isrc/idst for tunnel-mode SAs? */ + + /* validity check */ + if (mhp[0] == 0 || + mhp[SADB_EXT_SA] == 0 || + mhp[SADB_EXT_ADDRESS_SRC] == 0 || + mhp[SADB_EXT_ADDRESS_DST] == 0 || + mhp[SADB_EXT_LIFETIME_CURRENT] == 0 || + (mhp[SADB_EXT_LIFETIME_HARD] != 0 && + mhp[SADB_EXT_LIFETIME_SOFT] != 0)) { + rcpfk_seterror(rc, EINVAL, + "inappropriate EXPIRE message passed"); + return -1; + } + base = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + lft_current = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_CURRENT]; + lft_hard = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_HARD]; + lft_soft = (struct sadb_lifetime *)mhp[SADB_EXT_LIFETIME_SOFT]; + + rc->seq = base->sadb_msg_seq; + rc->satype = pfk2rct_satype(base->sadb_msg_satype); + if (rc->satype == 0) + return -1; + rc->spi = sa->sadb_sa_spi; + rc->sa_src = (struct sockaddr *)&rc->sa_src_storage; + rc->sa_dst = (struct sockaddr *)&rc->sa_dst_storage; + memcpy(rc->sa_src, src, SA_LEN(src)); + memcpy(rc->sa_dst, dst, SA_LEN(dst)); + + /* + * Set samode by using presence of inner-src/inner-dst. + * We could use SADB_X_SAFLAGS_TUNNEL also, but this isrc/idst is + * easier to pluck for now. Do direct RCT mapping for now also. + */ + rc->samode = mhp[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL ? + RCT_IPSM_TUNNEL : RCT_IPSM_TRANSPORT; + + rc->expired = mhp[SADB_EXT_LIFETIME_HARD] != 0 ? 2 : 1; + + /* actually racoon2 doesn't care about lifetime bytes */ + rc->lft_current_alloc = lft_current->sadb_lifetime_allocations; + rc->lft_current_add = lft_current->sadb_lifetime_addtime; + rc->lft_current_use = lft_current->sadb_lifetime_usetime; + if (lft_hard != NULL) { + rc->lft_hard_time = lft_hard->sadb_lifetime_addtime; + rc->lft_hard_bytes = lft_hard->sadb_lifetime_bytes; + } else { + rc->lft_hard_time = 0; + rc->lft_hard_bytes = 0; + } + if (lft_soft != NULL) { + rc->lft_soft_time = lft_soft->sadb_lifetime_addtime; + rc->lft_soft_bytes = lft_soft->sadb_lifetime_bytes; + } else { + rc->lft_soft_time = 0; + rc->lft_soft_bytes = 0; + } + + if (cb->cb_expire != 0 && cb->cb_expire(rc) < 0) + return -1; + + return 0; +} + +static struct sadb_alg * +findsupportedalg(struct sadb_supported *sup, int alg_id) +{ + int tlen; + struct sadb_alg *a; + + if (!sup) + return NULL; + + a = (struct sadb_alg *)(sup + 1); + tlen = PFKEY_UNUNIT64(sup->sadb_supported_len) - sizeof(*sup); + for ( ; tlen > 0; ++a, tlen -= sizeof(*a)) { + if (tlen < sizeof(*a)) { + /* invalid format */ + break; + } + + if (a->sadb_alg_id == alg_id) + return a; + } + + return NULL; +} + +/* + * check the algorithm is supported or not. + * RETURN VALUE: + * 0 = not supported + * non-zero = supported + */ +int +rcpfk_supported_auth(int algtype) +{ + int type; + + type = rct2pfk_authtype(algtype); + if (findsupportedalg(supported_map_auth, type) != NULL) + return 1; + return 0; +} + +int +rcpfk_supported_enc(int algtype) +{ + int type; + + type = rct2pfk_enctype(algtype); + if (findsupportedalg(supported_map_enc, type) != NULL) + return 1; + return 0; +} + +/* + * We don't have SA_LEN easily in OpenSolaris. + */ +static size_t +sa_len(struct sockaddr_storage *ss) +{ + switch (ss->ss_family) { + case AF_INET: + return (sizeof (struct sockaddr_in)); + case AF_INET6: + return (sizeof (struct sockaddr_in6)); + default: + return (sizeof (struct sockaddr)); + } +} + +/* + * Since we've done extended register, we need to be ready for extended + * ACQUIRE. + */ +static int +rcpfk_recv_acquire(caddr_t *mhp, struct rcpfk_msg *rc) +{ + sadb_msg_t *base = (sadb_msg_t *)mhp[0]; + sadb_address_t *addrext; + struct sockaddr_storage *src, *dst, *ss; + sadb_prop_t *eprop; + int ret; + + if (cb->cb_acquire == NULL) + return (-1); + + /* In OpenSolaris, there's no REGISTER for AH/ESP... check that here. */ + assert(base->sadb_msg_satype == 0); + rc->eno = 0; + + /* Extended ACQUIRE handling... */ + addrext = (sadb_address_t *)mhp[SADB_X_EXT_ADDRESS_INNER_SRC]; + if (addrext != NULL) { + assert(mhp[SADB_X_EXT_ADDRESS_INNER_DST] != NULL); + rc->samode = RCT_IPSM_TUNNEL; + ss = (struct sockaddr_storage *)PFKEY_ADDR_SADDR(addrext); + rc->sa_isrc = memcpy(&rc->sa_isrc_storage, ss, sa_len(ss)); + rc->pref_isrc = addrext->sadb_address_prefixlen; + rc->ul_iproto = addrext->sadb_address_proto; + addrext = (sadb_address_t *)mhp[SADB_X_EXT_ADDRESS_INNER_DST]; + ss = (struct sockaddr_storage *)PFKEY_ADDR_SADDR( + mhp[SADB_X_EXT_ADDRESS_INNER_DST]); + rc->sa_idst = memcpy(&rc->sa_idst_storage, ss, sa_len(ss)); + rc->pref_idst = addrext->sadb_address_prefixlen; + rc->ul_proto = (ss->ss_family == AF_INET) ? IPPROTO_ENCAP : + IPPROTO_IPV6; + } else { + rc->samode = RCT_IPSM_TRANSPORT; + rc->sa_isrc = rc->sa_idst = NULL; + addrext = (sadb_address_t *)mhp[SADB_EXT_ADDRESS_SRC]; + rc->ul_proto = addrext->sadb_address_proto; + } + + rc->slid = 0; /* 0 == No SLID available. */ + rc->satype = 0; /* 0 == Extended ACQUIRE from OpenSolaris */ + /* UNIQUE is implicit in the selectors from our ACQUIRE. */ + rc->ipsec_level = RCT_IPSL_REQUIRE; + rc->seq = base->sadb_msg_seq; + src = (struct sockaddr_storage *)PFKEY_ADDR_SADDR( + mhp[SADB_EXT_ADDRESS_SRC]); + dst = (struct sockaddr_storage *)PFKEY_ADDR_SADDR( + mhp[SADB_EXT_ADDRESS_DST]); + + if ((dst->ss_family == AF_INET && + IN_MULTICAST(ntohl(((struct sockaddr_in *)dst)->sin_addr.s_addr))) + || (dst->ss_family == AF_INET6 && + IN6_IS_ADDR_MULTICAST(&((struct sockaddr_in6 *)dst)->sin6_addr)) + ) { + rcpfk_seterror(rc, 0, "ignore ACQUIRE message " + "due to a multicast address"); + return (0); + } + rc->sa_src = memcpy(&rc->sa_src_storage, src, sa_len(src)); + rc->sa_dst = memcpy(&rc->sa_dst_storage, dst, sa_len(dst)); + eprop = (sadb_prop_t *)mhp[SADB_X_EXT_EPROP]; + assert(eprop != NULL); + rc->eprop = malloc(SADB_64TO8(eprop->sadb_prop_len)); + if (rc->eprop == NULL) { + rcpfk_seterror(rc, ENOMEM, "malloc(eprop) failed.\n"); + return (-1); + } + memcpy(rc->eprop, eprop, SADB_64TO8(eprop->sadb_prop_len)); + + ret = cb->cb_acquire(rc); + free(rc->eprop); + return ((ret < 0) ? -1 : 0); +} + +/* + * receive SADB_DELETE from kernel. + * OUT: + */ +static int +rcpfk_recv_delete(caddr_t *mhp, struct rcpfk_msg *rc) +{ + struct sadb_msg *base; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + + /* validity check */ + if (mhp[0] == 0 || + mhp[SADB_EXT_SA] == 0 || + mhp[SADB_EXT_ADDRESS_SRC] == 0 || + mhp[SADB_EXT_ADDRESS_DST] == 0) { + rcpfk_seterror(rc, EINVAL, + "inappropriate DELETE message passed"); + return -1; + } + base = (struct sadb_msg *)mhp[0]; + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + + rc->seq = base->sadb_msg_seq; + rc->satype = pfk2rct_satype(base->sadb_msg_satype); + if (rc->satype == 0) + return -1; + rc->spi = sa->sadb_sa_spi; + rc->sa_src = (struct sockaddr *)&rc->sa_src_storage; + rc->sa_dst = (struct sockaddr *)&rc->sa_dst_storage; + memcpy(rc->sa_src, src, SA_LEN(src)); + memcpy(rc->sa_dst, dst, SA_LEN(dst)); + + if (cb->cb_delete != 0 && cb->cb_delete(rc) < 0) + return -1; + + return 0; +} + +/* + * receive SADB_GET from kernel. + * OUT: + */ +static int +rcpfk_recv_get(caddr_t *mhp, struct rcpfk_msg *rc) +{ + struct sadb_msg *base; + struct sadb_sa *sa; + struct sockaddr *src, *dst; + struct sadb_lifetime *curlifetime; + uint8_t samode; + + /* XXX KEBE ASKS -- Do we need to map isrc/idst for tunnel-mode SAs? */ + + /* validity check */ + if (mhp[0] == 0 || + mhp[SADB_EXT_SA] == 0 || + mhp[SADB_EXT_ADDRESS_SRC] == 0 || + mhp[SADB_EXT_ADDRESS_DST] == 0) { + rcpfk_seterror(rc, + EINVAL, "inappropriate GET message passed"); + return -1; + } + base = (struct sadb_msg *)mhp[0]; + src = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_SRC]); + dst = PFKEY_ADDR_SADDR(mhp[SADB_EXT_ADDRESS_DST]); + sa = (struct sadb_sa *)mhp[SADB_EXT_SA]; + curlifetime = (struct sadb_lifetime*)mhp[SADB_EXT_LIFETIME_CURRENT]; + + rc->seq = base->sadb_msg_seq; + rc->satype = pfk2rct_satype(base->sadb_msg_satype); + if (rc->satype == 0) + return -1; + rc->spi = sa->sadb_sa_spi; + rc->sa_src = (struct sockaddr *)&rc->sa_src_storage; + rc->sa_dst = (struct sockaddr *)&rc->sa_dst_storage; + memcpy(rc->sa_src, src, SA_LEN(src)); + memcpy(rc->sa_dst, dst, SA_LEN(dst)); + + /* + * Set samode by using presence of inner-src/inner-dst. + * We could use SADB_X_SAFLAGS_TUNNEL also, but this isrc/idst is + * easier to pluck for now. Do direct RCT mapping for now also. + */ + samode = mhp[SADB_X_EXT_ADDRESS_INNER_SRC] != NULL ? + RCT_IPSM_TUNNEL : RCT_IPSM_TRANSPORT; + + rc->lft_current_bytes = curlifetime->sadb_lifetime_bytes; + + /* XXX KEBE SAYS FILL IN A LOT MORE HERE. */ + + if (cb->cb_add != 0 && cb->cb_add(rc) < 0) + return -1; + + return 0; +} + +static int +set_supported_algorithm(caddr_t m, struct sadb_supported **dstsup) +{ + struct sadb_supported *srcsup = (struct sadb_supported *)m; + struct sadb_supported *sup; + size_t len; + + len = PFKEY_EXTLEN(srcsup); + if ((sup = rc_malloc(len)) == NULL) + return -1; + memcpy(sup, srcsup, len); + + if (*dstsup != NULL) { + /* + * Algorithm change - free current one. + */ + rc_free(*dstsup); + } + *dstsup = sup; + + return 0; +} + +/* + * receive SADB_REGISTER from kernel. + * OUT: + */ +static int +rcpfk_recv_register(caddr_t *mhp, struct rcpfk_msg *rc) +{ + /* + * XXX KEBE SAYS check for supported algorithms here and construct + * tables. The functions rcpkf_supported_{auth,enc}() will consult + * these tables. + * + * Use near-original source, assume AH and ESP auths are identical + * in capabilities. (OpenSolaris enforces this by the design of + * ipsecalgs(1M).) + */ + + if (mhp[SADB_EXT_SUPPORTED_AUTH] != NULL && + set_supported_algorithm(mhp[SADB_EXT_SUPPORTED_AUTH], + &supported_map_auth) != 0) { + rcpfk_seterror(rc, 0, "No memory for supported auths."); + return (-1); + } + + if (mhp[SADB_EXT_SUPPORTED_ENCRYPT] != NULL && + set_supported_algorithm(mhp[SADB_EXT_SUPPORTED_ENCRYPT], + &supported_map_enc) != 0) { + rcpfk_seterror(rc, 0, "No memory for supported encrs."); + return (-1); + } + + return (0); +} + +/* + * receive SADB_X_INVERSE_ACQUIRE from kernel. + * OUT: + */ +static int +rcpfk_recv_invacq(caddr_t *mhp, struct rcpfk_msg *rc) +{ + sadb_msg_t *base = (struct sadb_msg *)mhp[0]; + /* + * This is only ever an error case for inverse-ACQUIRE. + * Call the ACQUIRE logic. + */ + if (cb->cb_acquire == NULL) + return (-1); + rc->eno = base->sadb_msg_errno; + assert(rc->eno != 0); + rc->diag = base->sadb_x_msg_diagnostic; + return (cb->cb_acquire(rc)); +} + +/* END OF RECEIVING MODULES... */ + +static rc_vchar_t * +rcpfk_recv_either(struct rcpfk_msg *rc, boolean_t pfkey) +{ + rc_vchar_t *buf; + struct sadb_msg base; + int len, reallen, sock; + + sock = pfkey ? rc->so : rc->pol_so; + if (sock == -1) + return (NULL); + + /* + * NOTE: spd_msg_t and sadb_msg_t are identically laid out. + * We exploit that to make this routine nice and small. + */ + len = recv(sock, (caddr_t)&base, sizeof(base), + MSG_PEEK | MSG_DONTWAIT); + if (len < 0) { + if (errno != EWOULDBLOCK) + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return NULL; + } + + if (len < sizeof(base)) { + rcpfk_seterror(rc, EINVAL, "invalid message length"); + return NULL; + } + + reallen = SADB_64TO8(base.sadb_msg_len); + if ((buf = rc_vmalloc(reallen)) == NULL) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + return NULL; + } + + len = recv(rc->so, buf->v, buf->l, 0); + if (len < 0) { + rcpfk_seterror(rc, errno, "%s", strerror(errno)); + goto err; + } else if (len != reallen) { + rcpfk_seterror(rc, EINVAL, "invalid message length"); + goto err; + } + + return buf; + + err: + rc_vfree(buf); + return NULL; +} + +/* + * Send an extended register. + * + */ +static int +rcpfk_send_ereg(rcpfk_msg_t *rc) +{ + rc_vchar_t *buf = NULL, *final = NULL; + sadb_x_ereg_t *ereg; + + if (rcpfk_set_sadbmsg(&buf, rc, SADB_REGISTER)) { + if (buf != NULL) + rc_vfree(buf); + return (-1); + } + + /* + * Just in-line what might've been rcpfk_set_sadbereg(). + * Since we only do AH and ESP (and possibly later IPCOMP), there's + * no need to allocate an oversized sadb_x_ereg_t - there's four + * bytes available already (AH, ESP, null/IPcomp, null). + */ + final = rc_vrealloc(buf, buf->l + sizeof (sadb_x_ereg_t)); + if (final == NULL) { + rc_vfree(buf); + return (-1); + } + ereg = (sadb_x_ereg_t *)(final->v + sizeof (sadb_msg_t)); + ereg->sadb_x_ereg_len = SADB_8TO64(sizeof (sadb_x_ereg_t)); + ereg->sadb_x_ereg_exttype = SADB_X_EXT_EREG; + ereg->sadb_x_ereg_satypes[0] = SADB_SATYPE_AH; + ereg->sadb_x_ereg_satypes[1] = SADB_SATYPE_ESP; + ereg->sadb_x_ereg_satypes[2] = 0; + + if (rcpfk_send(rc, final)) { + rc_vfree(final); + return (-1); + } + + rc_vfree(final); + return (0); +} + +/* + * KAME/WIDE says: + * IN: rpm->flags, cb + * OUT: rpm->so + * We need to add a PF_POLICY socket to the mix as well. + */ +int +rcpfk_init(rcpfk_msg_t *rpm, rcpfk_cb_t *cb_in) +{ + cb = &null_cb; + + pid = getpid(); + + dont_send = ((rpm->flags & PFK_FLAG_NOHARM) != 0); + + /* + * Return a socket. + */ + rpm->so = socket(PF_KEY, SOCK_RAW, PF_KEY_V2); + if (rpm->so == -1) { + rcpfk_seterror(rpm, errno, "%s", strerror(errno)); + return (-1); + } + + rpm->pol_so = socket(PF_POLICY, SOCK_RAW, PF_POLICY_V1); + if (rpm->pol_so == -1) { + rcpfk_seterror(rpm, errno, "%s", strerror(errno)); + (void) close(rpm->so); + return (-1); + } + + /* + * Perform REGISTER messages here. + * XXX KEBE SAYS - KAME/WIDE kernel doesn't do extended register. + * Send extended register and deal with it. + * Call rcpfk_handler() twice because we get TWO registers back from + * the kernel with an extended REGISTER --> AH and ESP. + */ + rpm->satype = 0; + if (rcpfk_send_ereg(rpm) || + rcpfk_handler(rpm) || rcpfk_handler(rpm)) + return (-1); + + cb = cb_in; + return (0); +} + +/* + * rcpfk_handler() handles the PF_KEY socket. Since we have PF_POLICY also, + * we need to check that as well. + * + * It appears that callers call here expecting a return for whatever message + * they were sent. + */ +int +rcpfk_handler(rcpfk_msg_t *rc) +{ + rc_vchar_t *msg; + int ret = -1; + sadb_msg_t *base; + struct sadb_ext *ext; + caddr_t p, ep; + caddr_t mhp[SADB_EXT_MAX + 1]; + int i; + boolean_t pfkey; + int (*recvfunc)(caddr_t *, rcpfk_msg_t *); + + /* Use KAME routine to receive PF_KEY or PF_POLICY message. */ + if ((msg = rcpfk_recv_either(rc, (pfkey = B_TRUE))) == NULL /* || + (msg = rcpfk_recv_either(rc, (pfkey = B_FALSE))) == NULL */) { + plog(PLOG_INTERR, PLOGLOC, NULL, + "rcpfk_recv_either() returned NULL."); + return -1; + } + + base = (sadb_msg_t *)msg->v; + + /* Check if it's a message we want to deal with. */ + if (base->sadb_msg_pid != pid && + (base->sadb_msg_pid == 0 && base->sadb_msg_errno != 0)) { + plog(PLOG_DEBUG, PLOGLOC, NULL, + "SADB/SPD message not for us, dropped."); + ret = 0; + goto bail; + } + + if (base->sadb_msg_errno != 0) { + rcpfk_seterror(rc, base->sadb_msg_errno, + "reported errno %d (%s), diag = %d\n", + base->sadb_msg_errno, strerror(base->sadb_msg_errno), + base->sadb_x_msg_diagnostic); + goto bail; + } + + /* initialize */ + for (i = 0; i < sizeof(mhp)/sizeof(mhp[0]); i++) + mhp[i] = 0; + mhp[0] = msg->v; + p = (caddr_t)msg->v; + ep = p + msg->l; + + /* skip base header */ + p += sizeof(struct sadb_msg); + + while (p < ep) { + ext = (struct sadb_ext *)p; + + /* length check */ + if (ep < p + sizeof(*ext) || + PFKEY_EXTLEN(ext) < sizeof(*ext) || + ep < p + PFKEY_EXTLEN(ext)) { + rcpfk_seterror(rc, EINVAL, + "invalid pfkey extension format length"); + goto bail; + } + + /* duplicate check */ + if (mhp[ext->sadb_ext_type] != 0) { + rcpfk_seterror(rc, EINVAL, + "duplicate pfkey/pfpol extension type=%d", + ext->sadb_ext_type); + goto bail; + } + + /* + * XXX For now be a bit more trusting of the kernel than + * racoon2's normal defaults. + */ + + mhp[ext->sadb_ext_type] = (caddr_t)ext; + + p += PFKEY_EXTLEN(ext); + } + if (p != ep) { + rcpfk_seterror(rc, EINVAL, "invalid pfkey extension format"); + goto bail; + } + + if (pfkey) { + if (rcpfk_msg[base->sadb_msg_type].recvfunc == 0) { + rcpfk_seterror(rc, EOPNOTSUPP, + "command %s not supported", + rcpfk_msg[base->sadb_msg_type].name); + goto bail; + } + recvfunc = rcpfk_msg[base->sadb_msg_type].recvfunc; + } else { + if (rcpfpol_msg[base->sadb_msg_type].recvfunc == 0) { + rcpfk_seterror(rc, EOPNOTSUPP, + "command %s not supported", + rcpfpol_msg[base->sadb_msg_type].name); + goto bail; + } + recvfunc = rcpfpol_msg[base->sadb_msg_type].recvfunc; + } + ret = (recvfunc(mhp, rc) == 0) ? 0 : -1; + +bail: + rc_vfree(msg); + return (ret); +} --- old/lib/if_pfkeyv2.c Fri Mar 27 12:10:04 2009 +++ new/lib/if_pfkeyv2.c Fri Mar 27 12:10:04 2009 @@ -31,6 +31,10 @@ * SUCH DAMAGE. */ +#ifdef sun +/* Replace this file with the OpenSolaris-specific version. */ +#include "./if_pfkeyv2-solaris.c" +#else #include #include #include @@ -2773,3 +2777,4 @@ return NULL; } +#endif /* sun/OpenSolaris */ --- old/lib/if_pfkeyv2.h Fri Mar 27 12:10:04 2009 +++ new/lib/if_pfkeyv2.h Fri Mar 27 12:10:04 2009 @@ -41,8 +41,12 @@ #define RCPFK_SOCKBUFSIZE 128 * 1024 /* racoon PF_KEY message container */ -struct rcpfk_msg { +typedef struct rcpfk_msg { int so; /* pfkey socket */ +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + int pol_so; /* PF_POLICY socket */ + int diag; /* PF_{KEY,POLICY} diagnostic code for EINVAL. */ +#endif char estr[RCPFK_ERRSTRBUFSIZE]; int eno; @@ -84,6 +88,28 @@ uint8_t pref_src; uint8_t pref_dst; uint8_t ul_proto; +#ifdef sun /* XXX KEBE SAYS OpenSolaris */ + /* + * OpenSolaris has inner-packet addrs for tunnel-mode SAs. + * Exploit the sp_{src,dst} for this. + */ +#define sa_isrc_storage sp_src_storage +#define sa_idst_storage sp_dst_storage +#define sa_isrc sp_src +#define sa_idst sp_dst +#define sa_natlocal_storage sa2_src_storage +#define sa_natremote_storage sa2_dst_storage +#define sa_natlocal sa2_src +#define sa_natremote sa2_dst +/* For readability -- we use prefixes for "inner" addresses, so use 'i'. */ +#define pref_isrc pref_src +#define pref_idst pref_dst + /* proto for our tunnel mode is always IPPROTO_{ENCAP,IPV6}. */ + uint8_t ul_iproto; + sadb_prop_t *eprop; /* Extended proposal... */ + + /* XXX KEBE SAYS - Phase I or IKEv2 ID pointers? */ +#endif uint8_t dir; uint8_t pltype; uint8_t ipsec_level; /* always require in racoon2 */ @@ -96,9 +122,9 @@ struct sockaddr_storage sa2_dst_storage; /* required for pfkey migrate */ struct sockaddr_storage sp_src_storage; struct sockaddr_storage sp_dst_storage; -}; +} rcpfk_msg_t; -struct rcpfk_cb { +typedef struct rcpfk_cb { int (*cb_getspi) (struct rcpfk_msg *); int (*cb_update) (struct rcpfk_msg *); int (*cb_add) (struct rcpfk_msg *); @@ -116,7 +142,7 @@ #ifdef SADB_X_MIGRATE int (*cb_migrate) (struct rcpfk_msg *); #endif -}; +} rcpfk_cb_t; extern int rcpfk_handler (struct rcpfk_msg *); extern int rcpfk_init (struct rcpfk_msg *, struct rcpfk_cb *); @@ -133,6 +159,11 @@ extern int rcpfk_send_spddelete (struct rcpfk_msg *); extern int rcpfk_send_spddelete2 (struct rcpfk_msg *); extern int rcpfk_send_spddump (struct rcpfk_msg *rc); +#ifdef sun +extern int rcpfk_send_inverse_acquire(struct rcpfk_msg *); +#endif +#ifdef SADB_X_MIGRATE extern int rcpfk_send_migrate (struct rcpfk_msg *rc); +#endif extern int rcpfk_supported_auth (int); extern int rcpfk_supported_enc (int); --- old/lib/missing/missing.h Fri Mar 27 12:10:05 2009 +++ new/lib/missing/missing.h Fri Mar 27 12:10:05 2009 @@ -47,3 +47,42 @@ # endif # endif #endif + +#ifndef timercmp +#define timercmp(tvp, uvp, cmp) \ + (((tvp)->tv_sec == (uvp)->tv_sec) ? \ + /* BEGIN CSTYLED */ \ + ((tvp)->tv_usec cmp (uvp)->tv_usec) : \ + ((tvp)->tv_sec cmp (uvp)->tv_sec)) \ + /* END CSTYLED */ +#endif + +#ifndef timeradd +#define timeradd(tvp, uvp, vvp) \ + do \ + { \ + (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \ + if ((vvp)->tv_usec >= 1000000) \ + { \ + (vvp)->tv_sec++; \ + (vvp)->tv_usec -= 1000000; \ + } \ + /* LINTED */ \ + } while (0) +#endif + +#ifndef timersub +#define timersub(tvp, uvp, vvp) \ + do \ + { \ + (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ + (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ + if ((vvp)->tv_usec < 0) \ + { \ + (vvp)->tv_sec--; \ + (vvp)->tv_usec += 1000000; \ + } \ + /* LINTED */ \ + } while (0) +#endif --- old/lib/pfkeyv2aux.h Fri Mar 27 12:10:05 2009 +++ new/lib/pfkeyv2aux.h Fri Mar 27 12:10:05 2009 @@ -37,7 +37,11 @@ # ifdef SADB_X_AALG_SHA2_256HMAC /* Linux */ # define SADB_X_AALG_SHA2_256 SADB_X_AALG_SHA2_256HMAC # else -# define SADB_X_AALG_SHA2_256 5 +# ifdef SADB_AALG_SHA256HMAC /* OpenSolaris */ +# define SADB_X_AALG_SHA2_256 SADB_AALG_SHA256HMAC +# else +# define SADB_X_AALG_SHA2_256 5 +# endif # endif #endif @@ -45,7 +49,11 @@ # ifdef SADB_X_AALG_SHA2_384HMAC /* Linux */ # define SADB_X_AALG_SHA2_384 SADB_X_AALG_SHA2_384HMAC # else -# define SADB_X_AALG_SHA2_384 6 +# ifdef SADB_AALG_SHA384HMAC /* OpenSolaris */ +# define SADB_X_AALG_SHA2_384 SADB_AALG_SHA384HMAC +# else +# define SADB_X_AALG_SHA2_384 6 +# endif # endif #endif @@ -53,7 +61,11 @@ # ifdef SADB_X_AALG_SHA2_512HMAC /* Linux */ # define SADB_X_AALG_SHA2_512 SADB_X_AALG_SHA2_512HMAC # else -# define SADB_X_AALG_SHA2_512 7 +# ifdef SADB_AALG_SHA512HMAC /* OpenSolaris */ +# define SADB_X_AALG_SHA2_512 SADB_AALG_SHA512HMAC +# else +# define SADB_X_AALG_SHA2_512 7 +# endif # endif #endif @@ -85,6 +97,10 @@ # endif #endif +#ifndef SADB_X_EALG_BLOWFISHCBC /* OpenSolaris */ +# define SADB_X_EALG_BLOWFISHCBC SADB_EALG_BLOWFISH +#endif + /* RFC 3602 */ #ifndef SADB_X_EALG_AES # ifdef SADB_X_EALG_AESCBC /* Linux */ --- old/lib/pidfile.c Fri Mar 27 12:10:05 2009 +++ new/lib/pidfile.c Fri Mar 27 12:10:05 2009 @@ -37,6 +37,7 @@ #include #include #include +#include #include "plog.h" #include "pidfile.h" @@ -76,11 +77,19 @@ "%s: open: %s\n", pidfile, strerror(errno)); goto fail; } +#if 1 /* XXX KEBE suggests using POSIX lockf() instead... */ + if (lockf(pidfile_fd, F_TLOCK, 0) == -1) { + plog(PLOG_INTERR, PLOGLOC, NULL, + "%s: lockf: %s\n", pidfile, strerror(errno)); + goto fail; + } +#else if (flock(pidfile_fd, LOCK_EX | LOCK_NB) == -1) { plog(PLOG_INTERR, PLOGLOC, NULL, "%s: flock: %s\n", pidfile, strerror(errno)); goto fail; } +#endif if (ftruncate(pidfile_fd, 0) == -1) { plog(PLOG_INTERR, PLOGLOC, NULL, "%s: ftruncate: %s\n", pidfile, strerror(errno)); @@ -87,7 +96,7 @@ rc_cleanup_pidfile(); return -1; } - snprintf(pidstr, sizeof(pidstr), "%d\n", pid); + snprintf(pidstr, sizeof(pidstr), "%d\n", (int)pid); if (write(pidfile_fd, pidstr, strlen(pidstr)) == -1) { plog(PLOG_INTERR, PLOGLOC, NULL, "%s: write: %s\n", pidfile, strerror(errno)); --- old/lib/racoon.h Fri Mar 27 12:10:06 2009 +++ new/lib/racoon.h Fri Mar 27 12:10:06 2009 @@ -79,6 +79,7 @@ extern int rcf_read (const char *, int); extern int rcf_clean (void); +extern void rcf_clean_ipsec_list(struct rcf_ipsec *); extern rc_vchar_t *rcf_readfile(const char *path, const char *errloc, int secret); --- old/lib/rc_net.c Fri Mar 27 12:10:06 2009 +++ new/lib/rc_net.c Fri Mar 27 12:10:06 2009 @@ -33,12 +33,17 @@ #include #include +#ifdef sun +#include +#endif + #include /* for in6_ifreq */ #ifdef HAVE_NET_IF_VAR_H # include /* for in6_ifreq */ #endif + #include -#ifndef __linux__ +#if !defined(__linux__) && !defined(sun) # include /* for in6_ifreq */ #endif @@ -462,6 +467,76 @@ new_head = new; } freeifaddrs(ifa0); +#elif defined(sun) + int s, maxif, len; + struct lifreq *liflist; + struct lifconf lifconf; + struct lifreq *lifr, *lifr_end; + + maxif = if_maxindex() + 1; + len = maxif * sizeof(struct sockaddr_storage) * 4; /* guess guess */ + + if ((liflist = (struct lifreq *)rc_malloc(len)) == NULL) { + plog(PLOG_INTERR, PLOGLOC, NULL, "no memory.\n"); + return NULL; + } + + if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, + "socket(SOCK_DGRAM) failed: %s\n", strerror(errno)); + rc_free(liflist); + return NULL; + } + memset(&lifconf, 0, sizeof(lifconf)); + lifconf.lifc_req = liflist; + lifconf.lifc_len = len; + if (ioctl(s, SIOCGLIFCONF, &lifconf) < 0) { + plog(PLOG_INTERR, PLOGLOC, NULL, + "ioctl(SIOCGLIFCONF) failed: %s\n", strerror(errno)); + rc_free(liflist); + return NULL; + } + close(s); + + /* Look for this interface in the list */ + lifr_end = (struct lifreq *)(lifconf.lifc_buf + lifconf.lifc_len); + + for (lifr = lifconf.lifc_req; + lifr < lifr_end; + lifr = (struct lifreq *)((caddr_t)lifr + sizeof (*lifr))) { + if (family != AF_UNSPEC && + lifr->lifr_addr.ss_family != family) + continue; + if (!suitable_ifaddr(lifr->lifr_name, + (struct sockaddr *)&lifr->lifr_addr)) + continue; + if ((new = rc_calloc(1, sizeof(*new))) == NULL) { + rc_free(liflist); + return NULL; + } + new->type = RCT_ADDR_INET; + new->port = ntohs(rcs_getsaport( + (struct sockaddr *)&lifr->lifr_addr)); + new->a.ipaddr = rcs_sadup( + (struct sockaddr *)&lifr->lifr_addr); + if (lifr->lifr_addr.ss_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)new->a.ipaddr; + if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) || + IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) { + sin6->sin6_scope_id = + ntohs(*(uint16_t *)&sin6->sin6_addr.s6_addr[2]); + sin6->sin6_addr.s6_addr[2] = 0; + sin6->sin6_addr.s6_addr[3] = 0; + } + } + for (p = new_head; p && p->next; p = p->next) + ; + if (p) + p->next = new; + else + new_head = new; + } + rc_free(liflist); #else /*!HAVE_GETIFADDRS*/ int s, maxif, len; struct ifreq *iflist; @@ -500,6 +575,7 @@ (sizeof((p)->ifr_name) + (p)->ifr_addr.sa_len > sizeof(struct ifreq) \ ? sizeof((p)->ifr_name) + (p)->ifr_addr.sa_len : sizeof(struct ifreq)) + for (ifr = ifconf.ifc_req; ifr < ifr_end; ifr = (struct ifreq *)((caddr_t)ifr + RCF_IFREQ_LEN(ifr))) { @@ -591,7 +667,7 @@ static int suitable_ifaddr6(const char *ifname, const struct sockaddr *ifaddr) { -#ifdef __linux__ +#if defined(__linux__) || defined(sun) return 1; /* XXX FIXME */ #else struct in6_ifreq ifr6; --- old/lib/rc_type.c Fri Mar 27 12:10:07 2009 +++ new/lib/rc_type.c Fri Mar 27 12:10:07 2009 @@ -36,8 +36,11 @@ # include #else # include -# include +# if 0 /* KEBE SAYS OpenSolaris hack */ +# include +# endif #endif + #ifdef HAVE_NETINET6_IPSEC_H # include #else @@ -44,9 +47,12 @@ # ifdef HAVE_NETIPSEC_IPSEC_H # include # else -# include +# if 0 /* KEBE SAYS OpenSolaris hack */ +# include +# endif # endif #endif + #include "pfkeyv2aux.h" #include @@ -72,6 +78,7 @@ } } +#ifndef sun /* KEBE SAYS nothing like this in OpenSolaris */ int rct2app_action(int type) { @@ -101,6 +108,7 @@ return 0; } } +#endif int rct2pfk_satype(int type) @@ -110,8 +118,12 @@ return SADB_SATYPE_ESP; case RCT_SATYPE_AH: return SADB_SATYPE_AH; + case 0: + return 0; +#ifndef sun case RCT_SATYPE_IPCOMP: return SADB_X_SATYPE_IPCOMP; +#endif default: errx(1, "satype=%d not supported", type); } @@ -125,8 +137,10 @@ return RCT_SATYPE_ESP; case SADB_SATYPE_AH: return RCT_SATYPE_AH; +#ifndef sun case SADB_X_SATYPE_IPCOMP: return RCT_SATYPE_IPCOMP; +#endif /* Linux kernel sends a message whose type is 0, * when mip6d installing xfrm_state for MIP6 */ @@ -144,8 +158,10 @@ return IPPROTO_ESP; case RCT_SATYPE_AH: return IPPROTO_AH; +#ifndef sun case RCT_SATYPE_IPCOMP: return IPPROTO_IPCOMP; +#endif default: errx(1, "satype=%d not supported", type); } @@ -156,7 +172,11 @@ { switch (type) { case RCT_ALG_NON_AUTH: +#ifdef sun + return 0; +#else return SADB_X_AALG_NULL; +#endif case RCT_ALG_HMAC_MD5: return SADB_AALG_MD5HMAC; case RCT_ALG_HMAC_SHA1: @@ -212,6 +232,7 @@ rct2pfk_comptype(int type) { switch (type) { +#ifndef sun /* Nothing in OpenSolaris for IPCOMP for now... */ case RCT_ALG_OUI: return SADB_X_CALG_OUI; case RCT_ALG_DEFLATE: @@ -218,11 +239,13 @@ return SADB_X_CALG_DEFLATE; case RCT_ALG_LZS: return SADB_X_CALG_LZS; +#endif default: errx(1, "comptype=%d not supported", type); } } +#ifndef sun int rct2pfk_samode(int type) { @@ -306,6 +329,7 @@ return IPSEC_PROTO_ANY; return type; } +#endif const char * rct2str(int type)