/
starttls.c
5595 lines (5373 loc) · 186 KB
/
starttls.c
1
2
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
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
108
109
110
111
112
113
114
115
116
117
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* tlspool/starttls.c -- Setup and validation handler for TLS session */
#include "whoami.h"
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include <ctype.h>
#include <unistd.h>
#include <syslog.h>
#include <errno.h>
#include <gnutls/gnutls.h>
#include <gnutls/pkcs11.h>
#include <gnutls/abstract.h>
#include <gnutls/dane.h>
#include <p11-kit/pkcs11.h>
#include <tlspool/commands.h>
#include <tlspool/internal.h>
#include <libtasn1.h>
#include <krb5.h>
/* Plus, from k5-int.h: */
krb5_error_code KRB5_CALLCONV krb5_decrypt_tkt_part(krb5_context,
const krb5_keyblock *,
krb5_ticket * );
#include <quick-der/api.h>
#include <quick-der/rfc4120.h>
typedef DER_OVLY_rfc4120_Ticket ticket_t;
typedef DER_OVLY_rfc4120_Authenticator authenticator_t;
typedef DER_OVLY_rfc4120_EncryptedData encrypted_data_t;
#include <tlspool/internal.h>
#ifdef WINDOWS_PORT
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <krb5.h>
#ifndef __MINGW64__
#include <arpa/inet.h>
#endif
#include <netinet/in.h>
#endif
#ifdef WINDOWS_PORT
#include <windows.h>
#define RECV_FLAGS 0
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#else /* WINDOWS_PORT */
#define RECV_FLAGS MSG_DONTWAIT | MSG_NOSIGNAL
#endif /* WINDOWS_PORT */
#include "manage.h"
#include "donai.h"
#include "trust.h"
#if EXPECTED_LID_TYPE_COUNT != LID_TYPE_CNT
#error "Set EXPECTED_LID_TYPE_COUNT in <tlspool/internal.h> to match LID_TYPE_CNT"
#endif
/* This module hosts TLS handlers which treat an individual connection.
*
* Initially, the TLS setup is processed, which means validating the
* connection. If and when this succeeds, a continued process is needed
* to encrypt and decrypt traffic while it is in transit.
*
* Every TLS connection (including the attempt to set it up) is hosted in
* its own thread. This means that it can abide time to wait for PINENTRY,
* LOCALID or LIDENTRY responses. It also means a very clear flow when the
* time comes to destroy a connection.
*
* While encrypting and decrypting traffic passing through, the thread
* will use its own poll() call, and thus offload the potentially large
* one of the main thread, which is supposed to be a low-traffic task.
* The set of file descriptors used by the session-handler threads are
* in contrast very small and can easily be started for every single
* packet passing through.
*
* Might the user terminate a process while this one is waiting for a
* callback command request, then the main TLS pool thread will take
* care of taking down this thread. To that end, it sets the followup
* pointer that normally holds a callback response to NULL, and then
* permits this thread to run again. This will lead to a shutdown of
* this process, and proper closing of all connections. The remote peer
* will therefore see the result of a local kill as a connection reset.
*
* In case one of the end points of the connection is terminated, a
* similar thing will happen; the thread will terminate itself after
* a cleanup of any outstanding resources. This, once again, leads
* to passing on the reset of a connection between the encrypted and
* side of the connection.
*/
/*
* GnuTLS infrastructure setup.
* Session-shared DH-keys, credentials structures, and so on.
*/
static gnutls_dh_params_t dh_params;
struct credinfo {
gnutls_credentials_type_t credtp;
void *cred;
};
#define EXPECTED_SRV_CREDCOUNT 3
#define EXPECTED_CLI_CREDCOUNT 3
static struct credinfo srv_creds [EXPECTED_SRV_CREDCOUNT];
static struct credinfo cli_creds [EXPECTED_CLI_CREDCOUNT];
static int srv_credcount = 0;
static int cli_credcount = 0;
static const char const *onthefly_p11uri = "pkcs11:manufacturer=ARPA2.net;token=TLS+Pool+internal;object=on-the-fly+signer;type=private;serial=1";
static unsigned long long onthefly_serial; //TODO: Fill with now * 1000
static gnutls_x509_crt_t onthefly_issuercrt = NULL;
static gnutls_privkey_t onthefly_issuerkey = NULL;
static gnutls_x509_privkey_t onthefly_subjectkey = NULL;
static pthread_mutex_t onthefly_signer_lock = PTHREAD_MUTEX_INITIALIZER;
#ifdef HAVE_TLS_KDH
static krb5_context krbctx_cli, krbctx_srv;
static krb5_keytab krb_kt_cli, krb_kt_srv;
static bool got_cc_cli, got_cc_srv;
static int have_key_tgt_cc (
struct command *cmd, // in, session context
krb5_context ctx, // in, kerberos context
bool use_cc, // in, whether to use cc
krb5_kvno kvno, // in, kvno (0 for highest)
krb5_enctype enctype,// in, enctype (0 for any)
char *p11uri, // in/opt, PKCS #11 pwd URI
krb5_keytab kt, // in/opt, keytab
krb5_keyblock *key, // opt/opt session key
krb5_creds **tgt, // out/opt, tkt granting tkt
krb5_ccache *cc); // out/opt, cred cache
static int have_service_ticket (
struct command *cmd, // in, session context
krb5_context ctx, // in, kerberos context
krb5_ccache cc_opt, // in/opt, credcache
krb5_principal cli, // in, client principal
krb5_creds **ticket);// out/opt, tkt granting tkt
#endif
/* The local variation on the ctlkeynode structure, with TLS-specific fields
*/
struct ctlkeynode_tls {
struct ctlkeynode regent; // Structure for ctlkey_register()
gnutls_session_t session; // Additional data specifically for TLS
pthread_t owner; // For interruption of copycat()
int plainfd; // Plain-side connection
int cryptfd; // Crypt-side connection
};
/* A local structure used for iterating over PKCS #11 entries. This is used
* to iterate over password attempts, no more than MAX_P11ITER_ATTEMPTS though.
*
* When a password is requested but none is available, the password request
* will be passed to the user using the PIN callback mechanism. When this
* is done, a warning may be given that the TLS Pool overtakes control over
* the account (when thusly configured). In support of that option, the
* $attempt is counted and the respective $p11pwd is CK_INVALID_HANDLE.
* TODO: Perhaps interact for saving, such as entering an certain string?
*
* When a number of attempts needs to be made before success, then any
* objects that precede a succeeded $attempt can be removed. The same may
* be true for any objects after it.
*
* This mechanism is useful during password changes. When a new password is
* desired by the KDC, then a random object is created and returned twice.
* To support repeated delivery, the password is stored in $newpwd;
* In this case, the safest choice is still to leave the last $p11pwd.
*
* The caller may decide to invoke the password changing procedure, namely
* after manual entry as evidenced by the condition
* (attempts >= 0) &&
* (attempts < MAX_P11_ITER_ATTEMPTS) &&
* (p11pwd [attempt] == CK_INVALID_HANDLE)
*
* TODO: This is a designed data structure, but not yet installed.
*
* TODO: It is more useful to abolish passwords, and truly use PKCS #11.
*/
#define MAX_P11ITER_ATTEMPTS 3
struct pkcs11iter {
struct command *cmd; // The session command structure
CK_SESSION_HANDLE p11ses; // The PKCS #11 session in motion
int attempt; // Starts at -1, incremented by pwd entry
CK_OBJECT_HANDLE p11pwd [MAX_P11ITER_ATTEMPTS];
// Sequence of $attempt objects returned
CK_OBJECT_HANDLE newpwd; // Set when a new password was offered
};
/* The list of accepted Exporter Label Prefixes for starttls_prng()
*/
char *tlsprng_label_prefixes [] = {
// Forbidden by RFC 5705: "client finished",
// Forbidden by RFC 5705: "server finished",
// Forbidden by RFC 5705: "master secret",
// Forbidden by RFC 5705: "key expansion",
"client EAP encryption", // not suited for DTLS
"ttls keying material", // not suited for DTLS
"ttls challenge", // not suited for DTLS
"EXTRACTOR-dtls_srtp",
"EXPORTER_DTLS_OVER_SCTP",
"EXPORTER-ETSI-TC-M2M-Bootstrap",
"EXPORTER-ETSI-TC-M2M-Connection",
"TLS_MK_Extr",
"EXPORTER_GBA_Digest",
"EXPORTER: teap session key seed", // not suited for DTLS
"EXPORTER-oneM2M-Bootstrap",
"EXPORTER-oneM2M-Connection",
NULL
};
/* The registry with the service names that are deemed safe for an
* anonymous precursor phase; that is, the service names that may offer
* ANON-DH initially, and immediately renegotiate an authenticated
* connection. See doc/anonymising-precursor.* for more information.
*
* The registry is ordered by case-independent service name, so it can
* be searched in 2log time. Service names are as defined by IANA in the
* "Service Name and Transport Protocol Port Number Registry".
*
* The entries in the registry depend on the role played; either as a
* client or as a server. This refers to the local node, and depends on
* uncertainty of the remote party's TLS implementation and whether or
* not the protocol could lead to the remote sending information that
* requires authentication before the secure renogiation into an
* authenticated connection has been completed by this side. This is
* a protocol-dependent matter and the registry provided here serves to
* encapsulate this knowledge inside the TLS Pool instead of bothering
* application designers with it. Entries that are not found in the
* registry are interpreted as not allowing an anonymising precursor.
*
* Note that ANONPRE_EXTEND_MASTER_SECRET cannot be verified before
* GnuTLS version 3.4.0; see "imap" below for the resulting impact. This
* also impacts dynamic linking, because 3.4.0 introduces the new function
* gnutls_ext_get_data() that is used for this requirement.
*/
#define ANONPRE_FORBID 0x00
#define ANONPRE_CLIENT 0x01
#define ANONPRE_SERVER 0x02
#define ANONPRE_EITHER (ANONPRE_CLIENT | ANONPRE_SERVER)
#define ANONPRE_EXTEND_MASTER_SECRET 0x10
struct anonpre_regentry {
char *service;
uint8_t flags;
};
struct anonpre_regentry anonpre_registry [] = {
/* This registry is commented out for now, although the code to use it seems
* to work fine. GnuTLS however, does not seem to support making the switch
* from ANON-ECDH to an authenticated handshake. Details:
* http://lists.gnutls.org/pipermail/gnutls-help/2015-November/003998.html
*
{ "generic_anonpre", ANONPRE_EITHER }, // Name invalid as per RFC 6335
{ "http", ANONPRE_CLIENT }, // Server also if it ignores client ID
#if GNUTLS_VERSION_NUMBER < 0x030400
{ "imap", ANONPRE_SERVER },
#else
{ "imap", ANONPRE_EITHER | ANONPRE_EXTEND_MASTER_SECRET },
#endif
{ "pop3", ANONPRE_EITHER },
{ "smtp", ANONPRE_EITHER },
*
* End of commenting out the registry
*/
};
const int anonpre_registry_size = sizeof (anonpre_registry) / sizeof (struct anonpre_regentry);
/* The registry of Key Usage and Extended Key Usage for any given service name.
*/
static const char *http_noncrit [] = { GNUTLS_KP_TLS_WWW_SERVER, GNUTLS_KP_TLS_WWW_CLIENT, NULL };
struct svcusage_regentry {
char *service;
unsigned int usage;
const char **oids_non_critical;
const char **oids_critical;
};
struct svcusage_regentry svcusage_registry [] = {
{ "generic_anonpre",
GNUTLS_KEY_KEY_ENCIPHERMENT |
GNUTLS_KEY_KEY_AGREEMENT,
NULL,
NULL
},
{ "http",
GNUTLS_KEY_DIGITAL_SIGNATURE |
GNUTLS_KEY_KEY_ENCIPHERMENT |
GNUTLS_KEY_KEY_AGREEMENT,
http_noncrit,
NULL
},
};
const int svcusage_registry_size = sizeof (svcusage_registry) / sizeof (struct svcusage_regentry);
/* The maximum number of bytes that can be passed over a TLS connection before
* the authentication is complete in case of a anonymous precursor within a
* protocol that ensures that this cannot be a problem.
*/
int maxpreauth;
/* The priorities cache for "NORMAL" -- used to preconfigure the server,
* actually to overcome its unwillingness to perform the handshake, and
* leave it to srv_clienthello() to setup the priority string.
*/
gnutls_priority_t priority_normal;
/* Map a GnuTLS call (usually a function call) to a POSIX errno,
* optionally reporting an errstr to avoid loosing information.
* Retain errno if it already exists.
* Continue if errno differs from 0, GnuTLS may "damage" it even when OK. */
#define E_g2e(errstr,gtlscall) { \
if (gtls_errno == GNUTLS_E_SUCCESS) { \
gtls_errno = (gtlscall); \
if (gtls_errno != GNUTLS_E_SUCCESS) { \
error_gnutls2posix (gtls_errno, errstr); \
} \
} \
}
/* Cleanup when GnuTLS leaves errno damaged but returns no gtls_errno */
#define E_gnutls_clear_errno() { \
if (gtls_errno == GNUTLS_E_SUCCESS) { \
errno = 0; \
} \
}
/* Error number translation, including error string setup. See E_g2e(). */
void error_gnutls2posix (int gtls_errno, char *new_errstr) {
char *errstr;
register int newerrno;
//
// Sanity checks
if (gtls_errno == GNUTLS_E_SUCCESS) {
return;
}
errstr = error_getstring ();
if (errstr != NULL) {
return;
}
//
// Report the textual error
if (new_errstr == NULL) {
new_errstr = "GnuTLS error";
}
tlog (TLOG_TLS, LOG_ERR, "%s: %s",
new_errstr,
gnutls_strerror (gtls_errno));
error_setstring (new_errstr);
//
// Translate error to a POSIX errno value
switch (gtls_errno) {
case GNUTLS_E_SUCCESS:
return;
case GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM:
case GNUTLS_E_UNKNOWN_CIPHER_TYPE:
case GNUTLS_E_UNSUPPORTED_VERSION_PACKET:
case GNUTLS_E_UNWANTED_ALGORITHM:
case GNUTLS_E_UNKNOWN_CIPHER_SUITE:
case GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE:
case GNUTLS_E_X509_UNKNOWN_SAN:
case GNUTLS_E_DH_PRIME_UNACCEPTABLE:
case GNUTLS_E_UNKNOWN_PK_ALGORITHM:
case GNUTLS_E_NO_TEMPORARY_RSA_PARAMS:
case GNUTLS_E_NO_COMPRESSION_ALGORITHMS:
case GNUTLS_E_NO_CIPHER_SUITES:
case GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED:
case GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE:
case GNUTLS_E_UNKNOWN_HASH_ALGORITHM:
case GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE:
case GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE:
case GNUTLS_E_NO_TEMPORARY_DH_PARAMS:
case GNUTLS_E_UNKNOWN_ALGORITHM:
case GNUTLS_E_UNSUPPORTED_SIGNATURE_ALGORITHM:
case GNUTLS_E_UNSAFE_RENEGOTIATION_DENIED:
case GNUTLS_E_X509_UNSUPPORTED_OID:
case GNUTLS_E_CHANNEL_BINDING_NOT_AVAILABLE:
case GNUTLS_E_INCOMPAT_DSA_KEY_WITH_TLS_PROTOCOL:
case GNUTLS_E_ECC_NO_SUPPORTED_CURVES:
case GNUTLS_E_ECC_UNSUPPORTED_CURVE:
case GNUTLS_E_X509_UNSUPPORTED_EXTENSION:
case GNUTLS_E_NO_CERTIFICATE_STATUS:
case GNUTLS_E_NO_APPLICATION_PROTOCOL:
#ifdef GNUTLS_E_NO_SELF_TEST
case GNUTLS_E_NO_SELF_TEST:
#endif
newerrno = EOPNOTSUPP;
break;
case GNUTLS_E_UNEXPECTED_PACKET_LENGTH:
case GNUTLS_E_INVALID_REQUEST:
newerrno = EINVAL;
break;
case GNUTLS_E_INVALID_SESSION:
case GNUTLS_E_REHANDSHAKE:
case GNUTLS_E_CERTIFICATE_KEY_MISMATCH:
newerrno = ENOTCONN;
break;
case GNUTLS_E_PUSH_ERROR:
case GNUTLS_E_PULL_ERROR:
case GNUTLS_E_PREMATURE_TERMINATION:
case GNUTLS_E_SESSION_EOF:
newerrno = ECONNRESET;
break;
case GNUTLS_E_UNEXPECTED_PACKET:
case GNUTLS_E_WARNING_ALERT_RECEIVED:
case GNUTLS_E_FATAL_ALERT_RECEIVED:
case GNUTLS_E_LARGE_PACKET:
case GNUTLS_E_ERROR_IN_FINISHED_PACKET:
case GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET:
case GNUTLS_E_MPI_SCAN_FAILED:
case GNUTLS_E_DECRYPTION_FAILED:
case GNUTLS_E_DECOMPRESSION_FAILED:
case GNUTLS_E_COMPRESSION_FAILED:
case GNUTLS_E_BASE64_DECODING_ERROR:
case GNUTLS_E_MPI_PRINT_FAILED:
case GNUTLS_E_GOT_APPLICATION_DATA:
case GNUTLS_E_RECORD_LIMIT_REACHED:
case GNUTLS_E_ENCRYPTION_FAILED:
case GNUTLS_E_PK_ENCRYPTION_FAILED:
case GNUTLS_E_PK_DECRYPTION_FAILED:
case GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER:
case GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE:
case GNUTLS_E_PKCS1_WRONG_PAD:
case GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION:
case GNUTLS_E_FILE_ERROR:
case GNUTLS_E_ASN1_ELEMENT_NOT_FOUND:
case GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND:
case GNUTLS_E_ASN1_DER_ERROR:
case GNUTLS_E_ASN1_VALUE_NOT_FOUND:
case GNUTLS_E_ASN1_GENERIC_ERROR:
case GNUTLS_E_ASN1_VALUE_NOT_VALID:
case GNUTLS_E_ASN1_TAG_ERROR:
case GNUTLS_E_ASN1_TAG_IMPLICIT:
case GNUTLS_E_ASN1_TYPE_ANY_ERROR:
case GNUTLS_E_ASN1_SYNTAX_ERROR:
case GNUTLS_E_ASN1_DER_OVERFLOW:
case GNUTLS_E_TOO_MANY_EMPTY_PACKETS:
case GNUTLS_E_TOO_MANY_HANDSHAKE_PACKETS:
case GNUTLS_E_SRP_PWD_PARSING_ERROR:
case GNUTLS_E_BASE64_ENCODING_ERROR:
case GNUTLS_E_OPENPGP_KEYRING_ERROR:
case GNUTLS_E_BASE64_UNEXPECTED_HEADER_ERROR:
case GNUTLS_E_OPENPGP_SUBKEY_ERROR:
case GNUTLS_E_CRYPTO_ALREADY_REGISTERED:
case GNUTLS_E_HANDSHAKE_TOO_LARGE:
case GNUTLS_E_BAD_COOKIE:
case GNUTLS_E_PARSING_ERROR:
case GNUTLS_E_CERTIFICATE_LIST_UNSORTED:
case GNUTLS_E_NO_PRIORITIES_WERE_SET:
#ifdef GNUTLS_E_PK_GENERATION_ERROR
case GNUTLS_E_PK_GENERATION_ERROR:
#endif
#ifdef GNUTLS_E_SELF_TEST_ERROR
case GNUTLS_E_SELF_TEST_ERROR:
#endif
#ifdef GNUTLS_E_SOCKETS_INIT_ERROR
case GNUTLS_E_SOCKETS_INIT_ERROR:
#endif
newerrno = EIO;
break;
case GNUTLS_E_MEMORY_ERROR:
case GNUTLS_E_SHORT_MEMORY_BUFFER:
newerrno = ENOMEM;
break;
case GNUTLS_E_AGAIN:
newerrno = EAGAIN;
break;
case GNUTLS_E_EXPIRED:
case GNUTLS_E_TIMEDOUT:
newerrno = ETIMEDOUT;
break;
case GNUTLS_E_DB_ERROR:
#ifdef ENODATA
newerrno = ENODATA;
#else
newerrno = ENOENT;
#endif
break;
case GNUTLS_E_SRP_PWD_ERROR:
case GNUTLS_E_INSUFFICIENT_CREDENTIALS:
case GNUTLS_E_HASH_FAILED:
case GNUTLS_E_PK_SIGN_FAILED:
case GNUTLS_E_CERTIFICATE_ERROR:
case GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION:
case GNUTLS_E_KEY_USAGE_VIOLATION:
case GNUTLS_E_NO_CERTIFICATE_FOUND:
case GNUTLS_E_OPENPGP_UID_REVOKED:
case GNUTLS_E_OPENPGP_GETKEY_FAILED:
case GNUTLS_E_PK_SIG_VERIFY_FAILED:
case GNUTLS_E_ILLEGAL_SRP_USERNAME:
case GNUTLS_E_INVALID_PASSWORD:
case GNUTLS_E_MAC_VERIFY_FAILED:
case GNUTLS_E_IA_VERIFY_FAILED:
case GNUTLS_E_UNKNOWN_SRP_USERNAME:
case GNUTLS_E_OPENPGP_PREFERRED_KEY_ERROR:
case GNUTLS_E_USER_ERROR:
case GNUTLS_E_AUTH_ERROR:
newerrno = EACCES;
break;
case GNUTLS_E_INTERRUPTED:
newerrno = EINTR;
break;
case GNUTLS_E_INTERNAL_ERROR:
case GNUTLS_E_CONSTRAINT_ERROR:
case GNUTLS_E_ILLEGAL_PARAMETER:
newerrno = EINVAL;
break;
case GNUTLS_E_SAFE_RENEGOTIATION_FAILED:
newerrno = ECONNREFUSED;
break;
case GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY:
case GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY:
#ifdef GNUTLS_E_LIB_IN_ERROR_STATE
case GNUTLS_E_LIB_IN_ERROR_STATE:
#endif
newerrno = ENOEXEC;
break;
case GNUTLS_E_RANDOM_FAILED:
newerrno = EBADF;
break;
case GNUTLS_E_CRYPTODEV_IOCTL_ERROR:
case GNUTLS_E_CRYPTODEV_DEVICE_ERROR:
case GNUTLS_E_HEARTBEAT_PONG_RECEIVED:
case GNUTLS_E_HEARTBEAT_PING_RECEIVED:
case GNUTLS_E_PKCS11_ERROR:
case GNUTLS_E_PKCS11_LOAD_ERROR:
case GNUTLS_E_PKCS11_PIN_ERROR:
case GNUTLS_E_PKCS11_SLOT_ERROR:
case GNUTLS_E_LOCKING_ERROR:
case GNUTLS_E_PKCS11_ATTRIBUTE_ERROR:
case GNUTLS_E_PKCS11_DEVICE_ERROR:
case GNUTLS_E_PKCS11_DATA_ERROR:
case GNUTLS_E_PKCS11_UNSUPPORTED_FEATURE_ERROR:
case GNUTLS_E_PKCS11_KEY_ERROR:
case GNUTLS_E_PKCS11_PIN_EXPIRED:
case GNUTLS_E_PKCS11_PIN_LOCKED:
case GNUTLS_E_PKCS11_SESSION_ERROR:
case GNUTLS_E_PKCS11_SIGNATURE_ERROR:
case GNUTLS_E_PKCS11_TOKEN_ERROR:
case GNUTLS_E_PKCS11_USER_ERROR:
case GNUTLS_E_CRYPTO_INIT_FAILED:
case GNUTLS_E_PKCS11_REQUESTED_OBJECT_NOT_AVAILBLE:
case GNUTLS_E_TPM_ERROR:
case GNUTLS_E_TPM_KEY_PASSWORD_ERROR:
case GNUTLS_E_TPM_SRK_PASSWORD_ERROR:
case GNUTLS_E_TPM_SESSION_ERROR:
case GNUTLS_E_TPM_KEY_NOT_FOUND:
case GNUTLS_E_TPM_UNINITIALIZED:
case GNUTLS_E_OCSP_RESPONSE_ERROR:
case GNUTLS_E_RANDOM_DEVICE_ERROR:
#ifdef EREMOTEIO
newerrno = EREMOTEIO;
#else
newerrno = EIO;
#endif
break;
default:
newerrno = EIO;
break;
}
errno = newerrno;
return;
}
/* Generate Diffie-Hellman parameters - for use with DHE
* kx algorithms. TODO: These should be discarded and regenerated
* once a day, once a week or once a month. Depending on the
* security requirements.
*/
static gtls_error generate_dh_params (void) {
unsigned int bits;
int gtls_errno = GNUTLS_E_SUCCESS;
bits = gnutls_sec_param_to_pk_bits (
GNUTLS_PK_DH,
GNUTLS_SEC_PARAM_LEGACY);
//TODO// Acquire DH-params lock
E_g2e ("Failed to initialise DH params",
gnutls_dh_params_init (
&dh_params));
E_g2e ("Failed to generate DH params",
gnutls_dh_params_generate2 (
dh_params,
bits));
//TODO// Release DH-params lock
return gtls_errno;
}
/* Load Diffie-Hellman parameters from file - or generate them when load fails.
*/
static gtls_error load_dh_params (void) {
gnutls_dh_params_t dhp;
gnutls_datum_t pkcs3;
char *filename = cfg_tls_dhparamfile ();
int gtls_errno = GNUTLS_E_SUCCESS;
memset (&pkcs3, 0, sizeof (pkcs3));
if (filename) {
E_g2e ("No PKCS #3 PEM file with DH params",
gnutls_load_file (
filename,
&pkcs3));
E_gnutls_clear_errno ();
E_g2e ("Failed to initialise DH params",
gnutls_dh_params_init (
&dhp));
E_g2e ("Failed to import DH params from PKCS #3 PEM",
gnutls_dh_params_import_pkcs3 (
dhp,
&pkcs3,
GNUTLS_X509_FMT_PEM));
E_gnutls_clear_errno ();
}
if (pkcs3.data != NULL) {
free (pkcs3.data);
}
if (gtls_errno != GNUTLS_E_SUCCESS) {
//
// File failed to load, so try to generate fresh DH params
int gtls_errno_stack0;
gtls_errno = GNUTLS_E_SUCCESS;
tlog (TLOG_CRYPTO, LOG_DEBUG, "Failed to load DH params from %s; generating fresh parameters", filename);
E_g2e ("Failed to generate DH params",
generate_dh_params ());
gtls_errno_stack0 = gtls_errno;
//TODO// Acquire DH-params lock
E_g2e ("Failed to format DH params as PKCS #3 PEM",
gnutls_dh_params_export2_pkcs3 (
dh_params,
GNUTLS_X509_FMT_PEM,
&pkcs3));
//TODO// Release DH-params lock
if ((gtls_errno == GNUTLS_E_SUCCESS) && (filename != NULL)) {
FILE *pemf;
//
// Best effor file save -- readback will parse
pemf = fopen (filename, "w");
if (pemf != NULL) {
fwrite (pkcs3.data, 1, pkcs3.size, pemf);
fclose (pemf);
tlog (TLOG_FILES, LOG_DEBUG, "Saved DH params to %s (best-effort)", filename);
}
E_gnutls_clear_errno ();
}
gtls_errno = gtls_errno_stack0;
} else {
gnutls_dh_params_t old_dh;
//TODO// Acquire DH-params lock
old_dh = dh_params;
dh_params = dhp;
//TODO// Release DH-params lock
if (old_dh) {
gnutls_dh_params_deinit (old_dh);
}
}
return gtls_errno;
}
/* Remove DH parameters, to be used during program cleanup. */
static void remove_dh_params (void) {
if (dh_params) {
gnutls_dh_params_deinit (dh_params);
dh_params = NULL;
}
}
/* A log printing function
*/
void log_gnutls (int level, const char *msg) {
tlog (TLOG_TLS, level, "GnuTLS: %s", msg);
}
/* Implement the GnuTLS function for token insertion callback. This function
* refers back to the generic callback for token insertion.
*/
int gnutls_token_callback (void *const userdata,
const char *const label,
unsigned retry) {
if (token_callback (label, retry)) {
return GNUTLS_E_SUCCESS;
} else {
return GNUTLS_E_PKCS11_TOKEN_ERROR;
}
}
/*
* Implement the GnuTLS function for PIN callback. This function calls
* the generic PIN callback operation.
*/
int gnutls_pin_callback (void *userdata,
int attempt,
const char *token_url,
const char *token_label,
unsigned int flags,
char *pin,
size_t pin_max) {
if (flags & GNUTLS_PIN_SO) {
return GNUTLS_E_USER_ERROR;
}
if (pin_callback (attempt, token_url, NULL, pin, pin_max)) {
return 0;
} else {
return GNUTLS_E_PKCS11_PIN_ERROR;
}
}
/* Register a PKCS #11 provider with the GnuTLS environment. */
void starttls_pkcs11_provider (char *p11path) {
unsigned int token_seq = 0;
char *p11uri;
if (gnutls_pkcs11_add_provider (p11path, NULL) != 0) {
fprintf (stderr, "Failed to register PKCS #11 library %s with GnuTLS\n", p11path);
exit (1);
}
while (gnutls_pkcs11_token_get_url (token_seq, 0, &p11uri) == 0) {
#ifdef DEBUG
fprintf (stderr, "DEBUG: Found token URI %s\n", p11uri);
#endif
//TODO// if (gnutls_pkcs11_token_get_info (p11uri, GNUTLS_PKCS11_TOKEN_LABEL-of-SERIAL-of-MANUFACTURER-of-MODEL, output, utput_size) == 0) { ... }
gnutls_free (p11uri);
token_seq++;
}
//TODO// Select token by name (value)
//TODO// if PIN available then set it up
//TODO:WHY?// free_p11pin ();
}
static void cleanup_starttls_credentials (void);/* Defined below */
static void cleanup_starttls_validation (void); /* Defined below */
#ifdef HAVE_TLS_KDH
static void cleanup_starttls_kerberos (void); /* Defined below */
static int setup_starttls_kerberos (void); /* Defined below */
#endif
static int setup_starttls_credentials (void); /* Defined below */
/* The global and static setup function for the starttls functions.
*/
void setup_starttls (void) {
const char *curver;
int gtls_errno = GNUTLS_E_SUCCESS;
char *otfsigcrt, *otfsigkey;
//
// Setup configuration variables
maxpreauth = cfg_tls_maxpreauth ();
//
// Basic library actions
tlog (TLOG_TLS, LOG_DEBUG, "Compiled against GnuTLS version %s", GNUTLS_VERSION);
curver = gnutls_check_version (GNUTLS_VERSION);
tlog (TLOG_TLS, LOG_DEBUG, "Running against %s GnuTLS version %s", curver? "acceptable": "OLDER", curver? curver: gnutls_check_version (NULL));
E_g2e ("GnuTLS global initialisation failed",
gnutls_global_init ());
E_gnutls_clear_errno ();
E_g2e ("GnuTLS PKCS #11 initialisation failed",
gnutls_pkcs11_init (
GNUTLS_PKCS11_FLAG_MANUAL, NULL));
//
// Setup logging / debugging
if (cfg_log_level () == LOG_DEBUG) {
gnutls_global_set_log_function (log_gnutls);
gnutls_global_set_log_level (9);
}
//
// Setup Kerberos
#ifdef HAVE_TLS_KDH
E_g2e ("Kerberos initialisation failed",
setup_starttls_kerberos ());
#endif
//
// Setup callbacks for user communication
gnutls_pkcs11_set_token_function (gnutls_token_callback, NULL);
gnutls_pkcs11_set_pin_function (gnutls_pin_callback, NULL);
//
// Setup DH parameters
E_g2e ("Loading DH params failed",
load_dh_params ());
//
// Setup shared credentials for all client server processes
E_g2e ("Failed to setup GnuTLS callback credentials",
setup_starttls_credentials ());
//
// Parse the default priority string
#ifdef HAVE_TLS_KDH
E_g2e ("Failed to setup NORMAL priority cache",
gnutls_priority_init (&priority_normal,
"NONE:"
"%ASYM_CERT_TYPES:"
"+VERS-TLS-ALL:+VERS-DTLS-ALL:"
"+COMP-NULL:"
"+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:"
"+ANON-ECDH:"
"+ECDHE-KRB:" // +ECDHE-KRB-RSA:+ECDHE-KRB-ECDSA:"
"+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:"
"+CTYPE-SRV-KRB:+CTYPE-SRV-X.509:+CTYPE-SRV-OPENPGP:"
"+CTYPE-CLI-KRB:+CTYPE-CLI-X.509:+CTYPE-CLI-OPENPGP:"
"+SRP:+SRP-RSA:+SRP-DSS",
NULL));
#else
E_g2e ("Failed to setup NORMAL priority cache",
gnutls_priority_init (&priority_normal,
"NONE:"
"+VERS-TLS-ALL:+VERS-DTLS-ALL:"
"+COMP-NULL:+CIPHER-ALL:+CURVE-ALL:+SIGN-ALL:+MAC-ALL:"
"+ANON-ECDH:"
"+ECDHE-RSA:+DHE-RSA:+ECDHE-ECDSA:+DHE-DSS:+RSA:"
"+CTYPE-X.509:+CTYPE-OPENPGP:"
"+SRP:+SRP-RSA:+SRP-DSS",
NULL));
#endif
//
// Try to setup on-the-fly signing key / certificate and gen a certkey
otfsigcrt = cfg_tls_onthefly_signcert ();
otfsigkey = cfg_tls_onthefly_signkey ();
fprintf (stderr, "DEBUG: gtls_errno = %d, otfsigcrt == %s, otfsigkey == %s\n", gtls_errno, otfsigcrt? otfsigcrt: "NULL", otfsigkey? otfsigkey: "NULL");
if ((gtls_errno == GNUTLS_E_SUCCESS) && (otfsigcrt != NULL)) {
FILE *crtfile = NULL;
fprintf (stderr, "DEBUG: gtls_errno==%d when initialising onthefly_issuercrt\n", gtls_errno);
E_g2e ("Failed to initialise on-the-fly issuer certificate structure",
gnutls_x509_crt_init (&onthefly_issuercrt));
if (strncmp (otfsigcrt, "file:", 5) == 0) {
// Provisionary support for the "file:" prefix
otfsigcrt += 5;
}
crtfile = fopen (otfsigcrt, "r");
if (crtfile == NULL) {
E_g2e ("Failed to open on-the-fly issuer certificate file",
GNUTLS_E_FILE_ERROR);
fprintf (stderr, "DEBUG: gtls_errno==%d after failing to open file for onthefly_issuercrt\n", gtls_errno);
} else {
char crt [5001];
size_t len = fread (crt, 1, sizeof (crt), crtfile);
if (ferror (crtfile)) {
E_g2e ("Failed to read on-the-fly issuer certificate from file",
GNUTLS_E_FILE_ERROR);
} else if ((len >= sizeof (crt)) || !feof (crtfile)) {
E_g2e ("Unexpectedly long on-the-fly issuer certificate file",
GNUTLS_E_FILE_ERROR);
} else {
gnutls_datum_t cd = {
.data = crt,
.size = len
};
fprintf (stderr, "DEBUG: gtls_errno==%d before importing onthefly_issuercrt\n", gtls_errno);
E_g2e ("Failed to import on-the-fly certificate from file",
gnutls_x509_crt_import (onthefly_issuercrt, &cd, GNUTLS_X509_FMT_DER));
fprintf (stderr, "DEBUG: gtls_errno==%d after importing onthefly_issuercrt\n", gtls_errno);
}
fclose (crtfile);
}
}
if ((gtls_errno == GNUTLS_E_SUCCESS) && (otfsigkey != NULL)) {
E_g2e ("Failed to initialise on-the-fly issuer private key structure",
gnutls_privkey_init (&onthefly_issuerkey));
fprintf (stderr, "DEBUG: before onthefly p11 import, gtlserrno = %d\n", gtls_errno);
E_g2e ("Failed to import pkcs11: URI into on-the-fly issuer private key",
gnutls_privkey_import_pkcs11_url (onthefly_issuerkey, otfsigkey));
fprintf (stderr, "DEBUG: after onthefly p11 import, gtlserrno = %d\n", gtls_errno);
}
fprintf (stderr, "DEBUG: When it matters, gtls_errno = %d, onthefly_issuercrt %s NULL, onthefly_issuerkey %s NULL\n", gtls_errno, onthefly_issuercrt?"!=":"==", onthefly_issuerkey?"!=":"==");
if ((gtls_errno == GNUTLS_E_SUCCESS) && (onthefly_issuercrt != NULL) && (onthefly_issuerkey != NULL)) {
E_g2e ("Failed to initialise on-the-fly certificate session key",
gnutls_x509_privkey_init (&onthefly_subjectkey));
E_g2e ("Failed to generate on-the-fly certificate session key",
gnutls_x509_privkey_generate (onthefly_subjectkey, GNUTLS_PK_RSA, 2048 /*TODO:FIXED*/, 0));
if (gtls_errno == GNUTLS_E_SUCCESS) {
tlog (TLOG_TLS, LOG_INFO, "Setup for on-the-fly signing with the TLS Pool");
} else {
tlog (TLOG_TLS, LOG_ERR, "Failed to setup on-the-fly signing (shall continue without it)");
gnutls_x509_privkey_deinit (onthefly_subjectkey);
onthefly_subjectkey = NULL;
}
} else {
gtls_errno = GNUTLS_E_SUCCESS;
E_gnutls_clear_errno ();
}
if (onthefly_subjectkey == NULL) {
if (onthefly_issuercrt != NULL) {
gnutls_x509_crt_deinit (onthefly_issuercrt);
onthefly_issuercrt = NULL;
}
if (onthefly_issuerkey != NULL) {
gnutls_privkey_deinit (onthefly_issuerkey);
onthefly_issuerkey = NULL;
}
}
//
// Finally, check whether there was any error setting up GnuTLS
if (gtls_errno != GNUTLS_E_SUCCESS) {
tlog (TLOG_TLS, LOG_CRIT, "FATAL: GnuTLS setup failed: %s", gnutls_strerror (gtls_errno));
exit (1);
}
//MOVED// //
//MOVED// // Setup the management databases
//MOVED// tlog (TLOG_DB, LOG_DEBUG, "Setting up management databases");
//MOVED// E_e2e ("Failed to setup management databases",
//MOVED// setup_management ());
//MOVED// if (errno != 0) {
//MOVED// tlog (TLOG_DB, LOG_CRIT, "FATAL: Management databases setup failed: %s", strerror (errno));
//MOVED// exit (1);
//MOVED// }
}
/* Cleanup the structures and resources that were setup for handling TLS.
*/
void cleanup_starttls (void) {
//MOVED// cleanup_management ();
if (onthefly_subjectkey != NULL) {
gnutls_x509_privkey_deinit (onthefly_subjectkey);
onthefly_subjectkey = NULL;
}
if (onthefly_issuercrt != NULL) {
gnutls_x509_crt_deinit (onthefly_issuercrt);
onthefly_issuercrt = NULL;
}
if (onthefly_issuerkey != NULL) {
gnutls_privkey_deinit (onthefly_issuerkey);
onthefly_issuerkey = NULL;
}
cleanup_starttls_credentials ();
#ifdef HAVE_TLS_KDH
cleanup_starttls_kerberos ();
#endif
remove_dh_params ();
gnutls_pkcs11_set_pin_function (NULL, NULL);
gnutls_pkcs11_set_token_function (NULL, NULL);
gnutls_pkcs11_deinit ();
gnutls_priority_deinit (priority_normal);
gnutls_global_deinit ();
}
/*
* The copycat function is a bidirectional transport between the given
* remote and local sockets, but it will encrypt traffic from local to
* remote, and decrypt traffic from remote to local. It will do this
* until one of the end points is shut down, at which time it will
* return and assume the context will close down both pre-existing
* sockets.
*
* This copycat actually has a few sharp claws to watch for -- shutdown
* of sockets may drop the last bit of information sent. First, the
* signal POLLHUP is best ignored because it travels asynchronously.
* Second, reading 0 is a good indicator of end-of-file and may be
* followed by an shutdown of reading from that stream. But, more
* importantly, the other side must have this information forwarded
* so it can shutdown. This means that a shutdown for writing to that
* stream is to be sent. Even when *both* sides have agreed to not send
* anything, they may still not have received all they were offered for
* reading, so we should SO_LINGER on the sockets so they can acknowledge,
* and after a timeout we can establish that shutdown failed and log and
* return an error for it.
* Will you believe that I had looked up if close() would suffice? The man
* page clearly stated yes. However, these articles offer much more detail:
* http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable
* http://www.greenend.org.uk/rjk/tech/poll.html
*
* This function blocks during its call to poll(), in a state that can easily
* be restarted. This is when thread cancellation is temporarily enabled.
* Other threads may use this to cancel the thread and have it joined with that
* thread which will subsume its tasks and restart the handshake. We might
* later make this more advanced, by using a cancel stack push/pull mechanisms
* to ensure that recv() always results in send() in spite of cancellation.
*
* The return value of copycat is a GNUTLS_E_ code, usually GNUTLS_E_SUCCESS.
* For the moment, only one special value is of concern, namely
* GNUTLS_E_REHANDSHAKE which client or server side may receive when an
* attempt is made to renegotiate the security of the connection.
*/
static int copycat (int local, int remote, gnutls_session_t wrapped, pool_handle_t client) {
char buf [1024];
struct pollfd inout [3];
ssize_t sz;
struct linger linger = { 1, 10 };
int have_client;