diff -Naur libreswan-3.29-orig/programs/pluto/ikev1.c libreswan-3.29/programs/pluto/ikev1.c --- libreswan-3.29-orig/programs/pluto/ikev1.c 2019-06-26 22:03:27.801184503 -0400 +++ libreswan-3.29/programs/pluto/ikev1.c 2019-06-27 13:26:11.443969779 -0400 @@ -2675,6 +2675,12 @@ passert(st != NULL); pexpect(!state_is_busy(st)); + if (result > STF_OK) { + if (st != NULL) { + linux_audit_conn(md->st, IS_IKE_SA_ESTABLISHED(md->st) ? LAK_CHILD_FAIL : LAK_PARENT_FAIL); + } + } + switch (result) { case STF_OK: { diff -Naur libreswan-3.29-orig/programs/pluto/ikev1_quick.c libreswan-3.29/programs/pluto/ikev1_quick.c --- libreswan-3.29-orig/programs/pluto/ikev1_quick.c 2019-06-26 22:03:27.803184531 -0400 +++ libreswan-3.29/programs/pluto/ikev1_quick.c 2019-06-27 13:23:53.787080070 -0400 @@ -1663,6 +1663,9 @@ if (!install_inbound_ipsec_sa(st)) return STF_INTERNAL_ERROR; /* ??? we may be partly committed */ + /* we only audit once for IPsec SA's, we picked the inbound SA */ + linux_audit_conn(st, LAK_CHILD_START); + /* encrypt message, except for fixed part of header */ if (!ikev1_encrypt_message(&rbody, st)) { diff -Naur libreswan-3.29-orig/programs/pluto/ikev2.c libreswan-3.29/programs/pluto/ikev2.c --- libreswan-3.29-orig/programs/pluto/ikev2.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/ikev2.c 2019-06-27 13:25:16.529215928 -0400 @@ -3204,6 +3204,13 @@ lswlog_v2_stf_status(buf, result); } + /* audit log failures - success is audit logged in ikev2_ike_sa_established() */ + if (result > STF_OK) { + if (st != NULL) { + linux_audit_conn(st, IS_IKE_SA_ESTABLISHED(st) ? LAK_CHILD_FAIL : LAK_PARENT_FAIL); + } + } + switch (result) { case STF_SUSPEND: diff -Naur libreswan-3.29-orig/programs/pluto/ikev2_child.c libreswan-3.29/programs/pluto/ikev2_child.c --- libreswan-3.29-orig/programs/pluto/ikev2_child.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/ikev2_child.c 2019-06-27 13:23:53.788080084 -0400 @@ -102,6 +102,10 @@ return STF_OK; } +/* + * The caller could have done the linux_audit_conn() call, except one case + * here deletes the state before returning an STF error + */ stf_status ikev2_child_sa_respond(struct msg_digest *md, pb_stream *outpbs, enum isakmp_xchg_types isa_xchg) diff -Naur libreswan-3.29-orig/programs/pluto/ikev2_parent.c libreswan-3.29/programs/pluto/ikev2_parent.c --- libreswan-3.29-orig/programs/pluto/ikev2_parent.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/ikev2_parent.c 2019-06-27 13:23:53.789080097 -0400 @@ -239,6 +239,7 @@ c->newest_isakmp_sa = ike->sa.st_serialno; v2_schedule_replace_event(&ike->sa); ike->sa.st_viable_parent = TRUE; + linux_audit_conn(&ike->sa, LAK_PARENT_START); pstat_sa_established(&ike->sa); } @@ -1581,6 +1582,24 @@ libreswan_log("IKE_AUTH response contained an unknown error notification (%d)", n); } else { libreswan_log("IKE_AUTH response contained the error notification %s", name); + /* + * There won't be a child state transition, so log if error is child related. + * see RFC 7296 Section 1.2 + */ + switch(n) { + case v2N_NO_PROPOSAL_CHOSEN: + case v2N_SINGLE_PAIR_REQUIRED: + case v2N_NO_ADDITIONAL_SAS: + case v2N_INTERNAL_ADDRESS_FAILURE: + case v2N_FAILED_CP_REQUIRED: + case v2N_TS_UNACCEPTABLE: + case v2N_INVALID_SELECTORS: + /* fallthrough */ + linux_audit_conn(st, LAK_CHILD_FAIL); + break; + default: + break; + } } } } @@ -3063,10 +3082,6 @@ ikev2_ike_sa_established(pexpect_ike_sa(st), md->svm, STATE_PARENT_R2); -#ifdef USE_LINUX_AUDIT - linux_audit_conn(st, LAK_PARENT_START); -#endif - if (LHAS(st->hidden_variables.st_nat_traversal, NATED_HOST)) { /* ensure we run keepalives if needed */ if (c->nat_keepalive) @@ -3801,10 +3816,6 @@ ikev2_ike_sa_established(pexpect_ike_sa(pst), md->svm, STATE_PARENT_I3); -#ifdef USE_LINUX_AUDIT - linux_audit_conn(st, LAK_PARENT_START); -#endif - if (LHAS(st->hidden_variables.st_nat_traversal, NATED_HOST)) { /* ensure we run keepalives if needed */ if (c->nat_keepalive) diff -Naur libreswan-3.29-orig/programs/pluto/kernel.c libreswan-3.29/programs/pluto/kernel.c --- libreswan-3.29-orig/programs/pluto/kernel.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/kernel.c 2019-06-27 13:23:53.790080111 -0400 @@ -3334,7 +3334,8 @@ } #ifdef USE_LINUX_AUDIT - linux_audit_conn(st, LAK_CHILD_START); + if (inbound_also) + linux_audit_conn(st, LAK_CHILD_START); #endif statetime_stop(&start, "%s()", __func__); @@ -3378,8 +3379,13 @@ { #ifdef USE_LINUX_AUDIT /* XXX in IKEv2 we get a spurious call with a parent st :( */ - if (IS_CHILD_SA(st)) - linux_audit_conn(st, LAK_CHILD_DESTROY); + if (IS_CHILD_SA(st)) { + /* child destruction already logged for STATE_CHILDSA_DEL state */ + if (st->st_esp.present || st->st_ah.present) { + /* ESP or AH means this was an established IPsec SA */ + linux_audit_conn(st, LAK_CHILD_DESTROY); + } + } #endif switch (kern_interface) { case USE_KLIPS: diff -Naur libreswan-3.29-orig/programs/pluto/linux_audit.c libreswan-3.29/programs/pluto/linux_audit.c --- libreswan-3.29-orig/programs/pluto/linux_audit.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/linux_audit.c 2019-06-27 13:24:21.474460154 -0400 @@ -176,12 +176,16 @@ zero(&cipher_str); /* OK: no pointer fields */ zero(&spi_str); /* OK: no pointer fields */ + ip_address_buf raddr_buf; + const char *raddr = ipstr(&c->spd.that.host_addr, &raddr_buf); + switch (op) { case LAK_PARENT_START: case LAK_PARENT_DESTROY: + case LAK_PARENT_FAIL: initiator = (st->st_original_role == ORIGINAL_INITIATOR) || IS_PHASE1_INIT(st->st_state); snprintf(head, sizeof(head), "op=%s direction=%s %s connstate=%lu ike-version=%s auth=%s", - op == LAK_PARENT_START ? "start" : "destroy", + op == LAK_PARENT_DESTROY ? "destroy" : "start", /* fail to start logged under op=start */ initiator ? "initiator" : "responder", conn_encode, st->st_serialno, @@ -191,7 +195,8 @@ st->st_oakley.auth, &esb)); snprintf(prfname, sizeof(prfname), "%s", - st->st_oakley.ta_prf->prf_ike_audit_name); + st->st_oakley.ta_prf == NULL ? "none" : + st->st_oakley.ta_prf->prf_ike_audit_name); if (st->st_oakley.ta_integ == &ike_alg_integ_none) { if (st->st_ike_version == IKEv1) { @@ -220,18 +225,21 @@ } snprintf(cipher_str, sizeof(cipher_str), - "cipher=%s ksize=%d integ=%s prf=%s pfs=%s", - st->st_oakley.ta_encrypt->encrypt_ike_audit_name, + "cipher=%s ksize=%d integ=%s prf=%s pfs=%s raddr=%s", + st->st_oakley.ta_encrypt == NULL ? "none" : + st->st_oakley.ta_encrypt->encrypt_ike_audit_name, st->st_oakley.enckeylen, integname, prfname, - st->st_oakley.ta_dh->common.name); + st->st_oakley.ta_dh == NULL ? "none" : + st->st_oakley.ta_dh->common.name, raddr); break; case LAK_CHILD_START: case LAK_CHILD_DESTROY: + case LAK_CHILD_FAIL: { snprintf(head, sizeof(head), "op=%s %s connstate=%lu, satype=%s samode=%s", - op == LAK_CHILD_START ? "start" : "destroy", + op == LAK_CHILD_DESTROY ? "destroy" : "start", /* fail to start logged under op=start */ conn_encode, st->st_serialno, st->st_esp.present ? "ipsec-esp" : (st->st_ah.present ? "ipsec-ah" : "ipsec-policy"), @@ -274,7 +282,7 @@ /* note: each arg appears twice because it is printed two ways */ snprintf(spi_str, sizeof(spi_str), - "in-spi=%" PRIu32 "(0x%08" PRIu32 ") out-spi=%" PRIu32 "(0x%08" PRIu32 ") in-ipcomp=%" PRIu32 "(0x%08" PRIu32 ") out-ipcomp=%" PRIu32 "(0x%08" PRIu32 ")", + "in-spi=%" PRIu32 "(0x%08" PRIu32 ") out-spi=%" PRIu32 "(0x%08" PRIu32 ") in-ipcomp=%" PRIu32 "(0x%08" PRIu32 ") out-ipcomp=%" PRIu32 "(0x%08" PRIu32 ") raddr=%s", ntohl(pi->attrs.spi), ntohl(pi->attrs.spi), ntohl(pi->our_spi), @@ -282,7 +290,8 @@ ntohl(st->st_ipcomp.attrs.spi), /* zero if missing */ ntohl(st->st_ipcomp.attrs.spi), /* zero if missing */ ntohl(st->st_ipcomp.our_spi), /* zero if missing */ - ntohl(st->st_ipcomp.our_spi)); /* zero if missing */ + ntohl(st->st_ipcomp.our_spi), /* zero if missing */ + raddr); break; } default: @@ -290,21 +299,18 @@ } free(conn_encode); /* allocated by audit_encode_nv_string() */ - ip_address_buf laddr_buf; - const char *laddr = ipstr(&c->spd.this.host_addr, &laddr_buf); - - ip_address_buf raddr_buf; - const char *raddr = ipstr(&c->spd.that.host_addr, &raddr_buf); - - snprintf(audit_str, sizeof(audit_str), "%s %s %s laddr=%s", + snprintf(audit_str, sizeof(audit_str), "%s %s %s", head, cipher_str, - spi_str, - laddr); + spi_str); + + ip_address_buf laddr_buf; + const char *laddr = ipstr(&c->spd.this.host_addr, &laddr_buf); - linux_audit((op == LAK_CHILD_START || op == LAK_CHILD_DESTROY) ? + linux_audit((op == LAK_CHILD_START || op == LAK_CHILD_DESTROY || op == LAK_CHILD_FAIL) ? AUDIT_CRYPTO_IPSEC_SA : AUDIT_CRYPTO_IKE_SA, - audit_str, raddr, AUDIT_RESULT_OK); + audit_str, laddr, + (op == LAK_PARENT_FAIL || op == LAK_CHILD_FAIL) ? AUDIT_RESULT_FAIL : AUDIT_RESULT_OK); } #if __GNUC__ >= 7 #pragma GCC diagnostic pop diff -Naur libreswan-3.29-orig/programs/pluto/log.h libreswan-3.29/programs/pluto/log.h --- libreswan-3.29-orig/programs/pluto/log.h 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/log.h 2019-06-27 13:23:53.791080125 -0400 @@ -174,7 +174,9 @@ LAK_PARENT_START, LAK_CHILD_START, LAK_PARENT_DESTROY, - LAK_CHILD_DESTROY + LAK_CHILD_DESTROY, + LAK_PARENT_FAIL, + LAK_CHILD_FAIL }; extern void linux_audit_init(void); extern void linux_audit(const int type, const char *message, diff -Naur libreswan-3.29-orig/programs/pluto/retry.c libreswan-3.29/programs/pluto/retry.c --- libreswan-3.29-orig/programs/pluto/retry.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/retry.c 2019-06-27 13:25:27.536367032 -0400 @@ -123,6 +123,10 @@ set_cur_state(st); /* ipsecdoi_replace would reset cur_state, set it again */ pstat_sa_failed(st, REASON_TOO_MANY_RETRANSMITS); + + /* placed here because IKEv1 doesn't do a proper state change to STF_FAIL/STF_FATAL */ + linux_audit_conn(st, IS_IKE_SA(st) ? LAK_PARENT_FAIL : LAK_CHILD_FAIL); + delete_state(st); /* note: no md->st to clear */ } diff -Naur libreswan-3.29-orig/programs/pluto/state.c libreswan-3.29/programs/pluto/state.c --- libreswan-3.29-orig/programs/pluto/state.c 2019-06-10 10:22:04.000000000 -0400 +++ libreswan-3.29/programs/pluto/state.c 2019-06-27 13:23:53.792080138 -0400 @@ -875,6 +875,16 @@ #ifdef USE_LINUX_AUDIT /* + * IKEv2 IKE failures are logged in the state transition conpletion. + * IKEv1 IKE failures do not go through a transition, so we catch + * these in delete_state() + */ + if (IS_IKE_SA(st) && st->st_ike_version == IKEv1 && + !IS_IKE_SA_ESTABLISHED(st)) { + linux_audit_conn(st, LAK_PARENT_FAIL); + } + + /* * only log parent state deletes, we log children in * ipsec_delete_sa() */