/* * This function is used to validate the returned proposal (protection suite) * we get from the responder against a proposal we sent. Only run as initiator. * We return 0 if a match is found (in any transform of this proposal), 1 * otherwise. Also see note in sa_add_transform() below. */ static int sa_validate_proto_xf(struct proto *match, struct payload *xf, int phase) { struct attr_validation_state *avs; struct proto_attr *pa; int found = 0; size_t i; u_int8_t xf_id; if (!match->xf_cnt) return 0; if (match->proto != GET_ISAKMP_PROP_PROTO(xf->context->p)) { LOG_DBG((LOG_SA, 70, "sa_validate_proto_xf: proto %p (#%d) " "protocol mismatch", match, match->no)); return 1; } avs = (struct attr_validation_state *)calloc(1, sizeof *avs); if (!avs) { log_error("sa_validate_proto_xf: calloc (1, %lu)", (unsigned long)sizeof *avs); return 1; } avs->phase = phase; /* Load the "proposal candidate" attribute set. */ (void)attribute_map(xf->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF, GET_ISAKMP_GEN_LENGTH(xf->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, sa_validate_xf_attrs, avs); xf_id = GET_ISAKMP_TRANSFORM_ID(xf->p); /* Check against the transforms we suggested. */ avs->mode++; for (pa = TAILQ_FIRST(&match->xfs); pa && !found; pa = TAILQ_NEXT(pa, next)) { if (xf_id != GET_ISAKMP_TRANSFORM_ID(pa->attrs)) continue; bzero(avs->checked, sizeof avs->checked); if (attribute_map(pa->attrs + ISAKMP_TRANSFORM_SA_ATTRS_OFF, pa->len - ISAKMP_TRANSFORM_SA_ATTRS_OFF, sa_validate_xf_attrs, avs) == 0) found++; LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: attr_map " "xf %p proto %p pa %p found %d", xf, match, pa, found)); if (!found) continue; /* * Require all attributes present and checked. XXX perhaps * not? */ for (i = 0; i < sizeof avs->checked; i++) if (avs->attrp[i] && !avs->checked[i]) found = 0; LOG_DBG((LOG_SA, 80, "sa_validate_proto_xf: req_attr " "xf %p proto %p pa %p found %d", xf, match, pa, found)); } free(avs); return found ? 0 : 1; }
QVariant style::attribute(const QString &a_type, const QString &a_name) const { return attribute_map(a_type)[a_name]; }
/* * 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; }
/* 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; }
/* Validate a proposal inside SA according to EXCHANGE's policy. */ static int ike_phase_1_validate_prop (struct exchange *exchange, struct sa *sa, struct sa *isakmp_sa) { struct conf_list *conf, *tags; struct conf_list_node *xf, *tag; struct proto *proto; struct validation_state vs; struct attr_node *node, *next_node; /* Get the list of transforms. */ conf = conf_get_list (exchange->policy, "Transforms"); if (!conf) return 0; for (xf = TAILQ_FIRST (&conf->fields); xf; xf = TAILQ_NEXT (xf, link)) { for (proto = TAILQ_FIRST (&sa->protos); proto; proto = TAILQ_NEXT (proto, link)) { /* Mark all attributes in our policy as unseen. */ LIST_INIT (&vs.attrs); vs.xf = xf; vs.life = 0; if (attribute_map (proto->chosen->p + ISAKMP_TRANSFORM_SA_ATTRS_OFF, GET_ISAKMP_GEN_LENGTH (proto->chosen->p) - ISAKMP_TRANSFORM_SA_ATTRS_OFF, attribute_unacceptable, &vs)) goto try_next; /* Sweep over unseen tags in this section. */ tags = conf_get_tag_list (xf->field); if (tags) { for (tag = TAILQ_FIRST (&tags->fields); tag; tag = TAILQ_NEXT (tag, link)) /* * XXX Should we care about attributes we have, they do not * provide? */ for (node = LIST_FIRST (&vs.attrs); node; node = next_node) { next_node = LIST_NEXT (node, link); if (node->type == constant_value (ike_attr_cst, tag->field)) { LIST_REMOVE (node, link); free (node); } } conf_free_list (tags); } /* Are there leftover tags in this section? */ node = LIST_FIRST (&vs.attrs); if (node) goto try_next; } /* All protocols were OK, we succeeded. */ LOG_DBG ((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: success")); conf_free_list (conf); if (vs.life) free (vs.life); return 1; try_next: /* Are there leftover tags in this section? */ node = LIST_FIRST (&vs.attrs); while (node) { LIST_REMOVE (node, link); free (node); node = LIST_FIRST (&vs.attrs); } if (vs.life) free (vs.life); } LOG_DBG ((LOG_NEGOTIATION, 20, "ike_phase_1_validate_prop: failure")); conf_free_list (conf); return 0; }