/* * Copyright (C) 2003, 2004, 2005, 2007 Free Software Foundation * * Author: Nikos Mavrogiannopoulos * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA * */ /* Functions that relate to the X.509 extension parsing. */ #include #include #include #include #include #include #include #include #include /* This function will attempt to return the requested extension found in * the given X509v3 certificate. The return value is allocated and stored into * ret. * * Critical will be either 0 or 1. * * If the extension does not exist, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will * be returned. */ int _gnutls_x509_crt_get_extension (gnutls_x509_crt_t cert, const char *extension_id, int indx, gnutls_datum_t * ret, unsigned int *_critical) { int k, result, len; char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; char str[1024]; char str_critical[10]; int critical = 0; char extnID[128]; gnutls_datum_t value; int indx_counter = 0; ret->data = NULL; ret->size = 0; k = 0; do { k++; snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", k); len = sizeof (str) - 1; result = asn1_read_value (cert->cert, name, str, &len); /* move to next */ if (result == ASN1_ELEMENT_NOT_FOUND) { break; } do { mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".extnID"); len = sizeof (extnID) - 1; result = asn1_read_value (cert->cert, name2, extnID, &len); if (result == ASN1_ELEMENT_NOT_FOUND) { gnutls_assert (); break; } else if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } /* Handle Extension */ if (strcmp (extnID, extension_id) == 0 && indx == indx_counter++) { /* extension was found */ /* read the critical status. */ mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".critical"); len = sizeof (str_critical); result = asn1_read_value (cert->cert, name2, str_critical, &len); if (result == ASN1_ELEMENT_NOT_FOUND) { gnutls_assert (); break; } else if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } if (str_critical[0] == 'T') critical = 1; else critical = 0; /* read the value. */ mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".extnValue"); result = _gnutls_x509_read_value (cert->cert, name2, &value, 0); if (result < 0) { gnutls_assert (); return result; } ret->data = value.data; ret->size = value.size; if (_critical) *_critical = critical; return 0; } } while (0); } while (1); if (result == ASN1_ELEMENT_NOT_FOUND) { return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } else { gnutls_assert (); return mhd_gtls_asn2err (result); } } /* This function will attempt to return the requested extension OID found in * the given X509v3 certificate. * * If you have passed the last extension, GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE will * be returned. */ int _gnutls_x509_crt_get_extension_oid (gnutls_x509_crt_t cert, int indx, void *oid, size_t * sizeof_oid) { int k, result, len; char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; char str[1024]; char extnID[128]; int indx_counter = 0; k = 0; do { k++; snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", k); len = sizeof (str) - 1; result = asn1_read_value (cert->cert, name, str, &len); /* move to next */ if (result == ASN1_ELEMENT_NOT_FOUND) { break; } do { mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".extnID"); len = sizeof (extnID) - 1; result = asn1_read_value (cert->cert, name2, extnID, &len); if (result == ASN1_ELEMENT_NOT_FOUND) { gnutls_assert (); break; } else if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } /* Handle Extension */ if (indx == indx_counter++) { len = strlen (extnID) + 1; if (*sizeof_oid < (unsigned) len) { *sizeof_oid = len; gnutls_assert (); return GNUTLS_E_SHORT_MEMORY_BUFFER; } memcpy (oid, extnID, len); *sizeof_oid = len - 1; return 0; } } while (0); } while (1); if (result == ASN1_ELEMENT_NOT_FOUND) { return GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE; } else { gnutls_assert (); return mhd_gtls_asn2err (result); } } /* This function will attempt to set the requested extension in * the given X509v3 certificate. * * Critical will be either 0 or 1. */ static int set_extension (ASN1_TYPE asn, const char *extension_id, const gnutls_datum_t * ext_data, unsigned int critical) { int result; const char *str; /* Add a new extension in the list. */ result = asn1_write_value (asn, "tbsCertificate.extensions", "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_write_value (asn, "tbsCertificate.extensions.?LAST.extnID", extension_id, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } if (critical == 0) str = "FALSE"; else str = "TRUE"; result = asn1_write_value (asn, "tbsCertificate.extensions.?LAST.critical", str, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = _gnutls_x509_write_value (asn, "tbsCertificate.extensions.?LAST.extnValue", ext_data, 0); if (result < 0) { gnutls_assert (); return result; } return 0; } /* Overwrite the given extension (using the index) * index here starts from one. */ static int overwrite_extension (ASN1_TYPE asn, unsigned int indx, const gnutls_datum_t * ext_data, unsigned int critical) { char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; const char *str; int result; snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", indx); if (critical == 0) str = "FALSE"; else str = "TRUE"; mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".critical"); result = asn1_write_value (asn, name2, str, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".extnValue"); result = _gnutls_x509_write_value (asn, name2, ext_data, 0); if (result < 0) { gnutls_assert (); return result; } return 0; } /* This function will attempt to overwrite the requested extension with * the given one. * * Critical will be either 0 or 1. */ int _gnutls_x509_crt_set_extension (gnutls_x509_crt_t cert, const char *ext_id, const gnutls_datum_t * ext_data, unsigned int critical) { int result; int k, len; char name[MAX_NAME_SIZE], name2[MAX_NAME_SIZE]; char extnID[128]; /* Find the index of the given extension. */ k = 0; do { k++; snprintf (name, sizeof (name), "tbsCertificate.extensions.?%u", k); len = sizeof (extnID) - 1; result = asn1_read_value (cert->cert, name, extnID, &len); /* move to next */ if (result == ASN1_ELEMENT_NOT_FOUND) { break; } do { mhd_gtls_str_cpy (name2, sizeof (name2), name); mhd_gtls_str_cat (name2, sizeof (name2), ".extnID"); len = sizeof (extnID) - 1; result = asn1_read_value (cert->cert, name2, extnID, &len); if (result == ASN1_ELEMENT_NOT_FOUND) { gnutls_assert (); break; } else if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } /* Handle Extension */ if (strcmp (extnID, ext_id) == 0) { /* extension was found */ return overwrite_extension (cert->cert, k, ext_data, critical); } } while (0); } while (1); if (result == ASN1_ELEMENT_NOT_FOUND) { return set_extension (cert->cert, ext_id, ext_data, critical); } else { gnutls_assert (); return mhd_gtls_asn2err (result); } return 0; } /* Here we only extract the KeyUsage field, from the DER encoded * extension. */ int _gnutls_x509_ext_extract_keyUsage (uint16_t * keyUsage, opaque * extnValue, int extnValueLen) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int len, result; uint8_t str[2]; str[0] = str[1] = 0; *keyUsage = 0; if ((result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.KeyUsage", &ext)) != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_der_decoding (&ext, extnValue, extnValueLen, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } len = sizeof (str); result = asn1_read_value (ext, "", str, &len); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return 0; } *keyUsage = str[0] | (str[1] << 8); asn1_delete_structure (&ext); return 0; } /* extract the basicConstraints from the DER encoded extension */ int _gnutls_x509_ext_extract_basicConstraints (int *CA, int *pathLenConstraint, opaque * extnValue, int extnValueLen) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; char str[128]; int len, result; if ((result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.BasicConstraints", &ext)) != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_der_decoding (&ext, extnValue, extnValueLen, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } if (pathLenConstraint) { result = _gnutls_x509_read_uint (ext, "pathLenConstraint", pathLenConstraint); if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) *pathLenConstraint = -1; else if (result != GNUTLS_E_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } } /* the default value of cA is false. */ len = sizeof (str) - 1; result = asn1_read_value (ext, "cA", str, &len); if (result == ASN1_SUCCESS && strcmp (str, "TRUE") == 0) *CA = 1; else *CA = 0; asn1_delete_structure (&ext); return 0; } /* generate the basicConstraints in a DER encoded extension * Use 0 or 1 (TRUE) for CA. * Use negative values for pathLenConstraint to indicate that the field * should not be present, >= 0 to indicate set values. */ int _gnutls_x509_ext_gen_basicConstraints (int CA, int pathLenConstraint, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; const char *str; int result; if (CA == 0) str = "FALSE"; else str = "TRUE"; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.BasicConstraints", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_write_value (ext, "cA", str, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } if (pathLenConstraint < 0) { result = asn1_write_value (ext, "pathLenConstraint", NULL, 0); if (result < 0) result = mhd_gtls_asn2err (result); } else result = _gnutls_x509_write_uint32 (ext, "pathLenConstraint", pathLenConstraint); if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return result; } result = _gnutls_x509_der_encode (ext, "", der_ext, 0); asn1_delete_structure (&ext); if (result < 0) { gnutls_assert (); return result; } return 0; } /* generate the keyUsage in a DER encoded extension * Use an ORed SEQUENCE of GNUTLS_KEY_* for usage. */ int _gnutls_x509_ext_gen_keyUsage (uint16_t usage, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int result; uint8_t str[2]; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.KeyUsage", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } str[0] = usage & 0xff; str[1] = usage >> 8; result = asn1_write_value (ext, "", str, 9); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } result = _gnutls_x509_der_encode (ext, "", der_ext, 0); asn1_delete_structure (&ext); if (result < 0) { gnutls_assert (); return result; } return 0; } static int write_new_general_name (ASN1_TYPE ext, const char *ext_name, gnutls_x509_subject_alt_name_t type, const char *data_string) { const char *str; int result; char name[128]; result = asn1_write_value (ext, ext_name, "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } switch (type) { case GNUTLS_SAN_DNSNAME: str = "dNSName"; break; case GNUTLS_SAN_RFC822NAME: str = "rfc822Name"; break; case GNUTLS_SAN_URI: str = "uniformResourceIdentifier"; break; case GNUTLS_SAN_IPADDRESS: str = "iPAddress"; break; default: gnutls_assert (); return GNUTLS_E_INTERNAL_ERROR; } if (ext_name[0] == 0) { /* no dot */ mhd_gtls_str_cpy (name, sizeof (name), "?LAST"); } else { mhd_gtls_str_cpy (name, sizeof (name), ext_name); mhd_gtls_str_cat (name, sizeof (name), ".?LAST"); } result = asn1_write_value (ext, name, str, 1); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } mhd_gtls_str_cat (name, sizeof (name), "."); mhd_gtls_str_cat (name, sizeof (name), str); result = asn1_write_value (ext, name, data_string, strlen (data_string)); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } return 0; } /* Convert the given name to GeneralNames in a DER encoded extension. * This is the same as subject alternative name. */ int _gnutls_x509_ext_gen_subject_alt_name (gnutls_x509_subject_alt_name_t type, const char *data_string, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int result; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.GeneralNames", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = write_new_general_name (ext, "", type, data_string); if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return result; } result = _gnutls_x509_der_encode (ext, "", der_ext, 0); asn1_delete_structure (&ext); if (result < 0) { gnutls_assert (); return result; } return 0; } /* generate the SubjectKeyID in a DER encoded extension */ int _gnutls_x509_ext_gen_key_id (const void *id, size_t id_size, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int result; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.SubjectKeyIdentifier", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_write_value (ext, "", id, id_size); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } result = _gnutls_x509_der_encode (ext, "", der_ext, 0); asn1_delete_structure (&ext); if (result < 0) { gnutls_assert (); return result; } return 0; } /* generate the AuthorityKeyID in a DER encoded extension */ int _gnutls_x509_ext_gen_auth_key_id (const void *id, size_t id_size, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int result; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.AuthorityKeyIdentifier", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_write_value (ext, "keyIdentifier", id, id_size); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } asn1_write_value (ext, "authorityCertIssuer", NULL, 0); asn1_write_value (ext, "authorityCertSerialNumber", NULL, 0); result = _gnutls_x509_der_encode (ext, "", der_ext, 0); asn1_delete_structure (&ext); if (result < 0) { gnutls_assert (); return result; } return 0; } /* Creates and encodes the CRL Distribution points. data_string should be a name * and type holds the type of the name. * reason_flags should be an or'ed sequence of GNUTLS_CRL_REASON_*. * */ int _gnutls_x509_ext_gen_crl_dist_points (gnutls_x509_subject_alt_name_t type, const void *data_string, unsigned int reason_flags, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; gnutls_datum_t gnames = { NULL, 0 }; int result; uint8_t reasons[2]; reasons[0] = reason_flags & 0xff; reasons[1] = reason_flags >> 8; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.CRLDistributionPoints", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); result = mhd_gtls_asn2err (result); goto cleanup; } result = asn1_write_value (ext, "", "NEW", 1); if (result != ASN1_SUCCESS) { gnutls_assert (); result = mhd_gtls_asn2err (result); goto cleanup; } if (reason_flags) { result = asn1_write_value (ext, "?LAST.reasons", reasons, 9); if (result != ASN1_SUCCESS) { gnutls_assert (); result = mhd_gtls_asn2err (result); goto cleanup; } } else { result = asn1_write_value (ext, "?LAST.reasons", NULL, 0); if (result != ASN1_SUCCESS) { gnutls_assert (); result = mhd_gtls_asn2err (result); goto cleanup; } } result = asn1_write_value (ext, "?LAST.cRLIssuer", NULL, 0); if (result != ASN1_SUCCESS) { gnutls_assert (); result = mhd_gtls_asn2err (result); goto cleanup; } /* When used as type CHOICE. */ result = asn1_write_value (ext, "?LAST.distributionPoint", "fullName", 1); if (result != ASN1_SUCCESS) { gnutls_assert (); result = mhd_gtls_asn2err (result); goto cleanup; } #if 0 /* only needed in old code (where defined as SEQUENCE OF) */ asn1_write_value (ext, "?LAST.distributionPoint.nameRelativeToCRLIssuer", NULL, 0); #endif result = write_new_general_name (ext, "?LAST.distributionPoint.fullName", type, data_string); if (result < 0) { gnutls_assert (); goto cleanup; } result = _gnutls_x509_der_encode (ext, "", der_ext, 0); if (result < 0) { gnutls_assert (); goto cleanup; } result = 0; cleanup: _gnutls_free_datum (&gnames); asn1_delete_structure (&ext); return result; } /* extract the proxyCertInfo from the DER encoded extension */ int _gnutls_x509_ext_extract_proxyCertInfo (int *pathLenConstraint, char **policyLanguage, char **policy, size_t * sizeof_policy, opaque * extnValue, int extnValueLen) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int result; gnutls_datum_t value; if ((result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.ProxyCertInfo", &ext)) != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } result = asn1_der_decoding (&ext, extnValue, extnValueLen, NULL); if (result != ASN1_SUCCESS) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } if (pathLenConstraint) { result = _gnutls_x509_read_uint (ext, "pCPathLenConstraint", pathLenConstraint); if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) *pathLenConstraint = -1; else if (result != GNUTLS_E_SUCCESS) { asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } } result = _gnutls_x509_read_value (ext, "proxyPolicy.policyLanguage", &value, 0); if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return result; } if (policyLanguage) *policyLanguage = gnutls_strdup (value.data); result = _gnutls_x509_read_value (ext, "proxyPolicy.policy", &value, 0); if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) { if (policy) *policy = NULL; if (sizeof_policy) *sizeof_policy = 0; } else if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return result; } else { if (policy) *policy = value.data; if (sizeof_policy) *sizeof_policy = value.size; } asn1_delete_structure (&ext); return 0; } /* generate the proxyCertInfo in a DER encoded extension */ int _gnutls_x509_ext_gen_proxyCertInfo (int pathLenConstraint, const char *policyLanguage, const char *policy, size_t sizeof_policy, gnutls_datum_t * der_ext) { ASN1_TYPE ext = ASN1_TYPE_EMPTY; int result; result = asn1_create_element (_gnutls_get_pkix (), "PKIX1.ProxyCertInfo", &ext); if (result != ASN1_SUCCESS) { gnutls_assert (); return mhd_gtls_asn2err (result); } if (pathLenConstraint < 0) { result = asn1_write_value (ext, "pCPathLenConstraint", NULL, 0); if (result < 0) result = mhd_gtls_asn2err (result); } else result = _gnutls_x509_write_uint32 (ext, "pCPathLenConstraint", pathLenConstraint); if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return result; } result = asn1_write_value (ext, "proxyPolicy.policyLanguage", policyLanguage, 1); if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } result = asn1_write_value (ext, "proxyPolicy.policy", policy, sizeof_policy); if (result < 0) { gnutls_assert (); asn1_delete_structure (&ext); return mhd_gtls_asn2err (result); } result = _gnutls_x509_der_encode (ext, "", der_ext, 0); asn1_delete_structure (&ext); if (result < 0) { gnutls_assert (); return result; } return 0; }