diff options
author | Reyk Floeter <reyk@cvs.openbsd.org> | 2010-06-10 08:29:48 +0000 |
---|---|---|
committer | Reyk Floeter <reyk@cvs.openbsd.org> | 2010-06-10 08:29:48 +0000 |
commit | cbacad9481b16997c98a0e08efdb22421cb06351 (patch) | |
tree | af07721e32e096984ca4ce5635bc41e178bbdc36 /sbin/iked/ikev2_msg.c | |
parent | 126acb62f30dad28dbf7ed763586ca56e1f3c7c8 (diff) |
i don't like splitting source code in too many source files but ikev2.c
has grown too large, so split it in 3 files and rename a few functions
to organize the code a bit better.
Diffstat (limited to 'sbin/iked/ikev2_msg.c')
-rw-r--r-- | sbin/iked/ikev2_msg.c | 788 |
1 files changed, 788 insertions, 0 deletions
diff --git a/sbin/iked/ikev2_msg.c b/sbin/iked/ikev2_msg.c new file mode 100644 index 00000000000..85d82138539 --- /dev/null +++ b/sbin/iked/ikev2_msg.c @@ -0,0 +1,788 @@ +/* $OpenBSD: ikev2_msg.c,v 1.1 2010/06/10 08:29:47 reyk Exp $ */ +/* $vantronix: ikev2.c,v 1.101 2010/06/03 07:57:33 reyk Exp $ */ + +/* + * Copyright (c) 2010 Reyk Floeter <reyk@vantronix.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <sys/uio.h> + +#include <netinet/in.h> +#include <netinet/ip_ipsp.h> +#include <arpa/inet.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <signal.h> +#include <errno.h> +#include <err.h> +#include <pwd.h> +#include <event.h> + +#include <openssl/sha.h> +#include <openssl/evp.h> + +#include "iked.h" +#include "ikev2.h" +#include "eap.h" +#include "dh.h" + +void +ikev2_msg_cb(int fd, short event, void *arg) +{ + struct iked_socket *sock = arg; + struct iked *env = sock->sock_env; + struct iked_message msg; + struct ike_header hdr; + u_int32_t natt = 0x00000000; + u_int8_t buf[IKED_MSGBUF_MAX]; + ssize_t len; + off_t off; + struct iovec iov[2]; + + bzero(&msg, sizeof(msg)); + bzero(buf, sizeof(buf)); + + msg.msg_peerlen = sizeof(msg.msg_peer); + msg.msg_locallen = sizeof(msg.msg_local); + memcpy(&msg.msg_local, &sock->sock_addr, sizeof(sock->sock_addr)); + + if ((len = recvfromto(fd, buf, sizeof(buf), 0, + (struct sockaddr*)&msg.msg_peer, &msg.msg_peerlen, + (struct sockaddr*)&msg.msg_local, &msg.msg_locallen)) < + (ssize_t)sizeof(natt)) + return; + + if (socket_getport(&msg.msg_local) == IKED_NATT_PORT) { + if (bcmp(&natt, buf, sizeof(natt)) != 0) + return; + msg.msg_natt = 1; + off = sizeof(natt); + } else + off = 0; + + if ((size_t)(len - off) <= sizeof(hdr)) + return; + memcpy(&hdr, buf + off, sizeof(hdr)); + + if ((msg.msg_data = ibuf_new(buf + off, len - off)) == NULL) + return; + + if (hdr.ike_version == IKEV1_VERSION) { + iov[0].iov_base = &msg; + iov[0].iov_len = sizeof(msg); + iov[1].iov_base = buf; + iov[1].iov_len = len; + + imsg_composev_proc(env, PROC_IKEV1, IMSG_IKE_MESSAGE, -1, + iov, 2); + goto done; + } + TAILQ_INIT(&msg.msg_proposals); + + msg.msg_fd = fd; + ikev2_recv(env, &msg); + + done: + message_cleanup(env, &msg); +} + +struct ibuf * +ikev2_msg_init(struct iked *env, struct iked_message *msg, + struct sockaddr_storage *peer, socklen_t peerlen, + struct sockaddr_storage *local, socklen_t locallen, int response) +{ + bzero(msg, sizeof(*msg)); + memcpy(&msg->msg_peer, peer, peerlen); + msg->msg_peerlen = peerlen; + memcpy(&msg->msg_local, local, locallen); + msg->msg_locallen = locallen; + msg->msg_response = response ? 1 : 0; + msg->msg_fd = -1; + msg->msg_data = ibuf_static(); + + return (msg->msg_data); +} + +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) + 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; + + 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); + + /* IKE header */ + if ((hdr = ikev2_add_header(buf, &sa, betoh32(oldhdr->ike_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, msg->msg_fd, &resp); + + done: + message_cleanup(env, &resp); +#endif + + /* Always fail */ + return (-1); +} + +int +ikev2_msg_send(struct iked *env, int fd, struct iked_message *msg) +{ + struct ibuf *buf = msg->msg_data; + u_int32_t natt = 0x00000000; + struct ike_header *hdr; + + if (buf == NULL || (hdr = ibuf_seek(msg->msg_data, + msg->msg_offset, sizeof(*hdr))) == NULL) + return (-1); + + log_info("%s: %s to %s from %s", __func__, + print_map(hdr->ike_exchange, ikev2_exchange_map), + print_host(&msg->msg_peer, NULL, 0), + print_host(&msg->msg_local, NULL, 0)); + + if (msg->msg_natt || (msg->msg_sa && msg->msg_sa->sa_natt)) { + if (ibuf_prepend(buf, &natt, sizeof(natt)) == -1) { + log_debug("%s: failed to set NAT-T", __func__); + return (-1); + } + } + if ((sendto(fd, ibuf_data(buf), ibuf_size(buf), 0, + (struct sockaddr *)&msg->msg_peer, msg->msg_peerlen)) == -1) { + log_warn("%s: sendto", __func__); + return (-1); + } + + return (0); +} + +u_int32_t +ikev2_msg_id(struct iked *env, struct iked_sa *sa, int response) +{ + if (response) + return (sa->sa_msgid); + + if (++sa->sa_msgid == UINT32_MAX) { + /* XXX we should close and renegotiate the connection now */ + log_debug("%s: IKEv2 message sequence overflow", __func__); + } + + return (sa->sa_msgid); +} + +struct ibuf * +ikev2_msg_encrypt(struct iked *env, struct iked_sa *sa, struct ibuf *src) +{ + size_t len, ivlen, encrlen, integrlen, blocklen, + outlen; + u_int8_t *buf, pad = 0, *ptr; + struct ibuf *integr, *encr, *dst = NULL, *out = NULL; + + buf = ibuf_data(src); + len = ibuf_size(src); + + log_debug("%s: decrypted length %d", __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; + integr = sa->sa_key_iauth; + } else { + encr = sa->sa_key_rencr; + integr = sa->sa_key_rauth; + } + + blocklen = cipher_length(sa->sa_encr); + ivlen = cipher_ivlength(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 %d", __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); /* new IV */ + 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; + + outlen = cipher_outlength(sa->sa_encr, 0); + cipher_final(sa->sa_encr, out->buf, &outlen); + if (outlen) + ibuf_add(dst, out->buf, outlen); + + if ((ptr = ibuf_advance(dst, integrlen)) == NULL) + goto done; + bzero(ptr, integrlen); + + log_debug("%s: length %d, padding %d, output length %d", + __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_integr(struct iked *env, struct iked_sa *sa, struct ibuf *src) +{ + int ret = -1; + size_t integrlen, tmplen; + struct ibuf *integr, *prf, *tmp = NULL; + u_int8_t *ptr; + + log_debug("%s: message length %d", __func__, ibuf_size(src)); + print_hex(ibuf_data(src), 0, ibuf_size(src)); + + if (sa == NULL || + sa->sa_integr == NULL) { + log_debug("%s: invalid SA", __func__); + return (-1); + } + + if (sa->sa_hdr.sh_initiator) { + integr = sa->sa_key_iauth; + prf = sa->sa_key_iprf; + } else { + integr = sa->sa_key_rauth; + prf = sa->sa_key_rprf; + } + + integrlen = hash_length(sa->sa_integr); + + log_debug("%s: integrity checksum length %d", __func__, + integrlen); + + /* + * Validate packet checksum + */ + if ((tmp = ibuf_new(NULL, hash_keylength(sa->sa_integr))) == NULL) + goto done; + + hash_setkey(sa->sa_integr, ibuf_data(integr), ibuf_size(integr)); + hash_init(sa->sa_integr); + hash_update(sa->sa_integr, ibuf_data(src), + ibuf_size(src) - integrlen); + hash_final(sa->sa_integr, ibuf_data(tmp), &tmplen); + + if (tmplen != integrlen) { + log_debug("%s: hash failure", __func__); + goto done; + } + + if ((ptr = ibuf_seek(src, + ibuf_size(src) - integrlen, integrlen)) == NULL) + goto done; + memcpy(ptr, ibuf_data(tmp), tmplen); + + print_hex(ibuf_data(tmp), 0, ibuf_size(tmp)); + + ret = 0; + done: + ibuf_release(tmp); + + return (ret); +} + +struct ibuf * +ikev2_msg_decrypt(struct iked *env, struct iked_sa *sa, + struct ibuf *msg, struct ibuf *src) +{ + size_t ivlen, encrlen, integrlen, blocklen, + outlen, tmplen; + u_int8_t pad, *ptr; + struct ibuf *integr, *encr, *tmp = NULL, *out = NULL; + off_t ivoff, encroff, integroff; + + if (sa == NULL || + sa->sa_encr == NULL || + sa->sa_integr == NULL) { + log_debug("%s: invalid SA", __func__); + print_hex(ibuf_data(src), 0, ibuf_size(src)); + goto done; + } + + if (!sa->sa_hdr.sh_initiator) { + encr = sa->sa_key_iencr; + integr = sa->sa_key_iauth; + } else { + encr = sa->sa_key_rencr; + integr = sa->sa_key_rauth; + } + + blocklen = cipher_length(sa->sa_encr); + ivlen = cipher_ivlength(sa->sa_encr); + ivoff = 0; + integrlen = hash_length(sa->sa_integr); + integroff = ibuf_size(src) - integrlen; + encroff = ivlen; + encrlen = ibuf_size(src) - integrlen - ivlen; + + log_debug("%s: IV length %d", __func__, ivlen); + print_hex(ibuf_data(src), 0, ivlen); + log_debug("%s: encrypted payload length %d", __func__, encrlen); + print_hex(ibuf_data(src), encroff, encrlen); + log_debug("%s: integrity checksum length %d", __func__, integrlen); + print_hex(ibuf_data(src), integroff, integrlen); + + /* + * Validate packet checksum + */ + if ((tmp = ibuf_new(NULL, ibuf_length(integr))) == NULL) + goto done; + + hash_setkey(sa->sa_integr, integr->buf, ibuf_length(integr)); + hash_init(sa->sa_integr); + hash_update(sa->sa_integr, ibuf_data(msg), + ibuf_size(msg) - integrlen); + hash_final(sa->sa_integr, tmp->buf, &tmplen); + + if (memcmp(tmp->buf, ibuf_data(src) + integroff, integrlen) != 0) { + log_debug("%s: integrity check failed", __func__); + goto done; + } + + log_debug("%s: integrity check succeeded", __func__, tmplen); + print_hex(tmp->buf, 0, tmplen); + + ibuf_release(tmp); + tmp = NULL; + + /* + * Decrypt the payload and strip any padding + */ + if ((encrlen % blocklen) != 0) { + log_debug("%s: unaligned encrypted payload", __func__); + goto done; + } + + cipher_setkey(sa->sa_encr, encr->buf, ibuf_length(encr)); + cipher_setiv(sa->sa_encr, ibuf_data(src) + ivoff, ivlen); + cipher_init_decrypt(sa->sa_encr); + + if ((out = ibuf_new(NULL, cipher_outlength(sa->sa_encr, + encrlen))) == NULL) + goto done; + + outlen = ibuf_length(out); + /* XXX why does it need encrlen + blocklen to work correctly? */ + cipher_update(sa->sa_encr, + ibuf_data(src) + encroff, encrlen + blocklen, + ibuf_data(out), &outlen); + cipher_final(sa->sa_encr, ibuf_seek(out, outlen, blocklen), &tmplen); + if (tmplen) + outlen += tmplen; + + /* + * XXX + * XXX the padding is wrong + * XXX + */ + ptr = ibuf_seek(out, outlen - 1, 1); + pad = *ptr; + + log_debug("%s: decrypted payload length %d/%d padding %d", + __func__, outlen, encrlen, pad); + print_hex(ibuf_data(out), 0, ibuf_size(out)); + + if (ibuf_setsize(out, outlen) != 0) + goto done; + + ibuf_release(src); + return (out); + done: + ibuf_release(tmp); + ibuf_release(out); + ibuf_release(src); + return (NULL); +} + +int +ikev2_msg_send_encrypt(struct iked *env, struct iked_sa *sa, + struct ibuf **ep, u_int8_t exchange, u_int8_t firstpayload, int response) +{ + struct iked_message resp; + struct ike_header *hdr; + struct ikev2_payload *pld; + struct ibuf *buf, *e = *ep; + int ret = -1; + + if ((buf = ikev2_msg_init(env, &resp, + &sa->sa_peer.addr, sa->sa_peer.addr.ss_len, + &sa->sa_local.addr, sa->sa_local.addr.ss_len, 1)) == NULL) + goto done; + + /* IKE header */ + if ((hdr = ikev2_add_header(buf, sa, + ikev2_msg_id(env, sa, response), + IKEV2_PAYLOAD_E, exchange, + response ? IKEV2_FLAG_RESPONSE : 0)) == NULL) + goto done; + + if ((pld = ikev2_add_payload(buf)) == NULL) + goto done; + + /* Encrypt message and add as an E payload */ + if ((e = ikev2_msg_encrypt(env, sa, e)) == NULL) { + log_debug("%s: encryption failed", __func__); + goto done; + } + if (ibuf_cat(buf, e) != 0) + goto done; + if (ikev2_next_payload(pld, ibuf_size(e), firstpayload) == -1) + goto done; + + if (ikev2_set_header(hdr, ibuf_size(buf) - sizeof(*hdr)) == -1) + goto done; + + /* Add integrity checksum (HMAC) */ + if (ikev2_msg_integr(env, sa, buf) != 0) { + log_debug("%s: integrity checksum failed", __func__); + goto done; + } + + resp.msg_data = buf; + resp.msg_sa = sa; + TAILQ_INIT(&resp.msg_proposals); + + sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1; + (void)ikev2_pld_parse(env, hdr, &resp, 0); + sa->sa_hdr.sh_initiator = sa->sa_hdr.sh_initiator ? 0 : 1; + + ret = ikev2_msg_send(env, sa->sa_fd, &resp); + + done: + /* e is cleaned up by the calling function */ + *ep = e; + message_cleanup(env, &resp); + + return (ret); +} + +struct ibuf * +ikev2_msg_auth(struct iked *env, struct iked_sa *sa, int response) +{ + struct ibuf *authmsg = NULL, *nonce, *prfkey, *buf; + u_int8_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 %d", + __func__, response ? "responder" : "initiator", + ibuf_size(authmsg)); + print_hex(ibuf_data(authmsg), 0, ibuf_size(authmsg)); + + return (authmsg); + + fail: + ibuf_release(authmsg); + return (NULL); +} + +int +ikev2_msg_authverify(struct iked *env, struct iked_sa *sa, + struct iked_auth *auth, u_int8_t *buf, size_t len, struct ibuf *authmsg) +{ + u_int8_t *key, *psk = NULL; + ssize_t keylen; + struct iked_id *id; + struct iked_dsa *dsa = NULL; + int ret = -1; + u_int8_t keytype; + + if (sa->sa_hdr.sh_initiator) + id = &sa->sa_rcert; + else + id = &sa->sa_icert; + + if ((dsa = dsa_verify_new(auth->auth_method, sa->sa_prf)) == NULL) { + log_debug("%s: invalid auth method", __func__); + return (-1); + } + + switch (auth->auth_method) { + case IKEV2_AUTH_SHARED_KEY_MIC: + if (!auth->auth_length) { + log_debug("%s: no pre-shared key found", __func__); + goto done; + } + if ((keylen = ikev2_psk(sa, auth->auth_data, + auth->auth_length, &psk)) == -1) { + log_debug("%s: failed to get PSK", __func__); + goto done; + } + key = psk; + keytype = 0; + break; + default: + if (id == NULL) { + log_debug("%s: no cert found", __func__); + goto done; + } + key = ibuf_data(id->id_buf); + keylen = ibuf_size(id->id_buf); + keytype = id->id_type; + break; + } + + log_debug("%s: method %s keylen %d type %s", __func__, + print_map(auth->auth_method, ikev2_auth_map), keylen, + print_map(id->id_type, ikev2_cert_map)); + + if (dsa_setkey(dsa, key, keylen, keytype) == NULL || + dsa_init(dsa) != 0 || + dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { + log_debug("%s: failed to compute digital signature", __func__); + goto done; + } + + if ((ret = dsa_verify_final(dsa, buf, len)) == 0) { + log_debug("%s: authentication successful", __func__); + sa_state(env, sa, IKEV2_STATE_AUTH_SUCCESS); + + if (!sa->sa_policy->pol_auth.auth_eap && + auth->auth_method == IKEV2_AUTH_SHARED_KEY_MIC) + sa_state(env, sa, IKEV2_STATE_VALID); + } else { + log_debug("%s: authentication failed", __func__); + sa_state(env, sa, IKEV2_STATE_AUTH_REQUEST); + } + + done: + if (psk != NULL) + free(psk); + dsa_free(dsa); + + return (ret); +} + +int +ikev2_msg_authsign(struct iked *env, struct iked_sa *sa, + struct iked_auth *auth, struct ibuf *authmsg) +{ + u_int8_t *key, *psk = NULL; + ssize_t keylen; + struct iked_hash *prf = sa->sa_prf; + struct iked_id *id; + struct iked_dsa *dsa = NULL; + struct ibuf *buf; + int ret = -1; + u_int8_t keytype; + + if (sa->sa_hdr.sh_initiator) + id = &sa->sa_icert; + else + id = &sa->sa_rcert; + + if ((dsa = dsa_sign_new(auth->auth_method, prf)) == NULL) { + log_debug("%s: invalid auth method", __func__); + return (-1); + } + + switch (auth->auth_method) { + case IKEV2_AUTH_SHARED_KEY_MIC: + if (!auth->auth_length) { + log_debug("%s: no pre-shared key found", __func__); + goto done; + } + if ((keylen = ikev2_psk(sa, auth->auth_data, + auth->auth_length, &psk)) == -1) { + log_debug("%s: failed to get PSK", __func__); + goto done; + } + key = psk; + keytype = 0; + break; + default: + if (id == NULL) { + log_debug("%s: no cert found", __func__); + goto done; + } + key = ibuf_data(id->id_buf); + keylen = ibuf_size(id->id_buf); + keytype = id->id_type; + break; + } + + if (dsa_setkey(dsa, key, keylen, keytype) == NULL || + dsa_init(dsa) != 0 || + dsa_update(dsa, ibuf_data(authmsg), ibuf_size(authmsg))) { + log_debug("%s: failed to compute digital signature", __func__); + goto done; + } + + ibuf_release(sa->sa_localauth.id_buf); + sa->sa_localauth.id_buf = NULL; + + if ((buf = ibuf_new(NULL, dsa_length(dsa))) == NULL) { + log_debug("%s: failed to get auth buffer", __func__); + goto done; + } + + if ((ret = dsa_sign_final(dsa, + ibuf_data(buf), ibuf_size(buf))) == -1) { + log_debug("%s: failed to create auth signature", __func__); + ibuf_release(buf); + goto done; + } + + sa->sa_localauth.id_type = auth->auth_method; + sa->sa_localauth.id_buf = buf; + + ret = 0; + done: + if (psk != NULL) + free(psk); + dsa_free(dsa); + + return (ret); +} |