Line data Source code
1 :
2 : /*
3 : * Copyright (C) Yichun Zhang (agentzh)
4 : */
5 :
6 :
7 : #ifndef DDEBUG
8 : #define DDEBUG 0
9 : #endif
10 : #include "ddebug.h"
11 :
12 :
13 : #if (NGX_HTTP_SSL)
14 :
15 :
16 : #include "ngx_http_lua_common.h"
17 :
18 :
19 : #ifndef NGX_LUA_NO_FFI_API
20 :
21 : #ifdef NGX_HTTP_LUA_USE_OCSP
22 : static int ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn,
23 : void *data);
24 : #endif
25 :
26 :
27 : int
28 0 : ngx_http_lua_ffi_ssl_get_ocsp_responder_from_der_chain(
29 : const char *chain_data, size_t chain_len, unsigned char *out,
30 : size_t *out_size, char **err)
31 : {
32 : #ifndef NGX_HTTP_LUA_USE_OCSP
33 :
34 : *err = "no OCSP support";
35 : return NGX_ERROR;
36 :
37 : #else
38 :
39 0 : int rc = NGX_OK;
40 0 : BIO *bio = NULL;
41 : char *s;
42 0 : X509 *cert = NULL, *issuer = NULL;
43 : size_t len;
44 0 : STACK_OF(OPENSSL_STRING) *aia = NULL;
45 :
46 : /* certificate */
47 :
48 0 : bio = BIO_new_mem_buf((char *) chain_data, chain_len);
49 0 : if (bio == NULL) {
50 0 : *err = "BIO_new_mem_buf() failed";
51 0 : rc = NGX_ERROR;
52 0 : goto done;
53 : }
54 :
55 0 : cert = d2i_X509_bio(bio, NULL);
56 0 : if (cert == NULL) {
57 0 : *err = "d2i_X509_bio() failed";
58 0 : rc = NGX_ERROR;
59 0 : goto done;
60 : }
61 :
62 : /* responder */
63 :
64 0 : aia = X509_get1_ocsp(cert);
65 0 : if (aia == NULL) {
66 0 : rc = NGX_DECLINED;
67 0 : goto done;
68 : }
69 :
70 : #if OPENSSL_VERSION_NUMBER >= 0x10000000L
71 0 : s = sk_OPENSSL_STRING_value(aia, 0);
72 : #else
73 : s = sk_value(aia, 0);
74 : #endif
75 0 : if (s == NULL) {
76 0 : rc = NGX_DECLINED;
77 0 : goto done;
78 : }
79 :
80 0 : len = ngx_strlen(s);
81 0 : if (len > *out_size) {
82 0 : len = *out_size;
83 0 : rc = NGX_BUSY;
84 :
85 : } else {
86 0 : rc = NGX_OK;
87 0 : *out_size = len;
88 : }
89 :
90 0 : ngx_memcpy(out, s, len);
91 :
92 0 : X509_email_free(aia);
93 0 : aia = NULL;
94 :
95 : /* issuer */
96 :
97 0 : if (BIO_eof(bio)) {
98 0 : *err = "no issuer certificate in chain";
99 0 : rc = NGX_ERROR;
100 0 : goto done;
101 : }
102 :
103 0 : issuer = d2i_X509_bio(bio, NULL);
104 0 : if (issuer == NULL) {
105 0 : *err = "d2i_X509_bio() failed";
106 0 : rc = NGX_ERROR;
107 0 : goto done;
108 : }
109 :
110 0 : if (X509_check_issued(issuer, cert) != X509_V_OK) {
111 0 : *err = "issuer certificate not next to leaf";
112 0 : rc = NGX_ERROR;
113 0 : goto done;
114 : }
115 :
116 0 : X509_free(issuer);
117 0 : X509_free(cert);
118 0 : BIO_free(bio);
119 :
120 0 : return rc;
121 :
122 0 : done:
123 :
124 0 : if (aia) {
125 0 : X509_email_free(aia);
126 : }
127 :
128 0 : if (issuer) {
129 0 : X509_free(issuer);
130 : }
131 :
132 0 : if (cert) {
133 0 : X509_free(cert);
134 : }
135 :
136 0 : if (bio) {
137 0 : BIO_free(bio);
138 : }
139 :
140 0 : if (rc == NGX_ERROR) {
141 0 : ERR_clear_error();
142 : }
143 :
144 0 : return rc;
145 :
146 : #endif /* NGX_HTTP_LUA_USE_OCSP */
147 : }
148 :
149 :
150 : int
151 0 : ngx_http_lua_ffi_ssl_create_ocsp_request(const char *chain_data,
152 : size_t chain_len, unsigned char *out, size_t *out_size, char **err)
153 : {
154 : #ifndef NGX_HTTP_LUA_USE_OCSP
155 :
156 : *err = "no OCSP support";
157 : return NGX_ERROR;
158 :
159 : #else
160 :
161 0 : int rc = NGX_ERROR;
162 0 : BIO *bio = NULL;
163 0 : X509 *cert = NULL, *issuer = NULL;
164 : size_t len;
165 : OCSP_CERTID *id;
166 0 : OCSP_REQUEST *ocsp = NULL;
167 :
168 : /* certificate */
169 :
170 0 : bio = BIO_new_mem_buf((char *) chain_data, chain_len);
171 0 : if (bio == NULL) {
172 0 : *err = "BIO_new_mem_buf() failed";
173 0 : goto failed;
174 : }
175 :
176 0 : cert = d2i_X509_bio(bio, NULL);
177 0 : if (cert == NULL) {
178 0 : *err = "d2i_X509_bio() failed";
179 0 : goto failed;
180 : }
181 :
182 0 : if (BIO_eof(bio)) {
183 0 : *err = "no issuer certificate in chain";
184 0 : goto failed;
185 : }
186 :
187 0 : issuer = d2i_X509_bio(bio, NULL);
188 0 : if (issuer == NULL) {
189 0 : *err = "d2i_X509_bio() failed";
190 0 : goto failed;
191 : }
192 :
193 0 : ocsp = OCSP_REQUEST_new();
194 0 : if (ocsp == NULL) {
195 0 : *err = "OCSP_REQUEST_new() failed";
196 0 : goto failed;
197 : }
198 :
199 0 : id = OCSP_cert_to_id(NULL, cert, issuer);
200 0 : if (id == NULL) {
201 0 : *err = "OCSP_cert_to_id() failed";
202 0 : goto failed;
203 : }
204 :
205 0 : if (OCSP_request_add0_id(ocsp, id) == NULL) {
206 0 : *err = "OCSP_request_add0_id() failed";
207 0 : goto failed;
208 : }
209 :
210 0 : len = i2d_OCSP_REQUEST(ocsp, NULL);
211 0 : if (len <= 0) {
212 0 : *err = "i2d_OCSP_REQUEST() failed";
213 0 : goto failed;
214 : }
215 :
216 0 : if (len > *out_size) {
217 0 : *err = "output buffer too small";
218 0 : *out_size = len;
219 0 : rc = NGX_BUSY;
220 0 : goto failed;
221 : }
222 :
223 0 : len = i2d_OCSP_REQUEST(ocsp, &out);
224 0 : if (len <= 0) {
225 0 : *err = "i2d_OCSP_REQUEST() failed";
226 0 : goto failed;
227 : }
228 :
229 0 : *out_size = len;
230 :
231 0 : OCSP_REQUEST_free(ocsp);
232 0 : X509_free(issuer);
233 0 : X509_free(cert);
234 0 : BIO_free(bio);
235 :
236 0 : return NGX_OK;
237 :
238 0 : failed:
239 :
240 0 : if (ocsp) {
241 0 : OCSP_REQUEST_free(ocsp);
242 : }
243 :
244 0 : if (issuer) {
245 0 : X509_free(issuer);
246 : }
247 :
248 0 : if (cert) {
249 0 : X509_free(cert);
250 : }
251 :
252 0 : if (bio) {
253 0 : BIO_free(bio);
254 : }
255 :
256 0 : ERR_clear_error();
257 :
258 0 : return rc;
259 :
260 : #endif /* NGX_HTTP_LUA_USE_OCSP */
261 : }
262 :
263 :
264 : int
265 0 : ngx_http_lua_ffi_ssl_validate_ocsp_response(const u_char *resp,
266 : size_t resp_len, const char *chain_data, size_t chain_len,
267 : u_char *errbuf, size_t *errbuf_size)
268 : {
269 : #ifndef NGX_HTTP_LUA_USE_OCSP
270 :
271 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
272 : "no OCSP support") - errbuf;
273 : return NGX_ERROR;
274 :
275 : #else
276 :
277 : int n;
278 0 : BIO *bio = NULL;
279 0 : X509 *cert = NULL, *issuer = NULL;
280 0 : OCSP_CERTID *id = NULL;
281 0 : OCSP_RESPONSE *ocsp = NULL;
282 0 : OCSP_BASICRESP *basic = NULL;
283 0 : STACK_OF(X509) *chain = NULL;
284 : ASN1_GENERALIZEDTIME *thisupdate, *nextupdate;
285 :
286 0 : ocsp = d2i_OCSP_RESPONSE(NULL, &resp, resp_len);
287 0 : if (ocsp == NULL) {
288 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
289 0 : "d2i_OCSP_RESPONSE() failed") - errbuf;
290 0 : goto error;
291 : }
292 :
293 0 : n = OCSP_response_status(ocsp);
294 :
295 0 : if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
296 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
297 : "OCSP response not successful (%d: %s)",
298 0 : n, OCSP_response_status_str(n)) - errbuf;
299 0 : goto error;
300 : }
301 :
302 0 : basic = OCSP_response_get1_basic(ocsp);
303 0 : if (basic == NULL) {
304 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
305 : "OCSP_response_get1_basic() failed")
306 0 : - errbuf;
307 0 : goto error;
308 : }
309 :
310 : /* get issuer certificate from chain */
311 :
312 0 : bio = BIO_new_mem_buf((char *) chain_data, chain_len);
313 0 : if (bio == NULL) {
314 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
315 : "BIO_new_mem_buf() failed")
316 0 : - errbuf;
317 0 : goto error;
318 : }
319 :
320 0 : cert = d2i_X509_bio(bio, NULL);
321 0 : if (cert == NULL) {
322 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
323 : "d2i_X509_bio() failed")
324 0 : - errbuf;
325 0 : goto error;
326 : }
327 :
328 0 : if (BIO_eof(bio)) {
329 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
330 : "no issuer certificate in chain")
331 0 : - errbuf;
332 0 : goto error;
333 : }
334 :
335 0 : issuer = d2i_X509_bio(bio, NULL);
336 0 : if (issuer == NULL) {
337 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
338 0 : "d2i_X509_bio() failed") - errbuf;
339 0 : goto error;
340 : }
341 :
342 0 : chain = sk_X509_new_null();
343 0 : if (chain == NULL) {
344 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
345 0 : "sk_X509_new_null() failed") - errbuf;
346 0 : goto error;
347 : }
348 :
349 0 : (void) sk_X509_push(chain, issuer);
350 :
351 0 : if (OCSP_basic_verify(basic, chain, NULL, OCSP_NOVERIFY) != 1) {
352 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
353 0 : "OCSP_basic_verify() failed") - errbuf;
354 0 : goto error;
355 : }
356 :
357 0 : id = OCSP_cert_to_id(NULL, cert, issuer);
358 0 : if (id == NULL) {
359 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
360 0 : "OCSP_cert_to_id() failed") - errbuf;
361 0 : goto error;
362 : }
363 :
364 0 : if (OCSP_resp_find_status(basic, id, &n, NULL, NULL,
365 : &thisupdate, &nextupdate)
366 : != 1)
367 : {
368 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
369 : "certificate status not found in the "
370 0 : "OCSP response") - errbuf;
371 0 : goto error;
372 : }
373 :
374 0 : if (n != V_OCSP_CERTSTATUS_GOOD) {
375 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
376 : "certificate status \"%s\" in the OCSP "
377 : "response", OCSP_cert_status_str(n))
378 0 : - errbuf;
379 0 : goto error;
380 : }
381 :
382 0 : if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {
383 0 : *errbuf_size = ngx_snprintf(errbuf, *errbuf_size,
384 0 : "OCSP_check_validity() failed") - errbuf;
385 0 : goto error;
386 : }
387 :
388 0 : sk_X509_free(chain);
389 0 : X509_free(cert);
390 0 : X509_free(issuer);
391 0 : BIO_free(bio);
392 0 : OCSP_CERTID_free(id);
393 0 : OCSP_BASICRESP_free(basic);
394 0 : OCSP_RESPONSE_free(ocsp);
395 :
396 0 : return NGX_OK;
397 :
398 0 : error:
399 :
400 0 : if (chain) {
401 0 : sk_X509_free(chain);
402 : }
403 :
404 0 : if (id) {
405 0 : OCSP_CERTID_free(id);
406 : }
407 :
408 0 : if (basic) {
409 0 : OCSP_BASICRESP_free(basic);
410 : }
411 :
412 0 : if (ocsp) {
413 0 : OCSP_RESPONSE_free(ocsp);
414 : }
415 :
416 0 : if (cert) {
417 0 : X509_free(cert);
418 : }
419 :
420 0 : if (issuer) {
421 0 : X509_free(issuer);
422 : }
423 :
424 0 : if (bio) {
425 0 : BIO_free(bio);
426 : }
427 :
428 0 : ERR_clear_error();
429 :
430 0 : return NGX_ERROR;
431 :
432 : #endif /* NGX_HTTP_LUA_USE_OCSP */
433 : }
434 :
435 :
436 : #ifdef NGX_HTTP_LUA_USE_OCSP
437 : static int
438 0 : ngx_http_lua_ssl_empty_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)
439 : {
440 0 : return SSL_TLSEXT_ERR_OK;
441 : }
442 : #endif
443 :
444 :
445 : int
446 0 : ngx_http_lua_ffi_ssl_set_ocsp_status_resp(ngx_http_request_t *r,
447 : const u_char *resp, size_t resp_len, char **err)
448 : {
449 : #ifndef NGX_HTTP_LUA_USE_OCSP
450 :
451 : *err = "no OCSP support";
452 : return NGX_ERROR;
453 :
454 : #else
455 :
456 : u_char *p;
457 : SSL_CTX *ctx;
458 : ngx_ssl_conn_t *ssl_conn;
459 :
460 0 : if (r->connection == NULL || r->connection->ssl == NULL) {
461 0 : *err = "bad request";
462 0 : return NGX_ERROR;
463 : }
464 :
465 0 : ssl_conn = r->connection->ssl->connection;
466 0 : if (ssl_conn == NULL) {
467 0 : *err = "bad ssl conn";
468 0 : return NGX_ERROR;
469 : }
470 :
471 0 : if (ssl_conn->tlsext_status_type == -1) {
472 : dd("no ocsp status req from client");
473 0 : return NGX_DECLINED;
474 : }
475 :
476 : /* we have to register an empty status callback here otherwise
477 : * OpenSSL won't send the response staple. */
478 :
479 0 : ctx = SSL_get_SSL_CTX(ssl_conn);
480 0 : SSL_CTX_set_tlsext_status_cb(ctx,
481 : ngx_http_lua_ssl_empty_status_callback);
482 :
483 0 : p = OPENSSL_malloc(resp_len);
484 0 : if (p == NULL) {
485 0 : *err = "OPENSSL_malloc() failed";
486 0 : return NGX_ERROR;
487 : }
488 :
489 0 : ngx_memcpy(p, resp, resp_len);
490 :
491 : dd("set ocsp resp: resp_len=%d", (int) resp_len);
492 0 : (void) SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, resp_len);
493 :
494 0 : return NGX_OK;
495 :
496 : #endif /* NGX_HTTP_LUA_USE_OCSP */
497 : }
498 :
499 : #endif /* NGX_LUA_NO_FFI_API */
500 :
501 :
502 : #endif /* NGX_HTTP_SSL */
|