source: mod_gnutls/test/data/ocsp.py @ d2f2f62

proxy-ticket
Last change on this file since d2f2f62 was d2f2f62, checked in by Krista Karppinen <krista.celestia@…>, 6 months ago

Rewrite OCSP responder in Python

Rewrite the OCSP responder CGI script in Python 3 for consistency
and maintainability

  • Property mode set to 100755
File size: 3.7 KB
Line 
1#!/usr/bin/python3
2# Python 3 wrapper to use "openssl ocsp" as a simple OCSP responder
3#
4# Copyright 2020 Krista Karppinen, Fiona Klute
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you
7# may not use this file except in compliance with the License.  You
8# may obtain a copy of the License at
9#
10#      http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15# implied.  See the License for the specific language governing
16# permissions and limitations under the License.
17
18# This is a CGI script to run the OpenSSL OCSP responder from a web
19# server. The CGI environment must provide the following four
20# variables to configure the OCSP responder:
21#
22# CA_CERT: CA certificate of the CA that issued the certificates this
23# OCSP reponder should provide status information for
24#
25# OCSP_INDEX: CA index file in the format used by OpenSSL
26#
27# OCSP_CERT: Certificate that should be used to sign OCSP reponses
28# (either CA_CERT or a dedicated OCSP signer certificate, see RFC
29# 6960, Section 4.2.2.2)
30#
31# OCSP_KEY: Private key for OCSP_CERT
32#
33# Additionally, the OpenSSL binary to use can be configured through
34# the OPENSSL environment variable. If it is not set, the PATH will be
35# searched.
36
37from http import HTTPStatus
38import os
39import shutil
40import subprocess
41import sys
42
43
44REQUEST_TYPE = 'application/ocsp-request'
45RESPONSE_TYPE = 'application/ocsp-response'
46
47
48def stdout(data):
49    sys.stdout.buffer.write(data)
50
51def stdout_line(line):
52    stdout(line.encode('utf-8'))
53    stdout(b'\n')
54
55def stdout_status(status, content_type='text/plain'):
56    stdout_line(f'Status: {status.value} {status.phrase}')
57    stdout_line(f'Content-Type: {content_type}\n')
58
59def stdout_response(status, response):
60    stdout_status(status, content_type=RESPONSE_TYPE)
61    stdout(response)
62
63
64def handle_get():
65    # GET OCSP requests are allowed by RFC 6960, Appendix A.1, but
66    # not implemented here. It should be possible to extract a GET
67    # request from the PATH_INFO CGI variable.
68    stdout_status(HTTPStatus.METHOD_NOT_ALLOWED)
69    stdout_line('OCSP GET request not implemented.')
70
71def handle_post():
72    content_type = os.getenv('CONTENT_TYPE')
73    content_length = os.getenv('CONTENT_LENGTH')
74    if content_type != REQUEST_TYPE or not content_length:
75        stdout_status(HTTPStatus.UNSUPPORTED_MEDIA_TYPE)
76        stdout_line(f'POST request must contain {REQUEST_TYPE} data.')
77        return
78
79    try:
80        openssl = os.getenv('OPENSSL') or shutil.which('openssl')
81        openssl_run = subprocess.run([openssl, 'ocsp',
82            '-index', os.getenv('OCSP_INDEX'),
83            '-CA', os.getenv('CA_CERT'),
84            '-rsigner', os.getenv('OCSP_CERT'),
85            '-rkey', os.getenv('OCSP_KEY'),
86            '-nmin', os.getenv('OCSP_VALID_MIN', '5'),
87            '-reqin', '-', '-respout', '-'],
88            stdin=sys.stdin.buffer, capture_output=True)
89
90        if openssl_run.returncode == 0:
91            stdout_response(HTTPStatus.OK, openssl_run.stdout)
92            sys.stderr.buffer.write(openssl_run.stderr)
93        else:
94            raise Exception('openssl process exited with return code '
95                            f'{openssl_run.returncode}, stdout: '
96                            f'{openssl_run.stdout}, stderr: '
97                            f'{openssl_run.stderr}')
98    except:
99        stdout_status(HTTPStatus.INTERNAL_SERVER_ERROR)
100        raise
101
102
103if __name__ == '__main__':
104    method = os.getenv('REQUEST_METHOD')
105    if method == 'GET':
106        handle_get()
107    elif method == 'POST':
108        handle_post()
109    else:
110        stdout_status(HTTPStatus.METHOD_NOT_ALLOWED)
Note: See TracBrowser for help on using the repository browser.