[PATCH] Add support for MS Catalog files

Jeremy Allison jra at samba.org
Tue Aug 7 19:21:07 UTC 2018


On Tue, Aug 07, 2018 at 11:40:39AM +0200, Andreas Schneider wrote:
> 
> Updated patchset attached.
> 
> 
> Thanks for the review!

LGTM. Really nice, clean code - thanks !

Reviewed-by: Jeremy Allison <jra at samba.org>

However, it adds a dependency on the package libtasn1-bin
for the ans1Parser program.

You'll need to get root at sn-devel to install that,
or change the patch to gate the compilation of this binary on
availability of libtasn1-bin, as currently it won't
configure without it.

Jeremy.

> -- 
> Andreas Schneider                      asn at samba.org
> Samba Team                             www.samba.org
> GPG-ID:     8DFF53E18F2ABC8D8F3C92237EE0FC4DCC014E3D

> From 28c9596dcec930a8a3fb434bb0b31ad38e55cd5d 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     | 1194 +++++++++++++++++++++++++++++++++++++
>  lib/mscat/mscat_pkcs7.c   |  284 +++++++++
>  lib/mscat/mscat_private.h |   27 +
>  lib/mscat/wscript         |   42 ++
>  wscript                   |    1 +
>  wscript_build             |    1 +
>  9 files changed, 1978 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..eac2184e7ad
> --- /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("dumpmscat");
> +	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..972922c4f75
> --- /dev/null
> +++ b/lib/mscat/mscat_ctl.c
> @@ -0,0 +1,1194 @@
> +/*
> + * 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 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) {
> +		DBG_ERR("Failed to read value '%s': %s\n",
> +			oid_name,
> +			asn1_strerror(rc));
> +		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 = data_blob_null;
> +	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) {
> +		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) {
> +		if (len + 7 < len) {
> +			return -1;
> +		}
> +		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 = NULL;
> +	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);
> +		DBG_ERR("Failed to create parser tree: %s - %s\n",
> +			asn1_strerror(rc),
> +			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\n",
> +			gnutls_strerror(rc));
> +		return -1;
> +	}
> +
> +	rc = asn1_create_element(ctl->asn1_desc,
> +				 "CATALOG.CertTrustList",
> +				 &ctl->tree_ctl);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to create CertTrustList ASN.1 element - %s\n",
> +			asn1_strerror(rc));
> +		return -1;
> +	}
> +
> +	rc = asn1_der_decoding(&ctl->tree_ctl,
> +			       ctl->raw_ctl.data,
> +			       ctl->raw_ctl.size,
> +			       error_string);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to parse ASN.1 CertTrustList: %s - %s\n",
> +			asn1_strerror(rc),
> +			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 = data_blob_null;
> +	size_t converted_size = 0;
> +	char *checksum = NULL;
> +	char *element = NULL;
> +	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_size = strlen(checksum) + 1;
> +	*pchecksum = talloc_move(mem_ctx, &checksum);
> +
> +	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 = data_blob_null;
> +	char *element = NULL;
> +	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_move(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) {
> +		DBG_ERR("Failed to create element for "
> +			"CATALOG.CatalogNameValue: %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = asn1_der_decoding(&name_value,
> +			       content->data,
> +			       content->length,
> +			       error_string);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to decode CATALOG.CatalogNameValue: %s - %s",
> +			asn1_strerror(rc),
> +			error_string);
> +		goto done;
> +	}
> +
> +	rc = mscat_asn1_read_value(mem_ctx,
> +				   name_value,
> +				   "name",
> +				   &name_blob);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to read 'name': %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = mscat_asn1_read_value(mem_ctx,
> +				   name_value,
> +				   "flags",
> +				   &flags_blob);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to read 'flags': %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = mscat_asn1_read_value(mem_ctx,
> +				   name_value,
> +				   "value",
> +				   &value_blob);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to read 'value': %s\n",
> +			asn1_strerror(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) {
> +		DBG_ERR("Failed to create element for "
> +			"CATALOG.CatalogMemberInfo: %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = asn1_der_decoding(&member_info,
> +			       content->data,
> +			       content->length,
> +			       error_string);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to decode CATALOG.CatalogMemberInfo: %s - %s",
> +			asn1_strerror(rc),
> +			error_string);
> +		goto done;
> +	}
> +
> +	rc = mscat_asn1_read_value(mem_ctx,
> +				   member_info,
> +				   "name",
> +				   &name_blob);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to read 'name': %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = mscat_asn1_read_value(mem_ctx,
> +				   member_info,
> +				   "id",
> +				   &id_blob);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to read 'id': %s\n",
> +			asn1_strerror(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) {
> +		DBG_ERR("Failed to create element for "
> +			"CATALOG.SpcPEImageData: %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = asn1_der_decoding(&spc_pe_image_data,
> +			       content->data,
> +			       content->length,
> +			       error_string);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to decode CATALOG.SpcPEImageData: %s - %s",
> +			asn1_strerror(rc),
> +			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) {
> +		/* Never seen in a printer catalog file yet */
> +		DBG_INFO("Please report a Samba bug and attach the catalog "
> +			 "file\n");
> +	}
> +
> +	cmp = strncmp((char *)choice_blob.data, "moniker", choice_blob.length);
> +	if (cmp == 0) {
> +		/* Never seen in a printer catalog file yet */
> +		DBG_INFO("Please report a Samba bug and attach the catalog "
> +			 "file\n");
> +	}
> +
> +	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_move(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 = NULL;
> +	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) {
> +		DBG_ERR("Failed to create element for "
> +			"CATALOG.SpcIndirectData: %s\n",
> +			asn1_strerror(rc));
> +		goto done;
> +	}
> +
> +	rc = asn1_der_decoding(&spc_indirect_data,
> +			       content->data,
> +			       content->length,
> +			       error_string);
> +	if (rc != ASN1_SUCCESS) {
> +		DBG_ERR("Failed to decode CATALOG.SpcIndirectData: %s - %s",
> +			asn1_strerror(rc),
> +			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) {
> +		DBG_ERR("Failed to find data.value in SpcIndirectData: %s\n",
> +			asn1_strerror(rc));
> +		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) {
> +		DBG_ERR("Failed to read 'messageDigest.digestAlgorithm.parameters': %s\n",
> +			asn1_strerror(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) {
> +			DBG_ERR("Failed to find messageDigest.digest in "
> +				"SpcIndirectData: %s\n",
> +				asn1_strerror(rc));
> +			goto done;
> +		}
> +	}
> +
> +	*pmac_algorithm = mac_algorithm;
> +	*pdigest = talloc_move(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 = NULL;
> +	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_move(m, &value);
> +				m->file.flags = flags;
> +
> +				continue;
> +			}
> +
> +			cmp = strcmp(name, "OSAttr");
> +			if (cmp == 0) {
> +				m->osattr.value = talloc_move(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_move(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 = NULL;
> +	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_move(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 = NULL;
> +	const char *el2 = NULL;
> +	const char *oid = NULL;
> +	char *name = NULL;
> +	uint32_t flags = 0;
> +	char *value = NULL;
> +	struct mscat_ctl_attribute *a = NULL;
> +	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_move(a, &name);
> +	a->flags = flags;
> +	a->value = talloc_move(a, &value);
> +
> +	*pattribute = talloc_move(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..55944232205
> --- /dev/null
> +++ b/lib/mscat/mscat_pkcs7.c
> @@ -0,0 +1,284 @@
> +/*
> + * 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 verification will fail.
> +	 */
> +	flags = GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS|
> +		GNUTLS_VERIFY_DISABLE_TIME_CHECKS;
> +
> +#if GNUTLS_VERSION_NUMBER >= 0x030600
> +	/* The "Microsoft Root Authority" certificate uses SHA1 */
> +	flags |= GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1;
> +#endif
> +
> +	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 f11c49dde67..f98d731e537 100644
> --- a/wscript
> +++ b/wscript
> @@ -279,6 +279,7 @@ def configure(conf):
>      if conf.env.with_ctdb:
>          conf.RECURSE('ctdb')
>      conf.RECURSE('lib/socket')
> +    conf.RECURSE('lib/mscat')
>      conf.RECURSE('packaging')
>  
>      conf.SAMBA_CHECK_UNDEFINED_SYMBOL_FLAGS()
> 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.18.0
> 




More information about the samba-technical mailing list