diff options
-rwxr-xr-x | Makefile | 5 | ||||
-rwxr-xr-x | TODO | 4 | ||||
-rwxr-xr-x | vchat-client.c | 13 | ||||
-rwxr-xr-x | vchat-config.h | 6 | ||||
-rwxr-xr-x | vchat-howto | 23 | ||||
-rwxr-xr-x | vchat-protocol.c | 372 | ||||
-rwxr-xr-x | vchat-ssl.c | 467 | ||||
-rwxr-xr-x | vchat-ssl.h | 53 | ||||
-rwxr-xr-x | vchat-ui.c | 3 | ||||
-rwxr-xr-x | vchat.h | 2 |
10 files changed, 642 insertions, 306 deletions
@@ -30,7 +30,7 @@ CFLAGS += $(OLDREADLINE) | |||
30 | PREFIX=/usr/local | 30 | PREFIX=/usr/local |
31 | 31 | ||
32 | LIBS = -lreadline -lncurses -lssl -lcrypto | 32 | LIBS = -lreadline -lncurses -lssl -lcrypto |
33 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o | 33 | OBJS = vchat-client.o vchat-ui.o vchat-protocol.o vchat-user.o vchat-commands.o vchat-ssl.o |
34 | 34 | ||
35 | 35 | ||
36 | ############################################## | 36 | ############################################## |
@@ -83,5 +83,8 @@ vchat-user.o: vchat-user.c vchat.h | |||
83 | vchat-commands.o: vchat-commands.c vchat.h vchat-config.h | 83 | vchat-commands.o: vchat-commands.c vchat.h vchat-config.h |
84 | $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c | 84 | $(CC) $(CFLAGS) -o vchat-commands.o -c vchat-commands.c |
85 | 85 | ||
86 | vchat-ssl.o: vchat-ssl.c vchat-ssl.h | ||
87 | $(CC) $(CFLAGS) -o vchat-ssl.o -c vchat-ssl.c | ||
88 | |||
86 | #vchat-client.1: vchat-client.sgml | 89 | #vchat-client.1: vchat-client.sgml |
87 | # docbook2man vchat-client.sgml | 90 | # docbook2man vchat-client.sgml |
@@ -1,3 +1,7 @@ | |||
1 | |||
2 | - SSL: print SSL fingerprint and let the user decide wether it's authentic | ||
3 | and store it to a fingerprint cache or not. | ||
4 | |||
1 | - more documentation | 5 | - more documentation |
2 | 6 | ||
3 | - .i with nickchanges | 7 | - .i with nickchanges |
diff --git a/vchat-client.c b/vchat-client.c index e0fc214..56cc721 100755 --- a/vchat-client.c +++ b/vchat-client.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <fcntl.h> | 25 | #include <fcntl.h> |
26 | #include <signal.h> | 26 | #include <signal.h> |
27 | #include <readline/readline.h> | 27 | #include <readline/readline.h> |
28 | #include <openssl/ssl.h> | ||
29 | #ifndef NO_LOCALE | 28 | #ifndef NO_LOCALE |
30 | #include <locale.h> | 29 | #include <locale.h> |
31 | #endif | 30 | #endif |
@@ -509,15 +508,11 @@ main (int argc, char **argv) | |||
509 | loadformats(GLOBAL_FORMAT_FILE); | 508 | loadformats(GLOBAL_FORMAT_FILE); |
510 | loadformats(getstroption (CF_FORMFILE)); | 509 | loadformats(getstroption (CF_FORMFILE)); |
511 | 510 | ||
512 | if (!getintoption(CF_USESSL)) { | 511 | if (!getintoption(CF_USESSL)) |
513 | setstroption(CF_SERVERHOST,"localhost"); | 512 | setstroption(CF_SERVERPORT,"2323"); |
514 | setstroption(CF_SERVERPORT,"2323"); | 513 | else |
515 | } else { | 514 | setstroption(CF_SERVERPORT,"2325"); |
516 | SSL_library_init (); | ||
517 | SSL_load_error_strings (); | ||
518 | } | ||
519 | 515 | ||
520 | |||
521 | /* install signal handler */ | 516 | /* install signal handler */ |
522 | signal (SIGINT, cleanup); | 517 | signal (SIGINT, cleanup); |
523 | signal (SIGHUP, cleanup); | 518 | signal (SIGHUP, cleanup); |
diff --git a/vchat-config.h b/vchat-config.h index e2bbc40..eb0afc1 100755 --- a/vchat-config.h +++ b/vchat-config.h | |||
@@ -24,14 +24,15 @@ | |||
24 | 24 | ||
25 | /* configuration array with structure as defined in vchat.h */ | 25 | /* configuration array with structure as defined in vchat.h */ |
26 | extern unsigned int usessl; | 26 | extern unsigned int usessl; |
27 | extern unsigned int ignssl; | ||
27 | extern unsigned int usetime; | 28 | extern unsigned int usetime; |
28 | extern unsigned int hscroll; | 29 | extern unsigned int hscroll; |
29 | 30 | ||
30 | static volatile configoption configoptions[] = { | 31 | static volatile configoption configoptions[] = { |
31 | /* config-option type name in file default value value localvar */ | 32 | /* config-option type name in file default value value localvar */ |
32 | {CF_NICK, CO_STR, "nick", NULL, NULL, { .pstr = &nick } }, | 33 | {CF_NICK, CO_STR, "nick", NULL, NULL, { .pstr = &nick } }, |
33 | {CF_FROM, CO_STR, "from", "vc-alpha-0.16", NULL, { NULL } }, | 34 | {CF_FROM, CO_STR, "from", "vc-alpha-0.17", NULL, { NULL } }, |
34 | {CF_SERVERHOST, CO_STR, "host", "pulse.flatline.de", NULL, { NULL } }, | 35 | {CF_SERVERHOST, CO_STR, "host", "localhost", NULL, { NULL } }, |
35 | {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, | 36 | {CF_SERVERPORT, CO_STR, "port", "2325", NULL, { NULL } }, |
36 | {CF_CIPHERSUITE, CO_STR, "ciphers", "HIGH:MEDIUM", NULL, { NULL } }, | 37 | {CF_CIPHERSUITE, CO_STR, "ciphers", "HIGH:MEDIUM", NULL, { NULL } }, |
37 | {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, | 38 | {CF_CONFIGFILE, CO_STR, "conffile", "~/.vchat/config", NULL, { NULL } }, |
@@ -41,6 +42,7 @@ static volatile configoption configoptions[] = { | |||
41 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, | 42 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, |
42 | {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, | 43 | {CF_ENCODING, CO_STR, "encoding", NULL, NULL, { .pstr = &encoding }}, |
43 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { .pint = &usessl } }, | 44 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { .pint = &usessl } }, |
45 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { .pint = &ignssl } }, | ||
44 | {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, | 46 | {CF_USECERT, CO_INT, "usecert", (char *) 1, (char *)-1, { NULL } }, |
45 | {CF_USETIME, CO_INT, "usetime", (char *) 1, (char *)-1, { .pint = &usetime } }, | 47 | {CF_USETIME, CO_INT, "usetime", (char *) 1, (char *)-1, { .pint = &usetime } }, |
46 | {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, | 48 | {CF_USETOPIC, CO_INT, "usetopicbar",(char *) 1, (char *)-1, { NULL } }, |
diff --git a/vchat-howto b/vchat-howto index da9ac6f..22c34b7 100755 --- a/vchat-howto +++ b/vchat-howto | |||
@@ -70,6 +70,29 @@ type: | |||
70 | 70 | ||
71 | $ echo host=vchat.berlin.ccc.de >> ~/.vchat/config | 71 | $ echo host=vchat.berlin.ccc.de >> ~/.vchat/config |
72 | 72 | ||
73 | If you want to ignore SSL-warnings due to missing CA-files, type: | ||
74 | |||
75 | $ echo ignssl=1 >> ~/.vchat/config | ||
76 | |||
77 | If you don't want to ignore SSL-warnings, get the root-certificates from: | ||
78 | |||
79 | http://www.cacert.org/certs/class3.txt | ||
80 | and | ||
81 | http://www.cacert.org/certs/root.txt | ||
82 | |||
83 | and copy them into your openssl-certs directory. For example: | ||
84 | |||
85 | # cp root.txt /etc/ssl/certs/ | ||
86 | # cp class3.txt /etc/ssl/certs/ | ||
87 | # cd /etc/ssl/certs | ||
88 | # ln -s root.txt `openssl x509 -in root.txt -hash | head -n 1`.0 | ||
89 | # ln -s class3.txt `openssl x509 -in class3.txt -hash | head -n 1`.0 | ||
90 | |||
91 | Now you can type: | ||
92 | |||
93 | $ echo ignssl=0 >> ~/.vchat/config | ||
94 | |||
95 | |||
73 | If you want a seperate private message window, type: | 96 | If you want a seperate private message window, type: |
74 | 97 | ||
75 | $ echo messages=10 >> ~/.vchat/config | 98 | $ echo messages=10 >> ~/.vchat/config |
diff --git a/vchat-protocol.c b/vchat-protocol.c index fc89cf3..274e856 100755 --- a/vchat-protocol.c +++ b/vchat-protocol.c | |||
@@ -33,6 +33,7 @@ | |||
33 | 33 | ||
34 | /* local includes */ | 34 | /* local includes */ |
35 | #include "vchat.h" | 35 | #include "vchat.h" |
36 | #include "vchat-ssl.h" | ||
36 | 37 | ||
37 | /* version of this module */ | 38 | /* version of this module */ |
38 | char *vchat_io_version = "$Id$"; | 39 | char *vchat_io_version = "$Id$"; |
@@ -43,14 +44,7 @@ unsigned int usingcert = 1; | |||
43 | 44 | ||
44 | /* locally global variables */ | 45 | /* locally global variables */ |
45 | /* SSL-connection */ | 46 | /* SSL-connection */ |
46 | static SSL *sslconn = NULL; | 47 | static BIO *sslconn = NULL; |
47 | |||
48 | /* declaration of an OpenSSL function which isn't exported by the includes, | ||
49 | * but by the library. we use it to set the accepted list of ciphers */ | ||
50 | STACK_OF(SSL_CIPHER) * ssl_create_cipher_list (const SSL_METHOD * meth, STACK_OF (SSL_CIPHER) ** pref, STACK_OF (SSL_CIPHER) ** sorted, const unsigned char *rule_str); | ||
51 | |||
52 | static char *sslpubkey = NULL; /* servers public key extracted from X.509 certificate */ | ||
53 | static int sslpubkeybits = 0; /* length of server public key */ | ||
54 | 48 | ||
55 | /* declaration of local helper functions */ | 49 | /* declaration of local helper functions */ |
56 | static void usersignon (char *); | 50 | static void usersignon (char *); |
@@ -80,281 +74,90 @@ static int getportnum (char *port); | |||
80 | extern int status; | 74 | extern int status; |
81 | 75 | ||
82 | int usessl = 1; | 76 | int usessl = 1; |
77 | int ignssl = 0; | ||
83 | char *encoding; | 78 | char *encoding; |
84 | 79 | ||
85 | /* connects to server */ | 80 | /* connects to server */ |
86 | int | 81 | int |
87 | vcconnect (char *server, char *port) | 82 | vcconnect (char *server, char *port) |
88 | { | 83 | { |
89 | /* used for tilde expansion of cert & key filenames */ | 84 | /* used for tilde expansion of cert & key filenames */ |
90 | char *tildex = NULL; | 85 | char *tildex = NULL; |
91 | /* buffer for X.509 subject of server certificate */ | ||
92 | char subjbuf[256]; | ||
93 | /* variables used to split the subject */ | ||
94 | char *subjv = NULL; | ||
95 | char *subjn = NULL; | ||
96 | char *subjc = NULL; | ||
97 | char *subjh = NULL; | ||
98 | /* pointer to key in certificate */ | ||
99 | EVP_PKEY *certpubkey = NULL; | ||
100 | /* temporary result */ | ||
101 | int result; | ||
102 | /* protocol independent server addresses */ | ||
103 | struct addrinfo hints, *addr, *tmpaddr; | ||
104 | /* SSL-context */ | ||
105 | SSL_CTX *sslctx = NULL; | ||
106 | /* SSL server certificate */ | ||
107 | X509 *sslserv = NULL; | ||
108 | /* SSL method function */ | ||
109 | SSL_METHOD *sslmeth = NULL; | ||
110 | |||
111 | /* pointer to tilde-expanded certificate/keyfile-names */ | ||
112 | char *certfile = NULL, *keyfile = NULL; | ||
113 | |||
114 | /* variable for verify return */ | ||
115 | long verify; | ||
116 | |||
117 | memset( &hints, 0, sizeof(hints)); | ||
118 | /* Expect v4 and v6 */ | ||
119 | hints.ai_family = PF_UNSPEC; | ||
120 | hints.ai_socktype = SOCK_STREAM; | ||
121 | if( getaddrinfo( server, port, &hints, &addr )) | ||
122 | return 0; | ||
123 | for( tmpaddr = addr; addr; addr = addr->ai_next ) | ||
124 | { | ||
125 | if( (serverfd = socket( addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0) | ||
126 | continue; | ||
127 | if( connect( serverfd, addr->ai_addr, addr->ai_addrlen ) < 0) | ||
128 | { | ||
129 | close( serverfd ); | ||
130 | serverfd = -1; | ||
131 | continue; | ||
132 | } | ||
133 | break; | ||
134 | } | ||
135 | if( serverfd < 0 ) | ||
136 | return 0; | ||
137 | freeaddrinfo( tmpaddr ); | ||
138 | 86 | ||
139 | /* inform user */ | 87 | /* vchat connection x509 store */ |
140 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, getportnum(port)); | 88 | vc_x509store_t vc_store; |
141 | writechan (tmpstr); | ||
142 | 89 | ||
143 | usessl = getintoption(CF_USESSL); | 90 | /* SSL-context */ |
91 | SSL_CTX *sslctx = NULL; | ||
144 | 92 | ||
145 | /* do we want to use SSL? */ | 93 | /* pointer to tilde-expanded certificate/keyfile-names */ |
146 | if (usessl) | 94 | char *certfile = NULL, *keyfile = NULL; |
147 | { | ||
148 | /* set ssl method -> SSLv2, SSLv3 & TLSv1 client */ | ||
149 | sslmeth = SSLv23_client_method (); | ||
150 | /* generate new SSL context */ | ||
151 | sslctx = SSL_CTX_new (sslmeth); | ||
152 | |||
153 | /* set passphrase-callback to own function from vchat-ui.c */ | ||
154 | SSL_CTX_set_default_passwd_cb (sslctx, passprompt); | ||
155 | |||
156 | /* set our list of accepted ciphers */ | ||
157 | ssl_create_cipher_list (sslctx->method, &(sslctx->cipher_list), &(sslctx->cipher_list_by_id), (unsigned char*)getstroption (CF_CIPHERSUITE)); | ||
158 | |||
159 | /* get name of certificate file */ | ||
160 | certfile = getstroption (CF_CERTFILE); | ||
161 | |||
162 | /* do we have a certificate file? */ | ||
163 | if (certfile) | ||
164 | { | ||
165 | /* does the filename start with a tilde? expand it! */ | ||
166 | if (certfile[0] == '~') | ||
167 | tildex = tilde_expand (certfile); | ||
168 | else | ||
169 | tildex = certfile; | ||
170 | |||
171 | if (usingcert) { | ||
172 | /* try to load certificate */ | ||
173 | result = SSL_CTX_use_certificate_file (sslctx, tildex, SSL_FILETYPE_PEM); | ||
174 | if (!result) | ||
175 | { | ||
176 | /* failed, inform user */ | ||
177 | snprintf (tmpstr, TMPSTRSIZE, "!! Loading user certificate fails: %s", ERR_error_string (ERR_get_error (), NULL)); | ||
178 | writecf (FS_ERR,tmpstr); | ||
179 | } | ||
180 | else | ||
181 | { | ||
182 | /* get name of key file */ | ||
183 | keyfile = getstroption (CF_KEYFILE); | ||
184 | |||
185 | /* if we don't have a key file, the key may be in the cert file */ | ||
186 | if (!keyfile) | ||
187 | keyfile = certfile; | ||
188 | |||
189 | /* does the filename start with a tilde? expand it! */ | ||
190 | if (keyfile[0] == '~') | ||
191 | tildex = tilde_expand (keyfile); | ||
192 | else | ||
193 | tildex = keyfile; | ||
194 | |||
195 | /* try to load key (automatically asks for passphrase, if encrypted */ | ||
196 | result = SSL_CTX_use_PrivateKey_file (sslctx, tildex, SSL_FILETYPE_PEM); | ||
197 | if (!result) | ||
198 | { | ||
199 | /* failed, inform user */ | ||
200 | snprintf (tmpstr, TMPSTRSIZE, "!! Loading private key fails: %s", ERR_error_string (ERR_get_error (), NULL)); | ||
201 | writecf (FS_ERR,tmpstr); | ||
202 | } | ||
203 | else | ||
204 | { | ||
205 | /* check if OpenSSL thinks key & cert belong together */ | ||
206 | result = SSL_CTX_check_private_key (sslctx); | ||
207 | if (!result) | ||
208 | { | ||
209 | /* they don't, inform user */ | ||
210 | snprintf (tmpstr, TMPSTRSIZE, "!! Verifying key and certificate fails: %s", ERR_error_string (ERR_get_error (), NULL)); | ||
211 | writecf (FS_ERR,tmpstr); | ||
212 | } | ||
213 | } | ||
214 | } | ||
215 | } | ||
216 | } | ||
217 | |||
218 | /* don't worry to much about servers X.509 certificate chain */ | ||
219 | SSL_CTX_set_verify_depth (sslctx, 0); | ||
220 | |||
221 | /* debug massage about verify mode */ | ||
222 | snprintf (tmpstr, TMPSTRSIZE, "# Connecting with verify depth %d in mode %d", SSL_CTX_get_verify_depth (sslctx), SSL_CTX_get_verify_mode (sslctx)); | ||
223 | writecf (FS_DBG,tmpstr); | ||
224 | |||
225 | /* generate new SSL connection object and associate | ||
226 | * filedescriptor of server connection */ | ||
227 | sslconn = SSL_new (sslctx); | ||
228 | SSL_set_fd (sslconn, serverfd); | ||
229 | |||
230 | /* try SSL handshake */ | ||
231 | if (SSL_connect (sslconn) <= 0) | ||
232 | { | ||
233 | /* no SSL connection possible */ | ||
234 | snprintf (tmpstr, TMPSTRSIZE, "!! SSL Connect fails: %s", ERR_error_string (ERR_get_error (), NULL)); | ||
235 | writecf (FS_ERR,tmpstr); | ||
236 | return 0; | ||
237 | } | ||
238 | 95 | ||
239 | /* debug message about used symmetric cipher */ | 96 | SSL_library_init (); |
240 | snprintf (tmpstr, TMPSTRSIZE, "# SSL connection uses %s cipher (%d bits)", SSL_get_cipher_name (sslconn), SSL_get_cipher_bits (sslconn, NULL)); | 97 | SSL_load_error_strings(); |
241 | writecf (FS_DBG,tmpstr); | ||
242 | |||
243 | /* if we can't get the servers certificate something is damn wrong */ | ||
244 | if (!(sslserv = SSL_get_peer_certificate (sslconn))) | ||
245 | return 0; | ||
246 | |||
247 | /* ugly dump of server certificate */ | ||
248 | /* TODO: make this happen elsewhere, and preferably only when user wants | ||
249 | * or a new key needs to be added to the known_hosts */ | ||
250 | writecf (FS_DBG,"# SSL Server information:"); | ||
251 | /* copy X.509 to local buffer */ | ||
252 | strncpy (subjbuf, sslserv->name, 256); | ||
253 | /* split a subject line and print the fullname/value pairs */ | ||
254 | subjv = &subjbuf[1]; | ||
255 | while (subjv) | ||
256 | { | ||
257 | subjc = strchr (subjv, '='); | ||
258 | subjc[0] = '\0'; | ||
259 | subjc++; | ||
260 | /* yeah, ugly */ | ||
261 | if (!strcasecmp ("C", subjv)) | ||
262 | { | ||
263 | subjn = "Country "; | ||
264 | } | ||
265 | else if (!strcasecmp ("ST", subjv)) | ||
266 | { | ||
267 | subjn = "State "; | ||
268 | } | ||
269 | else if (!strcasecmp ("L", subjv)) | ||
270 | { | ||
271 | subjn = "Location "; | ||
272 | } | ||
273 | else if (!strcasecmp ("O", subjv)) | ||
274 | { | ||
275 | subjn = "Organization"; | ||
276 | } | ||
277 | else if (!strcasecmp ("OU", subjv)) | ||
278 | { | ||
279 | subjn = "Organiz.unit"; | ||
280 | } | ||
281 | else if (!strcasecmp ("CN", subjv)) | ||
282 | { | ||
283 | subjn = "Common name "; | ||
284 | subjh = subjc; | ||
285 | } | ||
286 | else if (!strcasecmp ("Email", subjv)) | ||
287 | { | ||
288 | subjn = "Emailaddress"; | ||
289 | } | ||
290 | else | ||
291 | { | ||
292 | subjn = "UNKNOWN "; | ||
293 | } | ||
294 | subjv = strchr (subjc, '/'); | ||
295 | if (subjv) | ||
296 | { | ||
297 | subjv[0] = '\0'; | ||
298 | subjv++; | ||
299 | } | ||
300 | /* print value pair */ | ||
301 | snprintf (tmpstr, TMPSTRSIZE, "# %s: %s", subjn, subjc); | ||
302 | writecf (FS_DBG,tmpstr); | ||
303 | } | ||
304 | 98 | ||
305 | /* check if verifying the server's certificate yields any errors */ | 99 | vc_init_x509store(&vc_store); |
306 | verify = SSL_get_verify_result (sslconn); | ||
307 | if (verify) | ||
308 | { | ||
309 | /* it does - yield a warning to the user */ | ||
310 | snprintf (tmpstr, TMPSTRSIZE, "!! Certificate verification fails: %s", X509_verify_cert_error_string (verify)); | ||
311 | writecf (FS_ERR,tmpstr); | ||
312 | } | ||
313 | 100 | ||
314 | /* check if servers name in certificate matches the hostname we connected to */ | 101 | vc_x509store_setflags(&vc_store, VC_X509S_SSL_VERIFY_PEER); |
315 | if (subjh) | 102 | /* get name of certificate file */ |
316 | if (strcasecmp (server, subjh)) | 103 | certfile = getstroption (CF_CERTFILE); |
317 | { | 104 | |
318 | /* it doesn't - yield a warning and show difference */ | 105 | /* do we have a certificate file? */ |
319 | writecf (FS_ERR,"!! Server name does not match name in presented certificate!"); | 106 | if (certfile) { |
320 | snprintf (tmpstr, TMPSTRSIZE, "!! '%s' != '%s'", server, subjh); | 107 | /* does the filename start with a tilde? expand it! */ |
321 | writecf (FS_ERR,tmpstr); | 108 | if (certfile[0] == '~') |
322 | } | 109 | tildex = tilde_expand (certfile); |
323 | |||
324 | /* try to get the servers public key */ | ||
325 | certpubkey = X509_get_pubkey (sslserv); | ||
326 | if (certpubkey == NULL) | ||
327 | { | ||
328 | /* huh, we can't? */ | ||
329 | writecf (FS_ERR,"!! Can't get the public key associated to the certificate"); | ||
330 | } | ||
331 | /* check what type of key we've got here */ | ||
332 | else if (certpubkey->type == EVP_PKEY_RSA) | ||
333 | { | ||
334 | /* RSA key, convert bignum values from OpenSSL's structures to | ||
335 | * something readable */ | ||
336 | sslpubkey = BN_bn2hex (certpubkey->pkey.rsa->n); | ||
337 | sslpubkeybits = BN_num_bits (certpubkey->pkey.rsa->n); | ||
338 | /* dump keylength and hexstring of servers key to user */ | ||
339 | snprintf (tmpstr, TMPSTRSIZE, "# RSA public key%s: %d %s", (certpubkey->pkey.rsa-> d) ? " (private key available)" : "", sslpubkeybits, sslpubkey); | ||
340 | writecf (FS_DBG,tmpstr); | ||
341 | /* TODO: known_hosts check here ... */ | ||
342 | } | ||
343 | else if (certpubkey->type == EVP_PKEY_DSA) | ||
344 | { | ||
345 | /* Can't handle (and didn't encounter) DSA keys */ | ||
346 | writecf (FS_ERR,"# DSA Public Key (output currently not supported)"); | ||
347 | /* TODO: known_hosts check here ... */ | ||
348 | } | ||
349 | else | 110 | else |
350 | { | 111 | tildex = certfile; |
351 | writecf (FS_ERR,"# Public Key type not supported"); | 112 | |
352 | /* TODO: fail known_hosts check ... */ | 113 | if (usingcert) { |
353 | } | 114 | |
354 | } | 115 | vc_x509store_setflags(&vc_store, VC_X509S_USE_CERTIFICATE); |
116 | vc_x509store_setcertfile(&vc_store, tildex); | ||
117 | |||
118 | /* get name of key file */ | ||
119 | keyfile = getstroption (CF_KEYFILE); | ||
120 | |||
121 | /* if we don't have a key file, the key may be in the cert file */ | ||
122 | if (!keyfile) | ||
123 | keyfile = certfile; | ||
355 | 124 | ||
356 | /* if we didn't fail until now, we've got a connection. */ | 125 | /* does the filename start with a tilde? expand it! */ |
357 | return 1; | 126 | if (keyfile[0] == '~') |
127 | tildex = tilde_expand (keyfile); | ||
128 | else | ||
129 | tildex = keyfile; | ||
130 | |||
131 | vc_x509store_set_pkeycb(&vc_store, (vc_askpass_cb_t)passprompt); | ||
132 | vc_x509store_setkeyfile(&vc_store, tildex); | ||
133 | |||
134 | /* check if OpenSSL thinks key & cert belong together */ | ||
135 | /* result = SSL_CTX_check_private_key (sslctx); THS TODO (-> | ||
136 | * vchat-ssl.c) */ | ||
137 | } | ||
138 | } | ||
139 | |||
140 | usessl = getintoption(CF_USESSL); | ||
141 | vc_x509store_setignssl(&vc_store, getintoption(CF_IGNSSL)); | ||
142 | |||
143 | sslconn = vc_connect(server, getportnum(port), usessl, &vc_store, &sslctx); | ||
144 | |||
145 | if(sslconn == NULL) { | ||
146 | exitui(); | ||
147 | exit(-1); | ||
148 | } | ||
149 | |||
150 | serverfd = BIO_get_fd(sslconn, 0); | ||
151 | |||
152 | /* inform user */ | ||
153 | snprintf (tmpstr, TMPSTRSIZE, getformatstr(FS_CONNECTED), server, getportnum(port)); | ||
154 | writechan (tmpstr); | ||
155 | |||
156 | /* dump x509 details here... TODO */ | ||
157 | writecf (FS_DBG,"# SSL Server information: TODO :)"); | ||
158 | |||
159 | /* if we didn't fail until now, we've got a connection. */ | ||
160 | return 1; | ||
358 | } | 161 | } |
359 | 162 | ||
360 | /* disconnect from server */ | 163 | /* disconnect from server */ |
@@ -1021,11 +824,8 @@ networkinput (void) | |||
1021 | char *ltmp = buf; | 824 | char *ltmp = buf; |
1022 | buf[BUFSIZE-1] = '\0'; /* sanity stop */ | 825 | buf[BUFSIZE-1] = '\0'; /* sanity stop */ |
1023 | 826 | ||
1024 | /* check if we use ssl or if we don't and receive data at offset */ | 827 | /* receive data at offset */ |
1025 | if (!usessl) | 828 | bytes = BIO_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff); |
1026 | bytes = recv (serverfd, &buf[bufoff], BUFSIZE-1 - bufoff, 0); | ||
1027 | else | ||
1028 | bytes = SSL_read (sslconn, &buf[bufoff], BUFSIZE-1 - bufoff); | ||
1029 | 829 | ||
1030 | /* no bytes transferred? raise error message, bail out */ | 830 | /* no bytes transferred? raise error message, bail out */ |
1031 | if (bytes < 0) | 831 | if (bytes < 0) |
@@ -1093,23 +893,11 @@ networkoutput (char *msg) | |||
1093 | fprintf (stderr, ">| %s\n", msg); | 893 | fprintf (stderr, ">| %s\n", msg); |
1094 | #endif | 894 | #endif |
1095 | 895 | ||
1096 | /* TODO: err .. rework this (blocking) code */ | 896 | /* send data to server */ |
897 | if (BIO_write (sslconn, msg, strlen (msg)) != strlen (msg)) | ||
898 | writecf (FS_ERR,"Message sending fuzzy."); | ||
1097 | 899 | ||
1098 | /* check if we use ssl or if we don't and send data to server */ | 900 | /* send line termination to server */ |
1099 | if (!usessl) { | 901 | if (BIO_write (sslconn, "\r\n", 2) != 2) |
1100 | if (send (serverfd, msg, strlen (msg), 0) != strlen (msg)) { | 902 | writecf (FS_ERR,"Message sending fuzzy."); |
1101 | writecf (FS_ERR,"Message sending fuzzy."); | ||
1102 | } | ||
1103 | } else if (SSL_write (sslconn, msg, strlen (msg)) != strlen (msg)) { | ||
1104 | writecf (FS_ERR,"Message sending fuzzy."); | ||
1105 | } | ||
1106 | |||
1107 | /* check if we use ssl or if we don't and send line termination to server */ | ||
1108 | if (!usessl) { | ||
1109 | if (send (serverfd, "\r\n", 2, 0) != 2) { | ||
1110 | writecf (FS_ERR,"Message sending fuzzy!"); | ||
1111 | } | ||
1112 | } else if (SSL_write (sslconn, "\r\n", 2) != 2) { | ||
1113 | writecf (FS_ERR,"Message sending fuzzy."); | ||
1114 | } | ||
1115 | } | 903 | } |
diff --git a/vchat-ssl.c b/vchat-ssl.c new file mode 100755 index 0000000..5d68309 --- /dev/null +++ b/vchat-ssl.c | |||
@@ -0,0 +1,467 @@ | |||
1 | /* | ||
2 | * vchat-client - alpha version | ||
3 | * vchat-ssl.c - handling of SSL connection and X509 certificate | ||
4 | * verification | ||
5 | * | ||
6 | * Copyright (C) 2007 Thorsten Schroeder <ths@berlin.ccc.de> | ||
7 | * | ||
8 | * This program is free software. It can be redistributed and/or modified, | ||
9 | * provided that this copyright notice is kept intact. This program is | ||
10 | * distributed in the hope that it will be useful, but without any warranty; | ||
11 | * without even the implied warranty of merchantability or fitness for a | ||
12 | * particular purpose. In no event shall the copyright holder be liable for | ||
13 | * any direct, indirect, incidental or special damages arising in any way out | ||
14 | * of the use of this software. | ||
15 | * | ||
16 | */ | ||
17 | |||
18 | #include <stdio.h> | ||
19 | #include <stdlib.h> | ||
20 | #include <string.h> | ||
21 | |||
22 | #include <openssl/err.h> | ||
23 | #include <openssl/ssl.h> | ||
24 | #include <openssl/bio.h> | ||
25 | #include <openssl/evp.h> | ||
26 | #include <openssl/x509.h> | ||
27 | #include <openssl/x509v3.h> | ||
28 | #include <openssl/conf.h> | ||
29 | |||
30 | #include "vchat.h" | ||
31 | #include "vchat-ssl.h" | ||
32 | |||
33 | char *vchat_ssl_version = "$Id$"; | ||
34 | |||
35 | static int ignore_ssl; | ||
36 | |||
37 | #define VC_CTX_ERR_EXIT(se, cx) do { \ | ||
38 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", \ | ||
39 | ERR_error_string (ERR_get_error (), NULL)); \ | ||
40 | writecf(FS_ERR, tmpstr); \ | ||
41 | if(se) X509_STORE_free(se); \ | ||
42 | if(cx) SSL_CTX_free(cx); \ | ||
43 | return(0); \ | ||
44 | } while(0) | ||
45 | |||
46 | #define VC_SETCERT_ERR_EXIT(se, cx, err) do { \ | ||
47 | snprintf(tmpstr, TMPSTRSIZE, "CREATE CTX: %s", err); \ | ||
48 | writecf(FS_ERR, tmpstr); \ | ||
49 | if(se) X509_STORE_free(se); \ | ||
50 | if(cx) SSL_CTX_free(cx); \ | ||
51 | return(NULL); \ | ||
52 | } while(0) | ||
53 | |||
54 | SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) | ||
55 | { | ||
56 | int i = 0; | ||
57 | int n = 0; | ||
58 | int flags = 0; | ||
59 | int r = 0; | ||
60 | SSL_CTX *ctx = NULL; | ||
61 | X509_STORE *store = NULL; | ||
62 | vc_x509verify_cb_t verify_callback = NULL; | ||
63 | |||
64 | if( !(ctx = SSL_CTX_new(SSLv3_method())) ) | ||
65 | VC_CTX_ERR_EXIT(store, ctx); | ||
66 | |||
67 | if( !(store = vc_x509store_create(vc_store)) ) | ||
68 | VC_CTX_ERR_EXIT(store, ctx); | ||
69 | |||
70 | SSL_CTX_set_cert_store(ctx, store); | ||
71 | store = NULL; | ||
72 | SSL_CTX_set_options(ctx, SSL_OP_ALL|SSL_OP_NO_SSLv2); | ||
73 | SSL_CTX_set_cipher_list(ctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); | ||
74 | |||
75 | SSL_CTX_set_verify_depth (ctx, 2); | ||
76 | |||
77 | if( !(verify_callback = vc_store->callback) ) | ||
78 | verify_callback = vc_verify_callback; | ||
79 | |||
80 | if( !(vc_store->flags & VC_X509S_SSL_VERIFY_MASK) ) { | ||
81 | writecf(FS_DBG, tmpstr); | ||
82 | flags = SSL_VERIFY_NONE; | ||
83 | } | ||
84 | else { | ||
85 | if(vc_store->flags & VC_X509S_SSL_VERIFY_PEER) | ||
86 | flags |= SSL_VERIFY_PEER; | ||
87 | if(vc_store->flags & VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT) | ||
88 | flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; | ||
89 | if(vc_store->flags & VC_X509S_SSL_VERIFY_CLIENT_ONCE) | ||
90 | flags |= SSL_VERIFY_CLIENT_ONCE; | ||
91 | } | ||
92 | |||
93 | SSL_CTX_set_verify(ctx, flags, verify_callback); | ||
94 | |||
95 | if(vc_store->flags & VC_X509S_USE_CERTIFICATE) { | ||
96 | if(vc_store->use_certfile) | ||
97 | SSL_CTX_use_certificate_chain_file(ctx, vc_store->use_certfile); | ||
98 | else { | ||
99 | SSL_CTX_use_certificate(ctx, | ||
100 | sk_X509_value(vc_store->use_certs, 0)); | ||
101 | for(i=0,n=sk_X509_num(vc_store->use_certs); i<n; i++) | ||
102 | SSL_CTX_add_extra_chain_cert(ctx, | ||
103 | sk_X509_value(vc_store->use_certs, i)); | ||
104 | } | ||
105 | |||
106 | SSL_CTX_set_default_passwd_cb(ctx, vc_store->askpass_callback); | ||
107 | |||
108 | if(vc_store->use_keyfile) { | ||
109 | r=SSL_CTX_use_PrivateKey_file(ctx, vc_store->use_keyfile, | ||
110 | SSL_FILETYPE_PEM); | ||
111 | } else if(vc_store->use_key) | ||
112 | r=SSL_CTX_use_PrivateKey(ctx, vc_store->use_key); | ||
113 | |||
114 | if(r!=1) | ||
115 | VC_SETCERT_ERR_EXIT(store, ctx, "Load private key failed"); | ||
116 | |||
117 | } | ||
118 | |||
119 | SSL_CTX_set_app_data(ctx, vc_store); | ||
120 | return(ctx); | ||
121 | } | ||
122 | |||
123 | #define VC_CONNSSL_ERR_EXIT(_cx, cx, cn) do { \ | ||
124 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \ | ||
125 | ERR_error_string (ERR_get_error (), NULL)); \ | ||
126 | writecf(FS_ERR, tmpstr); \ | ||
127 | if(cn) BIO_free_all(cn); \ | ||
128 | if(*cx) SSL_CTX_free(*cx); \ | ||
129 | if(_cx) *cx = 0; \ | ||
130 | return(0); \ | ||
131 | } while(0) | ||
132 | |||
133 | BIO * vc_connect_ssl(char *host, int port, vc_x509store_t *vc_store, | ||
134 | SSL_CTX **ctx) | ||
135 | { | ||
136 | BIO *conn = NULL; | ||
137 | int _ctx = 0; | ||
138 | |||
139 | if(*ctx) { | ||
140 | CRYPTO_add( &((*ctx)->references), 1, CRYPTO_LOCK_SSL_CTX ); | ||
141 | if( vc_store && vc_store != SSL_CTX_get_app_data(*ctx) ) { | ||
142 | SSL_CTX_set_cert_store(*ctx, vc_x509store_create(vc_store)); | ||
143 | SSL_CTX_set_app_data(*ctx, vc_store); | ||
144 | } | ||
145 | } else { | ||
146 | *ctx = vc_create_sslctx(vc_store); | ||
147 | _ctx = 1; | ||
148 | } | ||
149 | |||
150 | if( !(conn = BIO_new_ssl_connect(*ctx)) ) | ||
151 | VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn); | ||
152 | |||
153 | BIO_set_conn_hostname(conn, host); | ||
154 | BIO_set_conn_int_port(conn, &port); | ||
155 | |||
156 | fflush(stdout); | ||
157 | if(BIO_do_connect(conn) <= 0) | ||
158 | VC_CONNSSL_ERR_EXIT(_ctx, ctx, conn); | ||
159 | |||
160 | if(_ctx) | ||
161 | SSL_CTX_free(*ctx); | ||
162 | |||
163 | return(conn); | ||
164 | } | ||
165 | |||
166 | #define VC_CONN_ERR_EXIT(cn) do { \ | ||
167 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", \ | ||
168 | ERR_error_string(ERR_get_error(), NULL)); \ | ||
169 | if(ERR_get_error()) \ | ||
170 | writecf(FS_ERR, tmpstr); \ | ||
171 | if(cn) BIO_free_all(cn); \ | ||
172 | return(NULL); \ | ||
173 | } while(0) | ||
174 | |||
175 | #define VC_VERIFICATION_ERR_EXIT(cn, err) do { \ | ||
176 | snprintf(tmpstr, TMPSTRSIZE, \ | ||
177 | "[SSL ERROR] certificate verify failed: %s", err); \ | ||
178 | writecf(FS_ERR, tmpstr); \ | ||
179 | if(cn && !ignore_ssl) { BIO_free_all(cn); return(NULL); } \ | ||
180 | } while(0) | ||
181 | |||
182 | BIO * vc_connect( char *host, int port, int use_ssl, | ||
183 | vc_x509store_t *vc_store, SSL_CTX **ctx) | ||
184 | { | ||
185 | BIO *conn = NULL; | ||
186 | SSL *ssl = NULL; | ||
187 | |||
188 | if(use_ssl) { | ||
189 | if( !(conn = vc_connect_ssl(host, port, vc_store, ctx)) ) | ||
190 | VC_CONN_ERR_EXIT(conn); | ||
191 | |||
192 | |||
193 | BIO_get_ssl(conn, &ssl); | ||
194 | if(!vc_verify_cert_hostname(SSL_get_peer_certificate(ssl), host)) | ||
195 | VC_VERIFICATION_ERR_EXIT(conn, "Hostname does not match!"); | ||
196 | |||
197 | return(conn); | ||
198 | } | ||
199 | |||
200 | *ctx = 0; | ||
201 | |||
202 | if( !(conn = BIO_new_connect(host)) ) | ||
203 | VC_CONN_ERR_EXIT(conn); | ||
204 | |||
205 | BIO_set_conn_int_port(conn, &port); | ||
206 | |||
207 | if(BIO_do_connect(conn) <= 0) | ||
208 | VC_CONN_ERR_EXIT(conn); | ||
209 | |||
210 | return(conn); | ||
211 | } | ||
212 | |||
213 | int vc_verify_cert_hostname(X509 *cert, char *host) | ||
214 | { | ||
215 | |||
216 | int i = 0; | ||
217 | int j = 0; | ||
218 | int n = 0; | ||
219 | int extcount = 0; | ||
220 | int ok = 0; | ||
221 | |||
222 | X509_NAME *subj = NULL; | ||
223 | const char *extstr = NULL; | ||
224 | CONF_VALUE *nval = NULL; | ||
225 | unsigned char *data = NULL; | ||
226 | X509_EXTENSION *ext = NULL; | ||
227 | X509V3_EXT_METHOD *meth = NULL; | ||
228 | STACK_OF(CONF_VALUE) *val = NULL; | ||
229 | |||
230 | char name[256]; | ||
231 | memset(&name, 0, sizeof(name)); | ||
232 | |||
233 | if((extcount = X509_get_ext_count(cert)) > 0) { | ||
234 | |||
235 | for(i=0; !ok && i < extcount; i++) { | ||
236 | |||
237 | meth = NULL; | ||
238 | |||
239 | ext = X509_get_ext(cert, i); | ||
240 | extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext))); | ||
241 | |||
242 | if(!strcasecmp(extstr, "subjectAltName")) { | ||
243 | |||
244 | if( !(meth = X509V3_EXT_get(ext)) ) | ||
245 | break; | ||
246 | |||
247 | if( !(meth->d2i) ) | ||
248 | break; | ||
249 | |||
250 | data = ext->value->data; | ||
251 | |||
252 | val = meth->i2v(meth, meth->d2i(0, &data, ext->value->length), 0); | ||
253 | for( j=0, n=sk_CONF_VALUE_num(val); j<n; j++ ) { | ||
254 | nval = sk_CONF_VALUE_value(val, j); | ||
255 | if( !strcasecmp(nval->name, "DNS") && | ||
256 | !strcasecmp(nval->value, host) ) { | ||
257 | ok = 1; | ||
258 | break; | ||
259 | } | ||
260 | } | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | |||
265 | if( !ok && (subj = X509_get_subject_name(cert)) && | ||
266 | X509_NAME_get_text_by_NID(subj, NID_commonName, | ||
267 | name, sizeof(name)) > 0 ) { | ||
268 | name[sizeof(name)-1] = '\0'; | ||
269 | if(!strcasecmp(name, host)) | ||
270 | ok = 1; | ||
271 | } | ||
272 | |||
273 | //printf("[*] vc_verify_cert_hostname() return: %d\n", ok); | ||
274 | return(ok); | ||
275 | } | ||
276 | |||
277 | int vc_verify_cert(X509 *cert, vc_x509store_t *vc_store) | ||
278 | { | ||
279 | int result = -1; | ||
280 | X509_STORE *store = NULL; | ||
281 | X509_STORE_CTX *ctx = NULL; | ||
282 | |||
283 | if( !(store = vc_x509store_create(vc_store)) ) | ||
284 | return(result); | ||
285 | |||
286 | if( (ctx = X509_STORE_CTX_new()) != 0 ) { | ||
287 | if(X509_STORE_CTX_init(ctx, store, cert, 0) == 1) | ||
288 | result = (X509_verify_cert(ctx) == 1); | ||
289 | X509_STORE_CTX_free(ctx); | ||
290 | } | ||
291 | |||
292 | X509_STORE_free(store); | ||
293 | return(result); | ||
294 | } | ||
295 | |||
296 | #define VC_STORE_ERR_EXIT(s) do { \ | ||
297 | fprintf(stderr, "[E] SSL_STORE: %s\n", ERR_error_string (ERR_get_error (), NULL)); \ | ||
298 | if(s) X509_STORE_free(s); \ | ||
299 | return(0); \ | ||
300 | } while(0) | ||
301 | |||
302 | X509_STORE *vc_x509store_create(vc_x509store_t *vc_store) | ||
303 | { | ||
304 | int i = 0; | ||
305 | int n = 0; | ||
306 | X509_STORE *store = NULL; | ||
307 | X509_LOOKUP *lookup = NULL; | ||
308 | |||
309 | store = X509_STORE_new(); | ||
310 | |||
311 | if(vc_store->callback) | ||
312 | X509_STORE_set_verify_cb_func(store, vc_store->callback); | ||
313 | else | ||
314 | X509_STORE_set_verify_cb_func(store, vc_verify_callback); | ||
315 | |||
316 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file())) ) | ||
317 | VC_STORE_ERR_EXIT(store); | ||
318 | |||
319 | if(!vc_store->cafile) { | ||
320 | if( !(vc_store->flags & VC_X509S_NODEF_CAFILE) ) | ||
321 | X509_LOOKUP_load_file(lookup, 0, X509_FILETYPE_DEFAULT); | ||
322 | } else if( !X509_LOOKUP_load_file(lookup, vc_store->cafile, | ||
323 | X509_FILETYPE_PEM) ) | ||
324 | VC_STORE_ERR_EXIT(store); | ||
325 | |||
326 | if(vc_store->crlfile) { | ||
327 | if( !X509_load_crl_file(lookup, vc_store->crlfile, | ||
328 | X509_FILETYPE_PEM) ) | ||
329 | VC_STORE_ERR_EXIT(store); | ||
330 | |||
331 | X509_STORE_set_flags( store, | ||
332 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL ); | ||
333 | } | ||
334 | |||
335 | if( !(lookup = X509_STORE_add_lookup(store, X509_LOOKUP_hash_dir())) ) | ||
336 | VC_STORE_ERR_EXIT(store); | ||
337 | |||
338 | if( !vc_store->capath ) { | ||
339 | if( !(vc_store->flags & VC_X509S_NODEF_CAPATH) ) | ||
340 | X509_LOOKUP_add_dir(lookup, 0, X509_FILETYPE_DEFAULT); | ||
341 | } else if( !X509_LOOKUP_add_dir(lookup, vc_store->capath, | ||
342 | X509_FILETYPE_PEM) ) | ||
343 | VC_STORE_ERR_EXIT(store); | ||
344 | |||
345 | for( i=0, n=sk_X509_num(vc_store->certs); i<n; i++) | ||
346 | if( !X509_STORE_add_cert(store, sk_X509_value(vc_store->certs, i)) ) | ||
347 | VC_STORE_ERR_EXIT(store); | ||
348 | |||
349 | for( i=0, n=sk_X509_CRL_num(vc_store->crls); i<n; i++) | ||
350 | if( !X509_STORE_add_crl(store, | ||
351 | sk_X509_CRL_value(vc_store->crls, i)) ) | ||
352 | VC_STORE_ERR_EXIT(store); | ||
353 | |||
354 | return(store); | ||
355 | } | ||
356 | |||
357 | int vc_verify_callback(int ok, X509_STORE_CTX *store) | ||
358 | { | ||
359 | if(!ok) { | ||
360 | /* XXX handle action/abort */ | ||
361 | if(!ignore_ssl) | ||
362 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s", | ||
363 | X509_verify_cert_error_string(store->error)); | ||
364 | else | ||
365 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR] %s (ignored)", | ||
366 | X509_verify_cert_error_string(store->error)); | ||
367 | |||
368 | writecf(FS_ERR, tmpstr); | ||
369 | ok = ignore_ssl; | ||
370 | } | ||
371 | return(ok); | ||
372 | } | ||
373 | |||
374 | void vc_x509store_setflags(vc_x509store_t *store, int flags) | ||
375 | { | ||
376 | store->flags |= flags; | ||
377 | } | ||
378 | |||
379 | void vc_x509store_setignssl(vc_x509store_t *store, int ignore) | ||
380 | { | ||
381 | store->ignore_ssl |= ignore; | ||
382 | ignore_ssl = ignore; | ||
383 | } | ||
384 | |||
385 | void vc_x509store_clearflags(vc_x509store_t *store, int flags) | ||
386 | { | ||
387 | store->flags &= ~flags; | ||
388 | } | ||
389 | |||
390 | void vc_x509store_setcb(vc_x509store_t *store, | ||
391 | vc_x509verify_cb_t callback) | ||
392 | { | ||
393 | store->callback = callback; | ||
394 | } | ||
395 | |||
396 | void vc_x509store_set_pkeycb(vc_x509store_t *store, | ||
397 | vc_askpass_cb_t callback) | ||
398 | { | ||
399 | store->askpass_callback = callback; | ||
400 | } | ||
401 | |||
402 | void vc_x509store_addcert(vc_x509store_t *store, X509 *cert) | ||
403 | { | ||
404 | sk_X509_push(store->certs, cert); | ||
405 | } | ||
406 | |||
407 | void vc_x509store_setcafile(vc_x509store_t *store, char *file) | ||
408 | { | ||
409 | if( store->cafile) free(store->cafile); | ||
410 | store->cafile = ( file ? strdup(file) : 0 ); | ||
411 | } | ||
412 | |||
413 | void vc_x509store_setcapath(vc_x509store_t *store, char *path) | ||
414 | { | ||
415 | if( store->capath) free(store->capath); | ||
416 | store->capath = ( path ? strdup(path) : 0 ); | ||
417 | } | ||
418 | |||
419 | void vc_x509store_setcrlfile(vc_x509store_t *store, char *file) | ||
420 | { | ||
421 | if( store->crlfile) free(store->crlfile); | ||
422 | store->crlfile = ( file ? strdup(file) : 0 ); | ||
423 | } | ||
424 | |||
425 | void vc_x509store_setkeyfile(vc_x509store_t *store, char *file) | ||
426 | { | ||
427 | if( store->use_keyfile) free(store->use_keyfile); | ||
428 | store->use_keyfile = ( file ? strdup(file) : 0 ); | ||
429 | } | ||
430 | |||
431 | void vc_x509store_setcertfile(vc_x509store_t *store, char *file) | ||
432 | { | ||
433 | if( store->use_certfile) free(store->use_certfile); | ||
434 | store->use_certfile = ( file ? strdup(file) : 0 ); | ||
435 | } | ||
436 | |||
437 | |||
438 | void vc_init_x509store(vc_x509store_t *s) | ||
439 | { | ||
440 | s->cafile = NULL; | ||
441 | s->capath = NULL; | ||
442 | s->crlfile = NULL; | ||
443 | s->callback = NULL; | ||
444 | s->askpass_callback = NULL; | ||
445 | s->certs = sk_X509_new_null(); | ||
446 | s->crls = sk_X509_CRL_new_null(); | ||
447 | s->use_certfile = NULL; | ||
448 | s->use_certs = sk_X509_new_null(); | ||
449 | s->use_keyfile = NULL; | ||
450 | s->use_key = NULL; | ||
451 | s->flags = 0; | ||
452 | s->ignore_ssl = 0; | ||
453 | } | ||
454 | |||
455 | void vc_cleanup_x509store(vc_x509store_t *s) | ||
456 | { | ||
457 | if(s->cafile) free(s->cafile); | ||
458 | if(s->capath) free(s->capath); | ||
459 | if(s->crlfile) free(s->crlfile); | ||
460 | if(s->use_certfile) free(s->use_certfile); | ||
461 | if(s->use_keyfile) free(s->use_keyfile); | ||
462 | if(s->use_key) free(s->use_key); | ||
463 | sk_X509_free(s->certs); | ||
464 | sk_X509_free(s->crls); | ||
465 | sk_X509_free(s->use_certs); | ||
466 | s->ignore_ssl = 0; | ||
467 | } | ||
diff --git a/vchat-ssl.h b/vchat-ssl.h new file mode 100755 index 0000000..58e9dec --- /dev/null +++ b/vchat-ssl.h | |||
@@ -0,0 +1,53 @@ | |||
1 | |||
2 | /* types */ | ||
3 | |||
4 | typedef int (*vc_x509verify_cb_t)(int, X509_STORE_CTX *); | ||
5 | typedef int (*vc_askpass_cb_t)(char *, int, int, void *); | ||
6 | typedef struct { | ||
7 | char *cafile; | ||
8 | char *capath; | ||
9 | char *crlfile; | ||
10 | vc_x509verify_cb_t callback; | ||
11 | vc_askpass_cb_t askpass_callback; | ||
12 | STACK_OF(X509) *certs; | ||
13 | STACK_OF(X509_CRL) *crls; | ||
14 | char *use_certfile; | ||
15 | STACK_OF(X509) *use_certs; | ||
16 | char *use_keyfile; | ||
17 | EVP_PKEY *use_key; | ||
18 | int flags; | ||
19 | int ignore_ssl; | ||
20 | } vc_x509store_t; | ||
21 | |||
22 | /* prototypes */ | ||
23 | |||
24 | BIO * vc_connect(char *, int , int, vc_x509store_t *, SSL_CTX **); | ||
25 | BIO * vc_connect_ssl(char *, int, vc_x509store_t *, SSL_CTX **); | ||
26 | SSL_CTX * vc_create_sslctx( vc_x509store_t *); | ||
27 | void vc_init_x509store(vc_x509store_t *); | ||
28 | void vc_cleanup_x509store(vc_x509store_t *); | ||
29 | void vc_x509store_setcafile(vc_x509store_t *, char *); | ||
30 | void vc_x509store_setcapath(vc_x509store_t *, char *); | ||
31 | void vc_x509store_setcrlfile(vc_x509store_t *, char *); | ||
32 | void vc_x509store_setkeyfile(vc_x509store_t *, char *); | ||
33 | void vc_x509store_setcertfile(vc_x509store_t *, char *); | ||
34 | void vc_x509store_addcert(vc_x509store_t *, X509 *); | ||
35 | void vc_x509store_setcb(vc_x509store_t *, vc_x509verify_cb_t); | ||
36 | void vc_x509store_set_pkeycb(vc_x509store_t *, vc_askpass_cb_t); | ||
37 | void vc_x509store_setflags(vc_x509store_t *, int); | ||
38 | void vc_x509store_setignssl(vc_x509store_t *, int); | ||
39 | void vc_x509store_clearflags(vc_x509store_t *, int); | ||
40 | int vc_verify_cert(X509 *, vc_x509store_t *); | ||
41 | int vc_verify_cert_hostname(X509 *, char *); | ||
42 | int vc_verify_callback(int, X509_STORE_CTX *); | ||
43 | X509_STORE * vc_x509store_create(vc_x509store_t *); | ||
44 | |||
45 | #define VC_X509S_NODEF_CAFILE 0x01 | ||
46 | #define VC_X509S_NODEF_CAPATH 0x02 | ||
47 | #define VC_X509S_USE_CERTIFICATE 0x04 | ||
48 | #define VC_X509S_SSL_VERIFY_NONE 0x10 | ||
49 | #define VC_X509S_SSL_VERIFY_PEER 0x20 | ||
50 | #define VC_X509S_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x40 | ||
51 | #define VC_X509S_SSL_VERIFY_CLIENT_ONCE 0x80 | ||
52 | #define VC_X509S_SSL_VERIFY_MASK 0xF0 | ||
53 | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <termios.h> | 24 | #include <termios.h> |
25 | #include <sys/ioctl.h> | 25 | #include <sys/ioctl.h> |
26 | #include <time.h> | 26 | #include <time.h> |
27 | #include <string.h> | ||
27 | #include <readline/readline.h> | 28 | #include <readline/readline.h> |
28 | #include <readline/history.h> | 29 | #include <readline/history.h> |
29 | #include <openssl/pem.h> | 30 | #include <openssl/pem.h> |
@@ -35,7 +36,7 @@ char *vchat_ui_version = "$Id$"; | |||
35 | 36 | ||
36 | /* externally used variables */ | 37 | /* externally used variables */ |
37 | /* current string in topic window */ | 38 | /* current string in topic window */ |
38 | char topicstr[TOPICSTRSIZE] = "[] VChat 0.16"; | 39 | char topicstr[TOPICSTRSIZE] = "[] VChat 0.17"; |
39 | /* current string in console window */ | 40 | /* current string in console window */ |
40 | char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands"; | 41 | char consolestr[CONSOLESTRSIZE] = "[ Get help: .h for server /h for client commands"; |
41 | 42 | ||
@@ -37,7 +37,7 @@ typedef struct servermessage servermessage; | |||
37 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; | 37 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; |
38 | typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, | 38 | typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, |
39 | CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, CF_LOGINSCRIPT, | 39 | CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, CF_LOGINSCRIPT, |
40 | CF_USESSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, | 40 | CF_USESSL, CF_IGNSSL, CF_USECERT, CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, |
41 | CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, | 41 | CF_USETOPIC, CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, |
42 | CF_ENCODING, CF_BELLPRIV } confopt; | 42 | CF_ENCODING, CF_BELLPRIV } confopt; |
43 | 43 | ||