url-vars.el (url-mime-charset-string): Changed the default to nil to
[bpt/emacs.git] / src / gnutls.c
CommitLineData
8af55556
TZ
1/* GnuTLS glue for GNU Emacs.
2 Copyright (C) 2010 Free Software Foundation, Inc.
3
4This file is part of GNU Emacs.
5
6GNU Emacs is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11GNU Emacs is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18
19#include <config.h>
20#include <errno.h>
21#include <setjmp.h>
22
23#include "lisp.h"
24#include "process.h"
25
26#ifdef HAVE_GNUTLS
27#include <gnutls/gnutls.h>
28
29Lisp_Object Qgnutls_code;
30Lisp_Object Qgnutls_anon, Qgnutls_x509pki;
31Lisp_Object Qgnutls_e_interrupted, Qgnutls_e_again,
32 Qgnutls_e_invalid_session, Qgnutls_e_not_ready_for_handshake;
33int global_initialized;
34
74f1829d 35static void
bac5cef8
LMI
36emacs_gnutls_handshake (struct Lisp_Process *proc)
37{
38 gnutls_session_t state = proc->gnutls_state;
39 int ret;
40
41 if (proc->gnutls_initstage < GNUTLS_STAGE_HANDSHAKE_CANDO)
42 return;
43
44 if (proc->gnutls_initstage < GNUTLS_STAGE_TRANSPORT_POINTERS_SET)
e6059fa2
LMI
45 {
46 gnutls_transport_set_ptr2 (state,
47 (gnutls_transport_ptr_t) (long) proc->infd,
48 (gnutls_transport_ptr_t) (long) proc->outfd);
bac5cef8 49
e6059fa2
LMI
50 proc->gnutls_initstage = GNUTLS_STAGE_TRANSPORT_POINTERS_SET;
51 }
bac5cef8
LMI
52
53 ret = gnutls_handshake (state);
54 proc->gnutls_initstage = GNUTLS_STAGE_HANDSHAKE_TRIED;
55
56 if (ret == GNUTLS_E_SUCCESS)
e6059fa2
LMI
57 {
58 /* here we're finally done. */
59 proc->gnutls_initstage = GNUTLS_STAGE_READY;
60 }
bac5cef8
LMI
61}
62
8af55556 63int
df7fcaff 64emacs_gnutls_write (int fildes, struct Lisp_Process *proc, char *buf,
8af55556
TZ
65 unsigned int nbyte)
66{
67 register int rtnval, bytes_written;
df7fcaff
LMI
68 gnutls_session_t state = proc->gnutls_state;
69
70 if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
e6059fa2 71 return -1;
8af55556
TZ
72
73 bytes_written = 0;
74
75 while (nbyte > 0)
76 {
77 rtnval = gnutls_write (state, buf, nbyte);
78
79 if (rtnval == -1)
80 {
81 if (errno == EINTR)
82 continue;
83 else
84 return (bytes_written ? bytes_written : -1);
85 }
86
87 buf += rtnval;
88 nbyte -= rtnval;
89 bytes_written += rtnval;
90 }
91 fsync (STDOUT_FILENO);
92
93 return (bytes_written);
94}
95
96int
df7fcaff 97emacs_gnutls_read (int fildes, struct Lisp_Process *proc, char *buf,
8af55556
TZ
98 unsigned int nbyte)
99{
100 register int rtnval;
df7fcaff
LMI
101 gnutls_session_t state = proc->gnutls_state;
102
e6059fa2
LMI
103 if (proc->gnutls_initstage != GNUTLS_STAGE_READY)
104 {
105 emacs_gnutls_handshake (proc);
106 return -1;
107 }
8af55556 108
ec9f09be
LMI
109 rtnval = gnutls_read (state, buf, nbyte);
110 if (rtnval >= 0)
111 return rtnval;
112 else
df7fcaff 113 return 0;
8af55556
TZ
114}
115
116/* convert an integer error to a Lisp_Object; it will be either a
117 known symbol like `gnutls_e_interrupted' and `gnutls_e_again' or
118 simply the integer value of the error. GNUTLS_E_SUCCESS is mapped
119 to Qt. */
74f1829d
JB
120static Lisp_Object
121gnutls_make_error (int error)
8af55556
TZ
122{
123 switch (error)
e6059fa2
LMI
124 {
125 case GNUTLS_E_SUCCESS:
126 return Qt;
127 case GNUTLS_E_AGAIN:
128 return Qgnutls_e_again;
129 case GNUTLS_E_INTERRUPTED:
130 return Qgnutls_e_interrupted;
131 case GNUTLS_E_INVALID_SESSION:
132 return Qgnutls_e_invalid_session;
133 }
8af55556
TZ
134
135 return make_number (error);
136}
137
138DEFUN ("gnutls-get-initstage", Fgnutls_get_initstage, Sgnutls_get_initstage, 1, 1, 0,
74f1829d 139 doc: /* Return the GnuTLS init stage of process PROC.
8af55556 140See also `gnutls-boot'. */)
74f1829d 141 (Lisp_Object proc)
8af55556
TZ
142{
143 CHECK_PROCESS (proc);
144
145 return make_number (GNUTLS_INITSTAGE (proc));
146}
147
148DEFUN ("gnutls-errorp", Fgnutls_errorp, Sgnutls_errorp, 1, 1, 0,
74f1829d
JB
149 doc: /* Return t if ERROR indicates a GnuTLS problem.
150ERROR is an integer or a symbol with an integer `gnutls-code' property.
151usage: (gnutls-errorp ERROR) */)
152 (Lisp_Object err)
8af55556 153{
74f1829d 154 if (EQ (err, Qt)) return Qnil;
8af55556
TZ
155
156 return Qt;
157}
158
159DEFUN ("gnutls-error-fatalp", Fgnutls_error_fatalp, Sgnutls_error_fatalp, 1, 1, 0,
74f1829d
JB
160 doc: /* Check if ERROR is fatal.
161ERROR is an integer or a symbol with an integer `gnutls-code' property.
162usage: (gnutls-error-fatalp ERROR) */)
163 (Lisp_Object err)
8af55556
TZ
164{
165 Lisp_Object code;
166
167 if (EQ (err, Qt)) return Qnil;
168
169 if (SYMBOLP (err))
8af55556 170 {
e6059fa2
LMI
171 code = Fget (err, Qgnutls_code);
172 if (NUMBERP (code))
173 {
174 err = code;
175 }
176 else
177 {
178 error ("Symbol has no numeric gnutls-code property");
179 }
8af55556 180 }
8af55556
TZ
181
182 if (!NUMBERP (err))
183 error ("Not an error symbol or code");
184
185 if (0 == gnutls_error_is_fatal (XINT (err)))
186 return Qnil;
187
188 return Qt;
189}
190
191DEFUN ("gnutls-error-string", Fgnutls_error_string, Sgnutls_error_string, 1, 1, 0,
74f1829d
JB
192 doc: /* Return a description of ERROR.
193ERROR is an integer or a symbol with an integer `gnutls-code' property.
194usage: (gnutls-error-string ERROR) */)
195 (Lisp_Object err)
8af55556
TZ
196{
197 Lisp_Object code;
198
199 if (EQ (err, Qt)) return build_string ("Not an error");
200
201 if (SYMBOLP (err))
8af55556 202 {
e6059fa2
LMI
203 code = Fget (err, Qgnutls_code);
204 if (NUMBERP (code))
205 {
206 err = code;
207 }
208 else
209 {
210 return build_string ("Symbol has no numeric gnutls-code property");
211 }
8af55556 212 }
8af55556
TZ
213
214 if (!NUMBERP (err))
215 return build_string ("Not an error symbol or code");
216
217 return build_string (gnutls_strerror (XINT (err)));
218}
219
220DEFUN ("gnutls-deinit", Fgnutls_deinit, Sgnutls_deinit, 1, 1, 0,
74f1829d 221 doc: /* Deallocate GNU TLS resources associated with process PROC.
8af55556 222See also `gnutls-init'. */)
74f1829d 223 (Lisp_Object proc)
8af55556
TZ
224{
225 gnutls_session_t state;
226
227 CHECK_PROCESS (proc);
228 state = XPROCESS (proc)->gnutls_state;
229
230 if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_INIT)
e6059fa2 231 {
8af55556
TZ
232 gnutls_deinit (state);
233 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT - 1;
e6059fa2 234 }
8af55556
TZ
235
236 return Qt;
237}
238
239/* Initializes global GNU TLS state to defaults.
240Call `gnutls-global-deinit' when GNU TLS usage is no longer needed.
241Returns zero on success. */
74f1829d
JB
242static Lisp_Object
243gnutls_emacs_global_init (void)
8af55556
TZ
244{
245 int ret = GNUTLS_E_SUCCESS;
246
247 if (!global_initialized)
248 ret = gnutls_global_init ();
249
250 global_initialized = 1;
251
252 return gnutls_make_error (ret);
253}
254
255/* Deinitializes global GNU TLS state.
256See also `gnutls-global-init'. */
74f1829d
JB
257static Lisp_Object
258gnutls_emacs_global_deinit (void)
8af55556
TZ
259{
260 if (global_initialized)
261 gnutls_global_deinit ();
262
263 global_initialized = 0;
264
265 return gnutls_make_error (GNUTLS_E_SUCCESS);
266}
267
74f1829d
JB
268static void
269gnutls_log_function (int level, const char* string)
8ed70bf3 270{
74f1829d 271 message ("gnutls.c: [%d] %s", level, string);
d2e9d0bb
LMI
272}
273
8ed70bf3 274DEFUN ("gnutls-boot", Fgnutls_boot, Sgnutls_boot, 3, 7, 0,
74f1829d 275 doc: /* Initialize client-mode GnuTLS for process PROC.
8af55556
TZ
276Currently only client mode is supported. Returns a success/failure
277value you can check with `gnutls-errorp'.
278
74f1829d 279PRIORITY-STRING is a string describing the priority.
8af55556
TZ
280TYPE is either `gnutls-anon' or `gnutls-x509pki'.
281TRUSTFILE is a PEM encoded trust file for `gnutls-x509pki'.
282KEYFILE is ... for `gnutls-x509pki' (TODO).
283CALLBACK is ... for `gnutls-x509pki' (TODO).
8ed70bf3
LMI
284LOGLEVEL is the debug level requested from GnuTLS, try 4.
285
286LOGLEVEL will be set for this process AND globally for GnuTLS. So if
287you set it higher or lower at any point, it affects global debugging.
8af55556
TZ
288
289Note that the priority is set on the client. The server does not use
290the protocols's priority except for disabling protocols that were not
291specified.
292
74f1829d 293Processes must be initialized with this function before other GnuTLS
8af55556
TZ
294functions are used. This function allocates resources which can only
295be deallocated by calling `gnutls-deinit' or by calling it again.
296
297Each authentication type may need additional information in order to
298work. For X.509 PKI (`gnutls-x509pki'), you need TRUSTFILE and
299KEYFILE and optionally CALLBACK. */)
74f1829d
JB
300 (Lisp_Object proc, Lisp_Object priority_string, Lisp_Object type,
301 Lisp_Object trustfile, Lisp_Object keyfile, Lisp_Object callback,
302 Lisp_Object loglevel)
8af55556
TZ
303{
304 int ret = GNUTLS_E_SUCCESS;
305
8ed70bf3
LMI
306 int max_log_level = 0;
307
8af55556
TZ
308 /* TODO: GNUTLS_X509_FMT_DER is also an option. */
309 int file_format = GNUTLS_X509_FMT_PEM;
310
311 gnutls_session_t state;
312 gnutls_certificate_credentials_t x509_cred;
313 gnutls_anon_client_credentials_t anon_cred;
8af55556
TZ
314 Lisp_Object global_init;
315
316 CHECK_PROCESS (proc);
317 CHECK_SYMBOL (type);
318 CHECK_STRING (priority_string);
319
320 state = XPROCESS (proc)->gnutls_state;
df7fcaff 321 XPROCESS (proc)->gnutls_p = 1;
8af55556 322
8ed70bf3
LMI
323 if (NUMBERP (loglevel))
324 {
8ed70bf3
LMI
325 gnutls_global_set_log_function (gnutls_log_function);
326 gnutls_global_set_log_level (XINT (loglevel));
327 max_log_level = XINT (loglevel);
328 XPROCESS (proc)->gnutls_log_level = max_log_level;
329 }
df7fcaff 330
8af55556
TZ
331 /* always initialize globals. */
332 global_init = gnutls_emacs_global_init ();
333 if (! NILP (Fgnutls_errorp (global_init)))
334 return global_init;
335
336 /* deinit and free resources. */
337 if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_CRED_ALLOC)
e6059fa2 338 {
8ed70bf3
LMI
339 GNUTLS_LOG (1, max_log_level, "deallocating credentials");
340
8af55556 341 if (EQ (type, Qgnutls_x509pki))
e6059fa2 342 {
8ed70bf3
LMI
343 GNUTLS_LOG (2, max_log_level, "deallocating x509 credentials");
344 x509_cred = XPROCESS (proc)->gnutls_x509_cred;
8af55556 345 gnutls_certificate_free_credentials (x509_cred);
e6059fa2 346 }
8af55556 347 else if (EQ (type, Qgnutls_anon))
e6059fa2 348 {
8ed70bf3
LMI
349 GNUTLS_LOG (2, max_log_level, "deallocating anon credentials");
350 anon_cred = XPROCESS (proc)->gnutls_anon_cred;
8af55556 351 gnutls_anon_free_client_credentials (anon_cred);
e6059fa2 352 }
8af55556 353 else
e6059fa2 354 {
8af55556
TZ
355 error ("unknown credential type");
356 ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
e6059fa2 357 }
8af55556
TZ
358
359 if (GNUTLS_INITSTAGE (proc) >= GNUTLS_STAGE_INIT)
e6059fa2 360 {
8ed70bf3 361 GNUTLS_LOG (1, max_log_level, "deallocating x509 credentials");
8af55556 362 Fgnutls_deinit (proc);
e6059fa2
LMI
363 }
364 }
8af55556
TZ
365
366 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_EMPTY;
367
8ed70bf3
LMI
368 GNUTLS_LOG (1, max_log_level, "allocating credentials");
369
8af55556 370 if (EQ (type, Qgnutls_x509pki))
e6059fa2 371 {
8ed70bf3
LMI
372 GNUTLS_LOG (2, max_log_level, "allocating x509 credentials");
373 x509_cred = XPROCESS (proc)->gnutls_x509_cred;
8af55556
TZ
374 if (gnutls_certificate_allocate_credentials (&x509_cred) < 0)
375 memory_full ();
e6059fa2 376 }
8af55556 377 else if (EQ (type, Qgnutls_anon))
e6059fa2 378 {
8ed70bf3
LMI
379 GNUTLS_LOG (2, max_log_level, "allocating anon credentials");
380 anon_cred = XPROCESS (proc)->gnutls_anon_cred;
8af55556
TZ
381 if (gnutls_anon_allocate_client_credentials (&anon_cred) < 0)
382 memory_full ();
e6059fa2 383 }
8af55556 384 else
e6059fa2 385 {
8af55556
TZ
386 error ("unknown credential type");
387 ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
e6059fa2 388 }
8af55556
TZ
389
390 if (ret < GNUTLS_E_SUCCESS)
e6059fa2 391 return gnutls_make_error (ret);
8af55556
TZ
392
393 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_ALLOC;
394
8af55556 395 if (EQ (type, Qgnutls_x509pki))
e6059fa2 396 {
8af55556 397 if (STRINGP (trustfile))
e6059fa2 398 {
8ed70bf3 399 GNUTLS_LOG (1, max_log_level, "setting the trustfile");
8af55556
TZ
400 ret = gnutls_certificate_set_x509_trust_file
401 (x509_cred,
a6f3211f 402 SDATA (trustfile),
8af55556
TZ
403 file_format);
404
405 if (ret < GNUTLS_E_SUCCESS)
406 return gnutls_make_error (ret);
e6059fa2 407 }
8af55556 408
8af55556 409 if (STRINGP (keyfile))
e6059fa2 410 {
8ed70bf3 411 GNUTLS_LOG (1, max_log_level, "setting the keyfile");
8af55556
TZ
412 ret = gnutls_certificate_set_x509_crl_file
413 (x509_cred,
a6f3211f 414 SDATA (keyfile),
8af55556
TZ
415 file_format);
416
417 if (ret < GNUTLS_E_SUCCESS)
418 return gnutls_make_error (ret);
e6059fa2
LMI
419 }
420 }
8af55556
TZ
421
422 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_FILES;
423
8ed70bf3
LMI
424 GNUTLS_LOG (1, max_log_level, "gnutls_init");
425
8af55556
TZ
426 ret = gnutls_init (&state, GNUTLS_CLIENT);
427
428 if (ret < GNUTLS_E_SUCCESS)
e6059fa2 429 return gnutls_make_error (ret);
8af55556
TZ
430
431 XPROCESS (proc)->gnutls_state = state;
432
433 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_INIT;
434
8ed70bf3
LMI
435 GNUTLS_LOG (1, max_log_level, "setting the priority string");
436
74f1829d
JB
437 ret = gnutls_priority_set_direct (state,
438 (char*) SDATA (priority_string),
439 NULL);
8af55556
TZ
440
441 if (ret < GNUTLS_E_SUCCESS)
e6059fa2 442 return gnutls_make_error (ret);
8af55556
TZ
443
444 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_PRIORITY;
445
8af55556 446 if (EQ (type, Qgnutls_x509pki))
e6059fa2 447 {
8af55556 448 ret = gnutls_cred_set (state, GNUTLS_CRD_CERTIFICATE, x509_cred);
e6059fa2 449 }
8af55556 450 else if (EQ (type, Qgnutls_anon))
e6059fa2 451 {
8af55556 452 ret = gnutls_cred_set (state, GNUTLS_CRD_ANON, anon_cred);
e6059fa2 453 }
8af55556 454 else
e6059fa2 455 {
8af55556
TZ
456 error ("unknown credential type");
457 ret = GNUTLS_EMACS_ERROR_INVALID_TYPE;
e6059fa2 458 }
8af55556
TZ
459
460 if (ret < GNUTLS_E_SUCCESS)
e6059fa2 461 return gnutls_make_error (ret);
8af55556 462
8ed70bf3
LMI
463 XPROCESS (proc)->gnutls_anon_cred = anon_cred;
464 XPROCESS (proc)->gnutls_x509_cred = x509_cred;
8af55556
TZ
465 XPROCESS (proc)->gnutls_cred_type = type;
466
467 GNUTLS_INITSTAGE (proc) = GNUTLS_STAGE_CRED_SET;
468
bac5cef8
LMI
469 emacs_gnutls_handshake (XPROCESS (proc));
470
8af55556
TZ
471 return gnutls_make_error (GNUTLS_E_SUCCESS);
472}
473
474DEFUN ("gnutls-bye", Fgnutls_bye,
475 Sgnutls_bye, 2, 2, 0,
74f1829d 476 doc: /* Terminate current GnuTLS connection for process PROC.
8af55556
TZ
477The connection should have been initiated using `gnutls-handshake'.
478
479If CONT is not nil the TLS connection gets terminated and further
74f1829d 480receives and sends will be disallowed. If the return value is zero you
8af55556
TZ
481may continue using the connection. If CONT is nil, GnuTLS actually
482sends an alert containing a close request and waits for the peer to
483reply with the same message. In order to reuse the connection you
484should wait for an EOF from the peer.
485
486This function may also return `gnutls-e-again', or
487`gnutls-e-interrupted'. */)
488 (Lisp_Object proc, Lisp_Object cont)
489{
490 gnutls_session_t state;
491 int ret;
492
493 CHECK_PROCESS (proc);
494
495 state = XPROCESS (proc)->gnutls_state;
496
497 ret = gnutls_bye (state,
498 NILP (cont) ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
499
500 return gnutls_make_error (ret);
501}
502
8af55556
TZ
503void
504syms_of_gnutls (void)
505{
506 global_initialized = 0;
507
508 Qgnutls_code = intern_c_string ("gnutls-code");
509 staticpro (&Qgnutls_code);
510
511 Qgnutls_anon = intern_c_string ("gnutls-anon");
512 staticpro (&Qgnutls_anon);
513
514 Qgnutls_x509pki = intern_c_string ("gnutls-x509pki");
515 staticpro (&Qgnutls_x509pki);
516
517 Qgnutls_e_interrupted = intern_c_string ("gnutls-e-interrupted");
518 staticpro (&Qgnutls_e_interrupted);
519 Fput (Qgnutls_e_interrupted, Qgnutls_code,
520 make_number (GNUTLS_E_INTERRUPTED));
521
522 Qgnutls_e_again = intern_c_string ("gnutls-e-again");
523 staticpro (&Qgnutls_e_again);
524 Fput (Qgnutls_e_again, Qgnutls_code,
525 make_number (GNUTLS_E_AGAIN));
526
527 Qgnutls_e_invalid_session = intern_c_string ("gnutls-e-invalid-session");
528 staticpro (&Qgnutls_e_invalid_session);
529 Fput (Qgnutls_e_invalid_session, Qgnutls_code,
530 make_number (GNUTLS_E_INVALID_SESSION));
531
532 Qgnutls_e_not_ready_for_handshake =
533 intern_c_string ("gnutls-e-not-ready-for-handshake");
534 staticpro (&Qgnutls_e_not_ready_for_handshake);
535 Fput (Qgnutls_e_not_ready_for_handshake, Qgnutls_code,
536 make_number (GNUTLS_E_APPLICATION_ERROR_MIN));
537
538 defsubr (&Sgnutls_get_initstage);
539 defsubr (&Sgnutls_errorp);
540 defsubr (&Sgnutls_error_fatalp);
541 defsubr (&Sgnutls_error_string);
542 defsubr (&Sgnutls_boot);
543 defsubr (&Sgnutls_deinit);
8af55556
TZ
544 defsubr (&Sgnutls_bye);
545}
546#endif