diff options
| author | erdgeist <> | 2007-06-27 21:59:32 +0000 |
|---|---|---|
| committer | erdgeist <> | 2007-06-27 21:59:32 +0000 |
| commit | 3c11bea99886b006ca499e1be6a3a17d225cedc7 (patch) | |
| tree | 687a3e0e762669af85c5858420856b49686795cd /vchat-protocol.c | |
| parent | d4861ca31f0406f5c49023bc2c3bc4cfa54e3693 (diff) | |
Introducing new ssl code
Diffstat (limited to 'vchat-protocol.c')
| -rwxr-xr-x | vchat-protocol.c | 372 |
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 */ |
| 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 | } |
