summaryrefslogtreecommitdiff
path: root/vchat-protocol.c
diff options
context:
space:
mode:
authorerdgeist <>2007-06-27 21:59:32 +0000
committererdgeist <>2007-06-27 21:59:32 +0000
commit3c11bea99886b006ca499e1be6a3a17d225cedc7 (patch)
tree687a3e0e762669af85c5858420856b49686795cd /vchat-protocol.c
parentd4861ca31f0406f5c49023bc2c3bc4cfa54e3693 (diff)
Introducing new ssl code
Diffstat (limited to 'vchat-protocol.c')
-rwxr-xr-xvchat-protocol.c372
1 files changed, 80 insertions, 292 deletions
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 */
38char *vchat_io_version = "$Id$"; 39char *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 */
46static SSL *sslconn = NULL; 47static 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 */
50STACK_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
52static char *sslpubkey = NULL; /* servers public key extracted from X.509 certificate */
53static int sslpubkeybits = 0; /* length of server public key */
54 48
55/* declaration of local helper functions */ 49/* declaration of local helper functions */
56static void usersignon (char *); 50static void usersignon (char *);
@@ -80,281 +74,90 @@ static int getportnum (char *port);
80extern int status; 74extern int status;
81 75
82int usessl = 1; 76int usessl = 1;
77int ignssl = 0;
83char *encoding; 78char *encoding;
84 79
85/* connects to server */ 80/* connects to server */
86int 81int
87vcconnect (char *server, char *port) 82vcconnect (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}