int ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct ikev2_keyexchange kex; u_int8_t *buf; size_t len; u_int8_t *msgbuf = ibuf_data(msg->msg_data); memcpy(&kex, msgbuf + offset, sizeof(kex)); log_debug("%s: dh group %s reserved %d", __func__, print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map), betoh16(kex.kex_reserved)); buf = msgbuf + offset + sizeof(kex); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(kex); print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { ibuf_release(msg->msg_parent->msg_ke); if ((msg->msg_parent->msg_ke = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to get exchange", __func__); return (-1); } } return (0); }
int ikev2_pld_id(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left, u_int payload) { u_int8_t *ptr; struct ikev2_id id; size_t len; struct iked_id *idp, idb; struct iked_sa *sa = msg->msg_sa; u_int8_t *msgbuf = ibuf_data(msg->msg_data); char idstr[IKED_ID_SIZE]; if (ikev2_validate_id(msg, offset, left, pld, &id)) return (-1); bzero(&idb, sizeof(idb)); /* Don't strip the Id payload header */ ptr = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld); idb.id_type = id.id_type; idb.id_offset = sizeof(id); if ((idb.id_buf = ibuf_new(ptr, len)) == NULL) return (-1); if (ikev2_print_id(&idb, idstr, sizeof(idstr)) == -1) { log_debug("%s: malformed id", __func__); return (-1); } log_debug("%s: id %s length %zu", __func__, idstr, len); if (!ikev2_msg_frompeer(msg)) { ibuf_release(idb.id_buf); return (0); } if (!((sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDr) || (!sa->sa_hdr.sh_initiator && payload == IKEV2_PAYLOAD_IDi))) { log_debug("%s: unexpected id payload", __func__); return (0); } idp = &msg->msg_parent->msg_id; if (idp->id_type) { log_debug("%s: duplicate id payload", __func__); return (-1); } idp->id_buf = idb.id_buf; idp->id_offset = idb.id_offset; idp->id_type = idb.id_type; return (0); }
int ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct iked_sa *sa = msg->msg_sa; struct ikev2_cert cert; u_int8_t *buf; ssize_t len; u_int8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_certreq(msg, offset, left, pld, &cert)) return (-1); offset += sizeof(cert); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert); log_debug("%s: type %s length %zd", __func__, print_map(cert.cert_type, ikev2_cert_map), len); /* This will actually be caught by earlier checks. */ if (len < 0) { log_debug("%s: invalid certificate request length", __func__); return (-1); } print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); if (cert.cert_type == IKEV2_CERT_X509_CERT) { if (!len || (len % SHA_DIGEST_LENGTH) != 0) { log_debug("%s: invalid certificate request", __func__); return (-1); } } if (msg->msg_sa == NULL) return (-1); /* Optional certreq for PSK */ if (sa->sa_hdr.sh_initiator) sa->sa_stateinit |= IKED_REQ_CERT; else sa->sa_statevalid |= IKED_REQ_CERT; ca_setreq(env, &sa->sa_hdr, &sa->sa_policy->pol_localid, cert.cert_type, buf, len, PROC_CERT); return (0); }
int ikev2_pld_e(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct iked_sa *sa = msg->msg_sa; struct ibuf *e = NULL; u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_message emsg; u_int8_t *buf; size_t len; int ret = -1; buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld); if ((e = ibuf_new(buf, len)) == NULL) goto done; if (ikev2_msg_frompeer(msg)) { e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e); } else { sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1; e = ikev2_msg_decrypt(env, msg->msg_sa, msg->msg_data, e); sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1; } if (e == NULL) goto done; /* * Parse decrypted payload */ bzero(&emsg, sizeof(emsg)); memcpy(&emsg, msg, sizeof(*msg)); emsg.msg_data = e; emsg.msg_e = 1; emsg.msg_parent = msg; TAILQ_INIT(&emsg.msg_proposals); ret = ikev2_pld_payloads(env, &emsg, 0, ibuf_size(e), pld->pld_nextpayload, 0); done: ibuf_release(e); return (ret); }
int ikev2_pld_auth(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_auth auth; struct iked_id *idp; u_int8_t *buf; size_t len; struct iked_sa *sa = msg->msg_sa; u_int8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_auth(msg, offset, left, pld, &auth)) return (-1); offset += sizeof(auth); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(auth); log_debug("%s: method %s length %zu", __func__, print_map(auth.auth_method, ikev2_auth_map), len); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); /* The AUTH payload indicates if the responder wants EAP or not */ if (!sa_stateok(sa, IKEV2_STATE_EAP)) sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST); idp = &msg->msg_parent->msg_auth; if (idp->id_type) { log_debug("%s: duplicate auth payload", __func__); return (-1); } ibuf_release(idp->id_buf); idp->id_type = auth.auth_method; idp->id_offset = 0; if ((idp->id_buf = ibuf_new(buf, len)) == NULL) return (-1); return (0); }
int ikev2_pld_certreq(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct iked_sa *sa = msg->msg_sa; struct ikev2_cert cert; u_int8_t *buf; size_t len; u_int8_t *msgbuf = ibuf_data(msg->msg_data); memcpy(&cert, msgbuf + offset, sizeof(cert)); offset += sizeof(cert); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert); log_debug("%s: type %s signatures length %d", __func__, print_map(cert.cert_type, ikev2_cert_map), len); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); if (!len || (len % SHA_DIGEST_LENGTH) != 0) { log_debug("%s: invalid certificate request", __func__); return (-1); } if (msg->msg_sa == NULL) return (-1); /* Optional certreq for PSK */ if (sa->sa_hdr.sh_initiator) sa->sa_stateinit |= IKED_REQ_CERT; else sa->sa_statevalid |= IKED_REQ_CERT; ca_setreq(env, &sa->sa_hdr, &sa->sa_policy->pol_localid, cert.cert_type, buf, len, PROC_CERT); return (0); }
int ikev2_pld_ke(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_keyexchange kex; u_int8_t *buf; size_t len; u_int8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_ke(msg, offset, left, pld, &kex)) return (-1); log_debug("%s: dh group %s reserved %d", __func__, print_map(betoh16(kex.kex_dhgroup), ikev2_xformdh_map), betoh16(kex.kex_reserved)); buf = msgbuf + offset + sizeof(kex); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(kex); if (len == 0) { log_debug("%s: malformed payload: no KE data given", __func__); return (-1); } /* This will actually be caught by earlier checks. */ if (left < len) { log_debug("%s: malformed payload: smaller than specified " "(%zu < %zu)", __func__, left, len); return (-1); } print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { ibuf_release(msg->msg_parent->msg_ke); if ((msg->msg_parent->msg_ke = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to get exchange", __func__); return (-1); } } return (0); }
int ikev2_pld_cp(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cp cp; struct ikev2_cfg *cfg; u_int8_t *buf; size_t len, i; u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_sa *sa = msg->msg_sa; if (ikev2_validate_cp(msg, offset, left, pld, &cp)) return (-1); offset += sizeof(cp); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cp); log_debug("%s: type %s length %zu", __func__, print_map(cp.cp_type, ikev2_cp_map), len); print_hex(buf, 0, len); for (i = 0; i < len;) { cfg = (struct ikev2_cfg *)(buf + i); log_debug("%s: %s 0x%04x length %d", __func__, print_map(betoh16(cfg->cfg_type), ikev2_cfg_map), betoh16(cfg->cfg_type), betoh16(cfg->cfg_length)); i += betoh16(cfg->cfg_length) + sizeof(*cfg); } if (!ikev2_msg_frompeer(msg)) return (0); if (sa) sa->sa_cp = cp.cp_type; return (0); }
int ikev2_pld_cert(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_cert cert; u_int8_t *buf; size_t len; struct iked_id *certid; u_int8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_cert(msg, offset, left, pld, &cert)) return (-1); offset += sizeof(cert); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(cert); log_debug("%s: type %s length %zu", __func__, print_map(cert.cert_type, ikev2_cert_map), len); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); certid = &msg->msg_parent->msg_cert; if (certid->id_type) { log_debug("%s: duplicate cert payload", __func__); return (-1); } if ((certid->id_buf = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to save cert", __func__); return (-1); } certid->id_type = cert.cert_type; certid->id_offset = 0; return (0); }
int ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { size_t len; u_int8_t *buf; u_int8_t *msgbuf = ibuf_data(msg->msg_data); if (ikev2_validate_nonce(msg, offset, left, pld)) return (-1); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld); if (len == 0) { log_debug("%s: malformed payload: no NONCE given", __func__); return (-1); } /* This will actually be caught by earlier checks. */ if (left < len) { log_debug("%s: malformed payload: smaller than specified " "(%zu < %zu)", __func__, left, len); return (-1); } print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { ibuf_release(msg->msg_nonce); if ((msg->msg_nonce = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to get peer nonce", __func__); return (-1); } msg->msg_parent->msg_nonce = msg->msg_nonce; } return (0); }
int ikev2_pld_nonce(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { size_t len; u_int8_t *buf; u_int8_t *msgbuf = ibuf_data(msg->msg_data); buf = msgbuf + offset; len = betoh16(pld->pld_length) - sizeof(*pld); print_hex(buf, 0, len); if (ikev2_msg_frompeer(msg)) { ibuf_release(msg->msg_nonce); if ((msg->msg_nonce = ibuf_new(buf, len)) == NULL) { log_debug("%s: failed to get peer nonce", __func__); return (-1); } msg->msg_parent->msg_nonce = msg->msg_nonce; } return (0); }
int ikev2_pld_delete(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct iked_childsa **peersas = NULL; struct iked_sa *sa = msg->msg_sa; struct ikev2_delete *del, *localdel; struct ibuf *resp = NULL; u_int64_t *localspi = NULL; u_int64_t spi64, spi = 0; u_int32_t spi32; u_int8_t *buf, *msgbuf = ibuf_data(msg->msg_data); size_t found = 0, failed = 0; int cnt, i, len, sz, ret = -1; /* Skip if it's a reply and we don't have to deal with it */ if (ikev2_msg_frompeer(msg) && sa && (sa->sa_stateflags & IKED_REQ_INF)) { sa->sa_stateflags &= ~IKED_REQ_INF; if ((sa->sa_stateflags & IKED_REQ_DELETE) == 0) return (0); } if ((del = ibuf_seek(msg->msg_data, offset, sizeof(*del))) == NULL) return (-1); cnt = betoh16(del->del_nspi); sz = del->del_spisize; log_debug("%s: proto %s spisize %d nspi %d", __func__, print_map(del->del_protoid, ikev2_saproto_map), sz, cnt); buf = msgbuf + offset + sizeof(*del); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*del); print_hex(buf, 0, len); switch (sz) { case 4: case 8: break; default: if (ikev2_msg_frompeer(msg) && del->del_protoid == IKEV2_SAPROTO_IKE) { /* Send an empty informational response */ if ((resp = ibuf_static()) == NULL) goto done; ret = ikev2_send_ike_e(env, sa, resp, IKEV2_PAYLOAD_NONE, IKEV2_EXCHANGE_INFORMATIONAL, 1); ibuf_release(resp); sa_state(env, sa, IKEV2_STATE_CLOSED); return (ret); } log_debug("%s: invalid SPI size", __func__); return (-1); } if ((len / sz) != cnt) { log_debug("%s: invalid payload length %d/%d != %d", __func__, len, sz, cnt); return (-1); } if (ikev2_msg_frompeer(msg) && ((peersas = calloc(cnt, sizeof(struct iked_childsa *))) == NULL || (localspi = calloc(cnt, sizeof(u_int64_t))) == NULL)) { log_warn("%s", __func__); goto done; } for (i = 0; i < cnt; i++) { switch (sz) { case 4: memcpy(&spi32, buf + (i * sz), sizeof(spi32)); spi = betoh32(spi32); break; case 8: memcpy(&spi64, buf + (i * sz), sizeof(spi64)); spi = betoh64(spi64); break; } log_debug("%s: spi %s", __func__, print_spi(spi, sz)); if (peersas == NULL || sa == NULL) continue; if ((peersas[i] = childsa_lookup(sa, spi, del->del_protoid)) == NULL) { log_warnx("%s: CHILD SA doesn't exist for spi %s", __func__, print_spi(spi, del->del_spisize)); goto done; } if (ikev2_childsa_delete(env, sa, del->del_protoid, spi, &localspi[i], 0) == -1) failed++; else found++; /* * Flows are left in the require mode so that it would be * possible to quickly negotiate a new Child SA */ } /* Parsed outgoing message? */ if (!ikev2_msg_frompeer(msg)) goto done; if (sa && (sa->sa_stateflags & IKED_REQ_DELETE)) { /* Finish rekeying */ sa->sa_stateflags &= ~IKED_REQ_DELETE; ret = 0; goto done; } /* Response to the INFORMATIONAL with Delete payload */ if ((resp = ibuf_static()) == NULL) goto done; if (found) { if ((localdel = ibuf_advance(resp, sizeof(*localdel))) == NULL) goto done; localdel->del_protoid = del->del_protoid; localdel->del_spisize = del->del_spisize; localdel->del_nspi = htobe16(found); for (i = 0; i < cnt; i++) { switch (sz) { case 4: spi32 = htobe32(localspi[i]); if (ibuf_add(resp, &spi32, sizeof(spi32)) != 0) goto done; break; case 8: spi64 = htobe64(localspi[i]); if (ibuf_add(resp, &spi64, sizeof(spi64)) != 0) goto done; break; } } log_warnx("%s: deleted %d spis", __func__, found); } if (found) { ret = ikev2_send_ike_e(env, sa, resp, IKEV2_PAYLOAD_DELETE, IKEV2_EXCHANGE_INFORMATIONAL, 1); } else { /* XXX should we send an INVALID_SPI notification? */ ret = 0; } done: if (localspi) free(localspi); if (peersas) free(peersas); ibuf_release(resp); return (ret); }
int ikev2_pld_notify(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct ikev2_notify *n; u_int8_t *buf, md[SHA_DIGEST_LENGTH]; size_t len; u_int32_t spi32; u_int64_t spi64; struct iked_spi *rekey; u_int16_t type; u_int16_t group; if ((n = ibuf_seek(msg->msg_data, offset, sizeof(*n))) == NULL) return (-1); type = betoh16(n->n_type); log_debug("%s: protoid %s spisize %d type %s", __func__, print_map(n->n_protoid, ikev2_saproto_map), n->n_spisize, print_map(type, ikev2_n_map)); len = betoh16(pld->pld_length) - sizeof(*pld) - sizeof(*n); if ((buf = ibuf_seek(msg->msg_data, offset + sizeof(*n), len)) == NULL) return (-1); print_hex(buf, 0, len); if (!ikev2_msg_frompeer(msg)) return (0); switch (type) { case IKEV2_N_NAT_DETECTION_SOURCE_IP: case IKEV2_N_NAT_DETECTION_DESTINATION_IP: if (ikev2_nat_detection(env, msg, md, sizeof(md), type) == -1) return (-1); if (len != sizeof(md) || memcmp(buf, md, len) != 0) { log_debug("%s: %s detected NAT, enabling " "UDP encapsulation", __func__, print_map(type, ikev2_n_map)); /* * Enable UDP encapsulation of ESP packages if * the check detected NAT. */ if (msg->msg_sa != NULL) msg->msg_sa->sa_udpencap = 1; } print_hex(md, 0, sizeof(md)); break; case IKEV2_N_INVALID_KE_PAYLOAD: if (len != sizeof(group)) { log_debug("%s: malformed notification", __func__); return (-1); } if (!msg->msg_sa->sa_hdr.sh_initiator) { log_debug("%s: not an initiator", __func__); sa_free(env, msg->msg_sa); msg->msg_sa = NULL; return (-1); } memcpy(&group, buf, len); group = betoh16(group); if ((msg->msg_policy->pol_peerdh = group_get(group)) == NULL) { log_debug("%s: unable to select DH group %d", __func__, group); return (-1); } log_debug("%s: responder selected DH group %d", __func__, group); sa_free(env, msg->msg_sa); msg->msg_sa = NULL; timer_initialize(env, &env->sc_inittmr, ikev2_init_ike_sa, NULL); timer_register(env, &env->sc_inittmr, IKED_INITIATOR_INITIAL); break; case IKEV2_N_NO_ADDITIONAL_SAS: /* This makes sense for Child SAs only atm */ if (msg->msg_sa->sa_stateflags & IKED_REQ_CHILDSA) { ikev2_disable_rekeying(env, msg->msg_sa); msg->msg_sa->sa_stateflags &= ~IKED_REQ_CHILDSA; } break; case IKEV2_N_REKEY_SA: if (len != n->n_spisize) { log_debug("%s: malformed notification", __func__); return (-1); } rekey = &msg->msg_parent->msg_rekey; if (rekey->spi != 0) { log_debug("%s: rekeying of multiple SAs not supported", __func__); return (-1); } switch (n->n_spisize) { case 4: memcpy(&spi32, buf, len); rekey->spi = betoh32(spi32); break; case 8: memcpy(&spi64, buf, len); rekey->spi = betoh64(spi64); break; default: log_debug("%s: invalid spi size %d", __func__, n->n_spisize); return (-1); } rekey->spi_size = n->n_spisize; rekey->spi_protoid = n->n_protoid; log_debug("%s: rekey %s spi %s", __func__, print_map(n->n_protoid, ikev2_saproto_map), print_spi(rekey->spi, n->n_spisize)); break; } return (0); }
int ikev2_pld_payloads(struct iked *env, struct iked_message *msg, size_t offset, size_t length, u_int payload) { struct ikev2_payload pld; u_int e; int ret; u_int8_t *msgbuf = ibuf_data(msg->msg_data); size_t left; /* Check if message was decrypted in an E payload */ e = msg->msg_e ? IKED_E : 0; while (payload != 0 && offset < length) { /* Bytes left in datagram. */ left = length - offset; if (ikev2_validate_pld(msg, offset, left, &pld)) return (-1); log_debug("%s: %spayload %s" " nextpayload %s critical 0x%02x length %d", __func__, e ? "decrypted " : "", print_map(payload, ikev2_payload_map), print_map(pld.pld_nextpayload, ikev2_payload_map), pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, betoh16(pld.pld_length)); /* Skip over generic payload header. */ offset += sizeof(pld); left -= sizeof(pld); ret = 0; switch (payload | e) { case IKEV2_PAYLOAD_SA: case IKEV2_PAYLOAD_SA | IKED_E: ret = ikev2_pld_sa(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_KE: case IKEV2_PAYLOAD_KE | IKED_E: ret = ikev2_pld_ke(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_IDi | IKED_E: case IKEV2_PAYLOAD_IDr | IKED_E: ret = ikev2_pld_id(env, &pld, msg, offset, left, payload); break; case IKEV2_PAYLOAD_CERT | IKED_E: ret = ikev2_pld_cert(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_CERTREQ: case IKEV2_PAYLOAD_CERTREQ | IKED_E: ret = ikev2_pld_certreq(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_AUTH | IKED_E: ret = ikev2_pld_auth(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NONCE: case IKEV2_PAYLOAD_NONCE | IKED_E: ret = ikev2_pld_nonce(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_NOTIFY: case IKEV2_PAYLOAD_NOTIFY | IKED_E: ret = ikev2_pld_notify(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_DELETE | IKED_E: ret = ikev2_pld_delete(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_TSi | IKED_E: case IKEV2_PAYLOAD_TSr | IKED_E: ret = ikev2_pld_ts(env, &pld, msg, offset, left, payload); break; case IKEV2_PAYLOAD_SK: ret = ikev2_pld_e(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_CP | IKED_E: ret = ikev2_pld_cp(env, &pld, msg, offset, left); break; case IKEV2_PAYLOAD_EAP | IKED_E: ret = ikev2_pld_eap(env, &pld, msg, offset, left); break; default: print_hex(msgbuf, offset, betoh16(pld.pld_length) - sizeof(pld)); break; } if (ret != 0 && ikev2_msg_frompeer(msg)) { (void)ikev2_send_informational(env, msg); return (-1); } /* Encrypted payload must appear last */ if (payload == IKEV2_PAYLOAD_SK) return (0); payload = pld.pld_nextpayload; offset += betoh16(pld.pld_length) - sizeof(pld); } return (0); }
int ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, struct iked_message *msg, off_t offset) { struct ikev2_transform xfrm; char id[BUFSIZ]; u_int8_t *msgbuf = ibuf_data(msg->msg_data); memcpy(&xfrm, msgbuf + offset, sizeof(xfrm)); switch (xfrm.xfrm_type) { case IKEV2_XFORMTYPE_ENCR: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformencr_map), sizeof(id)); break; case IKEV2_XFORMTYPE_PRF: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformprf_map), sizeof(id)); break; case IKEV2_XFORMTYPE_INTEGR: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformauth_map), sizeof(id)); break; case IKEV2_XFORMTYPE_DH: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformdh_map), sizeof(id)); break; case IKEV2_XFORMTYPE_ESN: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformesn_map), sizeof(id)); break; default: snprintf(id, sizeof(id), "<%d>", betoh16(xfrm.xfrm_id)); break; } log_debug("%s: more %d reserved %d length %d" " type %s id %s", __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, betoh16(xfrm.xfrm_length), print_map(xfrm.xfrm_type, ikev2_xformtype_map), id); /* * Parse transform attributes, if available */ msg->msg_attrlength = 0; if ((u_int)betoh16(xfrm.xfrm_length) > sizeof(xfrm)) ikev2_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), betoh16(xfrm.xfrm_length) - sizeof(xfrm)); if (ikev2_msg_frompeer(msg)) { if (config_add_transform(msg->msg_parent->msg_prop, xfrm.xfrm_type, betoh16(xfrm.xfrm_id), msg->msg_attrlength, msg->msg_attrlength) == NULL) { log_debug("%s: failed to add transform", __func__); return (-1); } } /* Next transform */ offset += betoh16(xfrm.xfrm_length); if (xfrm.xfrm_more == IKEV2_XFORM_MORE) ikev2_pld_xform(env, sap, msg, offset); return (0); }
int ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, off_t offset) { struct ikev2_sa_proposal sap; struct iked_proposal *prop = NULL; u_int32_t spi32; u_int64_t spi = 0, spi64; u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_proposals *props; props = &msg->msg_parent->msg_proposals; memcpy(&sap, msgbuf + offset, sizeof(sap)); offset += sizeof(sap); if (sap.sap_spisize) { switch (sap.sap_spisize) { case 4: memcpy(&spi32, msgbuf + offset, 4); spi = betoh32(spi32); break; case 8: memcpy(&spi64, msgbuf + offset, 8); spi = betoh64(spi64); break; default: log_debug("%s: unsupported SPI size %d", __func__, sap.sap_spisize); return (-1); } offset += sap.sap_spisize; } log_debug("%s: more %d reserved %d length %d" " proposal #%d protoid %s spisize %d xforms %d spi %s", __func__, sap.sap_more, sap.sap_reserved, betoh16(sap.sap_length), sap.sap_proposalnr, print_map(sap.sap_protoid, ikev2_saproto_map), sap.sap_spisize, sap.sap_transforms, print_spi(spi, sap.sap_spisize)); if (ikev2_msg_frompeer(msg)) { if ((msg->msg_parent->msg_prop = config_add_proposal(props, sap.sap_proposalnr, sap.sap_protoid)) == NULL) { log_debug("%s: invalid proposal", __func__); return (-1); } prop = msg->msg_parent->msg_prop; prop->prop_peerspi.spi = spi; prop->prop_peerspi.spi_protoid = sap.sap_protoid; prop->prop_peerspi.spi_size = sap.sap_spisize; prop->prop_localspi.spi_protoid = sap.sap_protoid; prop->prop_localspi.spi_size = sap.sap_spisize; } /* * Parse the attached transforms */ if (sap.sap_transforms && ikev2_pld_xform(env, &sap, msg, offset) != 0) { log_debug("%s: invalid proposal transforms", __func__); return (-1); } return (0); }
int ikev2_pld_payloads(struct iked *env, struct iked_message *msg, off_t offset, size_t length, u_int payload, int quick) { struct ikev2_payload pld; u_int e; int ret; u_int8_t *msgbuf = ibuf_data(msg->msg_data); /* Check if message was decrypted in an E payload */ e = msg->msg_e ? IKED_E : 0; if (quick) print_debug("%s: %spayloads", __func__, e ? "decrypted " : ""); else ikev2_pld_payloads(env, msg, offset, length, payload, 1); while (payload != 0 && offset < (off_t)length) { memcpy(&pld, msgbuf + offset, sizeof(pld)); if (quick) print_debug(" %s", print_map(payload, ikev2_payload_map)); else log_debug("%s: %spayload %s" " nextpayload %s critical 0x%02x length %d", __func__, e ? "decrypted " : "", print_map(payload, ikev2_payload_map), print_map(pld.pld_nextpayload, ikev2_payload_map), pld.pld_reserved & IKEV2_CRITICAL_PAYLOAD, betoh16(pld.pld_length)); offset += sizeof(pld); ret = 0; if (quick) goto next; switch (payload | e) { case IKEV2_PAYLOAD_SA: case IKEV2_PAYLOAD_SA | IKED_E: ret = ikev2_pld_sa(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_KE: case IKEV2_PAYLOAD_KE | IKED_E: ret = ikev2_pld_ke(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_IDi | IKED_E: case IKEV2_PAYLOAD_IDr | IKED_E: ret = ikev2_pld_id(env, &pld, msg, offset, payload); break; case IKEV2_PAYLOAD_CERT | IKED_E: ret = ikev2_pld_cert(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_CERTREQ: case IKEV2_PAYLOAD_CERTREQ | IKED_E: ret = ikev2_pld_certreq(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_AUTH | IKED_E: ret = ikev2_pld_auth(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_NONCE: case IKEV2_PAYLOAD_NONCE | IKED_E: ret = ikev2_pld_nonce(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_NOTIFY: case IKEV2_PAYLOAD_NOTIFY | IKED_E: ret = ikev2_pld_notify(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_DELETE | IKED_E: ret = ikev2_pld_delete(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_TSi | IKED_E: case IKEV2_PAYLOAD_TSr | IKED_E: ret = ikev2_pld_ts(env, &pld, msg, offset, payload); break; case IKEV2_PAYLOAD_SK: ret = ikev2_pld_e(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_CP | IKED_E: ret = ikev2_pld_cp(env, &pld, msg, offset); break; case IKEV2_PAYLOAD_EAP | IKED_E: ret = ikev2_pld_eap(env, &pld, msg, offset); break; default: print_hex(msgbuf, offset, betoh16(pld.pld_length) - sizeof(pld)); break; } if (ret != 0 && ikev2_msg_frompeer(msg)) { (void)ikev2_send_informational(env, msg); return (-1); } /* Encrypted payload must appear last */ if (payload == IKEV2_PAYLOAD_SK) return (0); next: payload = pld.pld_nextpayload; offset += betoh16(pld.pld_length) - sizeof(pld); } if (quick) print_debug("\n"); return (0); }
/* * NB: This function parses both the SA header and the first proposal. * Additional proposals are ignored. */ int ikev2_pld_sa(struct iked *env, struct ikev2_payload *pld, struct iked_message *msg, size_t offset, size_t left) { struct ikev2_sa_proposal sap; struct iked_proposal *prop = NULL; u_int32_t spi32; u_int64_t spi = 0, spi64; u_int8_t *msgbuf = ibuf_data(msg->msg_data); struct iked_proposals *props; size_t total; if (ikev2_validate_sa(msg, offset, left, pld, &sap)) return (-1); if (sap.sap_more) log_debug("%s: more than one proposal specified", __func__); /* Assumed size of the first proposals, including SPI if present. */ total = (betoh16(sap.sap_length) - sizeof(sap)); props = &msg->msg_parent->msg_proposals; offset += sizeof(sap); left -= sizeof(sap); if (sap.sap_spisize) { if (left < sap.sap_spisize) { log_debug("%s: malformed payload: SPI larger than " "actual payload (%zu < %d)", __func__, left, sap.sap_spisize); return (-1); } if (total < sap.sap_spisize) { log_debug("%s: malformed payload: SPI larger than " "proposal (%zu < %d)", __func__, total, sap.sap_spisize); return (-1); } if (total < sap.sap_spisize) { log_debug("%s: malformed payload: SPI too large " "(%zu < %d)", __func__, total, sap.sap_spisize); return (-1); } switch (sap.sap_spisize) { case 4: memcpy(&spi32, msgbuf + offset, 4); spi = betoh32(spi32); break; case 8: memcpy(&spi64, msgbuf + offset, 8); spi = betoh64(spi64); break; default: log_debug("%s: unsupported SPI size %d", __func__, sap.sap_spisize); return (-1); } offset += sap.sap_spisize; left -= sap.sap_spisize; /* Assumed size of the proposal, now without SPI. */ total -= sap.sap_spisize; } /* * As we verified sanity of packet headers, this check will * be always false, but just to be sure we keep it. */ if (left < total) { log_debug("%s: payload malformed: too long for payload " "(%zu < %zu)", __func__, left, total); return (-1); } log_debug("%s: more %d reserved %d length %d" " proposal #%d protoid %s spisize %d xforms %d spi %s", __func__, sap.sap_more, sap.sap_reserved, betoh16(sap.sap_length), sap.sap_proposalnr, print_map(sap.sap_protoid, ikev2_saproto_map), sap.sap_spisize, sap.sap_transforms, print_spi(spi, sap.sap_spisize)); if (ikev2_msg_frompeer(msg)) { if ((msg->msg_parent->msg_prop = config_add_proposal(props, sap.sap_proposalnr, sap.sap_protoid)) == NULL) { log_debug("%s: invalid proposal", __func__); return (-1); } prop = msg->msg_parent->msg_prop; prop->prop_peerspi.spi = spi; prop->prop_peerspi.spi_protoid = sap.sap_protoid; prop->prop_peerspi.spi_size = sap.sap_spisize; prop->prop_localspi.spi_protoid = sap.sap_protoid; prop->prop_localspi.spi_size = sap.sap_spisize; } /* * Parse the attached transforms */ if (sap.sap_transforms && ikev2_pld_xform(env, &sap, msg, offset, total) != 0) { log_debug("%s: invalid proposal transforms", __func__); return (-1); } return (0); }
int ikev2_pld_xform(struct iked *env, struct ikev2_sa_proposal *sap, struct iked_message *msg, size_t offset, size_t total) { struct ikev2_transform xfrm; char id[BUFSIZ]; int ret = 0; size_t xfrm_length; if (ikev2_validate_xform(msg, offset, total, &xfrm)) return (-1); xfrm_length = betoh16(xfrm.xfrm_length); switch (xfrm.xfrm_type) { case IKEV2_XFORMTYPE_ENCR: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformencr_map), sizeof(id)); break; case IKEV2_XFORMTYPE_PRF: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformprf_map), sizeof(id)); break; case IKEV2_XFORMTYPE_INTEGR: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformauth_map), sizeof(id)); break; case IKEV2_XFORMTYPE_DH: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformdh_map), sizeof(id)); break; case IKEV2_XFORMTYPE_ESN: strlcpy(id, print_map(betoh16(xfrm.xfrm_id), ikev2_xformesn_map), sizeof(id)); break; default: snprintf(id, sizeof(id), "<%d>", betoh16(xfrm.xfrm_id)); break; } log_debug("%s: more %d reserved %d length %zu" " type %s id %s", __func__, xfrm.xfrm_more, xfrm.xfrm_reserved, xfrm_length, print_map(xfrm.xfrm_type, ikev2_xformtype_map), id); /* * Parse transform attributes, if available */ msg->msg_attrlength = 0; if (xfrm_length > sizeof(xfrm)) { if (ikev2_pld_attr(env, &xfrm, msg, offset + sizeof(xfrm), xfrm_length - sizeof(xfrm)) != 0) { return (-1); } } if (ikev2_msg_frompeer(msg)) { if (config_add_transform(msg->msg_parent->msg_prop, xfrm.xfrm_type, betoh16(xfrm.xfrm_id), msg->msg_attrlength, msg->msg_attrlength) == NULL) { log_debug("%s: failed to add transform", __func__); return (-1); } } /* Next transform */ offset += xfrm_length; total -= xfrm_length; if (xfrm.xfrm_more == IKEV2_XFORM_MORE) ret = ikev2_pld_xform(env, sap, msg, offset, total); else if (total != 0) { /* No more transforms but still some data left. */ log_debug("%s: less data than specified, %zu bytes left", __func__, total); ret = -1; } return (ret); }