r25000: Fix some more C++ compatibility warnings.
[jelmer/samba4-debian.git] / source / libcli / util / asn1.c
index 1124cc1701be94686ff4a75fb9156268e2338355..8bd091a3195730f9f8aecf9dfb61937cb449cc34 100644 (file)
@@ -5,7 +5,7 @@
    
    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 "asn_1.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(struct asn1_data *data)
 {
-       talloc_free(data->data);
+       talloc_free(data);
 }
 
 /* write to the ASN1 buffer, advancing the buffer pointer */
@@ -33,7 +42,7 @@ BOOL asn1_write(struct asn1_data *data, const void *p, int len)
        if (data->has_error) return False;
        if (data->length < data->ofs+len) {
                uint8_t *newp;
-               newp = talloc_realloc(NULL, data->data, uint8_t, data->ofs+len);
+               newp = talloc_realloc(data, data->data, uint8_t, data->ofs+len);
                if (!newp) {
                        asn1_free(data);
                        data->has_error = True;
@@ -59,7 +68,7 @@ BOOL asn1_push_tag(struct asn1_data *data, uint8_t tag)
        struct nesting *nesting;
 
        asn1_write_uint8(data, tag);
-       nesting = talloc(NULL, struct nesting);
+       nesting = talloc(data, struct nesting);
        if (!nesting) {
                data->has_error = True;
                return False;
@@ -87,7 +96,27 @@ BOOL asn1_pop_tag(struct asn1_data *data)
        /* 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;
@@ -176,32 +205,67 @@ BOOL asn1_write_Integer(struct asn1_data *data, int i)
        return asn1_pop_tag(data);
 }
 
-/* write an object ID to a ASN1 buffer */
-BOOL asn1_write_OID(struct asn1_data *data, const char *OID)
+BOOL ber_write_OID_String(DATA_BLOB *blob, const char *OID)
 {
        uint_t v, v2;
        const char *p = (const char *)OID;
        char *newp;
+       int i;
 
-       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;
+       v = strtoul(p, &newp, 10);
+       if (newp[0] != '.') return False;
+       p = newp + 1;
+
+       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))
+               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);
 }
 
@@ -214,11 +278,18 @@ BOOL asn1_write_OctetString(struct 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 general string */
 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;
 }
@@ -277,7 +348,7 @@ BOOL asn1_check_BOOLEAN(struct asn1_data *data, BOOL v)
 BOOL asn1_load(struct asn1_data *data, DATA_BLOB blob)
 {
        ZERO_STRUCTP(data);
-       data->data = talloc_memdup(NULL, blob.data, blob.length);
+       data->data = talloc_memdup(data, blob.data, blob.length);
        if (!data->data) {
                data->has_error = True;
                return False;
@@ -292,8 +363,12 @@ BOOL asn1_peek(struct asn1_data *data, void *p, int len)
        if (len < 0 || data->ofs + len < data->ofs || data->ofs + len < len)
                return False;
 
-       if (data->ofs + len > data->length)
+       if (data->ofs + len > data->length) {
+               /* 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;
@@ -349,7 +424,7 @@ BOOL asn1_start_tag(struct asn1_data *data, uint8_t tag)
                data->has_error = True;
                return False;
        }
-       nesting = talloc(NULL, struct nesting);
+       nesting = talloc(data, struct nesting);
        if (!nesting) {
                data->has_error = True;
                return False;
@@ -376,28 +451,10 @@ BOOL asn1_start_tag(struct asn1_data *data, uint8_t tag)
        nesting->start = data->ofs;
        nesting->next = data->nesting;
        data->nesting = nesting;
-       return !data->has_error;
-}
-
-
-/* Get the length to be expected in buf */
-BOOL asn1_object_length(uint8_t *buf, size_t buf_length,
-                       uint8_t tag, size_t *result)
-{
-       struct asn1_data data;
-
-       /* Fake the asn1_load to avoid the memdup, this is just to be able to
-        * re-use the length-reading in asn1_start_tag */
-       ZERO_STRUCT(data);
-       data.data = buf;
-       data.length = buf_length;
-       if (!asn1_start_tag(&data, tag))
+       if (asn1_tag_remaining(data) == -1) {
                return False;
-       *result = asn1_tag_remaining(&data)+data.ofs;
-       /* We can't use asn1_end_tag here, as we did not consume the complete
-        * tag, so asn1_end_tag would flag an error and not free nesting */
-       talloc_free(data.nesting);
-       return True;
+       }
+       return !data->has_error;
 }
 
 /* stop reading a tag */
@@ -426,40 +483,96 @@ BOOL asn1_end_tag(struct asn1_data *data)
 /* work out how many bytes are left in this nested tag */
 int asn1_tag_remaining(struct asn1_data *data)
 {
+       int remaining;
+       if (data->has_error) {
+               return -1;
+       }
+
        if (!data->nesting) {
                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 ASN1 buffer */
-BOOL asn1_read_OID(struct asn1_data *data, const char **OID)
+/* read an object ID from a data blob */
+BOOL ber_read_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB blob, const char **OID)
 {
-       uint8_t b;
+       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(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(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(struct asn1_data *data, TALLOC_CTX *mem_ctx, const char **OID)
+{
+       DATA_BLOB blob;
+       int len;
+
        if (!asn1_start_tag(data, ASN1_OID)) return False;
-       asn1_read_uint8(data, &b);
 
-       tmp_oid = talloc_asprintf(NULL, "%u",  b/40);
-       tmp_oid = talloc_asprintf_append(tmp_oid, " %u",  b%40);
+       len = asn1_tag_remaining(data);
+       if (len < 0) {
+               data->has_error = True;
+               return False;
+       }
 
-       while (!data->has_error && asn1_tag_remaining(data) > 0) {
-               uint_t v = 0;
-               do {
-                       asn1_read_uint8(data, &b);
-                       v = (v<<7) | (b&0x7f);
-               } while (!data->has_error && b & 0x80);
-               tmp_oid = talloc_asprintf_append(tmp_oid, " %u",  v);
+       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 = talloc_strdup(NULL, tmp_oid);
-       talloc_free(tmp_oid);
+       if (!ber_read_OID_String(mem_ctx, blob, OID)) {
+               data->has_error = True;
+               data_blob_free(&blob);
+               return False;
+       }
 
-       return (*OID && !data->has_error);
+       data_blob_free(&blob);
+       return True;
 }
 
 /* check that the next object ID is correct */
@@ -467,9 +580,10 @@ BOOL asn1_check_OID(struct asn1_data *data, const char *OID)
 {
        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) {
+               talloc_free(discard_const(id));
                data->has_error = True;
                return False;
        }
@@ -477,29 +591,37 @@ BOOL asn1_check_OID(struct asn1_data *data, const char *OID)
        return True;
 }
 
-/* read a GeneralString from a ASN1 buffer */
-BOOL asn1_read_GeneralString(struct 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);
        if (len < 0) {
                data->has_error = True;
                return False;
        }
-       *s = talloc_size(NULL, len+1);
+       *s = talloc_array(mem_ctx, char, len+1);
        if (! *s) {
                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(struct 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);
@@ -509,9 +631,15 @@ BOOL asn1_read_OctetString(struct asn1_data *data, DATA_BLOB *blob)
                data->has_error = True;
                return False;
        }
-       *blob = data_blob(NULL, len);
+       *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);
@@ -532,6 +660,10 @@ BOOL asn1_read_ContextSimple(struct asn1_data *data, uint8_t num, DATA_BLOB *blo
                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;
@@ -543,7 +675,7 @@ BOOL asn1_read_implicit_Integer(struct asn1_data *data, int *i)
        uint8_t b;
        *i = 0;
 
-       while (asn1_tag_remaining(data)>0) {
+       while (!data->has_error && asn1_tag_remaining(data)>0) {
                if (!asn1_read_uint8(data, &b)) return False;
                *i = (*i << 8) + b;
        }
@@ -559,7 +691,6 @@ BOOL asn1_read_Integer(struct asn1_data *data, int *i)
        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 interger */
@@ -568,7 +699,7 @@ BOOL asn1_read_enumerated(struct asn1_data *data, int *v)
        *v = 0;
        
        if (!asn1_start_tag(data, ASN1_ENUMERATED)) return False;
-       while (asn1_tag_remaining(data)>0) {
+       while (!data->has_error && asn1_tag_remaining(data)>0) {
                uint8_t b;
                asn1_read_uint8(data, &b);
                *v = (*v << 8) + b;
@@ -598,3 +729,32 @@ BOOL asn1_write_enumerated(struct asn1_data *data, uint8_t 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;
+}