comparison src/event/ngx_event_openssl.c @ 5425:1356a3b96924

SSL: added ability to set keys used for Session Tickets (RFC5077). In order to support key rollover, ssl_session_ticket_key can be defined multiple times. The first key will be used to issue and resume Session Tickets, while the rest will be used only to resume them. ssl_session_ticket_key session_tickets/current.key; ssl_session_ticket_key session_tickets/prev-1h.key; ssl_session_ticket_key session_tickets/prev-2h.key; Please note that nginx supports Session Tickets even without explicit configuration of the keys and this feature should be only used in setups where SSL traffic is distributed across multiple nginx servers. Signed-off-by: Piotr Sikora <piotr@cloudflare.com>
author Piotr Sikora <piotr@cloudflare.com>
date Fri, 11 Oct 2013 16:05:24 -0700
parents 767aa37f12de
children 9868c72f6f43
comparison
equal deleted inserted replaced
5424:767aa37f12de 5425:1356a3b96924
35 static void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess); 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, 36 static void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,
37 ngx_slab_pool_t *shpool, ngx_uint_t n); 37 ngx_slab_pool_t *shpool, ngx_uint_t n);
38 static void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp, 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); 39 ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
40
41 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
42 static int ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
43 unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
44 HMAC_CTX *hctx, int enc);
45 #endif
40 46
41 static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); 47 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); 48 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); 49 static void ngx_openssl_exit(ngx_cycle_t *cycle);
44 50
80 86
81 87
82 int ngx_ssl_connection_index; 88 int ngx_ssl_connection_index;
83 int ngx_ssl_server_conf_index; 89 int ngx_ssl_server_conf_index;
84 int ngx_ssl_session_cache_index; 90 int ngx_ssl_session_cache_index;
91 int ngx_ssl_session_ticket_keys_index;
85 int ngx_ssl_certificate_index; 92 int ngx_ssl_certificate_index;
86 int ngx_ssl_stapling_index; 93 int ngx_ssl_stapling_index;
87 94
88 95
89 ngx_int_t 96 ngx_int_t
132 } 139 }
133 140
134 ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, 141 ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,
135 NULL); 142 NULL);
136 if (ngx_ssl_session_cache_index == -1) { 143 if (ngx_ssl_session_cache_index == -1) {
144 ngx_ssl_error(NGX_LOG_ALERT, log, 0,
145 "SSL_CTX_get_ex_new_index() failed");
146 return NGX_ERROR;
147 }
148
149 ngx_ssl_session_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL,
150 NULL, NULL);
151 if (ngx_ssl_session_ticket_keys_index == -1) {
137 ngx_ssl_error(NGX_LOG_ALERT, log, 0, 152 ngx_ssl_error(NGX_LOG_ALERT, log, 0,
138 "SSL_CTX_get_ex_new_index() failed"); 153 "SSL_CTX_get_ex_new_index() failed");
139 return NGX_ERROR; 154 return NGX_ERROR;
140 } 155 }
141 156
2238 node->right = sentinel; 2253 node->right = sentinel;
2239 ngx_rbt_red(node); 2254 ngx_rbt_red(node);
2240 } 2255 }
2241 2256
2242 2257
2258 #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
2259
2260 ngx_int_t
2261 ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
2262 {
2263 u_char buf[48];
2264 ssize_t n;
2265 ngx_str_t *path;
2266 ngx_file_t file;
2267 ngx_uint_t i;
2268 ngx_array_t *keys;
2269 ngx_file_info_t fi;
2270 ngx_ssl_session_ticket_key_t *key;
2271
2272 if (paths == NULL) {
2273 return NGX_OK;
2274 }
2275
2276 keys = ngx_array_create(cf->pool, paths->nelts,
2277 sizeof(ngx_ssl_session_ticket_key_t));
2278 if (keys == NULL) {
2279 return NGX_ERROR;
2280 }
2281
2282 path = paths->elts;
2283 for (i = 0; i < paths->nelts; i++) {
2284
2285 if (ngx_conf_full_name(cf->cycle, &path[i], 1) != NGX_OK) {
2286 return NGX_ERROR;
2287 }
2288
2289 ngx_memzero(&file, sizeof(ngx_file_t));
2290 file.name = path[i];
2291 file.log = cf->log;
2292
2293 file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, 0, 0);
2294 if (file.fd == NGX_INVALID_FILE) {
2295 ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
2296 ngx_open_file_n " \"%V\" failed", &file.name);
2297 return NGX_ERROR;
2298 }
2299
2300 if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {
2301 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
2302 ngx_fd_info_n " \"%V\" failed", &file.name);
2303 goto failed;
2304 }
2305
2306 if (ngx_file_size(&fi) != 48) {
2307 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2308 "\"%V\" must be 48 bytes", &file.name);
2309 goto failed;
2310 }
2311
2312 n = ngx_read_file(&file, buf, 48, 0);
2313
2314 if (n == NGX_ERROR) {
2315 ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,
2316 ngx_read_file_n " \"%V\" failed", &file.name);
2317 goto failed;
2318 }
2319
2320 if (n != 48) {
2321 ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,
2322 ngx_read_file_n " \"%V\" returned only "
2323 "%z bytes instead of 48", &file.name, n);
2324 goto failed;
2325 }
2326
2327 key = ngx_array_push(keys);
2328 if (key == NULL) {
2329 goto failed;
2330 }
2331
2332 ngx_memcpy(key->name, buf, 16);
2333 ngx_memcpy(key->aes_key, buf + 16, 16);
2334 ngx_memcpy(key->hmac_key, buf + 32, 16);
2335
2336 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
2337 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
2338 ngx_close_file_n " \"%V\" failed", &file.name);
2339 }
2340 }
2341
2342 if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_ticket_keys_index, keys)
2343 == 0)
2344 {
2345 ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
2346 "SSL_CTX_set_ex_data() failed");
2347 return NGX_ERROR;
2348 }
2349
2350 if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx,
2351 ngx_ssl_session_ticket_key_callback)
2352 == 0)
2353 {
2354 ngx_log_error(NGX_LOG_WARN, cf->log, 0,
2355 "nginx was built with Session Tickets support, however, "
2356 "now it is linked dynamically to an OpenSSL library "
2357 "which has no tlsext support, therefore Session Tickets "
2358 "are not available");
2359 }
2360
2361 return NGX_OK;
2362
2363 failed:
2364
2365 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
2366 ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
2367 ngx_close_file_n " \"%V\" failed", &file.name);
2368 }
2369
2370 return NGX_ERROR;
2371 }
2372
2373
2374 #ifdef OPENSSL_NO_SHA256
2375 #define ngx_ssl_session_ticket_md EVP_sha1
2376 #else
2377 #define ngx_ssl_session_ticket_md EVP_sha256
2378 #endif
2379
2380
2381 static int
2382 ngx_ssl_session_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,
2383 unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,
2384 HMAC_CTX *hctx, int enc)
2385 {
2386 SSL_CTX *ssl_ctx;
2387 ngx_uint_t i;
2388 ngx_array_t *keys;
2389 ngx_ssl_session_ticket_key_t *key;
2390 #if (NGX_DEBUG)
2391 u_char buf[32];
2392 ngx_connection_t *c;
2393 #endif
2394
2395 ssl_ctx = SSL_get_SSL_CTX(ssl_conn);
2396
2397 keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_ticket_keys_index);
2398 if (keys == NULL) {
2399 return -1;
2400 }
2401
2402 key = keys->elts;
2403
2404 #if (NGX_DEBUG)
2405 c = ngx_ssl_get_connection(ssl_conn);
2406 #endif
2407
2408 if (enc == 1) {
2409 /* encrypt session ticket */
2410
2411 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
2412 "ssl session ticket encrypt, key: \"%*s\" (%s session)",
2413 ngx_hex_dump(buf, key[0].name, 16) - buf, buf,
2414 SSL_session_reused(ssl_conn) ? "reused" : "new");
2415
2416 RAND_pseudo_bytes(iv, 16);
2417 EVP_EncryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[0].aes_key, iv);
2418 HMAC_Init_ex(hctx, key[0].hmac_key, 16,
2419 ngx_ssl_session_ticket_md(), NULL);
2420 memcpy(name, key[0].name, 16);
2421
2422 return 0;
2423
2424 } else {
2425 /* decrypt session ticket */
2426
2427 for (i = 0; i < keys->nelts; i++) {
2428 if (ngx_memcmp(name, key[i].name, 16) == 0) {
2429 goto found;
2430 }
2431 }
2432
2433 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
2434 "ssl session ticket decrypt, key: \"%*s\" not found",
2435 ngx_hex_dump(buf, name, 16) - buf, buf);
2436
2437 return 0;
2438
2439 found:
2440
2441 ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
2442 "ssl session ticket decrypt, key: \"%*s\"%s",
2443 ngx_hex_dump(buf, key[i].name, 16) - buf, buf,
2444 (i == 0) ? " (default)" : "");
2445
2446 HMAC_Init_ex(hctx, key[i].hmac_key, 16,
2447 ngx_ssl_session_ticket_md(), NULL);
2448 EVP_DecryptInit_ex(ectx, EVP_aes_128_cbc(), NULL, key[i].aes_key, iv);
2449
2450 return (i == 0) ? 1 : 2 /* renew */;
2451 }
2452 }
2453
2454 #else
2455
2456 ngx_int_t
2457 ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)
2458 {
2459 if (paths) {
2460 ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
2461 "\"ssl_session_ticket_keys\" ignored, not supported");
2462 }
2463
2464 return NGX_OK;
2465 }
2466
2467 #endif
2468
2469
2243 void 2470 void
2244 ngx_ssl_cleanup_ctx(void *data) 2471 ngx_ssl_cleanup_ctx(void *data)
2245 { 2472 {
2246 ngx_ssl_t *ssl = data; 2473 ngx_ssl_t *ssl = data;
2247 2474