From 08a676e4253975c56620b738dbd61ccadc718ca7 Mon Sep 17 00:00:00 2001 From: Ron Barber Date: Mon, 20 Jan 2014 11:30:20 -0600 Subject: [PATCH 1/5] TS-612 ATS does not allow password protected certificates --- .../configuration/ssl_multicert.config.en.rst | 21 +- iocore/net/SSLUtils.cc | 179 +++++++++++++++++- lib/ts/Makefile.am | 2 + lib/ts/ink_process.cc | 174 +++++++++++++++++ lib/ts/ink_process.h | 42 ++++ lib/ts/libts.h | 1 + proxy/config/ssl_multicert.config.default | 10 +- 7 files changed, 421 insertions(+), 8 deletions(-) create mode 100644 lib/ts/ink_process.cc create mode 100644 lib/ts/ink_process.h diff --git a/doc/reference/configuration/ssl_multicert.config.en.rst b/doc/reference/configuration/ssl_multicert.config.en.rst index 331e47e862a..c1da0042074 100644 --- a/doc/reference/configuration/ssl_multicert.config.en.rst +++ b/doc/reference/configuration/ssl_multicert.config.en.rst @@ -75,6 +75,16 @@ ssl_ca_name=FILENAME the certificate chain. `FILENAME` is resolved relative to the :ts:cv:`proxy.config.ssl.CA.cert.path` configuration variable. +ssl_key_pass_dialog=[builtin|exec:/path/to/program] + Method used to provide a pass phrase for encrypted private keys. + Two options are supported: builtin and exec + ``builtin`` - Requests passphrase via stdin/stdout. Useful for + debugging. + ``exec:`` - Executes a program and accepts the output from stdout for + the pass phrase. The intent is that this program runs a security + check to ensure that the system is not compromised by an attacker + before providing the pass phrase. + ssl_ticket_enabled=1|0 Enable :rfc:`5077` stateless TLS session tickets. To support this, OpenSSL should be upgraded to version 0.9.8f or higher. This @@ -153,9 +163,18 @@ key. dest_ip=111.11.11.1 ssl_cert_name=server.pem ssl_ticket_enabled=1 ticket_key_name=ticket.key The following example configures Traffic Server to use the SSL -certificate ``server.pem`` and disable sessiont ticket for all +certificate ``server.pem`` and disable session ticket for all requests to the IP address 111.11.11.1. :: dest_ip=111.11.11.1 ssl_cert_name=server.pem ssl_ticket_enabled=0 + +The following example configures Traffic Server to use the SSL +certificate ``server.pem`` which includes an encrypted private key. +The external program /usr/bin/mypass will be called on startup with one +parameter (foo), the program will return the pass phrase to decrypt the key. + +:: + + ssl_cert_name=server.pem ssl_key_pass_dialog="exec:/usr/bin/mypass foo" diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 833ac7de759..63af77f7e89 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -30,6 +30,8 @@ #include #include #include +#include +#include #if HAVE_OPENSSL_EVP_H #include @@ -54,6 +56,10 @@ #define SSL_CA_TAG "ssl_ca_name" #define SSL_SESSION_TICKET_ENABLED "ssl_ticket_enabled" #define SSL_SESSION_TICKET_KEY_FILE_TAG "ticket_key_name" +#define SSL_KEY_PASS_DIALOG "ssl_key_pass_dialog" + +// define max accepted passphrase length +#define SSL_PASS_MAX 256 #ifndef evp_md_func #ifdef OPENSSL_NO_SHA256 @@ -293,6 +299,154 @@ ssl_context_enable_tickets(SSL_CTX * ctx, const char * ticket_key_path) #endif /* TS_USE_TLS_TICKETS */ } +struct passphrase_cb_userdata +{ + const SSLConfigParams * _configParams; + const char * _serverDialog; + const char * _serverCert; + const char * _serverKey; + + passphrase_cb_userdata(const SSLConfigParams *params,const char *dialog, const char *cert, const char *key) : + _configParams(params), _serverDialog(dialog), _serverCert(cert), _serverKey(key) {} +}; + +int +ssl_getpassword(const char* prompt, char* buffer, int size) +{ + fprintf(stdout, "%s", prompt); + + // disable echo and line buffering + struct termios tty_attr; + + if (tcgetattr(STDIN_FILENO, &tty_attr) < 0) + return -1; + + // save current setting + const tcflag_t c_lflag = tty_attr.c_lflag; + + tty_attr.c_lflag &= ~ICANON; // no buffer, no backspace + tty_attr.c_lflag &= ~ECHO; // no echo + tty_attr.c_lflag &= ~ISIG; // no signal for ctrl-c + + if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0) + return -1; + + int i = 0; + int ch = 0; + *buffer = 0; + while ((ch = getchar()) != '\n' && ch != EOF) { + // make sure room in buffer + if (i >= size - 1) { + return -1; + } + + buffer[i] = ch; + buffer[++i] = 0; + } + + // restore previous setting + tty_attr.c_lflag = c_lflag; + + if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0) + return -1; + + return i; +} + +int +ssl_private_key_passphrase_callback(char *buf, int size, int rwflag, void *userdata) +{ + static bool oneTime = false; + *buf = 0; + passphrase_cb_userdata *ud = static_cast (userdata); + + // if no dialog configured, leave + if(NULL == ud->_serverDialog) { + return 0; + } + + enum SSL_PASSPHASE_MODE + { + SSL_PASSPHASE_MODE_BUILTIN = 0, + SSL_PASSPHASE_MODE_EXEC = 1, + }; + + SSL_PASSPHASE_MODE mode = SSL_PASSPHASE_MODE_BUILTIN; + const char *exec = NULL; + char pass[SSL_PASS_MAX]; + + Debug("ssl", "ssl_private_key_passphrase_callback rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog); + + if(strncmp(ud->_serverDialog,"exec:",5) == 0) { + mode = SSL_PASSPHASE_MODE_EXEC; + exec = &ud->_serverDialog[5]; + } + // only respond to reading private keys, not writing them (does ats even do that?) + if (0 == rwflag) { + if (SSL_PASSPHASE_MODE_BUILTIN == mode) { + // output request + if (!oneTime) { + fprintf(stdout, "Some of your private key files are encrypted for security reasons.\n"); + fprintf(stdout, "In order to read them you have to provide the pass phrases.\n"); + oneTime = true; + } + fprintf(stdout, "ssl_cert_name=%s", ud->_serverCert); + if (ud->_serverKey) { // output ssl_key_name if provided + fprintf(stdout, " ssl_key_name=%s", ud->_serverKey); + } + fprintf(stdout, "\n"); + // get passphrase + // if buildin mode, use getpass fn, otherwise, read a line from stdin + if (SSL_PASSPHASE_MODE_BUILTIN == mode) { + // if error, then no passphrase + if (ssl_getpassword("Enter passphrase:", pass, sizeof (pass)) <= 0) { + *pass = 0; + } + fprintf(stdout, "\n"); + } else { + char * line = NULL; + size_t len = 0; + ssize_t read; + + read = getline(&line, &len, stdin); // get the line + + // validate response + if (read > 0 && line) { + memcpy(pass, line, read); + pass[read] = 0; + } + if (line) { + free(line); + } + } + if (strlen(pass) < static_cast (size)) { + strcpy(buf, pass); + } + } else { // mode is exec + // execute the dialog program and use the first line output as the passphrase + char pwd[1024]; + getcwd(pwd, 1024); + FILE *f = ink_popen(exec, "r"); + if (f) { + if (fgets(pass, sizeof (pass), f)) { + // remove any ending CR or LF + for(char *psrc=pass, *pdst = buf;*psrc;psrc++,pdst++) { + *pdst = *psrc; + if(*pdst == '\n' || *pdst == '\r') { + *pdst = 0; + break; + } + } + } + ink_pclose(f); + } else {// popen failed + Error("could not open dialog '%s' - %s", exec, strerror(errno)); + } + } + } + return strlen(buf); +} + void SSLInitializeLibrary() { @@ -407,7 +561,8 @@ SSLInitServerContext( const SSLConfigParams * params, const char * serverCertPtr, const char * serverCaCertPtr, - const char * serverKeyPtr) + const char * serverKeyPtr, + const char * serverDialog) { int session_id_context; int server_verify_client; @@ -435,6 +590,11 @@ SSLInitServerContext( #endif SSL_CTX_set_quiet_shutdown(ctx, 1); + passphrase_cb_userdata ud(params, serverDialog, serverCertPtr, serverKeyPtr); + + SSL_CTX_set_default_passwd_cb(ctx, ssl_private_key_passphrase_callback); + SSL_CTX_set_default_passwd_cb_userdata(ctx, &ud); + // XXX OpenSSL recommends that we should use SSL_CTX_use_certificate_chain_file() here. That API // also loads only the first certificate, but it allows the intermediate CA certificate chain to // be in the same file. SSL_CTX_use_certificate_chain_file() was added in OpenSSL 0.9.3. @@ -671,13 +831,14 @@ ssl_store_ssl_context( xptr& ca, xptr& key, const int session_ticket_enabled, - xptr& ticket_key_filename) + xptr& ticket_key_filename, + xptr& dialog) { SSL_CTX * ctx; xptr certpath; xptr session_key_path; - ctx = ssl_context_enable_sni(SSLInitServerContext(params, cert, ca, key), lookup); + ctx = ssl_context_enable_sni(SSLInitServerContext(params, cert, ca, key, dialog), lookup); if (!ctx) { return false; } @@ -736,7 +897,8 @@ ssl_extract_certificate( xptr& ca, // CA public certificate xptr& key, // Private key int& session_ticket_enabled, // session ticket enabled - xptr& ticket_key_filename) // session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)] + xptr& ticket_key_filename, // session key file. [key_name (16Byte) + HMAC_secret (16Byte) + AES_key (16Byte)] + xptr& dialog) // Private key dialog { for (int i = 0; i < MATCHER_MAX_TOKENS; ++i) { const char * label; @@ -772,6 +934,10 @@ ssl_extract_certificate( if (strcasecmp(label, SSL_SESSION_TICKET_KEY_FILE_TAG) == 0) { ticket_key_filename = ats_strdup(value); } + + if (strcasecmp(label, SSL_KEY_PASS_DIALOG) == 0) { + dialog = ats_strdup(value); + } } if (!cert) { @@ -828,6 +994,7 @@ SSLParseCertificateConfiguration( xptr key; int session_ticket_enabled = -1; xptr ticket_key_filename; + xptr dialog; const char * errPtr; errPtr = parseConfigLine(line, &line_info, &sslCertTags); @@ -837,8 +1004,8 @@ SSLParseCertificateConfiguration( __func__, params->configFilePath, line_num, errPtr); REC_SignalError(errBuf, alarmAlready); } else { - if (ssl_extract_certificate(&line_info, addr, cert, ca, key, session_ticket_enabled, ticket_key_filename)) { - if (!ssl_store_ssl_context(params, lookup, addr, cert, ca, key, session_ticket_enabled, ticket_key_filename)) { + if (ssl_extract_certificate(&line_info, addr, cert, ca, key, session_ticket_enabled, ticket_key_filename,dialog)) { + if (!ssl_store_ssl_context(params, lookup, addr, cert, ca, key, session_ticket_enabled, ticket_key_filename,dialog)) { Error("failed to load SSL certificate specification from %s line %u", params->configFilePath, line_num); } diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am index 18461b0bd73..74c58e1b02c 100644 --- a/lib/ts/Makefile.am +++ b/lib/ts/Makefile.am @@ -128,6 +128,8 @@ libtsutil_la_SOURCES = \ ink_mutex.cc \ ink_mutex.h \ ink_platform.h \ + ink_process.cc \ + ink_process.h \ ink_queue.cc \ ink_queue.h \ ink_queue_ext.cc \ diff --git a/lib/ts/ink_process.cc b/lib/ts/ink_process.cc new file mode 100644 index 00000000000..e5d46abf191 --- /dev/null +++ b/lib/ts/ink_process.cc @@ -0,0 +1,174 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/************************************************************************** + + ink_process.cc + + Generic process interface. +**************************************************************************/ +#include "ink_process.h" +#include "ink_resource.h" +#include "ink_memory.h" + +#include +#include +#include +#include + +#if defined(darwin) +# include +#endif + +typedef std::map FilePidMap; +typedef std::map::iterator FilePidMapIter; + +static FilePidMap ink_popen_file_to_pid; + +static void +convert_string_to_argv(const char *cmdLine, char ***argv) +{ + char *str1, *token; + char *saveptr; + int argc = 0; + char * cmdLineCopy = ats_strdup(cmdLine); + char **newArgv; + newArgv = static_cast(ats_malloc(sizeof(char *))); + newArgv[0] = NULL; + + for ( str1 = cmdLineCopy; ; str1 = NULL) { + token = strtok_r(str1, " \t", &saveptr); + if (token == NULL) + break; + newArgv[argc++] = ats_strdup(token); + newArgv = static_cast(ats_realloc(newArgv,(argc+1) * sizeof(char *))); + newArgv[argc] = NULL; + } + ats_free(cmdLineCopy); + + *argv = newArgv; +} + +FILE * +ink_popen(const char *progname, const char *mode) +{ + char **argv; + + convert_string_to_argv(progname,&argv); + + // check to see if we can access the exec + if (access(argv[0], X_OK) == -1) { + /* chdir() in child wouldn't have worked */ + int save = errno; // Error() could change errno + errno = save; + return NULL; + } + + // validate mode + if (*mode != 'r' && *mode != 'w') { + errno = EINVAL; + return NULL; + } + // create read[0] & write[1] ends of pipe + int fds[2]; + if (-1 == pipe(fds)) { + return NULL; + } + + pid_t pid; + FILE *fp; + + // Redirect new standard input (fd 0) or output(fd 1) + posix_spawn_file_actions_t file_actions; + posix_spawn_file_actions_init(&file_actions); + if (*mode == 'r') { // map stdout from child process to readable fd + posix_spawn_file_actions_adddup2(&file_actions, fds[1], STDOUT_FILENO); + fp = fdopen(fds[0], "r"); + } else { // map stdin to child process to a writable fd + posix_spawn_file_actions_adddup2(&file_actions, fds[0], STDIN_FILENO); + fp = fdopen(fds[1], "w"); + } + posix_spawn_file_actions_addclose(&file_actions, fds[0]); + posix_spawn_file_actions_addclose(&file_actions, fds[1]); + + int status; + + char **env = NULL; +#if defined(linux) + env = environ; +#elif defined(darwin) + env = *_NSGetEnviron(); +#endif + + // spawn the new process + status = posix_spawn(&pid, argv[0], &file_actions, NULL, argv, env); + posix_spawn_file_actions_destroy(&file_actions); + // close unused fds + if (*mode == 'r') { + close(fds[1]); // close unused write end + } else { + close(fds[0]); // close unused read end + } + // check status of posix_spawn + if (status == 0) { + ink_popen_file_to_pid[fp] = pid; + } else { // failed to spawn, clean up + fclose(fp); + fp = NULL; + errno = status; + } + + // free up argv + for(int i=0;argv[i];i++) { + ats_free(argv[i]); + } + ats_free(argv); + + return fp; +} + +int +ink_pclose(FILE *f) +{ + int iReturn = -1; + // make sure the FILE * is valid for us + FilePidMapIter it = ink_popen_file_to_pid.find(f); + if (it == ink_popen_file_to_pid.end()) { + errno = ECHILD; + return -1; + } + + pid_t pid = it->second; + int status; + + // wait for process to exit + if (waitpid(pid, &status, 0) != -1) { + iReturn = fclose(f); + } else { // some sort of error occured errno has reason + iReturn = -1; + } + + // removing our mapping + ink_popen_file_to_pid.erase(it); + return iReturn; +} diff --git a/lib/ts/ink_process.h b/lib/ts/ink_process.h new file mode 100644 index 00000000000..59d4d6530fc --- /dev/null +++ b/lib/ts/ink_process.h @@ -0,0 +1,42 @@ +/** @file + + A brief file description + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +/************************************************************************** + Generic process spawning interface + +**************************************************************************/ + +#ifndef _INK_PROCESS_H +#define _INK_PROCESS_H + +#include + +////////////////////////////////////////////////////////////////////////////// +// +// The POSIX threads interface +// +////////////////////////////////////////////////////////////////////////////// + +FILE *ink_popen(const char *progname, const char *mode); +int ink_pclose(FILE *f); +#endif /*_INK_PROCESS_H*/ diff --git a/lib/ts/libts.h b/lib/ts/libts.h index 27c9e921320..c5daa69c385 100644 --- a/lib/ts/libts.h +++ b/lib/ts/libts.h @@ -62,6 +62,7 @@ #include "ink_memory.h" #include "ink_mutex.h" #include "ink_queue.h" +#include "ink_process.h" #include "ink_rand.h" #include "ink_resolver.h" #include "ink_resource.h" diff --git a/proxy/config/ssl_multicert.config.default b/proxy/config/ssl_multicert.config.default index be6099459b7..bc547a27b44 100644 --- a/proxy/config/ssl_multicert.config.default +++ b/proxy/config/ssl_multicert.config.default @@ -28,7 +28,7 @@ # square brackets if they have a port, eg, [::1]:80. # # ssl_key_name=FILENAME -# The name of the file containg the private key for this certificate. +# The name of the file containing the private key for this certificate. # If the key is contained in the certificate file, this field can be # omitted. # @@ -40,9 +40,17 @@ # The name of the file containing the TLS certificate. This is the # only field that is required to be present. # +# ssl_key_pass_dialog=[builtin|exec:/path/to/program] +# Method used to provide a pass phrase for encrypted private keys. +# Two options are supported: builtin and exec +# builtin - Requests passphrase via stdin/stdout. Useful for debugging. +# exec: - Executes a program and uses the stdout output for the pass +# phrase. +# # Examples: # ssl_cert_name=foo.pem # dest_ip=* ssl_cert_name=bar.pem ssl_key_name=barKey.pem # dest_ip=209.131.48.79 ssl_cert_name=server.pem ssl_key_name=serverKey.pem # dest_ip=10.0.0.1:99 ssl_cert_name=port99.pem +# ssl_cert_name=foo.pem ssl_key_pass_dialog="exec:/usr/bin/mypass foo" From af5b5a370547956f504f8d73f7f9ac7dc32a010e Mon Sep 17 00:00:00 2001 From: Ron Barber Date: Tue, 21 Jan 2014 11:24:50 -0600 Subject: [PATCH 2/5] Making environ (in ink_popoen) available to all non-darwin builds --- lib/ts/ink_process.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/ts/ink_process.cc b/lib/ts/ink_process.cc index e5d46abf191..267a96957cf 100644 --- a/lib/ts/ink_process.cc +++ b/lib/ts/ink_process.cc @@ -114,10 +114,10 @@ ink_popen(const char *progname, const char *mode) int status; char **env = NULL; -#if defined(linux) - env = environ; -#elif defined(darwin) +#if defined(darwin) env = *_NSGetEnviron(); +#else + env = environ; #endif // spawn the new process From 1539b1b3d3a021b9ba5e26b6e1b04a2566c781ed Mon Sep 17 00:00:00 2001 From: Ron Barber Date: Tue, 21 Jan 2014 14:44:28 -0600 Subject: [PATCH 3/5] Fixed signature for SSLInitServerContext --- iocore/net/P_SSLUtils.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iocore/net/P_SSLUtils.h b/iocore/net/P_SSLUtils.h index 5ed2d826ec7..a08a5655046 100644 --- a/iocore/net/P_SSLUtils.h +++ b/iocore/net/P_SSLUtils.h @@ -54,7 +54,8 @@ SSLInitServerContext( const SSLConfigParams * param, const char * serverCertPtr, const char * serverCaCertPtr, - const char * serverKeyPtr); + const char * serverKeyPtr, + const char * serverDialog); // Create and initialize a SSL client context. SSL_CTX * SSLInitClientContext(const SSLConfigParams * param); From 370bad00c91219c73d29db174399e9b3d2e95a1a Mon Sep 17 00:00:00 2001 From: Ron Barber Date: Wed, 22 Jan 2014 14:48:57 -0600 Subject: [PATCH 4/5] Incorporating J. Peach's comments --- .../configuration/ssl_multicert.config.en.rst | 31 ++- iocore/net/SSLUtils.cc | 238 ++++++++++-------- lib/ts/ink_process.cc | 174 ------------- lib/ts/ink_process.h | 42 ---- proxy/config/ssl_multicert.config.default | 4 +- 5 files changed, 162 insertions(+), 327 deletions(-) delete mode 100644 lib/ts/ink_process.cc delete mode 100644 lib/ts/ink_process.h diff --git a/doc/reference/configuration/ssl_multicert.config.en.rst b/doc/reference/configuration/ssl_multicert.config.en.rst index c1da0042074..53ae61ebe82 100644 --- a/doc/reference/configuration/ssl_multicert.config.en.rst +++ b/doc/reference/configuration/ssl_multicert.config.en.rst @@ -75,15 +75,21 @@ ssl_ca_name=FILENAME the certificate chain. `FILENAME` is resolved relative to the :ts:cv:`proxy.config.ssl.CA.cert.path` configuration variable. -ssl_key_pass_dialog=[builtin|exec:/path/to/program] - Method used to provide a pass phrase for encrypted private keys. +ssl_key_dialog=builtin|"exec:/path/to/program [args]" + Method used to provide a pass phrase for encrypted private keys. If the + pass phrase is incorrect, SSL negotiation for this dest_ip will fail for + clients who attempt to connect. Two options are supported: builtin and exec - ``builtin`` - Requests passphrase via stdin/stdout. Useful for - debugging. - ``exec:`` - Executes a program and accepts the output from stdout for - the pass phrase. The intent is that this program runs a security - check to ensure that the system is not compromised by an attacker - before providing the pass phrase. + ``builtin`` - Requests pass phrase via stdin/stdout. User will be + provided the ssl_cert_name and be prompted for the pass phrase. + Useful for debugging. + ``exec:`` - Executes program /path/to/program and passes args, if + specified, to the program and reads the output from stdout for + the pass phrase. If args are provided then the entire exec: string + must be quoted with "" (see examples). Arguments with white space + are supported by single quoting ('). The intent is that this + program runs a security check to ensure that the system is not + compromised by an attacker before providing the pass phrase. ssl_ticket_enabled=1|0 Enable :rfc:`5077` stateless TLS session tickets. To support this, @@ -170,11 +176,14 @@ requests to the IP address 111.11.11.1. dest_ip=111.11.11.1 ssl_cert_name=server.pem ssl_ticket_enabled=0 -The following example configures Traffic Server to use the SSL +The following examples configure Traffic Server to use the SSL certificate ``server.pem`` which includes an encrypted private key. The external program /usr/bin/mypass will be called on startup with one -parameter (foo), the program will return the pass phrase to decrypt the key. +parameter (foo) in the first example, and with two parameters (foo) +and (ba r) in the second example, the program (mypass) will return the +pass phrase to decrypt the key. :: - ssl_cert_name=server.pem ssl_key_pass_dialog="exec:/usr/bin/mypass foo" + ssl_cert_name=server.pem ssl_key_dialog="exec:/usr/bin/mypass foo" + ssl_cert_name=server.pem ssl_key_dialog="exec:/usr/bin/mypass foo 'ba r'" diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index 63af77f7e89..be330541c73 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -56,10 +56,7 @@ #define SSL_CA_TAG "ssl_ca_name" #define SSL_SESSION_TICKET_ENABLED "ssl_ticket_enabled" #define SSL_SESSION_TICKET_KEY_FILE_TAG "ticket_key_name" -#define SSL_KEY_PASS_DIALOG "ssl_key_pass_dialog" - -// define max accepted passphrase length -#define SSL_PASS_MAX 256 +#define SSL_KEY_DIALOG "ssl_key_dialog" #ifndef evp_md_func #ifdef OPENSSL_NO_SHA256 @@ -310,19 +307,43 @@ struct passphrase_cb_userdata _configParams(params), _serverDialog(dialog), _serverCert(cert), _serverKey(key) {} }; -int +// RAII implementation for struct termios +struct ssl_termios : public termios +{ + ssl_termios(int fd) { + _fd = -1; + // populate base class data + if(tcgetattr(fd, this) == 0) { // success + _fd = fd; + } + // save our copy + _initialAttr = *this; + } + + ~ssl_termios() { + if(_fd != -1) { + tcsetattr(_fd, 0, &_initialAttr); + } + } + + bool ok() { + return (_fd != -1); + } + +private: + int _fd; + struct termios _initialAttr; +}; + +static int ssl_getpassword(const char* prompt, char* buffer, int size) { fprintf(stdout, "%s", prompt); // disable echo and line buffering - struct termios tty_attr; + ssl_termios tty_attr(STDIN_FILENO); - if (tcgetattr(STDIN_FILENO, &tty_attr) < 0) - return -1; - - // save current setting - const tcflag_t c_lflag = tty_attr.c_lflag; + if(!tty_attr.ok()) return -1; tty_attr.c_lflag &= ~ICANON; // no buffer, no backspace tty_attr.c_lflag &= ~ECHO; // no echo @@ -343,110 +364,104 @@ ssl_getpassword(const char* prompt, char* buffer, int size) buffer[i] = ch; buffer[++i] = 0; } - - // restore previous setting - tty_attr.c_lflag = c_lflag; - - if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0) - return -1; - return i; } -int -ssl_private_key_passphrase_callback(char *buf, int size, int rwflag, void *userdata) +static int +ssl_private_key_passphrase_callback_exec(char *buf, int size, int rwflag, void *userdata) { - static bool oneTime = false; + if(0 == size) return 0; + *buf = 0; passphrase_cb_userdata *ud = static_cast (userdata); - // if no dialog configured, leave - if(NULL == ud->_serverDialog) { - return 0; + char *pass = static_cast (ats_malloc(size)); + + Debug("ssl", "ssl_private_key_passphrase_callback_exec rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog); + + // only respond to reading private keys, not writing them (does ats even do that?) + if (0 == rwflag) { + // execute the dialog program and use the first line output as the passphrase + FILE *f = popen(ud->_serverDialog, "r"); + if (f) { + if (fgets(pass, size, f)) { + // remove any ending CR or LF + for (char *psrc = pass, *pdst = buf; *psrc; psrc++, pdst++) { + *pdst = *psrc; + if (*pdst == '\n' || *pdst == '\r') { + *pdst = 0; + break; + } + } + } + pclose(f); + } else {// popen failed + Error("could not open dialog '%s' - %s", ud->_serverDialog, strerror(errno)); + } } + memset(pass, 0, size); // paranoid, zero out the buffer + ats_free(pass); + return strlen(buf); +} - enum SSL_PASSPHASE_MODE - { - SSL_PASSPHASE_MODE_BUILTIN = 0, - SSL_PASSPHASE_MODE_EXEC = 1, - }; +static int +ssl_private_key_passphrase_callback_builtin(char *buf, int size, int rwflag, void *userdata) +{ + if(0 == size) return 0; - SSL_PASSPHASE_MODE mode = SSL_PASSPHASE_MODE_BUILTIN; - const char *exec = NULL; - char pass[SSL_PASS_MAX]; + *buf = 0; + passphrase_cb_userdata *ud = static_cast (userdata); + + char *pass = static_cast (ats_malloc(size)); Debug("ssl", "ssl_private_key_passphrase_callback rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog); - if(strncmp(ud->_serverDialog,"exec:",5) == 0) { - mode = SSL_PASSPHASE_MODE_EXEC; - exec = &ud->_serverDialog[5]; - } // only respond to reading private keys, not writing them (does ats even do that?) if (0 == rwflag) { - if (SSL_PASSPHASE_MODE_BUILTIN == mode) { - // output request - if (!oneTime) { - fprintf(stdout, "Some of your private key files are encrypted for security reasons.\n"); - fprintf(stdout, "In order to read them you have to provide the pass phrases.\n"); - oneTime = true; - } - fprintf(stdout, "ssl_cert_name=%s", ud->_serverCert); - if (ud->_serverKey) { // output ssl_key_name if provided - fprintf(stdout, " ssl_key_name=%s", ud->_serverKey); - } - fprintf(stdout, "\n"); - // get passphrase - // if buildin mode, use getpass fn, otherwise, read a line from stdin - if (SSL_PASSPHASE_MODE_BUILTIN == mode) { - // if error, then no passphrase - if (ssl_getpassword("Enter passphrase:", pass, sizeof (pass)) <= 0) { - *pass = 0; - } - fprintf(stdout, "\n"); - } else { - char * line = NULL; - size_t len = 0; - ssize_t read; - - read = getline(&line, &len, stdin); // get the line - - // validate response - if (read > 0 && line) { - memcpy(pass, line, read); - pass[read] = 0; - } - if (line) { - free(line); - } - } - if (strlen(pass) < static_cast (size)) { - strcpy(buf, pass); - } - } else { // mode is exec - // execute the dialog program and use the first line output as the passphrase - char pwd[1024]; - getcwd(pwd, 1024); - FILE *f = ink_popen(exec, "r"); - if (f) { - if (fgets(pass, sizeof (pass), f)) { - // remove any ending CR or LF - for(char *psrc=pass, *pdst = buf;*psrc;psrc++,pdst++) { - *pdst = *psrc; - if(*pdst == '\n' || *pdst == '\r') { - *pdst = 0; - break; - } - } - } - ink_pclose(f); - } else {// popen failed - Error("could not open dialog '%s' - %s", exec, strerror(errno)); - } + // output request + fprintf(stdout, "Some of your private key files are encrypted for security reasons.\n"); + fprintf(stdout, "In order to read them you have to provide the pass phrases.\n"); + fprintf(stdout, "ssl_cert_name=%s", ud->_serverCert); + if (ud->_serverKey) { // output ssl_key_name if provided + fprintf(stdout, " ssl_key_name=%s", ud->_serverKey); + } + fprintf(stdout, "\n"); + // get passphrase + // if error, then no passphrase + if (ssl_getpassword("Enter passphrase:", pass, size) <= 0) { + *pass = 0; + } + fprintf(stdout, "\n"); + if (strlen(pass) < static_cast (size)) { + strcpy(buf, pass); } } + memset(pass, 0, size); // paranoid, zero out the buffer + ats_free(pass); return strlen(buf); } +static bool +ssl_private_key_validate_exec(const char *cmdLine) +{ + if(NULL == cmdLine) { + errno = EINVAL; + return false; + } + + bool bReturn = false; + char *cmdLineCopy = ats_strdup(cmdLine); + char *ptr = cmdLineCopy; + + while(*ptr && !isspace(*ptr)) ++ptr; + *ptr = 0; + if (access(cmdLineCopy, X_OK) != -1) { + bReturn = true; + } + ats_free(cmdLineCopy); + return bReturn; +} + void SSLInitializeLibrary() { @@ -590,10 +605,30 @@ SSLInitServerContext( #endif SSL_CTX_set_quiet_shutdown(ctx, 1); + // pass phrase dialog configuration passphrase_cb_userdata ud(params, serverDialog, serverCertPtr, serverKeyPtr); + bool bIsExec = false; + + if(serverDialog && strncmp(serverDialog,"exec:",5) == 0) { + bIsExec = true; + ud._serverDialog = &serverDialog[5]; + // validate the exec program + if(!ssl_private_key_validate_exec(ud._serverDialog)) { + SSLError("failed to access '%s' pass phrase program: %s", (const char *)ud._serverDialog,strerror(errno)); + goto fail; + } + } - SSL_CTX_set_default_passwd_cb(ctx, ssl_private_key_passphrase_callback); - SSL_CTX_set_default_passwd_cb_userdata(ctx, &ud); + if(serverDialog) { + int (*passwd_cb)(char *buf, int size, int rwflag, void *userdata); + if(bIsExec) { // use exec handler call back + passwd_cb = ssl_private_key_passphrase_callback_exec; + } else { // use stdin/out + passwd_cb = ssl_private_key_passphrase_callback_builtin; + } + SSL_CTX_set_default_passwd_cb(ctx, passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(ctx, &ud); + } // XXX OpenSSL recommends that we should use SSL_CTX_use_certificate_chain_file() here. That API // also loads only the first certificate, but it allows the intermediate CA certificate chain to @@ -680,10 +715,17 @@ SSLInitServerContext( goto fail; } } +#define SSL_CLEAR_PW_REFERENCES { \ + memset(static_cast(&ud),0,sizeof(ud));\ + SSL_CTX_set_default_passwd_cb(ctx, NULL);\ + SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL);\ + } + SSL_CLEAR_PW_REFERENCES return ssl_context_enable_ecdh(ctx); fail: + SSL_CLEAR_PW_REFERENCES SSL_CTX_free(ctx); return NULL; } @@ -935,7 +977,7 @@ ssl_extract_certificate( ticket_key_filename = ats_strdup(value); } - if (strcasecmp(label, SSL_KEY_PASS_DIALOG) == 0) { + if (strcasecmp(label, SSL_KEY_DIALOG) == 0) { dialog = ats_strdup(value); } } diff --git a/lib/ts/ink_process.cc b/lib/ts/ink_process.cc deleted file mode 100644 index 267a96957cf..00000000000 --- a/lib/ts/ink_process.cc +++ /dev/null @@ -1,174 +0,0 @@ -/** @file - - A brief file description - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -/************************************************************************** - - ink_process.cc - - Generic process interface. -**************************************************************************/ -#include "ink_process.h" -#include "ink_resource.h" -#include "ink_memory.h" - -#include -#include -#include -#include - -#if defined(darwin) -# include -#endif - -typedef std::map FilePidMap; -typedef std::map::iterator FilePidMapIter; - -static FilePidMap ink_popen_file_to_pid; - -static void -convert_string_to_argv(const char *cmdLine, char ***argv) -{ - char *str1, *token; - char *saveptr; - int argc = 0; - char * cmdLineCopy = ats_strdup(cmdLine); - char **newArgv; - newArgv = static_cast(ats_malloc(sizeof(char *))); - newArgv[0] = NULL; - - for ( str1 = cmdLineCopy; ; str1 = NULL) { - token = strtok_r(str1, " \t", &saveptr); - if (token == NULL) - break; - newArgv[argc++] = ats_strdup(token); - newArgv = static_cast(ats_realloc(newArgv,(argc+1) * sizeof(char *))); - newArgv[argc] = NULL; - } - ats_free(cmdLineCopy); - - *argv = newArgv; -} - -FILE * -ink_popen(const char *progname, const char *mode) -{ - char **argv; - - convert_string_to_argv(progname,&argv); - - // check to see if we can access the exec - if (access(argv[0], X_OK) == -1) { - /* chdir() in child wouldn't have worked */ - int save = errno; // Error() could change errno - errno = save; - return NULL; - } - - // validate mode - if (*mode != 'r' && *mode != 'w') { - errno = EINVAL; - return NULL; - } - // create read[0] & write[1] ends of pipe - int fds[2]; - if (-1 == pipe(fds)) { - return NULL; - } - - pid_t pid; - FILE *fp; - - // Redirect new standard input (fd 0) or output(fd 1) - posix_spawn_file_actions_t file_actions; - posix_spawn_file_actions_init(&file_actions); - if (*mode == 'r') { // map stdout from child process to readable fd - posix_spawn_file_actions_adddup2(&file_actions, fds[1], STDOUT_FILENO); - fp = fdopen(fds[0], "r"); - } else { // map stdin to child process to a writable fd - posix_spawn_file_actions_adddup2(&file_actions, fds[0], STDIN_FILENO); - fp = fdopen(fds[1], "w"); - } - posix_spawn_file_actions_addclose(&file_actions, fds[0]); - posix_spawn_file_actions_addclose(&file_actions, fds[1]); - - int status; - - char **env = NULL; -#if defined(darwin) - env = *_NSGetEnviron(); -#else - env = environ; -#endif - - // spawn the new process - status = posix_spawn(&pid, argv[0], &file_actions, NULL, argv, env); - posix_spawn_file_actions_destroy(&file_actions); - // close unused fds - if (*mode == 'r') { - close(fds[1]); // close unused write end - } else { - close(fds[0]); // close unused read end - } - // check status of posix_spawn - if (status == 0) { - ink_popen_file_to_pid[fp] = pid; - } else { // failed to spawn, clean up - fclose(fp); - fp = NULL; - errno = status; - } - - // free up argv - for(int i=0;argv[i];i++) { - ats_free(argv[i]); - } - ats_free(argv); - - return fp; -} - -int -ink_pclose(FILE *f) -{ - int iReturn = -1; - // make sure the FILE * is valid for us - FilePidMapIter it = ink_popen_file_to_pid.find(f); - if (it == ink_popen_file_to_pid.end()) { - errno = ECHILD; - return -1; - } - - pid_t pid = it->second; - int status; - - // wait for process to exit - if (waitpid(pid, &status, 0) != -1) { - iReturn = fclose(f); - } else { // some sort of error occured errno has reason - iReturn = -1; - } - - // removing our mapping - ink_popen_file_to_pid.erase(it); - return iReturn; -} diff --git a/lib/ts/ink_process.h b/lib/ts/ink_process.h deleted file mode 100644 index 59d4d6530fc..00000000000 --- a/lib/ts/ink_process.h +++ /dev/null @@ -1,42 +0,0 @@ -/** @file - - A brief file description - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -/************************************************************************** - Generic process spawning interface - -**************************************************************************/ - -#ifndef _INK_PROCESS_H -#define _INK_PROCESS_H - -#include - -////////////////////////////////////////////////////////////////////////////// -// -// The POSIX threads interface -// -////////////////////////////////////////////////////////////////////////////// - -FILE *ink_popen(const char *progname, const char *mode); -int ink_pclose(FILE *f); -#endif /*_INK_PROCESS_H*/ diff --git a/proxy/config/ssl_multicert.config.default b/proxy/config/ssl_multicert.config.default index bc547a27b44..69a90ee049e 100644 --- a/proxy/config/ssl_multicert.config.default +++ b/proxy/config/ssl_multicert.config.default @@ -40,7 +40,7 @@ # The name of the file containing the TLS certificate. This is the # only field that is required to be present. # -# ssl_key_pass_dialog=[builtin|exec:/path/to/program] +# ssl_key_dialog=[builtin|exec:/path/to/program] # Method used to provide a pass phrase for encrypted private keys. # Two options are supported: builtin and exec # builtin - Requests passphrase via stdin/stdout. Useful for debugging. @@ -52,5 +52,5 @@ # dest_ip=* ssl_cert_name=bar.pem ssl_key_name=barKey.pem # dest_ip=209.131.48.79 ssl_cert_name=server.pem ssl_key_name=serverKey.pem # dest_ip=10.0.0.1:99 ssl_cert_name=port99.pem -# ssl_cert_name=foo.pem ssl_key_pass_dialog="exec:/usr/bin/mypass foo" +# ssl_cert_name=foo.pem ssl_key_dialog="exec:/usr/bin/mypass foo 'ba r'" From b6274fce636eb6385d191a3974cb4885256ef63c Mon Sep 17 00:00:00 2001 From: Ron Barber Date: Thu, 30 Jan 2014 16:11:48 -0600 Subject: [PATCH 5/5] TS:612 Use pem_password_cb, check OpenSSL version, remove ink_process references, eliminate unnecessary temporary buffers --- iocore/net/SSLUtils.cc | 69 ++++++++++++++++++------------------------ lib/ts/Makefile.am | 2 -- lib/ts/libts.h | 1 - 3 files changed, 30 insertions(+), 42 deletions(-) diff --git a/iocore/net/SSLUtils.cc b/iocore/net/SSLUtils.cc index be330541c73..5d8e7c345f1 100644 --- a/iocore/net/SSLUtils.cc +++ b/iocore/net/SSLUtils.cc @@ -66,6 +66,11 @@ #endif #endif +// openssl version must be 0.9.4 or greater +#if (OPENSSL_VERSION_NUMBER < 0x00090400L) +#error Traffic Server requires an OpenSSL library version 0.9.4 or greater +#endif + #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) // openssl returns a const SSL_METHOD typedef const SSL_METHOD * ink_ssl_method_t; #else @@ -375,8 +380,6 @@ ssl_private_key_passphrase_callback_exec(char *buf, int size, int rwflag, void * *buf = 0; passphrase_cb_userdata *ud = static_cast (userdata); - char *pass = static_cast (ats_malloc(size)); - Debug("ssl", "ssl_private_key_passphrase_callback_exec rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog); // only respond to reading private keys, not writing them (does ats even do that?) @@ -384,12 +387,11 @@ ssl_private_key_passphrase_callback_exec(char *buf, int size, int rwflag, void * // execute the dialog program and use the first line output as the passphrase FILE *f = popen(ud->_serverDialog, "r"); if (f) { - if (fgets(pass, size, f)) { + if (fgets(buf, size, f)) { // remove any ending CR or LF - for (char *psrc = pass, *pdst = buf; *psrc; psrc++, pdst++) { - *pdst = *psrc; - if (*pdst == '\n' || *pdst == '\r') { - *pdst = 0; + for (char *pass = buf; *pass; pass++) { + if (*pass == '\n' || *pass == '\r') { + *pass = 0; break; } } @@ -399,8 +401,6 @@ ssl_private_key_passphrase_callback_exec(char *buf, int size, int rwflag, void * Error("could not open dialog '%s' - %s", ud->_serverDialog, strerror(errno)); } } - memset(pass, 0, size); // paranoid, zero out the buffer - ats_free(pass); return strlen(buf); } @@ -412,8 +412,6 @@ ssl_private_key_passphrase_callback_builtin(char *buf, int size, int rwflag, voi *buf = 0; passphrase_cb_userdata *ud = static_cast (userdata); - char *pass = static_cast (ats_malloc(size)); - Debug("ssl", "ssl_private_key_passphrase_callback rwflag=%d serverDialog=%s", rwflag, ud->_serverDialog); // only respond to reading private keys, not writing them (does ats even do that?) @@ -428,16 +426,11 @@ ssl_private_key_passphrase_callback_builtin(char *buf, int size, int rwflag, voi fprintf(stdout, "\n"); // get passphrase // if error, then no passphrase - if (ssl_getpassword("Enter passphrase:", pass, size) <= 0) { - *pass = 0; + if (ssl_getpassword("Enter passphrase:", buf, size) <= 0) { + *buf = 0; } fprintf(stdout, "\n"); - if (strlen(pass) < static_cast (size)) { - strcpy(buf, pass); - } } - memset(pass, 0, size); // paranoid, zero out the buffer - ats_free(pass); return strlen(buf); } @@ -607,24 +600,22 @@ SSLInitServerContext( // pass phrase dialog configuration passphrase_cb_userdata ud(params, serverDialog, serverCertPtr, serverKeyPtr); - bool bIsExec = false; - - if(serverDialog && strncmp(serverDialog,"exec:",5) == 0) { - bIsExec = true; - ud._serverDialog = &serverDialog[5]; - // validate the exec program - if(!ssl_private_key_validate_exec(ud._serverDialog)) { - SSLError("failed to access '%s' pass phrase program: %s", (const char *)ud._serverDialog,strerror(errno)); - goto fail; - } - } - if(serverDialog) { - int (*passwd_cb)(char *buf, int size, int rwflag, void *userdata); - if(bIsExec) { // use exec handler call back + if (serverDialog) { + pem_password_cb * passwd_cb = NULL; + if (strncmp(serverDialog, "exec:", 5) == 0) { + ud._serverDialog = &serverDialog[5]; + // validate the exec program + if (!ssl_private_key_validate_exec(ud._serverDialog)) { + SSLError("failed to access '%s' pass phrase program: %s", (const char *) ud._serverDialog, strerror(errno)); + goto fail; + } passwd_cb = ssl_private_key_passphrase_callback_exec; - } else { // use stdin/out + } else if (strcmp(serverDialog, "builtin") == 0) { passwd_cb = ssl_private_key_passphrase_callback_builtin; + } else { // unknown config + SSLError("unknown "SSL_KEY_DIALOG" configuration value '%s'", serverDialog); + goto fail; } SSL_CTX_set_default_passwd_cb(ctx, passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ctx, &ud); @@ -715,17 +706,17 @@ SSLInitServerContext( goto fail; } } -#define SSL_CLEAR_PW_REFERENCES { \ - memset(static_cast(&ud),0,sizeof(ud));\ - SSL_CTX_set_default_passwd_cb(ctx, NULL);\ - SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL);\ +#define SSL_CLEAR_PW_REFERENCES(UD,CTX) { \ + memset(static_cast(&UD),0,sizeof(UD));\ + SSL_CTX_set_default_passwd_cb(CTX, NULL);\ + SSL_CTX_set_default_passwd_cb_userdata(CTX, NULL);\ } - SSL_CLEAR_PW_REFERENCES + SSL_CLEAR_PW_REFERENCES(ud,ctx) return ssl_context_enable_ecdh(ctx); fail: - SSL_CLEAR_PW_REFERENCES + SSL_CLEAR_PW_REFERENCES(ud,ctx) SSL_CTX_free(ctx); return NULL; } diff --git a/lib/ts/Makefile.am b/lib/ts/Makefile.am index 74c58e1b02c..18461b0bd73 100644 --- a/lib/ts/Makefile.am +++ b/lib/ts/Makefile.am @@ -128,8 +128,6 @@ libtsutil_la_SOURCES = \ ink_mutex.cc \ ink_mutex.h \ ink_platform.h \ - ink_process.cc \ - ink_process.h \ ink_queue.cc \ ink_queue.h \ ink_queue_ext.cc \ diff --git a/lib/ts/libts.h b/lib/ts/libts.h index c5daa69c385..27c9e921320 100644 --- a/lib/ts/libts.h +++ b/lib/ts/libts.h @@ -62,7 +62,6 @@ #include "ink_memory.h" #include "ink_mutex.h" #include "ink_queue.h" -#include "ink_process.h" #include "ink_rand.h" #include "ink_resolver.h" #include "ink_resource.h"