diff options
Diffstat (limited to '')
-rw-r--r-- | lib/mscat/dumpmscat.c | 197 | ||||
-rw-r--r-- | lib/mscat/mscat.asn | 136 | ||||
-rw-r--r-- | lib/mscat/mscat.h | 105 | ||||
-rw-r--r-- | lib/mscat/mscat_ctl.c | 1197 | ||||
-rw-r--r-- | lib/mscat/mscat_pkcs7.c | 292 | ||||
-rw-r--r-- | lib/mscat/mscat_private.h | 27 | ||||
-rw-r--r-- | lib/mscat/wscript | 41 |
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') |