diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2016-04-15 16:27:42 +0200 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2016-04-15 16:27:42 +0200 |
| commit | c5c4ee4d6a9aa5554ad29be79c4ee3e6bd79c70f (patch) | |
| tree | 3adc212952389dec26c03f2579317eade7875155 | |
| parent | 3ff11bc9ec47ea24e578444ffc9985884f32038b (diff) | |
Make fingerprint pinning an option
| -rwxr-xr-x | vchat-config.h | 1 | ||||
| -rwxr-xr-x | vchat-ssl.c | 190 | ||||
| -rwxr-xr-x | vchat.h | 2 |
3 files changed, 101 insertions, 92 deletions
diff --git a/vchat-config.h b/vchat-config.h index 9e10999..f7123d7 100755 --- a/vchat-config.h +++ b/vchat-config.h | |||
| @@ -39,6 +39,7 @@ static volatile configoption configoptions[] = { | |||
| 39 | {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, | 39 | {CF_FORMFILE, CO_STR, "formatfile", "~/.vchat/formats", NULL, { NULL } }, |
| 40 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, | 40 | {CF_LOGINSCRIPT, CO_STR, "loginscript","~/.vchat/loginscript", NULL, { NULL } }, |
| 41 | {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, | 41 | {CF_FINGERPRINT, CO_STR, "fingerprint","~/.vchat/fingerprint", NULL, { NULL } }, |
| 42 | {CF_PINFINGER, CO_INT, "pinfinger", (char *) 0, (char *)-1, { 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, { NULL } }, | 44 | {CF_USESSL, CO_INT, "usessl", (char *) 1, (char *)-1, { NULL } }, |
| 44 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { NULL } }, | 45 | {CF_IGNSSL, CO_INT, "ignssl", (char *) 0, (char *)-1, { NULL } }, |
diff --git a/vchat-ssl.c b/vchat-ssl.c index ef5b96e..b344d10 100755 --- a/vchat-ssl.c +++ b/vchat-ssl.c | |||
| @@ -154,8 +154,21 @@ static SSL_CTX * vc_create_sslctx( vc_x509store_t *vc_store ) | |||
| 154 | 154 | ||
| 155 | int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) | 155 | int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) |
| 156 | { | 156 | { |
| 157 | BIO *ssl_conn = NULL; | ||
| 158 | SSL_CTX * ctx = vc_create_sslctx(vc_store); | 157 | SSL_CTX * ctx = vc_create_sslctx(vc_store); |
| 158 | X509 *peercert = NULL; | ||
| 159 | BIO *ssl_conn = NULL; | ||
| 160 | const SSL *sslp = NULL; | ||
| 161 | const SSL_CIPHER * cipher = NULL; | ||
| 162 | |||
| 163 | /* To display and check server fingerprint */ | ||
| 164 | char fingerprint[EVP_MAX_MD_SIZE*4]; | ||
| 165 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | ||
| 166 | unsigned int fingerprint_len; | ||
| 167 | |||
| 168 | FILE *fingerprint_file = NULL; | ||
| 169 | char * fp = fingerprint; | ||
| 170 | |||
| 171 | long result; | ||
| 159 | 172 | ||
| 160 | if( !ctx ) | 173 | if( !ctx ) |
| 161 | return 1; | 174 | return 1; |
| @@ -163,112 +176,107 @@ int vc_connect_ssl( BIO **conn, vc_x509store_t *vc_store ) | |||
| 163 | ssl_conn = BIO_new_ssl(ctx, 1); | 176 | ssl_conn = BIO_new_ssl(ctx, 1); |
| 164 | SSL_CTX_free(ctx); | 177 | SSL_CTX_free(ctx); |
| 165 | 178 | ||
| 166 | if( ssl_conn ) { | 179 | if( !ssl_conn ) |
| 167 | BIO_push( ssl_conn, *conn ); | 180 | goto ssl_error; |
| 168 | *conn = ssl_conn; | ||
| 169 | fflush(stdout); | ||
| 170 | 181 | ||
| 171 | if( BIO_do_handshake( *conn ) > 0 ) { | 182 | BIO_push( ssl_conn, *conn ); |
| 172 | /* Show information about cipher used */ | 183 | *conn = ssl_conn; |
| 173 | const SSL *sslp = NULL; | 184 | fflush(stdout); |
| 174 | const SSL_CIPHER * cipher = NULL; | ||
| 175 | 185 | ||
| 176 | /* Get cipher object */ | 186 | if( BIO_do_handshake( *conn ) <= 0 ) |
| 177 | BIO_get_ssl(ssl_conn, &sslp); | 187 | goto ssl_error; |
| 178 | if (sslp) | ||
| 179 | cipher = SSL_get_current_cipher(sslp); | ||
| 180 | if (cipher) { | ||
| 181 | char cipher_desc[TMPSTRSIZE]; | ||
| 182 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); | ||
| 183 | writecf(FS_SERV, tmpstr); | ||
| 184 | } else { | ||
| 185 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); | ||
| 186 | writecf(FS_ERR, tmpstr); | ||
| 187 | } | ||
| 188 | 188 | ||
| 189 | /* Accept being connected, _if_ verification passed */ | 189 | /* Show information about cipher used */ |
| 190 | if (sslp) { | 190 | /* Get cipher object */ |
| 191 | long result = SSL_get_verify_result(sslp); | 191 | BIO_get_ssl(ssl_conn, &sslp); |
| 192 | #if 1 == 1 | 192 | if (sslp) |
| 193 | if (result == X509_V_OK) { | 193 | cipher = SSL_get_current_cipher(sslp); |
| 194 | return 0; | 194 | if (cipher) { |
| 195 | } else if (getintoption(CF_IGNSSL)) { | 195 | char cipher_desc[TMPSTRSIZE]; |
| 196 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); | 196 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CIPHER ] %s", SSL_CIPHER_description(cipher, cipher_desc, TMPSTRSIZE)); |
| 197 | return 0; | 197 | writecf(FS_SERV, tmpstr); |
| 198 | } | 198 | } else { |
| 199 | #else | 199 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ERROR ] Cipher not known / SSL object can't be queried!"); |
| 200 | /* show & verify fingerprint */ | 200 | writecf(FS_ERR, tmpstr); |
| 201 | if ((result == X509_V_OK) || getintoption(CF_IGNSSL)) { | 201 | } |
| 202 | X509 *peercert = SSL_get_peer_certificate(sslp); | 202 | |
| 203 | /* Accept being connected, _if_ verification passed */ | ||
| 204 | if (!sslp) | ||
| 205 | goto ssl_error; | ||
| 203 | 206 | ||
| 204 | /* FIXME: this IS bad code */ | 207 | peercert = SSL_get_peer_certificate(sslp); |
| 205 | char new_fingerprint[TMPSTRSIZE]; | 208 | if (!peercert) |
| 206 | char old_fingerprint[TMPSTRSIZE]; | 209 | goto ssl_error; |
| 207 | FILE *fingerprint_file = NULL; | ||
| 208 | 210 | ||
| 209 | unsigned int fingerprint_len; | 211 | /* show basic information about peer cert */ |
| 210 | unsigned char fingerprint_bin[EVP_MAX_MD_SIZE]; | 212 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); |
| 213 | writecf(FS_SERV, tmpstr); | ||
| 214 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
| 215 | writecf(FS_SERV, tmpstr); | ||
| 211 | 216 | ||
| 212 | /* show basic information about peer cert */ | 217 | /* calculate fingerprint */ |
| 213 | snprintf(tmpstr, TMPSTRSIZE, "[SSL SUBJECT ] %s", X509_NAME_oneline(X509_get_subject_name(peercert),0,0)); | 218 | if (!X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) |
| 214 | writecf(FS_SERV, tmpstr); | 219 | goto ssl_error; |
| 215 | snprintf(tmpstr, TMPSTRSIZE, "[SSL ISSUER ] %s", X509_NAME_oneline(X509_get_issuer_name(peercert),0,0)); | ||
| 216 | writecf(FS_SERV, tmpstr); | ||
| 217 | 220 | ||
| 218 | /* calculate fingerprint */ | 221 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len <= EVP_MAX_MD_SIZE )); |
| 219 | if (X509_digest(peercert,EVP_sha1(),fingerprint_bin,&fingerprint_len)) { | 222 | for (j=0; j<(int)fingerprint_len; j++) |
| 220 | int j; | 223 | fp += sprintf(nf, "%02X:", fingerprint_bin[j]); |
| 221 | assert ( ( fingerprint_len > 1 ) && (fingerprint_len * 3 < TMPSTRSIZE )); | 224 | assert ( fp > fingerprint ); |
| 222 | char * nf = new_fingerprint; | 225 | fp[-1] = 0; |
| 223 | for (j=0; j<(int)fingerprint_len; j++) | 226 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] from server: %s", fingerprint); |
| 224 | nf += snprintf(nf, 4, "%02X:", fingerprint_bin[j]); | 227 | writecf(FS_SERV, tmpstr); |
| 225 | assert ( nf > new_fingerprint ); | ||
| 226 | nf[-1] = 0; | ||
| 227 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] from server: %s", new_fingerprint); | ||
| 228 | writecf(FS_SERV, tmpstr); | ||
| 229 | } | ||
| 230 | 228 | ||
| 231 | // we don't need the peercert anymore | 229 | /* we don't need the peercert anymore */ |
| 232 | X509_free(peercert); | 230 | X509_free(peercert); |
| 233 | 231 | ||
| 234 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); | 232 | /* If verify of x509 chain was requested, do the check here */ |
| 235 | if (fingerprint_file) { | 233 | result = SSL_get_verify_result(sslp); |
| 236 | char * r = fgets(old_fingerprint, TMPSTRSIZE, fingerprint_file); | 234 | if (result != X509_V_OK && !getintoption(CF_IGNSSL) ) |
| 237 | fclose(fingerprint_file); | 235 | goto ssl_error; |
| 238 | 236 | ||
| 239 | if (r) { | 237 | if (result != X509_V_OK) |
| 240 | // chomp | 238 | writecf(FS_ERR, "[SSL VERIFY ERROR ] FAILURE IGNORED!!!"); |
| 241 | char *nl = strchr(r, '\n'); | ||
| 242 | if (nl) *nl = 0; | ||
| 243 | 239 | ||
| 244 | /* verify fingerprint matches stored version */ | 240 | /* verify fingerprint */ |
| 245 | if (!strcmp(new_fingerprint, old_fingerprint)) | 241 | if (getintoption(CF_PIN_FINGERPRINT)) { |
| 246 | return 0; | ||
| 247 | } | ||
| 248 | 242 | ||
| 249 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] from %s: %s", getstroption(CF_FINGERPRINT), r ? old_fingerprint : "<FILE READ ERROR>" ); | 243 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "r"); |
| 250 | writecf(FS_ERR, tmpstr); | 244 | if (fingerprint_file) { |
| 251 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | 245 | |
| 252 | return 1; | 246 | /* Read fingerprint from file */ |
| 253 | } else { | 247 | char old_fingerprint[EVP_MAX_MD_SIZE*4]; |
| 254 | /* FIXME: there might be other errors than missing file */ | 248 | char * r = fgets(old_fingerprint, sizeof(old_fingerprint), fingerprint_file); |
| 255 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | 249 | fclose(fingerprint_file); |
| 256 | if (!fingerprint_file) { | 250 | |
| 257 | snprintf (tmpstr, TMPSTRSIZE, "Can't write fingerprint file, %s.", strerror(errno)); | 251 | if (r) { |
| 258 | writecf(FS_ERR, tmpstr); | 252 | /* chomp */ |
| 259 | } else { | 253 | char *nl = strchr(r, '\n'); |
| 260 | fputs(new_fingerprint, fingerprint_file); | 254 | if (nl) *nl = 0; |
| 261 | fclose(fingerprint_file); | 255 | |
| 262 | writecf(FS_SERV, "Stored fingerprint."); | 256 | /* verify fingerprint matches stored version */ |
| 263 | return 0; | 257 | if (!strcmp(fingerprint, old_fingerprint)) |
| 264 | } | 258 | return 0; |
| 265 | } | ||
| 266 | } | ||
| 267 | #endif | ||
| 268 | } | 259 | } |
| 260 | |||
| 261 | snprintf(tmpstr, TMPSTRSIZE, "[SSL FINGERPRINT ] from %s: %s", getstroption(CF_FINGERPRINT), r ? old_fingerprint : "<FILE READ ERROR>" ); | ||
| 262 | writecf(FS_ERR, tmpstr); | ||
| 263 | writecf(FS_ERR, "[SSL CONNECT ERROR] Fingerprint mismatch! Server cert updated?"); | ||
| 264 | return 1; | ||
| 265 | } | ||
| 266 | |||
| 267 | fingerprint_file = fopen(tilde_expand(getstroption(CF_FINGERPRINT)), "w"); | ||
| 268 | if (!fingerprint_file) { | ||
| 269 | snprintf (tmpstr, TMPSTRSIZE, "[WARNING] Can't write fingerprint file, %s.", strerror(errno)); | ||
| 270 | writecf(FS_ERR, tmpstr); | ||
| 271 | } else { | ||
| 272 | fputs(fingerprint, fingerprint_file); | ||
| 273 | fclose(fingerprint_file); | ||
| 274 | writecf(FS_SERV, "Stored fingerprint."); | ||
| 269 | } | 275 | } |
| 276 | return 0; | ||
| 270 | } | 277 | } |
| 271 | 278 | ||
| 279 | ssl_error: | ||
| 272 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); | 280 | snprintf(tmpstr, TMPSTRSIZE, "[SSL CONNECT ERROR] %s", ERR_error_string (ERR_get_error (), NULL)); |
| 273 | writecf(FS_ERR, tmpstr); | 281 | writecf(FS_ERR, tmpstr); |
| 274 | 282 | ||
| @@ -31,7 +31,7 @@ typedef struct servermessage servermessage; | |||
| 31 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; | 31 | typedef enum { CO_NIL, CO_STR, CO_INT } conftype; |
| 32 | typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, | 32 | typedef enum { CF_NIL, CF_NICK, CF_FROM, CF_SERVERHOST, CF_SERVERPORT, |
| 33 | CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, | 33 | CF_CIPHERSUITE, CF_CONFIGFILE, CF_CERTFILE, CF_KEYFILE, CF_FORMFILE, |
| 34 | CF_LOGINSCRIPT, CF_FINGERPRINT, CF_USESSL, CF_IGNSSL, CF_VERIFYSSL, CF_USECERT, | 34 | CF_LOGINSCRIPT, CF_FINGERPRINT, CF_PINFINGER, CF_USESSL, CF_IGNSSL, CF_VERIFYSSL, CF_USECERT, |
| 35 | CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, | 35 | CF_PRIVHEIGHT, CF_PRIVCOLLAPS, CF_HSCROLL, CF_CHANNEL, CF_USETIME, CF_USETOPIC, |
| 36 | CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, CF_ENCODING, | 36 | CF_SCROLLBPRIV, CF_SCROLLBACK, CF_SCROLLBPRIVT, CF_SCROLLBACKT, CF_ENCODING, |
| 37 | CF_BELLPRIV, CF_CASEFIRST, CF_AUTORECONN, CF_KEEPALIVE } confopt; | 37 | CF_BELLPRIV, CF_CASEFIRST, CF_AUTORECONN, CF_KEEPALIVE } confopt; |
