/* do X.509 operations for IKEv2 * * Copyright (C) 2008 Antony Antony * Copyright (C) 2008 David McCullough * Copyright (C) 2009-2010 Paul Wouters * Copyright (C) 2012-2013 Paul Wouters * Copyright (C) 2013 D. Hugh Redelmeier * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include "sysdep.h" #include "constants.h" #include "lswlog.h" #include "defs.h" #include "cookie.h" #include "id.h" #include "x509.h" #include "x509more.h" #include "certs.h" #include "connections.h" /* needs id.h */ #include "state.h" #include "packet.h" #include "md5.h" #include "sha1.h" #include "crypto.h" /* requires sha1.h and md5.h */ #include "ike_alg.h" #include "log.h" #include "demux.h" /* needs packet.h */ #include "ikev2.h" #include "server.h" #include "vendor.h" #include "keys.h" #include "ipsec_doi.h" static stf_status ikev2_send_certreq(struct state *st, struct msg_digest *md, enum phase1_role role, enum next_payload_types_ikev2 np, pb_stream *outpbs); /* Send v2CERT and v2 CERT */ stf_status ikev2_send_cert(struct state *st, struct msg_digest *md, enum phase1_role role, enum next_payload_types_ikev2 np, pb_stream *outpbs) { struct ikev2_cert cert; /* flag : to send a certificate request aka CERTREQ */ bool send_certreq = FALSE; struct connection *c = st->st_connection; cert_t mycert = st->st_connection->spd.this.cert; { /* decide the next payload; * send a CERTREQ no preloaded public key exists */ send_certreq = (c->policy & POLICY_RSASIG) && !has_preloaded_public_key(st) && (role == O_INITIATOR) && (st->st_connection->spd.that.ca.ptr != NULL); } DBG(DBG_CONTROL, DBG_log("Thinking about sending a certificate request (CERTREQ)"); DBG_log(" my policy is : %s", prettypolicy(c->policy)); DBG_log(" my next payload will %sbe a certificate request", send_certreq ? "" : "not ")); if (!send_certreq) { DBG(DBG_CONTROL, DBG_log("I did not send a certificate request (CERTREQ) because")); if (!(c->policy & POLICY_RSASIG)) { DBG(DBG_CONTROL, DBG_log(" RSA digital signatures are not being used. (PSK)")); } else if (has_preloaded_public_key(st)) { DBG(DBG_CONTROL, DBG_log(" has a preloaded a public for that end in st")); } else if (!(role == O_INITIATOR)) { DBG(DBG_CONTROL, DBG_log(" my role is not O_INITIATOR")); } else if (!(st->st_connection->spd.that.ca.ptr != NULL)) { DBG(DBG_CONTROL, DBG_log(" no known CA for the other end")); } else { DBG(DBG_CONTROL, DBG_log(" we reached an unexpected state - a bad day? I don't feel like sending a certificate request (CERTREQ)")); } } cert.isac_critical = ISAKMP_PAYLOAD_NONCRITICAL; if (DBGP(IMPAIR_SEND_BOGUS_ISAKMP_FLAG)) { libreswan_log( " setting bogus ISAKMP_PAYLOAD_LIBRESWAN_BOGUS flag in ISAKMP payload"); cert.isac_critical |= ISAKMP_PAYLOAD_LIBRESWAN_BOGUS; } cert.isac_enc = mycert.ty; if (send_certreq) { cert.isac_critical = ISAKMP_PAYLOAD_NONCRITICAL; if (DBGP(IMPAIR_SEND_BOGUS_ISAKMP_FLAG)) { libreswan_log(" setting bogus ISAKMP_PAYLOAD_LIBRESWAN_BOGUS flag in ISAKMP payload"); cert.isac_critical |= ISAKMP_PAYLOAD_LIBRESWAN_BOGUS; } cert.isac_np = ISAKMP_NEXT_v2CERTREQ; } else { cert.isac_np = np; /* * If we have a remote id configured in the conn, * we can send it here to signal we insist on it. * if (st->st_connection->spd.that.id) * cert.isaa_np = ISAKMP_NEXT_v2IDr; */ } /* send own (Initiator CERT) */ { pb_stream cert_pbs; DBG_log("I am sending my certificate"); if (!out_struct(&cert, &ikev2_certificate_desc, outpbs, &cert_pbs)) return STF_INTERNAL_ERROR; if (!out_chunk(get_mycert(mycert), &cert_pbs, "CERT")) return STF_INTERNAL_ERROR; close_output_pbs(&cert_pbs); } /* send CERTREQ */ if (send_certreq) { DBG(DBG_CONTROL, DBG_log("going to send a certreq")); ikev2_send_certreq(st, md, role, np, outpbs); } return STF_OK; } static stf_status ikev2_send_certreq(struct state *st, struct msg_digest *md, enum phase1_role role UNUSED, enum next_payload_types_ikev2 np, pb_stream *outpbs) { if (st->st_connection->kind == CK_PERMANENT) { DBG(DBG_CONTROL, DBG_log("connection->kind is CK_PERMANENT so send CERTREQ")); if (!ikev2_build_and_ship_CR(CERT_X509_SIGNATURE, st->st_connection->spd.that.ca, outpbs, np)) return STF_INTERNAL_ERROR; } else { generalName_t *ca = NULL; DBG(DBG_CONTROL, DBG_log("connection->kind is not CK_PERMANENT (instance), so collect CAs")); if (collect_rw_ca_candidates(md, &ca)) { generalName_t *gn; DBG(DBG_CONTROL, DBG_log("connection is RW, lookup CA candidates")); for (gn = ca; gn != NULL; gn = gn->next) { if (!ikev2_build_and_ship_CR(CERT_X509_SIGNATURE, gn->name, outpbs, gn->next == NULL ? np : ISAKMP_NEXT_v2CERTREQ)) return STF_INTERNAL_ERROR; } free_generalNames(ca, FALSE); } else { DBG(DBG_CONTROL, DBG_log("Not a roadwarrior instance, sending empty CA in CERTREQ")); if (!ikev2_build_and_ship_CR(CERT_X509_SIGNATURE, empty_chunk, outpbs, np)) return STF_INTERNAL_ERROR; } } return STF_OK; } /* just for ref copy from ikev1_main.c doi_log_cert_thinking(md , st->st_oakley.auth , mycert.type , st->st_connection->spd.this.sendcert , st->hidden_variables.st_got_certrequest , send_cert); doi_log_cert_thinking(struct msg_digest *md UNUSED , u_int16_t auth , enum ike_cert_type certtype , enum certpolicy policy , bool gotcertrequest , bool send_cert) */ bool doi_send_ikev2_cert_thinking(struct state *st) { cert_t mycert = st->st_connection->spd.this.cert; enum ike_cert_type certtype = mycert.ty; enum certpolicy policy = st->st_connection->spd.this.sendcert; bool send_cert = FALSE; struct connection *c = st->st_connection; /* decide to send_cert or not */ send_cert = (c->policy & POLICY_RSASIG) && mycert.ty != CERT_NONE && ((st->st_connection->spd.this.sendcert == cert_sendifasked && st->hidden_variables.st_got_certrequest) || st->st_connection->spd.this.sendcert == cert_alwayssend); /* log the steps led to the decision */ DBG(DBG_CONTROL, DBG_log("IKEv2 thinking whether to send my certificate:")); DBG(DBG_CONTROL, DBG_log(" my policy has %s RSASIG, the policy is : %s", (c->policy & POLICY_RSASIG) ? "" : "no", prettypolicy(c->policy))); DBG(DBG_CONTROL, { bool gotcertrequest = st->hidden_variables.st_got_certrequest; DBG_log(" sendcert: %s and I did%s get a certificate request ", enum_show(&certpolicy_type_names, policy), gotcertrequest ? "" : " not"); }); DBG(DBG_CONTROL, DBG_log(" so %ssend cert.", send_cert ? "" : "do not ")); if (!send_cert) { if (!(c->policy & POLICY_RSASIG)) { DBG(DBG_CONTROL, DBG_log("I did not send a certificate because digital signatures are not being used. (PSK)")); } else if (certtype == CERT_NONE) { DBG(DBG_CONTROL, DBG_log("I did not send a certificate because I do not have one.")); } else if (policy == cert_sendifasked) { DBG(DBG_CONTROL, DBG_log("I did not send my certificate because I was not asked to.")); } } return send_cert; }