gnutls.c 37.9 KB
Newer Older
Ted Zlatanov's avatar
Ted Zlatanov committed
1
/* GnuTLS glue for GNU Emacs.
2
   Copyright (C) 2010-2011  Free Software Foundation, Inc.
Ted Zlatanov's avatar
Ted Zlatanov committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

This file is part of GNU Emacs.

GNU Emacs 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 3 of the License, or
(at your option) any later version.

GNU Emacs 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 GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */

#include <config.h>
#include <errno.h>
#include <setjmp.h>

#include "lisp.h"
#include "process.h"

#ifdef HAVE_GNUTLS
#include <gnutls/gnutls.h>

29 30 31 32 33 34 35 36
#ifdef WINDOWSNT
#include <windows.h>
#include "w32.h"
#endif

static int
emacs_gnutls_handle_error (gnutls_session_t, int err);

37
static Lisp_Object Qgnutls_dll;
38 39 40
static Lisp_Object Qgnutls_code;
static Lisp_Object Qgnutls_anon, Qgnutls_x509pki;
static Lisp_Object Qgnutls_e_interrupted, Qgnutls_e_again,
Ted Zlatanov's avatar
Ted Zlatanov committed
41
  Qgnutls_e_invalid_session, Qgnutls_e_not_ready_for_handshake;
42
static int gnutls_global_initialized;
Ted Zlatanov's avatar
Ted Zlatanov committed
43

44
/* The following are for the property list of `gnutls-boot'.  */
45 46
static Lisp_Object Qgnutls_bootprop_priority;
static Lisp_Object Qgnutls_bootprop_trustfiles;
47 48
static Lisp_Object Qgnutls_bootprop_keylist;
static Lisp_Object Qgnutls_bootprop_crlfiles;
49 50 51
static Lisp_Object Qgnutls_bootprop_callbacks;
static Lisp_Object Qgnutls_bootprop_loglevel;
static Lisp_Object Qgnutls_bootprop_hostname;
52
static Lisp_Object Qgnutls_bootprop_min_prime_bits;
53 54
static Lisp_Object Qgnutls_bootprop_verify_flags;
static Lisp_Object Qgnutls_bootprop_verify_hostname_error;
55 56

/* Callback keys for `gnutls-boot'.  Unused currently.  */
57
static Lisp_Object Qgnutls_bootprop_callbacks_verify;
58

59 60 61 62 63 64 65
static void gnutls_log_function (int, const char *);
static void gnutls_log_function2 (int, const char*, const char*);


#ifdef WINDOWSNT

/* Macro for defining functions that will be loaded from the GnuTLS DLL.  */
66
#define DEF_GNUTLS_FN(rettype,func,args) static rettype (FAR CDECL *fn_##func)args
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

/* Macro for loading GnuTLS functions from the library.  */
#define LOAD_GNUTLS_FN(lib,func) {					\
    fn_##func = (void *) GetProcAddress (lib, #func);			\
    if (!fn_##func) return 0;						\
  }

DEF_GNUTLS_FN (gnutls_alert_description_t, gnutls_alert_get,
               (gnutls_session_t));
DEF_GNUTLS_FN (const char *, gnutls_alert_get_name,
               (gnutls_alert_description_t));
DEF_GNUTLS_FN (int, gnutls_alert_send_appropriate, (gnutls_session_t, int));
DEF_GNUTLS_FN (int, gnutls_anon_allocate_client_credentials,
               (gnutls_anon_client_credentials_t *));
DEF_GNUTLS_FN (void, gnutls_anon_free_client_credentials,
               (gnutls_anon_client_credentials_t));
DEF_GNUTLS_FN (int, gnutls_bye, (gnutls_session_t, gnutls_close_request_t));
DEF_GNUTLS_FN (int, gnutls_certificate_allocate_credentials,
               (gnutls_certificate_credentials_t *));
DEF_GNUTLS_FN (void, gnutls_certificate_free_credentials,
               (gnutls_certificate_credentials_t));
DEF_GNUTLS_FN (const gnutls_datum_t *, gnutls_certificate_get_peers,
               (gnutls_session_t, unsigned int *));
DEF_GNUTLS_FN (void, gnutls_certificate_set_verify_flags,
               (gnutls_certificate_credentials_t, unsigned int));
DEF_GNUTLS_FN (int, gnutls_certificate_set_x509_crl_file,
               (gnutls_certificate_credentials_t, const char *,
                gnutls_x509_crt_fmt_t));
DEF_GNUTLS_FN (int, gnutls_certificate_set_x509_key_file,
               (gnutls_certificate_credentials_t, const char *, const char *,
                gnutls_x509_crt_fmt_t));
DEF_GNUTLS_FN (int, gnutls_certificate_set_x509_trust_file,
               (gnutls_certificate_credentials_t, const char *,
                gnutls_x509_crt_fmt_t));
DEF_GNUTLS_FN (gnutls_certificate_type_t, gnutls_certificate_type_get,
               (gnutls_session_t));
DEF_GNUTLS_FN (int, gnutls_certificate_verify_peers2,
               (gnutls_session_t, unsigned int *));
DEF_GNUTLS_FN (int, gnutls_credentials_set,
               (gnutls_session_t, gnutls_credentials_type_t, void *));
DEF_GNUTLS_FN (void, gnutls_deinit, (gnutls_session_t));
108 109
DEF_GNUTLS_FN (void, gnutls_dh_set_prime_bits,
               (gnutls_session_t, unsigned int));
110 111 112 113
DEF_GNUTLS_FN (int, gnutls_error_is_fatal, (int));
DEF_GNUTLS_FN (int, gnutls_global_init, (void));
DEF_GNUTLS_FN (void, gnutls_global_set_log_function, (gnutls_log_func));
DEF_GNUTLS_FN (void, gnutls_global_set_log_level, (int));
114 115 116 117
DEF_GNUTLS_FN (void, gnutls_global_set_mem_functions,
	       (gnutls_alloc_function, gnutls_alloc_function,
		gnutls_is_secure_function, gnutls_realloc_function,
		gnutls_free_function));
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
DEF_GNUTLS_FN (int, gnutls_handshake, (gnutls_session_t));
DEF_GNUTLS_FN (int, gnutls_init, (gnutls_session_t *, gnutls_connection_end_t));
DEF_GNUTLS_FN (int, gnutls_priority_set_direct,
               (gnutls_session_t, const char *, const char **));
DEF_GNUTLS_FN (size_t, gnutls_record_check_pending, (gnutls_session_t));
DEF_GNUTLS_FN (ssize_t, gnutls_record_recv, (gnutls_session_t, void *, size_t));
DEF_GNUTLS_FN (ssize_t, gnutls_record_send,
               (gnutls_session_t, const void *, size_t));
DEF_GNUTLS_FN (const char *, gnutls_strerror, (int));
DEF_GNUTLS_FN (void, gnutls_transport_set_errno, (gnutls_session_t, int));
DEF_GNUTLS_FN (void, gnutls_transport_set_lowat, (gnutls_session_t, int));
DEF_GNUTLS_FN (void, gnutls_transport_set_ptr2,
               (gnutls_session_t, gnutls_transport_ptr_t,
                gnutls_transport_ptr_t));
DEF_GNUTLS_FN (void, gnutls_transport_set_pull_function,
               (gnutls_session_t, gnutls_pull_func));
DEF_GNUTLS_FN (void, gnutls_transport_set_push_function,
               (gnutls_session_t, gnutls_push_func));
DEF_GNUTLS_FN (int, gnutls_x509_crt_check_hostname,
               (gnutls_x509_crt_t, const char *));
DEF_GNUTLS_FN (void, gnutls_x509_crt_deinit, (gnutls_x509_crt_t));
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 *));

static int
init_gnutls_functions (Lisp_Object libraries)
{
  HMODULE library;
148
  int max_log_level = 1;
149 150 151

  if (!(library = w32_delayed_load (libraries, Qgnutls_dll)))
    {
152
      GNUTLS_LOG (1, max_log_level, "GnuTLS library not found");
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
      return 0;
    }

  LOAD_GNUTLS_FN (library, gnutls_alert_get);
  LOAD_GNUTLS_FN (library, gnutls_alert_get_name);
  LOAD_GNUTLS_FN (library, gnutls_alert_send_appropriate);
  LOAD_GNUTLS_FN (library, gnutls_anon_allocate_client_credentials);
  LOAD_GNUTLS_FN (library, gnutls_anon_free_client_credentials);
  LOAD_GNUTLS_FN (library, gnutls_bye);
  LOAD_GNUTLS_FN (library, gnutls_certificate_allocate_credentials);
  LOAD_GNUTLS_FN (library, gnutls_certificate_free_credentials);
  LOAD_GNUTLS_FN (library, gnutls_certificate_get_peers);
  LOAD_GNUTLS_FN (library, gnutls_certificate_set_verify_flags);
  LOAD_GNUTLS_FN (library, gnutls_certificate_set_x509_crl_file);
  LOAD_GNUTLS_FN (library, gnutls_certificate_set_x509_key_file);
  LOAD_GNUTLS_FN (library, gnutls_certificate_set_x509_trust_file);
  LOAD_GNUTLS_FN (library, gnutls_certificate_type_get);
  LOAD_GNUTLS_FN (library, gnutls_certificate_verify_peers2);
  LOAD_GNUTLS_FN (library, gnutls_credentials_set);
  LOAD_GNUTLS_FN (library, gnutls_deinit);
173
  LOAD_GNUTLS_FN (library, gnutls_dh_set_prime_bits);
174 175 176 177
  LOAD_GNUTLS_FN (library, gnutls_error_is_fatal);
  LOAD_GNUTLS_FN (library, gnutls_global_init);
  LOAD_GNUTLS_FN (library, gnutls_global_set_log_function);
  LOAD_GNUTLS_FN (library, gnutls_global_set_log_level);
178
  LOAD_GNUTLS_FN (library, gnutls_global_set_mem_functions);
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
  LOAD_GNUTLS_FN (library, gnutls_handshake);
  LOAD_GNUTLS_FN (library, gnutls_init);
  LOAD_GNUTLS_FN (library, gnutls_priority_set_direct);
  LOAD_GNUTLS_FN (library, gnutls_record_check_pending);
  LOAD_GNUTLS_FN (library, gnutls_record_recv);
  LOAD_GNUTLS_FN (library, gnutls_record_send);
  LOAD_GNUTLS_FN (library, gnutls_strerror);
  LOAD_GNUTLS_FN (library, gnutls_transport_set_errno);
  LOAD_GNUTLS_FN (library, gnutls_transport_set_lowat);
  LOAD_GNUTLS_FN (library, gnutls_transport_set_ptr2);
  LOAD_GNUTLS_FN (library, gnutls_transport_set_pull_function);
  LOAD_GNUTLS_FN (library, gnutls_transport_set_push_function);
  LOAD_GNUTLS_FN (library, gnutls_x509_crt_check_hostname);
  LOAD_GNUTLS_FN (library, gnutls_x509_crt_deinit);
  LOAD_GNUTLS_FN (library, gnutls_x509_crt_import);
  LOAD_GNUTLS_FN (library, gnutls_x509_crt_init);

196
  max_log_level = global_gnutls_log_level;
197 198

  GNUTLS_LOG2 (1, max_log_level, "GnuTLS library loaded:",
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
               SDATA (Fget (Qgnutls_dll, QCloaded_from)));
  return 1;
}

#else /* !WINDOWSNT */

#define fn_gnutls_alert_get			gnutls_alert_get
#define fn_gnutls_alert_get_name		gnutls_alert_get_name
#define fn_gnutls_alert_send_appropriate	gnutls_alert_send_appropriate
#define fn_gnutls_anon_allocate_client_credentials gnutls_anon_allocate_client_credentials
#define fn_gnutls_anon_free_client_credentials	gnutls_anon_free_client_credentials
#define fn_gnutls_bye				gnutls_bye
#define fn_gnutls_certificate_allocate_credentials gnutls_certificate_allocate_credentials
#define fn_gnutls_certificate_free_credentials	gnutls_certificate_free_credentials
#define fn_gnutls_certificate_get_peers		gnutls_certificate_get_peers
#define fn_gnutls_certificate_set_verify_flags	gnutls_certificate_set_verify_flags
#define fn_gnutls_certificate_set_x509_crl_file	gnutls_certificate_set_x509_crl_file
216
#define fn_gnutls_certificate_set_x509_key_file gnutls_certificate_set_x509_key_file
217
#define fn_gnutls_certificate_set_x509_trust_file gnutls_certificate_set_x509_trust_file
218 219 220 221
#define fn_gnutls_certificate_type_get		gnutls_certificate_type_get
#define fn_gnutls_certificate_verify_peers2	gnutls_certificate_verify_peers2
#define fn_gnutls_credentials_set		gnutls_credentials_set
#define fn_gnutls_deinit			gnutls_deinit
222
#define fn_gnutls_dh_set_prime_bits		gnutls_dh_set_prime_bits
223 224 225 226
#define fn_gnutls_error_is_fatal		gnutls_error_is_fatal
#define fn_gnutls_global_init			gnutls_global_init
#define fn_gnutls_global_set_log_function	gnutls_global_set_log_function
#define fn_gnutls_global_set_log_level		gnutls_global_set_log_level
227
#define fn_gnutls_global_set_mem_functions	gnutls_global_set_mem_functions
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
#define fn_gnutls_handshake			gnutls_handshake
#define fn_gnutls_init				gnutls_init
#define fn_gnutls_priority_set_direct		gnutls_priority_set_direct
#define fn_gnutls_record_check_pending		gnutls_record_check_pending
#define fn_gnutls_record_recv			gnutls_record_recv
#define fn_gnutls_record_send			gnutls_record_send
#define fn_gnutls_strerror			gnutls_strerror
#define fn_gnutls_transport_set_errno		gnutls_transport_set_errno
#define fn_gnutls_transport_set_ptr2		gnutls_transport_set_ptr2
#define fn_gnutls_x509_crt_check_hostname	gnutls_x509_crt_check_hostname
#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

#endif /* !WINDOWSNT */


245
static void
246 247 248 249 250 251 252 253 254 255 256 257
gnutls_log_function (int level, const char* string)
{
  message ("gnutls.c: [%d] %s", level, string);
}

static void
gnutls_log_function2 (int level, const char* string, const char* extra)
{
  message ("gnutls.c: [%d] %s %s", level, string, extra);
}

static int
258 259 260 261 262 263
emacs_gnutls_handshake (struct Lisp_Process *proc)
{
  gnutls_session_t state = proc->gnutls_state;
  int ret;

  if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO)
264
    return -1;
265 266

  if (proc->gnutls_initstage < GNUTLS_STAGE_TRANSPORT_POINTERS_SET)
267
    {
268 269 270 271
#ifdef WINDOWSNT
      /* On W32 we cannot transfer socket handles between different runtime
         libraries, so we tell GnuTLS to use our special push/pull
         functions.  */
272 273 274 275 276
      fn_gnutls_transport_set_ptr2 (state,
                                    (gnutls_transport_ptr_t) proc,
                                    (gnutls_transport_ptr_t) proc);
      fn_gnutls_transport_set_push_function (state, &emacs_gnutls_push);
      fn_gnutls_transport_set_pull_function (state, &emacs_gnutls_pull);
277 278 279 280 281 282 283 284

      /* For non blocking sockets or other custom made pull/push
         functions the gnutls_transport_set_lowat must be called, with
         a zero low water mark value. (GnuTLS 2.10.4 documentation)

         (Note: this is probably not strictly necessary as the lowat
          value is only used when no custom pull/push functions are
          set.)  */
285
      fn_gnutls_transport_set_lowat (state, 0);
286
#else
287 288 289
      /* This is how GnuTLS takes sockets: as file descriptors passed
         in.  For an Emacs process socket, infd and outfd are the
         same but we use this two-argument version for clarity.  */
290 291 292
      fn_gnutls_transport_set_ptr2 (state,
                                    (gnutls_transport_ptr_t) (long) proc->infd,
                                    (gnutls_transport_ptr_t) (long) proc->outfd);
293
#endif
294

295 296
      proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET;
    }
297

298 299
  do
    {
300
      ret = fn_gnutls_handshake (state);
301 302
      emacs_gnutls_handle_error (state, ret);
    }
303
  while (ret < 0 && fn_gnutls_error_is_fatal (ret) == 0);
304

305 306 307
  proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;

  if (ret == GNUTLS_E_SUCCESS)
308
    {
309
      /* Here we're finally done.  */
310 311
      proc->gnutls_initstage = GNUTLS_STAGE_READY;
    }
312 313
  else
    {
314
      fn_gnutls_alert_send_appropriate (state, ret);
315 316
    }
  return ret;
317 318
}

319 320 321 322 323 324 325 326 327 328 329 330
int
emacs_gnutls_record_check_pending (gnutls_session_t state)
{
  return fn_gnutls_record_check_pending (state);
}

void
emacs_gnutls_transport_set_errno (gnutls_session_t state, int err)
{
  fn_gnutls_transport_set_errno (state, err);
}

331
EMACS_INT
332
emacs_gnutls_write (struct Lisp_Process *proc, const char *buf, EMACS_INT nbyte)
Ted Zlatanov's avatar
Ted Zlatanov committed
333
{
334
  ssize_t rtnval = 0;
335
  EMACS_INT bytes_written;
336 337
  gnutls_session_t state = proc->gnutls_state;

338 339 340 341 342 343 344
  if (proc->gnutls_initstage != GNUTLS_STAGE_READY) {
#ifdef EWOULDBLOCK
    errno = EWOULDBLOCK;
#endif
#ifdef EAGAIN
    errno = EAGAIN;
#endif
345
    return 0;
346
  }
Ted Zlatanov's avatar
Ted Zlatanov committed
347 348 349 350 351

  bytes_written = 0;

  while (nbyte > 0)
    {
352
      rtnval = fn_gnutls_record_send (state, buf, nbyte);
Ted Zlatanov's avatar
Ted Zlatanov committed
353

354
      if (rtnval < 0)
Ted Zlatanov's avatar
Ted Zlatanov committed
355
        {
356
          if (rtnval == GNUTLS_E_AGAIN || rtnval == GNUTLS_E_INTERRUPTED)
Ted Zlatanov's avatar
Ted Zlatanov committed
357 358
            continue;
          else
359
            break;
Ted Zlatanov's avatar
Ted Zlatanov committed
360 361 362 363 364 365 366
        }

      buf += rtnval;
      nbyte -= rtnval;
      bytes_written += rtnval;
    }

367
  emacs_gnutls_handle_error (state, rtnval);
Ted Zlatanov's avatar
Ted Zlatanov committed
368 369 370
  return (bytes_written);
}

371
EMACS_INT
372
emacs_gnutls_read (struct Lisp_Process *proc, char *buf, EMACS_INT nbyte)
Ted Zlatanov's avatar
Ted Zlatanov committed
373
{
374
  ssize_t rtnval;
375 376
  gnutls_session_t state = proc->gnutls_state;

377 378 379 380 381
  if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
    {
      emacs_gnutls_handshake (proc);
      return -1;
    }
382
  rtnval = fn_gnutls_record_recv (state, buf, nbyte);
383 384
  if (rtnval >= 0)
    return rtnval;
385 386 387
  else if (emacs_gnutls_handle_error (state, rtnval) == 0)
    /* non-fatal error */
    return -1;
388
  else {
Juanma Barranquero's avatar
Juanma Barranquero committed
389
    /* a fatal error occurred */
390
    return 0;
391
  }
Ted Zlatanov's avatar
Ted Zlatanov committed
392 393
}

394 395 396 397 398 399 400
/* report a GnuTLS error to the user.
   Returns zero if the error code was successfully handled. */
static int
emacs_gnutls_handle_error (gnutls_session_t session, int err)
{
  int max_log_level = 0;

401
  int ret;
402 403 404 405 406 407
  const char *str;

  /* TODO: use a Lisp_Object generated by gnutls_make_error?  */
  if (err >= 0)
    return 0;

408
  max_log_level = global_gnutls_log_level;
409 410 411

  /* TODO: use gnutls-error-fatalp and gnutls-error-string.  */

412
  str = fn_gnutls_strerror (err);
413 414 415
  if (!str)
    str = "unknown";

416
  if (fn_gnutls_error_is_fatal (err))
417 418 419 420 421 422 423 424 425 426 427 428 429 430
    {
      ret = err;
      GNUTLS_LOG2 (0, max_log_level, "fatal error:", str);
    }
  else
    {
      ret = 0;
      GNUTLS_LOG2 (1, max_log_level, "non-fatal error:", str);
      /* TODO: EAGAIN AKA Qgnutls_e_again should be level 2.  */
    }

  if (err == GNUTLS_E_WARNING_ALERT_RECEIVED
      || err == GNUTLS_E_FATAL_ALERT_RECEIVED)
    {
431
      int alert = fn_gnutls_alert_get (session);
432
      int level = (err == GNUTLS_E_FATAL_ALERT_RECEIVED) ? 0 : 1;
433
      str = fn_gnutls_alert_get_name (alert);
434 435 436 437 438 439 440 441
      if (!str)
	str = "unknown";

      GNUTLS_LOG2 (level, max_log_level, "Received alert: ", str);
    }
  return ret;
}

Ted Zlatanov's avatar
Ted Zlatanov committed
442 443 444 445
/* convert an integer error to a Lisp_Object; it will be either a
   known symbol like `gnutls_e_interrupted' and `gnutls_e_again' or
   simply the integer value of the error.  GNUTLS_E_SUCCESS is mapped
   to Qt.  */
446
static Lisp_Object
447
gnutls_make_error (int err)
Ted Zlatanov's avatar
Ted Zlatanov committed
448
{
449
  switch (err)
450 451 452 453 454 455 456 457 458 459
    {
    case GNUTLS_E_SUCCESS:
      return Qt;
    case GNUTLS_E_AGAIN:
      return Qgnutls_e_again;
    case GNUTLS_E_INTERRUPTED:
      return Qgnutls_e_interrupted;
    case GNUTLS_E_INVALID_SESSION:
      return Qgnutls_e_invalid_session;
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
460

461
  return make_number (err);
Ted Zlatanov's avatar
Ted Zlatanov committed
462 463 464
}

DEFUN ("gnutls-get-initstage", Fgnutls_get_initstage, Sgnutls_get_initstage, 1, 1, 0,
465
       doc: /* Return the GnuTLS init stage of process PROC.
Ted Zlatanov's avatar
Ted Zlatanov committed
466
See also `gnutls-boot'.  */)
467
  (Lisp_Object proc)
Ted Zlatanov's avatar
Ted Zlatanov committed
468 469 470 471 472 473 474
{
  CHECK_PROCESS (proc);

  return make_number (GNUTLS_INITSTAGE (proc));
}

DEFUN ("gnutls-errorp", Fgnutls_errorp, Sgnutls_errorp, 1, 1, 0,
475 476 477 478
       doc: /* Return t if ERROR indicates a GnuTLS problem.
ERROR is an integer or a symbol with an integer `gnutls-code' property.
usage: (gnutls-errorp ERROR)  */)
  (Lisp_Object err)
Ted Zlatanov's avatar
Ted Zlatanov committed
479
{
480
  if (EQ (err, Qt)) return Qnil;
Ted Zlatanov's avatar
Ted Zlatanov committed
481 482 483 484 485

  return Qt;
}

DEFUN ("gnutls-error-fatalp", Fgnutls_error_fatalp, Sgnutls_error_fatalp, 1, 1, 0,
486 487 488 489
       doc: /* Check if ERROR is fatal.
ERROR is an integer or a symbol with an integer `gnutls-code' property.
usage: (gnutls-error-fatalp ERROR)  */)
  (Lisp_Object err)
Ted Zlatanov's avatar
Ted Zlatanov committed
490 491 492 493 494 495 496
{
  Lisp_Object code;

  if (EQ (err, Qt)) return Qnil;

  if (SYMBOLP (err))
    {
497 498 499 500 501 502 503 504 505
      code = Fget (err, Qgnutls_code);
      if (NUMBERP (code))
	{
	  err = code;
	}
      else
	{
	  error ("Symbol has no numeric gnutls-code property");
	}
Ted Zlatanov's avatar
Ted Zlatanov committed
506 507 508 509 510
    }

  if (!NUMBERP (err))
    error ("Not an error symbol or code");

511
  if (0 == fn_gnutls_error_is_fatal (XINT (err)))
Ted Zlatanov's avatar
Ted Zlatanov committed
512 513 514 515 516 517
    return Qnil;

  return Qt;
}

DEFUN ("gnutls-error-string", Fgnutls_error_string, Sgnutls_error_string, 1, 1, 0,
518 519 520 521
       doc: /* Return a description of ERROR.
ERROR is an integer or a symbol with an integer `gnutls-code' property.
usage: (gnutls-error-string ERROR)  */)
  (Lisp_Object err)
Ted Zlatanov's avatar
Ted Zlatanov committed
522 523 524 525 526 527 528
{
  Lisp_Object code;

  if (EQ (err, Qt)) return build_string ("Not an error");

  if (SYMBOLP (err))
    {
529 530 531 532 533 534 535 536 537
      code = Fget (err, Qgnutls_code);
      if (NUMBERP (code))
	{
	  err = code;
	}
      else
	{
	  return build_string ("Symbol has no numeric gnutls-code property");
	}
Ted Zlatanov's avatar
Ted Zlatanov committed
538 539 540 541 542
    }

  if (!NUMBERP (err))
    return build_string ("Not an error symbol or code");

543
  return build_string (fn_gnutls_strerror (XINT (err)));
Ted Zlatanov's avatar
Ted Zlatanov committed
544 545 546
}

DEFUN ("gnutls-deinit", Fgnutls_deinit, Sgnutls_deinit, 1, 1, 0,
547
       doc: /* Deallocate GnuTLS resources associated with process PROC.
Ted Zlatanov's avatar
Ted Zlatanov committed
548
See also `gnutls-init'.  */)
549
  (Lisp_Object proc)
Ted Zlatanov's avatar
Ted Zlatanov committed
550 551 552 553 554 555 556
{
  gnutls_session_t state;

  CHECK_PROCESS (proc);
  state = XPROCESS (proc)->gnutls_state;

  if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_INIT)
557
    {
558
      fn_gnutls_deinit (state);
Ted Zlatanov's avatar
Ted Zlatanov committed
559
      GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT - 1;
560
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
561 562 563 564

  return Qt;
}

565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
DEFUN ("gnutls-available-p", Fgnutls_available_p, Sgnutls_available_p, 0, 0, 0,
       doc: /* Return t if GnuTLS is available in this instance of Emacs.  */)
     (void)
{
#ifdef WINDOWSNT
  Lisp_Object found = Fassq (Qgnutls_dll, Vlibrary_cache);
  if (CONSP (found))
    return XCDR (found);
  else
    {
      Lisp_Object status;
      status = init_gnutls_functions (Vdynamic_library_alist) ? Qt : Qnil;
      Vlibrary_cache = Fcons (Fcons (Qgnutls_dll, status), Vlibrary_cache);
      return status;
    }
#else
  return Qt;
#endif
}


586 587
/* Initializes global GnuTLS state to defaults.
Call `gnutls-global-deinit' when GnuTLS usage is no longer needed.
Ted Zlatanov's avatar
Ted Zlatanov committed
588
Returns zero on success.  */
589
static Lisp_Object
590
emacs_gnutls_global_init (void)
Ted Zlatanov's avatar
Ted Zlatanov committed
591 592 593
{
  int ret = GNUTLS_E_SUCCESS;

594
  if (!gnutls_global_initialized)
595 596 597 598 599
    {
      fn_gnutls_global_set_mem_functions (xmalloc, xmalloc, NULL,
					  xrealloc, xfree);
      ret = fn_gnutls_global_init ();
    }
600
  gnutls_global_initialized = 1;
Ted Zlatanov's avatar
Ted Zlatanov committed
601 602 603 604

  return gnutls_make_error (ret);
}

605
#if 0
606
/* Deinitializes global GnuTLS state.
Ted Zlatanov's avatar
Ted Zlatanov committed
607
See also `gnutls-global-init'.  */
608
static Lisp_Object
609
emacs_gnutls_global_deinit (void)
Ted Zlatanov's avatar
Ted Zlatanov committed
610
{
611
  if (gnutls_global_initialized)
Ted Zlatanov's avatar
Ted Zlatanov committed
612 613
    gnutls_global_deinit ();

614
  gnutls_global_initialized = 0;
Ted Zlatanov's avatar
Ted Zlatanov committed
615 616 617

  return gnutls_make_error (GNUTLS_E_SUCCESS);
}
618
#endif
Ted Zlatanov's avatar
Ted Zlatanov committed
619

620 621
DEFUN ("gnutls-boot", Fgnutls_boot, Sgnutls_boot, 3, 3, 0,
       doc: /* Initialize GnuTLS client for process PROC with TYPE+PROPLIST.
Ted Zlatanov's avatar
Ted Zlatanov committed
622 623 624
Currently only client mode is supported.  Returns a success/failure
value you can check with `gnutls-errorp'.

625 626 627
TYPE is a symbol, either `gnutls-anon' or `gnutls-x509pki'.
PROPLIST is a property list with the following keys:

628 629
:hostname is a string naming the remote host.

630
:priority is a GnuTLS priority string, defaults to "NORMAL".
631

632
:trustfiles is a list of PEM-encoded trust files for `gnutls-x509pki'.
633

634 635 636 637
:crlfiles is a list of PEM-encoded CRL lists for `gnutls-x509pki'.

:keylist is an alist of PEM-encoded key files and PEM-encoded
certificates for `gnutls-x509pki'.
638 639 640

:callbacks is an alist of callback functions, see below.

641
:loglevel is the debug level requested from GnuTLS, try 4.
642

643 644 645 646 647 648
:verify-flags is a bitset as per GnuTLS'
gnutls_certificate_set_verify_flags.

:verify-hostname-error, if non-nil, makes a hostname mismatch an
error.  Otherwise it will be just a warning.

649 650 651
:min-prime-bits is the minimum accepted number of bits the client will
accept in Diffie-Hellman key exchange.

652 653 654
The debug level will be set for this process AND globally for GnuTLS.
So if you set it higher or lower at any point, it affects global
debugging.
Ted Zlatanov's avatar
Ted Zlatanov committed
655 656 657 658 659

Note that the priority is set on the client.  The server does not use
the protocols's priority except for disabling protocols that were not
specified.

660
Processes must be initialized with this function before other GnuTLS
Ted Zlatanov's avatar
Ted Zlatanov committed
661 662 663
functions are used.  This function allocates resources which can only
be deallocated by calling `gnutls-deinit' or by calling it again.

664 665 666
The callbacks alist can have a `verify' key, associated with a
verification function (UNUSED).

Ted Zlatanov's avatar
Ted Zlatanov committed
667
Each authentication type may need additional information in order to
668 669 670
work.  For X.509 PKI (`gnutls-x509pki'), you probably need at least
one trustfile (usually a CA bundle).  */)
  (Lisp_Object proc, Lisp_Object type, Lisp_Object proplist)
Ted Zlatanov's avatar
Ted Zlatanov committed
671 672 673
{
  int ret = GNUTLS_E_SUCCESS;

674 675
  int max_log_level = 0;

Ted Zlatanov's avatar
Ted Zlatanov committed
676 677 678
  /* TODO: GNUTLS_X509_FMT_DER is also an option.  */
  int file_format = GNUTLS_X509_FMT_PEM;

679 680 681 682 683
  unsigned int gnutls_verify_flags = GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT;
  gnutls_x509_crt_t gnutls_verify_cert;
  unsigned int gnutls_verify_cert_list_size;
  const gnutls_datum_t *gnutls_verify_cert_list;

Ted Zlatanov's avatar
Ted Zlatanov committed
684 685 686 687
  gnutls_session_t state;
  gnutls_certificate_credentials_t x509_cred;
  gnutls_anon_client_credentials_t anon_cred;
  Lisp_Object global_init;
688
  char const *priority_string_ptr = "NORMAL"; /* default priority string.  */
689
  Lisp_Object tail;
690
  unsigned int peer_verification;
691
  char* c_hostname;
692 693 694 695

  /* Placeholders for the property list elements.  */
  Lisp_Object priority_string;
  Lisp_Object trustfiles;
696 697
  Lisp_Object crlfiles;
  Lisp_Object keylist;
698
  /* Lisp_Object callbacks; */
699
  Lisp_Object loglevel;
700 701
  Lisp_Object hostname;
  Lisp_Object verify_flags;
Paul Eggert's avatar
Paul Eggert committed
702
  /* Lisp_Object verify_error; */
703
  Lisp_Object verify_hostname_error;
704
  Lisp_Object prime_bits;
Ted Zlatanov's avatar
Ted Zlatanov committed
705 706 707

  CHECK_PROCESS (proc);
  CHECK_SYMBOL (type);
708 709
  CHECK_LIST (proplist);

710 711 712 713 714 715
  if (NILP (Fgnutls_available_p ()))
    {
      error ("GnuTLS not available");
      return gnutls_make_error (GNUTLS_EMACS_ERROR_NOT_LOADED);
    }

716 717 718
  hostname              = Fplist_get (proplist, Qgnutls_bootprop_hostname);
  priority_string       = Fplist_get (proplist, Qgnutls_bootprop_priority);
  trustfiles            = Fplist_get (proplist, Qgnutls_bootprop_trustfiles);
719 720
  keylist               = Fplist_get (proplist, Qgnutls_bootprop_keylist);
  crlfiles              = Fplist_get (proplist, Qgnutls_bootprop_crlfiles);
Paul Eggert's avatar
Paul Eggert committed
721
  /* callbacks          = Fplist_get (proplist, Qgnutls_bootprop_callbacks); */
722 723
  loglevel              = Fplist_get (proplist, Qgnutls_bootprop_loglevel);
  verify_flags          = Fplist_get (proplist, Qgnutls_bootprop_verify_flags);
724
  /* verify_error       = Fplist_get (proplist, Qgnutls_bootprop_verify_error); */
725
  verify_hostname_error = Fplist_get (proplist, Qgnutls_bootprop_verify_hostname_error);
726
  prime_bits            = Fplist_get (proplist, Qgnutls_bootprop_min_prime_bits);
727 728 729 730 731

  if (!STRINGP (hostname))
    error ("gnutls-boot: invalid :hostname parameter");

  c_hostname = SSDATA (hostname);
Ted Zlatanov's avatar
Ted Zlatanov committed
732 733

  state = XPROCESS (proc)->gnutls_state;
734
  XPROCESS (proc)->gnutls_p = 1;
Ted Zlatanov's avatar
Ted Zlatanov committed
735

736 737
  if (NUMBERP (loglevel))
    {
738 739
      fn_gnutls_global_set_log_function (gnutls_log_function);
      fn_gnutls_global_set_log_level (XINT (loglevel));
740 741 742
      max_log_level = XINT (loglevel);
      XPROCESS (proc)->gnutls_log_level = max_log_level;
    }
743

Ted Zlatanov's avatar
Ted Zlatanov committed
744
  /* always initialize globals.  */
745
  global_init = emacs_gnutls_global_init ();
Ted Zlatanov's avatar
Ted Zlatanov committed
746 747 748 749 750
  if (! NILP (Fgnutls_errorp (global_init)))
    return global_init;

  /* deinit and free resources.  */
  if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_CRED_ALLOC)
751
    {
752 753
      GNUTLS_LOG (1, max_log_level, "deallocating credentials");

Ted Zlatanov's avatar
Ted Zlatanov committed
754
      if (EQ (type, Qgnutls_x509pki))
755
	{
756 757
          GNUTLS_LOG (2, max_log_level, "deallocating x509 credentials");
          x509_cred = XPROCESS (proc)->gnutls_x509_cred;
758
          fn_gnutls_certificate_free_credentials (x509_cred);
759
	}
Ted Zlatanov's avatar
Ted Zlatanov committed
760
      else if (EQ (type, Qgnutls_anon))
761
	{
762 763
          GNUTLS_LOG (2, max_log_level, "deallocating anon credentials");
          anon_cred = XPROCESS (proc)->gnutls_anon_cred;
764
          fn_gnutls_anon_free_client_credentials (anon_cred);
765
	}
Ted Zlatanov's avatar
Ted Zlatanov committed
766
      else
767
	{
Ted Zlatanov's avatar
Ted Zlatanov committed
768 769
          error ("unknown credential type");
          ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
770
	}
Ted Zlatanov's avatar
Ted Zlatanov committed
771 772

      if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_INIT)
773
	{
774
          GNUTLS_LOG (1, max_log_level, "deallocating x509 credentials");
Ted Zlatanov's avatar
Ted Zlatanov committed
775
          Fgnutls_deinit (proc);
776 777
	}
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
778 779 780

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_EMPTY;

781 782
  GNUTLS_LOG (1, max_log_level, "allocating credentials");

Ted Zlatanov's avatar
Ted Zlatanov committed
783
  if (EQ (type, Qgnutls_x509pki))
784
    {
785 786
      GNUTLS_LOG (2, max_log_level, "allocating x509 credentials");
      x509_cred = XPROCESS (proc)->gnutls_x509_cred;
787
      fn_gnutls_certificate_allocate_credentials (&x509_cred);
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803

      if (NUMBERP (verify_flags))
        {
          gnutls_verify_flags = XINT (verify_flags);
          GNUTLS_LOG (2, max_log_level, "setting verification flags");
        }
      else if (NILP (verify_flags))
        {
          /* The default is already GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT.  */
          GNUTLS_LOG (2, max_log_level, "using default verification flags");
        }
      else
        {
          /* The default is already GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT.  */
          GNUTLS_LOG (2, max_log_level, "ignoring invalid verify-flags");
        }
804
      fn_gnutls_certificate_set_verify_flags (x509_cred, gnutls_verify_flags);
805
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
806
  else if (EQ (type, Qgnutls_anon))
807
    {
808 809
      GNUTLS_LOG (2, max_log_level, "allocating anon credentials");
      anon_cred = XPROCESS (proc)->gnutls_anon_cred;
810
      fn_gnutls_anon_allocate_client_credentials (&anon_cred);
811
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
812
  else
813
    {
Ted Zlatanov's avatar
Ted Zlatanov committed
814 815
      error ("unknown credential type");
      ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
816
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
817 818

  if (ret < GNUTLS_E_SUCCESS)
819
    return gnutls_make_error (ret);
Ted Zlatanov's avatar
Ted Zlatanov committed
820 821 822 823

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_ALLOC;

  if (EQ (type, Qgnutls_x509pki))
824
    {
825
      for (tail = trustfiles; !NILP (tail); tail = Fcdr (tail))
826
	{
827 828 829 830
	  Lisp_Object trustfile = Fcar (tail);
          if (STRINGP (trustfile))
            {
              GNUTLS_LOG2 (1, max_log_level, "setting the trustfile: ",
831
                           SSDATA (trustfile));
832
              ret = fn_gnutls_certificate_set_x509_trust_file
833
                (x509_cred,
834
                 SSDATA (trustfile),
835
                 file_format);
836

837 838 839 840 841 842
              if (ret < GNUTLS_E_SUCCESS)
                return gnutls_make_error (ret);
            }
          else
            {
              error ("Sorry, GnuTLS can't use non-string trustfile %s",
843
                     SDATA (trustfile));
844 845
            }
        }
Ted Zlatanov's avatar
Ted Zlatanov committed
846

847
      for (tail = crlfiles; !NILP (tail); tail = Fcdr (tail))
848
	{
849 850
	  Lisp_Object crlfile = Fcar (tail);
          if (STRINGP (crlfile))
851
            {
852 853
              GNUTLS_LOG2 (1, max_log_level, "setting the CRL file: ",
                           SSDATA (crlfile));
854
              ret = fn_gnutls_certificate_set_x509_crl_file
855
                (x509_cred,
856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
                 SSDATA (crlfile),
                 file_format);

              if (ret < GNUTLS_E_SUCCESS)
                return gnutls_make_error (ret);
            }
          else
            {
              error ("Sorry, GnuTLS can't use non-string CRL file %s",
                     SDATA (crlfile));
            }
        }

      for (tail = keylist; !NILP (tail); tail = Fcdr (tail))
	{
	  Lisp_Object keyfile = Fcar (Fcar (tail));
	  Lisp_Object certfile = Fcar (Fcdr (tail));
          if (STRINGP (keyfile) && STRINGP (certfile))
            {
              GNUTLS_LOG2 (1, max_log_level, "setting the client key file: ",
                           SSDATA (keyfile));
              GNUTLS_LOG2 (1, max_log_level, "setting the client cert file: ",
                           SSDATA (certfile));
879
              ret = fn_gnutls_certificate_set_x509_key_file
880 881
                (x509_cred,
                 SSDATA (certfile),
882
                 SSDATA (keyfile),
883
                 file_format);
884

885 886 887 888 889
              if (ret < GNUTLS_E_SUCCESS)
                return gnutls_make_error (ret);
            }
          else
            {
890 891 892 893 894 895
              if (STRINGP (keyfile))
                error ("Sorry, GnuTLS can't use non-string client cert file %s",
                       SDATA (certfile));
              else
                error ("Sorry, GnuTLS can't use non-string client key file %s",
                       SDATA (keyfile));
896 897
            }
        }
898
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
899 900 901

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_FILES;

902 903 904 905 906 907 908 909
  GNUTLS_LOG (1, max_log_level, "gnutls callbacks");

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CALLBACKS;

#ifdef HAVE_GNUTLS_CALLBACK_CERTIFICATE_VERIFY
#else
#endif

910 911
  GNUTLS_LOG (1, max_log_level, "gnutls_init");

912
  ret = fn_gnutls_init (&state, GNUTLS_CLIENT);
Ted Zlatanov's avatar
Ted Zlatanov committed
913 914

  if (ret < GNUTLS_E_SUCCESS)
915
    return gnutls_make_error (ret);
Ted Zlatanov's avatar
Ted Zlatanov committed
916 917 918 919 920

  XPROCESS (proc)->gnutls_state = state;

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT;

921 922
  if (STRINGP (priority_string))
    {
923
      priority_string_ptr = SSDATA (priority_string);
924 925 926 927 928 929 930 931
      GNUTLS_LOG2 (1, max_log_level, "got non-default priority string:",
                   priority_string_ptr);
    }
  else
    {
      GNUTLS_LOG2 (1, max_log_level, "using default priority string:",
                   priority_string_ptr);
    }
932

933 934
  GNUTLS_LOG (1, max_log_level, "setting the priority string");

935 936 937
  ret = fn_gnutls_priority_set_direct (state,
                                       priority_string_ptr,
                                       NULL);
Ted Zlatanov's avatar
Ted Zlatanov committed
938 939

  if (ret < GNUTLS_E_SUCCESS)
940
    return gnutls_make_error (ret);
Ted Zlatanov's avatar
Ted Zlatanov committed
941 942 943

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_PRIORITY;

944 945 946 947 948
  if (!EQ (prime_bits, Qnil))
    {
      fn_gnutls_dh_set_prime_bits (state, XUINT (prime_bits));
    }

Ted Zlatanov's avatar
Ted Zlatanov committed
949
  if (EQ (type, Qgnutls_x509pki))
950
    {
951
      ret = fn_gnutls_credentials_set (state, GNUTLS_CRD_CERTIFICATE, x509_cred);
952
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
953
  else if (EQ (type, Qgnutls_anon))
954
    {
955
      ret = fn_gnutls_credentials_set (state, GNUTLS_CRD_ANON, anon_cred);
956
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
957
  else
958
    {
Ted Zlatanov's avatar
Ted Zlatanov committed
959 960
      error ("unknown credential type");
      ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
961
    }
Ted Zlatanov's avatar
Ted Zlatanov committed
962 963

  if (ret < GNUTLS_E_SUCCESS)
964
    return gnutls_make_error (ret);
Ted Zlatanov's avatar
Ted Zlatanov committed
965

966 967
  XPROCESS (proc)->gnutls_anon_cred = anon_cred;
  XPROCESS (proc)->gnutls_x509_cred = x509_cred;
Ted Zlatanov's avatar
Ted Zlatanov committed
968 969 970 971
  XPROCESS (proc)->gnutls_cred_type = type;

  GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_SET;

972
  ret = emacs_gnutls_handshake (XPROCESS (proc));
973

974 975 976 977 978 979 980 981 982
  if (ret < GNUTLS_E_SUCCESS)
    return gnutls_make_error (ret);

  /* Now verify the peer, following
     http://www.gnu.org/software/gnutls/manual/html_node/Verifying-peer_0027s-certificate.html.
     The peer should present at least one certificate in the chain; do a
     check of the certificate's hostname with
     gnutls_x509_crt_check_hostname() against :hostname.  */

983
  ret = fn_gnutls_certificate_verify_peers2 (state, &peer_verification);
984 985 986

  if (ret < GNUTLS_E_SUCCESS)
    return gnutls_make_error (ret);
Paul Eggert's avatar
Paul Eggert committed
987

988
  if (XINT (loglevel) > 0 && peer_verification & GNUTLS_CERT_INVALID)
Paul Eggert's avatar
Paul Eggert committed
989
    message ("%s certificate could not be verified.",
990
             c_hostname);
Paul Eggert's avatar
Paul Eggert committed
991

992 993 994
 if (peer_verification & GNUTLS_CERT_REVOKED)
   GNUTLS_LOG2 (1, max_log_level, "certificate was revoked (CRL):",
                c_hostname);
Paul Eggert's avatar
Paul Eggert committed
995

996 997 998
 if (peer_verification & GNUTLS_CERT_SIGNER_NOT_FOUND)
   GNUTLS_LOG2 (1, max_log_level, "certificate signer was not found:",
                c_hostname);
Paul Eggert's avatar
Paul Eggert committed
999

1000