X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=apps%2Focsp.c;fp=apps%2Focsp.c;h=5f9c5cf326126c94a86b4c9d8dc0bdde8301148e;hp=d85892202abe19f933068f73f4b670897a1c320b;hb=582311d7b469b4f57a29e9c3965c4d1eb4b477d4;hpb=d8c78e5f4ac5c882ca878b9e9038896d5786aafa diff --git a/apps/ocsp.c b/apps/ocsp.c index d85892202a..5f9c5cf326 100644 --- a/apps/ocsp.c +++ b/apps/ocsp.c @@ -22,6 +22,7 @@ /* Needs to be included before the openssl headers */ #include "apps.h" +#include "http_server.h" #include "progs.h" #include "internal/sockets.h" #include @@ -31,44 +32,12 @@ #include #include #include -#include DEFINE_STACK_OF(OCSP_CERTID) DEFINE_STACK_OF(CONF_VALUE) DEFINE_STACK_OF(X509) DEFINE_STACK_OF_STRING() -#ifndef HAVE_FORK -# if defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_WINDOWS) -# define HAVE_FORK 0 -# else -# define HAVE_FORK 1 -# endif -#endif - -#if HAVE_FORK -# undef NO_FORK -#else -# define NO_FORK -#endif - -#if !defined(NO_FORK) && !defined(OPENSSL_NO_SOCK) \ - && !defined(OPENSSL_NO_POSIX_IO) -# define OCSP_DAEMON -# include -# include -# include -# include -# define MAXERRLEN 1000 /* limit error text sent to syslog to 1000 bytes */ -#else -# undef LOG_INFO -# undef LOG_WARNING -# undef LOG_ERR -# define LOG_INFO 0 -# define LOG_WARNING 1 -# define LOG_ERR 2 -#endif - #if defined(OPENSSL_SYS_VXWORKS) /* not supported */ int setpgid(pid_t pid, pid_t pgid) @@ -105,19 +74,13 @@ static void make_ocsp_response(BIO *err, OCSP_RESPONSE **resp, OCSP_REQUEST *req const EVP_MD *resp_md); static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser); -static BIO *init_responder(const char *port); -static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, int timeout); -static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp); -static void log_message(int level, const char *fmt, ...); +static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, + int timeout); +static int send_ocsp_response(BIO *cbio, const OCSP_RESPONSE *resp); static char *prog; -static int multi = 0; -#ifdef OCSP_DAEMON -static int acfd = (int) INVALID_SOCKET; +#ifdef HTTP_DAEMON static int index_changed(CA_DB *); -static void spawn_loop(void); -static int print_syslog(const char *str, size_t len, void *levPtr); -static void socket_timeout(int signum); #endif typedef enum OPTION_choice { @@ -162,7 +125,7 @@ const OPTIONS ocsp_options[] = { "Connection timeout (in seconds) to the OCSP responder"}, {"resp_no_certs", OPT_RESP_NO_CERTS, '-', "Don't include any certificates in response"}, -#ifdef OCSP_DAEMON +#ifdef HTTP_DAEMON {"multi", OPT_MULTI, 'p', "run multiple responder processes"}, #endif {"no_certs", OPT_NO_CERTS, '-', @@ -540,7 +503,7 @@ int ocsp_main(int argc, char **argv) trailing_md = 1; break; case OPT_MULTI: -#ifdef OCSP_DAEMON +#ifdef HTTP_DAEMON multi = atoi(opt_arg()); #endif break; @@ -584,9 +547,14 @@ int ocsp_main(int argc, char **argv) } if (req == NULL && port != NULL) { - acbio = init_responder(port); +#ifndef OPENSSL_NO_SOCK + acbio = http_server_init_bio(prog, port); if (acbio == NULL) goto end; +#else + BIO_printf(bio_err, "Cannot act as server - sockets not supported\n"); + goto end; +#endif } if (rsignfile != NULL) { @@ -630,20 +598,20 @@ int ocsp_main(int argc, char **argv) } } -#ifdef OCSP_DAEMON +#ifdef HTTP_DAEMON if (multi && acbio != NULL) - spawn_loop(); + spawn_loop(prog); if (acbio != NULL && req_timeout > 0) signal(SIGALRM, socket_timeout); #endif if (acbio != NULL) - log_message(LOG_INFO, "waiting for OCSP client connections..."); + log_message(prog, LOG_INFO, "waiting for OCSP client connections..."); redo_accept: if (acbio != NULL) { -#ifdef OCSP_DAEMON +#ifdef HTTP_DAEMON if (index_changed(rdb)) { CA_DB *newrdb = load_index(ridx_filename, NULL); @@ -652,7 +620,7 @@ redo_accept: rdb = newrdb; } else { free_index(newrdb); - log_message(LOG_ERR, "error reloading updated index: %s", + log_message(prog, LOG_ERR, "error reloading updated index: %s", ridx_filename); } } @@ -663,9 +631,8 @@ redo_accept: goto redo_accept; if (req == NULL) { - resp = - OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, - NULL); + resp = OCSP_response_create(OCSP_RESPONSE_STATUS_MALFORMEDREQUEST, + NULL); send_ocsp_response(cbio, resp); goto done_resp; } @@ -733,7 +700,7 @@ redo_accept: goto end; #else BIO_printf(bio_err, - "Error creating connect BIO - sockets not supported.\n"); + "Error creating connect BIO - sockets not supported\n"); goto end; #endif } else if (respin != NULL) { @@ -873,41 +840,7 @@ redo_accept: return ret; } -static void -log_message(int level, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); -#ifdef OCSP_DAEMON - if (multi) { - char buf[1024]; - if (vsnprintf(buf, sizeof(buf), fmt, ap) > 0) { - syslog(level, "%s", buf); - } - if (level >= LOG_ERR) - ERR_print_errors_cb(print_syslog, &level); - } -#endif - if (!multi) { - BIO_printf(bio_err, "%s: ", prog); - BIO_vprintf(bio_err, fmt, ap); - BIO_printf(bio_err, "\n"); - } - va_end(ap); -} - -#ifdef OCSP_DAEMON - -static int print_syslog(const char *str, size_t len, void *levPtr) -{ - int level = *(int *)levPtr; - int ilen = (len > MAXERRLEN) ? MAXERRLEN : len; - - syslog(level, "%.*s", ilen, str); - - return ilen; -} +#ifdef HTTP_DAEMON static int index_changed(CA_DB *rdb) { @@ -925,131 +858,6 @@ static int index_changed(CA_DB *rdb) return 0; } -static void killall(int ret, pid_t *kidpids) -{ - int i; - - for (i = 0; i < multi; ++i) - if (kidpids[i] != 0) - (void)kill(kidpids[i], SIGTERM); - OPENSSL_free(kidpids); - sleep(1); - exit(ret); -} - -static int termsig = 0; - -static void noteterm (int sig) -{ - termsig = sig; -} - -/* - * Loop spawning up to `multi` child processes, only child processes return - * from this function. The parent process loops until receiving a termination - * signal, kills extant children and exits without returning. - */ -static void spawn_loop(void) -{ - pid_t *kidpids = NULL; - int status; - int procs = 0; - int i; - - openlog(prog, LOG_PID, LOG_DAEMON); - - if (setpgid(0, 0)) { - syslog(LOG_ERR, "fatal: error detaching from parent process group: %s", - strerror(errno)); - exit(1); - } - kidpids = app_malloc(multi * sizeof(*kidpids), "child PID array"); - for (i = 0; i < multi; ++i) - kidpids[i] = 0; - - signal(SIGINT, noteterm); - signal(SIGTERM, noteterm); - - while (termsig == 0) { - pid_t fpid; - - /* - * Wait for a child to replace when we're at the limit. - * Slow down if a child exited abnormally or waitpid() < 0 - */ - while (termsig == 0 && procs >= multi) { - if ((fpid = waitpid(-1, &status, 0)) > 0) { - for (i = 0; i < procs; ++i) { - if (kidpids[i] == fpid) { - kidpids[i] = 0; - --procs; - break; - } - } - if (i >= multi) { - syslog(LOG_ERR, "fatal: internal error: " - "no matching child slot for pid: %ld", - (long) fpid); - killall(1, kidpids); - } - if (status != 0) { - if (WIFEXITED(status)) - syslog(LOG_WARNING, "child process: %ld, exit status: %d", - (long)fpid, WEXITSTATUS(status)); - else if (WIFSIGNALED(status)) - syslog(LOG_WARNING, "child process: %ld, term signal %d%s", - (long)fpid, WTERMSIG(status), -#ifdef WCOREDUMP - WCOREDUMP(status) ? " (core dumped)" : -#endif - ""); - sleep(1); - } - break; - } else if (errno != EINTR) { - syslog(LOG_ERR, "fatal: waitpid(): %s", strerror(errno)); - killall(1, kidpids); - } - } - if (termsig) - break; - - switch(fpid = fork()) { - case -1: /* error */ - /* System critically low on memory, pause and try again later */ - sleep(30); - break; - case 0: /* child */ - OPENSSL_free(kidpids); - signal(SIGINT, SIG_DFL); - signal(SIGTERM, SIG_DFL); - if (termsig) - _exit(0); - if (RAND_poll() <= 0) { - syslog(LOG_ERR, "fatal: RAND_poll() failed"); - _exit(1); - } - return; - default: /* parent */ - for (i = 0; i < multi; ++i) { - if (kidpids[i] == 0) { - kidpids[i] = fpid; - procs++; - break; - } - } - if (i >= multi) { - syslog(LOG_ERR, "fatal: internal error: no free child slots"); - killall(1, kidpids); - } - break; - } - } - - /* The loop above can only break on termsig */ - syslog(LOG_INFO, "terminating on signal: %d", termsig); - killall(0, kidpids); -} #endif static int add_ocsp_cert(OCSP_REQUEST **req, X509 *cert, @@ -1336,209 +1144,32 @@ static char **lookup_serial(CA_DB *db, ASN1_INTEGER *ser) return rrow; } -/* Quick and dirty OCSP server: read in and parse input request */ - -static BIO *init_responder(const char *port) -{ -#ifdef OPENSSL_NO_SOCK - BIO_printf(bio_err, - "Error setting up accept BIO - sockets not supported.\n"); - return NULL; -#else - BIO *acbio = NULL, *bufbio = NULL; - - bufbio = BIO_new(BIO_f_buffer()); - if (bufbio == NULL) - goto err; - acbio = BIO_new(BIO_s_accept()); - if (acbio == NULL - || BIO_set_bind_mode(acbio, BIO_BIND_REUSEADDR) < 0 - || BIO_set_accept_port(acbio, port) < 0) { - log_message(LOG_ERR, "Error setting up accept BIO"); - goto err; - } - - BIO_set_accept_bios(acbio, bufbio); - bufbio = NULL; - if (BIO_do_accept(acbio) <= 0) { - log_message(LOG_ERR, "Error starting accept"); - goto err; - } - - return acbio; - - err: - BIO_free_all(acbio); - BIO_free(bufbio); - return NULL; -#endif -} - -#ifndef OPENSSL_NO_SOCK -/* - * Decode %xx URL-decoding in-place. Ignores mal-formed sequences. - */ -static int urldecode(char *p) -{ - unsigned char *out = (unsigned char *)p; - unsigned char *save = out; - - for (; *p; p++) { - if (*p != '%') - *out++ = *p; - else if (isxdigit(_UC(p[1])) && isxdigit(_UC(p[2]))) { - /* Don't check, can't fail because of ixdigit() call. */ - *out++ = (OPENSSL_hexchar2int(p[1]) << 4) - | OPENSSL_hexchar2int(p[2]); - p += 2; - } - else - return -1; - } - *out = '\0'; - return (int)(out - save); -} -#endif - -#ifdef OCSP_DAEMON -static void socket_timeout(int signum) -{ - if (acfd != (int)INVALID_SOCKET) - (void)shutdown(acfd, SHUT_RD); -} -#endif - static int do_responder(OCSP_REQUEST **preq, BIO **pcbio, BIO *acbio, int timeout) { -#ifdef OPENSSL_NO_SOCK - return 0; +#ifndef OPENSSL_NO_SOCK + return http_server_get_asn1_req(ASN1_ITEM_rptr(OCSP_RESPONSE), + (ASN1_VALUE **)preq, pcbio, acbio, + prog, 1 /* accept_get */, timeout); #else - int len; - OCSP_REQUEST *req = NULL; - char inbuf[2048], reqbuf[2048]; - char *p, *q; - BIO *cbio = NULL, *getbio = NULL, *b64 = NULL; - const char *client; - + BIO_printf(bio_err, + "Error getting OCSP request - sockets not supported\n"); *preq = NULL; - - /* Connection loss before accept() is routine, ignore silently */ - if (BIO_do_accept(acbio) <= 0) - return 0; - - cbio = BIO_pop(acbio); - *pcbio = cbio; - client = BIO_get_peer_name(cbio); - -# ifdef OCSP_DAEMON - if (timeout > 0) { - (void) BIO_get_fd(cbio, &acfd); - alarm(timeout); - } -# endif - - /* Read the request line. */ - len = BIO_gets(cbio, reqbuf, sizeof(reqbuf)); - if (len <= 0) - goto out; - - if (strncmp(reqbuf, "GET ", 4) == 0) { - /* Expecting GET {sp} /URL {sp} HTTP/1.x */ - for (p = reqbuf + 4; *p == ' '; ++p) - continue; - if (*p != '/') { - log_message(LOG_INFO, "Invalid request -- bad URL: %s", client); - goto out; - } - p++; - - /* Splice off the HTTP version identifier. */ - for (q = p; *q; q++) - if (*q == ' ') - break; - if (strncmp(q, " HTTP/1.", 8) != 0) { - log_message(LOG_INFO, - "Invalid request -- bad HTTP version: %s", client); - goto out; - } - *q = '\0'; - - /* - * Skip "GET / HTTP..." requests often used by load-balancers. Note: - * 'p' was incremented above to point to the first byte *after* the - * leading slash, so with 'GET / ' it is now an empty string. - */ - if (p[0] == '\0') - goto out; - - len = urldecode(p); - if (len <= 0) { - log_message(LOG_INFO, - "Invalid request -- bad URL encoding: %s", client); - goto out; - } - if ((getbio = BIO_new_mem_buf(p, len)) == NULL - || (b64 = BIO_new(BIO_f_base64())) == NULL) { - log_message(LOG_ERR, "Could not allocate base64 bio: %s", client); - goto out; - } - BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); - getbio = BIO_push(b64, getbio); - } else if (strncmp(reqbuf, "POST ", 5) != 0) { - log_message(LOG_INFO, "Invalid request -- bad HTTP verb: %s", client); - goto out; - } - - /* Read and skip past the headers. */ - for (;;) { - len = BIO_gets(cbio, inbuf, sizeof(inbuf)); - if (len <= 0) - goto out; - if ((inbuf[0] == '\r') || (inbuf[0] == '\n')) - break; - } - -# ifdef OCSP_DAEMON - /* Clear alarm before we close the client socket */ - alarm(0); - timeout = 0; -# endif - - /* Try to read OCSP request */ - if (getbio != NULL) { - req = d2i_OCSP_REQUEST_bio(getbio, NULL); - BIO_free_all(getbio); - } else { - req = d2i_OCSP_REQUEST_bio(cbio, NULL); - } - - if (req == NULL) - log_message(LOG_ERR, "Error parsing OCSP request"); - - *preq = req; - -out: -# ifdef OCSP_DAEMON - if (timeout > 0) - alarm(0); - acfd = (int)INVALID_SOCKET; -# endif - return 1; + return 0; #endif } -static int send_ocsp_response(BIO *cbio, OCSP_RESPONSE *resp) +static int send_ocsp_response(BIO *cbio, const OCSP_RESPONSE *resp) { - char http_resp[] = - "HTTP/1.0 200 OK\r\nContent-type: application/ocsp-response\r\n" - "Content-Length: %d\r\n\r\n"; - if (cbio == NULL) - return 0; - BIO_printf(cbio, http_resp, i2d_OCSP_RESPONSE(resp, NULL)); - i2d_OCSP_RESPONSE_bio(cbio, resp); - (void)BIO_flush(cbio); - return 1; +#ifndef OPENSSL_NO_SOCK + return http_server_send_asn1_resp(cbio, "application/ocsp-response", + ASN1_ITEM_rptr(OCSP_RESPONSE), + (const ASN1_VALUE *)resp); +#else + BIO_printf(bio_err, + "Error sending OCSP response - sockets not supported\n"); + return 0; +#endif } #ifndef OPENSSL_NO_SOCK