Commit 40d072cf authored by Leigh B. Stoller's avatar Leigh B. Stoller

A fair amount of cleanup, both of the ssl stuff and of tmcd in general.

Deal with ssl/nossl clients; at Chad's suggestion add a small handshake
tag to ssl enabled tmcc/tmcd which tells tmcd that it needs to enter
full SSL mode. This allows old tmcc to connect to an ssl enabled tmcd,
and still work okay.

I've also ironed out the verification stuff. At the client, we make sure
that the CommonName field of the peer cert maps to the same address that
we connected to (bossnode).

At the server, we check the OU field of the cert (we create the client
certs with the OU field set to the node type; a convention I made up!).
It must match the type of the node, as we get it from the nodes table.
Also check the CommonName to make sure it matches our hostname. This is
by no means bulletproof, but perfection is costly, and we don't have the
money!

Also cleaned up the REDIRECT testmode stuff. Instead of ifdef'ed under
TESTMODE, leave it compiled in all the time, but only allow it from the
local node (where tmcd is running). Mere users will not be able to
access it, but testbed people can use it since they have accounts on the
boss node.
parent 658ee16b
...@@ -20,9 +20,9 @@ TMLIBS = ${OBJDIR}/lib/libtb/libtb.a ...@@ -20,9 +20,9 @@ TMLIBS = ${OBJDIR}/lib/libtb/libtb.a
# #
# For SSL enabled tmcd/tmcc # For SSL enabled tmcd/tmcc
# #
#CFLAGS += -DWITHSSL -DETCDIR='"$(INSTALL_ETCDIR)"' #CFLAGS += -DWITHSSL -DETCDIR='"$(INSTALL_ETCDIR)"'
#TMLIBS += -lssl -lcrypto #TMLIBS += -lssl -lcrypto
#SSLOBJ = ssl.o #SSLOBJ = ssl.o
ifeq ($(EVENTSYS),1) ifeq ($(EVENTSYS),1)
TMCDCFLAGS = `elvin-config --cflags vin4c` \ TMCDCFLAGS = `elvin-config --cflags vin4c` \
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
* Insert Copyright Here. * Insert Copyright Here.
*/ */
#define TBSERVER_PORT 7777 #define TBSERVER_PORT 7777
#define MYBUFSIZE 2048 #define MYBUFSIZE 2048
/* /*
* As the tmcd changes, incompatable changes with older version of * As the tmcd changes, incompatable changes with older version of
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
* sure to change it there too! * sure to change it there too!
* *
* Note, this is assumed to be an integer. No need for 3.23.479 ... * Note, this is assumed to be an integer. No need for 3.23.479 ...
* NB: See ron/libsetup.pm. That is version 4! I'll merge that in.
*/ */
#define DEFAULT_VERSION 2 #define DEFAULT_VERSION 2
#define CURRENT_VERSION 3 #define CURRENT_VERSION 3
...@@ -12,10 +12,13 @@ ...@@ -12,10 +12,13 @@
*/ */
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <netdb.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include "decls.h" #include "decls.h"
...@@ -31,6 +34,11 @@ ...@@ -31,6 +34,11 @@
#define SERVER_CERTFILE "server.pem" #define SERVER_CERTFILE "server.pem"
#define CLIENT_CERTFILE "client.pem" #define CLIENT_CERTFILE "client.pem"
/*
* This is used by tmcd to determine if the connection is ssl or not.
*/
int isssl;
/* /*
* On the client, we search a couple of dirs for the pem file. * On the client, we search a couple of dirs for the pem file.
*/ */
...@@ -46,6 +54,8 @@ static char *clientcertdirs[] = { ...@@ -46,6 +54,8 @@ static char *clientcertdirs[] = {
static SSL *ssl; static SSL *ssl;
static SSL_CTX *ctx; static SSL_CTX *ctx;
static int client = 0; static int client = 0;
static char nosslbuf[MYBUFSIZE];
static int nosslbuflen, nosslbufidx;
static void tmcd_sslerror(); static void tmcd_sslerror();
static void tmcd_sslprint(const char *fmt, ...); static void tmcd_sslprint(const char *fmt, ...);
...@@ -180,24 +190,49 @@ tmcd_client_sslinit(void) ...@@ -180,24 +190,49 @@ tmcd_client_sslinit(void)
int int
tmcd_sslaccept(int sock, struct sockaddr *addr, socklen_t *addrlen) tmcd_sslaccept(int sock, struct sockaddr *addr, socklen_t *addrlen)
{ {
int newsock; int newsock, cc;
if ((newsock = accept(sock, addr, addrlen)) < 0) if ((newsock = accept(sock, addr, addrlen)) < 0)
return -1; return -1;
/*
* Read the first bit. It indicates whether we need to SSL
* handshake or not.
*/
if ((cc = read(newsock, nosslbuf, sizeof(nosslbuf) - 1)) <= 0) {
error("sslaccept: reading request");
if (cc == 0)
errno = EIO;
return -1;
}
if (strncmp(nosslbuf, SPEAKSSL, strlen(SPEAKSSL))) {
/*
* No ssl. Need to return this data on the next read.
* See below.
*/
isssl = 0;
nosslbuflen = cc;
nosslbufidx = 0;
return newsock;
}
isssl = 1;
nosslbuflen = 0;
if (! (ssl = SSL_new(ctx))) { if (! (ssl = SSL_new(ctx))) {
tmcd_sslerror(); tmcd_sslerror();
errno = EIO;
return -1; return -1;
} }
if (! SSL_set_fd(ssl, newsock)) { if (! SSL_set_fd(ssl, newsock)) {
tmcd_sslerror(); tmcd_sslerror();
errno = EIO;
return -1; return -1;
} }
if (SSL_accept(ssl) <= 0) { if (SSL_accept(ssl) <= 0) {
tmcd_sslerror(); tmcd_sslerror();
errno = EAUTH;
return -1; return -1;
} }
tmcd_sslverify(newsock, 0);
return newsock; return newsock;
} }
...@@ -209,53 +244,136 @@ tmcd_sslaccept(int sock, struct sockaddr *addr, socklen_t *addrlen) ...@@ -209,53 +244,136 @@ tmcd_sslaccept(int sock, struct sockaddr *addr, socklen_t *addrlen)
int int
tmcd_sslconnect(int sock, const struct sockaddr *name, socklen_t namelen) tmcd_sslconnect(int sock, const struct sockaddr *name, socklen_t namelen)
{ {
char *cp = SPEAKSSL;
int cc;
X509 *peer;
char cname[256];
struct hostent *he;
struct in_addr ipaddr;
if (connect(sock, name, namelen) < 0) if (connect(sock, name, namelen) < 0)
return -1; return -1;
/*
* Send our special tag which says we speak SSL.
*/
if ((cc = write(sock, cp, strlen(cp))) != strlen(cp)) {
if (cc >= 0) {
error("sslconnect: short write\n");
errno = EIO;
}
return -1;
}
if (! (ssl = SSL_new(ctx))) { if (! (ssl = SSL_new(ctx))) {
tmcd_sslerror(); tmcd_sslerror();
errno = EIO;
return -1; return -1;
} }
if (! SSL_set_fd(ssl, sock)) { if (! SSL_set_fd(ssl, sock)) {
tmcd_sslerror(); tmcd_sslerror();
errno = EIO;
return -1; return -1;
} }
if (SSL_connect(ssl) <= 0) { if (SSL_connect(ssl) <= 0) {
tmcd_sslerror(); tmcd_sslerror();
return -1; goto badauth;
}
/*
* Do the verification dance.
*/
if (SSL_get_verify_result(ssl) != X509_V_OK) {
tmcd_sslprint("Certificate did not verify!\n");
goto badauth;
}
if (! (peer = SSL_get_peer_certificate(ssl))) {
tmcd_sslprint("No certificate was presented by the peer!\n");
goto badauth;
}
/*
* Grab the common name from the cert.
*/
X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName, cname, sizeof(cname));
/*
* On the client, the common name must map to the same
* host we just connected to. This should be enough of
* a check?
*/
ipaddr = ((struct sockaddr_in *)name)->sin_addr;
if (!(he = gethostbyaddr((char *) &ipaddr, sizeof(ipaddr), AF_INET))) {
error("Could not reverse map %s: %s\n",
inet_ntoa(ipaddr), hstrerror(h_errno));
goto badauth;
}
if (strcmp(he->h_name, cname)) {
error("Certificate commonname mismatch: %s!=%s\n",
he->h_name, cname);
goto badauth;
} }
tmcd_sslverify(sock, 0);
return 0; return 0;
badauth:
errno = EAUTH;
return -1;
} }
/* /*
* Verify the certificate of the peer. * Verify the certificate of the client.
*/ */
int int
tmcd_sslverify(int sock, char *host) tmcd_sslverify_client(char *nodeid, char *class, char *type, int islocal)
{ {
X509 *peer; X509 *peer;
char *cp, buf[256]; char cname[256], unitname[256];
if (SSL_get_verify_result(ssl) != X509_V_OK) { if (SSL_get_verify_result(ssl) != X509_V_OK) {
tmcd_sslprint("Certificate did not verify!\n"); error("sslverify: Certificate did not verify!\n");
return 1; return -1;
} }
if (! (peer = SSL_get_peer_certificate(ssl))) { if (! (peer = SSL_get_peer_certificate(ssl))) {
tmcd_sslprint("No certificate was presented by the peer!\n"); error("sslverify: No certificate presented!\n");
return 1; return -1;
} }
if ((cp = X509_NAME_oneline(X509_get_subject_name(peer), 0, 0))) { /*
printf("Peer subject: %s\n", cp); * Grab stuff from the cert.
free(cp); */
X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_organizationalUnitName,
unitname, sizeof(unitname));
X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
NID_commonName,
cname, sizeof(cname));
/*
* On the server, things are a bit more difficult since
* we share a common cert locally and a per group cert remotely.
*
* Make sure common name matches.
*/
if (strcmp(cname, BOSSNODE)) {
error("sslverify: commonname mismatch: %s!=%s\n",
cname, BOSSNODE);
return -1;
} }
if ((cp = X509_NAME_oneline(X509_get_issuer_name(peer), 0, 0))) { /*
printf("Peer issuer: %s\n", cp); * If the node is remote, then the unitname must match the type.
free(cp); * Simply a convention.
*/
if (!islocal && strcmp(unitname, type)) {
error("sslverify: unitname mismatch: %s!=%s\n",
unitname, type);
return -1;
} }
return 0; return 0;
...@@ -272,8 +390,13 @@ tmcd_sslwrite(int sock, const void *buf, size_t nbytes) ...@@ -272,8 +390,13 @@ tmcd_sslwrite(int sock, const void *buf, size_t nbytes)
int cc; int cc;
errno = 0; errno = 0;
if ((cc = SSL_write(ssl, buf, nbytes)) <= 0) { if (isssl || client)
if (cc < 0) { cc = SSL_write(ssl, buf, nbytes);
else
cc = write(sock, buf, nbytes);
if (cc <= 0) {
if (cc < 0 && isssl) {
tmcd_sslerror(); tmcd_sslerror();
} }
return cc; return cc;
...@@ -287,11 +410,27 @@ tmcd_sslwrite(int sock, const void *buf, size_t nbytes) ...@@ -287,11 +410,27 @@ tmcd_sslwrite(int sock, const void *buf, size_t nbytes)
int int
tmcd_sslread(int sock, void *buf, size_t nbytes) tmcd_sslread(int sock, void *buf, size_t nbytes)
{ {
int cc; int cc = 0;
if (nosslbuflen) {
char *bp = (char *) buf, *cp = &nosslbuf[nosslbufidx];
while (cc < nbytes && nosslbuflen) {
*bp = *cp;
bp++; cp++; cc++;
nosslbuflen--; nosslbufidx++;
}
return cc;
}
errno = 0; errno = 0;
if ((cc = SSL_read(ssl, buf, nbytes)) <= 0) { if (isssl || client)
if (cc < 0) { cc = SSL_read(ssl, buf, nbytes);
else
cc = read(sock, buf, nbytes);
if (cc <= 0) {
if (cc < 0 && isssl) {
tmcd_sslerror(); tmcd_sslerror();
} }
return cc; return cc;
...@@ -310,6 +449,7 @@ tmcd_sslclose(int sock) ...@@ -310,6 +449,7 @@ tmcd_sslclose(int sock)
SSL_free(ssl); SSL_free(ssl);
ssl = NULL; ssl = NULL;
} }
nosslbuflen = 0;
close(sock); close(sock);
return 0; return 0;
} }
...@@ -345,8 +485,7 @@ tmcd_sslprint(const char *fmt, ...) ...@@ -345,8 +485,7 @@ tmcd_sslprint(const char *fmt, ...)
if (client) { if (client) {
fputs(buf, stderr); fputs(buf, stderr);
fputs("\n", stderr);
} }
else else
error("%s\n", buf); error("%s", buf);
} }
...@@ -9,7 +9,14 @@ int tmcd_sslconnect(int sock, const struct sockaddr *, socklen_t); ...@@ -9,7 +9,14 @@ int tmcd_sslconnect(int sock, const struct sockaddr *, socklen_t);
int tmcd_sslwrite(int sock, const void *buf, size_t nbytes); int tmcd_sslwrite(int sock, const void *buf, size_t nbytes);
int tmcd_sslread(int sock, void *buf, size_t nbytes); int tmcd_sslread(int sock, void *buf, size_t nbytes);
int tmcd_sslclose(int sock); int tmcd_sslclose(int sock);
int tmcd_sslverify(int sock, char *host); int tmcd_sslverify_client(char *, char *, char *, int);
int isssl;
/*
* The client sends this tag to indicate that it is SSL capable.
* Only local nodes can skip SSL. Remote nodes must use SSL!
*/
#define SPEAKSSL "ISPEAKSSL_TMCDV10"
/* /*
* When compiled to use SSL, redefine the routines appropriately * When compiled to use SSL, redefine the routines appropriately
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <netdb.h>
#include <ctype.h> #include <ctype.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -18,6 +19,7 @@ ...@@ -18,6 +19,7 @@
#include "config.h" #include "config.h"
#include "ssl.h" #include "ssl.h"
#include "log.h" #include "log.h"
#include "tbdefs.h"
#ifdef EVENTSYS #ifdef EVENTSYS
#include "event.h" #include "event.h"
...@@ -42,14 +44,15 @@ ...@@ -42,14 +44,15 @@
#define DBNAME_SIZE 64 #define DBNAME_SIZE 64
#define DEFAULT_DBNAME TBDBNAME #define DEFAULT_DBNAME TBDBNAME
static int debug = 0; int debug = 0;
static int portnum = TBSERVER_PORT; static int portnum = TBSERVER_PORT;
static char dbname[DBNAME_SIZE]; static char dbname[DBNAME_SIZE];
static struct in_addr myipaddr;
static int nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid); static int nodeidtoexp(char *nodeid, char *pid, char *eid, char *gid);
static int iptonodeid(struct in_addr ipaddr, char *bufp); static int iptonodeid(struct in_addr, char *, char *, char *, int *);
static int nodeidtonickname(char *nodeid, char *nickname); static int nodeidtonickname(char *nodeid, char *nickname);
static int nodeidtocontrolnet(char *nodeid, int *net); static int nodeidtocontrolnet(char *nodeid, int *net);
static int checkdbredirect(struct in_addr ipaddr); static int checkdbredirect(char *nodeid);
static void tcpserver(int sock); static void tcpserver(int sock);
static void udpserver(int sock); static void udpserver(int sock);
static int handle_request(int, struct sockaddr_in *, char *, int); static int handle_request(int, struct sockaddr_in *, char *, int);
...@@ -76,7 +79,7 @@ static event_handle_t event_handle = NULL; ...@@ -76,7 +79,7 @@ static event_handle_t event_handle = NULL;
*/ */
#define COMMAND_PROTOTYPE(x) \ #define COMMAND_PROTOTYPE(x) \
static int \ static int \
x(int sock, struct in_addr ipaddr, char *rdata, int tcp, int vers) x(int sock, char *nodeid, char *rdata, int tcp, int vers)
COMMAND_PROTOTYPE(doreboot); COMMAND_PROTOTYPE(doreboot);
COMMAND_PROTOTYPE(dostatus); COMMAND_PROTOTYPE(dostatus);
...@@ -104,7 +107,7 @@ COMMAND_PROTOTYPE(docreator); ...@@ -104,7 +107,7 @@ COMMAND_PROTOTYPE(docreator);
struct command { struct command {
char *cmdname; char *cmdname;
int (*func)(int, struct in_addr, char *, int, int); int (*func)(int, char *, char *, int, int);
} command_array[] = { } command_array[] = {
{ "reboot", doreboot }, { "reboot", doreboot },
{ "status", dostatus }, { "status", dostatus },
...@@ -161,8 +164,8 @@ static void ...@@ -161,8 +164,8 @@ static void
cleanup() cleanup()
{ {
signal(SIGHUP, SIG_IGN); signal(SIGHUP, SIG_IGN);
killpg(0, SIGHUP);
killme = 1; killme = 1;
killpg(0, SIGHUP);
} }
int int
...@@ -173,6 +176,7 @@ main(int argc, char **argv) ...@@ -173,6 +176,7 @@ main(int argc, char **argv)
struct sockaddr_in name; struct sockaddr_in name;
FILE *fp; FILE *fp;
char buf[BUFSIZ]; char buf[BUFSIZ];
struct hostent *he;
extern char build_info[]; extern char build_info[];
while ((ch = getopt(argc, argv, "dp:c:")) != -1) while ((ch = getopt(argc, argv, "dp:c:")) != -1)
...@@ -215,6 +219,21 @@ main(int argc, char **argv) ...@@ -215,6 +219,21 @@ main(int argc, char **argv)
info("daemon starting (version %d)\n", CURRENT_VERSION); info("daemon starting (version %d)\n", CURRENT_VERSION);
info("%s\n", build_info); info("%s\n", build_info);
/*
* Grab our IP for security check below.
*/
#ifdef LBS
strcpy(buf, BOSSNODE);
#else
if (gethostname(buf, sizeof(buf)) < 0)
pfatal("getting hostname");
#endif
if ((he = gethostbyname(buf)) == NULL) {
error("Could not get IP (%s) - %s\n", buf, hstrerror(h_errno));
exit(1);
}
memcpy((char *)&myipaddr, he->h_addr, he->h_length);
/* /*
* Setup TCP socket for incoming connections. * Setup TCP socket for incoming connections.
*/ */
...@@ -343,7 +362,7 @@ main(int argc, char **argv) ...@@ -343,7 +362,7 @@ main(int argc, char **argv)
} }
/* /*
* Listen for UDP requests. This not a secure channel, and so this should * Listen for UDP requests. This is not a secure channel, and so this should
* eventually be killed off. * eventually be killed off.
*/ */
static void static void
...@@ -375,8 +394,7 @@ udpserver(int sock) ...@@ -375,8 +394,7 @@ udpserver(int sock)
} }
/* /*
* Listen for TCP requests. This not a secure channel, and so this should * Listen for TCP requests.
* eventually be killed off.
*/ */
static void static void
tcpserver(int sock) tcpserver(int sock)
...@@ -421,7 +439,10 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp) ...@@ -421,7 +439,10 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
struct sockaddr_in redirect_client; struct sockaddr_in redirect_client;
int redirect = 0; int redirect = 0;
char buf[BUFSIZ], *bp; char buf[BUFSIZ], *bp;
int i, cc, err = 0; char nodeid[TBDB_FLEN_NODEID];
char class[TBDB_FLEN_NODECLASS];
char type[TBDB_FLEN_NODETYPE];
int i, cc, islocal, err = 0;
int version = DEFAULT_VERSION; int version = DEFAULT_VERSION;
cc = strlen(rdata); cc = strlen(rdata);
...@@ -459,14 +480,24 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp) ...@@ -459,14 +480,24 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
bp++; bp++;
} }
#ifndef TESTMODE /* Start with default DB */
strcpy(dbname, DEFAULT_DBNAME);
/* /*
* IN TESTMODE, we allow redirect. * Map the ip to a nodeid.
* Otherwise not since that would be a (minor) privacy
* risk, by allowing testbed nodes to get info for other
* nodes.
*/ */
if (redirect) { if (iptonodeid(client->sin_addr, nodeid, class, type, &islocal)) {
error("No such node: %s\n", inet_ntoa(client->sin_addr));
goto skipit;
}
/*
* Redirect is allowed from the local host only!
* I use this for testing. See below where I test redirect
* if the verification fails.
*/
if (redirect &&
redirect_client.sin_addr.s_addr != myipaddr.s_addr) {
char buf1[32], buf2[32]; char buf1[32], buf2[32];
strcpy(buf1, inet_ntoa(redirect_client.sin_addr)); strcpy(buf1, inet_ntoa(redirect_client.sin_addr));
...@@ -475,21 +506,49 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp) ...@@ -475,21 +506,49 @@ handle_request(int sock, struct sockaddr_in *client, char *rdata, int istcp)
info("%s INVALID REDIRECT: %s\n", buf1, buf2); info("%s INVALID REDIRECT: %s\n", buf1, buf2);
goto skipit; goto skipit;
} }
#ifdef WITHSSL