Annotation of embedaddon/nginx/src/event/ngx_event_openssl.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (C) Igor Sysoev
! 4: * Copyright (C) Nginx, Inc.
! 5: */
! 6:
! 7:
! 8: #include <ngx_config.h>
! 9: #include <ngx_core.h>
! 10: #include <ngx_event.h>
! 11:
! 12:
! 13: typedef struct {
! 14: ngx_uint_t engine; /* unsigned engine:1; */
! 15: } ngx_openssl_conf_t;
! 16:
! 17:
! 18: static int ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);
! 19: static void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,
! 20: int ret);
! 21: static void ngx_ssl_handshake_handler(ngx_event_t *ev);
! 22: static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
! 23: static void ngx_ssl_write_handler(ngx_event_t *wev);
! 24: static void ngx_ssl_read_handler(ngx_event_t *rev);
! 25: static void ngx_ssl_shutdown_handler(ngx_event_t *ev);
! 26: static void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,
! 27: ngx_err_t err, char *text);
! 28: static void ngx_ssl_clear_error(ngx_log_t *log);
! 29:
! 30: ngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);
! 31: static int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,
! 32: ngx_ssl_session_t *sess);
! 33: static ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,
! 34: u_char *id, int len, int *copy);
! 35: static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);
! 36: static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
! 37: ngx_slab_pool_t *shpool, ngx_uint_t n);
! 38: static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
! 39: ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
! 40:
! 41: static void *ngx_openssl_create_conf(ngx_cycle_t *cycle);
! 42: static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
! 43: static void ngx_openssl_exit(ngx_cycle_t *cycle);
! 44:
! 45:
! 46: static ngx_command_t ngx_openssl_commands[] = {
! 47:
! 48: { ngx_string("ssl_engine"),
! 49: NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
! 50: ngx_openssl_engine,
! 51: 0,
! 52: 0,
! 53: NULL },
! 54:
! 55: ngx_null_command
! 56: };
! 57:
! 58:
! 59: static ngx_core_module_t ngx_openssl_module_ctx = {
! 60: ngx_string("openssl"),
! 61: ngx_openssl_create_conf,
! 62: NULL
! 63: };
! 64:
! 65:
! 66: ngx_module_t ngx_openssl_module = {
! 67: NGX_MODULE_V1,
! 68: &ngx_openssl_module_ctx, /* module context */
! 69: ngx_openssl_commands, /* module directives */
! 70: NGX_CORE_MODULE, /* module type */
! 71: NULL, /* init master */
! 72: NULL, /* init module */
! 73: NULL, /* init process */
! 74: NULL, /* init thread */
! 75: NULL, /* exit thread */
! 76: NULL, /* exit process */
! 77: ngx_openssl_exit, /* exit master */
! 78: NGX_MODULE_V1_PADDING
! 79: };
! 80:
! 81:
! 82: int ngx_ssl_connection_index;
! 83: int ngx_ssl_server_conf_index;
! 84: int ngx_ssl_session_cache_index;
! 85: int ngx_ssl_certificate_index;
! 86: int ngx_ssl_stapling_index;
! 87:
! 88:
! 89: ngx_int_t
! 90: ngx_ssl_init(ngx_log_t *log)
! 91: {
! 92: OPENSSL_config(NULL);
! 93:
! 94: SSL_library_init();
! 95: SSL_load_error_strings();
! 96:
! 97: OpenSSL_add_all_algorithms();
! 98:
! 99: #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
! 100: #ifndef SSL_OP_NO_COMPRESSION
! 101: {
! 102: /*
! 103: * Disable gzip compression in OpenSSL prior to 1.0.0 version,
! 104: * this saves about 522K per connection.
! 105: */
! 106: int n;
! 107: STACK_OF(SSL_COMP) *ssl_comp_methods;
! 108:
! 109: ssl_comp_methods = SSL_COMP_get_compression_methods();
! 110: n = sk_SSL_COMP_num(ssl_comp_methods);
! 111:
! 112: while (n--) {
! 113: (void) sk_SSL_COMP_pop(ssl_comp_methods);
! 114: }
! 115: }
! 116: #endif
! 117: #endif
! 118:
! 119: ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
! 120:
! 121: if (ngx_ssl_connection_index == -1) {
! 122: ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed");
! 123: return NGX_ERROR;
! 124: }
! 125:
! 126: ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
! 127: NULL);
! 128: if (ngx_ssl_server_conf_index == -1) {
! 129: ngx_ssl_error(NGX_LOG_ALERT, log, 0,
! 130: "SSL_CTX_get_ex_new_index() failed");
! 131: return NGX_ERROR;
! 132: }
! 133:
! 134: ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
! 135: NULL);
! 136: if (ngx_ssl_session_cache_index == -1) {
! 137: ngx_ssl_error(NGX_LOG_ALERT, log, 0,
! 138: "SSL_CTX_get_ex_new_index() failed");
! 139: return NGX_ERROR;
! 140: }
! 141:
! 142: ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
! 143: NULL);
! 144: if (ngx_ssl_certificate_index == -1) {
! 145: ngx_ssl_error(NGX_LOG_ALERT, log, 0,
! 146: "SSL_CTX_get_ex_new_index() failed");
! 147: return NGX_ERROR;
! 148: }
! 149:
! 150: ngx_ssl_stapling_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
! 151: NULL);
! 152: if (ngx_ssl_stapling_index == -1) {
! 153: ngx_ssl_error(NGX_LOG_ALERT, log, 0,
! 154: "SSL_CTX_get_ex_new_index() failed");
! 155: return NGX_ERROR;
! 156: }
! 157:
! 158: return NGX_OK;
! 159: }
! 160:
! 161:
! 162: ngx_int_t
! 163: ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)
! 164: {
! 165: ssl->ctx = SSL_CTX_new(SSLv23_method());
! 166:
! 167: if (ssl->ctx == NULL) {
! 168: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "SSL_CTX_new() failed");
! 169: return NGX_ERROR;
! 170: }
! 171:
! 172: if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {
! 173: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 174: "SSL_CTX_set_ex_data() failed");
! 175: return NGX_ERROR;
! 176: }
! 177:
! 178: /* client side options */
! 179:
! 180: SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);
! 181: SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG);
! 182:
! 183: /* server side options */
! 184:
! 185: SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);
! 186: SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);
! 187:
! 188: /* this option allow a potential SSL 2.0 rollback (CAN-2005-2969) */
! 189: SSL_CTX_set_options(ssl->ctx, SSL_OP_MSIE_SSLV2_RSA_PADDING);
! 190:
! 191: SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);
! 192: SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);
! 193: SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);
! 194:
! 195: SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
! 196:
! 197: SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
! 198:
! 199: if (!(protocols & NGX_SSL_SSLv2)) {
! 200: SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);
! 201: }
! 202: if (!(protocols & NGX_SSL_SSLv3)) {
! 203: SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);
! 204: }
! 205: if (!(protocols & NGX_SSL_TLSv1)) {
! 206: SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);
! 207: }
! 208: #ifdef SSL_OP_NO_TLSv1_1
! 209: if (!(protocols & NGX_SSL_TLSv1_1)) {
! 210: SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);
! 211: }
! 212: #endif
! 213: #ifdef SSL_OP_NO_TLSv1_2
! 214: if (!(protocols & NGX_SSL_TLSv1_2)) {
! 215: SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);
! 216: }
! 217: #endif
! 218:
! 219: #ifdef SSL_OP_NO_COMPRESSION
! 220: SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);
! 221: #endif
! 222:
! 223: #ifdef SSL_MODE_RELEASE_BUFFERS
! 224: SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);
! 225: #endif
! 226:
! 227: SSL_CTX_set_read_ahead(ssl->ctx, 1);
! 228:
! 229: SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);
! 230:
! 231: return NGX_OK;
! 232: }
! 233:
! 234:
! 235: ngx_int_t
! 236: ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
! 237: ngx_str_t *key)
! 238: {
! 239: BIO *bio;
! 240: X509 *x509;
! 241: u_long n;
! 242:
! 243: if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
! 244: return NGX_ERROR;
! 245: }
! 246:
! 247: /*
! 248: * we can't use SSL_CTX_use_certificate_chain_file() as it doesn't
! 249: * allow to access certificate later from SSL_CTX, so we reimplement
! 250: * it here
! 251: */
! 252:
! 253: bio = BIO_new_file((char *) cert->data, "r");
! 254: if (bio == NULL) {
! 255: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 256: "BIO_new_file(\"%s\") failed", cert->data);
! 257: return NGX_ERROR;
! 258: }
! 259:
! 260: x509 = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
! 261: if (x509 == NULL) {
! 262: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 263: "PEM_read_bio_X509_AUX(\"%s\") failed", cert->data);
! 264: BIO_free(bio);
! 265: return NGX_ERROR;
! 266: }
! 267:
! 268: if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {
! 269: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 270: "SSL_CTX_use_certificate(\"%s\") failed", cert->data);
! 271: X509_free(x509);
! 272: BIO_free(bio);
! 273: return NGX_ERROR;
! 274: }
! 275:
! 276: if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509)
! 277: == 0)
! 278: {
! 279: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 280: "SSL_CTX_set_ex_data() failed");
! 281: return NGX_ERROR;
! 282: }
! 283:
! 284: X509_free(x509);
! 285:
! 286: /* read rest of the chain */
! 287:
! 288: for ( ;; ) {
! 289:
! 290: x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
! 291: if (x509 == NULL) {
! 292: n = ERR_peek_last_error();
! 293:
! 294: if (ERR_GET_LIB(n) == ERR_LIB_PEM
! 295: && ERR_GET_REASON(n) == PEM_R_NO_START_LINE)
! 296: {
! 297: /* end of file */
! 298: ERR_clear_error();
! 299: break;
! 300: }
! 301:
! 302: /* some real error */
! 303:
! 304: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 305: "PEM_read_bio_X509(\"%s\") failed", cert->data);
! 306: BIO_free(bio);
! 307: return NGX_ERROR;
! 308: }
! 309:
! 310: if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {
! 311: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 312: "SSL_CTX_add_extra_chain_cert(\"%s\") failed",
! 313: cert->data);
! 314: X509_free(x509);
! 315: BIO_free(bio);
! 316: return NGX_ERROR;
! 317: }
! 318: }
! 319:
! 320: BIO_free(bio);
! 321:
! 322: if (ngx_conf_full_name(cf->cycle, key, 1) != NGX_OK) {
! 323: return NGX_ERROR;
! 324: }
! 325:
! 326: if (SSL_CTX_use_PrivateKey_file(ssl->ctx, (char *) key->data,
! 327: SSL_FILETYPE_PEM)
! 328: == 0)
! 329: {
! 330: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 331: "SSL_CTX_use_PrivateKey_file(\"%s\") failed", key->data);
! 332: return NGX_ERROR;
! 333: }
! 334:
! 335: return NGX_OK;
! 336: }
! 337:
! 338:
! 339: ngx_int_t
! 340: ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
! 341: ngx_int_t depth)
! 342: {
! 343: STACK_OF(X509_NAME) *list;
! 344:
! 345: SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_http_ssl_verify_callback);
! 346:
! 347: SSL_CTX_set_verify_depth(ssl->ctx, depth);
! 348:
! 349: if (cert->len == 0) {
! 350: return NGX_OK;
! 351: }
! 352:
! 353: if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
! 354: return NGX_ERROR;
! 355: }
! 356:
! 357: if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
! 358: == 0)
! 359: {
! 360: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 361: "SSL_CTX_load_verify_locations(\"%s\") failed",
! 362: cert->data);
! 363: return NGX_ERROR;
! 364: }
! 365:
! 366: list = SSL_load_client_CA_file((char *) cert->data);
! 367:
! 368: if (list == NULL) {
! 369: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 370: "SSL_load_client_CA_file(\"%s\") failed", cert->data);
! 371: return NGX_ERROR;
! 372: }
! 373:
! 374: /*
! 375: * before 0.9.7h and 0.9.8 SSL_load_client_CA_file()
! 376: * always leaved an error in the error queue
! 377: */
! 378:
! 379: ERR_clear_error();
! 380:
! 381: SSL_CTX_set_client_CA_list(ssl->ctx, list);
! 382:
! 383: return NGX_OK;
! 384: }
! 385:
! 386:
! 387: ngx_int_t
! 388: ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,
! 389: ngx_int_t depth)
! 390: {
! 391: SSL_CTX_set_verify_depth(ssl->ctx, depth);
! 392:
! 393: if (cert->len == 0) {
! 394: return NGX_OK;
! 395: }
! 396:
! 397: if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {
! 398: return NGX_ERROR;
! 399: }
! 400:
! 401: if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)
! 402: == 0)
! 403: {
! 404: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 405: "SSL_CTX_load_verify_locations(\"%s\") failed",
! 406: cert->data);
! 407: return NGX_ERROR;
! 408: }
! 409:
! 410: return NGX_OK;
! 411: }
! 412:
! 413:
! 414: ngx_int_t
! 415: ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)
! 416: {
! 417: X509_STORE *store;
! 418: X509_LOOKUP *lookup;
! 419:
! 420: if (crl->len == 0) {
! 421: return NGX_OK;
! 422: }
! 423:
! 424: if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {
! 425: return NGX_ERROR;
! 426: }
! 427:
! 428: store = SSL_CTX_get_cert_store(ssl->ctx);
! 429:
! 430: if (store == NULL) {
! 431: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 432: "SSL_CTX_get_cert_store() failed");
! 433: return NGX_ERROR;
! 434: }
! 435:
! 436: lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());
! 437:
! 438: if (lookup == NULL) {
! 439: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 440: "X509_STORE_add_lookup() failed");
! 441: return NGX_ERROR;
! 442: }
! 443:
! 444: if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)
! 445: == 0)
! 446: {
! 447: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 448: "X509_LOOKUP_load_file(\"%s\") failed", crl->data);
! 449: return NGX_ERROR;
! 450: }
! 451:
! 452: X509_STORE_set_flags(store,
! 453: X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
! 454:
! 455: return NGX_OK;
! 456: }
! 457:
! 458:
! 459: static int
! 460: ngx_http_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)
! 461: {
! 462: #if (NGX_DEBUG)
! 463: char *subject, *issuer;
! 464: int err, depth;
! 465: X509 *cert;
! 466: X509_NAME *sname, *iname;
! 467: ngx_connection_t *c;
! 468: ngx_ssl_conn_t *ssl_conn;
! 469:
! 470: ssl_conn = X509_STORE_CTX_get_ex_data(x509_store,
! 471: SSL_get_ex_data_X509_STORE_CTX_idx());
! 472:
! 473: c = ngx_ssl_get_connection(ssl_conn);
! 474:
! 475: cert = X509_STORE_CTX_get_current_cert(x509_store);
! 476: err = X509_STORE_CTX_get_error(x509_store);
! 477: depth = X509_STORE_CTX_get_error_depth(x509_store);
! 478:
! 479: sname = X509_get_subject_name(cert);
! 480: subject = sname ? X509_NAME_oneline(sname, NULL, 0) : "(none)";
! 481:
! 482: iname = X509_get_issuer_name(cert);
! 483: issuer = iname ? X509_NAME_oneline(iname, NULL, 0) : "(none)";
! 484:
! 485: ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 486: "verify:%d, error:%d, depth:%d, "
! 487: "subject:\"%s\",issuer: \"%s\"",
! 488: ok, err, depth, subject, issuer);
! 489:
! 490: if (sname) {
! 491: OPENSSL_free(subject);
! 492: }
! 493:
! 494: if (iname) {
! 495: OPENSSL_free(issuer);
! 496: }
! 497: #endif
! 498:
! 499: return 1;
! 500: }
! 501:
! 502:
! 503: static void
! 504: ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)
! 505: {
! 506: ngx_connection_t *c;
! 507:
! 508: if (where & SSL_CB_HANDSHAKE_START) {
! 509: c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);
! 510:
! 511: if (c->ssl->handshaked) {
! 512: c->ssl->renegotiation = 1;
! 513: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL renegotiation");
! 514: }
! 515: }
! 516: }
! 517:
! 518:
! 519: RSA *
! 520: ngx_ssl_rsa512_key_callback(SSL *ssl, int is_export, int key_length)
! 521: {
! 522: static RSA *key;
! 523:
! 524: if (key_length == 512) {
! 525: if (key == NULL) {
! 526: key = RSA_generate_key(512, RSA_F4, NULL, NULL);
! 527: }
! 528: }
! 529:
! 530: return key;
! 531: }
! 532:
! 533:
! 534: ngx_int_t
! 535: ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
! 536: {
! 537: DH *dh;
! 538: BIO *bio;
! 539:
! 540: /*
! 541: * -----BEGIN DH PARAMETERS-----
! 542: * MIGHAoGBALu8LcrYRnSQfEP89YDpz9vZWKP1aLQtSwju1OsPs1BMbAMCducQgAxc
! 543: * y7qokiYUxb7spWWl/fHSh6K8BJvmd4Bg6RqSp1fjBI9osHb302zI8pul34HcLKcl
! 544: * 7OZicMyaUDXYzs7vnqAnSmOrHlj6/UmI0PZdFGdX2gcd8EXP4WubAgEC
! 545: * -----END DH PARAMETERS-----
! 546: */
! 547:
! 548: static unsigned char dh1024_p[] = {
! 549: 0xBB, 0xBC, 0x2D, 0xCA, 0xD8, 0x46, 0x74, 0x90, 0x7C, 0x43, 0xFC, 0xF5,
! 550: 0x80, 0xE9, 0xCF, 0xDB, 0xD9, 0x58, 0xA3, 0xF5, 0x68, 0xB4, 0x2D, 0x4B,
! 551: 0x08, 0xEE, 0xD4, 0xEB, 0x0F, 0xB3, 0x50, 0x4C, 0x6C, 0x03, 0x02, 0x76,
! 552: 0xE7, 0x10, 0x80, 0x0C, 0x5C, 0xCB, 0xBA, 0xA8, 0x92, 0x26, 0x14, 0xC5,
! 553: 0xBE, 0xEC, 0xA5, 0x65, 0xA5, 0xFD, 0xF1, 0xD2, 0x87, 0xA2, 0xBC, 0x04,
! 554: 0x9B, 0xE6, 0x77, 0x80, 0x60, 0xE9, 0x1A, 0x92, 0xA7, 0x57, 0xE3, 0x04,
! 555: 0x8F, 0x68, 0xB0, 0x76, 0xF7, 0xD3, 0x6C, 0xC8, 0xF2, 0x9B, 0xA5, 0xDF,
! 556: 0x81, 0xDC, 0x2C, 0xA7, 0x25, 0xEC, 0xE6, 0x62, 0x70, 0xCC, 0x9A, 0x50,
! 557: 0x35, 0xD8, 0xCE, 0xCE, 0xEF, 0x9E, 0xA0, 0x27, 0x4A, 0x63, 0xAB, 0x1E,
! 558: 0x58, 0xFA, 0xFD, 0x49, 0x88, 0xD0, 0xF6, 0x5D, 0x14, 0x67, 0x57, 0xDA,
! 559: 0x07, 0x1D, 0xF0, 0x45, 0xCF, 0xE1, 0x6B, 0x9B
! 560: };
! 561:
! 562: static unsigned char dh1024_g[] = { 0x02 };
! 563:
! 564:
! 565: if (file->len == 0) {
! 566:
! 567: dh = DH_new();
! 568: if (dh == NULL) {
! 569: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "DH_new() failed");
! 570: return NGX_ERROR;
! 571: }
! 572:
! 573: dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
! 574: dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
! 575:
! 576: if (dh->p == NULL || dh->g == NULL) {
! 577: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, "BN_bin2bn() failed");
! 578: DH_free(dh);
! 579: return NGX_ERROR;
! 580: }
! 581:
! 582: SSL_CTX_set_tmp_dh(ssl->ctx, dh);
! 583:
! 584: DH_free(dh);
! 585:
! 586: return NGX_OK;
! 587: }
! 588:
! 589: if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
! 590: return NGX_ERROR;
! 591: }
! 592:
! 593: bio = BIO_new_file((char *) file->data, "r");
! 594: if (bio == NULL) {
! 595: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 596: "BIO_new_file(\"%s\") failed", file->data);
! 597: return NGX_ERROR;
! 598: }
! 599:
! 600: dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
! 601: if (dh == NULL) {
! 602: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 603: "PEM_read_bio_DHparams(\"%s\") failed", file->data);
! 604: BIO_free(bio);
! 605: return NGX_ERROR;
! 606: }
! 607:
! 608: SSL_CTX_set_tmp_dh(ssl->ctx, dh);
! 609:
! 610: DH_free(dh);
! 611: BIO_free(bio);
! 612:
! 613: return NGX_OK;
! 614: }
! 615:
! 616:
! 617: ngx_int_t
! 618: ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)
! 619: {
! 620: #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
! 621: #ifndef OPENSSL_NO_ECDH
! 622: int nid;
! 623: EC_KEY *ecdh;
! 624:
! 625: /*
! 626: * Elliptic-Curve Diffie-Hellman parameters are either "named curves"
! 627: * from RFC 4492 section 5.1.1, or explicitly described curves over
! 628: * binary fields. OpenSSL only supports the "named curves", which provide
! 629: * maximum interoperability.
! 630: */
! 631:
! 632: nid = OBJ_sn2nid((const char *) name->data);
! 633: if (nid == 0) {
! 634: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 635: "Unknown curve name \"%s\"", name->data);
! 636: return NGX_ERROR;
! 637: }
! 638:
! 639: ecdh = EC_KEY_new_by_curve_name(nid);
! 640: if (ecdh == NULL) {
! 641: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 642: "Unable to create curve \"%s\"", name->data);
! 643: return NGX_ERROR;
! 644: }
! 645:
! 646: SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);
! 647:
! 648: SSL_CTX_set_tmp_ecdh(ssl->ctx, ecdh);
! 649:
! 650: EC_KEY_free(ecdh);
! 651: #endif
! 652: #endif
! 653:
! 654: return NGX_OK;
! 655: }
! 656:
! 657:
! 658: ngx_int_t
! 659: ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)
! 660: {
! 661: ngx_ssl_connection_t *sc;
! 662:
! 663: sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));
! 664: if (sc == NULL) {
! 665: return NGX_ERROR;
! 666: }
! 667:
! 668: sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);
! 669:
! 670: sc->connection = SSL_new(ssl->ctx);
! 671:
! 672: if (sc->connection == NULL) {
! 673: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_new() failed");
! 674: return NGX_ERROR;
! 675: }
! 676:
! 677: if (SSL_set_fd(sc->connection, c->fd) == 0) {
! 678: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_fd() failed");
! 679: return NGX_ERROR;
! 680: }
! 681:
! 682: if (flags & NGX_SSL_CLIENT) {
! 683: SSL_set_connect_state(sc->connection);
! 684:
! 685: } else {
! 686: SSL_set_accept_state(sc->connection);
! 687: }
! 688:
! 689: if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) {
! 690: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed");
! 691: return NGX_ERROR;
! 692: }
! 693:
! 694: c->ssl = sc;
! 695:
! 696: return NGX_OK;
! 697: }
! 698:
! 699:
! 700: ngx_int_t
! 701: ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session)
! 702: {
! 703: if (session) {
! 704: if (SSL_set_session(c->ssl->connection, session) == 0) {
! 705: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_session() failed");
! 706: return NGX_ERROR;
! 707: }
! 708: }
! 709:
! 710: return NGX_OK;
! 711: }
! 712:
! 713:
! 714: ngx_int_t
! 715: ngx_ssl_handshake(ngx_connection_t *c)
! 716: {
! 717: int n, sslerr;
! 718: ngx_err_t err;
! 719:
! 720: ngx_ssl_clear_error(c->log);
! 721:
! 722: n = SSL_do_handshake(c->ssl->connection);
! 723:
! 724: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n);
! 725:
! 726: if (n == 1) {
! 727:
! 728: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 729: return NGX_ERROR;
! 730: }
! 731:
! 732: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
! 733: return NGX_ERROR;
! 734: }
! 735:
! 736: #if (NGX_DEBUG)
! 737: {
! 738: char buf[129], *s, *d;
! 739: #if OPENSSL_VERSION_NUMBER >= 0x10000000L
! 740: const
! 741: #endif
! 742: SSL_CIPHER *cipher;
! 743:
! 744: cipher = SSL_get_current_cipher(c->ssl->connection);
! 745:
! 746: if (cipher) {
! 747: SSL_CIPHER_description(cipher, &buf[1], 128);
! 748:
! 749: for (s = &buf[1], d = buf; *s; s++) {
! 750: if (*s == ' ' && *d == ' ') {
! 751: continue;
! 752: }
! 753:
! 754: if (*s == LF || *s == CR) {
! 755: continue;
! 756: }
! 757:
! 758: *++d = *s;
! 759: }
! 760:
! 761: if (*d != ' ') {
! 762: d++;
! 763: }
! 764:
! 765: *d = '\0';
! 766:
! 767: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 768: "SSL: %s, cipher: \"%s\"",
! 769: SSL_get_version(c->ssl->connection), &buf[1]);
! 770:
! 771: if (SSL_session_reused(c->ssl->connection)) {
! 772: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 773: "SSL reused session");
! 774: }
! 775:
! 776: } else {
! 777: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 778: "SSL no shared ciphers");
! 779: }
! 780: }
! 781: #endif
! 782:
! 783: c->ssl->handshaked = 1;
! 784:
! 785: c->recv = ngx_ssl_recv;
! 786: c->send = ngx_ssl_write;
! 787: c->recv_chain = ngx_ssl_recv_chain;
! 788: c->send_chain = ngx_ssl_send_chain;
! 789:
! 790: /* initial handshake done, disable renegotiation (CVE-2009-3555) */
! 791: if (c->ssl->connection->s3) {
! 792: c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
! 793: }
! 794:
! 795: return NGX_OK;
! 796: }
! 797:
! 798: sslerr = SSL_get_error(c->ssl->connection, n);
! 799:
! 800: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
! 801:
! 802: if (sslerr == SSL_ERROR_WANT_READ) {
! 803: c->read->ready = 0;
! 804: c->read->handler = ngx_ssl_handshake_handler;
! 805: c->write->handler = ngx_ssl_handshake_handler;
! 806:
! 807: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 808: return NGX_ERROR;
! 809: }
! 810:
! 811: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
! 812: return NGX_ERROR;
! 813: }
! 814:
! 815: return NGX_AGAIN;
! 816: }
! 817:
! 818: if (sslerr == SSL_ERROR_WANT_WRITE) {
! 819: c->write->ready = 0;
! 820: c->read->handler = ngx_ssl_handshake_handler;
! 821: c->write->handler = ngx_ssl_handshake_handler;
! 822:
! 823: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 824: return NGX_ERROR;
! 825: }
! 826:
! 827: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
! 828: return NGX_ERROR;
! 829: }
! 830:
! 831: return NGX_AGAIN;
! 832: }
! 833:
! 834: err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
! 835:
! 836: c->ssl->no_wait_shutdown = 1;
! 837: c->ssl->no_send_shutdown = 1;
! 838: c->read->eof = 1;
! 839:
! 840: if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
! 841: ngx_log_error(NGX_LOG_INFO, c->log, err,
! 842: "peer closed connection in SSL handshake");
! 843:
! 844: return NGX_ERROR;
! 845: }
! 846:
! 847: c->read->error = 1;
! 848:
! 849: ngx_ssl_connection_error(c, sslerr, err, "SSL_do_handshake() failed");
! 850:
! 851: return NGX_ERROR;
! 852: }
! 853:
! 854:
! 855: static void
! 856: ngx_ssl_handshake_handler(ngx_event_t *ev)
! 857: {
! 858: ngx_connection_t *c;
! 859:
! 860: c = ev->data;
! 861:
! 862: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 863: "SSL handshake handler: %d", ev->write);
! 864:
! 865: if (ev->timedout) {
! 866: c->ssl->handler(c);
! 867: return;
! 868: }
! 869:
! 870: if (ngx_ssl_handshake(c) == NGX_AGAIN) {
! 871: return;
! 872: }
! 873:
! 874: c->ssl->handler(c);
! 875: }
! 876:
! 877:
! 878: ssize_t
! 879: ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl)
! 880: {
! 881: u_char *last;
! 882: ssize_t n, bytes;
! 883: ngx_buf_t *b;
! 884:
! 885: bytes = 0;
! 886:
! 887: b = cl->buf;
! 888: last = b->last;
! 889:
! 890: for ( ;; ) {
! 891:
! 892: n = ngx_ssl_recv(c, last, b->end - last);
! 893:
! 894: if (n > 0) {
! 895: last += n;
! 896: bytes += n;
! 897:
! 898: if (last == b->end) {
! 899: cl = cl->next;
! 900:
! 901: if (cl == NULL) {
! 902: return bytes;
! 903: }
! 904:
! 905: b = cl->buf;
! 906: last = b->last;
! 907: }
! 908:
! 909: continue;
! 910: }
! 911:
! 912: if (bytes) {
! 913:
! 914: if (n == 0 || n == NGX_ERROR) {
! 915: c->read->ready = 1;
! 916: }
! 917:
! 918: return bytes;
! 919: }
! 920:
! 921: return n;
! 922: }
! 923: }
! 924:
! 925:
! 926: ssize_t
! 927: ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)
! 928: {
! 929: int n, bytes;
! 930:
! 931: if (c->ssl->last == NGX_ERROR) {
! 932: c->read->error = 1;
! 933: return NGX_ERROR;
! 934: }
! 935:
! 936: if (c->ssl->last == NGX_DONE) {
! 937: c->read->ready = 0;
! 938: c->read->eof = 1;
! 939: return 0;
! 940: }
! 941:
! 942: bytes = 0;
! 943:
! 944: ngx_ssl_clear_error(c->log);
! 945:
! 946: /*
! 947: * SSL_read() may return data in parts, so try to read
! 948: * until SSL_read() would return no data
! 949: */
! 950:
! 951: for ( ;; ) {
! 952:
! 953: n = SSL_read(c->ssl->connection, buf, size);
! 954:
! 955: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_read: %d", n);
! 956:
! 957: if (n > 0) {
! 958: bytes += n;
! 959: }
! 960:
! 961: c->ssl->last = ngx_ssl_handle_recv(c, n);
! 962:
! 963: if (c->ssl->last == NGX_OK) {
! 964:
! 965: size -= n;
! 966:
! 967: if (size == 0) {
! 968: return bytes;
! 969: }
! 970:
! 971: buf += n;
! 972:
! 973: continue;
! 974: }
! 975:
! 976: if (bytes) {
! 977: return bytes;
! 978: }
! 979:
! 980: switch (c->ssl->last) {
! 981:
! 982: case NGX_DONE:
! 983: c->read->ready = 0;
! 984: c->read->eof = 1;
! 985: return 0;
! 986:
! 987: case NGX_ERROR:
! 988: c->read->error = 1;
! 989:
! 990: /* fall through */
! 991:
! 992: case NGX_AGAIN:
! 993: return c->ssl->last;
! 994: }
! 995: }
! 996: }
! 997:
! 998:
! 999: static ngx_int_t
! 1000: ngx_ssl_handle_recv(ngx_connection_t *c, int n)
! 1001: {
! 1002: int sslerr;
! 1003: ngx_err_t err;
! 1004:
! 1005: if (c->ssl->renegotiation) {
! 1006: /*
! 1007: * disable renegotiation (CVE-2009-3555):
! 1008: * OpenSSL (at least up to 0.9.8l) does not handle disabled
! 1009: * renegotiation gracefully, so drop connection here
! 1010: */
! 1011:
! 1012: ngx_log_error(NGX_LOG_NOTICE, c->log, 0, "SSL renegotiation disabled");
! 1013:
! 1014: while (ERR_peek_error()) {
! 1015: ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0,
! 1016: "ignoring stale global SSL error");
! 1017: }
! 1018:
! 1019: ERR_clear_error();
! 1020:
! 1021: c->ssl->no_wait_shutdown = 1;
! 1022: c->ssl->no_send_shutdown = 1;
! 1023:
! 1024: return NGX_ERROR;
! 1025: }
! 1026:
! 1027: if (n > 0) {
! 1028:
! 1029: if (c->ssl->saved_write_handler) {
! 1030:
! 1031: c->write->handler = c->ssl->saved_write_handler;
! 1032: c->ssl->saved_write_handler = NULL;
! 1033: c->write->ready = 1;
! 1034:
! 1035: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
! 1036: return NGX_ERROR;
! 1037: }
! 1038:
! 1039: ngx_post_event(c->write, &ngx_posted_events);
! 1040: }
! 1041:
! 1042: return NGX_OK;
! 1043: }
! 1044:
! 1045: sslerr = SSL_get_error(c->ssl->connection, n);
! 1046:
! 1047: err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
! 1048:
! 1049: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
! 1050:
! 1051: if (sslerr == SSL_ERROR_WANT_READ) {
! 1052: c->read->ready = 0;
! 1053: return NGX_AGAIN;
! 1054: }
! 1055:
! 1056: if (sslerr == SSL_ERROR_WANT_WRITE) {
! 1057:
! 1058: ngx_log_error(NGX_LOG_INFO, c->log, 0,
! 1059: "peer started SSL renegotiation");
! 1060:
! 1061: c->write->ready = 0;
! 1062:
! 1063: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
! 1064: return NGX_ERROR;
! 1065: }
! 1066:
! 1067: /*
! 1068: * we do not set the timer because there is already the read event timer
! 1069: */
! 1070:
! 1071: if (c->ssl->saved_write_handler == NULL) {
! 1072: c->ssl->saved_write_handler = c->write->handler;
! 1073: c->write->handler = ngx_ssl_write_handler;
! 1074: }
! 1075:
! 1076: return NGX_AGAIN;
! 1077: }
! 1078:
! 1079: c->ssl->no_wait_shutdown = 1;
! 1080: c->ssl->no_send_shutdown = 1;
! 1081:
! 1082: if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {
! 1083: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 1084: "peer shutdown SSL cleanly");
! 1085: return NGX_DONE;
! 1086: }
! 1087:
! 1088: ngx_ssl_connection_error(c, sslerr, err, "SSL_read() failed");
! 1089:
! 1090: return NGX_ERROR;
! 1091: }
! 1092:
! 1093:
! 1094: static void
! 1095: ngx_ssl_write_handler(ngx_event_t *wev)
! 1096: {
! 1097: ngx_connection_t *c;
! 1098:
! 1099: c = wev->data;
! 1100:
! 1101: c->read->handler(c->read);
! 1102: }
! 1103:
! 1104:
! 1105: /*
! 1106: * OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer
! 1107: * before the SSL_write() call to decrease a SSL overhead.
! 1108: *
! 1109: * Besides for protocols such as HTTP it is possible to always buffer
! 1110: * the output to decrease a SSL overhead some more.
! 1111: */
! 1112:
! 1113: ngx_chain_t *
! 1114: ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
! 1115: {
! 1116: int n;
! 1117: ngx_uint_t flush;
! 1118: ssize_t send, size;
! 1119: ngx_buf_t *buf;
! 1120:
! 1121: if (!c->ssl->buffer) {
! 1122:
! 1123: while (in) {
! 1124: if (ngx_buf_special(in->buf)) {
! 1125: in = in->next;
! 1126: continue;
! 1127: }
! 1128:
! 1129: n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);
! 1130:
! 1131: if (n == NGX_ERROR) {
! 1132: return NGX_CHAIN_ERROR;
! 1133: }
! 1134:
! 1135: if (n == NGX_AGAIN) {
! 1136: return in;
! 1137: }
! 1138:
! 1139: in->buf->pos += n;
! 1140: c->sent += n;
! 1141:
! 1142: if (in->buf->pos == in->buf->last) {
! 1143: in = in->next;
! 1144: }
! 1145: }
! 1146:
! 1147: return in;
! 1148: }
! 1149:
! 1150:
! 1151: /* the maximum limit size is the maximum int32_t value - the page size */
! 1152:
! 1153: if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {
! 1154: limit = NGX_MAX_INT32_VALUE - ngx_pagesize;
! 1155: }
! 1156:
! 1157: buf = c->ssl->buf;
! 1158:
! 1159: if (buf == NULL) {
! 1160: buf = ngx_create_temp_buf(c->pool, NGX_SSL_BUFSIZE);
! 1161: if (buf == NULL) {
! 1162: return NGX_CHAIN_ERROR;
! 1163: }
! 1164:
! 1165: c->ssl->buf = buf;
! 1166: }
! 1167:
! 1168: if (buf->start == NULL) {
! 1169: buf->start = ngx_palloc(c->pool, NGX_SSL_BUFSIZE);
! 1170: if (buf->start == NULL) {
! 1171: return NGX_CHAIN_ERROR;
! 1172: }
! 1173:
! 1174: buf->pos = buf->start;
! 1175: buf->last = buf->start;
! 1176: buf->end = buf->start + NGX_SSL_BUFSIZE;
! 1177: }
! 1178:
! 1179: send = buf->last - buf->pos;
! 1180: flush = (in == NULL) ? 1 : buf->flush;
! 1181:
! 1182: for ( ;; ) {
! 1183:
! 1184: while (in && buf->last < buf->end && send < limit) {
! 1185: if (in->buf->last_buf || in->buf->flush) {
! 1186: flush = 1;
! 1187: }
! 1188:
! 1189: if (ngx_buf_special(in->buf)) {
! 1190: in = in->next;
! 1191: continue;
! 1192: }
! 1193:
! 1194: size = in->buf->last - in->buf->pos;
! 1195:
! 1196: if (size > buf->end - buf->last) {
! 1197: size = buf->end - buf->last;
! 1198: }
! 1199:
! 1200: if (send + size > limit) {
! 1201: size = (ssize_t) (limit - send);
! 1202: }
! 1203:
! 1204: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 1205: "SSL buf copy: %d", size);
! 1206:
! 1207: ngx_memcpy(buf->last, in->buf->pos, size);
! 1208:
! 1209: buf->last += size;
! 1210: in->buf->pos += size;
! 1211: send += size;
! 1212:
! 1213: if (in->buf->pos == in->buf->last) {
! 1214: in = in->next;
! 1215: }
! 1216: }
! 1217:
! 1218: if (!flush && send < limit && buf->last < buf->end) {
! 1219: break;
! 1220: }
! 1221:
! 1222: size = buf->last - buf->pos;
! 1223:
! 1224: if (size == 0) {
! 1225: buf->flush = 0;
! 1226: c->buffered &= ~NGX_SSL_BUFFERED;
! 1227: return in;
! 1228: }
! 1229:
! 1230: n = ngx_ssl_write(c, buf->pos, size);
! 1231:
! 1232: if (n == NGX_ERROR) {
! 1233: return NGX_CHAIN_ERROR;
! 1234: }
! 1235:
! 1236: if (n == NGX_AGAIN) {
! 1237: break;
! 1238: }
! 1239:
! 1240: buf->pos += n;
! 1241: c->sent += n;
! 1242:
! 1243: if (n < size) {
! 1244: break;
! 1245: }
! 1246:
! 1247: flush = 0;
! 1248:
! 1249: buf->pos = buf->start;
! 1250: buf->last = buf->start;
! 1251:
! 1252: if (in == NULL || send == limit) {
! 1253: break;
! 1254: }
! 1255: }
! 1256:
! 1257: buf->flush = flush;
! 1258:
! 1259: if (buf->pos < buf->last) {
! 1260: c->buffered |= NGX_SSL_BUFFERED;
! 1261:
! 1262: } else {
! 1263: c->buffered &= ~NGX_SSL_BUFFERED;
! 1264: }
! 1265:
! 1266: return in;
! 1267: }
! 1268:
! 1269:
! 1270: ssize_t
! 1271: ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
! 1272: {
! 1273: int n, sslerr;
! 1274: ngx_err_t err;
! 1275:
! 1276: ngx_ssl_clear_error(c->log);
! 1277:
! 1278: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);
! 1279:
! 1280: n = SSL_write(c->ssl->connection, data, size);
! 1281:
! 1282: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n);
! 1283:
! 1284: if (n > 0) {
! 1285:
! 1286: if (c->ssl->saved_read_handler) {
! 1287:
! 1288: c->read->handler = c->ssl->saved_read_handler;
! 1289: c->ssl->saved_read_handler = NULL;
! 1290: c->read->ready = 1;
! 1291:
! 1292: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 1293: return NGX_ERROR;
! 1294: }
! 1295:
! 1296: ngx_post_event(c->read, &ngx_posted_events);
! 1297: }
! 1298:
! 1299: return n;
! 1300: }
! 1301:
! 1302: sslerr = SSL_get_error(c->ssl->connection, n);
! 1303:
! 1304: err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
! 1305:
! 1306: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_get_error: %d", sslerr);
! 1307:
! 1308: if (sslerr == SSL_ERROR_WANT_WRITE) {
! 1309: c->write->ready = 0;
! 1310: return NGX_AGAIN;
! 1311: }
! 1312:
! 1313: if (sslerr == SSL_ERROR_WANT_READ) {
! 1314:
! 1315: ngx_log_error(NGX_LOG_INFO, c->log, 0,
! 1316: "peer started SSL renegotiation");
! 1317:
! 1318: c->read->ready = 0;
! 1319:
! 1320: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 1321: return NGX_ERROR;
! 1322: }
! 1323:
! 1324: /*
! 1325: * we do not set the timer because there is already
! 1326: * the write event timer
! 1327: */
! 1328:
! 1329: if (c->ssl->saved_read_handler == NULL) {
! 1330: c->ssl->saved_read_handler = c->read->handler;
! 1331: c->read->handler = ngx_ssl_read_handler;
! 1332: }
! 1333:
! 1334: return NGX_AGAIN;
! 1335: }
! 1336:
! 1337: c->ssl->no_wait_shutdown = 1;
! 1338: c->ssl->no_send_shutdown = 1;
! 1339: c->write->error = 1;
! 1340:
! 1341: ngx_ssl_connection_error(c, sslerr, err, "SSL_write() failed");
! 1342:
! 1343: return NGX_ERROR;
! 1344: }
! 1345:
! 1346:
! 1347: static void
! 1348: ngx_ssl_read_handler(ngx_event_t *rev)
! 1349: {
! 1350: ngx_connection_t *c;
! 1351:
! 1352: c = rev->data;
! 1353:
! 1354: c->write->handler(c->write);
! 1355: }
! 1356:
! 1357:
! 1358: void
! 1359: ngx_ssl_free_buffer(ngx_connection_t *c)
! 1360: {
! 1361: if (c->ssl->buf && c->ssl->buf->start) {
! 1362: if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) {
! 1363: c->ssl->buf->start = NULL;
! 1364: }
! 1365: }
! 1366: }
! 1367:
! 1368:
! 1369: ngx_int_t
! 1370: ngx_ssl_shutdown(ngx_connection_t *c)
! 1371: {
! 1372: int n, sslerr, mode;
! 1373: ngx_err_t err;
! 1374:
! 1375: if (c->timedout) {
! 1376: mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;
! 1377: SSL_set_quiet_shutdown(c->ssl->connection, 1);
! 1378:
! 1379: } else {
! 1380: mode = SSL_get_shutdown(c->ssl->connection);
! 1381:
! 1382: if (c->ssl->no_wait_shutdown) {
! 1383: mode |= SSL_RECEIVED_SHUTDOWN;
! 1384: }
! 1385:
! 1386: if (c->ssl->no_send_shutdown) {
! 1387: mode |= SSL_SENT_SHUTDOWN;
! 1388: }
! 1389:
! 1390: if (c->ssl->no_wait_shutdown && c->ssl->no_send_shutdown) {
! 1391: SSL_set_quiet_shutdown(c->ssl->connection, 1);
! 1392: }
! 1393: }
! 1394:
! 1395: SSL_set_shutdown(c->ssl->connection, mode);
! 1396:
! 1397: ngx_ssl_clear_error(c->log);
! 1398:
! 1399: n = SSL_shutdown(c->ssl->connection);
! 1400:
! 1401: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_shutdown: %d", n);
! 1402:
! 1403: sslerr = 0;
! 1404:
! 1405: /* SSL_shutdown() never returns -1, on error it returns 0 */
! 1406:
! 1407: if (n != 1 && ERR_peek_error()) {
! 1408: sslerr = SSL_get_error(c->ssl->connection, n);
! 1409:
! 1410: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 1411: "SSL_get_error: %d", sslerr);
! 1412: }
! 1413:
! 1414: if (n == 1 || sslerr == 0 || sslerr == SSL_ERROR_ZERO_RETURN) {
! 1415: SSL_free(c->ssl->connection);
! 1416: c->ssl = NULL;
! 1417:
! 1418: return NGX_OK;
! 1419: }
! 1420:
! 1421: if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {
! 1422: c->read->handler = ngx_ssl_shutdown_handler;
! 1423: c->write->handler = ngx_ssl_shutdown_handler;
! 1424:
! 1425: if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
! 1426: return NGX_ERROR;
! 1427: }
! 1428:
! 1429: if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
! 1430: return NGX_ERROR;
! 1431: }
! 1432:
! 1433: if (sslerr == SSL_ERROR_WANT_READ) {
! 1434: ngx_add_timer(c->read, 30000);
! 1435: }
! 1436:
! 1437: return NGX_AGAIN;
! 1438: }
! 1439:
! 1440: err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;
! 1441:
! 1442: ngx_ssl_connection_error(c, sslerr, err, "SSL_shutdown() failed");
! 1443:
! 1444: SSL_free(c->ssl->connection);
! 1445: c->ssl = NULL;
! 1446:
! 1447: return NGX_ERROR;
! 1448: }
! 1449:
! 1450:
! 1451: static void
! 1452: ngx_ssl_shutdown_handler(ngx_event_t *ev)
! 1453: {
! 1454: ngx_connection_t *c;
! 1455: ngx_connection_handler_pt handler;
! 1456:
! 1457: c = ev->data;
! 1458: handler = c->ssl->handler;
! 1459:
! 1460: if (ev->timedout) {
! 1461: c->timedout = 1;
! 1462: }
! 1463:
! 1464: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, "SSL shutdown handler");
! 1465:
! 1466: if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
! 1467: return;
! 1468: }
! 1469:
! 1470: handler(c);
! 1471: }
! 1472:
! 1473:
! 1474: static void
! 1475: ngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,
! 1476: char *text)
! 1477: {
! 1478: int n;
! 1479: ngx_uint_t level;
! 1480:
! 1481: level = NGX_LOG_CRIT;
! 1482:
! 1483: if (sslerr == SSL_ERROR_SYSCALL) {
! 1484:
! 1485: if (err == NGX_ECONNRESET
! 1486: || err == NGX_EPIPE
! 1487: || err == NGX_ENOTCONN
! 1488: || err == NGX_ETIMEDOUT
! 1489: || err == NGX_ECONNREFUSED
! 1490: || err == NGX_ENETDOWN
! 1491: || err == NGX_ENETUNREACH
! 1492: || err == NGX_EHOSTDOWN
! 1493: || err == NGX_EHOSTUNREACH)
! 1494: {
! 1495: switch (c->log_error) {
! 1496:
! 1497: case NGX_ERROR_IGNORE_ECONNRESET:
! 1498: case NGX_ERROR_INFO:
! 1499: level = NGX_LOG_INFO;
! 1500: break;
! 1501:
! 1502: case NGX_ERROR_ERR:
! 1503: level = NGX_LOG_ERR;
! 1504: break;
! 1505:
! 1506: default:
! 1507: break;
! 1508: }
! 1509: }
! 1510:
! 1511: } else if (sslerr == SSL_ERROR_SSL) {
! 1512:
! 1513: n = ERR_GET_REASON(ERR_peek_error());
! 1514:
! 1515: /* handshake failures */
! 1516: if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC /* 103 */
! 1517: || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG /* 129 */
! 1518: || n == SSL_R_DIGEST_CHECK_FAILED /* 149 */
! 1519: || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST /* 151 */
! 1520: || n == SSL_R_EXCESSIVE_MESSAGE_SIZE /* 152 */
! 1521: || n == SSL_R_LENGTH_MISMATCH /* 159 */
! 1522: || n == SSL_R_NO_CIPHERS_PASSED /* 182 */
! 1523: || n == SSL_R_NO_CIPHERS_SPECIFIED /* 183 */
! 1524: || n == SSL_R_NO_COMPRESSION_SPECIFIED /* 187 */
! 1525: || n == SSL_R_NO_SHARED_CIPHER /* 193 */
! 1526: || n == SSL_R_RECORD_LENGTH_MISMATCH /* 213 */
! 1527: #ifdef SSL_R_PARSE_TLSEXT
! 1528: || n == SSL_R_PARSE_TLSEXT /* 227 */
! 1529: #endif
! 1530: || n == SSL_R_UNEXPECTED_MESSAGE /* 244 */
! 1531: || n == SSL_R_UNEXPECTED_RECORD /* 245 */
! 1532: || n == SSL_R_UNKNOWN_ALERT_TYPE /* 246 */
! 1533: || n == SSL_R_UNKNOWN_PROTOCOL /* 252 */
! 1534: || n == SSL_R_WRONG_VERSION_NUMBER /* 267 */
! 1535: || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC /* 281 */
! 1536: #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
! 1537: || n == SSL_R_RENEGOTIATE_EXT_TOO_LONG /* 335 */
! 1538: || n == SSL_R_RENEGOTIATION_ENCODING_ERR /* 336 */
! 1539: || n == SSL_R_RENEGOTIATION_MISMATCH /* 337 */
! 1540: #endif
! 1541: #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
! 1542: || n == SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED /* 338 */
! 1543: #endif
! 1544: #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
! 1545: || n == SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING /* 345 */
! 1546: #endif
! 1547: || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
! 1548: || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE /* 1010 */
! 1549: || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC /* 1020 */
! 1550: || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED /* 1021 */
! 1551: || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW /* 1022 */
! 1552: || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE /* 1030 */
! 1553: || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE /* 1040 */
! 1554: || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE /* 1041 */
! 1555: || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE /* 1042 */
! 1556: || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE /* 1043 */
! 1557: || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED /* 1044 */
! 1558: || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED /* 1045 */
! 1559: || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN /* 1046 */
! 1560: || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER /* 1047 */
! 1561: || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA /* 1048 */
! 1562: || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED /* 1049 */
! 1563: || n == SSL_R_TLSV1_ALERT_DECODE_ERROR /* 1050 */
! 1564: || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR /* 1051 */
! 1565: || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION /* 1060 */
! 1566: || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION /* 1070 */
! 1567: || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY /* 1071 */
! 1568: || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR /* 1080 */
! 1569: || n == SSL_R_TLSV1_ALERT_USER_CANCELLED /* 1090 */
! 1570: || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION) /* 1100 */
! 1571: {
! 1572: switch (c->log_error) {
! 1573:
! 1574: case NGX_ERROR_IGNORE_ECONNRESET:
! 1575: case NGX_ERROR_INFO:
! 1576: level = NGX_LOG_INFO;
! 1577: break;
! 1578:
! 1579: case NGX_ERROR_ERR:
! 1580: level = NGX_LOG_ERR;
! 1581: break;
! 1582:
! 1583: default:
! 1584: break;
! 1585: }
! 1586: }
! 1587: }
! 1588:
! 1589: ngx_ssl_error(level, c->log, err, text);
! 1590: }
! 1591:
! 1592:
! 1593: static void
! 1594: ngx_ssl_clear_error(ngx_log_t *log)
! 1595: {
! 1596: while (ERR_peek_error()) {
! 1597: ngx_ssl_error(NGX_LOG_ALERT, log, 0, "ignoring stale global SSL error");
! 1598: }
! 1599:
! 1600: ERR_clear_error();
! 1601: }
! 1602:
! 1603:
! 1604: void ngx_cdecl
! 1605: ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)
! 1606: {
! 1607: int flags;
! 1608: u_long n;
! 1609: va_list args;
! 1610: u_char *p, *last;
! 1611: u_char errstr[NGX_MAX_CONF_ERRSTR];
! 1612: const char *data;
! 1613:
! 1614: last = errstr + NGX_MAX_CONF_ERRSTR;
! 1615:
! 1616: va_start(args, fmt);
! 1617: p = ngx_vslprintf(errstr, last - 1, fmt, args);
! 1618: va_end(args);
! 1619:
! 1620: p = ngx_cpystrn(p, (u_char *) " (SSL:", last - p);
! 1621:
! 1622: for ( ;; ) {
! 1623:
! 1624: n = ERR_peek_error_line_data(NULL, NULL, &data, &flags);
! 1625:
! 1626: if (n == 0) {
! 1627: break;
! 1628: }
! 1629:
! 1630: if (p >= last) {
! 1631: goto next;
! 1632: }
! 1633:
! 1634: *p++ = ' ';
! 1635:
! 1636: ERR_error_string_n(n, (char *) p, last - p);
! 1637:
! 1638: while (p < last && *p) {
! 1639: p++;
! 1640: }
! 1641:
! 1642: if (p < last && *data && (flags & ERR_TXT_STRING)) {
! 1643: *p++ = ':';
! 1644: p = ngx_cpystrn(p, (u_char *) data, last - p);
! 1645: }
! 1646:
! 1647: next:
! 1648:
! 1649: (void) ERR_get_error();
! 1650: }
! 1651:
! 1652: ngx_log_error(level, log, err, "%s)", errstr);
! 1653: }
! 1654:
! 1655:
! 1656: ngx_int_t
! 1657: ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,
! 1658: ssize_t builtin_session_cache, ngx_shm_zone_t *shm_zone, time_t timeout)
! 1659: {
! 1660: long cache_mode;
! 1661:
! 1662: if (builtin_session_cache == NGX_SSL_NO_SCACHE) {
! 1663: SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);
! 1664: return NGX_OK;
! 1665: }
! 1666:
! 1667: SSL_CTX_set_session_id_context(ssl->ctx, sess_ctx->data, sess_ctx->len);
! 1668:
! 1669: if (builtin_session_cache == NGX_SSL_NONE_SCACHE) {
! 1670:
! 1671: /*
! 1672: * If the server explicitly says that it does not support
! 1673: * session reuse (see SSL_SESS_CACHE_OFF above), then
! 1674: * Outlook Express fails to upload a sent email to
! 1675: * the Sent Items folder on the IMAP server via a separate IMAP
! 1676: * connection in the background. Therefore we have a special
! 1677: * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)
! 1678: * where the server pretends that it supports session reuse,
! 1679: * but it does not actually store any session.
! 1680: */
! 1681:
! 1682: SSL_CTX_set_session_cache_mode(ssl->ctx,
! 1683: SSL_SESS_CACHE_SERVER
! 1684: |SSL_SESS_CACHE_NO_AUTO_CLEAR
! 1685: |SSL_SESS_CACHE_NO_INTERNAL_STORE);
! 1686:
! 1687: SSL_CTX_sess_set_cache_size(ssl->ctx, 1);
! 1688:
! 1689: return NGX_OK;
! 1690: }
! 1691:
! 1692: cache_mode = SSL_SESS_CACHE_SERVER;
! 1693:
! 1694: if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {
! 1695: cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;
! 1696: }
! 1697:
! 1698: SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);
! 1699:
! 1700: if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) {
! 1701:
! 1702: if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) {
! 1703: SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache);
! 1704: }
! 1705: }
! 1706:
! 1707: SSL_CTX_set_timeout(ssl->ctx, (long) timeout);
! 1708:
! 1709: if (shm_zone) {
! 1710: SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);
! 1711: SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);
! 1712: SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);
! 1713:
! 1714: if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)
! 1715: == 0)
! 1716: {
! 1717: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 1718: "SSL_CTX_set_ex_data() failed");
! 1719: return NGX_ERROR;
! 1720: }
! 1721: }
! 1722:
! 1723: return NGX_OK;
! 1724: }
! 1725:
! 1726:
! 1727: ngx_int_t
! 1728: ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)
! 1729: {
! 1730: size_t len;
! 1731: ngx_slab_pool_t *shpool;
! 1732: ngx_ssl_session_cache_t *cache;
! 1733:
! 1734: if (data) {
! 1735: shm_zone->data = data;
! 1736: return NGX_OK;
! 1737: }
! 1738:
! 1739: if (shm_zone->shm.exists) {
! 1740: shm_zone->data = data;
! 1741: return NGX_OK;
! 1742: }
! 1743:
! 1744: shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
! 1745:
! 1746: cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));
! 1747: if (cache == NULL) {
! 1748: return NGX_ERROR;
! 1749: }
! 1750:
! 1751: shpool->data = cache;
! 1752: shm_zone->data = cache;
! 1753:
! 1754: ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,
! 1755: ngx_ssl_session_rbtree_insert_value);
! 1756:
! 1757: ngx_queue_init(&cache->expire_queue);
! 1758:
! 1759: len = sizeof(" in SSL session shared cache \"\"") + shm_zone->shm.name.len;
! 1760:
! 1761: shpool->log_ctx = ngx_slab_alloc(shpool, len);
! 1762: if (shpool->log_ctx == NULL) {
! 1763: return NGX_ERROR;
! 1764: }
! 1765:
! 1766: ngx_sprintf(shpool->log_ctx, " in SSL session shared cache \"%V\"%Z",
! 1767: &shm_zone->shm.name);
! 1768:
! 1769: return NGX_OK;
! 1770: }
! 1771:
! 1772:
! 1773: /*
! 1774: * The length of the session id is 16 bytes for SSLv2 sessions and
! 1775: * between 1 and 32 bytes for SSLv3/TLSv1, typically 32 bytes.
! 1776: * It seems that the typical length of the external ASN1 representation
! 1777: * of a session is 118 or 119 bytes for SSLv3/TSLv1.
! 1778: *
! 1779: * Thus on 32-bit platforms we allocate separately an rbtree node,
! 1780: * a session id, and an ASN1 representation, they take accordingly
! 1781: * 64, 32, and 128 bytes.
! 1782: *
! 1783: * On 64-bit platforms we allocate separately an rbtree node + session_id,
! 1784: * and an ASN1 representation, they take accordingly 128 and 128 bytes.
! 1785: *
! 1786: * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,
! 1787: * so they are outside the code locked by shared pool mutex
! 1788: */
! 1789:
! 1790: static int
! 1791: ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)
! 1792: {
! 1793: int len;
! 1794: u_char *p, *id, *cached_sess;
! 1795: uint32_t hash;
! 1796: SSL_CTX *ssl_ctx;
! 1797: ngx_shm_zone_t *shm_zone;
! 1798: ngx_connection_t *c;
! 1799: ngx_slab_pool_t *shpool;
! 1800: ngx_ssl_sess_id_t *sess_id;
! 1801: ngx_ssl_session_cache_t *cache;
! 1802: u_char buf[NGX_SSL_MAX_SESSION_SIZE];
! 1803:
! 1804: len = i2d_SSL_SESSION(sess, NULL);
! 1805:
! 1806: /* do not cache too big session */
! 1807:
! 1808: if (len > (int) NGX_SSL_MAX_SESSION_SIZE) {
! 1809: return 0;
! 1810: }
! 1811:
! 1812: p = buf;
! 1813: i2d_SSL_SESSION(sess, &p);
! 1814:
! 1815: c = ngx_ssl_get_connection(ssl_conn);
! 1816:
! 1817: ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
! 1818: shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);
! 1819:
! 1820: cache = shm_zone->data;
! 1821: shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
! 1822:
! 1823: ngx_shmtx_lock(&shpool->mutex);
! 1824:
! 1825: /* drop one or two expired sessions */
! 1826: ngx_ssl_expire_sessions(cache, shpool, 1);
! 1827:
! 1828: cached_sess = ngx_slab_alloc_locked(shpool, len);
! 1829:
! 1830: if (cached_sess == NULL) {
! 1831:
! 1832: /* drop the oldest non-expired session and try once more */
! 1833:
! 1834: ngx_ssl_expire_sessions(cache, shpool, 0);
! 1835:
! 1836: cached_sess = ngx_slab_alloc_locked(shpool, len);
! 1837:
! 1838: if (cached_sess == NULL) {
! 1839: sess_id = NULL;
! 1840: goto failed;
! 1841: }
! 1842: }
! 1843:
! 1844: sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
! 1845:
! 1846: if (sess_id == NULL) {
! 1847:
! 1848: /* drop the oldest non-expired session and try once more */
! 1849:
! 1850: ngx_ssl_expire_sessions(cache, shpool, 0);
! 1851:
! 1852: sess_id = ngx_slab_alloc_locked(shpool, sizeof(ngx_ssl_sess_id_t));
! 1853:
! 1854: if (sess_id == NULL) {
! 1855: goto failed;
! 1856: }
! 1857: }
! 1858:
! 1859: #if (NGX_PTR_SIZE == 8)
! 1860:
! 1861: id = sess_id->sess_id;
! 1862:
! 1863: #else
! 1864:
! 1865: id = ngx_slab_alloc_locked(shpool, sess->session_id_length);
! 1866:
! 1867: if (id == NULL) {
! 1868:
! 1869: /* drop the oldest non-expired session and try once more */
! 1870:
! 1871: ngx_ssl_expire_sessions(cache, shpool, 0);
! 1872:
! 1873: id = ngx_slab_alloc_locked(shpool, sess->session_id_length);
! 1874:
! 1875: if (id == NULL) {
! 1876: goto failed;
! 1877: }
! 1878: }
! 1879:
! 1880: #endif
! 1881:
! 1882: ngx_memcpy(cached_sess, buf, len);
! 1883:
! 1884: ngx_memcpy(id, sess->session_id, sess->session_id_length);
! 1885:
! 1886: hash = ngx_crc32_short(sess->session_id, sess->session_id_length);
! 1887:
! 1888: ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 1889: "ssl new session: %08XD:%d:%d",
! 1890: hash, sess->session_id_length, len);
! 1891:
! 1892: sess_id->node.key = hash;
! 1893: sess_id->node.data = (u_char) sess->session_id_length;
! 1894: sess_id->id = id;
! 1895: sess_id->len = len;
! 1896: sess_id->session = cached_sess;
! 1897:
! 1898: sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);
! 1899:
! 1900: ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);
! 1901:
! 1902: ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);
! 1903:
! 1904: ngx_shmtx_unlock(&shpool->mutex);
! 1905:
! 1906: return 0;
! 1907:
! 1908: failed:
! 1909:
! 1910: if (cached_sess) {
! 1911: ngx_slab_free_locked(shpool, cached_sess);
! 1912: }
! 1913:
! 1914: if (sess_id) {
! 1915: ngx_slab_free_locked(shpool, sess_id);
! 1916: }
! 1917:
! 1918: ngx_shmtx_unlock(&shpool->mutex);
! 1919:
! 1920: ngx_log_error(NGX_LOG_ALERT, c->log, 0,
! 1921: "could not add new SSL session to the session cache");
! 1922:
! 1923: return 0;
! 1924: }
! 1925:
! 1926:
! 1927: static ngx_ssl_session_t *
! 1928: ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn, u_char *id, int len,
! 1929: int *copy)
! 1930: {
! 1931: #if OPENSSL_VERSION_NUMBER >= 0x0090707fL
! 1932: const
! 1933: #endif
! 1934: u_char *p;
! 1935: uint32_t hash;
! 1936: ngx_int_t rc;
! 1937: ngx_shm_zone_t *shm_zone;
! 1938: ngx_slab_pool_t *shpool;
! 1939: ngx_rbtree_node_t *node, *sentinel;
! 1940: ngx_ssl_session_t *sess;
! 1941: ngx_ssl_sess_id_t *sess_id;
! 1942: ngx_ssl_session_cache_t *cache;
! 1943: u_char buf[NGX_SSL_MAX_SESSION_SIZE];
! 1944: #if (NGX_DEBUG)
! 1945: ngx_connection_t *c;
! 1946: #endif
! 1947:
! 1948: hash = ngx_crc32_short(id, (size_t) len);
! 1949: *copy = 0;
! 1950:
! 1951: #if (NGX_DEBUG)
! 1952: c = ngx_ssl_get_connection(ssl_conn);
! 1953:
! 1954: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 1955: "ssl get session: %08XD:%d", hash, len);
! 1956: #endif
! 1957:
! 1958: shm_zone = SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl_conn),
! 1959: ngx_ssl_session_cache_index);
! 1960:
! 1961: cache = shm_zone->data;
! 1962:
! 1963: sess = NULL;
! 1964:
! 1965: shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
! 1966:
! 1967: ngx_shmtx_lock(&shpool->mutex);
! 1968:
! 1969: node = cache->session_rbtree.root;
! 1970: sentinel = cache->session_rbtree.sentinel;
! 1971:
! 1972: while (node != sentinel) {
! 1973:
! 1974: if (hash < node->key) {
! 1975: node = node->left;
! 1976: continue;
! 1977: }
! 1978:
! 1979: if (hash > node->key) {
! 1980: node = node->right;
! 1981: continue;
! 1982: }
! 1983:
! 1984: /* hash == node->key */
! 1985:
! 1986: sess_id = (ngx_ssl_sess_id_t *) node;
! 1987:
! 1988: rc = ngx_memn2cmp(id, sess_id->id, (size_t) len, (size_t) node->data);
! 1989:
! 1990: if (rc == 0) {
! 1991:
! 1992: if (sess_id->expire > ngx_time()) {
! 1993: ngx_memcpy(buf, sess_id->session, sess_id->len);
! 1994:
! 1995: ngx_shmtx_unlock(&shpool->mutex);
! 1996:
! 1997: p = buf;
! 1998: sess = d2i_SSL_SESSION(NULL, &p, sess_id->len);
! 1999:
! 2000: return sess;
! 2001: }
! 2002:
! 2003: ngx_queue_remove(&sess_id->queue);
! 2004:
! 2005: ngx_rbtree_delete(&cache->session_rbtree, node);
! 2006:
! 2007: ngx_slab_free_locked(shpool, sess_id->session);
! 2008: #if (NGX_PTR_SIZE == 4)
! 2009: ngx_slab_free_locked(shpool, sess_id->id);
! 2010: #endif
! 2011: ngx_slab_free_locked(shpool, sess_id);
! 2012:
! 2013: sess = NULL;
! 2014:
! 2015: goto done;
! 2016: }
! 2017:
! 2018: node = (rc < 0) ? node->left : node->right;
! 2019: }
! 2020:
! 2021: done:
! 2022:
! 2023: ngx_shmtx_unlock(&shpool->mutex);
! 2024:
! 2025: return sess;
! 2026: }
! 2027:
! 2028:
! 2029: void
! 2030: ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
! 2031: {
! 2032: SSL_CTX_remove_session(ssl, sess);
! 2033:
! 2034: ngx_ssl_remove_session(ssl, sess);
! 2035: }
! 2036:
! 2037:
! 2038: static void
! 2039: ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)
! 2040: {
! 2041: size_t len;
! 2042: u_char *id;
! 2043: uint32_t hash;
! 2044: ngx_int_t rc;
! 2045: ngx_shm_zone_t *shm_zone;
! 2046: ngx_slab_pool_t *shpool;
! 2047: ngx_rbtree_node_t *node, *sentinel;
! 2048: ngx_ssl_sess_id_t *sess_id;
! 2049: ngx_ssl_session_cache_t *cache;
! 2050:
! 2051: shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);
! 2052:
! 2053: if (shm_zone == NULL) {
! 2054: return;
! 2055: }
! 2056:
! 2057: cache = shm_zone->data;
! 2058:
! 2059: id = sess->session_id;
! 2060: len = (size_t) sess->session_id_length;
! 2061:
! 2062: hash = ngx_crc32_short(id, len);
! 2063:
! 2064: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
! 2065: "ssl remove session: %08XD:%uz", hash, len);
! 2066:
! 2067: shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
! 2068:
! 2069: ngx_shmtx_lock(&shpool->mutex);
! 2070:
! 2071: node = cache->session_rbtree.root;
! 2072: sentinel = cache->session_rbtree.sentinel;
! 2073:
! 2074: while (node != sentinel) {
! 2075:
! 2076: if (hash < node->key) {
! 2077: node = node->left;
! 2078: continue;
! 2079: }
! 2080:
! 2081: if (hash > node->key) {
! 2082: node = node->right;
! 2083: continue;
! 2084: }
! 2085:
! 2086: /* hash == node->key */
! 2087:
! 2088: sess_id = (ngx_ssl_sess_id_t *) node;
! 2089:
! 2090: rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);
! 2091:
! 2092: if (rc == 0) {
! 2093:
! 2094: ngx_queue_remove(&sess_id->queue);
! 2095:
! 2096: ngx_rbtree_delete(&cache->session_rbtree, node);
! 2097:
! 2098: ngx_slab_free_locked(shpool, sess_id->session);
! 2099: #if (NGX_PTR_SIZE == 4)
! 2100: ngx_slab_free_locked(shpool, sess_id->id);
! 2101: #endif
! 2102: ngx_slab_free_locked(shpool, sess_id);
! 2103:
! 2104: goto done;
! 2105: }
! 2106:
! 2107: node = (rc < 0) ? node->left : node->right;
! 2108: }
! 2109:
! 2110: done:
! 2111:
! 2112: ngx_shmtx_unlock(&shpool->mutex);
! 2113: }
! 2114:
! 2115:
! 2116: static void
! 2117: ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
! 2118: ngx_slab_pool_t *shpool, ngx_uint_t n)
! 2119: {
! 2120: time_t now;
! 2121: ngx_queue_t *q;
! 2122: ngx_ssl_sess_id_t *sess_id;
! 2123:
! 2124: now = ngx_time();
! 2125:
! 2126: while (n < 3) {
! 2127:
! 2128: if (ngx_queue_empty(&cache->expire_queue)) {
! 2129: return;
! 2130: }
! 2131:
! 2132: q = ngx_queue_last(&cache->expire_queue);
! 2133:
! 2134: sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue);
! 2135:
! 2136: if (n++ != 0 && sess_id->expire > now) {
! 2137: return;
! 2138: }
! 2139:
! 2140: ngx_queue_remove(q);
! 2141:
! 2142: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
! 2143: "expire session: %08Xi", sess_id->node.key);
! 2144:
! 2145: ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);
! 2146:
! 2147: ngx_slab_free_locked(shpool, sess_id->session);
! 2148: #if (NGX_PTR_SIZE == 4)
! 2149: ngx_slab_free_locked(shpool, sess_id->id);
! 2150: #endif
! 2151: ngx_slab_free_locked(shpool, sess_id);
! 2152: }
! 2153: }
! 2154:
! 2155:
! 2156: static void
! 2157: ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,
! 2158: ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
! 2159: {
! 2160: ngx_rbtree_node_t **p;
! 2161: ngx_ssl_sess_id_t *sess_id, *sess_id_temp;
! 2162:
! 2163: for ( ;; ) {
! 2164:
! 2165: if (node->key < temp->key) {
! 2166:
! 2167: p = &temp->left;
! 2168:
! 2169: } else if (node->key > temp->key) {
! 2170:
! 2171: p = &temp->right;
! 2172:
! 2173: } else { /* node->key == temp->key */
! 2174:
! 2175: sess_id = (ngx_ssl_sess_id_t *) node;
! 2176: sess_id_temp = (ngx_ssl_sess_id_t *) temp;
! 2177:
! 2178: p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id,
! 2179: (size_t) node->data, (size_t) temp->data)
! 2180: < 0) ? &temp->left : &temp->right;
! 2181: }
! 2182:
! 2183: if (*p == sentinel) {
! 2184: break;
! 2185: }
! 2186:
! 2187: temp = *p;
! 2188: }
! 2189:
! 2190: *p = node;
! 2191: node->parent = temp;
! 2192: node->left = sentinel;
! 2193: node->right = sentinel;
! 2194: ngx_rbt_red(node);
! 2195: }
! 2196:
! 2197:
! 2198: void
! 2199: ngx_ssl_cleanup_ctx(void *data)
! 2200: {
! 2201: ngx_ssl_t *ssl = data;
! 2202:
! 2203: SSL_CTX_free(ssl->ctx);
! 2204: }
! 2205:
! 2206:
! 2207: ngx_int_t
! 2208: ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2209: {
! 2210: s->data = (u_char *) SSL_get_version(c->ssl->connection);
! 2211: return NGX_OK;
! 2212: }
! 2213:
! 2214:
! 2215: ngx_int_t
! 2216: ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2217: {
! 2218: s->data = (u_char *) SSL_get_cipher_name(c->ssl->connection);
! 2219: return NGX_OK;
! 2220: }
! 2221:
! 2222:
! 2223: ngx_int_t
! 2224: ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2225: {
! 2226: int len;
! 2227: u_char *p, *buf;
! 2228: SSL_SESSION *sess;
! 2229:
! 2230: sess = SSL_get0_session(c->ssl->connection);
! 2231:
! 2232: len = i2d_SSL_SESSION(sess, NULL);
! 2233:
! 2234: buf = ngx_alloc(len, c->log);
! 2235: if (buf == NULL) {
! 2236: return NGX_ERROR;
! 2237: }
! 2238:
! 2239: s->len = 2 * len;
! 2240: s->data = ngx_pnalloc(pool, 2 * len);
! 2241: if (s->data == NULL) {
! 2242: ngx_free(buf);
! 2243: return NGX_ERROR;
! 2244: }
! 2245:
! 2246: p = buf;
! 2247: i2d_SSL_SESSION(sess, &p);
! 2248:
! 2249: ngx_hex_dump(s->data, buf, len);
! 2250:
! 2251: ngx_free(buf);
! 2252:
! 2253: return NGX_OK;
! 2254: }
! 2255:
! 2256:
! 2257: ngx_int_t
! 2258: ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2259: {
! 2260: size_t len;
! 2261: BIO *bio;
! 2262: X509 *cert;
! 2263:
! 2264: s->len = 0;
! 2265:
! 2266: cert = SSL_get_peer_certificate(c->ssl->connection);
! 2267: if (cert == NULL) {
! 2268: return NGX_OK;
! 2269: }
! 2270:
! 2271: bio = BIO_new(BIO_s_mem());
! 2272: if (bio == NULL) {
! 2273: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "BIO_new() failed");
! 2274: X509_free(cert);
! 2275: return NGX_ERROR;
! 2276: }
! 2277:
! 2278: if (PEM_write_bio_X509(bio, cert) == 0) {
! 2279: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "PEM_write_bio_X509() failed");
! 2280: goto failed;
! 2281: }
! 2282:
! 2283: len = BIO_pending(bio);
! 2284: s->len = len;
! 2285:
! 2286: s->data = ngx_pnalloc(pool, len);
! 2287: if (s->data == NULL) {
! 2288: goto failed;
! 2289: }
! 2290:
! 2291: BIO_read(bio, s->data, len);
! 2292:
! 2293: BIO_free(bio);
! 2294: X509_free(cert);
! 2295:
! 2296: return NGX_OK;
! 2297:
! 2298: failed:
! 2299:
! 2300: BIO_free(bio);
! 2301: X509_free(cert);
! 2302:
! 2303: return NGX_ERROR;
! 2304: }
! 2305:
! 2306:
! 2307: ngx_int_t
! 2308: ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2309: {
! 2310: u_char *p;
! 2311: size_t len;
! 2312: ngx_uint_t i;
! 2313: ngx_str_t cert;
! 2314:
! 2315: if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {
! 2316: return NGX_ERROR;
! 2317: }
! 2318:
! 2319: if (cert.len == 0) {
! 2320: s->len = 0;
! 2321: return NGX_OK;
! 2322: }
! 2323:
! 2324: len = cert.len - 1;
! 2325:
! 2326: for (i = 0; i < cert.len - 1; i++) {
! 2327: if (cert.data[i] == LF) {
! 2328: len++;
! 2329: }
! 2330: }
! 2331:
! 2332: s->len = len;
! 2333: s->data = ngx_pnalloc(pool, len);
! 2334: if (s->data == NULL) {
! 2335: return NGX_ERROR;
! 2336: }
! 2337:
! 2338: p = s->data;
! 2339:
! 2340: for (i = 0; i < cert.len - 1; i++) {
! 2341: *p++ = cert.data[i];
! 2342: if (cert.data[i] == LF) {
! 2343: *p++ = '\t';
! 2344: }
! 2345: }
! 2346:
! 2347: return NGX_OK;
! 2348: }
! 2349:
! 2350:
! 2351: ngx_int_t
! 2352: ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2353: {
! 2354: char *p;
! 2355: size_t len;
! 2356: X509 *cert;
! 2357: X509_NAME *name;
! 2358:
! 2359: s->len = 0;
! 2360:
! 2361: cert = SSL_get_peer_certificate(c->ssl->connection);
! 2362: if (cert == NULL) {
! 2363: return NGX_OK;
! 2364: }
! 2365:
! 2366: name = X509_get_subject_name(cert);
! 2367: if (name == NULL) {
! 2368: X509_free(cert);
! 2369: return NGX_ERROR;
! 2370: }
! 2371:
! 2372: p = X509_NAME_oneline(name, NULL, 0);
! 2373:
! 2374: for (len = 0; p[len]; len++) { /* void */ }
! 2375:
! 2376: s->len = len;
! 2377: s->data = ngx_pnalloc(pool, len);
! 2378: if (s->data == NULL) {
! 2379: OPENSSL_free(p);
! 2380: X509_free(cert);
! 2381: return NGX_ERROR;
! 2382: }
! 2383:
! 2384: ngx_memcpy(s->data, p, len);
! 2385:
! 2386: OPENSSL_free(p);
! 2387: X509_free(cert);
! 2388:
! 2389: return NGX_OK;
! 2390: }
! 2391:
! 2392:
! 2393: ngx_int_t
! 2394: ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2395: {
! 2396: char *p;
! 2397: size_t len;
! 2398: X509 *cert;
! 2399: X509_NAME *name;
! 2400:
! 2401: s->len = 0;
! 2402:
! 2403: cert = SSL_get_peer_certificate(c->ssl->connection);
! 2404: if (cert == NULL) {
! 2405: return NGX_OK;
! 2406: }
! 2407:
! 2408: name = X509_get_issuer_name(cert);
! 2409: if (name == NULL) {
! 2410: X509_free(cert);
! 2411: return NGX_ERROR;
! 2412: }
! 2413:
! 2414: p = X509_NAME_oneline(name, NULL, 0);
! 2415:
! 2416: for (len = 0; p[len]; len++) { /* void */ }
! 2417:
! 2418: s->len = len;
! 2419: s->data = ngx_pnalloc(pool, len);
! 2420: if (s->data == NULL) {
! 2421: OPENSSL_free(p);
! 2422: X509_free(cert);
! 2423: return NGX_ERROR;
! 2424: }
! 2425:
! 2426: ngx_memcpy(s->data, p, len);
! 2427:
! 2428: OPENSSL_free(p);
! 2429: X509_free(cert);
! 2430:
! 2431: return NGX_OK;
! 2432: }
! 2433:
! 2434:
! 2435: ngx_int_t
! 2436: ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2437: {
! 2438: size_t len;
! 2439: X509 *cert;
! 2440: BIO *bio;
! 2441:
! 2442: s->len = 0;
! 2443:
! 2444: cert = SSL_get_peer_certificate(c->ssl->connection);
! 2445: if (cert == NULL) {
! 2446: return NGX_OK;
! 2447: }
! 2448:
! 2449: bio = BIO_new(BIO_s_mem());
! 2450: if (bio == NULL) {
! 2451: X509_free(cert);
! 2452: return NGX_ERROR;
! 2453: }
! 2454:
! 2455: i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert));
! 2456: len = BIO_pending(bio);
! 2457:
! 2458: s->len = len;
! 2459: s->data = ngx_pnalloc(pool, len);
! 2460: if (s->data == NULL) {
! 2461: BIO_free(bio);
! 2462: X509_free(cert);
! 2463: return NGX_ERROR;
! 2464: }
! 2465:
! 2466: BIO_read(bio, s->data, len);
! 2467: BIO_free(bio);
! 2468: X509_free(cert);
! 2469:
! 2470: return NGX_OK;
! 2471: }
! 2472:
! 2473:
! 2474: ngx_int_t
! 2475: ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)
! 2476: {
! 2477: X509 *cert;
! 2478:
! 2479: if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {
! 2480: ngx_str_set(s, "FAILED");
! 2481: return NGX_OK;
! 2482: }
! 2483:
! 2484: cert = SSL_get_peer_certificate(c->ssl->connection);
! 2485:
! 2486: if (cert) {
! 2487: ngx_str_set(s, "SUCCESS");
! 2488:
! 2489: } else {
! 2490: ngx_str_set(s, "NONE");
! 2491: }
! 2492:
! 2493: X509_free(cert);
! 2494:
! 2495: return NGX_OK;
! 2496: }
! 2497:
! 2498:
! 2499: static void *
! 2500: ngx_openssl_create_conf(ngx_cycle_t *cycle)
! 2501: {
! 2502: ngx_openssl_conf_t *oscf;
! 2503:
! 2504: oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t));
! 2505: if (oscf == NULL) {
! 2506: return NULL;
! 2507: }
! 2508:
! 2509: /*
! 2510: * set by ngx_pcalloc():
! 2511: *
! 2512: * oscf->engine = 0;
! 2513: */
! 2514:
! 2515: return oscf;
! 2516: }
! 2517:
! 2518:
! 2519: static char *
! 2520: ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
! 2521: {
! 2522: ngx_openssl_conf_t *oscf = conf;
! 2523:
! 2524: ENGINE *engine;
! 2525: ngx_str_t *value;
! 2526:
! 2527: if (oscf->engine) {
! 2528: return "is duplicate";
! 2529: }
! 2530:
! 2531: oscf->engine = 1;
! 2532:
! 2533: value = cf->args->elts;
! 2534:
! 2535: engine = ENGINE_by_id((const char *) value[1].data);
! 2536:
! 2537: if (engine == NULL) {
! 2538: ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
! 2539: "ENGINE_by_id(\"%V\") failed", &value[1]);
! 2540: return NGX_CONF_ERROR;
! 2541: }
! 2542:
! 2543: if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) {
! 2544: ngx_ssl_error(NGX_LOG_WARN, cf->log, 0,
! 2545: "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed",
! 2546: &value[1]);
! 2547:
! 2548: ENGINE_free(engine);
! 2549:
! 2550: return NGX_CONF_ERROR;
! 2551: }
! 2552:
! 2553: ENGINE_free(engine);
! 2554:
! 2555: return NGX_CONF_OK;
! 2556: }
! 2557:
! 2558:
! 2559: static void
! 2560: ngx_openssl_exit(ngx_cycle_t *cycle)
! 2561: {
! 2562: EVP_cleanup();
! 2563: ENGINE_cleanup();
! 2564: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>