return asn1_pop_tag(data);
}
-bool ber_write_OID_String(DATA_BLOB *blob, const char *OID)
+/* write a BIT STRING */
+bool asn1_write_BitString(struct asn1_data *data, const void *p, size_t length, uint8_t padding)
{
- uint_t v, v2;
+ if (!asn1_push_tag(data, ASN1_BIT_STRING)) return false;
+ if (!asn1_write_uint8(data, padding)) return false;
+ if (!asn1_write(data, p, length)) return false;
+ return asn1_pop_tag(data);
+}
+
+bool ber_write_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char *OID)
+{
+ unsigned int v, v2;
const char *p = (const char *)OID;
char *newp;
int i;
+ if (!isdigit(*p)) return false;
v = strtoul(p, &newp, 10);
if (newp[0] != '.') return false;
p = newp + 1;
+ if (!isdigit(*p)) 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));
+ *blob = data_blob_talloc(mem_ctx, NULL, strlen(OID));
if (!blob->data) return false;
blob->data[0] = 40*v + v2;
i = 1;
while (*p) {
+ if (!isdigit(*p)) return false;
v = strtoul(p, &newp, 10);
if (newp[0] == '.') {
p = newp + 1;
+ /* check for empty last component */
+ if (!*p) return false;
} else if (newp[0] == '\0') {
p = newp;
} else {
return true;
}
+/**
+ * Serialize partial OID string.
+ * Partial OIDs are in the form:
+ * 1:2.5.6:0x81
+ * 1:2.5.6:0x8182
+ */
+bool ber_write_partial_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB *blob, const char *partial_oid)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+ char *oid = talloc_strdup(tmp_ctx, partial_oid);
+ char *p;
+
+ /* truncate partial part so ber_write_OID_String() works */
+ p = strchr(oid, ':');
+ if (p) {
+ *p = '\0';
+ p++;
+ }
+
+ if (!ber_write_OID_String(mem_ctx, blob, oid)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+
+ /* Add partially encoded sub-identifier */
+ if (p) {
+ DATA_BLOB tmp_blob = strhex_to_data_blob(tmp_ctx, p);
+ if (!data_blob_append(mem_ctx, blob, tmp_blob.data,
+ tmp_blob.length)) {
+ talloc_free(tmp_ctx);
+ return false;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+
+ return true;
+}
+
/* write an object ID to a ASN1 buffer */
bool asn1_write_OID(struct asn1_data *data, const char *OID)
{
if (!asn1_push_tag(data, ASN1_OID)) return false;
- if (!ber_write_OID_String(&blob, OID)) {
+ if (!ber_write_OID_String(NULL, &blob, OID)) {
data->has_error = true;
return false;
}
if (!asn1_write(data, blob.data, blob.length)) {
+ data_blob_free(&blob);
data->has_error = true;
return false;
}
return !data->has_error;
}
+/* write a BOOLEAN in a simple context */
+bool asn1_write_BOOLEAN_context(struct asn1_data *data, bool v, int context)
+{
+ asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(context));
+ asn1_write_uint8(data, v ? 0xFF : 0);
+ asn1_pop_tag(data);
+ return !data->has_error;
+}
+
+bool asn1_read_BOOLEAN_context(struct asn1_data *data, bool *v, int context)
+{
+ uint8_t tmp = 0;
+ asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(context));
+ 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(struct asn1_data *data, bool v)
{
return (b == tag);
}
+/*
+ * just get the needed size the tag would consume
+ */
+bool asn1_peek_tag_needed_size(struct asn1_data *data, uint8_t tag, size_t *size)
+{
+ off_t start_ofs = data->ofs;
+ uint8_t b;
+ size_t taglen = 0;
+
+ if (data->has_error) {
+ return false;
+ }
+
+ if (!asn1_read_uint8(data, &b)) {
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return false;
+ }
+
+ if (b != tag) {
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return false;
+ }
+
+ if (!asn1_read_uint8(data, &b)) {
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return false;
+ }
+
+ if (b & 0x80) {
+ int n = b & 0x7f;
+ if (!asn1_read_uint8(data, &b)) {
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return false;
+ }
+ if (n > 4) {
+ /*
+ * We should not allow more than 4 bytes
+ * for the encoding of the tag length.
+ *
+ * Otherwise we'd overflow the taglen
+ * variable on 32 bit systems.
+ */
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return false;
+ }
+ taglen = b;
+ while (n > 1) {
+ if (!asn1_read_uint8(data, &b)) {
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return false;
+ }
+ taglen = (taglen << 8) | b;
+ n--;
+ }
+ } else {
+ taglen = b;
+ }
+
+ *size = (data->ofs - start_ofs) + taglen;
+
+ data->ofs = start_ofs;
+ data->has_error = false;
+ return true;
+}
+
/* start reading a nested asn1 structure */
bool asn1_start_tag(struct asn1_data *data, uint8_t tag)
{
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)
+/**
+ * Internal implementation for reading binary OIDs
+ * Reading is done as far in the buffer as valid OID
+ * till buffer ends or not valid sub-identifier is found.
+ */
+static bool _ber_read_OID_String_impl(TALLOC_CTX *mem_ctx, DATA_BLOB blob,
+ char **OID, size_t *bytes_eaten)
{
int i;
uint8_t *b;
- uint_t v;
+ unsigned int v;
char *tmp_oid = NULL;
if (blob.length < 2) return false;
tmp_oid = talloc_asprintf_append_buffer(tmp_oid, ".%u", b[0]%40);
if (!tmp_oid) goto nomem;
+ if (bytes_eaten != NULL) {
+ *bytes_eaten = 0;
+ }
+
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 (bytes_eaten)
+ *bytes_eaten = i+1;
}
if (!tmp_oid) goto nomem;
}
- if (v != 0) {
- talloc_free(tmp_oid);
+ *OID = tmp_oid;
+ return true;
+
+nomem:
+ return false;
+}
+
+/* read an object ID from a data blob */
+bool ber_read_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB blob, char **OID)
+{
+ size_t bytes_eaten;
+
+ if (!_ber_read_OID_String_impl(mem_ctx, blob, OID, &bytes_eaten))
+ return false;
+
+ return (bytes_eaten == blob.length);
+}
+
+/**
+ * Deserialize partial OID string.
+ * Partial OIDs are in the form:
+ * 1:2.5.6:0x81
+ * 1:2.5.6:0x8182
+ */
+bool ber_read_partial_OID_String(TALLOC_CTX *mem_ctx, DATA_BLOB blob,
+ char **partial_oid)
+{
+ size_t bytes_left;
+ size_t bytes_eaten;
+ char *identifier = NULL;
+ char *tmp_oid = NULL;
+
+ if (!_ber_read_OID_String_impl(mem_ctx, blob, &tmp_oid, &bytes_eaten))
return false;
+
+ if (bytes_eaten < blob.length) {
+ bytes_left = blob.length - bytes_eaten;
+ identifier = hex_encode_talloc(mem_ctx, &blob.data[bytes_eaten], bytes_left);
+ if (!identifier) goto nomem;
+
+ *partial_oid = talloc_asprintf_append_buffer(tmp_oid, ":0x%s", identifier);
+ if (!*partial_oid) goto nomem;
+ TALLOC_FREE(identifier);
+ } else {
+ *partial_oid = tmp_oid;
}
- *OID = tmp_oid;
return true;
-nomem:
+nomem:
+ TALLOC_FREE(identifier);
+ TALLOC_FREE(tmp_oid);
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)
+bool asn1_read_OID(struct asn1_data *data, TALLOC_CTX *mem_ctx, char **OID)
{
DATA_BLOB blob;
int len;
/* check that the next object ID is correct */
bool asn1_check_OID(struct asn1_data *data, const char *OID)
{
- const char *id;
+ char *id;
if (!asn1_read_OID(data, data, &id)) return false;
if (strcmp(id, OID) != 0) {
- talloc_free(discard_const(id));
+ talloc_free(id);
data->has_error = true;
return false;
}
- talloc_free(discard_const(id));
+ talloc_free(id);
return true;
}
return false;
}
*blob = data_blob_talloc(mem_ctx, NULL, len+1);
- if (!blob->data) {
+ if (!blob->data || blob->length < len) {
data->has_error = true;
return false;
}
if (data->has_error) {
data_blob_free(blob);
- *blob = data_blob(NULL, 0);
+ *blob = data_blob_null;
return false;
}
return true;
bool asn1_read_implicit_Integer(struct asn1_data *data, int *i)
{
uint8_t b;
+ bool first_byte = true;
*i = 0;
while (!data->has_error && asn1_tag_remaining(data)>0) {
if (!asn1_read_uint8(data, &b)) return false;
+ if (first_byte) {
+ if (b & 0x80) {
+ /* Number is negative.
+ Set i to -1 for sign extend. */
+ *i = -1;
+ }
+ first_byte = false;
+ }
*i = (*i << 8) + b;
}
return !data->has_error;
return asn1_end_tag(data);
}
+/* read a BIT STRING */
+bool asn1_read_BitString(struct asn1_data *data, TALLOC_CTX *mem_ctx, DATA_BLOB *blob, uint8_t *padding)
+{
+ int len;
+ ZERO_STRUCTP(blob);
+ if (!asn1_start_tag(data, ASN1_BIT_STRING)) return false;
+ len = asn1_tag_remaining(data);
+ if (len < 0) {
+ data->has_error = true;
+ return false;
+ }
+ if (!asn1_read_uint8(data, padding)) return false;
+
+ *blob = data_blob_talloc(mem_ctx, NULL, len+1);
+ if (!blob->data || blob->length < len) {
+ data->has_error = true;
+ return false;
+ }
+ if (asn1_read(data, blob->data, len - 1)) {
+ blob->length--;
+ blob->data[len] = 0;
+ asn1_end_tag(data);
+ }
+
+ if (data->has_error) {
+ data_blob_free(blob);
+ *blob = data_blob_null;
+ *padding = 0;
+ return false;
+ }
+ return true;
+}
+
/* read an integer */
bool asn1_read_enumerated(struct asn1_data *data, int *v)
{
return !data->has_error;
}
+/*
+ Get us the data just written without copying
+*/
+bool asn1_blob(const struct asn1_data *asn1, DATA_BLOB *blob)
+{
+ if (asn1->has_error) {
+ return false;
+ }
+ if (asn1->nesting != NULL) {
+ return false;
+ }
+ blob->data = asn1->data;
+ blob->length = asn1->length;
+ return true;
+}
+
+/*
+ Fill in an asn1 struct without making a copy
+*/
+void asn1_load_nocopy(struct asn1_data *data, uint8_t *buf, size_t len)
+{
+ ZERO_STRUCTP(data);
+ data->data = buf;
+ data->length = len;
+}
+
/*
check if a ASN.1 blob is a full tag
*/
if (size > blob.length) {
return STATUS_MORE_ENTRIES;
+ }
+
+ *packet_size = size;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS asn1_peek_full_tag(DATA_BLOB blob, uint8_t tag, size_t *packet_size)
+{
+ struct asn1_data asn1;
+ size_t size;
+ bool ok;
+
+ ZERO_STRUCT(asn1);
+ asn1.data = blob.data;
+ asn1.length = blob.length;
+
+ ok = asn1_peek_tag_needed_size(&asn1, tag, &size);
+ if (!ok) {
+ return NT_STATUS_INVALID_BUFFER_SIZE;
+ }
+
+ if (size > blob.length) {
+ *packet_size = size;
+ return STATUS_MORE_ENTRIES;
}
*packet_size = size;