PROP_CERTIFICATE_PEM,
PROP_PRIVATE_KEY,
PROP_PRIVATE_KEY_PEM,
- PROP_ISSUER
+ PROP_ISSUER,
+ PROP_PKCS11_URI,
+ PROP_PRIVATE_KEY_PKCS11_URI,
+ PROP_NOT_VALID_BEFORE,
+ PROP_NOT_VALID_AFTER,
+ PROP_SUBJECT_NAME,
+ PROP_ISSUER_NAME,
+ PROP_DNS_NAMES,
+ PROP_IP_ADDRESSES,
};
struct _GTlsCertificateGnutls
GTlsCertificate parent_instance;
gnutls_x509_crt_t cert;
- gnutls_x509_privkey_t key;
+ gnutls_privkey_t key;
+
+ gchar *pkcs11_uri;
+ gchar *private_key_pkcs11_uri;
GTlsCertificateGnutls *issuer;
GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
g_clear_pointer (&gnutls->cert, gnutls_x509_crt_deinit);
- g_clear_pointer (&gnutls->key, gnutls_x509_privkey_deinit);
+ g_clear_pointer (&gnutls->key, gnutls_privkey_deinit);
+
+ g_clear_pointer (&gnutls->pkcs11_uri, g_free);
+ g_clear_pointer (&gnutls->private_key_pkcs11_uri, g_free);
g_clear_object (&gnutls->issuer);
G_OBJECT_CLASS (g_tls_certificate_gnutls_parent_class)->finalize (object);
}
+static GPtrArray *
+get_subject_alt_names (GTlsCertificateGnutls *cert,
+ gnutls_x509_subject_alt_name_t type)
+{
+ GPtrArray *data = NULL;
+ guint8 *san = NULL;
+ size_t san_size;
+ guint san_type;
+ guint critical;
+ guint i;
+ guint status;
+
+ if (type == GNUTLS_SAN_IPADDRESS)
+ data = g_ptr_array_new_with_free_func (g_object_unref);
+ else
+ data = g_ptr_array_new_with_free_func ((GDestroyNotify)g_bytes_unref);
+
+ for (i = 0; ; i++)
+ {
+ san_size = 0;
+ san = NULL;
+ status = gnutls_x509_crt_get_subject_alt_name2 (cert->cert, i, san, &san_size, &san_type, &critical);
+ if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+ return data;
+ else if (san_type != (guint)type)
+ continue;
+
+ if (san_size == 0)
+ continue;
+
+ san = g_malloc (san_size);
+ status = gnutls_x509_crt_get_subject_alt_name2 (cert->cert, i, san, &san_size, &san_type, &critical);
+ if (status == (guint)type)
+ {
+ if (status == (guint)GNUTLS_SAN_IPADDRESS)
+ {
+ if (san_size == 4)
+ g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV4));
+ else if (san_size == 16)
+ g_ptr_array_add (data, g_inet_address_new_from_bytes (san, G_SOCKET_FAMILY_IPV6));
+ }
+ else
+ {
+ g_assert (status == (guint)GNUTLS_SAN_DNSNAME);
+ g_ptr_array_add (data, g_bytes_new (san, san_size));
+ }
+ }
+
+ g_free (san);
+ }
+
+ return data;
+}
+
+static void
+export_privkey (GTlsCertificateGnutls *gnutls,
+ gnutls_x509_crt_fmt_t format,
+ void **output_data,
+ size_t *output_size)
+{
+ gnutls_x509_privkey_t x509_privkey = NULL;
+ int status;
+
+ if (!gnutls->key)
+ goto err;
+
+ status = gnutls_privkey_export_x509 (gnutls->key, &x509_privkey);
+ if (status != 0)
+ goto err;
+
+ *output_size = 0;
+ status = gnutls_x509_privkey_export_pkcs8 (x509_privkey,
+ format,
+ NULL, GNUTLS_PKCS_PLAIN,
+ NULL, output_size);
+ if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
+ goto err;
+
+ *output_data = g_malloc (*output_size);
+ status = gnutls_x509_privkey_export_pkcs8 (x509_privkey,
+ format,
+ NULL, GNUTLS_PKCS_PLAIN,
+ *output_data, output_size);
+ if (status == 0)
+ {
+ gnutls_x509_privkey_deinit (x509_privkey);
+ return;
+ }
+
+ g_free (*output_data);
+
+err:
+ *output_data = NULL;
+ *output_size = 0;
+
+ if (x509_privkey)
+ gnutls_x509_privkey_deinit (x509_privkey);
+}
+
static void
g_tls_certificate_gnutls_get_property (GObject *object,
guint prop_id,
GParamSpec *pspec)
{
GTlsCertificateGnutls *gnutls = G_TLS_CERTIFICATE_GNUTLS (object);
- GByteArray *certificate;
- char *certificate_pem;
+ GByteArray *byte_array;
+ char *pem;
+ guint8 *der;
int status;
size_t size;
+ gnutls_x509_dn_t dn;
+ gnutls_datum_t data;
+ time_t time;
switch (prop_id)
{
GNUTLS_X509_FMT_DER,
NULL, &size);
if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
- certificate = NULL;
+ byte_array = NULL;
else
{
- certificate = g_byte_array_sized_new (size);
- certificate->len = size;
+ byte_array = g_byte_array_sized_new (size);
+ byte_array->len = size;
status = gnutls_x509_crt_export (gnutls->cert,
GNUTLS_X509_FMT_DER,
- certificate->data, &size);
+ byte_array->data, &size);
if (status != 0)
{
- g_byte_array_free (certificate, TRUE);
- certificate = NULL;
+ g_byte_array_free (byte_array, TRUE);
+ byte_array = NULL;
}
}
- g_value_take_boxed (value, certificate);
+ g_value_take_boxed (value, byte_array);
break;
case PROP_CERTIFICATE_PEM:
GNUTLS_X509_FMT_PEM,
NULL, &size);
if (status != GNUTLS_E_SHORT_MEMORY_BUFFER)
- certificate_pem = NULL;
+ pem = NULL;
else
{
- certificate_pem = g_malloc (size);
+ pem = g_malloc (size);
status = gnutls_x509_crt_export (gnutls->cert,
GNUTLS_X509_FMT_PEM,
- certificate_pem, &size);
+ pem, &size);
if (status != 0)
- {
- g_free (certificate_pem);
- certificate_pem = NULL;
- }
+ g_clear_pointer (&pem, g_free);
+ }
+ g_value_take_string (value, pem);
+ break;
+
+ case PROP_PRIVATE_KEY:
+ export_privkey (gnutls, GNUTLS_X509_FMT_DER, (void **)&der, &size);
+ if (size > 0 && size <= G_MAXUINT)
+ {
+ byte_array = g_byte_array_new_take (der, size);
+ g_value_take_boxed (value, byte_array);
}
- g_value_take_string (value, certificate_pem);
+ break;
+
+ case PROP_PRIVATE_KEY_PEM:
+ export_privkey (gnutls, GNUTLS_X509_FMT_PEM, (void **)&pem, &size);
+ if (size > 0)
+ g_value_take_string (value, pem);
break;
case PROP_ISSUER:
g_value_set_object (value, gnutls->issuer);
break;
+ case PROP_PKCS11_URI:
+ g_value_set_string (value, gnutls->pkcs11_uri);
+ break;
+
+ case PROP_PRIVATE_KEY_PKCS11_URI:
+ g_value_set_string (value, gnutls->private_key_pkcs11_uri);
+ break;
+
+ case PROP_NOT_VALID_BEFORE:
+ time = gnutls_x509_crt_get_activation_time (gnutls->cert);
+ if (time != (time_t)-1)
+ g_value_take_boxed (value, g_date_time_new_from_unix_utc (time));
+ break;
+
+ case PROP_NOT_VALID_AFTER:
+ time = gnutls_x509_crt_get_expiration_time (gnutls->cert);
+ if (time != (time_t)-1)
+ g_value_take_boxed (value, g_date_time_new_from_unix_utc (time));
+ break;
+
+ case PROP_SUBJECT_NAME:
+ status = gnutls_x509_crt_get_subject (gnutls->cert, &dn);
+ if (status != GNUTLS_E_SUCCESS)
+ return;
+
+ status = gnutls_x509_dn_get_str (dn, &data);
+ if (status != GNUTLS_E_SUCCESS)
+ return;
+
+ g_value_take_string (value, g_strndup ((gchar *)data.data, data.size));
+ gnutls_free (data.data);
+ break;
+
+ case PROP_ISSUER_NAME:
+ status = gnutls_x509_crt_get_issuer (gnutls->cert, &dn);
+ if (status != GNUTLS_E_SUCCESS)
+ return;
+
+ status = gnutls_x509_dn_get_str (dn, &data);
+ if (status != GNUTLS_E_SUCCESS)
+ return;
+
+ g_value_take_string (value, g_strndup ((gchar *)data.data, data.size));
+ gnutls_free (data.data);
+ break;
+
+ case PROP_DNS_NAMES:
+ g_value_take_boxed (value, get_subject_alt_names (gnutls, GNUTLS_SAN_DNSNAME));
+ break;
+
+ case PROP_IP_ADDRESSES:
+ g_value_take_boxed (value, get_subject_alt_names (gnutls, GNUTLS_SAN_IPADDRESS));
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
data.data = bytes->data;
data.size = bytes->len;
if (!gnutls->key)
- gnutls_x509_privkey_init (&gnutls->key);
- status = gnutls_x509_privkey_import (gnutls->key, &data,
- GNUTLS_X509_FMT_DER);
- if (status != 0)
- {
- int pkcs8_status =
- gnutls_x509_privkey_import_pkcs8 (gnutls->key, &data,
- GNUTLS_X509_FMT_DER, NULL,
- GNUTLS_PKCS_PLAIN);
- if (pkcs8_status == 0)
- status = 0;
- }
+ gnutls_privkey_init (&gnutls->key);
+ status = gnutls_privkey_import_x509_raw (gnutls->key, &data,
+ GNUTLS_X509_FMT_DER,
+ NULL, GNUTLS_PKCS_PLAIN);
if (status == 0)
gnutls->have_key = TRUE;
else if (!gnutls->construct_error)
data.data = (void *)string;
data.size = strlen (string);
if (!gnutls->key)
- gnutls_x509_privkey_init (&gnutls->key);
- status = gnutls_x509_privkey_import (gnutls->key, &data,
- GNUTLS_X509_FMT_PEM);
- if (status != 0)
- {
- int pkcs8_status =
- gnutls_x509_privkey_import_pkcs8 (gnutls->key, &data,
- GNUTLS_X509_FMT_PEM, NULL,
- GNUTLS_PKCS_PLAIN);
- if (pkcs8_status == 0)
- status = 0;
- }
+ gnutls_privkey_init (&gnutls->key);
+ status = gnutls_privkey_import_x509_raw (gnutls->key, &data,
+ GNUTLS_X509_FMT_PEM,
+ NULL, GNUTLS_PKCS_PLAIN);
if (status == 0)
gnutls->have_key = TRUE;
else if (!gnutls->construct_error)
gnutls->issuer = g_value_dup_object (value);
break;
+ case PROP_PKCS11_URI:
+ string = g_value_get_string (value);
+ if (!string)
+ break;
+ g_return_if_fail (gnutls->have_cert == FALSE);
+ g_return_if_fail (!gnutls->pkcs11_uri);
+
+ gnutls->pkcs11_uri = g_strdup (string);
+
+ status = gnutls_x509_crt_import_url (gnutls->cert, string, GNUTLS_PKCS11_OBJ_FLAG_CRT);
+ if (status == GNUTLS_E_SUCCESS)
+ {
+ gnutls->have_cert = TRUE;
+ }
+ else if (!gnutls->construct_error)
+ {
+ gnutls->construct_error =
+ g_error_new (G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+ _("Could not import PKCS #11 certificate URI: %s"),
+ gnutls_strerror (status));
+ }
+ break;
+
+ case PROP_PRIVATE_KEY_PKCS11_URI:
+ string = g_value_get_string (value);
+ if (!string)
+ break;
+ g_return_if_fail (gnutls->have_key == FALSE);
+ g_return_if_fail (!gnutls->private_key_pkcs11_uri);
+
+ gnutls->private_key_pkcs11_uri = g_strdup (string);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
guint num_certs, i;
gnutls_x509_crt_t *chain;
GTlsCertificateFlags gtls_flags;
+ GError *error = NULL;
cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
num_certs = 0;
g_free (chain);
if (identity)
- gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity);
+ {
+ gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity, &error);
+ if (error)
+ {
+ g_warning ("Error verifying TLS certificate: %s", error->message);
+ g_error_free (error);
+ }
+ }
return gtls_flags;
}
g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
+ g_object_class_override_property (gobject_class, PROP_PKCS11_URI, "pkcs11-uri");
+ g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PKCS11_URI, "private-key-pkcs11-uri");
+ g_object_class_override_property (gobject_class, PROP_NOT_VALID_BEFORE, "not-valid-before");
+ g_object_class_override_property (gobject_class, PROP_NOT_VALID_AFTER, "not-valid-after");
+ g_object_class_override_property (gobject_class, PROP_SUBJECT_NAME, "subject-name");
+ g_object_class_override_property (gobject_class, PROP_ISSUER_NAME, "issuer-name");
+ g_object_class_override_property (gobject_class, PROP_DNS_NAMES, "dns-names");
+ g_object_class_override_property (gobject_class, PROP_IP_ADDRESSES, "ip-addresses");
}
static void
}
gboolean
+g_tls_certificate_gnutls_is_pkcs11_backed (GTlsCertificateGnutls *gnutls)
+{
+ return gnutls->pkcs11_uri != NULL;
+}
+
+gboolean
g_tls_certificate_gnutls_has_key (GTlsCertificateGnutls *gnutls)
{
return gnutls->have_key;
int status;
g_return_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (gnutls));
- g_return_if_fail (pcert != NULL);
- g_return_if_fail (pcert_length != NULL);
- g_return_if_fail (pkey != NULL);
+ g_return_if_fail (pcert);
+ g_return_if_fail (pcert_length);
+ g_return_if_fail (pkey);
/* We will do this loop twice. It's probably more efficient than
* re-allocating memory.
*/
chain = gnutls;
- while (chain != NULL)
+ while (chain)
{
num_certs++;
chain = chain->issuer;
/* Now do the actual copy of the whole chain. */
chain = gnutls;
- while (chain != NULL)
+ while (chain)
{
gnutls_x509_crt_t cert;
gnutls_datum_t data;
chain = chain->issuer;
}
- if (gnutls->key != NULL)
- {
- gnutls_x509_privkey_t x509_privkey;
- gnutls_privkey_t privkey;
+ if (gnutls->key)
+ {
+ gnutls_x509_privkey_t x509_privkey;
- gnutls_x509_privkey_init (&x509_privkey);
- gnutls_x509_privkey_cpy (x509_privkey, gnutls->key);
+ gnutls_privkey_export_x509 (gnutls->key, &x509_privkey);
+ gnutls_privkey_import_x509 (*pkey, x509_privkey, GNUTLS_PRIVKEY_IMPORT_COPY);
+ gnutls_x509_privkey_deinit (x509_privkey);
+ }
+ else if (gnutls->private_key_pkcs11_uri || gnutls->pkcs11_uri)
+ {
+ int status;
- gnutls_privkey_init (&privkey);
- gnutls_privkey_import_x509 (privkey, x509_privkey, GNUTLS_PRIVKEY_IMPORT_COPY);
- *pkey = privkey;
- gnutls_x509_privkey_deinit (x509_privkey);
- }
- else
- {
- *pkey = NULL;
- }
+ status = gnutls_privkey_import_pkcs11_url (*pkey,
+ gnutls->private_key_pkcs11_uri ? gnutls->private_key_pkcs11_uri : gnutls->pkcs11_uri);
+ if (status != GNUTLS_E_SUCCESS)
+ {
+ gnutls_privkey_deinit (*pkey);
+ *pkey = NULL;
+ g_info ("Failed to copy PKCS #11 private key: %s", gnutls_strerror (status));
+ }
+ }
+ else
+ {
+ gnutls_privkey_deinit (*pkey);
+ *pkey = NULL;
+ }
}
void
unsigned int pcert_length,
gnutls_privkey_t pkey)
{
- if (pcert != NULL)
+ if (pcert)
{
for (unsigned int i = 0; i < pcert_length; i++)
gnutls_pcert_deinit (&pcert[i]);
g_free (pcert);
}
- if (pkey != NULL)
+ if (pkey)
gnutls_privkey_deinit (pkey);
}
return gtls_flags;
}
-static gboolean
-verify_identity_hostname (GTlsCertificateGnutls *gnutls,
- GSocketConnectable *identity)
+GTlsCertificateFlags
+g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
+ GSocketConnectable *identity,
+ GError **error)
{
+ GTlsCertificateFlags result = 0;
const char *hostname;
+ char *free_hostname = NULL;
if (G_IS_NETWORK_ADDRESS (identity))
hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
else if (G_IS_NETWORK_SERVICE (identity))
hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
- else
- return FALSE;
-
- return gnutls_x509_crt_check_hostname (gnutls->cert, hostname);
-}
-
-static gboolean
-verify_identity_ip (GTlsCertificateGnutls *gnutls,
- GSocketConnectable *identity)
-{
- GInetAddress *addr;
- int i, ret = 0;
- gsize addr_size;
- const guint8 *addr_bytes;
-
- if (G_IS_INET_SOCKET_ADDRESS (identity))
- addr = g_object_ref (g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity)));
- else {
- const char *hostname;
-
- if (G_IS_NETWORK_ADDRESS (identity))
- hostname = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
- else if (G_IS_NETWORK_SERVICE (identity))
- hostname = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
- else
- return FALSE;
-
- addr = g_inet_address_new_from_string (hostname);
- if (!addr)
- return FALSE;
- }
-
- addr_bytes = g_inet_address_to_bytes (addr);
- addr_size = g_inet_address_get_native_size (addr);
-
- for (i = 0; ret >= 0; i++)
+ else if (G_IS_INET_SOCKET_ADDRESS (identity))
{
- char san[500];
- size_t san_size;
+ GInetAddress *addr;
- san_size = sizeof (san);
- ret = gnutls_x509_crt_get_subject_alt_name (gnutls->cert, i,
- san, &san_size, NULL);
-
- if ((ret == GNUTLS_SAN_IPADDRESS) && (addr_size == san_size))
- {
- if (memcmp (addr_bytes, san, addr_size) == 0)
- {
- g_object_unref (addr);
- return TRUE;
- }
- }
+ addr = g_inet_socket_address_get_address (G_INET_SOCKET_ADDRESS (identity));
+ hostname = free_hostname = g_inet_address_to_string (addr);
+ }
+ else
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ _("Cannot verify peer identity of unexpected type %s"), G_OBJECT_TYPE_NAME (identity));
+ return G_TLS_CERTIFICATE_BAD_IDENTITY;
}
- g_object_unref (addr);
- return FALSE;
-}
-
-GTlsCertificateFlags
-g_tls_certificate_gnutls_verify_identity (GTlsCertificateGnutls *gnutls,
- GSocketConnectable *identity)
-{
- if (verify_identity_hostname (gnutls, identity))
- return 0;
- else if (verify_identity_ip (gnutls, identity))
- return 0;
+ g_assert (hostname);
+ if (!gnutls_x509_crt_check_hostname (gnutls->cert, hostname))
+ result |= G_TLS_CERTIFICATE_BAD_IDENTITY;
- /* FIXME: check sRVName and uniformResourceIdentifier
- * subjectAltNames, if appropriate for @identity.
- */
+ g_free (free_hostname);
- return G_TLS_CERTIFICATE_BAD_IDENTITY;
+ return result;
}
void