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 */
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;
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;
/* 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;
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);
}
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;
}
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;
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;
data->has_error = True;
return False;
}
- nesting = talloc(NULL, struct nesting);
+ nesting = talloc(data, struct nesting);
if (!nesting) {
data->has_error = True;
return False;
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 */
/* 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 */
{
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;
}
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);
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);
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;
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;
}
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 */
*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;
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;
+}