/* Add a NAT-D payload to our message. */ static int nat_t_add_nat_d(struct message *msg, struct sockaddr *sa) { int ret; u_int8_t *hbuf, *buf; size_t hbuflen, buflen; hbuf = nat_t_generate_nat_d_hash(msg, sa, &hbuflen); if (!hbuf) { log_print("nat_t_add_nat_d: NAT-D hash gen failed"); return -1; } buflen = ISAKMP_NAT_D_DATA_OFF + hbuflen; buf = malloc(buflen); if (!buf) { log_error("nat_t_add_nat_d: malloc (%lu) failed", (unsigned long)buflen); free(hbuf); return -1; } SET_ISAKMP_GEN_LENGTH(buf, buflen); memcpy(buf + ISAKMP_NAT_D_DATA_OFF, hbuf, hbuflen); free(hbuf); if (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_RFC) ret = message_add_payload(msg, ISAKMP_PAYLOAD_NAT_D, buf, buflen, 1); else if (msg->exchange->flags & EXCHANGE_FLAG_NAT_T_DRAFT) ret = message_add_payload(msg, ISAKMP_PAYLOAD_NAT_D_DRAFT, buf, buflen, 1); else ret = -1; if (ret) { free(buf); return -1; } return 0; }
/* * As "the server", this ends REQ/REPLY mode. * As "the client", this ends SET/ACK mode. */ static int cfg_responder_send_ATTR(struct message *msg) { struct ipsec_exch *ie = msg->exchange->data; struct sa *isakmp_sa = msg->isakmp_sa; u_int8_t *hashp = 0, *attrp; u_int16_t attrlen; char *id_string; if (msg->exchange->phase == 2) { hashp = cfg_add_hash(msg); if (!hashp) return -1; } /* We are responder, check isakmp_sa for other side. */ if (isakmp_sa->initiator ^ (ie->cfg_type == ISAKMP_CFG_REQUEST)) id_string = ipsec_id_string(isakmp_sa->id_i, isakmp_sa->id_i_len); else id_string = ipsec_id_string(isakmp_sa->id_r, isakmp_sa->id_r_len); if (!id_string) { log_print("cfg_responder_send_ATTR: cannot parse client's ID"); return -1; } if (cfg_encode_attributes(&ie->attrs, (ie->cfg_type == ISAKMP_CFG_SET ? ISAKMP_CFG_ACK : ISAKMP_CFG_REPLY), ie->cfg_id, id_string, &attrp, &attrlen)) { free(id_string); return -1; } free(id_string); if (message_add_payload(msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen, 1)) { free(attrp); return -1; } if (msg->exchange->phase == 2) if (cfg_finalize_hash(msg, hashp, attrp, attrlen)) return -1; return 0; }
u_int8_t * cfg_add_hash(struct message *msg) { struct ipsec_sa *isa = msg->isakmp_sa->data; struct hash *hash = hash_get(isa->hash); u_int8_t *hashp; hashp = malloc(ISAKMP_HASH_SZ + hash->hashsize); if (!hashp) { log_error("cfg_add_hash: malloc (%lu) failed", ISAKMP_HASH_SZ + (unsigned long)hash->hashsize); return 0; } if (message_add_payload(msg, ISAKMP_PAYLOAD_HASH, hashp, ISAKMP_HASH_SZ + hash->hashsize, 1)) { free(hashp); return 0; } return hashp; }
/* Add one NAT-T VENDOR payload. */ static int nat_t_add_vendor_payload(struct message *msg, struct nat_t_cap *cap) { size_t buflen = cap->hashsize + ISAKMP_GEN_SZ; u_int8_t *buf; if (disable_nat_t) return 0; buf = malloc(buflen); if (!buf) { log_error("nat_t_add_vendor_payload: malloc (%lu) failed", (unsigned long)buflen); return -1; } SET_ISAKMP_GEN_LENGTH(buf, buflen); memcpy(buf + ISAKMP_VENDOR_ID_OFF, cap->hash, cap->hashsize); if (message_add_payload(msg, ISAKMP_PAYLOAD_VENDOR, buf, buflen, 1)) { free(buf); return -1; } return 0; }
/* * When we are "the server", this starts SET/ACK mode * When we are "the client", this starts REQ/REPLY mode */ static int cfg_initiator_send_ATTR(struct message *msg) { struct sa *isakmp_sa = msg->isakmp_sa; struct ipsec_exch *ie = msg->exchange->data; u_int8_t *hashp = 0, *attrp, *attr; size_t attrlen, off; char *id_string, *cfg_mode, *field; struct sockaddr *sa; #define CFG_ATTR_BIT_MAX ISAKMP_CFG_ATTR_FUTURE_MIN /* XXX */ bitstr_t bit_decl(attrbits, CFG_ATTR_BIT_MAX); u_int16_t bit, length; u_int32_t life; if (msg->exchange->phase == 2) { hashp = cfg_add_hash(msg); if (!hashp) return -1; } /* We initiated this exchange, check isakmp_sa for other side. */ if (isakmp_sa->initiator) id_string = ipsec_id_string(isakmp_sa->id_r, isakmp_sa->id_r_len); else id_string = ipsec_id_string(isakmp_sa->id_i, isakmp_sa->id_i_len); if (!id_string) { log_print("cfg_initiator_send_ATTR: cannot parse ID"); goto fail; } /* Check for attribute list to send to the other side */ attrlen = 0; bit_nclear(attrbits, 0, CFG_ATTR_BIT_MAX - 1); cfg_mode = conf_get_str(id_string, "Mode"); if (!cfg_mode || strcmp(cfg_mode, "SET") == 0) { /* SET/ACK mode */ ie->cfg_type = ISAKMP_CFG_SET; LOG_DBG((LOG_NEGOTIATION, 10, "cfg_initiator_send_ATTR: SET/ACK mode")); #define ATTRFIND(STR,ATTR4,LEN4,ATTR6,LEN6) do \ { \ if ((sa = conf_get_address (id_string, STR)) != NULL) \ switch (sa->sa_family) { \ case AF_INET: \ bit_set (attrbits, ATTR4); \ attrlen += ISAKMP_ATTR_SZ + LEN4; \ break; \ case AF_INET6: \ bit_set (attrbits, ATTR6); \ attrlen += ISAKMP_ATTR_SZ + LEN6; \ break; \ default: \ break; \ } \ free (sa); \ } while (0) /* * XXX We don't simultaneously support IPv4 and IPv6 * addresses. */ ATTRFIND("Address", ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS, 4, ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS, 16); ATTRFIND("Netmask", ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK, 4, ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK, 16); ATTRFIND("Nameserver", ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS, 4, ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS, 16); ATTRFIND("WINS-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS, 4, ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS, 16); ATTRFIND("DHCP-server", ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP, 4, ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP, 16); #ifdef notyet ATTRFIND("Network", ISAKMP_CFG_ATTR_INTERNAL_IP4_SUBNET, 8, ISAKMP_CFG_ATTR_INTERNAL_IP6_SUBNET, 17); #endif #undef ATTRFIND if (conf_get_str(id_string, "Lifetime")) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY); attrlen += ISAKMP_ATTR_SZ + 4; } } else { struct conf_list *alist; struct conf_list_node *anode; ie->cfg_type = ISAKMP_CFG_REQUEST; LOG_DBG((LOG_NEGOTIATION, 10, "cfg_initiator_send_ATTR: REQ/REPLY mode")); alist = conf_get_list(id_string, "Attributes"); if (alist) { for (anode = TAILQ_FIRST(&alist->fields); anode; anode = TAILQ_NEXT(anode, link)) { if (strcasecmp(anode->field, "Address") == 0) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS); bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS); attrlen += ISAKMP_ATTR_SZ * 2; } else if (strcasecmp(anode->field, "Netmask") == 0) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK); bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK); attrlen += ISAKMP_ATTR_SZ * 2; } else if (strcasecmp(anode->field, "Nameserver") == 0) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS); bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS); attrlen += ISAKMP_ATTR_SZ * 2; } else if (strcasecmp(anode->field, "WINS-server") == 0) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS); bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS); attrlen += ISAKMP_ATTR_SZ * 2; } else if (strcasecmp(anode->field, "DHCP-server") == 0) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP); bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP); attrlen += ISAKMP_ATTR_SZ * 2; } else if (strcasecmp(anode->field, "Lifetime") == 0) { bit_set(attrbits, ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY); attrlen += ISAKMP_ATTR_SZ; } else { log_print("cfg_initiator_send_ATTR: " "unknown attribute %.20s in " "section [%s]", anode->field, id_string); } } conf_free_list(alist); } } if (attrlen == 0) { /* No data found. */ log_print("cfg_initiator_send_ATTR: no IKECFG attributes " "found for [%s]", id_string); /* * We can continue, but this indicates a configuration error * that the user probably will want to correct. */ free(id_string); return 0; } attrlen += ISAKMP_ATTRIBUTE_SZ; attrp = calloc(1, attrlen); if (!attrp) { log_error("cfg_initiator_send_ATTR: calloc (1, %lu) failed", (unsigned long)attrlen); goto fail; } if (message_add_payload(msg, ISAKMP_PAYLOAD_ATTRIBUTE, attrp, attrlen, 1)) { free(attrp); goto fail; } SET_ISAKMP_ATTRIBUTE_TYPE(attrp, ie->cfg_type); getrandom((u_int8_t *) & ie->cfg_id, sizeof ie->cfg_id); SET_ISAKMP_ATTRIBUTE_ID(attrp, ie->cfg_id); off = ISAKMP_ATTRIBUTE_SZ; /* * Use the bitstring built previously to collect the right * parameters for attrp. */ for (bit = 0; bit < CFG_ATTR_BIT_MAX; bit++) if (bit_test(attrbits, bit)) { attr = attrp + off; SET_ISAKMP_ATTR_TYPE(attr, bit); if (ie->cfg_type == ISAKMP_CFG_REQUEST) { off += ISAKMP_ATTR_SZ; continue; } /* All the other are similar, this is the odd one. */ if (bit == ISAKMP_CFG_ATTR_INTERNAL_ADDRESS_EXPIRY) { life = conf_get_num(id_string, "Lifetime", 1200); SET_ISAKMP_ATTR_LENGTH_VALUE(attr, 4); encode_32(attr + ISAKMP_ATTR_VALUE_OFF, life); off += ISAKMP_ATTR_SZ + 4; continue; } switch (bit) { case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: length = 4; break; case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: length = 16; break; default: length = 0; /* Silence gcc. */ } switch (bit) { case ISAKMP_CFG_ATTR_INTERNAL_IP4_ADDRESS: case ISAKMP_CFG_ATTR_INTERNAL_IP6_ADDRESS: field = "Address"; break; case ISAKMP_CFG_ATTR_INTERNAL_IP4_NETMASK: case ISAKMP_CFG_ATTR_INTERNAL_IP6_NETMASK: field = "Netmask"; break; case ISAKMP_CFG_ATTR_INTERNAL_IP4_DNS: case ISAKMP_CFG_ATTR_INTERNAL_IP6_DNS: field = "Nameserver"; break; case ISAKMP_CFG_ATTR_INTERNAL_IP4_DHCP: case ISAKMP_CFG_ATTR_INTERNAL_IP6_DHCP: field = "DHCP-server"; break; case ISAKMP_CFG_ATTR_INTERNAL_IP4_NBNS: case ISAKMP_CFG_ATTR_INTERNAL_IP6_NBNS: field = "WINS-server"; break; default: field = 0; /* Silence gcc. */ } sa = conf_get_address(id_string, field); SET_ISAKMP_ATTR_LENGTH_VALUE(attr, length); memcpy(attr + ISAKMP_ATTR_VALUE_OFF, sockaddr_addrdata(sa), length); free(sa); off += ISAKMP_ATTR_SZ + length; } if (msg->exchange->phase == 2) if (cfg_finalize_hash(msg, hashp, attrp, attrlen)) goto fail; return 0; fail: free(id_string); return -1; }
int ike_phase_1_send_ID (struct message *msg) { struct exchange *exchange = msg->exchange; u_int8_t *buf; char header[80]; ssize_t sz; struct sockaddr *src; int initiator = exchange->initiator; u_int8_t **id; size_t *id_len; char *my_id = 0; u_int8_t id_type; /* Choose the right fields to fill-in. */ id = initiator ? &exchange->id_i : &exchange->id_r; id_len = initiator ? &exchange->id_i_len : &exchange->id_r_len; if (exchange->name) my_id = conf_get_str (exchange->name, "ID"); if (!my_id) my_id = conf_get_str ("General", "Default-phase-1-ID"); msg->transport->vtbl->get_src (msg->transport, &src); sz = my_id ? ipsec_id_size (my_id, &id_type) : sockaddr_addrlen (src); if (sz == -1) return -1; sz += ISAKMP_ID_DATA_OFF; buf = malloc (sz); if (!buf) { log_error ("ike_phase_1_send_ID: malloc (%lu) failed", (unsigned long)sz); return -1; } SET_IPSEC_ID_PROTO (buf + ISAKMP_ID_DOI_DATA_OFF, 0); SET_IPSEC_ID_PORT (buf + ISAKMP_ID_DOI_DATA_OFF, 0); if (my_id) { SET_ISAKMP_ID_TYPE (buf, id_type); switch (id_type) { case IPSEC_ID_IPV4_ADDR: case IPSEC_ID_IPV6_ADDR: /* Already in network byteorder. */ memcpy (buf + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src), sockaddr_addrlen (src)); break; case IPSEC_ID_FQDN: case IPSEC_ID_USER_FQDN: case IPSEC_ID_KEY_ID: memcpy (buf + ISAKMP_ID_DATA_OFF, conf_get_str (my_id, "Name"), sz - ISAKMP_ID_DATA_OFF); break; default: log_print ("ike_phase_1_send_ID: unsupported ID type %d", id_type); free (buf); return -1; } } else { switch (src->sa_family) { case AF_INET: SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV4_ADDR); break; case AF_INET6: SET_ISAKMP_ID_TYPE (buf, IPSEC_ID_IPV6_ADDR); break; } /* Already in network byteorder. */ memcpy (buf + ISAKMP_ID_DATA_OFF, sockaddr_addrdata (src), sockaddr_addrlen (src)); } if (message_add_payload (msg, ISAKMP_PAYLOAD_ID, buf, sz, 1)) { free (buf); return -1; } *id_len = sz - ISAKMP_GEN_SZ; *id = malloc (*id_len); if (!*id) { log_error ("ike_phase_1_send_ID: malloc (%lu) failed", (unsigned long)*id_len); return -1; } memcpy (*id, buf + ISAKMP_GEN_SZ, *id_len); snprintf (header, sizeof header, "ike_phase_1_send_ID: %s", constant_name (ipsec_id_cst, GET_ISAKMP_ID_TYPE (buf))); LOG_DBG_BUF ((LOG_NEGOTIATION, 40, header, buf + ISAKMP_ID_DATA_OFF, sz - ISAKMP_ID_DATA_OFF)); 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 i, value, update_nextp; struct payload *p; struct proto *proto; 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 (%d, %lu) failed", conf->cnt, (unsigned 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 (%d, %lu) failed", conf->cnt, (unsigned 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); 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, TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA])->p + ISAKMP_GEN_SZ, sa_len - ISAKMP_GEN_SZ); memcpy (ie->sa_i_b + sa_len - ISAKMP_GEN_SZ, TAILQ_FIRST (&msg->payload[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]; } conf_free_list (conf); free (transform); free (transform_len); return 0; bail_out: if (sa_buf) free (sa_buf); if (proposal) free (proposal); if (transform) { for (i = 0; i < conf->cnt; i++) if (transform[i]) free (transform[i]); free (transform); } if (transform_len) free (transform_len); conf_free_list (conf); return -1; }
/* Encrypt the HASH into a SIG type. */ static int rsa_sig_encode_hash (struct message *msg) { struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; size_t hashsize = ie->hash->hashsize; struct cert_handler *handler; char header[80]; int initiator = exchange->initiator; u_int8_t *buf, *data, *buf2; u_int32_t datalen; u_int8_t *id; size_t id_len; int idtype; void *sent_key; id = initiator ? exchange->id_i : exchange->id_r; id_len = initiator ? exchange->id_i_len : exchange->id_r_len; /* We may have been provided these by the kernel */ buf = (u_int8_t *)conf_get_str (exchange->name, "Credentials"); if (buf && (idtype = conf_get_num (exchange->name, "Credential_Type", -1) != -1)) { exchange->sent_certtype = idtype; handler = cert_get (idtype); if (!handler) { log_print ("rsa_sig_encode_hash: cert_get (%d) failed", idtype); return -1; } exchange->sent_cert = handler->cert_from_printable ((char *)buf); if (!exchange->sent_cert) { log_print ("rsa_sig_encode_hash: failed to retrieve certificate"); return -1; } handler->cert_serialize (exchange->sent_cert, &data, &datalen); if (!data) { log_print ("rsa_sig_encode_hash: cert serialization failed"); return -1; } goto aftercert; /* Skip all the certificate discovery */ } /* XXX This needs to be configurable. */ idtype = ISAKMP_CERTENC_KEYNOTE; /* Find a certificate with subjectAltName = id. */ handler = cert_get (idtype); if (!handler) { idtype = ISAKMP_CERTENC_X509_SIG; handler = cert_get (idtype); if (!handler) { log_print ("rsa_sig_encode_hash: cert_get(%d) failed", idtype); return -1; } } if (handler->cert_obtain (id, id_len, 0, &data, &datalen) == 0) { if (idtype == ISAKMP_CERTENC_KEYNOTE) { idtype = ISAKMP_CERTENC_X509_SIG; handler = cert_get (idtype); if (!handler) { log_print ("rsa_sig_encode_hash: cert_get(%d) failed", idtype); return -1; } if (handler->cert_obtain (id, id_len, 0, &data, &datalen) == 0) { LOG_DBG ((LOG_MISC, 10, "rsa_sig_encode_hash: no certificate to send")); goto skipcert; } } else { LOG_DBG ((LOG_MISC, 10, "rsa_sig_encode_hash: no certificate to send")); goto skipcert; } } /* Let's store the certificate we are going to use */ exchange->sent_certtype = idtype; exchange->sent_cert = handler->cert_get (data, datalen); if (!exchange->sent_cert) { free (data); log_print ("rsa_sig_encode_hash: failed to get certificate from wire " "encoding"); return -1; } aftercert: buf = realloc (data, ISAKMP_CERT_SZ + datalen); if (!buf) { log_error ("rsa_sig_encode_hash: realloc (%p, %d) failed", data, ISAKMP_CERT_SZ + datalen); free (data); return -1; } memmove (buf + ISAKMP_CERT_SZ, buf, datalen); SET_ISAKMP_CERT_ENCODING (buf, idtype); if (message_add_payload (msg, ISAKMP_PAYLOAD_CERT, buf, ISAKMP_CERT_SZ + datalen, 1)) { free (buf); return -1; } skipcert: /* Again, we may have these from the kernel */ buf = (u_int8_t *)conf_get_str (exchange->name, "PKAuthentication"); if (buf) { key_from_printable (ISAKMP_KEY_RSA, ISAKMP_KEYTYPE_PRIVATE, (char *)buf, &data, &datalen); if (!data || datalen == -1) { log_print ("rsa_sig_encode_hash: badly formatted RSA private key"); return 0; } sent_key = key_internalize (ISAKMP_KEY_RSA, ISAKMP_KEYTYPE_PRIVATE, data, datalen); if (!sent_key) { log_print ("rsa_sig_encode_hash: bad RSA private key from dynamic " "SA acquisition subsystem"); return 0; } #if defined (USE_PRIVSEP) { /* With USE_PRIVSEP, the sent_key should be a key number. */ void *key = sent_key; sent_key = monitor_RSA_upload_key (key); } #endif } else /* Try through the regular means. */ { switch (id[ISAKMP_ID_TYPE_OFF - ISAKMP_GEN_SZ]) { case IPSEC_ID_IPV4_ADDR: case IPSEC_ID_IPV6_ADDR: util_ntoa ((char **)&buf2, id[ISAKMP_ID_TYPE_OFF - ISAKMP_GEN_SZ] == IPSEC_ID_IPV4_ADDR ? AF_INET : AF_INET6, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ); if (!buf2) return 0; break; case IPSEC_ID_FQDN: case IPSEC_ID_USER_FQDN: buf2 = calloc (id_len - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1, sizeof (char)); if (!buf2) { log_print ("rsa_sig_encode_hash: malloc (%lu) failed", (unsigned long)id_len - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ + 1); return 0; } memcpy (buf2, id + ISAKMP_ID_DATA_OFF - ISAKMP_GEN_SZ, id_len - ISAKMP_ID_DATA_OFF + ISAKMP_GEN_SZ); break; /* XXX Support more ID types? */ default: buf2 = 0; return 0; } #if defined (USE_PRIVSEP) sent_key = monitor_RSA_get_private_key (exchange->name, (char *)buf2); #else sent_key = ike_auth_get_key (IKE_AUTH_RSA_SIG, exchange->name, (char *)buf2, 0); #endif free (buf2); /* Did we find a key? */ if (!sent_key) { log_print ("rsa_sig_encode_hash: could not get private key"); return -1; } } #if !defined (USE_PRIVSEP) /* Enable RSA blinding. */ if (RSA_blinding_on (sent_key, NULL) != 1) { log_error ("rsa_sig_encode_hash: RSA_blinding_on () failed."); return -1; } #endif /* XXX hashsize is not necessarily prf->blocksize. */ buf = malloc (hashsize); if (!buf) { log_error ("rsa_sig_encode_hash: malloc (%lu) failed", (unsigned long)hashsize); return -1; } if (ike_auth_hash (exchange, buf) == -1) { free (buf); return -1; } snprintf (header, sizeof header, "rsa_sig_encode_hash: HASH_%c", initiator ? 'I' : 'R'); LOG_DBG_BUF ((LOG_MISC, 80, header, buf, hashsize)); #if !defined (USE_PRIVSEP) data = malloc (RSA_size (sent_key)); if (!data) { log_error ("rsa_sig_encode_hash: malloc (%d) failed", RSA_size (sent_key)); return -1; } datalen = RSA_private_encrypt (hashsize, buf, data, sent_key, RSA_PKCS1_PADDING); #else datalen = monitor_RSA_private_encrypt (hashsize, buf, &data, sent_key, RSA_PKCS1_PADDING); #endif /* USE_PRIVSEP */ if (datalen == -1) { log_print ("rsa_sig_encode_hash: RSA_private_encrypt () failed"); if (data) free (data); free (buf); monitor_RSA_free (sent_key); return -1; } free (buf); buf = realloc (data, ISAKMP_SIG_SZ + datalen); if (!buf) { log_error ("rsa_sig_encode_hash: realloc (%p, %d) failed", data, ISAKMP_SIG_SZ + datalen); free (data); return -1; } memmove (buf + ISAKMP_SIG_SZ, buf, datalen); snprintf (header, sizeof header, "rsa_sig_encode_hash: SIG_%c", initiator ? 'I' : 'R'); LOG_DBG_BUF ((LOG_MISC, 80, header, buf + ISAKMP_SIG_DATA_OFF, datalen)); if (message_add_payload (msg, ISAKMP_PAYLOAD_SIG, buf, ISAKMP_SIG_SZ + datalen, 1)) { free (buf); return -1; } return 0; }