aboutsummaryrefslogtreecommitdiffstats
path: root/main/openswan/CVE-2013-2052.patch
diff options
context:
space:
mode:
Diffstat (limited to 'main/openswan/CVE-2013-2052.patch')
-rw-r--r--main/openswan/CVE-2013-2052.patch346
1 files changed, 346 insertions, 0 deletions
diff --git a/main/openswan/CVE-2013-2052.patch b/main/openswan/CVE-2013-2052.patch
new file mode 100644
index 0000000000..a34a67789b
--- /dev/null
+++ b/main/openswan/CVE-2013-2052.patch
@@ -0,0 +1,346 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+commit 7d0ca355a5c7f8337130d4b0b3e7686f2fa4d4c2
+Author: Paul Wouters <pwouters@redhat.com>
+Date: Thu Apr 25 12:44:55 2013 -0400
+
+ * security: atodn() / atoid() buffer overflow
+
+ lib/libswan/x509dn.c:atodn() does not perform any length checking
+ whatsoever on the output buffer.
+
+ Affected:
+ - Libreswan 3.0 and 3.1 (3.2 disabled the oe= option)
+ - Openswan versions up to and including 2.6.38
+ - Possibly certain strongswan 3.x/4.x versions
+
+ This overflow is exposed (pre-authentication) only in opportunistic
+ encryption mode. When it is called via receiving a certificate
+ via IKEv1 or IKEv2, and when it is loaded from disk, the buffers
+ passed to atodn() are big enough.
+
+ This means this vulnerability can only be triggered when:
+ - Opportunistic Encryption is enabled (oe=yes)
+ - The attacker is local in the same network and adds a malicious
+ reverse DNS record to the client's IP, or
+ - The attacker can trigger an OE DNS lookup to a client fully
+ configured with OE and their own key.
+
+ Libreswan and openswan versions do not enable Opportunistic Encryption
+ per default. Most distributions like RHEL, Fedora, Debian and Ubuntu
+ also do not enable OE per default.
+
+ This patch addresses the vulnerability in atodn() and further limits the
+ atoid() call not to traverse into the ASN1 case when triggered by non-cert
+ cases such as opportunistic encryption.
+
+ Vulnerability discoverd by Florian Weimer <fweimer@redhat.com> of the
+ Red Hat Product Security Team.
+
+ Patch by D. Hugh Redelmeier <hugh@mimosa.com> and Paul Wouters <pwouters@redhat.com>
+
+diff --git a/include/asn1.h b/include/asn1.h
+index d69ebf9..b812488 100644
+- --- a/include/asn1.h
++++ b/include/asn1.h
+@@ -84,8 +84,10 @@ typedef enum {
+ #define ASN1_BODY 0x20
+ #define ASN1_RAW 0x40
+
+- -#define ASN1_INVALID_LENGTH 0xffffffff
++#define ASN1_INVALID_LENGTH (~(size_t) 0) /* largest size_t */
+
++#define ASN1_MAX_LEN (1U << (8*3)) /* don't handle objects with length greater than this */
++#define ASN1_MAX_LEN_LEN 4 /* no coded length takes more than 4 bytes. */
+
+ /* definition of an ASN.1 object */
+
+diff --git a/include/id.h b/include/id.h
+index d1825b4..b440a11 100644
+- --- a/include/id.h
++++ b/include/id.h
+@@ -47,7 +47,7 @@ extern const struct id *resolve_myid(const struct id *id);
+ extern void set_myFQDN(void);
+ extern void free_myFQDN(void);
+
+- -extern err_t atoid(char *src, struct id *id, bool myid_ok);
++extern err_t atoid(char *src, struct id *id, bool myid_ok, bool oe_only);
+ extern void iptoid(const ip_address *ip, struct id *id);
+ extern unsigned char* temporary_cyclic_buffer(void);
+ extern int idtoa(const struct id *id, char *dst, size_t dstlen);
+diff --git a/lib/libswan/id.c b/lib/libswan/id.c
+index 4442971..31ca7e5 100644
+- --- a/lib/libswan/id.c
++++ b/lib/libswan/id.c
+@@ -58,27 +58,29 @@ temporary_cyclic_buffer(void)
+
+ /* Convert textual form of id into a (temporary) struct id.
+ * Note that if the id is to be kept, unshare_id_content will be necessary.
++ * This function should be split into parts so the boolean arguments can be
++ * removed -- Paul
+ */
+ err_t
+- -atoid(char *src, struct id *id, bool myid_ok)
++atoid(char *src, struct id *id, bool myid_ok, bool oe_only)
+ {
+ err_t ugh = NULL;
+
+ *id = empty_id;
+
+- - if (myid_ok && streq("%myid", src))
++ if (!oe_only && myid_ok && streq("%myid", src))
+ {
+ id->kind = ID_MYID;
+ }
+- - else if (streq("%fromcert", src))
++ else if (!oe_only && streq("%fromcert", src))
+ {
+ id->kind = ID_FROMCERT;
+ }
+- - else if (streq("%none", src))
++ else if (!oe_only && streq("%none", src))
+ {
+ id->kind = ID_NONE;
+ }
+- - else if (strchr(src, '=') != NULL)
++ else if (!oe_only && strchr(src, '=') != NULL)
+ {
+ /* we interpret this as an ASCII X.501 ID_DER_ASN1_DN */
+ id->kind = ID_DER_ASN1_DN;
+@@ -112,7 +114,7 @@ atoid(char *src, struct id *id, bool myid_ok)
+ {
+ if (*src == '@')
+ {
+- - if (*(src+1) == '#')
++ if (!oe_only && *(src+1) == '#')
+ {
+ /* if there is a second specifier (#) on the line
+ * we interprete this as ID_KEY_ID
+@@ -123,7 +125,7 @@ atoid(char *src, struct id *id, bool myid_ok)
+ ugh = ttodata(src+2, 0, 16, (char *)id->name.ptr
+ , strlen(src), &id->name.len);
+ }
+- - else if (*(src+1) == '~')
++ else if (!oe_only && *(src+1) == '~')
+ {
+ /* if there is a second specifier (~) on the line
+ * we interprete this as a binary ID_DER_ASN1_DN
+@@ -134,7 +136,7 @@ atoid(char *src, struct id *id, bool myid_ok)
+ ugh = ttodata(src+2, 0, 16, (char *)id->name.ptr
+ , strlen(src), &id->name.len);
+ }
+- - else if (*(src+1) == '[')
++ else if (!oe_only && *(src+1) == '[')
+ {
+ /* if there is a second specifier ([) on the line
+ * we interprete this as a text ID_KEY_ID, and we remove
+diff --git a/lib/libswan/secrets.c b/lib/libswan/secrets.c
+index 6e9466b..8ff80e0 100644
+- --- a/lib/libswan/secrets.c
++++ b/lib/libswan/secrets.c
+@@ -1223,7 +1223,7 @@ lsw_process_secret_records(struct secret **psecrets, int verbose,
+ }
+ else
+ {
+- - ugh = atoid(flp->tok, &id, FALSE);
++ ugh = atoid(flp->tok, &id, FALSE, FALSE);
+ }
+
+ if (ugh != NULL)
+diff --git a/lib/libswan/x509dn.c b/lib/libswan/x509dn.c
+index 61407e5..7731856 100644
+- --- a/lib/libswan/x509dn.c
++++ b/lib/libswan/x509dn.c
+@@ -472,7 +472,7 @@ static const x501rdn_t x501rdns[] = {
+ {"TCGID" , {oid_TCGID, 12}, ASN1_PRINTABLESTRING}
+ };
+
+- -#define X501_RDN_ROOF 24
++#define X501_RDN_ROOF elemsof(x501rdns)
+
+ /* Maximum length of ASN.1 distinquished name */
+ #define ASN1_BUF_LEN 512
+@@ -775,11 +775,11 @@ atodn(char *src, chunk_t *dn)
+ UNKNOWN_OID = 4
+ } state_t;
+
+- - u_char oid_len_buf[3];
+- - u_char name_len_buf[3];
+- - u_char rdn_seq_len_buf[3];
+- - u_char rdn_set_len_buf[3];
+- - u_char dn_seq_len_buf[3];
++ u_char oid_len_buf[ASN1_MAX_LEN_LEN];
++ u_char name_len_buf[ASN1_MAX_LEN_LEN];
++ u_char rdn_seq_len_buf[ASN1_MAX_LEN_LEN];
++ u_char rdn_set_len_buf[ASN1_MAX_LEN_LEN];
++ u_char dn_seq_len_buf[ASN1_MAX_LEN_LEN];
+
+ chunk_t asn1_oid_len = { oid_len_buf, 0 };
+ chunk_t asn1_name_len = { name_len_buf, 0 };
+@@ -797,7 +797,7 @@ atodn(char *src, chunk_t *dn)
+
+ err_t ugh = NULL;
+
+- - u_char *dn_ptr = dn->ptr + 4;
++ u_char *dn_ptr = dn->ptr + 1 + ASN1_MAX_LEN_LEN; /* leave room for prefix */
+
+ state_t state = SEARCH_OID;
+
+@@ -885,25 +885,37 @@ atodn(char *src, chunk_t *dn)
+ code_asn1_length(rdn_set_len, &asn1_rdn_set_len);
+
+ /* encode the relative distinguished name */
+- - *dn_ptr++ = ASN1_SET;
+- - chunkcpy(dn_ptr, asn1_rdn_set_len);
+- - *dn_ptr++ = ASN1_SEQUENCE;
+- - chunkcpy(dn_ptr, asn1_rdn_seq_len);
+- - *dn_ptr++ = ASN1_OID;
+- - chunkcpy(dn_ptr, asn1_oid_len);
+- - chunkcpy(dn_ptr, x501rdns[pos].oid);
+- - /* encode the ASN.1 character string type of the name */
+- - *dn_ptr++ = (x501rdns[pos].type == ASN1_PRINTABLESTRING
+- - && !is_printablestring(name))? ASN1_T61STRING : x501rdns[pos].type;
+- - chunkcpy(dn_ptr, asn1_name_len);
+- - chunkcpy(dn_ptr, name);
+- -
+- - /* accumulate the length of the distinguished name sequence */
+- - dn_seq_len += 1 + asn1_rdn_set_len.len + rdn_set_len;
+- -
+- - /* reset name and change state */
+- - name = empty_chunk;
+- - state = SEARCH_OID;
++ if (IDTOA_BUF < dn_ptr - dn->ptr
++ + 1 + asn1_rdn_set_len.len /* set */
++ + 1 + asn1_rdn_seq_len.len /* sequence */
++ + 1 + asn1_oid_len.len + x501rdns[pos].oid.len /* oid len, oid */
++ + 1 + asn1_name_len.len + name.len /* type name */
++ ) {
++ /* no room! */
++ ugh = "DN is too big";
++ state = UNKNOWN_OID;
++ /* I think that it is safe to continue (but perhaps pointless) */
++ } else {
++ *dn_ptr++ = ASN1_SET;
++ chunkcpy(dn_ptr, asn1_rdn_set_len);
++ *dn_ptr++ = ASN1_SEQUENCE;
++ chunkcpy(dn_ptr, asn1_rdn_seq_len);
++ *dn_ptr++ = ASN1_OID;
++ chunkcpy(dn_ptr, asn1_oid_len);
++ chunkcpy(dn_ptr, x501rdns[pos].oid);
++ /* encode the ASN.1 character string type of the name */
++ *dn_ptr++ = (x501rdns[pos].type == ASN1_PRINTABLESTRING
++ && !is_printablestring(name))? ASN1_T61STRING : x501rdns[pos].type;
++ chunkcpy(dn_ptr, asn1_name_len);
++ chunkcpy(dn_ptr, name);
++
++ /* accumulate the length of the distinguished name sequence */
++ dn_seq_len += 1 + asn1_rdn_set_len.len + rdn_set_len;
++
++ /* reset name and change state */
++ name = empty_chunk;
++ state = SEARCH_OID;
++ }
+ }
+ break;
+ case UNKNOWN_OID:
+@@ -911,9 +923,9 @@ atodn(char *src, chunk_t *dn)
+ }
+ } while (*src++ != '\0');
+
+- - /* complete the distinguished name sequence*/
+- - code_asn1_length(dn_seq_len, &asn1_dn_seq_len);
+- - dn->ptr += 3 - asn1_dn_seq_len.len;
++ /* complete the distinguished name sequence: prefix it with ASN1_SEQUENCE and length */
++ code_asn1_length((size_t)dn_seq_len, &asn1_dn_seq_len);
++ dn->ptr += ASN1_MAX_LEN_LEN + 1 - 1 - asn1_dn_seq_len.len;
+ dn->len = 1 + asn1_dn_seq_len.len + dn_seq_len;
+ dn_ptr = dn->ptr;
+ *dn_ptr++ = ASN1_SEQUENCE;
+diff --git a/programs/pluto/connections.c b/programs/pluto/connections.c
+index e8d326b..f08521b 100644
+- --- a/programs/pluto/connections.c
++++ b/programs/pluto/connections.c
+@@ -911,7 +911,7 @@ extract_end(struct end *dst, const struct whack_end *src, const char *which)
+ }
+ else
+ {
+- - err_t ugh = atoid(src->id, &dst->id, TRUE);
++ err_t ugh = atoid(src->id, &dst->id, TRUE, FALSE);
+
+ if (ugh != NULL)
+ {
+diff --git a/programs/pluto/dnskey.c b/programs/pluto/dnskey.c
+index 5525d12..78f1d0a 100644
+- --- a/programs/pluto/dnskey.c
++++ b/programs/pluto/dnskey.c
+@@ -277,8 +277,12 @@ decode_iii(char **pp, struct id *gw_id)
+ if (*p == '@')
+ {
+ /* gateway specification in this record is @FQDN */
+- - err_t ugh = atoid(p, gw_id, FALSE);
+
++ if(strspn(p,' ') >= IDTOA_BUF) {
++ return builddiag("malformed FQDN in TXT " our_TXT_attr_string ": ID too large for IDTOA_BUF");
++ }
++
++ err_t ugh = atoid(p, gw_id, FALSE, TRUE); /* only run OE related parts of atoid() */
+ if (ugh != NULL)
+ return builddiag("malformed FQDN in TXT " our_TXT_attr_string ": %s"
+ , ugh);
+diff --git a/programs/pluto/myid.c b/programs/pluto/myid.c
+index bdd0e12..2e92f25 100644
+- --- a/programs/pluto/myid.c
++++ b/programs/pluto/myid.c
+@@ -103,7 +103,7 @@ set_myid(enum myid_state s, char *idstr)
+ if (idstr != NULL)
+ {
+ struct id id;
+- - err_t ugh = atoid(idstr, &id, FALSE);
++ err_t ugh = atoid(idstr, &id, FALSE, FALSE);
+
+ if (ugh != NULL)
+ {
+diff --git a/programs/pluto/rcv_whack.c b/programs/pluto/rcv_whack.c
+index 1725357..7d5072c 100644
+- --- a/programs/pluto/rcv_whack.c
++++ b/programs/pluto/rcv_whack.c
+@@ -259,7 +259,7 @@ static void
+ key_add_request(const struct whack_message *msg)
+ {
+ struct id keyid;
+- - err_t ugh = atoid(msg->keyid, &keyid, FALSE);
++ err_t ugh = atoid(msg->keyid, &keyid, FALSE, FALSE);
+
+ if (ugh != NULL)
+ {
+diff --git a/programs/showhostkey/showhostkey.c b/programs/showhostkey/showhostkey.c
+index c9fe9cf..bf87080 100644
+- --- a/programs/showhostkey/showhostkey.c
++++ b/programs/showhostkey/showhostkey.c
+@@ -203,7 +203,7 @@ struct secret *pick_key(struct secret *host_secrets
+ struct secret *s;
+ err_t e;
+
+- - e = atoid(idname, &id, FALSE);
++ e = atoid(idname, &id, FALSE, FALSE);
+ if(e) {
+ printf("%s: key '%s' is invalid\n", progname, idname);
+ exit(4);
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.13 (GNU/Linux)
+
+iQIcBAEBCAAGBQJRkWmnAAoJEIX/S0OzD8b5EZIP+wb5LyvL4jXGYJzvalkCjWL3
+1cZp5H672jGdVvW/G3bJ5unhjpRt9ASxebHR/4LfWZuWG5U4gdPRjcz1YcuNwVnB
+xOXZ4ELWYRFFblkkHz+GO5rSRwmWhFnyGvDdN5Oh6VBcmegHvaKk6uVLPXZJpVdg
+2U1+s+x3EkrcP6IJyTa9pyhZiDWcdYVn3seyHcFCNa3R/Xkwefi3HwA2w8+L18NX
+NvIMUx2aXj70cBE5VAg+XJWIZ2Rrlf2zHDM96GUUfGIIH1mzpuxYCFbpGqISmOYI
+AAumQ9I4kQGy0ZkWn41Et3ppJvcRFoMlAz70Ay+nbZ/+eqQH9B3KfplfX2UrsXAn
+SVvMPypkMfjhUbPG8AWr//6+a0uZxa0PyibNXhhdr+3ocANaZ8ty+ehFmVl0DIBM
+rc582erQ8s4Bj8v+4vy1TzkR5HXWhwWhCjD0EnU8zGGjZ2u+1BAYgzTUG4Nqo+/Q
+ziJdc71vy+OqyLXTFMdekUuRl40BXuFHHUv6jWeslgIh2/1Z/A0NZzxs2sMFCkEW
+anTG32ridJSCqQhSXZ4xW07O5F45csH6qgze2jQdYEizATYsDqeKazEZhmakUsow
+v5gj85f5VYGWjoYjKr/HbrueEbeGpV3Twf4tZ6XyCxAjJEt6N8XWidSiMeL3gNIm
+cgXmYH+ak4nDLJGyaYDt
+=5y9o
+-----END PGP SIGNATURE-----