Annotation of embedaddon/nginx/src/event/ngx_event_openssl_stapling.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (C) Maxim Dounin
! 4: * Copyright (C) Nginx, Inc.
! 5: */
! 6:
! 7:
! 8: #include <ngx_config.h>
! 9: #include <ngx_core.h>
! 10: #include <ngx_event.h>
! 11: #include <ngx_event_connect.h>
! 12:
! 13:
! 14: #ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB
! 15:
! 16:
! 17: typedef struct {
! 18: ngx_str_t staple;
! 19: ngx_msec_t timeout;
! 20:
! 21: ngx_resolver_t *resolver;
! 22: ngx_msec_t resolver_timeout;
! 23:
! 24: ngx_addr_t *addrs;
! 25: ngx_str_t host;
! 26: ngx_str_t uri;
! 27: in_port_t port;
! 28:
! 29: SSL_CTX *ssl_ctx;
! 30:
! 31: X509 *cert;
! 32: X509 *issuer;
! 33:
! 34: time_t valid;
! 35:
! 36: unsigned verify:1;
! 37: unsigned loading:1;
! 38: } ngx_ssl_stapling_t;
! 39:
! 40:
! 41: typedef struct ngx_ssl_ocsp_ctx_s ngx_ssl_ocsp_ctx_t;
! 42:
! 43: struct ngx_ssl_ocsp_ctx_s {
! 44: X509 *cert;
! 45: X509 *issuer;
! 46:
! 47: ngx_uint_t naddrs;
! 48:
! 49: ngx_addr_t *addrs;
! 50: ngx_str_t host;
! 51: ngx_str_t uri;
! 52: in_port_t port;
! 53:
! 54: ngx_resolver_t *resolver;
! 55: ngx_msec_t resolver_timeout;
! 56:
! 57: ngx_msec_t timeout;
! 58:
! 59: void (*handler)(ngx_ssl_ocsp_ctx_t *r);
! 60: void *data;
! 61:
! 62: ngx_buf_t *request;
! 63: ngx_buf_t *response;
! 64: ngx_peer_connection_t peer;
! 65:
! 66: ngx_int_t (*process)(ngx_ssl_ocsp_ctx_t *r);
! 67:
! 68: ngx_uint_t state;
! 69:
! 70: ngx_uint_t code;
! 71: ngx_uint_t count;
! 72:
! 73: ngx_uint_t done;
! 74:
! 75: u_char *header_name_start;
! 76: u_char *header_name_end;
! 77: u_char *header_start;
! 78: u_char *header_end;
! 79:
! 80: ngx_pool_t *pool;
! 81: ngx_log_t *log;
! 82: };
! 83:
! 84:
! 85: static ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,
! 86: ngx_str_t *file);
! 87: static ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl);
! 88: static ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,
! 89: ngx_str_t *responder);
! 90:
! 91: static int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,
! 92: void *data);
! 93: static void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);
! 94: static void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);
! 95:
! 96: static void ngx_ssl_stapling_cleanup(void *data);
! 97:
! 98: static ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(void);
! 99: static void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);
! 100: static void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);
! 101: static void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);
! 102: static void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);
! 103: static void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);
! 104: static void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);
! 105: static void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);
! 106:
! 107: static ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);
! 108: static ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);
! 109: static ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);
! 110: static ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);
! 111: static ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);
! 112: static ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);
! 113:
! 114: static u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);
! 115:
! 116:
! 117: ngx_int_t
! 118: ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
! 119: ngx_str_t *responder, ngx_uint_t verify)
! 120: {
! 121: ngx_int_t rc;
! 122: ngx_pool_cleanup_t *cln;
! 123: ngx_ssl_stapling_t *staple;
! 124:
! 125: staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));
! 126: if (staple == NULL) {
! 127: return NGX_ERROR;
! 128: }
! 129:
! 130: cln = ngx_pool_cleanup_add(cf->pool, 0);
! 131: if (cln == NULL) {
! 132: return NGX_ERROR;
! 133: }
! 134:
! 135: cln->handler = ngx_ssl_stapling_cleanup;
! 136: cln->data = staple;
! 137:
! 138: if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_stapling_index, staple)
! 139: == 0)
! 140: {
! 141: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 142: "SSL_CTX_set_ex_data() failed");
! 143: return NGX_ERROR;
! 144: }
! 145:
! 146: staple->ssl_ctx = ssl->ctx;
! 147: staple->timeout = 60000;
! 148: staple->verify = verify;
! 149:
! 150: if (file->len) {
! 151: /* use OCSP response from the file */
! 152:
! 153: if (ngx_ssl_stapling_file(cf, ssl, file) != NGX_OK) {
! 154: return NGX_ERROR;
! 155: }
! 156:
! 157: goto done;
! 158: }
! 159:
! 160: rc = ngx_ssl_stapling_issuer(cf, ssl);
! 161:
! 162: if (rc == NGX_DECLINED) {
! 163: return NGX_OK;
! 164: }
! 165:
! 166: if (rc != NGX_OK) {
! 167: return NGX_ERROR;
! 168: }
! 169:
! 170: rc = ngx_ssl_stapling_responder(cf, ssl, responder);
! 171:
! 172: if (rc == NGX_DECLINED) {
! 173: return NGX_OK;
! 174: }
! 175:
! 176: if (rc != NGX_OK) {
! 177: return NGX_ERROR;
! 178: }
! 179:
! 180: done:
! 181:
! 182: SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);
! 183: SSL_CTX_set_tlsext_status_arg(ssl->ctx, staple);
! 184:
! 185: return NGX_OK;
! 186: }
! 187:
! 188:
! 189: static ngx_int_t
! 190: ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)
! 191: {
! 192: BIO *bio;
! 193: int len;
! 194: u_char *p, *buf;
! 195: OCSP_RESPONSE *response;
! 196: ngx_ssl_stapling_t *staple;
! 197:
! 198: staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
! 199:
! 200: if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {
! 201: return NGX_ERROR;
! 202: }
! 203:
! 204: bio = BIO_new_file((char *) file->data, "r");
! 205: if (bio == NULL) {
! 206: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 207: "BIO_new_file(\"%s\") failed", file->data);
! 208: return NGX_ERROR;
! 209: }
! 210:
! 211: response = d2i_OCSP_RESPONSE_bio(bio, NULL);
! 212: if (response == NULL) {
! 213: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 214: "d2i_OCSP_RESPONSE_bio(\"%s\") failed", file->data);
! 215: BIO_free(bio);
! 216: return NGX_ERROR;
! 217: }
! 218:
! 219: len = i2d_OCSP_RESPONSE(response, NULL);
! 220: if (len <= 0) {
! 221: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 222: "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
! 223: goto failed;
! 224: }
! 225:
! 226: buf = ngx_alloc(len, ssl->log);
! 227: if (buf == NULL) {
! 228: goto failed;
! 229: }
! 230:
! 231: p = buf;
! 232: len = i2d_OCSP_RESPONSE(response, &p);
! 233: if (len <= 0) {
! 234: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 235: "i2d_OCSP_RESPONSE(\"%s\") failed", file->data);
! 236: ngx_free(buf);
! 237: goto failed;
! 238: }
! 239:
! 240: OCSP_RESPONSE_free(response);
! 241: BIO_free(bio);
! 242:
! 243: staple->staple.data = buf;
! 244: staple->staple.len = len;
! 245:
! 246: return NGX_OK;
! 247:
! 248: failed:
! 249:
! 250: OCSP_RESPONSE_free(response);
! 251: BIO_free(bio);
! 252:
! 253: return NGX_ERROR;
! 254: }
! 255:
! 256:
! 257: static ngx_int_t
! 258: ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl)
! 259: {
! 260: int i, n, rc;
! 261: X509 *cert, *issuer;
! 262: X509_STORE *store;
! 263: X509_STORE_CTX *store_ctx;
! 264: STACK_OF(X509) *chain;
! 265: ngx_ssl_stapling_t *staple;
! 266:
! 267: staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
! 268: cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);
! 269:
! 270: #if OPENSSL_VERSION_NUMBER >= 0x10001000L
! 271: SSL_CTX_get_extra_chain_certs(ssl->ctx, &chain);
! 272: #else
! 273: chain = ssl->ctx->extra_certs;
! 274: #endif
! 275:
! 276: n = sk_X509_num(chain);
! 277:
! 278: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
! 279: "SSL get issuer: %d extra certs", n);
! 280:
! 281: for (i = 0; i < n; i++) {
! 282: issuer = sk_X509_value(chain, i);
! 283: if (X509_check_issued(issuer, cert) == X509_V_OK) {
! 284: CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);
! 285:
! 286: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
! 287: "SSL get issuer: found %p in extra certs", issuer);
! 288:
! 289: staple->cert = cert;
! 290: staple->issuer = issuer;
! 291:
! 292: return NGX_OK;
! 293: }
! 294: }
! 295:
! 296: store = SSL_CTX_get_cert_store(ssl->ctx);
! 297: if (store == NULL) {
! 298: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 299: "SSL_CTX_get_cert_store() failed");
! 300: return NGX_ERROR;
! 301: }
! 302:
! 303: store_ctx = X509_STORE_CTX_new();
! 304: if (store_ctx == NULL) {
! 305: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 306: "X509_STORE_CTX_new() failed");
! 307: return NGX_ERROR;
! 308: }
! 309:
! 310: if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {
! 311: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 312: "X509_STORE_CTX_init() failed");
! 313: return NGX_ERROR;
! 314: }
! 315:
! 316: rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);
! 317:
! 318: if (rc == -1) {
! 319: ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,
! 320: "X509_STORE_CTX_get1_issuer() failed");
! 321: X509_STORE_CTX_free(store_ctx);
! 322: return NGX_ERROR;
! 323: }
! 324:
! 325: if (rc == 0) {
! 326: ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
! 327: "\"ssl_stapling\" ignored, issuer certificate not found");
! 328: X509_STORE_CTX_free(store_ctx);
! 329: return NGX_DECLINED;
! 330: }
! 331:
! 332: X509_STORE_CTX_free(store_ctx);
! 333:
! 334: ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,
! 335: "SSL get issuer: found %p in cert store", issuer);
! 336:
! 337: staple->cert = cert;
! 338: staple->issuer = issuer;
! 339:
! 340: return NGX_OK;
! 341: }
! 342:
! 343:
! 344: static ngx_int_t
! 345: ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder)
! 346: {
! 347: ngx_url_t u;
! 348: char *s;
! 349: ngx_ssl_stapling_t *staple;
! 350: STACK_OF(OPENSSL_STRING) *aia;
! 351:
! 352: staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
! 353:
! 354: if (responder->len == 0) {
! 355:
! 356: /* extract OCSP responder URL from certificate */
! 357:
! 358: aia = X509_get1_ocsp(staple->cert);
! 359: if (aia == NULL) {
! 360: ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
! 361: "\"ssl_stapling\" ignored, "
! 362: "no OCSP responder URL in the certificate");
! 363: return NGX_DECLINED;
! 364: }
! 365:
! 366: #if OPENSSL_VERSION_NUMBER >= 0x10000000L
! 367: s = sk_OPENSSL_STRING_value(aia, 0);
! 368: #else
! 369: s = sk_value(aia, 0);
! 370: #endif
! 371: if (s == NULL) {
! 372: ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
! 373: "\"ssl_stapling\" ignored, "
! 374: "no OCSP responder URL in the certificate");
! 375: X509_email_free(aia);
! 376: return NGX_DECLINED;
! 377: }
! 378:
! 379: responder->len = ngx_strlen(s);
! 380: responder->data = ngx_palloc(cf->pool, responder->len);
! 381: if (responder->data == NULL) {
! 382: X509_email_free(aia);
! 383: return NGX_ERROR;
! 384: }
! 385:
! 386: ngx_memcpy(responder->data, s, responder->len);
! 387: X509_email_free(aia);
! 388: }
! 389:
! 390: ngx_memzero(&u, sizeof(ngx_url_t));
! 391:
! 392: u.url = *responder;
! 393: u.default_port = 80;
! 394: u.uri_part = 1;
! 395:
! 396: if (u.url.len > 7
! 397: && ngx_strncasecmp(u.url.data, (u_char *) "http://", 7) == 0)
! 398: {
! 399: u.url.len -= 7;
! 400: u.url.data += 7;
! 401:
! 402: } else {
! 403: ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
! 404: "\"ssl_stapling\" ignored, "
! 405: "invalid URL prefix in OCSP responder \"%V\"", &u.url);
! 406: return NGX_DECLINED;
! 407: }
! 408:
! 409: if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
! 410: if (u.err) {
! 411: ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
! 412: "\"ssl_stapling\" ignored, "
! 413: "%s in OCSP responder \"%V\"", u.err, &u.url);
! 414: return NGX_DECLINED;
! 415: }
! 416:
! 417: return NGX_ERROR;
! 418: }
! 419:
! 420: staple->addrs = u.addrs;
! 421: staple->host = u.host;
! 422: staple->uri = u.uri;
! 423: staple->port = u.port;
! 424:
! 425: if (staple->uri.len == 0) {
! 426: ngx_str_set(&staple->uri, "/");
! 427: }
! 428:
! 429: return NGX_OK;
! 430: }
! 431:
! 432:
! 433: ngx_int_t
! 434: ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
! 435: ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
! 436: {
! 437: ngx_ssl_stapling_t *staple;
! 438:
! 439: staple = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_stapling_index);
! 440:
! 441: staple->resolver = resolver;
! 442: staple->resolver_timeout = resolver_timeout;
! 443:
! 444: return NGX_OK;
! 445: }
! 446:
! 447:
! 448: static int
! 449: ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
! 450: {
! 451: int rc;
! 452: u_char *p;
! 453: ngx_connection_t *c;
! 454: ngx_ssl_stapling_t *staple;
! 455:
! 456: c = ngx_ssl_get_connection(ssl_conn);
! 457:
! 458: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
! 459: "SSL certificate status callback");
! 460:
! 461: staple = data;
! 462: rc = SSL_TLSEXT_ERR_NOACK;
! 463:
! 464: if (staple->staple.len) {
! 465: /* we have to copy ocsp response as OpenSSL will free it by itself */
! 466:
! 467: p = OPENSSL_malloc(staple->staple.len);
! 468: if (p == NULL) {
! 469: ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "OPENSSL_malloc() failed");
! 470: return SSL_TLSEXT_ERR_NOACK;
! 471: }
! 472:
! 473: ngx_memcpy(p, staple->staple.data, staple->staple.len);
! 474:
! 475: SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);
! 476:
! 477: rc = SSL_TLSEXT_ERR_OK;
! 478: }
! 479:
! 480: ngx_ssl_stapling_update(staple);
! 481:
! 482: return rc;
! 483: }
! 484:
! 485:
! 486: static void
! 487: ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)
! 488: {
! 489: ngx_ssl_ocsp_ctx_t *ctx;
! 490:
! 491: if (staple->host.len == 0
! 492: || staple->loading || staple->valid >= ngx_time())
! 493: {
! 494: return;
! 495: }
! 496:
! 497: staple->loading = 1;
! 498:
! 499: ctx = ngx_ssl_ocsp_start();
! 500: if (ctx == NULL) {
! 501: return;
! 502: }
! 503:
! 504: ctx->cert = staple->cert;
! 505: ctx->issuer = staple->issuer;
! 506:
! 507: ctx->addrs = staple->addrs;
! 508: ctx->host = staple->host;
! 509: ctx->uri = staple->uri;
! 510: ctx->port = staple->port;
! 511: ctx->timeout = staple->timeout;
! 512:
! 513: ctx->resolver = staple->resolver;
! 514: ctx->resolver_timeout = staple->resolver_timeout;
! 515:
! 516: ctx->handler = ngx_ssl_stapling_ocsp_handler;
! 517: ctx->data = staple;
! 518:
! 519: ngx_ssl_ocsp_request(ctx);
! 520:
! 521: return;
! 522: }
! 523:
! 524:
! 525: static void
! 526: ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)
! 527: {
! 528: #if OPENSSL_VERSION_NUMBER >= 0x0090707fL
! 529: const
! 530: #endif
! 531: u_char *p;
! 532: int n;
! 533: size_t len;
! 534: ngx_str_t response;
! 535: X509_STORE *store;
! 536: STACK_OF(X509) *chain;
! 537: OCSP_CERTID *id;
! 538: OCSP_RESPONSE *ocsp;
! 539: OCSP_BASICRESP *basic;
! 540: ngx_ssl_stapling_t *staple;
! 541: ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
! 542:
! 543: staple = ctx->data;
! 544: ocsp = NULL;
! 545: basic = NULL;
! 546: id = NULL;
! 547:
! 548: if (ctx->code != 200) {
! 549: goto error;
! 550: }
! 551:
! 552: /* check the response */
! 553:
! 554: len = ctx->response->last - ctx->response->pos;
! 555: p = ctx->response->pos;
! 556:
! 557: ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);
! 558: if (ocsp == NULL) {
! 559: ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
! 560: "d2i_OCSP_RESPONSE() failed");
! 561: goto error;
! 562: }
! 563:
! 564: n = OCSP_response_status(ocsp);
! 565:
! 566: if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
! 567: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 568: "OCSP response not successful (%d: %s)",
! 569: n, OCSP_response_status_str(n));
! 570: goto error;
! 571: }
! 572:
! 573: basic = OCSP_response_get1_basic(ocsp);
! 574: if (basic == NULL) {
! 575: ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
! 576: "OCSP_response_get1_basic() failed");
! 577: goto error;
! 578: }
! 579:
! 580: store = SSL_CTX_get_cert_store(staple->ssl_ctx);
! 581: if (store == NULL) {
! 582: ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
! 583: "SSL_CTX_get_cert_store() failed");
! 584: goto error;
! 585: }
! 586:
! 587: #if OPENSSL_VERSION_NUMBER >= 0x10001000L
! 588: SSL_CTX_get_extra_chain_certs(staple->ssl_ctx, &chain);
! 589: #else
! 590: chain = staple->ssl_ctx->extra_certs;
! 591: #endif
! 592:
! 593: if (OCSP_basic_verify(basic, chain, store,
! 594: staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY)
! 595: != 1)
! 596: {
! 597: ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
! 598: "OCSP_basic_verify() failed");
! 599: goto error;
! 600: }
! 601:
! 602: id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
! 603: if (id == NULL) {
! 604: ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
! 605: "OCSP_cert_to_id() failed");
! 606: goto error;
! 607: }
! 608:
! 609: if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
! 610: &thisupdate, &nextupdate)
! 611: != 1)
! 612: {
! 613: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 614: "certificate status not found in the OCSP response");
! 615: goto error;
! 616: }
! 617:
! 618: if (n != V_OCSP_CERTSTATUS_GOOD) {
! 619: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 620: "certificate status \"%s\" in the OCSP response",
! 621: OCSP_cert_status_str(n));
! 622: goto error;
! 623: }
! 624:
! 625: if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
! 626: ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,
! 627: "OCSP_check_validity() failed");
! 628: goto error;
! 629: }
! 630:
! 631: OCSP_CERTID_free(id);
! 632: OCSP_BASICRESP_free(basic);
! 633: OCSP_RESPONSE_free(ocsp);
! 634:
! 635: /* copy the response to memory not in ctx->pool */
! 636:
! 637: response.len = len;
! 638: response.data = ngx_alloc(response.len, ctx->log);
! 639:
! 640: if (response.data == NULL) {
! 641: goto done;
! 642: }
! 643:
! 644: ngx_memcpy(response.data, ctx->response->pos, response.len);
! 645:
! 646: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 647: "ssl ocsp response, %s, %uz",
! 648: OCSP_cert_status_str(n), response.len);
! 649:
! 650: if (staple->staple.data) {
! 651: ngx_free(staple->staple.data);
! 652: }
! 653:
! 654: staple->staple = response;
! 655:
! 656: done:
! 657:
! 658: staple->loading = 0;
! 659: staple->valid = ngx_time() + 3600; /* ssl_stapling_valid */
! 660:
! 661: ngx_ssl_ocsp_done(ctx);
! 662: return;
! 663:
! 664: error:
! 665:
! 666: staple->loading = 0;
! 667: staple->valid = ngx_time() + 300; /* ssl_stapling_err_valid */
! 668:
! 669: if (id) {
! 670: OCSP_CERTID_free(id);
! 671: }
! 672:
! 673: if (basic) {
! 674: OCSP_BASICRESP_free(basic);
! 675: }
! 676:
! 677: if (ocsp) {
! 678: OCSP_RESPONSE_free(ocsp);
! 679: }
! 680:
! 681: ngx_ssl_ocsp_done(ctx);
! 682: }
! 683:
! 684:
! 685: static void
! 686: ngx_ssl_stapling_cleanup(void *data)
! 687: {
! 688: ngx_ssl_stapling_t *staple = data;
! 689:
! 690: if (staple->issuer) {
! 691: X509_free(staple->issuer);
! 692: }
! 693:
! 694: if (staple->staple.data) {
! 695: ngx_free(staple->staple.data);
! 696: }
! 697: }
! 698:
! 699:
! 700: static ngx_ssl_ocsp_ctx_t *
! 701: ngx_ssl_ocsp_start(void)
! 702: {
! 703: ngx_log_t *log;
! 704: ngx_pool_t *pool;
! 705: ngx_ssl_ocsp_ctx_t *ctx;
! 706:
! 707: pool = ngx_create_pool(2048, ngx_cycle->log);
! 708: if (pool == NULL) {
! 709: return NULL;
! 710: }
! 711:
! 712: ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));
! 713: if (ctx == NULL) {
! 714: ngx_destroy_pool(pool);
! 715: return NULL;
! 716: }
! 717:
! 718: log = ngx_palloc(pool, sizeof(ngx_log_t));
! 719: if (log == NULL) {
! 720: ngx_destroy_pool(pool);
! 721: return NULL;
! 722: }
! 723:
! 724: ctx->pool = pool;
! 725:
! 726: *log = *ctx->pool->log;
! 727:
! 728: ctx->pool->log = log;
! 729: ctx->log = log;
! 730:
! 731: log->handler = ngx_ssl_ocsp_log_error;
! 732: log->data = ctx;
! 733: log->action = "requesting certificate status";
! 734:
! 735: return ctx;
! 736: }
! 737:
! 738:
! 739: static void
! 740: ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)
! 741: {
! 742: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 743: "ssl ocsp done");
! 744:
! 745: if (ctx->peer.connection) {
! 746: ngx_close_connection(ctx->peer.connection);
! 747: }
! 748:
! 749: ngx_destroy_pool(ctx->pool);
! 750: }
! 751:
! 752:
! 753: static void
! 754: ngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)
! 755: {
! 756: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 757: "ssl ocsp error");
! 758:
! 759: ctx->code = 0;
! 760: ctx->handler(ctx);
! 761: }
! 762:
! 763:
! 764: static void
! 765: ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)
! 766: {
! 767: ngx_resolver_ctx_t *resolve, temp;
! 768:
! 769: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 770: "ssl ocsp request");
! 771:
! 772: if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {
! 773: ngx_ssl_ocsp_error(ctx);
! 774: return;
! 775: }
! 776:
! 777: if (ctx->resolver) {
! 778: /* resolve OCSP responder hostname */
! 779:
! 780: temp.name = ctx->host;
! 781:
! 782: resolve = ngx_resolve_start(ctx->resolver, &temp);
! 783: if (resolve == NULL) {
! 784: ngx_ssl_ocsp_error(ctx);
! 785: return;
! 786: }
! 787:
! 788: if (resolve == NGX_NO_RESOLVER) {
! 789: ngx_log_error(NGX_LOG_WARN, ctx->log, 0,
! 790: "no resolver defined to resolve %V", &ctx->host);
! 791: goto connect;
! 792: }
! 793:
! 794: resolve->name = ctx->host;
! 795: resolve->type = NGX_RESOLVE_A;
! 796: resolve->handler = ngx_ssl_ocsp_resolve_handler;
! 797: resolve->data = ctx;
! 798: resolve->timeout = ctx->resolver_timeout;
! 799:
! 800: if (ngx_resolve_name(resolve) != NGX_OK) {
! 801: ngx_ssl_ocsp_error(ctx);
! 802: return;
! 803: }
! 804:
! 805: return;
! 806: }
! 807:
! 808: connect:
! 809:
! 810: ngx_ssl_ocsp_connect(ctx);
! 811: }
! 812:
! 813:
! 814: static void
! 815: ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)
! 816: {
! 817: ngx_ssl_ocsp_ctx_t *ctx = resolve->data;
! 818:
! 819: u_char *p;
! 820: size_t len;
! 821: in_port_t port;
! 822: ngx_uint_t i;
! 823: struct sockaddr_in *sin;
! 824:
! 825: ngx_log_debug0(NGX_LOG_ALERT, ctx->log, 0,
! 826: "ssl ocsp resolve handler");
! 827:
! 828: if (resolve->state) {
! 829: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 830: "%V could not be resolved (%i: %s)",
! 831: &resolve->name, resolve->state,
! 832: ngx_resolver_strerror(resolve->state));
! 833: goto failed;
! 834: }
! 835:
! 836: #if (NGX_DEBUG)
! 837: {
! 838: in_addr_t addr;
! 839:
! 840: for (i = 0; i < resolve->naddrs; i++) {
! 841: addr = ntohl(resolve->addrs[i]);
! 842:
! 843: ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 844: "name was resolved to %ud.%ud.%ud.%ud",
! 845: (addr >> 24) & 0xff, (addr >> 16) & 0xff,
! 846: (addr >> 8) & 0xff, addr & 0xff);
! 847: }
! 848: }
! 849: #endif
! 850:
! 851: ctx->naddrs = resolve->naddrs;
! 852: ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));
! 853:
! 854: if (ctx->addrs == NULL) {
! 855: goto failed;
! 856: }
! 857:
! 858: port = htons(ctx->port);
! 859:
! 860: for (i = 0; i < resolve->naddrs; i++) {
! 861:
! 862: sin = ngx_pcalloc(ctx->pool, sizeof(struct sockaddr_in));
! 863: if (sin == NULL) {
! 864: goto failed;
! 865: }
! 866:
! 867: sin->sin_family = AF_INET;
! 868: sin->sin_port = port;
! 869: sin->sin_addr.s_addr = resolve->addrs[i];
! 870:
! 871: ctx->addrs[i].sockaddr = (struct sockaddr *) sin;
! 872: ctx->addrs[i].socklen = sizeof(struct sockaddr_in);
! 873:
! 874: len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
! 875:
! 876: p = ngx_pnalloc(ctx->pool, len);
! 877: if (p == NULL) {
! 878: goto failed;
! 879: }
! 880:
! 881: len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
! 882:
! 883: ctx->addrs[i].name.len = len;
! 884: ctx->addrs[i].name.data = p;
! 885: }
! 886:
! 887: ngx_resolve_name_done(resolve);
! 888:
! 889: ngx_ssl_ocsp_connect(ctx);
! 890: return;
! 891:
! 892: failed:
! 893:
! 894: ngx_resolve_name_done(resolve);
! 895: ngx_ssl_ocsp_error(ctx);
! 896: }
! 897:
! 898:
! 899: static void
! 900: ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)
! 901: {
! 902: ngx_int_t rc;
! 903:
! 904: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 905: "ssl ocsp connect");
! 906:
! 907: /* TODO: use all ip addresses */
! 908:
! 909: ctx->peer.sockaddr = ctx->addrs[0].sockaddr;
! 910: ctx->peer.socklen = ctx->addrs[0].socklen;
! 911: ctx->peer.name = &ctx->addrs[0].name;
! 912: ctx->peer.get = ngx_event_get_peer;
! 913: ctx->peer.log = ctx->log;
! 914: ctx->peer.log_error = NGX_ERROR_ERR;
! 915:
! 916: rc = ngx_event_connect_peer(&ctx->peer);
! 917:
! 918: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 919: "ssl ocsp connect peer done");
! 920:
! 921: if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
! 922: ngx_ssl_ocsp_error(ctx);
! 923: return;
! 924: }
! 925:
! 926: ctx->peer.connection->data = ctx;
! 927: ctx->peer.connection->pool = ctx->pool;
! 928:
! 929: ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;
! 930: ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;
! 931:
! 932: ctx->process = ngx_ssl_ocsp_process_status_line;
! 933:
! 934: ngx_add_timer(ctx->peer.connection->read, ctx->timeout);
! 935: ngx_add_timer(ctx->peer.connection->write, ctx->timeout);
! 936:
! 937: if (rc == NGX_OK) {
! 938: ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);
! 939: return;
! 940: }
! 941: }
! 942:
! 943:
! 944: static void
! 945: ngx_ssl_ocsp_write_handler(ngx_event_t *wev)
! 946: {
! 947: ssize_t n, size;
! 948: ngx_connection_t *c;
! 949: ngx_ssl_ocsp_ctx_t *ctx;
! 950:
! 951: c = wev->data;
! 952: ctx = c->data;
! 953:
! 954: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,
! 955: "ssl ocsp write handler");
! 956:
! 957: if (wev->timedout) {
! 958: ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
! 959: "OCSP responder timed out");
! 960: ngx_ssl_ocsp_error(ctx);
! 961: return;
! 962: }
! 963:
! 964: size = ctx->request->last - ctx->request->pos;
! 965:
! 966: n = ngx_send(c, ctx->request->pos, size);
! 967:
! 968: if (n == NGX_ERROR) {
! 969: ngx_ssl_ocsp_error(ctx);
! 970: return;
! 971: }
! 972:
! 973: if (n > 0) {
! 974: ctx->request->pos += n;
! 975:
! 976: if (n == size) {
! 977: wev->handler = ngx_ssl_ocsp_dummy_handler;
! 978:
! 979: if (wev->timer_set) {
! 980: ngx_del_timer(wev);
! 981: }
! 982:
! 983: if (ngx_handle_write_event(wev, 0) != NGX_OK) {
! 984: ngx_ssl_ocsp_error(ctx);
! 985: }
! 986:
! 987: return;
! 988: }
! 989: }
! 990:
! 991: if (!wev->timer_set) {
! 992: ngx_add_timer(wev, ctx->timeout);
! 993: }
! 994: }
! 995:
! 996:
! 997: static void
! 998: ngx_ssl_ocsp_read_handler(ngx_event_t *rev)
! 999: {
! 1000: ssize_t n, size;
! 1001: ngx_int_t rc;
! 1002: ngx_ssl_ocsp_ctx_t *ctx;
! 1003: ngx_connection_t *c;
! 1004:
! 1005: c = rev->data;
! 1006: ctx = c->data;
! 1007:
! 1008: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,
! 1009: "ssl ocsp read handler");
! 1010:
! 1011: if (rev->timedout) {
! 1012: ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
! 1013: "OCSP responder timed out");
! 1014: ngx_ssl_ocsp_error(ctx);
! 1015: return;
! 1016: }
! 1017:
! 1018: if (ctx->response == NULL) {
! 1019: ctx->response = ngx_create_temp_buf(ctx->pool, 16384);
! 1020: if (ctx->response == NULL) {
! 1021: ngx_ssl_ocsp_error(ctx);
! 1022: return;
! 1023: }
! 1024: }
! 1025:
! 1026: for ( ;; ) {
! 1027:
! 1028: size = ctx->response->end - ctx->response->last;
! 1029:
! 1030: n = ngx_recv(c, ctx->response->last, size);
! 1031:
! 1032: if (n > 0) {
! 1033: ctx->response->last += n;
! 1034:
! 1035: rc = ctx->process(ctx);
! 1036:
! 1037: if (rc == NGX_ERROR) {
! 1038: ngx_ssl_ocsp_error(ctx);
! 1039: return;
! 1040: }
! 1041:
! 1042: continue;
! 1043: }
! 1044:
! 1045: if (n == NGX_AGAIN) {
! 1046:
! 1047: if (ngx_handle_read_event(rev, 0) != NGX_OK) {
! 1048: ngx_ssl_ocsp_error(ctx);
! 1049: }
! 1050:
! 1051: return;
! 1052: }
! 1053:
! 1054: break;
! 1055: }
! 1056:
! 1057: ctx->done = 1;
! 1058:
! 1059: rc = ctx->process(ctx);
! 1060:
! 1061: if (rc == NGX_DONE) {
! 1062: /* ctx->handler() was called */
! 1063: return;
! 1064: }
! 1065:
! 1066: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 1067: "OCSP responder prematurely closed connection");
! 1068:
! 1069: ngx_ssl_ocsp_error(ctx);
! 1070: }
! 1071:
! 1072:
! 1073: static void
! 1074: ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)
! 1075: {
! 1076: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
! 1077: "ssl ocsp dummy handler");
! 1078: }
! 1079:
! 1080:
! 1081: static ngx_int_t
! 1082: ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)
! 1083: {
! 1084: int len;
! 1085: u_char *p;
! 1086: uintptr_t escape;
! 1087: ngx_str_t binary, base64;
! 1088: ngx_buf_t *b;
! 1089: OCSP_CERTID *id;
! 1090: OCSP_REQUEST *ocsp;
! 1091:
! 1092: ocsp = OCSP_REQUEST_new();
! 1093: if (ocsp == NULL) {
! 1094: ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
! 1095: "OCSP_REQUEST_new() failed");
! 1096: return NGX_ERROR;
! 1097: }
! 1098:
! 1099: id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);
! 1100: if (id == NULL) {
! 1101: ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
! 1102: "OCSP_cert_to_id() failed");
! 1103: goto failed;
! 1104: }
! 1105:
! 1106: if (OCSP_request_add0_id(ocsp, id) == NULL) {
! 1107: ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
! 1108: "OCSP_request_add0_id() failed");
! 1109: goto failed;
! 1110: }
! 1111:
! 1112: len = i2d_OCSP_REQUEST(ocsp, NULL);
! 1113: if (len <= 0) {
! 1114: ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,
! 1115: "i2d_OCSP_REQUEST() failed");
! 1116: goto failed;
! 1117: }
! 1118:
! 1119: binary.len = len;
! 1120: binary.data = ngx_palloc(ctx->pool, len);
! 1121: if (binary.data == NULL) {
! 1122: goto failed;
! 1123: }
! 1124:
! 1125: p = binary.data;
! 1126: len = i2d_OCSP_REQUEST(ocsp, &p);
! 1127: if (len <= 0) {
! 1128: ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,
! 1129: "i2d_OCSP_REQUEST() failed");
! 1130: goto failed;
! 1131: }
! 1132:
! 1133: base64.len = ngx_base64_encoded_length(binary.len);
! 1134: base64.data = ngx_palloc(ctx->pool, base64.len);
! 1135: if (base64.data == NULL) {
! 1136: goto failed;
! 1137: }
! 1138:
! 1139: ngx_encode_base64(&base64, &binary);
! 1140:
! 1141: escape = ngx_escape_uri(NULL, base64.data, base64.len,
! 1142: NGX_ESCAPE_URI_COMPONENT);
! 1143:
! 1144: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1145: "ssl ocsp request length %z, escape %d",
! 1146: base64.len, escape);
! 1147:
! 1148: len = sizeof("GET ") - 1 + ctx->uri.len + sizeof("/") - 1
! 1149: + base64.len + 2 * escape + sizeof(" HTTP/1.0" CRLF) - 1
! 1150: + sizeof("Host: ") - 1 + ctx->host.len + sizeof(CRLF) - 1
! 1151: + sizeof(CRLF) - 1;
! 1152:
! 1153: b = ngx_create_temp_buf(ctx->pool, len);
! 1154: if (b == NULL) {
! 1155: goto failed;
! 1156: }
! 1157:
! 1158: p = b->last;
! 1159:
! 1160: p = ngx_cpymem(p, "GET ", sizeof("GET ") - 1);
! 1161: p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);
! 1162:
! 1163: if (ctx->uri.data[ctx->uri.len - 1] != '/') {
! 1164: *p++ = '/';
! 1165: }
! 1166:
! 1167: if (escape == 0) {
! 1168: p = ngx_cpymem(p, base64.data, base64.len);
! 1169:
! 1170: } else {
! 1171: p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,
! 1172: NGX_ESCAPE_URI_COMPONENT);
! 1173: }
! 1174:
! 1175: p = ngx_cpymem(p, " HTTP/1.0" CRLF, sizeof(" HTTP/1.0" CRLF) - 1);
! 1176: p = ngx_cpymem(p, "Host: ", sizeof("Host: ") - 1);
! 1177: p = ngx_cpymem(p, ctx->host.data, ctx->host.len);
! 1178: *p++ = CR; *p++ = LF;
! 1179:
! 1180: /* add "\r\n" at the header end */
! 1181: *p++ = CR; *p++ = LF;
! 1182:
! 1183: b->last = p;
! 1184: ctx->request = b;
! 1185:
! 1186: return NGX_OK;
! 1187:
! 1188: failed:
! 1189:
! 1190: OCSP_REQUEST_free(ocsp);
! 1191:
! 1192: return NGX_ERROR;
! 1193: }
! 1194:
! 1195:
! 1196: static ngx_int_t
! 1197: ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)
! 1198: {
! 1199: ngx_int_t rc;
! 1200:
! 1201: rc = ngx_ssl_ocsp_parse_status_line(ctx);
! 1202:
! 1203: if (rc == NGX_OK) {
! 1204: #if 0
! 1205: ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1206: "ssl ocsp status line \"%*s\"",
! 1207: ctx->response->pos - ctx->response->start,
! 1208: ctx->response->start);
! 1209: #endif
! 1210:
! 1211: ctx->process = ngx_ssl_ocsp_process_headers;
! 1212: return ctx->process(ctx);
! 1213: }
! 1214:
! 1215: if (rc == NGX_AGAIN) {
! 1216: return NGX_AGAIN;
! 1217: }
! 1218:
! 1219: /* rc == NGX_ERROR */
! 1220:
! 1221: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 1222: "OCSP responder sent invalid response");
! 1223:
! 1224: return NGX_ERROR;
! 1225: }
! 1226:
! 1227:
! 1228: static ngx_int_t
! 1229: ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)
! 1230: {
! 1231: u_char ch;
! 1232: u_char *p;
! 1233: ngx_buf_t *b;
! 1234: enum {
! 1235: sw_start = 0,
! 1236: sw_H,
! 1237: sw_HT,
! 1238: sw_HTT,
! 1239: sw_HTTP,
! 1240: sw_first_major_digit,
! 1241: sw_major_digit,
! 1242: sw_first_minor_digit,
! 1243: sw_minor_digit,
! 1244: sw_status,
! 1245: sw_space_after_status,
! 1246: sw_status_text,
! 1247: sw_almost_done
! 1248: } state;
! 1249:
! 1250: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1251: "ssl ocsp process status line");
! 1252:
! 1253: state = ctx->state;
! 1254: b = ctx->response;
! 1255:
! 1256: for (p = b->pos; p < b->last; p++) {
! 1257: ch = *p;
! 1258:
! 1259: switch (state) {
! 1260:
! 1261: /* "HTTP/" */
! 1262: case sw_start:
! 1263: switch (ch) {
! 1264: case 'H':
! 1265: state = sw_H;
! 1266: break;
! 1267: default:
! 1268: return NGX_ERROR;
! 1269: }
! 1270: break;
! 1271:
! 1272: case sw_H:
! 1273: switch (ch) {
! 1274: case 'T':
! 1275: state = sw_HT;
! 1276: break;
! 1277: default:
! 1278: return NGX_ERROR;
! 1279: }
! 1280: break;
! 1281:
! 1282: case sw_HT:
! 1283: switch (ch) {
! 1284: case 'T':
! 1285: state = sw_HTT;
! 1286: break;
! 1287: default:
! 1288: return NGX_ERROR;
! 1289: }
! 1290: break;
! 1291:
! 1292: case sw_HTT:
! 1293: switch (ch) {
! 1294: case 'P':
! 1295: state = sw_HTTP;
! 1296: break;
! 1297: default:
! 1298: return NGX_ERROR;
! 1299: }
! 1300: break;
! 1301:
! 1302: case sw_HTTP:
! 1303: switch (ch) {
! 1304: case '/':
! 1305: state = sw_first_major_digit;
! 1306: break;
! 1307: default:
! 1308: return NGX_ERROR;
! 1309: }
! 1310: break;
! 1311:
! 1312: /* the first digit of major HTTP version */
! 1313: case sw_first_major_digit:
! 1314: if (ch < '1' || ch > '9') {
! 1315: return NGX_ERROR;
! 1316: }
! 1317:
! 1318: state = sw_major_digit;
! 1319: break;
! 1320:
! 1321: /* the major HTTP version or dot */
! 1322: case sw_major_digit:
! 1323: if (ch == '.') {
! 1324: state = sw_first_minor_digit;
! 1325: break;
! 1326: }
! 1327:
! 1328: if (ch < '0' || ch > '9') {
! 1329: return NGX_ERROR;
! 1330: }
! 1331:
! 1332: break;
! 1333:
! 1334: /* the first digit of minor HTTP version */
! 1335: case sw_first_minor_digit:
! 1336: if (ch < '0' || ch > '9') {
! 1337: return NGX_ERROR;
! 1338: }
! 1339:
! 1340: state = sw_minor_digit;
! 1341: break;
! 1342:
! 1343: /* the minor HTTP version or the end of the request line */
! 1344: case sw_minor_digit:
! 1345: if (ch == ' ') {
! 1346: state = sw_status;
! 1347: break;
! 1348: }
! 1349:
! 1350: if (ch < '0' || ch > '9') {
! 1351: return NGX_ERROR;
! 1352: }
! 1353:
! 1354: break;
! 1355:
! 1356: /* HTTP status code */
! 1357: case sw_status:
! 1358: if (ch == ' ') {
! 1359: break;
! 1360: }
! 1361:
! 1362: if (ch < '0' || ch > '9') {
! 1363: return NGX_ERROR;
! 1364: }
! 1365:
! 1366: ctx->code = ctx->code * 10 + ch - '0';
! 1367:
! 1368: if (++ctx->count == 3) {
! 1369: state = sw_space_after_status;
! 1370: }
! 1371:
! 1372: break;
! 1373:
! 1374: /* space or end of line */
! 1375: case sw_space_after_status:
! 1376: switch (ch) {
! 1377: case ' ':
! 1378: state = sw_status_text;
! 1379: break;
! 1380: case '.': /* IIS may send 403.1, 403.2, etc */
! 1381: state = sw_status_text;
! 1382: break;
! 1383: case CR:
! 1384: state = sw_almost_done;
! 1385: break;
! 1386: case LF:
! 1387: goto done;
! 1388: default:
! 1389: return NGX_ERROR;
! 1390: }
! 1391: break;
! 1392:
! 1393: /* any text until end of line */
! 1394: case sw_status_text:
! 1395: switch (ch) {
! 1396: case CR:
! 1397: state = sw_almost_done;
! 1398: break;
! 1399: case LF:
! 1400: goto done;
! 1401: }
! 1402: break;
! 1403:
! 1404: /* end of status line */
! 1405: case sw_almost_done:
! 1406: switch (ch) {
! 1407: case LF:
! 1408: goto done;
! 1409: default:
! 1410: return NGX_ERROR;
! 1411: }
! 1412: }
! 1413: }
! 1414:
! 1415: b->pos = p;
! 1416: ctx->state = state;
! 1417:
! 1418: return NGX_AGAIN;
! 1419:
! 1420: done:
! 1421:
! 1422: b->pos = p + 1;
! 1423: ctx->state = sw_start;
! 1424:
! 1425: return NGX_OK;
! 1426: }
! 1427:
! 1428:
! 1429: static ngx_int_t
! 1430: ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)
! 1431: {
! 1432: size_t len;
! 1433: ngx_int_t rc;
! 1434:
! 1435: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1436: "ssl ocsp process headers");
! 1437:
! 1438: for ( ;; ) {
! 1439: rc = ngx_ssl_ocsp_parse_header_line(ctx);
! 1440:
! 1441: if (rc == NGX_OK) {
! 1442:
! 1443: ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1444: "ssl ocsp header \"%*s: %*s\"",
! 1445: ctx->header_name_end - ctx->header_name_start,
! 1446: ctx->header_name_start,
! 1447: ctx->header_end - ctx->header_start,
! 1448: ctx->header_start);
! 1449:
! 1450: len = ctx->header_name_end - ctx->header_name_start;
! 1451:
! 1452: if (len == sizeof("Content-Type") - 1
! 1453: && ngx_strncasecmp(ctx->header_name_start,
! 1454: (u_char *) "Content-Type",
! 1455: sizeof("Content-Type") - 1)
! 1456: == 0)
! 1457: {
! 1458: len = ctx->header_end - ctx->header_start;
! 1459:
! 1460: if (len != sizeof("application/ocsp-response") - 1
! 1461: || ngx_strncasecmp(ctx->header_start,
! 1462: (u_char *) "application/ocsp-response",
! 1463: sizeof("application/ocsp-response") - 1)
! 1464: != 0)
! 1465: {
! 1466: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 1467: "OCSP responder sent invalid "
! 1468: "\"Content-Type\" header: \"%*s\"",
! 1469: ctx->header_end - ctx->header_start,
! 1470: ctx->header_start);
! 1471: return NGX_ERROR;
! 1472: }
! 1473:
! 1474: continue;
! 1475: }
! 1476:
! 1477: /* TODO: honor Content-Length */
! 1478:
! 1479: continue;
! 1480: }
! 1481:
! 1482: if (rc == NGX_DONE) {
! 1483: break;
! 1484: }
! 1485:
! 1486: if (rc == NGX_AGAIN) {
! 1487: return NGX_AGAIN;
! 1488: }
! 1489:
! 1490: /* rc == NGX_ERROR */
! 1491:
! 1492: ngx_log_error(NGX_LOG_ERR, ctx->log, 0,
! 1493: "OCSP responder sent invalid response");
! 1494:
! 1495: return NGX_ERROR;
! 1496: }
! 1497:
! 1498: ctx->process = ngx_ssl_ocsp_process_body;
! 1499: return ctx->process(ctx);
! 1500: }
! 1501:
! 1502: static ngx_int_t
! 1503: ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)
! 1504: {
! 1505: u_char c, ch, *p;
! 1506: enum {
! 1507: sw_start = 0,
! 1508: sw_name,
! 1509: sw_space_before_value,
! 1510: sw_value,
! 1511: sw_space_after_value,
! 1512: sw_almost_done,
! 1513: sw_header_almost_done
! 1514: } state;
! 1515:
! 1516: state = ctx->state;
! 1517:
! 1518: for (p = ctx->response->pos; p < ctx->response->last; p++) {
! 1519: ch = *p;
! 1520:
! 1521: #if 0
! 1522: ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1523: "s:%d in:'%02Xd:%c'", state, ch, ch);
! 1524: #endif
! 1525:
! 1526: switch (state) {
! 1527:
! 1528: /* first char */
! 1529: case sw_start:
! 1530:
! 1531: switch (ch) {
! 1532: case CR:
! 1533: ctx->header_end = p;
! 1534: state = sw_header_almost_done;
! 1535: break;
! 1536: case LF:
! 1537: ctx->header_end = p;
! 1538: goto header_done;
! 1539: default:
! 1540: state = sw_name;
! 1541: ctx->header_name_start = p;
! 1542:
! 1543: c = (u_char) (ch | 0x20);
! 1544: if (c >= 'a' && c <= 'z') {
! 1545: break;
! 1546: }
! 1547:
! 1548: if (ch >= '0' && ch <= '9') {
! 1549: break;
! 1550: }
! 1551:
! 1552: return NGX_ERROR;
! 1553: }
! 1554: break;
! 1555:
! 1556: /* header name */
! 1557: case sw_name:
! 1558: c = (u_char) (ch | 0x20);
! 1559: if (c >= 'a' && c <= 'z') {
! 1560: break;
! 1561: }
! 1562:
! 1563: if (ch == ':') {
! 1564: ctx->header_name_end = p;
! 1565: state = sw_space_before_value;
! 1566: break;
! 1567: }
! 1568:
! 1569: if (ch == '-') {
! 1570: break;
! 1571: }
! 1572:
! 1573: if (ch >= '0' && ch <= '9') {
! 1574: break;
! 1575: }
! 1576:
! 1577: if (ch == CR) {
! 1578: ctx->header_name_end = p;
! 1579: ctx->header_start = p;
! 1580: ctx->header_end = p;
! 1581: state = sw_almost_done;
! 1582: break;
! 1583: }
! 1584:
! 1585: if (ch == LF) {
! 1586: ctx->header_name_end = p;
! 1587: ctx->header_start = p;
! 1588: ctx->header_end = p;
! 1589: goto done;
! 1590: }
! 1591:
! 1592: return NGX_ERROR;
! 1593:
! 1594: /* space* before header value */
! 1595: case sw_space_before_value:
! 1596: switch (ch) {
! 1597: case ' ':
! 1598: break;
! 1599: case CR:
! 1600: ctx->header_start = p;
! 1601: ctx->header_end = p;
! 1602: state = sw_almost_done;
! 1603: break;
! 1604: case LF:
! 1605: ctx->header_start = p;
! 1606: ctx->header_end = p;
! 1607: goto done;
! 1608: default:
! 1609: ctx->header_start = p;
! 1610: state = sw_value;
! 1611: break;
! 1612: }
! 1613: break;
! 1614:
! 1615: /* header value */
! 1616: case sw_value:
! 1617: switch (ch) {
! 1618: case ' ':
! 1619: ctx->header_end = p;
! 1620: state = sw_space_after_value;
! 1621: break;
! 1622: case CR:
! 1623: ctx->header_end = p;
! 1624: state = sw_almost_done;
! 1625: break;
! 1626: case LF:
! 1627: ctx->header_end = p;
! 1628: goto done;
! 1629: }
! 1630: break;
! 1631:
! 1632: /* space* before end of header line */
! 1633: case sw_space_after_value:
! 1634: switch (ch) {
! 1635: case ' ':
! 1636: break;
! 1637: case CR:
! 1638: state = sw_almost_done;
! 1639: break;
! 1640: case LF:
! 1641: goto done;
! 1642: default:
! 1643: state = sw_value;
! 1644: break;
! 1645: }
! 1646: break;
! 1647:
! 1648: /* end of header line */
! 1649: case sw_almost_done:
! 1650: switch (ch) {
! 1651: case LF:
! 1652: goto done;
! 1653: default:
! 1654: return NGX_ERROR;
! 1655: }
! 1656:
! 1657: /* end of header */
! 1658: case sw_header_almost_done:
! 1659: switch (ch) {
! 1660: case LF:
! 1661: goto header_done;
! 1662: default:
! 1663: return NGX_ERROR;
! 1664: }
! 1665: }
! 1666: }
! 1667:
! 1668: ctx->response->pos = p;
! 1669: ctx->state = state;
! 1670:
! 1671: return NGX_AGAIN;
! 1672:
! 1673: done:
! 1674:
! 1675: ctx->response->pos = p + 1;
! 1676: ctx->state = sw_start;
! 1677:
! 1678: return NGX_OK;
! 1679:
! 1680: header_done:
! 1681:
! 1682: ctx->response->pos = p + 1;
! 1683: ctx->state = sw_start;
! 1684:
! 1685: return NGX_DONE;
! 1686: }
! 1687:
! 1688:
! 1689: static ngx_int_t
! 1690: ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)
! 1691: {
! 1692: ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,
! 1693: "ssl ocsp process body");
! 1694:
! 1695: if (ctx->done) {
! 1696: ctx->handler(ctx);
! 1697: return NGX_DONE;
! 1698: }
! 1699:
! 1700: return NGX_AGAIN;
! 1701: }
! 1702:
! 1703:
! 1704: static u_char *
! 1705: ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)
! 1706: {
! 1707: u_char *p;
! 1708: ngx_ssl_ocsp_ctx_t *ctx;
! 1709:
! 1710: p = buf;
! 1711:
! 1712: if (log->action) {
! 1713: p = ngx_snprintf(buf, len, " while %s", log->action);
! 1714: len -= p - buf;
! 1715: }
! 1716:
! 1717: ctx = log->data;
! 1718:
! 1719: if (ctx) {
! 1720: p = ngx_snprintf(p, len, ", responder: %V", &ctx->host);
! 1721: }
! 1722:
! 1723: return p;
! 1724: }
! 1725:
! 1726:
! 1727: #else
! 1728:
! 1729:
! 1730: ngx_int_t
! 1731: ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,
! 1732: ngx_str_t *responder, ngx_uint_t verify)
! 1733: {
! 1734: ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
! 1735: "\"ssl_stapling\" ignored, not supported");
! 1736:
! 1737: return NGX_OK;
! 1738: }
! 1739:
! 1740: ngx_int_t
! 1741: ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,
! 1742: ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)
! 1743: {
! 1744: return NGX_OK;
! 1745: }
! 1746:
! 1747:
! 1748: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>