// Copyright (c) 2004 David Muse
// See the file COPYING for more information
#include <rudiments/daemonprocess.h>
#include <rudiments/permissions.h>
#include <rudiments/inetserversocket.h>
#include <rudiments/charstring.h>
#include <rudiments/error.h>
#include <rudiments/file.h>
#include <openssl/err.h>
#ifdef RUDIMENTS_NAMESPACE
using namespace rudiments;
#endif
int passwdCallback(char *buf, int size, int rwflag, void *userdata) {
charstring::copy(buf,(char *)userdata,size);
buf[size-1]=(char)NULL;
return charstring::length(buf);
}
class myserver : public daemonprocess, public inetserversocket {
public:
myserver() : daemonprocess(), inetserversocket() {}
void listen();
};
void myserver::listen() {
// make sure that only one instance is running
int pid=checkForPidFile("/tmp/svr.pidfile");
if (pid>-1) {
printf("Sorry, an instance of this server is already running with process id: %d\n",pid);
return;
}
// detach from the controlling terminal
//detach();
// create a pid file which is used to make sure that only one instance
// is running and can also be used to kill the process
createPidFile("/tmp/svr.pidfile",permissions::ownerReadWrite());
// initalize the libarary
SSL_library_init();
SSL_load_error_strings();
// Create a new server context usng SSLv2.
SSL_CTX *ctx=SSL_CTX_new(SSLv2_server_method());
// in cases where a re-negotiation must take place during an SSL_read
// or SSL_write, automatically re-negotiate, then retry the read/write
// instead of causing the read/write to fail
SSL_CTX_set_mode(ctx,SSL_MODE_AUTO_RETRY);
// load the server's certificate chain
SSL_CTX_use_certificate_chain_file(ctx,"server.pem");
// if the private key requires a password in order to read it,
// supply "password"
SSL_CTX_set_default_passwd_cb(ctx,passwdCallback);
SSL_CTX_set_default_passwd_cb_userdata(ctx,(void *)"password");
// load the server's private key
SSL_CTX_use_PrivateKey_file(ctx,"server.pem",SSL_FILETYPE_PEM);
// Instruct the server to request the client's certificate. Servers
// always send certificates to clients, but in order for a client to
// send a certificate to a server, the server must request it.
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
// load certificates for the signing authorities that we trust
SSL_CTX_load_verify_locations(ctx,"ca.pem",0);
#if (OPENSSL_VERSION_NUMBER < 0x0090600fL)
// client certificates must be directly signed
// by one of the signing authorities that we trust
SSL_CTX_set_verify_depth(ctx,1);
#endif
// Instruct the context to use an ephemeral
// dh key for encrypting the session.
SSL_CTX_set_options(ctx,SSL_OP_SINGLE_DH_USE);
// Get parameters for generating the dh key from dh1024.pem.
BIO *bio=BIO_new_file("dh1024.pem","r");
#if (OPENSSL_VERSION_NUMBER < 0x00904000)
DH *dh=PEM_read_bio_DHparams(bio,NULL,NULL);
#else
DH *dh=PEM_read_bio_DHparams(bio,NULL,NULL,NULL);
#endif
BIO_free(bio);
// Supply the context with the dh parameters for generating the key.
SSL_CTX_set_tmp_dh(ctx,dh);
// listen on inet socket port 8000
if (!inetserversocket::listen(NULL,8000,15)) {
printf("couldn't listen on port 8000\n");
exit(0);
}
// loop...
for (;;) {
// attach the ssl context to the server
setSSLContext(ctx);
// accept a client connection
filedescriptor *clientsock=accept();
if (clientsock) {
// make sure the client sent a certificate
X509 *certificate=SSL_get_peer_certificate(
clientsock->getSSL());
if (!certificate) {
printf("peer sent no certificate\n");
clientsock->close();
delete clientsock;
continue;
}
// make sure the certificate was valid
long result=SSL_get_verify_result(
clientsock->getSSL());
if (result!=X509_V_OK) {
printf("SSL_get_verify_result failed: %d\n",
result);
clientsock->close();
continue;
}
// Make sure the commonname in the certificate is the
// one we expect it to be (client.localdomain).
// (we may also want to check the subject name field or
// certificate extension for the commonname because
// sometimes it's there instead of in the commonname
// field)
char commonname[256];
X509_NAME_get_text_by_NID(
X509_get_subject_name(certificate),
NID_commonName,commonname,256);
if (charstring::compareIgnoringCase(commonname,
"client.localdomain")) {
printf("%s!=client.localdomain\n",commonname);
clientsock->close();
delete clientsock;
continue;
}
// read 5 bytes from the client and display it
char buffer[6];
buffer[5]=(char)NULL;
clientsock->read((char *)buffer,5);
printf("%s\n",buffer);
// write "hello" back to the client
clientsock->write("hello",5);
} else {
// if errno>0 then we got a system error, otherwise we
// got an SSL-specific error
if (errno) {
printf("accept failed: %s\n",
error::getErrorString());
} else {
printf("accept failed: ");
ERR_print_errors_fp(stdout);
printf("\n");
}
}
// close the socket and clean up
clientsock->close();
delete clientsock;
}
}
myserver *mysvr;
// define a function to shut down the process cleanly
RETSIGTYPE shutDown() {
printf("shutting down\n");
mysvr->close();
delete mysvr;
file::remove("/tmp/svr.pidfile");
exit(0);
}
int main(int argc, const char **argv) {
mysvr=new myserver();
// set up signal handlers for clean shutdown
mysvr->handleShutDown((RETSIGTYPE *)shutDown);
mysvr->handleCrash((RETSIGTYPE *)shutDown);
mysvr->listen();
}
syntax highlighted by Code2HTML, v. 0.9.1