/* * Copyright (C) Yichun Zhang (agentzh) */ #ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #if (NGX_HTTP_SSL) #include "ngx_http_lua_common.h" #ifndef NGX_LUA_NO_FFI_API #ifdef NGX_HTTP_LUA_USE_OCSP static int ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn, void *data); #endif int ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain( const char *chain_data, size_t chain_len, unsigned char *out, size_t *out_size, char **err) { #ifndef NGX_HTTP_LUA_USE_OCSP *err = "no OCSP support"; return NGX_ERROR; #else int rc = NGX_OK; BIO *bio = NULL; char *s; X509 *cert = NULL, *issuer = NULL; size_t len; STACK_OF(OPENSSL_STRING) *aia = NULL; /* certificate */ bio = BIO_new_mem_buf((char *) chain_data, chain_len); if (bio == NULL) { *err = "BIO_new_mem_buf() failed"; rc = NGX_ERROR; goto done; } cert = d2i_X509_bio(bio, NULL); if (cert == NULL) { *err = "d2i_X509_bio() failed"; rc = NGX_ERROR; goto done; } /* responder */ aia = X509_get1_ocsp(cert); if (aia == NULL) { rc = NGX_DECLINED; goto done; } #if OPENSSL_VERSION_NUMBER >= 0x10000000L s = sk_OPENSSL_STRING_value(aia, 0); #else s = sk_value(aia, 0); #endif if (s == NULL) { rc = NGX_DECLINED; goto done; } len = ngx_strlen(s); if (len > *out_size) { len = *out_size; rc = NGX_BUSY; } else { rc = NGX_OK; *out_size = len; } ngx_memcpy(out, s, len); X509_email_free(aia); aia = NULL; /* issuer */ if (BIO_eof(bio)) { *err = "no issuer certificate in chain"; rc = NGX_ERROR; goto done; } issuer = d2i_X509_bio(bio, NULL); if (issuer == NULL) { *err = "d2i_X509_bio() failed"; rc = NGX_ERROR; goto done; } if (X509_check_issued(issuer, cert) != X509_V_OK) { *err = "issuer certificate not next to leaf"; rc = NGX_ERROR; goto done; } X509_free(issuer); X509_free(cert); BIO_free(bio); return rc; done: if (aia) { X509_email_free(aia); } if (issuer) { X509_free(issuer); } if (cert) { X509_free(cert); } if (bio) { BIO_free(bio); } if (rc == NGX_ERROR) { ERR_clear_error(); } return rc; #endif /* NGX_HTTP_LUA_USE_OCSP */ } int ngx_http_lua_ffi_ssl_create_ocsp_request(const char *chain_data, size_t chain_len, unsigned char *out, size_t *out_size, char **err) { #ifndef NGX_HTTP_LUA_USE_OCSP *err = "no OCSP support"; return NGX_ERROR; #else int rc = NGX_ERROR; BIO *bio = NULL; X509 *cert = NULL, *issuer = NULL; size_t len; OCSP_CERTID *id; OCSP_REQUEST *ocsp = NULL; /* certificate */ bio = BIO_new_mem_buf((char *) chain_data, chain_len); if (bio == NULL) { *err = "BIO_new_mem_buf() failed"; goto failed; } cert = d2i_X509_bio(bio, NULL); if (cert == NULL) { *err = "d2i_X509_bio() failed"; goto failed; } if (BIO_eof(bio)) { *err = "no issuer certificate in chain"; goto failed; } issuer = d2i_X509_bio(bio, NULL); if (issuer == NULL) { *err = "d2i_X509_bio() failed"; goto failed; } ocsp = OCSP_REQUEST_new(); if (ocsp == NULL) { *err = "OCSP_REQUEST_new() failed"; goto failed; } id = OCSP_cert_to_id(NULL, cert, issuer); if (id == NULL) { *err = "OCSP_cert_to_id() failed"; goto failed; } if (OCSP_request_add0_id(ocsp, id) == NULL) { *err = "OCSP_request_add0_id() failed"; goto failed; } len = i2d_OCSP_REQUEST(ocsp, NULL); if (len <= 0) { *err = "i2d_OCSP_REQUEST() failed"; goto failed; } if (len > *out_size) { *err = "output buffer too small"; *out_size = len; rc = NGX_BUSY; goto failed; } len = i2d_OCSP_REQUEST(ocsp, &out); if (len <= 0) { *err = "i2d_OCSP_REQUEST() failed"; goto failed; } *out_size = len; OCSP_REQUEST_free(ocsp); X509_free(issuer); X509_free(cert); BIO_free(bio); return NGX_OK; failed: if (ocsp) { OCSP_REQUEST_free(ocsp); } if (issuer) { X509_free(issuer); } if (cert) { X509_free(cert); } if (bio) { BIO_free(bio); } ERR_clear_error(); return rc; #endif /* NGX_HTTP_LUA_USE_OCSP */ } int ngx_http_lua_ffi_ssl_validate_ocsp_response(const u_char *resp, size_t resp_len, const char *chain_data, size_t chain_len, u_char *errbuf, size_t *errbuf_size) { #ifndef NGX_HTTP_LUA_USE_OCSP *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no OCSP support") - errbuf; return NGX_ERROR; #else int n; BIO *bio = NULL; X509 *cert = NULL, *issuer = NULL; OCSP_CERTID *id = NULL; OCSP_RESPONSE *ocsp = NULL; OCSP_BASICRESP *basic = NULL; STACK_OF(X509) *chain = NULL; ASN1_GENERALIZEDTIME *thisupdate, *nextupdate; ocsp = d2i_OCSP_RESPONSE(NULL, &resp, resp_len); if (ocsp == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "d2i_OCSP_RESPONSE() failed") - errbuf; goto error; } n = OCSP_response_status(ocsp); if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "OCSP response not successful (%d: %s)", n, OCSP_response_status_str(n)) - errbuf; goto error; } basic = OCSP_response_get1_basic(ocsp); if (basic == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "OCSP_response_get1_basic() failed") - errbuf; goto error; } /* get issuer certificate from chain */ bio = BIO_new_mem_buf((char *) chain_data, chain_len); if (bio == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "BIO_new_mem_buf() failed") - errbuf; goto error; } cert = d2i_X509_bio(bio, NULL); if (cert == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "d2i_X509_bio() failed") - errbuf; goto error; } if (BIO_eof(bio)) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "no issuer certificate in chain") - errbuf; goto error; } issuer = d2i_X509_bio(bio, NULL); if (issuer == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "d2i_X509_bio() failed") - errbuf; goto error; } chain = sk_X509_new_null(); if (chain == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "sk_X509_new_null() failed") - errbuf; goto error; } (void) sk_X509_push(chain, issuer); if (OCSP_basic_verify(basic, chain, NULL, OCSP_NOVERIFY) != 1) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "OCSP_basic_verify() failed") - errbuf; goto error; } id = OCSP_cert_to_id(NULL, cert, issuer); if (id == NULL) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "OCSP_cert_to_id() failed") - errbuf; goto error; } if (OCSP_resp_find_status(basic, id, &n, NULL, NULL, &thisupdate, &nextupdate) != 1) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "certificate status not found in the " "OCSP response") - errbuf; goto error; } if (n != V_OCSP_CERTSTATUS_GOOD) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "certificate status \"%s\" in the OCSP " "response", OCSP_cert_status_str(n)) - errbuf; goto error; } if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) { *errbuf_size = ngx_snprintf(errbuf, *errbuf_size, "OCSP_check_validity() failed") - errbuf; goto error; } sk_X509_free(chain); X509_free(cert); X509_free(issuer); BIO_free(bio); OCSP_CERTID_free(id); OCSP_BASICRESP_free(basic); OCSP_RESPONSE_free(ocsp); return NGX_OK; error: if (chain) { sk_X509_free(chain); } if (id) { OCSP_CERTID_free(id); } if (basic) { OCSP_BASICRESP_free(basic); } if (ocsp) { OCSP_RESPONSE_free(ocsp); } if (cert) { X509_free(cert); } if (issuer) { X509_free(issuer); } if (bio) { BIO_free(bio); } ERR_clear_error(); return NGX_ERROR; #endif /* NGX_HTTP_LUA_USE_OCSP */ } #ifdef NGX_HTTP_LUA_USE_OCSP static int ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn, void *data) { return SSL_TLSEXT_ERR_OK; } #endif int ngx_http_lua_ffi_ssl_set_ocsp_status_resp(ngx_http_request_t *r, const u_char *resp, size_t resp_len, char **err) { #ifndef NGX_HTTP_LUA_USE_OCSP *err = "no OCSP support"; return NGX_ERROR; #else u_char *p; SSL_CTX *ctx; ngx_ssl_conn_t *ssl_conn; if (r->connection == NULL || r->connection->ssl == NULL) { *err = "bad request"; return NGX_ERROR; } ssl_conn = r->connection->ssl->connection; if (ssl_conn == NULL) { *err = "bad ssl conn"; return NGX_ERROR; } if (ssl_conn->tlsext_status_type == -1) { dd("no ocsp status req from client"); return NGX_DECLINED; } /* we have to register an empty status callback here otherwise * OpenSSL won't send the response staple. */ ctx = SSL_get_SSL_CTX(ssl_conn); SSL_CTX_set_tlsext_status_cb(ctx, ngx_http_lua_ssl_empty_status_callback); p = OPENSSL_malloc(resp_len); if (p == NULL) { *err = "OPENSSL_malloc() failed"; return NGX_ERROR; } ngx_memcpy(p, resp, resp_len); dd("set ocsp resp: resp_len=%d", (int) resp_len); (void) SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, resp_len); ssl_conn->tlsext_status_expected = 1; return NGX_OK; #endif /* NGX_HTTP_LUA_USE_OCSP */ } #endif /* NGX_LUA_NO_FFI_API */ #endif /* NGX_HTTP_SSL */