[PATCH] Add support for MS Catalog files

Andreas Schneider asn at samba.org
Thu Jun 21 16:05:28 UTC 2018


Hi,

the attached patch adds support for parsing MS Catalog files. This will be 
needed for MS-PAR support in future.

For the cryptography it is using GnuTLS and for the asn1 part it uses 
libtasn1. libtasn1 is used by GnuTLS and maintained by Nikos 
Mavrogiannopoulos. As we already use GnuTLS we already consume libtasn1 
through it.

libtasn1 is fuzzed via GnuTLS on oss-fuzz.

It is very well documented, see:
https://www.gnu.org/software/libtasn1/manual/libtasn1.html

It would make sense to use it for other asn1 stuff in Samba.

Review is much appreciated.


Thanks!

	Andreas

-- 
Andreas Schneider                      asn at samba.org
Samba Team                             www.samba.org
GPG-ID:     8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D
-------------- next part --------------
>From 8d9cf0a6f2d37af822edc9caef9bd62b8fad6ec2 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     | 1165 +++++++++++++++++++++++++++++++++++++
 lib/mscat/mscat_pkcs7.c   |  279 +++++++++
 lib/mscat/mscat_private.h |   27 +
 lib/mscat/wscript         |   42 ++
 wscript                   |    1 +
 wscript_build             |    1 +
 9 files changed, 1944 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..2333de8a738
--- /dev/null
+++ b/lib/mscat/mscat_ctl.c
@@ -0,0 +1,1165 @@
+/*
+ * 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_strdup(mem_ctx, oid_str);
+}
+
+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) {
+		len = (len + 7) / 8;
+	}
+
+	if (len == 0) {
+		*blob = data_blob_null;
+		return 0;
+	}
+
+	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;
+	}
+	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;
+		}
+		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