r26694: asn1: Fix header and some typo's.
[samba.git] / source4 / libcli / util / asn1.c
index 09d4fbb6c9abc42aaaec10ec524651064ff42565..aad55382d9b31613546ce7a150a99e4f5cd618ec 100644 (file)
@@ -1,11 +1,11 @@
 /* 
    Unix SMB/CIFS implementation.
-   simple SPNEGO routines
+   simple ASN1 routines
    Copyright (C) Andrew Tridgell 2001
    
    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
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
+#include "libcli/util/asn_1.h"
+
+/* allocate an asn1 structure */
+struct asn1_data *asn1_init(TALLOC_CTX *mem_ctx)
+{
+       struct asn1_data *ret = talloc_zero(mem_ctx, struct asn1_data);
+       if (ret == NULL) {
+               DEBUG(0,("asn1_init failed! out of memory\n"));
+       }
+       return ret;
+}
 
 /* free an asn1 structure */
-void asn1_free(ASN1_DATA *data)
+void asn1_free(struct asn1_data *data)
 {
-       SAFE_FREE(data->data);
+       talloc_free(data);
 }
 
 /* write to the ASN1 buffer, advancing the buffer pointer */
-BOOL asn1_write(ASN1_DATA *data, const void *p, int len)
+bool asn1_write(struct asn1_data *data, const void *p, int len)
 {
-       if (data->has_error) return False;
+       if (data->has_error) return false;
        if (data->length < data->ofs+len) {
-               uint8 *newp;
-               newp = Realloc(data->data, data->ofs+len);
+               uint8_t *newp;
+               newp = talloc_realloc(data, data->data, uint8_t, data->ofs+len);
                if (!newp) {
-                       SAFE_FREE(data->data);
-                       data->has_error = True;
-                       return False;
+                       asn1_free(data);
+                       data->has_error = true;
+                       return false;
                }
                data->data = newp;
                data->length = data->ofs+len;
        }
        memcpy(data->data + data->ofs, p, len);
        data->ofs += len;
-       return True;
+       return true;
 }
 
-/* useful fn for writing a uint8 */
-BOOL asn1_write_uint8(ASN1_DATA *data, uint8 v)
+/* useful fn for writing a uint8_t */
+bool asn1_write_uint8(struct asn1_data *data, uint8_t v)
 {
        return asn1_write(data, &v, 1);
 }
 
 /* push a tag onto the asn1 data buffer. Used for nested structures */
-BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
+bool asn1_push_tag(struct asn1_data *data, uint8_t tag)
 {
        struct nesting *nesting;
 
        asn1_write_uint8(data, tag);
-       nesting = (struct nesting *)malloc(sizeof(struct nesting));
+       nesting = talloc(data, struct nesting);
        if (!nesting) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
 
        nesting->start = data->ofs;
@@ -71,7 +81,7 @@ BOOL asn1_push_tag(ASN1_DATA *data, uint8 tag)
 }
 
 /* pop a tag */
-BOOL asn1_pop_tag(ASN1_DATA *data)
+bool asn1_pop_tag(struct asn1_data *data)
 {
        struct nesting *nesting;
        size_t len;
@@ -79,23 +89,43 @@ BOOL asn1_pop_tag(ASN1_DATA *data)
        nesting = data->nesting;
 
        if (!nesting) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
        len = data->ofs - (nesting->start+1);
        /* yes, this is ugly. We don't know in advance how many bytes the length
           of a tag will take, so we assumed 1 byte. If we were wrong then we 
           need to correct our mistake */
-       if (len > 255) {
+       if (len > 0xFFFFFF) {
+               data->data[nesting->start] = 0x84;
+               if (!asn1_write_uint8(data, 0)) return false;
+               if (!asn1_write_uint8(data, 0)) return false;
+               if (!asn1_write_uint8(data, 0)) return false;
+               if (!asn1_write_uint8(data, 0)) return false;
+               memmove(data->data+nesting->start+5, data->data+nesting->start+1, len);
+               data->data[nesting->start+1] = (len>>24) & 0xFF;
+               data->data[nesting->start+2] = (len>>16) & 0xFF;
+               data->data[nesting->start+3] = (len>>8) & 0xFF;
+               data->data[nesting->start+4] = len&0xff;
+       } else if (len > 0xFFFF) {
+               data->data[nesting->start] = 0x83;
+               if (!asn1_write_uint8(data, 0)) return false;
+               if (!asn1_write_uint8(data, 0)) return false;
+               if (!asn1_write_uint8(data, 0)) return false;
+               memmove(data->data+nesting->start+4, data->data+nesting->start+1, len);
+               data->data[nesting->start+1] = (len>>16) & 0xFF;
+               data->data[nesting->start+2] = (len>>8) & 0xFF;
+               data->data[nesting->start+3] = len&0xff;
+       } else if (len > 255) {
                data->data[nesting->start] = 0x82;
-               if (!asn1_write_uint8(data, 0)) return False;
-               if (!asn1_write_uint8(data, 0)) return False;
+               if (!asn1_write_uint8(data, 0)) return false;
+               if (!asn1_write_uint8(data, 0)) return false;
                memmove(data->data+nesting->start+3, data->data+nesting->start+1, len);
                data->data[nesting->start+1] = len>>8;
                data->data[nesting->start+2] = len&0xff;
        } else if (len > 127) {
                data->data[nesting->start] = 0x81;
-               if (!asn1_write_uint8(data, 0)) return False;
+               if (!asn1_write_uint8(data, 0)) return false;
                memmove(data->data+nesting->start+2, data->data+nesting->start+1, len);
                data->data[nesting->start+1] = len;
        } else {
@@ -103,53 +133,144 @@ BOOL asn1_pop_tag(ASN1_DATA *data)
        }
 
        data->nesting = nesting->next;
-       free(nesting);
-       return True;
+       talloc_free(nesting);
+       return true;
+}
+
+/* "i" is the one's complement representation, as is the normal result of an
+ * implicit signed->unsigned conversion */
+
+static bool push_int_bigendian(struct asn1_data *data, unsigned int i, bool negative)
+{
+       uint8_t lowest = i & 0xFF;
+
+       i = i >> 8;
+       if (i != 0)
+               if (!push_int_bigendian(data, i, negative))
+                       return false;
+
+       if (data->nesting->start+1 == data->ofs) {
+
+               /* We did not write anything yet, looking at the highest
+                * valued byte */
+
+               if (negative) {
+                       /* Don't write leading 0xff's */
+                       if (lowest == 0xFF)
+                               return true;
+
+                       if ((lowest & 0x80) == 0) {
+                               /* The only exception for a leading 0xff is if
+                                * the highest bit is 0, which would indicate
+                                * a positive value */
+                               if (!asn1_write_uint8(data, 0xff))
+                                       return false;
+                       }
+               } else {
+                       if (lowest & 0x80) {
+                               /* The highest bit of a positive integer is 1,
+                                * this would indicate a negative number. Push
+                                * a 0 to indicate a positive one */
+                               if (!asn1_write_uint8(data, 0))
+                                       return false;
+                       }
+               }
+       }
+
+       return asn1_write_uint8(data, lowest);
+}
+
+/* write an Integer without the tag framing. Needed for example for the LDAP
+ * Abandon Operation */
+
+bool asn1_write_implicit_Integer(struct asn1_data *data, int i)
+{
+       if (i == -1) {
+               /* -1 is special as it consists of all-0xff bytes. In
+                    push_int_bigendian this is the only case that is not
+                    properly handled, as all 0xff bytes would be handled as
+                    leading ones to be ignored. */
+               return asn1_write_uint8(data, 0xff);
+       } else {
+               return push_int_bigendian(data, i, i<0);
+       }
 }
 
 
 /* write an integer */
-BOOL asn1_write_Integer(ASN1_DATA *data, int i)
+bool asn1_write_Integer(struct asn1_data *data, int i)
 {
-       if (!asn1_push_tag(data, ASN1_INTEGER)) return False;
-       do {
-               asn1_write_uint8(data, i);
-               i = i >> 8;
-       } while (i);
+       if (!asn1_push_tag(data, ASN1_INTEGER)) return false;
+       if (!asn1_write_implicit_Integer(data, i)) return false;
        return asn1_pop_tag(data);
 }
 
-/* write an object ID to a ASN1 buffer */
-BOOL asn1_write_OID(ASN1_DATA *data, const char *OID)
+bool ber_write_OID_String(DATA_BLOB *blob, const char *OID)
 {
-       unsigned v, v2;
+       uint_t v, v2;
        const char *p = (const char *)OID;
        char *newp;
+       int i;
+
+       v = strtoul(p, &newp, 10);
+       if (newp[0] != '.') return false;
+       p = newp + 1;
 
-       if (!asn1_push_tag(data, ASN1_OID))
-               return False;
-       v = strtol(p, &newp, 10);
-       p = newp;
-       v2 = strtol(p, &newp, 10);
-       p = newp;
-       if (!asn1_write_uint8(data, 40*v + v2))
-               return False;
+       v2 = strtoul(p, &newp, 10);
+       if (newp[0] != '.') return false;
+       p = newp + 1;
 
+       /*the ber representation can't use more space then the string one */
+       *blob = data_blob(NULL, strlen(OID));
+       if (!blob->data) return false;
+
+       blob->data[0] = 40*v + v2;
+
+       i = 1;
        while (*p) {
-               v = strtol(p, &newp, 10);
-               p = newp;
-               if (v >= (1<<28)) asn1_write_uint8(data, 0x80 | ((v>>28)&0xff));
-               if (v >= (1<<21)) asn1_write_uint8(data, 0x80 | ((v>>21)&0xff));
-               if (v >= (1<<14)) asn1_write_uint8(data, 0x80 | ((v>>14)&0xff));
-               if (v >= (1<<7)) asn1_write_uint8(data, 0x80 | ((v>>7)&0xff));
-               if (!asn1_write_uint8(data, v&0x7f))
-                       return False;
+               v = strtoul(p, &newp, 10);
+               if (newp[0] == '.') {
+                       p = newp + 1;
+               } else if (newp[0] == '\0') {
+                       p = newp;
+               } else {
+                       data_blob_free(blob);
+                       return false;
+               }
+               if (v >= (1<<28)) blob->data[i++] = (0x80 | ((v>>28)&0x7f));
+               if (v >= (1<<21)) blob->data[i++] = (0x80 | ((v>>21)&0x7f));
+               if (v >= (1<<14)) blob->data[i++] = (0x80 | ((v>>14)&0x7f));
+               if (v >= (1<<7)) blob->data[i++] = (0x80 | ((v>>7)&0x7f));
+               blob->data[i++] = (v&0x7f);
        }
+
+       blob->length = i;
+
+       return true;
+}
+
+/* write an object ID to a ASN1 buffer */
+bool asn1_write_OID(struct asn1_data *data, const char *OID)
+{
+       DATA_BLOB blob;
+
+       if (!asn1_push_tag(data, ASN1_OID)) return false;
+
+       if (!ber_write_OID_String(&blob, OID)) {
+               data->has_error = true;
+               return false;
+       }
+
+       if (!asn1_write(data, blob.data, blob.length)) {
+               data->has_error = true;
+               return false;
+       }
+       data_blob_free(&blob);
        return asn1_pop_tag(data);
 }
 
 /* write an octet string */
-BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
+bool asn1_write_OctetString(struct asn1_data *data, const void *p, size_t length)
 {
        asn1_push_tag(data, ASN1_OCTET_STRING);
        asn1_write(data, p, length);
@@ -157,114 +278,180 @@ BOOL asn1_write_OctetString(ASN1_DATA *data, const void *p, size_t length)
        return !data->has_error;
 }
 
+/* write a LDAP string */
+bool asn1_write_LDAPString(struct asn1_data *data, const char *s)
+{
+       asn1_write(data, s, strlen(s));
+       return !data->has_error;
+}
+
+/* write a LDAP string from a DATA_BLOB */
+bool asn1_write_DATA_BLOB_LDAPString(struct asn1_data *data, const DATA_BLOB *s)
+{
+       asn1_write(data, s->data, s->length);
+       return !data->has_error;
+}
+
 /* write a general string */
-BOOL asn1_write_GeneralString(ASN1_DATA *data, const char *s)
+bool asn1_write_GeneralString(struct asn1_data *data, const char *s)
 {
        asn1_push_tag(data, ASN1_GENERAL_STRING);
-       asn1_write(data, s, strlen(s));
+       asn1_write_LDAPString(data, s);
        asn1_pop_tag(data);
        return !data->has_error;
 }
 
-/* write a BOOLEAN */
-BOOL asn1_write_BOOLEAN(ASN1_DATA *data, BOOL v)
+bool asn1_write_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blob)
 {
-       asn1_write_uint8(data, ASN1_BOOLEAN);
-       asn1_write_uint8(data, v);
+       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(num));
+       asn1_write(data, blob->data, blob->length);
+       asn1_pop_tag(data);
        return !data->has_error;
 }
 
-/* write a BOOLEAN - hmm, I suspect this one is the correct one, and the 
-   above boolean is bogus. Need to check */
-BOOL asn1_write_BOOLEAN2(ASN1_DATA *data, BOOL v)
+/* write a BOOLEAN */
+bool asn1_write_BOOLEAN(struct asn1_data *data, bool v)
 {
        asn1_push_tag(data, ASN1_BOOLEAN);
-       asn1_write_uint8(data, v);
+       asn1_write_uint8(data, v ? 0xFF : 0);
        asn1_pop_tag(data);
        return !data->has_error;
 }
 
+bool asn1_read_BOOLEAN(struct asn1_data *data, bool *v)
+{
+       uint8_t tmp = 0;
+       asn1_start_tag(data, ASN1_BOOLEAN);
+       asn1_read_uint8(data, &tmp);
+       if (tmp == 0xFF) {
+               *v = true;
+       } else {
+               *v = false;
+       }
+       asn1_end_tag(data);
+       return !data->has_error;
+}
+
 /* check a BOOLEAN */
-BOOL asn1_check_BOOLEAN(ASN1_DATA *data, BOOL v)
+bool asn1_check_BOOLEAN(struct asn1_data *data, bool v)
 {
-       uint8 b = 0;
+       uint8_t b = 0;
 
        asn1_read_uint8(data, &b);
        if (b != ASN1_BOOLEAN) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
        asn1_read_uint8(data, &b);
        if (b != v) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
        return !data->has_error;
 }
 
 
-/* load a ASN1_DATA structure with a lump of data, ready to be parsed */
-BOOL asn1_load(ASN1_DATA *data, DATA_BLOB blob)
+/* load a struct asn1_data structure with a lump of data, ready to be parsed */
+bool asn1_load(struct asn1_data *data, DATA_BLOB blob)
 {
        ZERO_STRUCTP(data);
-       data->data = memdup(blob.data, blob.length);
+       data->data = talloc_memdup(data, blob.data, blob.length);
        if (!data->data) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
        data->length = blob.length;
-       return True;
+       return true;
 }
 
-/* read from a ASN1 buffer, advancing the buffer pointer */
-BOOL asn1_read(ASN1_DATA *data, void *p, int len)
+/* Peek into an ASN1 buffer, not advancing the pointer */
+bool asn1_peek(struct asn1_data *data, void *p, int len)
 {
+       if (data->has_error)
+               return false;
+
+       if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len)
+               return false;
+
        if (data->ofs + len > data->length) {
-               data->has_error = True;
-               return False;
+               /* we need to mark the buffer as consumed, so the caller knows
+                  this was an out of data error, and not a decode error */
+               data->ofs = data->length;
+               return false;
        }
+
        memcpy(p, data->data + data->ofs, len);
+       return true;
+}
+
+/* read from a ASN1 buffer, advancing the buffer pointer */
+bool asn1_read(struct asn1_data *data, void *p, int len)
+{
+       if (!asn1_peek(data, p, len)) {
+               data->has_error = true;
+               return false;
+       }
+
        data->ofs += len;
-       return True;
+       return true;
 }
 
-/* read a uint8 from a ASN1 buffer */
-BOOL asn1_read_uint8(ASN1_DATA *data, uint8 *v)
+/* read a uint8_t from a ASN1 buffer */
+bool asn1_read_uint8(struct asn1_data *data, uint8_t *v)
 {
        return asn1_read(data, v, 1);
 }
 
+bool asn1_peek_uint8(struct asn1_data *data, uint8_t *v)
+{
+       return asn1_peek(data, v, 1);
+}
+
+bool asn1_peek_tag(struct asn1_data *data, uint8_t tag)
+{
+       uint8_t b;
+
+       if (asn1_tag_remaining(data) <= 0) {
+               return false;
+       }
+
+       if (!asn1_peek_uint8(data, &b))
+               return false;
+
+       return (b == tag);
+}
+
 /* start reading a nested asn1 structure */
-BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
+bool asn1_start_tag(struct asn1_data *data, uint8_t tag)
 {
-       uint8 b;
+       uint8_t b;
        struct nesting *nesting;
        
        if (!asn1_read_uint8(data, &b))
-               return False;
+               return false;
 
        if (b != tag) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
-       nesting = (struct nesting *)malloc(sizeof(struct nesting));
+       nesting = talloc(data, struct nesting);
        if (!nesting) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
 
        if (!asn1_read_uint8(data, &b)) {
-               return False;
+               return false;
        }
 
        if (b & 0x80) {
                int n = b & 0x7f;
                if (!asn1_read_uint8(data, &b))
-                       return False;
+                       return false;
                nesting->taglen = b;
                while (n > 1) {
                        if (!asn1_read_uint8(data, &b)) 
-                               return False;
+                               return false;
                        nesting->taglen = (nesting->taglen << 8) | b;
                        n--;
                }
@@ -274,155 +461,310 @@ BOOL asn1_start_tag(ASN1_DATA *data, uint8 tag)
        nesting->start = data->ofs;
        nesting->next = data->nesting;
        data->nesting = nesting;
+       if (asn1_tag_remaining(data) == -1) {
+               return false;
+       }
        return !data->has_error;
 }
 
-
 /* stop reading a tag */
-BOOL asn1_end_tag(ASN1_DATA *data)
+bool asn1_end_tag(struct asn1_data *data)
 {
        struct nesting *nesting;
 
        /* make sure we read it all */
        if (asn1_tag_remaining(data) != 0) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
 
        nesting = data->nesting;
 
        if (!nesting) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
 
        data->nesting = nesting->next;
-       free(nesting);
-       return True;
+       talloc_free(nesting);
+       return true;
 }
 
 /* work out how many bytes are left in this nested tag */
-int asn1_tag_remaining(ASN1_DATA *data)
+int asn1_tag_remaining(struct asn1_data *data)
 {
+       int remaining;
+       if (data->has_error) {
+               return -1;
+       }
+
        if (!data->nesting) {
-               data->has_error = True;
+               data->has_error = true;
                return -1;
        }
-       return data->nesting->taglen - (data->ofs - data->nesting->start);
+       remaining = data->nesting->taglen - (data->ofs - data->nesting->start);
+       if (remaining > (data->length - data->ofs)) {
+               data->has_error = true;
+               return -1;
+       }
+       return remaining;
+}
+
+/* read an object ID from a data blob */
+bool ber_read_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB blob, const char **OID)
+{
+       int i;
+       uint8_t *b;
+       uint_t v;
+       char *tmp_oid = NULL;
+
+       if (blob.length < 2) return false;
+
+       b = blob.data;
+
+       tmp_oid = talloc_asprintf(mem_ctx, "%u",  b[0]/40);
+       if (!tmp_oid) goto nomem;
+       tmp_oid = talloc_asprintf_append_buffer(tmp_oid, ".%u",  b[0]%40);
+       if (!tmp_oid) goto nomem;
+
+       for(i = 1, v = 0; i < blob.length; i++) {
+               v = (v<<7) | (b[i]&0x7f);
+               if ( ! (b[i] & 0x80)) {
+                       tmp_oid = talloc_asprintf_append_buffer(tmp_oid, ".%u",  v);
+                       v = 0;
+               }
+               if (!tmp_oid) goto nomem;
+       }
+
+       if (v != 0) {
+               talloc_free(tmp_oid);
+               return false;
+       }
+
+       *OID = tmp_oid;
+       return true;
+
+nomem: 
+       return false;
 }
 
 /* read an object ID from a ASN1 buffer */
-BOOL asn1_read_OID(ASN1_DATA *data, char **OID)
+bool asn1_read_OID(struct asn1_data *data, TALLOC_CTX *mem_ctx, const char **OID)
 {
-       uint8 b;
-       pstring oid;
-       fstring el;
+       DATA_BLOB blob;
+       int len;
 
-       if (!asn1_start_tag(data, ASN1_OID)) return False;
-       asn1_read_uint8(data, &b);
+       if (!asn1_start_tag(data, ASN1_OID)) return false;
 
-       oid[0] = 0;
-       snprintf(el, sizeof(el), "%u",  b/40);
-       pstrcat(oid, el);
-       snprintf(el, sizeof(el), " %u",  b%40);
-       pstrcat(oid, el);
+       len = asn1_tag_remaining(data);
+       if (len < 0) {
+               data->has_error = true;
+               return false;
+       }
 
-       while (asn1_tag_remaining(data) > 0) {
-               unsigned v = 0;
-               do {
-                       asn1_read_uint8(data, &b);
-                       v = (v<<7) | (b&0x7f);
-               } while (!data->has_error && b & 0x80);
-               snprintf(el, sizeof(el), " %u",  v);
-               pstrcat(oid, el);
+       blob = data_blob(NULL, len);
+       if (!blob.data) {
+               data->has_error = true;
+               return false;
        }
 
+       asn1_read(data, blob.data, len);
        asn1_end_tag(data);
+       if (data->has_error) {
+               data_blob_free(&blob);
+               return false;
+       }
 
-       *OID = strdup(oid);
+       if (!ber_read_OID_String(mem_ctx, blob, OID)) {
+               data->has_error = true;
+               data_blob_free(&blob);
+               return false;
+       }
 
-       return !data->has_error;
+       data_blob_free(&blob);
+       return true;
 }
 
 /* check that the next object ID is correct */
-BOOL asn1_check_OID(ASN1_DATA *data, const char *OID)
+bool asn1_check_OID(struct asn1_data *data, const char *OID)
 {
-       char *id;
+       const char *id;
 
-       if (!asn1_read_OID(data, &id)) return False;
+       if (!asn1_read_OID(data, data, &id)) return false;
 
        if (strcmp(id, OID) != 0) {
-               data->has_error = True;
-               return False;
+               talloc_free(discard_const(id));
+               data->has_error = true;
+               return false;
        }
-       free(id);
-       return True;
+       talloc_free(discard_const(id));
+       return true;
 }
 
-/* read a GeneralString from a ASN1 buffer */
-BOOL asn1_read_GeneralString(ASN1_DATA *data, char **s)
+/* read a LDAPString from a ASN1 buffer */
+bool asn1_read_LDAPString(struct asn1_data *data, TALLOC_CTX *mem_ctx, char **s)
 {
        int len;
-       if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return False;
        len = asn1_tag_remaining(data);
-       *s = malloc(len+1);
+       if (len < 0) {
+               data->has_error = true;
+               return false;
+       }
+       *s = talloc_array(mem_ctx, char, len+1);
        if (! *s) {
-               data->has_error = True;
-               return False;
+               data->has_error = true;
+               return false;
        }
        asn1_read(data, *s, len);
        (*s)[len] = 0;
-       asn1_end_tag(data);
        return !data->has_error;
 }
 
+
+/* read a GeneralString from a ASN1 buffer */
+bool asn1_read_GeneralString(struct asn1_data *data, TALLOC_CTX *mem_ctx, char **s)
+{
+       if (!asn1_start_tag(data, ASN1_GENERAL_STRING)) return false;
+       if (!asn1_read_LDAPString(data, mem_ctx, s)) return false;
+       return asn1_end_tag(data);
+}
+
+
 /* read a octet string blob */
-BOOL asn1_read_OctetString(ASN1_DATA *data, DATA_BLOB *blob)
+bool asn1_read_OctetString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob)
 {
        int len;
        ZERO_STRUCTP(blob);
-       if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return False;
+       if (!asn1_start_tag(data, ASN1_OCTET_STRING)) return false;
        len = asn1_tag_remaining(data);
+       if (len < 0) {
+               data->has_error = true;
+               return false;
+       }
+       *blob = data_blob_talloc(mem_ctx, NULL, len+1);
+       if (!blob->data) {
+               data->has_error = true;
+               return false;
+       }
+       asn1_read(data, blob->data, len);
+       asn1_end_tag(data);
+       blob->length--;
+       blob->data[len] = 0;
+       
+       if (data->has_error) {
+               data_blob_free(blob);
+               *blob = data_blob(NULL, 0);
+               return false;
+       }
+       return true;
+}
+
+bool asn1_read_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blob)
+{
+       int len;
+       ZERO_STRUCTP(blob);
+       if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(num))) return false;
+       len = asn1_tag_remaining(data);
+       if (len < 0) {
+               data->has_error = true;
+               return false;
+       }
        *blob = data_blob(NULL, len);
+       if ((len != 0) && (!blob->data)) {
+               data->has_error = true;
+               return false;
+       }
        asn1_read(data, blob->data, len);
        asn1_end_tag(data);
        return !data->has_error;
 }
 
-/* read an interger */
-BOOL asn1_read_Integer(ASN1_DATA *data, int *i)
+/* read an integer without tag*/
+bool asn1_read_implicit_Integer(struct asn1_data *data, int *i)
 {
-       uint8 b;
+       uint8_t b;
        *i = 0;
-       
-       if (!asn1_start_tag(data, ASN1_INTEGER)) return False;
-       while (asn1_tag_remaining(data)>0) {
-               asn1_read_uint8(data, &b);
+
+       while (!data->has_error && asn1_tag_remaining(data)>0) {
+               if (!asn1_read_uint8(data, &b)) return false;
                *i = (*i << 8) + b;
        }
+       return !data->has_error;        
+       
+}
+
+/* read an integer */
+bool asn1_read_Integer(struct asn1_data *data, int *i)
+{
+       *i = 0;
+
+       if (!asn1_start_tag(data, ASN1_INTEGER)) return false;
+       if (!asn1_read_implicit_Integer(data, i)) return false;
        return asn1_end_tag(data);      
+}
+
+/* read an integer */
+bool asn1_read_enumerated(struct asn1_data *data, int *v)
+{
+       *v = 0;
        
+       if (!asn1_start_tag(data, ASN1_ENUMERATED)) return false;
+       while (!data->has_error && asn1_tag_remaining(data)>0) {
+               uint8_t b;
+               asn1_read_uint8(data, &b);
+               *v = (*v << 8) + b;
+       }
+       return asn1_end_tag(data);      
 }
 
-/* check a enumarted value is correct */
-BOOL asn1_check_enumerated(ASN1_DATA *data, int v)
+/* check a enumerated value is correct */
+bool asn1_check_enumerated(struct asn1_data *data, int v)
 {
-       uint8 b;
-       if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
+       uint8_t b;
+       if (!asn1_start_tag(data, ASN1_ENUMERATED)) return false;
        asn1_read_uint8(data, &b);
        asn1_end_tag(data);
 
        if (v != b)
-               data->has_error = False;
+               data->has_error = false;
 
        return !data->has_error;
 }
 
-/* write an enumarted value to the stream */
-BOOL asn1_write_enumerated(ASN1_DATA *data, uint8 v)
+/* write an enumerated value to the stream */
+bool asn1_write_enumerated(struct asn1_data *data, uint8_t v)
 {
-       if (!asn1_push_tag(data, ASN1_ENUMERATED)) return False;
+       if (!asn1_push_tag(data, ASN1_ENUMERATED)) return false;
        asn1_write_uint8(data, v);
        asn1_pop_tag(data);
        return !data->has_error;
 }
+
+/*
+  check if a ASN.1 blob is a full tag
+*/
+NTSTATUS asn1_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size)
+{
+       struct asn1_data *asn1 = asn1_init(NULL);
+       int size;
+
+       NT_STATUS_HAVE_NO_MEMORY(asn1);
+
+       asn1->data = blob.data;
+       asn1->length = blob.length;
+       asn1_start_tag(asn1, tag);
+       if (asn1->has_error) {
+               talloc_free(asn1);
+               return STATUS_MORE_ENTRIES;
+       }
+       size = asn1_tag_remaining(asn1) + asn1->ofs;
+
+       talloc_free(asn1);
+
+       if (size > blob.length) {
+               return STATUS_MORE_ENTRIES;
+       }               
+
+       *packet_size = size;
+       return NT_STATUS_OK;
+}