int eap_challenge_request(struct iked *env, struct iked_sa *sa, struct eap_header *hdr) { struct eap_message *eap; struct eap_mschap_challenge *ms; const char *name; int ret = -1; struct ibuf *e; if ((e = ibuf_static()) == NULL) return (-1); if ((eap = ibuf_advance(e, sizeof(*eap))) == NULL) goto done; eap->eap_code = EAP_CODE_REQUEST; eap->eap_id = hdr->eap_id + 1; eap->eap_type = sa->sa_policy->pol_auth.auth_eap; switch (sa->sa_policy->pol_auth.auth_eap) { case EAP_TYPE_MSCHAP_V2: name = IKED_USER; /* XXX should be user-configurable */ eap->eap_length = htobe16(sizeof(*eap) + sizeof(*ms) + strlen(name)); if ((ms = ibuf_advance(e, sizeof(*ms))) == NULL) return (-1); ms->msc_opcode = EAP_MSOPCODE_CHALLENGE; ms->msc_id = eap->eap_id; ms->msc_length = htobe16(sizeof(*ms) + strlen(name)); ms->msc_valuesize = sizeof(ms->msc_challenge); arc4random_buf(ms->msc_challenge, sizeof(ms->msc_challenge)); if (ibuf_add(e, name, strlen(name)) == -1) goto done; /* Store the EAP challenge value */ sa->sa_eap.id_type = eap->eap_type; if ((sa->sa_eap.id_buf = ibuf_new(ms->msc_challenge, sizeof(ms->msc_challenge))) == NULL) goto done; break; default: log_debug("%s: unsupported EAP type %s", __func__, print_map(eap->eap_type, eap_type_map)); goto done; } ret = ikev2_send_ike_e(env, sa, e, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); done: ibuf_release(e); return (ret); }
struct ibuf * ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) { struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf; uint8_t *ptr; struct iked_id *id; size_t tmplen; /* * Create the payload to be signed/MAC'ed for AUTH */ if (!response) { if ((nonce = sa->sa_rnonce) == NULL || (sa->sa_iid.id_type == 0) || (prfkey = sa->sa_key_iprf) == NULL || (buf = sa->sa_1stmsg) == NULL) return (NULL); id = &sa->sa_iid; } else { if ((nonce = sa->sa_inonce) == NULL || (sa->sa_rid.id_type == 0) || (prfkey = sa->sa_key_rprf) == NULL || (buf = sa->sa_2ndmsg) == NULL) return (NULL); id = &sa->sa_rid; } if ((authmsg = ibuf_dup(buf)) == NULL) return (NULL); if (ibuf_cat(authmsg, nonce) != 0) goto fail; if ((hash_setkey(sa->sa_prf, ibuf_data(prfkey), ibuf_size(prfkey))) == NULL) goto fail; if ((ptr = ibuf_advance(authmsg, hash_length(sa->sa_prf))) == NULL) goto fail; hash_init(sa->sa_prf); hash_update(sa->sa_prf, ibuf_data(id->id_buf), ibuf_size(id->id_buf)); hash_final(sa->sa_prf, ptr, &tmplen); if (tmplen != hash_length(sa->sa_prf)) goto fail; log_debug("%s: %s auth data length %zu", __func__, response ? "responder" : "initiator", ibuf_size(authmsg)); print_hex(ibuf_data(authmsg), 0, ibuf_size(authmsg)); return (authmsg); fail: ibuf_release(authmsg); return (NULL); }
ssize_t eap_identity_request(struct ibuf *e) { struct eap_message *eap; if ((eap = ibuf_advance(e, sizeof(*eap))) == NULL) return (-1); eap->eap_code = EAP_CODE_REQUEST; eap->eap_id = 0; eap->eap_length = htobe16(sizeof(*eap)); eap->eap_type = EAP_TYPE_IDENTITY; return (sizeof(*eap)); }
int eap_success(struct iked *env, struct iked_sa *sa, struct eap_header *hdr) { struct eap_header *resp; int ret = -1; struct ibuf *e; if ((e = ibuf_static()) == NULL) return (-1); if ((resp = ibuf_advance(e, sizeof(*resp))) == NULL) goto done; resp->eap_code = EAP_CODE_SUCCESS; resp->eap_id = hdr->eap_id; resp->eap_length = htobe16(sizeof(*resp)); ret = ikev2_send_ike_e(env, sa, e, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); done: ibuf_release(e); return (ret); }
int eap_mschap(struct iked *env, struct iked_sa *sa, struct eap_message *eap) { struct iked_user *usr; struct eap_message *resp; struct eap_mschap_response *msr; struct eap_mschap_peer *msp; struct eap_mschap *ms; struct eap_mschap_success *mss; u_int8_t *ptr, *pass; size_t len, passlen; char *name, *msg; u_int8_t ntresponse[EAP_MSCHAP_NTRESPONSE_SZ]; u_int8_t successmsg[EAP_MSCHAP_SUCCESS_SZ]; struct ibuf *eapmsg = NULL; int ret = -1; if (!sa_stateok(sa, IKEV2_STATE_EAP)) { log_debug("%s: unexpected EAP", __func__); return (0); /* ignore */ } if (sa->sa_hdr.sh_initiator) { log_debug("%s: initiator EAP not supported", __func__); return (-1); } /* Only MSCHAP-V2 */ if (eap->eap_type != EAP_TYPE_MSCHAP_V2) { log_debug("%s: unsupported type EAP-%s", __func__, print_map(eap->eap_type, eap_type_map)); return (-1); } if (betoh16(eap->eap_length) < (sizeof(*eap) + sizeof(*ms))) { log_debug("%s: short message", __func__); return (-1); } ms = (struct eap_mschap *)(eap + 1); ptr = (u_int8_t *)(eap + 1); switch (ms->ms_opcode) { case EAP_MSOPCODE_RESPONSE: msr = (struct eap_mschap_response *)ms; if (betoh16(eap->eap_length) < (sizeof(*eap) + sizeof(*msr))) { log_debug("%s: short response", __func__); return (-1); } ptr += sizeof(*msr); len = betoh16(eap->eap_length) - sizeof(*eap) - sizeof(*msr); if (len == 0 && sa->sa_eapid != NULL) name = strdup(sa->sa_eapid); else name = get_string(ptr, len); if (name == NULL) { log_debug("%s: invalid response name", __func__); return (-1); } if ((usr = user_lookup(env, name)) == NULL) { log_debug("%s: unknown user '%s'", __func__, name); free(name); return (-1); } free(name); if ((pass = string2unicode(usr->usr_pass, &passlen)) == NULL) return (-1); msp = &msr->msr_response.resp_peer; mschap_nt_response(ibuf_data(sa->sa_eap.id_buf), msp->msp_challenge, usr->usr_name, strlen(usr->usr_name), pass, passlen, ntresponse); if (memcmp(ntresponse, msp->msp_ntresponse, sizeof(ntresponse)) != 0) { log_debug("%s: '%s' authentication failed", __func__, usr->usr_name); free(pass); /* XXX should we send an EAP failure packet? */ return (-1); } bzero(&successmsg, sizeof(successmsg)); mschap_auth_response(pass, passlen, ntresponse, ibuf_data(sa->sa_eap.id_buf), msp->msp_challenge, usr->usr_name, strlen(usr->usr_name), successmsg); if ((sa->sa_eapmsk = ibuf_new(NULL, MSCHAP_MSK_SZ)) == NULL) { log_debug("%s: failed to get MSK", __func__); free(pass); return (-1); } mschap_msk(pass, passlen, ntresponse, ibuf_data(sa->sa_eapmsk)); free(pass); log_info("%s: '%s' authenticated", __func__, usr->usr_name); if ((eapmsg = ibuf_static()) == NULL) return (-1); msg = " M=Welcome"; if ((resp = ibuf_advance(eapmsg, sizeof(*resp))) == NULL) goto done; resp->eap_code = EAP_CODE_REQUEST; resp->eap_id = eap->eap_id + 1; resp->eap_length = htobe16(sizeof(*resp) + sizeof(*mss) + sizeof(successmsg) + strlen(msg)); resp->eap_type = EAP_TYPE_MSCHAP_V2; if ((mss = ibuf_advance(eapmsg, sizeof(*mss))) == NULL) goto done; mss->mss_opcode = EAP_MSOPCODE_SUCCESS; mss->mss_id = msr->msr_id; mss->mss_length = htobe16(sizeof(*mss) + sizeof(successmsg) + strlen(msg)); if (ibuf_add(eapmsg, successmsg, sizeof(successmsg)) != 0) goto done; if (ibuf_add(eapmsg, msg, strlen(msg)) != 0) goto done; break; case EAP_MSOPCODE_SUCCESS: if ((eapmsg = ibuf_static()) == NULL) return (-1); if ((resp = ibuf_advance(eapmsg, sizeof(*resp))) == NULL) goto done; resp->eap_code = EAP_CODE_RESPONSE; resp->eap_id = eap->eap_id; resp->eap_length = htobe16(sizeof(*resp) + sizeof(*ms)); resp->eap_type = EAP_TYPE_MSCHAP_V2; if ((ms = ibuf_advance(eapmsg, sizeof(*ms))) == NULL) goto done; ms->ms_opcode = EAP_MSOPCODE_SUCCESS; break; case EAP_MSOPCODE_FAILURE: case EAP_MSOPCODE_CHANGE_PASSWORD: case EAP_MSOPCODE_CHALLENGE: default: log_debug("%s: EAP-%s unsupported " "responder operation %s", __func__, print_map(eap->eap_type, eap_type_map), print_map(ms->ms_opcode, eap_msopcode_map)); return (-1); } if (eapmsg != NULL) ret = ikev2_send_ike_e(env, sa, eapmsg, IKEV2_PAYLOAD_EAP, IKEV2_EXCHANGE_IKE_AUTH, 1); if (ret == 0) sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS); done: ibuf_release(eapmsg); return (ret); }
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); }
struct ibuf * ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) { size_t len, encrlen, integrlen, blocklen, outlen; uint8_t *buf, pad = 0, *ptr; struct ibuf *encr, *dst = NULL, *out = NULL; buf = ibuf_data(src); len = ibuf_size(src); log_debug("%s: decrypted length %zu", __func__, len); print_hex(buf, 0, len); if (sa == NULL || sa->sa_encr == NULL || sa->sa_integr == NULL) { log_debug("%s: invalid SA", __func__); goto done; } if (sa->sa_hdr.sh_initiator) encr = sa->sa_key_iencr; else encr = sa->sa_key_rencr; blocklen = cipher_length(sa->sa_encr); integrlen = hash_length(sa->sa_integr); encrlen = roundup(len + sizeof(pad), blocklen); pad = encrlen - (len + sizeof(pad)); /* * Pad the payload and encrypt it */ if (pad) { if ((ptr = ibuf_advance(src, pad)) == NULL) goto done; arc4random_buf(ptr, pad); } if (ibuf_add(src, &pad, sizeof(pad)) != 0) goto done; log_debug("%s: padded length %zu", __func__, ibuf_size(src)); print_hex(ibuf_data(src), 0, ibuf_size(src)); cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr)); cipher_setiv(sa->sa_encr, NULL, 0); cipher_init_encrypt(sa->sa_encr); if ((dst = ibuf_dup(sa->sa_encr->encr_iv)) == NULL) goto done; if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr, encrlen))) == NULL) goto done; outlen = ibuf_size(out); cipher_update(sa->sa_encr, ibuf_data(src), encrlen, ibuf_data(out), &outlen); if (outlen && ibuf_add(dst, ibuf_data(out), outlen) != 0) goto done; if ((ptr = ibuf_advance(dst, integrlen)) == NULL) goto done; explicit_bzero(ptr, integrlen); log_debug("%s: length %zu, padding %d, output length %zu", __func__, len + sizeof(pad), pad, ibuf_size(dst)); print_hex(ibuf_data(dst), 0, ibuf_size(dst)); ibuf_release(src); ibuf_release(out); return (dst); done: ibuf_release(src); ibuf_release(out); ibuf_release(dst); return (NULL); }
int ikev2_msg_valid_ike_sa(struct iked *env, struct ike_header *oldhdr, struct iked_message *msg) { #if 0 /* XXX Disabled, see comment below */ struct iked_message resp; struct ike_header *hdr; struct ikev2_payload *pld; struct ikev2_notify *n; struct ibuf *buf; struct iked_sa sa; #endif if (msg->msg_sa != NULL && msg->msg_policy != NULL) { /* * Only permit informational requests from initiator * on closing SAs (for DELETE). */ if (msg->msg_sa->sa_state == IKEV2_STATE_CLOSING) { if (((oldhdr->ike_flags & (IKEV2_FLAG_INITIATOR|IKEV2_FLAG_RESPONSE)) == IKEV2_FLAG_INITIATOR) && (oldhdr->ike_exchange == IKEV2_EXCHANGE_INFORMATIONAL)) return (0); return (-1); } return (0); } #if 0 /* * XXX Sending INVALID_IKE_SPIs notifications is disabled * XXX because it is not mandatory and ignored by most * XXX implementations. We might want to enable it in * XXX combination with a rate-limitation to avoid DoS situations. */ /* Fail without error message */ if (msg->msg_response || msg->msg_policy == NULL) return (-1); /* Invalid IKE SA, return notification */ if ((buf = ikev2_msg_init(env, &resp, &msg->msg_peer, msg->msg_peerlen, &msg->msg_local, msg->msg_locallen, 1)) == NULL) goto done; resp.msg_fd = msg->msg_fd; bzero(&sa, sizeof(sa)); if ((oldhdr->ike_flags & IKEV2_FLAG_INITIATOR) == 0) sa.sa_hdr.sh_initiator = 1; sa.sa_hdr.sh_ispi = betoh64(oldhdr->ike_ispi); sa.sa_hdr.sh_rspi = betoh64(oldhdr->ike_rspi); resp.msg_msgid = betoh32(oldhdr->ike_msgid); /* IKE header */ if ((hdr = ikev2_add_header(buf, &sa, resp.msg_msgid, IKEV2_PAYLOAD_NOTIFY, IKEV2_EXCHANGE_INFORMATIONAL, IKEV2_FLAG_RESPONSE)) == NULL) goto done; /* SA payload */ if ((pld = ikev2_add_payload(buf)) == NULL) goto done; if ((n = ibuf_advance(buf, sizeof(*n))) == NULL) goto done; n->n_protoid = IKEV2_SAPROTO_IKE; n->n_spisize = 0; n->n_type = htobe16(IKEV2_N_INVALID_IKE_SPI); if (ikev2_next_payload(pld, sizeof(*n), IKEV2_PAYLOAD_NONE) == -1) goto done; if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) goto done; (void)ikev2_pld_parse(env, hdr, &resp, 0); (void)ikev2_msg_send(env, &resp); done: ikev2_msg_cleanup(env, &resp); #endif /* Always fail */ return (-1); }