Ticket #714: 0001-NSS-based-implementation-of-SSL-related-operations.patch
File 0001-NSS-based-implementation-of-SSL-related-operations.patch, 12.6 KB (added by , at 2012-07-24T15:12:22Z) |
---|
-
new file .gitignore
From 625d0e0cca7464eebccd55c6744dd5ce773c6dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= <mcepl@redhat.com> Date: Tue, 24 Jul 2012 17:08:51 +0200 Subject: [PATCH] NSS-based implementation of SSL-related operations. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * no verification support * ssl_des3_encrypt implemented and functional. Signed-off-by: Matěj Cepl <mcepl@redhat.com> --- .gitignore | 7 ++ configure | 6 +- lib/ssl_nss.c | 330 ++++++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 257 insertions(+), 86 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1899086
- + 1 *~ 2 *.o 3 .depend/ 4 Makefile.settings 5 bitlbee 6 bitlbee.pc 7 config.h -
configure
diff --git a/configure b/configure index 697a33b..c0d3735 100755
a b if [ "$ret" = "0" ]; then 453 453 exit 1 454 454 fi; 455 455 456 if [ "$msn" = "1" -a "$ssl" != "openssl" -a "$ssl" != "gnutls" ]; then457 # Needed for MSN only. OpenSSL exportsnice cipher functions already,456 if [ "$msn" = "1" -a "$ssl" != "openssl" -a "$ssl" != "gnutls" -a "$ssl" != "nss" ]; then 457 # Needed for MSN only. OpenSSL and NSS export nice cipher functions already, 458 458 # in case of GnuTLS we should be able to use gcrypt. Otherwise, use 459 # built-in stuff. (Since right now those are the only t wosupported459 # built-in stuff. (Since right now those are the only three supported 460 460 # SSL modules anyway, this is mostly unnecessary.) 461 461 echo 'DES=des.o' >> Makefile.settings 462 462 fi -
lib/ssl_nss.c
diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index d50620d..4e7a79d 100644
a b 39 39 #include <seccomon.h> 40 40 #include <secerr.h> 41 41 #include <sslerr.h> 42 #include <assert.h> 43 #include <unistd.h> 42 44 43 45 int ssl_errno = 0; 44 46 45 47 static gboolean initialized = FALSE; 46 48 47 struct scd 48 { 49 #define SSLDEBUG 0 50 51 struct scd { 49 52 ssl_input_function func; 50 53 gpointer data; 51 54 int fd; 55 char *hostname; 52 56 PRFileDesc *prfd; 53 57 gboolean established; 54 58 gboolean verify; 55 59 }; 56 60 57 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); 58 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); 61 static gboolean ssl_connected(gpointer data, gint source, 62 b_input_condition cond); 63 static gboolean ssl_starttls_real(gpointer data, gint source, 64 b_input_condition cond); 59 65 60 61 static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig,PRBool isserver)66 static SECStatus nss_auth_cert(void *arg, PRFileDesc * socket, PRBool checksig, 67 PRBool isserver) 62 68 { 63 69 return SECSuccess; 64 70 } 65 71 66 static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)72 static SECStatus nss_bad_cert(void *arg, PRFileDesc * socket) 67 73 { 68 74 PRErrorCode err; 69 75 70 if(!arg) return SECFailure; 76 if (!arg) 77 return SECFailure; 71 78 72 *(PRErrorCode *) arg = err = PORT_GetError();79 *(PRErrorCode *) arg = err = PORT_GetError(); 73 80 74 switch (err) {81 switch (err) { 75 82 case SEC_ERROR_INVALID_AVA: 76 83 case SEC_ERROR_INVALID_TIME: 77 84 case SEC_ERROR_BAD_SIGNATURE: … … static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) 93 100 } 94 101 } 95 102 96 97 void ssl_init( void ) 103 void ssl_init(void) 98 104 { 99 PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 105 PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); 106 // https://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224 107 // This NSS function is not intended for use with SSL, which 108 // requires that the certificate and key database files be 109 // opened. Relates to whole non-verification of servers for now. 100 110 NSS_NoDB_Init(NULL); 101 111 NSS_SetDomesticPolicy(); 102 112 initialized = TRUE; 103 113 } 104 114 105 void *ssl_connect( char *host, int port, gboolean verify, ssl_input_function func, gpointer data ) 115 void *ssl_connect(char *host, int port, gboolean verify, 116 ssl_input_function func, gpointer data) 106 117 { 107 struct scd *conn = g_new0( struct scd, 1);108 109 conn->fd = proxy_connect( host, port, ssl_connected, conn);118 struct scd *conn = g_new0(struct scd, 1); 119 120 conn->fd = proxy_connect(host, port, ssl_connected, conn); 110 121 conn->func = func; 111 122 conn->data = data; 112 113 if( conn->fd < 0 ) 114 { 115 g_free( conn ); 116 return( NULL ); 123 conn->hostname = g_strdup(host); 124 125 if (conn->fd < 0) { 126 g_free(conn->hostname); 127 g_free(conn); 128 return (NULL); 117 129 } 118 119 if( !initialized ) 120 { 130 131 if (!initialized) { 121 132 ssl_init(); 122 133 } 123 134 124 125 return( conn ); 135 return (conn); 126 136 } 127 137 128 static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) 138 static gboolean ssl_starttls_real(gpointer data, gint source, 139 b_input_condition cond) 129 140 { 130 141 struct scd *conn = data; 131 142 132 return ssl_connected( conn, conn->fd, B_EV_IO_WRITE);143 return ssl_connected(conn, conn->fd, B_EV_IO_WRITE); 133 144 } 134 145 135 void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data ) 146 void *ssl_starttls(int fd, char *hostname, gboolean verify, 147 ssl_input_function func, gpointer data) 136 148 { 137 struct scd *conn = g_new0( struct scd, 1);149 struct scd *conn = g_new0(struct scd, 1); 138 150 139 151 conn->fd = fd; 140 152 conn->func = func; 141 153 conn->data = data; 154 conn->hostname = hostname; 155 156 /* For now, SSL verification is globally enabled by setting the cafile 157 setting in bitlbee.conf. Commented out by default because probably 158 not everyone has this file in the same place and plenty of folks 159 may not have the cert of their private Jabber server in it. */ 142 160 conn->verify = verify && global.conf->cafile; 143 161 144 162 /* This function should be called via a (short) timeout instead of … … void *ssl_starttls( int fd, char *hostname, gboolean verify, ssl_input_function 150 168 In short, doing things like this makes the rest of the code a lot 151 169 simpler. */ 152 170 153 b_timeout_add( 1, ssl_starttls_real, conn);171 b_timeout_add(1, ssl_starttls_real, conn); 154 172 155 173 return conn; 156 174 } 157 175 158 static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) 176 static gboolean ssl_connected(gpointer data, gint source, 177 b_input_condition cond) 159 178 { 160 179 struct scd *conn = data; 161 180 162 181 /* Right now we don't have any verification functionality for NSS. */ 163 182 164 if( conn->verify ) 165 { 166 conn->func( conn->data, 1, NULL, cond ); 167 if( source >= 0 ) closesocket( source ); 168 g_free( conn ); 183 if (conn->verify) { 184 conn->func(conn->data, 1, NULL, cond); 185 if (source >= 0) 186 closesocket(source); 187 g_free(conn->hostname); 188 g_free(conn); 169 189 170 190 return FALSE; 171 191 } 172 173 if ( source == -1)192 193 if (source == -1) 174 194 goto ssl_connected_failure; 175 195 176 196 /* Until we find out how to handle non-blocking I/O with NSS... */ 177 sock_make_blocking( conn->fd);178 197 sock_make_blocking(conn->fd); 198 179 199 conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); 200 if (!conn->prfd) 201 goto ssl_connected_failure; 180 202 SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); 181 203 SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); 182 SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL); 183 SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB()); 204 SSL_BadCertHook(conn->prfd, (SSLBadCertHandler) nss_bad_cert, NULL); 205 SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate) nss_auth_cert, 206 (void *)CERT_GetDefaultCertDB()); 207 SSL_SetURL(conn->prfd, conn->hostname); 184 208 SSL_ResetHandshake(conn->prfd, PR_FALSE); 185 209 186 210 if (SSL_ForceHandshake(conn->prfd)) { 187 211 goto ssl_connected_failure; 188 212 } 189 190 213 191 214 conn->established = TRUE; 192 conn->func( conn->data, 0, conn, cond);215 conn->func(conn->data, 0, conn, cond); 193 216 return FALSE; 194 195 ssl_connected_failure: 196 197 conn->func( conn->data, 0, NULL, cond ); 198 199 PR_Close( conn -> prfd ); 200 if( source >= 0 ) closesocket( source ); 201 g_free( conn ); 202 217 218 ssl_connected_failure: 219 220 conn->func(conn->data, 0, NULL, cond); 221 222 if (conn->prfd) 223 PR_Close(conn->prfd); 224 if (source >= 0) 225 closesocket(source); 226 g_free(conn->hostname); 227 g_free(conn); 228 203 229 return FALSE; 204 230 } 205 231 206 int ssl_read( void *conn, char *buf, int len)232 int ssl_read(void *conn, char *buf, int len) 207 233 { 208 if( !((struct scd*)conn)->established ) 209 return( 0 ); 210 211 return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) ); 234 int st; 235 PRErrorCode PR_err; 236 237 if (!((struct scd *)conn)->established) { 238 ssl_errno = SSL_NOHANDSHAKE; 239 return -1; 240 } 241 242 st = PR_Read(((struct scd *)conn)->prfd, buf, len); 243 PR_err = PR_GetError(); 244 245 ssl_errno = SSL_OK; 246 if (PR_err == PR_WOULD_BLOCK_ERROR) 247 ssl_errno = SSL_AGAIN; 248 249 if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) 250 len = write(STDERR_FILENO, buf, st); 251 252 return st; 212 253 } 213 254 214 int ssl_write( void *conn, const char *buf, int len)255 int ssl_write(void *conn, const char *buf, int len) 215 256 { 216 if( !((struct scd*)conn)->established ) 217 return( 0 ); 218 219 return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) ); 257 int st; 258 PRErrorCode PR_err; 259 260 if (!((struct scd *)conn)->established) { 261 ssl_errno = SSL_NOHANDSHAKE; 262 return -1; 263 } 264 st = PR_Write(((struct scd *)conn)->prfd, buf, len); 265 266 ssl_errno = SSL_OK; 267 if (PR_err == PR_WOULD_BLOCK_ERROR) 268 ssl_errno = SSL_AGAIN; 269 270 if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) 271 len = write(2, buf, st); 272 273 return st; 220 274 } 221 275 222 int ssl_pending( void *conn)276 int ssl_pending(void *conn) 223 277 { 224 struct scd *c = (struct scd *) 278 struct scd *c = (struct scd *)conn; 225 279 226 if ( c == NULL) {280 if (c == NULL) { 227 281 return 0; 228 282 } 229 283 230 return ( c->established && SSL_DataPending( c->prfd ) > 0);284 return (c->established && SSL_DataPending(c->prfd) > 0); 231 285 } 232 286 233 void ssl_disconnect( void *conn_)287 void ssl_disconnect(void *conn_) 234 288 { 235 289 struct scd *conn = conn_; 236 237 PR_Close( conn->prfd ); 238 closesocket( conn->fd ); 239 240 g_free( conn ); 290 291 // When we swich to NSS_Init, we should have here 292 // NSS_Shutdown(); 293 294 if (conn->prfd) 295 PR_Close(conn->prfd); 296 297 g_free(conn->hostname); 298 g_free(conn); 241 299 } 242 300 243 int ssl_getfd( void *conn)301 int ssl_getfd(void *conn) 244 302 { 245 return ( ((struct scd*)conn)->fd);303 return (((struct scd *)conn)->fd); 246 304 } 247 305 248 b_input_condition ssl_getdirection( void *conn)306 b_input_condition ssl_getdirection(void *conn) 249 307 { 250 308 /* Just in case someone calls us, let's return the most likely case: */ 251 309 return B_EV_IO_READ; 252 310 } 253 311 254 char *ssl_verify_strerror( int code ) 312 char *ssl_verify_strerror(int code) 313 { 314 return 315 g_strdup 316 ("SSL certificate verification not supported by BitlBee NSS code."); 317 } 318 319 size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, 320 const unsigned char *input, size_t input_len, 321 const unsigned char *iv, unsigned char **res) 255 322 { 256 return g_strdup( "SSL certificate verification not supported by BitlBee NSS code." ); 323 #define CIPHER_MECH CKM_DES3_CBC 324 #define MAX_OUTPUT_LEN 72 325 326 int len1; 327 unsigned int len2; 328 329 PK11Context *ctx = NULL; 330 PK11SlotInfo *slot = NULL; 331 SECItem keyItem; 332 SECItem ivItem; 333 SECItem *secParam = NULL; 334 PK11SymKey *symKey = NULL; 335 336 size_t rc; 337 SECStatus rv; 338 339 if (!initialized) { 340 ssl_init(); 341 } 342 343 keyItem.data = (unsigned char *)key; 344 keyItem.len = key_len; 345 346 slot = PK11_GetBestSlot(CIPHER_MECH, NULL); 347 if (slot == NULL) { 348 fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n", 349 PR_GetError()); 350 rc = 0; 351 goto out; 352 } 353 354 symKey = 355 PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT, 356 &keyItem, NULL); 357 if (symKey == NULL) { 358 fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n", 359 PR_GetError()); 360 rc = 0; 361 goto out; 362 } 363 364 ivItem.data = (unsigned char *)iv; 365 /* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */ 366 ivItem.len = 8; 367 368 secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem); 369 if (secParam == NULL) { 370 fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n", 371 PR_GetError()); 372 rc = 0; 373 goto out; 374 } 375 376 ctx = 377 PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey, 378 secParam); 379 if (ctx == NULL) { 380 fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n", 381 PR_GetError()); 382 rc = 0; 383 goto out; 384 } 385 386 *res = g_new0(unsigned char, MAX_OUTPUT_LEN); 387 388 rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN, 389 (unsigned char *)input, input_len); 390 if (rv != SECSuccess) { 391 fprintf(stderr, "PK11_CipherOp failed (err %d)\n", 392 PR_GetError()); 393 rc = 0; 394 goto out; 395 } 396 397 assert(len1 <= MAX_OUTPUT_LEN); 398 399 rv = PK11_DigestFinal(ctx, *res + len1, &len2, 400 (unsigned int)MAX_OUTPUT_LEN - len1); 401 if (rv != SECSuccess) { 402 fprintf(stderr, "PK11_DigestFinal failed (err %d)\n", 403 PR_GetError()); 404 rc = 0; 405 goto out; 406 } 407 408 rc = len1 + len2; 409 410 out: 411 if (ctx) 412 PK11_DestroyContext(ctx, PR_TRUE); 413 if (symKey) 414 PK11_FreeSymKey(symKey); 415 if (secParam) 416 SECITEM_FreeItem(secParam, PR_TRUE); 417 if (slot) 418 PK11_FreeSlot(slot); 419 420 return rc; 257 421 }