References: bnc#824322,CVE-2013-2053
Upstream: yes

Merge openswan-libreswan-backport-949437-atodn.patch
from  http://libreswan.org/security/CVE-2013-2053/
Patch SHA1: cdf51ed780798c9fa15fc71279673fde5c8f7379
---
 include/asn1.h                     |  4 ++-
 include/id.h                       |  2 +-
 lib/libopenswan/id.c               | 18 +++++-----
 lib/libopenswan/secrets.c          |  2 +-
 lib/libopenswan/x509dn.c           | 71 ++++++++++++++++++++++----------------
 programs/pluto/connections.c       |  2 +-
 programs/pluto/dnskey.c            |  6 +++-
 programs/pluto/myid.c              |  2 +-
 programs/pluto/rcv_whack.c         |  2 +-
 programs/showhostkey/showhostkey.c |  2 +-
 10 files changed, 66 insertions(+), 45 deletions(-)

diff --git a/include/asn1.h b/include/asn1.h
index f1d7906..14c80aa 100644
--- a/include/asn1.h
+++ b/include/asn1.h
@@ -74,8 +74,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 749bfda..1124fc9 100644
--- a/include/id.h
+++ b/include/id.h
@@ -48,7 +48,7 @@ extern void show_myid_status(void);
 extern const struct id *resolve_myid(const struct id *id);
 extern void set_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/libopenswan/id.c b/lib/libopenswan/id.c
index e5b31f2..dc75b28 100644
--- a/lib/libopenswan/id.c
+++ b/lib/libopenswan/id.c
@@ -59,27 +59,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;
@@ -113,7 +115,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
@@ -124,7 +126,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
@@ -135,7 +137,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/libopenswan/secrets.c b/lib/libopenswan/secrets.c
index e080bfd..5f81096 100644
--- a/lib/libopenswan/secrets.c
+++ b/lib/libopenswan/secrets.c
@@ -1191,7 +1191,7 @@ osw_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/libopenswan/x509dn.c b/lib/libopenswan/x509dn.c
index 5f8f273..32b55b6 100644
--- a/lib/libopenswan/x509dn.c
+++ b/lib/libopenswan/x509dn.c
@@ -464,7 +464,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
@@ -734,11 +734,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 };
@@ -756,7 +756,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;
 
@@ -829,25 +829,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:
@@ -855,9 +867,10 @@ 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 b8346cd..7dd93d6 100644
--- a/programs/pluto/connections.c
+++ b/programs/pluto/connections.c
@@ -916,7 +916,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 a092708..b2730e3 100644
--- a/programs/pluto/dnskey.c
+++ b/programs/pluto/dnskey.c
@@ -281,8 +281,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 47db5e6..1d74a54 100644
--- a/programs/pluto/myid.c
+++ b/programs/pluto/myid.c
@@ -102,7 +102,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 79867b2..1ac1a66 100644
--- a/programs/pluto/rcv_whack.c
+++ b/programs/pluto/rcv_whack.c
@@ -238,7 +238,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 4c469ee..739bb9b 100644
--- a/programs/showhostkey/showhostkey.c
+++ b/programs/showhostkey/showhostkey.c
@@ -193,7 +193,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);
-- 
1.8.1.4

