access: http: only warn on deflate errors
[vlc.git] / modules / misc / gnutls.c
blobb3227c48bea6f4f883f38d5b8d431cec0f0355e1
1 /*****************************************************************************
2 * gnutls.c
3 *****************************************************************************
4 * Copyright (C) 2004-2012 Rémi Denis-Courmont
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program 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 Lesser General Public License for more details.
16 * You should have received a copy of the GNU Öesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 /*****************************************************************************
22 * Preamble
23 *****************************************************************************/
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
29 #include <time.h>
30 #include <errno.h>
31 #include <assert.h>
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_tls.h>
36 #include <vlc_block.h>
37 #include <vlc_dialog.h>
39 #include <gnutls/gnutls.h>
40 #include <gnutls/x509.h>
41 #include "dhparams.h"
43 /*****************************************************************************
44 * Module descriptor
45 *****************************************************************************/
46 static int OpenClient (vlc_tls_creds_t *);
47 static void CloseClient (vlc_tls_creds_t *);
48 static int OpenServer (vlc_tls_creds_t *, const char *, const char *);
49 static void CloseServer (vlc_tls_creds_t *);
51 #define PRIORITIES_TEXT N_("TLS cipher priorities")
52 #define PRIORITIES_LONGTEXT N_("Ciphers, key exchange methods, " \
53 "hash functions and compression methods can be selected. " \
54 "Refer to GNU TLS documentation for detailed syntax.")
55 static const char *const priorities_values[] = {
56 "PERFORMANCE",
57 "NORMAL",
58 "SECURE128",
59 "SECURE256",
60 "EXPORT",
62 static const char *const priorities_text[] = {
63 N_("Performance (prioritize faster ciphers)"),
64 N_("Normal"),
65 N_("Secure 128-bits (exclude 256-bits ciphers)"),
66 N_("Secure 256-bits (prioritize 256-bits ciphers)"),
67 N_("Export (include insecure ciphers)"),
70 vlc_module_begin ()
71 set_shortname( "GNU TLS" )
72 set_description( N_("GNU TLS transport layer security") )
73 set_capability( "tls client", 1 )
74 set_callbacks( OpenClient, CloseClient )
75 set_category( CAT_ADVANCED )
76 set_subcategory( SUBCAT_ADVANCED_NETWORK )
78 add_submodule ()
79 set_description( N_("GNU TLS server") )
80 set_capability( "tls server", 1 )
81 set_category( CAT_ADVANCED )
82 set_subcategory( SUBCAT_ADVANCED_NETWORK )
83 set_callbacks( OpenServer, CloseServer )
85 add_string ("gnutls-priorities", "NORMAL", PRIORITIES_TEXT,
86 PRIORITIES_LONGTEXT, false)
87 change_string_list (priorities_values, priorities_text)
88 vlc_module_end ()
90 static vlc_mutex_t gnutls_mutex = VLC_STATIC_MUTEX;
92 /**
93 * Initializes GnuTLS with proper locking.
94 * @return VLC_SUCCESS on success, a VLC error code otherwise.
96 static int gnutls_Init (vlc_object_t *p_this)
98 int ret = VLC_EGENERIC;
100 vlc_mutex_lock (&gnutls_mutex);
101 if (gnutls_global_init ())
103 msg_Err (p_this, "cannot initialize GnuTLS");
104 goto error;
107 const char *psz_version = gnutls_check_version ("3.0.20");
108 if (psz_version == NULL)
110 msg_Err (p_this, "unsupported GnuTLS version");
111 gnutls_global_deinit ();
112 goto error;
115 msg_Dbg (p_this, "GnuTLS v%s initialized", psz_version);
116 ret = VLC_SUCCESS;
118 error:
119 vlc_mutex_unlock (&gnutls_mutex);
120 return ret;
125 * Deinitializes GnuTLS.
127 static void gnutls_Deinit (vlc_object_t *p_this)
129 vlc_mutex_lock (&gnutls_mutex);
131 gnutls_global_deinit ();
132 msg_Dbg (p_this, "GnuTLS deinitialized");
133 vlc_mutex_unlock (&gnutls_mutex);
137 static int gnutls_Error (vlc_object_t *obj, int val)
139 switch (val)
141 case GNUTLS_E_AGAIN:
142 #ifdef WIN32
143 WSASetLastError (WSAEWOULDBLOCK);
144 #else
145 errno = EAGAIN;
146 #endif
147 break;
149 case GNUTLS_E_INTERRUPTED:
150 #ifdef WIN32
151 WSASetLastError (WSAEINTR);
152 #else
153 errno = EINTR;
154 #endif
155 break;
157 default:
158 msg_Err (obj, "%s", gnutls_strerror (val));
159 #ifndef NDEBUG
160 if (!gnutls_error_is_fatal (val))
161 msg_Err (obj, "Error above should be handled");
162 #endif
163 #ifdef WIN32
164 WSASetLastError (WSAECONNRESET);
165 #else
166 errno = ECONNRESET;
167 #endif
169 return -1;
171 #define gnutls_Error(o, val) gnutls_Error(VLC_OBJECT(o), val)
173 struct vlc_tls_sys
175 gnutls_session_t session;
176 bool handshaked;
181 * Sends data through a TLS session.
183 static int gnutls_Send (void *opaque, const void *buf, size_t length)
185 vlc_tls_t *session = opaque;
186 vlc_tls_sys_t *sys = session->sys;
188 int val = gnutls_record_send (sys->session, buf, length);
189 return (val < 0) ? gnutls_Error (session, val) : val;
194 * Receives data through a TLS session.
196 static int gnutls_Recv (void *opaque, void *buf, size_t length)
198 vlc_tls_t *session = opaque;
199 vlc_tls_sys_t *sys = session->sys;
201 int val = gnutls_record_recv (sys->session, buf, length);
202 return (val < 0) ? gnutls_Error (session, val) : val;
207 * Starts or continues the TLS handshake.
209 * @return -1 on fatal error, 0 on successful handshake completion,
210 * 1 if more would-be blocking recv is needed,
211 * 2 if more would-be blocking send is required.
213 static int gnutls_ContinueHandshake (vlc_tls_t *session, const char *host,
214 const char *service)
216 vlc_tls_sys_t *sys = session->sys;
217 int val;
219 #ifdef WIN32
220 WSASetLastError (0);
221 #endif
224 val = gnutls_handshake (sys->session);
225 msg_Dbg (session, "TLS handshake: %s", gnutls_strerror (val));
227 if ((val == GNUTLS_E_AGAIN) || (val == GNUTLS_E_INTERRUPTED))
228 /* I/O event: return to caller's poll() loop */
229 return 1 + gnutls_record_get_direction (sys->session);
231 while (val < 0 && !gnutls_error_is_fatal (val));
233 if (val < 0)
235 #ifdef WIN32
236 msg_Dbg (session, "Winsock error %d", WSAGetLastError ());
237 #endif
238 msg_Err (session, "TLS handshake error: %s", gnutls_strerror (val));
239 return -1;
242 sys->handshaked = true;
243 (void) host; (void) service;
244 return 0;
249 * Looks up certificate in known hosts data base.
250 * @return 0 on success, -1 on failure.
252 static int gnutls_CertSearch (vlc_tls_t *obj, const char *host,
253 const char *service,
254 const gnutls_datum_t *restrict datum)
256 assert (host != NULL);
258 /* Look up mismatching certificate in store */
259 int val = gnutls_verify_stored_pubkey (NULL, NULL, host, service,
260 GNUTLS_CRT_X509, datum, 0);
261 const char *msg;
262 switch (val)
264 case 0:
265 msg_Dbg (obj, "certificate key match for %s", host);
266 return 0;
267 case GNUTLS_E_NO_CERTIFICATE_FOUND:
268 msg_Dbg (obj, "no known certificates for %s", host);
269 msg = N_("You attempted to reach %s. "
270 "However the security certificate presented by the server "
271 "is unknown and could not be authenticated by any trusted "
272 "Certification Authority. "
273 "This problem may be caused by a configuration error "
274 "or an attempt to breach your security or your privacy.\n\n"
275 "If in doubt, abort now.\n");
276 break;
277 case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
278 msg_Dbg (obj, "certificate keys mismatch for %s", host);
279 msg = N_("You attempted to reach %s. "
280 "However the security certificate presented by the server "
281 "changed since the previous visit "
282 "and was not authenticated by any trusted "
283 "Certification Authority. "
284 "This problem may be caused by a configuration error "
285 "or an attempt to breach your security or your privacy.\n\n"
286 "If in doubt, abort now.\n");
287 break;
288 default:
289 msg_Err (obj, "certificate key match error for %s: %s", host,
290 gnutls_strerror (val));
291 return -1;
294 if (dialog_Question (obj, _("Insecure site"), vlc_gettext (msg),
295 _("Abort"), _("View certificate"), NULL, host) != 2)
296 return -1;
298 gnutls_x509_crt_t cert;
299 gnutls_datum_t desc;
301 if (gnutls_x509_crt_init (&cert))
302 return -1;
303 if (gnutls_x509_crt_import (cert, datum, GNUTLS_X509_FMT_DER)
304 || gnutls_x509_crt_print (cert, GNUTLS_CRT_PRINT_ONELINE, &desc))
306 gnutls_x509_crt_deinit (cert);
307 return -1;
309 gnutls_x509_crt_deinit (cert);
311 val = dialog_Question (obj, _("Insecure site"),
312 _("This is the certificate presented by %s:\n%s\n\n"
313 "If in doubt, abort now.\n"),
314 _("Abort"), _("Accept 24 hours"),
315 _("Accept permanently"), host, desc.data);
316 gnutls_free (desc.data);
318 time_t expiry = 0;
319 switch (val)
321 case 2:
322 time (&expiry);
323 expiry += 24 * 60 * 60;
324 case 3:
325 val = gnutls_store_pubkey (NULL, NULL, host, service,
326 GNUTLS_CRT_X509, datum, expiry, 0);
327 if (val)
328 msg_Err (obj, "cannot store X.509 certificate: %s",
329 gnutls_strerror (val));
330 return 0;
332 return -1;
336 static struct
338 unsigned flag;
339 const char msg[29];
340 } cert_errs[] =
342 { GNUTLS_CERT_INVALID, "Certificate not verified" },
343 { GNUTLS_CERT_REVOKED, "Certificate revoked" },
344 { GNUTLS_CERT_SIGNER_NOT_FOUND, "Signer not found" },
345 { GNUTLS_CERT_SIGNER_NOT_CA, "Signer not a CA" },
346 { GNUTLS_CERT_INSECURE_ALGORITHM, "Signature algorithm insecure" },
347 { GNUTLS_CERT_NOT_ACTIVATED, "Certificate not activated" },
348 { GNUTLS_CERT_EXPIRED, "Certificate expired" },
352 static int gnutls_HandshakeAndValidate (vlc_tls_t *session, const char *host,
353 const char *service)
355 vlc_tls_sys_t *sys = session->sys;
357 int val = gnutls_ContinueHandshake (session, host, service);
358 if (val)
359 return val;
361 /* certificates chain verification */
362 unsigned status;
364 val = gnutls_certificate_verify_peers2 (sys->session, &status);
365 if (val)
367 msg_Err (session, "Certificate verification error: %s",
368 gnutls_strerror (val));
369 return -1;
371 if (status)
373 msg_Err (session, "Certificate verification failure (0x%04X)", status);
374 for (size_t i = 0; i < sizeof (cert_errs) / sizeof (cert_errs[0]); i++)
375 if (status & cert_errs[i].flag)
376 msg_Err (session, " * %s", cert_errs[i].msg);
377 if (status & ~(GNUTLS_CERT_INVALID|GNUTLS_CERT_SIGNER_NOT_FOUND))
378 return -1;
381 /* certificate (host)name verification */
382 const gnutls_datum_t *data;
383 unsigned count;
384 data = gnutls_certificate_get_peers (sys->session, &count);
385 if (data == NULL || count == 0)
387 msg_Err (session, "Peer certificate not available");
388 return -1;
390 msg_Dbg (session, "%u certificate(s) in the list", count);
392 if (val || host == NULL)
393 return val;
394 if (status && gnutls_CertSearch (session, host, service, data))
395 return -1;
397 gnutls_x509_crt_t cert;
398 val = gnutls_x509_crt_init (&cert);
399 if (val)
401 msg_Err (session, "X.509 fatal error: %s", gnutls_strerror (val));
402 return -1;
405 val = gnutls_x509_crt_import (cert, data, GNUTLS_X509_FMT_DER);
406 if (val)
408 msg_Err (session, "Certificate import error: %s",
409 gnutls_strerror (val));
410 goto error;
413 val = !gnutls_x509_crt_check_hostname (cert, host);
414 if (val)
416 msg_Err (session, "Certificate does not match \"%s\"", host);
417 val = gnutls_CertSearch (session, host, service, data);
419 error:
420 gnutls_x509_crt_deinit (cert);
421 return val;
424 static int
425 gnutls_SessionPrioritize (vlc_object_t *obj, gnutls_session_t session)
427 char *priorities = var_InheritString (obj, "gnutls-priorities");
428 if (unlikely(priorities == NULL))
429 return VLC_ENOMEM;
431 const char *errp;
432 int val = gnutls_priority_set_direct (session, priorities, &errp);
433 if (val < 0)
435 msg_Err (obj, "cannot set TLS priorities \"%s\": %s", errp,
436 gnutls_strerror (val));
437 val = VLC_EGENERIC;
439 else
440 val = VLC_SUCCESS;
441 free (priorities);
442 return val;
447 * TLS credentials private data
449 struct vlc_tls_creds_sys
451 gnutls_certificate_credentials_t x509_cred;
452 gnutls_dh_params_t dh_params; /* XXX: used for server only */
453 int (*handshake) (vlc_tls_t *, const char *, const char *);
454 /* ^^ XXX: useful for server only */
459 * Terminates TLS session and releases session data.
460 * You still have to close the socket yourself.
462 static void gnutls_SessionClose (vlc_tls_creds_t *crd, vlc_tls_t *session)
464 vlc_tls_sys_t *sys = session->sys;
466 if (sys->handshaked)
467 gnutls_bye (sys->session, GNUTLS_SHUT_WR);
468 gnutls_deinit (sys->session);
470 free (sys);
471 (void) crd;
476 * Initializes a server-side TLS session.
478 static int gnutls_SessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
479 int type, int fd)
481 vlc_tls_sys_t *sys = malloc (sizeof (*session->sys));
482 if (unlikely(sys == NULL))
483 return VLC_ENOMEM;
485 session->sys = sys;
486 session->sock.p_sys = session;
487 session->sock.pf_send = gnutls_Send;
488 session->sock.pf_recv = gnutls_Recv;
489 session->handshake = crd->sys->handshake;
490 sys->handshaked = false;
492 int val = gnutls_init (&sys->session, type);
493 if (val != 0)
495 msg_Err (session, "cannot initialize TLS session: %s",
496 gnutls_strerror (val));
497 free (sys);
498 return VLC_EGENERIC;
501 if (gnutls_SessionPrioritize (VLC_OBJECT (crd), sys->session))
502 goto error;
504 val = gnutls_credentials_set (sys->session, GNUTLS_CRD_CERTIFICATE,
505 crd->sys->x509_cred);
506 if (val < 0)
508 msg_Err (session, "cannot set TLS session credentials: %s",
509 gnutls_strerror (val));
510 goto error;
513 gnutls_transport_set_ptr (sys->session,
514 (gnutls_transport_ptr_t)(intptr_t)fd);
515 return VLC_SUCCESS;
517 error:
518 gnutls_SessionClose (crd, session);
519 return VLC_EGENERIC;
522 static int gnutls_ServerSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
523 int fd, const char *hostname)
525 int val = gnutls_SessionOpen (crd, session, GNUTLS_SERVER, fd);
526 if (val != VLC_SUCCESS)
527 return val;
529 if (session->handshake == gnutls_HandshakeAndValidate)
530 gnutls_certificate_server_set_request (session->sys->session,
531 GNUTLS_CERT_REQUIRE);
532 assert (hostname == NULL);
533 return VLC_SUCCESS;
536 static int gnutls_ClientSessionOpen (vlc_tls_creds_t *crd, vlc_tls_t *session,
537 int fd, const char *hostname)
539 int val = gnutls_SessionOpen (crd, session, GNUTLS_CLIENT, fd);
540 if (val != VLC_SUCCESS)
541 return val;
543 vlc_tls_sys_t *sys = session->sys;
545 /* minimum DH prime bits */
546 gnutls_dh_set_prime_bits (sys->session, 1024);
548 if (likely(hostname != NULL))
549 /* fill Server Name Indication */
550 gnutls_server_name_set (sys->session, GNUTLS_NAME_DNS,
551 hostname, strlen (hostname));
553 return VLC_SUCCESS;
558 * Adds one or more Certificate Authorities to the trusted set.
560 * @param path (UTF-8) path to an X.509 certificates list.
562 * @return -1 on error, 0 on success.
564 static int gnutls_AddCA (vlc_tls_creds_t *crd, const char *path)
566 block_t *block = block_FilePath (path);
567 if (block == NULL)
569 msg_Err (crd, "cannot read trusted CA from %s: %m", path);
570 return VLC_EGENERIC;
573 gnutls_datum_t d = {
574 .data = block->p_buffer,
575 .size = block->i_buffer,
578 int val = gnutls_certificate_set_x509_trust_mem (crd->sys->x509_cred, &d,
579 GNUTLS_X509_FMT_PEM);
580 block_Release (block);
581 if (val < 0)
583 msg_Err (crd, "cannot load trusted CA from %s: %s", path,
584 gnutls_strerror (val));
585 return VLC_EGENERIC;
587 msg_Dbg (crd, " %d trusted CA%s added from %s", val, (val != 1) ? "s" : "",
588 path);
590 /* enables peer's certificate verification */
591 crd->sys->handshake = gnutls_HandshakeAndValidate;
592 return VLC_SUCCESS;
597 * Adds a Certificates Revocation List to be sent to TLS clients.
599 * @param path (UTF-8) path of the CRL file.
601 * @return -1 on error, 0 on success.
603 static int gnutls_AddCRL (vlc_tls_creds_t *crd, const char *path)
605 block_t *block = block_FilePath (path);
606 if (block == NULL)
608 msg_Err (crd, "cannot read CRL from %s: %m", path);
609 return VLC_EGENERIC;
612 gnutls_datum_t d = {
613 .data = block->p_buffer,
614 .size = block->i_buffer,
617 int val = gnutls_certificate_set_x509_crl_mem (crd->sys->x509_cred, &d,
618 GNUTLS_X509_FMT_PEM);
619 block_Release (block);
620 if (val < 0)
622 msg_Err (crd, "cannot add CRL (%s): %s", path, gnutls_strerror (val));
623 return VLC_EGENERIC;
625 msg_Dbg (crd, "%d CRL%s added from %s", val, (val != 1) ? "s" : "", path);
626 return VLC_SUCCESS;
631 * Allocates a whole server's TLS credentials.
633 static int OpenServer (vlc_tls_creds_t *crd, const char *cert, const char *key)
635 int val;
637 if (gnutls_Init (VLC_OBJECT(crd)))
638 return VLC_EGENERIC;
640 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
641 if (unlikely(sys == NULL))
642 goto error;
644 crd->sys = sys;
645 crd->add_CA = gnutls_AddCA;
646 crd->add_CRL = gnutls_AddCRL;
647 crd->open = gnutls_ServerSessionOpen;
648 crd->close = gnutls_SessionClose;
649 /* No certificate validation by default */
650 sys->handshake = gnutls_ContinueHandshake;
652 /* Sets server's credentials */
653 val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
654 if (val != 0)
656 msg_Err (crd, "cannot allocate credentials: %s",
657 gnutls_strerror (val));
658 goto error;
661 block_t *certblock = block_FilePath (cert);
662 if (certblock == NULL)
664 msg_Err (crd, "cannot read certificate chain from %s: %m", cert);
665 return VLC_EGENERIC;
668 block_t *keyblock = block_FilePath (key);
669 if (keyblock == NULL)
671 msg_Err (crd, "cannot read private key from %s: %m", key);
672 block_Release (certblock);
673 return VLC_EGENERIC;
676 gnutls_datum_t pub = {
677 .data = certblock->p_buffer,
678 .size = certblock->i_buffer,
679 }, priv = {
680 .data = keyblock->p_buffer,
681 .size = keyblock->i_buffer,
684 val = gnutls_certificate_set_x509_key_mem (sys->x509_cred, &pub, &priv,
685 GNUTLS_X509_FMT_PEM);
686 block_Release (keyblock);
687 block_Release (certblock);
688 if (val < 0)
690 msg_Err (crd, "cannot load X.509 key: %s", gnutls_strerror (val));
691 gnutls_certificate_free_credentials (sys->x509_cred);
692 goto error;
695 /* FIXME:
696 * - support other cipher suites
698 val = gnutls_dh_params_init (&sys->dh_params);
699 if (val >= 0)
701 const gnutls_datum_t data = {
702 .data = (unsigned char *)dh_params,
703 .size = sizeof (dh_params) - 1,
706 val = gnutls_dh_params_import_pkcs3 (sys->dh_params, &data,
707 GNUTLS_X509_FMT_PEM);
708 if (val == 0)
709 gnutls_certificate_set_dh_params (sys->x509_cred,
710 sys->dh_params);
712 if (val < 0)
714 msg_Err (crd, "cannot initialize DHE cipher suites: %s",
715 gnutls_strerror (val));
718 return VLC_SUCCESS;
720 error:
721 free (sys);
722 gnutls_Deinit (VLC_OBJECT(crd));
723 return VLC_EGENERIC;
727 * Destroys a TLS server object.
729 static void CloseServer (vlc_tls_creds_t *crd)
731 vlc_tls_creds_sys_t *sys = crd->sys;
733 /* all sessions depending on the server are now deinitialized */
734 gnutls_certificate_free_credentials (sys->x509_cred);
735 gnutls_dh_params_deinit (sys->dh_params);
736 free (sys);
738 gnutls_Deinit (VLC_OBJECT(crd));
742 * Initializes a client-side TLS credentials.
744 static int OpenClient (vlc_tls_creds_t *crd)
746 if (gnutls_Init (VLC_OBJECT(crd)))
747 return VLC_EGENERIC;
749 vlc_tls_creds_sys_t *sys = malloc (sizeof (*sys));
750 if (unlikely(sys == NULL))
751 goto error;
753 crd->sys = sys;
754 //crd->add_CA = gnutls_AddCA;
755 //crd->add_CRL = gnutls_AddCRL;
756 crd->open = gnutls_ClientSessionOpen;
757 crd->close = gnutls_SessionClose;
758 sys->handshake = gnutls_HandshakeAndValidate;
760 int val = gnutls_certificate_allocate_credentials (&sys->x509_cred);
761 if (val != 0)
763 msg_Err (crd, "cannot allocate credentials: %s",
764 gnutls_strerror (val));
765 goto error;
768 val = gnutls_certificate_set_x509_system_trust (sys->x509_cred);
769 if (val < 0)
770 msg_Err (crd, "cannot load trusted Certificate Authorities: %s",
771 gnutls_strerror (val));
772 else
773 msg_Dbg (crd, "loaded %d trusted CAs", val);
775 gnutls_certificate_set_verify_flags (sys->x509_cred,
776 GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
778 return VLC_SUCCESS;
779 error:
780 free (sys);
781 gnutls_Deinit (VLC_OBJECT(crd));
782 return VLC_EGENERIC;
785 static void CloseClient (vlc_tls_creds_t *crd)
787 vlc_tls_creds_sys_t *sys = crd->sys;
789 gnutls_certificate_free_credentials (sys->x509_cred);
790 free (sys);
792 gnutls_Deinit (VLC_OBJECT(crd));