/* * Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation * * Author: Nikos Mavroyanopoulos * * This file is part of GNUTLS-EXTRA. * * GNUTLS-EXTRA 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 2 of the * License, or (at your option) any later version. * * GNUTLS-EXTRA 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 GNUTLS-EXTRA; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * */ /* This file includes all functions that were in the 0.5.x and 0.8.x * gnutls API. They are now implemented over the new certificate parsing * API. */ #include #include #include #include /* memset */ #include #include #include #include /*- * gnutls_x509_extract_dn - This function parses an RDN sequence * @idn: should contain a DER encoded RDN sequence * @rdn: a pointer to a structure to hold the name * * This function will return the name of the given RDN sequence. * The name will be returned as a gnutls_x509_dn structure. * Returns a negative error code in case of an error. * -*/ int gnutls_x509_extract_dn (const gnutls_datum_t * idn, gnutls_x509_dn * rdn) { ASN1_TYPE dn = ASN1_TYPE_EMPTY; int result; size_t len; if ((result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.Name", &dn)) != ASN1_SUCCESS) { return _gnutls_asn2err (result); } result = asn1_der_decoding (&dn, idn->data, idn->size, NULL); if (result != ASN1_SUCCESS) { /* couldn't decode DER */ asn1_delete_structure (&dn); return _gnutls_asn2err (result); } memset (rdn, 0, sizeof (gnutls_x509_dn)); len = sizeof (rdn->country); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, rdn->country, &len); len = sizeof (rdn->organization); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, rdn->organization, &len); len = sizeof (rdn->organizational_unit_name); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, rdn->organizational_unit_name, &len); len = sizeof (rdn->common_name); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_X520_COMMON_NAME, 0, 0, rdn->common_name, &len); len = sizeof (rdn->locality_name); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, rdn->locality_name, &len); len = sizeof (rdn->state_or_province_name); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, rdn->state_or_province_name, &len); len = sizeof (rdn->email); _gnutls_x509_parse_dn_oid (dn, "", GNUTLS_OID_PKCS9_EMAIL, 0, 0, rdn->email, &len); asn1_delete_structure (&dn); return 0; } /*- * gnutls_x509_extract_certificate_dn - This function returns the certificate's distinguished name * @cert: should contain an X.509 DER encoded certificate * @ret: a pointer to a structure to hold the peer's name * * This function will return the name of the certificate holder. The name is gnutls_x509_dn structure and * is a obtained by the peer's certificate. If the certificate send by the * peer is invalid, or in any other failure this function returns error. * Returns a negative error code in case of an error. * -*/ int gnutls_x509_extract_certificate_dn (const gnutls_datum_t * cert, gnutls_x509_dn * ret) { gnutls_x509_crt_t xcert; int result; size_t len; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } len = sizeof (ret->country); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, ret->country, &len); len = sizeof (ret->organization); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, ret->organization, &len); len = sizeof (ret->organizational_unit_name); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, ret->organizational_unit_name, &len); len = sizeof (ret->common_name); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, ret->common_name, &len); len = sizeof (ret->locality_name); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, ret->locality_name, &len); len = sizeof (ret->state_or_province_name); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, ret->state_or_province_name, &len); len = sizeof (ret->email); gnutls_x509_crt_get_dn_by_oid (xcert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, ret->email, &len); gnutls_x509_crt_deinit (xcert); return 0; } /*- * gnutls_x509_extract_certificate_issuer_dn - This function returns the certificate's issuer distinguished name * @cert: should contain an X.509 DER encoded certificate * @ret: a pointer to a structure to hold the issuer's name * * This function will return the name of the issuer stated in the certificate. The name is a gnutls_x509_dn structure and * is a obtained by the peer's certificate. If the certificate send by the * peer is invalid, or in any other failure this function returns error. * Returns a negative error code in case of an error. * -*/ int gnutls_x509_extract_certificate_issuer_dn (const gnutls_datum_t * cert, gnutls_x509_dn * ret) { gnutls_x509_crt_t xcert; int result; size_t len; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } len = sizeof (ret->country); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, ret->country, &len); len = sizeof (ret->organization); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, ret->organization, &len); len = sizeof (ret->organizational_unit_name); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, ret->organizational_unit_name, &len); len = sizeof (ret->common_name); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, ret->common_name, &len); len = sizeof (ret->locality_name); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, ret->locality_name, &len); len = sizeof (ret->state_or_province_name); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, ret->state_or_province_name, &len); len = sizeof (ret->email); gnutls_x509_crt_get_issuer_dn_by_oid (xcert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, ret->email, &len); gnutls_x509_crt_deinit (xcert); return 0; } /*- * gnutls_x509_extract_certificate_subject_alt_name - This function returns the certificate's alternative name, if any * @cert: should contain an X.509 DER encoded certificate * @seq: specifies the sequence number of the alt name (0 for the first one, 1 for the second etc.) * @ret: is the place where the alternative name will be copied to * @ret_size: holds the size of ret. * * This function will return the alternative names, contained in the * given certificate. * * This is specified in X509v3 Certificate Extensions. * GNUTLS will return the Alternative name, or a negative * error code. * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if ret_size is not enough to hold the alternative * name, or the type of alternative name if everything was ok. The type is * one of the enumerated GNUTLS_X509_SUBJECT_ALT_NAME. * * If the certificate does not have an Alternative name with the specified * sequence number then returns GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; * -*/ int gnutls_x509_extract_certificate_subject_alt_name (const gnutls_datum_t * cert, int seq, char *ret, int *ret_size) { gnutls_x509_crt_t xcert; int result; size_t size = *ret_size; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } result = gnutls_x509_crt_get_subject_alt_name (xcert, seq, ret, &size, NULL); *ret_size = size; gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_extract_certificate_ca_status - This function returns the certificate CA status * @cert: should contain an X.509 DER encoded certificate * * This function will return certificates CA status, by reading the * basicConstraints X.509 extension. If the certificate is a CA a positive * value will be returned, or zero if the certificate does not have * CA flag set. * * A negative value may be returned in case of parsing error. * If the certificate does not contain the basicConstraints extension * GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will be returned. * -*/ int gnutls_x509_extract_certificate_ca_status (const gnutls_datum_t * cert) { gnutls_x509_crt_t xcert; int result; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } result = gnutls_x509_crt_get_ca_status (xcert, NULL); gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_extract_certificate_activation_time - This function returns the peer's certificate activation time * @cert: should contain an X.509 DER encoded certificate * * This function will return the certificate's activation time in UNIX time * (ie seconds since 00:00:00 UTC January 1, 1970). * Returns a (time_t) -1 in case of an error. * -*/ time_t gnutls_x509_extract_certificate_activation_time (const gnutls_datum_t * cert) { gnutls_x509_crt_t xcert; time_t result; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } result = gnutls_x509_crt_get_activation_time (xcert); gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_extract_certificate_expiration_time - This function returns the certificate's expiration time * @cert: should contain an X.509 DER encoded certificate * * This function will return the certificate's expiration time in UNIX time * (ie seconds since 00:00:00 UTC January 1, 1970). * Returns a (time_t) -1 in case of an error. * -*/ time_t gnutls_x509_extract_certificate_expiration_time (const gnutls_datum_t * cert) { gnutls_x509_crt_t xcert; time_t result; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } result = gnutls_x509_crt_get_expiration_time (xcert); gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_extract_certificate_version - This function returns the certificate's version * @cert: is an X.509 DER encoded certificate * * This function will return the X.509 certificate's version (1, 2, 3). This is obtained by the X509 Certificate * Version field. Returns a negative value in case of an error. * -*/ int gnutls_x509_extract_certificate_version (const gnutls_datum_t * cert) { gnutls_x509_crt_t xcert; int result; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } result = gnutls_x509_crt_get_version (xcert); gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_extract_certificate_serial - This function returns the certificate's serial number * @cert: is an X.509 DER encoded certificate * @result: The place where the serial number will be copied * @result_size: Holds the size of the result field. * * This function will return the X.509 certificate's serial number. * This is obtained by the X509 Certificate serialNumber * field. Serial is not always a 32 or 64bit number. Some CAs use * large serial numbers, thus it may be wise to handle it as something * opaque. * Returns a negative value in case of an error. * -*/ int gnutls_x509_extract_certificate_serial (const gnutls_datum_t * cert, char *result, int *result_size) { gnutls_x509_crt_t xcert; size_t size = *result_size; int ret; ret = gnutls_x509_crt_init (&xcert); if (ret < 0) return ret; ret = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_x509_crt_deinit (xcert); return ret; } ret = gnutls_x509_crt_get_serial (xcert, result, &size); *result_size = size; gnutls_x509_crt_deinit (xcert); return ret; } /*- * gnutls_x509_extract_certificate_pk_algorithm - This function returns the certificate's PublicKey algorithm * @cert: is a DER encoded X.509 certificate * @bits: if bits is non null it will hold the size of the parameters' in bits * * This function will return the public key algorithm of an X.509 * certificate. * * If bits is non null, it should have enough size to hold the parameters * size in bits. For RSA the bits returned is the modulus. * For DSA the bits returned are of the public * exponent. * * Returns a member of the gnutls_pk_algorithm_t enumeration on success, * or a negative value on error. * -*/ int gnutls_x509_extract_certificate_pk_algorithm (const gnutls_datum_t * cert, int *bits) { gnutls_x509_crt_t xcert; int result; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } result = gnutls_x509_crt_get_pk_algorithm (xcert, bits); gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_extract_certificate_dn_string - This function returns the certificate's distinguished name * @cert: should contain an X.509 DER encoded certificate * @buf: a pointer to a structure to hold the peer's name * @sizeof_buf: holds the size of 'buf' * @issuer: if non zero, then extract the name of the issuer, instead of the holder * * This function will copy the name of the certificate holder in the provided buffer. The name * will be in the form "C=xxxx,O=yyyy,CN=zzzz" as described in RFC2253. * * Returns GNUTLS_E_SHORT_MEMORY_BUFFER if the provided buffer is not long enough, * and 0 on success. * -*/ int gnutls_x509_extract_certificate_dn_string (char *buf, unsigned int sizeof_buf, const gnutls_datum_t * cert, int issuer) { gnutls_x509_crt_t xcert; int result; result = gnutls_x509_crt_init (&xcert); if (result < 0) return result; result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_x509_crt_deinit (xcert); return result; } if (!issuer) result = gnutls_x509_crt_get_dn (xcert, buf, &sizeof_buf); else result = gnutls_x509_crt_get_issuer_dn (xcert, buf, &sizeof_buf); gnutls_x509_crt_deinit (xcert); return result; } /*- * gnutls_x509_verify_certificate - This function verifies given certificate list * @cert_list: is the certificate list to be verified * @cert_list_length: holds the number of certificate in cert_list * @CA_list: is the CA list which will be used in verification * @CA_list_length: holds the number of CA certificate in CA_list * @CRL_list: not used * @CRL_list_length: not used * * This function will try to verify the given certificate list and return its status (TRUSTED, EXPIRED etc.). * The return value (status) should be one or more of the gnutls_certificate_status_t * enumerated elements bitwise or'd. Note that expiration and activation dates are not checked * by this function, you should check them using the appropriate functions. * * This function understands the basicConstraints (2.5.29.19) PKIX extension. * This means that only a certificate authority can sign a certificate. * * However you must also check the peer's name in order to check if the verified certificate belongs to the * actual peer. * * The return value (status) should be one or more of the gnutls_certificate_status_t * enumerated elements bitwise or'd. * * GNUTLS_CERT_INVALID: the peer's certificate is not valid. * * GNUTLS_CERT_REVOKED: the certificate has been revoked. * * A negative error code is returned in case of an error. * GNUTLS_E_NO_CERTIFICATE_FOUND is returned to indicate that * no certificate was sent by the peer. * * -*/ int gnutls_x509_verify_certificate (const gnutls_datum_t * cert_list, int cert_list_length, const gnutls_datum_t * CA_list, int CA_list_length, const gnutls_datum_t * CRL_list, int CRL_list_length) { unsigned int verify; gnutls_x509_crt_t *peer_certificate_list = NULL; gnutls_x509_crt_t *ca_certificate_list = NULL; gnutls_x509_crl_t *crl_list = NULL; int peer_certificate_list_size = 0, i, x, ret; int ca_certificate_list_size = 0, crl_list_size = 0; if (cert_list == NULL || cert_list_length == 0) return GNUTLS_E_NO_CERTIFICATE_FOUND; /* generate a list of gnutls_certs based on the auth info * raw certs. */ peer_certificate_list_size = cert_list_length; peer_certificate_list = gnutls_calloc (1, peer_certificate_list_size * sizeof (gnutls_x509_crt_t)); if (peer_certificate_list == NULL) { gnutls_assert (); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } ca_certificate_list_size = CA_list_length; ca_certificate_list = gnutls_calloc (1, ca_certificate_list_size * sizeof (gnutls_x509_crt_t)); if (ca_certificate_list == NULL) { gnutls_assert (); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } /* allocate memory for CRL */ crl_list_size = CRL_list_length; crl_list = gnutls_calloc (1, crl_list_size * sizeof (gnutls_x509_crl_t)); if (crl_list == NULL) { gnutls_assert (); ret = GNUTLS_E_MEMORY_ERROR; goto cleanup; } /* convert certA_list to gnutls_cert* list */ for (i = 0; i < peer_certificate_list_size; i++) { ret = gnutls_x509_crt_init (&peer_certificate_list[i]); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crt_import (peer_certificate_list[i], &cert_list[i], GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_assert (); goto cleanup; } } /* convert CA_list to gnutls_x509_cert* list */ for (i = 0; i < ca_certificate_list_size; i++) { ret = gnutls_x509_crt_init (&ca_certificate_list[i]); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crt_import (ca_certificate_list[i], &CA_list[i], GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_assert (); goto cleanup; } } #ifdef ENABLE_PKI /* convert CRL_list to gnutls_x509_crl* list */ for (i = 0; i < crl_list_size; i++) { ret = gnutls_x509_crl_init (&crl_list[i]); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = gnutls_x509_crl_import (crl_list[i], &CRL_list[i], GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_assert (); goto cleanup; } } #endif /* Verify certificate */ ret = gnutls_x509_crt_list_verify (peer_certificate_list, peer_certificate_list_size, ca_certificate_list, ca_certificate_list_size, crl_list, crl_list_size, 0, &verify); if (ret < 0) { gnutls_assert (); goto cleanup; } ret = verify; cleanup: if (peer_certificate_list != NULL) for (x = 0; x < peer_certificate_list_size; x++) { if (peer_certificate_list[x] != NULL) gnutls_x509_crt_deinit (peer_certificate_list[x]); } if (ca_certificate_list != NULL) for (x = 0; x < ca_certificate_list_size; x++) { if (ca_certificate_list[x] != NULL) gnutls_x509_crt_deinit (ca_certificate_list[x]); } #ifdef ENABLE_PKI if (crl_list != NULL) for (x = 0; x < crl_list_size; x++) { if (crl_list[x] != NULL) gnutls_x509_crl_deinit (crl_list[x]); } gnutls_free (crl_list); #endif gnutls_free (ca_certificate_list); gnutls_free (peer_certificate_list); return ret; } /*- * gnutls_x509_extract_key_pk_algorithm - This function returns the keys's PublicKey algorithm * @cert: is a DER encoded private key * * This function will return the public key algorithm of a DER encoded private * key. * * Returns a member of the gnutls_pk_algorithm_t enumeration on success, * or GNUTLS_E_UNKNOWN_PK_ALGORITHM on error. * -*/ int gnutls_x509_extract_key_pk_algorithm (const gnutls_datum_t * key) { gnutls_x509_privkey_t pkey; int ret, pk; ret = gnutls_x509_privkey_init (&pkey); if (ret < 0) { gnutls_assert (); return ret; } ret = gnutls_x509_privkey_import (pkey, key, GNUTLS_X509_FMT_DER); if (ret < 0) { gnutls_assert (); return ret; } pk = gnutls_x509_privkey_get_pk_algorithm (pkey); gnutls_x509_privkey_deinit (pkey); return pk; } #ifdef ENABLE_PKI /*- * gnutls_x509_pkcs7_extract_certificate - This function returns a certificate in a PKCS7 certificate set * @pkcs7_struct: should contain a PKCS7 DER formatted structure * @indx: contains the index of the certificate to extract * @certificate: the contents of the certificate will be copied there * @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_x509_pkcs7_extract_certificate (const gnutls_datum_t * pkcs7_struct, int indx, char *certificate, int *certificate_size) { gnutls_pkcs7_t pkcs7; int result; size_t size = *certificate_size; result = gnutls_pkcs7_init (&pkcs7); if (result < 0) return result; result = gnutls_pkcs7_import (pkcs7, pkcs7_struct, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_pkcs7_deinit (pkcs7); return result; } result = gnutls_pkcs7_get_crt_raw (pkcs7, indx, certificate, &size); *certificate_size = size; gnutls_pkcs7_deinit (pkcs7); return result; } /*- * gnutls_x509_pkcs7_extract_certificate_count - This function returns the number of certificates in a PKCS7 certificate set * @pkcs7_struct: should contain a PKCS7 DER formatted structure * * This function will return the number of certifcates in the PKCS7 or * RFC2630 certificate set. * * Returns a negative value on failure. * -*/ int gnutls_x509_pkcs7_extract_certificate_count (const gnutls_datum_t * pkcs7_struct) { gnutls_pkcs7_t pkcs7; int result; result = gnutls_pkcs7_init (&pkcs7); if (result < 0) return result; result = gnutls_pkcs7_import (pkcs7, pkcs7_struct, GNUTLS_X509_FMT_DER); if (result < 0) { gnutls_pkcs7_deinit (pkcs7); return result; } result = gnutls_pkcs7_get_crt_count (pkcs7); gnutls_pkcs7_deinit (pkcs7); return result; } #endif