Mercurial > emacs
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 |