Commit ffe40d2e authored by Leigh Stoller's avatar Leigh Stoller

First round of ssl'ification of tmcd/tmcc. This needs to be looked at

by smarter brains by me (I have asked Dave to look it over). Anyway ...

I added a top level ssl directory which has a bunch of goo for
creating certificates and keys.  I currently create a Certificate
Authority, a server certificate, and a client certificate. The private
keys for all three are unencrypted, so no password is required. All
key/cert combos can be installed on boss. The client side needs the
key/cert pair (in one file), and the CA cert (no key!). There are
install targets to do this. NOTE, you do not want to create/install
these without being careful, since you could instantly invalidate all
the clients!

I have added the necessary SSL routines to tmcd/tmcc. See the ssl.c
and ssl.h file. I have set it up so that with all you need to do is
uncomment three lines in the makefile, and accept,connect,read,write,
and close are redirected to SSL'ified versions in ssl.c. The current
security model is that the client and server both "demand" certificate
verification from the other side (as opposed to just server side
verification). tmcd reads in server.pem, while tmcc reads in
client.pem. Both read in the emulab.pem (CA cert with no private
key).

Initial testing indicates I have done this at least partially
correctly. Whoever invented this stuff has a really twisted mind
though. There are some questions at the top of ssl.c that need to be
answered.

Oh, also redid all the syslog stuff throughout tmcd.
parent 9440d3dd
...@@ -1179,6 +1179,7 @@ esac ...@@ -1179,6 +1179,7 @@ esac
outfiles="$outfiles Makeconf GNUmakefile \ outfiles="$outfiles Makeconf GNUmakefile \
assign/GNUmakefile \ assign/GNUmakefile \
ssl/GNUmakefile \
capture/GNUmakefile \ capture/GNUmakefile \
db/GNUmakefile db/nalloc db/nfree db/if2port db/backup \ db/GNUmakefile db/nalloc db/nfree db/if2port db/backup \
db/webcontrol db/node_status db/genelists db/genelists.proxy \ db/webcontrol db/node_status db/genelists db/genelists.proxy \
......
...@@ -251,6 +251,7 @@ esac] ...@@ -251,6 +251,7 @@ esac]
outfiles="$outfiles Makeconf GNUmakefile \ outfiles="$outfiles Makeconf GNUmakefile \
assign/GNUmakefile \ assign/GNUmakefile \
ssl/GNUmakefile \
capture/GNUmakefile \ capture/GNUmakefile \
db/GNUmakefile db/nalloc db/nfree db/if2port db/backup \ db/GNUmakefile db/nalloc db/nfree db/if2port db/backup \
db/webcontrol db/node_status db/genelists db/genelists.proxy \ db/webcontrol db/node_status db/genelists db/genelists.proxy \
......
...@@ -94,6 +94,19 @@ error(const char *fmt, ...) ...@@ -94,6 +94,19 @@ error(const char *fmt, ...)
va_end(args); va_end(args);
} }
void
errorc(const char *fmt, ...)
{
va_list args;
char buf[BUFSIZ];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
error("%s : %s\n", buf, strerror(errno));
}
void void
fatal(const char *fmt, ...) fatal(const char *fmt, ...)
{ {
...@@ -122,7 +135,7 @@ pwarning(const char *fmt, ...) ...@@ -122,7 +135,7 @@ pwarning(const char *fmt, ...)
vsnprintf(buf, sizeof(buf), fmt, args); vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args); va_end(args);
warning("%s : %s", buf, strerror(errno)); warning("%s : %s\n", buf, strerror(errno));
} }
void void
......
...@@ -7,6 +7,7 @@ int loginit(int usesyslog, char *name); ...@@ -7,6 +7,7 @@ int loginit(int usesyslog, char *name);
void info(const char *fmt, ...); void info(const char *fmt, ...);
void warning(const char *fmt, ...); void warning(const char *fmt, ...);
void error(const char *fmt, ...); void error(const char *fmt, ...);
void errorc(const char *fmt, ...);
void fatal(const char *fmt, ...); void fatal(const char *fmt, ...);
void pwarning(const char *fmt, ...); void pwarning(const char *fmt, ...);
void pfatal(const char *fmt, ...); void pfatal(const char *fmt, ...);
#
# Insert Copyright Here.
#
SRCDIR = @srcdir@
TESTBED_SRCDIR = @top_srcdir@
EVENTSYS = @EVENTSYS@
OBJDIR = ..
SUBDIR = ssl
include $(OBJDIR)/Makeconf
all:
include $(TESTBED_SRCDIR)/GNUmakerules
#
# The openssl config file.
#
SSLCONF = $(SRCDIR)/openssl.cnf
#
# You do not want to run these targets unless you are sure you
# know what you are doing! You really do not want to install these
# unless you are very sure you know what you are doing. You could
# mess up all the clients when the CA changes out from under them.
#
pems: emulab.pem server.pem client.pem
emulab.pem: dirsmade $(SSLCONF)
#
# Create the Certificate Authority.
# The certificate (no key!) is installed on both boss and remote nodes.
#
openssl req -new -x509 -config $(SSLCONF) \
-keyout cakey.pem -out cacert.pem
cp cacert.pem emulab.pem
server.pem: dirsmade $(SSLCONF)
#
# Create the server side private key and certificate request.
#
openssl req -new -config $(SSLCONF) \
-keyout servkey.pem -out servreq.pem
#
# Combine key and cert request.
#
cat servkey.pem servreq.pem > newreq.pem
#
# Sign the server cert request, creating a server certificate.
#
openssl ca -policy policy_anything -config $(SSLCONF) \
-out servcert.pem \
-cert cacert.pem -keyfile cakey.pem \
-infiles newreq.pem
#
# Combine the key and the certificate into one file which is installed
# on boss and used by tmcd.
#
cat servkey.pem servcert.pem > server.pem
rm -f newreq.pem
client.pem: dirsmade $(SSLCONF)
#
# Create a client side private key and certificate request.
#
openssl req -new -config $(SSLCONF) \
-keyout clientkey.pem -out clientreq.pem
#
# Sign the client cert request, creating a client certificate.
#
openssl ca -policy policy_anything -config $(SSLCONF) \
-out clientcert.pem \
-cert cacert.pem -keyfile cakey.pem \
-infiles clientreq.pem
#
# Combine the key and the certificate into one file which is installed
# on each remote node and used by tmcc. Installed on boss too so
# we can test tmcc there.
#
cat clientkey.pem clientcert.pem > client.pem
dirsmade:
-mkdir -p certs
-mkdir -p newcerts
-mkdir -p crl
echo "01" > serial
touch index.txt
touch dirsmade
#
# You do not want to run these targets unless you are sure you
# know what you are doing!
#
boss-install: $(INSTALL_ETCDIR)/emulab.pem \
$(INSTALL_ETCDIR)/server.pem \
$(INSTALL_ETCDIR)/client.pem
client-install: $(INSTALL_ETCDIR)/client.pem
clean:
rm -f *.pem serial index.txt *.old dirsmade
rm -rf certs crl
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# $FreeBSD: src/crypto/openssl/apps/openssl.cnf,v 1.1.1.1.2.3 2000/11/26 11:32:48 kris Exp $
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = . # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem# The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extentions to add to the cert
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days= 30 # how long before next CRL
default_md = md5 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 1024
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert
encrypt_key = no
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = US
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Utah
localityName = Locality Name (eg, city)
localityName_default = Salt Lake City
organizationName = Organization Name (eg, company)
organizationName_default = Utah Network Testbed
organizationalUnitName = Organizational Unit Name (eg, section)
#organizationalUnitName_default =
commonName = Common Name (eg, YOUR name)
commonName_default = Emulab.Net
commonName_max = 64
emailAddress = Email Address
emailAddress_default = testbed-ops@fast.cs.utah.edu
emailAddress_max = 40
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints=CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier=keyid:always,issuer:always
...@@ -13,24 +13,32 @@ all: tmcd tmcc tmcd.restart findif ...@@ -13,24 +13,32 @@ all: tmcd tmcc tmcd.restart findif
include $(TESTBED_SRCDIR)/GNUmakerules include $(TESTBED_SRCDIR)/GNUmakerules
CFLAGS += -O -g -Wall -DUDP -I${OBJDIR} -I/usr/local/include CFLAGS += -O -g -Wall -DUDP \
-I${OBJDIR} -I/usr/local/include -I${TESTBED_SRCDIR}/lib/libtb
TMLIBS = ${OBJDIR}/lib/libtb/libtb.a
#
# For SSL enabled tmcd/tmcc
#
#CFLAGS += -DWITHSSL -DETCDIR='"$(INSTALL_ETCDIR)"'
#TMLIBS += -lssl -lcrypto
#SSLOBJ = ssl.o
ifeq ($(EVENTSYS),1) ifeq ($(EVENTSYS),1)
TMCDCFLAGS = `elvin-config --cflags vin4c` \ TMCDCFLAGS = `elvin-config --cflags vin4c` \
-I$(TESTBED_SRCDIR)/event/lib -DEVENTSYS -I$(TESTBED_SRCDIR)/event/lib -DEVENTSYS
TMCDLIBS = ${OBJDIR}/event/lib/libevent.a \ TMCDLIBS = ${OBJDIR}/event/lib/libevent.a
${OBJDIR}/lib/libtb/libtb.a
ELVINFLAGS = `elvin-config --libs vin4c` ELVINFLAGS = `elvin-config --libs vin4c`
endif endif
tmcd: tmcd.c ${TMCDLIBS} decls.h version.o $(SSLOBJ)
tmcd: tmcd.c ${TMCDLIBS} decls.h version.o
$(CC) $(CFLAGS) $(TMCDCFLAGS) -o tmcd $< \ $(CC) $(CFLAGS) $(TMCDCFLAGS) -o tmcd $< \
version.o $(LFLAGS) -L/usr/local/lib/mysql -lmysqlclient \ version.o $(SSLOBJ) \
$(ELVINFLAGS) $(TMCDLDFLAGS) $(TMCDLIBS) $(LFLAGS) -L/usr/local/lib/mysql -lmysqlclient \
$(ELVINFLAGS) $(TMCDLDFLAGS) $(TMCDLIBS) $(TMLIBS)
tmcc: tmcc.o tmcc: tmcc.o $(SSLOBJ)
$(CC) $(CFLAGS) -g -o tmcc tmcc.o $(LFLAGS) -static $(CC) $(CFLAGS) -g -o tmcc tmcc.o $(SSLOBJ) $(LFLAGS) -static $(TMLIBS)
findif: findif.o findif: findif.o
$(CC) $(CFLAGS) -g -o findif findif.o $(LFLAGS) -static $(CC) $(CFLAGS) -g -o findif findif.o $(LFLAGS) -static
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
*/ */
#define TBSERVER_PORT 7777 #define TBSERVER_PORT 7777
#define MYBUFSIZE 1024 #define MYBUFSIZE 2048
/* /*
* As the tmcd changes, incompatable changes with older version of * As the tmcd changes, incompatable changes with older version of
......
/*
* SSL support for tmcd/tmcc.
*
* Questions:
* 1. Should the CA have an encrypted key? If we do not distribute the
* key with the the cert (to the remote nodes), does it really matter?
* Only the cert is needed to operate.
* 2. Should the client/server keys be encrypted? Doing so requires a
* password to use them, and that would obviously be a pain.
* 3. Whats a challenge password? Do we need to worry about those?
* 4. How do we verify a certificate?
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "decls.h"
#include "ssl.h"
#include "log.h"
#include "config.h"
/* Passed in from the makefile */
#ifndef ETCDIR
#define ETCDIR "/usr/testbed/etc"
#endif
#define EMULAB_CERTFILE "emulab.pem"
#define SERVER_CERTFILE "server.pem"
#define CLIENT_CERTFILE "client.pem"
/*
* On the client, we search a couple of dirs for the pem file.
*/
static char *clientcertdirs[] = {
"/etc/testbed",
"/etc/rc.d/testbed",
"/usr/local/etc/testbed",
ETCDIR,
0
};
/* Keep all this stuff private. */
static SSL *ssl;
static SSL_CTX *ctx;
static int client = 0;
static void tmcd_sslerror();
static void tmcd_sslprint(const char *fmt, ...);
/*
* Init our SSL context. This is done once, in the parent.
*/
int
tmcd_server_sslinit(void)
{
char buf[BUFSIZ];
client = 0;
SSL_library_init();
SSL_load_error_strings();
if (! (ctx = SSL_CTX_new(SSLv23_method()))) {
tmcd_sslerror();
return 1;
}
sprintf(buf, "%s/%s", ETCDIR, SERVER_CERTFILE);
/*
* Load our server key and certificate and then check it.
*/
if (! SSL_CTX_use_certificate_file(ctx, buf, SSL_FILETYPE_PEM)) {
tmcd_sslerror();
return 1;
}
if (! SSL_CTX_use_PrivateKey_file(ctx, buf, SSL_FILETYPE_PEM)) {
tmcd_sslerror();
return 1;
}
if (SSL_CTX_check_private_key(ctx) != 1) {
tmcd_sslerror();
return 1;
}
/*
* Load our CA so that we can request client authentication.
* The CA list is sent to the client.
*/
sprintf(buf, "%s/%s", ETCDIR, EMULAB_CERTFILE);
if (! SSL_CTX_load_verify_locations(ctx, buf, NULL)) {
tmcd_sslerror();
return 1;
}
/*
* Make it so the client must provide authentication.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
return 0;
}
int
tmcd_client_sslinit(void)
{
char buf[BUFSIZ], **cp;
client = 1;
SSL_library_init();
SSL_load_error_strings();
if (! (ctx = SSL_CTX_new(SSLv23_method()))) {
tmcd_sslerror();
return 1;
}
/*
* Search for the certificate.
*/
cp = clientcertdirs;
while (*cp) {
sprintf(buf, "%s/%s", *cp, CLIENT_CERTFILE);
if (access(buf, R_OK) == 0)
break;
cp++;
}
if (! *cp) {
error("Could not find a client certificate!\n");
return 1;
}
/*
* Load our client certificate.
*/
if (! SSL_CTX_use_certificate_file(ctx, buf, SSL_FILETYPE_PEM)) {
tmcd_sslerror();
return 1;
}
if (! SSL_CTX_use_PrivateKey_file(ctx, buf, SSL_FILETYPE_PEM)) {
tmcd_sslerror();
return 1;
}
if (SSL_CTX_check_private_key(ctx) != 1) {
tmcd_sslerror();
return 1;
}
/*
* Load our CA so that we can do authentication.
*/
sprintf(buf, "%s/%s", *cp, EMULAB_CERTFILE);
if (! SSL_CTX_load_verify_locations(ctx, buf, NULL)) {
tmcd_sslerror();
return 1;
}
/*
* Make it so the server must provide authentication.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, 0);
return 0;
}
/*
* Handshake a new server connection. This creates a new SSL, which
* is static and local to the process.
*/
int
tmcd_sslaccept(int sock, struct sockaddr *addr, socklen_t *addrlen)
{
int newsock;
if ((newsock = accept(sock, addr, addrlen)) < 0)
return -1;
if (! (ssl = SSL_new(ctx))) {
tmcd_sslerror();
return -1;
}
if (! SSL_set_fd(ssl, newsock)) {
tmcd_sslerror();
return -1;
}
if (SSL_accept(ssl) <= 0) {
tmcd_sslerror();
return -1;
}
tmcd_sslverify(newsock, 0);
return newsock;
}
/*
* Handshake a new client connection. This creates a new SSL, which
* is static and local to the process.
*/
int
tmcd_sslconnect(int sock, const struct sockaddr *name, socklen_t namelen)
{
if (connect(sock, name, namelen) < 0)
return -1;
if (! (ssl = SSL_new(ctx))) {
tmcd_sslerror();
return -1;
}
if (! SSL_set_fd(ssl, sock)) {
tmcd_sslerror();
return -1;
}
if (SSL_connect(ssl) <= 0) {
tmcd_sslerror();
return -1;
}
tmcd_sslverify(sock, 0);
return 0;
}
/*
* Verify the certificate of the peer.
*/
int
tmcd_sslverify(int sock, char *host)
{
X509 *peer;
char *cp, buf[256];
if (SSL_get_verify_result(ssl) != X509_V_OK) {
tmcd_sslprint("Certificate did not verify!\n");
return 1;
}
if (! (peer = SSL_get_peer_certificate(ssl))) {
tmcd_sslprint("No certificate was presented by the peer!\n");
return 1;
}
if ((cp = X509_NAME_oneline(X509_get_subject_name(peer), 0, 0))) {
printf("Peer subject: %s\n", cp);
free(cp);
}
if ((cp = X509_NAME_oneline(X509_get_issuer_name(peer), 0, 0))) {
printf("Peer issuer: %s\n", cp);
free(cp);
}
return 0;
}
/*
* Write stuff out. According to docs, the write call will not
* return until the entire amount is written. Not that it matters; the
* caller is going to check and rewrite if short.
*/
int
tmcd_sslwrite(int sock, const void *buf, size_t nbytes)
{
int cc;
errno = 0;
if ((cc = SSL_write(ssl, buf, nbytes)) <= 0) {
if (cc < 0) {
tmcd_sslerror();
}
return cc;
}
return cc;
}
/*
* Read stuff in.
*/
int
tmcd_sslread(int sock, void *buf, size_t nbytes)
{
int cc;
errno = 0;
if ((cc = SSL_read(ssl, buf, nbytes)) <= 0) {
if (cc < 0) {
tmcd_sslerror();