Allow SSL certificate and key to be passed in through a file handle.
git-svn-id: https://shellinabox.googlecode.com/svn/trunk@46 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
parent
f16b2cb543
commit
e40a555cbf
9 changed files with 214 additions and 8 deletions
|
@ -94,6 +94,7 @@ int serverSupportsSSL();
|
|||
void serverEnableSSL(Server *server, int flag);
|
||||
void serverSetCertificate(Server *server, const char *filename,
|
||||
int autoGenerateMissing);
|
||||
void serverSetCertificateFd(Server *server, int fd);
|
||||
void serverSetNumericHosts(Server *server, int numericHosts);
|
||||
|
||||
void httpTransfer(HttpConnection *http, char *msg, int len);
|
||||
|
|
|
@ -16,6 +16,7 @@ serverLoop
|
|||
serverSupportsSSL
|
||||
serverEnableSSL
|
||||
serverSetCertificate
|
||||
serverSetCertificateFd
|
||||
serverSetNumericHosts
|
||||
httpTransfer
|
||||
httpTransferPartialReply
|
||||
|
|
|
@ -537,6 +537,10 @@ void serverSetCertificate(struct Server *server, const char *filename,
|
|||
sslSetCertificate(&server->ssl, filename, autoGenerateMissing);
|
||||
}
|
||||
|
||||
void serverSetCertificateFd(struct Server *server, int fd) {
|
||||
sslSetCertificateFd(&server->ssl, fd);
|
||||
}
|
||||
|
||||
void serverSetNumericHosts(struct Server *server, int numericHosts) {
|
||||
server->numericHosts = numericHosts;
|
||||
}
|
||||
|
|
|
@ -111,6 +111,7 @@ void serverLoop(struct Server *server);
|
|||
void serverEnableSSL(struct Server *server, int flag);
|
||||
void serverSetCertificate(struct Server *server, const char *filename,
|
||||
int autoGenerateMissing);
|
||||
void serverSetCertificateFd(struct Server *server, int fd);
|
||||
void serverSetNumericHosts(struct Server *server, int numericHosts);
|
||||
struct Trie *serverGetHttpHandlers(struct Server *server);
|
||||
|
||||
|
|
156
libhttp/ssl.c
156
libhttp/ssl.c
|
@ -90,7 +90,11 @@ long (*SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
|
|||
void (*SSL_CTX_free)(SSL_CTX *);
|
||||
SSL_CTX * (*SSL_CTX_new)(SSL_METHOD *);
|
||||
int (*SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int);
|
||||
int (*SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *,
|
||||
const unsigned char *, long);
|
||||
int (*SSL_CTX_use_certificate_file)(SSL_CTX *, const char *, int);
|
||||
int (*SSL_CTX_use_certificate_ASN1)(SSL_CTX *, long,
|
||||
const unsigned char *);
|
||||
long (*SSL_ctrl)(SSL *, int, long, void *);
|
||||
void (*SSL_free)(SSL *);
|
||||
int (*SSL_get_error)(const SSL *, int);
|
||||
|
@ -200,7 +204,9 @@ static void loadSSL(void) {
|
|||
{ { &SSL_CTX_free }, "SSL_CTX_free" },
|
||||
{ { &SSL_CTX_new }, "SSL_CTX_new" },
|
||||
{ { &SSL_CTX_use_PrivateKey_file }, "SSL_CTX_use_PrivateKey_file" },
|
||||
{ { &SSL_CTX_use_PrivateKey_ASN1 }, "SSL_CTX_use_PrivateKey_ASN1" },
|
||||
{ { &SSL_CTX_use_certificate_file },"SSL_CTX_use_certificate_file"},
|
||||
{ { &SSL_CTX_use_certificate_ASN1 },"SSL_CTX_use_certificate_ASN1"},
|
||||
{ { &SSL_ctrl }, "SSL_ctrl" },
|
||||
{ { &SSL_free }, "SSL_free" },
|
||||
{ { &SSL_get_error }, "SSL_get_error" },
|
||||
|
@ -419,6 +425,156 @@ void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
static const unsigned char *sslSecureReadASCIIFileToMem(int fd) {
|
||||
size_t inc = 16384;
|
||||
size_t bufSize = inc;
|
||||
size_t len = 0;
|
||||
unsigned char *buf;
|
||||
check((buf = malloc(bufSize)) != NULL);
|
||||
for (;;) {
|
||||
check(len < bufSize - 1);
|
||||
size_t readLen = bufSize - len - 1;
|
||||
ssize_t bytesRead = NOINTR(read(fd, buf + len, readLen));
|
||||
if (bytesRead > 0) {
|
||||
len += bytesRead;
|
||||
}
|
||||
if (bytesRead != readLen) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Instead of calling realloc(), allocate a new buffer, copy the data,
|
||||
// and then clear the old buffer. This way, we are not accidentally
|
||||
// leaving key material in memory.
|
||||
unsigned char *newBuf;
|
||||
check((newBuf = malloc(bufSize + inc)) != NULL);
|
||||
memcpy(newBuf, buf, len);
|
||||
memset(buf, 0, bufSize);
|
||||
free(buf);
|
||||
buf = newBuf;
|
||||
bufSize += inc;
|
||||
}
|
||||
check(len < bufSize);
|
||||
buf[len] = '\000';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static const unsigned char *sslPEMtoASN1(const unsigned char *pem,
|
||||
const char *record,
|
||||
long *size) {
|
||||
*size = -1;
|
||||
char *marker;
|
||||
check((marker = stringPrintf(NULL, "-----BEGIN %s-----",record))!=NULL);
|
||||
unsigned char *ptr = (unsigned char *)strstr((char *)pem, marker);
|
||||
if (!ptr) {
|
||||
free(marker);
|
||||
return NULL;
|
||||
} else {
|
||||
ptr += strlen(marker);
|
||||
}
|
||||
*marker = '\000';
|
||||
check((marker = stringPrintf(marker, "-----END %s-----",record))!=NULL);
|
||||
unsigned char *end = (unsigned char *)strstr((char *)ptr, marker);
|
||||
free(marker);
|
||||
if (!end) {
|
||||
return NULL;
|
||||
}
|
||||
unsigned char *ret;
|
||||
size_t maxSize = (((end - ptr)*6)+7)/8;
|
||||
check((ret = malloc(maxSize)) != NULL);
|
||||
unsigned char *out = ret;
|
||||
unsigned bits = 0;
|
||||
int count = 0;
|
||||
while (ptr < end) {
|
||||
unsigned char ch = *ptr++;
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
ch -= 'A';
|
||||
} else if (ch >= 'a' && ch <= 'z') {
|
||||
ch -= 'a' - 26;
|
||||
} else if (ch >= '0' && ch <= '9') {
|
||||
ch += 52 - '0';
|
||||
} else if (ch == '+') {
|
||||
ch += 62 - '+';
|
||||
} else if (ch == '/') {
|
||||
ch += 63 - '/';
|
||||
} else if (ch == '=') {
|
||||
while (ptr < end) {
|
||||
if ((ch = *ptr++) != '=' && ch > ' ') {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
break;
|
||||
} else if (ch <= ' ') {
|
||||
continue;
|
||||
} else {
|
||||
err:
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
check(ch <= 63);
|
||||
check(count >= 0);
|
||||
check(count <= 6);
|
||||
bits = (bits << 6) | ch;
|
||||
count += 6;
|
||||
if (count >= 8) {
|
||||
*out++ = (bits >> (count -= 8)) & 0xFF;
|
||||
}
|
||||
}
|
||||
check(out - ret <= maxSize);
|
||||
*size = out - ret;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
void sslSetCertificateFd(struct SSLSupport *ssl, int fd) {
|
||||
#ifdef HAVE_OPENSSL
|
||||
check(serverSupportsSSL());
|
||||
check(fd >= 0);
|
||||
check(ssl->sslContext = SSL_CTX_new(SSLv23_server_method()));
|
||||
const unsigned char *data = sslSecureReadASCIIFileToMem(fd);
|
||||
check(!NOINTR(close(fd)));
|
||||
long dataSize = (long)strlen((const char *)data);
|
||||
long certSize, rsaSize, dsaSize, ecSize;
|
||||
const unsigned char *cert = sslPEMtoASN1(data, "CERTIFICATE", &certSize);
|
||||
const unsigned char *rsa = sslPEMtoASN1(data, "RSA PRIVATE KEY", &rsaSize);
|
||||
const unsigned char *dsa = sslPEMtoASN1(data, "DSA PRIVATE KEY", &dsaSize);
|
||||
const unsigned char *ec = sslPEMtoASN1(data, "EC PRIVATE KEY", &ecSize);
|
||||
if (!certSize || !(rsaSize > 0 || dsaSize > 0 || ecSize > 0) ||
|
||||
!SSL_CTX_use_certificate_ASN1(ssl->sslContext, certSize, cert) ||
|
||||
(rsaSize > 0 &&
|
||||
!SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, ssl->sslContext, rsa,
|
||||
rsaSize)) ||
|
||||
(dsaSize > 0 &&
|
||||
!SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_DSA, ssl->sslContext, dsa,
|
||||
dsaSize)) ||
|
||||
(ecSize > 0 &&
|
||||
!SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, ssl->sslContext, ec,
|
||||
ecSize)) ||
|
||||
!SSL_CTX_check_private_key(ssl->sslContext)) {
|
||||
fatal("Cannot read valid certificate from fd %d. Check file format.", fd);
|
||||
}
|
||||
dcheck(!ERR_peek_error());
|
||||
ERR_clear_error();
|
||||
memset((char *)data, 0, dataSize);
|
||||
free((char *)data);
|
||||
memset((char *)cert, 0, certSize);
|
||||
free((char *)cert);
|
||||
if (rsaSize > 0) {
|
||||
memset((char *)rsa, 0, rsaSize);
|
||||
free((char *)rsa);
|
||||
}
|
||||
if (dsaSize > 0) {
|
||||
memset((char *)dsa, 0, dsaSize);
|
||||
free((char *)dsa);
|
||||
}
|
||||
if (ecSize > 0) {
|
||||
memset((char *)ec, 0, ecSize);
|
||||
free((char *)ec);
|
||||
}
|
||||
ssl->generateMissing = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int sslEnable(struct SSLSupport *ssl, int enabled) {
|
||||
int old = ssl->enabled;
|
||||
ssl->enabled = enabled;
|
||||
|
|
|
@ -83,7 +83,11 @@ extern long (*x_SSL_CTX_ctrl)(SSL_CTX *, int, long, void *);
|
|||
extern void (*x_SSL_CTX_free)(SSL_CTX *);
|
||||
extern SSL_CTX*(*x_SSL_CTX_new)(SSL_METHOD *);
|
||||
extern int (*x_SSL_CTX_use_PrivateKey_file)(SSL_CTX *, const char *, int);
|
||||
extern int (*x_SSL_CTX_use_PrivateKey_ASN1)(int, SSL_CTX *,
|
||||
const unsigned char *, long);
|
||||
extern int (*x_SSL_CTX_use_certificate_file)(SSL_CTX *, const char *, int);
|
||||
extern int (*x_SSL_CTX_use_certificate_ASN1)(SSL_CTX *, long,
|
||||
const unsigned char *);
|
||||
extern long (*x_SSL_ctrl)(SSL *, int, long, void *);
|
||||
extern void (*x_SSL_free)(SSL *);
|
||||
extern int (*x_SSL_get_error)(const SSL *, int);
|
||||
|
@ -119,7 +123,9 @@ extern SSL_METHOD *(*x_SSLv23_server_method)(void);
|
|||
#define SSL_CTX_free x_SSL_CTX_free
|
||||
#define SSL_CTX_new x_SSL_CTX_new
|
||||
#define SSL_CTX_use_PrivateKey_file x_SSL_CTX_use_PrivateKey_file
|
||||
#define SSL_CTX_use_PrivateKey_ASN1 x_SSL_CTX_use_PrivateKey_ASN1
|
||||
#define SSL_CTX_use_certificate_file x_SSL_CTX_use_certificate_file
|
||||
#define SSL_CTX_use_certificate_ASN1 x_SSL_CTX_use_certificate_ASN1
|
||||
#define SSL_ctrl x_SSL_ctrl
|
||||
#define SSL_free x_SSL_free
|
||||
#define SSL_get_error x_SSL_get_error
|
||||
|
@ -175,6 +181,7 @@ void deleteSSL(struct SSLSupport *ssl);
|
|||
void sslGenerateCertificate(const char *certificate, const char *serverName);
|
||||
void sslSetCertificate(struct SSLSupport *ssl, const char *filename,
|
||||
int autoGenerateMissing);
|
||||
void sslSetCertificateFd(struct SSLSupport *ssl, int fd);
|
||||
int sslEnable(struct SSLSupport *ssl, int enabled);
|
||||
void sslBlockSigPipe();
|
||||
int sslUnblockSigPipe();
|
||||
|
|
|
@ -1036,7 +1036,6 @@ int forkLauncher(void) {
|
|||
// Temporarily drop most permissions. We still retain the ability to
|
||||
// switch back to root, which is necessary for launching "login".
|
||||
lowerPrivileges();
|
||||
NOINTR(close(pair[0]));
|
||||
closeAllFds((int []){ pair[1] }, 1);
|
||||
launcherDaemon(pair[1]);
|
||||
fatal("exit() failed!");
|
||||
|
|
|
@ -74,10 +74,11 @@
|
|||
static int port;
|
||||
static int portMin;
|
||||
static int portMax;
|
||||
static int noBeep = 0;
|
||||
static int numericHosts = 0;
|
||||
static int enableSSL = 1;
|
||||
static int noBeep = 0;
|
||||
static int numericHosts = 0;
|
||||
static int enableSSL = 1;
|
||||
static char *certificateDir;
|
||||
static int certificateFd = -1;
|
||||
static HashMap *externalFiles;
|
||||
static Server *cgiServer;
|
||||
static char *cgiSessionKey;
|
||||
|
@ -592,7 +593,8 @@ static void usage(void) {
|
|||
" ${user} - user name",
|
||||
!serverSupportsSSL() ? "" :
|
||||
" -c, --cert=CERTDIR set certificate dir "
|
||||
"(default: $PWD)\n",
|
||||
"(default: $PWD)\n"
|
||||
" --cert-fd=FD set certificate file from fd",
|
||||
group, PORTNUM,
|
||||
!serverSupportsSSL() ? "" :
|
||||
" -t, --disable-ssl disable transparent SSL support\n",
|
||||
|
@ -623,6 +625,7 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
{ "help", 0, 0, 'h' },
|
||||
{ "background", 2, 0, 'b' },
|
||||
{ "cert", 1, 0, 'c' },
|
||||
{ "cert-fd", 1, 0, 0 },
|
||||
{ "cgi", 2, 0, 0 },
|
||||
{ "debug", 0, 0, 'd' },
|
||||
{ "static-file", 1, 0, 'f' },
|
||||
|
@ -670,10 +673,30 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
if (!hasSSL) {
|
||||
warn("Ignoring certificate directory, as SSL support is unavailable");
|
||||
}
|
||||
if (certificateFd) {
|
||||
fatal("Cannot set both a certificate directory and file handle");
|
||||
}
|
||||
if (certificateDir) {
|
||||
fatal("Only one certificate directory can be selected");
|
||||
}
|
||||
check(certificateDir = strdup(optarg));
|
||||
} else if (!idx--) {
|
||||
// Certificate file descriptor
|
||||
if (!hasSSL) {
|
||||
warn("Ignoring certificate directory, as SSL support is unavailable");
|
||||
}
|
||||
if (certificateDir) {
|
||||
fatal("Cannot set both a certificate directory and file handle");
|
||||
}
|
||||
if (certificateFd >= 0) {
|
||||
fatal("Only one certificate file handle can be provided");
|
||||
}
|
||||
int tmpFd = strtoint(optarg, 3, INT_MAX);
|
||||
certificateFd = dup(tmpFd);
|
||||
if (certificateFd < 0) {
|
||||
fatal("Invalid certificate file handle");
|
||||
}
|
||||
check(!NOINTR(close(tmpFd)));
|
||||
} else if (!idx--) {
|
||||
// CGI
|
||||
if (demonize) {
|
||||
|
@ -823,7 +846,7 @@ static void parseArgs(int argc, char * const argv[]) {
|
|||
if (fd >= 0) {
|
||||
char buf[40];
|
||||
NOINTR(write(fd, buf, snprintf(buf, 40, "%d", (int)getpid())));
|
||||
NOINTR(close(fd));
|
||||
check(!NOINTR(close(fd)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -902,7 +925,8 @@ int main(int argc, char * const argv[]) {
|
|||
fflush(stdout);
|
||||
free(cgiRoot);
|
||||
check(!NOINTR(close(fds[1])));
|
||||
closeAllFds((int []){ launcherFd, serverGetFd(server) }, 2);
|
||||
closeAllFds((int []){ launcherFd, serverGetFd(server),
|
||||
certificateFd }, certificateFd >= 0 ? 3 : 2);
|
||||
logSetLogLevel(MSG_QUIET);
|
||||
}
|
||||
serverEnableSSL(server, enableSSL);
|
||||
|
@ -910,7 +934,9 @@ int main(int argc, char * const argv[]) {
|
|||
// Enable SSL support (if available)
|
||||
if (enableSSL) {
|
||||
check(serverSupportsSSL());
|
||||
if (certificateDir) {
|
||||
if (certificateFd >= 0) {
|
||||
serverSetCertificateFd(server, certificateFd);
|
||||
} else if (certificateDir) {
|
||||
char *tmp;
|
||||
if (strchr(certificateDir, '%')) {
|
||||
fatal("Invalid certificate directory name \"%s\".", certificateDir);
|
||||
|
|
|
@ -52,6 +52,7 @@ shellinaboxd \- publish command line shell through AJAX interface
|
|||
.B shellinaboxd
|
||||
[\ \fB-b\fP\ | \fB--background\fP[\fB=\fP\fIpidfile\fP]\ ]
|
||||
[\ \fB-c\fP\ | \fB--cert=\fP\fIcertdir\fP\ ]
|
||||
[\ \fB-c\fP\ | \fB--cert-fd=\fP\fIfd\fP\ ]
|
||||
[\ \fB--cgi\fP[\fB=\fP\fIportrange\fP]\ ]
|
||||
[\ \fB-d\fP\ | \fB--debug\fP\ ]
|
||||
[\ \fB-f\fP\ | \fB--static-file=\fP\fIurl\fP:\fIfile\fP\ ]
|
||||
|
@ -138,6 +139,16 @@ certificate. Due to this usability problem, and due to the perceived
|
|||
security implications, the use of auto-generated self-signed
|
||||
certificates is intended for testing or in intranet deployments, only.
|
||||
.TP
|
||||
\fB--cert-fd=\fP\fIfd\fP
|
||||
Instead of providing a
|
||||
.B --cert
|
||||
directory, it is also possible to provide a filedescriptor
|
||||
.I fd
|
||||
where the certificate and key can be retrieved. While this option disables
|
||||
.B SNI
|
||||
support, it does offer an alternative solution for securely providing
|
||||
the private key data to the daemon.
|
||||
.TP
|
||||
\fB--cgi\fP[\fB=\fP\fIportrange\fP]
|
||||
Instead of running
|
||||
.B shellinaboxd
|
||||
|
|
Loading…
Reference in a new issue