52 | 52 |
|
53 | 53 |
#define CIPHERS "DEFAULT:!EXPORT:!LOW:!RC4:!SSLv2"
|
54 | 54 |
|
|
55 |
/* Wrappers around driver_alloc() that check */
|
|
56 |
/* for OOM. */
|
|
57 |
void erl_exit(int n, char* v, ...) { abort(); }
|
|
58 |
void *ftls_alloc(ErlDrvSizeT size);
|
|
59 |
void *ftls_realloc(void *ptr, ErlDrvSizeT size);
|
|
60 |
ErlDrvBinary *ftls_alloc_binary(ErlDrvSizeT size);
|
|
61 |
ErlDrvBinary *ftls_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size);
|
|
62 |
|
|
63 |
void *ftls_alloc(ErlDrvSizeT size) {
|
|
64 |
void *p = driver_alloc(size);
|
|
65 |
if (p == NULL) {
|
|
66 |
erl_exit(1, "fast_tls: Can't allocate %lu bytes of memory\n", size);
|
|
67 |
}
|
|
68 |
return p;
|
|
69 |
}
|
|
70 |
|
|
71 |
void *ftls_realloc(void *ptr, ErlDrvSizeT size) {
|
|
72 |
void *p = driver_realloc(ptr, size);
|
|
73 |
if (p == NULL) {
|
|
74 |
erl_exit(1, "fast_tls: Can't reallocate %lu bytes of memory\n", size);
|
|
75 |
}
|
|
76 |
return p;
|
|
77 |
}
|
|
78 |
|
|
79 |
ErlDrvBinary *ftls_alloc_binary(ErlDrvSizeT size) {
|
|
80 |
ErlDrvBinary *p = driver_alloc_binary(size);
|
|
81 |
if (p == NULL) {
|
|
82 |
erl_exit(1, "fast_tls: Can't allocate %lu binary\n", size);
|
|
83 |
}
|
|
84 |
return p;
|
|
85 |
}
|
|
86 |
|
|
87 |
ErlDrvBinary *ftls_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size) {
|
|
88 |
ErlDrvBinary *p = driver_realloc_binary(bin, size);
|
|
89 |
if (p == NULL) {
|
|
90 |
erl_exit(1, "fast_tls: Can't reallocate %lu binary\n", size);
|
|
91 |
}
|
|
92 |
return p;
|
|
93 |
}
|
|
94 |
|
55 | 95 |
/**
|
56 | 96 |
* Prepare the SSL options flag.
|
57 | 97 |
**/
|
|
108 | 148 |
char *key;
|
109 | 149 |
time_t key_mtime;
|
110 | 150 |
time_t dh_mtime;
|
|
151 |
time_t ca_mtime;
|
111 | 152 |
SSL_CTX *ssl_ctx;
|
112 | 153 |
struct bucket *next;
|
113 | 154 |
};
|
|
125 | 166 |
{
|
126 | 167 |
size_t size = 1 << (MIN_LEVEL + 1);
|
127 | 168 |
size_t i;
|
128 | |
ht.buckets = (struct bucket **)driver_alloc(sizeof(struct bucket *) * size);
|
|
169 |
ht.buckets = ftls_alloc(sizeof(struct bucket *) * size);
|
129 | 170 |
ht.split = 0;
|
130 | 171 |
ht.level = MIN_LEVEL;
|
131 | 172 |
for (i = 0; i < size; i++)
|
|
134 | 175 |
}
|
135 | 176 |
|
136 | 177 |
static void hash_table_insert(char *key, time_t key_mtime, time_t dh_mtime,
|
137 | |
SSL_CTX *ssl_ctx)
|
|
178 |
time_t ca_mtime, SSL_CTX *ssl_ctx)
|
138 | 179 |
{
|
139 | 180 |
int level, split;
|
140 | 181 |
uint32_t hash = str_hash(key);
|
|
155 | 196 |
if (el->hash == hash && strcmp(el->key, key) == 0) {
|
156 | 197 |
el->key_mtime = key_mtime;
|
157 | 198 |
el->dh_mtime = dh_mtime;
|
|
199 |
el->ca_mtime = ca_mtime;
|
158 | 200 |
if (el->ssl_ctx != NULL)
|
159 | 201 |
SSL_CTX_free(el->ssl_ctx);
|
160 | 202 |
el->ssl_ctx = ssl_ctx;
|
|
167 | 209 |
if (ht.buckets[bucket] != NULL)
|
168 | 210 |
do_split = !0;
|
169 | 211 |
|
170 | |
new_bucket_el = (struct bucket *)driver_alloc(sizeof(struct bucket));
|
|
212 |
new_bucket_el = ftls_alloc(sizeof(struct bucket));
|
171 | 213 |
new_bucket_el->hash = hash;
|
172 | |
new_bucket_el->key = (char *)driver_alloc(strlen(key) + 1);
|
|
214 |
new_bucket_el->key = ftls_alloc(strlen(key) + 1);
|
173 | 215 |
strcpy(new_bucket_el->key, key);
|
174 | 216 |
new_bucket_el->key_mtime = key_mtime;
|
175 | 217 |
new_bucket_el->dh_mtime = dh_mtime;
|
|
218 |
new_bucket_el->ca_mtime = ca_mtime;
|
176 | 219 |
new_bucket_el->ssl_ctx = ssl_ctx;
|
177 | 220 |
new_bucket_el->next = ht.buckets[bucket];
|
178 | 221 |
ht.buckets[bucket] = new_bucket_el;
|
|
200 | 243 |
size = 1 << (level + 1);
|
201 | 244 |
ht.split = split;
|
202 | 245 |
ht.level = level;
|
203 | |
ht.buckets = (struct bucket **)
|
204 | |
driver_realloc(ht.buckets, sizeof(struct bucket *) * size);
|
|
246 |
ht.buckets = ftls_realloc(ht.buckets, sizeof(struct bucket *) * size);
|
205 | 247 |
for (i = 1 << level; i < size; i++)
|
206 | 248 |
ht.buckets[i] = NULL;
|
207 | 249 |
} else
|
|
210 | 252 |
}
|
211 | 253 |
|
212 | 254 |
static SSL_CTX *hash_table_lookup(char *key, time_t *key_mtime,
|
213 | |
time_t *dh_mtime)
|
|
255 |
time_t *dh_mtime, time_t *ca_mtime)
|
214 | 256 |
{
|
215 | 257 |
int level, split;
|
216 | 258 |
uint32_t hash = str_hash(key);
|
|
229 | 271 |
if (el->hash == hash && strcmp(el->key, key) == 0) {
|
230 | 272 |
*key_mtime = el->key_mtime;
|
231 | 273 |
*dh_mtime = el->dh_mtime;
|
|
274 |
*ca_mtime = el->ca_mtime;
|
232 | 275 |
return el->ssl_ctx;
|
233 | 276 |
}
|
234 | 277 |
el = el->next;
|
|
240 | 283 |
|
241 | 284 |
static ErlDrvData tls_drv_start(ErlDrvPort port, char *buff)
|
242 | 285 |
{
|
243 | |
tls_data *d = (tls_data *)driver_alloc(sizeof(tls_data));
|
|
286 |
tls_data *d = ftls_alloc(sizeof(tls_data));
|
244 | 287 |
d->port = port;
|
245 | 288 |
d->bio_read = NULL;
|
246 | 289 |
d->bio_write = NULL;
|
|
446 | 489 |
unsigned long error_code = ERR_get_error(); \
|
447 | 490 |
char *error_string = error_code ? \
|
448 | 491 |
ERR_error_string(error_code, NULL) : \
|
449 | |
NULL; \
|
450 | |
int error_string_length = error_string ? \
|
451 | |
strlen(error_string) : 0; \
|
|
492 |
""; \
|
|
493 |
int error_string_length = strlen(error_string); \
|
452 | 494 |
if (error_code) \
|
453 | 495 |
rlen = errstrlen + error_string_length + 3; \
|
454 | 496 |
else \
|
455 | 497 |
rlen = errstrlen + 1; \
|
456 | |
b = driver_alloc_binary(rlen); \
|
|
498 |
b = ftls_alloc_binary(rlen); \
|
457 | 499 |
b->orig_bytes[0] = 1; \
|
458 | 500 |
strncpy(b->orig_bytes + 1, errstr, errstrlen); \
|
459 | 501 |
if (error_code) { \
|
|
525 | 567 |
case SET_CERTIFICATE_FILE_CONNECT: {
|
526 | 568 |
time_t key_mtime = 0;
|
527 | 569 |
time_t dh_mtime = 0;
|
|
570 |
time_t ca_mtime = 0;
|
528 | 571 |
char *key_file = buf;
|
529 | 572 |
size_t key_file_len = strlen(key_file);
|
530 | 573 |
char *ciphers = key_file + key_file_len + 1;
|
|
533 | 576 |
size_t protocol_options_len = strlen(protocol_options);
|
534 | 577 |
char *dh_file = protocol_options + protocol_options_len + 1;
|
535 | 578 |
size_t dh_file_len = strlen(dh_file);
|
536 | |
char *hash_key = (char *)driver_alloc(key_file_len +
|
|
579 |
char *ca_file = dh_file + dh_file_len + 1;
|
|
580 |
size_t ca_file_len = strlen(ca_file);
|
|
581 |
char *hash_key = ftls_alloc(key_file_len +
|
537 | 582 |
ciphers_len +
|
538 | 583 |
protocol_options_len +
|
539 | |
dh_file_len + 1);
|
|
584 |
dh_file_len +
|
|
585 |
ca_file_len + 1);
|
540 | 586 |
long options = 0L;
|
541 | 587 |
|
542 | 588 |
if (protocol_options_len != 0) {
|
|
544 | 590 |
char *popts = po;
|
545 | 591 |
char *strtok_buf;
|
546 | 592 |
|
|
593 |
if (!po) {
|
|
594 |
erl_exit(1, "fast_tls: strdup failed");
|
|
595 |
}
|
|
596 |
|
547 | 597 |
while ((po = strtok_r(po, delim, &strtok_buf)) != NULL) {
|
548 | 598 |
set_option_flag(po, &options);
|
549 | 599 |
po = NULL;
|
|
552 | 602 |
free(popts);
|
553 | 603 |
}
|
554 | 604 |
|
555 | |
sprintf(hash_key, "%s%s%s%s", key_file, ciphers, protocol_options,
|
556 | |
dh_file);
|
557 | |
SSL_CTX *ssl_ctx = hash_table_lookup(hash_key, &key_mtime, &dh_mtime);
|
|
605 |
sprintf(hash_key, "%s%s%s%s%s", key_file, ciphers, protocol_options,
|
|
606 |
dh_file, ca_file);
|
|
607 |
SSL_CTX *ssl_ctx = hash_table_lookup(hash_key, &key_mtime, &dh_mtime, &ca_mtime);
|
558 | 608 |
|
559 | 609 |
if (dh_file_len == 0)
|
560 | 610 |
dh_file = NULL;
|
561 | 611 |
|
|
612 |
if (ca_file_len == 0)
|
|
613 |
ca_file = NULL;
|
|
614 |
|
562 | 615 |
if (is_modified(key_file, &key_mtime) ||
|
563 | 616 |
is_modified(dh_file, &dh_mtime) ||
|
|
617 |
is_modified(ca_file, &ca_mtime) ||
|
564 | 618 |
ssl_ctx == NULL)
|
565 | 619 |
{
|
566 | 620 |
SSL_CTX *ctx;
|
567 | 621 |
|
568 | |
hash_table_insert(hash_key, key_mtime, dh_mtime, NULL);
|
|
622 |
hash_table_insert(hash_key, key_mtime, dh_mtime, ca_mtime, NULL);
|
569 | 623 |
|
570 | 624 |
ctx = SSL_CTX_new(SSLv23_method());
|
571 | 625 |
die_unless(ctx, "SSL_CTX_new failed");
|
|
592 | 646 |
#endif
|
593 | 647 |
|
594 | 648 |
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
595 | |
SSL_CTX_set_default_verify_paths(ctx);
|
|
649 |
if (ca_file)
|
|
650 |
SSL_CTX_load_verify_locations(ctx, ca_file, NULL);
|
|
651 |
else
|
|
652 |
SSL_CTX_set_default_verify_paths(ctx);
|
596 | 653 |
#ifdef SSL_MODE_RELEASE_BUFFERS
|
597 | 654 |
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
|
598 | 655 |
#endif
|
|
610 | 667 |
SSL_CTX_set_info_callback(ctx, &ssl_info_callback);
|
611 | 668 |
|
612 | 669 |
ssl_ctx = ctx;
|
613 | |
hash_table_insert(hash_key, key_mtime, dh_mtime, ssl_ctx);
|
|
670 |
hash_table_insert(hash_key, key_mtime, dh_mtime, ca_mtime, ssl_ctx);
|
614 | 671 |
}
|
615 | 672 |
|
616 | 673 |
driver_free(hash_key);
|
|
630 | 687 |
|
631 | 688 |
d->bio_read = BIO_new(BIO_s_mem());
|
632 | 689 |
d->bio_write = BIO_new(BIO_s_mem());
|
|
690 |
|
|
691 |
die_unless(d->bio_read, "BIO_new failed");
|
|
692 |
die_unless(d->bio_write, "BIO_new failed");
|
633 | 693 |
|
634 | 694 |
SSL_set_bio(d->ssl, d->bio_read, d->bio_write);
|
635 | 695 |
|
|
660 | 720 |
if (d->send_buffer2 == NULL) {
|
661 | 721 |
d->send_buffer2_len = len;
|
662 | 722 |
d->send_buffer2_size = len;
|
663 | |
d->send_buffer2 = driver_alloc(d->send_buffer2_size);
|
|
723 |
d->send_buffer2 = ftls_alloc(d->send_buffer2_size);
|
664 | 724 |
memcpy(d->send_buffer2, buf, len);
|
665 | 725 |
} else {
|
666 | 726 |
if (d->send_buffer2_size <
|
|
669 | 729 |
d->send_buffer2_len + len) {
|
670 | 730 |
d->send_buffer2_size *= 2;
|
671 | 731 |
}
|
672 | |
d->send_buffer2 = driver_realloc(d->send_buffer2,
|
|
732 |
d->send_buffer2 = ftls_realloc(d->send_buffer2,
|
673 | 733 |
d->send_buffer2_size);
|
674 | 734 |
}
|
675 | 735 |
memcpy(d->send_buffer2 + d->send_buffer2_len,
|
|
684 | 744 |
res == SSL_ERROR_WANT_WRITE) {
|
685 | 745 |
d->send_buffer_len = len;
|
686 | 746 |
d->send_buffer_size = len;
|
687 | |
d->send_buffer = driver_alloc(d->send_buffer_size);
|
|
747 |
d->send_buffer = ftls_alloc(d->send_buffer_size);
|
688 | 748 |
memcpy(d->send_buffer, buf, len);
|
689 | 749 |
} else {
|
690 | 750 |
die_unless(0, "SSL_write failed");
|
|
696 | 756 |
case GET_ENCRYPTED_OUTPUT:
|
697 | 757 |
die_unless(d->ssl, "SSL not initialized");
|
698 | 758 |
size = BIO_ctrl_pending(d->bio_write) + 1;
|
699 | |
b = driver_alloc_binary(size);
|
|
759 |
b = ftls_alloc_binary(size);
|
700 | 760 |
b->orig_bytes[0] = 0;
|
701 | 761 |
BIO_read(d->bio_write, b->orig_bytes + 1, size - 1);
|
702 | 762 |
*rbuf = (char *)b;
|
703 | 763 |
return size;
|
704 | 764 |
case GET_DECRYPTED_INPUT: {
|
705 | 765 |
int retcode = 0;
|
|
766 |
die_unless(d->ssl, "SSL not initialized");
|
706 | 767 |
if (!SSL_is_init_finished(d->ssl))
|
707 | 768 |
{
|
708 | 769 |
retcode = 2;
|
|
738 | 799 |
}
|
739 | 800 |
size = BUF_SIZE + 1;
|
740 | 801 |
rlen = 1;
|
741 | |
b = driver_alloc_binary(size);
|
|
802 |
b = ftls_alloc_binary(size);
|
742 | 803 |
b->orig_bytes[0] = retcode;
|
743 | 804 |
|
744 | 805 |
res = 0;
|
|
753 | 814 |
rlen += res;
|
754 | 815 |
if (size - rlen < BUF_SIZE) {
|
755 | 816 |
size *= 2;
|
756 | |
b = driver_realloc_binary(b, size);
|
|
817 |
b = ftls_realloc_binary(b, size);
|
757 | 818 |
}
|
758 | 819 |
}
|
759 | 820 |
|
|
761 | 822 |
char *error = "client renegotiations forbidden";
|
762 | 823 |
int error_len = strlen(error);
|
763 | 824 |
rlen = error_len + 1;
|
764 | |
b = driver_alloc_binary(rlen);
|
|
825 |
b = ftls_alloc_binary(rlen);
|
765 | 826 |
b->orig_bytes[0] = 1;
|
766 | 827 |
strncpy(b->orig_bytes + 1, error, error_len);
|
767 | 828 |
*rbuf = (char *)b;
|
|
779 | 840 |
}
|
780 | 841 |
// TODO
|
781 | 842 |
}
|
782 | |
b = driver_realloc_binary(b, rlen);
|
|
843 |
b = ftls_realloc_binary(b, rlen);
|
783 | 844 |
*rbuf = (char *)b;
|
784 | 845 |
return rlen;
|
785 | 846 |
} else {
|
786 | |
b = driver_alloc_binary(1);
|
|
847 |
b = ftls_alloc_binary(1);
|
787 | 848 |
b->orig_bytes[0] = 2;
|
788 | 849 |
*rbuf = (char *)b;
|
789 | 850 |
return 1;
|
|
794 | 855 |
cert = SSL_get_peer_certificate(d->ssl);
|
795 | 856 |
if (cert == NULL)
|
796 | 857 |
{
|
797 | |
b = driver_alloc_binary(1);
|
|
858 |
b = ftls_alloc_binary(1);
|
798 | 859 |
b->orig_bytes[0] = 1;
|
799 | 860 |
*rbuf = (char *)b;
|
800 | 861 |
return 1;
|
801 | 862 |
} else {
|
802 | 863 |
unsigned char *tmp_buf;
|
803 | |
rlen = i2d_X509(cert, NULL);
|
804 | |
if (rlen >= 0)
|
|
864 |
int encode_len = i2d_X509(cert, NULL);
|
|
865 |
if (encode_len >= 0)
|
805 | 866 |
{
|
806 | |
rlen++;
|
807 | |
b = driver_alloc_binary(rlen);
|
|
867 |
rlen = encode_len + 1;
|
|
868 |
b = ftls_alloc_binary(rlen);
|
808 | 869 |
b->orig_bytes[0] = 0;
|
809 | 870 |
tmp_buf = (unsigned char *)&b->orig_bytes[1];
|
810 | 871 |
i2d_X509(cert, &tmp_buf);
|
|
816 | 877 |
}
|
817 | 878 |
break;
|
818 | 879 |
case GET_VERIFY_RESULT:
|
819 | |
b = driver_alloc_binary(1);
|
|
880 |
b = ftls_alloc_binary(1);
|
820 | 881 |
b->orig_bytes[0] = SSL_get_verify_result(d->ssl);
|
821 | 882 |
*rbuf = (char *)b;
|
822 | 883 |
return 1;
|
823 | 884 |
break;
|
824 | 885 |
}
|
825 | 886 |
|
826 | |
b = driver_alloc_binary(1);
|
|
887 |
b = ftls_alloc_binary(1);
|
827 | 888 |
b->orig_bytes[0] = 0;
|
828 | 889 |
*rbuf = (char *)b;
|
829 | 890 |
return 1;
|