[PATCH] Add support for MS Catalog files

Andreas Schneider asn at samba.org
Fri Jun 22 13:54:49 UTC 2018


On Thursday, 21 June 2018 18:36:22 CEST Jeremy Allison via samba-technical 
wrote:
> A few inline comments.
> 
> Jeremy.
> 
> On Thu, Jun 21, 2018 at 06:05:28PM +0200, Andreas Schneider via samba-
technical wrote:
> > +
> > +static char *mscat_asn1_get_oid(TALLOC_CTX *mem_ctx,
> > +				asn1_node root,
> > +				const char *oid_name)
> > +{
> > +	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
> > +	char oid_str[32] = {0};
> > +	int oid_len = sizeof(oid_str);
> > +	int rc;
> > +
> > +	rc = asn1_read_value(root,
> > +			     oid_name,
> > +			     oid_str,
> > +			     &oid_len);
> 
> Should there be checks here that oid_len < sizeof(oid_str) ?
> 
> What about null termination ?

oid_len is and in and out value. So it is normally always smaller than what 
can fit into the string.

> > +	if (rc != ASN1_SUCCESS) {
> > +		asn1_perror(rc);
> > +		fprintf(stderr,
> > +			"Failed to read value '%s': %s\n",
> > +			oid_name,
> > +			error_string);
> > +		return NULL;
> > +	}
> > +
> > +	return talloc_strdup(mem_ctx, oid_str);

I'm using talloc_strndup(mem_ctx, oid_str, oid_len); now as this ensures NUL 
termination.

> > +}
> > +
> > +static bool mscat_asn1_oid_equal(const char *o1, const char *o2)
> > +{
> > +	int cmp;
> > +
> > +	cmp = strcmp(o1, o2);
> > +	if (cmp != 0) {
> > +		return false;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static int mscat_asn1_read_value(TALLOC_CTX *mem_ctx,
> > +				 asn1_node root,
> > +				 const char *name,
> > +				 DATA_BLOB *blob)
> > +{
> > +	DATA_BLOB tmp;
> > +	unsigned int etype = ASN1_ETYPE_INVALID;
> > +	int len = 0;
> > +	int rc;
> > +
> > +	rc = asn1_read_value_type(root, name, NULL, &len, &etype);
> > +	if (rc != ASN1_SUCCESS && len == 0) {
> > +		return rc;
> > +	}
> > +
> > +	if (etype == ASN1_ETYPE_BIT_STRING) {
> 
> Integer wrap on len ?

Fixed

> > +		DBG_DEBUG("Decode element (startEnd)  %s",
> > +			  el2);
> > +
> > +		rc = asn1_der_decoding_startEnd(ctl->tree_ctl,
> > +						ctl->raw_ctl.data,
> > +						ctl->raw_ctl.size,
> > +						el2,
> > +						&content_start,
> > +						&content_end);
> > +		if (rc != ASN1_SUCCESS) {
> > +			goto done;
> > +		}
> > +		content_len = content_end - content_start + 1;
> 
> Arithmetic checks on values read from the file please.

I've added them, but content_start and content_end are already check in 
libtasn1 that start is smaller than end.


Updated patch attached.


Thanks,

	Andreas

-- 
Andreas Schneider                      asn at samba.org
Samba Team                             www.samba.org
GPG-ID:     8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D
-------------- next part --------------
>From e39da2e84aafa55e2acb52c897285b4bb1b7a129 Mon Sep 17 00:00:00 2001
From: Andreas Schneider <asn at samba.org>
Date: Tue, 20 Dec 2016 08:52:14 +0100
Subject: [PATCH] lib: Add support to parse MS Catalog files

Signed-off-by: Andreas Schneider <asn at samba.org>
---
 lib/mscat/dumpmscat.c     |  188 ++++++
 lib/mscat/mscat.asn       |  136 +++++
 lib/mscat/mscat.h         |  105 ++++
 lib/mscat/mscat_ctl.c     | 1177 +++++++++++++++++++++++++++++++++++++
 lib/mscat/mscat_pkcs7.c   |  279 +++++++++
 lib/mscat/mscat_private.h |   27 +
 lib/mscat/wscript         |   42 ++
 wscript                   |    1 +
 wscript_build             |    1 +
 9 files changed, 1956 insertions(+)
 create mode 100644 lib/mscat/dumpmscat.c
 create mode 100644 lib/mscat/mscat.asn
 create mode 100644 lib/mscat/mscat.h
 create mode 100644 lib/mscat/mscat_ctl.c
 create mode 100644 lib/mscat/mscat_pkcs7.c
 create mode 100644 lib/mscat/mscat_private.h
 create mode 100644 lib/mscat/wscript

diff --git a/lib/mscat/dumpmscat.c b/lib/mscat/dumpmscat.c
new file mode 100644
index 00000000000..912f3d8e6a7
--- /dev/null
+++ b/lib/mscat/dumpmscat.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2016      Andreas Schneider <asn at samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <talloc.h>
+
+#include <libtasn1.h>
+#include <gnutls/pkcs7.h>
+
+#include "mscat.h"
+
+static const char *mac_to_string(enum mscat_mac_algorithm algo) {
+	switch(algo) {
+		case MSCAT_MAC_NULL:
+			return "NULL";
+		case MSCAT_MAC_MD5:
+			return "MD5";
+		case MSCAT_MAC_SHA1:
+			return "SHA1";
+		case MSCAT_MAC_SHA256:
+			return "SHA256";
+		case MSCAT_MAC_SHA512:
+			return "SHA512";
+		case MSCAT_MAC_UNKNOWN:
+			return "UNKNOWN";
+	}
+
+	return "UNKNOWN";
+}
+
+int main(int argc, char *argv[]) {
+	TALLOC_CTX *mem_ctx;
+	const char *filename = NULL;
+	const char *ca_file = NULL;
+	struct mscat_pkcs7 *cat_pkcs7;
+	struct mscat_ctl *msctl;
+	unsigned int member_count = 0;
+	unsigned int attribute_count = 0;
+	unsigned int i;
+	int rc;
+
+	if (argc < 1) {
+		return -1;
+	}
+	filename = argv[1];
+
+	if (filename == NULL || filename[0] == '\0') {
+		return -1;
+	}
+
+	mem_ctx = talloc_init("parsemscat");
+	if (mem_ctx == NULL) {
+		fprintf(stderr, "Failed to initialize talloc\n");
+		exit(1);
+	}
+
+	/* READ MS ROOT CERTIFICATE */
+
+	cat_pkcs7 = mscat_pkcs7_init(mem_ctx);
+	if (cat_pkcs7 == NULL) {
+		exit(1);
+	}
+
+	rc = mscat_pkcs7_import_catfile(cat_pkcs7,
+					filename);
+	if (rc != 0) {
+		exit(1);
+	}
+
+	if (argc >= 2) {
+		ca_file = argv[2];
+	}
+
+	rc = mscat_pkcs7_verify(cat_pkcs7, ca_file);
+	if (rc != 0) {
+		printf("FAILED TO VERIFY CATALOG FILE!\n");
+		exit(1);
+	}
+	printf("CATALOG FILE VERIFIED!\n\n");
+
+	msctl = mscat_ctl_init(mem_ctx);
+	if (msctl == NULL) {
+		exit(1);
+	}
+
+	rc = mscat_ctl_import(msctl, cat_pkcs7);
+	if (rc != 0) {
+		exit(1);
+	}
+
+	member_count = mscat_ctl_get_member_count(msctl);
+	printf("CATALOG MEMBER COUNT=%d\n", member_count);
+
+	for (i = 0; i < member_count; i++) {
+		struct mscat_ctl_member *m;
+		size_t j;
+
+		rc = mscat_ctl_get_member(msctl,
+					  mem_ctx,
+					  i + 1,
+					  &m);
+		if (rc != 0) {
+			exit(1);
+		}
+
+		printf("CATALOG MEMBER\n");
+		if (m->checksum.type == MSCAT_CHECKSUM_STRING) {
+			printf("  CHECKSUM: %s\n", m->checksum.string);
+		} else if (m->checksum.type == MSCAT_CHECKSUM_BLOB) {
+			printf("  CHECKSUM: ");
+			for (j = 0; j < m->checksum.size; j++) {
+				printf("%X", m->checksum.blob[j]);
+			}
+			printf("\n");
+		}
+		printf("\n");
+
+		if (m->file.name != NULL) {
+			printf("  FILE: %s, FLAGS=0x%08x\n",
+			       m->file.name,
+			       m->file.flags);
+		}
+
+		if (m->info.guid != NULL) {
+			printf("  GUID: %s, ID=0x%08x\n",
+			       m->info.guid,
+			       m->info.id);
+		}
+
+		if (m->osattr.value != NULL) {
+			printf("  OSATTR: %s, FLAGS=0x%08x\n",
+			       m->osattr.value,
+			       m->osattr.flags);
+		}
+
+		if (m->mac.type != MSCAT_MAC_UNKNOWN) {
+			printf("  MAC: %s, DIGEST: ",
+			       mac_to_string(m->mac.type));
+			for (j = 0; j < m->mac.digest_size; j++) {
+				printf("%X", m->mac.digest[j]);
+			}
+			printf("\n");
+		}
+		printf("\n");
+	}
+	printf("\n");
+
+	attribute_count = mscat_ctl_get_attribute_count(msctl);
+	printf("CATALOG ATTRIBUTE COUNT=%d\n", attribute_count);
+
+	for (i = 0; i < attribute_count; i++) {
+		struct mscat_ctl_attribute *a;
+
+		rc = mscat_ctl_get_attribute(msctl,
+					     mem_ctx,
+					     i + 1,
+					     &a);
+		if (rc != 0) {
+			exit(1);
+		}
+
+		printf("  NAME=%s, FLAGS=0x%08x, VALUE=%s\n",
+		       a->name,
+		       a->flags,
+		       a->value);
+	}
+	talloc_free(mem_ctx);
+	return 0;
+}
diff --git a/lib/mscat/mscat.asn b/lib/mscat/mscat.asn
new file mode 100644
index 00000000000..a4bdd057793
--- /dev/null
+++ b/lib/mscat/mscat.asn
@@ -0,0 +1,136 @@
+--
+--  ASN.1 Description for Microsoft Catalog Files
+--
+--    Copyright 2016 Andreas Schneider <asn at samba.org>
+--    Copyright 2016 Nikos Mavrogiannopoulos <nmav at redhat.com>
+--
+--  This program is free software: you can redistribute it and/or modify
+--  it under the terms of the GNU Lesser General Public License as published
+--  by 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,
+--  but WITHOUT ANY WARRANTY; without even the implied warranty of
+--  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+--  GNU Lesser General Public License for more details.
+--
+--  You should have received a copy of the GNU Lesser General Public License
+--  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+--
+CATALOG {}
+DEFINITIONS IMPLICIT TAGS ::= -- assuming implicit tags, should try explicit too
+
+BEGIN
+
+-- CATALOG_NAME_VALUE
+CatalogNameValue ::= SEQUENCE { -- 180
+    name       BMPString,
+    flags      INTEGER, -- 10010001
+    value      OCTET STRING -- UTF-16-LE
+}
+
+-- CATALOG_MEMBER_INFO
+CatalogMemberInfo ::= SEQUENCE {
+    name       BMPString,
+    id         INTEGER -- 0200
+}
+
+CatalogMemberInfo2 ::= SEQUENCE {
+    memId       OBJECT IDENTIFIER,
+    unknown     SET OF SpcLink
+}
+
+-- SPC_INDIRECT_DATA
+SpcIndirectData ::= SEQUENCE {
+    data        SpcAttributeTypeAndOptionalValue,
+    messageDigest DigestInfo
+}
+
+SpcAttributeTypeAndOptionalValue ::= SEQUENCE {
+    type        OBJECT IDENTIFIER,
+    value       ANY DEFINED BY type OPTIONAL
+}
+
+DigestInfo ::= SEQUENCE {
+    digestAlgorithm AlgorithmIdentifier,
+    digest OCTET STRING
+}
+
+AlgorithmIdentifier ::=  SEQUENCE  {
+    algorithm   OBJECT IDENTIFIER,
+    parameters  ANY DEFINED BY algorithm OPTIONAL
+                -- contains a value of the type
+}
+
+-- SPC_PE_IMAGE_DATA
+SpcPEImageData ::= SEQUENCE {
+    flags       SpcPeImageFlags DEFAULT includeResources,
+    link        [0] EXPLICIT SpcLink OPTIONAL
+}
+
+SpcPeImageFlags ::= BIT STRING {
+    includeResources            (0),
+    includeDebugInfo            (1),
+    includeImportAddressTable   (2)
+}
+
+SpcLink ::= CHOICE {
+    url         [0]    IMPLICIT IA5String,
+    moniker     [1]    IMPLICIT SpcSerializedObject,
+    file        [2]    EXPLICIT SpcString
+}
+
+SpcSerializedObject ::= SEQUENCE {
+    classId     OCTET STRING, -- GUID
+    data        OCTET STRING  -- Binary structure
+}
+
+SpcString ::= CHOICE {
+    unicode     [0] IMPLICIT BMPString,
+    ascii       [1] IMPLICIT IA5String
+}
+
+-- SPC_IMAGE_DATA_FILE
+SpcImageDataFile ::= SEQUENCE {
+    flags       BIT STRING,
+    file        SpcLink
+}
+
+-----------------------------------------------------------
+-- CERT_TRUST_LIST STRUCTURE
+-----------------------------------------------------------
+
+CatalogListId ::= SEQUENCE {
+    oid OBJECT IDENTIFIER
+}
+
+CatalogListMemberId ::= SEQUENCE {
+    oid OBJECT IDENTIFIER,
+    optional NULL
+}
+
+MemberAttribute ::= SEQUENCE {
+    contentType OBJECT IDENTIFIER,
+    content SET OF ANY DEFINED BY contentType
+}
+
+CatalogListMember ::= SEQUENCE {
+    checksum OCTET STRING, -- The member checksum (e.g. SHA1)
+    attributes SET OF MemberAttribute OPTIONAL
+}
+
+CatalogAttribute ::= SEQUENCE {
+    dataId OBJECT IDENTIFIER,
+    encapsulated_data OCTET STRING -- encapsulates CatNameValue or SpcPeImageData
+}
+
+CertTrustList ::= SEQUENCE {
+    catalogListId CatalogListId,
+    unknownString OCTET STRING, -- 16 bytes MD5 hash?
+    trustUtcTime UTCTime,
+    catalogListMemberId CatalogListMemberId,
+    members SEQUENCE OF CatalogListMember,
+    attributes [0] EXPLICIT SEQUENCE OF CatalogAttribute OPTIONAL
+}
+
+END
diff --git a/lib/mscat/mscat.h b/lib/mscat/mscat.h
new file mode 100644
index 00000000000..fbf60ffb117
--- /dev/null
+++ b/lib/mscat/mscat.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016      Andreas Schneider <asn at samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MSCAT_H
+#define _MSCAT_H
+
+#include <stdbool.h>
+#include <talloc.h>
+#include <gnutls/pkcs7.h>
+#include <libtasn1.h>
+
+enum mscat_mac_algorithm {
+	MSCAT_MAC_UNKNOWN,
+	MSCAT_MAC_NULL,
+	MSCAT_MAC_MD5,
+	MSCAT_MAC_SHA1,
+	MSCAT_MAC_SHA256,
+	MSCAT_MAC_SHA512
+};
+
+struct mscat_pkcs7;
+
+struct mscat_pkcs7 *mscat_pkcs7_init(TALLOC_CTX *mem_ctx);
+
+int mscat_pkcs7_import_catfile(struct mscat_pkcs7 *mp7,
+			       const char *catfile);
+
+int mscat_pkcs7_verify(struct mscat_pkcs7 *mp7,
+		       const char *ca_file);
+
+struct mscat_ctl;
+
+struct mscat_ctl *mscat_ctl_init(TALLOC_CTX *mem_ctx);
+
+int mscat_ctl_import(struct mscat_ctl *ctl,
+		     struct mscat_pkcs7 *pkcs7);
+
+int mscat_ctl_get_member_count(struct mscat_ctl *ctl);
+
+enum mscat_checksum_type {
+	MSCAT_CHECKSUM_STRING = 1,
+	MSCAT_CHECKSUM_BLOB
+};
+
+struct mscat_ctl_member {
+	struct {
+		enum mscat_checksum_type type;
+		union {
+			const char *string;
+			uint8_t *blob;
+		};
+		size_t size;
+	} checksum;
+	struct {
+		const char *name;
+		uint32_t flags;
+	} file;
+	struct {
+		const char *value;
+		uint32_t flags;
+	} osattr;
+	struct {
+		const char *guid;
+		uint32_t id;
+	} info;
+	struct {
+		enum mscat_mac_algorithm type;
+		uint8_t *digest;
+		size_t digest_size;
+	} mac;
+};
+
+int mscat_ctl_get_member(struct mscat_ctl *ctl,
+			 TALLOC_CTX *mem_ctx,
+			 unsigned int idx,
+			 struct mscat_ctl_member **member);
+
+int mscat_ctl_get_attribute_count(struct mscat_ctl *ctl);
+
+struct mscat_ctl_attribute {
+	const char *name;
+	uint32_t flags;
+	const char *value;
+};
+
+int mscat_ctl_get_attribute(struct mscat_ctl *ctl,
+			    TALLOC_CTX *mem_ctx,
+			    unsigned int idx,
+			    struct mscat_ctl_attribute **pattribute);
+
+#endif /* _MSCAT_H */
diff --git a/lib/mscat/mscat_ctl.c b/lib/mscat/mscat_ctl.c
new file mode 100644
index 00000000000..335773236b4
--- /dev/null
+++ b/lib/mscat/mscat_ctl.c
@@ -0,0 +1,1177 @@
+/*
+ * Copyright (c) 2016      Andreas Schneider <asn at samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <util/debug.h>
+#include <util/byteorder.h>
+#include <util/data_blob.h>
+#include <charset.h>
+
+#include "mscat.h"
+#include "mscat_private.h"
+
+#define ASN1_NULL_DATA "\x05\x00"
+#define ASN1_NULL_DATA_SIZE 2
+
+#define HASH_SHA1_OBJID                "1.3.14.3.2.26"
+#define HASH_SHA256_OBJID              "2.16.840.1.101.3.4.2.1"
+#define HASH_SHA512_OBJID              "2.16.840.1.101.3.4.2.3"
+
+#define SPC_INDIRECT_DATA_OBJID        "1.3.6.1.4.1.311.2.1.4"
+#define SPC_PE_IMAGE_DATA_OBJID        "1.3.6.1.4.1.311.2.1.15"
+
+#define CATALOG_LIST_OBJOID            "1.3.6.1.4.1.311.12.1.1"
+#define CATALOG_LIST_MEMBER_OBJOID     "1.3.6.1.4.1.311.12.1.2"
+#define CATALOG_LIST_MEMBER_V2_OBJOID  "1.3.6.1.4.1.311.12.1.3"
+
+#define CAT_NAME_VALUE_OBJID           "1.3.6.1.4.1.311.12.2.1"
+#define CAT_MEMBERINFO_OBJID           "1.3.6.1.4.1.311.12.2.2"
+
+extern const asn1_static_node mscat_asn1_tab[];
+
+struct mscat_ctl {
+	int version;
+	ASN1_TYPE asn1_desc;
+	ASN1_TYPE tree_ctl;
+	gnutls_datum_t raw_ctl;
+};
+
+static char *mscat_asn1_get_oid(TALLOC_CTX *mem_ctx,
+				asn1_node root,
+				const char *oid_name)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	char oid_str[32] = {0};
+	int oid_len = sizeof(oid_str);
+	int rc;
+
+	rc = asn1_read_value(root,
+			     oid_name,
+			     oid_str,
+			     &oid_len);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		fprintf(stderr,
+			"Failed to read value '%s': %s\n",
+			oid_name,
+			error_string);
+		return NULL;
+	}
+
+	return talloc_strndup(mem_ctx, oid_str, oid_len);
+}
+
+static bool mscat_asn1_oid_equal(const char *o1, const char *o2)
+{
+	int cmp;
+
+	cmp = strcmp(o1, o2);
+	if (cmp != 0) {
+		return false;
+	}
+
+	return true;
+}
+
+static int mscat_asn1_read_value(TALLOC_CTX *mem_ctx,
+				 asn1_node root,
+				 const char *name,
+				 DATA_BLOB *blob)
+{
+	DATA_BLOB tmp;
+	unsigned int etype = ASN1_ETYPE_INVALID;
+	int len = 0;
+	int rc;
+
+	rc = asn1_read_value_type(root, name, NULL, &len, &etype);
+	if (rc != ASN1_SUCCESS && len == 0) {
+		return rc;
+	}
+
+	if (etype == ASN1_ETYPE_BIT_STRING) {
+		if (len + 7 < len) {
+			return -1;
+		}
+		len = (len + 7) / 8;
+	}
+
+	if (len == 0) {
+		*blob = data_blob_null;
+		return 0;
+	}
+
+	if (len + 1 < len) {
+		return -1;
+	}
+	tmp = data_blob_talloc_zero(mem_ctx, len + 1);
+	if (tmp.data == NULL) {
+		return -1;
+	}
+
+	rc = asn1_read_value(root,
+			     name,
+			     tmp.data,
+			     &len);
+	if (rc != ASN1_SUCCESS) {
+		data_blob_free(&tmp);
+		return rc;
+	}
+
+	if (etype == ASN1_ETYPE_BIT_STRING) {
+		len = (len + 7) / 8;
+		if (len + 7 < len) {
+			return -1;
+		}
+	}
+	tmp.length = len;
+
+	*blob = tmp;
+
+	return 0;
+}
+
+static int mscat_ctl_cleanup(struct mscat_ctl *ctl)
+{
+	if (ctl->asn1_desc != ASN1_TYPE_EMPTY) {
+		asn1_delete_structure(&ctl->asn1_desc);
+	}
+
+	return 0;
+}
+
+struct mscat_ctl *mscat_ctl_init(TALLOC_CTX *mem_ctx)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	struct mscat_ctl *cat_ctl;
+	int rc;
+
+	cat_ctl = talloc_zero(mem_ctx, struct mscat_ctl);
+	if (cat_ctl == NULL) {
+		return NULL;
+	}
+	talloc_set_destructor(cat_ctl, mscat_ctl_cleanup);
+
+	cat_ctl->asn1_desc = ASN1_TYPE_EMPTY;
+	cat_ctl->tree_ctl = ASN1_TYPE_EMPTY;
+
+	rc = asn1_array2tree(mscat_asn1_tab,
+			     &cat_ctl->asn1_desc,
+			     error_string);
+	if (rc != ASN1_SUCCESS) {
+		talloc_free(cat_ctl);
+		asn1_perror(rc);
+		DBG_ERR("Failed to create parser tree: %s",
+			error_string);
+		return NULL;
+	}
+
+	return cat_ctl;
+}
+
+int mscat_ctl_import(struct mscat_ctl *ctl,
+		     struct mscat_pkcs7 *pkcs7)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	TALLOC_CTX *tmp_ctx = NULL;
+	char *oid;
+	bool ok;
+	int rc;
+
+	rc = gnutls_pkcs7_get_embedded_data(pkcs7->c,
+					    GNUTLS_PKCS7_EDATA_GET_RAW,
+					    &ctl->raw_ctl);
+	if (rc != GNUTLS_E_SUCCESS) {
+		DBG_ERR("Failed to get embedded data from pkcs7: %s",
+			gnutls_strerror(rc));
+		return -1;
+	}
+
+	rc = asn1_create_element(ctl->asn1_desc,
+				 "CATALOG.CertTrustList",
+				 &ctl->tree_ctl);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to create CertTrustList ASN.1 element");
+		return -1;
+	}
+
+	rc = asn1_der_decoding(&ctl->tree_ctl,
+			       ctl->raw_ctl.data,
+			       ctl->raw_ctl.size,
+			       error_string);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to parse ASN.1 CertTrustList: %s",
+			error_string);
+		return -1;
+	}
+
+	tmp_ctx = talloc_new(ctl);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	oid = mscat_asn1_get_oid(tmp_ctx,
+				 ctl->tree_ctl,
+				 "catalogListId.oid");
+	if (oid == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	ok = mscat_asn1_oid_equal(oid, CATALOG_LIST_OBJOID);
+	if (!ok) {
+		DBG_ERR("Invalid oid (%s), expected CATALOG_LIST_OBJOID",
+			oid);
+		rc = -1;
+		goto done;
+	}
+	talloc_free(oid);
+
+	oid = mscat_asn1_get_oid(tmp_ctx,
+				 ctl->tree_ctl,
+				 "catalogListMemberId.oid");
+	if (oid == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	ok = mscat_asn1_oid_equal(oid, CATALOG_LIST_MEMBER_V2_OBJOID);
+	if (ok) {
+		ctl->version = 2;
+	} else {
+		ok = mscat_asn1_oid_equal(oid, CATALOG_LIST_MEMBER_OBJOID);
+		if (ok) {
+			ctl->version = 1;
+		} else {
+			DBG_ERR("Invalid oid (%s), expected "
+				"CATALOG_LIST_MEMBER_OBJOID",
+				oid);
+			rc = -1;
+			goto done;
+		}
+	}
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+static int ctl_get_member_checksum_string(struct mscat_ctl *ctl,
+					  TALLOC_CTX *mem_ctx,
+					  unsigned int idx,
+					  const char **pchecksum,
+					  size_t *pchecksum_size)
+{
+	TALLOC_CTX *tmp_ctx;
+	DATA_BLOB chksum_ucs2;
+	size_t converted_size = 0;
+	char *checksum;
+	char *element;
+	int rc = -1;
+	bool ok;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	element = talloc_asprintf(tmp_ctx,
+				  "members.?%u.checksum",
+				  idx);
+	if (element == NULL) {
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   ctl->tree_ctl,
+				   element,
+				   &chksum_ucs2);
+	talloc_free(element);
+	if (rc != 0) {
+		goto done;
+	}
+
+	ok = convert_string_talloc(mem_ctx,
+				   CH_UTF16LE,
+				   CH_UNIX,
+				   chksum_ucs2.data,
+				   chksum_ucs2.length,
+				   (void **)&checksum,
+				   &converted_size);
+	if (!ok) {
+		rc = -1;
+		goto done;
+	}
+
+	*pchecksum = talloc_steal(mem_ctx, checksum);
+	*pchecksum_size = strlen(checksum + 1);
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+static int ctl_get_member_checksum_blob(struct mscat_ctl *ctl,
+					TALLOC_CTX *mem_ctx,
+					unsigned int idx,
+					uint8_t **pchecksum,
+					size_t *pchecksum_size)
+{
+	TALLOC_CTX *tmp_ctx;
+	DATA_BLOB chksum;
+	char *element;
+	int rc = -1;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	element = talloc_asprintf(tmp_ctx,
+				  "members.?%u.checksum",
+				  idx);
+	if (element == NULL) {
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   ctl->tree_ctl,
+				   element,
+				   &chksum);
+	talloc_free(element);
+	if (rc != 0) {
+		goto done;
+	}
+
+	*pchecksum = talloc_steal(mem_ctx, chksum.data);
+	*pchecksum_size = chksum.length;
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+static int ctl_parse_name_value(struct mscat_ctl *ctl,
+				TALLOC_CTX *mem_ctx,
+				DATA_BLOB *content,
+				char **pname,
+				uint32_t *pflags,
+				char **pvalue)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	ASN1_TYPE name_value = ASN1_TYPE_EMPTY;
+	TALLOC_CTX *tmp_ctx;
+	DATA_BLOB name_blob = data_blob_null;
+	DATA_BLOB flags_blob = data_blob_null;
+	DATA_BLOB value_blob = data_blob_null;
+	size_t converted_size = 0;
+	bool ok;
+	int rc;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	rc = asn1_create_element(ctl->asn1_desc,
+				 "CATALOG.CatalogNameValue",
+				 &name_value);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = asn1_der_decoding(&name_value,
+			       content->data,
+			       content->length,
+			       error_string);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to decode CATALOG.CatalogNameValue - %s",
+			error_string);
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(mem_ctx,
+				   name_value,
+				   "name",
+				   &name_blob);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(mem_ctx,
+				   name_value,
+				   "flags",
+				   &flags_blob);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(mem_ctx,
+				   name_value,
+				   "value",
+				   &value_blob);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	ok = convert_string_talloc(mem_ctx,
+				   CH_UTF16BE,
+				   CH_UNIX,
+				   name_blob.data,
+				   name_blob.length,
+				   (void **)pname,
+				   &converted_size);
+	if (!ok) {
+		rc = ASN1_MEM_ERROR;
+		goto done;
+	}
+
+	*pflags = RIVAL(flags_blob.data, 0);
+
+	ok = convert_string_talloc(mem_ctx,
+				   CH_UTF16LE,
+				   CH_UNIX,
+				   value_blob.data,
+				   value_blob.length,
+				   (void **)pvalue,
+				   &converted_size);
+	if (!ok) {
+		rc = ASN1_MEM_ERROR;
+		goto done;
+	}
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+static int ctl_parse_member_info(struct mscat_ctl *ctl,
+				 TALLOC_CTX *mem_ctx,
+				 DATA_BLOB *content,
+				 char **pname,
+				 uint32_t *pid)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	ASN1_TYPE member_info = ASN1_TYPE_EMPTY;
+	TALLOC_CTX *tmp_ctx;
+	DATA_BLOB name_blob = data_blob_null;
+	DATA_BLOB id_blob = data_blob_null;
+	size_t converted_size = 0;
+	bool ok;
+	int rc;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	rc = asn1_create_element(ctl->asn1_desc,
+				 "CATALOG.CatalogMemberInfo",
+				 &member_info);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = asn1_der_decoding(&member_info,
+			       content->data,
+			       content->length,
+			       error_string);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to decode CATALOG.CatalogMemberInfo - %s",
+			error_string);
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(mem_ctx,
+				   member_info,
+				   "name",
+				   &name_blob);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(mem_ctx,
+				   member_info,
+				   "id",
+				   &id_blob);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	ok = convert_string_talloc(mem_ctx,
+				   CH_UTF16BE,
+				   CH_UNIX,
+				   name_blob.data,
+				   name_blob.length,
+				   (void **)pname,
+				   &converted_size);
+	if (!ok) {
+		rc = ASN1_MEM_ERROR;
+		goto done;
+	}
+
+	*pid = RSVAL(id_blob.data, 0);
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+
+static int ctl_spc_pe_image_data(struct mscat_ctl *ctl,
+				 TALLOC_CTX *mem_ctx,
+				 DATA_BLOB *content,
+				 char **pfile)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	ASN1_TYPE spc_pe_image_data = ASN1_TYPE_EMPTY;
+	DATA_BLOB flags_blob = data_blob_null;
+	DATA_BLOB choice_blob = data_blob_null;
+	char *file = NULL;
+	TALLOC_CTX *tmp_ctx;
+	int cmp;
+	int rc;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	rc = asn1_create_element(ctl->asn1_desc,
+				 "CATALOG.SpcPEImageData",
+				 &spc_pe_image_data);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = asn1_der_decoding(&spc_pe_image_data,
+			       content->data,
+			       content->length,
+			       error_string);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to decode CATALOG.SpcPEImageData - %s",
+			error_string);
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   spc_pe_image_data,
+				   "flags",
+				   &flags_blob);
+	if (rc == ASN1_SUCCESS) {
+		uint32_t flags = RIVAL(flags_blob.data, 0);
+
+		DBG_ERR(">>> SPC_PE_IMAGE_DATA FLAGS=0x%08x",
+			flags);
+	} else  {
+		DBG_ERR("Failed to parse 'flags' in CATALOG.SpcPEImageData - %s",
+			asn1_strerror(rc));
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   spc_pe_image_data,
+				   "link",
+				   &choice_blob);
+	if (rc != ASN1_SUCCESS) {
+		DBG_ERR("Failed to parse 'link' in CATALOG.SpcPEImageData - %s",
+			asn1_strerror(rc));
+		goto done;
+	}
+
+	cmp = strncmp((char *)choice_blob.data, "url", choice_blob.length);
+	if (cmp == 0) {
+	}
+
+	cmp = strncmp((char *)choice_blob.data, "moniker", choice_blob.length);
+	if (cmp == 0) {
+	}
+
+	cmp = strncmp((char *)choice_blob.data, "file", choice_blob.length);
+	if (cmp == 0) {
+		DATA_BLOB file_blob;
+		char *link;
+
+		rc = mscat_asn1_read_value(tmp_ctx,
+					   spc_pe_image_data,
+					   "link.file",
+					   &choice_blob);
+		if (rc != ASN1_SUCCESS) {
+			goto done;
+		}
+
+		link = talloc_asprintf(tmp_ctx, "link.file.%s", (char *)choice_blob.data);
+		if (link == NULL) {
+			rc = -1;
+			goto done;
+		}
+
+		rc = mscat_asn1_read_value(tmp_ctx,
+					   spc_pe_image_data,
+					   link,
+					   &file_blob);
+		if (rc != ASN1_SUCCESS) {
+			DBG_ERR("Failed to read '%s' - %s",
+				link,
+				asn1_strerror(rc));
+			rc = -1;
+			goto done;
+		}
+
+		cmp = strncmp((char *)choice_blob.data, "unicode", choice_blob.length);
+		if (cmp == 0) {
+			size_t converted_size = 0;
+			bool ok;
+
+			ok = convert_string_talloc(tmp_ctx,
+						   CH_UTF16BE,
+						   CH_UNIX,
+						   file_blob.data,
+						   file_blob.length,
+						   (void **)&file,
+						   &converted_size);
+			if (!ok) {
+				rc = -1;
+				goto done;
+			}
+		}
+
+		cmp = strncmp((char *)choice_blob.data, "ascii", choice_blob.length);
+		if (cmp == 0) {
+			file = talloc_strndup(tmp_ctx,
+					      (char *)file_blob.data,
+					      file_blob.length);
+			if (file == NULL) {
+				rc = -1;
+				goto done;
+			}
+		}
+	}
+
+	if (file != NULL) {
+		*pfile = talloc_steal(mem_ctx, file);
+	}
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+static int ctl_spc_indirect_data(struct mscat_ctl *ctl,
+				 TALLOC_CTX *mem_ctx,
+				 DATA_BLOB *content,
+				 enum mscat_mac_algorithm *pmac_algorithm,
+				 uint8_t **pdigest,
+				 size_t *pdigest_size)
+{
+	char error_string[ASN1_MAX_ERROR_DESCRIPTION_SIZE] = {0};
+	ASN1_TYPE spc_indirect_data = ASN1_TYPE_EMPTY;
+	TALLOC_CTX *tmp_ctx;
+	enum mscat_mac_algorithm mac_algorithm = MSCAT_MAC_UNKNOWN;
+	const char *oid;
+	DATA_BLOB data_value_blob = data_blob_null;
+	DATA_BLOB digest_parameters_blob = data_blob_null;
+	DATA_BLOB digest_blob = data_blob_null;
+	bool ok;
+	int rc;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	rc = asn1_create_element(ctl->asn1_desc,
+				 "CATALOG.SpcIndirectData",
+				 &spc_indirect_data);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	rc = asn1_der_decoding(&spc_indirect_data,
+			       content->data,
+			       content->length,
+			       error_string);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to decode CATALOG.SpcIndirectData - %s",
+			error_string);
+		goto done;
+	}
+
+	oid = mscat_asn1_get_oid(tmp_ctx,
+				 spc_indirect_data,
+				 "data.type");
+	if (oid == NULL) {
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   spc_indirect_data,
+				   "data.value",
+				   &data_value_blob);
+	if (rc != ASN1_SUCCESS) {
+		asn1_perror(rc);
+		DBG_ERR("Failed to find data.value in SpcIndirectData");
+		goto done;
+	}
+
+	ok = mscat_asn1_oid_equal(oid, SPC_PE_IMAGE_DATA_OBJID);
+	if (ok) {
+		char *file = NULL;
+
+		rc = ctl_spc_pe_image_data(ctl,
+					   tmp_ctx,
+					   &data_value_blob,
+					   &file);
+		if (rc != 0) {
+			goto done;
+		}
+
+		/* Just returns <<<Obsolete>>> as file */
+		DBG_NOTICE(">>> LINK: %s",
+			   file);
+	}
+
+	oid = mscat_asn1_get_oid(tmp_ctx,
+				 spc_indirect_data,
+				 "messageDigest.digestAlgorithm.algorithm");
+	if (oid == NULL) {
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   spc_indirect_data,
+				   "messageDigest.digestAlgorithm.parameters",
+				   &digest_parameters_blob);
+	if (rc == ASN1_SUCCESS) {
+		/* Make sure we don't have garbage */
+		int cmp;
+
+		if (digest_parameters_blob.length != ASN1_NULL_DATA_SIZE) {
+			rc = -1;
+			goto done;
+		}
+		cmp = memcmp(digest_parameters_blob.data,
+			     ASN1_NULL_DATA,
+			     digest_parameters_blob.length);
+		if (cmp != 0) {
+			rc = -1;
+			goto done;
+		}
+	} else if (rc != ASN1_ELEMENT_NOT_FOUND) {
+		asn1_perror(rc);
+		goto done;
+	}
+
+	ok = mscat_asn1_oid_equal(oid, HASH_SHA1_OBJID);
+	if (ok) {
+		mac_algorithm = MSCAT_MAC_SHA1;
+	}
+
+	ok = mscat_asn1_oid_equal(oid, HASH_SHA256_OBJID);
+	if (ok) {
+		mac_algorithm = MSCAT_MAC_SHA256;
+	}
+
+	if (mac_algorithm != MSCAT_MAC_UNKNOWN &&
+	    mac_algorithm != MSCAT_MAC_NULL) {
+		rc = mscat_asn1_read_value(tmp_ctx,
+					   spc_indirect_data,
+					   "messageDigest.digest",
+					   &digest_blob);
+		if (rc != ASN1_SUCCESS) {
+			asn1_perror(rc);
+			DBG_ERR("Failed to find messageDigest.digest in "
+				"SpcIndirectData");
+			goto done;
+		}
+	}
+
+	*pmac_algorithm = mac_algorithm;
+	*pdigest = talloc_steal(mem_ctx, digest_blob.data);
+	*pdigest_size = digest_blob.length;
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+static int ctl_get_member_attributes(struct mscat_ctl *ctl,
+				     TALLOC_CTX *mem_ctx,
+				     unsigned int idx,
+				     struct mscat_ctl_member *m)
+{
+	TALLOC_CTX *tmp_ctx;
+	char *el1;
+	int count = 0;
+	int i;
+	int rc = -1;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	el1 = talloc_asprintf(tmp_ctx,
+			      "members.?%u.attributes",
+			      idx);
+	if (el1 == NULL) {
+		goto done;
+	}
+
+	rc = asn1_number_of_elements(ctl->tree_ctl,
+				     el1,
+				     &count);
+	if (rc != ASN1_SUCCESS) {
+		goto done;
+	}
+
+	for (i = 0; i < count; i++) {
+		int content_start = 0;
+		int content_end = 0;
+		size_t content_len;
+		DATA_BLOB content;
+		char *el2;
+		char *oid;
+		bool ok;
+
+		el2 = talloc_asprintf(tmp_ctx,
+				      "%s.?%d.contentType",
+				      el1,
+				      i + 1);
+		if (el2 == NULL) {
+			rc = -1;
+			goto done;
+		}
+
+		oid = mscat_asn1_get_oid(tmp_ctx,
+					 ctl->tree_ctl,
+					 el2);
+		talloc_free(el2);
+		if (oid == NULL) {
+			rc = -1;
+			goto done;
+		}
+
+		/* FIXME Looks like this is always 1 */
+		el2 = talloc_asprintf(tmp_ctx,
+				      "%s.?%d.content.?1",
+				      el1,
+				      i + 1);
+		if (el2 == NULL) {
+			rc = -1;
+			goto done;
+		}
+
+		DBG_DEBUG("Decode element (startEnd)  %s",
+			  el2);
+
+		rc = asn1_der_decoding_startEnd(ctl->tree_ctl,
+						ctl->raw_ctl.data,
+						ctl->raw_ctl.size,
+						el2,
+						&content_start,
+						&content_end);
+		if (rc != ASN1_SUCCESS) {
+			goto done;
+		}
+		if (content_start < content_end) {
+			goto done;
+		}
+		content_len = content_end - content_start + 1;
+
+		DBG_DEBUG("Content data_blob length: %zu",
+			  content_len);
+
+		content = data_blob_talloc_zero(tmp_ctx, content_len);
+		if (content.data == NULL) {
+			rc = -1;
+			goto done;
+		}
+		memcpy(content.data,
+		       &ctl->raw_ctl.data[content_start],
+		       content_len);
+
+		ok = mscat_asn1_oid_equal(oid, CAT_NAME_VALUE_OBJID);
+		if (ok) {
+			char *name;
+			uint32_t flags;
+			char *value;
+			int cmp;
+
+			rc = ctl_parse_name_value(ctl,
+						  tmp_ctx,
+						  &content,
+						  &name,
+						  &flags,
+						  &value);
+			if (rc != 0) {
+				goto done;
+			}
+
+			DBG_DEBUG("Parsed NameValue: name=%s, flags=%u, value=%s",
+				  name,
+				  flags,
+				  value);
+
+			cmp = strcmp(name, "File");
+			if (cmp == 0) {
+				m->file.name = talloc_steal(m, value);
+				m->file.flags = flags;
+
+				continue;
+			}
+
+			cmp = strcmp(name, "OSAttr");
+			if (cmp == 0) {
+				m->osattr.value = talloc_steal(m, value);
+				m->osattr.flags = flags;
+
+				continue;
+			}
+		}
+
+		ok = mscat_asn1_oid_equal(oid, CAT_MEMBERINFO_OBJID);
+		if (ok) {
+			char *name;
+			uint32_t id;
+
+			rc = ctl_parse_member_info(ctl,
+						   tmp_ctx,
+						   &content,
+						   &name,
+						   &id);
+			if (rc != 0) {
+				goto done;
+			}
+
+			m->info.guid = talloc_steal(m, name);
+			m->info.id = id;
+
+			continue;
+		}
+
+		ok = mscat_asn1_oid_equal(oid, SPC_INDIRECT_DATA_OBJID);
+		if (ok) {
+			rc = ctl_spc_indirect_data(ctl,
+						  m,
+						  &content,
+						  &m->mac.type,
+						  &m->mac.digest,
+						  &m->mac.digest_size);
+			if (rc != 0) {
+				goto done;
+			}
+
+			continue;
+		}
+	}
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+int mscat_ctl_get_member(struct mscat_ctl *ctl,
+			 TALLOC_CTX *mem_ctx,
+			 unsigned int idx,
+			 struct mscat_ctl_member **pmember)
+{
+	TALLOC_CTX *tmp_ctx;
+	struct mscat_ctl_member *m;
+	int rc = -1;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	m = talloc_zero(tmp_ctx, struct mscat_ctl_member);
+	if (m == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	if (ctl->version == 1) {
+		m->checksum.type = MSCAT_CHECKSUM_STRING;
+		rc = ctl_get_member_checksum_string(ctl,
+						    m,
+						    idx,
+						    &m->checksum.string,
+						    &m->checksum.size);
+	} else if (ctl->version == 2) {
+		m->checksum.type = MSCAT_CHECKSUM_BLOB;
+		rc = ctl_get_member_checksum_blob(ctl,
+						  m,
+						  idx,
+						  &m->checksum.blob,
+						  &m->checksum.size);
+	}
+	if (rc != 0) {
+		goto done;
+	}
+
+	rc = ctl_get_member_attributes(ctl,
+				       mem_ctx,
+				       idx,
+				       m);
+	if (rc != 0) {
+		goto done;
+	}
+
+	*pmember = talloc_steal(mem_ctx, m);
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+int mscat_ctl_get_member_count(struct mscat_ctl *ctl)
+{
+	int count = 0;
+	int rc;
+
+	rc = asn1_number_of_elements(ctl->tree_ctl,
+				     "members",
+				     &count);
+	if (rc != ASN1_SUCCESS) {
+		return -1;
+	}
+
+	return count;
+}
+
+int mscat_ctl_get_attribute(struct mscat_ctl *ctl,
+			    TALLOC_CTX *mem_ctx,
+			    unsigned int idx,
+			    struct mscat_ctl_attribute **pattribute)
+{
+	TALLOC_CTX *tmp_ctx;
+	const char *el1;
+	const char *el2;
+	const char *oid;
+	char *name = NULL;
+	uint32_t flags = 0;
+	char *value = NULL;
+	struct mscat_ctl_attribute *a;
+	DATA_BLOB encapsulated_data_blob = data_blob_null;
+	int rc;
+
+	tmp_ctx = talloc_new(mem_ctx);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	a = talloc_zero(tmp_ctx, struct mscat_ctl_attribute);
+	if (a == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	el1 = talloc_asprintf(tmp_ctx,
+				  "attributes.?%u.dataId",
+				  idx);
+	if (el1 == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	oid = mscat_asn1_get_oid(tmp_ctx,
+				 ctl->tree_ctl,
+				 el1);
+	if (oid == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	el2 = talloc_asprintf(tmp_ctx,
+				  "attributes.?%u.encapsulated_data",
+				  idx);
+	if (el2 == NULL) {
+		rc = -1;
+		goto done;
+	}
+
+	rc = mscat_asn1_read_value(tmp_ctx,
+				   ctl->tree_ctl,
+				   el2,
+				   &encapsulated_data_blob);
+	if (rc != ASN1_SUCCESS) {
+		goto done;
+	}
+
+	rc = ctl_parse_name_value(ctl,
+				  tmp_ctx,
+				  &encapsulated_data_blob,
+				  &name,
+				  &flags,
+				  &value);
+	if (rc != 0) {
+		goto done;
+	}
+
+	a->name = talloc_steal(a, name);
+	a->flags = flags;
+	a->value = talloc_steal(a, value);
+
+	*pattribute = talloc_steal(mem_ctx, a);
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+int mscat_ctl_get_attribute_count(struct mscat_ctl *ctl)
+{
+	int count = 0;
+	int rc;
+
+	rc = asn1_number_of_elements(ctl->tree_ctl,
+				     "attributes",
+				     &count);
+	if (rc != ASN1_SUCCESS) {
+		return -1;
+	}
+
+	return count;
+}
diff --git a/lib/mscat/mscat_pkcs7.c b/lib/mscat/mscat_pkcs7.c
new file mode 100644
index 00000000000..e71f233a6e0
--- /dev/null
+++ b/lib/mscat/mscat_pkcs7.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2016      Andreas Schneider <asn at samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <util/debug.h>
+#include <util/data_blob.h>
+
+#include "mscat.h"
+#include "mscat_private.h"
+
+#define PKCS7_CTL_OBJID                "1.3.6.1.4.1.311.10.1"
+
+static int mscat_pkcs7_cleanup(struct mscat_pkcs7 *mp7)
+{
+	if (mp7->c != NULL) {
+		gnutls_pkcs7_deinit(mp7->c);
+	}
+
+	return 0;
+}
+
+struct mscat_pkcs7 *mscat_pkcs7_init(TALLOC_CTX *mem_ctx)
+{
+	struct mscat_pkcs7 *pkcs7;
+	int rc;
+
+	pkcs7 = talloc_zero(mem_ctx, struct mscat_pkcs7);
+	if (pkcs7 == NULL) {
+		return NULL;
+	}
+	talloc_set_destructor(pkcs7, mscat_pkcs7_cleanup);
+
+	rc = gnutls_pkcs7_init(&pkcs7->c);
+	if (rc != 0) {
+		talloc_free(pkcs7);
+		return NULL;
+	}
+
+	return pkcs7;
+}
+
+static int mscat_read_file(TALLOC_CTX *mem_ctx,
+			   const char *filename,
+			   DATA_BLOB *pblob)
+{
+	struct stat sb = {0};
+	size_t alloc_size;
+	size_t count;
+	DATA_BLOB blob;
+	FILE *fp;
+	int rc;
+
+	fp = fopen(filename, "r");
+	if (fp == NULL) {
+		return -1;
+	}
+
+	rc = fstat(fileno(fp), &sb);
+	if (rc != 0) {
+		goto error;
+	}
+
+	if (!S_ISREG(sb.st_mode)) {
+		errno = EINVAL;
+		goto error;
+	}
+	if (SIZE_MAX - 1 < (unsigned long)sb.st_size) {
+		errno = ENOMEM;
+		goto error;
+	}
+	alloc_size = sb.st_size + 1;
+
+	blob = data_blob_talloc_zero(mem_ctx, alloc_size);
+	if (blob.data == NULL) {
+		goto error;
+	}
+
+	count = fread(blob.data, 1, blob.length, fp);
+	if (count != blob.length) {
+		if (ferror(fp)) {
+			goto error;
+		}
+	}
+	blob.data[count] = '\0';
+	blob.length = count;
+	fclose(fp);
+
+	*pblob = blob;
+
+	return 0;
+error:
+	data_blob_free(&blob);
+	fclose(fp);
+	return rc;
+}
+
+int mscat_pkcs7_import_catfile(struct mscat_pkcs7 *mp7,
+			       const char *catfile)
+{
+	TALLOC_CTX *tmp_ctx;
+	gnutls_datum_t mscat_data = {
+		.size = 0,
+	};
+	DATA_BLOB blob;
+	int rc;
+
+	tmp_ctx = talloc_new(mp7);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	rc = mscat_read_file(tmp_ctx,
+			     catfile,
+			     &blob);
+	if (rc == -1) {
+		DBG_ERR("Failed to read catalog file '%s' - %s",
+			catfile,
+			strerror(errno));
+		goto done;
+	}
+
+	mscat_data.data = blob.data;
+	mscat_data.size = blob.length;
+
+	rc = gnutls_pkcs7_import(mp7->c,
+				 &mscat_data,
+				 GNUTLS_X509_FMT_DER);
+	if (rc < 0) {
+		DBG_ERR("Failed to import PKCS7 from '%s' - %s",
+			catfile,
+			gnutls_strerror(rc));
+		goto done;
+	}
+
+	rc = 0;
+done:
+	talloc_free(tmp_ctx);
+	return rc;
+}
+
+int mscat_pkcs7_verify(struct mscat_pkcs7 *mp7,
+		       const char *ca_file)
+{
+	TALLOC_CTX *tmp_ctx = NULL;
+	gnutls_x509_trust_list_t tl = NULL;
+	gnutls_datum_t ca_data;
+	DATA_BLOB blob;
+	uint32_t flags = 0;
+	const char *oid;
+	int count;
+	int cmp;
+	int rc;
+	int i;
+
+	oid = gnutls_pkcs7_get_embedded_data_oid(mp7->c);
+	if (oid == NULL) {
+		DBG_ERR("Failed to get oid - %s",
+			gnutls_strerror(errno));
+		return -1;
+	}
+
+	cmp = strcmp(oid, PKCS7_CTL_OBJID);
+	if (cmp != 0) {
+		DBG_ERR("Invalid oid in catalog file! oid: %s, expected: %s",
+			oid,
+			PKCS7_CTL_OBJID);
+		return -1;
+	}
+
+	tmp_ctx = talloc_new(mp7);
+	if (tmp_ctx == NULL) {
+		return -1;
+	}
+
+	rc = gnutls_x509_trust_list_init(&tl,
+					 0); /* default size */
+	if (rc != 0) {
+		DBG_ERR("Failed to create trust list - %s",
+			gnutls_strerror(rc));
+		goto done;
+	}
+
+
+	/* Load the system trust list */
+	rc = gnutls_x509_trust_list_add_system_trust(tl, 0, 0);
+	if (rc < 0) {
+		DBG_ERR("Failed to add system trust list - %s",
+			gnutls_strerror(rc));
+		goto done;
+	}
+	DBG_INFO("Loaded %d CAs", rc);
+
+	if (ca_file != NULL) {
+		rc = mscat_read_file(tmp_ctx,
+				     ca_file,
+				     &blob);
+		if (rc != 0) {
+			DBG_ERR("Failed to read CA file '%s' - %s",
+				ca_file,
+				strerror(errno));
+			goto done;
+		}
+
+		ca_data.data = blob.data;
+		ca_data.size = blob.length;
+
+		rc = gnutls_x509_trust_list_add_trust_mem(tl,
+							  &ca_data,
+							  NULL, /* crls */
+							  GNUTLS_X509_FMT_DER,
+							  0, /* tl_flags */
+							  0); /* tl_vflags */
+		if (rc < 0) {
+			DBG_ERR("Failed to add '%s' to trust list - %s (%d)",
+				ca_file,
+				gnutls_strerror(rc),
+				rc);
+			goto done;
+		}
+		DBG_INFO("Loaded %d additional CAs", rc);
+	}
+
+	/*
+	 * Drivers often exist for quite some time, so it is possible that one
+	 * of the certificates in the trust list expired.
+	 * This is not a big deal, but we need to disable the time checks
+	 * or the versification will fail.
+	 */
+	flags = GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS|
+		GNUTLS_VERIFY_DISABLE_TIME_CHECKS;
+
+	count = gnutls_pkcs7_get_signature_count(mp7->c);
+	if (count == 0) {
+		DBG_ERR("Failed to verify catalog file, no signatures found");
+		goto done;
+	}
+
+	for (i = 0; i < count; i++) {
+		rc = gnutls_pkcs7_verify(mp7->c,
+					 tl,
+					 NULL, /* vdata */
+					 0,    /* vdata_size */
+					 i,    /* index */
+					 NULL, /* data */
+					 flags);   /* flags */
+		if (rc < 0) {
+			DBG_ERR("Failed to verify catalog file - %s (%d)",
+				gnutls_strerror(rc),
+				rc);
+			goto done;
+		}
+	}
+
+	rc = 0;
+done:
+	gnutls_x509_trust_list_deinit(tl, 1);
+	talloc_free(tmp_ctx);
+	return rc;
+}
diff --git a/lib/mscat/mscat_private.h b/lib/mscat/mscat_private.h
new file mode 100644
index 00000000000..d79b364ceb0
--- /dev/null
+++ b/lib/mscat/mscat_private.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016      Andreas Schneider <asn at samba.org>
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _MSCAT_PRIVATE_H
+#define _MSCAT_PRIVATE_H
+
+#include <gnutls/pkcs7.h>
+
+struct mscat_pkcs7 {
+	gnutls_pkcs7_t c;
+};
+
+#endif /* _MSCAT_PRIVATE_H */
diff --git a/lib/mscat/wscript b/lib/mscat/wscript
new file mode 100644
index 00000000000..951de7fe42a
--- /dev/null
+++ b/lib/mscat/wscript
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+import os
+import Logs
+import sys
+
+def configure(conf):
+    pkg_name = 'libtasn1'
+    pkg_minversion = '3.8'
+
+    if conf.CHECK_BUNDLED_SYSTEM_PKG(pkg_name, minversion=pkg_minversion):
+        if not conf.find_program('asn1Parser', var='ASN1PARSER'):
+            Logs.error('ERROR: You need to make sure asn1Parser is available')
+            sys.exit(1)
+
+    conf.CHECK_FUNCS_IN('gnutls_pkcs7_get_embedded_data_oid', 'gnutls')
+
+def build(bld):
+    if bld.CONFIG_SET('HAVE_LIBTASN1') and bld.CONFIG_SET('HAVE_GNUTLS_PKCS7_GET_EMBEDDED_DATA_OID'):
+        bld.SAMBA_GENERATOR('MSCAT_PARSER',
+                            source='mscat.asn',
+                            target='mscat_asn1_tab.c',
+                            rule='${ASN1PARSER} --output ${TGT} ${SRC}',
+                            group='build_source')
+
+        bld.SAMBA_LIBRARY('mscat',
+                          source='''
+                                 mscat_asn1_tab.c
+                                 mscat_ctl.c
+                                 mscat_pkcs7.c
+                                 ''',
+                          deps='''
+                               talloc
+                               gnutls
+                               libtasn1
+                               samba-util
+                               ''',
+                          private_library=True)
+
+        bld.SAMBA_BINARY('dumpmscat',
+                         source='dumpmscat.c',
+                         deps='mscat')
diff --git a/wscript b/wscript
index dbfc1210f7f..3afc64c7caa 100644
--- a/wscript
+++ b/wscript
@@ -251,6 +251,7 @@ def configure(conf):
     if conf.env.with_ctdb:
         conf.RECURSE('ctdb')
     conf.RECURSE('lib/socket')
+    conf.RECURSE('lib/mscat')
     conf.RECURSE('auth')
     conf.RECURSE('packaging')
 
diff --git a/wscript_build b/wscript_build
index 54548769b19..51fe553a8c1 100644
--- a/wscript_build
+++ b/wscript_build
@@ -117,6 +117,7 @@ bld.RECURSE('libcli/echo')
 bld.RECURSE('libcli/dns')
 bld.RECURSE('libcli/samsync')
 bld.RECURSE('libcli/registry')
+bld.RECURSE('lib/mscat')
 bld.RECURSE('source4/lib/policy')
 bld.RECURSE('libcli/named_pipe_auth')
 if bld.CONFIG_GET('ENABLE_SELFTEST'):
-- 
2.17.1



More information about the samba-technical mailing list