diff options
author | Dirk Engling <erdgeist@erdgeist.org> | 2022-05-20 01:12:42 +0200 |
---|---|---|
committer | Dirk Engling <erdgeist@erdgeist.org> | 2022-05-20 01:12:42 +0200 |
commit | 52a72be2a6790a5a59d19c11847aa27b9e32ea89 (patch) | |
tree | 2779a209384f525583d6945ebec83a4098f513a0 | |
parent | 92c67507e7b9b94341b3453b01a124f642aa68fb (diff) |
Implement a happy path mbedtls wrapper
-rwxr-xr-x | Makefile | 26 | ||||
-rw-r--r-- | vchat-connection.c | 1 | ||||
-rwxr-xr-x | vchat-tls.c | 699 |
3 files changed, 450 insertions, 276 deletions
@@ -6,18 +6,35 @@ | |||
6 | # configuration # | 6 | # configuration # |
7 | ############################################## | 7 | ############################################## |
8 | 8 | ||
9 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-tls.o vchat-connection.o | ||
10 | |||
11 | LIBS = -lncurses | ||
12 | LIBS += -lreadline | ||
13 | |||
9 | CFLAGS = -Wall -Os | 14 | CFLAGS = -Wall -Os |
10 | #CFLAGS = -Wall -g -ggdb | ||
11 | 15 | ||
12 | ## use this line when you've got an readline before 4.(x|2) | 16 | ## use this line when you've got an readline before 4.(x|2) |
13 | #CFLAGS += -DOLDREADLINE | 17 | #CFLAGS += -DOLDREADLINE |
14 | |||
15 | CFLAGS += $(OLDREADLINE) | 18 | CFLAGS += $(OLDREADLINE) |
16 | 19 | ||
20 | ##### Enable this for using the OpenSSL library | ||
21 | CFLAGS += -DTLS_LIB_OPENSSL -I"/usr/local/opt/openssl@1.1/include" | ||
22 | LIBS += -lssl -lcrypto | ||
23 | |||
24 | ##### Enable this for using the mbedTLS library | ||
25 | #CFLAGS += -DTLS_LIB_MBEDTLS | ||
26 | #LIBS += -lmbedx509 -lmbedtls -lmbedcrypto | ||
27 | |||
17 | ## you might need one or more of these: | 28 | ## you might need one or more of these: |
29 | #CFLAGS+= -Wextra -Wall -g -ggdb | ||
30 | #CFLAGS+= -arch x86_64 -Wno-deprecated-declarations | ||
31 | #CFLAGS+= -arch i386 -Wno-deprecated-declarations | ||
18 | #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib | 32 | #CFLAGS += -I/usr/local/ssl/include -L/usr/local/ssl/lib |
19 | #CFLAGS += -I/usr/local/include -L/usr/local/lib | 33 | #CFLAGS += -I/usr/local/include -L/usr/local/lib |
20 | #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib | 34 | #CFLAGS += -I/usr/pkg/include -L/usr/pkg/lib |
35 | #LDFLAGS += -L"/usr/local/opt/openssl@1.1/lib" | ||
36 | #CFLAGS += -I../readline-6.3 | ||
37 | #LIBS += ../readline-6.3/libreadline.a | ||
21 | 38 | ||
22 | ## enable dietlibc | 39 | ## enable dietlibc |
23 | #CC = diet cc | 40 | #CC = diet cc |
@@ -26,14 +43,9 @@ CFLAGS += $(OLDREADLINE) | |||
26 | ## enable debug code | 43 | ## enable debug code |
27 | #CFLAGS += -DDEBUG | 44 | #CFLAGS += -DDEBUG |
28 | 45 | ||
29 | #LDFLAGS = -L"/usr/local/opt/openssl@1.1/lib" | ||
30 | |||
31 | ## the install prefix best is /usr/local | 46 | ## the install prefix best is /usr/local |
32 | PREFIX=/usr/local | 47 | PREFIX=/usr/local |
33 | 48 | ||
34 | LIBS = -lssl -lcrypto -lncurses -lreadline | ||
35 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-tls.o vchat-connection.o | ||
36 | |||
37 | 49 | ||
38 | ############################################## | 50 | ############################################## |
39 | # general targets # | 51 | # general targets # |
diff --git a/vchat-connection.c b/vchat-connection.c index c0648c8..5ab4dd4 100644 --- a/vchat-connection.c +++ b/vchat-connection.c | |||
@@ -135,6 +135,7 @@ vc_connect (const char *server, const char *port) | |||
135 | close(serverfd); | 135 | close(serverfd); |
136 | serverfd = -1; | 136 | serverfd = -1; |
137 | errno = EIO; | 137 | errno = EIO; |
138 | vc_tls_cleanup(); | ||
138 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); | 139 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CANTCONNECT), server, port ); |
139 | writechan (tmpstr); | 140 | writechan (tmpstr); |
140 | return -1; | 141 | return -1; |
diff --git a/vchat-tls.c b/vchat-tls.c index 205f0e0..4b555fd 100755 --- a/vchat-tls.c +++ b/vchat-tls.c | |||
@@ -28,53 +28,48 @@ | |||
28 | const char *vchat_tls_version = "vchat-tls.c $Id$"; | 28 | const char *vchat_tls_version = "vchat-tls.c $Id$"; |
29 | const char *vchat_tls_version_external = "Unknown implementation; version unknown"; | 29 | const char *vchat_tls_version_external = "Unknown implementation; version unknown"; |
30 | 30 | ||
31 | void vc_cleanup_x509store(vc_x509store_t *store) | 31 | /* Helpers to work with vc_x509store_t used by all tls libs */ |
32 | { | 32 | void vc_cleanup_x509store(vc_x509store_t *store) { |
33 | free(store->cafile); | 33 | free(store->cafile); |
34 | free(store->capath); | 34 | free(store->capath); |
35 | free(store->crlfile); | 35 | free(store->crlfile); |
36 | free(store->certfile); | 36 | free(store->certfile); |
37 | free(store->keyfile); | 37 | free(store->keyfile); |
38 | memset(store, 0, sizeof(vc_x509store_t)); | 38 | memset(store, 0, sizeof(vc_x509store_t)); |
39 | } | 39 | } |
40 | 40 | ||
41 | void vc_x509store_setflags(vc_x509store_t *store, int flags) { store->flags |= flags; } | 41 | void vc_x509store_setflags(vc_x509store_t *store, int flags) { store->flags |= flags; } |
42 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) { store->flags &= ~flags; } | 42 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) { store->flags &= ~flags; } |
43 | void vc_x509store_set_pkeycb(vc_x509store_t *store, vc_askpass_cb_t callback) { store->askpass_callback = callback; } | 43 | void vc_x509store_set_pkeycb(vc_x509store_t *store, vc_askpass_cb_t callback) { store->askpass_callback = callback; } |
44 | 44 | ||
45 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) | 45 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) { |
46 | { | 46 | free(store->cafile); |
47 | free(store->cafile); | 47 | store->cafile = ( file ? strdup(file) : 0 ); |
48 | store->cafile = ( file ? strdup(file) : 0 ); | 48 | store->flags |= VC_X509S_USE_CAFILE; |
49 | store->flags |= VC_X509S_USE_CAFILE; | ||
50 | } | 49 | } |
51 | 50 | ||
52 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) | 51 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) { |
53 | { | 52 | free(store->capath); |
54 | free(store->capath); | 53 | store->capath = ( path ? strdup(path) : 0 ); |
55 | store->capath = ( path ? strdup(path) : 0 ); | ||
56 | } | 54 | } |
57 | 55 | ||
58 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) | 56 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) { |
59 | { | 57 | free(store->crlfile); |
60 | free(store->crlfile); | 58 | store->crlfile = ( file ? strdup(file) : 0 ); |
61 | store->crlfile = ( file ? strdup(file) : 0 ); | ||
62 | } | 59 | } |
63 | 60 | ||
64 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) | 61 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) { |
65 | { | 62 | free(store->keyfile); |
66 | free(store->keyfile); | 63 | store->keyfile = ( file ? strdup(file) : 0 ); |
67 | store->keyfile = ( file ? strdup(file) : 0 ); | ||
68 | } | 64 | } |
69 | 65 | ||
70 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) | 66 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) { |
71 | { | 67 | free(store->certfile); |
72 | free(store->certfile); | 68 | store->certfile = ( file ? strdup(file) : 0 ); |
73 | store->certfile = ( file ? strdup(file) : 0 ); | 69 | store->flags |= VC_X509S_USE_CERTIFICATE; |
74 | store->flags |= VC_X509S_USE_CERTIFICATE; | ||
75 | } | 70 | } |
76 | 71 | ||
77 | //// OPENSSL SPECIFIC | 72 | #ifdef TLS_LIB_OPENSSL |
78 | 73 | ||
79 | #include <openssl/err.h> | 74 | #include <openssl/err.h> |
80 | #include <openssl/ssl.h> | 75 | #include <openssl/ssl.h> |
@@ -86,22 +81,21 @@ void vc_x509store_setcertfile(vc_x509store_t *store, char *file) | |||
86 | 81 | ||
87 | void vchat_tls_get_version_external() | 82 | void vchat_tls_get_version_external() |
88 | { | 83 | { |
89 | snprintf(tmpstr, sizeof(tmpstr), "OpenSSL %s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); | 84 | snprintf(tmpstr, sizeof(tmpstr), "OpenSSL %s with %s", SSLeay_version(SSLEAY_VERSION), SSLeay_version(SSLEAY_CFLAGS)); |
90 | vchat_tls_version_external = strdup(tmpstr); | 85 | vchat_tls_version_external = strdup(tmpstr); |
91 | } | 86 | } |
92 | 87 | ||
93 | /* Helpers to work with vc_x509store_t used by all tls libs */ | ||
94 | void vc_init_x509store(vc_x509store_t *store) | 88 | void vc_init_x509store(vc_x509store_t *store) |
95 | { | 89 | { |
96 | static int sslinit; | 90 | static int sslinit; |
97 | if (!sslinit++) { | 91 | if (!sslinit++) { |
98 | SSL_library_init (); | 92 | SSL_library_init (); |
99 | SSL_load_error_strings(); | 93 | SSL_load_error_strings(); |
100 | } | 94 | } |
101 | memset(store, 0, sizeof(vc_x509store_t)); | 95 | memset(store, 0, sizeof(vc_x509store_t)); |
102 | 96 | ||
103 | /* We want to make verifying the peer the default */ | 97 | /* We want to make verifying the peer the default */ |
104 | store->flags |= VC_X509S_SSL_VERIFY_PEER; | 98 | store->flags |= VC_X509S_SSL_VERIFY_PEER; |
105 | } | 99 | } |
106 | 100 | ||
107 | /* connection BIO for openssl */ | 101 | /* connection BIO for openssl */ |
@@ -113,278 +107,445 @@ static X509_STORE * vc_x509store_create(vc_x509store_t *); | |||
113 | 107 | ||
114 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) | 108 | static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) |
115 | { | 109 | { |
116 | int flags = 0; | 110 | int flags = 0; |
117 | 111 | ||
118 | /* Explicitly use TLSv1 (or maybe later) */ | 112 | /* Explicitly use TLSv1 (or maybe later) */ |
119 | SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); | 113 | SSL_CTX *ctx = SSL_CTX_new(TLS_client_method()); |
120 | X509_STORE *store = vc_x509store_create(vc_store); | 114 | X509_STORE *store = vc_x509store_create(vc_store); |
121 | 115 | ||
122 | if (!ctx || !store) { | 116 | if (!ctx || !store) { |
123 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: %s",ERR_error_string (ERR_get_error (), NULL)); | 117 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: %s",ERR_error_string (ERR_get_error (), NULL)); |
124 | writecf(FS_ERR, tmpstr); | 118 | writecf(FS_ERR, tmpstr); |
125 | if (store) | 119 | if (store) |
126 | X509_STORE_free(store); | 120 | X509_STORE_free(store); |
127 | if (ctx) | 121 | if (ctx) |
128 | SSL_CTX_free(ctx); | 122 | SSL_CTX_free(ctx); |
129 | return NULL; | 123 | return NULL; |
130 | } | 124 | } |
131 | 125 | ||
132 | SSL_CTX_set_cert_store(ctx, store); | 126 | SSL_CTX_set_cert_store(ctx, store); |
133 | 127 | ||
134 | /* Disable some insecure protocols explicitly */ | 128 | /* Disable some insecure protocols explicitly */ |
135 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); | 129 | SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); |
136 | if (getstroption(CF_CIPHERSUITE)) | 130 | if (getstroption(CF_CIPHERSUITE)) |
137 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); | 131 | SSL_CTX_set_cipher_list(ctx, getstroption(CF_CIPHERSUITE)); |
138 | else | 132 | else |
139 | SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); | 133 | SSL_CTX_set_cipher_list(ctx, "ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA"); |
140 | 134 | ||
141 | SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); | 135 | SSL_CTX_set_verify_depth (ctx, getintoption(CF_VERIFYSSL)); |
142 | 136 | ||
143 | if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { | 137 | if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { |
144 | writecf(FS_DBG, tmpstr); | 138 | writecf(FS_DBG, tmpstr); |
145 | flags = SSL_VERIFY_NONE; | 139 | flags = SSL_VERIFY_NONE; |
146 | } else { | 140 | } else { |
147 | if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | 141 | if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) |
148 | flags |= SSL_VERIFY_PEER; | 142 | flags |= SSL_VERIFY_PEER; |
149 | if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | 143 | if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) |
150 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | 144 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
151 | if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | 145 | if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) |
152 | flags |= SSL_VERIFY_CLIENT_ONCE; | 146 | flags |= SSL_VERIFY_CLIENT_ONCE; |
153 | } | 147 | } |
154 | 148 | ||
155 | SSL_CTX_set_verify(ctx, flags, vc_verify_callback); | 149 | SSL_CTX_set_verify(ctx, flags, vc_verify_callback); |
156 | 150 | ||
157 | if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { | 151 | if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { |
158 | int r = 0; | 152 | int r = 0; |
159 | if(vc_store->certfile) | 153 | if(vc_store->certfile) |
160 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->certfile); | 154 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->certfile); |
161 | 155 | ||
162 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | 156 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); |
163 | 157 | ||
164 | if(vc_store->keyfile) | 158 | if(vc_store->keyfile) |
165 | r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->keyfile, | 159 | r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->keyfile, |
166 | SSL_FILETYPE_PEM); | 160 | SSL_FILETYPE_PEM); |
167 | 161 | ||
168 | if( r!=1 || !SSL_CTX_check_private_key(ctx)) { | 162 | if( r!=1 || !SSL_CTX_check_private_key(ctx)) { |
169 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: Load private key failed"); | 163 | snprintf(tmpstr, sizeof(tmpstr), "CREATE CTX: Load private key failed"); |
170 | writecf(FS_ERR, tmpstr); | 164 | writecf(FS_ERR, tmpstr); |
171 | SSL_CTX_free(ctx); | 165 | SSL_CTX_free(ctx); |
172 | return NULL; | 166 | return NULL; |
173 | } | 167 | } |
174 | } | 168 | } |
175 | 169 | ||
176 | SSL_CTX_set_app_data(ctx, vc_store); | 170 | SSL_CTX_set_app_data(ctx, vc_store); |
177 | return(ctx); | 171 | return(ctx); |
178 | } | 172 | } |
179 | 173 | ||
180 | int vc_tls_connect( int serverfd, vc_x509store_t *vc_store ) | 174 | int vc_tls_connect( int serverfd, vc_x509store_t *vc_store ) |
181 | { | 175 | { |
182 | SSL_CTX * ctx = vc_create_sslctx(vc_store); | 176 | SSL_CTX * ctx = vc_create_sslctx(vc_store); |
183 | X509 *peercert = NULL; | 177 | X509 *peercert = NULL; |
184 | BIO *ssl_conn = NULL; | 178 | BIO *ssl_conn = NULL; |
185 | const SSL *sslp = NULL; | 179 | const SSL *sslp = NULL; |
186 | const SSL_CIPHER * cipher = NULL; | 180 | const SSL_CIPHER * cipher = NULL; |
187 | |||
188 | server_conn = BIO_new_socket( serverfd, 1 ); | ||
189 | 181 | ||
190 | /* To display and check server fingerprint */ | 182 | server_conn = BIO_new_socket( serverfd, 1 ); |
191 | char fingerprint[EVP_MAX_MD_SIZE*4]; | ||
192 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
193 | unsigned int fingerprint_len; | ||
194 | 183 | ||
195 | FILE *fingerprint_file = NULL; | 184 | /* To display and check server fingerprint */ |
196 | char * fp = fingerprint; | 185 | char fingerprint[EVP_MAX_MD_SIZE*4]; |
186 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
187 | unsigned int fingerprint_len; | ||
197 | 188 | ||
198 | long result, j; | 189 | FILE *fingerprint_file = NULL; |
190 | char * fp = fingerprint; | ||
199 | 191 | ||
200 | if( !ctx ) | 192 | long result, j; |
201 | goto all_errors; | ||
202 | 193 | ||
203 | ssl_conn = BIO_new_ssl(ctx, 1); | 194 | if( !ctx ) |
204 | SSL_CTX_free(ctx); | 195 | goto all_errors; |
205 | 196 | ||
206 | if( !ssl_conn ) | 197 | ssl_conn = BIO_new_ssl(ctx, 1); |
207 | goto ssl_error; | 198 | SSL_CTX_free(ctx); |
208 | 199 | ||
209 | BIO_push( ssl_conn, server_conn ); | 200 | if( !ssl_conn ) |
210 | server_conn = ssl_conn; | 201 | goto ssl_error; |
211 | fflush(stdout); | ||
212 | 202 | ||
213 | if( BIO_do_handshake( server_conn ) <= 0 ) | 203 | BIO_push( ssl_conn, server_conn ); |
214 | goto ssl_error; | 204 | server_conn = ssl_conn; |
205 | fflush(stdout); | ||
215 | 206 | ||
216 | /* Show information about cipher used */ | 207 | if( BIO_do_handshake( server_conn ) <= 0 ) |
217 | /* Get cipher object */ | 208 | goto ssl_error; |
218 | BIO_get_ssl(ssl_conn, &sslp); | ||
219 | if (!sslp) | ||
220 | goto ssl_error; | ||
221 | 209 | ||
222 | cipher = SSL_get_current_cipher(sslp); | 210 | /* Show information about cipher used */ |
223 | if (cipher) { | 211 | /* Get cipher object */ |
224 | char cipher_desc[TMPSTRSIZE]; | 212 | BIO_get_ssl(ssl_conn, &sslp); |
225 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | 213 | if (!sslp) |
226 | writecf(FS_SERV, tmpstr); | 214 | goto ssl_error; |
227 | } else { | ||
228 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); | ||
229 | writecf(FS_ERR, tmpstr); | ||
230 | } | ||
231 | |||
232 | /* Accept being connected, _if_ verification passed */ | ||
233 | peercert = SSL_get_peer_certificate(sslp); | ||
234 | if (!peercert) | ||
235 | goto ssl_error; | ||
236 | |||
237 | /* show basic information about peer cert */ | ||
238 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); | ||
239 | writecf(FS_SERV, tmpstr); | ||
240 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
241 | writecf(FS_SERV, tmpstr); | ||
242 | |||
243 | /* calculate fingerprint */ | ||
244 | if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) | ||
245 | goto ssl_error; | ||
246 | |||
247 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); | ||
248 | for (j=0; j<(int)fingerprint_len; j++) | ||
249 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
250 | assert ( fp > fingerprint ); | ||
251 | fp[-1] = 0; | ||
252 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); | ||
253 | writecf(FS_SERV, tmpstr); | ||
254 | |||
255 | /* we don't need the peercert anymore */ | ||
256 | X509_free(peercert); | ||
257 | |||
258 | /* verify fingerprint */ | ||
259 | if (getintoption(CF_PINFINGER)) { | ||
260 | |||
261 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); | ||
262 | if (fingerprint_file) { | ||
263 | |||
264 | /* Read fingerprint from file */ | ||
265 | char old_fingerprint[EVP_MAX_MD_SIZE*4]; | ||
266 | char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
267 | fclose(fingerprint_file); | ||
268 | |||
269 | if (r) { | ||
270 | /* chomp */ | ||
271 | char *nl = strchr(r, '\n'); | ||
272 | if (nl) *nl = 0; | ||
273 | |||
274 | /* verify fingerprint matches stored version */ | ||
275 | if (!strcmp(fingerprint, old_fingerprint)) | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "<FILE READ ERROR>", getstroption(CF_FINGERPRINT)); | ||
280 | writecf(FS_ERR, tmpstr); | ||
281 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | ||
282 | return 1; | ||
283 | } | ||
284 | 215 | ||
285 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | 216 | cipher = SSL_get_current_cipher(sslp); |
286 | if (!fingerprint_file) { | 217 | if (cipher) { |
287 | snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); | 218 | char cipher_desc[TMPSTRSIZE]; |
288 | writecf(FS_ERR, tmpstr); | 219 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); |
220 | writecf(FS_SERV, tmpstr); | ||
289 | } else { | 221 | } else { |
290 | fputs(fingerprint, fingerprint_file); | 222 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); |
291 | fclose(fingerprint_file); | 223 | writecf(FS_ERR, tmpstr); |
292 | writecf(FS_SERV, "Stored fingerprint."); | ||
293 | } | 224 | } |
294 | return 0; | ||
295 | } | ||
296 | 225 | ||
297 | /* If verify of x509 chain was requested, do the check here */ | 226 | /* Accept being connected, _if_ verification passed */ |
298 | result = SSL_get_verify_result(sslp); | 227 | peercert = SSL_get_peer_certificate(sslp); |
228 | if (!peercert) | ||
229 | goto ssl_error; | ||
299 | 230 | ||
300 | if (result == X509_V_OK) | 231 | /* show basic information about peer cert */ |
301 | return 0; | 232 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); |
233 | writecf(FS_SERV, tmpstr); | ||
234 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
235 | writecf(FS_SERV, tmpstr); | ||
302 | 236 | ||
303 | if (getintoption(CF_IGNSSL)) { | 237 | /* calculate fingerprint */ |
304 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | 238 | if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) |
305 | return 0; | 239 | goto ssl_error; |
306 | } | 240 | |
241 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); | ||
242 | for (j=0; j<(int)fingerprint_len; j++) | ||
243 | fp += sprintf(fp, "%02X:", fingerprint_bin[j]); | ||
244 | assert ( fp > fingerprint ); | ||
245 | fp[-1] = 0; | ||
246 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from server)", fingerprint); | ||
247 | writecf(FS_SERV, tmpstr); | ||
248 | |||
249 | /* we don't need the peercert anymore */ | ||
250 | X509_free(peercert); | ||
251 | |||
252 | /* verify fingerprint */ | ||
253 | if (getintoption(CF_PINFINGER)) { | ||
254 | |||
255 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); | ||
256 | if (fingerprint_file) { | ||
257 | |||
258 | /* Read fingerprint from file */ | ||
259 | char old_fingerprint[EVP_MAX_MD_SIZE*4]; | ||
260 | char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); | ||
261 | fclose(fingerprint_file); | ||
262 | |||
263 | if (r) { | ||
264 | /* chomp */ | ||
265 | char *nl = strchr(r, '\n'); | ||
266 | if (nl) *nl = 0; | ||
267 | |||
268 | /* verify fingerprint matches stored version */ | ||
269 | if (!strcmp(fingerprint, old_fingerprint)) | ||
270 | return 0; | ||
271 | } | ||
272 | |||
273 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] %s (from %s)", r ? old_fingerprint : "<FILE READ ERROR>", getstroption(CF_FINGERPRINT)); | ||
274 | writecf(FS_ERR, tmpstr); | ||
275 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | ||
276 | return 1; | ||
277 | } | ||
278 | |||
279 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | ||
280 | if (!fingerprint_file) { | ||
281 | snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); | ||
282 | writecf(FS_ERR, tmpstr); | ||
283 | } else { | ||
284 | fputs(fingerprint, fingerprint_file); | ||
285 | fclose(fingerprint_file); | ||
286 | writecf(FS_SERV, "Stored fingerprint."); | ||
287 | } | ||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /* If verify of x509 chain was requested, do the check here */ | ||
292 | if (X509_V_OK == SSL_get_verify_result(sslp)) | ||
293 | return 0; | ||
294 | |||
295 | if (getintoption(CF_IGNSSL)) { | ||
296 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | ||
297 | return 0; | ||
298 | } | ||
307 | 299 | ||
308 | ssl_error: | 300 | ssl_error: |
309 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); | 301 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); |
310 | writecf(FS_ERR, tmpstr); | 302 | writecf(FS_ERR, tmpstr); |
311 | all_errors: | 303 | all_errors: |
312 | BIO_free_all( server_conn ); | 304 | BIO_free_all( server_conn ); |
313 | server_conn = NULL; | 305 | server_conn = NULL; |
314 | return 1; | 306 | return 1; |
315 | } | 307 | } |
316 | 308 | ||
317 | #define VC_STORE_ERR_EXIT(s) do { \ | 309 | #define VC_STORE_ERR_EXIT(s) do { \ |
318 | fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ | 310 | fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ |
319 | if(s) X509_STORE_free(s); \ | 311 | if(s) X509_STORE_free(s); \ |
320 | return(0); \ | 312 | return(0); \ |
321 | } while(0) | 313 | } while(0) |
322 | 314 | ||
323 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) | 315 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) { |
324 | { | 316 | X509_STORE *store = NULL; |
325 | X509_STORE *store = NULL; | 317 | X509_LOOKUP *lookup = NULL; |
326 | X509_LOOKUP *lookup = NULL; | ||
327 | 318 | ||
328 | store = X509_STORE_new(); | 319 | store = X509_STORE_new(); |
329 | 320 | ||
330 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | 321 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); |
331 | 322 | ||
332 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) | 323 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) |
333 | VC_STORE_ERR_EXIT(store); | 324 | VC_STORE_ERR_EXIT(store); |
334 | 325 | ||
335 | if (!vc_store->cafile) { | 326 | if (!vc_store->cafile) { |
336 | if( !(vc_store->flags & VC_X509S_USE_CAFILE) ) | 327 | if( !(vc_store->flags & VC_X509S_USE_CAFILE) ) |
337 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | 328 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); |
338 | } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, | 329 | } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, |
339 | X509_FILETYPE_PEM) ) | 330 | X509_FILETYPE_PEM) ) |
340 | VC_STORE_ERR_EXIT(store); | 331 | VC_STORE_ERR_EXIT(store); |
341 | 332 | ||
342 | if (vc_store->crlfile) { | 333 | if (vc_store->crlfile) { |
343 | if( !X509_load_crl_file(lookup, vc_store->crlfile, | 334 | if( !X509_load_crl_file(lookup, vc_store->crlfile, |
344 | X509_FILETYPE_PEM) ) | 335 | X509_FILETYPE_PEM) ) |
345 | VC_STORE_ERR_EXIT(store); | 336 | VC_STORE_ERR_EXIT(store); |
346 | 337 | ||
347 | X509_STORE_set_flags( store, | 338 | X509_STORE_set_flags( store, |
348 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); | 339 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); |
349 | } | 340 | } |
350 | 341 | ||
351 | if ( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) | 342 | if ( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) |
352 | VC_STORE_ERR_EXIT(store); | 343 | VC_STORE_ERR_EXIT(store); |
353 | 344 | ||
354 | if ( !vc_store->capath ) { | 345 | if ( !vc_store->capath ) { |
355 | if( !(vc_store->flags & VC_X509S_USE_CAPATH) ) | 346 | if( !(vc_store->flags & VC_X509S_USE_CAPATH) ) |
356 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | 347 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); |
357 | } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, | 348 | } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, |
358 | X509_FILETYPE_PEM) ) | 349 | X509_FILETYPE_PEM) ) |
359 | VC_STORE_ERR_EXIT(store); | 350 | VC_STORE_ERR_EXIT(store); |
360 | 351 | ||
361 | return(store); | 352 | return(store); |
362 | } | 353 | } |
363 | 354 | ||
364 | int vc_verify_callback(int ok, X509_STORE_CTX *store) | 355 | int vc_verify_callback(int ok, X509_STORE_CTX *store) { |
356 | if(!ok) { | ||
357 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | ||
358 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | ||
359 | writecf(FS_ERR, tmpstr); | ||
360 | } | ||
361 | return (ok | getintoption(CF_IGNSSL)); | ||
362 | } | ||
363 | |||
364 | ssize_t vc_tls_sendmessage(const void *buf, size_t size) { | ||
365 | return BIO_write(server_conn, buf, size); | ||
366 | } | ||
367 | |||
368 | ssize_t vc_tls_receivemessage(void *buf, size_t size) { | ||
369 | ssize_t received = (ssize_t)BIO_read (server_conn, buf, size); | ||
370 | if (received != 0) | ||
371 | return received; | ||
372 | if (BIO_should_retry(server_conn)) | ||
373 | return -2; | ||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | void vc_tls_cleanup() { | ||
378 | BIO_free_all( server_conn ); | ||
379 | server_conn = NULL; | ||
380 | } | ||
381 | #endif | ||
382 | |||
383 | #ifdef TLS_LIB_MBEDTLS | ||
384 | |||
385 | #include <mbedtls/net_sockets.h> | ||
386 | #include <mbedtls/ssl.h> | ||
387 | #include <mbedtls/entropy.h> | ||
388 | #include <mbedtls/ctr_drbg.h> | ||
389 | #include <mbedtls/x509.h> | ||
390 | #include <mbedtls/pk.h> | ||
391 | #include <mbedtls/debug.h> | ||
392 | #include "mbedtls/error.h" | ||
393 | |||
394 | #include <sys/socket.h> | ||
395 | |||
396 | const char *DRBG_PERS = "mbed TLS vchat client"; | ||
397 | |||
398 | typedef struct { | ||
399 | mbedtls_entropy_context _entropy; | ||
400 | mbedtls_ctr_drbg_context _ctr_drbg; | ||
401 | mbedtls_x509_crt _cacert; | ||
402 | mbedtls_x509_crt _cert; | ||
403 | mbedtls_pk_context _key; | ||
404 | mbedtls_ssl_context _ssl; | ||
405 | mbedtls_ssl_config _conf; | ||
406 | } mbedstate; | ||
407 | static mbedstate _mbedtls_state; | ||
408 | |||
409 | void vchat_tls_get_version_external() | ||
410 | { | ||
411 | snprintf(tmpstr, sizeof(tmpstr), "%s", MBEDTLS_VERSION_STRING_FULL); | ||
412 | vchat_tls_version_external = strdup(tmpstr); | ||
413 | } | ||
414 | |||
415 | static int static_tcp_recv(void *ctx, unsigned char *buf, size_t len ) { | ||
416 | return recv((int)(intptr_t)ctx, buf, len, 0); | ||
417 | } | ||
418 | static int static_tcp_send(void *ctx, const unsigned char *buf, size_t len ) { | ||
419 | return send((int)(intptr_t)ctx, buf, len, 0); | ||
420 | } | ||
421 | |||
422 | void vc_init_x509store(vc_x509store_t *store) | ||
365 | { | 423 | { |
366 | if(!ok) { | 424 | static int sslinit; |
367 | snprintf(tmpstr, TMPSTRSIZE, "[SSL VERIFY ERROR ] %s", | 425 | if (!sslinit++) { |
368 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store))); | 426 | mbedtls_entropy_init(&_mbedtls_state._entropy); |
369 | writecf(FS_ERR, tmpstr); | 427 | mbedtls_ctr_drbg_init(&_mbedtls_state._ctr_drbg); |
370 | } | 428 | |
371 | return (ok | getintoption(CF_IGNSSL)); | 429 | mbedtls_ctr_drbg_seed(&_mbedtls_state._ctr_drbg, mbedtls_entropy_func, &_mbedtls_state._entropy, |
430 | (const unsigned char *) DRBG_PERS, sizeof (DRBG_PERS)); | ||
431 | } | ||
432 | memset(store, 0, sizeof(vc_x509store_t)); | ||
433 | |||
434 | /* We want to make verifying the peer the default */ | ||
435 | store->flags |= VC_X509S_SSL_VERIFY_PEER; | ||
436 | } | ||
437 | |||
438 | static void vc_tls_report_error(int error, char *message) { | ||
439 | size_t used = snprintf(tmpstr, sizeof(tmpstr), message); | ||
440 | mbedtls_strerror(error, tmpstr + used, sizeof(tmpstr) - used); | ||
441 | writecf(FS_ERR, tmpstr); | ||
442 | } | ||
443 | |||
444 | int vc_tls_connect( int serverfd, vc_x509store_t *vc_store ) | ||
445 | { | ||
446 | /* Some aliases for shorter references */ | ||
447 | mbedstate *s = &_mbedtls_state; | ||
448 | mbedtls_ssl_config *conf = &_mbedtls_state._conf; | ||
449 | mbedtls_ssl_context *ssl = &_mbedtls_state._ssl; | ||
450 | int ret; | ||
451 | |||
452 | mbedtls_x509_crt_init(&s->_cacert); | ||
453 | mbedtls_x509_crt_init(&s->_cert); | ||
454 | mbedtls_pk_init(&s->_key); | ||
455 | |||
456 | mbedtls_ssl_config_init(conf); | ||
457 | mbedtls_ssl_config_defaults(conf, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); | ||
458 | /* TODO: Always verify peer */ | ||
459 | mbedtls_ssl_conf_authmode(conf, MBEDTLS_SSL_VERIFY_NONE); | ||
460 | mbedtls_ssl_conf_rng(conf, mbedtls_ctr_drbg_random, &s->_ctr_drbg); | ||
461 | |||
462 | /* mbedtls_ssl_conf_ciphersuites( */ | ||
463 | |||
464 | /* Read in all certs */ | ||
465 | if (vc_store->cafile) { | ||
466 | mbedtls_x509_crt_parse_file(&s->_cacert, vc_store->cafile); | ||
467 | mbedtls_ssl_conf_ca_chain(conf, &s->_cacert, NULL); | ||
468 | } | ||
469 | |||
470 | mbedtls_x509_crt_parse_file(&s->_cert, vc_store->certfile); | ||
471 | char *password = NULL; | ||
472 | char password_buf[1024]; | ||
473 | while (1) { | ||
474 | #if MBEDTLS_SSL_MAJOR_VERSION_3 < 3 | ||
475 | ret = mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password); | ||
476 | #else | ||
477 | // ret = fprintf(stderr, "ERROR: %d\n", mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password, mbedtls_ctr_drbg_random, &s->_ctr_drbg)); | ||
478 | ret = mbedtls_pk_parse_keyfile(&s->_key, vc_store->keyfile, password, mbedtls_ctr_drbg_random, &s->_ctr_drbg); | ||
479 | #endif | ||
480 | if (!ret) | ||
481 | break; | ||
482 | if (ret != MBEDTLS_ERR_PK_PASSWORD_REQUIRED && ret != MBEDTLS_ERR_PK_PASSWORD_MISMATCH) { | ||
483 | vc_tls_report_error(ret, "CREATE CTX: Loading key failed, mbedtls reports: "); | ||
484 | return -1; | ||
485 | } | ||
486 | if (ret == MBEDTLS_ERR_PK_PASSWORD_MISMATCH) | ||
487 | vc_tls_report_error(ret, "Wrong passphrase, mbedtls reports: "); | ||
488 | vc_store->askpass_callback(password_buf, sizeof(password_buf), 0, NULL); | ||
489 | password = password_buf; | ||
490 | } | ||
491 | memset_s(password_buf, sizeof(password_buf), 0, sizeof(password_buf)); | ||
492 | |||
493 | #if 0 | ||
494 | /* pk member made private in mbedtls 3 */ | ||
495 | if (mbedtls_pk_check_pair(&(s->_cert.pk), &s->_key)) { | ||
496 | fprintf(stderr, "KEYPAIR MISSMATCH\n"); | ||
497 | } | ||
498 | #endif | ||
499 | mbedtls_ssl_conf_own_cert(conf, &s->_cert, &s->_key); | ||
500 | |||
501 | /* Config constructed, pass to ssl */ | ||
502 | /* Init ssl and config structs and configure ssl ctx */ | ||
503 | mbedtls_ssl_init(ssl); | ||
504 | mbedtls_ssl_setup(ssl, conf); | ||
505 | /* TODO: mbedtls_ssl_set_hostname(&ssl, SERVER_NAME) */ | ||
506 | |||
507 | mbedtls_ssl_set_bio(ssl, (void*)(intptr_t)serverfd, static_tcp_send, static_tcp_recv, NULL ); | ||
508 | |||
509 | while ((ret = mbedtls_ssl_handshake(ssl)) != 0) { | ||
510 | if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { | ||
511 | vc_tls_report_error(ret, "TLS handshake failed, mbedtls reports: "); | ||
512 | return -1; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | mbedtls_ssl_get_verify_result(ssl); | ||
517 | |||
518 | return 0; | ||
372 | } | 519 | } |
373 | 520 | ||
374 | ssize_t vc_tls_sendmessage(const void *buf, size_t size) { | 521 | ssize_t vc_tls_sendmessage(const void *buf, size_t size) { |
375 | return BIO_write(server_conn, buf, size); | 522 | return mbedtls_ssl_write( &_mbedtls_state._ssl, buf, size); |
376 | } | 523 | } |
377 | 524 | ||
378 | ssize_t vc_tls_receivemessage(void *buf, size_t size) { | 525 | ssize_t vc_tls_receivemessage(void *buf, size_t size) { |
379 | ssize_t received = (ssize_t)BIO_read (server_conn, buf, size); | 526 | ssize_t received = (ssize_t)mbedtls_ssl_read (&_mbedtls_state._ssl, buf, size); |
380 | if (received != 0) | 527 | switch (received) { |
381 | return received; | 528 | case MBEDTLS_ERR_SSL_WANT_READ: |
382 | if (BIO_should_retry(server_conn)) | 529 | case MBEDTLS_ERR_SSL_WANT_WRITE: |
383 | return -2; | 530 | return -2; |
384 | return 0; | 531 | case MBEDTLS_ERR_SSL_CONN_EOF: |
532 | case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: | ||
533 | case 0: | ||
534 | return 0; | ||
535 | default: | ||
536 | if (received > 0) | ||
537 | return received; | ||
538 | return -1; | ||
539 | } | ||
385 | } | 540 | } |
386 | 541 | ||
387 | void vc_tls_cleanup() { | 542 | void vc_tls_cleanup() { |
388 | BIO_free_all( server_conn ); | 543 | mbedtls_x509_crt_free(&_mbedtls_state._cacert); |
389 | server_conn = NULL; | 544 | mbedtls_x509_crt_free(&_mbedtls_state._cert); |
545 | mbedtls_pk_free(&_mbedtls_state._key); | ||
546 | mbedtls_ssl_free(&_mbedtls_state._ssl ); | ||
547 | mbedtls_ssl_config_free(&_mbedtls_state._conf ); | ||
548 | mbedtls_ctr_drbg_free(&_mbedtls_state._ctr_drbg ); | ||
390 | } | 549 | } |
550 | |||
551 | #endif | ||