diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..26f0a97
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+SOURCES/ikev1_dsa.fax.bz2
+SOURCES/ikev1_psk.fax.bz2
+SOURCES/ikev2.fax.bz2
+SOURCES/libreswan-3.27.tar.gz
diff --git a/.libreswan.metadata b/.libreswan.metadata
new file mode 100644
index 0000000..b2b4b4e
--- /dev/null
+++ b/.libreswan.metadata
@@ -0,0 +1,4 @@
+b35cd50b8bc0a08b9c07713bf19c72d53bfe66bb SOURCES/ikev1_dsa.fax.bz2
+861d97bf488f9e296cad8c43ab72f111a5b1a848 SOURCES/ikev1_psk.fax.bz2
+fcaf77f3deae3d8e99cdb3b1f8abea63167a0633 SOURCES/ikev2.fax.bz2
+c2e4b418ea286168bb022620a6af6a70cecffd14 SOURCES/libreswan-3.27.tar.gz
diff --git a/SOURCES/libreswan-3.25-1664521-fips-keysize.patch b/SOURCES/libreswan-3.25-1664521-fips-keysize.patch
new file mode 100644
index 0000000..e382f8c
--- /dev/null
+++ b/SOURCES/libreswan-3.25-1664521-fips-keysize.patch
@@ -0,0 +1,35 @@
+diff --git a/programs/pluto/connections.c b/programs/pluto/connections.c
+index 45e5bee1f..db1ac5303 100644
+--- a/programs/pluto/connections.c
++++ b/programs/pluto/connections.c
+@@ -846,9 +846,10 @@ static void load_end_nss_certificate(const char *which, CERTCertificate *cert,
+ if (libreswan_fipsmode()) {
+ SECKEYPublicKey *pk = CERT_ExtractPublicKey(cert);
+ passert(pk != NULL);
+- if (pk->u.rsa.modulus.len < FIPS_MIN_RSA_KEY_SIZE) {
++ if (pk->u.rsa.modulus.len * BITS_PER_BYTE < FIPS_MIN_RSA_KEY_SIZE) {
+ whack_log(RC_FATAL,
+- "FIPS: Rejecting cert with key size under %d",
++ "FIPS: Rejecting cert with key size %d which is under %d",
++ pk->u.rsa.modulus.len * BITS_PER_BYTE,
+ FIPS_MIN_RSA_KEY_SIZE);
+ SECKEY_DestroyPublicKey(pk);
+ return;
+diff --git a/programs/pluto/nss_cert_verify.c b/programs/pluto/nss_cert_verify.c
+index b4de167bb..9b031354b 100644
+--- a/programs/pluto/nss_cert_verify.c
++++ b/programs/pluto/nss_cert_verify.c
+@@ -460,9 +460,10 @@ static bool import_der_cert(CERTCertDBHandle *handle,
+ if (libreswan_fipsmode()) {
+ SECKEYPublicKey *pk = CERT_ExtractPublicKey(cert);
+ passert(pk != NULL);
+- if (pk->u.rsa.modulus.len < FIPS_MIN_RSA_KEY_SIZE) {
+- libreswan_log("FIPS: Rejecting cert with key size under %d",
+- FIPS_MIN_RSA_KEY_SIZE);
++ if ((pk->u.rsa.modulus.len * BITS_PER_BYTE) < FIPS_MIN_RSA_KEY_SIZE) {
++ libreswan_log("FIPS: Rejecting peer cert with key size %d under %d",
++ pk->u.rsa.modulus.len * BITS_PER_BYTE,
++ FIPS_MIN_RSA_KEY_SIZE);
+ SECKEY_DestroyPublicKey(pk);
+ /*
+ * XXX: Since the certificate isn't added to
diff --git a/SOURCES/libreswan-3.25-EKU-1639404.patch b/SOURCES/libreswan-3.25-EKU-1639404.patch
new file mode 100644
index 0000000..7249f6c
--- /dev/null
+++ b/SOURCES/libreswan-3.25-EKU-1639404.patch
@@ -0,0 +1,108 @@
+diff --git a/lib/libswan/Makefile b/lib/libswan/Makefile
+index 510148ad1..0f5c26228 100644
+--- a/lib/libswan/Makefile
++++ b/lib/libswan/Makefile
+@@ -200,10 +200,6 @@ CFLAGS+=-I${LIBRESWANSRCDIR}/include ${PORTINCLUDE}
+ CFLAGS+=$(USERLAND_CFLAGS)
+ CFLAGS+=${CROSSFLAGS}
+
+-ifeq ($(NSS_REQ_AVA_COPY),true)
+-CFLAGS+=-DNSS_REQ_AVA_COPY
+-endif
+-
+ OBJS += $(abs_builddir)/version.o
+
+ include $(top_srcdir)/mk/library.mk
+diff --git a/mk/config.mk b/mk/config.mk
+index 3f2bd55c1..fcdabd1fb 100644
+--- a/mk/config.mk
++++ b/mk/config.mk
+@@ -242,6 +242,17 @@ NSPR_LDFLAGS ?= -lnspr4
+ # Use nss copy for CERT_CompareAVA
+ # See https://bugzilla.mozilla.org/show_bug.cgi?id=1336487
+ NSS_REQ_AVA_COPY?=true
++ifeq ($(NSS_REQ_AVA_COPY),true)
++NSSFLAGS+=-DNSS_REQ_AVA_COPY
++endif
++
++# Use nss IPsec profile for X509 validation. This is less restrictive
++# ok EKU's. This is not yet in upstream nss.
++# See https://bugzilla.mozilla.org/show_bug.cgi?id=1252891
++NSS_HAS_IPSEC_PROFILE?=false
++ifeq ($(NSS_HAS_IPSEC_PROFILE),true)
++NSSFLAGS+=-DNSS_IPSEC_PROFILE
++endif
+
+ # Use a local copy of xfrm.h. This can be needed on older systems
+ # that do not ship linux/xfrm.h, or when the shipped version is too
+diff --git a/programs/pluto/nss_cert_verify.c b/programs/pluto/nss_cert_verify.c
+index 95c637f53..7d458ac2a 100644
+--- a/programs/pluto/nss_cert_verify.c
++++ b/programs/pluto/nss_cert_verify.c
+@@ -299,6 +299,28 @@ static int vfy_chain_pkix(CERTCertificate **chain, int chain_len,
+ cvout[1].value.pointer.chain = NULL;
+ cvout[2].type = cert_po_end;
+
++ int fin;
++
++#ifdef NSS_IPSEC_PROFILE
++ SECStatus rv = CERT_PKIXVerifyCert(end_cert, certificateUsageIPsec,
++ cvin, cvout, NULL);
++ if (rv != SECSuccess || cur_log->count > 0) {
++ if (cur_log->count > 0 && cur_log->head != NULL) {
++ fin = nss_err_to_revfail(cur_log->head);
++ } else {
++ /*
++ * An rv != SECSuccess without CERTVerifyLog
++ * results should not * happen, but catch it anyway
++ */
++ loglog(RC_LOG_SERIOUS, "X509: unspecified NSS verification failure");
++ fin = VERIFY_RET_FAIL;
++ }
++ } else {
++ DBG(DBG_X509, DBG_log("certificate is valid"));
++ *end_out = end_cert;
++ fin = VERIFY_RET_OK;
++ }
++#else
+ /* kludge alert!!
+ * verification may be performed twice: once with the
+ * 'client' usage and once with 'server', which is an NSS
+@@ -307,12 +329,10 @@ static int vfy_chain_pkix(CERTCertificate **chain, int chain_len,
+ * KU/EKU combinations
+ */
+
+- int fin;
+ SECCertificateUsage usage;
+
+ for (usage = certificateUsageSSLClient; ; usage = certificateUsageSSLServer) {
+ SECStatus rv = CERT_PKIXVerifyCert(end_cert, usage, cvin, cvout, NULL);
+-
+ if (rv != SECSuccess || cur_log->count > 0) {
+ if (cur_log->count > 0 && cur_log->head != NULL) {
+ if (usage == certificateUsageSSLClient &&
+@@ -346,6 +366,7 @@ static int vfy_chain_pkix(CERTCertificate **chain, int chain_len,
+ }
+ break;
+ }
++#endif
+ pexpect(fin != 0);
+
+ CERT_DestroyCertList(trustcl);
+diff --git a/programs/pluto/plutomain.c b/programs/pluto/plutomain.c
+index 50582822d..007d73f45 100644
+--- a/programs/pluto/plutomain.c
++++ b/programs/pluto/plutomain.c
+@@ -180,6 +180,12 @@ static const char compile_time_interop_options[] = ""
+ " BROKEN_POPEN"
+ #endif
+ " NSS"
++#ifdef NSS_REQ_AVA_COPY
++ " (AVA copy)"
++#endif
++#ifdef NSS_IPSEC_PROFILE
++ " (IPsec profile)"
++#endif
+ #ifdef USE_DNSSEC
+ " DNSSEC"
+ #endif
diff --git a/SOURCES/libreswan-3.27-1645137-addconn.patch b/SOURCES/libreswan-3.27-1645137-addconn.patch
new file mode 100644
index 0000000..b8be440
--- /dev/null
+++ b/SOURCES/libreswan-3.27-1645137-addconn.patch
@@ -0,0 +1,12 @@
+diff -Naur libreswan-3.28/lib/libipsecconf/confread.c libreswan-3.28-orig/lib/libipsecconf/confread.c
+--- libreswan-3.28/lib/libipsecconf/confread.c 2018-11-08 01:28:24.869885136 -0500
++++ libreswan-3.28-orig/lib/libipsecconf/confread.c 2018-11-04 21:24:36.000000000 -0500
+@@ -827,7 +827,7 @@
+ break;
+ }
+ }
+- pfreeany((*the_strings)[field]);
++
+
+ if (kw->string == NULL) {
+ starter_error_append(perrl, "Invalid %s value",
diff --git a/SOURCES/libreswan-3.27-1648776-v1v2split.patch b/SOURCES/libreswan-3.27-1648776-v1v2split.patch
new file mode 100644
index 0000000..c19a3e5
--- /dev/null
+++ b/SOURCES/libreswan-3.27-1648776-v1v2split.patch
@@ -0,0 +1,145 @@
+diff -Naur libreswan-3.27-orig/lib/libipsecconf/confread.c libreswan-3.27/lib/libipsecconf/confread.c
+--- libreswan-3.27-orig/lib/libipsecconf/confread.c 2018-12-09 12:45:14.559217654 -0500
++++ libreswan-3.27/lib/libipsecconf/confread.c 2018-12-09 13:05:01.641609352 -0500
+@@ -148,7 +148,7 @@
+ cfg->conn_default.policy =
+ POLICY_TUNNEL |
+ POLICY_ENCRYPT | POLICY_PFS |
+- POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW | /* ikev2=permit */
++ POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE | /* ikev2=insist */
+ POLICY_SAREF_TRACK | /* sareftrack=yes */
+ POLICY_IKE_FRAG_ALLOW | /* ike_frag=yes */
+ POLICY_ESN_NO; /* esn=no */
+@@ -1257,16 +1257,15 @@
+ break;
+
+ case fo_permit:
+- /* this is the default for now */
+- pv2 = POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW;
+- break;
++ starter_error_append(perrl, "ikev2=permit is no longer accepted. Use ikev2=yes or ikev2=no");
++ return TRUE;
+
+ case fo_propose:
+- pv2 = POLICY_IKEV1_ALLOW | POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE;
+- break;
++ starter_error_append(perrl, "ikev2=propose is no longer accepted. Use ikev2=yes or ikev2=no");
++ return TRUE;
+
+ case fo_insist:
+- pv2 = POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE;
++ pv2 = POLICY_IKEV2_ALLOW | POLICY_IKEV2_PROPOSE;
+ break;
+ }
+ conn->policy = (conn->policy & ~POLICY_IKEV2_MASK) | pv2;
+diff -Naur libreswan-3.27-orig/lib/libipsecconf/keywords.c libreswan-3.27/lib/libipsecconf/keywords.c
+--- libreswan-3.27-orig/lib/libipsecconf/keywords.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libipsecconf/keywords.c 2018-12-09 13:02:15.541619284 -0500
+@@ -74,7 +74,7 @@
+ static const struct keyword_enum_values kw_keyexchange_list = VALUES_INITIALIZER(kw_keyexchange_values);
+
+ /*
+- * Values for Four-State options, such as ikev2
++ * Values for Four-State options, such as ppk
+ */
+ static const struct keyword_enum_value kw_fourvalued_values[] = {
+ { "never", fo_never },
+diff -Naur libreswan-3.27-orig/programs/configs/d.ipsec.conf/ikev2.xml libreswan-3.27/programs/configs/d.ipsec.conf/ikev2.xml
+--- libreswan-3.27-orig/programs/configs/d.ipsec.conf/ikev2.xml 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/configs/d.ipsec.conf/ikev2.xml 2018-12-09 13:02:15.541619284 -0500
+@@ -1,25 +1,13 @@
+
+ ikev2
+
+-IKEv2 (RFC 7296) settings to be used. Currently the accepted
+-values are permit(the default),
+-signifying IKEv2 will be accepted if received, but IKEv1 will be used
+-when initiating; never or no signifying no IKEv2 negotiation should be
+-transmitted or accepted; propose or
+-yes signifying that we permit IKEv1
+-and IKEv2, and use IKEv2 as the default to initiate; and insistsignifying we only accept and receive IKEv2 -
+-IKEv1 negotiations will be rejected.
+-
+-If the ikev2= setting is set to permit
+-or propose, Libreswan will try and detect a
+-"bid down" attack from IKEv2 to IKEv1. Since there is no standard for
+-transmitting the IKEv2 capability with IKEv1, Libreswan uses a special
+-Vendor ID "CAN-IKEv2". If a fall back from IKEv2 to IKEv1 was detected,
+-and the IKEv1 negotiation contains Vendor ID "CAN-IKEv2", Libreswan will
+-immediately attempt and IKEv2 rekey and refuse to use the IKEv1 connection.
+-With an ikev2= setting of insist, no IKEv1
+-negotiation is allowed, and no bid down attack is possible.
++Wether to use IKEv1 (RFC 4301) or IKEv2 (RFC 7296) as the Internet Key Exchange (IKE) protcol.
++Currently the accepted values are no (or never)
++signifying only IKEv1 is accepted, or insist(the default),
++signifying only IKEv2 is accepted. Previous versions allowed the keywords
++propose, yes or permit
++that would allow either IKEv1 or IKEv2, but this is no longer supported and both options
++now cause the connection to fail to load.
++
+
+
+diff -Naur libreswan-3.27-orig/programs/whack/whack.c libreswan-3.27/programs/whack/whack.c
+--- libreswan-3.27-orig/programs/whack/whack.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/whack/whack.c 2018-12-09 13:06:01.825333781 -0500
+@@ -108,7 +108,7 @@
+ " [--mtu ] \\\n"
+ " [--priority ] [--reqid ] \\\n"
+ " [--tfc ] [--send-no-esp-tfc] \\\n"
+- " [--ikev1-allow | --ikev2-allow | --ikev2-propose] \\\n"
++ " [--ikev1-allow | --ikev2-allow ] \\\n"
+ " [--allow-narrowing] [--sareftrack] [--sarefconntrack] \\\n"
+ " [--ikefrag-allow | --ikefrag-force] [--no-ikepad] \\\n"
+ " [--esn ] [--no-esn] [--decap-dscp] [--nopmtudisc] [--mobike] \\\n"
+@@ -711,7 +711,6 @@
+
+ PS("ikev1-allow", IKEV1_ALLOW),
+ PS("ikev2-allow", IKEV2_ALLOW),
+- PS("ikev2-propose", IKEV2_PROPOSE),
+
+ PS("allow-narrowing", IKEV2_ALLOW_NARROWING),
+ #ifdef XAUTH_HAVE_PAM
+@@ -1627,8 +1626,6 @@
+ case CDP_SINGLETON + POLICY_IKEV1_ALLOW_IX:
+ /* --ikev2-allow */
+ case CDP_SINGLETON + POLICY_IKEV2_ALLOW_IX:
+- /* --ikev2-propose */
+- case CDP_SINGLETON + POLICY_IKEV2_PROPOSE_IX:
+
+ /* --allow-narrowing */
+ case CDP_SINGLETON + POLICY_IKEV2_ALLOW_NARROWING_IX:
+@@ -2191,6 +2188,15 @@
+ break;
+ }
+
++ /* fixup old to new style IKEv1/IKEv2 settings */
++ if (msg.policy & POLICY_IKEV2_ALLOW) {
++ /* IKEv2 now always has ALLOW+PROPOSE */
++ msg.policy |= POLICY_IKEV2_PROPOSE;
++ }
++ if (msg.policy & POLICY_IKEV2_ALLOW &&
++ msg.policy & POLICY_IKEV1_ALLOW) {
++ diag("connection cannot be both ikev1 and ikev2");
++ }
+
+ if (oppo_dport != 0)
+ setportof(htons(oppo_dport), &msg.oppo_peer_client);
+@@ -2267,11 +2273,12 @@
+ diag("must specify connection authentication, eg --rsasig, --psk or --auth-null for non-shunt connection");
+
+ /*
+- * If neither v1 nor v2, default to v1
+- * (backward compatibility)
++ * If neither v1 nor v2, default to v2
+ */
+- if (!(msg.policy & POLICY_IKEV2_MASK))
+- msg.policy |= POLICY_IKEV1_ALLOW;
++ if (!(msg.policy & POLICY_IKEV2_MASK)) {
++ msg.policy |= POLICY_IKEV2_ALLOW;
++ msg.policy |= POLICY_IKEV2_PROPOSE;
++ }
+
+ /*
+ * ??? this test can never fail:
diff --git a/SOURCES/libreswan-3.27-1671793-delete.patch b/SOURCES/libreswan-3.27-1671793-delete.patch
new file mode 100644
index 0000000..3ef2cd8
--- /dev/null
+++ b/SOURCES/libreswan-3.27-1671793-delete.patch
@@ -0,0 +1,90 @@
+diff -Naur libreswan-3.27-orig/include/pluto_constants.h libreswan-3.27/include/pluto_constants.h
+--- libreswan-3.27-orig/include/pluto_constants.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/pluto_constants.h 2019-02-05 16:53:07.442895489 -0500
+@@ -421,6 +421,8 @@
+
+ IMPAIR_SEND_PKCS7_THINGIE_IX,
+
++ IMPAIR_IKEv1_DEL_WITH_NOTIFY_IX,
++
+ IMPAIR_roof_IX /* first unassigned IMPAIR */
+ };
+
+@@ -463,6 +465,7 @@
+ #define IMPAIR_REPLAY_DUPLICATES LELEM(IMPAIR_REPLAY_DUPLICATES_IX)
+ #define IMPAIR_REPLAY_FORWARD LELEM(IMPAIR_REPLAY_FORWARD_IX)
+ #define IMPAIR_REPLAY_BACKWARD LELEM(IMPAIR_REPLAY_BACKWARD_IX)
++#define IMPAIR_IKEv1_DEL_WITH_NOTIFY LELEM(IMPAIR_IKEv1_DEL_WITH_NOTIFY_IX)
+
+ #define IMPAIR_REPLAY_ENCRYPTED LELEM(IMPAIR_REPLAY_ENCRYPTED_IX)
+ #define IMPAIR_CORRUPT_ENCRYPTED LELEM(IMPAIR_CORRUPT_ENCRYPTED_IX)
+@@ -478,6 +481,8 @@
+
+ #define IMPAIR_SEND_PKCS7_THINGIE LELEM(IMPAIR_SEND_PKCS7_THINGIE_IX)
+
++#define IMPAIR_IKEv1_DEL_WITH_NOTIFY LELEM(IMPAIR_IKEv1_DEL_WITH_NOTIFY_IX)
++
+ /* State of exchanges
+ *
+ * The name of the state describes the last message sent, not the
+diff -Naur libreswan-3.27-orig/lib/libswan/impair.c libreswan-3.27/lib/libswan/impair.c
+--- libreswan-3.27-orig/lib/libswan/impair.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/impair.c 2019-02-05 16:51:07.522924972 -0500
+@@ -87,6 +87,7 @@
+ S(IMPAIR_SUPPRESS_RETRANSMITS, "impair-suppress-retransmits", "causes pluto to never send retransmits (wait the full timeout)"),
+ S(IMPAIR_TIMEOUT_ON_RETRANSMIT, "impair-timeout-on-retransmit", "causes pluto to 'retry' (switch protocol) on the first retransmit"),
+ S(IMPAIR_UNKNOWN_PAYLOAD_CRITICAL, "impair-unknown-payload-critical", "mark the unknown payload as critical"),
++ S(IMPAIR_IKEv1_DEL_WITH_NOTIFY, "impair-ikev1-del-with-notify", "causes pluto to send IKE Delete with additional bogus Notify payload"),
+
+ #undef S
+ };
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_main.c libreswan-3.27/programs/pluto/ikev1_main.c
+--- libreswan-3.27-orig/programs/pluto/ikev1_main.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev1_main.c 2019-02-05 16:48:16.241538787 -0500
+@@ -2347,12 +2347,36 @@
+ .isad_protoid = ns->proto,
+ .isad_nospi = 1,
+ };
++
++ if (DBGP(IMPAIR_IKEv1_DEL_WITH_NOTIFY))
++ isad.isad_np = ISAKMP_NEXT_N; /* Notify */
++
+ passert(out_struct(&isad, &isakmp_delete_desc, &r_hdr_pbs,
+ &del_pbs));
+ passert(out_raw(&ns->spi, sizeof(ipsec_spi_t),
+ &del_pbs,
+ "delete payload"));
+ close_output_pbs(&del_pbs);
++
++ if (DBGP(IMPAIR_IKEv1_DEL_WITH_NOTIFY)) {
++ pb_stream cruft_pbs;
++
++ libreswan_log("IMPAIR: adding bogus Notify payload after IKE Delete payload");
++ struct isakmp_notification isan = {
++ .isan_np = ISAKMP_NEXT_NONE,
++ .isan_doi = ISAKMP_DOI_IPSEC,
++ .isan_protoid = PROTO_ISAKMP,
++ .isan_spisize = COOKIE_SIZE * 2,
++ .isan_type = INVALID_PAYLOAD_TYPE,
++ };
++
++ passert(out_struct(&isan, &isakmp_notification_desc, &r_hdr_pbs,
++ &cruft_pbs));
++ passert(out_raw(&ns->spi, sizeof(ipsec_spi_t), &cruft_pbs,
++ "notify payload"));
++ close_output_pbs(&cruft_pbs);
++ }
++
+ }
+ }
+
+@@ -2599,7 +2623,9 @@
+ rc->policy &= ~POLICY_UP;
+ if (!shared_phase1_connection(rc)) {
+ flush_pending_by_connection(rc);
++ /* This also deletes the IKE SA, clear pointer */
+ delete_states_by_connection(rc, FALSE);
++ md->st = NULL;
+ }
+ reset_cur_connection();
+ }
diff --git a/SOURCES/libreswan-3.27-outgoing-ports-1668342.patch b/SOURCES/libreswan-3.27-outgoing-ports-1668342.patch
new file mode 100644
index 0000000..2af73f5
--- /dev/null
+++ b/SOURCES/libreswan-3.27-outgoing-ports-1668342.patch
@@ -0,0 +1,29 @@
+diff -up libreswan-3.27/lib/libswan/unbound.c.orig libreswan-3.27/lib/libswan/unbound.c
+--- libreswan-3.27/lib/libswan/unbound.c.orig 2019-01-24 16:28:27.191695613 +0100
++++ libreswan-3.27/lib/libswan/unbound.c 2019-02-01 12:16:36.029309870 +0100
+@@ -95,6 +95,25 @@ static void unbound_ctx_config(bool do_d
+ DBG(DBG_DNS, DBG_log("/etc/resolv.conf usage activated"));
+ }
+
++ /* Set a limit on outgoing ports, some ports are prohibited by SELinux policy */
++ errno = 0;
++ ugh = ub_ctx_set_option(dns_ctx, "outgoing-port-avoid:", "0-65535");
++ if (ugh != 0) {
++ loglog(RC_LOG_SERIOUS, "error setting outgoing-port-avoid: %s: %s",
++ ub_strerror(ugh), strerror(errno));
++ } else {
++ DBG(DBG_DNS, DBG_log("outgoing-port-avoid set 0-65535"));
++ }
++
++ errno = 0;
++ ugh = ub_ctx_set_option(dns_ctx, "outgoing-port-permit:", "32768-60999");
++ if (ugh != 0) {
++ loglog(RC_LOG_SERIOUS, "error setting outgoing-port-permit: %s: %s",
++ ub_strerror(ugh), strerror(errno));
++ } else {
++ DBG(DBG_DNS, DBG_log("outgoing-port-permit set 32768-60999"));
++ }
++
+ if (!do_dnssec) {
+ /* No DNSSEC - nothing more to configure */
+ DBG(DBG_DNS, DBG_log("dnssec validation disabled by configuration"));
diff --git a/SOURCES/libreswan-3.27-parser.patch b/SOURCES/libreswan-3.27-parser.patch
new file mode 100644
index 0000000..ea98c90
--- /dev/null
+++ b/SOURCES/libreswan-3.27-parser.patch
@@ -0,0 +1,7144 @@
+diff -Naur libreswan-3.27-orig/include/alg_byname.h libreswan-3.27/include/alg_byname.h
+--- libreswan-3.27-orig/include/alg_byname.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/alg_byname.h 2019-02-15 16:32:28.971835267 -0500
+@@ -30,26 +30,26 @@
+ * messages better align with the input files.
+ */
+
+-bool alg_byname_ok(const struct proposal_parser *parser,
++bool alg_byname_ok(struct proposal_parser *parser,
+ const struct ike_alg *alg, shunk_t print_name);
+
+ /*
+ * Helper functions to implement most of the lookup.
+ */
+
+-const struct ike_alg *encrypt_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *encrypt_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length,
+ shunk_t print_name);
+
+-const struct ike_alg *prf_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *prf_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length,
+ shunk_t print_name);
+
+-const struct ike_alg *integ_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *integ_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length,
+ shunk_t print_name);
+
+-const struct ike_alg *dh_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *dh_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length,
+ shunk_t print_name);
+
+diff -Naur libreswan-3.27-orig/include/alg_info.h libreswan-3.27/include/alg_info.h
+--- libreswan-3.27-orig/include/alg_info.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/alg_info.h 1969-12-31 19:00:00.000000000 -0500
+@@ -1,245 +0,0 @@
+-/* Algorithm info parsing and creation functions
+- *
+- * Author: JuanJo Ciarlante
+- * Copyright (C) 2007 Michael Richardson
+- * Copyright (C) 2012-2013 Paul Wouters
+- * Copyright (C) 2013 D. Hugh Redelmeier
+- * Copyright (C) 2013 Paul Wouters
+- * Copyright (C) 2015-2017 Andrew Cagney
+- *
+- * 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.
+- */
+-
+-#ifndef ALG_INFO_H
+-#define ALG_INFO_H
+-
+-#include "lswcdefs.h"
+-#include "constants.h"
+-#include "ike_alg.h"
+-#include "shunk.h"
+-
+-struct alg_info;
+-struct proposal_protocol;
+-struct proposal_info;
+-struct proposal_policy;
+-struct proposal_parser;
+-
+-typedef const struct ike_alg *(alg_byname_fn)(const struct proposal_parser *parser,
+- shunk_t name, size_t key_bit_length,
+- shunk_t print_name);
+-
+-/*
+- * Parameters to tune the parser.
+- */
+-
+-struct proposal_policy {
+- bool ikev1;
+- bool ikev2;
+- bool pfs; /* For CHILD SA, use DH from IKE SA */
+- /*
+- * According to current policy, is the algorithm ok
+- * (supported)? If it isn't return FALSE.
+- *
+- * For instance, an IKE algorithm requires in-process support;
+- * while an ESP/AH algorithm requires kernel support.
+- */
+- bool (*alg_is_ok)(const struct ike_alg *alg);
+- /*
+- * Print a warning. Signature needs to match libreswan_log.
+- */
+- int (*warning)(const char *fmt, ...) PRINTF_LIKE(1);
+-};
+-
+-/*
+- * Defaults to fill in.
+- */
+-
+-struct proposal_defaults {
+- const struct ike_alg **dh;
+- const struct ike_alg **prf;
+- const struct ike_alg **integ;
+- const struct ike_alg **encrypt;
+-};
+-
+-/*
+- * Parameters to set the parser's basic behaviour - ESP/AH/IKE.
+- */
+-
+-struct proposal_protocol {
+- const char *name;
+- enum ike_alg_key ikev1_alg_id;
+-
+- /*
+- * Lists of defaults.
+- */
+- const struct proposal_defaults *ikev1_defaults;
+- const struct proposal_defaults *ikev2_defaults;
+-
+- /*
+- * Is the proposal OK?
+- *
+- * This is the final check, if this succeeds then the proposal
+- * is added.
+- */
+- bool (*proposal_ok)(const struct proposal_parser *parser,
+- const struct proposal_info *proposal);
+-
+- /*
+- * XXX: Is the proto-id needed? Parser should be protocol
+- * agnostic.
+- */
+- unsigned protoid;
+-
+- /*
+- * This lookup functions must set err and return null if NAME
+- * isn't valid.
+- */
+- alg_byname_fn *encrypt_alg_byname;
+- alg_byname_fn *prf_alg_byname;
+- alg_byname_fn *integ_alg_byname;
+- alg_byname_fn *dh_alg_byname;
+-};
+-
+-/*
+- * Everything combined.
+- */
+-struct proposal_parser {
+- const struct proposal_protocol *protocol;
+- const struct proposal_param *param;
+- const struct proposal_policy *policy;
+- char *err_buf;
+- size_t err_buf_len;
+-};
+-
+-/*
+- * A proposal as decoded by the parser.
+- */
+-
+-struct proposal_info {
+- /*
+- * The encryption algorithm and key length.
+- *
+- * Because struct encrypt_desc still specifies multiple key
+- * lengths, ENCKEYLEN is still required.
+- */
+- const struct encrypt_desc *encrypt;
+- size_t enckeylen; /* keylength for ESP transform (bits) */
+- /*
+- * The integrity and PRF algorithms.
+- */
+- const struct prf_desc *prf;
+- const struct integ_desc *integ;
+- /*
+- * PFS/DH negotiation.
+- */
+- const struct oakley_group_desc *dh;
+- /*
+- * Which protocol is this proposal intended for?
+- */
+- const struct proposal_protocol *protocol;
+-};
+-
+-/* common prefix of struct alg_info_esp and struct alg_info_ike */
+-struct alg_info {
+- int alg_info_cnt;
+- int ref_cnt;
+- struct proposal_info proposals[128];
+-};
+-
+-struct alg_info_esp {
+- struct alg_info ai; /* common prefix */
+-};
+-
+-struct alg_info_ike {
+- struct alg_info ai; /* common prefix */
+-};
+-
+-extern void alg_info_free(struct alg_info *alg_info);
+-extern void alg_info_addref(struct alg_info *alg_info);
+-extern void alg_info_delref(struct alg_info *alg_info);
+-
+-extern struct alg_info_ike *alg_info_ike_create_from_str(const struct proposal_policy *policy,
+- const char *alg_str,
+- char *err_buf, size_t err_buf_len);
+-
+-extern struct alg_info_esp *alg_info_esp_create_from_str(const struct proposal_policy *policy,
+- const char *alg_str,
+- char *err_buf, size_t err_buf_len);
+-
+-extern struct alg_info_esp *alg_info_ah_create_from_str(const struct proposal_policy *policy,
+- const char *alg_str,
+- char *err_buf, size_t err_buf_len);
+-
+-size_t lswlog_proposal_info(struct lswlog *log, const struct proposal_info *proposal);
+-size_t lswlog_alg_info(struct lswlog *log, const struct alg_info *alg_info);
+-
+-/*
+- * Iterate through the elements of an ESP or IKE table.
+- *
+- * Use __typeof__ instead of const to get around ALG_INFO some times
+- * being const and sometimes not.
+- *
+- * XXX: yes, they are the same!
+- */
+-
+-#define FOR_EACH_PROPOSAL_INFO(ALG_INFO, PROPOSAL_INFO) \
+- for (__typeof__((ALG_INFO)->proposals[0]) *(PROPOSAL_INFO) = (ALG_INFO)->proposals; \
+- (PROPOSAL_INFO) < (ALG_INFO)->proposals + (ALG_INFO)->alg_info_cnt; \
+- (PROPOSAL_INFO)++)
+-
+-#define FOR_EACH_ESP_INFO(ALG_INFO, ESP_INFO) \
+- FOR_EACH_PROPOSAL_INFO(&((ALG_INFO)->ai), ESP_INFO)
+-
+-#define FOR_EACH_IKE_INFO(ALG_INFO, IKE_INFO) \
+- FOR_EACH_PROPOSAL_INFO(&((ALG_INFO)->ai), IKE_INFO)
+-
+-/*
+- * Error indicated by err_buf[0] != '\0'.
+- *
+- * POLICY should be used to guard algorithm supported checks. For
+- * instance: if POLICY=IKEV1, then IKEv1 support is required (IKEv2 is
+- * don't care); and if POLICY=IKEV1|IKEV2, then both IKEv1 and IKEv2
+- * support is required.
+- *
+- * Parsing with POLICY=IKEV1, but then proposing the result using
+- * IKEv2 is a program error. The IKEv2 should complain loudly and,
+- * we hope, not crash.
+- *
+- * Parsing with POLICY='0' is allowed. It will accept the algorithms
+- * unconditionally (spi.c seems to need this).
+- */
+-
+-struct proposal_parser proposal_parser(const struct proposal_policy *policy,
+- const struct proposal_protocol *protocol,
+- char *err_buf, size_t err_buf_len);
+-
+-bool alg_info_parse_str(const struct proposal_parser *parser,
+- struct alg_info *alg_info,
+- shunk_t alg_str);
+-
+-/*
+- * Check that encrypt==AEAD and/or integ==none don't contradict.
+- */
+-bool proposal_aead_none_ok(const struct proposal_parser *parser,
+- const struct proposal_info *proposal);
+-
+-bool alg_info_pfs_vs_dh_check(const struct proposal_parser *parser,
+- struct alg_info_esp *aie);
+-
+-#if 0
+-/* return true if it really is an error (when impaired return false) */
+-bool proposal_error(const struct proposal_parser *parser,
+- const char *message, ...) PRINTF_LIKE(2);
+-#endif
+-
+-bool impair_proposal_errors(const struct proposal_parser *parser);
+-
+-#endif /* ALG_INFO_H */
+diff -Naur libreswan-3.27-orig/include/ike_alg.h libreswan-3.27/include/ike_alg.h
+--- libreswan-3.27-orig/include/ike_alg.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/ike_alg.h 2019-02-15 16:32:28.972835277 -0500
+@@ -252,7 +252,7 @@
+ * Easier to just require that this contain everything then
+ * poke around in multiple places.
+ */
+- const char *names[5];
++ const char *names[6];
+ /*
+ * See above.
+ *
+diff -Naur libreswan-3.27-orig/include/lswlog.h libreswan-3.27/include/lswlog.h
+--- libreswan-3.27-orig/include/lswlog.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/lswlog.h 2019-02-15 16:32:28.973835286 -0500
+@@ -577,7 +577,7 @@
+ #define PEXPECT_LOG(FMT, ...) \
+ libreswan_pexpect_log(__func__, \
+ PASSERT_BASENAME, __LINE__, \
+- FMT, __VA_ARGS__)
++ FMT,##__VA_ARGS__)
+
+
+ /*
+diff -Naur libreswan-3.27-orig/include/names_constant.h libreswan-3.27/include/names_constant.h
+--- libreswan-3.27-orig/include/names_constant.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/names_constant.h 2019-02-15 16:32:28.973835286 -0500
+@@ -41,6 +41,8 @@
+ extern enum_names auth_alg_names;
+ extern enum_names oakley_lifetime_names;
+
++extern enum_names ike_version_names;
++extern enum_names ike_version_liveness_names;
+ extern enum_names version_names;
+ extern enum_names doi_names;
+ extern enum_names ikev1_payload_names;
+diff -Naur libreswan-3.27-orig/include/pluto_constants.h libreswan-3.27/include/pluto_constants.h
+--- libreswan-3.27-orig/include/pluto_constants.h 2019-02-15 16:31:43.034408076 -0500
++++ libreswan-3.27/include/pluto_constants.h 2019-02-15 16:32:28.974835295 -0500
+@@ -28,6 +28,12 @@
+ # define DEFAULT_DNSSEC_ROOTKEY_FILE ""
+ # endif
+
++enum ike_version {
++ IKEv1 = 1,
++ IKEv2 = 2,
++#define IKE_VERSION_ROOF 3
++};
++
+ /*
+ * IETF has no recommendations
+ * FIPS SP800-77 sayas IKE max is 24h, IPsec max is 8h
+diff -Naur libreswan-3.27-orig/include/proposals.h libreswan-3.27/include/proposals.h
+--- libreswan-3.27-orig/include/proposals.h 1969-12-31 19:00:00.000000000 -0500
++++ libreswan-3.27/include/proposals.h 2019-02-15 16:32:28.975835304 -0500
+@@ -0,0 +1,264 @@
++/* Proposals, for libreswan
++ *
++ * Author: JuanJo Ciarlante
++ * Copyright (C) 2007 Michael Richardson
++ * Copyright (C) 2012-2013 Paul Wouters
++ * Copyright (C) 2013 D. Hugh Redelmeier
++ * Copyright (C) 2013 Paul Wouters
++ * Copyright (C) 2015-2019 Andrew Cagney
++ *
++ * 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.
++ */
++
++#ifndef PROPOSALS_H
++#define PROPOSALS_H
++
++#include "lswcdefs.h"
++#include "constants.h"
++#include "ike_alg.h"
++#include "shunk.h"
++
++struct alg_info;
++struct proposal_protocol;
++struct proposal;
++struct proposals;
++struct proposal_policy;
++struct proposal_parser;
++
++/*
++ * XXX: needs to be merged with IKE_ALG_TYPE.
++ */
++enum proposal_algorithm {
++ PROPOSAL_encrypt,
++ PROPOSAL_prf,
++ PROPOSAL_integ,
++ PROPOSAL_dh,
++ PROPOSAL_ALGORITHM_ROOF,
++};
++
++/*
++ * Everything combined.
++ */
++struct proposal_parser {
++ const struct proposal_protocol *protocol;
++ const struct proposal_param *param;
++ const struct proposal_policy *policy;
++ /* need to eliminate hardwired size */
++ char error[200];
++};
++
++/*
++ * Parameters to tune the parser.
++ */
++
++struct proposal_policy {
++ enum ike_version version;
++ unsigned parser_version;
++ bool pfs; /* For CHILD SA, use DH from IKE SA */
++ bool check_pfs_vs_dh;
++ /*
++ * According to current policy, is the algorithm ok
++ * (supported)? If it isn't return FALSE.
++ *
++ * For instance, an IKE algorithm requires in-process support;
++ * while an ESP/AH algorithm requires kernel support.
++ */
++ bool (*alg_is_ok)(const struct ike_alg *alg);
++ /*
++ * Print a warning. Signature needs to match libreswan_log.
++ */
++ int (*warning)(const char *fmt, ...) PRINTF_LIKE(1);
++};
++
++/*
++ * Defaults the parser uses to fill things in.
++ */
++
++struct proposal_defaults {
++ const struct ike_alg **dh;
++ const struct ike_alg **prf;
++ const struct ike_alg **integ;
++ const struct ike_alg **encrypt;
++};
++
++/*
++ * The protocol - ESP/AH/IKE - the parser is processing.
++ */
++
++typedef const struct ike_alg *(alg_byname_fn)(struct proposal_parser *parser,
++ shunk_t name, size_t key_bit_length,
++ shunk_t print_name);
++
++struct proposal_protocol {
++ const char *name;
++ enum ike_alg_key ikev1_alg_id;
++
++ /*
++ * Lists of defaults for each IKE version.
++ */
++ const struct proposal_defaults *defaults[IKE_VERSION_ROOF];
++
++ /*
++ * Is the proposal OK?
++ *
++ * This is the final check, if this succeeds then the proposal
++ * is added.
++ */
++ bool (*proposal_ok)(struct proposal_parser *parser,
++ const struct proposal *proposal);
++
++ /*
++ * XXX: Is the proto-id needed? Parser should be protocol
++ * agnostic.
++ */
++ unsigned protoid;
++
++ /*
++ * This lookup functions must set err and return null if NAME
++ * isn't valid.
++ */
++ alg_byname_fn *encrypt_alg_byname;
++ alg_byname_fn *prf_alg_byname;
++ alg_byname_fn *integ_alg_byname;
++ alg_byname_fn *dh_alg_byname;
++};
++
++/*
++ * A proposal as decoded by the parser.
++ */
++
++struct algorithm {
++ const struct ike_alg *desc;
++ /*
++ * Because struct encrypt_desc still specifies multiple key
++ * lengths, ENCKEYLEN is still required.
++ */
++ int enckeylen; /* only one! */
++ struct algorithm *next;
++};
++
++/* return counts of encrypt=aead and integ=none */
++bool proposal_encrypt_aead(const struct proposal *proposal);
++bool proposal_encrypt_norm(const struct proposal *proposal);
++bool proposal_integ_none(const struct proposal *proposal);
++
++unsigned nr_proposals(struct proposals *proposals);
++
++extern void proposals_addref(struct proposals **proposals);
++extern void proposals_delref(struct proposals **proposals);
++
++extern struct proposal *alloc_proposal(struct proposal_parser *parser);
++extern void free_proposal(struct proposal **proposal);
++
++void free_algorithms(struct proposal *proposal, enum proposal_algorithm algorithm);
++void append_proposal(struct proposals *proposals, struct proposal **proposal);
++void append_algorithm(struct proposal_parser *parser,
++ struct proposal *proposal, enum proposal_algorithm algorithm,
++ const struct ike_alg *alg, int enckeylen);
++
++struct proposal_parser *alloc_proposal_parser(const struct proposal_policy *policy,
++ const struct proposal_protocol *protocol);
++void free_proposal_parser(struct proposal_parser **parser);
++struct proposal_parser *ike_proposal_parser(const struct proposal_policy *policy);
++struct proposal_parser *esp_proposal_parser(const struct proposal_policy *policy);
++struct proposal_parser *ah_proposal_parser(const struct proposal_policy *policy);
++
++/*
++ * XXX: useful?
++ */
++struct ike_proposals {
++ struct proposals *p;
++};
++
++struct child_proposals {
++ struct proposals *p;
++};
++
++void fmt_proposal(struct lswlog *log,
++ const struct proposal *proposal);
++void fmt_proposals(struct lswlog *log, const struct proposals *proposals);
++
++/*
++ * Iterate through all the proposals and the proposal's algorithms.
++ *
++ * Use __typeof__ instead of const to get around ALG_INFO some times
++ * being const and sometimes not.
++ */
++
++struct proposal *next_proposal(const struct proposals *proposals,
++ struct proposal *last_proposal);
++
++#define FOR_EACH_PROPOSAL(PROPOSALS, PROPOSAL) \
++ for (struct proposal *PROPOSAL = next_proposal(PROPOSALS, NULL); \
++ PROPOSAL != NULL; \
++ PROPOSAL = next_proposal(PROPOSALS, PROPOSAL))
++
++struct algorithm *next_algorithm(const struct proposal *proposal,
++ enum proposal_algorithm algorithm,
++ struct algorithm *last);
++
++#define FOR_EACH_ALGORITHM(PROPOSAL, TYPE, ALGORITHM) \
++ for (struct algorithm *ALGORITHM = next_algorithm(PROPOSAL, PROPOSAL_##TYPE, NULL); \
++ ALGORITHM != NULL; ALGORITHM = next_algorithm(PROPOSAL, PROPOSAL_##TYPE, ALGORITHM))
++
++/*
++ * Error indicated by err_buf[0] != '\0'.
++ *
++ * POLICY should be used to guard algorithm supported checks. For
++ * instance: if POLICY=IKEV1, then IKEv1 support is required (IKEv2 is
++ * don't care); and if POLICY=IKEV1|IKEV2, then both IKEv1 and IKEv2
++ * support is required.
++ *
++ * Parsing with POLICY=IKEV1, but then proposing the result using
++ * IKEv2 is a program error. The IKEv2 should complain loudly and,
++ * we hope, not crash.
++ *
++ * Parsing with POLICY='0' is allowed. It will accept the algorithms
++ * unconditionally (spi.c seems to need this).
++ */
++
++struct proposals *proposals_from_str(struct proposal_parser *parser,
++ const char *str);
++
++bool v1_proposals_parse_str(struct proposal_parser *parser,
++ struct proposals *proposals,
++ shunk_t alg_str);
++bool v2_proposals_parse_str(struct proposal_parser *parser,
++ struct proposals *proposals,
++ shunk_t alg_str);
++
++/*
++ * Check that encrypt==AEAD and/or integ==none don't contradict.
++ */
++bool proposal_aead_none_ok(struct proposal_parser *parser,
++ const struct proposal *proposal);
++
++void proposal_error(struct proposal_parser *parser,
++ const char *message, ...) PRINTF_LIKE(2);
++
++bool impair_proposal_errors(struct proposal_parser *parser);
++
++/*
++ * Convert a generic proposal back into something the IKEv1 code can
++ * digest.
++ */
++struct v1_proposal {
++ int enckeylen;
++ const struct encrypt_desc *encrypt;
++ const struct prf_desc *prf;
++ const struct integ_desc *integ;
++ const struct oakley_group_desc *dh;
++ const struct proposal_protocol *protocol;
++};
++
++struct v1_proposal v1_proposal(const struct proposal *proposal);
++
++#endif /* PROPOSALS_H */
+diff -Naur libreswan-3.27-orig/include/shunk.h libreswan-3.27/include/shunk.h
+--- libreswan-3.27-orig/include/shunk.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/include/shunk.h 2019-02-15 16:32:28.975835304 -0500
+@@ -1,7 +1,6 @@
+-/*
+- * string fragments, for libreswan
++/* constant string (octet) fragments, for libreswan
+ *
+- * Copyright (C) 2018 Andrew Cagney
++ * Copyright (C) 2018-2019 Andrew Cagney
+ *
+ * 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
+@@ -21,8 +20,15 @@
+ #include /* size_t */
+
+ /*
+- * shunk_t is a rip of of chunk_t, but with a character pointer. It
+- * is intended for string slicing.
++ * Think of shunk_t and shunk_t as opposite solutions to the same
++ * problem - carving up streams of octets:
++ *
++ * shunk_t's buffer is constant making it good for manipulating static
++ * constant data (such as "a string"), chunk_t's is not.
++ *
++ * shunk_t's buffer is of type 'char' (which may or may not be signed)
++ * making it easier to manipulate strings, chunk_t's is uint8_t making
++ * it easier to manipulate raw bytes.
+ */
+
+ struct shunk {
+@@ -32,21 +38,49 @@
+
+ typedef struct shunk shunk_t;
+
+-extern const shunk_t empty_shunk;
++/*
++ * Just like for strings, an empty or zero length shunk such as
++ * {.ptr="",.len = 0} should not be confused with the NULL shunk
++ * (i.e., {.ptr=NULL,.len=0}).
++ *
++ * Use 'null_shunk' in initialisers. The only exception is static
++ * initializers - which will get a compiler error - and NULL_SHUNK can
++ * be used.
++ */
++
++#define NULL_SHUNK { .ptr = NULL, .len = 0, }
++extern const shunk_t null_shunk;
+
+ shunk_t shunk1(const char *ptr); /* strlen() implied */
+ shunk_t shunk2(const char *ptr, int len);
+
+ /*
+- * shunk version of strsep() (which is like strtok())
++ * A shunk version of strsep() (which is like strtok()) - split INPUT
++ * in two using a character from the DELIM set.
++ *
++ * If INPUT contains a character from the DELIM set, return the
++ * characters before the DELIM character as the next TOKEN, and set
++ * INPUT to the sub-string following the DELIM character.
++ *
++ * If INPUT contains no character from the DELIM set, return INPUT as
++ * the next TOKEN (possibly empty), and set INPUT to the null_shunk.
++ *
++ * If INPUT is the null_shunk, return the null_shunk as the next
++ * TOKEN, string remains unchanged (still the null_shunk).
++ *
++ * One way to implement a simple parser is to use TOKEN.ptr==NULL as
++ * an end-of-input indicator:
+ *
+- * Split SHUNK in two using the DELIM set. Return a shunk of the
+- * characters up to but not including DELIM (or the entire string if
+- * DELIM isn't found. Update SHUNK to be one past DELIM.
++ * shunk_t token = shunk_strsep(&input, ",");
++ * while (token.ptr != NULL) {
++ * ... process token ...
++ * token = shunk_strsep(&input, ",");
++ * }
+ *
+- * XXX: should this return the DELIM?
++ * XXX: Provided INPUT.ptr is non-NULL, INPUT.ptr[-1] is the DELIM
++ * character; should this be made an explict parameter.
+ */
+-shunk_t shunk_strsep(shunk_t *shunk, const char *delim);
++shunk_t shunk_strsep(shunk_t *input, const char *delim);
+
+ /*
+ * shunk version of string compare functions (or at least libreswan's
+diff -Naur libreswan-3.27-orig/lib/libswan/ah_info.c libreswan-3.27/lib/libswan/ah_info.c
+--- libreswan-3.27-orig/lib/libswan/ah_info.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/ah_info.c 2019-02-15 16:50:12.776728076 -0500
+@@ -22,50 +22,71 @@
+
+ #include "lswalloc.h"
+ #include "lswlog.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "alg_byname.h"
+ #include "lswfips.h"
+
+ #include "ike_alg.h"
+ #include "ike_alg_integ.h"
+
+-static bool ah_proposal_ok(const struct proposal_parser *parser,
+- const struct proposal_info *proposal)
++static bool ah_proposal_ok(struct proposal_parser *parser,
++ const struct proposal *proposal)
+ {
+- impaired_passert(PROPOSAL_PARSER, proposal->encrypt == NULL);
+- impaired_passert(PROPOSAL_PARSER, proposal->prf == NULL);
+- impaired_passert(PROPOSAL_PARSER, proposal->integ != NULL);
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_encrypt, NULL) == NULL);
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_prf, NULL) == NULL);
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_integ, NULL) != NULL);
+
+ /* ah=null is invalid */
+- if (!IMPAIR(ALLOW_NULL_NONE) &&
+- proposal->integ == &ike_alg_integ_none) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "AH cannot have 'none' as the integrity algorithm");
+- if (!impair_proposal_errors(parser)) {
+- return false;
++ if (!IMPAIR(ALLOW_NULL_NONE)) {
++ FOR_EACH_ALGORITHM(proposal, integ, alg) {
++ /* passerts */
++ const struct integ_desc *integ = integ_desc(alg->desc);
++ if (integ == &ike_alg_integ_none) {
++ proposal_error(parser, "AH cannot have 'none' as the integrity algorithm");
++ if (!impair_proposal_errors(parser)) {
++ return false;
++ }
++ }
+ }
+ }
+
+ return true;
+ }
+
+-static const struct ike_alg *default_ah_integ[] = {
++static const struct ike_alg *default_v1_ah_integ[] = {
+ #ifdef USE_SHA1
+ &ike_alg_integ_sha1.common,
+ #endif
+ NULL,
+ };
+
+-const struct proposal_defaults ah_defaults = {
+- .integ = default_ah_integ,
++static const struct ike_alg *default_v2_ah_integ[] = {
++#ifdef USE_SHA2
++ &ike_alg_integ_sha2_512.common,
++ &ike_alg_integ_sha2_256.common,
++#endif
++ NULL,
++};
++
++const struct proposal_defaults v1_ah_defaults = {
++ .integ = default_v1_ah_integ,
++};
++
++const struct proposal_defaults v2_ah_defaults = {
++ .integ = default_v2_ah_integ,
+ };
+
+ const struct proposal_protocol ah_proposal_protocol = {
+ .name = "AH",
+ .ikev1_alg_id = IKEv1_ESP_ID,
+ .protoid = PROTO_IPSEC_AH,
+- .ikev1_defaults = &ah_defaults,
+- .ikev2_defaults = &ah_defaults,
++ .defaults = {
++ [IKEv1] = &v1_ah_defaults,
++ [IKEv2] = &v2_ah_defaults,
++ },
+ .proposal_ok = ah_proposal_ok,
+ .integ_alg_byname = integ_alg_byname,
+ .dh_alg_byname = dh_alg_byname,
+@@ -77,7 +98,7 @@
+ * parser configuration - encryption isn't allowed.
+ *
+ * ??? the only difference between
+- * alg_info_ah_create_from_str and alg_info_esp_create_from_str
++ * ah_proposals_create_from_str and alg_info_esp_create_from_str
+ * is in the second argument to proposal_parser.
+ *
+ * XXX: On the other hand, since "struct ike_info" and "struct
+@@ -87,33 +108,7 @@
+
+ /* This function is tested in testing/algparse/algparse.c */
+
+-struct alg_info_esp *alg_info_ah_create_from_str(const struct proposal_policy *policy,
+- const char *alg_str,
+- char *err_buf, size_t err_buf_len)
++struct proposal_parser *ah_proposal_parser(const struct proposal_policy *policy)
+ {
+- shunk_t string = shunk1(alg_str);
+- const struct proposal_parser parser = proposal_parser(policy,
+- &ah_proposal_protocol,
+- err_buf, err_buf_len);
+-
+- /*
+- * alg_info storage should be sized dynamically
+- * but this may require two passes to know
+- * transform count in advance.
+- */
+- struct alg_info_esp *alg_info_ah = alloc_thing(struct alg_info_esp, "alg_info_ah");
+-
+- if (!alg_info_parse_str(&parser, &alg_info_ah->ai, string)) {
+- passert(err_buf[0] != '\0');
+- alg_info_free(&alg_info_ah->ai);
+- return NULL;
+- }
+-
+- if (!alg_info_pfs_vs_dh_check(&parser, alg_info_ah)) {
+- passert(err_buf[0] != '\0');
+- alg_info_free(&alg_info_ah->ai);
+- return NULL;
+- }
+-
+- return alg_info_ah;
++ return alloc_proposal_parser(policy, &ah_proposal_protocol);
+ }
+diff -Naur libreswan-3.27-orig/lib/libswan/alg_byname.c libreswan-3.27/lib/libswan/alg_byname.c
+--- libreswan-3.27-orig/lib/libswan/alg_byname.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/alg_byname.c 2019-02-15 16:32:28.976835314 -0500
+@@ -17,11 +17,11 @@
+ #include
+
+ #include "lswlog.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "alg_byname.h"
+ #include "ike_alg.h"
+
+-bool alg_byname_ok(const struct proposal_parser *parser,
++bool alg_byname_ok(struct proposal_parser *parser,
+ const struct ike_alg *alg, shunk_t print_name)
+ {
+ const struct proposal_protocol *protocol = parser->protocol;
+@@ -30,19 +30,27 @@
+ * If the connection is IKEv1|IKEv2 then this code will
+ * exclude anything not supported by both protocols.
+ */
+- if (policy->ikev1 && alg->id[protocol->ikev1_alg_id] < 0) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv1",
+- protocol->name, ike_alg_type_name(alg->algo_type),
+- PRI_shunk(print_name));
+- return false;
+- }
+- if (policy->ikev2 && alg->id[IKEv2_ALG_ID] < 0) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv2",
+- protocol->name, ike_alg_type_name(alg->algo_type),
+- PRI_shunk(print_name));
+- return false;
++ switch (policy->version) {
++ case IKEv1:
++ /* IKEv1 has different IDs for ESP/IKE/AH */
++ if (alg->id[protocol->ikev1_alg_id] < 0) {
++ proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv1",
++ protocol->name, ike_alg_type_name(alg->algo_type),
++ PRI_shunk(print_name));
++ return false;
++ }
++ break;
++ case IKEv2:
++ if (alg->id[IKEv2_ALG_ID] < 0) {
++ proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported by IKEv2",
++ protocol->name, ike_alg_type_name(alg->algo_type),
++ PRI_shunk(print_name));
++ return false;
++ }
++ break;
++ default:
++ /* ignore */
++ break;
+ }
+ /*
+ * According to parser policy, is the algorithm "implemented"?
+@@ -54,10 +62,9 @@
+ */
+ passert(policy->alg_is_ok != NULL);
+ if (!policy->alg_is_ok(alg)) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s %s algorithm '"PRI_SHUNK"' is not supported",
+- protocol->name, ike_alg_type_name(alg->algo_type),
+- PRI_shunk(print_name));
++ proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported",
++ protocol->name, ike_alg_type_name(alg->algo_type),
++ PRI_shunk(print_name));
+ return false;
+ }
+ /*
+@@ -69,16 +76,15 @@
+ * Since it likely involves a lookup, it is left until last.
+ */
+ if (!ike_alg_is_valid(alg)) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s %s algorithm '"PRI_SHUNK"' is not valid",
+- protocol->name, ike_alg_type_name(alg->algo_type),
+- PRI_shunk(print_name));
++ proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not valid",
++ protocol->name, ike_alg_type_name(alg->algo_type),
++ PRI_shunk(print_name));
+ return false;
+ }
+ return true;
+ }
+
+-static const struct ike_alg *alg_byname(const struct proposal_parser *parser,
++static const struct ike_alg *alg_byname(struct proposal_parser *parser,
+ const struct ike_alg_type *type,
+ shunk_t name, shunk_t print_name)
+ {
+@@ -91,15 +97,14 @@
+ */
+ if (ike_alg_enum_match(type, protocol->ikev1_alg_id, name) >= 0 ||
+ ike_alg_enum_match(type, IKEv2_ALG_ID, name) >= 0) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s %s algorithm '"PRI_SHUNK"' is not supported",
+- protocol->name, ike_alg_type_name(type),
+- PRI_shunk(print_name));
++ proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not supported",
++ protocol->name, ike_alg_type_name(type),
++ PRI_shunk(print_name));
++
+ } else {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s %s algorithm '"PRI_SHUNK"' is not recognized",
+- protocol->name, ike_alg_type_name(type),
+- PRI_shunk(print_name));
++ proposal_error(parser, "%s %s algorithm '"PRI_SHUNK"' is not recognized",
++ protocol->name, ike_alg_type_name(type),
++ PRI_shunk(print_name));
+ }
+ return NULL;
+ }
+@@ -108,14 +113,14 @@
+ * Does it pass muster?
+ */
+ if (!alg_byname_ok(parser, alg, print_name)) {
+- passert(parser->err_buf[0] != '\0');
++ passert(parser->error[0] != '\0');
+ return NULL;
+ }
+
+ return alg;
+ }
+
+-const struct ike_alg *encrypt_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *encrypt_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length,
+ shunk_t print_name)
+ {
+@@ -127,10 +132,9 @@
+ const struct encrypt_desc *encrypt = encrypt_desc(alg);
+ if (!IMPAIR(SEND_KEY_SIZE_CHECK) && key_bit_length > 0) {
+ if (encrypt->keylen_omitted) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s does not take variable key lengths",
+- enum_short_name(&ikev2_trans_type_encr_names,
+- encrypt->common.id[IKEv2_ALG_ID]));
++ proposal_error(parser, "%s does not take variable key lengths",
++ enum_short_name(&ikev2_trans_type_encr_names,
++ encrypt->common.id[IKEv2_ALG_ID]));
+ if (!impair_proposal_errors(parser)) {
+ return NULL;
+ }
+@@ -141,8 +145,7 @@
+ * should instead generate a real list from
+ * encrypt.
+ */
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "wrong encryption key length - key size must be 128 (default), 192 or 256");
++ proposal_error(parser, "wrong encryption key length - key size must be 128 (default), 192 or 256");
+ if (!impair_proposal_errors(parser)) {
+ return NULL;
+ }
+@@ -151,21 +154,21 @@
+ return alg;
+ }
+
+-const struct ike_alg *prf_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *prf_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length UNUSED,
+ shunk_t print_name)
+ {
+ return alg_byname(parser, IKE_ALG_PRF, name, print_name);
+ }
+
+-const struct ike_alg *integ_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *integ_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length UNUSED,
+ shunk_t print_name)
+ {
+ return alg_byname(parser, IKE_ALG_INTEG, name, print_name);
+ }
+
+-const struct ike_alg *dh_alg_byname(const struct proposal_parser *parser,
++const struct ike_alg *dh_alg_byname(struct proposal_parser *parser,
+ shunk_t name, size_t key_bit_length UNUSED,
+ shunk_t print_name)
+ {
+diff -Naur libreswan-3.27-orig/lib/libswan/alg_info.c libreswan-3.27/lib/libswan/alg_info.c
+--- libreswan-3.27-orig/lib/libswan/alg_info.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/alg_info.c 1969-12-31 19:00:00.000000000 -0500
+@@ -1,884 +0,0 @@
+-/*
+- * Algorithm info parsing and creation functions
+- * Author: JuanJo Ciarlante
+- *
+- * Copyright (C) 2012 Paul Wouters
+- * Copyright (C) 2015-2018 Andrew Cagney
+- *
+- * 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 "lswlog.h"
+-#include "lswalloc.h"
+-#include "constants.h"
+-#include "alg_info.h"
+-#include "ike_alg.h"
+-#include "ike_alg_integ.h"
+-#include "ike_alg_dh.h"
+-#include "alg_byname.h"
+-
+-/*
+- * Add the proposal defaults for the specific algorithm.
+- */
+-
+-typedef struct proposal_info merge_alg_default_t(struct proposal_info proposal,
+- const struct ike_alg *default_alg);
+-
+-static struct proposal_info merge_dh_default(struct proposal_info proposal,
+- const struct ike_alg *default_alg)
+-{
+- proposal.dh = oakley_group_desc(default_alg);
+- return proposal;
+-}
+-
+-static struct proposal_info merge_encrypt_default(struct proposal_info proposal,
+- const struct ike_alg *default_alg)
+-{
+- proposal.encrypt = encrypt_desc(default_alg);
+- return proposal;
+-}
+-
+-static struct proposal_info merge_prf_default(struct proposal_info proposal,
+- const struct ike_alg *default_alg)
+-{
+- proposal.prf = prf_desc(default_alg);
+- return proposal;
+-}
+-
+-static struct proposal_info merge_integ_default(struct proposal_info proposal,
+- const struct ike_alg *default_alg)
+-{
+- proposal.integ = integ_desc(default_alg);
+- return proposal;
+-}
+-
+-static bool add_proposal_defaults(const struct proposal_parser *parser,
+- const struct proposal_defaults *defaults,
+- struct alg_info *alg_info,
+- const struct proposal_info *proposal);
+-
+-static bool add_alg_defaults(const struct proposal_parser *parser,
+- const struct proposal_defaults *defaults,
+- struct alg_info *alg_info,
+- const struct proposal_info *proposal,
+- const struct ike_alg_type *type,
+- const struct ike_alg **default_algs,
+- merge_alg_default_t *merge_alg_default)
+-{
+- /*
+- * Use VALID_ALG to add the valid algorithms into VALID_ALGS.
+- */
+- for (const struct ike_alg **default_alg = default_algs;
+- *default_alg; default_alg++) {
+- const struct ike_alg *alg = *default_alg;
+- if (!alg_byname_ok(parser, alg,
+- shunk1(alg->name))) {
+- DBG(DBG_PROPOSAL_PARSER,
+- DBG_log("skipping default %s",
+- parser->err_buf));
+- parser->err_buf[0] = '\0';
+- continue;
+- }
+- /* add it */
+- DBG(DBG_PROPOSAL_PARSER,
+- DBG_log("adding default %s %s",
+- ike_alg_type_name(type),
+- alg->name));
+- struct proposal_info merged_proposal = merge_alg_default(*proposal,
+- *default_alg);
+- if (!add_proposal_defaults(parser, defaults,
+- alg_info, &merged_proposal)) {
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- }
+- return true;
+-}
+-
+-/*
+- * Validate the proposal and, suppressing duplicates, add it to the
+- * proposal list.
+- */
+-
+-static bool add_proposal(const struct proposal_parser *parser,
+- struct alg_info *alg_info,
+- const struct proposal_info *proposal)
+-{
+- /* duplicate? */
+- FOR_EACH_PROPOSAL_INFO(alg_info, existing_proposal) {
+- /*
+- * key length 0 is like a wild-card (it actually means
+- * propose default and strongest key lengths) so if
+- * either is zero just treat it as a match.
+- */
+- if (existing_proposal->encrypt == proposal->encrypt &&
+- existing_proposal->prf == proposal->prf &&
+- existing_proposal->integ == proposal->integ &&
+- existing_proposal->dh == proposal->dh &&
+- (existing_proposal->enckeylen == proposal->enckeylen ||
+- existing_proposal->enckeylen == 0 ||
+- proposal->enckeylen == 0)) {
+- if (IMPAIR(PROPOSAL_PARSER)) {
+- libreswan_log("IMPAIR: including duplicate %s proposal encrypt=%s enckeylen=%zu prf=%s integ=%s dh=%s",
+- proposal->protocol->name,
+- proposal->encrypt != NULL ? proposal->encrypt->common.name : "n/a",
+- proposal->enckeylen,
+- proposal->prf != NULL ? proposal->prf->common.name : "n/a",
+- proposal->integ != NULL ? proposal->integ->common.name : "n/a",
+- proposal->dh != NULL ? proposal->dh->common.name : "n/a");
+- } else {
+- DBG(DBG_CRYPT,
+- DBG_log("discarding duplicate %s proposal encrypt=%s enckeylen=%zu prf=%s integ=%s dh=%s",
+- proposal->protocol->name,
+- proposal->encrypt != NULL ? proposal->encrypt->common.name : "n/a",
+- proposal->enckeylen,
+- proposal->prf != NULL ? proposal->prf->common.name : "n/a",
+- proposal->integ != NULL ? proposal->integ->common.name : "n/a",
+- proposal->dh != NULL ? proposal->dh->common.name : "n/a"));
+- return true;
+- }
+- }
+- }
+-
+- /* Overflow? */
+- if ((unsigned)alg_info->alg_info_cnt >= elemsof(alg_info->proposals)) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "more than %zu %s algorithms specified",
+- elemsof(alg_info->proposals),
+- proposal->protocol->name);
+- /* drop it like a rock */
+- return false;
+- }
+-
+- /* back end? */
+- if (!proposal->protocol->proposal_ok(parser, proposal)) {
+- return false;
+- }
+-
+- alg_info->proposals[alg_info->alg_info_cnt++] = *proposal;
+- return true;
+-}
+-
+-/*
+- * For all the algorithms, when an algorithm is missing (NULL), and
+- * there are defaults, add them.
+- */
+-
+-static bool add_proposal_defaults(const struct proposal_parser *parser,
+- const struct proposal_defaults *defaults,
+- struct alg_info *alg_info,
+- const struct proposal_info *proposal)
+-{
+- /*
+- * Note that the order in which things are recursively added -
+- * MODP, ENCR, PRF/HASH - affects test results. It determines
+- * things like the order of proposals.
+- */
+- if (proposal->dh == NULL &&
+- defaults != NULL && defaults->dh != NULL) {
+- return add_alg_defaults(parser, defaults,
+- alg_info, proposal,
+- &ike_alg_dh, defaults->dh,
+- merge_dh_default);
+- } else if (proposal->encrypt == NULL &&
+- defaults != NULL && defaults->encrypt != NULL) {
+- return add_alg_defaults(parser, defaults,
+- alg_info, proposal,
+- &ike_alg_encrypt, defaults->encrypt,
+- merge_encrypt_default);
+- } else if (proposal->prf == NULL &&
+- defaults != NULL && defaults->prf != NULL) {
+- return add_alg_defaults(parser, defaults,
+- alg_info, proposal,
+- &ike_alg_prf, defaults->prf,
+- merge_prf_default);
+- } else if (proposal->integ == NULL &&
+- proposal->encrypt != NULL &&
+- encrypt_desc_is_aead(proposal->encrypt)) {
+- /*
+- * Since AEAD, integrity is always 'none'.
+- */
+- struct proposal_info merged_proposal = *proposal;
+- merged_proposal.integ = &ike_alg_integ_none;
+- return add_proposal_defaults(parser, defaults,
+- alg_info, &merged_proposal);
+- } else if (proposal->integ == NULL &&
+- defaults != NULL && defaults->integ != NULL) {
+- return add_alg_defaults(parser, defaults,
+- alg_info, proposal,
+- &ike_alg_integ, defaults->integ,
+- merge_integ_default);
+- } else if (proposal->integ == NULL &&
+- proposal->prf != NULL &&
+- proposal->encrypt != NULL &&
+- !encrypt_desc_is_aead(proposal->encrypt)) {
+- /*
+- * Since non-AEAD, use an integrity algorithm that is
+- * implemented using the PRF.
+- */
+- struct proposal_info merged_proposal = *proposal;
+- for (const struct integ_desc **algp = next_integ_desc(NULL);
+- algp != NULL; algp = next_integ_desc(algp)) {
+- const struct integ_desc *alg = *algp;
+- if (alg->prf == proposal->prf) {
+- merged_proposal.integ = alg;
+- break;
+- }
+- }
+- if (merged_proposal.integ == NULL) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "%s integrity derived from PRF '%s' is not supported",
+- proposal->protocol->name,
+- proposal->prf->common.name);
+- return false;
+- }
+- return add_proposal_defaults(parser, defaults,
+- alg_info, &merged_proposal);
+- } else {
+- return add_proposal(parser, alg_info, proposal);
+- }
+-}
+-
+-static bool merge_default_proposals(const struct proposal_parser *parser,
+- struct alg_info *alg_info,
+- const struct proposal_info *proposal)
+-{
+- /*
+- * If there's a hint of IKEv1 being enabled then prefer its
+- * larger set of defaults.
+- *
+- * This should increase the odds of both ends interoperating.
+- *
+- * For instance, the IKEv2 defaults were preferred and one end
+- * has ikev2=never then, in aggressive mode, things don't
+- * work.
+- */
+- const struct proposal_defaults *defaults = (parser->policy->ikev1
+- ? proposal->protocol->ikev1_defaults
+- : proposal->protocol->ikev2_defaults);
+- return add_proposal_defaults(parser, defaults,
+- alg_info, proposal);
+-}
+-
+-static const struct ike_alg *lookup_byname(const struct proposal_parser *parser,
+- alg_byname_fn *alg_byname,
+- shunk_t name,
+- size_t key_bit_length,
+- shunk_t print_name,
+- const char *what)
+-{
+- if (name.len > 0) {
+- if (alg_byname != NULL) {
+- const struct ike_alg *alg = alg_byname(parser, name, key_bit_length,
+- print_name);
+- if (alg == NULL) {
+- DBG(DBG_PROPOSAL_PARSER,
+- DBG_log("%s_byname('"PRI_SHUNK"') failed: %s",
+- what, PRI_shunk(name),
+- parser->err_buf));
+- passert(parser->err_buf[0] != '\0');
+- return NULL;
+- }
+- DBG(DBG_PROPOSAL_PARSER,
+- DBG_log("%s_byname('"PRI_SHUNK"') returned '%s'",
+- what, PRI_shunk(name), alg->name));
+- return alg;
+- } else {
+- DBG(DBG_PROPOSAL_PARSER,
+- DBG_log("ignoring %s '"PRI_SHUNK"'",
+- what, PRI_shunk(name)));
+- return NULL;
+- }
+- }
+- return NULL;
+-}
+-
+-static int parse_eklen(char *err_buf, size_t err_buf_len,
+- shunk_t buf)
+-{
+- /* convert - if present */
+- char *end = NULL;
+- long eklen = strtol(buf.ptr, &end, 10);
+- if (buf.ptr + buf.len != end) {
+- snprintf(err_buf, err_buf_len,
+- "encryption key length '"PRI_SHUNK"' contains a non-numeric character",
+- PRI_shunk(buf));
+- return 0;
+- }
+- if (eklen >= INT_MAX) {
+- snprintf(err_buf, err_buf_len,
+- "encryption key length '"PRI_SHUNK"' WAY too big",
+- PRI_shunk(buf));
+- return 0;
+- }
+- if (eklen == 0) {
+- snprintf(err_buf, err_buf_len,
+- "encryption key length is zero");
+- return 0;
+- }
+- return eklen;
+-}
+-
+-/*
+- * Try to parse any of -, _,
+- * , or . Strings like aes_gcm_16 and
+- * aes_gcm_16_256 end up in alg[0], while strings like aes_gcm_16-256
+- * end up in alg[0]-alg[1].
+- */
+-
+-struct token {
+- char sep;
+- shunk_t alg;
+-};
+-
+-static bool parse_encrypt(const struct proposal_parser *parser,
+- struct token **tokens,
+- struct proposal_info *proposal)
+-{
+- shunk_t ealg = (*tokens)[0].alg;
+- shunk_t eklen = (*tokens)[1].alg;
+- if (eklen.len > 0 && isdigit(eklen.ptr[0])) {
+- /* assume - */
+- int enckeylen = parse_eklen(parser->err_buf,
+- parser->err_buf_len,
+- eklen);
+- if (enckeylen <= 0) {
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- /* print - */
+- shunk_t print_name = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr);
+- proposal->enckeylen = enckeylen;
+- proposal->encrypt =
+- encrypt_desc(lookup_byname(parser,
+- encrypt_alg_byname,
+- ealg, proposal->enckeylen,
+- print_name, "encryption"));
+- /* Was - rejected? */
+- if (parser->err_buf[0] != '\0') {
+- return false;
+- }
+- *tokens += 2; /* consume both tokens */
+- return true;
+- }
+- /* try */
+- shunk_t print_name = ealg;
+- proposal->encrypt =
+- encrypt_desc(lookup_byname(parser,
+- encrypt_alg_byname,
+- ealg, proposal->enckeylen,
+- print_name, "encryption"));
+- if (parser->err_buf[0] != '\0') {
+- /*
+- * Could it be or _? Work
+- * backwards skipping any digits.
+- */
+- shunk_t end = shunk2(ealg.ptr + ealg.len, 0);
+- while (end.ptr > ealg.ptr && isdigit(end.ptr[-1])) {
+- end.ptr--;
+- end.len++;
+- }
+- if (end.len == 0) {
+- /*
+- * no trailing and was rejected
+- */
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- /* try to convert */
+- int enckeylen = parse_eklen(parser->err_buf, parser->err_buf_len, end);
+- if (enckeylen <= 0) {
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- proposal->enckeylen = enckeylen;
+- /*
+- * trim from ; and then trim any
+- * trailing '_'
+- */
+- ealg.len = end.ptr - ealg.ptr;
+- if (end.ptr > ealg.ptr && end.ptr[-1] == '_') {
+- ealg.len -= 1;
+- }
+- /* try again */
+- parser->err_buf[0] = '\0';
+- proposal->encrypt =
+- encrypt_desc(lookup_byname(parser,
+- encrypt_alg_byname,
+- ealg, proposal->enckeylen,
+- print_name, "encryption"));
+- if (parser->err_buf[0] != '\0') {
+- return false;
+- }
+- }
+- *tokens += 1; /* consume one token */
+- return true;
+-}
+-
+-static bool parser_alg_info_add(const struct proposal_parser *parser,
+- struct token *tokens, struct proposal_info proposal,
+- struct alg_info *alg_info)
+-{
+- LSWDBGP(DBG_PROPOSAL_PARSER, buf) {
+- lswlogs(buf, "algs:");
+- for (struct token *token = tokens; token->alg.ptr != NULL; token++) {
+- lswlogf(buf, " algs[%tu] = '"PRI_SHUNK"'",
+- token - tokens, PRI_shunk(token->alg));
+- }
+- }
+-
+- bool lookup_encrypt = parser->protocol->encrypt_alg_byname != NULL;
+- if (!lookup_encrypt && IMPAIR(PROPOSAL_PARSER)) {
+- /* Force lookup, will discard any error. */
+- lookup_encrypt = true;
+- }
+- if (lookup_encrypt && tokens->alg.ptr != NULL && tokens->sep != ';') {
+- if (!parse_encrypt(parser, &tokens, &proposal)) {
+- if (IMPAIR(PROPOSAL_PARSER)) {
+- /* ignore the lookup and stumble on */
+- parser->err_buf[0] = '\0';
+- } else {
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- }
+- }
+-
+- bool lookup_prf = parser->protocol->prf_alg_byname != NULL;
+- if (!lookup_prf && IMPAIR(PROPOSAL_PARSER)) {
+- /*
+- * Force PRF lookup when the folloing token looks like
+- * an INTEG algorithm (i.e., its lookup succeeds).
+- * Otherwise something like ah=sha1 gets parsed as
+- * ah=[encr]-sha1-[integ]-[dh].
+- */
+- shunk_t prf = tokens[0].alg;
+- shunk_t integ = tokens[1].alg;
+- if (prf.ptr != NULL && integ.ptr != NULL) {
+- lookup_prf = (lookup_byname(parser, integ_alg_byname,
+- integ, 0, integ, "integrity")
+- != NULL);
+- parser->err_buf[0] = '\0';
+- }
+- }
+- if (lookup_prf && tokens->alg.ptr != NULL && tokens->sep != ';') {
+- shunk_t prf = tokens[0].alg;
+- proposal.prf = prf_desc(lookup_byname(parser,
+- prf_alg_byname,
+- prf, 0, prf, "PRF"));
+- if (parser->err_buf[0] != '\0') {
+- return false;
+- }
+- tokens += 1; /* consume one arg */
+- }
+-
+- bool lookup_integ = parser->protocol->integ_alg_byname != NULL;
+- if (!lookup_integ && IMPAIR(PROPOSAL_PARSER)) {
+- /* force things */
+- lookup_integ = true;
+- }
+- if (lookup_integ && tokens->alg.ptr != NULL && tokens->sep != ';') {
+- shunk_t integ = tokens[0].alg;
+- proposal.integ = integ_desc(lookup_byname(parser,
+- integ_alg_byname,
+- integ, 0, integ, "integrity"));
+- if (parser->err_buf[0] != '\0') {
+- if (tokens[1].alg.ptr != NULL) {
+- /*
+- * This alg should have been
+- * integrity, since the next would be
+- * DH; error applies.
+- */
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- if (tokens[1].alg.ptr == NULL &&
+- parser->protocol->prf_alg_byname == NULL) {
+- /*
+- * Only one arg, integrity is prefered
+- * to DH (and no PRF); error applies.
+- */
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- /* let DH try */
+- parser->err_buf[0] = '\0';
+- } else {
+- tokens += 1; /* consume one arg */
+- }
+- }
+-
+- bool lookup_dh = parser->protocol->dh_alg_byname || IMPAIR(PROPOSAL_PARSER);
+- if (lookup_dh && tokens->alg.ptr != NULL) {
+- shunk_t dh = tokens[0].alg;
+- proposal.dh = oakley_group_desc(lookup_byname(parser,
+- dh_alg_byname,
+- dh, 0,
+- dh, "DH"));
+- if (parser->err_buf[0] != '\0') {
+- return false;
+- }
+- tokens += 1; /* consume one arg */
+- }
+-
+- if (tokens->alg.ptr != NULL) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "'"PRI_SHUNK"' unexpected",
+- PRI_shunk(tokens[0].alg));
+- return false;
+- }
+-
+- if (IMPAIR(PROPOSAL_PARSER)) {
+- return add_proposal(parser, alg_info, &proposal);
+- } else {
+- return merge_default_proposals(parser, alg_info, &proposal);
+- }
+-}
+-
+-
+-bool alg_info_parse_str(const struct proposal_parser *parser,
+- struct alg_info *alg_info,
+- shunk_t alg_str)
+-{
+- DBG(DBG_PROPOSAL_PARSER,
+- DBG_log("parsing '"PRI_SHUNK"' for %s",
+- PRI_shunk(alg_str), parser->protocol->name));
+-
+- /* use default if no string */
+- if (alg_str.ptr == NULL) {
+- const struct proposal_info proposal = {
+- .protocol = parser->protocol,
+- };
+- return merge_default_proposals(parser, alg_info, &proposal);
+- }
+-
+- if (alg_str.len == 0) {
+- /* XXX: hack to keep testsuite happy */
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "String ended with invalid char, just after \"\"");
+- return false;
+- }
+-
+- shunk_t prop_ptr = alg_str;
+- do {
+- /* find the next proposal */
+- shunk_t prop = shunk_strsep(&prop_ptr, ",");
+- /* parse it */
+- struct token tokens[8];
+- zero(&tokens);
+- struct token *token = tokens;
+- char last_sep = '\0';
+- shunk_t alg_ptr = prop;
+- do {
+- if (token + 1 >= tokens+elemsof(tokens)) {
+- /* space for NULL? */
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "proposal too long");
+- return false;
+- }
+- /* find the next alg */
+- shunk_t alg = shunk_strsep(&alg_ptr, "-;,");
+- *token++ = (struct token) {
+- .alg = alg,
+- .sep = last_sep,
+- };
+- last_sep = alg.ptr[alg.len]; /* save separator */
+- } while (alg_ptr.len > 0);
+- struct proposal_info proposal = {
+- .protocol = parser->protocol,
+- };
+- if (!parser_alg_info_add(parser, tokens, proposal,
+- alg_info)) {
+- passert(parser->err_buf[0] != '\0');
+- return false;
+- }
+- } while (prop_ptr.len > 0);
+- return true;
+-}
+-
+-struct proposal_parser proposal_parser(const struct proposal_policy *policy,
+- const struct proposal_protocol *protocol,
+- char *err_buf, size_t err_buf_len)
+-{
+- const struct proposal_parser parser = {
+- .policy = policy,
+- .protocol = protocol,
+- .err_buf = err_buf,
+- .err_buf_len = err_buf_len,
+- };
+- err_buf[0] = '\0';
+- return parser;
+-}
+-
+-bool proposal_aead_none_ok(const struct proposal_parser *parser,
+- const struct proposal_info *proposal)
+-{
+- if (IMPAIR(ALLOW_NULL_NONE)) {
+- return true;
+- }
+-
+- if (proposal->encrypt != NULL &&
+- encrypt_desc_is_aead(proposal->encrypt) &&
+- proposal->integ != NULL &&
+- proposal->integ != &ike_alg_integ_none) {
+- /*
+- * For instance, esp=aes_gcm-sha1" is invalid.
+- */
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "AEAD %s encryption algorithm '%s' must have 'none' as the integrity algorithm",
+- proposal->protocol->name,
+- proposal->encrypt->common.name);
+- return false;
+- }
+-
+- if (proposal->encrypt != NULL &&
+- !encrypt_desc_is_aead(proposal->encrypt) &&
+- proposal->integ != NULL &&
+- proposal->integ == &ike_alg_integ_none) {
+- /*
+- * For instance, esp=aes_cbc-none" is invalid.
+- */
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "non-AEAD %s encryption algorithm '%s' cannot have 'none' as the integrity algorithm",
+- proposal->protocol->name,
+- proposal->encrypt->common.name);
+- return false;
+- }
+-
+- return true;
+-}
+-
+-/*
+- * alg_info struct can be shared by several connections instances,
+- * handle free() with ref_cnts.
+- *
+- * Use alg_info_free() if the value returned by *_parse_str() is found
+- * to be (semantically) bogus.
+- */
+-
+-void alg_info_free(struct alg_info *alg_info)
+-{
+- passert(alg_info);
+- passert(alg_info->ref_cnt == 0);
+- pfree(alg_info);
+-}
+-
+-void alg_info_addref(struct alg_info *alg_info)
+-{
+- alg_info->ref_cnt++;
+-}
+-
+-void alg_info_delref(struct alg_info *alg_info)
+-{
+- passert(alg_info->ref_cnt != 0);
+- alg_info->ref_cnt--;
+- if (alg_info->ref_cnt == 0)
+- alg_info_free(alg_info);
+-}
+-
+-size_t lswlog_proposal_info(struct lswlog *log,
+- const struct proposal_info *proposal)
+-{
+- size_t size = 0;
+- const char *sep = "";
+-
+- if (proposal->encrypt != NULL) {
+- size += lswlogs(log, sep); sep = "-";
+- size += lswlogs(log, proposal->encrypt->common.fqn);
+- if (proposal->enckeylen != 0) {
+- size += lswlogf(log, "_%zd", proposal->enckeylen);
+- }
+- } else if (IMPAIR(PROPOSAL_PARSER)) {
+- size += lswlogs(log, sep); sep = "-";
+- size += lswlogs(log, "[ENCRYPT]");
+- }
+-
+- if (proposal->prf != NULL) {
+- size += lswlogs(log, sep); sep = "-";
+- size += lswlogs(log, proposal->prf->common.fqn);
+- } else if (IMPAIR(PROPOSAL_PARSER)) {
+- size += lswlogs(log, sep); sep = "-";
+- size += lswlogs(log, "[PRF]");
+- }
+-
+- if (proposal->integ != NULL && proposal->prf == NULL) {
+- size += lswlogs(log, sep); sep = "-";
+- size += lswlogs(log, proposal->integ->common.fqn);
+- } else if (!(proposal->integ == &ike_alg_integ_none &&
+- encrypt_desc_is_aead(proposal->encrypt)) &&
+- proposal->integ != NULL && proposal->integ->prf != proposal->prf) {
+- size += lswlogs(log, sep); sep = "-";
+- size += lswlogs(log, proposal->integ->common.fqn);
+- } else if (IMPAIR(PROPOSAL_PARSER)) {
+- size += lswlogs(log, sep); sep = "-";
+- if (proposal->integ != NULL) {
+- size += lswlogs(log, proposal->integ->common.fqn);
+- } else {
+- size += lswlogs(log, "[INTEG]");
+- }
+- }
+-
+- if (proposal->dh != NULL) {
+- size += lswlogs(log, sep); sep = "-"; /* sep not subsequently used */
+- size += lswlogs(log, proposal->dh->common.fqn);
+- } else if (IMPAIR(PROPOSAL_PARSER)) {
+- size += lswlogs(log, sep); sep = "-"; /* sep not subsequently used */
+- size += lswlogs(log, "[DH]");
+- }
+-
+- return size;
+-}
+-
+-size_t lswlog_alg_info(struct lswlog *log, const struct alg_info *alg_info)
+-{
+- size_t size = 0;
+- const char *sep = "";
+- FOR_EACH_PROPOSAL_INFO(alg_info, proposal) {
+- size += lswlogs(log, sep);
+- size += lswlog_proposal_info(log, proposal);
+- sep = ", ";
+- }
+- return size;
+-}
+-
+-/*
+- * When PFS=no ignore any DH algorithms, and when PFS=yes reject
+- * mixing implict and explicit DH.
+- */
+-bool alg_info_pfs_vs_dh_check(const struct proposal_parser *parser,
+- struct alg_info_esp *aie)
+-{
+- if (aie->ai.alg_info_cnt <= 0) {
+- /* let caller deal with no proposals. */
+- return true;
+- }
+-
+- /* scrape the proposals for dh algorithms */
+- struct proposal_info *first_null = NULL;
+- struct proposal_info *first_dh = NULL;
+- struct proposal_info *second_dh = NULL;
+- struct proposal_info *first_none = NULL;
+- FOR_EACH_ESP_INFO(aie, alg) {
+- if (alg->dh == NULL) {
+- if (first_null == NULL) {
+- first_null = alg;
+- }
+- } else if (alg->dh == &ike_alg_dh_none) {
+- if (first_none == NULL) {
+- first_none = alg;
+- }
+- } else if (first_dh == NULL) {
+- first_dh = alg;
+- } else if (second_dh == NULL && first_dh->dh != alg->dh) {
+- second_dh = alg;
+- }
+- }
+-
+- if (first_dh == NULL && first_none == NULL) {
+- /* no DH is always ok */
+- return true;
+- }
+-
+- /*
+- * Try to generate very specific errors first. For instance,
+- * given PFS=no esp=aes,aes;dh21, an error stating that dh21
+- * is not valid because of PFS is more helpful than an error
+- * saying that all or no proposals need PFS.
+- */
+-
+- /*
+- * Since PFS=NO overrides any DH, don't silently ignore it.
+- * Check this early so that a conflict with PFS=no code gets
+- * reported before anything else.
+- */
+- if (!parser->policy->pfs && (first_dh != NULL || first_none != NULL)) {
+- FOR_EACH_ESP_INFO(aie, alg) {
+- if (alg->dh == &ike_alg_dh_none) {
+- parser->policy->warning("ignoring redundant %s DH algorithm NONE as PFS policy is disabled",
+- parser->protocol->name);
+- } else if (alg->dh != NULL) {
+- parser->policy->warning("ignoring %s DH algorithm %s as PFS policy is disabled",
+- parser->protocol->name,
+- alg->dh->common.fqn);
+- }
+- alg->dh = NULL;
+- }
+- return true;
+- }
+-
+- /*
+- * Since at least one proposal included DH, all proposals
+- * should. A proposal without DH is an error.
+- *
+- * (The converse, no proposals including DH was handled right
+- * at the start).
+- */
+- if (first_null != NULL) {
+- /* DH was specified */
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "either all or no %s proposals should specify DH",
+- parser->protocol->name);
+- if (!impair_proposal_errors(parser)) {
+- return false;
+- }
+- }
+-
+- /*
+- * IKEv1 only allows one DH algorithm.
+- */
+- if (parser->policy->ikev1) {
+- if (first_dh != NULL && second_dh != NULL) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "more than one IKEv1 %s DH algorithm (%s, %s) is not allowed in quick mode",
+- parser->protocol->name,
+- first_dh->dh->common.fqn,
+- second_dh->dh->common.fqn);
+- if (!impair_proposal_errors(parser)) {
+- return false;
+- }
+- }
+- }
+-
+- /*
+- * IKEv2, only implements one DH algorithm.
+- */
+- if (parser->policy->ikev2) {
+- if (first_dh != NULL && second_dh != NULL) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "more than one IKEv2 %s DH algorithm (%s, %s) requires unimplemented CHILD_SA INVALID_KE",
+- parser->protocol->name,
+- first_dh->dh->common.fqn,
+- second_dh->dh->common.fqn);
+- if (!impair_proposal_errors(parser)) {
+- return false;
+- }
+- }
+- }
+-
+- return true;
+-}
+-
+-bool impair_proposal_errors(const struct proposal_parser *parser)
+-{
+- pexpect(parser->err_buf[0] != '\0');
+- if (IMPAIR(PROPOSAL_PARSER)) {
+- libreswan_log("IMPAIR: ignoring proposal error: %s",
+- parser->err_buf);
+- parser->err_buf[0] = '\0';
+- return true;
+- } else {
+- return false;
+- }
+-}
+diff -Naur libreswan-3.27-orig/lib/libswan/esp_info.c libreswan-3.27/lib/libswan/esp_info.c
+--- libreswan-3.27-orig/lib/libswan/esp_info.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/esp_info.c 2019-02-15 16:50:12.776728076 -0500
+@@ -22,7 +22,7 @@
+
+ #include "lswalloc.h"
+ #include "lswlog.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "alg_byname.h"
+ #include "lswfips.h"
+
+@@ -33,8 +33,8 @@
+ /*
+ * Add ESP alg info _with_ logic (policy):
+ */
+-static bool esp_proposal_ok(const struct proposal_parser *parser,
+- const struct proposal_info *proposal)
++static bool esp_proposal_ok(struct proposal_parser *parser,
++ const struct proposal *proposal)
+ {
+ if (!proposal_aead_none_ok(parser, proposal)) {
+ if (!impair_proposal_errors(parser)) {
+@@ -42,12 +42,19 @@
+ }
+ }
+
+- impaired_passert(PROPOSAL_PARSER, proposal->encrypt != NULL);
+- impaired_passert(PROPOSAL_PARSER, proposal->prf == NULL);
+- impaired_passert(PROPOSAL_PARSER, proposal->integ != NULL);
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_encrypt, NULL) != NULL);
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_prf, NULL) == NULL);
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_integ, NULL) != NULL);
+ return true;
+ }
+
++/*
++ * since esp= must have an encryption algorithm this is normally
++ * ignored.
++ */
+ static const struct ike_alg *default_esp_encrypt[] = {
+ #ifdef USE_AES
+ &ike_alg_encrypt_aes_cbc.common,
+@@ -55,24 +62,39 @@
+ NULL,
+ };
+
+-static const struct ike_alg *default_esp_integ[] = {
++static const struct ike_alg *default_v1_esp_integ[] = {
+ #ifdef USE_SHA1
+ &ike_alg_integ_sha1.common,
+ #endif
+ NULL,
+ };
+
+-static const struct proposal_defaults esp_defaults = {
++static const struct ike_alg *default_v2_esp_integ[] = {
++#ifdef USE_SHA2
++ &ike_alg_integ_sha2_512.common,
++ &ike_alg_integ_sha2_256.common,
++#endif
++ NULL,
++};
++
++static const struct proposal_defaults v1_esp_defaults = {
+ .encrypt = default_esp_encrypt,
+- .integ = default_esp_integ,
++ .integ = default_v1_esp_integ,
++};
++
++static const struct proposal_defaults v2_esp_defaults = {
++ .encrypt = default_esp_encrypt,
++ .integ = default_v2_esp_integ,
+ };
+
+ static const struct proposal_protocol esp_proposal_protocol = {
+ .name = "ESP",
+ .ikev1_alg_id = IKEv1_ESP_ID,
+ .protoid = PROTO_IPSEC_ESP,
+- .ikev1_defaults = &esp_defaults,
+- .ikev2_defaults = &esp_defaults,
++ .defaults = {
++ [IKEv1] = &v1_esp_defaults,
++ [IKEv2] = &v2_esp_defaults,
++ },
+ .proposal_ok = esp_proposal_ok,
+ .encrypt_alg_byname = encrypt_alg_byname,
+ .integ_alg_byname = integ_alg_byname,
+@@ -81,7 +103,7 @@
+
+ /*
+ * ??? the only difference between
+- * alg_info_ah_create_from_str and alg_info_esp_create_from_str
++ * alg_info_ah_create_from_str and esp_proposals_create_from_str
+ * is in the second argument to proposal_parser.
+ *
+ * XXX: On the other hand, since "struct ike_info" and "struct
+@@ -91,33 +113,7 @@
+
+ /* This function is tested in testing/algparse/algparse.c */
+
+-struct alg_info_esp *alg_info_esp_create_from_str(const struct proposal_policy *policy,
+- const char *alg_str,
+- char *err_buf, size_t err_buf_len)
++struct proposal_parser *esp_proposal_parser(const struct proposal_policy *policy)
+ {
+- shunk_t string = shunk1(alg_str);
+- const struct proposal_parser parser = proposal_parser(policy,
+- &esp_proposal_protocol,
+- err_buf, err_buf_len);
+-
+- /*
+- * alg_info storage should be sized dynamically
+- * but this may require two passes to know
+- * transform count in advance.
+- */
+- struct alg_info_esp *alg_info_esp = alloc_thing(struct alg_info_esp,
+- "alg_info_esp");
+- if (!alg_info_parse_str(&parser, &alg_info_esp->ai, string)) {
+- passert(err_buf[0] != '\0');
+- alg_info_free(&alg_info_esp->ai);
+- return NULL;
+- }
+-
+- if (!alg_info_pfs_vs_dh_check(&parser, alg_info_esp)) {
+- passert(err_buf[0] != '\0');
+- alg_info_free(&alg_info_esp->ai);
+- return NULL;
+- }
+-
+- return alg_info_esp;
++ return alloc_proposal_parser(policy, &esp_proposal_protocol);
+ }
+diff -Naur libreswan-3.27-orig/lib/libswan/ike_alg.c libreswan-3.27/lib/libswan/ike_alg.c
+--- libreswan-3.27-orig/lib/libswan/ike_alg.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/ike_alg.c 2019-02-15 16:32:28.978835332 -0500
+@@ -37,7 +37,7 @@
+ #include "ike_alg.h"
+ #include "ike_alg_integ.h"
+ #include "ike_alg_encrypt.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "ike_alg_prf.h"
+ #include "ike_alg_prf_hmac_ops.h"
+ #include "ike_alg_prf_nss_ops.h"
+@@ -589,7 +589,7 @@
+ static bool integ_desc_is_ike(const struct ike_alg *alg)
+ {
+ const struct integ_desc *integ = integ_desc(alg);
+- return integ->prf != NULL;
++ return integ->prf != NULL || integ == &ike_alg_integ_none;
+ }
+
+ static struct algorithm_table integ_algorithms = ALGORITHM_TABLE(integ_descriptors);
+diff -Naur libreswan-3.27-orig/lib/libswan/ike_alg_dh.c libreswan-3.27/lib/libswan/ike_alg_dh.c
+--- libreswan-3.27-orig/lib/libswan/ike_alg_dh.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/ike_alg_dh.c 2019-02-15 16:32:28.979835342 -0500
+@@ -186,7 +186,7 @@
+ .algo_type = IKE_ALG_DH,
+ .name = "DH19",
+ .fqn = "DH19",
+- .names = { "dh19", "ecp_256", },
++ .names = { "dh19", "ecp_256", "ecp256" },
+ .id = {
+ [IKEv1_OAKLEY_ID] = OAKLEY_GROUP_ECP_256,
+ [IKEv1_ESP_ID] = -1,
+@@ -205,7 +205,7 @@
+ .algo_type = IKE_ALG_DH,
+ .name = "DH20",
+ .fqn = "DH20",
+- .names = { "dh20", "ecp_384", },
++ .names = { "dh20", "ecp_384", "ecp384" },
+ .id = {
+ [IKEv1_OAKLEY_ID] = OAKLEY_GROUP_ECP_384,
+ [IKEv1_ESP_ID] = -1,
+@@ -224,7 +224,7 @@
+ .algo_type = IKE_ALG_DH,
+ .name = "DH21",
+ .fqn = "DH21",
+- .names = { "dh21", "ecp_521", },
++ .names = { "dh21", "ecp_521", "ecp521" },
+ .id = {
+ [IKEv1_OAKLEY_ID] = OAKLEY_GROUP_ECP_521,
+ [IKEv1_ESP_ID] = -1,
+diff -Naur libreswan-3.27-orig/lib/libswan/ike_alg_sha2.c libreswan-3.27/lib/libswan/ike_alg_sha2.c
+--- libreswan-3.27-orig/lib/libswan/ike_alg_sha2.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/ike_alg_sha2.c 2019-02-15 16:32:28.979835342 -0500
+@@ -79,7 +79,7 @@
+ .common = {
+ .name = "sha2_256",
+ .fqn = "HMAC_SHA2_256_128",
+- .names = { "sha2", "sha256", "sha2_256", "hmac_sha2_256", "hmac_sha2_256_128", },
++ .names = { "sha2", "sha256", "sha2_256", "sha2_256_128", "hmac_sha2_256", "hmac_sha2_256_128", },
+ .algo_type = IKE_ALG_INTEG,
+ .id = {
+ [IKEv1_OAKLEY_ID] = OAKLEY_SHA2_256,
+@@ -206,7 +206,7 @@
+ .common = {
+ .name = "sha2_384",
+ .fqn = "HMAC_SHA2_384_192",
+- .names = { "sha384", "sha2_384", "hmac_sha2_384", "hmac_sha2_384_192", },
++ .names = { "sha384", "sha2_384", "sha2_384_192", "hmac_sha2_384", "hmac_sha2_384_192", },
+ .algo_type = IKE_ALG_INTEG,
+ .id = {
+ [IKEv1_OAKLEY_ID] = OAKLEY_SHA2_384,
+@@ -308,7 +308,7 @@
+ .common = {
+ .name = "sha2_512",
+ .fqn = "HMAC_SHA2_512_256",
+- .names = { "sha512", "sha2_512", "hmac_sha2_512", "hmac_sha2_512_256", },
++ .names = { "sha512", "sha2_512", "sha2_512_256", "hmac_sha2_512", "hmac_sha2_512_256", },
+ .algo_type = IKE_ALG_INTEG,
+ .id = {
+ [IKEv1_OAKLEY_ID] = OAKLEY_SHA2_512,
+diff -Naur libreswan-3.27-orig/lib/libswan/ike_info.c libreswan-3.27/lib/libswan/ike_info.c
+--- libreswan-3.27-orig/lib/libswan/ike_info.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/ike_info.c 2019-02-15 16:50:12.777728085 -0500
+@@ -27,10 +27,10 @@
+ #include "ike_alg_integ.h"
+ #include "ike_alg_prf.h"
+ #include "ike_alg_dh.h"
+-#include "alg_info.h"
++#include "proposals.h"
+
+-static bool ike_proposal_ok(const struct proposal_parser *parser,
+- const struct proposal_info *proposal)
++static bool ike_proposal_ok(struct proposal_parser *parser,
++ const struct proposal *proposal)
+ {
+ if (!proposal_aead_none_ok(parser, proposal)) {
+ if (!impair_proposal_errors(parser)) {
+@@ -42,27 +42,42 @@
+ * Check that the ALG_INFO spec is implemented.
+ */
+
+- impaired_passert(PROPOSAL_PARSER, proposal->encrypt != NULL);
+- passert(proposal->encrypt == NULL || ike_alg_is_ike(&(proposal->encrypt->common)));
+- passert(IMPAIR(PROPOSAL_PARSER) || proposal->enckeylen == 0 ||
+- encrypt_has_key_bit_length(proposal->encrypt,
+- proposal->enckeylen));
+-
+- impaired_passert(PROPOSAL_PARSER, proposal->prf != NULL);
+- passert(proposal->prf == NULL || ike_alg_is_ike(&(proposal->prf->common)));
+-
+- impaired_passert(PROPOSAL_PARSER, proposal->integ != NULL);
+- passert(proposal->integ == &ike_alg_integ_none ||
+- proposal->integ == NULL ||
+- ike_alg_is_ike(&proposal->integ->common));
+-
+- impaired_passert(PROPOSAL_PARSER, proposal->dh != NULL);
+- passert(proposal->dh == NULL || ike_alg_is_ike(&(proposal->dh->common)));
+- if (proposal->dh == &ike_alg_dh_none) {
+- snprintf(parser->err_buf, parser->err_buf_len,
+- "IKE DH algorithm 'none' not permitted");
+- if (!impair_proposal_errors(parser)) {
+- return false;
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_encrypt, NULL) != NULL);
++ FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
++ const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
++ passert(ike_alg_is_ike(&encrypt->common));
++ passert(IMPAIR(PROPOSAL_PARSER) ||
++ alg->enckeylen == 0 ||
++ encrypt_has_key_bit_length(encrypt,
++ alg->enckeylen));
++ }
++
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_prf, NULL) != NULL);
++ FOR_EACH_ALGORITHM(proposal, prf, alg) {
++ const struct prf_desc *prf = prf_desc(alg->desc);
++ passert(ike_alg_is_ike(&prf->common));
++ }
++
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_integ, NULL) != NULL);
++ FOR_EACH_ALGORITHM(proposal, integ, alg) {
++ const struct integ_desc *integ = integ_desc(alg->desc);
++ passert(integ == &ike_alg_integ_none ||
++ ike_alg_is_ike(&integ->common));
++ }
++
++ impaired_passert(PROPOSAL_PARSER,
++ next_algorithm(proposal, PROPOSAL_dh, NULL) != NULL);
++ FOR_EACH_ALGORITHM(proposal, dh, alg) {
++ const struct oakley_group_desc *dh = dh_desc(alg->desc);
++ passert(ike_alg_is_ike(&dh->common));
++ if (dh == &ike_alg_dh_none) {
++ proposal_error(parser, "IKE DH algorithm 'none' not permitted");
++ if (!impair_proposal_errors(parser)) {
++ return false;
++ }
+ }
+ }
+
+@@ -84,9 +99,20 @@
+ };
+ static const struct ike_alg *default_ikev2_groups[] = {
+ &oakley_group_modp2048.common,
++ &oakley_group_modp3072.common,
++ &oakley_group_modp4096.common,
++ &oakley_group_modp8192.common,
++ &oakley_group_dh19.common,
++ &oakley_group_dh20.common,
++ &oakley_group_dh21.common,
++ &oakley_group_dh31.common,
+ NULL,
+ };
+
++/*
++ * since ike= must have an encryption algorithm this is normally
++ * ignored.
++ */
+ static const struct ike_alg *default_ike_ealgs[] = {
+ #ifdef USE_AES
+ &ike_alg_encrypt_aes_cbc.common,
+@@ -97,7 +123,7 @@
+ NULL,
+ };
+
+-static const struct ike_alg *default_ike_aalgs[] = {
++static const struct ike_alg *default_v1_ike_prfs[] = {
+ #ifdef USE_SHA2
+ &ike_alg_prf_sha2_256.common,
+ &ike_alg_prf_sha2_512.common,
+@@ -108,24 +134,34 @@
+ NULL,
+ };
+
++static const struct ike_alg *default_v2_ike_prfs[] = {
++#ifdef USE_SHA2
++ &ike_alg_prf_sha2_512.common,
++ &ike_alg_prf_sha2_256.common,
++#endif
++ NULL,
++};
++
+ const struct proposal_defaults ikev1_ike_defaults = {
+ .dh = default_ikev1_groups,
+ .encrypt = default_ike_ealgs,
+- .prf = default_ike_aalgs,
++ .prf = default_v1_ike_prfs,
+ };
+
+ const struct proposal_defaults ikev2_ike_defaults = {
+ .dh = default_ikev2_groups,
+ .encrypt = default_ike_ealgs,
+- .prf = default_ike_aalgs,
++ .prf = default_v2_ike_prfs,
+ };
+
+ const struct proposal_protocol ike_proposal_protocol = {
+ .name = "IKE",
+ .ikev1_alg_id = IKEv1_OAKLEY_ID,
+ .protoid = PROTO_ISAKMP,
+- .ikev1_defaults = &ikev1_ike_defaults,
+- .ikev2_defaults = &ikev2_ike_defaults,
++ .defaults = {
++ [IKEv1] = &ikev1_ike_defaults,
++ [IKEv2] = &ikev2_ike_defaults,
++ },
+ .proposal_ok = ike_proposal_ok,
+ .encrypt_alg_byname = encrypt_alg_byname,
+ .prf_alg_byname = prf_alg_byname,
+@@ -133,25 +169,7 @@
+ .dh_alg_byname = dh_alg_byname,
+ };
+
+-struct alg_info_ike *alg_info_ike_create_from_str(const struct proposal_policy *policy,
+- const char *alg_str,
+- char *err_buf, size_t err_buf_len)
++struct proposal_parser *ike_proposal_parser(const struct proposal_policy *policy)
+ {
+- /*
+- * alg_info storage should be sized dynamically
+- * but this may require two passes to know
+- * transform count in advance.
+- */
+- struct alg_info_ike *alg_info_ike = alloc_thing(struct alg_info_ike, "alg_info_ike");
+- const struct proposal_parser parser = proposal_parser(policy,
+- &ike_proposal_protocol,
+- err_buf, err_buf_len);
+-
+- if (!alg_info_parse_str(&parser, &alg_info_ike->ai, shunk1(alg_str))) {
+- passert(err_buf[0] != '\0');
+- alg_info_free(&alg_info_ike->ai);
+- return NULL;
+- }
+-
+- return alg_info_ike;
++ return alloc_proposal_parser(policy, &ike_proposal_protocol);
+ }
+diff -Naur libreswan-3.27-orig/lib/libswan/Makefile libreswan-3.27/lib/libswan/Makefile
+--- libreswan-3.27-orig/lib/libswan/Makefile 2019-02-15 16:31:43.029408030 -0500
++++ libreswan-3.27/lib/libswan/Makefile 2019-02-15 16:32:28.981835360 -0500
+@@ -74,7 +74,12 @@
+ OBJS += satot.o
+ OBJS += ultot.o
+
+-OBJS += alg_info.o esp_info.o ike_info.o ah_info.o
++OBJS += proposals.o
++OBJS += v1_proposals.o
++OBJS += v2_proposals.o
++OBJS += esp_info.o
++OBJS += ah_info.o
++OBJS += ike_info.o
+
+ OBJS += ckaid.o
+
+diff -Naur libreswan-3.27-orig/lib/libswan/proposals.c libreswan-3.27/lib/libswan/proposals.c
+--- libreswan-3.27-orig/lib/libswan/proposals.c 1969-12-31 19:00:00.000000000 -0500
++++ libreswan-3.27/lib/libswan/proposals.c 2019-02-15 16:46:01.378390204 -0500
+@@ -0,0 +1,573 @@
++/*
++ * Algorithm info parsing and creation functions
++ * Author: JuanJo Ciarlante
++ *
++ * Copyright (C) 2012 Paul Wouters
++ * Copyright (C) 2015-2019 Andrew Cagney
++ *
++ * 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 "lswlog.h"
++#include "lswalloc.h"
++#include "constants.h"
++#include "proposals.h"
++#include "ike_alg.h"
++#include "ike_alg_integ.h"
++#include "ike_alg_dh.h"
++#include "alg_byname.h"
++
++struct proposal {
++ /*
++ * The algorithm entries.
++ */
++ struct algorithm *algorithms[PROPOSAL_ALGORITHM_ROOF];
++ /*
++ * Which protocol is this proposal intended for?
++ */
++ const struct proposal_protocol *protocol;
++ struct proposal *next;
++};
++
++struct proposals {
++ int ref_cnt;
++ struct proposal *proposals;
++};
++
++struct proposal_parser *alloc_proposal_parser(const struct proposal_policy *policy,
++ const struct proposal_protocol *protocol)
++{
++ struct proposal_parser *parser = alloc_thing(struct proposal_parser, "parser");
++ parser->policy = policy;
++ parser->protocol = protocol;
++ parser->error[0] = '\0';
++ return parser;
++}
++
++void free_proposal_parser(struct proposal_parser **parser)
++{
++ pfree(*parser);
++ *parser = NULL;
++}
++
++bool proposal_encrypt_aead(const struct proposal *proposal)
++{
++ if (proposal->algorithms[PROPOSAL_encrypt] == NULL) {
++ return false;
++ }
++ FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
++ const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
++ if (!encrypt_desc_is_aead(encrypt)) {
++ return false;
++ }
++ }
++ return true;
++}
++
++bool proposal_encrypt_norm(const struct proposal *proposal)
++{
++ if (proposal->algorithms[PROPOSAL_encrypt] == NULL) {
++ return false;
++ }
++ FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
++ const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
++ if (encrypt_desc_is_aead(encrypt)) {
++ return false;
++ }
++ }
++ return true;
++}
++
++bool proposal_integ_none(const struct proposal *proposal)
++{
++ /* interpret NULL as NONE */
++ FOR_EACH_ALGORITHM(proposal, integ, alg) {
++ const struct integ_desc *integ = integ_desc(alg->desc);
++ if (integ != &ike_alg_integ_none) {
++ return false;
++ }
++ }
++ return true;
++}
++
++bool proposal_aead_none_ok(struct proposal_parser *parser,
++ const struct proposal *proposal)
++{
++ if (IMPAIR(ALLOW_NULL_NONE)) {
++ return true;
++ }
++
++ if (proposal->algorithms[PROPOSAL_encrypt] == NULL) {
++ return true;
++ }
++
++ /* are any and all encrypt algorithms AEAD? */
++ bool aead = proposal_encrypt_aead(proposal);
++ bool norm = proposal_encrypt_norm(proposal);
++
++ if (!aead && !norm) {
++ proposal_error(parser, "AEAD and non-AEAD %s encryption algorithm can not be combined",
++ proposal->protocol->name);
++ return false;
++ }
++
++ /* are any and all integ algorithms NONE? */
++ bool none = proposal_integ_none(proposal);
++
++ if (aead && !none) {
++ const struct ike_alg *encrypt = proposal->algorithms[PROPOSAL_encrypt]->desc;
++ /*
++ * At least one of the integrity algorithms wasn't
++ * NONE. For instance, esp=aes_gcm-sha1" is invalid.
++ */
++ proposal_error(parser, "AEAD %s encryption algorithm '%s' must have 'none' as the integrity algorithm",
++ proposal->protocol->name,
++ encrypt->name);
++ return false;
++ }
++
++ if (norm && none) {
++ const struct ike_alg *encrypt = proposal->algorithms[PROPOSAL_encrypt]->desc;
++ /*
++ * Not AEAD and either there was no integrity
++ * algorithm (implying NONE) or at least one integrity
++ * algorithm was NONE. For instance,
++ * esp=aes_cbc-none" is invalid.
++ */
++ proposal_error(parser, "non-AEAD %s encryption algorithm '%s' cannot have 'none' as the integrity algorithm",
++ proposal->protocol->name,
++ encrypt->name);
++ return false;
++ }
++
++ return true;
++}
++
++
++/*
++ * proposals struct can be shared by several connections instances,
++ * handle free() with ref_cnts.
++ */
++
++void proposals_addref(struct proposals **proposals)
++{
++ if ((*proposals) != NULL) {
++ (*proposals)->ref_cnt++;
++ }
++}
++
++void proposals_delref(struct proposals **proposals)
++{
++ if ((*proposals) != NULL) {
++ if ((*proposals)->ref_cnt == 0) {
++ free_proposal(&(*proposals)->proposals);
++ pfree((*proposals));
++ } else {
++ (*proposals)->ref_cnt--;
++ }
++ *proposals = NULL;
++ }
++}
++struct proposal *next_proposal(const struct proposals *proposals,
++ struct proposal *last)
++{
++ if (last == NULL) {
++ return proposals->proposals;
++ } else {
++ return last->next;
++ }
++}
++
++unsigned nr_proposals(struct proposals *proposals)
++{
++ unsigned nr = 0;
++ FOR_EACH_PROPOSAL(proposals, proposal) {
++ nr++;
++ }
++ return nr;
++}
++
++void append_proposal(struct proposals *proposals, struct proposal **proposal)
++{
++ struct proposal **end = &proposals->proposals;
++ /* check for duplicates */
++ while ((*end) != NULL) {
++ bool same = true;
++ for (enum proposal_algorithm pa = 0;
++ same && pa < PROPOSAL_ALGORITHM_ROOF; pa++) {
++ struct algorithm *old = (*end)->algorithms[pa];
++ struct algorithm *new = (*proposal)->algorithms[pa];
++ while (same) {
++ if (new == NULL && old == NULL) {
++ break;
++ }
++ if (new == NULL || old == NULL) {
++ same = false;
++ break;
++ }
++ if (new->desc != old->desc) {
++ same = false;
++ break;
++ }
++ /*
++ * If list already contains encryption
++ * with ENCKEYLEN=0 then new is a
++ * duplicate as 0 generates all keys.
++ * Ignore reverse vis aes128,aes.
++ */
++ if (old->desc->algo_type == IKE_ALG_ENCRYPT &&
++ (old->enckeylen != 0 &&
++ new->enckeylen != old->enckeylen)) {
++ same = false;
++ break;
++ }
++ new = new->next;
++ old = old->next;
++ }
++ }
++ if (same) {
++ /* parser->policy->warning("discarding duplicate proposal"); */
++ free_proposal(proposal);
++ return;
++ }
++ end = &(*end)->next;
++ }
++ *end = *proposal;
++ *proposal = NULL;
++}
++
++struct v1_proposal v1_proposal(const struct proposal *proposal)
++{
++ struct v1_proposal v1 = {
++ .protocol = proposal->protocol,
++#define D(ALG) .ALG = proposal->algorithms[PROPOSAL_##ALG] != NULL ? ALG##_desc(proposal->algorithms[PROPOSAL_##ALG]->desc) : NULL
++ D(encrypt),
++ D(prf),
++ D(integ),
++ D(dh),
++#undef D
++ };
++ v1.enckeylen = proposal->algorithms[PROPOSAL_encrypt] != NULL ? proposal->algorithms[PROPOSAL_encrypt]->enckeylen : 0;
++
++ return v1;
++}
++
++struct algorithm *next_algorithm(const struct proposal *proposal,
++ enum proposal_algorithm algorithm,
++ struct algorithm *last)
++{
++ if (last == NULL) {
++ /*
++ * Hack, there should there a way to index algorithm
++ * types; however the old enum proved very dangerous.
++ */
++ passert(algorithm < elemsof(proposal->algorithms));
++ return proposal->algorithms[algorithm];
++ } else {
++ return last->next;
++ }
++}
++
++void free_algorithms(struct proposal *proposal,
++ enum proposal_algorithm algorithm)
++{
++ passert(algorithm < elemsof(proposal->algorithms));
++ struct algorithm *alg = proposal->algorithms[algorithm];
++ while (alg != NULL) {
++ struct algorithm *del = alg;
++ alg = alg->next;
++ pfree(del);
++ }
++ proposal->algorithms[algorithm] = NULL;
++}
++
++struct proposal *alloc_proposal(struct proposal_parser *parser)
++{
++ struct proposal *proposal = alloc_thing(struct proposal, "proposal");
++ proposal->protocol = parser->protocol;
++ return proposal;
++}
++
++void free_proposal(struct proposal **proposals)
++{
++ struct proposal *proposal = *proposals;
++ while (proposal != NULL) {
++ struct proposal *del = proposal;
++ proposal = proposal->next;
++ for (enum proposal_algorithm algorithm = 0;
++ algorithm < PROPOSAL_ALGORITHM_ROOF;
++ algorithm++) {
++ free_algorithms(del, algorithm);
++ }
++ pfree(del);
++ }
++ *proposals = NULL;
++}
++
++void append_algorithm(struct proposal_parser *parser,
++ struct proposal *proposal,
++ enum proposal_algorithm algorithm,
++ const struct ike_alg *alg,
++ int enckeylen)
++{
++ passert(algorithm < elemsof(proposal->algorithms));
++ struct algorithm **end = &proposal->algorithms[algorithm];
++ /* find end, and check for duplicates */
++ while ((*end) != NULL) {
++ /*
++ * enckeylen=0 acts as a wildcard
++ */
++ if (alg == (*end)->desc &&
++ (alg->algo_type != IKE_ALG_ENCRYPT ||
++ ((*end)->enckeylen == 0 ||
++ enckeylen == (*end)->enckeylen))) {
++ parser->policy->warning("discarding duplicate algorithm '%s'",
++ alg->name);
++ return;
++ }
++ end = &(*end)->next;
++ }
++ struct algorithm new_algorithm = {
++ .desc = alg,
++ .enckeylen = enckeylen,
++ };
++ *end = clone_thing(new_algorithm, "alg");
++}
++
++void fmt_proposal(struct lswlog *log,
++ const struct proposal *proposal)
++{
++ const char *ps = "";
++
++ const char *as = "";
++
++ as = ps;
++ FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
++ const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
++ lswlogs(log, as); ps = "-"; as = "+";
++ lswlogs(log, encrypt->common.fqn);
++ if (alg->enckeylen != 0) {
++ lswlogf(log, "_%d", alg->enckeylen);
++ }
++ }
++
++ as = ps;
++ FOR_EACH_ALGORITHM(proposal, prf, alg) {
++ const struct prf_desc *prf = prf_desc(alg->desc);
++ lswlogs(log, as); ps = "-"; as = "+";
++ lswlogs(log, prf->common.fqn);
++ }
++
++ as = ps;
++ if ((proposal->algorithms[PROPOSAL_prf] == NULL &&
++ proposal->algorithms[PROPOSAL_integ] != NULL) ||
++ (IMPAIR(PROPOSAL_PARSER) &&
++ proposal->algorithms[PROPOSAL_integ] != NULL)) {
++ FOR_EACH_ALGORITHM(proposal, integ, alg) {
++ const struct integ_desc *integ = integ_desc(alg->desc);
++ lswlogs(log, as); ps = "-"; as = "+";
++ lswlogs(log, integ->common.fqn);
++ }
++ }
++
++ as = ps;
++ FOR_EACH_ALGORITHM(proposal, dh, alg) {
++ const struct oakley_group_desc *dh = dh_desc(alg->desc);
++ lswlogs(log, as); ps = "-"; as = "+";
++ lswlogs(log, dh->common.fqn);
++ }
++}
++
++void fmt_proposals(struct lswlog *log, const struct proposals *proposals)
++{
++ const char *sep = "";
++ FOR_EACH_PROPOSAL(proposals, proposal) {
++ lswlogs(log, sep);
++ fmt_proposal(log, proposal);
++ sep = ", ";
++ }
++}
++
++/*
++ * When PFS=no ignore any DH algorithms, and when PFS=yes reject
++ * mixing implict and explicit DH.
++ */
++static bool proposals_pfs_vs_dh_check(struct proposal_parser *parser,
++ struct proposals *proposals)
++{
++ /* scrape the proposals for dh algorithms */
++ const struct proposal *first_null = NULL;
++ const struct proposal *first_none = NULL;
++ const struct ike_alg *first_dh = NULL;
++ const struct ike_alg *second_dh = NULL;
++ FOR_EACH_PROPOSAL(proposals, proposal) {
++ if (proposal->algorithms[PROPOSAL_dh] == NULL) {
++ if (first_null == NULL) {
++ first_null = proposal;
++ }
++ } else if (proposal->algorithms[PROPOSAL_dh]->desc == &ike_alg_dh_none.common) {
++ if (first_none == NULL) {
++ first_none = proposal;
++ }
++ } else if (first_dh == NULL) {
++ first_dh = proposal->algorithms[PROPOSAL_dh]->desc;
++ } else if (second_dh == NULL &&
++ first_dh != proposal->algorithms[PROPOSAL_dh]->desc) {
++ second_dh = proposal->algorithms[PROPOSAL_dh]->desc;
++ }
++ }
++
++ if (first_dh == NULL && first_none == NULL) {
++ /* no DH is always ok */
++ return true;
++ }
++
++ /*
++ * Try to generate very specific errors first. For instance,
++ * given PFS=no esp=aes,aes;dh21, an error stating that dh21
++ * is not valid because of PFS is more helpful than an error
++ * saying that all or no proposals need PFS.
++ */
++
++ /*
++ * Since PFS=NO overrides any DH, don't silently ignore it.
++ * Check this early so that a conflict with PFS=no code gets
++ * reported before anything else.
++ */
++ if (!parser->policy->pfs && (first_dh != NULL || first_none != NULL)) {
++ FOR_EACH_PROPOSAL(proposals, proposal) {
++ const struct ike_alg *dh = NULL;
++ if (proposal->algorithms[PROPOSAL_dh] != NULL) {
++ dh = proposal->algorithms[PROPOSAL_dh]->desc;
++ }
++ if (dh == &ike_alg_dh_none.common) {
++ parser->policy->warning("ignoring redundant %s DH algorithm NONE as PFS policy is disabled",
++ parser->protocol->name);
++ } else if (dh != NULL) {
++ parser->policy->warning("ignoring %s DH algorithm %s as PFS policy is disabled",
++ parser->protocol->name,
++ dh->fqn);
++ }
++ free_algorithms(proposal, PROPOSAL_dh);
++ }
++ return true;
++ }
++
++ /*
++ * Since at least one proposal included DH, all proposals
++ * should. A proposal without DH is an error.
++ *
++ * (The converse, no proposals including DH was handled right
++ * at the start).
++ */
++ if (first_null != NULL) {
++ /* DH was specified */
++ proposal_error(parser, "either all or no %s proposals should specify DH",
++ parser->protocol->name);
++ if (!impair_proposal_errors(parser)) {
++ return false;
++ }
++ }
++
++ switch (parser->policy->version) {
++
++ case IKEv1:
++ /*
++ * IKEv1 only allows one DH algorithm.
++ */
++ if (first_dh != NULL && second_dh != NULL) {
++ proposal_error(parser, "more than one IKEv1 %s DH algorithm (%s, %s) is not allowed in quick mode",
++ parser->protocol->name,
++ first_dh->fqn,
++ second_dh->fqn);
++ if (!impair_proposal_errors(parser)) {
++ return false;
++ }
++ }
++ break;
++
++ case IKEv2:
++ /*
++ * IKEv2, only implements one DH algorithm.
++ */
++ if (first_dh != NULL && second_dh != NULL) {
++ proposal_error(parser, "more than one IKEv2 %s DH algorithm (%s, %s) requires unimplemented CHILD_SA INVALID_KE",
++ parser->protocol->name,
++ first_dh->fqn,
++ second_dh->fqn);
++ if (!impair_proposal_errors(parser)) {
++ return false;
++ }
++ }
++ break;
++
++ default:
++ /* ignore */
++ break;
++ }
++
++ return true;
++}
++
++void proposal_error(struct proposal_parser *parser, const char *fmt, ...)
++{
++ va_list ap;
++ va_start(ap, fmt);
++ vsnprintf(parser->error, sizeof(parser->error), fmt, ap);
++ va_end(ap);
++}
++
++bool impair_proposal_errors(struct proposal_parser *parser)
++{
++ pexpect(parser->error[0] != '\0');
++ if (IMPAIR(PROPOSAL_PARSER)) {
++ libreswan_log("IMPAIR: ignoring proposal error: %s",
++ parser->error);
++ parser->error[0] = '\0';
++ return true;
++ } else {
++ return false;
++ }
++}
++
++struct proposals *proposals_from_str(struct proposal_parser *parser,
++ const char *str)
++{
++ struct proposals *proposals = alloc_thing(struct proposals, "proposals");
++ unsigned parser_version = parser->policy->parser_version;
++ if (parser_version == 0) {
++ parser_version = parser->policy->version;
++ }
++ bool ok;
++ switch (parser_version) {
++ case 2: ok = v2_proposals_parse_str(parser, proposals, shunk1(str)); break;
++ default: ok = v1_proposals_parse_str(parser, proposals, shunk1(str)); break;
++ }
++ if (!ok) {
++ proposals_delref(&proposals);
++ return NULL;
++ }
++ if (proposals->proposals == NULL) {
++ proposals_delref(&proposals);
++ return NULL;
++ }
++ if (parser->policy->check_pfs_vs_dh &&
++ !proposals_pfs_vs_dh_check(parser, proposals)) {
++ pexpect(parser->error[0] != '\0');
++ proposals_delref(&proposals);
++ return NULL;
++ }
++ return proposals;
++}
+diff -Naur libreswan-3.27-orig/lib/libswan/shunk.c libreswan-3.27/lib/libswan/shunk.c
+--- libreswan-3.27-orig/lib/libswan/shunk.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/lib/libswan/shunk.c 2019-02-15 16:32:28.982835370 -0500
+@@ -1,6 +1,6 @@
+-/* string fragments, for libreswan
++/* Constant string (octet) fragments, for libreswan
+ *
+- * Copyright (C) 2018 Andrew Cagney
++ * Copyright (C) 2018-2019 Andrew Cagney
+ *
+ * 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
+@@ -20,12 +20,17 @@
+
+ #include "shunk.h"
+
+-const shunk_t empty_shunk;
++/*
++ * Don't mistake a NULL_SHUNK for an empty shunk - just like for
++ * strings they are different.
++ */
++
++const shunk_t null_shunk = NULL_SHUNK;
+
+ shunk_t shunk1(const char *ptr)
+ {
+ if (ptr == NULL) {
+- return empty_shunk;
++ return null_shunk;
+ } else {
+ return shunk2(ptr, strlen(ptr));
+ }
+@@ -34,28 +39,37 @@
+ shunk_t shunk2(const char *ptr, int len)
+ {
+ /*
+- * Since a zero length string and a NULL string pointer are
+- * considered to be different, don't convert the former into
+- * an empty_chunk.
++ * Since a zero length string is not the same as a NULL
++ * string, don't try to be smart and convert the former into
++ * the latter.
+ */
+ return (shunk_t) { .ptr = ptr, .len = len, };
+ }
+
+-shunk_t shunk_strsep(shunk_t *shunk, const char *delim)
++shunk_t shunk_strsep(shunk_t *input, const char *delim)
+ {
+- shunk_t token = shunk2(shunk->ptr, 0);
+- while (shunk->len > 0) {
+- if (strchr(delim, *shunk->ptr) != NULL) {
++ /*
++ * If INPUT is NULL, the loop is skipped and NULL is
++ * returned.
++ */
++ shunk_t token = shunk2(input->ptr, 0);
++ while (input->len > 0) {
++ if (strchr(delim, *input->ptr) != NULL) {
+ /* discard delim */
+- shunk->ptr++;
+- shunk->len--;
++ input->ptr++;
++ input->len--;
+ return token;
+ }
+ /* advance, transfering the char */
+ token.len++;
+- shunk->ptr++;
+- shunk->len--;
++ input->ptr++;
++ input->len--;
+ }
++ /*
++ * Flag this as the last token by setting INPUT to NULL; next
++ * call will return the NULL shunk.
++ */
++ *input = null_shunk;
+ return token;
+ }
+
+diff -Naur libreswan-3.27-orig/lib/libswan/v1_proposals.c libreswan-3.27/lib/libswan/v1_proposals.c
+--- libreswan-3.27-orig/lib/libswan/v1_proposals.c 1969-12-31 19:00:00.000000000 -0500
++++ libreswan-3.27/lib/libswan/v1_proposals.c 2019-02-15 16:47:22.485144451 -0500
+@@ -0,0 +1,578 @@
++/* V1 algorithm proposal parsing, for libreswan
++ *
++ * Copyright (C) 2012 Paul Wouters
++ * Copyright (C) 2015-2019 Andrew Cagney
++ *
++ * 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 "lswlog.h"
++#include "lswalloc.h"
++#include "constants.h"
++#include "proposals.h"
++#include "ike_alg.h"
++#include "ike_alg_integ.h"
++#include "ike_alg_dh.h"
++#include "alg_byname.h"
++
++/*
++ * Add the proposal defaults for the specific algorithm.
++ */
++
++typedef struct v1_proposal merge_alg_default_t(struct v1_proposal proposal,
++ const struct ike_alg *default_alg);
++
++static struct v1_proposal merge_dh_default(struct v1_proposal proposal,
++ const struct ike_alg *default_alg)
++{
++ proposal.dh = oakley_group_desc(default_alg);
++ return proposal;
++}
++
++static struct v1_proposal merge_encrypt_default(struct v1_proposal proposal,
++ const struct ike_alg *default_alg)
++{
++ proposal.encrypt = encrypt_desc(default_alg);
++ return proposal;
++}
++
++static struct v1_proposal merge_prf_default(struct v1_proposal proposal,
++ const struct ike_alg *default_alg)
++{
++ proposal.prf = prf_desc(default_alg);
++ return proposal;
++}
++
++static struct v1_proposal merge_integ_default(struct v1_proposal proposal,
++ const struct ike_alg *default_alg)
++{
++ proposal.integ = integ_desc(default_alg);
++ return proposal;
++}
++
++static bool add_proposal_defaults(struct proposal_parser *parser,
++ const struct proposal_defaults *defaults,
++ struct proposals *proposals,
++ const struct v1_proposal *proposal);
++
++static bool add_alg_defaults(struct proposal_parser *parser,
++ const struct proposal_defaults *defaults,
++ struct proposals *proposals,
++ const struct v1_proposal *proposal,
++ const struct ike_alg_type *type,
++ const struct ike_alg **default_algs,
++ merge_alg_default_t *merge_alg_default)
++{
++ /*
++ * Use VALID_ALG to add the valid algorithms into VALID_ALGS.
++ */
++ for (const struct ike_alg **default_alg = default_algs;
++ *default_alg; default_alg++) {
++ const struct ike_alg *alg = *default_alg;
++ if (!alg_byname_ok(parser, alg,
++ shunk1(alg->name))) {
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("skipping default %s",
++ parser->error));
++ parser->error[0] = '\0';
++ continue;
++ }
++ /* add it */
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("adding default %s %s",
++ ike_alg_type_name(type),
++ alg->name));
++ struct v1_proposal merged_proposal = merge_alg_default(*proposal,
++ *default_alg);
++ if (!add_proposal_defaults(parser, defaults,
++ proposals, &merged_proposal)) {
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ }
++ return true;
++}
++
++/*
++ * Validate the proposal and, suppressing duplicates, add it to the
++ * proposal list.
++ */
++
++static bool add_proposal(struct proposal_parser *parser,
++ struct proposals *proposals,
++ const struct v1_proposal *proposal)
++{
++ struct proposal *new = alloc_proposal(parser);
++ if (proposal->encrypt != NULL) {
++ append_algorithm(parser, new, PROPOSAL_encrypt,
++ &proposal->encrypt->common,
++ proposal->enckeylen);
++ }
++#define A(NAME) \
++ if (proposal->NAME != NULL) { \
++ append_algorithm(parser, new, PROPOSAL_##NAME, \
++ &proposal->NAME->common, 0); \
++ }
++ A(prf);
++ A(integ);
++ A(dh);
++#undef A
++ /* back end? */
++ if (!proposal->protocol->proposal_ok(parser, new)) {
++ free_proposal(&new);
++ return false;
++ }
++ append_proposal(proposals, &new);
++ return true;
++}
++
++/*
++ * For all the algorithms, when an algorithm is missing (NULL), and
++ * there are defaults, add them.
++ */
++
++static bool add_proposal_defaults(struct proposal_parser *parser,
++ const struct proposal_defaults *defaults,
++ struct proposals *proposals,
++ const struct v1_proposal *proposal)
++{
++ /*
++ * Note that the order in which things are recursively added -
++ * MODP, ENCR, PRF/HASH - affects test results. It determines
++ * things like the order of proposals.
++ */
++ if (proposal->dh == NULL &&
++ defaults != NULL && defaults->dh != NULL) {
++ return add_alg_defaults(parser, defaults,
++ proposals, proposal,
++ &ike_alg_dh, defaults->dh,
++ merge_dh_default);
++ } else if (proposal->encrypt == NULL &&
++ defaults != NULL && defaults->encrypt != NULL) {
++ return add_alg_defaults(parser, defaults,
++ proposals, proposal,
++ &ike_alg_encrypt, defaults->encrypt,
++ merge_encrypt_default);
++ } else if (proposal->prf == NULL &&
++ defaults != NULL && defaults->prf != NULL) {
++ return add_alg_defaults(parser, defaults,
++ proposals, proposal,
++ &ike_alg_prf, defaults->prf,
++ merge_prf_default);
++ } else if (proposal->integ == NULL &&
++ proposal->encrypt != NULL &&
++ encrypt_desc_is_aead(proposal->encrypt)) {
++ /*
++ * Since AEAD, integrity is always 'none'.
++ */
++ struct v1_proposal merged_proposal = *proposal;
++ merged_proposal.integ = &ike_alg_integ_none;
++ return add_proposal_defaults(parser, defaults,
++ proposals, &merged_proposal);
++ } else if (proposal->integ == NULL &&
++ defaults != NULL && defaults->integ != NULL) {
++ return add_alg_defaults(parser, defaults,
++ proposals, proposal,
++ &ike_alg_integ, defaults->integ,
++ merge_integ_default);
++ } else if (proposal->integ == NULL &&
++ proposal->prf != NULL &&
++ proposal->encrypt != NULL &&
++ !encrypt_desc_is_aead(proposal->encrypt)) {
++ /*
++ * Since non-AEAD, use an integrity algorithm that is
++ * implemented using the PRF.
++ */
++ struct v1_proposal merged_proposal = *proposal;
++ for (const struct integ_desc **algp = next_integ_desc(NULL);
++ algp != NULL; algp = next_integ_desc(algp)) {
++ const struct integ_desc *alg = *algp;
++ if (alg->prf == proposal->prf) {
++ merged_proposal.integ = alg;
++ break;
++ }
++ }
++ if (merged_proposal.integ == NULL) {
++ proposal_error(parser, "%s integrity derived from PRF '%s' is not supported",
++ proposal->protocol->name,
++ proposal->prf->common.name);
++ return false;
++ }
++ return add_proposal_defaults(parser, defaults,
++ proposals, &merged_proposal);
++ } else {
++ return add_proposal(parser, proposals, proposal);
++ }
++}
++
++static bool merge_default_proposals(struct proposal_parser *parser,
++ struct proposals *proposals,
++ const struct v1_proposal *proposal)
++{
++ /*
++ * If there's a hint of IKEv1 being enabled then prefer its
++ * larger set of defaults.
++ *
++ * This should increase the odds of both ends interoperating.
++ *
++ * For instance, the IKEv2 defaults were preferred and one end
++ * has ikev2=never then, in aggressive mode, things don't
++ * work.
++ */
++ passert(parser->policy->version < elemsof(proposal->protocol->defaults));
++ const struct proposal_defaults *defaults =
++ proposal->protocol->defaults[parser->policy->version];
++ return add_proposal_defaults(parser, defaults,
++ proposals, proposal);
++}
++
++static const struct ike_alg *lookup_byname(struct proposal_parser *parser,
++ alg_byname_fn *alg_byname,
++ shunk_t name,
++ size_t key_bit_length,
++ shunk_t print_name,
++ const char *what)
++{
++ if (name.len > 0) {
++ if (alg_byname != NULL) {
++ const struct ike_alg *alg = alg_byname(parser, name, key_bit_length,
++ print_name);
++ if (alg == NULL) {
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("%s_byname('"PRI_SHUNK"') failed: %s",
++ what, PRI_shunk(name),
++ parser->error));
++ passert(parser->error[0] != '\0');
++ return NULL;
++ }
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("%s_byname('"PRI_SHUNK"') returned '%s'",
++ what, PRI_shunk(name), alg->name));
++ return alg;
++ } else {
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("ignoring %s '"PRI_SHUNK"'",
++ what, PRI_shunk(name)));
++ return NULL;
++ }
++ }
++ return NULL;
++}
++
++static int parse_eklen(struct proposal_parser *parser, shunk_t buf)
++{
++ /* convert - if present */
++ char *end = NULL;
++ long eklen = strtol(buf.ptr, &end, 10);
++ if (buf.ptr + buf.len != end) {
++ proposal_error(parser, "encryption key length '"PRI_SHUNK"' contains a non-numeric character",
++ PRI_shunk(buf));
++ return 0;
++ }
++ if (eklen >= INT_MAX) {
++ proposal_error(parser, "encryption key length '"PRI_SHUNK"' WAY too big",
++ PRI_shunk(buf));
++ return 0;
++ }
++ if (eklen == 0) {
++ proposal_error(parser, "encryption key length is zero");
++ return 0;
++ }
++ return eklen;
++}
++
++/*
++ * Try to parse any of -, _,
++ * , or . Strings like aes_gcm_16 and
++ * aes_gcm_16_256 end up in alg[0], while strings like aes_gcm_16-256
++ * end up in alg[0]-alg[1].
++ */
++
++struct token {
++ char sep;
++ shunk_t alg;
++};
++
++static bool parse_encrypt(struct proposal_parser *parser,
++ struct token **tokens,
++ struct v1_proposal *proposal)
++{
++ shunk_t ealg = (*tokens)[0].alg;
++ shunk_t eklen = (*tokens)[1].alg;
++ if (eklen.len > 0 && isdigit(eklen.ptr[0])) {
++ /* assume - */
++ int enckeylen = parse_eklen(parser, eklen);
++ if (enckeylen <= 0) {
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ /* print - */
++ shunk_t print_name = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr);
++ proposal->enckeylen = enckeylen;
++ proposal->encrypt =
++ encrypt_desc(lookup_byname(parser,
++ encrypt_alg_byname,
++ ealg, proposal->enckeylen,
++ print_name, "encryption"));
++ /* Was - rejected? */
++ if (parser->error[0] != '\0') {
++ return false;
++ }
++ *tokens += 2; /* consume both tokens */
++ return true;
++ }
++ /* try */
++ shunk_t print_name = ealg;
++ proposal->encrypt =
++ encrypt_desc(lookup_byname(parser,
++ encrypt_alg_byname,
++ ealg, proposal->enckeylen,
++ print_name, "encryption"));
++ if (parser->error[0] != '\0') {
++ /*
++ * Could it be or _? Work
++ * backwards skipping any digits.
++ */
++ shunk_t end = shunk2(ealg.ptr + ealg.len, 0);
++ while (end.ptr > ealg.ptr && isdigit(end.ptr[-1])) {
++ end.ptr--;
++ end.len++;
++ }
++ if (end.len == 0) {
++ /*
++ * no trailing and was rejected
++ */
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ /* try to convert */
++ int enckeylen = parse_eklen(parser, end);
++ if (enckeylen <= 0) {
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ proposal->enckeylen = enckeylen;
++ /*
++ * trim from ; and then trim any
++ * trailing '_'
++ */
++ ealg.len = end.ptr - ealg.ptr;
++ if (end.ptr > ealg.ptr && end.ptr[-1] == '_') {
++ ealg.len -= 1;
++ }
++ /* try again */
++ parser->error[0] = '\0';
++ proposal->encrypt =
++ encrypt_desc(lookup_byname(parser,
++ encrypt_alg_byname,
++ ealg, proposal->enckeylen,
++ print_name, "encryption"));
++ if (parser->error[0] != '\0') {
++ return false;
++ }
++ }
++ *tokens += 1; /* consume one token */
++ return true;
++}
++
++static bool parser_proposals_add(struct proposal_parser *parser,
++ struct token *tokens, struct v1_proposal proposal,
++ struct proposals *proposals)
++{
++ LSWDBGP(DBG_PROPOSAL_PARSER, buf) {
++ lswlogs(buf, "algs:");
++ for (struct token *token = tokens; token->alg.ptr != NULL; token++) {
++ lswlogf(buf, " algs[%tu] = '"PRI_SHUNK"'",
++ token - tokens, PRI_shunk(token->alg));
++ }
++ }
++
++ bool lookup_encrypt = parser->protocol->encrypt_alg_byname != NULL;
++ if (!lookup_encrypt && IMPAIR(PROPOSAL_PARSER)) {
++ /* Force lookup, will discard any error. */
++ lookup_encrypt = true;
++ }
++ if (lookup_encrypt && tokens->alg.ptr != NULL && tokens->sep != ';') {
++ if (!parse_encrypt(parser, &tokens, &proposal)) {
++ if (IMPAIR(PROPOSAL_PARSER)) {
++ /* ignore the lookup and stumble on */
++ parser->error[0] = '\0';
++ } else {
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ }
++ }
++
++ bool lookup_prf = parser->protocol->prf_alg_byname != NULL;
++ if (!lookup_prf && IMPAIR(PROPOSAL_PARSER)) {
++ /*
++ * When impaired, only force PRF lookup when the the
++ * token after this one is a valid INTEG algorithm.
++ * Otherwise something like ah=sha1 gets parsed as
++ * ah=[encr]-sha1-[integ]-[dh] instead of
++ * ah=[encr]-[prf]-sha1-[dh].
++ */
++ shunk_t prf = tokens[0].alg;
++ shunk_t integ = tokens[1].alg;
++ if (prf.ptr != NULL && integ.ptr != NULL) {
++ lookup_prf = (lookup_byname(parser, integ_alg_byname,
++ integ, 0, integ, "integrity")
++ != NULL);
++ parser->error[0] = '\0';
++ }
++ }
++ if (lookup_prf && tokens->alg.ptr != NULL && tokens->sep != ';') {
++ shunk_t prf = tokens[0].alg;
++ proposal.prf = prf_desc(lookup_byname(parser,
++ prf_alg_byname,
++ prf, 0, prf, "PRF"));
++ if (parser->error[0] != '\0') {
++ return false;
++ }
++ tokens += 1; /* consume one arg */
++ }
++
++ /*
++ * By default, don't allow IKE's [...]---[....].
++ * Instead fill in integrity using the above PRF.
++ *
++ * XXX: The parser and output isn't consistent in that for ESP
++ * it parses - but for IKE it parses
++ * -. This seems to lead to confusion when
++ * printing proposals - ike=aes_gcm-sha1 gets mis-read as as
++ * using sha1 as integrity. ike-aes_gcm-none-sha1 would
++ * clarify this but that makes for a fun parse.
++ */
++ bool lookup_integ = (parser->protocol->prf_alg_byname == NULL &&
++ parser->protocol->integ_alg_byname != NULL);
++ if (!lookup_integ && IMPAIR(PROPOSAL_PARSER)) {
++ /* force things */
++ lookup_integ = true;
++ }
++ if (lookup_integ && tokens->alg.ptr != NULL && tokens->sep != ';') {
++ shunk_t integ = tokens[0].alg;
++ proposal.integ = integ_desc(lookup_byname(parser,
++ integ_alg_byname,
++ integ, 0, integ, "integrity"));
++ if (parser->error[0] != '\0') {
++ if (tokens[1].alg.ptr != NULL) {
++ /*
++ * This alg should have been
++ * integrity, since the next would be
++ * DH; error applies.
++ */
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ if (tokens[1].alg.ptr == NULL &&
++ parser->protocol->prf_alg_byname == NULL) {
++ /*
++ * Only one arg, integrity is prefered
++ * to DH (and no PRF); error applies.
++ */
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ /* let DH try */
++ parser->error[0] = '\0';
++ } else {
++ tokens += 1; /* consume one arg */
++ }
++ }
++
++ bool lookup_dh = parser->protocol->dh_alg_byname || IMPAIR(PROPOSAL_PARSER);
++ if (lookup_dh && tokens->alg.ptr != NULL) {
++ shunk_t dh = tokens[0].alg;
++ proposal.dh = oakley_group_desc(lookup_byname(parser,
++ dh_alg_byname,
++ dh, 0,
++ dh, "DH"));
++ if (parser->error[0] != '\0') {
++ return false;
++ }
++ tokens += 1; /* consume one arg */
++ }
++
++ if (tokens->alg.ptr != NULL) {
++ proposal_error(parser, "'"PRI_SHUNK"' unexpected",
++ PRI_shunk(tokens[0].alg));
++ return false;
++ }
++
++ if (IMPAIR(PROPOSAL_PARSER)) {
++ return add_proposal(parser, proposals, &proposal);
++ } else {
++ return merge_default_proposals(parser, proposals, &proposal);
++ }
++}
++
++bool v1_proposals_parse_str(struct proposal_parser *parser,
++ struct proposals *proposals,
++ shunk_t alg_str)
++{
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("parsing '"PRI_SHUNK"' for %s",
++ PRI_shunk(alg_str), parser->protocol->name));
++
++ /* use default if no string */
++ if (alg_str.ptr == NULL) {
++ const struct v1_proposal proposal = {
++ .protocol = parser->protocol,
++ };
++ return merge_default_proposals(parser, proposals, &proposal);
++ }
++
++ if (alg_str.len == 0) {
++ /* XXX: hack to keep testsuite happy */
++ proposal_error(parser, "String ended with invalid char, just after \"\"");
++ return false;
++ }
++
++ shunk_t prop_ptr = alg_str;
++ do {
++ /* find the next proposal */
++ shunk_t prop = shunk_strsep(&prop_ptr, ",");
++ /* parse it */
++ struct token tokens[8];
++ zero(&tokens);
++ struct token *token = tokens;
++ char last_sep = '\0';
++ shunk_t alg_ptr = prop;
++ do {
++ if (token + 1 >= tokens+elemsof(tokens)) {
++ /* space for NULL? */
++ proposal_error(parser, "proposal too long");
++ return false;
++ }
++ /* find the next alg */
++ shunk_t alg = shunk_strsep(&alg_ptr, "-;,");
++ *token++ = (struct token) {
++ .alg = alg,
++ .sep = last_sep,
++ };
++ last_sep = alg.ptr[alg.len]; /* save separator */
++ } while (alg_ptr.len > 0);
++ struct v1_proposal proposal = {
++ .protocol = parser->protocol,
++ };
++ if (!parser_proposals_add(parser, tokens, proposal,
++ proposals)) {
++ passert(parser->error[0] != '\0');
++ return false;
++ }
++ } while (prop_ptr.len > 0);
++ return true;
++}
+diff -Naur libreswan-3.27-orig/lib/libswan/v2_proposals.c libreswan-3.27/lib/libswan/v2_proposals.c
+--- libreswan-3.27-orig/lib/libswan/v2_proposals.c 1969-12-31 19:00:00.000000000 -0500
++++ libreswan-3.27/lib/libswan/v2_proposals.c 2019-02-15 16:49:23.278267768 -0500
+@@ -0,0 +1,398 @@
++/* V2 algorithm proposal parsing, for libreswan
++ *
++ * Copyright (C) 2019 Andrew Cagney
++ *
++ * 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 "lswlog.h"
++#include "lswalloc.h"
++#include "constants.h"
++#include "proposals.h"
++#include "ike_alg.h"
++#include "ike_alg_integ.h"
++#include "ike_alg_dh.h"
++#include "alg_byname.h"
++
++/*
++ * For all the algorithms, when an algorithm is missing (NULL), and
++ * there are defaults, add them.
++ */
++
++static void merge_algorithms(struct proposal_parser *parser,
++ struct proposal *proposal,
++ enum proposal_algorithm algorithm,
++ const struct ike_alg **defaults)
++{
++ if (defaults == NULL) {
++ return;
++ }
++ if (next_algorithm(proposal, algorithm, NULL) != NULL) {
++ return;
++ }
++ for (const struct ike_alg **alg = defaults; (*alg) != NULL; alg++) {
++ append_algorithm(parser, proposal, algorithm, *alg, 0);
++ }
++}
++
++static bool merge_defaults(struct proposal_parser *parser,
++ struct proposal *proposal)
++{
++ pexpect(parser->policy->version < elemsof(parser->protocol->defaults));
++ const struct proposal_defaults *defaults =
++ parser->protocol->defaults[parser->policy->version];
++ merge_algorithms(parser, proposal, PROPOSAL_encrypt, defaults->encrypt);
++ merge_algorithms(parser, proposal, PROPOSAL_prf, defaults->prf);
++ if (next_algorithm(proposal, PROPOSAL_integ, NULL) == NULL) {
++ if (proposal_encrypt_aead(proposal)) {
++ /*
++ * Since AEAD, integrity is always 'none'.
++ */
++ append_algorithm(parser, proposal, PROPOSAL_integ,
++ &ike_alg_integ_none.common, 0);
++ } else if (defaults->integ != NULL) {
++ /*
++ * Merge in the defaults.
++ */
++ merge_algorithms(parser, proposal, PROPOSAL_integ,
++ defaults->integ);
++ } else if (next_algorithm(proposal, PROPOSAL_prf, NULL) != NULL &&
++ proposal_encrypt_norm(proposal)) {
++ /*
++ * Since non-AEAD, use integrity algorithms
++ * that are implemented using the PRFs.
++ */
++ FOR_EACH_ALGORITHM(proposal, prf, prf) {
++ const struct integ_desc *integ = NULL;
++ for (const struct integ_desc **integp = next_integ_desc(NULL);
++ integp != NULL; integp = next_integ_desc(integp)) {
++ if ((*integp)->prf != NULL &&
++ &(*integp)->prf->common == prf->desc) {
++ integ = *integp;
++ break;
++ }
++ }
++ if (integ == NULL) {
++ proposal_error(parser, "%s integrity derived from PRF '%s' is not supported",
++ parser->protocol->name,
++ prf->desc->name);
++ return false;
++ }
++ append_algorithm(parser, proposal, PROPOSAL_integ,
++ &integ->common, 0);
++ }
++ }
++ }
++ merge_algorithms(parser, proposal, PROPOSAL_dh, defaults->dh);
++ return true;
++}
++
++static bool parse_alg(struct proposal_parser *parser,
++ struct proposal *proposal,
++ enum proposal_algorithm algorithm,
++ alg_byname_fn *alg_byname,
++ shunk_t token, int enckeylen, shunk_t print,
++ const char *what)
++{
++ if (alg_byname == NULL) {
++ /* n/a */
++ return false;
++ }
++ if (token.len == 0) {
++ /* will error at end */
++ return false;
++ }
++ const struct ike_alg *alg = alg_byname(parser, token, enckeylen, print);
++ if (alg == NULL) {
++ if (DBGP(DBG_PROPOSAL_PARSER)) {
++ DBG_log("%s_byname('"PRI_SHUNK"') failed: %s",
++ what, PRI_shunk(token),
++ parser->error);
++ }
++ pexpect(parser->error[0] != '\0');
++ return false;
++ }
++ DBGF(DBG_PROPOSAL_PARSER, "adding %s algorithm %s[_%d]",
++ what, alg->name, enckeylen);
++ append_algorithm(parser, proposal, algorithm, alg, enckeylen);
++ return true;
++}
++
++/*
++ * tokenize into
++ */
++
++struct token {
++ char delim;
++ shunk_t alg;
++ shunk_t input;
++};
++
++static void next(struct token *token)
++{
++ if (token->delim == '\0') {
++ /* first call, set delim to something bogus */
++ token->delim = ' ';
++ } else {
++ token->delim = token->input.ptr != NULL ? token->input.ptr[-1] : ' ';
++ }
++ token->alg = shunk_strsep(&token->input, "-;+");
++ if (DBGP(DBG_PROPOSAL_PARSER)) {
++ if (token->alg.ptr == NULL) {
++ DBG_log("delim: n/a alg: end-of-input");
++ } else {
++ DBG_log("delim: '%c' alg: '"PRI_SHUNK"'",
++ token->delim, PRI_shunk(token->alg));
++ }
++ }
++}
++
++/*
++ * Try to parse any of -, _,
++ * , or using some look-ahead.
++ */
++
++static int parse_eklen(struct proposal_parser *parser, shunk_t buf)
++{
++ /* convert - if present */
++ char *end = NULL;
++ long eklen = strtol(buf.ptr, &end, 10);
++ if (buf.ptr + buf.len != end) {
++ proposal_error(parser, "encryption key length '"PRI_SHUNK"' contains a non-numeric character",
++ PRI_shunk(buf));
++ return 0;
++ }
++ if (eklen >= INT_MAX) {
++ proposal_error(parser, "encryption key length '"PRI_SHUNK"' WAY too big",
++ PRI_shunk(buf));
++ return 0;
++ }
++ if (eklen == 0) {
++ proposal_error(parser, "encryption key length is zero");
++ return 0;
++ }
++ return eklen;
++}
++
++static bool parse_encrypt(struct proposal_parser *parser,
++ struct proposal *proposal, struct token *token)
++{
++ alg_byname_fn *alg_byname = parser->protocol->encrypt_alg_byname;
++ if (alg_byname == NULL) {
++ return false;
++ }
++ if (token->alg.len == 0) {
++ return false;
++ }
++ shunk_t ealg = token->alg;
++ /* try - using look-ahead? */
++ struct token lookahead = *token;
++ next(&lookahead);
++ if (lookahead.delim == '-' &&
++ lookahead.alg.len > 0 &&
++ isdigit(lookahead.alg.ptr[0])) {
++ shunk_t eklen = lookahead.alg;
++ /* assume - */
++ int enckeylen = parse_eklen(parser, eklen);
++ if (enckeylen <= 0) {
++ pexpect(parser->error[0] != '\0');
++ return false;
++ }
++ /* print "-" in errors */
++ shunk_t print_name = shunk2(ealg.ptr, eklen.ptr + eklen.len - ealg.ptr);
++ if (!parse_alg(parser, proposal, PROPOSAL_encrypt, alg_byname,
++ ealg, enckeylen, print_name, "encrypt")) {
++ return false;
++ }
++ *token = lookahead;
++ return true;
++ }
++ /* try (no key len) */
++ shunk_t print_name = token->alg;
++ if (!parse_alg(parser, proposal, PROPOSAL_encrypt, alg_byname,
++ ealg, 0, print_name, "encrypt")) {
++ /*
++ * Could it be or _? Work
++ * backwards skipping any digits.
++ */
++ shunk_t end = shunk2(ealg.ptr + ealg.len, 0);
++ while (end.ptr > ealg.ptr && isdigit(end.ptr[-1])) {
++ end.ptr--;
++ end.len++;
++ }
++ if (end.len == 0) {
++ /*
++ * no trailing and was rejected
++ */
++ pexpect(parser->error[0] != '\0');
++ return false;
++ }
++ /* try to convert */
++ int enckeylen = parse_eklen(parser, end);
++ if (enckeylen <= 0) {
++ pexpect(parser->error[0] != '\0');
++ return false;
++ }
++ /*
++ * trim from ; and then trim any
++ * trailing '_'
++ */
++ ealg.len = end.ptr - ealg.ptr;
++ if (end.ptr > ealg.ptr && end.ptr[-1] == '_') {
++ ealg.len -= 1;
++ }
++ /* try again */
++ if (!parse_alg(parser, proposal, PROPOSAL_encrypt, alg_byname,
++ ealg, enckeylen, print_name, "encrypt")) {
++ return false;
++ }
++ }
++ return true;
++}
++
++static bool parse_proposal(struct proposal_parser *parser,
++ struct proposals *proposals UNUSED, shunk_t input)
++{
++ if (DBGP(DBG_PROPOSAL_PARSER)) {
++ DBG_log("proposal: '"PRI_SHUNK"'", PRI_shunk(input));
++ }
++
++ char error[sizeof(parser->error)] = "";
++ struct proposal *proposal = alloc_proposal(parser);
++
++ struct token token = {
++ .input = input,
++ };
++ next(&token);
++ /*
++ * Encryption is not optional.
++ */
++ bool lookup_encrypt = parser->protocol->encrypt_alg_byname != NULL;
++ if (lookup_encrypt) {
++ if (!parse_encrypt(parser, proposal, &token)) {
++ free_proposal(&proposal);
++ return false;
++ }
++ error[0] = parser->error[0] = '\0';
++ next(&token);
++ while (token.delim == '+' &&
++ parse_encrypt(parser, proposal, &token)) {
++ error[0] = parser->error[0] = '\0';
++ next(&token);
++ }
++ }
++#define PARSE_ALG(STOP, ALG) \
++ if (error[0] == '\0' && parser->error[0] != '\0') { \
++ strcpy(error, parser->error); \
++ DBGF(DBG_PROPOSAL_PARSER, "saved first error: %s", error); \
++ } \
++ if (token.delim != STOP && \
++ parse_alg(parser, proposal, PROPOSAL_##ALG, \
++ parser->protocol->ALG##_alg_byname, \
++ token.alg, 0, token.alg, #ALG)) { \
++ error[0] = parser->error[0] = '\0'; \
++ next(&token); \
++ while (token.delim == '+' && \
++ parse_alg(parser, proposal, PROPOSAL_##ALG, \
++ parser->protocol->ALG##_alg_byname, \
++ token.alg, 0, token.alg, #ALG)) { \
++ error[0] = parser->error[0] = '\0'; \
++ next(&token); \
++ } \
++ }
++ PARSE_ALG(';', prf);
++ /*
++ * By default, don't allow ike=...---... but do
++ * allow esp=...-. In the case of IKE, when integrity
++ * is required, it is filled in using the PRF.
++ *
++ * XXX: The parser and output isn't consistent in that for ESP
++ * it parses - but for IKE it parses
++ * -. This seems to lead to confusion when
++ * printing proposals - ike=aes_gcm-sha1 gets mis-read as as
++ * using sha1 as integrity. ike-aes_gcm-none-sha1 would
++ * clarify this but that makes for a fun parse.
++ */
++ if (parser->protocol->prf_alg_byname == NULL ||
++ IMPAIR(PROPOSAL_PARSER)) {
++ PARSE_ALG(';', integ);
++ }
++ PARSE_ALG('\0', dh);
++ if (error[0] != '\0') {
++ DBGF(DBG_PROPOSAL_PARSER, "return first error: %s", error);
++ free_proposal(&proposal);
++ strcpy(parser->error, error);
++ return false;
++ }
++ if (parser->error[0] != '\0') {
++ DBGF(DBG_PROPOSAL_PARSER, "return last error: %s", parser->error);
++ free_proposal(&proposal);
++ return false;
++ }
++ if (token.alg.ptr != NULL) {
++ proposal_error(parser, "'"PRI_SHUNK"' unexpected",
++ PRI_shunk(token.alg));
++ free_proposal(&proposal);
++ return false;
++ }
++ if (!IMPAIR(PROPOSAL_PARSER) &&
++ !merge_defaults(parser, proposal)) {
++ free_proposal(&proposal);
++ return false;
++ }
++ /* back end? */
++ if (!parser->protocol->proposal_ok(parser, proposal)) {
++ free_proposal(&proposal);
++ return false;
++ }
++ append_proposal(proposals, &proposal);
++ return true;
++}
++
++bool v2_proposals_parse_str(struct proposal_parser *parser,
++ struct proposals *proposals,
++ shunk_t input)
++{
++ DBG(DBG_PROPOSAL_PARSER,
++ DBG_log("parsing '"PRI_SHUNK"' for %s",
++ PRI_shunk(input), parser->protocol->name));
++
++ /* use default if no string */
++ if (input.ptr == NULL) {
++ struct proposal *proposal = alloc_proposal(parser);
++ if (!merge_defaults(parser, proposal)) {
++ free_proposal(&proposal);
++ return false;
++ }
++ append_proposal(proposals, &proposal);
++ return true;
++ }
++
++ if (input.len == 0) {
++ /* XXX: hack to keep testsuite happy */
++ proposal_error(parser, "String ended with invalid char, just after \"\"");
++ return false;
++ }
++
++ do {
++ /* find the next proposal */
++ shunk_t proposal = shunk_strsep(&input, ",");
++ if (!parse_proposal(parser, proposals, proposal)) {
++ pexpect(parser->error[0] != '\0');
++ return false;
++ }
++ } while (input.len > 0);
++ return true;
++}
+diff -Naur libreswan-3.27-orig/programs/algparse/algparse.c libreswan-3.27/programs/algparse/algparse.c
+--- libreswan-3.27-orig/programs/algparse/algparse.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/algparse/algparse.c 2019-02-15 16:49:23.278267768 -0500
+@@ -9,15 +9,15 @@
+ #include "lswconf.h"
+
+ #include "ike_alg.h"
+-#include "alg_info.h"
++#include "proposals.h"
+
+ static bool test_proposals = false;
+ static bool test_algs = false;
+ static bool verbose = false;
+ static bool debug = false;
+ static bool impair = false;
+-static bool ikev1 = false;
+-static bool ikev2 = false;
++static enum ike_version ike_version = IKEv2;
++static unsigned parser_version = 0;
+ static bool fips = false;
+ static bool pfs = false;
+ static int failures = 0;
+@@ -25,23 +25,23 @@
+ enum status { PASSED = 0, FAILED = 1, ERROR = 126, };
+ enum expect { FAIL = false, PASS = true, COUNT, };
+
+-#define CHECK(TYPE,PARSE,OK) { \
++#define CHECK(CHECK,PARSE,OK) { \
+ struct proposal_policy policy = { \
+- .ikev1 = ikev1, \
+- .ikev2 = ikev2, \
++ .version = ike_version, \
++ .parser_version = parser_version, \
+ .alg_is_ok = OK, \
+ .pfs = pfs, \
+ .warning = warning, \
++ .check_pfs_vs_dh = CHECK, \
+ }; \
+ printf("algparse "); \
+ if (fips) { \
+ printf("-fips "); \
+ } \
+- if (ikev1) { \
+- printf("-v1 "); \
+- } \
+- if (ikev2) { \
+- printf("-v2 "); \
++ switch (ike_version) { \
++ case IKEv1: printf("-v1 "); break; \
++ case IKEv2: printf("-v2 "); break; \
++ default: break; \
+ } \
+ if (pfs) { \
+ printf("-pfs "); \
+@@ -52,21 +52,19 @@
+ printf("'%s=%s'\n", #PARSE, algstr); \
+ } \
+ fflush(NULL); \
+- char err_buf[512] = ""; /* ??? big enough? */ \
+- struct alg_info_##TYPE *e = \
+- alg_info_##PARSE##_create_from_str(&policy, \
+- algstr, \
+- err_buf, \
+- sizeof(err_buf)); \
+- if (e != NULL) { \
+- passert(err_buf[0] == '\0'); \
+- FOR_EACH_PROPOSAL_INFO(&e->ai, proposal) { \
++ struct proposal_parser *parser = \
++ PARSE##_proposal_parser(&policy); \
++ struct proposals *proposals = \
++ proposals_from_str(parser, algstr); \
++ if (proposals != NULL) { \
++ pexpect(parser->error[0] == '\0'); \
++ FOR_EACH_PROPOSAL(proposals, proposal) { \
+ LSWLOG_FILE(stdout, log) { \
+ lswlogf(log, "\t"); \
+- lswlog_proposal_info(log, proposal); \
++ fmt_proposal(log, proposal); \
+ } \
+ } \
+- alg_info_free(&e->ai); \
++ proposals_delref(&proposals); \
+ if (expected == FAIL) { \
+ failures++; \
+ fprintf(stderr, \
+@@ -76,8 +74,8 @@
+ algstr == NULL ? "" : algstr); \
+ } \
+ } else { \
+- passert(err_buf[0]); \
+- printf("\tERROR: %s\n", err_buf); \
++ pexpect(parser->error[0]); \
++ printf("\tERROR: %s\n", parser->error); \
+ if (expected == PASS) { \
+ failures++; \
+ fprintf(stderr, \
+@@ -89,6 +87,7 @@
+ failures++; \
+ } \
+ } \
++ free_proposal_parser(&parser); \
+ fflush(NULL); \
+ }
+
+@@ -122,17 +121,17 @@
+
+ static void esp(enum expect expected, const char *algstr)
+ {
+- CHECK(esp, esp, kernel_alg_is_ok);
++ CHECK(true, esp, kernel_alg_is_ok);
+ }
+
+ static void ah(enum expect expected, const char *algstr)
+ {
+- CHECK(esp, ah, kernel_alg_is_ok);
++ CHECK(true, ah, kernel_alg_is_ok);
+ }
+
+ static void ike(enum expect expected, const char *algstr)
+ {
+- CHECK(ike, ike, ike_alg_is_ike);
++ CHECK(false, ike, ike_alg_is_ike);
+ }
+
+ typedef void (protocol_t)(enum expect expected, const char *);
+@@ -203,8 +202,8 @@
+ esp(!fips, "3des-sha1;modp1024");
+ esp(!fips, "3des-sha1;modp1536");
+ esp(true, "3des-sha1;modp2048");
+- esp(!ikev1, "3des-sha1;dh21");
+- esp(!ikev1, "3des-sha1;ecp_521");
++ esp(ike_version == IKEv2, "3des-sha1;dh21");
++ esp(ike_version == IKEv2, "3des-sha1;ecp_521");
+ esp(false, "3des-sha1;dh23");
+ esp(false, "3des-sha1;dh24");
+ esp(true, "3des-sha1");
+@@ -218,7 +217,7 @@
+ esp(true, "aes-sha384");
+ esp(true, "aes-sha512");
+ esp(true, "aes128-sha1");
+- esp(true, "aes128-aes_xcbc");
++ esp(!fips, "aes128-aes_xcbc");
+ esp(true, "aes192-sha1");
+ esp(true, "aes256-sha1");
+ esp(true, "aes256-sha");
+@@ -300,18 +299,18 @@
+ esp(!fips, "twofish");
+
+ esp(!fips, "camellia_cbc_256-hmac_sha2_512_256;modp8192"); /* long */
+- esp(!fips, "null_auth_aes_gmac_256-null;modp8192"); /* long */
++ esp(true, "null_auth_aes_gmac_256-null;modp8192"); /* long */
+ esp(true, "3des-sha1;modp8192"); /* allow ';' when unambigious */
+ esp(true, "3des-sha1-modp8192"); /* allow '-' when unambigious */
+ esp(!pfs, "aes-sha1,3des-sha1;modp8192");
+ esp(true, "aes-sha1-modp8192,3des-sha1-modp8192"); /* silly */
+ esp(true, "aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192"); /* suppress duplicates */
+
+- esp(!ikev1, "aes;none");
+- esp(!ikev1 && !pfs, "aes;none,aes");
+- esp(!ikev1, "aes;none,aes;modp2048");
+- esp(!ikev1, "aes-sha1-none");
+- esp(!ikev1, "aes-sha1;none");
++ esp(ike_version == IKEv2, "aes;none");
++ esp(ike_version == IKEv2 && !pfs, "aes;none,aes");
++ esp(ike_version == IKEv2, "aes;none,aes;modp2048");
++ esp(ike_version == IKEv2, "aes-sha1-none");
++ esp(ike_version == IKEv2, "aes-sha1;none");
+
+ /*
+ * should this be supported - for now man page says not
+@@ -374,9 +373,9 @@
+ ah(true, "sha2_256");
+ ah(true, "sha2_384");
+ ah(true, "sha2_512");
+- ah(true, "aes_xcbc");
+- ah(!ikev1, "sha2-none");
+- ah(!ikev1, "sha2;none");
++ ah(!fips, "aes_xcbc");
++ ah(ike_version == IKEv2, "sha2-none");
++ ah(ike_version == IKEv2, "sha2;none");
+ ah(true, "sha1-modp8192,sha1-modp8192,sha1-modp8192"); /* suppress duplicates */
+ ah(impair, "aes-sha1");
+ ah(false, "vanityhash1");
+@@ -400,12 +399,15 @@
+ ike(true, "3des;dh21");
+ ike(true, "3des-sha1;dh21");
+ ike(true, "3des-sha1-ecp_521");
+- ike(!ikev1, "aes_gcm");
++ ike(ike_version == IKEv2, "aes_gcm");
+ ike(true, "aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192"); /* suppress duplicates */
+ ike(false, "aes;none");
+ ike(false, "id2"); /* should be rejected; idXXX removed */
+ ike(false, "3des-id2"); /* should be rejected; idXXX removed */
+ ike(false, "aes_ccm"); /* ESP/AH only */
++ ike(impair, "aes_gcm-sha1-none-modp2048");
++ ike(impair, "aes_gcm+aes_gcm-sha1-none-modp2048");
++ ike(false, "aes+aes_gcm"); /* mixing AEAD and NORM encryption */
+ }
+
+ static void usage(void)
+@@ -432,9 +434,8 @@
+ "\n"
+ "Additional options:\n"
+ "\n"
+- " -v1 | -ikev1: require IKEv1 support\n"
+- " -v2 | -ikev2: require IKEv2 support\n"
+- " default: require either IKEv1 or IKEv2 support\n"
++ " -v2 | -ikev2: configure for IKEv2 (default)\n"
++ " -v1 | -ikev1: configure for IKEv1\n"
+ " -pfs | -pfs=yes | -pfs=no: specify PFS (perfect forward privicy)\n"
+ " default: no\n"
+ " -fips | -fips=yes | -fips=no: force NSS's FIPS mode\n"
+@@ -446,6 +447,8 @@
+ " -v --verbose: be more verbose\n"
+ " -d --debug: enable debug logging\n"
+ " -i --impair: disable all algorithm parser checks\n"
++ " -p1: simple parser\n"
++ " -p2: complex parser\n"
+ "\n"
+ "Examples:\n"
+ "\n"
+@@ -483,10 +486,14 @@
+ test_proposals = true;
+ } else if (streq(arg, "ta")) {
+ test_algs = true;
++ } else if (streq(arg, "p1")) {
++ parser_version = 1;
++ } else if (streq(arg, "p2")) {
++ parser_version = 2;
+ } else if (streq(arg, "v1") || streq(arg, "ikev1")) {
+- ikev1 = true;
++ ike_version = IKEv1;
+ } else if (streq(arg, "v2") || streq(arg, "ikev2")) {
+- ikev2 = true;
++ ike_version = IKEv2;
+ } else if (streq(arg, "pfs") || streq(arg, "pfs=yes") || streq(arg, "pfs=on")) {
+ pfs = true;
+ } else if (streq(arg, "pfs=no") || streq(arg, "pfs=off")) {
+diff -Naur libreswan-3.27-orig/programs/pluto/connections.c libreswan-3.27/programs/pluto/connections.c
+--- libreswan-3.27-orig/programs/pluto/connections.c 2019-02-15 16:31:43.031408048 -0500
++++ libreswan-3.27/programs/pluto/connections.c 2019-02-15 16:32:28.986835407 -0500
+@@ -69,7 +69,7 @@
+ #include "peerlog.h"
+ #include "keys.h"
+ #include "whack.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "spdb.h"
+ #include "ike_alg.h"
+ #include "kernel_alg.h"
+@@ -352,17 +352,13 @@
+ sr = next_sr;
+ }
+
+- if (c->alg_info_ike != NULL) {
+- alg_info_delref(&c->alg_info_ike->ai);
+- c->alg_info_ike = NULL;
+- }
+- free_ikev2_proposals(&c->ike_proposals);
++ proposals_delref(&c->ike_proposals.p);
++ proposals_delref(&c->child_proposals.p);
+
+- if (c->alg_info_esp != NULL) {
+- alg_info_delref(&c->alg_info_esp->ai);
+- c->alg_info_esp = NULL;
+- }
+- free_ikev2_proposals(&c->esp_or_ah_proposals);
++ free_ikev2_proposals(&c->v2_ike_proposals);
++ free_ikev2_proposals(&c->v2_ike_auth_child_proposals);
++ free_ikev2_proposals(&c->v2_create_child_proposals);
++ c->v2_create_child_proposals_default_dh = NULL; /* static pointer */
+
+ pfree(c);
+ }
+@@ -807,11 +803,8 @@
+ }
+
+ /* increment references to algo's, if any */
+- if (c->alg_info_ike != NULL)
+- alg_info_addref(&c->alg_info_ike->ai);
+-
+- if (c->alg_info_esp != NULL)
+- alg_info_addref(&c->alg_info_esp->ai);
++ proposals_addref(&c->ike_proposals.p);
++ proposals_addref(&c->child_proposals.p);
+
+ if (c->pool != NULL)
+ reference_addresspool(c);
+@@ -1496,8 +1489,6 @@
+ c->connalias = wm->connalias;
+ c->dnshostname = wm->dnshostname;
+ c->policy = wm->policy;
+- c->alg_info_ike = NULL;
+- c->alg_info_esp = NULL;
+ c->sighash_policy = wm->sighash_policy;
+
+ if (NEVER_NEGOTIATE(c->policy)) {
+@@ -1556,49 +1547,35 @@
+ /* IKE cipher suites */
+
+ if (!LIN(POLICY_AUTH_NEVER, wm->policy) && wm->ike != NULL) {
+- char err_buf[256] = ""; /* ??? big enough? */
+
+ const struct proposal_policy proposal_policy = {
+- .ikev1 = LIN(POLICY_IKEV1_ALLOW, wm->policy),
+- /*
+- * logic needs to match pick_initiator()
+- *
+- * XXX: Once pluto is changed to IKEv1 XOR
+- * IKEv2 it should be possible to move this
+- * magic into pluto proper and instead pass a
+- * simple boolean.
+- */
+- .ikev2 = LIN(POLICY_IKEV2_PROPOSE | POLICY_IKEV2_ALLOW, wm->policy),
++ /* * logic needs to match pick_initiator() */
++ .version = LIN(POLICY_IKEV2_ALLOW, wm->policy) ? IKEv2 : IKEv1,
+ .alg_is_ok = ike_alg_is_ike,
+ .pfs = LIN(POLICY_PFS, wm->policy),
++ .check_pfs_vs_dh = false,
+ .warning = libreswan_log,
+ };
+
+- c->alg_info_ike = alg_info_ike_create_from_str(&proposal_policy, wm->ike,
+- err_buf, sizeof(err_buf));
++ struct proposal_parser *parser = ike_proposal_parser(&proposal_policy);
++ c->ike_proposals.p = proposals_from_str(parser, wm->ike);
+
+- if (c->alg_info_ike == NULL) {
+- pexpect(err_buf[0]); /* something */
++ if (c->ike_proposals.p == NULL) {
++ pexpect(parser->error[0]); /* something */
+ loglog(RC_FATAL, "Failed to add connection \"%s\": ike string error: %s",
+- wm->name, err_buf);
++ wm->name, parser->error);
++ free_proposal_parser(&parser);
+ pfree(c);
+ return;
+ }
++ free_proposal_parser(&parser);
+
+- /* from here on, error returns should alg_info_free(&c->alg_info_ike->ai); */
++ /* from here on, error returns should alg_info_free(&c->ike_proposals->ai); */
+
+ LSWDBGP(DBG_CRYPT | DBG_CONTROL, buf) {
+ lswlogs(buf, "ike (phase1) algorithm values: ");
+- lswlog_alg_info(buf, &c->alg_info_ike->ai);
++ fmt_proposals(buf, c->ike_proposals.p);
+ };
+- if (c->alg_info_ike->ai.alg_info_cnt == 0) {
+- loglog(RC_FATAL,
+- "Failed to add connection \"%s\": got 0 transforms for ike=\"%s\"",
+- wm->name, wm->ike);
+- alg_info_free(&c->alg_info_ike->ai);
+- pfree(c);
+- return;
+- }
+ }
+
+ /* ESP or AH cipher suites (but not both) */
+@@ -1607,10 +1584,7 @@
+ DBG(DBG_CONTROL,
+ DBG_log("from whack: got --esp=%s", wm->esp));
+
+- char err_buf[256] = ""; /* ??? big enough? */
+-
+ const struct proposal_policy proposal_policy = {
+- .ikev1 = LIN(POLICY_IKEV1_ALLOW, wm->policy),
+ /*
+ * logic needs to match pick_initiator()
+ *
+@@ -1619,11 +1593,16 @@
+ * magic into pluto proper and instead pass a
+ * simple boolean.
+ */
+- .ikev2 = LIN(POLICY_IKEV2_PROPOSE | POLICY_IKEV2_ALLOW, wm->policy),
++ .version = LIN(POLICY_IKEV2_ALLOW, wm->policy) ? IKEv2 : IKEv1,
+ .alg_is_ok = kernel_alg_is_ok,
+ .pfs = LIN(POLICY_PFS, wm->policy),
++ .check_pfs_vs_dh = true,
+ .warning = libreswan_log,
+ };
++ struct proposal_parser *(*fn)(const struct proposal_policy *policy) =
++ (c->policy & POLICY_ENCRYPT ? esp_proposal_parser :
++ ah_proposal_parser);
++ struct proposal_parser *parser = fn(&proposal_policy);
+
+ /*
+ * We checked above that exactly one of
+@@ -1632,42 +1611,22 @@
+ * function is called (and those functions are
+ * almost identical).
+ */
+- c->alg_info_esp =
+- /* function: */
+- (c->policy & POLICY_ENCRYPT ?
+- alg_info_esp_create_from_str :
+- alg_info_ah_create_from_str)
+- /* arguments: */
+- (&proposal_policy,
+- wm->esp, err_buf, sizeof(err_buf));
+-
+- if (c->alg_info_esp == NULL) {
++ c->child_proposals.p = proposals_from_str(parser, wm->esp);
++ if (c->child_proposals.p == NULL) {
+ loglog(RC_FATAL,
+ "Failed to add connection \"%s\", esp=\"%s\" is invalid: %s",
+- wm->name, wm->esp, err_buf);
+- if (c->alg_info_ike != NULL)
+- alg_info_free(&c->alg_info_ike->ai);
++ wm->name, wm->esp, parser->error);
++ free_proposal_parser(&parser);
+ pfree(c);
+ return;
+ }
++ free_proposal_parser(&parser);
+
+- /* from here on, error returns should alg_info_free(&c->alg_info_esp->ai); */
+-
+- if (c->alg_info_esp->ai.alg_info_cnt == 0) {
+- loglog(RC_FATAL,
+- "Failed to add connection \"%s\", esp=\"%s\" contained 0 valid transforms",
+- wm->name, wm->esp);
+- if (c->alg_info_ike != NULL)
+- alg_info_free(&c->alg_info_ike->ai);
+- if (c->alg_info_esp != NULL) \
+- alg_info_free(&c->alg_info_esp->ai);
+- pfree(c);
+- return;
+- }
++ /* from here on, error returns should alg_info_free(&c->child_proposals->ai); */
+
+ LSWDBGP(DBG_CONTROL, buf) {
+ lswlogs(buf, "ESP/AH string values: ");
+- lswlog_alg_info(buf, &c->alg_info_esp->ai);
++ fmt_proposals(buf, c->child_proposals.p);
+ };
+ }
+
+diff -Naur libreswan-3.27-orig/programs/pluto/connections.h libreswan-3.27/programs/pluto/connections.h
+--- libreswan-3.27-orig/programs/pluto/connections.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/connections.h 2019-02-15 16:32:28.986835407 -0500
+@@ -29,6 +29,7 @@
+ #define CONNECTIONS_H
+
+ #include "fd.h"
++#include "proposals.h"
+
+ /* There are two kinds of connections:
+ * - ISAKMP connections, between hosts (for IKE communication)
+@@ -318,8 +319,8 @@
+ /* if multiple policies, next one to apply */
+ struct connection *policy_next;
+
+- struct alg_info_esp *alg_info_esp; /* ??? OK for AH too? */
+- struct alg_info_ike *alg_info_ike;
++ struct ike_proposals ike_proposals;
++ struct child_proposals child_proposals;
+
+ /*
+ * The ALG_INFO converted to IKEv2 format.
+@@ -327,9 +328,23 @@
+ * Since they are allocated on-demand so there's no need to
+ * worry about copying them when a connection object gets
+ * cloned.
++ *
++ * For a child SA, two different proposals are used:
++ *
++ * - during the IKE_AUTH exchange a proposal stripped of any
++ * DH (it uses keying material from the IKE SA's SKSEED).
++ *
++ * - during a CREATE_CHILD_SA exchange, a mash up of the
++ * proposal and the IKE SA's DH algorithm. Since the IKE
++ * SA's DH can change, it too is saved so a rebuild can be
++ * triggered.
++ *
++ * XXX: has to be a better way?
+ */
+- struct ikev2_proposals *ike_proposals;
+- struct ikev2_proposals *esp_or_ah_proposals;
++ struct ikev2_proposals *v2_ike_proposals;
++ struct ikev2_proposals *v2_ike_auth_child_proposals;
++ struct ikev2_proposals *v2_create_child_proposals;
++ const struct oakley_group_desc *v2_create_child_proposals_default_dh;
+
+ /* host_pair linkage */
+ struct host_pair *host_pair;
+diff -Naur libreswan-3.27-orig/programs/pluto/crypto.c libreswan-3.27/programs/pluto/crypto.c
+--- libreswan-3.27-orig/programs/pluto/crypto.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/crypto.c 2019-02-15 16:32:28.987835416 -0500
+@@ -33,11 +33,12 @@
+ #include "state.h"
+ #include "log.h"
+ #include "crypto.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "ike_alg.h"
+ #include "test_buffer.h"
+ #include "connections.h"
+
++#include "ike_alg_integ.h"
+ #include "kernel_alg.h"
+
+ /*
+@@ -47,7 +48,7 @@
+ */
+ void ike_alg_show_connection(const struct connection *c, const char *instance)
+ {
+- if (c->alg_info_ike != NULL) {
++ if (c->ike_proposals.p != NULL) {
+ /*
+ * List the algorithms as found in alg_info_ike and as
+ * will be fed into the proposal code.
+@@ -79,30 +80,46 @@
+ LSWLOG_WHACK(RC_COMMENT, buf) {
+ lswlogf(buf, "\"%s\"%s: IKE algorithms: ",
+ c->name, instance);
+- lswlog_alg_info(buf, &c->alg_info_ike->ai);
++ fmt_proposals(buf, c->ike_proposals.p);
+ }
+ }
+
+ const struct state *st = state_with_serialno(c->newest_isakmp_sa);
+
+ if (st != NULL) {
+- /*
+- * Convert the crypt-suite into 'struct proposal_info'
+- * so that the parser's print-alg code can be used.
+- */
+- const struct proposal_info p = {
+- .encrypt = st->st_oakley.ta_encrypt,
+- .enckeylen = st->st_oakley.enckeylen,
+- .prf = st->st_oakley.ta_prf,
+- .integ = st->st_oakley.ta_integ,
+- .dh = st->st_oakley.ta_dh,
+- };
+- const char *v = st->st_ikev2 ? "IKEv2" : "IKE";
+ LSWLOG_WHACK(RC_COMMENT, buf) {
+ lswlogf(buf,
+ "\"%s\"%s: %s algorithm newest: ",
+- c->name, instance, v);
+- lswlog_proposal_info(buf, &p);
++ c->name, instance,
++ "IKE");
++ const struct trans_attrs *ta = &st->st_oakley;
++ const char *sep = "";
++ if (ta->ta_encrypt != NULL) {
++ lswlogs(buf, sep); sep = "-";
++ lswlogs(buf, ta->ta_encrypt->common.fqn);
++ if (ta->enckeylen != 0) {
++ lswlogf(buf, "_%d", ta->enckeylen);
++ }
++ }
++ if (ta->ta_prf != NULL) {
++ lswlogs(buf, sep); sep = "-";
++ lswlogs(buf, ta->ta_prf->common.fqn);
++ }
++ /* XXX: should just print everything */
++ if (ta->ta_integ != NULL) {
++ if ((ta->ta_prf == NULL) ||
++ (encrypt_desc_is_aead(ta->ta_encrypt) &&
++ ta->ta_integ != &ike_alg_integ_none) ||
++ (!encrypt_desc_is_aead(ta->ta_encrypt) &&
++ ta->ta_integ->prf != ta->ta_prf)) {
++ lswlogs(buf, sep); sep = "-";
++ lswlogs(buf, ta->ta_integ->common.fqn);
++ }
++ }
++ if (ta->ta_dh != NULL) {
++ lswlogs(buf, sep); sep = "-";
++ lswlogs(buf, ta->ta_dh->common.fqn);
++ }
+ }
+ }
+ }
+diff -Naur libreswan-3.27-orig/programs/pluto/db_ops.c libreswan-3.27/programs/pluto/db_ops.c
+--- libreswan-3.27-orig/programs/pluto/db_ops.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/db_ops.c 2019-02-15 16:32:28.987835416 -0500
+@@ -62,6 +62,7 @@
+ #include "defs.h"
+ #include "state.h"
+ #include "packet.h"
++#include "proposals.h"
+ #include "spdb.h"
+ #include "db_ops.h"
+ #include "log.h"
+diff -Naur libreswan-3.27-orig/programs/pluto/hmac.c libreswan-3.27/programs/pluto/hmac.c
+--- libreswan-3.27-orig/programs/pluto/hmac.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/hmac.c 2019-02-15 16:32:28.988835425 -0500
+@@ -27,7 +27,7 @@
+ #include "constants.h"
+ #include "defs.h"
+ #include "crypto.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "ike_alg.h"
+
+ #include
+diff -Naur libreswan-3.27-orig/programs/pluto/hostpair.c libreswan-3.27/programs/pluto/hostpair.c
+--- libreswan-3.27-orig/programs/pluto/hostpair.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/hostpair.c 2019-02-15 16:32:28.988835425 -0500
+@@ -57,7 +57,7 @@
+ #include "log.h"
+ #include "keys.h"
+ #include "whack.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "spdb.h"
+ #include "ike_alg.h"
+ #include "kernel_alg.h"
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_aggr.c libreswan-3.27/programs/pluto/ikev1_aggr.c
+--- libreswan-3.27-orig/programs/pluto/ikev1_aggr.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev1_aggr.c 2019-02-15 16:32:28.989835435 -0500
+@@ -24,7 +24,7 @@
+
+ #include "constants.h" /* for dup_any()!?! ... */
+ #include "lswlog.h"
+-#include "alg_info.h"
++#include "proposals.h"
+
+ #include "defs.h"
+ #include "state.h"
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev1.h libreswan-3.27/programs/pluto/ikev1.h
+--- libreswan-3.27-orig/programs/pluto/ikev1.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev1.h 2019-02-15 16:32:28.989835435 -0500
+@@ -6,11 +6,14 @@
+ #include "packet.h" /* for pb_stream */
+ #include "fd.h"
+
++struct child_proposals;
++struct ike_proposals;
++
+ /* ikev1.c */
+
+ extern void init_ikev1(void);
+
+-const struct oakley_group_desc *ikev1_quick_pfs(struct alg_info_esp *aie);
++const struct oakley_group_desc *ikev1_quick_pfs(const struct child_proposals proposals);
+
+ void ikev1_init_out_pbs_echo_hdr(struct msg_digest *md, bool enc, uint8_t np,
+ pb_stream *output_stream, uint8_t *output_buffer,
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_quick.c libreswan-3.27/programs/pluto/ikev1_quick.c
+--- libreswan-3.27-orig/programs/pluto/ikev1_quick.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev1_quick.c 2019-02-15 16:32:28.990835444 -0500
+@@ -79,20 +79,22 @@
+ #include "virtual.h" /* needs connections.h */
+ #include "ikev1_dpd.h"
+ #include "pluto_x509.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "ip_address.h"
+
+ #include
+
+-const struct oakley_group_desc *ikev1_quick_pfs(struct alg_info_esp *aie)
++const struct oakley_group_desc *ikev1_quick_pfs(const struct child_proposals proposals)
+ {
+- if (aie == NULL) {
++ if (proposals.p == NULL) {
+ return NULL;
+ }
+- if (aie->ai.alg_info_cnt == 0) {
++ struct proposal *proposal = next_proposal(proposals.p, NULL);
++ struct algorithm *dh = next_algorithm(proposal, PROPOSAL_dh, NULL);
++ if (dh == NULL) {
+ return NULL;
+ }
+- return aie->ai.proposals[0].dh;
++ return dh_desc(dh->desc);
+ }
+
+ /* accept_PFS_KE
+@@ -816,7 +818,7 @@
+ * use that group.
+ * if not, fallback to old use-same-as-P1 behaviour
+ */
+- st->st_pfs_group = ikev1_quick_pfs(c->alg_info_esp);
++ st->st_pfs_group = ikev1_quick_pfs(c->child_proposals);
+ /* otherwise, use the same group as during Phase 1:
+ * since no negotiation is possible, we pick one that is
+ * very likely supported.
+@@ -832,8 +834,8 @@
+ }
+ lswlogf(buf, " {using isakmp#%lu msgid:%08" PRIx32 " proposal=",
+ isakmp_sa->st_serialno, st->st_msgid);
+- if (st->st_connection->alg_info_esp != NULL) {
+- lswlog_alg_info(buf, &st->st_connection->alg_info_esp->ai);
++ if (st->st_connection->child_proposals.p != NULL) {
++ fmt_proposals(buf, st->st_connection->child_proposals.p);
+ } else {
+ lswlogf(buf, "defaults");
+ }
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev1_spdb_struct.c libreswan-3.27/programs/pluto/ikev1_spdb_struct.c
+--- libreswan-3.27-orig/programs/pluto/ikev1_spdb_struct.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev1_spdb_struct.c 2019-02-15 16:32:28.991835453 -0500
+@@ -47,7 +47,7 @@
+ #include "crypto.h"
+
+ #include "ikev1.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "kernel_alg.h"
+ #include "ike_alg.h"
+ #include "ike_alg_encrypt.h"
+@@ -305,17 +305,18 @@
+ return false;
+ }
+
+- if (c->alg_info_esp== NULL) {
++ if (c->child_proposals.p == NULL) {
+ DBGF(DBG_CONTROL, "ESP IPsec Transform verified unconditionally; no alg_info to check against");
+ return true;
+ }
+
+- FOR_EACH_ESP_INFO(c->alg_info_esp, esp_info) {
+- if (esp_info->encrypt == ta->ta_encrypt &&
+- (esp_info->enckeylen == 0 ||
+- ta->enckeylen == 0 ||
+- esp_info->enckeylen == ta->enckeylen) &&
+- esp_info->integ == ta->ta_integ) {
++ FOR_EACH_PROPOSAL(c->child_proposals.p, proposal) {
++ struct v1_proposal algs = v1_proposal(proposal);
++ if (algs.encrypt == ta->ta_encrypt &&
++ (algs.enckeylen == 0 ||
++ ta->enckeylen == 0 ||
++ algs.enckeylen == ta->enckeylen) &&
++ algs.integ == ta->ta_integ) {
+ DBG(DBG_CONTROL,
+ DBG_log("ESP IPsec Transform verified; matches alg_info entry"));
+ return true;
+@@ -351,14 +352,15 @@
+ ta->ta_dh->common.fqn);
+ return false;
+ }
+- if (c->alg_info_esp == NULL) {
++ if (c->child_proposals.p == NULL) {
+ DBG(DBG_CONTROL,
+ DBG_log("AH IPsec Transform verified unconditionally; no alg_info to check against"));
+ return true;
+ }
+
+- FOR_EACH_ESP_INFO(c->alg_info_esp, esp_info) { /* really AH */
+- if (esp_info->integ == ta->ta_integ) {
++ FOR_EACH_PROPOSAL(c->child_proposals.p, proposal) { /* really AH */
++ struct v1_proposal algs = v1_proposal(proposal);
++ if (algs.integ == ta->ta_integ) {
+ DBG(DBG_CONTROL,
+ DBG_log("ESP IPsec Transform verified; matches alg_info entry"));
+ return true;
+@@ -401,12 +403,12 @@
+ * Aggr-Mode - Max transforms == 2 - Multiple
+ * transforms, 1 DH group
+ */
+- revised_sadb = oakley_alg_makedb(st->st_connection->alg_info_ike,
++ revised_sadb = oakley_alg_makedb(st->st_connection->ike_proposals,
+ auth_method,
+ aggressive_mode);
+ } else {
+ revised_sadb = kernel_alg_makedb(st->st_connection->policy,
+- st->st_connection->alg_info_esp,
++ st->st_connection->child_proposals,
+ TRUE);
+
+ /* add IPcomp proposal if policy asks for it */
+@@ -984,7 +986,7 @@
+ }
+
+ static bool ikev1_verify_ike(const struct trans_attrs *ta,
+- struct alg_info_ike *alg_info_ike)
++ struct ike_proposals ike_proposals)
+ {
+ if (ta->ta_encrypt == NULL) {
+ loglog(RC_LOG_SERIOUS,
+@@ -1005,7 +1007,7 @@
+ loglog(RC_LOG_SERIOUS, "OAKLEY proposal refused: missing DH");
+ return false;
+ }
+- if (alg_info_ike == NULL) {
++ if (ike_proposals.p == NULL) {
+ DBG(DBG_CONTROL,
+ DBG_log("OAKLEY proposal verified unconditionally; no alg_info to check against"));
+ return true;
+@@ -1016,14 +1018,14 @@
+ * if specified in "esp" string
+ */
+ bool ealg_insecure = (ta->enckeylen < 128);
+-
+- FOR_EACH_IKE_INFO(alg_info_ike, ike_info) {
+- if (ike_info->encrypt == ta->ta_encrypt &&
+- (ike_info->enckeylen == 0 ||
++ FOR_EACH_PROPOSAL(ike_proposals.p, proposal) {
++ struct v1_proposal algs = v1_proposal(proposal);
++ if (algs.encrypt == ta->ta_encrypt &&
++ (algs.enckeylen == 0 ||
+ ta->enckeylen == 0 ||
+- ike_info->enckeylen == ta->enckeylen) &&
+- ike_info->prf == ta->ta_prf &&
+- ike_info->dh == ta->ta_dh) {
++ algs.enckeylen == ta->enckeylen) &&
++ algs.prf == ta->ta_prf &&
++ algs.dh == ta->ta_dh) {
+ if (ealg_insecure) {
+ loglog(RC_LOG_SERIOUS,
+ "You should NOT use insecure/broken IKE algorithms (%s)!",
+@@ -1522,7 +1524,7 @@
+ }
+ /*
+ * check if this keylen is compatible
+- * with specified alg_info_ike.
++ * with specified ike_proposals.
+ */
+ if (!encrypt_has_key_bit_length(ta.ta_encrypt, val)) {
+ ugh = "peer proposed key_len not valid for encrypt algo setup specified";
+@@ -1584,9 +1586,9 @@
+ }
+
+ /*
+- * ML: at last check for allowed transforms in alg_info_ike
++ * ML: at last check for allowed transforms in ike_proposals
+ */
+- if (!ikev1_verify_ike(&ta, c->alg_info_ike)) {
++ if (!ikev1_verify_ike(&ta, c->ike_proposals)) {
+ /*
+ * already logged; UGH acts as a skip
+ * rest of checks flag
+@@ -1715,7 +1717,7 @@
+ const struct connection *c = st->st_connection;
+
+ /*
+- * Construct the proposals by combining ALG_INFO_IKE with the
++ * Construct the proposals by combining IKE_PROPOSALS with the
+ * AUTH (proof of identity) extracted from the aggressive mode
+ * SADB. As if by magic, attrs[2] is always the
+ * authentication method.
+@@ -1733,7 +1735,7 @@
+ * Max transforms == 2 - Multiple transforms, 1 DH
+ * group
+ */
+- revised_sadb = oakley_alg_makedb(c->alg_info_ike,
++ revised_sadb = oakley_alg_makedb(c->ike_proposals,
+ auth_method, TRUE);
+ }
+
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev2.c libreswan-3.27/programs/pluto/ikev2.c
+--- libreswan-3.27-orig/programs/pluto/ikev2.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev2.c 2019-02-15 16:32:28.993835472 -0500
+@@ -68,7 +68,7 @@
+ #include "vendor.h"
+ #include "ip_address.h"
+ #include "ikev2_send.h"
+-#include "alg_info.h" /* for ike_info / esp_info */
++#include "proposals.h" /* for ike_info / esp_info */
+
+ #include "ietf_constants.h"
+
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev2_crypto.c libreswan-3.27/programs/pluto/ikev2_crypto.c
+--- libreswan-3.27-orig/programs/pluto/ikev2_crypto.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev2_crypto.c 2019-02-15 16:32:28.993835472 -0500
+@@ -52,7 +52,7 @@
+ #include "ikev2.h"
+ #include "ikev2_prf.h"
+ #include "ike_alg.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "kernel_alg.h"
+ #include "crypt_symkey.h"
+ #include "ikev2_prf.h"
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev2.h libreswan-3.27/programs/pluto/ikev2.h
+--- libreswan-3.27-orig/programs/pluto/ikev2.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev2.h 2019-02-15 16:32:28.994835481 -0500
+@@ -97,11 +97,27 @@
+ void free_ikev2_proposal(struct ikev2_proposal **proposal);
+ void free_ikev2_proposals(struct ikev2_proposals **proposals);
+
+-void ikev2_need_ike_proposals(struct connection *c, const char *why);
++/*
++ * On-demand, generate proposals for either the IKE SA or the CHILD
++ * SA.
++ *
++ * For CHILD SAs, two different proposal suites are used: during the
++ * IKE_AUTH exchange a stripped down proposal that excludes DH; and
++ * during the CREATE_CHILD_SA exchange DH a mashed up proposal that
++ * can include the IKE SA's latest DH.
++ *
++ * This is done on-demand as, only at the point where the IKE or CHILD
++ * SA is being instantiated, is it clear what proposals are needed.
++ * For instance, when a CHILD SA shares an existing IKE SA, the CHILD
++ * won't need IKE proposals but will need the IKE SA's DH.
++ *
++ * XXX: Should the CREATE CHILD SA proposals be stored in the state?
++ */
+
+-void ikev2_need_esp_or_ah_proposals(struct connection *c,
+- const char *why,
+- const struct oakley_group_desc *default_dh);
++struct ikev2_proposals *get_v2_ike_proposals(struct connection *c, const char *why);
++struct ikev2_proposals *get_v2_ike_auth_child_proposals(struct connection *c, const char *why);
++struct ikev2_proposals *get_v2_create_child_proposals(struct connection *c, const char *why,
++ const struct oakley_group_desc *default_dh);
+
+ bool ikev2_emit_sa_proposal(pb_stream *pbs,
+ const struct ikev2_proposal *proposal,
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev2_parent.c libreswan-3.27/programs/pluto/ikev2_parent.c
+--- libreswan-3.27-orig/programs/pluto/ikev2_parent.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev2_parent.c 2019-02-15 16:32:28.996835500 -0500
+@@ -67,7 +67,7 @@
+ #include "pending.h"
+ #include "kernel.h"
+ #include "nat_traversal.h"
+-#include "alg_info.h" /* for ike_info / esp_info */
++#include "proposals.h" /* for ike_info / esp_info */
+ #include "key.h" /* for SECKEY_DestroyPublicKey */
+ #include "vendor.h"
+ #include "crypt_hash.h"
+@@ -848,8 +848,9 @@
+ * Initialize st->st_oakley, including the group number.
+ * Grab the DH group from the first configured proposal and build KE.
+ */
+- ikev2_need_ike_proposals(c, "IKE SA initiator selecting KE");
+- st->st_oakley.ta_dh = ikev2_proposals_first_dh(c->ike_proposals);
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA initiator selecting KE");
++ st->st_oakley.ta_dh = ikev2_proposals_first_dh(ike_proposals);
+ if (st->st_oakley.ta_dh == NULL) {
+ libreswan_log("proposals do not contain a valid DH");
+ delete_state(st); /* pops state? */
+@@ -991,7 +992,8 @@
+ }
+ /* SA out */
+ {
+- ikev2_need_ike_proposals(c, "IKE SA initiator emitting local proposals");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA initiator emitting local proposals");
+ /*
+ * Since this is an initial IKE exchange, the SPI is
+ * emitted as is part of the packet header and not the
+@@ -999,7 +1001,7 @@
+ */
+ u_char *sa_start = rbody.cur;
+ bool ret = ikev2_emit_sa_proposals(&rbody,
+- c->ike_proposals,
++ ike_proposals,
+ (chunk_t*)NULL);
+ if (!ret) {
+ libreswan_log("outsa fail");
+@@ -1328,7 +1330,8 @@
+ }
+
+ /* Get the proposals ready. */
+- ikev2_need_ike_proposals(c, "IKE SA responder matching remote proposals");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA responder matching remote proposals");
+
+ /*
+ * Select the proposal.
+@@ -1342,7 +1345,7 @@
+ /*expect_accepted*/ FALSE,
+ LIN(POLICY_OPPORTUNISTIC, c->policy),
+ &accepted_ike_proposal,
+- c->ike_proposals);
++ ike_proposals);
+ if (ret != STF_OK)
+ return ret;
+
+@@ -1812,9 +1815,10 @@
+ pstats(invalidke_recv_s, sg.sg_group);
+ pstats(invalidke_recv_u, st->st_oakley.ta_dh->group);
+
+- ikev2_need_ike_proposals(c, "IKE SA initiator validating remote's suggested KE");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA initiator validating remote's suggested KE");
+
+- if (ikev2_proposals_include_modp(c->ike_proposals, sg.sg_group)) {
++ if (ikev2_proposals_include_modp(ike_proposals, sg.sg_group)) {
+ DBG(DBG_CONTROLMORE, DBG_log("Suggested modp group is acceptable"));
+ /*
+ * Since there must be a group object
+@@ -2081,7 +2085,9 @@
+ /* SA body in and out */
+ struct payload_digest *const sa_pd =
+ md->chain[ISAKMP_NEXT_v2SA];
+- ikev2_need_ike_proposals(c, "IKE SA initiator accepting remote proposal");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA initiator accepting remote proposal");
++
+
+ stf_status ret = ikev2_process_sa_payload("IKE initiator (accepting)",
+ &sa_pd->pbs,
+@@ -2090,7 +2096,7 @@
+ /*expect_accepted*/ TRUE,
+ LIN(POLICY_OPPORTUNISTIC, c->policy),
+ &st->st_accepted_ike_proposal,
+- c->ike_proposals);
++ ike_proposals);
+ if (ret != STF_OK) {
+ DBG(DBG_CONTROLMORE, DBG_log("ikev2_parse_parent_sa_body() failed in ikev2_parent_inR1outI2()"));
+ return ret;
+@@ -3564,17 +3570,13 @@
+ sizeof(proto_info->our_spi));
+
+ /*
+- * UNSET_GROUP means strip DH from the proposal. A
+- * CHILD_SA established during an AUTH exchange does
++ * A CHILD_SA established during an AUTH exchange does
+ * not propose DH - the IKE SA's SKEYSEED is always
+ * used.
+ */
+- free_ikev2_proposals(&cc->esp_or_ah_proposals);
+- ikev2_need_esp_or_ah_proposals(cc,
+- "IKE SA initiator emitting ESP/AH proposals",
+- &unset_group);
+-
+- if (!ikev2_emit_sa_proposals(&e_pbs_cipher, cc->esp_or_ah_proposals,
++ struct ikev2_proposals *child_proposals =
++ get_v2_ike_auth_child_proposals(cc, "IKE SA initiator emitting ESP/AH proposals");
++ if (!ikev2_emit_sa_proposals(&e_pbs_cipher, child_proposals,
+ &local_spi))
+ return STF_INTERNAL_ERROR;
+
+@@ -4374,31 +4376,25 @@
+ stf_status ret;
+
+ char *what;
+- const struct oakley_group_desc *default_dh;
++ struct ikev2_proposals *child_proposals;
+ if (isa_xchg == ISAKMP_v2_CREATE_CHILD_SA) {
+ if (st->st_state == STATE_V2_CREATE_I) {
+ what = "ESP/AH initiator accepting remote proposal";
+ } else {
+ what = "ESP/AH responder matching remote proposals";
+ }
+- default_dh = (c->policy & POLICY_PFS) != LEMPTY
++ const struct oakley_group_desc *default_dh = (c->policy & POLICY_PFS) != LEMPTY
+ ? ike->sa.st_oakley.ta_dh
+ : &ike_alg_dh_none;
++ child_proposals = get_v2_create_child_proposals(c, what, default_dh);
+ } else if (expect_accepted) {
+- what = "IKE SA initiator accepting remote ESP/AH proposal";
+- default_dh = &unset_group; /* no DH */
++ what = "IKE_AUTH initiator accepting remote ESP/AH proposal";
++ child_proposals = get_v2_ike_auth_child_proposals(c, what);
+ } else {
+- what = "IKE SA responder matching remote ESP/AH proposals";
+- default_dh = &unset_group; /* no DH */
+- }
+-
+- if (!expect_accepted) {
+- /* preparing to initiate or parse a request flush old ones */
+- free_ikev2_proposals(&c->esp_or_ah_proposals);
++ what = "IKE_AUTH responder matching remote ESP/AH proposals";
++ child_proposals = get_v2_ike_auth_child_proposals(c, what);
+ }
+
+- ikev2_need_esp_or_ah_proposals(c, what, default_dh);
+-
+ ret = ikev2_process_sa_payload(what,
+ &sa_pd->pbs,
+ /*expect_ike*/ FALSE,
+@@ -4406,7 +4402,7 @@
+ expect_accepted,
+ LIN(POLICY_OPPORTUNISTIC, c->policy),
+ &st->st_accepted_esp_or_ah_proposal,
+- c->esp_or_ah_proposals);
++ child_proposals);
+
+ if (ret != STF_OK) {
+ LSWLOG_RC(RC_LOG_SERIOUS, buf) {
+@@ -5217,8 +5213,8 @@
+ chunk_t local_spi;
+ setchunk(local_spi, (uint8_t*)&proto_info->our_spi,
+ sizeof(proto_info->our_spi));
+-
+- if (!ikev2_emit_sa_proposals(outpbs, cc->esp_or_ah_proposals,
++ passert(cc->v2_create_child_proposals != NULL);
++ if (!ikev2_emit_sa_proposals(outpbs, cc->v2_create_child_proposals,
+ &local_spi))
+ return STF_INTERNAL_ERROR;
+
+@@ -5312,12 +5308,11 @@
+ sizeof(st->st_icookie));
+ local_nonce = st->st_ni;
+
+- /* ??? why do we need to free the previous proposals? */
+- free_ikev2_proposals(&c->ike_proposals);
+- ikev2_need_ike_proposals(c, "IKE SA initiating rekey");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA initiating rekey");
+
+ /* send v2 IKE SAs*/
+- if (!ikev2_emit_sa_proposals(outpbs, c->ike_proposals, &local_spi)) {
++ if (!ikev2_emit_sa_proposals(outpbs, ike_proposals, &local_spi)) {
+ libreswan_log("outsa fail");
+ DBG(DBG_CONTROL, DBG_log("problem emitting connection ike proposals in CREATE_CHILD_SA"));
+ return STF_INTERNAL_ERROR;
+@@ -5379,7 +5374,8 @@
+ struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_v2SA];
+
+ /* Get the proposals ready. */
+- ikev2_need_ike_proposals(c, "IKE SA accept response to rekey");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA accept response to rekey");
+
+ stf_status ret = ikev2_process_sa_payload("IKE initiator (accepting)",
+ &sa_pd->pbs,
+@@ -5388,7 +5384,7 @@
+ /*expect_accepted*/ TRUE,
+ LIN(POLICY_OPPORTUNISTIC, c->policy),
+ &st->st_accepted_ike_proposal,
+- c->ike_proposals);
++ ike_proposals);
+ if (ret != STF_OK) {
+ DBG(DBG_CONTROLMORE, DBG_log("failed to accept IKE SA, REKEY, response, in process_ike_rekey_sa_pl_response"));
+ return ret;
+@@ -5424,7 +5420,8 @@
+ struct payload_digest *const sa_pd = md->chain[ISAKMP_NEXT_v2SA];
+
+ /* Get the proposals ready. */
+- ikev2_need_ike_proposals(c, "IKE SA responding to rekey");
++ struct ikev2_proposals *ike_proposals =
++ get_v2_ike_proposals(c, "IKE SA responding to rekey");
+
+ struct ikev2_proposal *accepted_ike_proposal = NULL;
+ stf_status ret = ikev2_process_sa_payload("IKE Rekey responder child",
+@@ -5434,7 +5431,7 @@
+ /*expect_accepted*/ FALSE,
+ LIN(POLICY_OPPORTUNISTIC, c->policy),
+ &accepted_ike_proposal,
+- c->ike_proposals);
++ ike_proposals);
+ if (ret != STF_OK)
+ return ret;
+
+@@ -6677,21 +6674,17 @@
+ if (sa_type == IPSEC_SA) {
+ const struct state *rst = state_with_serialno(p->replacing);
+
+- /*
+- * Because the proposal generated during AUTH won't contain DH,
+- * always force the proposal to be re-generated here. Not the
+- * most efficient, fix probably means moving the proposals to
+- * the state object.
+- */
+- free_ikev2_proposals(&c->esp_or_ah_proposals);
+ const struct oakley_group_desc *default_dh =
+ c->policy & POLICY_PFS ? ike->sa.st_oakley.ta_dh : NULL;
+
+- ikev2_need_esp_or_ah_proposals(c,
+- "ESP/AH initiator emitting proposals",
+- default_dh);
++ struct ikev2_proposals *child_proposals =
++ get_v2_create_child_proposals(c,
++ "ESP/AH initiator emitting proposals",
++ default_dh);
++ /* see ikev2_child_add_ipsec_payloads */
++ passert(c->v2_create_child_proposals != NULL);
+
+- st->st_pfs_group = ikev2_proposals_first_dh(c->esp_or_ah_proposals);
++ st->st_pfs_group = ikev2_proposals_first_dh(child_proposals);
+
+ DBG(DBG_CONTROLMORE, {
+ const char *pfsgroupname = st->st_pfs_group == NULL ?
+diff -Naur libreswan-3.27-orig/programs/pluto/ikev2_spdb_struct.c libreswan-3.27/programs/pluto/ikev2_spdb_struct.c
+--- libreswan-3.27-orig/programs/pluto/ikev2_spdb_struct.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/ikev2_spdb_struct.c 2019-02-15 16:32:28.998835518 -0500
+@@ -51,7 +51,7 @@
+
+ #include "crypto.h"
+
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "kernel_alg.h"
+ #include "ike_alg.h"
+ #include "ike_alg_integ.h"
+@@ -264,7 +264,7 @@
+ *
+ * PROPNUM is an int.
+ */
+-#define FOR_EACH_PROPOSAL(PROPNUM, PROPOSAL, PROPOSALS) \
++#define FOR_EACH_V2_PROPOSAL(PROPNUM, PROPOSAL, PROPOSALS) \
+ for ((PROPNUM) = 1, \
+ (PROPOSAL) = &(PROPOSALS)->proposal[(PROPNUM)]; \
+ (PROPNUM) < (PROPOSALS)->roof; \
+@@ -276,7 +276,7 @@
+ *
+ * PROPNUM, BASE, BOUND are all ints.
+ */
+-#define FOR_EACH_PROPOSAL_IN_RANGE(PROPNUM, PROPOSAL, PROPOSALS, BASE, BOUND) \
++#define FOR_EACH_V2_PROPOSAL_IN_RANGE(PROPNUM, PROPOSAL, PROPOSALS, BASE, BOUND) \
+ for ((PROPNUM) = ((BASE) > 0 ? (BASE) : 1), \
+ (PROPOSAL) = &(PROPOSALS)->proposal[(PROPNUM)]; \
+ (PROPNUM) < (BOUND) && (PROPNUM) < (PROPOSALS)->roof; \
+@@ -395,7 +395,7 @@
+ const char *proposal_sep = "";
+ int propnum;
+ const struct ikev2_proposal *proposal;
+- FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
++ FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
+ lswlogs(buf, proposal_sep);
+ proposal_sep = " ";
+ print_proposal(buf, propnum, proposal);
+@@ -443,8 +443,8 @@
+ {
+ int local_propnum;
+ const struct ikev2_proposal *local_proposal;
+- FOR_EACH_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
+- local_propnum_base, local_propnum_bound) {
++ FOR_EACH_V2_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
++ local_propnum_base, local_propnum_bound) {
+ struct ikev2_proposal_match *matching_local_proposal = &matching_local_proposals[local_propnum];
+ /* clear matched */
+ matching_local_proposal->matched_transform_types = LEMPTY;
+@@ -579,8 +579,8 @@
+ */
+ int local_propnum;
+ struct ikev2_proposal *local_proposal;
+- FOR_EACH_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
+- local_propnum_base, local_propnum_bound) {
++ FOR_EACH_V2_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
++ local_propnum_base, local_propnum_bound) {
+ if (local_proposal->protoid == remote_protoid) {
+ /*
+ * Search the proposal for transforms of this
+@@ -680,8 +680,8 @@
+
+ int local_propnum;
+ struct ikev2_proposal *local_proposal;
+- FOR_EACH_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
+- local_propnum_base, local_propnum_bound) {
++ FOR_EACH_V2_PROPOSAL_IN_RANGE(local_propnum, local_proposal, local_proposals,
++ local_propnum_base, local_propnum_bound) {
+ struct ikev2_proposal_match *matching_local_proposal = &matching_local_proposals[local_propnum];
+ LSWDBGP(DBG_CONTROLMORE, log) {
+ lswlogf(log, "comparing remote proposal %u containing ",
+@@ -834,7 +834,7 @@
+ {
+ int local_propnum;
+ struct ikev2_proposal *local_proposal;
+- FOR_EACH_PROPOSAL(local_propnum, local_proposal, local_proposals) {
++ FOR_EACH_V2_PROPOSAL(local_propnum, local_proposal, local_proposals) {
+ struct ikev2_proposal_match *matching_local_proposal = &matching_local_proposals[local_propnum];
+ enum ikev2_trans_type type;
+ struct ikev2_transforms *local_transforms;
+@@ -1156,7 +1156,7 @@
+ *
+ * Must be freed by this function.
+ */
+- stf_status status;
++ stf_status status = STF_FAIL; /* initialized just to silence gcc -Og */
+ LSWBUF(remote_print_buf) {
+ int matching_local_propnum = ikev2_process_proposals(sa_payload,
+ expect_ike, expect_spi,
+@@ -1448,7 +1448,7 @@
+
+ int propnum;
+ const struct ikev2_proposal *proposal;
+- FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
++ FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
+ if (!emit_proposal(&sa_pbs, proposal, propnum, local_spi,
+ (propnum < proposals->roof - 1
+ ? v2_PROPOSAL_NON_LAST
+@@ -1804,9 +1804,9 @@
+ return TRUE;
+ }
+
+-static struct ikev2_proposal *ikev2_proposal_from_proposal_info(const struct proposal_info *info,
++static struct ikev2_proposal *ikev2_proposal_from_proposal_info(const struct proposal *proposal,
+ enum ikev2_sec_proto_id protoid,
+- struct ikev2_proposals *proposals,
++ struct ikev2_proposals *v2_proposals,
+ const struct oakley_group_desc *default_dh)
+ {
+ /*
+@@ -1814,19 +1814,19 @@
+ * contain partially constructed stuff from an earlier
+ * iteration).
+ */
+- struct ikev2_proposal *proposal = &proposals->proposal[proposals->roof];
+- *proposal = (struct ikev2_proposal) {
++ struct ikev2_proposal *v2_proposal = &v2_proposals->proposal[v2_proposals->roof];
++ *v2_proposal = (struct ikev2_proposal) {
+ .protoid = protoid,
+- .propnum = proposals->roof,
++ .propnum = v2_proposals->roof,
+ };
+
+ /*
+ * Encryption.
+ */
+- const struct encrypt_desc *encrypt = info->encrypt;
+- if (encrypt != NULL) {
+- if (!append_encrypt_transform(proposal, encrypt,
+- info->enckeylen)) {
++ FOR_EACH_ALGORITHM(proposal, encrypt, alg) {
++ const struct encrypt_desc *encrypt = encrypt_desc(alg->desc);
++ if (!append_encrypt_transform(v2_proposal, encrypt,
++ alg->enckeylen)) {
+ return NULL;
+ }
+ }
+@@ -1834,22 +1834,22 @@
+ /*
+ * PRF.
+ */
+- const struct prf_desc *prf = info->prf;
+- if (prf != NULL) {
+- append_transform(proposal, IKEv2_TRANS_TYPE_PRF,
++ FOR_EACH_ALGORITHM(proposal, prf, alg) {
++ const struct prf_desc *prf = prf_desc(alg->desc);
++ append_transform(v2_proposal, IKEv2_TRANS_TYPE_PRF,
+ prf->common.id[IKEv2_ALG_ID], 0);
+ }
+
+ /*
+ * Integrity.
+ */
+- const struct integ_desc *integ = info->integ;
+- if (integ != NULL) {
++ FOR_EACH_ALGORITHM(proposal, integ, alg) {
++ const struct integ_desc *integ = integ_desc(alg->desc);
+ /*
+ * While INTEG=NONE is included in the proposal it
+ * omitted when emitted.
+ */
+- append_transform(proposal, IKEv2_TRANS_TYPE_INTEG,
++ append_transform(v2_proposal, IKEv2_TRANS_TYPE_INTEG,
+ integ->common.id[IKEv2_ALG_ID], 0);
+ }
+
+@@ -1860,20 +1860,25 @@
+ * happens during the AUTH exchange). Otherwise use either
+ * the proposed or default DH.
+ */
+- const struct oakley_group_desc *dh =
+- default_dh == &unset_group ? &ike_alg_dh_none
+- : info->dh != NULL ? info->dh
+- : default_dh;
+- if (dh != NULL) {
+- /*
+- * WHILE DH=NONE is included in the proposal it is
+- * omitted when emitted.
+- */
+- append_transform(proposal, IKEv2_TRANS_TYPE_DH,
+- dh->common.id[IKEv2_ALG_ID], 0);
++ if (default_dh == &unset_group) {
++ append_transform(v2_proposal, IKEv2_TRANS_TYPE_DH,
++ ike_alg_dh_none.common.id[IKEv2_ALG_ID], 0);
++ } else if (next_algorithm(proposal, PROPOSAL_dh, NULL) != NULL) {
++ FOR_EACH_ALGORITHM(proposal, dh, alg) {
++ const struct oakley_group_desc *dh = dh_desc(alg->desc);
++ /*
++ * WHILE DH=NONE is included in the proposal it is
++ * omitted when emitted.
++ */
++ append_transform(v2_proposal, IKEv2_TRANS_TYPE_DH,
++ dh->common.id[IKEv2_ALG_ID], 0);
++ }
++ } else if (default_dh != NULL) {
++ append_transform(v2_proposal, IKEv2_TRANS_TYPE_DH,
++ default_dh->common.id[IKEv2_ALG_ID], 0);
+ }
+
+- return proposal;
++ return v2_proposal;
+ }
+
+ /*
+@@ -1993,67 +1998,73 @@
+ };
+
+ /*
+- * Ensure c->ike_proposals is filled in. If not, build it.
+- *
+- * ??? if c->ike_proposals was set for v1 we won't change it.
++ * On-demand compute and return the IKE proposals for the connection.
+ *
+- * WARNING: alg_info_ike is IKEv1
++ * If the default alg_info_ike includes unknown algorithms those get
++ * dropped, which can lead to no proposals.
+ *
+- * If alg_info_ike includes unknown algorithms those get dropped,
+- * which can lead to no proposals.
+- * c->ike_proposals will not be NULL (see passert).
++ * Never returns NULL (see passert).
+ */
+
+-void ikev2_need_ike_proposals(struct connection *c, const char *why) {
+- if (c->ike_proposals != NULL) {
+- DBGF(DBG_CONTROL, "already constructed local IKE proposals for connection %s (%s)",
+- c->name, why);
+- return;
++struct ikev2_proposals *get_v2_ike_proposals(struct connection *c, const char *why)
++{
++ if (c->v2_ike_proposals != NULL) {
++ LSWDBGP(DBG_CONTROL, buf) {
++ lswlogf(buf, "using existing local IKE proposals for connection %s (%s): ",
++ c->name, why);
++ print_proposals(buf, c->v2_ike_proposals);
++ }
++ return c->v2_ike_proposals;
+ }
+
+ const char *notes;
+- if (c->alg_info_ike == NULL) {
++ if (c->ike_proposals.p == NULL) {
+ DBGF(DBG_CONTROL, "selecting default constructed local IKE proposals for connection %s (%s)",
+ c->name, why);
+- c->ike_proposals = &default_ikev2_ike_proposals;
++ c->v2_ike_proposals = &default_ikev2_ike_proposals;
+ notes = " (default)";
+ } else {
+ DBGF(DBG_CONTROL, "constructing local IKE proposals for %s (%s)",
+ c->name, why);
+- struct ikev2_proposals *proposals = alloc_thing(struct ikev2_proposals, "proposals");
+- int proposals_roof = c->alg_info_ike->ai.alg_info_cnt + 1;
+- proposals->proposal = alloc_things(struct ikev2_proposal, proposals_roof, "propsal");
+- proposals->on_heap = TRUE;
+- proposals->roof = 1;
++ struct ikev2_proposals *v2_proposals = alloc_thing(struct ikev2_proposals,
++ "proposals");
++ /* +1 as proposal[0] is empty */
++ int v2_proposals_roof = nr_proposals(c->ike_proposals.p) + 1;
++ v2_proposals->proposal = alloc_things(struct ikev2_proposal,
++ v2_proposals_roof, "propsal");
++ v2_proposals->on_heap = TRUE;
++ v2_proposals->roof = 1;
+
+- FOR_EACH_IKE_INFO(c->alg_info_ike, ike_info) {
++ FOR_EACH_PROPOSAL(c->ike_proposals.p, ike_info) {
+ LSWDBGP(DBG_CONTROL, buf) {
+ lswlogs(buf, "converting ike_info ");
+- lswlog_proposal_info(buf, ike_info);
++ fmt_proposal(buf, ike_info);
+ lswlogs(buf, " to ikev2 ...");
+ }
+
+- passert(proposals->roof < proposals_roof);
+- struct ikev2_proposal *proposal =
+- ikev2_proposal_from_proposal_info(ike_info, IKEv2_SEC_PROTO_IKE,
+- proposals, NULL);
+- if (proposal != NULL) {
++ passert(v2_proposals->roof < v2_proposals_roof);
++ struct ikev2_proposal *v2_proposal =
++ ikev2_proposal_from_proposal_info(ike_info,
++ IKEv2_SEC_PROTO_IKE,
++ v2_proposals, NULL);
++ if (v2_proposal != NULL) {
+ DBG(DBG_CONTROL,
+- DBG_log_ikev2_proposal("... ", proposal));
+- proposals->roof++;
++ DBG_log_ikev2_proposal("... ", v2_proposal));
++ v2_proposals->roof++;
+ }
+ }
+- c->ike_proposals = proposals;
++ c->v2_ike_proposals = v2_proposals;
+ notes = "";
+ }
+
+- LSWLOG(buf) {
++ LSWLOG_CONNECTION(c, buf) {
+ lswlogf(buf, "constructed local IKE proposals for %s (%s): ",
+ c->name, why);
+- print_proposals(buf, c->ike_proposals);
++ print_proposals(buf, c->v2_ike_proposals);
+ lswlogs(buf, notes);
+ }
+- passert(c->ike_proposals != NULL);
++ passert(c->v2_ike_proposals != NULL);
++ return c->v2_ike_proposals;
+ }
+
+ static struct ikev2_proposal default_ikev2_esp_proposal_missing_esn[] = {
+@@ -2143,19 +2154,23 @@
+ }
+ }
+
+-void ikev2_need_esp_or_ah_proposals(struct connection *c,
+- const char *why,
+- const struct oakley_group_desc *default_dh)
++static struct ikev2_proposals *get_v2_child_proposals(struct ikev2_proposals **child_proposals,
++ struct connection *c,
++ const char *why,
++ const struct oakley_group_desc *default_dh)
+ {
+- if (c->esp_or_ah_proposals != NULL) {
+- DBGF(DBG_CONTROL, "already constructed local ESP/AH proposals for %s (%s)",
+- c->name, why);
+- return;
++ if (*child_proposals != NULL) {
++ LSWDBGP(DBG_CONTROL, buf) {
++ lswlogf(buf, "using existing local ESP/AH proposals for %s (%s): ",
++ c->name, why);
++ print_proposals(buf, *child_proposals);
++ }
++ return *child_proposals;
+ }
+
+ const char *notes;
+- if (c->alg_info_esp == NULL) {
+- DBGF(DBG_CONTROL, "selecting default construvted local ESP/AH proposals for %s (%s)",
++ if (c->child_proposals.p == NULL) {
++ DBGF(DBG_CONTROL, "selecting default local ESP/AH proposals for %s (%s)",
+ c->name, why);
+ lset_t esp_ah = c->policy & (POLICY_ENCRYPT | POLICY_AUTHENTICATE);
+ struct ikev2_proposals *default_proposals_missing_esn;
+@@ -2186,38 +2201,38 @@
+ /*
+ * Clone the default proposal and add the missing ESN.
+ */
+- struct ikev2_proposals *proposals = alloc_thing(struct ikev2_proposals,
+- "cloned ESP/AH proposals");
+- proposals->on_heap = TRUE;
+- proposals->roof = default_proposals_missing_esn->roof;
++ struct ikev2_proposals *v2_proposals = alloc_thing(struct ikev2_proposals,
++ "cloned ESP/AH proposals");
++ v2_proposals->on_heap = TRUE;
++ v2_proposals->roof = default_proposals_missing_esn->roof;
+ if (add_empty_msdh_duplicates) {
+ /* add space for duplicates, minus the empty first proposal */
+- proposals->roof += default_proposals_missing_esn->roof - 1;
++ v2_proposals->roof += default_proposals_missing_esn->roof - 1;
+ }
+- proposals->proposal = alloc_things(struct ikev2_proposal, proposals->roof,
+- "ESP/AH proposals");
+- memcpy(proposals->proposal, default_proposals_missing_esn->proposal,
+- sizeof(proposals->proposal[0]) * default_proposals_missing_esn->roof);
++ v2_proposals->proposal = alloc_things(struct ikev2_proposal, v2_proposals->roof,
++ "ESP/AH proposals");
++ memcpy(v2_proposals->proposal, default_proposals_missing_esn->proposal,
++ sizeof(v2_proposals->proposal[0]) * default_proposals_missing_esn->roof);
+ if (add_empty_msdh_duplicates) {
+ /*
+ * Append duplicates but discarding
+ * proposal[0] which is filler.
+ */
+- memcpy(proposals->proposal + default_proposals_missing_esn->roof,
++ memcpy(v2_proposals->proposal + default_proposals_missing_esn->roof,
+ default_proposals_missing_esn->proposal + 1, /* skip "0" */
+- sizeof(proposals->proposal[0]) * (default_proposals_missing_esn->roof - 1));
++ sizeof(v2_proposals->proposal[0]) * (default_proposals_missing_esn->roof - 1));
+ }
+
+ int propnum;
+- struct ikev2_proposal *proposal;
+- FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
+- add_esn_transforms(proposal, c->policy);
++ struct ikev2_proposal *v2_proposal;
++ FOR_EACH_V2_PROPOSAL(propnum, v2_proposal, v2_proposals) {
++ add_esn_transforms(v2_proposal, c->policy);
+ }
+ if (default_dh != NULL && default_dh != &unset_group) {
+ DBGF(DBG_CONTROL, "adding dh %s to default proposals",
+ default_dh->common.name);
+- FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
+- append_transform(proposal,
++ FOR_EACH_V2_PROPOSAL(propnum, v2_proposal, v2_proposals) {
++ append_transform(v2_proposal,
+ IKEv2_TRANS_TYPE_DH,
+ default_dh->group, 0);
+ if (propnum >= default_proposals_missing_esn->roof)
+@@ -2225,7 +2240,7 @@
+ break;
+ }
+ }
+- c->esp_or_ah_proposals = proposals;
++ *child_proposals = v2_proposals;
+ notes = " (default)";
+ } else {
+ LSWDBGP(DBG_CONTROL, buf) {
+@@ -2250,17 +2265,18 @@
+ bool add_empty_msdh_duplicates = (c->policy & POLICY_MSDH_DOWNGRADE) &&
+ default_dh != &unset_group;
+
+- struct ikev2_proposals *proposals = alloc_thing(struct ikev2_proposals,
+- "ESP/AH proposals");
+- int proposals_roof = c->alg_info_esp->ai.alg_info_cnt + 1;
++ struct ikev2_proposals *v2_proposals = alloc_thing(struct ikev2_proposals,
++ "ESP/AH proposals");
++ /* proposal[0] is empty so +1 */
++ int v2_proposals_roof = nr_proposals(c->child_proposals.p) + 1;
+ if (add_empty_msdh_duplicates) {
+- /* make space for everything duplicated */
+- proposals_roof += c->alg_info_esp->ai.alg_info_cnt;
++ /* make space for everything duplicated; note +1 above */
++ v2_proposals_roof = v2_proposals_roof * 2 - 1;
+ }
+- proposals->proposal = alloc_things(struct ikev2_proposal, proposals_roof,
+- "ESP/AH proposal");
+- proposals->on_heap = TRUE;
+- proposals->roof = 1;
++ v2_proposals->proposal = alloc_things(struct ikev2_proposal, v2_proposals_roof,
++ "ESP/AH proposal");
++ v2_proposals->on_heap = TRUE;
++ v2_proposals->roof = 1;
+
+ enum ikev2_sec_proto_id protoid;
+ switch (c->policy & (POLICY_ENCRYPT | POLICY_AUTHENTICATE)) {
+@@ -2275,10 +2291,10 @@
+ }
+
+ for (int dup = 0; dup < (add_empty_msdh_duplicates ? 2 : 1); dup++) {
+- FOR_EACH_ESP_INFO(c->alg_info_esp, esp_info) {
++ FOR_EACH_PROPOSAL(c->child_proposals.p, esp_info) {
+ LSWDBGP(DBG_CONTROL, log) {
+ lswlogf(log, "converting proposal ");
+- lswlog_proposal_info(log, esp_info);
++ fmt_proposal(log, esp_info);
+ lswlogf(log, " to ikev2 ...");
+ }
+
+@@ -2286,37 +2302,94 @@
+ * Get the next proposal with the
+ * basics filled in.
+ */
+- passert(proposals->roof < proposals_roof);
+- if (dup && default_dh == NULL && esp_info->dh == NULL) {
++ passert(v2_proposals->roof < v2_proposals_roof);
++ if (dup && default_dh == NULL &&
++ next_algorithm(esp_info, PROPOSAL_dh, NULL) == NULL) {
+ /*
+ * First pass didn't include DH.
+ */
+ continue;
+ }
+- struct ikev2_proposal *proposal =
+- ikev2_proposal_from_proposal_info(esp_info, protoid,
+- proposals,
++ struct ikev2_proposal *v2_proposal =
++ ikev2_proposal_from_proposal_info(esp_info,
++ protoid,
++ v2_proposals,
+ dup ? &unset_group : default_dh);
+- if (proposal != NULL) {
+- add_esn_transforms(proposal, c->policy);
++ if (v2_proposal != NULL) {
++ add_esn_transforms(v2_proposal, c->policy);
+ DBG(DBG_CONTROL,
+- DBG_log_ikev2_proposal("... ", proposal));
+- proposals->roof++;
++ DBG_log_ikev2_proposal("... ", v2_proposal));
++ v2_proposals->roof++;
+ }
+ }
+ }
+
+- c->esp_or_ah_proposals = proposals;
++ *child_proposals = v2_proposals;
+ notes = "";
+ }
+
+- LSWLOG(buf) {
++ LSWLOG_CONNECTION(c, buf) {
+ lswlogf(buf, "constructed local ESP/AH proposals for %s (%s): ",
+ c->name, why);
+- print_proposals(buf, c->esp_or_ah_proposals);
++ print_proposals(buf, *child_proposals);
+ lswlogs(buf, notes);
+ }
+- passert(c->esp_or_ah_proposals != NULL);
++ passert(*child_proposals != NULL);
++ return *child_proposals;
++}
++
++/*
++ * If needed, generate the proposals for a CHILD SA being created
++ * during the IKE_AUTH exchange.
++ *
++ * Since a CHILD_SA established during an IKE_AUTH exchange does not
++ * propose DH (keying material is taken from the IKE SA's SKEYSEED),
++ * DH is stripped from the proposals.
++ *
++ * Since only things that affect this proposal suite are the
++ * connection's .policy bits and the contents .child_proposals, and
++ * modifiying those triggers the creation of a new connection (true?),
++ * the connection can be cached.
++ */
++
++struct ikev2_proposals *get_v2_ike_auth_child_proposals(struct connection *c, const char *why)
++{
++ /* UNSET_GROUP means strip DH from the proposal. */
++ return get_v2_child_proposals(&c->v2_ike_auth_child_proposals, c,
++ why, &unset_group);
++}
++
++/*
++ * If needed, generate the proposals for a CHILD SA being created (or
++ * re-keyed) during a CREATE_CHILD_SA exchange.
++ *
++ * If the proposals do not include DH, and PFS is enabled, then the
++ * DEFAULT_DH (DH used by the IKE SA) is added to all proposals.
++ *
++ * XXX:
++ *
++ * This means that the CHILD SA's proposal suite will change depending
++ * on what DH is negotiated by the IKE SA! Hence the need to save the
++ * DEFAULT_DH and check for change. It should probably be storing the
++ * proposal in the state.
++ *
++ * Horrible.
++ */
++struct ikev2_proposals *get_v2_create_child_proposals(struct connection *c, const char *why,
++ const struct oakley_group_desc *default_dh)
++{
++ if (c->v2_create_child_proposals_default_dh != default_dh) {
++ const char *old_fqn = (c->v2_create_child_proposals_default_dh != NULL
++ ? c->v2_create_child_proposals_default_dh->common.fqn
++ : "no-PFS");
++ const char *new_fqn = default_dh != NULL ? default_dh->common.fqn : "no-PFS";
++ DBGF(DBG_CONTROL, "create child proposal's DH changed from %s to %s, flushing",
++ old_fqn, new_fqn);
++ free_ikev2_proposals(&c->v2_create_child_proposals);
++ c->v2_create_child_proposals_default_dh = default_dh;
++ }
++ return get_v2_child_proposals(&c->v2_create_child_proposals, c, why,
++ c->v2_create_child_proposals_default_dh);
+ }
+
+ struct ipsec_proto_info *ikev2_child_sa_proto_info(struct state *st, lset_t policy)
+@@ -2358,7 +2431,7 @@
+ {
+ int propnum;
+ const struct ikev2_proposal *proposal;
+- FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
++ FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
+ const struct ikev2_transforms *transforms = &proposal->transforms[IKEv2_TRANS_TYPE_DH];
+ int t;
+ for (t = 0; t < transforms->transform[t].valid; t++) {
+@@ -2393,7 +2466,7 @@
+ {
+ int propnum;
+ const struct ikev2_proposal *proposal;
+- FOR_EACH_PROPOSAL(propnum, proposal, proposals) {
++ FOR_EACH_V2_PROPOSAL(propnum, proposal, proposals) {
+ const struct ikev2_transforms *transforms = &proposal->transforms[IKEv2_TRANS_TYPE_DH];
+ const struct ikev2_transform *transform;
+ FOR_EACH_TRANSFORM(transform, transforms) {
+diff -Naur libreswan-3.27-orig/programs/pluto/initiate.c libreswan-3.27/programs/pluto/initiate.c
+--- libreswan-3.27-orig/programs/pluto/initiate.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/initiate.c 2019-02-15 16:32:28.999835528 -0500
+@@ -63,7 +63,7 @@
+ #include "log.h"
+ #include "keys.h"
+ #include "whack.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "spdb.h"
+ #include "ike_alg.h"
+ #include "kernel_alg.h"
+@@ -287,15 +287,12 @@
+ /* We will only request an IPsec SA if policy isn't empty
+ * (ignoring Main Mode items).
+ * This is a fudge, but not yet important.
+- * If we are to proceed asynchronously, whackfd will be NULL_FD.
+ */
+
+ if (c->policy & (POLICY_ENCRYPT | POLICY_AUTHENTICATE)) {
+- struct alg_info_esp *alg = c->alg_info_esp;
+- struct db_sa *phase2_sa = kernel_alg_makedb(
+- c->policy, alg, TRUE);
+-
+- if (alg != NULL && phase2_sa == NULL) {
++ struct db_sa *phase2_sa =
++ kernel_alg_makedb(c->policy, c->child_proposals, TRUE);
++ if (c->child_proposals.p != NULL && phase2_sa == NULL) {
+ whack_log(RC_LOG_SERIOUS,
+ "cannot initiate: no acceptable kernel algorithms loaded");
+ reset_cur_connection();
+diff -Naur libreswan-3.27-orig/programs/pluto/kernel_bsdkame.c libreswan-3.27/programs/pluto/kernel_bsdkame.c
+--- libreswan-3.27-orig/programs/pluto/kernel_bsdkame.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/kernel_bsdkame.c 2019-02-15 16:32:28.999835528 -0500
+@@ -47,7 +47,6 @@
+ #include "whack.h" /* for RC_LOG_SERIOUS */
+ #include "packet.h" /* for pb_stream in nat_traversal.h */
+ #include "nat_traversal.h"
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+ #include "kernel_sadb.h"
+
+diff -Naur libreswan-3.27-orig/programs/pluto/kernel.c libreswan-3.27/programs/pluto/kernel.c
+--- libreswan-3.27-orig/programs/pluto/kernel.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/kernel.c 2019-02-15 16:32:29.000835537 -0500
+@@ -1176,7 +1176,6 @@
+ }
+ }
+
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+
+ void set_text_said(char *text_said, const ip_address *dst,
+diff -Naur libreswan-3.27-orig/programs/pluto/kernel_klips.c libreswan-3.27/programs/pluto/kernel_klips.c
+--- libreswan-3.27-orig/programs/pluto/kernel_klips.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/kernel_klips.c 2019-02-15 16:32:29.000835537 -0500
+@@ -48,7 +48,6 @@
+ #include "nat_traversal.h"
+ #include "server.h"
+
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+ #include "ip_address.h"
+
+diff -Naur libreswan-3.27-orig/programs/pluto/kernel_mast.c libreswan-3.27/programs/pluto/kernel_mast.c
+--- libreswan-3.27-orig/programs/pluto/kernel_mast.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/kernel_mast.c 2019-02-15 16:32:29.001835546 -0500
+@@ -48,7 +48,6 @@
+ #include "nat_traversal.h"
+ #include "server.h"
+
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+
+ static int next_free_mast_device = -1;
+diff -Naur libreswan-3.27-orig/programs/pluto/kernel_pfkey.c libreswan-3.27/programs/pluto/kernel_pfkey.c
+--- libreswan-3.27-orig/programs/pluto/kernel_pfkey.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/kernel_pfkey.c 2019-02-15 16:32:29.001835546 -0500
+@@ -63,7 +63,6 @@
+ #include "nat_traversal.h"
+
+ #include "lsw_select.h"
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+ #include "ip_address.h"
+
+diff -Naur libreswan-3.27-orig/programs/pluto/plutoalg.c libreswan-3.27/programs/pluto/plutoalg.c
+--- libreswan-3.27-orig/programs/pluto/plutoalg.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/plutoalg.c 2019-02-15 16:32:29.002835556 -0500
+@@ -34,7 +34,7 @@
+ #include "connections.h"
+ #include "state.h"
+ #include "kernel_alg.h"
+-#include "alg_info.h"
++#include "proposals.h"
+ #include "ike_alg.h"
+ #include "ike_alg_integ.h"
+ #include "ike_alg_encrypt.h"
+@@ -47,15 +47,16 @@
+ #include "ikev1.h" /* for ikev1_quick_dh() */
+
+ static bool kernel_alg_db_add(struct db_context *db_ctx,
+- const struct proposal_info *esp_info,
++ const struct proposal *proposal,
+ lset_t policy, bool logit)
+ {
+ int ealg_i = SADB_EALG_NONE;
+
++ struct v1_proposal algs = v1_proposal(proposal);
+ if (policy & POLICY_ENCRYPT) {
+- ealg_i = esp_info->encrypt->common.id[IKEv1_ESP_ID];
++ ealg_i = algs.encrypt->common.id[IKEv1_ESP_ID];
+ /* already checked by the parser? */
+- if (!kernel_alg_encrypt_ok(esp_info->encrypt)) {
++ if (!kernel_alg_encrypt_ok(algs.encrypt)) {
+ if (logit) {
+ loglog(RC_LOG_SERIOUS,
+ "requested kernel enc ealg_id=%d not present",
+@@ -68,10 +69,10 @@
+ }
+ }
+
+- int aalg_i = esp_info->integ->integ_ikev1_ah_transform;
++ int aalg_i = algs.integ->integ_ikev1_ah_transform;
+
+ /* already checked by the parser? */
+- if (!kernel_alg_integ_ok(esp_info->integ)) {
++ if (!kernel_alg_integ_ok(algs.integ)) {
+ DBG_log("kernel_alg_db_add() kernel auth aalg_id=%d not present",
+ aalg_i);
+ return FALSE;
+@@ -82,32 +83,32 @@
+ db_trans_add(db_ctx, ealg_i);
+
+ /* add ESP auth attr (if present) */
+- if (esp_info->integ != &ike_alg_integ_none) {
++ if (algs.integ != &ike_alg_integ_none) {
+ db_attr_add_values(db_ctx,
+ AUTH_ALGORITHM,
+- esp_info->integ->common.id[IKEv1_ESP_ID]);
++ algs.integ->common.id[IKEv1_ESP_ID]);
+ }
+
+ /* add keylength if specified in esp= string */
+- if (esp_info->enckeylen != 0) {
+- db_attr_add_values(db_ctx,
+- KEY_LENGTH,
+- esp_info->enckeylen);
++ if (algs.enckeylen != 0) {
++ db_attr_add_values(db_ctx,
++ KEY_LENGTH,
++ algs.enckeylen);
+ } else {
+ /* no key length - if required add default here and add another max entry */
+- int def_ks = (esp_info->encrypt->keylen_omitted ? 0
+- : esp_info->encrypt->keydeflen);
++ int def_ks = (algs.encrypt->keylen_omitted ? 0
++ : algs.encrypt->keydeflen);
+
+ if (def_ks != 0) {
+ db_attr_add_values(db_ctx, KEY_LENGTH, def_ks);
+ /* add this trans again with max key size */
+- int max_ks = encrypt_max_key_bit_length(esp_info->encrypt);
++ int max_ks = encrypt_max_key_bit_length(algs.encrypt);
+ if (def_ks != max_ks) {
+ db_trans_add(db_ctx, ealg_i);
+- if (esp_info->integ != &ike_alg_integ_none) {
++ if (algs.integ != &ike_alg_integ_none) {
+ db_attr_add_values(db_ctx,
+ AUTH_ALGORITHM,
+- esp_info->integ->common.id[IKEv1_ESP_ID]);
++ algs.integ->common.id[IKEv1_ESP_ID]);
+ }
+ db_attr_add_values(db_ctx,
+ KEY_LENGTH,
+@@ -120,8 +121,8 @@
+ db_trans_add(db_ctx, aalg_i);
+
+ /* add ESP auth attr */
+- db_attr_add_values(db_ctx,
+- AUTH_ALGORITHM, esp_info->integ->common.id[IKEv1_ESP_ID]);
++ db_attr_add_values(db_ctx, AUTH_ALGORITHM,
++ algs.integ->common.id[IKEv1_ESP_ID]);
+ }
+
+ return TRUE;
+@@ -135,7 +136,7 @@
+ * for now this function does free() previous returned
+ * malloced pointer (this quirk allows easier spdb.c change)
+ */
+-static struct db_context *kernel_alg_db_new(struct alg_info_esp *alg_info,
++static struct db_context *kernel_alg_db_new(struct child_proposals proposals,
+ lset_t policy, bool logit)
+ {
+ unsigned int trans_cnt = 0;
+@@ -157,25 +158,24 @@
+
+ /*
+ * Loop: for each element (struct esp_info) of
+- * alg_info, if kernel support is present then
++ * proposals, if kernel support is present then
+ * build the transform (and attrs)
+ *
+- * if NULL alg_info, propose everything ...
++ * if NULL proposals, propose everything ...
+ */
+
+ bool success = TRUE;
+- if (alg_info != NULL) {
+- FOR_EACH_ESP_INFO(alg_info, proposal) {
++ if (proposals.p != NULL) {
++ FOR_EACH_PROPOSAL(proposals.p, proposal) {
+ LSWDBGP(DBG_CONTROL | DBG_EMITTING, buf) {
+ lswlogs(buf, "adding proposal: ");
+- lswlog_proposal_info(buf, proposal);
++ fmt_proposal(buf, proposal);
+ }
+- if (!kernel_alg_db_add(ctx_new, proposal,
+- policy, logit))
++ if (!kernel_alg_db_add(ctx_new, proposal, policy, logit))
+ success = FALSE; /* ??? should we break? */
+ }
+ } else {
+- PEXPECT_LOG("%s", "alg_info should be non-NULL");
++ PEXPECT_LOG("%s", "proposals should be non-NULL");
+ }
+
+ if (!success) {
+@@ -268,7 +268,7 @@
+ * If this is NULL and PFS is required then callers fall back to using
+ * the parent's DH algorithm.
+ */
+- const struct oakley_group_desc *dh = ikev1_quick_pfs(c->alg_info_esp);
++ const struct oakley_group_desc *dh = ikev1_quick_pfs(c->child_proposals);
+ if (dh != NULL) {
+ pfsbuf = dh->common.fqn;
+ } else {
+@@ -278,7 +278,7 @@
+ pfsbuf = "";
+ }
+
+- if (c->alg_info_esp != NULL) {
++ if (c->child_proposals.p != NULL) {
+ LSWLOG_WHACK(RC_COMMENT, buf) {
+ /*
+ * If DH (PFS) was specified in the esp= or
+@@ -299,7 +299,7 @@
+ */
+ lswlogf(buf, "\"%s\"%s: %s algorithms: ",
+ c->name, instance, satype);
+- lswlog_alg_info(buf, &c->alg_info_esp->ai);
++ fmt_proposals(buf, c->child_proposals.p);
+ }
+ }
+
+@@ -326,10 +326,11 @@
+ }
+ }
+
+-struct db_sa *kernel_alg_makedb(lset_t policy, struct alg_info_esp *ei,
++struct db_sa *kernel_alg_makedb(lset_t policy,
++ struct child_proposals proposals,
+ bool logit)
+ {
+- if (ei == NULL) {
++ if (proposals.p == NULL) {
+ struct db_sa *sadb;
+ lset_t pm = POLICY_ENCRYPT | POLICY_AUTHENTICATE;
+
+@@ -344,7 +345,7 @@
+ return sadb;
+ }
+
+- struct db_context *dbnew = kernel_alg_db_new(ei, policy, logit);
++ struct db_context *dbnew = kernel_alg_db_new(proposals, policy, logit);
+
+ if (dbnew == NULL) {
+ libreswan_log("failed to translate esp_info to proposal, returning empty");
+diff -Naur libreswan-3.27-orig/programs/pluto/spdb.c libreswan-3.27/programs/pluto/spdb.c
+--- libreswan-3.27-orig/programs/pluto/spdb.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/spdb.c 2019-02-15 16:32:29.002835556 -0500
+@@ -51,7 +51,6 @@
+
+ #include "crypto.h"
+
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+ #include "ike_alg.h"
+ #include "db_ops.h"
+diff -Naur libreswan-3.27-orig/programs/pluto/spdb.h libreswan-3.27/programs/pluto/spdb.h
+--- libreswan-3.27-orig/programs/pluto/spdb.h 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/spdb.h 2019-02-15 16:32:29.002835556 -0500
+@@ -171,12 +171,12 @@
+ struct alg_info_ike;
+ struct alg_info_esp;
+
+-extern struct db_sa *oakley_alg_makedb(struct alg_info_ike *ai,
++extern struct db_sa *oakley_alg_makedb(const struct ike_proposals proposals,
+ enum ikev1_auth_method auth_method,
+ bool single_dh);
+
+ extern struct db_sa *kernel_alg_makedb(lset_t policy,
+- struct alg_info_esp *ei,
++ const struct child_proposals proposals,
+ bool logit);
+
+ #endif /* _SPDB_H_ */
+diff -Naur libreswan-3.27-orig/programs/pluto/spdb_struct.c libreswan-3.27/programs/pluto/spdb_struct.c
+--- libreswan-3.27-orig/programs/pluto/spdb_struct.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/spdb_struct.c 2019-02-15 16:32:29.003835565 -0500
+@@ -45,7 +45,6 @@
+
+ #include "crypto.h"
+
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+ #include "ike_alg.h"
+ #include "db_ops.h"
+@@ -82,30 +81,31 @@
+ * one DH group.
+ */
+
+-static struct db_sa *oakley_alg_mergedb(struct alg_info_ike *ai,
++static struct db_sa *oakley_alg_mergedb(struct ike_proposals ike_proposals,
+ enum ikev1_auth_method auth_method,
+ bool single_dh);
+
+-static struct alg_info_ike *ikev1_default_ike_info(void)
++static struct ike_proposals ikev1_default_ike_info(void)
+ {
+ static const struct proposal_policy policy = {
+- .ikev1 = TRUE,
++ .version = IKEv1,
++ .check_pfs_vs_dh = false,
+ .alg_is_ok = ike_alg_is_ike,
+ .warning = libreswan_log,
+ };
+
+- char err[100];
+- struct alg_info_ike *defaults =
+- alg_info_ike_create_from_str(&policy, NULL,
+- err, sizeof(err));
+- if (defaults == NULL) {
+- PEXPECT_LOG("Invalid IKEv1 default algorithms: %s", err);
++ struct proposal_parser *parser = ike_proposal_parser(&policy);
++ struct ike_proposals defaults = { .p = proposals_from_str(parser, NULL), };
++ if (defaults.p == NULL) {
++ PEXPECT_LOG("Invalid IKEv1 default algorithms: %s",
++ parser->error);
+ }
+
++ free_proposal_parser(&parser);
+ return defaults;
+ }
+
+-struct db_sa *oakley_alg_makedb(struct alg_info_ike *ai,
++struct db_sa *oakley_alg_makedb(const struct ike_proposals ike_proposals,
+ enum ikev1_auth_method auth_method,
+ bool single_dh)
+ {
+@@ -114,26 +114,25 @@
+ * standard defaults.
+ */
+
+- if (ai == NULL) {
++ if (ike_proposals.p == NULL) {
+ DBG(DBG_CONTROL, DBG_log(
+ "no specific IKE algorithms specified - using defaults"));
+- struct alg_info_ike *default_info
+- = ikev1_default_ike_info();
++ struct ike_proposals default_info = ikev1_default_ike_info();
+ struct db_sa *new_db = oakley_alg_mergedb(default_info,
+ auth_method,
+ single_dh);
+- pfree(default_info);
++ proposals_delref(&default_info.p);
+ return new_db;
+ } else {
+- return oakley_alg_mergedb(ai, auth_method, single_dh);
++ return oakley_alg_mergedb(ike_proposals, auth_method, single_dh);
+ }
+ }
+
+-struct db_sa *oakley_alg_mergedb(struct alg_info_ike *ai,
++struct db_sa *oakley_alg_mergedb(struct ike_proposals ike_proposals,
+ enum ikev1_auth_method auth_method,
+ bool single_dh)
+ {
+- passert(ai != NULL);
++ passert(ike_proposals.p != NULL);
+
+ struct db_sa *gsp = NULL;
+
+@@ -150,26 +149,28 @@
+ * when creating each item, we will use the first transform
+ * from the base item as the template.
+ */
+- FOR_EACH_IKE_INFO(ai, ike_info) {
++ FOR_EACH_PROPOSAL(ike_proposals.p, proposal) {
+ struct db_sa *emp_sp;
+
+- passert(ike_info->encrypt);
+- passert(ike_info->prf);
+- passert(ike_info->dh);
+-
+- unsigned ealg = ike_info->encrypt->common.ikev1_oakley_id;
+- unsigned halg = ike_info->prf->common.ikev1_oakley_id;
+- unsigned modp = ike_info->dh->group;
+- unsigned eklen = ike_info->enckeylen;
++ struct v1_proposal algs = v1_proposal(proposal);
++
++ passert(algs.encrypt != NULL);
++ passert(algs.prf != NULL);
++ passert(algs.dh != NULL);
++
++ unsigned ealg = algs.encrypt->common.ikev1_oakley_id;
++ unsigned halg = algs.prf->common.ikev1_oakley_id;
++ unsigned modp = algs.dh->group;
++ unsigned eklen = algs.enckeylen;
+
+ DBG(DBG_CONTROL,
+ DBG_log("oakley_alg_makedb() processing ealg=%s=%u halg=%s=%u modp=%s=%u eklen=%u",
+- ike_info->encrypt->common.name, ealg,
+- ike_info->prf->common.name, halg,
+- ike_info->dh->common.name, modp,
++ algs.encrypt->common.name, ealg,
++ algs.prf->common.name, halg,
++ algs.dh->common.name, modp,
+ eklen));
+
+- const struct encrypt_desc *enc_desc = ike_info->encrypt;
++ const struct encrypt_desc *enc_desc = algs.encrypt;
+ if (eklen != 0 && !encrypt_has_key_bit_length(enc_desc, eklen)) {
+ PEXPECT_LOG("IKEv1 proposal with ENCRYPT%s (specified) keylen:%d, not valid, should have been dropped",
+ enc_desc->common.name,
+@@ -251,7 +252,7 @@
+ * a different DH group, we try to deal with this.
+ */
+ if (single_dh && transcnt > 0 &&
+- ike_info->dh->group != last_modp) {
++ algs.dh->group != last_modp) {
+ if (
+ #ifdef USE_DH2
+ last_modp == OAKLEY_GROUP_MODP1024 ||
+@@ -269,13 +270,13 @@
+ }
+
+ loglog(RC_LOG_SERIOUS,
+- "transform (%s,%s,%s keylen %zd) ignored.",
++ "transform (%s,%s,%s keylen %d) ignored.",
+ enum_name(&oakley_enc_names,
+- ike_info->encrypt->common.ikev1_oakley_id),
++ algs.encrypt->common.ikev1_oakley_id),
+ enum_name(&oakley_hash_names,
+- ike_info->prf->common.ikev1_oakley_id),
+- ike_info->dh->common.name,
+- ike_info->enckeylen);
++ algs.prf->common.ikev1_oakley_id),
++ algs.dh->common.name,
++ algs.enckeylen);
+ free_sa(&emp_sp);
+ } else {
+ /*
+@@ -303,9 +304,9 @@
+ * Exclude 3des et.al. which do not include
+ * default key lengths in the proposal.
+ */
+- if (ike_info->enckeylen == 0 &&
+- !ike_info->encrypt->keylen_omitted) {
+- const struct encrypt_desc *enc_desc = ike_info->encrypt;
++ if (algs.enckeylen == 0 &&
++ !algs.encrypt->keylen_omitted) {
++ const struct encrypt_desc *enc_desc = algs.encrypt;
+ int def_ks = enc_desc->keydeflen;
+ passert(def_ks); /* ike=null not supported */
+ int max_ks = encrypt_max_key_bit_length(enc_desc);
+@@ -375,7 +376,7 @@
+ emp_sp = NULL;
+ }
+ }
+- last_modp = ike_info->dh->group;
++ last_modp = algs.dh->group;
+ }
+
+ transcnt++;
+diff -Naur libreswan-3.27-orig/programs/pluto/terminate.c libreswan-3.27/programs/pluto/terminate.c
+--- libreswan-3.27-orig/programs/pluto/terminate.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/pluto/terminate.c 2019-02-15 16:32:29.003835565 -0500
+@@ -53,7 +53,6 @@
+ #include "log.h"
+ #include "keys.h"
+ #include "whack.h"
+-#include "alg_info.h"
+ #include "spdb.h"
+ #include "ike_alg.h"
+ #include "kernel_alg.h"
+diff -Naur libreswan-3.27-orig/programs/spi/spi.c libreswan-3.27/programs/spi/spi.c
+--- libreswan-3.27-orig/programs/spi/spi.c 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/programs/spi/spi.c 2019-02-15 16:32:29.003835565 -0500
+@@ -61,7 +61,6 @@
+
+ #include "lswlog.h"
+ #include "lswtool.h"
+-#include "alg_info.h"
+ #include "kernel_alg.h"
+ #include "kernel_sadb.h"
+ #include "pfkey_help.h"
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.pfs.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v1.pfs.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.pfs.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/algparse.v1.pfs.txt 2019-02-15 16:46:01.379390213 -0500
+@@ -386,3 +386,9 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -v1 -pfs 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
++algparse -v1 -pfs 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
++algparse -v1 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
++algparse -v1 -pfs 'ike=aes+aes_gcm'
++ ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v1.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v1.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/algparse.v1.txt 2019-02-15 16:46:01.379390213 -0500
+@@ -418,3 +418,9 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -v1 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
++algparse -v1 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
++algparse -v1 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
++algparse -v1 'ike=aes+aes_gcm'
++ ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.pfs.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v2.pfs.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.pfs.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/algparse.v2.pfs.txt 2019-02-15 16:50:12.777728085 -0500
+@@ -1,11 +1,11 @@
+ algparse -v2 -pfs 'esp'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp='
+ ERROR: String ended with invalid char, just after ""
+ algparse -v2 -pfs 'esp=aes'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes;modp2048'
+- AES_CBC-HMAC_SHA1_96-MODP2048
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
+ algparse -v2 -pfs 'esp=aes-sha1'
+ AES_CBC-HMAC_SHA1_96
+ algparse -v2 -pfs 'esp=aes-sha1'
+@@ -13,7 +13,7 @@
+ algparse -v2 -pfs 'esp=aes-sha1-modp2048'
+ AES_CBC-HMAC_SHA1_96-MODP2048
+ algparse -v2 -pfs 'esp=aes-128'
+- AES_CBC_128-HMAC_SHA1_96
++ AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes-128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -v2 -pfs 'esp=aes-128-sha1'
+@@ -41,7 +41,7 @@
+ algparse -v2 -pfs 'esp=null-sha1'
+ NULL-HMAC_SHA1_96
+ algparse -v2 -pfs 'esp=aes_cbc'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes-sha'
+ AES_CBC-HMAC_SHA1_96
+ algparse -v2 -pfs 'esp=aes-sha1'
+@@ -73,13 +73,13 @@
+ algparse -v2 -pfs 'esp=aes256-sha2_512'
+ AES_CBC_256-HMAC_SHA2_512_256
+ algparse -v2 -pfs 'esp=camellia'
+- CAMELLIA_CBC-HMAC_SHA1_96
++ CAMELLIA_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=camellia128'
+- CAMELLIA_CBC_128-HMAC_SHA1_96
++ CAMELLIA_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=camellia192'
+- CAMELLIA_CBC_192-HMAC_SHA1_96
++ CAMELLIA_CBC_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=camellia256'
+- CAMELLIA_CBC_256-HMAC_SHA1_96
++ CAMELLIA_CBC_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes_ccm'
+ AES_CCM_16-NONE
+ algparse -v2 -pfs 'esp=aes_ccm_a-128-null'
+@@ -181,19 +181,19 @@
+ algparse -v2 -pfs 'esp=aes_gcm_16_256-null'
+ AES_GCM_16_256-NONE
+ algparse -v2 -pfs 'esp=aes_ctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aesctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes_ctr128'
+- AES_CTR_128-HMAC_SHA1_96
++ AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes_ctr192'
+- AES_CTR_192-HMAC_SHA1_96
++ AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=aes_ctr256'
+- AES_CTR_256-HMAC_SHA1_96
++ AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=serpent'
+- SERPENT_CBC-HMAC_SHA1_96
++ SERPENT_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=twofish'
+- TWOFISH_CBC-HMAC_SHA1_96
++ TWOFISH_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
+ CAMELLIA_CBC_256-HMAC_SHA2_512_256-MODP8192
+ algparse -v2 -pfs 'esp=null_auth_aes_gmac_256-null;modp8192'
+@@ -210,12 +210,12 @@
+ algparse -v2 -pfs 'esp=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
+ AES_CBC-HMAC_SHA1_96-MODP8192
+ algparse -v2 -pfs 'esp=aes;none'
+- AES_CBC-HMAC_SHA1_96-NONE
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
+ algparse -v2 -pfs 'esp=aes;none,aes'
+ ERROR: either all or no ESP proposals should specify DH
+ algparse -v2 -pfs 'esp=aes;none,aes;modp2048'
+- AES_CBC-HMAC_SHA1_96-NONE
+- AES_CBC-HMAC_SHA1_96-MODP2048
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
+ algparse -v2 -pfs 'esp=aes-sha1-none'
+ AES_CBC-HMAC_SHA1_96-NONE
+ algparse -v2 -pfs 'esp=aes-sha1;none'
+@@ -293,7 +293,7 @@
+ algparse -v2 -pfs 'esp=3des-sha1-modp8192,3des-sha2-modp2048'
+ ERROR: more than one IKEv2 ESP DH algorithm (MODP8192, MODP2048) requires unimplemented CHILD_SA INVALID_KE
+ algparse -v2 -pfs 'ah'
+- HMAC_SHA1_96
++ HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 -pfs 'ah='
+ ERROR: String ended with invalid char, just after ""
+ algparse -v2 -pfs 'ah=md5'
+@@ -345,32 +345,23 @@
+ algparse -v2 -pfs 'ah=ripemd'
+ ERROR: AH integrity algorithm 'ripemd' is not recognized
+ algparse -v2 -pfs 'ike'
+- AES_CBC-HMAC_SHA2_256-MODP2048
+- AES_CBC-HMAC_SHA2_512-MODP2048
+- AES_CBC-HMAC_SHA1-MODP2048
+- 3DES_CBC-HMAC_SHA2_256-MODP2048
+- 3DES_CBC-HMAC_SHA2_512-MODP2048
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 -pfs 'ike='
+ ERROR: String ended with invalid char, just after ""
+ algparse -v2 -pfs 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 -pfs 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 -pfs 'ike=3des-sha1;modp1536'
+ 3DES_CBC-HMAC_SHA1-MODP1536
+ algparse -v2 -pfs 'ike=3des;dh21'
+- 3DES_CBC-HMAC_SHA2_256-DH21
+- 3DES_CBC-HMAC_SHA2_512-DH21
+- 3DES_CBC-HMAC_SHA1-DH21
++ 3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
+ algparse -v2 -pfs 'ike=3des-sha1;dh21'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -v2 -pfs 'ike=3des-sha1-ecp_521'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -v2 -pfs 'ike=aes_gcm'
+- AES_GCM_16-HMAC_SHA2_256-MODP2048
+- AES_GCM_16-HMAC_SHA2_512-MODP2048
+- AES_GCM_16-HMAC_SHA1-MODP2048
++ AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 -pfs 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
+ AES_CBC-HMAC_SHA1-MODP8192
+ algparse -v2 -pfs 'ike=aes;none'
+@@ -381,3 +372,10 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -v2 -pfs 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported
++algparse -v2 -pfs 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: 'modp2048' unexpected
++algparse -v2 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ WARNING: discarding duplicate algorithm 'aes_gcm_16'
++ ERROR: 'modp2048' unexpected
++algparse -v2 -pfs 'ike=aes+aes_gcm'
++ ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v2.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v2.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/algparse.v2.txt 2019-02-15 16:50:12.778728094 -0500
+@@ -1,12 +1,12 @@
+ algparse -v2 'esp'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp='
+ ERROR: String ended with invalid char, just after ""
+ algparse -v2 'esp=aes'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes;modp2048'
+ WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes-sha1'
+ AES_CBC-HMAC_SHA1_96
+ algparse -v2 'esp=aes-sha1'
+@@ -15,7 +15,7 @@
+ WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
+ AES_CBC-HMAC_SHA1_96
+ algparse -v2 'esp=aes-128'
+- AES_CBC_128-HMAC_SHA1_96
++ AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes-128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -v2 'esp=aes-128-sha1'
+@@ -49,7 +49,7 @@
+ algparse -v2 'esp=null-sha1'
+ NULL-HMAC_SHA1_96
+ algparse -v2 'esp=aes_cbc'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes-sha'
+ AES_CBC-HMAC_SHA1_96
+ algparse -v2 'esp=aes-sha1'
+@@ -81,13 +81,13 @@
+ algparse -v2 'esp=aes256-sha2_512'
+ AES_CBC_256-HMAC_SHA2_512_256
+ algparse -v2 'esp=camellia'
+- CAMELLIA_CBC-HMAC_SHA1_96
++ CAMELLIA_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=camellia128'
+- CAMELLIA_CBC_128-HMAC_SHA1_96
++ CAMELLIA_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=camellia192'
+- CAMELLIA_CBC_192-HMAC_SHA1_96
++ CAMELLIA_CBC_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=camellia256'
+- CAMELLIA_CBC_256-HMAC_SHA1_96
++ CAMELLIA_CBC_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes_ccm'
+ AES_CCM_16-NONE
+ algparse -v2 'esp=aes_ccm_a-128-null'
+@@ -189,19 +189,19 @@
+ algparse -v2 'esp=aes_gcm_16_256-null'
+ AES_GCM_16_256-NONE
+ algparse -v2 'esp=aes_ctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aesctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes_ctr128'
+- AES_CTR_128-HMAC_SHA1_96
++ AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes_ctr192'
+- AES_CTR_192-HMAC_SHA1_96
++ AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes_ctr256'
+- AES_CTR_256-HMAC_SHA1_96
++ AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=serpent'
+- SERPENT_CBC-HMAC_SHA1_96
++ SERPENT_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=twofish'
+- TWOFISH_CBC-HMAC_SHA1_96
++ TWOFISH_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
+ WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
+ CAMELLIA_CBC_256-HMAC_SHA2_512_256
+@@ -228,16 +228,16 @@
+ AES_CBC-HMAC_SHA1_96
+ algparse -v2 'esp=aes;none'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes;none,aes'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes;none,aes;modp2048'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+ WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'esp=aes-sha1-none'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+ AES_CBC-HMAC_SHA1_96
+@@ -332,7 +332,7 @@
+ 3DES_CBC-HMAC_SHA1_96
+ 3DES_CBC-HMAC_SHA2_256_128
+ algparse -v2 'ah'
+- HMAC_SHA1_96
++ HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -v2 'ah='
+ ERROR: String ended with invalid char, just after ""
+ algparse -v2 'ah=md5'
+@@ -388,32 +388,23 @@
+ algparse -v2 'ah=ripemd'
+ ERROR: AH integrity algorithm 'ripemd' is not recognized
+ algparse -v2 'ike'
+- AES_CBC-HMAC_SHA2_256-MODP2048
+- AES_CBC-HMAC_SHA2_512-MODP2048
+- AES_CBC-HMAC_SHA1-MODP2048
+- 3DES_CBC-HMAC_SHA2_256-MODP2048
+- 3DES_CBC-HMAC_SHA2_512-MODP2048
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 'ike='
+ ERROR: String ended with invalid char, just after ""
+ algparse -v2 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 'ike=3des-sha1;modp1536'
+ 3DES_CBC-HMAC_SHA1-MODP1536
+ algparse -v2 'ike=3des;dh21'
+- 3DES_CBC-HMAC_SHA2_256-DH21
+- 3DES_CBC-HMAC_SHA2_512-DH21
+- 3DES_CBC-HMAC_SHA1-DH21
++ 3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
+ algparse -v2 'ike=3des-sha1;dh21'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -v2 'ike=3des-sha1-ecp_521'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -v2 'ike=aes_gcm'
+- AES_GCM_16-HMAC_SHA2_256-MODP2048
+- AES_GCM_16-HMAC_SHA2_512-MODP2048
+- AES_GCM_16-HMAC_SHA1-MODP2048
++ AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -v2 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
+ AES_CBC-HMAC_SHA1-MODP8192
+ algparse -v2 'ike=aes;none'
+@@ -424,3 +415,10 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -v2 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported
++algparse -v2 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: 'modp2048' unexpected
++algparse -v2 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ WARNING: discarding duplicate algorithm 'aes_gcm_16'
++ ERROR: 'modp2048' unexpected
++algparse -v2 'ike=aes+aes_gcm'
++ ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v.txt libreswan-3.27/testing/pluto/algparse-01/algparse.v.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-01/algparse.v.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/algparse.v.txt 2019-02-15 16:32:29.004835574 -0500
+@@ -13,7 +13,7 @@
+ algparse: SERPENT_CBC IKEv1: IKE ESP IKEv2: IKE ESP {256,192,*128} serpent
+ algparse: TWOFISH_CBC IKEv1: IKE ESP IKEv2: IKE ESP {256,192,*128} twofish
+ algparse: TWOFISH_SSH IKEv1: IKE IKEv2: IKE ESP {256,192,*128} twofish_cbc_ssh
+-algparse: NULL_AUTH_AES_GMAC IKEv1: ESP IKEv2: ESP {256,192,*128} aes_gmac
++algparse: NULL_AUTH_AES_GMAC IKEv1: ESP IKEv2: ESP FIPS {256,192,*128} aes_gmac
+ algparse: NULL IKEv1: ESP IKEv2: ESP []
+ algparse: CHACHA20_POLY1305 IKEv1: IKEv2: IKE ESP [*256] chacha20poly1305
+ algparse: Hash algorithms:
+@@ -28,17 +28,17 @@
+ algparse: HMAC_SHA2_256 IKEv1: IKE IKEv2: IKE FIPS sha2, sha256, sha2_256
+ algparse: HMAC_SHA2_384 IKEv1: IKE IKEv2: IKE FIPS sha384, sha2_384
+ algparse: HMAC_SHA2_512 IKEv1: IKE IKEv2: IKE FIPS sha512, sha2_512
+-algparse: AES_XCBC IKEv1: IKEv2: IKE FIPS aes128_xcbc
++algparse: AES_XCBC IKEv1: IKEv2: IKE aes128_xcbc
+ algparse: Integrity algorithms:
+ algparse: HMAC_MD5_96 IKEv1: IKE ESP AH IKEv2: IKE ESP AH md5, hmac_md5
+ algparse: HMAC_SHA1_96 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha, sha1, sha1_96, hmac_sha1
+-algparse: HMAC_SHA2_512_256 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha512, sha2_512, hmac_sha2_512
+-algparse: HMAC_SHA2_384_192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha384, sha2_384, hmac_sha2_384
+-algparse: HMAC_SHA2_256_128 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha2, sha256, sha2_256, hmac_sha2_256
++algparse: HMAC_SHA2_512_256 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha512, sha2_512, sha2_512_256, hmac_sha2_512
++algparse: HMAC_SHA2_384_192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha384, sha2_384, sha2_384_192, hmac_sha2_384
++algparse: HMAC_SHA2_256_128 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha2, sha256, sha2_256, sha2_256_128, hmac_sha2_256
+ algparse: HMAC_SHA2_256_TRUNCBUG IKEv1: ESP AH IKEv2: AH
+-algparse: AES_XCBC_96 IKEv1: ESP AH IKEv2: IKE ESP AH FIPS aes_xcbc, aes128_xcbc, aes128_xcbc_96
++algparse: AES_XCBC_96 IKEv1: ESP AH IKEv2: IKE ESP AH aes_xcbc, aes128_xcbc, aes128_xcbc_96
+ algparse: AES_CMAC_96 IKEv1: ESP AH IKEv2: ESP AH FIPS aes_cmac
+-algparse: NONE IKEv1: ESP IKEv2: ESP FIPS null
++algparse: NONE IKEv1: ESP IKEv2: IKE ESP FIPS null
+ algparse: DH algorithms:
+ algparse: NONE IKEv1: IKEv2: IKE ESP AH FIPS null, dh0
+ algparse: MODP1024 IKEv1: IKE ESP AH IKEv2: IKE ESP AH dh2
+@@ -48,8 +48,8 @@
+ algparse: MODP4096 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh16
+ algparse: MODP6144 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh17
+ algparse: MODP8192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh18
+-algparse: DH19 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_256
+-algparse: DH20 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_384
+-algparse: DH21 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_521
++algparse: DH19 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_256, ecp256
++algparse: DH20 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_384, ecp384
++algparse: DH21 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_521, ecp521
+ algparse: DH31 IKEv1: IKE IKEv2: IKE ESP AH curve25519
+ algparse: leak detective found no leaks
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/west.conf libreswan-3.27/testing/pluto/algparse-01/west.conf
+--- libreswan-3.27-orig/testing/pluto/algparse-01/west.conf 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/west.conf 2019-02-15 16:32:29.004835574 -0500
+@@ -10,4 +10,7 @@
+ protostack=netkey
+ plutodebug=all
+
++conn %default
++ ikev2=no
++
+ include /testing/baseconfigs/all/etc/ipsec.d/ipsec.conf.common
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-01/west.console.txt libreswan-3.27/testing/pluto/algparse-01/west.console.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-01/west.console.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-01/west.console.txt 2019-02-15 16:32:29.005835584 -0500
+@@ -5,8 +5,6 @@
+ ../bin/algparse.sh PATH/libexec/ipsec/algparse algparse*.txt
+ PATH/libexec/ipsec/algparse -v1 -pfs -t # algparse.v1.pfs.txt
+ PATH/libexec/ipsec/algparse -v1 -t # algparse.v1.txt
+-PATH/libexec/ipsec/algparse -v1 -v2 -pfs -t # algparse.v1.v2.pfs.txt
+-PATH/libexec/ipsec/algparse -v1 -v2 -t # algparse.v1.v2.txt
+ PATH/libexec/ipsec/algparse -v2 -pfs -t # algparse.v2.pfs.txt
+ PATH/libexec/ipsec/algparse -v2 -t # algparse.v2.txt
+ PATH/libexec/ipsec/algparse -v # algparse.v.txt
+@@ -48,7 +46,7 @@
+ SERPENT_CBC IKEv1: IKE ESP IKEv2: IKE ESP {256,192,*128} serpent
+ TWOFISH_CBC IKEv1: IKE ESP IKEv2: IKE ESP {256,192,*128} twofish
+ TWOFISH_SSH IKEv1: IKE IKEv2: IKE ESP {256,192,*128} twofish_cbc_ssh
+- NULL_AUTH_AES_GMAC IKEv1: ESP IKEv2: ESP {256,192,*128} aes_gmac
++ NULL_AUTH_AES_GMAC IKEv1: ESP IKEv2: ESP FIPS {256,192,*128} aes_gmac
+ NULL IKEv1: ESP IKEv2: ESP []
+ CHACHA20_POLY1305 IKEv1: IKEv2: IKE ESP [*256] chacha20poly1305
+ Hash algorithms:
+@@ -63,7 +61,7 @@
+ HMAC_SHA2_256 IKEv1: IKE IKEv2: IKE FIPS sha2, sha256, sha2_256
+ HMAC_SHA2_384 IKEv1: IKE IKEv2: IKE FIPS sha384, sha2_384
+ HMAC_SHA2_512 IKEv1: IKE IKEv2: IKE FIPS sha512, sha2_512
+- AES_XCBC IKEv1: IKEv2: IKE FIPS aes128_xcbc
++ AES_XCBC IKEv1: IKEv2: IKE aes128_xcbc
+ Integrity algorithms:
+ HMAC_MD5_96 IKEv1: IKE ESP AH IKEv2: IKE ESP AH md5, hmac_md5
+ HMAC_SHA1_96 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha, sha1, sha1_96, hmac_sha1
+@@ -71,7 +69,7 @@
+ HMAC_SHA2_384_192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha384, sha2_384, hmac_sha2_384
+ HMAC_SHA2_256_128 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha2, sha256, sha2_256, hmac_sha2_256
+ HMAC_SHA2_256_TRUNCBUG IKEv1: ESP AH IKEv2: AH
+- AES_XCBC_96 IKEv1: ESP AH IKEv2: IKE ESP AH FIPS aes_xcbc, aes128_xcbc, aes128_xcbc_96
++ AES_XCBC_96 IKEv1: ESP AH IKEv2: IKE ESP AH aes_xcbc, aes128_xcbc, aes128_xcbc_96
+ AES_CMAC_96 IKEv1: ESP AH IKEv2: ESP AH FIPS aes_cmac
+ NONE IKEv1: ESP IKEv2: ESP FIPS null
+ DH algorithms:
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.pfs.txt 2019-02-15 16:46:01.380390223 -0500
+@@ -57,7 +57,7 @@
+ algparse -fips -v1 -pfs 'esp=aes128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -fips -v1 -pfs 'esp=aes128-aes_xcbc'
+- AES_CBC_128-AES_XCBC_96
++ ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v1 -pfs 'esp=aes192-sha1'
+ AES_CBC_192-HMAC_SHA1_96
+ algparse -fips -v1 -pfs 'esp=aes256-sha1'
+@@ -197,7 +197,7 @@
+ algparse -fips -v1 -pfs 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
+ ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
+ algparse -fips -v1 -pfs 'esp=null_auth_aes_gmac_256-null;modp8192'
+- ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
++ NULL_AUTH_AES_GMAC_256-NONE-MODP8192
+ algparse -fips -v1 -pfs 'esp=3des-sha1;modp8192'
+ 3DES_CBC-HMAC_SHA1_96-MODP8192
+ algparse -fips -v1 -pfs 'esp=3des-sha1-modp8192'
+@@ -318,7 +318,7 @@
+ algparse -fips -v1 -pfs 'ah=sha2_512'
+ HMAC_SHA2_512_256
+ algparse -fips -v1 -pfs 'ah=aes_xcbc'
+- AES_XCBC_96
++ ERROR: AH integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v1 -pfs 'ah=sha2-none'
+ ERROR: AH DH algorithm 'none' is not supported by IKEv1
+ algparse -fips -v1 -pfs 'ah=sha2;none'
+@@ -378,3 +378,9 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -fips -v1 -pfs 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
++algparse -fips -v1 -pfs 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
++algparse -fips -v1 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
++algparse -fips -v1 -pfs 'ike=aes+aes_gcm'
++ ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v1.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v1.txt 2019-02-15 16:46:01.380390223 -0500
+@@ -61,7 +61,7 @@
+ algparse -fips -v1 'esp=aes128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -fips -v1 'esp=aes128-aes_xcbc'
+- AES_CBC_128-AES_XCBC_96
++ ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v1 'esp=aes192-sha1'
+ AES_CBC_192-HMAC_SHA1_96
+ algparse -fips -v1 'esp=aes256-sha1'
+@@ -201,7 +201,8 @@
+ algparse -fips -v1 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
+ ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
+ algparse -fips -v1 'esp=null_auth_aes_gmac_256-null;modp8192'
+- ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
++ WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
++ NULL_AUTH_AES_GMAC_256-NONE
+ algparse -fips -v1 'esp=3des-sha1;modp8192'
+ WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
+ 3DES_CBC-HMAC_SHA1_96
+@@ -345,7 +346,7 @@
+ algparse -fips -v1 'ah=sha2_512'
+ HMAC_SHA2_512_256
+ algparse -fips -v1 'ah=aes_xcbc'
+- AES_XCBC_96
++ ERROR: AH integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v1 'ah=sha2-none'
+ ERROR: AH DH algorithm 'none' is not supported by IKEv1
+ algparse -fips -v1 'ah=sha2;none'
+@@ -406,3 +407,9 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -fips -v1 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported by IKEv1
++algparse -fips -v1 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm' is not supported by IKEv1
++algparse -fips -v1 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ ERROR: IKE encryption algorithm 'aes_gcm+aes_gcm' is not recognized
++algparse -fips -v1 'ike=aes+aes_gcm'
++ ERROR: IKE encryption algorithm 'aes+aes_gcm' is not recognized
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.pfs.txt 2019-02-15 16:50:12.778728094 -0500
+@@ -1,11 +1,11 @@
+ algparse -fips -v2 -pfs 'esp'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp='
+ ERROR: String ended with invalid char, just after ""
+ algparse -fips -v2 -pfs 'esp=aes'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aes;modp2048'
+- AES_CBC-HMAC_SHA1_96-MODP2048
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
+ algparse -fips -v2 -pfs 'esp=aes-sha1'
+ AES_CBC-HMAC_SHA1_96
+ algparse -fips -v2 -pfs 'esp=aes-sha1'
+@@ -13,7 +13,7 @@
+ algparse -fips -v2 -pfs 'esp=aes-sha1-modp2048'
+ AES_CBC-HMAC_SHA1_96-MODP2048
+ algparse -fips -v2 -pfs 'esp=aes-128'
+- AES_CBC_128-HMAC_SHA1_96
++ AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aes-128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -fips -v2 -pfs 'esp=aes-128-sha1'
+@@ -41,7 +41,7 @@
+ algparse -fips -v2 -pfs 'esp=null-sha1'
+ ERROR: ESP encryption algorithm 'null' is not supported
+ algparse -fips -v2 -pfs 'esp=aes_cbc'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aes-sha'
+ AES_CBC-HMAC_SHA1_96
+ algparse -fips -v2 -pfs 'esp=aes-sha1'
+@@ -57,7 +57,7 @@
+ algparse -fips -v2 -pfs 'esp=aes128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -fips -v2 -pfs 'esp=aes128-aes_xcbc'
+- AES_CBC_128-AES_XCBC_96
++ ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v2 -pfs 'esp=aes192-sha1'
+ AES_CBC_192-HMAC_SHA1_96
+ algparse -fips -v2 -pfs 'esp=aes256-sha1'
+@@ -181,15 +181,15 @@
+ algparse -fips -v2 -pfs 'esp=aes_gcm_16_256-null'
+ AES_GCM_16_256-NONE
+ algparse -fips -v2 -pfs 'esp=aes_ctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aesctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aes_ctr128'
+- AES_CTR_128-HMAC_SHA1_96
++ AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aes_ctr192'
+- AES_CTR_192-HMAC_SHA1_96
++ AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=aes_ctr256'
+- AES_CTR_256-HMAC_SHA1_96
++ AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'esp=serpent'
+ ERROR: ESP encryption algorithm 'serpent' is not supported
+ algparse -fips -v2 -pfs 'esp=twofish'
+@@ -197,7 +197,7 @@
+ algparse -fips -v2 -pfs 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
+ ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
+ algparse -fips -v2 -pfs 'esp=null_auth_aes_gmac_256-null;modp8192'
+- ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
++ NULL_AUTH_AES_GMAC_256-NONE-MODP8192
+ algparse -fips -v2 -pfs 'esp=3des-sha1;modp8192'
+ 3DES_CBC-HMAC_SHA1_96-MODP8192
+ algparse -fips -v2 -pfs 'esp=3des-sha1-modp8192'
+@@ -210,12 +210,12 @@
+ algparse -fips -v2 -pfs 'esp=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
+ AES_CBC-HMAC_SHA1_96-MODP8192
+ algparse -fips -v2 -pfs 'esp=aes;none'
+- AES_CBC-HMAC_SHA1_96-NONE
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
+ algparse -fips -v2 -pfs 'esp=aes;none,aes'
+ ERROR: either all or no ESP proposals should specify DH
+ algparse -fips -v2 -pfs 'esp=aes;none,aes;modp2048'
+- AES_CBC-HMAC_SHA1_96-NONE
+- AES_CBC-HMAC_SHA1_96-MODP2048
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-NONE
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128-MODP2048
+ algparse -fips -v2 -pfs 'esp=aes-sha1-none'
+ AES_CBC-HMAC_SHA1_96-NONE
+ algparse -fips -v2 -pfs 'esp=aes-sha1;none'
+@@ -293,7 +293,7 @@
+ algparse -fips -v2 -pfs 'esp=3des-sha1-modp8192,3des-sha2-modp2048'
+ ERROR: more than one IKEv2 ESP DH algorithm (MODP8192, MODP2048) requires unimplemented CHILD_SA INVALID_KE
+ algparse -fips -v2 -pfs 'ah'
+- HMAC_SHA1_96
++ HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 -pfs 'ah='
+ ERROR: String ended with invalid char, just after ""
+ algparse -fips -v2 -pfs 'ah=md5'
+@@ -319,7 +319,7 @@
+ algparse -fips -v2 -pfs 'ah=sha2_512'
+ HMAC_SHA2_512_256
+ algparse -fips -v2 -pfs 'ah=aes_xcbc'
+- AES_XCBC_96
++ ERROR: AH integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v2 -pfs 'ah=sha2-none'
+ HMAC_SHA2_256_128-NONE
+ algparse -fips -v2 -pfs 'ah=sha2;none'
+@@ -345,32 +345,23 @@
+ algparse -fips -v2 -pfs 'ah=ripemd'
+ ERROR: AH integrity algorithm 'ripemd' is not recognized
+ algparse -fips -v2 -pfs 'ike'
+- AES_CBC-HMAC_SHA2_256-MODP2048
+- AES_CBC-HMAC_SHA2_512-MODP2048
+- AES_CBC-HMAC_SHA1-MODP2048
+- 3DES_CBC-HMAC_SHA2_256-MODP2048
+- 3DES_CBC-HMAC_SHA2_512-MODP2048
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 -pfs 'ike='
+ ERROR: String ended with invalid char, just after ""
+ algparse -fips -v2 -pfs 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 -pfs 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 -pfs 'ike=3des-sha1;modp1536'
+ ERROR: IKE DH algorithm 'modp1536' is not supported
+ algparse -fips -v2 -pfs 'ike=3des;dh21'
+- 3DES_CBC-HMAC_SHA2_256-DH21
+- 3DES_CBC-HMAC_SHA2_512-DH21
+- 3DES_CBC-HMAC_SHA1-DH21
++ 3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
+ algparse -fips -v2 -pfs 'ike=3des-sha1;dh21'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -fips -v2 -pfs 'ike=3des-sha1-ecp_521'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -fips -v2 -pfs 'ike=aes_gcm'
+- AES_GCM_16-HMAC_SHA2_256-MODP2048
+- AES_GCM_16-HMAC_SHA2_512-MODP2048
+- AES_GCM_16-HMAC_SHA1-MODP2048
++ AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 -pfs 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
+ AES_CBC-HMAC_SHA1-MODP8192
+ algparse -fips -v2 -pfs 'ike=aes;none'
+@@ -381,3 +372,10 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -fips -v2 -pfs 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported
++algparse -fips -v2 -pfs 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: 'modp2048' unexpected
++algparse -fips -v2 -pfs 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ WARNING: discarding duplicate algorithm 'aes_gcm_16'
++ ERROR: 'modp2048' unexpected
++algparse -fips -v2 -pfs 'ike=aes+aes_gcm'
++ ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v2.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v2.txt 2019-02-15 16:50:12.778728094 -0500
+@@ -1,12 +1,12 @@
+ algparse -fips -v2 'esp'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp='
+ ERROR: String ended with invalid char, just after ""
+ algparse -fips -v2 'esp=aes'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes;modp2048'
+ WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes-sha1'
+ AES_CBC-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes-sha1'
+@@ -15,7 +15,7 @@
+ WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
+ AES_CBC-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes-128'
+- AES_CBC_128-HMAC_SHA1_96
++ AES_CBC_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes-128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes-128-sha1'
+@@ -47,7 +47,7 @@
+ algparse -fips -v2 'esp=null-sha1'
+ ERROR: ESP encryption algorithm 'null' is not supported
+ algparse -fips -v2 'esp=aes_cbc'
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes-sha'
+ AES_CBC-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes-sha1'
+@@ -63,7 +63,7 @@
+ algparse -fips -v2 'esp=aes128-sha1'
+ AES_CBC_128-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes128-aes_xcbc'
+- AES_CBC_128-AES_XCBC_96
++ ERROR: ESP integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v2 'esp=aes192-sha1'
+ AES_CBC_192-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes256-sha1'
+@@ -187,15 +187,15 @@
+ algparse -fips -v2 'esp=aes_gcm_16_256-null'
+ AES_GCM_16_256-NONE
+ algparse -fips -v2 'esp=aes_ctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aesctr'
+- AES_CTR-HMAC_SHA1_96
++ AES_CTR-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes_ctr128'
+- AES_CTR_128-HMAC_SHA1_96
++ AES_CTR_128-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes_ctr192'
+- AES_CTR_192-HMAC_SHA1_96
++ AES_CTR_192-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes_ctr256'
+- AES_CTR_256-HMAC_SHA1_96
++ AES_CTR_256-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=serpent'
+ ERROR: ESP encryption algorithm 'serpent' is not supported
+ algparse -fips -v2 'esp=twofish'
+@@ -203,7 +203,8 @@
+ algparse -fips -v2 'esp=camellia_cbc_256-hmac_sha2_512_256;modp8192'
+ ERROR: ESP encryption algorithm 'camellia_cbc_256' is not supported
+ algparse -fips -v2 'esp=null_auth_aes_gmac_256-null;modp8192'
+- ERROR: ESP encryption algorithm 'null_auth_aes_gmac_256' is not supported
++ WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
++ NULL_AUTH_AES_GMAC_256-NONE
+ algparse -fips -v2 'esp=3des-sha1;modp8192'
+ WARNING: ignoring ESP DH algorithm MODP8192 as PFS policy is disabled
+ 3DES_CBC-HMAC_SHA1_96
+@@ -224,16 +225,16 @@
+ AES_CBC-HMAC_SHA1_96
+ algparse -fips -v2 'esp=aes;none'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes;none,aes'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes;none,aes;modp2048'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+ WARNING: ignoring ESP DH algorithm MODP2048 as PFS policy is disabled
+- AES_CBC-HMAC_SHA1_96
+- AES_CBC-HMAC_SHA1_96
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
++ AES_CBC-HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'esp=aes-sha1-none'
+ WARNING: ignoring redundant ESP DH algorithm NONE as PFS policy is disabled
+ AES_CBC-HMAC_SHA1_96
+@@ -328,7 +329,7 @@
+ 3DES_CBC-HMAC_SHA1_96
+ 3DES_CBC-HMAC_SHA2_256_128
+ algparse -fips -v2 'ah'
+- HMAC_SHA1_96
++ HMAC_SHA2_512_256+HMAC_SHA2_256_128
+ algparse -fips -v2 'ah='
+ ERROR: String ended with invalid char, just after ""
+ algparse -fips -v2 'ah=md5'
+@@ -355,7 +356,7 @@
+ algparse -fips -v2 'ah=sha2_512'
+ HMAC_SHA2_512_256
+ algparse -fips -v2 'ah=aes_xcbc'
+- AES_XCBC_96
++ ERROR: AH integrity algorithm 'aes_xcbc' is not supported
+ algparse -fips -v2 'ah=sha2-none'
+ WARNING: ignoring redundant AH DH algorithm NONE as PFS policy is disabled
+ HMAC_SHA2_256_128
+@@ -384,32 +385,23 @@
+ algparse -fips -v2 'ah=ripemd'
+ ERROR: AH integrity algorithm 'ripemd' is not recognized
+ algparse -fips -v2 'ike'
+- AES_CBC-HMAC_SHA2_256-MODP2048
+- AES_CBC-HMAC_SHA2_512-MODP2048
+- AES_CBC-HMAC_SHA1-MODP2048
+- 3DES_CBC-HMAC_SHA2_256-MODP2048
+- 3DES_CBC-HMAC_SHA2_512-MODP2048
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ AES_CBC+3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 'ike='
+ ERROR: String ended with invalid char, just after ""
+ algparse -fips -v2 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 'ike=3des-sha1'
+- 3DES_CBC-HMAC_SHA1-MODP2048
++ 3DES_CBC-HMAC_SHA1-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 'ike=3des-sha1;modp1536'
+ ERROR: IKE DH algorithm 'modp1536' is not supported
+ algparse -fips -v2 'ike=3des;dh21'
+- 3DES_CBC-HMAC_SHA2_256-DH21
+- 3DES_CBC-HMAC_SHA2_512-DH21
+- 3DES_CBC-HMAC_SHA1-DH21
++ 3DES_CBC-HMAC_SHA2_512+HMAC_SHA2_256-DH21
+ algparse -fips -v2 'ike=3des-sha1;dh21'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -fips -v2 'ike=3des-sha1-ecp_521'
+ 3DES_CBC-HMAC_SHA1-DH21
+ algparse -fips -v2 'ike=aes_gcm'
+- AES_GCM_16-HMAC_SHA2_256-MODP2048
+- AES_GCM_16-HMAC_SHA2_512-MODP2048
+- AES_GCM_16-HMAC_SHA1-MODP2048
++ AES_GCM_16-HMAC_SHA2_512+HMAC_SHA2_256-MODP2048+MODP3072+MODP4096+MODP8192+DH19+DH20+DH21+DH31
+ algparse -fips -v2 'ike=aes-sha1-modp8192,aes-sha1-modp8192,aes-sha1-modp8192'
+ AES_CBC-HMAC_SHA1-MODP8192
+ algparse -fips -v2 'ike=aes;none'
+@@ -420,3 +412,10 @@
+ ERROR: IKE PRF algorithm 'id2' is not recognized
+ algparse -fips -v2 'ike=aes_ccm'
+ ERROR: IKE encryption algorithm 'aes_ccm' is not supported
++algparse -fips -v2 'ike=aes_gcm-sha1-none-modp2048'
++ ERROR: 'modp2048' unexpected
++algparse -fips -v2 'ike=aes_gcm+aes_gcm-sha1-none-modp2048'
++ WARNING: discarding duplicate algorithm 'aes_gcm_16'
++ ERROR: 'modp2048' unexpected
++algparse -fips -v2 'ike=aes+aes_gcm'
++ ERROR: AEAD and non-AEAD IKE encryption algorithm can not be combined
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v.txt libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/algparse.v.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/algparse.v.txt 2019-02-15 16:32:29.006835593 -0500
+@@ -3,13 +3,14 @@
+ algparse: Encryption algorithm SERPENT_CBC disabled; not FIPS compliant
+ algparse: Encryption algorithm TWOFISH_CBC disabled; not FIPS compliant
+ algparse: Encryption algorithm TWOFISH_SSH disabled; not FIPS compliant
+-algparse: Encryption algorithm NULL_AUTH_AES_GMAC disabled; not FIPS compliant
+ algparse: Encryption algorithm NULL disabled; not FIPS compliant
+ algparse: Encryption algorithm CHACHA20_POLY1305 disabled; not FIPS compliant
+ algparse: Hash algorithm MD5 disabled; not FIPS compliant
+ algparse: PRF algorithm HMAC_MD5 disabled; not FIPS compliant
++algparse: PRF algorithm AES_XCBC disabled; not FIPS compliant
+ algparse: Integrity algorithm HMAC_MD5_96 disabled; not FIPS compliant
+ algparse: Integrity algorithm HMAC_SHA2_256_TRUNCBUG disabled; not FIPS compliant
++algparse: Integrity algorithm AES_XCBC_96 disabled; not FIPS compliant
+ algparse: DH algorithm MODP1024 disabled; not FIPS compliant
+ algparse: DH algorithm MODP1536 disabled; not FIPS compliant
+ algparse: DH algorithm DH31 disabled; not FIPS compliant
+@@ -23,6 +24,7 @@
+ algparse: AES_GCM_8 IKEv1: ESP IKEv2: IKE ESP FIPS {256,192,*128} aes_gcm_a
+ algparse: AES_CTR IKEv1: IKE ESP IKEv2: IKE ESP FIPS {256,192,*128} aesctr
+ algparse: AES_CBC IKEv1: IKE ESP IKEv2: IKE ESP FIPS {256,192,*128} aes
++algparse: NULL_AUTH_AES_GMAC IKEv1: ESP IKEv2: ESP FIPS {256,192,*128} aes_gmac
+ algparse: FIPS Hash algorithms:
+ algparse: SHA1 IKEv1: IKE IKEv2: FIPS sha
+ algparse: SHA2_256 IKEv1: IKE IKEv2: FIPS sha2, sha256
+@@ -33,15 +35,13 @@
+ algparse: HMAC_SHA2_256 IKEv1: IKE IKEv2: IKE FIPS sha2, sha256, sha2_256
+ algparse: HMAC_SHA2_384 IKEv1: IKE IKEv2: IKE FIPS sha384, sha2_384
+ algparse: HMAC_SHA2_512 IKEv1: IKE IKEv2: IKE FIPS sha512, sha2_512
+-algparse: AES_XCBC IKEv1: IKEv2: IKE FIPS aes128_xcbc
+ algparse: FIPS Integrity algorithms:
+ algparse: HMAC_SHA1_96 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha, sha1, sha1_96, hmac_sha1
+-algparse: HMAC_SHA2_512_256 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha512, sha2_512, hmac_sha2_512
+-algparse: HMAC_SHA2_384_192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha384, sha2_384, hmac_sha2_384
+-algparse: HMAC_SHA2_256_128 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha2, sha256, sha2_256, hmac_sha2_256
+-algparse: AES_XCBC_96 IKEv1: ESP AH IKEv2: IKE ESP AH FIPS aes_xcbc, aes128_xcbc, aes128_xcbc_96
++algparse: HMAC_SHA2_512_256 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha512, sha2_512, sha2_512_256, hmac_sha2_512
++algparse: HMAC_SHA2_384_192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha384, sha2_384, sha2_384_192, hmac_sha2_384
++algparse: HMAC_SHA2_256_128 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha2, sha256, sha2_256, sha2_256_128, hmac_sha2_256
+ algparse: AES_CMAC_96 IKEv1: ESP AH IKEv2: ESP AH FIPS aes_cmac
+-algparse: NONE IKEv1: ESP IKEv2: ESP FIPS null
++algparse: NONE IKEv1: ESP IKEv2: IKE ESP FIPS null
+ algparse: FIPS DH algorithms:
+ algparse: NONE IKEv1: IKEv2: IKE ESP AH FIPS null, dh0
+ algparse: MODP2048 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh14
+@@ -49,7 +49,7 @@
+ algparse: MODP4096 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh16
+ algparse: MODP6144 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh17
+ algparse: MODP8192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS dh18
+-algparse: DH19 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_256
+-algparse: DH20 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_384
+-algparse: DH21 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_521
++algparse: DH19 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_256, ecp256
++algparse: DH20 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_384, ecp384
++algparse: DH21 IKEv1: IKE IKEv2: IKE ESP AH FIPS ecp_521, ecp521
+ algparse: leak detective found no leaks
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.conf libreswan-3.27/testing/pluto/algparse-02-fips/west.conf
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.conf 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/west.conf 2019-02-15 16:32:29.006835593 -0500
+@@ -10,4 +10,7 @@
+ protostack=netkey
+ plutodebug=all
+
++conn %default
++ ikev2=no
++
+ include /testing/baseconfigs/all/etc/ipsec.d/ipsec.conf.common
+diff -Naur libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.console.txt libreswan-3.27/testing/pluto/algparse-02-fips/west.console.txt
+--- libreswan-3.27-orig/testing/pluto/algparse-02-fips/west.console.txt 2018-10-07 22:52:09.000000000 -0400
++++ libreswan-3.27/testing/pluto/algparse-02-fips/west.console.txt 2019-02-15 16:32:29.006835593 -0500
+@@ -7,8 +7,6 @@
+ ../bin/algparse.sh PATH/libexec/ipsec/algparse algparse*.txt
+ PATH/libexec/ipsec/algparse -v1 -pfs -t # algparse.v1.pfs.txt
+ PATH/libexec/ipsec/algparse -v1 -t # algparse.v1.txt
+-PATH/libexec/ipsec/algparse -v1 -v2 -pfs -t # algparse.v1.v2.pfs.txt
+-PATH/libexec/ipsec/algparse -v1 -v2 -t # algparse.v1.v2.txt
+ PATH/libexec/ipsec/algparse -v2 -pfs -t # algparse.v2.pfs.txt
+ PATH/libexec/ipsec/algparse -v2 -t # algparse.v2.txt
+ PATH/libexec/ipsec/algparse -v # algparse.v.txt
+@@ -50,6 +48,7 @@
+ AES_GCM_8 IKEv1: ESP IKEv2: IKE ESP FIPS {256,192,*128} aes_gcm_a
+ AES_CTR IKEv1: IKE ESP IKEv2: IKE ESP FIPS {256,192,*128} aesctr
+ AES_CBC IKEv1: IKE ESP IKEv2: IKE ESP FIPS {256,192,*128} aes
++ NULL_AUTH_AES_GMAC IKEv1: ESP IKEv2: ESP FIPS {256,192,*128} aes_gmac
+ FIPS Hash algorithms:
+ SHA1 IKEv1: IKE IKEv2: FIPS sha
+ SHA2_256 IKEv1: IKE IKEv2: FIPS sha2, sha256
+@@ -60,13 +59,11 @@
+ HMAC_SHA2_256 IKEv1: IKE IKEv2: IKE FIPS sha2, sha256, sha2_256
+ HMAC_SHA2_384 IKEv1: IKE IKEv2: IKE FIPS sha384, sha2_384
+ HMAC_SHA2_512 IKEv1: IKE IKEv2: IKE FIPS sha512, sha2_512
+- AES_XCBC IKEv1: IKEv2: IKE FIPS aes128_xcbc
+ FIPS Integrity algorithms:
+ HMAC_SHA1_96 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha, sha1, sha1_96, hmac_sha1
+ HMAC_SHA2_512_256 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha512, sha2_512, hmac_sha2_512
+ HMAC_SHA2_384_192 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha384, sha2_384, hmac_sha2_384
+ HMAC_SHA2_256_128 IKEv1: IKE ESP AH IKEv2: IKE ESP AH FIPS sha2, sha256, sha2_256, hmac_sha2_256
+- AES_XCBC_96 IKEv1: ESP AH IKEv2: IKE ESP AH FIPS aes_xcbc, aes128_xcbc, aes128_xcbc_96
+ AES_CMAC_96 IKEv1: ESP AH IKEv2: ESP AH FIPS aes_cmac
+ NONE IKEv1: ESP IKEv2: ESP FIPS null
+ FIPS DH algorithms:
diff --git a/SPECS/libreswan.spec b/SPECS/libreswan.spec
new file mode 100644
index 0000000..463f35c
--- /dev/null
+++ b/SPECS/libreswan.spec
@@ -0,0 +1,433 @@
+%global _hardened_build 1
+# These are rpm macros and are 0 or 1
+%global with_efence 0
+%global with_development 0
+%global with_cavstests 1
+# Libreswan config options
+%global libreswan_config \\\
+ FINALLIBEXECDIR=%{_libexecdir}/ipsec \\\
+ FINALMANDIR=%{_mandir} \\\
+ FIPSPRODUCTCHECK=%{_sysconfdir}/system-fips \\\
+ INC_RCDEFAULT=%{_initrddir} \\\
+ INC_USRLOCAL=%{_prefix} \\\
+ INITSYSTEM=systemd \\\
+ NSS_REQ_AVA_COPY=false \\\
+ NSS_HAS_IPSEC_PROFILE=true \\\
+ USE_DNSSEC=true \\\
+ USE_FIPSCHECK=true \\\
+ USE_LABELED_IPSEC=true \\\
+ USE_LDAP=true \\\
+ USE_LIBCAP_NG=true \\\
+ USE_LIBCURL=true \\\
+ USE_LINUX_AUDIT=true \\\
+ USE_NM=true \\\
+ USE_SECCOMP=true \\\
+ USE_XAUTHPAM=true \\\
+ USE_KLIPS=false \\\
+%{nil}
+
+#global prever rc1
+
+Name: libreswan
+Summary: IPsec implementation with IKEv1 and IKEv2 keying protocols
+# version is generated in the release script
+Version: 3.27
+Release: %{?prever:0.}9%{?prever:.%{prever}}%{?dist}
+License: GPLv2
+Url: https://libreswan.org/
+Source0: https://download.libreswan.org/%{?prever:with_development/}%{name}-%{version}%{?prever}.tar.gz
+%if 0%{with_cavstests}
+Source1: https://download.libreswan.org/cavs/ikev1_dsa.fax.bz2
+Source2: https://download.libreswan.org/cavs/ikev1_psk.fax.bz2
+Source3: https://download.libreswan.org/cavs/ikev2.fax.bz2
+%endif
+
+Patch1: libreswan-3.27-1645137-addconn.patch
+Patch2: libreswan-3.27-1648776-v1v2split.patch
+Patch3: libreswan-3.25-EKU-1639404.patch
+Patch4: libreswan-3.25-1664521-fips-keysize.patch
+Patch5: libreswan-3.27-outgoing-ports-1668342.patch
+Patch6: libreswan-3.27-1671793-delete.patch
+Patch7: libreswan-3.27-parser.patch
+
+Group: System Environment/Daemons
+BuildRequires: bison flex pkgconfig
+BuildRequires: systemd systemd-units systemd-devel
+Requires(post): coreutils bash systemd
+Requires(preun): systemd
+Requires(postun): systemd
+
+BuildRequires: pkgconfig hostname
+# minimum version for support for rhbz#1651314
+BuildRequires: nss-devel >= 3.39.0-1.4
+Requires: nss >= 3.39.0-1.4
+BuildRequires: nspr-devel
+BuildRequires: pam-devel
+BuildRequires: libevent-devel
+BuildRequires: unbound-devel >= 1.6.0-6 ldns-devel
+BuildRequires: libseccomp-devel
+BuildRequires: libselinux-devel
+BuildRequires: fipscheck-devel
+Requires: fipscheck%{_isa}
+Buildrequires: audit-libs-devel
+
+BuildRequires: libcap-ng-devel
+BuildRequires: openldap-devel curl-devel
+%if 0%{with_efence}
+BuildRequires: ElectricFence
+%endif
+BuildRequires: xmlto
+
+Requires: nss-tools, nss-softokn
+Requires: iproute >= 2.6.8
+Requires: unbound-libs >= 1.6.6
+
+%description
+Libreswan is a free implementation of IKE/IPsec for Linux. IPsec is
+the Internet Protocol Security and uses strong cryptography to provide
+both authentication and encryption services. These services allow you
+to build secure tunnels through untrusted networks. Everything passing
+through the untrusted net is encrypted by the ipsec gateway machine and
+decrypted by the gateway at the other end of the tunnel. The resulting
+tunnel is a virtual private network or VPN.
+
+This package contains the daemons and userland tools for setting up
+Libreswan.
+
+Libreswan also supports IKEv2 (RFC7296) and Secure Labeling
+
+Libreswan is based on Openswan-2.6.38 which in turn is based on FreeS/WAN-2.04
+
+%prep
+%setup -q -n libreswan-%{version}%{?prever}
+pathfix.py -i %{__python3} -pn programs/verify/verify.in programs/show/show.in \
+ testing/cert_verify/usage_test testing/pluto/ikev1-01-fuzzer/cve-2015-3204.py \
+ testing/pluto/ikev2-15-fuzzer/send_bad_packets.py testing/x509/dist_certs.py \
+ programs/_unbound-hook/_unbound-hook.in
+
+# replace unsupported KLIPS README
+echo "KLIPS is not supported with RHEL8" > README.KLIPS
+
+# linking to freebl is not needed
+sed -i "s/-lfreebl //" mk/config.mk
+
+# enable crypto-policies support
+sed -i "s:#[ ]*include \(.*\)\(/crypto-policies/back-ends/libreswan.config\)$:include \1\2:" programs/configs/ipsec.conf.in
+
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+
+%build
+%if 0%{with_efence}
+%global efence "-lefence"
+%endif
+
+#796683: -fno-strict-aliasing
+make %{?_smp_mflags} \
+%if 0%{with_development}
+ USERCOMPILE="-g -DGCC_LINT %(echo %{optflags} | sed -e s/-O[0-9]*/ /) %{?efence} -fPIE -pie -fno-strict-aliasing -Wformat-nonliteral -Wformat-security" \
+%else
+ USERCOMPILE="-g -DGCC_LINT %{optflags} %{?efence} -fPIE -pie -fno-strict-aliasing -Wformat-nonliteral -Wformat-security" \
+%endif
+ USERLINK="-g -pie -Wl,-z,relro,-z,now %{?efence}" \
+ %{libreswan_config} \
+ programs
+FS=$(pwd)
+
+# Add generation of HMAC checksums of the final stripped binaries
+%define __spec_install_post \
+ %{?__debug_package:%{__debug_install_post}} \
+ %{__arch_install_post} \
+ %{__os_install_post} \
+ fipshmac -d %{buildroot}%{_libdir}/fipscheck %{buildroot}%{_libexecdir}/ipsec/pluto \
+%{nil}
+
+%install
+make \
+ DESTDIR=%{buildroot} \
+ %{libreswan_config} \
+ install
+FS=$(pwd)
+rm -rf %{buildroot}/usr/share/doc/libreswan
+
+install -d -m 0755 %{buildroot}%{_rundir}/pluto
+# used when setting --perpeerlog without --perpeerlogbase
+install -d -m 0700 %{buildroot}%{_localstatedir}/log/pluto/peer
+install -d %{buildroot}%{_sbindir}
+
+install -d %{buildroot}%{_sysconfdir}/sysctl.d
+install -m 0644 packaging/fedora/libreswan-sysctl.conf \
+ %{buildroot}%{_sysconfdir}/sysctl.d/50-libreswan.conf
+
+install -d %{buildroot}%{_tmpfilesdir}
+install -m 0644 packaging/fedora/libreswan-tmpfiles.conf \
+ %{buildroot}%{_tmpfilesdir}/libreswan.conf
+
+mkdir -p %{buildroot}%{_libdir}/fipscheck
+
+echo "include %{_sysconfdir}/ipsec.d/*.secrets" \
+ > %{buildroot}%{_sysconfdir}/ipsec.secrets
+rm -fr %{buildroot}%{_sysconfdir}/rc.d/rc*
+
+%if 0%{with_cavstests}
+%check
+# There is an elaborate upstream testing infrastructure which we do not
+# run here - it takes hours and uses kvm
+# We only run the CAVS tests.
+cp %{SOURCE1} %{SOURCE2} %{SOURCE3} .
+bunzip2 *.fax.bz2
+
+: starting CAVS test for IKEv2
+%{buildroot}%{_libexecdir}/ipsec/cavp -v2 ikev2.fax | \
+ diff -u ikev2.fax - > /dev/null
+: starting CAVS test for IKEv1 RSASIG
+%{buildroot}%{_libexecdir}/ipsec/cavp -v1dsa ikev1_dsa.fax | \
+ diff -u ikev1_dsa.fax - > /dev/null
+: starting CAVS test for IKEv1 PSK
+%{buildroot}%{_libexecdir}/ipsec/cavp -v1psk ikev1_psk.fax | \
+ diff -u ikev1_psk.fax - > /dev/null
+: CAVS tests passed
+
+%{buildroot}%{_libexecdir}/ipsec/algparse -tp || { echo prooposal test failed; exit 1; }
+%{buildroot}%{_libexecdir}/ipsec/algparse -ta || { echo algorithm test failed; exit 1; }
+
+%endif
+
+%post
+%systemd_post ipsec.service
+
+%preun
+%systemd_preun ipsec.service
+
+%postun
+%systemd_postun_with_restart ipsec.service
+
+%files
+%doc CHANGES COPYING CREDITS README* LICENSE
+%doc docs/*.* docs/examples
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ipsec.conf
+%attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ipsec.secrets
+%attr(0700,root,root) %dir %{_sysconfdir}/ipsec.d
+%attr(0700,root,root) %dir %{_sysconfdir}/ipsec.d/policies
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ipsec.d/policies/*
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/sysctl.d/50-libreswan.conf
+%attr(0700,root,root) %dir %{_localstatedir}/log/pluto
+%attr(0700,root,root) %dir %{_localstatedir}/log/pluto/peer
+%attr(0755,root,root) %dir %{_rundir}/pluto
+%attr(0644,root,root) %{_tmpfilesdir}/libreswan.conf
+%attr(0644,root,root) %{_unitdir}/ipsec.service
+%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/pam.d/pluto
+%{_sbindir}/ipsec
+%{_libexecdir}/ipsec
+%attr(0644,root,root) %doc %{_mandir}/*/*
+%{_libdir}/fipscheck/pluto.hmac
+
+%changelog
+* Thu Feb 21 2019 Paul Wouters - 3.27-9
+- Resolves: rhbz#1648776 limit connections to be ikev1only or ikev2only and make ikev2only the default [man page update]
+
+* Fri Feb 15 2019 Paul Wouters - 3.27-8
+- Resolves: rhbz#1664101 system wide crypto policies causing IKE_INIT packet fragmentation
+
+* Tue Feb 05 2019 Paul Wouters - 3.27-7
+- Resolves: rhbz#1671793 proessing ISAKMP_NEXT_D with additional payloads causes dangling pointer to deleted state
+
+* Fri Feb 01 2019 Paul Wouters - 3.27-6
+- Resolves: rhbz#1668342 SELinux prevents libreswan from using some outbound ports causing DNS resolution failures at connection at load time
+
+* Thu Jan 10 2019 Paul Wouters - 3.27-5
+- Resolves: rhbz#1664522 libreswan 3.25 in FIPS mode is incorrectly rejecting X.509 public keys that are >= 3072 bits
+
+* Mon Dec 10 2018 Paul Wouters - 3.27-4
+- Resolves: rhbz#1657846 libreswan no longer needs to provide openswan in rhel8
+- Resolves: rhbz#1643388 libreswan: Unable to verify certificate with non-empty Extended Key Usage which does not include serverAuth or clientAuth
+- Resolves: rhbz#1657854 remove userland support for deprecated KLIPS IPsec stack support
+
+* Sun Dec 09 2018 Paul Wouters - 3.27-3
+- Resolves: rhbz#1648776 limit connections to be ikev1only or ikev2only and make ikev2only the default
+
+* Thu Nov 08 2018 Paul Wouters - 3.27-2
+- Resolves: rhbz#1645137 Libreswan segfaults when it loads configuration file with more then 5 connections
+
+* Mon Oct 08 2018 Paul Wouters - 3.27-1
+- Resolves: rhbz#1566574 Rebase to libreswan 3.27
+
+* Mon Sep 17 2018 Paul Wouters - 3.26-1
+- Resolves: rhbz#1566574 Rebase to libreswan 3.26
+- Resolves: rhbz#1527037 libreswan IPSEC implementation: should follow the policies of system-wide crypto policy
+- Resolves: rhbz#1375779 [IKEv2 Conformance] Test IKEv2.EN.R.1.1.6.7: Sending INVALID_KE_PAYLOAD failed
+- Resolves: rhbz#1085758 [TAHI][IKEv2] IKEv2.EN.I.1.2.1.1: Can't observe CREATE_CHILD_SA request for rekey
+- Resolves: rhbz#1053048 [TAHI][IKEv2] IKEv2.EN.I.1.2.4.1-7: libreswan doesn't sent CREATE_CHILD_SA after IKE_SA Lifetime timeout
+
+* Mon Aug 13 2018 Paul Wouters - 3.25-4
+- Resolves: rhbz#1590823 libreswan: Use Python 3 in RHEL 8
+
+* Wed Aug 01 2018 Charalampos Stratakis - 3.25-3.1
+- Rebuild for platform-python
+
+* Mon Jul 09 2018 Paul Wouters - 3.25-3
+- Cleanup shebangs for python3
+- Use the same options via macro for make programs and make install
+- Remove old ifdefs
+- Sync up patches to new upstream version
+- Add Requires: for unbound-libs >= 1.6.6
+- Enable crypto-policies support
+- Make rundir world readable for easier permission granting for socket
+
+* Tue Jun 26 2018 Charalampos Stratakis - 3.23-2.2
+- Make python shebangs point to python3
+
+* Fri Jun 22 2018 Troy Dawson - 3.23-2.1
+- Fix python shebangs (#1580773)
+
+* Mon Feb 19 2018 Paul Wouters - 3.23-2
+- Support crypto-policies package
+- Pull in some patches from upstream and IANA registry updates
+- gcc7 format-truncate fixes and workarounds
+
+* Wed Feb 07 2018 Fedora Release Engineering - 3.23-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Thu Jan 25 2018 Paul Wouters - 3.23-1
+- Updated to 3.23 - support for MOBIKE, PPK, CMAC, nic offload and performance improvements
+
+* Sat Jan 20 2018 Björn Esser - 3.22-1.1
+- Rebuilt for switch to libxcrypt
+
+* Mon Oct 23 2017 Paul Wouters - 3.22-1
+- Updated to 3.22 - many bugfixes, and unbound ipsecmod support
+
+* Wed Aug 9 2017 Paul Wouters - 3.21-1
+- Updated to 3.21
+
+* Thu Aug 03 2017 Fedora Release Engineering - 3.20-1.2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering - 3.20-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Tue Mar 14 2017 Paul Wouters - 3.20-1
+- Updated to 3.20
+
+* Fri Mar 03 2017 Paul Wouters - 3.20-0.1.dr4
+- Update to 3.20dr4 to test mozbz#1336487 export CERT_CompareAVA
+
+* Fri Feb 10 2017 Fedora Release Engineering - 3.19-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Fri Feb 03 2017 Paul Wouters - 3.19-2
+- Resolves: rhbz#1392191 libreswan: crash when OSX client connects
+- Improved uniqueid and session replacing support
+- Test Buffer warning fix on size_t
+- Re-introduce --configdir for backwards compatibility
+
+* Sun Jan 15 2017 Paul Wouters - 3.19-1
+- Updated to 3.19 (see download.libreswan.org/CHANGES)
+
+* Mon Dec 19 2016 Miro Hrončok - 3.18-1.1
+- Rebuild for Python 3.6
+
+* Fri Jul 29 2016 Paul Wouters - 3.18-1
+- Updated to 3.18 for CVE-2016-5391 rhbz#1361164 and VTI support
+- Remove support for /etc/sysconfig/pluto (use native systemd instead)
+
+* Thu May 05 2016 Paul Wouters - 3.17-2
+- Resolves: rhbz#1324956 prelink is gone, /etc/prelink.conf.d/* is no longer used
+
+* Thu Apr 07 2016 Paul Wouters - 3.17-1
+- Updated to 3.17 for CVE-2016-3071
+- Disable LIBCAP_NG as it prevents unbound-control from working properly
+- Temporarilly disable WERROR due to a few minor known issues
+
+* Thu Feb 04 2016 Fedora Release Engineering - 3.16-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Fri Dec 18 2015 Paul Wouters - 3.16-1
+- Updated to 3.16 (see https://download.libreswan.org/CHANGES)
+
+* Tue Aug 11 2015 Paul Wouters - 3.15-1
+- Updated to 3.15 (see http://download.libreswan.org/CHANGES)
+- Resolves: rhbz#CVE-2015-3240 IKE daemon restart when receiving a bad DH gx
+- NSS database creation moved from spec file to service file
+- Run CAVS tests on package build
+- Added BuildRequire systemd-units and xmlto
+- Bumped minimum required nss to 3.16.1
+- Install tmpfiles
+- Install sysctl file
+- Update doc files to include
+
+* Mon Jul 13 2015 Paul Wouters - 3.13-2
+- Resolves: rhbz#1238967 Switch libreswan to use python3
+
+* Wed Jun 17 2015 Fedora Release Engineering - 3.13-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Mon Jun 01 2015 Paul Wouters - 3.13-1
+- Updated to 3.13 for CVE-2015-3204
+
+* Fri Nov 07 2014 Paul Wouters - 3.12-1
+- Updated to 3.12 Various IKEv2 fixes
+
+* Wed Oct 22 2014 Paul Wouters - 3.11-1
+- Updated to 3.11 (many fixes, including startup fixes)
+- Resolves: rhbz#1144941 libreswan 3.10 upgrade breaks old ipsec.secrets configs
+- Resolves: rhbz#1147072 ikev1 aggr mode connection fails after libreswan upgrade
+- Resolves: rhbz#1144831 Libreswan appears to start with systemd before all the NICs are up and running
+
+* Tue Sep 09 2014 Paul Wouters - 3.10-3
+- Fix some coverity issues, auto=route on bootup and snprintf on 32bit machines
+
+* Mon Sep 01 2014 Paul Wouters - 3.10-1
+- Updated to 3.10, major bugfix release, new xauth status options
+
+* Sun Aug 17 2014 Fedora Release Engineering - 3.9-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Thu Jul 10 2014 Paul Wouters - 3.9-1
+- Updated to 3.9. IKEv2 enhancements, ESP/IKE algo enhancements
+- Mark libreswan-fips.conf as config file
+- attr modifier for man pages no longer needed
+- BUGS file no longer exists upstream
+
+* Sat Jun 07 2014 Fedora Release Engineering - 3.8-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Sat Jan 18 2014 Paul Wouters - 3.8-1
+- Updated to 3.8, fixes rhbz#CVE-2013-6467 (rhbz#1054102)
+
+* Wed Dec 11 2013 Paul Wouters - 3.7-1
+- Updated to 3.7, fixes CVE-2013-4564
+- Fixes creating a bogus NSS db on startup (rhbz#1005410)
+
+* Thu Oct 31 2013 Paul Wouters - 3.6-1
+- Updated to 3.6 (IKEv2, MODECFG, Cisco interop fixes)
+- Generate empty NSS db if none exists
+
+* Mon Aug 19 2013 Paul Wouters - 3.5-3
+- Add a Provides: for openswan-doc
+
+* Sat Aug 03 2013 Fedora Release Engineering - 3.5-1.1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Mon Jul 15 2013 Paul Wouters - 3.5-2
+- Added interop patch for (some?) Cisco VPN clients sending 16 zero
+ bytes of extraneous IKE data
+- Removed fipscheck_version
+
+* Sat Jul 13 2013 Paul Wouters - 3.5-1
+- Updated to 3.5
+
+* Thu Jun 06 2013 Paul Wouters - 3.4-1
+- Updated to 3.4, which only contains style changes to kernel coding style
+- IN MEMORIAM: June 3rd, 2013 Hugh Daniel
+
+* Mon May 13 2013 Paul Wouters - 3.3-1
+- Updated to 3.3, which resolves CVE-2013-2052
+
+* Sat Apr 13 2013 Paul Wouters - 3.2-1
+- Initial package for Fedora