From: Nikos Mavrogiannopoulos Date: Fri, 7 Feb 2003 13:33:10 +0000 (+0000) Subject: Added the new PKCS7 parsing functions. X-Git-Tag: gnutls_0_8_3~73 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=62343c097b67d07afd07fc7e90d71df289ab489c;p=thirdparty%2Fgnutls.git Added the new PKCS7 parsing functions. --- diff --git a/includes/gnutls/x509.h b/includes/gnutls/x509.h index 39f35c8143..aa728d0dcf 100644 --- a/includes/gnutls/x509.h +++ b/includes/gnutls/x509.h @@ -34,15 +34,15 @@ extern "C" { /* Some OIDs usually found in Distinguished names */ -#define X520_COUNTRY_NAME "2 5 4 6" -#define X520_ORGANIZATION_NAME "2 5 4 10" -#define X520_ORGANIZATIONAL_UNIT_NAME "2 5 4 11" -#define X520_COMMON_NAME "2 5 4 3" -#define X520_LOCALITY_NAME "2 5 4 7" -#define X520_STATE_OR_PROVINCE_NAME "2 5 4 8" -#define LDAP_DC "0 9 2342 19200300 100 1 25" -#define LDAP_UID "0 9 2342 19200300 100 1 1" -#define PKCS9_EMAIL "1 2 840 113549 1 9 1" +#define GNUTLS_OID_X520_COUNTRY_NAME "2 5 4 6" +#define GNUTLS_OID_X520_ORGANIZATION_NAME "2 5 4 10" +#define GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME "2 5 4 11" +#define GNUTLS_OID_X520_COMMON_NAME "2 5 4 3" +#define GNUTLS_OID_X520_LOCALITY_NAME "2 5 4 7" +#define GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME "2 5 4 8" +#define GNUTLS_OID_LDAP_DC "0 9 2342 19200300 100 1 25" +#define GNUTLS_OID_LDAP_UID "0 9 2342 19200300 100 1 1" +#define GNUTLS_OID_PKCS9_EMAIL "1 2 840 113549 1 9 1" /* Certificate handling functions */ @@ -110,6 +110,22 @@ int gnutls_x509_crl_get_certificate_count(gnutls_x509_crl crl); int gnutls_x509_crl_get_certificate(gnutls_x509_crl crl, int index, unsigned char* serial, int* serial_size, time_t* time); + +/* PKCS7 structures handling + */ + +struct gnutls_pkcs7_int; +typedef struct gnutls_pkcs7_int* gnutls_pkcs7; + +int gnutls_pkcs7_init(gnutls_pkcs7 * pkcs7); +void gnutls_pkcs7_deinit(gnutls_pkcs7 pkcs7); +int gnutls_pkcs7_import(gnutls_pkcs7 pkcs7, const gnutls_datum * data, + gnutls_x509_certificate_format format); +int gnutls_pkcs7_get_certificate(gnutls_pkcs7 pkcs7, + int indx, char* certificate, int* certificate_size); +int gnutls_pkcs7_extract_certificate_count(gnutls_pkcs7 pkcs7); + + #ifdef __cplusplus } #endif diff --git a/lib/Makefile.am b/lib/Makefile.am index 7be6afbbf8..92b3849035 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -47,7 +47,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c \ libgnutls_la_SOURCES = $(COBJECTS) libgnutls_la_LIBADD = $(MINITASN1_OBJECTS) x509/dn.lo x509/crl.lo x509/common.lo \ - x509/x509.lo + x509/x509.lo x509/pkcs7.lo x509/extensions.lo libgnutls_la_LDFLAGS = $(LIBASN1_LINK) $(LIBGCRYPT_LIBS) \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ diff --git a/lib/gnutls_x509.h b/lib/gnutls_x509.h index 6f15892ef5..dde7427f20 100644 --- a/lib/gnutls_x509.h +++ b/lib/gnutls_x509.h @@ -10,6 +10,8 @@ typedef enum ConvFlags { int _gnutls_x509_cert2gnutls_cert(gnutls_cert * gCert, gnutls_datum derCert, ConvFlags flags); +#define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE" +#define PEM_CERT_SEP "-----BEGIN CERTIFICATE" #define PEM_PKCS7_SEP "-----BEGIN PKCS7" #define PEM_KEY_RSA_SEP "-----BEGIN RSA" diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index 13e700c9dd..7590361274 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -1,8 +1,10 @@ INCLUDES = -I../ -I../minitasn1/ -EXTRA_DIST = crl.h dn.h common.h x509.h extensions.h +EXTRA_DIST = crl.h dn.h common.h x509.h extensions.h pkcs7.h noinst_LTLIBRARIES = libx509.la -libx509_la_SOURCES = crl.c dn.c common.c x509.c extensions.c +libx509_la_SOURCES = crl.c dn.c common.c x509.c extensions.c \ + pkcs7.c + diff --git a/lib/x509/common.h b/lib/x509/common.h index 56cc4910f3..368677f0cb 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -2,8 +2,10 @@ #define MAX_INT_DIGITS 4 void _gnutls_int2str(unsigned int k, char *data); -#define PEM_CERT_SEP2 "-----BEGIN X509 CERTIFICATE" -#define PEM_CERT_SEP "-----BEGIN CERTIFICATE" +#define PEM_CRL "X509 CRL" +#define PEM_X509_CERT "X509 CERTIFICATE" +#define PEM_X509_CERT2 "CERTIFICATE" +#define PEM_PKCS7 "PKCS7" time_t _gnutls_x509_utcTime2gtime(char *ttime); time_t _gnutls_x509_generalTime2gtime(char *ttime); diff --git a/lib/x509/crl.c b/lib/x509/crl.c index b4475ab450..597b4102e1 100644 --- a/lib/x509/crl.c +++ b/lib/x509/crl.c @@ -91,7 +91,7 @@ int gnutls_x509_crl_import(gnutls_x509_crl crl, const gnutls_datum * data, if (format == GNUTLS_X509_FMT_PEM) { opaque *out; - result = _gnutls_fbase64_decode("X509 CRL", data->data, data->size, + result = _gnutls_fbase64_decode(PEM_CRL, data->data, data->size, &out); if (result <= 0) { @@ -193,7 +193,8 @@ int gnutls_x509_crl_import(gnutls_x509_crl crl, const gnutls_datum * data, return 0; cleanup: - asn1_delete_structure(&crl->crl); + if (crl->crl) + asn1_delete_structure(&crl->crl); _gnutls_free_datum(&crl->signed_data); _gnutls_free_datum(&crl->signature); if (need_free) _gnutls_free_datum( &_data); diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c new file mode 100644 index 0000000000..df84ca84c2 --- /dev/null +++ b/lib/x509/pkcs7.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2003 Nikos Mavroyanopoulos + * + * This file is part of GNUTLS. + * + * The GNUTLS library 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 2.1 of the License, or (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * gnutls_pkcs7_init - This function initializes a gnutls_pkcs7 structure + * @pkcs7: The structure to be initialized + * + * This function will initialize a PKCS7 structure. PKCS7 stands for + * Certificate Revocation List. + * + * Returns 0 on success. + * + **/ +int gnutls_pkcs7_init(gnutls_pkcs7 * pkcs7) +{ + *pkcs7 = gnutls_calloc( 1, sizeof(gnutls_pkcs7_int)); + + if (*pkcs7) return 0; /* success */ + return GNUTLS_E_MEMORY_ERROR; +} + +/** + * gnutls_pkcs7_deinit - This function deinitializes memory used by a gnutls_pkcs7 structure + * @pkcs7: The structure to be initialized + * + * This function will deinitialize a PKCS7 structure. + * + **/ +void gnutls_pkcs7_deinit(gnutls_pkcs7 pkcs7) +{ + if (pkcs7->pkcs7) + asn1_delete_structure(&pkcs7->pkcs7); + + gnutls_free(pkcs7); +} + +/** + * gnutls_pkcs7_import - This function will import a DER or PEM encoded PKCS7 + * @pkcs7: The structure to store the parsed PKCS7. + * @data: The DER or PEM encoded PKCS7. + * @format: One of DER or PEM + * + * This function will convert the given DER or PEM encoded PKCS7 + * to the native gnutls_pkcs7 format. The output will be stored in 'pkcs7'. + * + * If the PKCS7 is PEM encoded it should have a header of "X509 PKCS7", and + * it must be a null terminated string. + * + * Returns 0 on success. + * + **/ +int gnutls_pkcs7_import(gnutls_pkcs7 pkcs7, const gnutls_datum * data, + gnutls_x509_certificate_format format) +{ + int result = 0, need_free = 0; + gnutls_datum _data = { data->data, data->size }; + + /* If the PKCS7 is in PEM format then decode it + */ + if (format == GNUTLS_X509_FMT_PEM) { + opaque *out; + + result = _gnutls_fbase64_decode(PEM_PKCS7, data->data, data->size, + &out); + + if (result <= 0) { + if (result==0) result = GNUTLS_E_INTERNAL_ERROR; + gnutls_assert(); + return result; + } + + _data.data = out; + _data.size = result; + + need_free = 1; + } + + pkcs7->pkcs7 = ASN1_TYPE_EMPTY; + + result = asn1_create_element(_gnutls_get_pkix(), + "PKIX1.ContentInfo", + &pkcs7->pkcs7, "pkcs7"); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + result = asn1_der_decoding(&pkcs7->pkcs7, _data.data, _data.size, NULL); + if (result != ASN1_SUCCESS) { + result = _gnutls_asn2err(result); + gnutls_assert(); + goto cleanup; + } + + if (need_free) _gnutls_free_datum( &_data); + + return 0; + + cleanup: + if (pkcs7->pkcs7) + asn1_delete_structure(&pkcs7->pkcs7); + if (need_free) _gnutls_free_datum( &_data); + return result; +} + + +/** + * gnutls_pkcs7_get_certificate - This function returns a certificate in a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7 structure + * @indx: contains the index of the certificate to extract + * @certificate: the contents of the certificate will be copied there (may be null) + * @certificate_size: should hold the size of the certificate + * + * This function will return a certificate of the PKCS7 or RFC2630 certificate set. + * Returns 0 on success. If the provided buffer is not long enough, + * then GNUTLS_E_SHORT_MEMORY_BUFFER is returned. + * + * After the last certificate has been read GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE + * will be returned. + * + **/ +int gnutls_pkcs7_get_certificate(gnutls_pkcs7 pkcs7, + int indx, char* certificate, int* certificate_size) +{ + ASN1_TYPE c2 = NULL; + int result, len; + char oid[128]; + opaque *tmp = NULL; + char root2[64]; + char counter[MAX_INT_DIGITS]; + int tmp_size; + + if (certificate_size == NULL) return GNUTLS_E_INVALID_REQUEST; + + /* root2 is used as a temp storage area + */ + len = sizeof(oid) - 1; + result = asn1_read_value(pkcs7->pkcs7, "pkcs7.contentType", oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ( strcmp( oid, "1 2 840 113549 1 7 2") != 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + tmp_size = 256; /* some initial size */ + tmp = gnutls_malloc(tmp_size); + if (tmp==NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value(pkcs7->pkcs7, "pkcs7.content", tmp, &tmp_size); + /* FIXME: a hard coded value + */ + if (result==ASN1_MEM_ERROR && tmp_size > 0 && tmp_size < 50*1024) { + tmp = gnutls_realloc_fast( tmp, tmp_size); + if (tmp==NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + result = asn1_read_value(pkcs7->pkcs7, "pkcs7.content", tmp, &tmp_size); + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* tmp, tmp_size hold the data and the size of the CertificateSet structure + * actually the ANY stuff. + */ + + /* Step 1. In case of a signed structure extract certificate set. + */ + if ((result=_gnutls_asn1_create_element + (_gnutls_get_pkix(), "PKIX1.SignedData", &c2, "c2")) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; } + + result = asn1_der_decoding(&c2, tmp, tmp_size, NULL); + if (result != ASN1_SUCCESS) { + /* couldn't decode DER */ + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* Step 2. Parse the CertificateSet + */ + + _gnutls_str_cpy( root2, sizeof(root2), "c2.certificates.?"); + _gnutls_int2str( indx+1, counter); + _gnutls_str_cat( root2, sizeof(root2), counter); + + len = sizeof(oid) - 1; + + result = asn1_read_value(c2, root2, oid, &len); + + if (result == ASN1_VALUE_NOT_FOUND) { + result = GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + goto cleanup; + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* if 'Certificate' is the choice found: + */ + if (strcmp( oid, "certificate") == 0) { + int start, end; + + result = asn1_der_decoding_startEnd(c2, tmp, tmp_size, + root2, &start, &end); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + end = end-start+1; + + if (certificate!=NULL && end <= *certificate_size) + memcpy( certificate, &tmp[start], end); + else { + *certificate_size = end; + result = GNUTLS_E_SHORT_MEMORY_BUFFER; + goto cleanup; + } + + *certificate_size = end; + + result = 0; + + } else { + result = GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE; + } + + cleanup: + if (c2) asn1_delete_structure(&c2); + gnutls_free(tmp); + return result; +} + + +/** + * gnutls_pkcs7_get_certificate_count - This function returns the number of certificates in a PKCS7 certificate set + * @pkcs7_struct: should contain a gnutls_pkcs7 structure + * + * This function will return the number of certifcates in the PKCS7 or + * RFC2630 certificate set. + * + * Returns a negative value on failure. + * + **/ +int gnutls_pkcs7_get_certificate_count(gnutls_pkcs7 pkcs7) +{ + ASN1_TYPE c2; + int result, len, count; + char oid[64]; + opaque *tmp = NULL; + int tmp_size; + + len = sizeof(oid) - 1; + + /* root2 is used as a temp storage area + */ + result = asn1_read_value(pkcs7->pkcs7, "pkcs7.contentType", oid, &len); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ( strcmp( oid, "1 2 840 113549 1 7 2") != 0) { + gnutls_assert(); + return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; + } + + tmp_size = 256; /* some initial size */ + tmp = gnutls_malloc(tmp_size); + if (tmp==NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + + result = asn1_read_value(pkcs7->pkcs7, "pkcs7.content", tmp, &tmp_size); + /* FIXME: a hard coded value + */ + if (result==ASN1_MEM_ERROR && tmp_size > 0 && tmp_size < 50*1024) { + tmp = gnutls_realloc_fast( tmp, tmp_size); + if (tmp==NULL) { + gnutls_assert(); + return GNUTLS_E_MEMORY_ERROR; + } + result = asn1_read_value(pkcs7->pkcs7, "pkcs7.content", tmp, &tmp_size); + } + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + /* tmp, tmp_size hold the data and the size of the CertificateSet structure + * actually the ANY stuff. + */ + + /* Step 1. In case of a signed structure count the certificate set. + */ + if ((result=_gnutls_asn1_create_element + (_gnutls_get_pkix(), "PKIX1.SignedData", &c2, "c2")) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = asn1_der_decoding(&c2, tmp, tmp_size, NULL); + if (result != ASN1_SUCCESS) { + /* couldn't decode DER */ + + gnutls_assert(); + asn1_delete_structure(&c2); + result = _gnutls_asn2err(result); + goto cleanup; + } + + gnutls_free(tmp); + + /* Step 2. Count the CertificateSet */ + + result = asn1_number_of_elements( c2, "c2.certificates", &count); + + asn1_delete_structure(&c2); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return 0; /* no certificates */ + } + + return count; + + cleanup: + gnutls_free(tmp); + return result; +} + diff --git a/lib/x509/pkcs7.h b/lib/x509/pkcs7.h new file mode 100644 index 0000000000..68979defdc --- /dev/null +++ b/lib/x509/pkcs7.h @@ -0,0 +1,6 @@ + +typedef struct gnutls_pkcs7_int { + ASN1_TYPE pkcs7; +} gnutls_pkcs7_int; + +typedef struct gnutls_pkcs7_int *gnutls_pkcs7; diff --git a/lib/x509/x509.c b/lib/x509/x509.c index ee7d5e2390..498fa8c169 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -93,12 +93,12 @@ int gnutls_x509_certificate_import(gnutls_x509_certificate cert, const gnutls_da opaque *out; /* Try the first header */ - result = _gnutls_fbase64_decode(PEM_CERT_SEP, data->data, data->size, + result = _gnutls_fbase64_decode(PEM_X509_CERT, data->data, data->size, &out); if (result <= 0) { /* try for the second header */ - result = _gnutls_fbase64_decode(PEM_CERT_SEP2, data->data, data->size, + result = _gnutls_fbase64_decode(PEM_X509_CERT2, data->data, data->size, &out); if (result <= 0) { @@ -201,7 +201,8 @@ int gnutls_x509_certificate_import(gnutls_x509_certificate cert, const gnutls_da return 0; cleanup: - asn1_delete_structure(&cert->cert); + if (cert->cert) + asn1_delete_structure(&cert->cert); _gnutls_free_datum(&cert->signed_data); _gnutls_free_datum(&cert->signature); if (need_free) _gnutls_free_datum( &_data);