[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] master 01/05: Add functions to gnutls.c for exporting cert
From: |
Lars Ingebrigtsen |
Subject: |
[Emacs-diffs] master 01/05: Add functions to gnutls.c for exporting certificate details |
Date: |
Sun, 23 Nov 2014 14:14:35 +0000 |
branch: master
commit a85950469e6fc045de6157f9ad739e28f30ecd8d
Author: Lars Magne Ingebrigtsen <address@hidden>
Date: Sun Nov 23 14:52:04 2014 +0100
Add functions to gnutls.c for exporting certificate details
* gnutls.c (gnutls_hex_string, gnutls_certificate_details)
(Fgnutls_peer_status): New functions to export TLS certificate
details to Emacs Lisp.
* process.h: Added more fields to Lisp_Process to track
certificate details.
* gnutls.c (Fgnutls_boot): Save certificate for later inspection.
---
src/ChangeLog | 11 ++
src/gnutls.c | 351 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/process.h | 3 +
3 files changed, 364 insertions(+), 1 deletions(-)
diff --git a/src/ChangeLog b/src/ChangeLog
index b75b2f7..10ef4fa 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,14 @@
+2014-11-23 Lars Magne Ingebrigtsen <address@hidden>
+
+ * gnutls.c (Fgnutls_boot): Save certificate for later inspection.
+
+ * process.h: Added more fields to Lisp_Process to track
+ certificate details.
+
+ * gnutls.c (gnutls_hex_string, gnutls_certificate_details)
+ (Fgnutls_peer_status): New functions to export TLS certificate
+ details to Emacs Lisp.
+
2014-11-23 Jan Djärv <address@hidden>
* gtkutil.c (gtk_adjustment_configure): Define for Gtk+ < 2.14.
diff --git a/src/gnutls.c b/src/gnutls.c
index 5d48f78..37d797a 100644
--- a/src/gnutls.c
+++ b/src/gnutls.c
@@ -18,6 +18,7 @@ along with GNU Emacs. If not, see
<http://www.gnu.org/licenses/>. */
#include <config.h>
#include <errno.h>
+#include <stdio.h>
#include "lisp.h"
#include "process.h"
@@ -61,6 +62,11 @@ static void gnutls_log_function2 (int, const char *, const
char *);
static void gnutls_audit_log_function (gnutls_session_t, const char *);
#endif
+static enum
+ {
+ CERTIFICATE_NOT_MATCHING = 2,
+ } extra_peer_verification_t;
+
#ifdef WINDOWSNT
@@ -146,6 +152,40 @@ DEF_GNUTLS_FN (int, gnutls_x509_crt_import,
(gnutls_x509_crt_t, const gnutls_datum_t *,
gnutls_x509_crt_fmt_t));
DEF_GNUTLS_FN (int, gnutls_x509_crt_init, (gnutls_x509_crt_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_fingerprint,
+ (gnutls_digest_algorithm_t,
+ const gnutls_datum_t*, void *, size_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_version,
+ (gnutls_x509_crt_t));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_serial,
+ (gnutls_x509_crt_t, void *, size_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_issuer_dn,
+ (gnutls_x509_crt_t, char *, size_t *));
+DEF_GNUTLS_FN (time_t, gnutls_x509_crt_get_activation_time,
+ (gnutls_x509_crt_t));
+DEF_GNUTLS_FN (time_t, gnutls_x509_crt_get_expiration_time,
+ (gnutls_x509_crt_t));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_dn,
+ (gnutls_x509_crt_t, char *, size_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_pk_algorithm,
+ (gnutls_x509_crt_t, unsigned int *));
+DEF_GNUTLS_FN (int, gnutls_pk_algorithm_get_name, (gnutls_pk_algorithm_t));
+DEF_GNUTLS_FN (int, gnutls_pk_bits_to_sec_param,
+ (gnutls_pk_algorithm_t, unsigned int));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_issuer_unique_id,
+ (gnutls_x509_crt_t, char *, size_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_subject_unique_id,
+ (gnutls_x509_crt_t, char *, size_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_signature_algorithm,
+ (gnutls_x509_crt_t));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_signature,
+ (gnutls_x509_crt_t, char *, size_t *));
+DEF_GNUTLS_FN (int, gnutls_x509_crt_get_key_id,
+ (gnutls_x509_crt_t, unsigned int,
+ unsigned char *, size_t *_size));
+DEF_GNUTLS_FN (const char*, gnutls_sec_param_get_name, (gnutls_sec_param_t));
+DEF_GNUTLS_FN (const char*, gnutls_sign_algorithm_get_name,
+ (gnutls_sign_algorithm_t));
static bool
init_gnutls_functions (void)
@@ -205,6 +245,23 @@ init_gnutls_functions (void)
LOAD_GNUTLS_FN (library, gnutls_x509_crt_deinit);
LOAD_GNUTLS_FN (library, gnutls_x509_crt_import);
LOAD_GNUTLS_FN (library, gnutls_x509_crt_init);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_fingerprint);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_version);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_serial);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_issuer_dn);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_activation_time);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_expiration_time);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_dn);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_pk_algorithm);
+ LOAD_GNUTLS_FN (library, gnutls_pk_algorithm_get_name);
+ LOAD_GNUTLS_FN (library, gnutls_pk_bits_to_sec_param);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_issuer_unique_id);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_subject_unique_id);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_signature_algorithm);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_signature);
+ LOAD_GNUTLS_FN (library, gnutls_x509_crt_get_key_id);
+ LOAD_GNUTLS_FN (library, gnutls_sec_param_get_name);
+ LOAD_GNUTLS_FN (library, gnutls_sign_algorithm_get_name);
max_log_level = global_gnutls_log_level;
@@ -260,6 +317,23 @@ init_gnutls_functions (void)
#define fn_gnutls_x509_crt_deinit gnutls_x509_crt_deinit
#define fn_gnutls_x509_crt_import gnutls_x509_crt_import
#define fn_gnutls_x509_crt_init gnutls_x509_crt_init
+#define fn_gnutls_x509_crt_get_fingerprint gnutls_x509_crt_get_fingerprint
+#define fn_gnutls_x509_crt_get_version gnutls_x509_crt_get_version
+#define fn_gnutls_x509_crt_get_serial gnutls_x509_crt_get_serial
+#define fn_gnutls_x509_crt_get_issuer_dn gnutls_x509_crt_get_issuer_dn
+#define fn_gnutls_x509_crt_get_activation_time
gnutls_x509_crt_get_activation_time
+#define fn_gnutls_x509_crt_get_expiration_time
gnutls_x509_crt_get_expiration_time
+#define fn_gnutls_x509_crt_get_dn gnutls_x509_crt_get_dn
+#define fn_gnutls_x509_crt_get_pk_algorithm
gnutls_x509_crt_get_pk_algorithm
+#define fn_gnutls_pk_algorithm_get_name gnutls_pk_algorithm_get_name
+#define fn_gnutls_pk_bits_to_sec_param gnutls_pk_bits_to_sec_param
+#define fn_gnutls_x509_crt_get_issuer_unique_id
gnutls_x509_crt_get_issuer_unique_id
+#define fn_gnutls_x509_crt_get_subject_unique_id
gnutls_x509_crt_get_subject_unique_id
+#define fn_gnutls_x509_crt_get_signature_algorithm
gnutls_x509_crt_get_signature_algorithm
+#define fn_gnutls_x509_crt_get_signature gnutls_x509_crt_get_signature
+#define fn_gnutls_x509_crt_get_key_id gnutls_x509_crt_get_key_id
+#define fn_gnutls_sec_param_get_name gnutls_sec_param_get_name
+#define fn_gnutls_sign_algorithm_get_name gnutls_sign_algorithm_get_name
#endif /* !WINDOWSNT */
@@ -693,6 +767,273 @@ DEFUN ("gnutls-available-p", Fgnutls_available_p,
Sgnutls_available_p, 0, 0, 0,
#endif
}
+Lisp_Object
+gnutls_hex_string (char *buf, size_t buf_size, char *prefix) {
+ size_t prefix_length = strlen (prefix);
+ char *string = malloc (buf_size * 3 + prefix_length);
+ Lisp_Object ret;
+
+ strcpy (string, prefix);
+
+ for (int i = 0; i < buf_size; i++)
+ sprintf (string + i * 3 + prefix_length,
+ i == buf_size - 1? "%02x": "%02x:",
+ ((unsigned char*)buf)[i]);
+
+ ret = build_string (string);
+ free (string);
+ return ret;
+}
+
+Lisp_Object
+gnutls_certificate_details (gnutls_x509_crt_t cert)
+{
+ Lisp_Object res = Qnil;
+ int err;
+
+ /* Version. */
+ {
+ int version = fn_gnutls_x509_crt_get_version (cert);
+ if (version >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":version"),
+ make_number (version)));
+ }
+
+ /* Serial. */
+ {
+ size_t serial_size = 0;
+
+ err = fn_gnutls_x509_crt_get_serial (cert, NULL, &serial_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *serial = malloc (serial_size);
+ err = fn_gnutls_x509_crt_get_serial (cert, serial, &serial_size);
+ if (err >= GNUTLS_E_SUCCESS) {
+ res = nconc2 (res, list2 (intern (":serial-number"),
+ gnutls_hex_string (serial, serial_size, "")));
+ }
+ free (serial);
+ }
+ }
+
+ /* Issuer. */
+ {
+ size_t dn_size = 0;
+
+ err = fn_gnutls_x509_crt_get_issuer_dn (cert, NULL, &dn_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *dn = malloc (dn_size);
+ err = fn_gnutls_x509_crt_get_issuer_dn (cert, dn, &dn_size);
+ if (err >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":issuer"),
+ make_string (dn, dn_size)));
+ free (dn);
+ }
+ }
+
+ /* Validity. */
+ {
+ char buf[11];
+ size_t buf_size = sizeof (buf);
+ struct tm t;
+ time_t tim = fn_gnutls_x509_crt_get_activation_time (cert);
+
+ if (gmtime_r (&tim, &t) != NULL &&
+ strftime (buf, buf_size, "%Y-%m-%d", &t) != 0)
+ res = nconc2 (res, list2 (intern (":valid-from"), build_string (buf)));
+
+ tim = fn_gnutls_x509_crt_get_expiration_time (cert);
+ if (gmtime_r (&tim, &t) != NULL &&
+ strftime (buf, buf_size, "%Y-%m-%d", &t) != 0)
+ res = nconc2 (res, list2 (intern (":valid-to"), build_string (buf)));
+ }
+
+ /* Subject. */
+ {
+ size_t dn_size = 0;
+
+ err = fn_gnutls_x509_crt_get_dn (cert, NULL, &dn_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *dn = malloc (dn_size);
+ err = fn_gnutls_x509_crt_get_dn (cert, dn, &dn_size);
+ if (err >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":subject"),
+ make_string (dn, dn_size)));
+ free (dn);
+ }
+ }
+
+ /* SubjectPublicKeyInfo. */
+ {
+ unsigned int bits;
+
+ err = fn_gnutls_x509_crt_get_pk_algorithm (cert, &bits);
+ if (err >= GNUTLS_E_SUCCESS) {
+ const char *name = fn_gnutls_pk_algorithm_get_name (err);
+ if (name)
+ res = nconc2 (res, list2 (intern (":public-key-algorithm"),
+ build_string (name)));
+
+ name = fn_gnutls_sec_param_get_name (fn_gnutls_pk_bits_to_sec_param
+ (err, bits));
+ res = nconc2 (res, list2 (intern (":certificate-security-level"),
+ build_string (name)));
+ }
+ }
+
+ /* Unique IDs. */
+ {
+ size_t buf_size = 0;
+
+ err = fn_gnutls_x509_crt_get_issuer_unique_id (cert, NULL, &buf_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *buf = malloc (buf_size);
+ err = fn_gnutls_x509_crt_get_issuer_unique_id (cert, buf, &buf_size);
+ if (err >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":issuer-unique-id"),
+ make_string (buf, buf_size)));
+ free (buf);
+ }
+
+ buf_size = 0;
+ err = fn_gnutls_x509_crt_get_subject_unique_id (cert, NULL, &buf_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *buf = malloc (buf_size);
+ err = fn_gnutls_x509_crt_get_subject_unique_id (cert, buf, &buf_size);
+ if (err >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":subject-unique-id"),
+ make_string (buf, buf_size)));
+ free (buf);
+ }
+ }
+
+ /* Signature. */
+ {
+ size_t buf_size = 0;
+
+ err = fn_gnutls_x509_crt_get_signature_algorithm (cert);
+ if (err >= GNUTLS_E_SUCCESS) {
+ const char *name = fn_gnutls_sign_algorithm_get_name (err);
+ if (name)
+ res = nconc2 (res, list2 (intern (":signature-algorithm"),
+ build_string (name)));
+
+ err = fn_gnutls_x509_crt_get_signature (cert, NULL, &buf_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ char *buf = malloc (buf_size);
+ err = fn_gnutls_x509_crt_get_signature (cert, buf, &buf_size);
+ if (err >= GNUTLS_E_SUCCESS) {
+ res = nconc2 (res, list2 (intern (":signature"),
+ gnutls_hex_string (buf, buf_size, "")));
+ }
+ free (buf);
+ }
+ }
+ }
+
+ /* Public key ID. */
+ {
+ size_t buf_size = 0;
+
+ err = fn_gnutls_x509_crt_get_key_id (cert, 0, NULL, &buf_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ unsigned char *buf = malloc (buf_size);
+ err = fn_gnutls_x509_crt_get_key_id (cert, 0, buf, &buf_size);
+ if (err >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":public-key-id"),
+ gnutls_hex_string ((char *)buf,
+ buf_size, "sha1:")));
+ free (buf);
+ }
+ }
+
+ /* Certificate fingerprint. */
+ {
+ size_t buf_size = 0;
+
+ err = fn_gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1,
+ NULL, &buf_size);
+ if (err == GNUTLS_E_SHORT_MEMORY_BUFFER) {
+ unsigned char *buf = malloc (buf_size);
+ err = fn_gnutls_x509_crt_get_fingerprint (cert, GNUTLS_DIG_SHA1,
+ buf, &buf_size);
+ if (err >= GNUTLS_E_SUCCESS)
+ res = nconc2 (res, list2 (intern (":certificate-id"),
+ gnutls_hex_string ((char *)buf,
+ buf_size, "sha1:")));
+ free (buf);
+ }
+ }
+
+ return res;
+}
+
+DEFUN ("gnutls-peer-status", Fgnutls_peer_status, Sgnutls_peer_status, 1, 1, 0,
+ doc: /* Return the status of the gnutls PROC peer certificate.
+The return value is a property list. */)
+ (Lisp_Object proc)
+{
+ Lisp_Object warnings = Qnil, result = Qnil;
+ unsigned int verification;
+
+ CHECK_PROCESS (proc);
+
+ if (XPROCESS (proc)->gnutls_p == 0)
+ return Qnil;
+
+ /* Then collect any warnings already computed by the handshake. */
+ verification = XPROCESS (proc)->gnutls_peer_verification;
+
+ if (verification & GNUTLS_CERT_INVALID)
+ warnings = Fcons (list2 (intern (":invalid"),
+ build_string("certificate could not be verified")),
+ warnings);
+
+ if (verification & GNUTLS_CERT_REVOKED)
+ warnings = Fcons (list2 (intern (":revoked"),
+ build_string("certificate was revoked (CRL)")),
+ warnings);
+
+ if (verification & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ warnings = Fcons (list2 (intern (":self-signed"),
+ build_string("certificate signer was not found
(self-signed)")),
+ warnings);
+
+ if (verification & GNUTLS_CERT_SIGNER_NOT_CA)
+ warnings = Fcons (list2 (intern (":not-ca"),
+ build_string("certificate signer is not a CA")),
+ warnings);
+
+ if (verification & GNUTLS_CERT_INSECURE_ALGORITHM)
+ warnings = Fcons (list2 (intern (":insecure"),
+ build_string("certificate was signed with an
insecure algorithm")),
+ warnings);
+
+ if (verification & GNUTLS_CERT_NOT_ACTIVATED)
+ warnings = Fcons (list2 (intern (":not-activated"),
+ build_string("certificate is not yet activated")),
+ warnings);
+
+ if (verification & GNUTLS_CERT_EXPIRED)
+ warnings = Fcons (list2 (intern (":expired"),
+ build_string("certificate has expired")),
+ warnings);
+
+ if (XPROCESS (proc)->gnutls_extra_peer_verification &
+ CERTIFICATE_NOT_MATCHING)
+ warnings = Fcons (list2 (intern (":no-host-match"),
+ build_string("certificate host does not match
hostname")),
+ warnings);
+
+ if (!NILP (warnings))
+ result = list2 (intern (":warnings"), warnings);
+
+ result = nconc2 (result, list2
+ (intern (":certificate"),
+ gnutls_certificate_details(XPROCESS
(proc)->gnutls_certificate)));
+
+ return result;
+}
+
/* Initializes global GnuTLS state to defaults.
Call `gnutls-global-deinit' when GnuTLS usage is no longer needed.
@@ -1048,6 +1389,8 @@ one trustfile (usually a CA bundle). */)
if (ret < GNUTLS_E_SUCCESS)
return gnutls_make_error (ret);
+ XPROCESS (proc)->gnutls_peer_verification = peer_verification;
+
if (XINT (loglevel) > 0 && peer_verification & GNUTLS_CERT_INVALID)
message ("%s certificate could not be verified.", c_hostname);
@@ -1126,8 +1469,12 @@ one trustfile (usually a CA bundle). */)
return gnutls_make_error (ret);
}
+ XPROCESS (proc)->gnutls_certificate = gnutls_verify_cert;
+
if (!fn_gnutls_x509_crt_check_hostname (gnutls_verify_cert, c_hostname))
{
+ XPROCESS (proc)->gnutls_extra_peer_verification |=
+ CERTIFICATE_NOT_MATCHING;
if (verify_error_all
|| !NILP (Fmember (QCgnutls_bootprop_hostname, verify_error)))
{
@@ -1141,7 +1488,6 @@ one trustfile (usually a CA bundle). */)
c_hostname);
}
}
- fn_gnutls_x509_crt_deinit (gnutls_verify_cert);
}
/* Set this flag only if the whole initialization succeeded. */
@@ -1173,6 +1519,8 @@ This function may also return `gnutls-e-again', or
state = XPROCESS (proc)->gnutls_state;
+ fn_gnutls_x509_crt_deinit (XPROCESS (proc)->gnutls_certificate);
+
ret = fn_gnutls_bye (state,
NILP (cont) ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
@@ -1224,6 +1572,7 @@ syms_of_gnutls (void)
defsubr (&Sgnutls_deinit);
defsubr (&Sgnutls_bye);
defsubr (&Sgnutls_available_p);
+ defsubr (&Sgnutls_peer_status);
DEFVAR_INT ("gnutls-log-level", global_gnutls_log_level,
doc: /* Logging level used by the GnuTLS functions.
diff --git a/src/process.h b/src/process.h
index 273ad92..56c0f6d 100644
--- a/src/process.h
+++ b/src/process.h
@@ -162,6 +162,9 @@ struct Lisp_Process
gnutls_session_t gnutls_state;
gnutls_certificate_client_credentials gnutls_x509_cred;
gnutls_anon_client_credentials_t gnutls_anon_cred;
+ gnutls_x509_crt_t gnutls_certificate;
+ unsigned int gnutls_peer_verification;
+ unsigned int gnutls_extra_peer_verification;
int gnutls_log_level;
int gnutls_handshakes_tried;
bool_bf gnutls_p : 1;