comparison src/gnutls.c @ 110584:9d94d76ce611

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