/* * 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 = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); struct payload *prop = TAILQ_FIRST (&msg->payload[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; }
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; }
/* Receive HASH and check that the exchange has been consistent. */ int ike_phase_1_recv_AUTH (struct message *msg) { struct exchange *exchange = msg->exchange; struct ipsec_exch *ie = exchange->data; struct prf *prf; struct hash *hash = ie->hash; char header[80]; size_t hashsize = hash->hashsize; int initiator = exchange->initiator; u_int8_t **hash_p, *id; size_t id_len; /* Choose the right fields to fill in */ hash_p = initiator ? &ie->hash_r : &ie->hash_i; id = initiator ? exchange->id_r : exchange->id_i; id_len = initiator ? exchange->id_r_len : exchange->id_i_len; /* The decoded hash will be in ie->hash_r or ie->hash_i */ if (ie->ike_auth->decode_hash (msg)) { message_drop (msg, ISAKMP_NOTIFY_INVALID_ID_INFORMATION, 0, 1, 0); return -1; } /* Allocate the prf and start calculating his HASH. */ prf = prf_alloc (ie->prf_type, hash->type, ie->skeyid, ie->skeyid_len); if (!prf) { /* XXX Log? */ return -1; } prf->Init (prf->prfctx); prf->Update (prf->prfctx, initiator ? ie->g_xr : ie->g_xi, ie->g_x_len); prf->Update (prf->prfctx, initiator ? ie->g_xi : ie->g_xr, ie->g_x_len); prf->Update (prf->prfctx, exchange->cookies + (initiator ? ISAKMP_HDR_RCOOKIE_OFF : ISAKMP_HDR_ICOOKIE_OFF), ISAKMP_HDR_ICOOKIE_LEN); prf->Update (prf->prfctx, exchange->cookies + (initiator ? ISAKMP_HDR_ICOOKIE_OFF : ISAKMP_HDR_RCOOKIE_OFF), ISAKMP_HDR_ICOOKIE_LEN); prf->Update (prf->prfctx, ie->sa_i_b, ie->sa_i_b_len); prf->Update (prf->prfctx, id, id_len); prf->Final (hash->digest, prf->prfctx); prf_free (prf); snprintf (header, sizeof header, "ike_phase_1_recv_AUTH: computed HASH_%c", initiator ? 'R' : 'I'); LOG_DBG_BUF ((LOG_NEGOTIATION, 80, header, hash->digest, hashsize)); /* Check that the hash we got matches the one we computed. */ if (memcmp (*hash_p, hash->digest, hashsize) != 0) { /* XXX Log? */ return -1; } 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; }
/* 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 = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_SA]); struct payload *prop = TAILQ_FIRST (&msg->payload[ISAKMP_PAYLOAD_PROPOSAL]); struct payload *xf = TAILQ_FIRST (&msg->payload[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; }