summaryrefslogtreecommitdiffstats
path: root/lib/mscat
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/mscat/dumpmscat.c197
-rw-r--r--lib/mscat/mscat.asn136
-rw-r--r--lib/mscat/mscat.h105
-rw-r--r--lib/mscat/mscat_ctl.c1197
-rw-r--r--lib/mscat/mscat_pkcs7.c292
-rw-r--r--lib/mscat/mscat_private.h27
-rw-r--r--lib/mscat/wscript41
7 files changed, 1995 insertions, 0 deletions
diff --git a/lib/mscat/dumpmscat.c b/lib/mscat/dumpmscat.c
new file mode 100644
index 0000000..5364610
--- /dev/null
+++ b/lib/mscat/dumpmscat.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2016 Andreas Schneider <asn@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);
+ }
+
+ rc = mscat_ctl_get_member_count(msctl);
+ if (rc < 0) {
+ exit(1);
+ }
+
+ member_count = rc;
+ 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");
+
+ rc = mscat_ctl_get_attribute_count(msctl);
+ if (rc < 0) {
+ exit(1);
+ }
+ attribute_count = rc;
+ 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 0000000..a4bdd05
--- /dev/null
+++ b/lib/mscat/mscat.asn
@@ -0,0 +1,136 @@
+--
+-- ASN.1 Description for Microsoft Catalog Files
+--
+-- Copyright 2016 Andreas Schneider <asn@samba.org>
+-- Copyright 2016 Nikos Mavrogiannopoulos <nmav@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 0000000..fbf60ff
--- /dev/null
+++ b/lib/mscat/mscat.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 Andreas Schneider <asn@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 0000000..292aa9a
--- /dev/null
+++ b/lib/mscat/mscat_ctl.c
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (c) 2016 Andreas Schneider <asn@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_node asn1_desc;
+ asn1_node 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 tmp_len = 0;
+ size_t len;
+ int rc;
+
+ rc = asn1_read_value_type(root, name, NULL, &tmp_len, &etype);
+ if (rc != ASN1_SUCCESS) {
+ return rc;
+ }
+ len = tmp_len;
+
+ 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,
+ &tmp_len);
+ if (rc != ASN1_SUCCESS) {
+ data_blob_free(&tmp);
+ return rc;
+ }
+ len = tmp_len;
+
+ 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 != NULL) {
+ 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 = NULL;
+ cat_ctl->tree_ctl = NULL;
+
+ 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\n",
+ 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\n",
+ 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,
+ &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_node name_value = NULL;
+ 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\n",
+ 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,
+ 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,
+ 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_node member_info = NULL;
+ 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\n",
+ 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,
+ 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_node spc_pe_image_data = NULL;
+ 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\n",
+ 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\n",
+ flags);
+ } else {
+ DBG_ERR("Failed to parse 'flags' in CATALOG.SpcPEImageData - %s\n",
+ 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\n",
+ 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\n",
+ 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,
+ &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_node spc_indirect_data = NULL;
+ 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\n",
+ 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\n",
+ 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\n",
+ 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\n",
+ 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\n",
+ 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 = NULL;
+ uint32_t id = 0;
+
+ 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 0000000..07d8514
--- /dev/null
+++ b/lib/mscat/mscat_pkcs7.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2016 Andreas Schneider <asn@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 = data_blob_null;
+ 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;
+ rc = -1;
+ goto error;
+ }
+ if (SIZE_MAX - 1 < (unsigned long)sb.st_size) {
+ errno = ENOMEM;
+ rc = -1;
+ goto error;
+ }
+ alloc_size = sb.st_size + 1;
+
+ blob = data_blob_talloc_zero(mem_ctx, alloc_size);
+ if (blob.data == NULL) {
+ rc = -1;
+ goto error;
+ }
+
+ count = fread(blob.data, 1, blob.length, fp);
+ if (count != blob.length) {
+ if (ferror(fp)) {
+ rc = -1;
+ 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 = {
+ .length = 0,
+ };
+ 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\n",
+ 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\n",
+ 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 = {
+ .length = 0,
+ };
+ 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\n",
+ 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\n",
+ 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\n",
+ 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\n",
+ gnutls_strerror(rc));
+ goto done;
+ }
+ DBG_INFO("Loaded %d CAs\n", 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\n",
+ 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)\n",
+ ca_file,
+ gnutls_strerror(rc),
+ rc);
+ goto done;
+ }
+ DBG_INFO("Loaded %d additional CAs\n", 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\n");
+ 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)\n",
+ 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 0000000..d79b364
--- /dev/null
+++ b/lib/mscat/mscat_private.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 Andreas Schneider <asn@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 0000000..00eb1d4
--- /dev/null
+++ b/lib/mscat/wscript
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+import os
+import sys
+from waflib import Logs
+
+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.warn('WARNING: ans1Parser hasn\'t been found! Please install it (e.g. libtasn1-bin)')
+
+def build(bld):
+ if (bld.CONFIG_SET('HAVE_LIBTASN1') and
+ bld.env.ASN1PARSER):
+
+ 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')