/* Generate and match a NAT-D hash against the NAT-D payload (pl.) data. */ static int nat_t_match_nat_d_payload(struct message *msg, struct sockaddr *sa) { struct payload *p; u_int8_t *hbuf; size_t hbuflen; int found = 0; /* * If there are no NAT-D payloads in the message, return "found" * as this will avoid NAT-T (see nat_t_exchange_check_nat_d()). */ if ((p = payload_first(msg, ISAKMP_PAYLOAD_NAT_D_DRAFT)) == NULL && (p = payload_first(msg, ISAKMP_PAYLOAD_NAT_D)) == NULL) return 1; hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen); if (!hbuf) return 0; for (; p; p = TAILQ_NEXT(p, link)) { if (GET_ISAKMP_GEN_LENGTH (p->p) != hbuflen + ISAKMP_NAT_D_DATA_OFF) continue; if (memcmp(p->p + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen) == 0) { found++; break; } } free(hbuf); return found; }
/* * Accept a set of transforms offered by the initiator and chose one we can * handle. */ int ike_phase_1_responder_recv_SA(struct message *msg) { struct exchange *exchange = msg->exchange; struct sa *sa = TAILQ_FIRST(&exchange->sa_list); struct ipsec_sa *isa = sa->data; struct payload *sa_p = payload_first(msg, ISAKMP_PAYLOAD_SA); struct payload *prop = payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL); struct ipsec_exch *ie = exchange->data; /* Mark the SA as handled. */ sa_p->flags |= PL_MARK; /* IKE requires that only one SA with only one proposal exists. */ if (TAILQ_NEXT(sa_p, link) || TAILQ_NEXT(prop, link)) { log_print("ike_phase_1_responder_recv_SA: " "multiple SA or proposal payloads in phase 1"); /* XXX Is there a better notification type? */ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } /* Chose a transform from the SA. */ if (message_negotiate_sa(msg, ike_phase_1_validate_prop) || !TAILQ_FIRST(&sa->protos)) return -1; /* XXX Move into message_negotiate_sa? */ ipsec_decode_transform(msg, sa, TAILQ_FIRST(&sa->protos), TAILQ_FIRST(&sa->protos)->chosen->p); ie->group = group_get(isa->group_desc); /* * Check that the mandatory attributes: encryption, hash, * authentication method and Diffie-Hellman group description, has * been supplied. */ if (!exchange->crypto || !ie->hash || !ie->ike_auth || !ie->group) { message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } /* Save the body for later hash computation. */ ie->sa_i_b_len = GET_ISAKMP_GEN_LENGTH(sa_p->p) - ISAKMP_GEN_SZ; ie->sa_i_b = malloc(ie->sa_i_b_len); if (!ie->sa_i_b) { /* XXX How to notify peer? */ log_error("ike_phase_1_responder_recv_SA: malloc (%lu) failed", (unsigned long)ie->sa_i_b_len); return -1; } memcpy(ie->sa_i_b, sa_p->p + ISAKMP_GEN_SZ, ie->sa_i_b_len); return 0; }
static int isakmp_responder(struct message *msg) { struct payload *p; u_int16_t type; switch (msg->exchange->type) { case ISAKMP_EXCH_INFO: for (p = payload_first(msg, ISAKMP_PAYLOAD_NOTIFY); p; p = TAILQ_NEXT(p, link)) { type = GET_ISAKMP_NOTIFY_MSG_TYPE(p->p); LOG_DBG((LOG_EXCHANGE, 10, "isakmp_responder: " "got NOTIFY of type %s", constant_name(isakmp_notify_cst, type))); switch (type) { case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE: case ISAKMP_NOTIFY_STATUS_DPD_R_U_THERE_ACK: dpd_handle_notify(msg, p); break; default: p->flags |= PL_MARK; break; } } for (p = payload_first(msg, ISAKMP_PAYLOAD_DELETE); p; p = TAILQ_NEXT(p, link)) { LOG_DBG((LOG_EXCHANGE, 10, "isakmp_responder: got DELETE, ignoring")); p->flags |= PL_MARK; } return 0; case ISAKMP_EXCH_TRANSACTION: /* return 0 isakmp_cfg_responder (msg); */ default: /* XXX So far we don't accept any proposals. */ if (payload_first(msg, ISAKMP_PAYLOAD_SA)) { message_drop(msg, ISAKMP_NOTIFY_NO_PROPOSAL_CHOSEN, 0, 1, 0); return -1; } } return 0; }
int cfg_verify_hash(struct message *msg) { struct payload *hashp = payload_first(msg, ISAKMP_PAYLOAD_HASH); struct ipsec_sa *isa = msg->isakmp_sa->data; struct prf *prf; u_int8_t *hash, *comp_hash; size_t hash_len; if (!hashp) { log_print("cfg_verify_hash: phase 2 message missing HASH"); message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); return -1; } hash = hashp->p; hash_len = GET_ISAKMP_GEN_LENGTH(hash); comp_hash = malloc(hash_len - ISAKMP_GEN_SZ); if (!comp_hash) { log_error("cfg_verify_hash: malloc (%lu) failed", (unsigned long)hash_len - ISAKMP_GEN_SZ); return -1; } /* Verify hash. */ prf = prf_alloc(isa->prf_type, isa->hash, isa->skeyid_a, isa->skeyid_len); if (!prf) { free(comp_hash); return -1; } prf->Init(prf->prfctx); prf->Update(prf->prfctx, msg->exchange->message_id, ISAKMP_HDR_MESSAGE_ID_LEN); prf->Update(prf->prfctx, hash + hash_len, msg->iov[0].iov_len - ISAKMP_HDR_SZ - hash_len); prf->Final(comp_hash, prf->prfctx); prf_free(prf); if (memcmp(hash + ISAKMP_GEN_SZ, comp_hash, hash_len - ISAKMP_GEN_SZ) != 0) { message_drop(msg, ISAKMP_NOTIFY_INVALID_HASH_INFORMATION, 0, 1, 0); free(comp_hash); return -1; } free(comp_hash); /* Mark the HASH as handled. */ hashp->flags |= PL_MARK; /* Mark message authenticated. */ msg->flags |= MSG_AUTHENTICATED; return 0; }
/* Figure out what transform the responder chose. */ int ike_phase_1_initiator_recv_SA(struct message *msg) { struct exchange *exchange = msg->exchange; struct sa *sa = TAILQ_FIRST(&exchange->sa_list); struct ipsec_exch *ie = exchange->data; struct ipsec_sa *isa = sa->data; struct payload *sa_p = payload_first(msg, ISAKMP_PAYLOAD_SA); struct payload *prop = payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL); struct payload *xf = payload_first(msg, ISAKMP_PAYLOAD_TRANSFORM); /* * IKE requires that only one SA with only one proposal exists and * since we are getting an answer on our transform offer, only one * transform. */ if (TAILQ_NEXT(sa_p, link) || TAILQ_NEXT(prop, link) || TAILQ_NEXT(xf, link)) { log_print("ike_phase_1_initiator_recv_SA: " "multiple SA, proposal or transform payloads in phase 1"); /* XXX Is there a better notification type? */ message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } /* Check that the chosen transform matches an offer. */ if (message_negotiate_sa(msg, ike_phase_1_validate_prop) || !TAILQ_FIRST(&sa->protos)) return -1; ipsec_decode_transform(msg, sa, TAILQ_FIRST(&sa->protos), xf->p); /* XXX I don't like exchange-specific stuff in here. */ if (exchange->type != ISAKMP_EXCH_AGGRESSIVE) ie->group = group_get(isa->group_desc); /* Mark the SA as handled. */ sa_p->flags |= PL_MARK; return 0; }
/* * As "the server", this starts REQ/REPLY (initiated by the client). * As "the client", this starts SET/ACK (initiated by the server). */ static int cfg_responder_recv_ATTR(struct message *msg) { struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE); struct ipsec_exch *ie = msg->exchange->data; struct sa *isakmp_sa = msg->isakmp_sa; struct isakmp_cfg_attr *attr; struct sockaddr *sa; char *addr; if (msg->exchange->phase == 2) if (cfg_verify_hash(msg)) return -1; ie->cfg_id = GET_ISAKMP_ATTRIBUTE_ID(attrp->p); ie->cfg_type = attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]; switch (ie->cfg_type) { case ISAKMP_CFG_REQUEST: case ISAKMP_CFG_SET: break; default: message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); log_print("cfg_responder_recv_ATTR: " "unexpected configuration message type %d", ie->cfg_type); return -1; } attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF, GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, cfg_decode_attribute, ie); switch (ie->cfg_type) { case ISAKMP_CFG_REQUEST: /* We're done. */ break; case ISAKMP_CFG_SET: { /* SET/ACK -- Client side (SET from server) */ const char *uk_addr = "<unknown>"; msg->transport->vtbl->get_dst(isakmp_sa->transport, &sa); if (sockaddr2text(sa, &addr, 0) < 0) addr = (char *) uk_addr; for (attr = LIST_FIRST(&ie->attrs); attr; attr = LIST_NEXT(attr, link)) LOG_DBG((LOG_NEGOTIATION, 50, "cfg_responder_recv_ATTR: " "server %s asks us to SET attribute %s", addr, constant_name(isakmp_cfg_attr_cst, attr->type))); /* * XXX Here's the place to add code to walk through * XXX each attribute and send them along to dhclient * XXX or whatever. Each attribute that we act upon * XXX (such as setting a netmask), should be marked * XXX like this for us to send the proper ACK * XXX response: attr->attr_used++; */ if (addr != uk_addr) free(addr); } break; default: break; } attrp->flags |= PL_MARK; return 0; }
/* * As "the server", this ends SET/ACK. * As "the client", this ends REQ/REPLY. */ static int cfg_initiator_recv_ATTR(struct message *msg) { struct payload *attrp = payload_first(msg, ISAKMP_PAYLOAD_ATTRIBUTE); struct ipsec_exch *ie = msg->exchange->data; struct sa *isakmp_sa = msg->isakmp_sa; struct isakmp_cfg_attr *attr; struct sockaddr *sa; const char *uk_addr = "<unknown>"; char *addr; if (msg->exchange->phase == 2) if (cfg_verify_hash(msg)) return -1; /* Sanity. */ if (ie->cfg_id != GET_ISAKMP_ATTRIBUTE_ID(attrp->p)) { log_print("cfg_initiator_recv_ATTR: " "cfg packet ID does not match!"); message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } switch (attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]) { case ISAKMP_CFG_ACK: if (ie->cfg_type != ISAKMP_CFG_SET) { log_print("cfg_initiator_recv_ATTR: " "bad packet type ACK"); message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } break; case ISAKMP_CFG_REPLY: if (ie->cfg_type != ISAKMP_CFG_REQUEST) { log_print("cfg_initiator_recv_ATTR: " "bad packet type REPLY"); message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } break; default: log_print("cfg_initiator_recv_ATTR: unexpected configuration " "message type %d", attrp->p[ISAKMP_ATTRIBUTE_TYPE_OFF]); message_drop(msg, ISAKMP_NOTIFY_PAYLOAD_MALFORMED, 0, 1, 0); return -1; } attribute_map(attrp->p + ISAKMP_ATTRIBUTE_ATTRS_OFF, GET_ISAKMP_GEN_LENGTH(attrp->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, cfg_decode_attribute, ie); switch (ie->cfg_type) { case ISAKMP_CFG_ACK: { /* SET/ACK -- Server side (ACK from client) */ msg->transport->vtbl->get_src(isakmp_sa->transport, &sa); if (sockaddr2text(sa, &addr, 0) < 0) addr = (char *) uk_addr; for (attr = LIST_FIRST(&ie->attrs); attr; attr = LIST_NEXT(attr, link)) LOG_DBG((LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "client %s ACKs attribute %s", addr, constant_name(isakmp_cfg_attr_cst, attr->type))); if (addr != uk_addr) free(addr); } break; case ISAKMP_CFG_REPLY: { /* * REQ/REPLY: effect attributes we've gotten * responses on. */ msg->transport->vtbl->get_src(isakmp_sa->transport, &sa); if (sockaddr2text(sa, &addr, 0) < 0) addr = (char *) uk_addr; for (attr = LIST_FIRST(&ie->attrs); attr; attr = LIST_NEXT(attr, link)) LOG_DBG((LOG_NEGOTIATION, 50, "cfg_initiator_recv_ATTR: " "server %s replied with attribute %s", addr, constant_name(isakmp_cfg_attr_cst, attr->type))); if (addr != uk_addr) free(addr); } break; default: break; } attrp->flags |= PL_MARK; return 0; }
/* Receive ID. */ int ike_phase_1_recv_ID(struct message *msg) { struct exchange *exchange = msg->exchange; struct payload *payload; char header[80], *rs = 0, *rid = 0, *p; int initiator = exchange->initiator; u_int8_t **id, id_type; size_t *id_len; ssize_t sz; struct sockaddr *sa; sa_family_t af = 0; payload = payload_first(msg, ISAKMP_PAYLOAD_ID); if (exchange->name) rs = conf_get_str(exchange->name, "Remote-ID"); if (rs) { sz = ipsec_id_size(rs, &id_type); if (sz == -1) { log_print("ike_phase_1_recv_ID: could not handle " "specified Remote-ID [%s]", rs); return -1; } rid = malloc(sz); if (!rid) { log_error("ike_phase_1_recv_ID: malloc (%lu) failed", (unsigned long)sz); return -1; } switch (id_type) { case IPSEC_ID_IPV4_ADDR: af = AF_INET; break; case IPSEC_ID_IPV6_ADDR: af = AF_INET6; break; } switch (id_type) { case IPSEC_ID_IPV4_ADDR: case IPSEC_ID_IPV6_ADDR: p = conf_get_str(rs, "Address"); if (!p) { log_print("ike_phase_1_recv_ID: failed to get " "Address in Remote-ID section [%s]", rs); free(rid); return -1; } if (text2sockaddr(p, 0, &sa, af, 0) == -1) { log_print("ike_phase_1_recv_ID: " "failed to parse address %s", p); free(rid); return -1; } if ((id_type == IPSEC_ID_IPV4_ADDR && sa->sa_family != AF_INET) || (id_type == IPSEC_ID_IPV6_ADDR && sa->sa_family != AF_INET6)) { log_print("ike_phase_1_recv_ID: " "address %s not of expected family", p); free(rid); free(sa); return -1; } memcpy(rid, sockaddr_addrdata(sa), sockaddr_addrlen(sa)); free(sa); break; case IPSEC_ID_FQDN: case IPSEC_ID_USER_FQDN: case IPSEC_ID_KEY_ID: p = conf_get_str(rs, "Name"); if (!p) { log_print("ike_phase_1_recv_ID: failed to " "get Name in Remote-ID section [%s]", rs); free(rid); return -1; } memcpy(rid, p, sz); break; default: log_print("ike_phase_1_recv_ID: " "unsupported ID type %d", id_type); free(rid); return -1; } /* Compare expected/desired and received remote ID */ if (bcmp(rid, payload->p + ISAKMP_ID_DATA_OFF, sz)) { free(rid); log_print("ike_phase_1_recv_ID: " "received remote ID other than expected %s", p); return -1; } free(rid); } /* Choose the right fields to fill in */ id = initiator ? &exchange->id_r : &exchange->id_i; id_len = initiator ? &exchange->id_r_len : &exchange->id_i_len; *id_len = GET_ISAKMP_GEN_LENGTH(payload->p) - ISAKMP_GEN_SZ; *id = malloc(*id_len); if (!*id) { log_error("ike_phase_1_recv_ID: malloc (%lu) failed", (unsigned long)*id_len); return -1; } memcpy(*id, payload->p + ISAKMP_GEN_SZ, *id_len); snprintf(header, sizeof header, "ike_phase_1_recv_ID: %s", constant_name(ipsec_id_cst, GET_ISAKMP_ID_TYPE(payload->p))); LOG_DBG_BUF((LOG_NEGOTIATION, 40, header, payload->p + ISAKMP_ID_DATA_OFF, *id_len + ISAKMP_GEN_SZ - ISAKMP_ID_DATA_OFF)); payload->flags |= PL_MARK; return 0; }
/* Offer a set of transforms to the responder in the MSG message. */ int ike_phase_1_initiator_send_SA(struct message *msg) { struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; u_int8_t *proposal = 0, *sa_buf = 0, *saved_nextp, *attr; u_int8_t **transform = 0; size_t transforms_len = 0, proposal_len, sa_len; size_t *transform_len = 0; struct conf_list *conf, *life_conf; struct conf_list_node *xf, *life; int value, update_nextp; size_t i; struct payload *p; struct proto *proto; struct proto_attr *pa; int group_desc = -1, new_group_desc; /* Get the list of transforms. */ conf = conf_get_list(exchange->policy, "Transforms"); if (!conf) return -1; transform = calloc(conf->cnt, sizeof *transform); if (!transform) { log_error("ike_phase_1_initiator_send_SA: calloc (%lu, %lu) " "failed", (u_long)conf->cnt, (u_long)sizeof *transform); goto bail_out; } transform_len = calloc(conf->cnt, sizeof *transform_len); if (!transform_len) { log_error("ike_phase_1_initiator_send_SA: calloc (%lu, %lu) " "failed", (u_long)conf->cnt, (u_long)sizeof *transform_len); goto bail_out; } for (xf = TAILQ_FIRST(&conf->fields), i = 0; i < conf->cnt; i++, xf = TAILQ_NEXT(xf, link)) { /* XXX The sizing needs to be dynamic. */ transform[i] = malloc(ISAKMP_TRANSFORM_SA_ATTRS_OFF + 16 * ISAKMP_ATTR_VALUE_OFF); if (!transform[i]) { log_error("ike_phase_1_initiator_send_SA: malloc (%d) " "failed", ISAKMP_TRANSFORM_SA_ATTRS_OFF + 16 * ISAKMP_ATTR_VALUE_OFF); goto bail_out; } SET_ISAKMP_TRANSFORM_NO(transform[i], i); SET_ISAKMP_TRANSFORM_ID(transform[i], IPSEC_TRANSFORM_KEY_IKE); SET_ISAKMP_TRANSFORM_RESERVED(transform[i], 0); attr = transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF; if (attribute_set_constant(xf->field, "ENCRYPTION_ALGORITHM", ike_encrypt_cst, IKE_ATTR_ENCRYPTION_ALGORITHM, &attr)) goto bail_out; if (attribute_set_constant(xf->field, "HASH_ALGORITHM", ike_hash_cst, IKE_ATTR_HASH_ALGORITHM, &attr)) goto bail_out; if (attribute_set_constant(xf->field, "AUTHENTICATION_METHOD", ike_auth_cst, IKE_ATTR_AUTHENTICATION_METHOD, &attr)) goto bail_out; if (attribute_set_constant(xf->field, "GROUP_DESCRIPTION", ike_group_desc_cst, IKE_ATTR_GROUP_DESCRIPTION, &attr)) { /* * If no group description exists, try looking for * a user-defined one. */ if (attribute_set_constant(xf->field, "GROUP_TYPE", ike_group_cst, IKE_ATTR_GROUP_TYPE, &attr)) goto bail_out; #if 0 if (attribute_set_bignum(xf->field, "GROUP_PRIME", IKE_ATTR_GROUP_PRIME, &attr)) goto bail_out; if (attribute_set_bignum(xf->field, "GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, &attr)) goto bail_out; if (attribute_set_bignum(xf->field, "GROUP_GENERATOR_2", IKE_ATTR_GROUP_GENERATOR_2, &attr)) goto bail_out; if (attribute_set_bignum(xf->field, "GROUP_CURVE_A", IKE_ATTR_GROUP_CURVE_A, &attr)) goto bail_out; if (attribute_set_bignum(xf->field, "GROUP_CURVE_B", IKE_ATTR_GROUP_CURVE_B, &attr)) goto bail_out; #endif } /* * Life durations are special, we should be able to specify * several, one per type. */ life_conf = conf_get_list(xf->field, "Life"); if (life_conf) { for (life = TAILQ_FIRST(&life_conf->fields); life; life = TAILQ_NEXT(life, link)) { attribute_set_constant(life->field, "LIFE_TYPE", ike_duration_cst, IKE_ATTR_LIFE_TYPE, &attr); /* * XXX Deals with 16 and 32 bit lifetimes * only */ value = conf_get_num(life->field, "LIFE_DURATION", 0); if (value) { if (value <= 0xffff) attr = attribute_set_basic( attr, IKE_ATTR_LIFE_DURATION, value); else { value = htonl(value); attr = attribute_set_var(attr, IKE_ATTR_LIFE_DURATION, (u_int8_t *)&value, sizeof value); } } } conf_free_list(life_conf); } attribute_set_constant(xf->field, "PRF", ike_prf_cst, IKE_ATTR_PRF, &attr); value = conf_get_num(xf->field, "KEY_LENGTH", 0); if (value) attr = attribute_set_basic(attr, IKE_ATTR_KEY_LENGTH, value); value = conf_get_num(xf->field, "FIELD_SIZE", 0); if (value) attr = attribute_set_basic(attr, IKE_ATTR_FIELD_SIZE, value); value = conf_get_num(xf->field, "GROUP_ORDER", 0); if (value) attr = attribute_set_basic(attr, IKE_ATTR_GROUP_ORDER, value); /* Record the real transform size. */ transforms_len += transform_len[i] = attr - transform[i]; /* XXX I don't like exchange-specific stuff in here. */ if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) { /* * Make sure that if a group description is specified, * it is specified for all transforms equally. */ attr = (u_int8_t *)conf_get_str(xf->field, "GROUP_DESCRIPTION"); new_group_desc = attr ? constant_value(ike_group_desc_cst, (char *)attr) : 0; if (group_desc == -1) group_desc = new_group_desc; else if (group_desc != new_group_desc) { log_print("ike_phase_1_initiator_send_SA: " "differing group descriptions in a " "proposal"); goto bail_out; } } /* * We need to check that we actually support our * configuration. */ if (attribute_map(transform[i] + ISAKMP_TRANSFORM_SA_ATTRS_OFF, transform_len[i] - ISAKMP_TRANSFORM_SA_ATTRS_OFF, exchange->doi->is_attribute_incompatible, msg)) { log_print("ike_phase_1_initiator_send_SA: " "section [%s] has unsupported attribute(s)", xf->field); goto bail_out; } } /* XXX I don't like exchange-specific stuff in here. */ if (exchange->type == ISAKMP_EXCH_AGGRESSIVE) ie->group = group_get(group_desc); proposal_len = ISAKMP_PROP_SPI_OFF; proposal = malloc(proposal_len); if (!proposal) { log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", (unsigned long)proposal_len); goto bail_out; } SET_ISAKMP_PROP_NO(proposal, 1); SET_ISAKMP_PROP_PROTO(proposal, ISAKMP_PROTO_ISAKMP); SET_ISAKMP_PROP_SPI_SZ(proposal, 0); SET_ISAKMP_PROP_NTRANSFORMS(proposal, conf->cnt); /* XXX I would like to see this factored out. */ proto = calloc(1, sizeof *proto); if (!proto) { log_error("ike_phase_1_initiator_send_SA: " "calloc (1, %lu) failed", (unsigned long)sizeof *proto); goto bail_out; } proto->no = 1; proto->proto = ISAKMP_PROTO_ISAKMP; proto->sa = TAILQ_FIRST(&exchange->sa_list); proto->xf_cnt = conf->cnt; TAILQ_INIT(&proto->xfs); for (i = 0; i < proto->xf_cnt; i++) { pa = (struct proto_attr *)calloc(1, sizeof *pa); if (!pa) goto bail_out; pa->len = transform_len[i]; pa->attrs = (u_int8_t *)malloc(pa->len); if (!pa->attrs) { free(pa); goto bail_out; } memcpy(pa->attrs, transform[i], pa->len); TAILQ_INSERT_TAIL(&proto->xfs, pa, next); } TAILQ_INSERT_TAIL(&TAILQ_FIRST(&exchange->sa_list)->protos, proto, link); sa_len = ISAKMP_SA_SIT_OFF + IPSEC_SIT_SIT_LEN; sa_buf = malloc(sa_len); if (!sa_buf) { log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", (unsigned long)sa_len); goto bail_out; } SET_ISAKMP_SA_DOI(sa_buf, IPSEC_DOI_IPSEC); SET_IPSEC_SIT_SIT(sa_buf + ISAKMP_SA_SIT_OFF, IPSEC_SIT_IDENTITY_ONLY); /* * Add the payloads. As this is a SA, we need to recompute the * lengths of the payloads containing others. */ if (message_add_payload(msg, ISAKMP_PAYLOAD_SA, sa_buf, sa_len, 1)) goto bail_out; SET_ISAKMP_GEN_LENGTH(sa_buf, sa_len + proposal_len + transforms_len); sa_buf = 0; saved_nextp = msg->nextp; if (message_add_payload(msg, ISAKMP_PAYLOAD_PROPOSAL, proposal, proposal_len, 0)) goto bail_out; SET_ISAKMP_GEN_LENGTH(proposal, proposal_len + transforms_len); proposal = 0; update_nextp = 0; for (i = 0; i < conf->cnt; i++) { if (message_add_payload(msg, ISAKMP_PAYLOAD_TRANSFORM, transform[i], transform_len[i], update_nextp)) goto bail_out; update_nextp = 1; transform[i] = 0; } msg->nextp = saved_nextp; /* Save SA payload body in ie->sa_i_b, length ie->sa_i_b_len. */ ie->sa_i_b_len = sa_len + proposal_len + transforms_len - ISAKMP_GEN_SZ; ie->sa_i_b = malloc(ie->sa_i_b_len); if (!ie->sa_i_b) { log_error("ike_phase_1_initiator_send_SA: malloc (%lu) failed", (unsigned long)ie->sa_i_b_len); goto bail_out; } memcpy(ie->sa_i_b, payload_first(msg, ISAKMP_PAYLOAD_SA)->p + ISAKMP_GEN_SZ, sa_len - ISAKMP_GEN_SZ); memcpy(ie->sa_i_b + sa_len - ISAKMP_GEN_SZ, payload_first(msg, ISAKMP_PAYLOAD_PROPOSAL)->p, proposal_len); transforms_len = 0; for (i = 0, p = TAILQ_FIRST(&msg->payload[ISAKMP_PAYLOAD_TRANSFORM]); i < conf->cnt; i++, p = TAILQ_NEXT(p, link)) { memcpy(ie->sa_i_b + sa_len + proposal_len + transforms_len - ISAKMP_GEN_SZ, p->p, transform_len[i]); transforms_len += transform_len[i]; } /* Advertise OpenBSD isakmpd. */ if (add_vendor_openbsd(msg)) goto bail_out; /* Advertise NAT-T capability. */ if (nat_t_add_vendor_payloads(msg)) goto bail_out; /* Advertise DPD capability. */ if (dpd_add_vendor_payload(msg)) goto bail_out; conf_free_list(conf); free(transform); free(transform_len); return 0; bail_out: free(sa_buf); free(proposal); if (transform) { for (i = 0; i < conf->cnt; i++) if (transform[i]) free(transform[i]); free(transform); } free(transform_len); conf_free_list(conf); return -1; }