aolserver4-nsopenssl-3.0beta26/0002755000175000017500000000000010737007304016313 5ustar frankiefrankieaolserver4-nsopenssl-3.0beta26/ca/0002755000175000017500000000000010737007304016676 5ustar frankiefrankieaolserver4-nsopenssl-3.0beta26/ca/Makefile0000644000175000017500000000451607550442011020337 0ustar frankiefrankie# Create test certificate authorities and certificates # $Header: /cvsroot/aolserver/nsopenssl/ca/Makefile,v 1.2 2002/10/08 02:33:45 scottg Exp $ # In progress -- figure out if openssl will create the subdirs specified in the ca1.conf file, # or if I have to manually do that here. MKDIR = /bin/mkdir -p TOUCH = /bin/touch ############################################################################### # CA #1 - Web Server Certificate Signing CA1 = ca1 ca1: ca1-dirs @openssl genrsa -des3 \ -passout pass:$(CA1) \ -out $(CA1)/$(CA1).key \ 1024 @openssl req -new -x509 -days 365 \ -config $(CA1).conf \ -passin pass:$(CA1) \ -key $(CA1)/$(CA1).key \ -out $(CA1)/$(CA1).pem ca1-dirs: @if [ ! -d $(CA1) ]; then \ $(MKDIR) $(CA1); \ $(MKDIR) $(CA1)/certificates; \ $(MKDIR) $(CA1)/keys; \ $(MKDIR) $(CA1)/csr; \ $(MKDIR) $(CA1)/crl; \ $(TOUCH) $(CA1)/index.txt; \ echo '01' > $(CA1)/serial; \ fi ############################################################################### # CA #1 - Web Server Certificate CA1_WEB_SERVER = ca1-web-server ca1-web-server: ca1 @openssl genrsa \ -out $(CA1)/keys/$(CA1_WEB_SERVER).pem \ 1024 @openssl req -new \ -config $(CA1).conf \ -key $(CA1)/keys/$(CA1_WEB_SERVER).pem \ -out $(CA1)/csr/$(CA1_WEB_SERVER).pem @openssl ca \ -config $(CA1).conf \ -key $(CA1) \ -out $(CA1)/certificates/$(CA1_WEB_SERVER).pem \ -infiles $(CA1)/csr/$(CA1_WEB_SERVER).pem @openssl verify \ -CAfile $(CA1)/$(CA1).pem \ $(CA1)/certificates/$(CA1_WEB_SERVER).pem ############################################################################### # CA #2 - Client Certificate Signing CA2 = ca2 CA2_ROOT = $(CA2) ca2: ca2-dirs @openssl genrsa -des3 \ -passout pass:$(CA2) \ -out $(CA2_ROOT)/$(CA2).key \ 1024 @openssl req -new -x509 -days 365 \ -config $(CA2).conf \ -passin pass:$(CA2) \ -key $(CA2_ROOT)/$(CA2).key \ -out $(CA2_ROOT)/$(CA2).pem ca2-dirs: @if [ ! -d $(CA2_ROOT) ]; then \ $(MKDIR) $(CA2_ROOT); \ $(MKDIR) $(CA2_ROOT)/certificates; \ $(MKDIR) $(CA2_ROOT)/crl; \ fi ############################################################################### ### END # Take a look at the key in readable format #openssl rsa -noout -text -in ca.key # Take a look at the certificate in readable format #openssl x509 -noout -text -in ca.crt aolserver4-nsopenssl-3.0beta26/ca/ca1.conf0000755000175000017500000000325207550442011020211 0ustar frankiefrankie# nsopenssl Test Certificate Authority Configuration File # # $Header: /cvsroot/aolserver/nsopenssl/ca/ca1.conf,v 1.2 2002/10/08 02:33:45 scottg Exp $ # # This configuration file is used to generate a CA cert and sign # test certificates for nsopenssl testing. [ ca ] default_ca = ca1 [ ca1 ] certificate = ca1/ca1.pem private_key = ca1/ca1.key new_certs_dir = ca1/certificates crl_dir = ca1/crl crl = ca1/crl/crl.pem database = ca1/index.txt serial = ca1/serial default_days = 365 default_crl_days = 30 default_md = md5 preserve = no policy = ca1-policy x509_extensions = ca1-extensions [ ca1-policy ] countryName = supplied stateOrProvinceName = supplied localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional # These extensions are added when a cert is signed by the CA [ ca1-extensions ] basicConstraints = CA:false [ req ] default_bits = 2048 default_keyfile = ca1/ca1.key default_md = md5 prompt = no distinguished_name = root-ca1-distinguished-name x509_extensions = root-ca1-x509-extensions [ root-ca1-distinguished-name ] commonName = nsopenssl CA #1 Test CA stateOrProvinceName = Florida countryName = US emailAddress = ca1@scottg.net organizationName = nsopenssl CA #1 Root Certification Authority [ root-ca1-x509-extensions ] basicConstraints = CA:true aolserver4-nsopenssl-3.0beta26/ca/ca2.conf0000755000175000017500000000323607550442011020214 0ustar frankiefrankie# nsopenssl Test Certificate Authority Configuration File # # $Header: /cvsroot/aolserver/nsopenssl/ca/ca2.conf,v 1.2 2002/10/08 02:33:45 scottg Exp $ # # This configuration file is used to generate a CA cert and sign # test certificates for nsopenssl testing. [ ca ] default_ca = ca2 [ ca2 ] certificate = ca2/ca2.pem private_key = ca2/ca2.key new_certs_dir = ca2/certificates crl_dir = ca2/crl crl = ca2/crl/crl.pem database = ca2/index.txt serial = ca2/serial default_days = 365 default_crl_days = 30 default_md = md5 preserve = no policy = ca2-policy x509_extensions = ca2-extensions [ ca2-policy ] countryName = USA stateOrProvinceName = Florida localityName = None organizationName = None organizationalUnitName = None commonName = scottg emailAddress = scott@scottg.net # These extensions are added when a cert is signed by the CA [ ca2-extensions ] basicConstraints = CA:false [ req ] default_bits = 2048 default_keyfile = ca2/ca2.key default_md = md5 prompt = no distinguished_name = root-ca2-distinguished-name x509_extensions = root-ca2-x509-extensions [ root-ca2-distinguished-name ] commonName = nsopenssl CA #2 Test CA stateOrProvinceName = Florida countryName = US emailAddress = ca2@scottg.net organizationName = nsopenssl CA #2 Root Certification Authority [ root-ca2-x509-extensions ] basicConstraints = CA:true aolserver4-nsopenssl-3.0beta26/nsopenssl.c0000644000175000017500000007427610263172426020523 0ustar frankiefrankie/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. * * Copyright (C) 2000-2003 Scott S. Goodwin * * Module originally written by Stefan Arentz. Early contributions made by * Freddie Mendoze and Rob Mayoff. * * Portions created by AOL are Copyright (C) 1999 America Online, Inc. * All Rights Reserved. */ /* * nsopenssl.c -- * * Implements SSLv2, SSLv3 and TLSv1 module using OpenSSL. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsopenssl/nsopenssl.c,v 1.77 2004/11/20 06:42:54 dossy Exp $, compiled: " __DATE__ " " __TIME__; #include "nsopenssl.h" extern Tcl_HashTable NsOpenSSLServers; extern void NsOpenSSLDriversLoad(char *server); extern int NsMakeTmpRSAKey(int keylen); static Ns_Mutex *locks; static Ns_DriverProc OpenSSLProc; static int InitOpenSSL(void); static void InitServerState(char *server); static int SeedPRNG(void); static void ThreadLockCallback(int mode, int n, const char *file, int line); static unsigned long ThreadIdCallback(void); static struct CRYPTO_dynlock_value *ThreadDynlockCreateCallback(char *file, int line); static void ThreadDynlockLockCallback(int mode, struct CRYPTO_dynlock_value *dynlock, const char *file, int line); static void ThreadDynlockDestroyCallback(struct CRYPTO_dynlock_value *dynlock, const char *file, int line); static void ServerShutdown(void *arg); static void LoadSSLContexts(char *server); static NsOpenSSLContext *LoadSSLContext(char *server, char *name); static int InitSSLDriver(char *server, NsOpenSSLDriver *ssldriver); static void LoadSSLDrivers(char *server); int Ns_ModuleVersion = 1; /* *---------------------------------------------------------------------- * * Ns_ModuleInit -- * * nsopenssl module initialization. * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ int Ns_ModuleInit(char *server, char *module) { static int initialized = NS_FALSE; int i; /* * Initialize one-time global stuff. */ if (initialized == NS_FALSE) { if (!STREQ(module, MODULE)) { Ns_Log(Fatal, "Module '%s' should be named '%s'", module, MODULE); } if (InitOpenSSL() == NS_ERROR) { Ns_Log(Error, "%s: OpenSSL failed to initialize", MODULE); return NS_ERROR; } Tcl_InitHashTable(&NsOpenSSLServers, TCL_STRING_KEYS); /* * Pre-generate temporary RSA keys for 512 and 1024 bit keys. */ NsMakeTmpRSAKey(512); NsMakeTmpRSAKey(1024); initialized = NS_TRUE; } /* * Initialize this virtual server's state information */ InitServerState(server); /* * Load this virtual server's SSL contexts from the configuration file. */ LoadSSLContexts(server); /* * Load and start the driver(s) for this virtual server. A driver manages * one SSL port; for a virtual server to use more than one port, you must * define a driver for each port. A driver must be associated with a named * SSL context. */ LoadSSLDrivers(server); /* * Create the nsopenssl Tcl commands for this virtual server's interps. */ NsOpenSSLTclInit(server); /* * Register a cleanup function to run at server shutdown time. */ Ns_RegisterAtShutdown(ServerShutdown, (void *) server); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLDebug -- * * Write message to log file. When not in debug mode, no logging is done, * function just returns. * *---------------------------------------------------------------------- */ #if 0 extern void NsOpenSSLDebug(char *fmt, ...) { va_list ap; Ns_LogSeverity severity = "Debug"; char buf[1000]; //va_start(ap, fmt); sprintf(&buf, fmt, ap); Ns_Log(severity, &buf); //va_end(ap); } #endif /* *---------------------------------------------------------------------- * * InitServerState -- * * Initialize a virtual server's state storage. This holds pointers to SSL * contexts stored by name, as well as default client and server SSL * contexts to use in cases where the programmer didn't explicitly name one * to use. * * Results: * * Side effects: * *---------------------------------------------------------------------- */ static void InitServerState(char *server) { Server *thisServer = NULL; Tcl_HashEntry *hPtr = NULL; int new = 0; Ns_DString ds; char *lockName = NULL; Ns_DStringInit(&ds); thisServer = ns_malloc(sizeof(Server)); if (thisServer == NULL) { Ns_Log(Fatal, "%s (%s): memory allocation failed"); } thisServer->server = server; thisServer->defaultservercontext = NULL; thisServer->defaultclientcontext = NULL; thisServer->nextSessionCacheId = 1; Ns_MutexInit(&thisServer->lock); Ns_DStringPrintf(&ds, "server:%s", server); lockName = Ns_DStringExport(&ds); Ns_MutexSetName2(&thisServer->lock, MODULE_SHORT, lockName); Ns_DStringTrunc(&ds, 0); ns_free(lockName); lockName = NULL; hPtr = Tcl_CreateHashEntry(&NsOpenSSLServers, server, &new); Tcl_SetHashValue(hPtr, thisServer); Tcl_InitHashTable(&thisServer->sslcontexts, TCL_STRING_KEYS); Tcl_InitHashTable(&thisServer->ssldrivers, TCL_STRING_KEYS); return; } /* *---------------------------------------------------------------------- * * LoadSSLContexts -- * * Load the SSL context for a virtual server. * * Results: * NS_OK or NS_ERROR * * Side effects: * Registers driver with AOLserver core. * *---------------------------------------------------------------------- */ static void LoadSSLContexts(char *server) { Server *thisServer = NsOpenSSLServerGet(server); NsOpenSSLContext *sslcontext = NULL; Ns_Set *sslcontexts = NULL; Ns_Set *defaults = NULL; char *path = NULL; char *name = NULL; char *value = NULL; int i = 0; path = Ns_ConfigGetPath(server, MODULE, "sslcontexts", NULL); sslcontexts = Ns_ConfigGetSection(path); /* * If no SSL contexts are defined for this virtual server, we won't start * any drivers for it, and we won't be able to use SSL at all. I'll soon be * adding the capability to build SSL contexts with the Tcl API dynamically * so it will make sense to do this at some point in the future if you want * to manage SSL conns but not through the AOLserver comm API. */ if (sslcontexts == NULL) { Ns_Log(Notice, "%s (%s): no SSL contexts defined for this server", server, MODULE); return; } for (i = 0; i < Ns_SetSize(sslcontexts); ++i) { name = Ns_SetKey(sslcontexts, i); Ns_Log(Notice, "%s (%s): loading SSL context '%s'", MODULE, server, name); sslcontext = LoadSSLContext(server, name); NsOpenSSLContextAdd(server, sslcontext); if (NsOpenSSLContextInit(server, sslcontext) == NS_ERROR) { Ns_Log(Error, "%s (%s): SSL context '%s' left uninitialized", MODULE, server, name); } } /* * Set default server SSL client and server contexts. These are used in * cases where the C or Tcl programmer does not specify what named SSL * context to use. */ path = Ns_ConfigGetPath(server, MODULE, "defaults", NULL); defaults = Ns_ConfigGetSection(path); if (defaults == NULL) { Ns_Log(Notice, "%s (%s): no default SSL contexts defined for this server", MODULE, server); return; } for (i = 0; i < Ns_SetSize(defaults); ++i) { name = Ns_SetKey(defaults, i); value = Ns_ConfigGetValue(path, name); sslcontext = Ns_OpenSSLServerSSLContextGet(server, value); if (sslcontext != NULL) { Ns_Log(Notice, "%s (%s): default SSL context for %s is %s", MODULE, server, name, value); if (STRIEQ(name, "server")) { thisServer->defaultservercontext = value; Ns_Log(Notice, "default server SSL context: %s", thisServer->defaultservercontext); } else if (STRIEQ(name, "client")) { thisServer->defaultclientcontext = value; Ns_Log(Notice, "default client SSL context: %s", thisServer->defaultclientcontext); } else { Ns_Log(Error, "%s (%s): bad parameter '%s' for default contexts", MODULE, server, name); } } else { Ns_Log(Error, "%s (%s): SSL context '%s' doesn't exist; can't use it as a default", MODULE, server, value); } } } /* *---------------------------------------------------------------------- * * LoadSSLContext -- * * Load values for a given SSL context from the configuration file. * * Results: * Pointer to SSL Context or NULL * * Side effects: * Memory may be allocated * *---------------------------------------------------------------------- */ static NsOpenSSLContext * LoadSSLContext(char *server, char *name) { NsOpenSSLContext *sslcontext = NULL; char *role = NULL; char *path = NULL; char *moduleDir = NULL; char *certFile = NULL; char *keyFile = NULL; char *caFile = NULL; char *caDir = NULL; char *protocols = NULL; char *cipherSuite = NULL; int sessionCache = DEFAULT_SESSION_CACHE; int sessionCacheSize = DEFAULT_SESSION_CACHE_SIZE; int sessionCacheTimeout = DEFAULT_SESSION_CACHE_TIMEOUT; int peerVerify = DEFAULT_PEER_VERIFY; int peerVerifyDepth = DEFAULT_PEER_VERIFY_DEPTH; int trace = DEFAULT_TRACE; path = Ns_ConfigGetPath(server, MODULE, "sslcontext", name, NULL); if (path == NULL) { Ns_Log(Error, "%s (%s): failed to find SSL context '%s' in configuration file", MODULE, server, name); return NULL; } sslcontext = NsOpenSSLContextCreate(server, name); /* * Must be "client" or "server" */ role = Ns_ConfigGetValue(path, "role"); if (role != NULL) { NsOpenSSLContextRoleSet(server, sslcontext, role); } /* * A default module directory is automatically set when the SSL context was * created, but you can override in the config file. */ moduleDir = Ns_ConfigGetValue(path, "moduledir"); if (moduleDir != NULL) { NsOpenSSLContextModuleDirSet(server, sslcontext, moduleDir); } /* * SSL clients don't require certificates, but SSL servers do. If certfile * or keyfile are NULL, are not found, or are not accessible, we'll fail * later when we try to instantiate the SSL context. */ certFile = Ns_ConfigGetValue(path, "certfile"); if (certFile != NULL) { NsOpenSSLContextCertFileSet(server, sslcontext, certFile); } keyFile = Ns_ConfigGetValue(path, "keyfile"); if (keyFile != NULL) { NsOpenSSLContextKeyFileSet(server, sslcontext, keyFile); } /* * The default protocols and ciphersuites are good for general use. */ protocols = Ns_ConfigGetValue(path, "protocols"); if (protocols != NULL) { NsOpenSSLContextProtocolsSet(server, sslcontext, protocols); } cipherSuite = Ns_ConfigGetValue(path, "ciphersuite"); if (cipherSuite != NULL) { NsOpenSSLContextCipherSuiteSet(server, sslcontext, cipherSuite); } /* * The CA file/dir isn't necessary unless you actually do cert * verification. The CA file is simply a bunch of PEM-format CA * certificates concatenated together. */ caFile = Ns_ConfigGetValue(path, "cafile"); if (caFile != NULL) { NsOpenSSLContextCAFileSet(server, sslcontext, caFile); } caDir = Ns_ConfigGetValue(path, "cadir"); if (caDir != NULL) { NsOpenSSLContextCADirSet(server, sslcontext, caDir); } /* * Peer verification will cause the server to request a client certificate. * If you aren't sure whether to turn it on or not, leave it off! */ if (Ns_ConfigGetBool(path, "peerverify", &peerVerify) == NS_TRUE) { NsOpenSSLContextPeerVerifySet(server, sslcontext, peerVerify); } /* * A certificate may be at the bottom of a chain. Verify depth determines * how many levels down from the root cert you're willing to trust.. */ if (Ns_ConfigGetInt(path, "peerverifydepth", &peerVerifyDepth) == NS_TRUE) { NsOpenSSLContextPeerVerifyDepthSet(server, sslcontext, peerVerifyDepth); } /* * Session caching defaults to on, and should always be on if you * have web browsers connecting. Some versions of MSIE and Netscape will * fail if you don't have session caching on. */ Ns_ConfigGetBool(path, "sessioncache", &sessionCache); if (sessionCache == NS_TRUE) { NsOpenSSLContextSessionCacheSet(server, sslcontext, sessionCache); } if (Ns_ConfigGetInt(path, "sessioncachesize", &sessionCacheSize) == NS_TRUE) { NsOpenSSLContextSessionCacheSizeSet(server, sslcontext, sessionCacheSize); } if (Ns_ConfigGetInt(path, "sessioncachetimeout", &sessionCacheTimeout) == NS_TRUE) { NsOpenSSLContextSessionCacheTimeoutSet(server, sslcontext, sessionCacheTimeout); } /* * Trace SSL handshake. */ Ns_ConfigGetBool(path, "trace", &trace); if (trace == NS_TRUE) { NsOpenSSLContextTraceSet(server, sslcontext, 1); } else { NsOpenSSLContextTraceSet(server, sslcontext, 0); } return sslcontext; } /* *---------------------------------------------------------------------- * * ServerShutdown -- * * Cleanup function to run at server shutdown. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void ServerShutdown(void *arg) { char *server = (char *) arg; Ns_Log(Notice, "Shutdown called for server %s", server); /* * for each vserver.driver * for each vserver.driver.conn * close, free * endfor * free vserver.driver * endfor * free vserver */ return; } /* *---------------------------------------------------------------------- * * InitOpenSSL -- * * Initialize the OpenSSL library. * * Results: * NS_OK * * Side effects: * Sets OpenSSL threading callbacks, seeds the pseudo random number * generator, initializes SSL session caching id generation. * *---------------------------------------------------------------------- */ static int InitOpenSSL(void) { int i = 0; int seedcnt = 0; size_t num_locks = 0; char *lockName = NULL; Ns_DString ds; Ns_DStringInit(&ds); /* * Initialize OpenSSL callbacks */ if (CRYPTO_set_mem_functions(ns_malloc, ns_realloc, ns_free) == 0) Ns_Log(Warning, "%s: OpenSSL memory callbacks failed in InitOpenSSL", MODULE); num_locks = CRYPTO_num_locks(); locks = ns_calloc(num_locks, sizeof(*locks)); for (i = 0; i < num_locks; i++) { Ns_DStringPrintf(&ds, "crypto:%d", i); lockName = Ns_DStringExport(&ds); Ns_MutexSetName2(locks + i, MODULE_SHORT, lockName); Ns_DStringTrunc(&ds, 0); ns_free(lockName); lockName = NULL; } Ns_DStringFree(&ds); CRYPTO_set_locking_callback(ThreadLockCallback); CRYPTO_set_id_callback(ThreadIdCallback); /* * Initialize the OpenSSL library itself */ SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); SSL_library_init(); X509V3_add_standard_extensions(); /* * Seed the OpenSSL Pseudo-Random Number Generator. */ while (! RAND_status() && seedcnt < 3) { seedcnt++; Ns_Log(Notice, "%s: Seeding OpenSSL's PRNG", MODULE); SeedPRNG(); } if (! RAND_status()) { Ns_Log(Warning, "%s: PRNG fails to have enough entropy after %d tries", MODULE, seedcnt); } return NS_OK; } /* *---------------------------------------------------------------------- * * SeedPRNG -- * * Seed OpenSSL's PRNG. OpenSSL will seed the PRNG transparently if * /dev/urandom is available. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * An NS_FALSE will result in the connection failing. This function * might be called at any time by the temporary key generating * function if the PRNG is not sufficiently entropinous (yes, I * made that word up). * * *---------------------------------------------------------------------- */ static int SeedPRNG(void) { double *buf_ptr = NULL; double *bufoffset_ptr = NULL; char *path = NULL; char *randomFile = NULL; size_t size = 0; int i = 0; int seedBytes = 0; int readBytes = 0; int maxBytes = 0; if (RAND_status()) { return NS_TRUE; } path = Ns_ConfigGetPath(NULL, MODULE, NULL); if (Ns_ConfigGetInt(path, "seedbytes", &seedBytes) == NS_FALSE) { seedBytes = DEFAULT_SEEDBYTES; } if (Ns_ConfigGetInt(path, "maxbytes", &maxBytes) == NS_FALSE) { maxBytes = DEFAULT_MAXBYTES; } /* * Try to use the file specified by the user. If PRNG fails to seed here, * you might try increasing the seedBytes parameter in nsd.tcl. */ randomFile = Ns_ConfigGetValue(path, "randomfile"); if (randomFile != NULL && access(randomFile, F_OK) == 0) { if ((readBytes = RAND_load_file(randomFile, maxBytes))) { Ns_Log(Notice, "%s: Obtained %d random bytes from %s", MODULE, readBytes, randomFile); } else { Ns_Log(Warning, "%s: Unable to retrieve any random data from %s", MODULE, randomFile); } } else { Ns_Log(Warning, "%s: No randomFile set and/or found", MODULE); } if (RAND_status()) { return NS_TRUE; } /* * Use Ns_DRand(), passing it seedBytes as the second argument to RAND_add. */ size = sizeof(double) * seedBytes; buf_ptr = Ns_Malloc(size); bufoffset_ptr = buf_ptr; for (i = 0; i < seedBytes; i++) { *bufoffset_ptr = Ns_DRand(); bufoffset_ptr++; } RAND_add(buf_ptr, seedBytes, (double) seedBytes); ns_free(buf_ptr); if (!RAND_status()) { Ns_Log(Warning, "%s: failed to seed PRNG", MODULE); return NS_FALSE; } return NS_TRUE; } /* *---------------------------------------------------------------------- * * ThreadLockCallback -- * * Lock or unlock a mutex for OpenSSL. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void ThreadLockCallback(int mode, int n, const char *file, int line) { if (mode & CRYPTO_LOCK) { Ns_MutexLock(locks + n); } else { Ns_MutexUnlock(locks + n); } } /* *---------------------------------------------------------------------- * * ThreadIdCallback -- * * Return this thread's id for OpenSSL. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static unsigned long ThreadIdCallback(void) { return (unsigned long) Ns_ThreadId(); } /* *---------------------------------------------------------------------- * * ThreadDynlockCreateCallback -- * * Create a dynamically-allocated mutex for OpenSSL. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static struct CRYPTO_dynlock_value * ThreadDynlockCreateCallback(char *file, int line) { Ns_Mutex *lock = NULL; Ns_DString ds; Ns_DStringInit(&ds); lock = ns_calloc(1, sizeof(*lock)); Ns_DStringVarAppend(&ds, "openssl: ", file, ": "); Ns_DStringPrintf(&ds, "%d", line); Ns_MutexSetName2(lock, MODULE, Ns_DStringValue(&ds)); return (struct CRYPTO_dynlock_value *) lock; } /* *---------------------------------------------------------------------- * * ThreadDynlockLockCallback -- * * Lock or unlock a dynamically-allocated mutex for OpenSSL. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void ThreadDynlockLockCallback(int mode, struct CRYPTO_dynlock_value *dynlock, const char *file, int line) { if (mode & CRYPTO_LOCK) { Ns_MutexLock((Ns_Mutex *) dynlock); } else { Ns_MutexUnlock((Ns_Mutex *) dynlock); } } /* *---------------------------------------------------------------------- * * ThreadDynlockDestroyCallback -- * * Destroy a dynamically-allocated mutex for OpenSSL. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void ThreadDynlockDestroyCallback(struct CRYPTO_dynlock_value *dynlock, const char *file, int line) { Ns_MutexDestroy((Ns_Mutex *) dynlock); } /* *---------------------------------------------------------------------- * * LoadSSLDrivers -- * * Load the SSL drivers for a virtual server. * * Results: * NS_OK or NS_ERROR * * Side effects: * Registers driver with AOLserver core. * *---------------------------------------------------------------------- */ static void LoadSSLDrivers(char *server) { NsOpenSSLContext *sslcontext = NULL; NsOpenSSLDriver *ssldriver = NULL; Ns_Set *ssldrivers = NULL; char *path = NULL; char *name = NULL; char *sslcontextname = NULL; int i = 0; path = Ns_ConfigGetPath(server, MODULE, "ssldrivers", NULL); ssldrivers = Ns_ConfigGetSection(path); if (ssldrivers == NULL) { Ns_Log(Notice, "%s (%s): no SSL drivers defined for this server", MODULE, server); return; } for (i = 0; i < Ns_SetSize(ssldrivers); ++i) { name = Ns_SetKey(ssldrivers, i); Ns_Log(Notice, "%s (%s): loading '%s' SSL driver", MODULE, server, name); path = Ns_ConfigGetPath(server, MODULE, "ssldriver", name, NULL); if (path == NULL) { Ns_Log(Error, "%s (%s): SSL driver '%s' not defined in configuration file", MODULE, server, name); continue; } sslcontextname = Ns_ConfigGetValue(path, "sslcontext"); if (sslcontextname == NULL) { Ns_Log(Error, "%s (%s): 'sslcontext' parameter not defined for driver '%s'", MODULE, server, name); continue; } sslcontext = Ns_OpenSSLServerSSLContextGet(server, sslcontextname); if (sslcontext == NULL) { Ns_Log(Error, "%s (%s): SSL context '%s' needed by driver '%s' not found", MODULE, server, sslcontextname, name); continue; } /* * Create the driver. */ ssldriver = ns_calloc(1, sizeof(NsOpenSSLDriver)); ssldriver->server = server; ssldriver->sslcontext = sslcontext; ssldriver->name = name; ssldriver->path = path; ssldriver->refcnt = 0; if (!Ns_ConfigGetInt(path, "port", &ssldriver->port)) { ssldriver->port = 443; } /* * Crank up the driver */ if (InitSSLDriver(server, ssldriver) != NS_OK) { } } } /* *---------------------------------------------------------------------- * * InitSSLDriver -- * * Initialize an SSL driver. * * Results: * NS_OK or NS_ERROR * * Side effects: * Registers driver with AOLserver core. * *---------------------------------------------------------------------- */ static int InitSSLDriver(char *server, NsOpenSSLDriver *ssldriver) { Ns_DriverInitData init; Server *thisServer = NULL; Tcl_HashEntry *hPtr = NULL; int new = 0; /* * Register the driver with AOLserver. */ init.version = NS_DRIVER_VERSION_1; init.name = MODULE; init.proc = OpenSSLProc; init.opts = NS_DRIVER_SSL; init.arg = ssldriver; init.path = ssldriver->path; if (Ns_DriverInit(server, MODULE, &init) != NS_ERROR) { return NS_ERROR; } /* * Add the driver to the virtual server's state info. */ thisServer = NsOpenSSLServerGet(server); Ns_MutexLock(&thisServer->lock); hPtr = Tcl_CreateHashEntry(&thisServer->ssldrivers, ssldriver->name, &new); if (new) { Tcl_SetHashValue(hPtr, ssldriver); } else { Ns_Log(Error, "%s (%s): duplicate SSL driver name: %s", MODULE, server, ssldriver->name); return NS_ERROR; } Ns_MutexUnlock(&thisServer->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * DestroySSLDriver -- * * Destroy an SSL driver. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ #if 0 static void DestroySSLDriver(NsOpenSSLDriver *ssldriver) { NsOpenSSLConn *sslconn; if (ssldriver == NULL) { return; } Ns_Log(Notice, "%s (%s): shutting down driver '%s'", MODULE, ssldriver->server, ssldriver->name); /* * Destroy connections that are still tied to this driver. We need to lock * the driver struct, set a flag that denotes it as no longer usable so new * conns that come in before we've free'd it will be refused. */ /* XXX lock */ if (ssldriver->refcnt > 0) { while ((sslconn = ssldriver->firstFreeConn) != NULL) { ssldriver->firstFreeConn = sslconn->next; /* XXX doesn't this need to have it's contents free'd? */ ns_free(sslconn); } } Ns_MutexDestroy(&ssldriver->lock); /* XXX should an SSL context be deallocated when it's refcnt reaches 0 ??? */ if (ssldriver->sslcontext != NULL) ssldriver->sslcontext->refcnt--; ns_free(ssldriver); /* * Remove driver from server state linked list. */ return; } #endif /* *---------------------------------------------------------------------- * * OpenSSLProc -- * * SSL driver callback proc. This driver performs the necessary * handshake and encryption of SSL. * * Results: * For close, always 0. For keep, 0 if connection could be * properly flushed, -1 otherwise. For send and recv, # of bytes * processed or -1 on error. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int OpenSSLProc(Ns_DriverCmd cmd, Ns_Sock *sock, struct iovec *bufs, int nbufs) { NsOpenSSLDriver *driver = (NsOpenSSLDriver *) sock->driver->arg; NsOpenSSLConn *conn = (NsOpenSSLConn *) sock->arg; int n = -1, total, op; switch (cmd) { case DriverRecv: case DriverSend: if (conn == NULL) { /* * If first connection, wrap SSL around the socket. */ conn = NsOpenSSLConnCreate(sock->sock, driver->sslcontext); conn->refcnt++; conn->peerport = driver->port; conn->recvwait = sock->driver->recvwait; conn->sendwait = sock->driver->sendwait; sock->arg = (void *) conn; } /* * Process each buffer one at a time. */ op = (cmd == DriverSend) ? NSOPENSSL_SEND : NSOPENSSL_RECV; total = 0; do { n = NsOpenSSLConnOp(conn->ssl, bufs->iov_base, (int) bufs->iov_len, op); if (n > 0) { total += n; } ++bufs; } while (n > 0 && --nbufs > 0); if (n > 0) { n = total; } break; case DriverKeep: if (conn != NULL && NsOpenSSLConnFlush(conn) == NS_OK) { n = 0; } else { n = -1; } break; case DriverClose: if (conn != NULL) { (void) NsOpenSSLConnFlush(conn); NsOpenSSLConnDestroy(conn); sock->arg = NULL; } n = 0; break; default: Ns_Log(Error, "%s (%s): Unsupported driver command '%d'", MODULE, driver->server, cmd); n = -1; break; } return n; } aolserver4-nsopenssl-3.0beta26/nsopenssl.h0000644000175000017500000002555510537737032020530 0ustar frankiefrankie/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. * * Copyright (C) 2000-2003 Scott S. Goodwin * * Module originally written by Stefan Arentz. Early contributions made by * Freddie Mendoze and Rob Mayoff. * * $Header: /cvsroot/aolserver/nsopenssl/nsopenssl.h,v 1.69 2006/03/28 17:56:30 apiskors Exp $ */ #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #endif #define SockError(i) Tcl_PosixError((i)) #ifdef __sun #include #endif /* * OpenSSL and AOLserver both define closesocket. */ #ifdef closesocket #undef closesocket #endif /* * OpenSSL Library */ #include #include #include #include #include /* * nsopenssl Default Settings. */ #include "defaults.h" /* * Hold SSL Context information. If refcnt is 0, then struct can be disposed * of or initialized. If refcnt is 1 and NsOpenSSLContextFree is called, the * data pointed to in the structure that needs to be freed will be freed. If * refcnt is > 1, a call to NsOpenSSLContextFree simply decrements refcnt. If * refcnt > 0 and NsOpenSSLContextInit is called, nothing happens. */ typedef struct NsOpenSSLContext { char *server; char *name; /* Name of this SSL context */ char *desc; int role; /* 0 = client, 1 = server */ int initialized; /* 1 = already initialized */ int refcnt; /* How many active conns I'm tied to */ char *moduleDir; char *certFile; /* PEM formatted certificate file */ char *keyFile; /* PEM formatted key file */ char *protocols; /* Allowed SSL protocols */ char *cipherSuite; /* OpenSSL-formatted cipher string */ char *caFile; /* PEM format CA file(s) concatenated */ char *caDir; /* CA directory */ int peerVerify; /* 0 = off; 1 = on */ int peerVerifyDepth; /* How deep verification path can be */ int sessionCache; /* 0 = off; 1 = on */ char *sessionCacheId; /* XXX needs to be free'd */ int sessionCacheSize; /* In bytes */ int sessionCacheTimeout; /* Flush session cache in seconds */ int trace; /* 0 = off; 1 = on */ int bufsize; int timeout; Ns_Mutex lock; SSL_CTX *sslctx; struct NsOpenSSLContext *next; struct Server *serverPtr; /* virtual server-specific data */ } NsOpenSSLContext; /* * Used to manage SSL drivers on top of the AOLserver comm driver. */ typedef struct NsOpenSSLDriver { Ns_Mutex lock; char *server; char *name; /* Name of this SSL driver */ char *path; char *dir; SOCKET lsock; int port; /* Port the core driver is listening on */ int refcnt; /* Number of conns tied to this driver */ struct NsOpenSSLContext *sslcontext; /* SSL context assoc with this driver */ int nagle; /* 0 = use TCP default; 1 = turn off nagle */ } NsOpenSSLDriver; /* * Used for both core-driven and C/Tcl API-driven conns */ typedef struct NsOpenSSLConn { Ns_Mutex lock; char *server; int peerport; /* port this connection came in or went out on */ char *peeraddr; /* String Peer IP from core server */ char peer[16]; /* peer's name */ struct NsOpenSSLContext *sslcontext; SSL_CTX *sslctx; SSL *ssl; /* initialized SSL instance itself */ SOCKET socket; int refcnt; /* don't ns_free() unless this is 0 */ int timeout; int sendwait; int recvwait; int type; /* CORE or TCLAPI */ struct Tcl_Time timer; /* for performance measurement */ struct Ns_Driver *driver; /* the core sock driver this conn belongs to */ struct NsOpenSSLDriver *ssldriver; /* the SSL driver this conn belongs to */ /* Tcl API use only */ //Tcl_Channel *getschan; //Tcl_Channel *putschan; } NsOpenSSLConn; /* * Manages each virtual server's specific SSL information. */ typedef struct Server { Ns_Mutex lock; char *server; Tcl_HashTable sslcontexts; Tcl_HashTable ssldrivers; char *defaultclientcontext; char *defaultservercontext; int nextSessionCacheId; } Server; /* * ssl.c */ extern NsOpenSSLConn * NsOpenSSLConnCreate(SOCKET socket, NsOpenSSLContext *sslcontext); extern void NsOpenSSLConnDestroy(NsOpenSSLConn *sslconn); extern int NsOpenSSLConnFlush(NsOpenSSLConn *sslconn); extern int NsOpenSSLConnSend(SSL *ssl, const void *buffer, int towrite); extern int NsOpenSSLConnOp(SSL *ssl, void *buffer, int bytes, int type); extern NsOpenSSLConn * Ns_OpenSSLSockConnect(char *server, char *host, int port, int async, int timeout, NsOpenSSLContext *sslcontext); extern NsOpenSSLConn * Ns_OpenSSLSockAccept(SOCKET sock, NsOpenSSLContext *sslcontext); extern SOCKET Ns_OpenSSLSockListen(char *addr, int port); extern int Ns_OpenSSLSockListenCallback(char *addr, int port, Ns_SockProc *proc, void *arg); extern int Ns_OpenSSLFetchUrl(char *server, Ns_DString *dsPtr, char *url, Ns_Set *headers, NsOpenSSLContext *sslcontext); /* * tclcmds.c */ extern void NsOpenSSLTclInit(char *server); /* * nsopenssl.c */ extern Server * NsOpenSSLServerGet(char *server); extern void NsOpenSSLContextAdd(char *server, NsOpenSSLContext *sslcontext); extern void Ns_OpenSSLServerSSLContextRemove(char *server, NsOpenSSLContext *sslcontext); extern NsOpenSSLContext * Ns_OpenSSLServerSSLContextGet(char *server, char *name); extern int Ns_OpenSSLIsPeerCertValid (NsOpenSSLConn *sslconn); extern NsOpenSSLContext * NsOpenSSLContextCreate(char *server, char *name); extern int NsOpenSSLContextInit(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextRelease (char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextDestroy(char *server, NsOpenSSLContext *sslcontext); extern NsOpenSSLContext * NsOpenSSLContextServerDefaultGet(char *server); extern NsOpenSSLContext * NsOpenSSLContextClientDefaultGet(char *server); extern int NsOpenSSLContextRoleSet(char *server, NsOpenSSLContext *sslcontext, char *role); extern char * NsOpenSSLContextRoleGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextModuleDirSet(char *server, NsOpenSSLContext *sslcontext, char *moduleDir); extern char * NsOpenSSLContextModuleDirGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextCertFileSet(char *server, NsOpenSSLContext *sslcontext, char *certFile); extern char * NsOpenSSLContextCertFileGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextKeyFileSet(char *server, NsOpenSSLContext *sslcontext, char *keyFile); extern char * NsOpenSSLContextKeyFileGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextProtocolsSet(char *server, NsOpenSSLContext *sslcontext, char *protocols); extern char * NsOpenSSLContextProtocolsGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextCipherSuiteSet(char *server, NsOpenSSLContext *sslcontext, char *cipherSuite); extern char * NsOpenSSLContextCipherSuiteGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextCAFileSet(char *server, NsOpenSSLContext *sslcontext, char *CAFile); extern char * NsOpenSSLContextCAFileGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextCADirSet(char *server, NsOpenSSLContext *sslcontext, char *CADir); extern char * NsOpenSSLContextCADirGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextPeerVerifySet(char *server, NsOpenSSLContext *sslcontext, int peerVerify); extern int NsOpenSSLContextPeerVerifyGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextPeerVerifyDepthSet(char *server, NsOpenSSLContext *sslcontext, int peerVerifyDepth); extern int NsOpenSSLContextPeerVerifyDepthGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextSessionCacheSet(char *server, NsOpenSSLContext *sslcontext, int sessionCache); extern int NsOpenSSLContextSessionCacheGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextSessionCacheSizeSet(char *server, NsOpenSSLContext *sslcontext, int sessionCacheSize); extern int NsOpenSSLContextSessionCacheSizeGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextSessionCacheTimeoutSet(char *server, NsOpenSSLContext *sslcontext, int sessionCacheTimeout); extern int NsOpenSSLContextSessionCacheTimeoutGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLContextTraceSet(char *server, NsOpenSSLContext *sslcontext, int trace); extern int NsOpenSSLContextTraceGet(char *server, NsOpenSSLContext *sslcontext); extern int NsOpenSSLModuleInit(char *server); /* * x509.c */ extern int Ns_OpenSSLX509CertValid(SSL *ssl); aolserver4-nsopenssl-3.0beta26/TODO0000644000175000017500000000417410052423276017007 0ustar frankiefrankieTODO for nsopenssl: - Make library loadable into tclsh - Make ns_openssl commands available to Tcl API conns - nsopenssl 3.0 release: - Ensure sslcontexts are not NULL before accessing (mostly tclcmds.c) - Remove all debug statements - Clean up log messages; ditch ones that are not really useful - Ensure clean shutdown operations (destroying all conns, then drivers, ...) - Validate client disconnect doesn't tie up reader thread - Ensure locking around structs is happening properly - Review session cache code - Clean up compiler warnings - Convert Tcl commands to TclObj commands nsopenssl 3.1 release: - Add client IP address to log messages - Fix OpenSSL version reporting - Review any further commands that can be converted to TclObjs - Add ability to refuse keepalive an a per-user agent basis - Automate the testing via wget, openssl command line - Add instrumentation to nsopenssl and OpenSSL to benchmark timing at every point - Add ability to turn off Nagle algorithm for SSL connections - See if nsopenssl can adjust OpenSSL's default buffer size w/o a recompile - Are there any tunings I can nsopenssl 4.0 release: - Revamp Tcl API -- major overhaul will require changing of Tcl proc names - Add ability to introspect on Tcl API in/out socket conns; currently can only do this with core driver conns. - Change version number scheme to match AOLserver - Review PRNG code - Ignore any ciphers or protocols listed in config that weren't compiled into OpenSSL library - Add benchmarking/performance testing - Figure out how to work with keepalive Wish List: - Move https.tcl into C - Give nsopenssl the ability to perform certificate operations so it can be used to drive a CA process. - Add CRL support - Add OCSP support - Add C and Tcl API for generation of CA / Client / Server cert - Add ability to wrap other module conns with an Ns_OpenSSLWrap C API function - Add ability to wrap ns_ldap conns - Add SSL session cache capability across multiple servers - Allow Tcl API sockcallbacks to be specified in config file (?) - Create pool of reusable conn structures aolserver4-nsopenssl-3.0beta26/Makefile0000644000175000017500000000761510263172426017764 0ustar frankiefrankie# # The contents of this file are subject to the AOLserver Public License # Version 1.1 (the "License"); you may not use this file except in # compliance with the License. You may obtain a copy of the License at # http://aolserver.com. # # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. # # Alternatively, the contents of this file may be used under the terms # of the GNU General Public License (the "GPL"), in which case the # provisions of GPL are applicable instead of those above. If you wish # to allow use of your version of this file only under the terms of the # GPL and not to allow others to use your version of this file under the # License, indicate your decision by deleting the provisions above and # replace them with the notice and other provisions required by the GPL. # If you do not delete the provisions above, a recipient may use your # version of this file under either the License or the GPL. # # Copyright (C) 2001-2003 Scott S. Goodwin # # Portions created by AOL are Copyright (C) 1999 America Online, Inc. All # Rights Reserved. # # # $Header: /cvsroot/aolserver/nsopenssl/Makefile,v 1.44 2004/11/20 06:42:54 dossy Exp $ # AOLSERVER ?= ../aolserver ifndef OPENSSL all: @echo "** " @echo "** OPENSSL variable not set." @echo "** nsopenssl will not be built." @echo "** " install: all clean: else # # Version number used in release tags. Valid VERs are "1.1c", "2.1", # "2.2beta7". VER "1.1c" will be translated into "v1_1c" by this Makefile. # VER_ = $(subst .,_,$(VER)) MODNAME = nsopenssl LIB = nsopenssl LIBOBJS = sslcontext.o ssl.o tclcmds.o x509.o LIBLIBS = -L$(OPENSSL)/lib -lssl -lcrypto MOD = nsopenssl.so MODOBJS = nsopenssl.o HDRS = nsopenssl.h MODLIBS = -L$(OPENSSL)/lib -lssl -lcrypto TCLMOD = https.tcl # Add static compilation ability, per grax3272 ifeq ($(STATIC),1) MODLIBS = $(OPENSSL)/lib/libssl.a $(OPENSSL)/lib/libcrypto.a endif # # Kerberos headers are included in case your OpenSSL library was built with # Kerberos support. This is generally true on RedHat 9 and possibly Fedora # Core. If OPENSSL_NO_KRB5 is define in then OpenSSL # *was not* compiled with Kerberos support. # CFLAGS += -I$(OPENSSL)/include -I/usr/kerberos/include INSTALL = install-https.tcl include $(AOLSERVER)/include/Makefile.module ## ## Extra install targets. ## install-https.tcl: $(INSTALL_SH) $(TCLMOD) $(INSTTCL) .PHONY: install-https.tcl # # Help the poor developer # help: @echo "**" @echo "** DEVELOPER HELP FOR THIS $(MODNAME)" @echo "**" @echo "** make tag VER=X.Y" @echo "** Tags the module CVS code with the given tag." @echo "** You can tag the CVS copy at any time, but follow the rules." @echo "** VER must be of the form:" @echo "** X.Y" @echo "** X.YbetaN" @echo "** You should browse CVS at SF to find the latest tag." @echo "**" @echo "** make file-release VER=X.Y" @echo "** Checks out the code for the given tag from CVS." @echo "** The result will be a releaseable tar.gz file of" @echo "** the form: module-X.Y.tar.gz." @echo "**" # # Tag the code in CVS right now # tag: @if [ "$(VER)" = "" ]; then echo 1>&2 "VER must be set to version number!"; exit 1; fi cvs rtag v$(VER_) $(MODNAME) # # Create a distribution file release # file-release: @if [ "$(VER)" = "" ]; then echo 1>&2 "VER must be set to version number!"; exit 1; fi @echo "(Just hit the return key when prompted for CVS password)" cvs -d :pserver:anonymous@cvs.sf.net:/cvsroot/aolserver login cd /tmp && cvs -d :pserver:anonymous@cvs.sf.net:/cvsroot/aolserver co -rv$(VER_) -d$(MODNAME)-$(VER) $(MODNAME) && tar cf - $(MODNAME)-$(VER) | gzip -c > $(MODNAME)-$(VER).tar.gz echo "--- FILE RELEASE is: /tmp/$(MODNAME)-$(VER).tar.gz" endif aolserver4-nsopenssl-3.0beta26/tests/0002755000175000017500000000000010737007304017455 5ustar frankiefrankieaolserver4-nsopenssl-3.0beta26/tests/nsopenssl.adp0000644000175000017500000000557507547200625022207 0ustar frankiefrankie SSL Test Page for the nsopenssl module

SSL Test Page for the nsopenssl module

(Copy this ADP page to your pageroot and run.)
ns_openssl info <%=[ns_openssl info]%>
ns_openssl clientcert exists <% if {[ns_openssl clientcert exists]} { ns_puts "Client cert exists" } else { ns_puts "Client cert does NOT exist" } %>
ns_openssl module name <%=[ns_openssl module name]%>
ns_openssl module port <%=[ns_openssl module port]%>
ns_openssl protocol <%=[ns_openssl protocol]%>
ns_openssl cipher name <%=[ns_openssl cipher name]%>
ns_openssl cipher strength <%=[ns_openssl cipher strength]%>
ns_openssl clientcert valid <% if {[ns_openssl clientcert valid]} { ns_puts "Client cert is valid" } else { ns_puts "Client cert is NOT valid" } %>
ns_openssl clientcert version <%=[ns_openssl clientcert version]%>
ns_openssl clientcert serial <%=[ns_openssl clientcert serial]%>
ns_openssl clientcert subject <% set var [ns_openssl clientcert subject] ns_puts "$var" %>
ns_openssl clientcert issuer <% set var [ns_openssl clientcert issuer] ns_puts "$var" %>
ns_openssl clientcert notbefore <%=[ns_openssl clientcert notbefore]%>
ns_openssl clientcert notafter <%=[ns_openssl clientcert notafter]%>
ns_openssl clientcert signature_algorithm <%=[ns_openssl clientcert signature_algorithm]%>
ns_openssl clientcert key_algorithm <%=[ns_openssl clientcert key_algorithm]%>
ns_openssl clientcert pem <%=[ns_openssl clientcert pem]%>

Client certificate support in nsopenssl brought to you by Scott S. Goodwin, http://scottg.net. Rob Mayoff refactored the code and added some extra client cert support.

Copyright © 2000 by Scott S. Goodwin

Send feedback, bugs and comments to me. Enjoy!!! aolserver4-nsopenssl-3.0beta26/tests/ns_openssl_socklisten.tcl0000755000175000017500000001004307547200625024607 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading test-socklisten.tcl" ## non-SSL socklisten/sockaccept # Listen on a non-SSL socket and read/write messages to a client ns_register_proc GET /do_socklisten do_socklisten proc do_socklisten {} { set p "do_socklisten" set sock [ns_socklisten [nsv_get . httpaddr] [nsv_get . listenport]] set fds [ns_sockaccept $sock] set rfd [lindex $fds 0] set wfd [lindex $fds 1] ns_log notice "$p: SERVER: RFD=$rfd; WFD=$wfd" set line [gets $rfd] ns_log notice "$p: SERVER READ A: $line" puts $wfd "MSG B" flush $wfd ns_log notice "$p: SERVER WROTE B" set line [gets $rfd] ns_log notice "$p: SERVER READ C: $line" puts $wfd "MSG D" flush $wfd ns_log notice "$p: SERVER WROTE D" # Really, we should let the client send the last message # and then close, as closing the fd's here seems to cause an # SSL_ERROR_SYSCALL in the NsOpenSSLSend function. Need # to debug this. ns_log notice "$p: SERVER CLOSING FDs" close $rfd close $wfd close $sock set content [do_content "tested ns_socklisten/ns_sockaccept
"] set rc [do_write [do_header $content] $content] } # do_connect # Connect to a non-SSL listening socket and read/write messages, closing at the end ns_register_proc GET /do_connect do_connect proc do_connect {} { set p "do_connect" set fds [ns_sockopen -nonblock [nsv_get . httpaddr] [nsv_get . listenport]] set rfd [lindex $fds 0] set wfd [lindex $fds 1] ns_log notice "$p: CLIENT RFD=$rfd; WFD=$wfd" puts $wfd "MSG A" flush $wfd ns_log notice "$p: CLIENT WROTE A" set line [gets $rfd] ns_log notice "$p: CLIENT READ B: $line" puts $wfd "MSG C" flush $wfd ns_log notice "$p: CLIENT WROTE C" set line [gets $rfd] ns_log notice "$p: CLIENT READ D: $line" ns_log notice "$p: CLIENT CLOSING FDs" close $rfd close $wfd set content [do_content "ran do_connect
"] set rc [do_write [do_header $content] $content] } ####################################################################################### # SSL socklisten/sockaccept # Listen on an SSL socket and read/write messages to a client ns_register_proc GET /do_ssl_socklisten do_ssl_socklisten proc do_ssl_socklisten {} { set p "do_ssl_socklisten" set sock [ns_openssl_socklisten [nsv_get . httpaddr] [nsv_get . listensslport]] set fds [ns_openssl_sockaccept $sock] set rfd [lindex $fds 0] set wfd [lindex $fds 1] ns_log notice "$p: SERVER: RFD=$rfd; WFD=$wfd" set line [gets $rfd] ns_log notice "$p: SERVER READ A: $line" puts $wfd "MSG B" flush $wfd ns_log notice "$p: SERVER WROTE B" set line [gets $rfd] ns_log notice "$p: SERVER READ C: $line" puts $wfd "MSG D" flush $wfd ns_log notice "$p: SERVER WROTE D" ns_log notice "$p: SERVER CLOSING FDs" close $rfd close $wfd close $sock set content [do_content "tested ns_openssl_socklisten/ns_openssl_sockaccept
"] set rc [do_write [do_header $content] $content] } # do_ssl_connect # Connect to as SSL listening socket and read/write messages, closing at the end ns_register_proc GET /do_ssl_connect do_ssl_connect proc do_ssl_connect {} { set p "do_ssl_connect" set fds [ns_openssl_sockopen -nonblock [nsv_get . httpaddr] [nsv_get . listensslport]] set rfd [lindex $fds 0] set wfd [lindex $fds 1] ns_log notice "$p: CLIENT RFD=$rfd; WFD=$wfd" puts $wfd "MSG A" flush $wfd ns_log notice "$p: CLIENT WROTE A" set line [gets $rfd] ns_log notice "$p: CLIENT READ B: $line" puts $wfd "MSG C" flush $wfd ns_log notice "$p: CLIENT WROTE C" set line [gets $rfd] ns_log notice "$p: CLIENT READ D: $line" ns_log notice "$p: CLIENT CLOSING FDs" close $rfd close $wfd set content [do_content "ran do_ssl_connect
"] set rc [do_write [do_header $content] $content] } ns_log notice "done loading test-socklisten.tcl" aolserver4-nsopenssl-3.0beta26/tests/https.tcl0000755000175000017500000001600707547200625021336 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading test-http.tcl" ##################################################################################### # test ns_httppost ns_register_proc GET /do_httppost httppost proc httppost {} { set qsset [ns_set new qsset] ns_set put $qsset user goodwin ns_set put $qsset pass blahblah set page [ns_httppost "http://[nsv_get . httpaddr]:[nsv_get . port]/test.cgi" "" $qsset] ns_log notice "PAGE = $page" set content [do_content "after: ns_httppost
"] set rc [do_write [do_header $content] $content] } # test ns_httpspost ns_register_proc GET /do_httpspost httpspost proc httpspost {} { set qsset [ns_set new qsset] ns_set put $qsset user goodwin ns_set put $qsset pass blahblah set page [ns_httpspost "https://[nsv_get . httpaddr]:[nsv_get . sslport]/test.cgi" "" $qsset] ns_log notice "PAGE = $page" set content [do_content "after: ns_httpspost
"] set rc [do_write [do_header $content] $content] } # test ns_httppost (multipart) ns_register_proc GET /do_httppost_multi httppost_multi proc httppost_multi {} { set qsset [ns_set new qsset] ns_set put $qsset user goodwin ns_set put $qsset pass blahblah set page [ns_httppost "http://[nsv_get . httpaddr]:[nsv_get . port]/test.cgi" "" $qsset] ns_log notice "PAGE =\n$page\nDONE\n" set content [do_content "after: ns_httppost_multi
"] set rc [do_write [do_header $content] $content] } # test ns_httpspost (multipart) ns_register_proc GET /do_httpspost_multi httpspost_multi proc httpspost_multi {} { set qsset [ns_set new qsset] ns_set put $qsset user goodwin ns_set put $qsset pass blahblah set page [ns_httpspost "https://[nsv_get . httpaddr]:[nsv_get . sslport]/test.cgi" "" $qsset "multipart/form-data"] ns_log notice "PAGE =\n$page\nDONE\n" set content [do_content "after: ns_httpspost_multi
"] set rc [do_write [do_header $content] $content] } # test ns_httppost (no data) ns_register_proc GET /do_httppost_nodata httppost_nodata proc httppost_nodata {} { set page [ns_httppost "http://[nsv_get . httpaddr]:[nsv_get . port]/test.cgi" "" ""] ns_log notice "PAGE = $page" set content [do_content "after: ns_httppost (no data)
"] set rc [do_write [do_header $content] $content] } # test ns_httpspost (no data) ns_register_proc GET /do_httpspost_nodata httpspost_nodata proc httpspost_nodata {} { set page [ns_httpspost "https://[nsv_get . httpaddr]:[nsv_get . sslport]/test.cgi" "" ""] ns_log notice "PAGE = $page" set content [do_content "after: ns_httpspost (no data)
"] set rc [do_write [do_header $content] $content] } # read a POST ns_register_proc POST /do_httppost_read httppost_read proc httppost_read {} { ns_log notice "READING" ns_log notice "R: [ns_conn form]" set setId [ns_conn form] # ns_set merge $setId [ns_conn form] set size [ns_set size $setId] for {set i 0} {$i < $size} {incr i} { set key [ns_set key $setId $i] set value [ns_set value $setId $i] ns_log notice "READING: key=$key value=$value" } set content [do_content "after: ns_httppost
"] set rc [do_write [do_header $content] $content] } # test ns_httpspost #ns_register_proc GET /do_httpspost httpspost #proc httpspost {} { # set page [ns_httpspost "https://[nsv_get . httpaddr]:[nsv_get . sslport]"] # ns_log notice "PAGE=$page" # set content [do_content "after: ns_httpspost
"] # set rc [do_write [do_header $content] $content] #} ##################################################################################### # test ns_geturl ns_register_proc GET /do_geturl geturl proc geturl { conn context } { #ns_return 200 text/html [ns_geturl https://www.wais.com/] ns_return 200 text/html [ns_geturl "http://[nsv_get . httpaddr]:[nsv_get . port]"] } # test ns_openssl_geturl ns_register_proc GET /do_openssl_geturl openssl_geturl proc openssl_geturl { conn context } { #ns_return 200 text/html [ns_geturl https://www.wais.com/] ns_return 200 text/html [ns_openssl_geturl "https://[nsv_get . httpaddr]:[nsv_get . sslport]"] } ##################################################################################### # test ns_httpopen ns_register_proc GET /do_httpopen httpopen proc httpopen {} { set cookie_set [ns_set create cookie_set] ns_set put $cookie_set Cookie "mycookiename=mycookievalue; Path=/; Domain=.eglin.af.mil" # set hlist [ns_httpopen GET "http://[nsv_get . httpaddr]:[nsv_get . port]" $cookie_set] set hlist [ns_httpopen GET "/testurl" $cookie_set] set rid [lindex $hlist 0] set wid [lindex $hlist 1] set setid [lindex $hlist 2] ns_log notice "RID=$rid WID=$wid SETID=$setid" set page [read $rid] close $rid close $wid ns_log notice "PAGE=$page" set content [do_content "after: ns_httpopen
"] set rc [do_write [do_header $content] $content] } # test ns_httpsopen ns_register_proc GET /do_httpsopen httpsopen proc httpsopen {} { set cookie_set [ns_set create cookie_set] ns_set put $cookie_set Cookie "mycookiename=mycookievalue; Path=/; Domain=.eglin.af.mil" set hlist [ns_httpsopen GET "https://[nsv_get . httpaddr]:[nsv_get . sslport]" $cookie_set] set rid [lindex $hlist 0] set wid [lindex $hlist 1] set setid [lindex $hlist 2] ns_log notice "RID=$rid WID=$wid SETID=$setid" set page [read $rid] close $rid close $wid ns_log notice "PAGE=$page" set content [do_content "after: ns_httpsopen
"] set rc [do_write [do_header $content] $content] } ##################################################################################### # test ns_httpget ns_register_proc GET /do_httpget httpget proc httpget {} { set page [ns_httpget "http://[nsv_get . httpaddr]:[nsv_get . port]"] ns_log notice "PAGE=$page" set content [do_content "after: ns_httpget
"] set rc [do_write [do_header $content] $content] } # test ns_httpsget ns_register_proc GET /do_httpsget httpsget proc httpsget {} { set page [ns_httpsget "https://[nsv_get . httpaddr]:[nsv_get . sslport]"] ns_log notice "PAGE=$page" set content [do_content "after: ns_httpsget
"] set rc [do_write [do_header $content] $content] } ##################################################################################### # test ns_httpget with a local url ns_register_proc GET /do_httpget_local httpget_local proc httpget_local {} { set page [ns_httpget "/testurl"] ns_log notice "PAGE=$page" set content [do_content "after: ns_httpget
"] set rc [do_write [do_header $content] $content] } # test ns_httpsget with a local url ns_register_proc GET /do_httpsget_local httpsget_local proc httpsget_local {} { set page [ns_httpsget "/testurl"] ns_log notice "PAGE=$page" set content [do_content "after: ns_httpsget
"] set rc [do_write [do_header $content] $content] } ns_log notice "done loading test-http.tcl" aolserver4-nsopenssl-3.0beta26/tests/ns_openssl_socklistencallback.tcl0000755000175000017500000000676307547200625026302 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading test-socklistencallback.tcl" # socklistencallback # NOTE: how do you stop listening on a listen callback? ns_register_proc GET /do_socklistencallback do_socklistencallback proc do_socklistencallback {} { ns_socklistencallback [nsv_get . httpaddr] [nsv_get . listenport] handle_socklistencallback set content [do_content "tested ns_socklistencallback
"] set rc [do_write [do_header $content] $content] } # SSL socklistecallback # NOTE: how do you stop listening on a listen callback? ns_register_proc GET /do_ssl_socklistencallback do_ssl_socklistencallback proc do_ssl_socklistencallback {} { ns_openssl_socklistencallback [nsv_get . httpaddr] [nsv_get . listensslport] handle_ssl_socklistencallback set content [do_content "tested ns_openssl_socklistencallback
"] set rc [do_write [do_header $content] $content] } proc handle_socklistencallback {rfd wfd} { set p "handle_socklistencallback" ns_log notice "$p: a client has connected to the socket" while {[set line [string trim [gets $rfd]]] != ""} { lappend headers $line } ns_log notice "$p: CLIENT HTTP HEADERS:" ns_log notice "$p: $headers" set content_htm \ " non-ssl test

Great! We were able to do a listen-callback on a non-SSL socket, read the client HTTP header and send back this HTML page. Hit the back button to return to the test page.

Here ar the HTTP client headers you sent me:

$headers " set myheader \ "HTTP/1.0 200 Document follows MIME-Version: 1.0 Content-Type: text/html Content-Length: [string length $content_htm]" puts $wfd \ "$myheader $content_htm" flush $wfd close $rfd close $wfd } proc handle_ssl_socklistencallback {rfd wfd} { set p "handle_ssl_socklistencallback" ns_log notice "$p: a client has connected to the SSL socket" while {[set line [string trim [gets $rfd]]] != ""} { lappend headers $line } ns_log notice "$p: CLIENT HTTP HEADERS:" # always catch the first attempt to print or log something # that was supposed to be read from the connection. If you're # using a test certificate, MSIE aborts the connection and then # makes a new connection after you accept the certificate. In this # case it means that $headers won't exist and trying to ns_log it # causes an error. if { [catch {ns_log notice "$p: $headers"} fid] } { ns_log notice "user aborted connection - MSIE probably being used" # Unlike in the test-socklisten2.tcl test script, we don't have to # go back to listening because the listener is still there. When # MSIE makes its second connection after the user has accepted the # untrusted certificate, this callback proc is run again. return } set content_htm \ " ssl test

Great! We were able to do a listen-callback on an SSL socket, read the client HTTP header and send back this HTML page. Hit the back button to return to the test page.

Here ar the HTTP client headers you sent me:

$headers " set myheader \ "HTTP/1.0 200 Document follows MIME-Version: 1.0 Content-Type: text/html Content-Length: [string length $content_htm]" puts $wfd \ "$myheader $content_htm" flush $wfd close $rfd close $wfd } ns_log notice "done loading test-socklistencallback.tcl" aolserver4-nsopenssl-3.0beta26/tests/0-socktests.tcl0000755000175000017500000001437607547200625022362 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading 0-socktests.tcl" nsv_set . httpaddr #%#ADDRESS#%# nsv_set . sslport #%#HTTPSPORT#%# nsv_set . port #%#HTTPPORT#%# nsv_set . listenport #%#LISTENPORT#%# nsv_set . listensslport #%#LISTENSSLPORT#%# # show test page ns_register_proc GET /testurl testurl proc testurl {conn} { set content [do_content "Choose a test link from below"] set rc [do_write [do_header $content] $content] } # Returns HTML to the client requesting this url ns_register_proc GET /hardcodedurl hardcodedurl proc hardcodedurl {conn} { set content_htm \ " ssltest

If you're reading this, then it worked. " set myheader \ "HTTP/1.0 200 Document follows MIME-Version: 1.0 Content-Type: text/html Content-Length: [string length $content_htm]" ns_write \ "$myheader $content_htm" } # show the url the browser requested ns_register_filter preauth GET /* showurl proc showurl {conn} { set url [ns_conn url] ns_log notice "rp: URL requested: $url" return filter_ok } proc do_write {header content} { ns_write \ "$header $content" return 1; } proc do_header {content} { return \ "HTTP/1.0 200 Document follows MIME-Version: 1.0 Content-Type: text/html Content-Length: [string length $content]" } proc do_content {content} { return \ " ssl-platform tests
$content
Normal Conn Tests SSL Conn Tests
testurl testurl
home ssl home
ns_sockopen ns_openssl_sockopen
ns_socklisten (1) | connect ns_openssl_socklisten (1) | connect
ns_socklisten (2) | connect ns_openssl_socklisten (2) | connect
ns_sockcallback | connect ns_openssl_sockcallback | connect
ns_socklistencallback | connect ns_openssl_socklistencallback | connect
ns_httpopen ns_httpsopen
ns_httpget ns_httpsget
ns_httpget local url ns_httpsget local url
ns_httppost | ns_httppost (no data) | ns_httppost (multipart) ns_httpspost | ns_httpspost (no data) | ns_httpspost (multipart)
Have Browser Form call test.cgi Have Browser Form call test.cgi
ns_geturl ns_openssl_geturl
file upload file upload
bigfile.dat bigfile.dat
Start Automated Tests Start Automated Tests
" } ns_log notice "done loading 0-socktests.tcl" aolserver4-nsopenssl-3.0beta26/tests/ns_httpspost.adp0000755000175000017500000000054007547200625022721 0ustar frankiefrankie ns_httpspost multipart test form target <% # Get form data and assign to variables set r [ns_conn form $conn] set username [ns_set get $r username] set userpassword [ns_set get $r userpassword] ns_puts "USERNAME = $username" ns_puts "USERPASSWORD = $userpassword" %> aolserver4-nsopenssl-3.0beta26/tests/ns_httpspost.htm0000755000175000017500000000072507547200625022752 0ustar frankiefrankie ns_httpspost multipart html form

ns_httpspost multipart html form

How would you like to view this cookbook?

aolserver4-nsopenssl-3.0beta26/tests/test-cert.pem0000644000175000017500000000206607570007716022104 0ustar frankiefrankie-----BEGIN CERTIFICATE----- MIIC7jCCAlegAwIBAgIBATANBgkqhkiG9w0BAQQFADCBqTELMAkGA1UEBhMCWFkx FTATBgNVBAgTDFNuYWtlIERlc2VydDETMBEGA1UEBxMKU25ha2UgVG93bjEXMBUG A1UEChMOU25ha2UgT2lsLCBMdGQxHjAcBgNVBAsTFUNlcnRpZmljYXRlIEF1dGhv cml0eTEVMBMGA1UEAxMMU25ha2UgT2lsIENBMR4wHAYJKoZIhvcNAQkBFg9jYUBz bmFrZW9pbC5kb20wHhcNOTgxMDIxMDg1ODM2WhcNOTkxMDIxMDg1ODM2WjCBpzEL MAkGA1UEBhMCWFkxFTATBgNVBAgTDFNuYWtlIERlc2VydDETMBEGA1UEBxMKU25h a2UgVG93bjEXMBUGA1UEChMOU25ha2UgT2lsLCBMdGQxFzAVBgNVBAsTDldlYnNl cnZlciBUZWFtMRkwFwYDVQQDExB3d3cuc25ha2VvaWwuZG9tMR8wHQYJKoZIhvcN AQkBFhB3d3dAc25ha2VvaWwuZG9tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB gQDH9Ge/s2zcH+da+rPTx/DPRp3xGjHZ4GG6pCmvADIEtBtKBFAcZ64n+Dy7Np8b vKR+yy5DGQiijsH1D/j8HlGE+q4TZ8OFk7BNBFazHxFbYI4OKMiCxdKzdif1yfaa lWoANFlAzlSdbxeGVHoT0K+gT5w3UxwZKv2DLbCTzLZyPwIDAQABoyYwJDAPBgNV HRMECDAGAQH/AgEAMBEGCWCGSAGG+EIBAQQEAwIAQDANBgkqhkiG9w0BAQQFAAOB gQAZUIHAL4D09oE6Lv2k56Gp38OBDuILvwLg1v1KL8mQR+KFjghCrtpqaztZqcDt 2q2QoyulCgSzHbEGmi0EsdkPfg6mp0penssIFePYNI+/8u9HT4LuKMJX15hxBam7 dUHzICxBVC1lnHyYGjDuAMhe396lYAn8bCld1/L4NMGBCQ== -----END CERTIFICATE----- aolserver4-nsopenssl-3.0beta26/tests/ns_openssl_socknread.tcl0000755000175000017500000000104707547200625024406 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading test-socknread.tcl" #proc do_socknread {timeout sock} { # set p "do_socknread" # set nread [ns_socknread $sock] # if !$nread { # set sel [ns_sockselect -timeout $timeout $sock {} {}] # if [string match "" [lindex $sel 0]] { # return -code error "ns_sockreadwait: Timeout waiting for remote" # } # set nread [ns_socknread $sock] # } # return $nread #} aolserver4-nsopenssl-3.0beta26/tests/ns_openssl_sockopen.tcl0000755000175000017500000000422307547200625024255 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading test-sockopen.tcl" # non-SSL sockopen # Connects to the server and reads the page specified by the url ns_register_proc GET /do_sockopen do_sockopen proc do_sockopen {} { set p "do_sockopen" set bytes 200 set fds [ns_sockopen -nonblock [nsv_get . httpaddr] [nsv_get . port]] set rfd [lindex $fds 0] set wfd [lindex $fds 1] ns_log notice "$p: RFD=$rfd; WFD=$wfd" ns_log notice "$p: sending to server: GET /hardcodedurl HTTP/1.0" puts $wfd "GET /hardcodedurl HTTP/1.0\r\n\r\n" flush $wfd while {[set line [string trim [gets $rfd]]] != ""} { lappend headers $line } set page [read $rfd $bytes] close $rfd close $wfd ns_log notice "$p: first $bytes bytes of page:" ns_log notice "$page" set content [do_content "tested ns_sockopen
"] set rc [do_write [do_header $content] $content] } # SSL sockopen # Connects to the SSL server and reads the page specified by the url ns_register_proc GET /do_ssl_sockopen do_ssl_sockopen proc do_ssl_sockopen {} { # for {set i 0} {$i < 5000} {incr i} { set p "do_ssl_sockopen" set bytes 200 set fds [ns_openssl_sockopen -nonblock [nsv_get . httpaddr] [nsv_get . sslport]] set rfd [lindex $fds 0] set wfd [lindex $fds 1] set validcert [lindex $fds 2] ns_log notice "$p: rfd=$rfd; wfd=$wfd; validcert=$validcert" if {$validcert} { ns_log notice "$p: server cert is valid" } else { ns_log notice "$p: server cert is invalid" } ns_log notice "$p: sending to server: GET /hardcodedurl HTTP/1.0" puts $wfd "GET /hardcodedurl HTTP/1.0\r\n\r\n" flush $wfd while {[set line [string trim [gets $rfd]]] != ""} { lappend headers $line } set page [read $rfd $bytes] close $rfd close $wfd ns_log notice "$p: first $bytes bytes of page:" ns_log notice "$page" set content [do_content "tested ns_ssl_sockopen
"] set rc [do_write [do_header $content] $content] # } } ns_log notice "done loading test-sockopen.tcl" aolserver4-nsopenssl-3.0beta26/tests/index.adp0000644000175000017500000000557507550370543021273 0ustar frankiefrankie SSL Test Page for the nsopenssl module

SSL Test Page for the nsopenssl module

(Copy this ADP page to your pageroot and run.)
ns_openssl info <%=[ns_openssl info]%>
ns_openssl clientcert exists <% if {[ns_openssl clientcert exists]} { ns_puts "Client cert exists" } else { ns_puts "Client cert does NOT exist" } %>
ns_openssl module name <%=[ns_openssl module name]%>
ns_openssl module port <%=[ns_openssl module port]%>
ns_openssl protocol <%=[ns_openssl protocol]%>
ns_openssl cipher name <%=[ns_openssl cipher name]%>
ns_openssl cipher strength <%=[ns_openssl cipher strength]%>
ns_openssl clientcert valid <% if {[ns_openssl clientcert valid]} { ns_puts "Client cert is valid" } else { ns_puts "Client cert is NOT valid" } %>
ns_openssl clientcert version <%=[ns_openssl clientcert version]%>
ns_openssl clientcert serial <%=[ns_openssl clientcert serial]%>
ns_openssl clientcert subject <% set var [ns_openssl clientcert subject] ns_puts "$var" %>
ns_openssl clientcert issuer <% set var [ns_openssl clientcert issuer] ns_puts "$var" %>
ns_openssl clientcert notbefore <%=[ns_openssl clientcert notbefore]%>
ns_openssl clientcert notafter <%=[ns_openssl clientcert notafter]%>
ns_openssl clientcert signature_algorithm <%=[ns_openssl clientcert signature_algorithm]%>
ns_openssl clientcert key_algorithm <%=[ns_openssl clientcert key_algorithm]%>
ns_openssl clientcert pem <%=[ns_openssl clientcert pem]%>

Client certificate support in nsopenssl brought to you by Scott S. Goodwin, http://scottg.net. Rob Mayoff refactored the code and added some extra client cert support.

Copyright © 2000 by Scott S. Goodwin

Send feedback, bugs and comments to me. Enjoy!!! aolserver4-nsopenssl-3.0beta26/tests/nsd.tcl0000644000175000017500000001334207550370543020755 0ustar frankiefrankie# $Header: /cvsroot/aolserver/nsopenssl/tests/nsd.tcl,v 1.1 2002/10/07 20:41:07 scottg Exp $ # # Define ports to use for testing # set httpsport_server 9050 set httpsport_sockserver 9051 set httpsport_sockclient 9052 # # Define modules to load # array set loadmodules [list nsopenssl nsopenssl$ext] # # nsopenssl (HTTPS) # ns_section "ns/server/${servername}/module/nsopenssl" ns_param ServerHostname $hostname ns_param ServerAddress $address ns_param ServerPort $httpsport_server # This should be last resort??? #ns_param ServerLocation "https://192.168.0.2:$https2port" ns_param ServerTrace true ns_param ServerCertFile server1-cert.pem ns_param ServerKeyFile server1-key-unsecure.pem ns_param ServerPeerVerify false ns_param ServerPeerVerifyDepth 10 ns_param ServerCADir server.cadir ns_param ServerCAFile ca1-cert.pem ns_param ServerProtocols "SSLv2, SSLv3, TLSv1" ns_param ServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param ServerSessionCache false ns_param ServerSessionCacheId 1 ns_param ServerSessionCacheSize 128 ns_param ServerSessionCacheTimeout 300 ns_param SockServerTrace true ns_param SockServerCertFile server1-cert.pem ns_param SockServerKeyFile server1-key-unsecure.pem ns_param SockServerPeerVerify true ns_param SockServerPeerVerifyDepth 10 ns_param SockServerCADir server.cadir ns_param SockServerCAFile ca1-cert.pem ns_param SockServerProtocols "SSLv2, SSLv3, TLSv1" ns_param SockServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockServerSessionCache false ns_param SockServerSessionCacheId 2 ns_param SockServerSessionCacheSize 128 ns_param SockServerSessionCacheTimeout 300 ns_param SockClientTrace true ns_param SockClientCertFile client1-cert.pem ns_param SockClientKeyFile client1-key-unsecure.pem ns_param SockClientPeerVerify false ns_param SockClientPeerVerifyDepth 10 ns_param SockClientCADir server.cadir ns_param SockClientCAFile ca1-cert.pem ns_param SockClientProtocols "SSLv2, SSLv3, TLSv1" ns_param SockClientCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockClientSessionCache false ns_param SockClientSessionCacheId 3 ns_param SockClientSessionCacheSize 128 ns_param SockClientSessionCacheTimeout 300 #ns_param RandomFile /dev/urandom #ns_param SeedBytes 1024 #ns_param ServerBufferSize 16384 #ns_param ServerSockTimeout 30 #ns_param ClientBufferSize 16384 #ns_param ClientSockTimeout 30 # # nsopenssl (HTTPS) (Loading a second copy of the module) # ns_section "ns/server/${servername}/module/nsopenssl2" ns_param ServerHostname $hostname ns_param ServerAddress $address ns_param ServerPort $httpsport_sockclient # This should be last resort??? #ns_param ServerLocation "https://192.168.0.2:$https2port" ns_param ServerTrace true ns_param ServerCertFile server1-cert.pem ns_param ServerKeyFile server1-key-unsecure.pem ns_param ServerPeerVerify false ns_param ServerPeerVerifyDepth 10 ns_param ServerCADir server.cadir ns_param ServerCAFile ca1-cert.pem ns_param ServerProtocols "SSLv2, SSLv3, TLSv1" ns_param ServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param ServerSessionCache false ns_param ServerSessionCacheId 1 ns_param ServerSessionCacheSize 128 ns_param ServerSessionCacheTimeout 300 ns_param SockServerTrace true ns_param SockServerCertFile server1-cert.pem ns_param SockServerKeyFile server1-key-unsecure.pem ns_param SockServerPeerVerify true ns_param SockServerPeerVerifyDepth 10 ns_param SockServerCADir server.cadir ns_param SockServerCAFile ca1-cert.pem ns_param SockServerProtocols "SSLv2, SSLv3, TLSv1" ns_param SockServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockServerSessionCache false ns_param SockServerSessionCacheId 2 ns_param SockServerSessionCacheSize 128 ns_param SockServerSessionCacheTimeout 300 ns_param SockClientTrace true ns_param SockClientCertFile client1-cert.pem ns_param SockClientKeyFile client1-key-unsecure.pem ns_param SockClientPeerVerify false ns_param SockClientPeerVerifyDepth 10 ns_param SockClientCADir server.cadir ns_param SockClientCAFile ca1-cert.pem ns_param SockClientProtocols "SSLv2, SSLv3, TLSv1" ns_param SockClientCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockClientSessionCache false ns_param SockClientSessionCacheId 3 ns_param SockClientSessionCacheSize 128 ns_param SockClientSessionCacheTimeout 300 #ns_param RandomFile /dev/urandom #ns_param SeedBytes 1024 #ns_param ServerBufferSize 16384 #ns_param ServerSockTimeout 30 #ns_param ClientBufferSize 16384 #ns_param ClientSockTimeout 30 aolserver4-nsopenssl-3.0beta26/tests/test-key.pem0000644000175000017500000000156707570007716021744 0ustar frankiefrankie-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDH9Ge/s2zcH+da+rPTx/DPRp3xGjHZ4GG6pCmvADIEtBtKBFAc Z64n+Dy7Np8bvKR+yy5DGQiijsH1D/j8HlGE+q4TZ8OFk7BNBFazHxFbYI4OKMiC xdKzdif1yfaalWoANFlAzlSdbxeGVHoT0K+gT5w3UxwZKv2DLbCTzLZyPwIDAQAB AoGBAJe+252YRV0ouSNKb36hlhc3uFVpU7vNlV1XiGrO8yauqKcbbtCfvoOG2FAb u9QI7a1r9MfnD66OOJQKcyKiv6mcR0FpDI/vwmcPiBbxaJJc+59/Aq2bOAoAgwfg Xp/F5hNbV1G90uifxgRYyVeJ3mfh5RLobeH0Y3sfn2UUuczBAkEA5N/eeJ2vVv+9 pkZViZatQ8OIO0h20DdWFNQH2Wqrr2kDZMzndVGganKJG2m0B3rRbe8AhfV4QM3P u+WHxfgYvQJBAN+nGaHJLr8Lm/+7GFDWxkvTeg4Z/D2OU2mUH3/vIPzzZHz5Rz6N RSWs/17LW/Cfp445xOBV41KAflnQn3YiXKsCQCKXVzdhlauPcWlc/faSiFNSjRdw p0xm4KK7PQh2H8aeF343nc/Ua2tLLj+vZR8wR9DWq3yTkf4xXPEoCrI4Af0CQQCI qIUb+18p80EOANd5xdKC0KSa6zvarRXAxHZ+6LICfgJf7r2ethmTTOL5nE8Ad72d RSVMYo61gbgyzywwtgxjAkBqtLZTBm1m9XGqIPWf5eSOegEz55Nmb2KJVaJLJzLN 7S5aWSFoGFkrQ84cSNZmEHInt+zi3kxD1f9gvb0p8oIS -----END RSA PRIVATE KEY----- aolserver4-nsopenssl-3.0beta26/tests/ns_openssl_sockcallback.tcl0000755000175000017500000000547707547200625025064 0ustar frankiefrankie# nsopenssl socket testing setup # Copyright (c) 2001 by Scott S. Goodwin # See http://scottg.net for more information ns_log notice "loading test-sockcallback.tcl" # sockcallback ns_register_proc GET /do_sockcallback do_sockcallback proc do_sockcallback {} { set sock [ns_socklisten [nsv_get . httpaddr] [nsv_get . listenport]] ns_sockcallback $sock handle_sockcallback r set content [do_content "tested ns_sockcallback
"] set rc [do_write [do_header $content] $content] } # SSL sockcallback ns_register_proc GET /do_ssl_sockcallback do_ssl_sockcallback proc do_ssl_sockcallback {} { set sock [ns_openssl_socklisten [nsv_get . httpaddr] [nsv_get . listensslport]] ns_openssl_sockcallback $sock handle_ssl_sockcallback r set content [do_content "tested ns_openssl_sockcallback
"] set rc [do_write [do_header $content] $content] } proc handle_sockcallback {sock when} { set p "handle_sockcallback" ns_log notice "$p: a client has connected to the socket" set fds [ns_sockaccept $sock] set rfd [lindex $fds 0] set wfd [lindex $fds 1] ns_log notice "$p: RFD=$rfd; WFD=$wfd" while {[set line [string trim [gets $rfd]]] != ""} { lappend headers $line } ns_log notice "$p: CLIENT HTTP HEADERS:" ns_log notice "$p: $headers" set content_htm \ " non-ssl test

Great! We were able to do a non-SSL sockcallback, read the client HTTP header and send back this HTML page. Hit the back button to return to the test page.

Here ar the HTTP client headers you sent me:

$headers " set myheader \ "HTTP/1.0 200 Document follows MIME-Version: 1.0 Content-Type: text/html Content-Length: [string length $content_htm]" puts $wfd \ "$myheader $content_htm" flush $wfd close $rfd close $wfd } proc handle_ssl_sockcallback {sock when} { set p "handle_ssl_sockcallback" ns_log notice "$p: a client has connected to the SSL socket" set fds [ns_openssl_sockaccept $sock] set rfd [lindex $fds 0] set wfd [lindex $fds 1] while {[set line [string trim [gets $rfd]]] != ""} { lappend headers $line } ns_log notice "$p: CLIENT HTTP HEADERS:" ns_log notice "$p: $headers" set content_htm \ " ssl test

Great! We were able to do an SSL sockcallback, read the client HTTP header and send back this HTML page. Hit the back button to return to the test page.

Here ar the HTTP client headers you sent me:

$headers " set myheader \ "HTTP/1.0 200 Document follows MIME-Version: 1.0 Content-Type: text/html Content-Length: [string length $content_htm]" puts $wfd \ "$myheader $content_htm" flush $wfd close $rfd close $wfd } ns_log notice "done loading test-sockcallback.tcl" aolserver4-nsopenssl-3.0beta26/tests/nsopenssl-tests.adp0000644000175000017500000000460607570007716023343 0ustar frankiefrankie SSL Test Page for the nsopenssl module

SSL Test Page for the nsopenssl module

(Copy this ADP page to your pageroot and run.)
ns_openssl info <%=[ns_openssl info]%>
ns_openssl clientcert exists <% if {[ns_openssl clientcert exists]} { ns_puts "Client cert exists" } else { ns_puts "Client cert does NOT exist" } %>
ns_openssl protocol <%=[ns_openssl protocol]%>
ns_openssl cipher name <%=[ns_openssl cipher name]%>
ns_openssl cipher strength <%=[ns_openssl cipher strength]%>
ns_openssl clientcert valid <% if {[ns_openssl clientcert valid]} { ns_puts "Client cert is valid" } else { ns_puts "Client cert is NOT valid" } %>
ns_openssl clientcert version <%=[ns_openssl clientcert version]%>
ns_openssl clientcert serial <%=[ns_openssl clientcert serial]%>
ns_openssl clientcert subject <% set var [ns_openssl clientcert subject] ns_puts "$var" %>
ns_openssl clientcert issuer <% set var [ns_openssl clientcert issuer] ns_puts "$var" %>
ns_openssl clientcert notbefore <%=[ns_openssl clientcert notbefore]%>
ns_openssl clientcert notafter <%=[ns_openssl clientcert notafter]%>
ns_openssl clientcert signature_algorithm <%=[ns_openssl clientcert signature_algorithm]%>
ns_openssl clientcert key_algorithm <%=[ns_openssl clientcert key_algorithm]%>
ns_openssl clientcert pem <%=[ns_openssl clientcert pem]%>

Copyright © 2000 by Scott S. Goodwin

Send feedback, bugs and comments to me. aolserver4-nsopenssl-3.0beta26/ssl.c0000644000175000017500000005561710537737033017303 0ustar frankiefrankie/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. * * Copyright (C) 2000-2003 Scott S. Goodwin * * Module originally written by Stefan Arentz. Early contributions made by * Freddie Mendoze and Rob Mayoff. */ /* * ssl.c -- * * Implements functions dealing with SSL_CTXs and SSL instance structures. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsopenssl/ssl.c,v 1.70 2006/03/28 17:56:30 apiskors Exp $, compiled: " __DATE__ " " __TIME__; #ifdef _WIN32 #define SHUT_WR SD_SEND #endif #include "nsopenssl.h" #ifdef _WIN32 /* Appear not to need here. */ #else #include #endif /* XXX put into defaults.h */ #define BUFSIZE 2048 typedef struct Stream { NsOpenSSLConn *sslconn; int error; int cnt; char *ptr; char buf[BUFSIZE + 1]; } Stream; static int GetLine(Stream *sPtr, Ns_DString *dsPtr); static int FillBuf(Stream *sPtr); /* *---------------------------------------------------------------------- * * NsOpenSSLConnCreate -- * * Create an SSL connection. The socket has already been accept()ed and is * ready for reading/writing. * * Results: * Pointer to sslconn, which might be NULL * * Side effects: * If the SSL connection was open then it will be forced to close first. * *---------------------------------------------------------------------- */ NsOpenSSLConn * NsOpenSSLConnCreate(SOCKET socket, NsOpenSSLContext *sslcontext) { NsOpenSSLConn *sslconn = NULL; int status = NS_FALSE; int n = 1; if (Ns_InfoShutdownPending()) { Ns_Log(Notice, "%s (%s): connection refused due to server shutdown pending", MODULE, sslcontext->server); } sslconn = ns_calloc(1, sizeof(NsOpenSSLConn)); if (sslconn == NULL) { Ns_Log(Error, "%s (%s): failed to create SSL connection structure", MODULE, sslcontext->server); return NULL; } //Ns_Log(Debug, "NsOpenSSLConnCreate(%p)", sslconn); /* * Default is a core-driven connection. Connections created by nsopenssl's * Tcl API are responsible for setting this value to TCLAPI. */ sslconn->type = CORE; /* * Set connection structure initial values. */ sslconn->server = sslcontext->server; sslconn->sslcontext = sslcontext; sslconn->socket = socket; sslconn->sendwait = DEFAULT_SENDWAIT; sslconn->recvwait = DEFAULT_RECVWAIT; sslconn->ssl = NULL; sslconn->sslctx = NULL; sslconn->peerport = -1; /* * It's the caller's responsibility to increment the reference count; the * same connection may be referenced multiple times simultaneously as when * we wrap the Tcl channels around a connection socket. */ sslconn->refcnt = 0; /* It's GMT, but we use this to do time diffs */ Tcl_GetTime(&sslconn->timer); /* * Instantiate the SSL structure from the sslcontext. */ sslconn->ssl = SSL_new(sslcontext->sslctx); if (sslconn->ssl == NULL) { Ns_Log(Error, "%s (%s): failed to create new SSL structure", MODULE, sslcontext->server); NsOpenSSLConnDestroy(sslconn); return NULL; } SSL_clear(sslconn->ssl); /* * Associate the socket with the SSL instance. */ SSL_set_fd(sslconn->ssl, socket); /* * Associate the connection structure with the SSL instance. */ SSL_set_app_data(sslconn->ssl, sslconn); /* * Define the SSL instance's role. */ if (sslcontext->role == SERVER_ROLE) { SSL_set_accept_state(sslconn->ssl); } else { SSL_set_connect_state(sslconn->ssl); } #if 0 /* * XXX don't need to explicitly run the handshake. Run the SSL handshake. */ if (NsOpenSSLConnHandshake(sslconn) != NS_OK) { NsOpenSSLConnDestroy(sslconn); } #endif #if 0 /* XXX add as config option before turning on */ /* * Turn off the Nagle algorithm. The Nagle algorithm waits for the ACK to * come back from the first packet sent before sending any further packets, * in hopes that the TCP output buffer has more data. It is intended to * increase the efficiency of network bandwidth usage by preventing lots of * small packets from being tranmitted the second the hit the TCP buffer, * thus clogging the network. For SSL the Nagle algorithm doesn't make much * sense as SSL will just about always have larger chunks of data to send; * in our case Nagle just slows down SSL handshake and communications for * no real benefit in network bandwidth usage. */ if (setsockopt(sslconn->socket, IPPROTO_TCP, TCP_NODELAY, (char *) &n, sizeof(n)) == -1) { Ns_Log(Warning, "%s (%s): unable to turn off Nagle algorithm", MODULE, sslconn->server); } #endif return sslconn; } /* *---------------------------------------------------------------------- * * NsOpenSSLConnDestroy -- * * Destroy an SSL connection. * * Results: * None. * * Side effects: * If the SSL connection was open then it will be forced to close first. * If the connection is still being referenced, no action is taken. * *---------------------------------------------------------------------- */ extern void NsOpenSSLConnDestroy(NsOpenSSLConn *sslconn) { int i = 0; int rc = 0; //Ns_Log(Debug, "NsOpenSSLConnDestroy(%p)", sslconn); if (sslconn == NULL) { return; } /* * Don't destroy the connection if it's still referenced somewhere. */ sslconn->refcnt--; if (sslconn->refcnt > 0) { return; } /* * Shutdown the Tcl channel wrapped around the socket, if there is one. */ // if (sslconn->chan != NULL) { // Ns_Log(Debug, "*** CHAN: DESTROYING: %s", Tcl_GetChannelName(sslconn->chan)); // Tcl_UnregisterChannel(NULL, sslconn->chan); // } /* * Shutdown the SSL connection and free the SSL structure. */ if (sslconn->ssl != NULL) { // SSL_set_shutdown(sslconn->ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); for (i = rc = 0; rc == 0 && i < 4; i++) { rc = SSL_shutdown(sslconn->ssl); } SSL_free(sslconn->ssl); } /* * Shutdown and close the socket itself, but only if it's an nsopenssl Tcl * API-created socket. If the core server is driving the socket, we leave * it alone and let the core driver do what it wants with it. */ //if (sslconn->type == TCLAPI) { // if (sslconn->socket != INVALID_SOCKET) { // shutdown(sslconn->socket, SHUT_RDWR); // ns_sockclose(sslconn->socket); // sslconn->socket = INVALID_SOCKET; // } // } ns_free(sslconn); sslconn = NULL; return; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLSockConnect -- * * Open an SSL connection to the given host and port. * * Arguments: * name: The name of the SSL context to use * host: The remote hosts name or IP address * port: The port to connect to * async: If 0, leave socket in synchronous mode, otherwise async * timeout: How long to wait for response from remote host * * Results: * A pointer to a new NsOpenSSLConn structure. * * Side effects: * Runs the SSL handshake. * *---------------------------------------------------------------------- */ extern NsOpenSSLConn * Ns_OpenSSLSockConnect(char *server, char *host, int port, int async, int timeout, NsOpenSSLContext *sslcontext) { NsOpenSSLConn *sslconn = NULL; SOCKET socket = INVALID_SOCKET; /* * Create the socket connection. */ if (timeout < 0) { socket = Ns_SockConnect(host, port); } else { socket = Ns_SockTimedConnect(host, port, timeout); } if (socket == INVALID_SOCKET) { return NULL; } /* * Wrap SSL around the socket. */ sslconn = NsOpenSSLConnCreate(socket, sslcontext); sslconn->type = TCLAPI; if (sslconn != NULL) { sslconn->refcnt++; if (async) { Ns_SockSetNonBlocking(sslconn->socket); } else { Ns_SockSetBlocking(sslconn->socket); } } return sslconn; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLSockAccept -- * * Accept a TCP socket, setting close on exec. * * Arguments: * name: the name of the SSL context to use for this connection * sock: the socket id that we're accept'ing on * * Results: * A socket or INVALID_SOCKET on error. * * Side effects: * The socket is always placed in non-blocking mode. * *---------------------------------------------------------------------- */ extern NsOpenSSLConn * Ns_OpenSSLSockAccept(SOCKET sock, NsOpenSSLContext *sslcontext) { NsOpenSSLConn *sslconn = NULL; if (sock == INVALID_SOCKET) { Ns_Log(Error, "%s (%s): attempted accept on invalid socket", MODULE, sslcontext->server); return NULL; } /* * Wrap SSL around socket. */ sslconn = NsOpenSSLConnCreate(sock, sslcontext); sslconn->type = TCLAPI; if (sslconn != NULL) { sslconn->refcnt++; Ns_SockSetNonBlocking(sslconn->socket); } return sslconn; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLSockListen -- * * Listen for connections with default backlog. Just a wrapper * around Ns_SockListen at the moment. * * Arguments: * name: the name of the SSL context to use for this connection * addr: the IP address to bind to * port: the port to listen on * * Results: * A socket. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern SOCKET Ns_OpenSSLSockListen(char *addr, int port) { return Ns_SockListen(addr, port); } #if 0 /* *---------------------------------------------------------------------- * * Ns_OpenSSLIsPeerCertValid -- * * Determine if the peer's certificate is valid. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLIsPeerCertValid(NsOpenSSLConn *sslconn) { if (SSL_get_verify_result(sslconn->ssl) == X509_V_OK) { return NS_TRUE; } else { return NS_FALSE; } /* Possible (long) values from SSL_get_verify_result: X509_V_OK X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT X509_V_ERR_UNABLE_TO_GET_CRL X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY X509_V_ERR_CERT_SIGNATURE_FAILURE X509_V_ERR_CRL_SIGNATURE_FAILURE X509_V_ERR_CERT_NOT_YET_VALID X509_V_ERR_CERT_HAS_EXPIRED X509_V_ERR_CRL_NOT_YET_VALID X509_V_ERR_CRL_HAS_EXPIRED X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD X509_V_ERR_OUT_OF_MEM X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE X509_V_ERR_CERT_CHAIN_TOO_LONG X509_V_ERR_CERT_REVOKED X509_V_ERR_APPLICATION_VERIFICATION */ } #endif /* *---------------------------------------------------------------------- * * Ns_OpenSSLFetchUrl -- * * Open up an HTTPS connection to an arbitrary URL. * * Results: * NS_OK or NS_ERROR. * * Side effects: * Page contents will be appended to the passed-in dstring. Headers * returned to us will be put into the passed-in Ns_Set. The set name * will be changed to a copy of the HTTP status line. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLFetchUrl(char *server, Ns_DString *dsPtr, char *url, Ns_Set *headers, NsOpenSSLContext *sslcontext) { NsOpenSSLConn *sslconn = NULL; Ns_Request *request = NULL; Ns_DString ds; Stream stream; char *p = NULL; int status = NS_ERROR; int tosend = 0; int n = 0; Ns_DStringInit(&ds); /* * Parse the URL and open a connection. */ Ns_DStringVarAppend(&ds, "GET ", url, " HTTP/1.0", NULL); request = Ns_ParseRequest(ds.string); if ( request == NULL || request->protocol == NULL || !STREQ(request->protocol, "https") || request->host == NULL ) { Ns_Log(Notice, "urlopen: invalid url '%s'", url); goto done; } /* * Open an SSL connection. */ if (request->port == 0) { request->port = 443; } sslconn = Ns_OpenSSLSockConnect(server, request->host, request->port, 0, 300, sslcontext); if (sslconn == NULL) { Ns_Log(Error, "%s (%s): Ns_OpenSSLFetchURL: failed to connect to '%s'", MODULE, server, url); goto done; } /* * Send HTTP GET request. */ Ns_DStringTrunc(&ds, 0); Ns_DStringVarAppend(&ds, "GET ", request->url, NULL); if (request->query != NULL) { Ns_DStringVarAppend(&ds, "?", request->query, NULL); } Ns_DStringAppend(&ds, " HTTP/1.0\r\nAccept: */*\r\n\r\n"); p = ds.string; tosend = ds.length; while (tosend > 0) { //n = NsOpenSSLConnSend(sslconn->ssl, p, tosend); n = NsOpenSSLConnOp(sslconn->ssl, p, tosend, NSOPENSSL_SEND); if (n <= 0) { Ns_Log(Error, "%s (%s): failed to send data to '%s'", MODULE, server, url); goto done; } tosend -= n; p += n; } /* * Buffer the socket and read the response line and then consume the * headers, parsing them into any given header set. */ stream.cnt = 0; stream.error = 0; stream.ptr = stream.buf; stream.sslconn = (NsOpenSSLConn *) sslconn; if (!GetLine (&stream, &ds)) { goto done; } if (headers != NULL && strncmp(ds.string, "HTTP", 4) == 0) { if (headers->name != NULL) { ns_free (headers->name); } headers->name = Ns_DStringExport(&ds); } do { if (!GetLine (&stream, &ds)) { goto done; } if (ds.length > 0 && headers != NULL && Ns_ParseHeader(headers, ds.string, Preserve) != NS_OK) { goto done; } } while (ds.length > 0); /* * Without any check on limit or total size, foolishly read * the remaining content into the dstring. */ do { Ns_DStringNAppend(dsPtr, stream.ptr, stream.cnt); } while (FillBuf(&stream)); if (!stream.error) { status = NS_OK; } done: if (request != NULL) { Ns_FreeRequest(request); } if (sslconn != NULL) { NsOpenSSLConnDestroy(sslconn); } Ns_DStringFree(&ds); return status; } /* *---------------------------------------------------------------------- * * NsOpenSSLConnOp -- * * Send/Recv data through an SSL connection. * * Results: * The number of bytes send or a negative number in case of an error. * * Side effects: * SSL handshake will be performed if it hasn't been. * *---------------------------------------------------------------------- */ extern int NsOpenSSLConnOp(SSL *ssl, void *buffer, int bytes, int type) { NsOpenSSLConn *sslconn = SSL_get_app_data(ssl); SOCKET socket = SSL_get_fd(ssl); int n, err; char *dir; /* * Perhaps we should enable SSL_MODE_AUTO_RETRY to avoid having to * handle retries in the case of SSL re-negotiation. */ retry: switch (type) { case NSOPENSSL_RECV: dir = "read"; n = SSL_read(ssl, (char *) buffer, bytes); break; case NSOPENSSL_SEND: /* * Unless the SSL_MODE_ENABLE_PARTIAL_WRITE option is * set via SSL_CTX_set_mode(), then SSL_write() returns * successful only when the entire buffer is written. */ dir = "write"; n = SSL_write(ssl, (char *) buffer, bytes); break; default: Ns_Log(Error, "%s (%s): Invalid command '%d'", MODULE, sslconn->server, type); return -1; } switch (SSL_get_error(ssl, n)) { case SSL_ERROR_NONE: /* Success: n > 0 */ break; case SSL_ERROR_ZERO_RETURN: /* Transport close alert received. */ break; case SSL_ERROR_WANT_WRITE: if (Ns_SockWait(sslconn->socket, NS_SOCK_WRITE, sslconn->sendwait) != NS_OK) { Ns_Log(Error, "%s (%s): SSL %s error: timed out in SSL_ERROR_WANT_WRITE", MODULE, sslconn->server, dir); n = -1; break; } goto retry; case SSL_ERROR_WANT_READ: if (Ns_SockWait(sslconn->socket, NS_SOCK_READ, sslconn->recvwait) != NS_OK) { Ns_Log(Error, "%s (%s): SSL %s error: timed out in SSL_ERROR_WANT_READ", MODULE, sslconn->server, dir); n = -1; break; } goto retry; case SSL_ERROR_WANT_X509_LOOKUP: Ns_Log(Warning, "%s (%s): SSL %s wants X509 Lookup; unsupported?", MODULE, sslconn->server, dir); n = -1; break; case SSL_ERROR_SYSCALL: err = ERR_get_error(); if (err) { Ns_Log(Debug, "%s (%s): SSL %s interrupted: %s", MODULE, sslconn->server, dir, ERR_reason_error_string(err)); } else if (n == 0) { Ns_Log(Debug, "%s (%s): SSL %s interrupted: unexpected eof", MODULE, sslconn->server, dir); } else { Ns_Log(Debug, "%s (%s): SSL %s interrupted: %s", MODULE, sslconn->server, dir, ns_sockstrerror(ns_sockerrno)); } n = -1; break; case SSL_ERROR_SSL: Ns_Log(Error, "%s (%s): SSL %s error: %s", MODULE, sslconn->server, dir, ERR_reason_error_string(ERR_get_error())); n = -1; break; default: Ns_Log(Error, "%s (%s): Unknown SSL %s error code in ssl.c (%d)", MODULE, sslconn->server, dir, n); n = -1; break; } /* * On error, we mark the SSL conn as received shutdown so that later * on, when we try to flush, we can detect that the conn has been * shut down. */ if (n < 0) { SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); } return n; } /* *---------------------------------------------------------------------- * * NsOpenSSLConnFlush -- * * Flush an SSL connection. * * Results: * Always NS_OK. * * Side effects: * None. * *---------------------------------------------------------------------- */ int NsOpenSSLConnFlush(NsOpenSSLConn *sslconn) { SSL *ssl = sslconn->ssl; BIO *bio; if (ssl != NULL) { if (SSL_get_shutdown(ssl) != 0) { return NS_ERROR; } bio = SSL_get_wbio(ssl); if (bio == NULL) { return NS_ERROR; } if (BIO_flush(bio) < 1) { Ns_Log(Error, "%s (%s): BIO returned error on flushing buffer", MODULE, sslconn->server); return NS_ERROR; } } return NS_OK; } /* *---------------------------------------------------------------------- * * FillBuf -- * * Fill the socket stream buffer. * * Results: * NS_TRUE if fill ok, NS_FALSE otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int FillBuf(Stream *sPtr) { //int n = NsOpenSSLConnRecv(sPtr->sslconn->ssl, sPtr->buf, BUFSIZE); int n = NsOpenSSLConnOp(sPtr->sslconn->ssl, sPtr->buf, BUFSIZE, NSOPENSSL_RECV); int status = NS_TRUE; if (n > 0) { sPtr->buf[n] = '\0'; sPtr->ptr = sPtr->buf; sPtr->cnt = n; } else if (n == 0) { status = NS_FALSE; } else { Ns_Log(Error, "FillBuf: failed to fill socket stream buffer"); sPtr->error = 1; status = NS_FALSE; } return status; } /* *---------------------------------------------------------------------- * * GetLine -- * * Copy the next line from the stream to a dstring, trimming * the \n and \r. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * The dstring is truncated on entry. * *---------------------------------------------------------------------- */ static int GetLine(Stream *sPtr, Ns_DString *dsPtr) { char *eol = NULL; int n = 0; Ns_DStringTrunc(dsPtr, 0); do { if (sPtr->cnt > 0) { eol = strchr(sPtr->ptr, '\n'); if (eol == NULL) { n = sPtr->cnt; } else { *eol++ = '\0'; n = eol - sPtr->ptr; } Ns_DStringNAppend (dsPtr, sPtr->ptr, n - 1); sPtr->ptr += n; sPtr->cnt -= n; if (eol != NULL) { n = dsPtr->length; if (n > 0 && dsPtr->string[n - 1] == '\r') { Ns_DStringTrunc (dsPtr, n - 1); } return NS_TRUE; } } } while (FillBuf(sPtr)); return NS_FALSE; } aolserver4-nsopenssl-3.0beta26/https.tcl0000644000175000017500000003306510065763110020164 0ustar frankiefrankie# The contents of this file are subject to the AOLserver Public License # Version 1.1 (the "License"); you may not use this file except in # compliance with the License. You may obtain a copy of the License at # http://aolserver.com. # # Software distributed under the License is distributed on an "AS IS" # basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See # the License for the specific language governing rights and limitations # under the License. # # The Original Code is AOLserver Code and related documentation # distributed by AOL. # # The Initial Developer of the Original Code is America Online, # Inc. Portions created by AOL are Copyright (C) 1999 America Online, # Inc. All Rights Reserved. # # Alternatively, the contents of this file may be used under the terms # of the GNU General Public License (the "GPL"), in which case the # provisions of GPL are applicable instead of those above. If you wish # to allow use of your version of this file only under the terms of the # GPL and not to allow others to use your version of this file under the # License, indicate your decision by deleting the provisions above and # replace them with the notice and other provisions required by the GPL. # If you do not delete the provisions above, a recipient may use your # version of this file under either the License or the GPL. # # Copyright (C) 2001-2003 Scott S. Goodwin # # Derived from http.tcl, originally written by AOL # # $Header: /cvsroot/aolserver/nsopenssl/https.tcl,v 1.25 2004/06/16 23:06:45 dossy Exp $ # # https.tcl -- # # Routines for opening non-blocking HTTPS (SSL) connections through the Tcl # socket interface. This is essentially a copy of http.tcl with appropriate # changes. # # # ========================================================================== # API procs # ========================================================================== # # ns_httpsopen - # Fetch a web page # # Results: # A tcl list {read file handle} {write file handle} {result headers set} # {name of the module} # # Side effects: # May throw an error on failure. # # XXX 'module' arg no longer necessary # XXX need to add an 'sslcontext' arg # XXX need to decide how to handle invalid certs proc ns_httpsopen {method url {rqset ""} {timeout 30} {pdata ""} {module ""}} { # # Determine if url is local; prepend site address if so. Not # pretty but it'll do for now. # if [string match /* $url] { if {$module == ""} { set module "nsopenssl" } set url "[ns_conn location]$url" ns_log notice "ADJUSTED URL: $url" } # # Verify that the URL is an HTTPS url. # if ![string match https://* $url] { return -code error "Invalid url \"$url\": "\ "ns_httpsopen only supports HTTPS" } # # Find each element in the URL # set url [split $url /] set hp [split [lindex $url 2] :] set host [lindex $hp 0] set port [lindex $hp 1] if [string match $port ""] { set port 443 } set uri /[join [lrange $url 3 end] /] # # Open a TCP connection to the host:port # set fds [ns_openssl_sockopen -nonblock $host $port] set rfd [lindex $fds 0] set wfd [lindex $fds 1] if [catch { # # First write the request, then the headers if they exist. # _ns_https_puts $timeout $wfd "$method $uri HTTP/1.0\r" if {$rqset != ""} { # # There are request headers # # XXX should check to see if req'd Accept and User-Agent headers are there */ for {set i 0} {$i < [ns_set size $rqset]} {incr i} { set key [ns_set key $rqset $i] set val [ns_set value $rqset $i] _ns_https_puts $timeout $wfd "$key: $val\r" } } else { # # No headers were specified, so send a minimum set of # required headers. # _ns_https_puts $timeout $wfd "Accept: */*\r" _ns_https_puts $timeout $wfd \ "User-Agent: [ns_info name]-Tcl/[ns_info version]\r" } # # Always send a Host: header because virtual hosting happens # even with HTTP/1.0. # if { $port == 443 } { set hostheader "Host: ${host}\r" } else { set hostheader "Host: ${host}:${port}\r" } _ns_https_puts $timeout $wfd $hostheader # # If optional content exists, then output that. Otherwise spit # out a newline to end the headers. # if {$pdata != ""} { _ns_https_puts $timeout $wfd "\r\n$pdata\r" } else { _ns_https_puts $timeout $wfd "\r" } flush $wfd # # Create a new set; its name will be the result line from # the server. Then read headers into the set. # set rpset [ns_set new [_ns_https_gets $timeout $rfd]] while 1 { set line [_ns_https_gets $timeout $rfd] if ![string length $line] { break } ns_parseheader $rpset $line } } errMsg] { # # Something went wrong during the request, so return an error. # global errorInfo close $wfd close $rfd if [info exists rpset] { ns_set free $rpset } return -code error -errorinfo $errorInfo $errMsg } # # Return a list of read file, write file, and headers set. # return [list $rfd $wfd $rpset] } # # ns_httpspost - # Perform a POST request. This wraps ns_httpsopen. # # Results: # The URL content. # # Side effects: # proc ns_httpspost {url {rqset ""} {qsset ""} {type ""} {filesets ""} {timeout 30} {body ""}} { # # Build the request. Since we're posting, we have to set # content-type and content-length ourselves. We'll add these to # rqset, overwriting if they already existed, which they # shouldn't. # # # Handle the case where the user puts "" to indicate the default timeout # if {$timeout == ""} { set timeout 30 } set boundary "-----------------rc029340985544hg24309nto8899o9" if {[string match "" $rqset]} { set rqset [ns_set new rqset] ns_set put $rqset "Accept" "*/*" ns_set put $rqset "User-Agent" "[ns_info name]-Tcl/[ns_info version]" } if {$type == ""} { ns_set put $rqset "Content-type" "application/x-www-form-urlencoded" } elseif {$type == "multipart/form-data"} { # Can't double-quote the boundary value because of form.tcl ns_set put $rqset "Content-type" "multipart/form-data, boundary=$boundary" } else { ns_set put $rqset "Content-type" "$type" } # # Build the query string to POST with # set querystring "" if {$type == "multipart/form-data"} { # # Set the standard POST form parameters # if {![string match "" $qsset]} { for {set i 0} {$i < [ns_set size $qsset]} {incr i} { set key [ns_set key $qsset $i] set value [ns_set value $qsset $i] append querystring "--${boundary}\r\n" append querystring "Content-Disposition: form-data; name=\"$key\"\r\n\r\n" append querystring "$value\r\n" # XXX Do we ever have multiple parameters for post elements that aren't files? # append querystring "Content-Disposition: form-data; " # foreach dispositionlist [ns_set key $qsset $i] { # append querystring "[lindex $dispositionlist 0]=\"[lindex $dispositionlist 1]\"; " # } # regsub {;\s+$} querystring "" $querystring # append querystring "\r\n\r\n" # append querystring "[ns_set value $qsset $i]\r\n" } } # # Add files to POST request, if any # # filesets is a list of ns_sets that contains the needed information to # include files in the post. Each file represents one ns_set in the list # Each ns_set consists of four keys: # # name: the name of the form element for this file # filename: the name of the file # content: the actual contents of the file # content-type: the type of contents in the file, such as text/plain # # filesets are only used with multipart/form-data # # XXX tmp ns_log notice "QUERYSTRING:\n$querystring" ns_log notice "*** FILESET == $filesets" if {![string match "" $filesets]} { ns_log notice "*** S1" foreach file $filesets { ns_log notice "*** S2" append querystring "--${boundary}\r\n" append querystring "Content-Disposition: form-data; name=\"[ns_set iget $file name]\"; filename=\"[ns_set iget $file filename]\"\r\n" append querystring "Content-Type: [ns_set iget $file content-type]\r\n\r\n" append querystring "[ns_set iget $file content]\r\n" } } # # Finish POST request # append querystring "--${boundary}--\n" ns_set put $rqset "Content-length" [string length $querystring] } elseif {![string match "" $qsset]} { for {set i 0} {$i < [ns_set size $qsset]} {incr i} { set key [ns_set key $qsset $i] set value [ns_set value $qsset $i] if { $i > 0 } { append querystring "&" } append querystring "$key=[ns_urlencode $value]" } ns_set put $rqset "Content-length" [string length $querystring] } else { # # Send $body as the POST request data. # set querystring $body ns_set put $rqset "Content-length" [string length $querystring] } # # Perform the actual request. # set http [ns_httpsopen POST $url $rqset $timeout $querystring] set rfd [lindex $http 0] # XXX close [lindex $http 1] set headers [lindex $http 2] set length [ns_set iget $headers content-length] if [string match "" $length] { set length -1 } set err [catch { # # Read the content. # while 1 { set buf [_ns_https_read $timeout $rfd $length] append page $buf if [string match "" $buf] { break } if {$length > 0} { incr length -[string length $buf] if {$length <= 0} { break } } } } errMsg] ns_set free $headers close $rfd if $err { global errorInfo return -code error -errorinfo $errorInfo $errMsg } return $page } # # ns_httpsget - # Perform a GET request. This wraps ns_httpsopen, but it also # knows how to follow redirects and will read the content into # a buffer. # # Results: # The URL content. # # Side effects: # Will only follow redirections 10 levels deep. # # XXX 'module' arg no longer necessary # XXX need to add an 'sslcontext' arg # XXX need to decide how to handle invalid certs proc ns_httpsget {url {timeout 30} {depth 0} {rqset ""} {module ""}} { if {[incr depth] > 10} { return -code error "ns_httpsget: Recursive redirection: $url" } # # Perform the actual request. # set https [ns_httpsopen GET $url $rqset $timeout "" $module] set rfd [lindex $https 0] # XXX close [lindex $https 1] set headers [lindex $https 2] set response [ns_set name $headers] set status [lindex $response 1] if {$status == 302} { # # The response was a redirect, so free the headers and # recurse. # set location [ns_set iget $headers location] if {$location != ""} { ns_set free $headers close $rfd # XXX should put check for https:// here... if {[string first https:// $location] != 0} { set url2 [split $url /] set hp [split [lindex $url2 2] :] set host [lindex $hp 0] set port [lindex $hp 1] if [string match $port ""] { set port 443 } regexp "^(.*)://" $url match method set location "$method://$host:$port/$location" } return [ns_httpsget $location $timeout $depth] } } set length [ns_set iget $headers content-length] if [string match "" $length] { set length -1 } set err [catch { # # Read the content. # while 1 { set buf [_ns_https_read $timeout $rfd $length] append page $buf if [string match "" $buf] { break } if {$length > 0} { incr length -[string length $buf] if {$length <= 0} { break } } } } errMsg] ns_set free $headers close $rfd if $err { global errorInfo return -code error -errorinfo $errorInfo $errMsg } return $page } # ========================================================================== # Local procs # ========================================================================== # # _ns_https_readable - # Return the number of bytes available to read from a # socket without blocking, waiting up to $timeout seconds for bytes to # arrive if none are currently available. # # Results: # Number of bytes that are waiting to be read # proc _ns_https_readable {timeout sock} { set nread [ns_socknread $sock] if !$nread { set sel [ns_sockselect -timeout $timeout $sock {} {}] if [string match "" [lindex $sel 0]] { return -code error "ns_sockreadwait: Timeout waiting for remote" } set nread [ns_socknread $sock] } return $nread } # # _ns_https_read - # Read upto $length bytes from a socket without blocking. # # Results: # Up to $length bytes that were read from the socket. May # throw and error on EOF. # proc _ns_https_read {timeout sock length} { set buf "" set nread [_ns_https_readable $timeout $sock] if {$nread > 0} { if {$length > 0 && $length < $nread} { set nread $length } set buf [read $sock $nread] } return $buf } # # _ns_https_gets - # Carefully read lines, one character at a time, from a # socket to avoid blocking. # # Results: # One line read from the socket # proc _ns_https_gets {timeout sock} { set line "" set done 0 while {!$done} { set nline [_ns_https_readable $timeout $sock] if !$nline {set done 1} while {!$done && $nline > 0} { set char [read $sock 1] if {$char == "\n"} {set done 1} append line $char incr nline -1 } } string trimright $line } # # _ns_https_puts - # Send a string out a socket. If the socket buffer is # full, wait for up to $timeout seconds. # # Results: # None. # # Side effects: # May return with an error code on timeout. # proc _ns_https_puts {timeout sock string} { if {[lindex [ns_sockselect -timeout $timeout {} $sock {}] 1] == ""} { return -code error "ns_socksend: Timeout writing to socket" } puts $sock $string } aolserver4-nsopenssl-3.0beta26/README0000644000175000017500000003617510263172426017207 0ustar frankiefrankie$Header: /cvsroot/aolserver/nsopenssl/README,v 1.6 2004/08/25 21:33:47 dossy Exp $ SSLv2, SSLv3, TLSv1 Module -------------------------- This module requires OpenSSL 0.9.6 or higher. This module also requires that you use nsd8x, not nsd76. Feature Highlights ------------------ * Open Source software (AOLserver Public License or GPL) * Useable for both commercial and non-commercial use * 128-bit strong cryptography world-wide * Support for SSLv2, SSLv3 and TLSv1 protocols * Support for both RSA and Diffie-Hellman ciphers * Support for client certificate verification * Clean, reviewable ANSI C source code Troubleshooting nsopenssl ------------------------- If the key for your certificate won't load, it may be protected by a passphrase. You can tell by looking at it. If you see something like: -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-CBC,C19C9EF4C9122A23 D3kU+2NbhHs+BYG8vEljim0aoqYjGbbcSR1i+1KM4753FTLfJBCNWSBha+m18pLO U2erWwNLLyAWH2bxVKJDedKE3Hpr2TAq1R43s5heSP4YqGITAWMiknRHJm9f86ZA ... then the key is passphrase-protected. An unprotected key will begin like this: -----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQCdLzpBdFeAuOadJbWBkWob1GOwGl0BQhub2zucE9qXjzpCFcjK Z6gOO6rsj1DiHmSHxlGY3jWK7FFiGuTFHW1BxsAi7IBjAPdqm/g52WgP0g3JQBpE ... Compiling the code ------------------ To compile this code, just type: gmake OPENSSL=/usr/local/ssl or: export OPENSSL=/usr/local/ssl gmake gmake install INST=/usr/local/aolserver **** NOTE: Solaris Users: You may have to add an extra library search path and the gcc library for the linker to work. Specifically, you may need to change this: MODLIBS = -L$(OPENSSL) -lssl -lcrypto \ to something like this: MODLIBS = -L$(OPENSSL) -lssl -lcrypto \ -L/usr/local/lib/gcc-lib/sparc-sun-solaris2.6/2.8.1 -lgcc Adjust for which version of Solaris and GCC you're using. Much thanks to Ron Patterson, ron.patterson@corp.usa.net, for this information. See nsd.tcl for a sample configuration that uses SSL on port 8443. To test the server, put the sample configuration from nsd.tcl into your server's nsd.tcl, copy the sample *.pem files to $INST/servers/server1/modules/nsopenssl, and start your server. Visit https://hostname:8443/. The default key and certificate for the non-existent 'SnakeOil' company are included for testing purposes. Do not use these on a real server -- they are for testing only. Development Environment ----------------------- The code was developed under Debian 2.2 with OpenSSL 0.9.6. It will probably run on different flavors of UNIX, though minor modifications may be necessary. You can see debug output by putting the server itself in debug mode. There isn't a separate debug option for nsopenssl, but you can turn on Trace to see the handshaking in action as clients connect. OpenSSL must be compiled as position-independent, but it does not build that way in the configuration that comes from the OpenSSL distribution. The OpenSSL 0.9.5a release doesn't appear to have an option for this so you'll have to include it in your compile step. gmake CC="gcc -fPIC" In addition, some operating systems (Solaris x86) may not support position-independent code that has inline assembler. The configuration that seems to work on these platforms is: ./config no-asm Then, followed by the same gmake step as before: gmake CC="gcc -fPIC" Configuration Options --------------------- For nsopenssl 3.x, AOLserver 4.x: ns_section "ns/server/${vs1_servername}/module/nsopenssl/sslcontexts" ns_param vs1_users_ctx "SSL context used for regular user access" ns_param vs1_admins_ctx "SSL context used for administrator access" ns_param vs1_client_ctx "SSL context used for outgoing script socket connections" ns_section "ns/server/${vs1_servername}/module/nsopenssl/defaults" ns_param server vs1_users_ctx ns_param client vs1_client_ctx ns_section "ns/server/${vs1_servername}/module/nsopenssl/sslcontext/vs1_users_ctx" ns_param Role server #ns_param ModuleDir /path/to/dir ns_param CertFile server/server.crt ns_param KeyFile server/server.key ns_param CADir ca-client/dir ns_param CAFile ca-client/ca-client.crt ns_param Protocols "SSLv3, TLSv1" ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:++EXP" ns_param PeerVerify false ns_param PeerVerifyDepth 3 ns_param Trace true ns_section "ns/server/${vs1_servername}/module/nsopenssl/sslcontext/vs1_admins_ctx" ns_param Role server #ns_param ModuleDir /path/to/dir ns_param CertFile server/server.crt ns_param KeyFile server/server.key ns_param CADir ca-client/dir ns_param CAFile ca-client/ca-client.crt #ns_param Protocols "All" ns_param Protocols "SSLv3, TLSv1" ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:++EXP" ns_param PeerVerify false ns_param PeerVerifyDepth 3 ns_param Trace true # SSL drivers. Each driver defines a port and a named SSL context to associate # with it. ns_section "ns/server/${vs1_servername}/module/nsopenssl/ssldrivers" ns_param vs1_users_drv "Driver for vs1 regular user access" ns_param vs1_admins_drv "Driver for vs1 administrator access" ns_section "ns/server/${vs1_servername}/module/nsopenssl/ssldriver/vs1_users_drv" ns_param sslcontext vs1_users_ctx ns_param port 7001 ns_param hostname $hostname ns_param address $address ns_section "ns/server/${vs1_servername}/module/nsopenssl/ssldriver/vs1_admins_drv" ns_param sslcontext vs1_admins_ctx ns_param port 7002 ns_param hostname $hostname ns_param address $address # # Modules to load # ns_section "ns/server/${vs1_servername}/modules" ns_param nssock ${bindir}/nssock${ext} ns_param nslog ${bindir}/nslog${ext} ns_param nscgi ${bindir}/nscgi${ext} ns_param nsopenssl ${bindir}/nsopenssl${ext} For versions prior to 2.x: ns_section "ns/server/${servername}/module/nsopenssl" ns_param port $httpsport ns_param hostname $hostname ns_param address 192.168.0.2 # will use global address param if this isn't set here ns_param CertFile certfile.pem ns_param KeyFile keyfile.pem ns_param Protocols All #ns_param Protocols "SSLv2,SSLv3, TLSv1, all" #ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" #ns_param SessionCache false #ns_param SessionCacheSize 512 #ns_param SessionCacheTimeout 300 ns_param ClientVerify true ns_param CADir ca ns_param CAFile ca.pem ns_param Trace false ns_param RandomFile /some/file ns_param SeedBytes 1024 For nsopenssl 2.x, AOLserver 3.x: ---------------------------------------------------------------- ns_section "ns/server/${servername}/module/nsopenssl" # NSD-driven connections: ns_param ServerPort $httpsport ns_param ServerHostname $hostname ns_param ServerAddress $address ns_param ServerCertFile certfile.pem ns_param ServerKeyFile keyfile.pem ns_param ServerProtocols "SSLv2, SSLv3, TLSv1" ns_param ServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param ServerSessionCache false ns_param ServerSessionCacheID 1 ns_param ServerSessionCacheSize 512 ns_param ServerSessionCacheTimeout 300 ns_param ServerPeerVerify true ns_param ServerPeerVerifyDepth 3 ns_param ServerCADir ca ns_param ServerCAFile ca.pem ns_param ServerTrace false # For listening and accepting SSL connections via Tcl/C API: ns_param SockServerCertFile certfile.pem ns_param SockServerKeyFile keyfile.pem ns_param SockServerProtocols "SSLv2, SSLv3, TLSv1" ns_param SockServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockServerSessionCache false ns_param SockServerSessionCacheID 2 ns_param SockServerSessionCacheSize 512 ns_param SockServerSessionCacheTimeout 300 ns_param SockServerPeerVerify true ns_param SockServerPeerVerifyDepth 3 ns_param SockServerCADir internal_ca ns_param SockServerCAFile internal_ca.pem ns_param SockServerTrace false # Outgoing SSL connections ns_param SockClientCertFile clientcertfile.pem ns_param SockClientKeyFile clientkeyfile.pem ns_param SockClientProtocols "SSLv2, SSLv3, TLSv1" ns_param SockClientCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockClientSessionCache false ns_param SockClientSessionCacheID 3 ns_param SockClientSessionCacheSize 512 ns_param SockClientSessionCacheTimeout 300 ns_param SockClientPeerVerify true ns_param SockServerPeerVerifyDepth 3 ns_param SockClientCADir ca ns_param SockClientCAFile ca.pem ns_param SockClientTrace false # OpenSSL library support: ns_param RandomFile /some/file ns_param SeedBytes 1024 And to load the module: ns_section "ns/server/${servername}/modules" ns_param nsopenssl ${bindir}/nsopenssl.${ext} Configuration Notes ------------------- Session caching is turned on by default. RandomFile isn't necessary, but if you want to use your own random bits, you can set this. On Linux, it won't matter: OpenSSL will use /dev/urandom to transparently seed the PRNG. SeedBytes is optional; it tells how many bytes to seed the PRNG with, but only if the PRNG actually needs seeding. It defaults to 1024 bytes. If seeding the PRNG fails, bump this number up. WARNING!!! If the client sends an invalid certificate, the connection is still accepted. Use 'ns_openssl clientcert valid' in your Tcl code or ADP page to determine if you received a client certificate and if it was valid. NOTE: Your key.pem file must *not* be protected by a passphrase or the server won't start. Tcl Interface Commands ---------------------- ns_openssl info - returns a Tcl list containing the SSL libary name, SSL library version, Crypto library name, Crypto library version. ns_openssl protocol - returns the protocol used by the current connection as a string: SSLv2, SSLv3, TLSv1 or UNKNOWN ns_openssl cipher name - returns the name of the cipher being used by the current connection (e.g. RSA_MD5) ns_openssl cipher strength - returns the strength of the cipher being used by the current connection as a number (e.g. 40, 56, 128 etc.) ns_openssl clientcert exists - returns 0 if no client certificate exists, or a 1 if a client certificate does exist. ns_openssl clientcert valid - returns 1 if client certificate was obtained *and* it is valid; 0 otherwise. ns_openssl clientcert version - returns a Tcl string containing the certificate's version number, e.g. "3". ns_openssl clientcert serial - returns a Tcl string containing the certificate's serial number, e.g. "27C6". ns_openssl clientcert subject - returns a Tcl string containing the certificate's subject name, e.g. "/C=US/O=U.S. Government/OU=DoD/OU=PKI/OU=USAF/CN=Goodwin.Scott.S.0300074002" ns_openssl clientcert issuer - returns a Tcl string containing the certificate's issuer name, e.g. "/C=US/O=U.S. Government/OU=DoD/OU=PKI/CN=Med CA-2" ns_openssl clientcert notbefore - returns a Tcl string containing the certificate's valid start date, e.g. "Aug 28 20:00:38 2000 GMT" ns_openssl clientcert notafter - returns a Tcl string containing the certificate's valid end date, e.g. "Aug 28 20:00:38 2002 GMT" ns_openssl clientcert signature_algorithm - returns a Tcl string containing the algorithm used for the signature, e.g. "sha1WithRSAEncryption" ns_openssl clientcert key_algorithm - returns a Tcl string containing the algorithm used for the key, e.g. "rsaEncryption" ns_openssl clientcert pem - returns a Tcl string containing the client's PEM-formatted certificate, which should have "--- BEGIN CERTIFICATE ---" and "--- END CERTIFICATE ---" lines in it. New Commands in 2.0: ns_openssl_sockopen hostname port - opens an SSL connection to the host on the specified port. ns_openssl_socklisten - listens for SSL connections from clients ns_openssl_sockaccept - accepts SSL connections from clients ns_openssl_sockcallback - run a script when the SSL socket is in a certain state ns_openssl_socklistencallback - listen for SSL connections and run a script when a client connects ns_httpsget - grab a page from an SSL server ns_httpsopen - open an SSL connection to a server ns_openssl_geturl - grab a page from an SSL server To use the last three commands, you'll need to have https.tcl installed in your server's module/tcl directory. Copyright Notices ----------------- The nsopenssl module was originally written and Copyrighted by Stefan Arentz. Parts of it are also copyrighted by Scott S. Goodwin. It is distributed under the AOLserver Public License. See the file license.txt for more information. This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/) This product links to cryptographic software (OpenSSL) originally written by Eric Young (eay@cryptsoft.com). There is no cryptographic software within the source code of this module. Related Links ------------- http://scottg.net Information on AOLserver and this module http://www.opennsd.org OpenNSD site http://www.aolserver.com AOLserver homepage http://www.openssl.org OpenSSL toolkit homepage http://www.modssl.org OpenSSL module for Apache http://www.thawte.com For getting test certificates Developers ---------- If you make any mods to nsopenssl and commit them, please be sure to tag after your commit. This allows anyone to go and get a named (i.e. tagged) snapshot of the code. You tag by doing: cvs -q tag nsopenssl-2_2_beta_1 Make sure you know what the latest tag is and increment accordingly. Design ------ The nsopenssl 3.x series is designed to be more modular. The three main data types of the module (SSL contexts, Drivers and Connections) are decoupled. SSL contexts can be created and initialized at any time and then used with Drivers and Connections. See nsopenssl.h for details. The process flow is as follows: - initialize OpenSSL and 'global' items from nsd.tcl - for each virtual server: - initialize SSL contexts defined in nsd.tcl - create drivers that are defined in nsd.tcl, tying them to an SSL context - Wait for incoming connections for core HTTP processing, and make available a C and Tcl API for creating new SSL contexts, initiating SSL listeners and initiating outgoing SSL connections aolserver4-nsopenssl-3.0beta26/tclcmds.c0000644000175000017500000015615310063262452020120 0ustar frankiefrankie/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. * * Copyright (C) 2000-2003 Scott S. Goodwin * * Module originally written by Stefan Arentz. Early contributions made by * Freddie Mendoze and Rob Mayoff. */ /* * tclcmds.c -- * * Tcl API for nsopenssl */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsopenssl/tclcmds.c,v 1.51 2004/06/13 04:21:31 scottg Exp $, compiled: " __DATE__ " " __TIME__; #include "nsopenssl.h" /* * Used to track both conn info and which chan to close. */ typedef struct ChanInfo { NsOpenSSLConn *sslconn; SOCKET socket; Tcl_Channel chan; void *otherchaninfo; } ChanInfo; static int CreateTclChannel(NsOpenSSLConn *sslconn, Tcl_Interp *interp); static int ChanCloseProc(ClientData arg, Tcl_Interp *interp); static int ChanInputProc(ClientData arg, char *buf, int bufSize, int *errorCodePtr); static int ChanOutputProc(ClientData arg, char *buf, int toWrite, int *errorCodePtr); static void ChanWatchProc(ClientData arg, int mask); static int ChanFlushProc(ClientData arg); static int ChanGetHandleProc(ClientData arg, int direction, ClientData *handlePtr); static void SetResultToX509Name(Tcl_Interp *interp, X509_NAME *name); static void SetResultToObjectName(Tcl_Interp *interp, ASN1_OBJECT *obj); static char * ValidTime(ASN1_UTCTIME *tm); static char * PEMCertificate(X509 *peercert); static int EnterSock(Tcl_Interp *interp, SOCKET sock); static int EnterDup(Tcl_Interp *interp, SOCKET sock); #if 0 static int EnterDupedSocks(Tcl_Interp *interp, SOCKET sock); #endif static int GetSet(Tcl_Interp *interp, char *flist, int write, fd_set **setPtrPtr, fd_set *setPtr, SOCKET *maxPtr); static void AppendReadyFiles (Tcl_Interp *interp, fd_set *setPtr, int write, char *flist, Tcl_DString *dsPtr); static Ns_SockProc SSLSockListenCallbackProc; static Ns_SockProc SSLSockCallbackProc; /* * Define a Tcl channel so we can use standard Tcl commands to read and write * on the connection. */ static Tcl_ChannelType opensslChannelType = { "openssl", /* Type name. */ TCL_CHANNEL_VERSION_2, /* channel version 2 */ ChanCloseProc, /* Close proc. */ ChanInputProc, /* Input proc. */ ChanOutputProc, /* Output proc. */ NULL, /* Seek proc. */ NULL, /* Set option proc. */ NULL, /* Get option proc. */ ChanWatchProc, /* Watch proc. (mandatory) */ ChanGetHandleProc, /* Get Handle */ NULL, /* Close2 proc */ NULL, /* Set blocking/nonblocking mode. */ ChanFlushProc, /* Flush proc */ NULL, /* Handler proc */ }; static Ns_TclInterpInitProc AddCmds; extern Tcl_ObjCmdProc NsTclOpenSSLObjCmd, NsTclOpenSSLSockAcceptObjCmd, NsTclOpenSSLSockOpenObjCmd, NsTclOpenSSLSockListenObjCmd, NsTclOpenSSLSockListenCallbackObjCmd, NsTclOpenSSLSockCallbackObjCmd, NsTclOpenSSLGetUrlObjCmd; extern Tcl_CmdProc NsTclOpenSSLGetUrlCmd, NsTclOpenSSLSockCheckCmd, NsTclOpenSSLSockNReadCmd, NsTclOpenSSLSockSelectCmd; typedef struct Cmd { char *name; Tcl_CmdProc *proc; Tcl_ObjCmdProc *objProc; } Cmd; static Cmd nsopensslCmds[] = { {"ns_openssl", NULL, NsTclOpenSSLObjCmd }, {"ns_openssl_sockopen", NULL, NsTclOpenSSLSockOpenObjCmd }, {"ns_openssl_geturl", NULL, NsTclOpenSSLGetUrlObjCmd }, {"ns_openssl_sockaccept", NULL, NsTclOpenSSLSockAcceptObjCmd }, {"ns_openssl_socklisten", NULL, NsTclOpenSSLSockListenObjCmd }, {"ns_openssl_sockcallback", NULL, NsTclOpenSSLSockCallbackObjCmd }, {"ns_openssl_socklistencallback", NULL, NsTclOpenSSLSockListenCallbackObjCmd }, #if 0 /* these ns_openssl_sock* commands are not implemented */ {"ns_openssl_socknread", NsTclOpenSSLSockNReadCmd, NULL }, {"ns_openssl_sockselect", NsTclOpenSSLSockSelectCmd, NULL }, {"ns_openssl_sockcheck", NsTclOpenSSLSockCheckCmd, NULL }, {"ns_openssl_socketpair", NsTclSSLSocketPairCmd, NULL }, {"ns_openssl_hostbyaddr", NsTclSSLGetByCmd, NULL }, {"ns_openssl_addrbyhost", NsTclSSLGetByCmd, (ClientData) 1 }, #endif {NULL, NULL, NULL} }; typedef struct SockListenCallback { char *server; NsOpenSSLContext *sslcontext; char *script; } SockListenCallback; typedef struct SockCallback { char *server; int when; char script[1]; } SockCallback; /* *---------------------------------------------------------------------- * * NsOpenSSLTclInit -- * * Initialize Tcl API for a virtual server. The last argument of * Ns_TclInitInterps is a pointer to a function that * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ void NsOpenSSLTclInit(char *server) { Server *thisServer = NsOpenSSLServerGet(server); Ns_TclInitInterps(server, AddCmds, (void *) thisServer); } /* *---------------------------------------------------------------------- * * AddCmds -- * * Add nsopenssl commands to Tcl interpreter. * * Results: * NS_OK or NS_ERROR. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int AddCmds(Tcl_Interp *interp, void *arg) { Cmd *cmd = (Cmd *) &nsopensslCmds; while (cmd->name != NULL) { if (cmd->objProc != NULL) { Tcl_CreateObjCommand(interp, cmd->name, cmd->objProc, arg, NULL); } else { Tcl_CreateCommand(interp, cmd->name, cmd->proc, arg, NULL); } ++cmd; } return NS_OK; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLObjCmd -- * * Implements ns_openssl command, which returns information about clients * connected to the nsopenssl server, including client certificates. * * Results: * Tcl string result. * * Side effects: * None. * *---------------------------------------------------------------------- */ int NsTclOpenSSLObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { // XXX Server *thisServer = (Server *) arg; NsOpenSSLConn *sslconn = NULL; X509 *peercert = NULL; SSL_CIPHER *cipher = NULL; Ns_Conn *conn = NULL; char *string = NULL; char *name = NULL; int integer = 0; int status = TCL_OK; static CONST char *opts[] = { "info", "module", "protocol", "port", "peerport", "cipher", "clientcert" }; enum ISubCmdIdx { CInfoIdx, CModuleIdx, CProtocolIdx, CPortIdx, CPeerPortIdx, CCipherIdx, CClientCertIdx } opt; if (objc < 2) { Tcl_WrongNumArgs(interp, 1, objv, "option"); return TCL_ERROR; } if (Tcl_GetIndexFromObj(interp, objv[1], opts, "option", 0, (int *) &opt) != TCL_OK) { return TCL_ERROR; } if (opt == CInfoIdx) { Tcl_SetResult(interp, OPENSSL_VERSION_TEXT, TCL_STATIC); return TCL_OK; } /* * AOLserver stashes a pointer to the conn in the interp. We then use that * to get a pointer to our SSL conn through the core driver's context. If * conn is NULL, it means our connection is not driver by the comm API, so * we need to get the connection information back another way. */ /* XXX needs rewiring to allow for reporting info on non-nsd-driven conns */ conn = Ns_TclGetConn(interp); if (conn == NULL) { Tcl_AppendResult(interp, "this is not a connection thread", NULL); return TCL_ERROR; } else { name = Ns_ConnDriverName(conn); if (name != NULL && STREQ(name, MODULE)) { sslconn = (NsOpenSSLConn *) Ns_ConnDriverContext(conn); } if (sslconn == NULL) { Tcl_AppendResult(interp, "this is a connection thread, but not an SSL connection thread", NULL); return TCL_ERROR; } } switch (opt) { case CModuleIdx: /* * Implement: * ns_openssl module name * ns_openssl module port */ if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); return TCL_ERROR; } if (STREQ(Tcl_GetString(objv[2]), "name")) { Tcl_SetResult(interp, MODULE, TCL_VOLATILE); } else if (STREQ(Tcl_GetString(objv[2]), "port")) { /* XXX peerport is the port this conn came in on -- clean up */ sprintf(interp->result, "%d", sslconn->peerport); } break; case CProtocolIdx: switch (sslconn->ssl->session->ssl_version) { case SSL2_VERSION: string = "SSLv2"; break; case SSL3_VERSION: string = "SSLv3"; break; case TLS1_VERSION: string = "TLSv1"; break; default: string = "UNKNOWN"; } Tcl_SetResult(interp, string, TCL_VOLATILE); break; case CPortIdx: case CPeerPortIdx: sprintf(interp->result, "%d", sslconn->peerport); break; case CCipherIdx: cipher = SSL_get_current_cipher(sslconn->ssl); if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); return TCL_ERROR; } if (STREQ(Tcl_GetString(objv[2]), "name")) { string = (sslconn->ssl != NULL ? (char *) SSL_CIPHER_get_name(cipher) : NULL); Tcl_SetResult(interp, string, TCL_VOLATILE); } else if (STREQ(Tcl_GetString(objv[2]), "strength")) { integer = SSL_CIPHER_get_bits(cipher, &integer); sprintf(interp->result, "%d", integer); } break; case CClientCertIdx: /* * Implement: * ns_openssl clientcert exists * ns_openssl clientcert version * ns_openssl clientcert serial * ns_openssl clientcert subject * ns_openssl clientcert issuer * ns_openssl clientcert notbefore * ns_openssl clientcert notafter * ns_openssl clientcert signaturealgorithm * ns_openssl clientcert key_algorithm * ns_openssl clientcert pem * ns_openssl clientcert valid */ if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "option"); return TCL_ERROR; } peercert = (sslconn == NULL) ? NULL : SSL_get_peer_certificate(sslconn->ssl); if (STREQ(Tcl_GetString(objv[2]), "exists")) { Tcl_SetResult(interp, peercert == NULL ? "0" : "1", TCL_STATIC); } else if (STREQ(Tcl_GetString(objv[2]), "version")) { sprintf(interp->result, "%lu", peercert == NULL ? 0 : X509_get_version(peercert) + 1); } else if (STREQ(Tcl_GetString(objv[2]), "serial")) { sprintf(interp->result, "%ld", peercert == NULL ? 0 : ASN1_INTEGER_get(X509_get_serialNumber(peercert))); } else if (STREQ(Tcl_GetString(objv[2]), "subject")) { if (peercert != NULL) { SetResultToX509Name(interp, X509_get_subject_name(peercert)); } } else if (STREQ(Tcl_GetString(objv[2]), "issuer")) { if (peercert != NULL) { SetResultToX509Name(interp, X509_get_issuer_name(peercert)); } } else if (STREQ(Tcl_GetString(objv[2]), "notbefore")) { if (peercert != NULL) { string = ValidTime(X509_get_notBefore(peercert)); if (string == NULL) { Tcl_SetResult(interp, "error getting notbefore", TCL_STATIC); status = TCL_ERROR; } else { Tcl_SetResult(interp, string, TCL_DYNAMIC); } } } else if (STREQ(Tcl_GetString(objv[2]), "notafter")) { if (peercert != NULL) { string = ValidTime(X509_get_notAfter(peercert)); if (string == NULL) { Tcl_SetResult(interp, "error getting notafter", TCL_STATIC); status = TCL_ERROR; } else { Tcl_SetResult(interp, string, TCL_DYNAMIC); } } } else if (STREQ(Tcl_GetString(objv[2]), "signature_algorithm")) { if (peercert != NULL) { SetResultToObjectName(interp, peercert->cert_info->signature-> algorithm); } } else if (STREQ(Tcl_GetString(objv[2]), "key_algorithm")) { if (peercert != NULL) { SetResultToObjectName(interp, peercert->cert_info->key->algor-> algorithm); } } else if (STREQ(Tcl_GetString(objv[2]), "pem")) { if (peercert != NULL) { string = PEMCertificate(peercert); if (string == NULL) { Tcl_SetResult(interp, "error getting pem", TCL_STATIC); status = TCL_ERROR; } else { Tcl_SetResult(interp, string, TCL_DYNAMIC); } } } else if (STREQ(Tcl_GetString(objv[2]), "valid")) { sprintf(interp->result, "%d", peercert != NULL && SSL_get_verify_result(sslconn->ssl) == X509_V_OK); } else { /* XXX revalidate the list below (see if Tcl has a better library function for this) */ Tcl_AppendResult(interp, "unknown command \"", Tcl_GetString(objv[2]), "\": should be one of: exists version serial subject issuer notbefore notafter signature_algorithm key_algorithm pem valid", NULL); return TCL_ERROR; } break; case CInfoIdx: /* NEVER REACHED */ break; } return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockOpenObjCmd -- * * Open a tcp connection to a host/port via SSL. * * Results: * Tcl result. * * Side effects: * Will open a connection and register a Tcl channel. * *---------------------------------------------------------------------- */ int NsTclOpenSSLSockOpenObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Server *thisServer = (Server *) arg; NsOpenSSLConn *sslconn = NULL; NsOpenSSLContext *sslcontext = NULL; char *name = NULL; int first = 1; int async = 0; int timeout = -1; int sslctx = 0; int port = 0; CONST char *args = "?-nonblock|-timeout seconds? host port ?sslcontext?"; /* * (3) ns_sockopen host port * (4) ns_sockopen -nonblock host port * (5) ns_sockopen -timeout seconds host port * (4) ns_sockopen host port sslcontext * (5) ns_sockopen -nonblock host port sslcontext * (6) ns_sockopen -timeout seconds host port sslcontext */ /* * Works out to this matrix where the # is the number of args: * * sslcontext? * * Y N * --------- * no '-' 4 3 * -nonblock 5 4 * -timeout 6 5 */ if (objc < 3 || objc > 6) { Tcl_WrongNumArgs(interp, 1, objv, "?-nonblock|-timeout seconds? host port ?sslcontext?"); return TCL_ERROR; } if (STREQ(Tcl_GetString(objv[1]), "-nonblock")) { if (objc == 4) { sslctx = 0; } else if (objc == 5) { sslctx = 1; } else { Tcl_WrongNumArgs(interp, 1, objv, args); return TCL_ERROR; } first = 2; async = 1; } else if (STREQ(Tcl_GetString(objv[1]), "-timeout")) { if (objc == 5) { sslctx = 0; } else if (objc == 6) { sslctx = 1; } else { Tcl_WrongNumArgs(interp, 1, objv, args); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[2], &timeout) != TCL_OK) { return TCL_ERROR; } first = 3; } else { if (objc == 3) { sslctx = 0; } else if (objc == 4) { sslctx = 1; } else { Tcl_WrongNumArgs(interp, 1, objv, args); return TCL_ERROR; } } if (Tcl_GetIntFromObj(interp, objv[first + 1], &port) != TCL_OK) { return TCL_ERROR; } /* * Get the named SSL context. If there is no named SSL context, attempt to * use the default. */ if (sslctx) { name = (char *) Tcl_GetString(objv[first + 2]); sslcontext = Ns_OpenSSLServerSSLContextGet(thisServer->server, name); } else { sslcontext = NsOpenSSLContextClientDefaultGet(thisServer->server); } if (sslcontext == NULL) { Tcl_SetResult(interp, "failed to use either named or default client SSL context", TCL_STATIC); return TCL_ERROR; } /* * Perform the connection. */ sslconn = Ns_OpenSSLSockConnect( thisServer->server, Tcl_GetString(objv[first]), port, async, timeout, sslcontext ); if (sslconn == NULL) { Tcl_AppendResult(interp, "could not connect to \"", Tcl_GetString(objv[first]), ":", Tcl_GetString(objv[first + 1]), "\"", NULL); return TCL_ERROR; } /* * Create the Tcl channel that let's us use gets, puts etc. and layer it on * top of the conn. */ if (CreateTclChannel(sslconn, interp) != NS_OK) { Ns_Log(Warning, "%s: %s: Tcl channel not available", MODULE, sslconn->server); //Ns_Log(Debug, "--->>> BEFORE ConnDestroy: SockOpen"); NsOpenSSLConnDestroy(sslconn); return TCL_ERROR; } /* * Append "1" as the third element returned if peer's certificate is valid; * "0" otherwise. */ if (Ns_OpenSSLX509CertVerify(sslconn->ssl)) { Tcl_AppendElement(interp, "1"); } else { Tcl_AppendElement(interp, "0"); } return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockListenObjCmd -- * * Listen on a TCP port. * * Results: * Tcl result. * * Side effects: * Will listen on a port. * *---------------------------------------------------------------------- */ extern int NsTclOpenSSLSockListenObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Server *thisServer = (Server *) arg; SOCKET socket = INVALID_SOCKET; char *addr = NULL; int port = 0; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "address port"); return TCL_ERROR; } addr = Tcl_GetString(objv[1]); if (STREQ(addr, "*")) { addr = NULL; } if (Tcl_GetIntFromObj(interp, objv[2], &port) != TCL_OK) { return TCL_ERROR; } socket = Ns_OpenSSLSockListen(addr, port); if (socket == INVALID_SOCKET) { Tcl_AppendResult(interp, "could not listen on \"", addr, ":", Tcl_GetString(objv[2]), "\"", NULL); return TCL_ERROR; } return EnterSock(interp, socket); } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockAcceptObjCmd -- * * Accept a connection from a listening socket. * * Results: * Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ /* XXX SSL context needs to be passed */ extern int NsTclOpenSSLSockAcceptObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Server *thisServer = (Server *) arg; NsOpenSSLConn *sslconn = NULL; NsOpenSSLContext *sslcontext = NULL; SOCKET socket = INVALID_SOCKET; if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "sockId"); return TCL_ERROR; } if (Ns_TclGetOpenFd(interp, Tcl_GetString(objv[1]), 0, (int *) &socket) != TCL_OK) { return TCL_ERROR; } /* * Perform normal socket accept */ socket = Ns_SockAccept(socket, NULL, 0); if (socket == INVALID_SOCKET) { Tcl_AppendResult(interp, "accept failed: ", SockError(interp), NULL); return TCL_ERROR; } /* Figure out which SSL context to use in creating the SSL connection */ /* XXX update API to accept last arg of sslcontext */ //if (sslctx) { // name = (char *) Tcl_GetString(objv[first + 2]); // sslcontext = Ns_OpenSSLServerSSLContextGet(thisServer->server, module, name); //} else { sslcontext = NsOpenSSLContextServerDefaultGet(thisServer->server); //} if (sslcontext == NULL) { Tcl_SetResult(interp, "failed to use either named or default client SSL context", TCL_STATIC); return TCL_ERROR; } sslconn = Ns_OpenSSLSockAccept(socket, sslcontext); if (sslconn == NULL) { Tcl_SetResult(interp, "SSL accept failed", TCL_STATIC); return TCL_ERROR; } if (CreateTclChannel(sslconn, interp) != NS_OK) { Ns_Log(Error, "%s (%s): Tcl channel not available", MODULE, sslconn->server); //Ns_Log(Debug, "--->>> BEFORE ConnDestroy: SockAccept"); NsOpenSSLConnDestroy(sslconn); return TCL_ERROR; } /* * Append "1" as the third element returned if peer certificate * is found to be valid; "0" otherwise. Is this the best way to do * it? */ if (Ns_OpenSSLX509CertVerify(sslconn)) { Tcl_AppendElement(interp, "1"); } else { Tcl_AppendElement(interp, "0"); } return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLGetUrlObjCmd -- * * Implements ns_openssl_geturl. * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ /* XXX SSL context needs to be passed */ /* XXX restructure this function to not use the 'done' label */ extern int NsTclOpenSSLGetUrlObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Server *thisServer = (Server *) arg; NsOpenSSLContext *sslcontext = NULL; Ns_DString ds; Ns_Set *headers = NULL; int status = TCL_ERROR; char *url = NULL; Ns_DStringInit(&ds); if ((objc != 3) && (objc != 2)) { Tcl_WrongNumArgs(interp, 1, objv, " url ?headersSetIdVar?"); goto done; } if (objc == 2) { headers = NULL; } else { headers = Ns_SetCreate(NULL); } url = Tcl_GetString(objv[1]); if (url[1] == '/') { if (Ns_FetchPage(&ds, url, Ns_TclInterpServer(interp)) != NS_OK) { Tcl_AppendResult(interp, "Could not get contents of URL \"", url, "\"", NULL); goto done; } } else { /* Figure out which SSL context to use in creating the SSL connection */ /* XXX update API to accept last arg of sslcontext */ //if (sslctx) { // name = (char *) Tcl_GetString(objv[first + 2]); // sslcontext = Ns_OpenSSLServerSSLContextGet(thisServer->server, module, name); //} else { sslcontext = NsOpenSSLContextClientDefaultGet(thisServer->server); //} if (sslcontext == NULL) { Tcl_SetResult(interp, "failed to use either named or default client SSL context", TCL_STATIC); goto done; } if (Ns_OpenSSLFetchUrl(thisServer->server, &ds, url, headers, sslcontext) != NS_OK) { Tcl_AppendResult(interp, "Could not get contents of URL \"", url, "\"", NULL); if (headers != NULL) { Ns_SetFree(headers); } goto done; } } if (objc == 3) { Ns_TclEnterSet(interp, headers, 1); /* XXX there's probably a Tcl_Obj way of doing the following */ Tcl_SetVar(interp, Tcl_GetString(objv[2]), interp->result, 0); } Tcl_SetResult(interp, ds.string, TCL_VOLATILE); status = TCL_OK; done: Ns_DStringFree(&ds); return status; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockNReadCmd -- * * Gets the number of bytes that a socket has waiting to be * read. * * Results: * Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int NsTclOpenSSLSockNReadCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv) { Server *thisServer = (Server *) arg; Tcl_Channel chan = NULL; SOCKET socket = INVALID_SOCKET; int nread = 0; int status = TCL_ERROR; if (argc != 2) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " sockId\"", NULL); goto done; } chan = Tcl_GetChannel(interp, argv[1], NULL); if ( chan == NULL || Ns_TclGetOpenFd(interp, argv[1], 0, (int *) &socket) != TCL_OK ) { goto done; } if (ns_sockioctl(socket, FIONREAD, &nread) != 0) { Tcl_AppendResult(interp, "ns_sockioctl failed: ", SockError(interp), NULL); goto done; } nread += Tcl_InputBuffered(chan); sprintf(interp->result, "%d", nread); status = TCL_OK; done: return status; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockCheckCmd -- * * Check if a socket is still connected, useful for nonblocking. * * Results: * Tcl result. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int NsTclOpenSSLSockCheckCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char **argv) { Server *thisServer = (Server *) arg; SOCKET socket = INVALID_SOCKET; int status = TCL_ERROR; if (argc != 2) { Tcl_AppendResult(interp, "wrong # of args: should be \"", argv[0], " sockId\"", NULL); goto done; } if (Ns_TclGetOpenFd(interp, argv[1], 1, (int *) &socket) != TCL_OK) { goto done; } if (send(socket, NULL, 0, 0) != 0) { interp->result = "0"; } else { interp->result = "1"; } status = TCL_OK; done: return status; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSelectCmd -- * * Imlements ns_sockselect: basically a tcl version of * select(2). * * Results: * Tcl result. * * Side effects: * See docs. * *---------------------------------------------------------------------- */ extern int NsTclOpenSSLSockSelectCmd(ClientData arg, Tcl_Interp *interp, int argc, CONST char *argv[]) { Server *thisServer = (Server *) arg; fd_set rset; fd_set wset; fd_set eset; fd_set *rPtr = NULL; fd_set *wPtr = NULL; fd_set *ePtr = NULL; SOCKET maxfd = INVALID_SOCKET; Tcl_Channel chan = NULL; Tcl_DString dsRfd; Tcl_DString dsNbuf; struct timeval tv; struct timeval *tvPtr = NULL; char **fargv = NULL; int fargc = 0; int i; int status = TCL_ERROR; int first; Tcl_DStringInit(&dsRfd); Tcl_DStringInit(&dsNbuf); if (argc != 6 && argc != 4) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?-timeout sec? rfds wfds efds\"", NULL); return TCL_ERROR; } if (argc == 4) { tvPtr = NULL; first = 1; } else { tvPtr = &tv; if (strcmp(argv[1], "-timeout") != 0) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?-timeout sec? rfds wfds efds\"", NULL); return TCL_ERROR; } tv.tv_usec = 0; if (Tcl_GetInt(interp, argv[2], &i) != TCL_OK) { return TCL_ERROR; } tv.tv_sec = i; first = 3; } /* * Readable fd's are treated differently because they may * have buffered input. Before doing a select, see if they * have any waiting data that's been buffered by the channel. */ if (Tcl_SplitList(interp, argv[first++], &fargc, &fargv) != TCL_OK) { return TCL_ERROR; } for (i = 0; i < fargc; ++i) { chan = Tcl_GetChannel(interp, fargv[i], NULL); if (chan == NULL) { goto done; } if (Tcl_InputBuffered(chan) > 0) { Tcl_DStringAppendElement(&dsNbuf, fargv[i]); } else { Tcl_DStringAppendElement(&dsRfd, fargv[i]); } } /* * Since at least one read fd had buffered input, * turn the select into a polling select just * to pick up anything else ready right now. */ if (dsNbuf.length > 0) { tv.tv_sec = 0; tv.tv_usec = 0; tvPtr = &tv; } maxfd = 0; if (GetSet(interp, dsRfd.string, 0, &rPtr, &rset, &maxfd) != TCL_OK) { goto done; } if (GetSet(interp, argv[first++], 1, &wPtr, &wset, &maxfd) != TCL_OK) { goto done; } if (GetSet(interp, argv[first++], 0, &ePtr, &eset, &maxfd) != TCL_OK) { goto done; } /* * Return immediately if we're not doing a select on anything. */ if (dsNbuf.length == 0 && rPtr == NULL && wPtr == NULL && ePtr == NULL && tvPtr == NULL) { status = TCL_OK; } else { /* * Actually perform the select. */ do { i = select(maxfd + 1, rPtr, wPtr, ePtr, tvPtr); } while (i < 0 && ns_sockerrno == EINTR); if (i == -1) { Tcl_AppendResult(interp, "select failed: ", SockError(interp), NULL); } else { if (i == 0) { /* * The sets can have any random value now */ if (rPtr != NULL) { FD_ZERO(rPtr); } if (wPtr != NULL) { FD_ZERO(wPtr); } if (ePtr != NULL) { FD_ZERO(ePtr); } } AppendReadyFiles(interp, rPtr, 0, dsRfd.string, &dsNbuf); first -= 2; AppendReadyFiles(interp, wPtr, 1, argv[first++], NULL); AppendReadyFiles(interp, ePtr, 0, argv[first++], NULL); status = TCL_OK; } } done: Tcl_DStringFree(&dsRfd); Tcl_DStringFree(&dsNbuf); ckfree((char *) fargv); return status; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockCallbackObjCmd -- * * Register a Tcl callback to be run when a certain state exists * on a socket. * * Results: * Tcl result. * * Side effects: * A callback will be registered. * *---------------------------------------------------------------------- */ extern int NsTclOpenSSLSockCallbackObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Server *thisServer = (Server *) arg; SockCallback *cbPtr = NULL; SOCKET socket = INVALID_SOCKET; int when = 0; char *s = NULL; if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "sockId script when"); return TCL_ERROR; } s = Tcl_GetString(objv[3]); while (*s != '\0') { if (*s == 'r') { when |= NS_SOCK_READ; } else if (*s == 'w') { when |= NS_SOCK_WRITE; } else if (*s == 'e') { when |= NS_SOCK_EXCEPTION; } else if (*s == 'x') { when |= NS_SOCK_EXIT; } else { Tcl_AppendResult(interp, "invalid when specification \"", Tcl_GetString(objv[3]), "\": should be one or more of r, w, e, or x", NULL); return TCL_ERROR; } ++s; } if (when == 0) { Tcl_AppendResult(interp, "invalid when specification \"", Tcl_GetString(objv[3]), "\": should be one or more of r, w, e, or x", NULL); return TCL_ERROR; } if (Ns_TclGetOpenFd(interp, Tcl_GetString(objv[1]), (when & NS_SOCK_WRITE), (int *) &socket) != TCL_OK) { return TCL_ERROR; } socket = ns_sockdup(socket); if (socket == INVALID_SOCKET) { Tcl_AppendResult(interp, "dup failed: ", SockError(interp), NULL); return TCL_ERROR; } cbPtr = ns_malloc(sizeof(SockCallback) + strlen(Tcl_GetString(objv[2]))); cbPtr->server = thisServer->server; cbPtr->when = when; strcpy(cbPtr->script, Tcl_GetString(objv[2])); if (Ns_SockCallback(socket, SSLSockCallbackProc, cbPtr, when | NS_SOCK_EXIT) != NS_OK) { interp->result = "could not register callback"; ns_sockclose(socket); ns_free(cbPtr); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * NsTclOpenSSLSockListenCallbackObjCmd -- * * Listen on a socket and register a callback to run when * connections arrive. * * Results: * Tcl result. * * Side effects: * Will register a callback and listen on a socket. * *---------------------------------------------------------------------- */ int NsTclOpenSSLSockListenCallbackObjCmd(ClientData arg, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Server *thisServer = (Server *) arg; SockListenCallback *lcbPtr = NULL; int port = 0; char *addr = NULL; /* * ns_openssl_socklistencallback host port script * ns_openssl_socklistencallback host port script sslcontext */ if (objc != 4 && objc != 5) { Tcl_WrongNumArgs(interp, 1, objv, "address port script ?sslcontext?"); return TCL_ERROR; } if (Tcl_GetIntFromObj(interp, objv[2], &port) != TCL_OK) { return TCL_ERROR; } addr = Tcl_GetString(objv[1]); if (STREQ(addr, "*")) { addr = NULL; } lcbPtr = ns_malloc(sizeof(SockListenCallback)); lcbPtr->server = thisServer->server; lcbPtr->script = strdup(Tcl_GetString(objv[3])); if (objc == 5) { lcbPtr->sslcontext = Ns_OpenSSLServerSSLContextGet(thisServer->server, (char *) Tcl_GetString(objv[5])); } else { lcbPtr->sslcontext = NsOpenSSLContextServerDefaultGet(thisServer->server); } /* XXX check lcbPtr->sslcontext: if NULL, fail with error message !!! */ #if 0 if (sslcontext == NULL) { Tcl_SetResult(interp, "failed to use either named or default client SSL context", TCL_STATIC); return TCL_ERROR; } #endif if (Ns_SockListenCallback(addr, port, SSLSockListenCallbackProc, lcbPtr) != NS_OK) { Ns_Log(Error, "NsTclOpenSSLSockListenCallbackCmd: COULD NOT REGISTER CALLBACK"); Tcl_SetResult(interp, "could not register callback", TCL_STATIC); ns_free(lcbPtr); return TCL_ERROR; } return TCL_OK; } /* *---------------------------------------------------------------------- * * EnterSock, EnterDup -- * * Append a socket handle to the tcl result and register its * channel. * * Results: * Tcl result. * * Side effects: * Will create channel, append handle to result. * *---------------------------------------------------------------------- */ static int EnterSock(Tcl_Interp *interp, SOCKET sock) { Tcl_Channel chan = NULL; chan = Tcl_MakeTcpClientChannel((ClientData) sock); if (chan == NULL) { Tcl_AppendResult(interp, "could not open socket", NULL); ns_sockclose(sock); return TCL_ERROR; } Tcl_SetChannelOption(interp, chan, "-translation", "binary"); Tcl_RegisterChannel(interp, chan); sprintf(interp->result, "%s", Tcl_GetChannelName(chan)); return TCL_OK; } static int EnterDup(Tcl_Interp *interp, SOCKET sock) { sock = ns_sockdup(sock); if (sock == INVALID_SOCKET) { Tcl_AppendResult(interp, "could not dup socket: ", ns_sockstrerror(ns_sockerrno), NULL); return TCL_ERROR; } return EnterSock(interp, sock); } #if 0 static int EnterDupedSocks(Tcl_Interp *interp, SOCKET sock) { if (EnterSock(interp, sock) != TCL_OK || EnterDup(interp, sock) != TCL_OK) { return TCL_ERROR; } return TCL_OK; } #endif /* *---------------------------------------------------------------------- * * SetResultToX509Name -- * * Set the Tcl interpreter's result to the string form of the * specified X.509 name. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void SetResultToX509Name(Tcl_Interp *interp, X509_NAME *name) { char *string = NULL; string = X509_NAME_oneline(name, NULL, 0); Tcl_SetResult(interp, string, TCL_VOLATILE); OPENSSL_free(string); } /* *---------------------------------------------------------------------- * * SetResultToObjectName -- * * Set the Tcl interpreter's result to the string form of the * specified ASN.1 object name. * * Results: * None. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void SetResultToObjectName(Tcl_Interp *interp, ASN1_OBJECT *obj) { int nid = 0; char *string = NULL; nid = OBJ_obj2nid(obj); if (nid == NID_undef) { Tcl_SetResult(interp, "UNKNOWN", TCL_STATIC); } else { string = (char *) OBJ_nid2ln(nid); if (string == NULL) { Tcl_SetResult(interp, "ERROR", TCL_STATIC); } else { Tcl_SetResult(interp, string, TCL_VOLATILE); } } } /* *---------------------------------------------------------------------- * * ValidTime -- * * Takes an ASN1_UTCTIME value and converts it into a string of * the form "Aug 28 20:00:38 2002 GMT" * * Results: * Pointer to null-terminated string allocated by Tcl_Alloc. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char * ValidTime(ASN1_UTCTIME *tm) { char *result = NULL; BIO *bio = NULL; unsigned int n = 0; if ((bio = BIO_new(BIO_s_mem())) == NULL) { return NULL; } ASN1_UTCTIME_print(bio, tm); n = BIO_pending(bio); result = Tcl_Alloc(n + 1); n = BIO_read(bio, result, (signed int) n); result[n] = '\0'; BIO_free(bio); return result; } /* *---------------------------------------------------------------------- * * PEMCertificate -- * * Retrieves the certificate in PEM format * * Results: * Pointer to null-terminated string that contains the PEM * certificate, allocated by Tcl_Alloc. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char * PEMCertificate(X509 *peercert) { char *result = NULL; BIO *bio = NULL; unsigned int n = 0; if ((bio = BIO_new(BIO_s_mem())) == NULL) { return NULL; } PEM_write_bio_X509(bio, peercert); n = BIO_pending(bio); result = Tcl_Alloc(n + 1); n = BIO_read(bio, result, (signed int) n); result[n] = '\0'; BIO_free(bio); return result; } /* *---------------------------------------------------------------------- * * CreateTclChannel -- * * Dup connection sock and wrap read and write Tcl channels * around them. * * Results: * Tcl result. * * Side effects: * *---------------------------------------------------------------------- */ static int CreateTclChannel(NsOpenSSLConn *sslconn, Tcl_Interp *interp) { ChanInfo *getschan = NULL; ChanInfo *putschan = NULL; Tcl_DString ds; char channelName[16 + TCL_INTEGER_SPACE]; Tcl_DStringInit(&ds); /* * The ns_sock API in AOLserver passes back a separate read and write fds * to work with. In our case, we're using the same socket underneath both, * but to maintain consistency we also create two separate channels and * pass back two separate fds to the caller. */ getschan = ns_calloc(1, sizeof(ChanInfo)); getschan->sslconn = sslconn; putschan = ns_calloc(1, sizeof(ChanInfo)); putschan->sslconn = sslconn; getschan->otherchaninfo = (void *) putschan; putschan->otherchaninfo = (void *) getschan; /* * Set up the read channel. */ getschan->socket = sslconn->socket; sprintf(channelName, "openssl%d", getschan->socket); getschan->chan = Tcl_CreateChannel( &opensslChannelType, channelName, (ClientData) getschan, (TCL_READABLE | TCL_WRITABLE) ); if (getschan->chan == (Tcl_Channel) NULL) { Ns_Log(Error, "%s: %s: could not create new Tcl channel", MODULE, sslconn->server); Tcl_AppendResult (interp, "could not create new Tcl channel", NULL); return TCL_ERROR; } Tcl_SetChannelBufferSize(getschan->chan, BUFSIZ); Tcl_SetChannelOption(interp, getschan->chan, "-translation", "binary"); Tcl_RegisterChannel(interp, getschan->chan); /* * Set up the write channel. */ putschan->socket = ns_sockdup(sslconn->socket); sprintf(channelName, "openssl%d", putschan->socket); putschan->chan = Tcl_CreateChannel( &opensslChannelType, channelName, (ClientData) putschan, (TCL_READABLE | TCL_WRITABLE) ); if (putschan->chan == (Tcl_Channel) NULL) { Ns_Log(Error, "%s: %s: could not create new Tcl channel", MODULE, sslconn->server); Tcl_AppendResult (interp, "could not create new Tcl channel", NULL); return TCL_ERROR; } Tcl_SetChannelBufferSize(putschan->chan, BUFSIZ); Tcl_SetChannelOption(interp, putschan->chan, "-translation", "binary"); Tcl_RegisterChannel(interp, putschan->chan); /* * Append the fd names to the result. */ Tcl_DStringAppendElement(&ds, Tcl_GetChannelName(getschan->chan)); Tcl_DStringAppendElement(&ds, Tcl_GetChannelName(putschan->chan)); //Ns_Log(Debug, "*** CHAN CREATE: %s", Tcl_GetChannelName(getschan->chan)); //Ns_Log(Debug, "*** CHAN CREATE: %s", Tcl_GetChannelName(putschan->chan)); Tcl_DStringResult(interp, &ds); return TCL_OK; } /* *---------------------------------------------------------------------- * * ChanOutputProc -- * * Callback activated by Tcl puts and write commands. Sends data * to the connected system. * * Results: * Tcl result. * * Side effects: * *---------------------------------------------------------------------- */ static int ChanOutputProc(ClientData arg, char *buf, int towrite, int *errorCodePtr) { ChanInfo *chaninfo = (ChanInfo *) arg; int rc = 0; rc = NsOpenSSLConnOp(chaninfo->sslconn->ssl, (void *) buf, towrite, NSOPENSSL_SEND); return rc; } /* *---------------------------------------------------------------------- * * ChanInputProc -- * * Callback activated by Tcl gets and read on the Tcl channel. Reads * data from the connected system. * * Results: * Number of bytes read. * * Side effects: * Places read data into buf, may set errorCodePtr, and adjusts * connection state's read buffer pointer. * *---------------------------------------------------------------------- */ static int ChanInputProc(ClientData arg, char *buf, int bufSize, int *errorCodePtr) { ChanInfo *chaninfo = (ChanInfo *) arg; int rc = 0; rc = NsOpenSSLConnOp(chaninfo->sslconn->ssl, (void *) buf, bufSize, NSOPENSSL_RECV); return rc; } /* *---------------------------------------------------------------------- * * ChanCloseProc -- * * Close down the Tcl channels and clean up the connection state * data. * * Results: * Tcl result. * * Side effects: * Will call functions to shutdown the SSL connection and free all * data associated with the connection. * * Note that this proc is called twice, once for the read channel * and once for the write channel, so we need to check and see if * conn has already been freed. * *---------------------------------------------------------------------- */ static int ChanCloseProc(ClientData arg, Tcl_Interp *interp) { ChanInfo *chaninfo = (ChanInfo *) arg; ChanInfo *otherchaninfo = NULL; //Ns_Log(Debug, "*** CHAN DESTROY: %s", Tcl_GetChannelName(chaninfo->chan)); Tcl_UnregisterChannel(interp, chaninfo->chan); ns_sockclose(chaninfo->socket); chaninfo->socket = INVALID_SOCKET; otherchaninfo = (ChanInfo *) chaninfo->otherchaninfo; if (otherchaninfo->socket == INVALID_SOCKET) { //Ns_Log(Debug, "*** SSL DESTROY"); ns_free(otherchaninfo); NsOpenSSLConnDestroy(chaninfo->sslconn); ns_free(chaninfo); } return TCL_OK; } /* *---------------------------------------------------------------------- * * ChanFlushProc -- * * Flush the date in the connection buffers. * * Results: * TCL_OK. * * Side effects: * Will open a connection and register two Tcl channels. * *---------------------------------------------------------------------- */ static int ChanFlushProc (ClientData arg) { ChanInfo *chaninfo = (ChanInfo *) arg; //Ns_Log(Debug, "ChanFlushProc %s", Tcl_GetChannelName(chaninfo->chan)); NsOpenSSLConnFlush(chaninfo->sslconn); return TCL_OK; } /* *---------------------------------------------------------------------- * * ChanGetHandleProc -- * * Return the read or write socket. * * Results: * TCL_OK * * Side effects: * * *---------------------------------------------------------------------- */ static int ChanGetHandleProc(ClientData arg, int direction, ClientData *handlePtr) { ChanInfo *chaninfo = (ChanInfo *) arg; *handlePtr = (ClientData) chaninfo->sslconn->socket; return TCL_OK; } /* *---------------------------------------------------------------------- * * ChanWatchProc -- * * Callback proc used by the Tcl channels. Doesn't do anything for us at * the moment, but it is still required to be defined. Not having it * causes a segfault when Tcl tries to work with it. Go read the * Tcl_CreateChannel man page for Tcl 8.3+. * * Results: * None. * *---------------------------------------------------------------------- */ static void ChanWatchProc(ClientData arg, int mask) { return; } /* *---------------------------------------------------------------------- * * SSLSockListenCallbackProc -- * * This is the C wrapper callback that is registered from * ns_openssl_socklistencallback. * * Results: * NS_TRUE or NS_FALSE on error * * Side effects: * Will run Tcl script. * *---------------------------------------------------------------------- */ static int SSLSockListenCallbackProc(SOCKET sock, void *arg, int why) { SockListenCallback *lcbPtr = arg; NsOpenSSLConn *sslconn = NULL; Tcl_Interp *interp = NULL; Tcl_DString script; Tcl_Obj *listPtr = NULL; Tcl_Obj **objv = NULL; int result = TCL_ERROR; int objc = 0; //Ns_Log(Debug, "*** SockListenCallbackProc running"); interp = Ns_TclAllocateInterp(lcbPtr->server); sslconn = Ns_OpenSSLSockAccept(sock, lcbPtr->sslcontext); if (sslconn == NULL) { Tcl_AppendResult(interp, "SSL accept failed \"", NULL); return TCL_ERROR; } //Ns_Log(Debug, "*** SockListenCallbackProc running 2"); result = CreateTclChannel(sslconn, interp); if (result == TCL_OK) { //Ns_Log(Debug, "*** SockListenCallbackProc running 3"); listPtr = Tcl_GetObjResult(interp); if (Tcl_ListObjGetElements(interp, listPtr, &objc, &objv) == TCL_OK && objc == 1) { Tcl_DStringInit(&script); Tcl_DStringAppend(&script, lcbPtr->script, -1); Tcl_DStringAppendElement(&script, Tcl_GetString(objv[0])); result = Tcl_EvalEx(interp, script.string, script.length, 0); Tcl_DStringFree(&script); } //Ns_Log(Debug, "*** SockListenCallbackProc running 4"); } if (result != TCL_OK) { //Ns_Log(Debug, "*** SockListenCallbackProc running 5"); Ns_TclLogError(interp); } Ns_TclDeAllocateInterp(interp); return NS_TRUE; } /* *---------------------------------------------------------------------- * * AppendReadyFiles -- * * Find files in an fd_set that are selected and append them to * the tcl result, and also an optional passed-in dstring. * * Results: * None. * * Side effects: * Ready files will be appended to pds if not null, and also * interp->result. * *---------------------------------------------------------------------- */ static void AppendReadyFiles (Tcl_Interp * interp, fd_set * setPtr, int write, char *flist, Tcl_DString * dsPtr) { int fargc = 0; char **fargv = NULL; SOCKET socket = INVALID_SOCKET; Tcl_DString ds; Tcl_DStringInit(&ds); if (dsPtr == NULL) { dsPtr = &ds; } Tcl_SplitList(interp, flist, &fargc, &fargv); while (fargc--) { Ns_TclGetOpenFd(interp, fargv[fargc], write, (int *) &socket); if (FD_ISSET(socket, setPtr)) { Tcl_DStringAppendElement(dsPtr, fargv[fargc]); } } /* * Append the ready files to the tcl interp. */ Tcl_AppendElement(interp, dsPtr->string); ckfree((char *) fargv); Tcl_DStringFree(&ds); } /* *---------------------------------------------------------------------- * * GetSet -- * * Take a Tcl list of files and set bits for each in the list in * an fd_set. * * Results: * Tcl result. * * Side effects: * Will set bits in fd_set. ppset may be NULL on error, or * a valid fd_set on success. Max fd will be returned in *maxPtr. * *---------------------------------------------------------------------- */ static int GetSet(Tcl_Interp * interp, char *flist, int write, fd_set ** setPtrPtr, fd_set * setPtr, SOCKET * maxPtr) { SOCKET socket = INVALID_SOCKET; int fargc = 0; char **fargv = NULL; int status = TCL_ERROR; if (Tcl_SplitList(interp, flist, &fargc, &fargv) != TCL_OK) { return TCL_ERROR; } if (fargc == 0) { /* * Tcl_SplitList failed, so abort. */ ckfree((char *) fargv); *setPtrPtr = NULL; return TCL_OK; } else { *setPtrPtr = setPtr; } FD_ZERO(setPtr); status = TCL_OK; /* * Loop over each file, try to get its FD, and set the bit in * the fd_set. */ while (fargc--) { if (Ns_TclGetOpenFd(interp, fargv[fargc], write, (int *) &socket) != TCL_OK) { status = TCL_ERROR; break; } if (socket > *maxPtr) { *maxPtr = socket; } FD_SET(socket, setPtr); } ckfree((char *) fargv); return status; } /* *---------------------------------------------------------------------- * * SSLSockCallbackProc -- * * Callback that is registered from ns_sockcallback. * * Results: * NS_TRUE or NS_FALSE on error * * Side effects: * Will run Tcl script. * *---------------------------------------------------------------------- */ static int SSLSockCallbackProc(SOCKET sock, void *arg, int why) { SockCallback *cbPtr = arg; Tcl_Interp *interp = NULL; /* XXX not initialized */ Tcl_DString script; char *w = NULL; int status = TCL_ERROR; if (why != NS_SOCK_EXIT || (cbPtr->when & NS_SOCK_EXIT)) { interp = Ns_TclAllocateInterp(cbPtr->server); status = EnterDup(interp, sock); if (status == TCL_OK) { Tcl_DStringInit (&script); Tcl_DStringAppend (&script, cbPtr->script, -1); Tcl_DStringAppendElement (&script, interp->result); if (why == NS_SOCK_READ) { w = "r"; } else if (why == NS_SOCK_WRITE) { w = "w"; } else if (why == NS_SOCK_EXCEPTION) { w = "e"; } else { w = "x"; } Tcl_DStringAppendElement(&script, w); status = Tcl_EvalEx(interp, script.string, script.length, 0); Tcl_DStringFree(&script); } if (status != TCL_OK) { Ns_TclLogError(interp); } else if (!STREQ(interp->result, "1")) { why = NS_SOCK_EXIT; } Ns_TclDeAllocateInterp(interp); } if (why == NS_SOCK_EXIT) { ns_sockclose(sock); ns_free(cbPtr); return NS_FALSE; } return NS_TRUE; } aolserver4-nsopenssl-3.0beta26/defaults.h0000644000175000017500000000265210263172426020300 0ustar frankiefrankie/* * Defaults Settings */ /* Turn this on to show debug info */ //#define DEBUG_NSOPENSSL 1 #define MODULE "nsopenssl" #define MODULE_SHORT "ssl" #define SERVER_ROLE 1 #define CLIENT_ROLE 0 #define DEFAULT_PROTOCOLS "All" #define DEFAULT_CIPHER_LIST SSL_DEFAULT_CIPHER_LIST //#define DEFAULT_CERT_FILE "certificate.pem" //#define DEFAULT_KEY_FILE "key.pem" #define DEFAULT_CA_FILE "ca.pem" #define DEFAULT_CA_DIR "ca" #define DEFAULT_PEER_VERIFY NS_FALSE #define DEFAULT_PEER_VERIFY_DEPTH 3 #define DEFAULT_SESSION_CACHE NS_TRUE #define DEFAULT_SESSION_CACHE_SIZE 128 #define DEFAULT_SESSION_CACHE_TIMEOUT 300 #define DEFAULT_TRACE NS_FALSE #define DEFAULT_TIMEOUT 30 #define DEFAULT_BUFFER_SIZE 16384 #define DEFAULT_SEEDBYTES 1024 #define DEFAULT_MAXBYTES 1024000 #define DEFAULT_SENDWAIT 60 #define DEFAULT_RECVWAIT 60 #define CONFIG_MODULE_DIR "ModuleDir" #define CONFIG_RANDOM_FILE "RandomFile" #define CONFIG_SEEDBYTES "SeedBytes" #define NSOPENSSL_RECV 0 #define NSOPENSSL_SEND 1 #define CORE 0 #define TCLAPI 1 aolserver4-nsopenssl-3.0beta26/x509.c0000644000175000017500000002234610037107353017167 0ustar frankiefrankie/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. * * Copyright (C) 2000-2003 Scott S. Goodwin * * Module originally written by Stefan Arentz. Early contributions made by * Freddie Mendoze and Rob Mayoff. */ /* * x509.c -- * * Implements functions that work with X509 certificates. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsopenssl/x509.c,v 1.1 2004/04/14 01:07:55 scottg Exp $, compiled: " __DATE__ " " __TIME__; #include "nsopenssl.h" /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertGetFromSSL -- * * Return the X509 certificate for the given SSL instance. * * Results: * A pointer to an X509 certificate or NULL if there is no certificate. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern X509 * Ns_OpenSSLX509CertGetFromSSL(SSL *ssl) { return SSL_get_peer_certificate(ssl); } /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertPEM -- * * Return the PEM-formatted certificate. * * Results: * A pointer to the PEM-formatted certificate. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern char * Ns_OpenSSLX509CertPEM(X509 *certificate) { char *result = NULL; BIO *bio = NULL; unsigned int n = 0; bio = BIO_new(BIO_s_mem()); PEM_write_bio_X509(bio, certificate); n = BIO_pending(bio); result = Tcl_Alloc(n + 1); n = BIO_read(bio, result, (signed int) n); result[n] = '\0'; BIO_free(bio); return result; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertVerify -- * * Determine if the certificate associated with the given SSL instance is * valid. You only call this function if you already KNOW that a * certificate exists. From the SSL_get_verify_result() man page: If no * peer certificate was presented, the returned result code is X509_V_OK. * This is because no verification error occurred, it does however not * indicate success. SSL_get_verify_result() is only useful in connection * with SSL_get_peer_certificate(3). * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLX509CertVerify(SSL *ssl) { switch(SSL_get_verify_result(ssl)) { case X509_V_OK: return NS_TRUE; break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: Ns_Log(Warning, "X509 certificate: unable to get issuer certificate"); break; case X509_V_ERR_UNABLE_TO_GET_CRL: Ns_Log(Warning, "X509 certificate: unable to get CRL"); break; case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: Ns_Log(Warning, "X509 certificate: unable to decrypt certificate signature"); break; case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: Ns_Log(Warning, "X509 certificate: unable to decrypt CRL signature"); break; case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: Ns_Log(Warning, "X509 certificate: unable to decode issuer public key"); break; case X509_V_ERR_CERT_SIGNATURE_FAILURE: Ns_Log(Warning, "X509 certificate: certificate signature failure"); break; case X509_V_ERR_CRL_SIGNATURE_FAILURE: Ns_Log(Warning, "X509 certificate: CRL signature failure"); break; case X509_V_ERR_CERT_NOT_YET_VALID: Ns_Log(Warning, "X509 certificate: certificate not yet valid"); break; case X509_V_ERR_CERT_HAS_EXPIRED: Ns_Log(Warning, "X509 certificate: certificate has expired"); break; case X509_V_ERR_CRL_NOT_YET_VALID: Ns_Log(Warning, "X509 certificate: CRL not yet valid"); break; case X509_V_ERR_CRL_HAS_EXPIRED: Ns_Log(Warning, "X509 certificate: CRL has expired"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: Ns_Log(Warning, "X509 certificate: error in certificate 'not before' field"); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: Ns_Log(Warning, "X509 certificate: error in certificate 'not after' field"); break; case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: Ns_Log(Warning, "X509 certificate: error in CRL 'last update' field"); break; case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: Ns_Log(Warning, "X509 certificate: error in CRL 'next update' field"); break; case X509_V_ERR_OUT_OF_MEM: Ns_Log(Warning, "X509 certificate: out of memory"); break; case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: Ns_Log(Warning, "X509 certificate: depth zero self-signed certificate"); break; case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: Ns_Log(Warning, "X509 certificate: self-signed certificate in chain"); break; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: Ns_Log(Warning, "X509 certificate: unable to get issuer certificate locally"); break; case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: Ns_Log(Warning, "X509 certificate: unable to verify leaf signature"); break; case X509_V_ERR_CERT_CHAIN_TOO_LONG: Ns_Log(Warning, "X509 certificate: certificate chain too long"); break; case X509_V_ERR_CERT_REVOKED: Ns_Log(Warning, "X509 certificate: certificate revoked"); break; case X509_V_ERR_APPLICATION_VERIFICATION: Ns_Log(Warning, "X509 certificate: application verification"); break; default: Ns_Log(Error, "X509 certificate: unknown result from SSL certificate verification result"); break; } return NS_FALSE; } #if 0 /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertSubject -- * * Return the subject field of the given certificate. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLX509CertSubject(SSL *ssl) { if (peercert != NULL) { SetResultToX509Name(interp, X509_get_subject_name(peercert)); } return NS_TRUE; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertNotBefore -- * * Return the 'not before' date of the certificate. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLX509CertNotBefore(SSL *ssl) { return NS_TRUE; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertNotAfter -- * * Return the 'not after' date of the certificate. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLX509CertNotAfter(SSL *ssl) { return NS_TRUE; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertSerial -- * * Return the serial number of the given certificate. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLX509CertSerial(SSL *ssl) { return NS_TRUE; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLX509CertVersion -- * * Return the version of the given certificate. * * Results: * NS_TRUE or NS_FALSE. * * Side effects: * None. * *---------------------------------------------------------------------- */ extern int Ns_OpenSSLX509CertVersion(SSL *ssl) { return NS_TRUE; } #endif aolserver4-nsopenssl-3.0beta26/nsd.tcl0000644000175000017500000002053210263172426017605 0ustar frankiefrankie############################################################################### # # Sample configurations for nsopenssl # # This file contains three sets of configuration examples, one for each of 1.x, # 2.x and 3.x versions of nsopenssl. # # Parameters followed by the comment "# default" show the default values if you # don't specify that parameter. # # Parameters followed by the comment "# mandatory" indicate that you must # specify a value for that parameter. # # $Header: /cvsroot/aolserver/nsopenssl/nsd.tcl,v 1.13 2004/10/27 16:40:08 dossy Exp $ ############################################################################### # # nsopenssl version 3.x configuration # # # Global nsopenssl settings # ns_section "ns/module/nsopenssl" ns_param RandomFile /some/file ns_param SeedBytes 1024 # # Virtual Server specific nsopenssl configurations # # SSL contexts. Each SSL context is a template that SSL connections are created # from. A single SSL context may be used by multiple drivers, sockservers and # sockclients. ns_section "ns/server/${servername}/module/nsopenssl/sslcontexts" ns_param users "SSL context used for regular user access" ns_param admins "SSL context used for administrator access" ns_param client "SSL context used for outgoing script socket connections" # We explicitly tell the server which SSL contexts to use as defaults when an # SSL context is not specified for a particular client or server SSL # connection. Driver connections do not use defaults; they must be explicitly # specificied in the driver section. The Tcl API will use the defaults as there # is currently no provision to specify which SSL context to use for a # particular connection via an ns_openssl Tcl command. ns_section "ns/server/${servername}/module/nsopenssl/defaults" ns_param server users ns_param client client ns_section "ns/server/${servername}/module/nsopenssl/sslcontext/users" ns_param Role server #ns_param ModuleDir /path/to/dir ns_param CertFile server/server.crt ns_param KeyFile server/server.key ns_param CADir ca-client/dir ns_param CAFile ca-client/ca-client.crt ns_param Protocols "SSLv3, TLSv1" ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+EXP" ns_param PeerVerify false ns_param PeerVerifyDepth 3 ns_param Trace false ns_section "ns/server/${servername}/module/nsopenssl/sslcontext/admins" ns_param Role server #ns_param ModuleDir /path/to/dir ns_param CertFile server/server.crt ns_param KeyFile server/server.key ns_param CADir ca-client/dir ns_param CAFile ca-client/ca-client.crt ns_param Protocols "All" ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+EXP" ns_param PeerVerify false ns_param PeerVerifyDepth 3 ns_param Trace false ns_section "ns/server/${servername}/module/nsopenssl/sslcontext/client" ns_param Role client #ns_param ModuleDir /path/to/dir ns_param CertFile client/client.crt ns_param KeyFile client/client.key ns_param CADir ca-server/dir ns_param CAFile ca-server/ca-server.crt ns_param Protocols "SSLv2, SSLv3, TLSv1" ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param PeerVerify false ns_param PeerVerifyDepth 3 ns_param Trace false # SSL drivers. Each driver defines a port to listen on and an explitictly named # SSL context to associate with it. Note that you can now have multiple driver # connections within a single virtual server, which can be tied to different # SSL contexts. Isn't that cool? ns_section "ns/server/${servername}/module/nsopenssl/ssldrivers" ns_param users "Driver for regular user access" ns_param admins "Driver for administrator access" ns_section "ns/server/${servername}/module/nsopenssl/ssldriver/users" ns_param sslcontext users ns_param port $httpsport_users ns_param hostname $hostname ns_param address $address ns_section "ns/server/${servername}/module/nsopenssl/ssldriver/admins" ns_param sslcontext admins ns_param port $httpsport_admins ns_param hostname $hostname ns_param address $address # # Modules to load # ns_section "ns/server/${servername}/modules" ... ns_param nsopenssl ${bindir}/nsopenssl${ext} ############################################################################### # # nsopenssl version 2.x configuration # ns_section "ns/server/${servername}/module/nsopenssl" # NSD-driven connections: ns_param ServerPort $httpsport ns_param ServerHostname $hostname ns_param ServerAddress $address ns_param ServerCertFile certfile.pem ns_param ServerKeyFile keyfile.pem ns_param ServerProtocols "SSLv2, SSLv3, TLSv1" ns_param ServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param ServerSessionCache false ns_param ServerSessionCacheID 1 ns_param ServerSessionCacheSize 128 ns_param ServerSessionCacheTimeout 300 ns_param ServerPeerVerify true ns_param ServerPeerVerifyDepth 3 ns_param ServerCADir ca ns_param ServerCAFile ca.pem ns_param ServerTrace false # For listening and accepting SSL connections via Tcl/C API: ns_param SockServerCertFile certfile.pem ns_param SockServerKeyFile keyfile.pem ns_param SockServerProtocols "SSLv2, SSLv3, TLSv1" ns_param SockServerCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockServerSessionCache false ns_param SockServerSessionCacheID 2 ns_param SockServerSessionCacheSize 128 ns_param SockServerSessionCacheTimeout 300 ns_param SockServerPeerVerify true ns_param SockServerPeerVerifyDepth 3 ns_param SockServerCADir internal_ca ns_param SockServerCAFile internal_ca.pem ns_param SockServerTrace false # Outgoing SSL connections ns_param SockClientCertFile clientcertfile.pem ns_param SockClientKeyFile clientkeyfile.pem ns_param SockClientProtocols "SSLv2, SSLv3, TLSv1" ns_param SockClientCipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SockClientSessionCache false ns_param SockClientSessionCacheID 3 ns_param SockClientSessionCacheSize 128 ns_param SockClientSessionCacheTimeout 300 ns_param SockClientPeerVerify true ns_param SockServerPeerVerifyDepth 3 ns_param SockClientCADir ca ns_param SockClientCAFile ca.pem ns_param SockClientTrace false # Typically where you store your certificates # Defaults to $AOLSERVER/servers/${servername}/modules/nsopenssl ns_param ModuleDir /path/to/dir # OpenSSL library support: ns_param RandomFile /some/file ns_param SeedBytes 1024 ############################################################################### # # DEPRECATED: nsopenssl 1.x configuration # ns_section "ns/server/${servername}/module/nsopenssl" ns_param address $address ns_param port $httpsport ns_param hostname $hostname ns_param CertFile certfile.pem ns_param KeyFile keyfile.pem ns_param Protocol All #ns_param Protocol SSLv2 #ns_param Protocol SSLv3 #ns_param Protocol TLSv1 ns_param CipherSuite "ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP" ns_param SessionCache true ns_param SessionCacheSize 128 ns_param SessionCacheTimeout 300 ns_param ClientVerify true ns_param CADir ca ns_param CAFile ca.pem ns_param Trace false ns_param RandomFile /some/file ns_section "ns/server/${servername}/modules" ns_param nsopenssl ${bindir}/nsopenssl${ext} aolserver4-nsopenssl-3.0beta26/sslcontext.c0000644000175000017500000014536210537737033020705 0ustar frankiefrankie/* * The contents of this file are subject to the AOLserver Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://aolserver.com. * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and limitations * under the License. * * Alternatively, the contents of this file may be used under the terms * of the GNU General Public License (the "GPL"), in which case the * provisions of GPL are applicable instead of those above. If you wish * to allow use of your version of this file only under the terms of the * GPL and not to allow others to use your version of this file under the * License, indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by the GPL. * If you do not delete the provisions above, a recipient may use your * version of this file under either the License or the GPL. * * Copyright (C) 2000-2003 Scott S. Goodwin * * Module originally written by Stefan Arentz. Early contributions made by * Freddie Mendoze and Rob Mayoff. * * Portions created by AOL are Copyright (C) 1999 America Online, Inc. * All Rights Reserved. */ /* * sslcontext.c -- * * Manages SSL context state structures. */ static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/nsopenssl/sslcontext.c,v 1.11 2006/03/28 17:56:30 apiskors Exp $, compiled: " __DATE__ " " __TIME__; #include "nsopenssl.h" Tcl_HashTable NsOpenSSLServers; RSA *rsa_512, *rsa_1024; static RSA *IssueTmpRSAKey(SSL *ssl, int export, int keylen); static char *SSLContextSessionCacheIdNew(char *server); static void OpenSSLTrace(SSL *ssl, int where, int rc); static void SSLContextCAFileInit(NsOpenSSLContext *sslcontext); static void SSLContextCADirInit(NsOpenSSLContext *sslcontext); static int SSLContextCiphersInit(NsOpenSSLContext *sslcontext); static int SSLContextProtocolsInit(NsOpenSSLContext *sslcontext); static int SSLContextCertFileInit(NsOpenSSLContext *sslcontext); static void SSLContextPeerVerifyInit(NsOpenSSLContext *sslcontext); static void SSLContextPeerVerifyDepthInit(NsOpenSSLContext *sslcontext); static void SSLContextSessionCacheInit(NsOpenSSLContext *sslcontext); static void SSLContextTraceInit(NsOpenSSLContext *sslcontext); static int PeerVerifyCallback(int preverify_ok, X509_STORE_CTX *x509_ctx); /* *---------------------------------------------------------------------- * * NsOpenSSLContextCreate -- * * Create a new NsOpenSSLContext structure * * Results: * Pointer to resulting struct or NULL on error * * Side effects: * Memory is allocated. All structure values are set to defaults. * These defaults can be overridden by calls to the * NsOpenSSLContext* functions. * *---------------------------------------------------------------------- */ NsOpenSSLContext * NsOpenSSLContextCreate(char *server, char *name) { NsOpenSSLContext *sslcontext = NULL; Ns_DString ds; char *lockName = NULL; Ns_DStringInit(&ds); /* * Check to see if the context name is already in use. The name of an SSL * context must be unique within a virtual server. */ if (Ns_OpenSSLServerSSLContextGet(server, name) != NULL) { Ns_Log(Error, "%s (%s): SSL context with name %s already defined", MODULE, server, name); return NULL; } /* * Create the SSL context. */ sslcontext = ns_calloc(1, sizeof(*sslcontext)); Ns_MutexInit(&sslcontext->lock); Ns_DStringPrintf(&ds, "ctx:%s", name); lockName = Ns_DStringExport(&ds); Ns_MutexSetName2(&sslcontext->lock, MODULE_SHORT, lockName); Ns_DStringTrunc(&ds, 0); ns_free(lockName); lockName = NULL; /* * Set SSL context initial values. */ sslcontext->server = server; sslcontext->name = name; sslcontext->initialized = NS_FALSE; sslcontext->refcnt = 0; sslcontext->peerVerify = DEFAULT_PEER_VERIFY; sslcontext->peerVerifyDepth = DEFAULT_PEER_VERIFY_DEPTH; sslcontext->protocols = DEFAULT_PROTOCOLS; sslcontext->cipherSuite = DEFAULT_CIPHER_LIST; sslcontext->sessionCache = DEFAULT_SESSION_CACHE; sslcontext->sessionCacheSize = DEFAULT_SESSION_CACHE_SIZE; sslcontext->sessionCacheTimeout = DEFAULT_SESSION_CACHE_TIMEOUT; sslcontext->trace = DEFAULT_TRACE; sslcontext->bufsize = DEFAULT_BUFFER_SIZE; sslcontext->timeout = DEFAULT_TIMEOUT; sslcontext->sessionCacheId = SSLContextSessionCacheIdNew(server); Ns_HomePath(&ds, "servers", server, "modules", MODULE, NULL); sslcontext->moduleDir = Ns_DStringExport(&ds); Ns_DStringTrunc(&ds, 0); //Ns_HomePath(&ds, "servers", server, "modules", MODULE, DEFAULT_CERT_FILE, NULL); //sslcontext->certFile = Ns_DStringExport(&ds); sslcontext->certFile = NULL; //Ns_DStringTrunc(&ds, 0); //Ns_HomePath(&ds, "servers", server, "modules", MODULE, DEFAULT_KEY_FILE, NULL); //sslcontext->keyFile = Ns_DStringExport(&ds); sslcontext->keyFile = NULL; //Ns_DStringTrunc(&ds, 0); Ns_HomePath(&ds, "servers", server, "modules", MODULE, DEFAULT_CA_FILE, NULL); sslcontext->caFile = Ns_DStringExport(&ds); Ns_DStringTrunc(&ds, 0); Ns_HomePath(&ds, "servers", server, "modules", MODULE, DEFAULT_CA_DIR, NULL); sslcontext->caDir = Ns_DStringExport(&ds); Ns_DStringTrunc(&ds, 0); Ns_DStringFree(&ds); return sslcontext; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextDestroy -- * * Destroy an NsOpenSSLContext structure * * Results: * NS_OK or NS_ERROR * * Side effects: * Memory is deallocated. * *---------------------------------------------------------------------- */ int NsOpenSSLContextDestroy(char *server, NsOpenSSLContext *sslcontext) { ns_free(sslcontext->certFile); ns_free(sslcontext->keyFile); ns_free(sslcontext->caFile); ns_free(sslcontext->caDir); ns_free(sslcontext); #if 0 /* XXX REMOVE THE CONTEXT FROM THE SERVER STATE */ Ns_OpenSSLServerContextRemove(); #endif return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextInit -- * * Initialize an SSL Context. This runs all of the SSL_CTX calls to * create the SSL instance template. This template is used to create the * SSL objects for each connection. * * Results: * NS_OK or NS_ERROR * * Side effects: * Marks the SSL Context as 'read-only'; no changes can be made to the * SSL Context after this point unless you explicitly call * NsOpenSSLContextRelease. * *---------------------------------------------------------------------- */ /* XXX move most critical stuff to top of this func (i.e. cert doesn't load, * XXX doesn't matter what else is done */ int NsOpenSSLContextInit(char *server, NsOpenSSLContext *sslcontext) { if (sslcontext == NULL) { Ns_Log(Error, "%s (%s): SSL context is NULL", MODULE, server); return NS_ERROR; } if (!STREQ(server, sslcontext->server)) { Ns_Log(Error, "%s (%s): SSL context server field (%s) does not match the virtual server name", MODULE, server, sslcontext->server); return NS_ERROR; } /* * Initialize the SSL_CTX based on the role this context will play. */ if (sslcontext->role) { sslcontext->sslctx = SSL_CTX_new(SSLv23_server_method()); } else { sslcontext->sslctx = SSL_CTX_new(SSLv23_client_method()); } if (sslcontext->sslctx == NULL) { /* XXX FAILURE: clean up and then free the struct */ Ns_Log(Error, "%s (%s): OpenSSL failed to create new SSL_CTX structure", MODULE, server); return NS_ERROR; } /* XXX this is always over-ridden by SSL_set_app_data */ #if 0 /* Allows us to get context struct from within OpenSSL callbacks */ SSL_CTX_set_app_data(sslcontext->sslctx, sslcontext); #endif /* Enable SSL bug compatibility */ SSL_CTX_set_options(sslcontext->sslctx, SSL_OP_ALL); /* This apparently prevents some sort of DH attack */ SSL_CTX_set_options(sslcontext->sslctx, SSL_OP_SINGLE_DH_USE); /* Temporary key callback required for 40-bit export browsers */ SSL_CTX_set_tmp_rsa_callback(sslcontext->sslctx, IssueTmpRSAKey); /* * Failure in one of these will cause SSL context to be left uninitialized. */ /* * WARNING!: InitKeyFile *must* be called before InitCertFile; not doing so * will cause subsequent calls to InitCertFile to fail with File Not Found * error if you're using the same certificate and key for multiple driver * instances. I believe this is a bug in OpenSSL, as the error returned * comes from that library after the SSL_CTX_use_certificate_chain_file * call. */ if ( SSLContextCiphersInit(sslcontext) == NS_ERROR || SSLContextProtocolsInit(sslcontext) == NS_ERROR || SSLContextCertFileInit(sslcontext) == NS_ERROR ) { return NS_ERROR; } /* * Peer verify initialization must come before CA file and directory * initialization. */ SSLContextPeerVerifyDepthInit(sslcontext); SSLContextPeerVerifyInit(sslcontext); SSLContextCAFileInit(sslcontext); SSLContextCADirInit(sslcontext); SSLContextSessionCacheInit(sslcontext); SSLContextTraceInit(sslcontext); /* * We succeeded in initializing the context. We now have an OpenSSL SSL_CTX * structure we can use to create SSL connections. */ sslcontext->initialized = 1; return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextRelease -- * * Release an SSL Context so you can modify it. * * Results: * NS_OK or NS_ERROR * * Side effects: * An SSL Context that has a refcnt > 0 won't be released because refcnt * conns are currently using the structure. Once released, the SSL * Context can't be used for connections again until * NsOpenSSLContextInit() is called to (re-)initialize the SSL_CTX * structure inside of it: this would be bad if you release the context * used for incoming conns to your site. * *---------------------------------------------------------------------- */ #if 0 int NsOpenSSLContextRelease(char *server, NsOpenSSLContext *sslcontext) { if (sslcontext->readonly) { Ns_Log(Error, "%s (%s): attempting to modify a read-only SSL context", MODULE, server); return NS_ERROR; } /* XXX lock */ if (sslcontext->refcnt > 0) { Ns_Log(Error, "%s (%s): attempted to release SSL context '%s' while still in use by active connections", MODULE, server, sslcontext->name); return NS_ERROR; } Ns_Log(Warning, "%s (%s): releasing SSL context '%s' to be writeable", MODULE, server, sslcontext->name); sslcontext->readonly = NS_FALSE; /* XXX unlock */ return NS_OK; } #endif /* *---------------------------------------------------------------------- * * NsOpenSSLContextRoleSet -- * * Set the role (either client or server) * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextRoleSet(char *server, NsOpenSSLContext *sslcontext, char *role) { Ns_MutexLock(&sslcontext->lock); if (STREQ(role, "client")) { sslcontext->role = 0; } else if (STREQ(role, "server")) { sslcontext->role = 1; } else { Ns_Log(Error, "%s (%s): illegal SSL context role: '%s'", MODULE, server, role); return NS_ERROR; } Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextRoleGet -- * * Get the role (either client or server) * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextRoleGet(char *server, NsOpenSSLContext *sslcontext) { Ns_MutexLock(&sslcontext->lock); if (sslcontext->role == 0) { return "client"; } else if (sslcontext->role == 1) { return "server"; } else { return "undefined"; } Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextModuleDirSet -- * * Set the module directory for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextModuleDirSet(char *server, NsOpenSSLContext *sslcontext, char *moduleDir) { Ns_MutexLock(&sslcontext->lock); sslcontext->moduleDir = moduleDir; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextModuleDirGet -- * * Get the module directory for a particular SSL context * * Results: * String pointer; might be NULL * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextModuleDirGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->moduleDir; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCertFileSet -- * * Sets and loads the specified certificate for the given SSL context. * You MUST load the certificate before you attempt to load the private * key. The certificate must be in PEM format. You can put the * certificate chain in the same file: simply append the CA certs to the * end of your certificate file and they'll be passed to the client at * connection time. If no certs are appended, no cert chain will be * passed to the client. * * Warning: you should have already set the context's moduleDir if you * don't want the default. Alternatively, the certFile can be an absolute * path. If it is a relative path, that path will be prepended by the * whatever the moduleDir parameter is set to in your nsd.tcl file, or by * the default moduleDir path. * * Results: * NS_OK or NS_ERROR * * Side effects: * Note that moduleDir must already be set before this call. It is * guaranteed to be set to the default location already. * *---------------------------------------------------------------------- */ int NsOpenSSLContextCertFileSet(char *server, NsOpenSSLContext *sslcontext, char *certFile) { Ns_DString ds; Ns_DStringInit(&ds); Ns_MutexLock(&sslcontext->lock); sslcontext->certFile = ns_strdup(certFile); if (!Ns_PathIsAbsolute(sslcontext->certFile)) { Ns_MakePath(&ds, sslcontext->moduleDir, sslcontext->certFile, NULL); sslcontext->certFile = Ns_DStringExport(&ds); Ns_DStringFree(&ds); } Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCertFileGet -- * * Get the certificate pathname for a particular SSL context * * Results: * String pointer; might be NULL * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextCertFileGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->certFile; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextKeyFileSet -- * * Set the private key pathname for a particular SSL context, * load the key and validate that it works with the certificate. * The key MUST NOT be passphrase-protected. * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextKeyFileSet(char *server, NsOpenSSLContext *sslcontext, char *keyFile) { Ns_DString ds; Ns_DStringInit(&ds); Ns_MutexLock(&sslcontext->lock); sslcontext->keyFile = ns_strdup(keyFile); if (!Ns_PathIsAbsolute(sslcontext->keyFile)) { Ns_MakePath(&ds, sslcontext->moduleDir, sslcontext->keyFile, NULL); sslcontext->keyFile = Ns_DStringExport(&ds); Ns_DStringFree(&ds); } Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextKeyFileGet -- * * Get the key pathname for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextKeyFileGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->keyFile; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCipherSuiteSet -- * * Set the cipher suite for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextCipherSuiteSet(char *server, NsOpenSSLContext *sslcontext, char *cipherSuite) { Ns_MutexLock(&sslcontext->lock); sslcontext->cipherSuite = cipherSuite; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCipherSuiteGet -- * * Get the cipher suite string for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextCipherSuiteGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->cipherSuite; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextProtocolsSet -- * * Set the protocols for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextProtocolsSet(char *server, NsOpenSSLContext *sslcontext, char *protocols) { Ns_MutexLock(&sslcontext->lock); sslcontext->protocols = protocols; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextProtocolsGet -- * * Get the protocols for a particular SSL context * * Results: * ???? * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextProtocolsGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->protocols; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCAFileSet -- * * Set the CA file for a particular SSL context and load it. * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ /* XXX change all these to return voids */ int NsOpenSSLContextCAFileSet(char *server, NsOpenSSLContext *sslcontext, char *caFile) { Ns_DString ds; Ns_DStringInit(&ds); Ns_MutexLock(&sslcontext->lock); sslcontext->caFile = ns_strdup(caFile); if (!Ns_PathIsAbsolute(sslcontext->caFile)) { Ns_MakePath(&ds, sslcontext->moduleDir, sslcontext->caFile, NULL); sslcontext->caFile = Ns_DStringExport(&ds); Ns_DStringFree(&ds); } Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCAFileGet -- * * Get the CA file for a particular SSL context * * Results: * String pointer; might be NULL * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextCAFileGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->caFile; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCADirSet -- * * Set the CA directory for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextCADirSet(char *server, NsOpenSSLContext *sslcontext, char *caDir) { Ns_DString ds; Ns_DStringInit(&ds); Ns_MutexLock(&sslcontext->lock); sslcontext->caDir = ns_strdup(caDir); if (!Ns_PathIsAbsolute(sslcontext->caDir)) { Ns_MakePath(&ds, sslcontext->moduleDir, sslcontext->caDir, NULL); sslcontext->caDir = Ns_DStringExport(&ds); Ns_DStringFree(&ds); } Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextCADirGet -- * * Get the CA directory for a particular SSL context * * Results: * String pointer, might be NULL * * Side effects: * None * *---------------------------------------------------------------------- */ char * NsOpenSSLContextCADirGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->caDir; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextPeerVerifySet -- * * Set whether peer verify is on or off for a particular SSL * context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextPeerVerifySet(char *server, NsOpenSSLContext *sslcontext, int peerVerify) { /* XXX handle default case where peerVerify is NULL */ Ns_MutexLock(&sslcontext->lock); sslcontext->peerVerify = peerVerify; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextPeerVerifyGet -- * * Get whether peer verify is on or off for a particular SSL * context * * Results: * NS_TRUE or NS_FALSE * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextPeerVerifyGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->peerVerify; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextPeerVerifyDepthSet -- * * Set the depth that a peer certificate can be chained for * validation purposes for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextPeerVerifyDepthSet(char *server, NsOpenSSLContext *sslcontext, int peerVerifyDepth) { Ns_MutexLock(&sslcontext->lock); sslcontext->peerVerifyDepth = peerVerifyDepth; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextPeerVerifyDepthGet -- * * Get the depth that a peer certificate can be chained for * validation purposes for a particular SSL context * * Results: * Integer * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextPeerVerifyDepthGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->peerVerifyDepth; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextSessionCacheSet -- * * Set whether session caching is on or off for a particular SSL * context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextSessionCacheSet(char *server, NsOpenSSLContext *sslcontext, int sessionCache) { Ns_MutexLock(&sslcontext->lock); sslcontext->sessionCache = sessionCache; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextSessionCacheGet -- * * Get whether session caching is on or off for a particular SSL * context * * Results: * NS_TRUE or NS_FALSE * * Side effects: * None * *---------------------------------------------------------------------- */ /* XXX should I be managing these function calls by passing the name */ /* XXX of the context rather than a pointer to the context itself? */ int NsOpenSSLContextSessionCacheGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->sessionCache; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextSessionCacheSizeSet -- * * Set the size of a session cache for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextSessionCacheSizeSet(char *server, NsOpenSSLContext *sslcontext, int sessionCacheSize) { Ns_MutexLock(&sslcontext->lock); sslcontext->sessionCacheSize = sessionCacheSize; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextSessionCacheSizeGet -- * * Get the size of a session cache for a particular SSL context * * Results: * Integer * * Side effects: * None * *---------------------------------------------------------------------- */ /* XXX should session cache size be limited to size int? */ int NsOpenSSLContextSessionCacheSizeGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->sessionCacheSize; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextSessionCacheTimeoutSet -- * * Set the timeout for cache entries for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextSessionCacheTimeoutSet(char *server, NsOpenSSLContext *sslcontext, int sessionCacheTimeout) { Ns_MutexLock(&sslcontext->lock); sslcontext->sessionCacheTimeout = sessionCacheTimeout; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextSessionCacheTimeoutGet -- * * Get the timeout for cache entries for a particular SSL context * * Results: * Integer * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextSessionCacheTimeoutGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->sessionCacheTimeout; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextTraceSet -- * * Set SSL handshake tracing for a particular SSL context * * Results: * NS_OK or NS_ERROR * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextTraceSet(char *server, NsOpenSSLContext *sslcontext, int trace) { Ns_MutexLock(&sslcontext->lock); sslcontext->trace = trace; Ns_MutexUnlock(&sslcontext->lock); return NS_OK; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextTraceGet -- * * Get SSL handshake tracing for a particular SSL context * * Results: * NS_TRUE or NS_FALSE * * Side effects: * None * *---------------------------------------------------------------------- */ int NsOpenSSLContextTraceGet(char *server, NsOpenSSLContext *sslcontext) { return sslcontext->trace; } /* *---------------------------------------------------------------------- * * NsOpenSSLServerGet -- * * Return the named virtual server's state structure. * * Results: * A pointer to Server struct. * * Side effects: * *---------------------------------------------------------------------- */ Server * NsOpenSSLServerGet(char *server) { Server *thisServer = NULL; Tcl_HashEntry *hPtr = NULL; /* XXX lock */ hPtr = Tcl_FindHashEntry(&NsOpenSSLServers, server); if (hPtr != NULL) { thisServer = Tcl_GetHashValue(hPtr); } /* XXX unlock */ return thisServer; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextAdd -- * * Add an SSL context to a server state info * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ void NsOpenSSLContextAdd(char *server, NsOpenSSLContext *sslcontext) { Server *thisServer = NULL; Tcl_HashEntry *hPtr = NULL; int new = 0; if (sslcontext == NULL) { Ns_Log(Warning, "%s (%s): attempt to add SSL context to server failed", MODULE, server); } else { thisServer = NsOpenSSLServerGet(server); Ns_MutexLock(&thisServer->lock); hPtr = Tcl_CreateHashEntry(&thisServer->sslcontexts, sslcontext->name, &new); if (new) { Tcl_SetHashValue(hPtr, sslcontext); } else { Ns_Log(Error, "%s (%s): duplicate SSL context name: %s", MODULE, server, sslcontext->name); } Ns_MutexUnlock(&thisServer->lock); } return; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextRemove -- * * Remove an SSL context from server state info * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ void NsOpenSSLContextRemove(char *server, NsOpenSSLContext *sslcontext) { Server *thisServer = NULL; Tcl_HashEntry *hPtr = NULL; if (sslcontext == NULL) { return; } thisServer = NsOpenSSLServerGet(server); Ns_MutexLock(&thisServer->lock); hPtr = Tcl_FindHashEntry(&thisServer->sslcontexts, sslcontext->name); if (hPtr != NULL) { Tcl_DeleteHashEntry(hPtr); } Ns_MutexUnlock(&thisServer->lock); return; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLServerSSLContextGet -- * * Get an SSL context from server state info * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ NsOpenSSLContext * Ns_OpenSSLServerSSLContextGet(char *server, char *name) { NsOpenSSLContext *sslcontext = NULL; Server *thisServer = NULL; Tcl_HashEntry *hPtr = NULL; if (name == NULL) { Ns_Log(Error, "%s (%s): attempt to get SSL context with NULL name", MODULE, server); return NULL; } thisServer = NsOpenSSLServerGet(server); Ns_MutexLock(&thisServer->lock); hPtr = Tcl_FindHashEntry(&thisServer->sslcontexts, name); if (hPtr != NULL) { sslcontext = Tcl_GetHashValue(hPtr); } Ns_MutexUnlock(&thisServer->lock); return sslcontext; } /* *---------------------------------------------------------------------- * * NsOpenSSLContextServerDefaultGet -- * * Return the virtual server's default server SSL context. * * Results: * * Side effects: * *---------------------------------------------------------------------- */ NsOpenSSLContext * NsOpenSSLContextServerDefaultGet(char *server) { Server *thisServer = NsOpenSSLServerGet(server); return Ns_OpenSSLServerSSLContextGet(server, thisServer->defaultservercontext); } /* *---------------------------------------------------------------------- * * NsOpenSSLContextClientDefaultGet -- * * Return the virtual server's default client SSL context. * * Results: * * Side effects: * *---------------------------------------------------------------------- */ NsOpenSSLContext * NsOpenSSLContextClientDefaultGet(char *server) { Server *thisServer = NsOpenSSLServerGet(server); return Ns_OpenSSLServerSSLContextGet(server, thisServer->defaultclientcontext); } /* *---------------------------------------------------------------------- * * IssueTmpRSAKey -- * * Give out the temporary key when needed. This is a callback * function used by OpenSSL and is required for 40-bit browsers. * * Results: * Returns a pointer to the server's temporary RSA key. * * Side effects: * None * *---------------------------------------------------------------------- */ static RSA * IssueTmpRSAKey(SSL *ssl, int export, int keylen) { NsOpenSSLConn *sslconn; char *server = "none"; RSA *rsaPtr = NULL; sslconn = (NsOpenSSLConn *) SSL_get_app_data(ssl); if (sslconn != NULL && sslconn->ssldriver != NULL) { server = sslconn->ssldriver->server; } switch (keylen) { case 512: rsaPtr = rsa_512; break; case 1024: rsaPtr = rsa_1024; break; default: Ns_Log(Error, "nsopenssl (%s): unexpected request for a %d-bit temporary RSA key", server, keylen); break; } return rsaPtr; } int NsMakeTmpRSAKey(int keylen) { RSA **rsaPtrPtr; switch (keylen) { case 512: rsaPtrPtr = &rsa_512; break; case 1024: rsaPtrPtr = &rsa_1024; break; default: Ns_Log(Error, "nsopenssl: unexpected request to generate a %d-bit temporary RSA key", keylen); return NS_ERROR; } Ns_Log(Notice, "nsopenssl: generating %d-bit temporary RSA key ...", keylen); *rsaPtrPtr = RSA_generate_key(keylen, RSA_F4, NULL, NULL); return NS_OK; } /* *---------------------------------------------------------------------- * * Ns_OpenSSLSessionCacheIdNew -- * * Generate and return a new session cache id. Because we need each session * cache to have a unique id across the entire application, we prefix the * number with the module name and the name of the virtual server. We check * to ensure that the generated cache id is not greater than * SSL_MAX_SSL_SESSION_ID_LENGTH, which at the time of this writing is 32 * bytes. * * Results: * * Side effects: * *---------------------------------------------------------------------- */ static char * SSLContextSessionCacheIdNew(char *server) { Server *thisServer = NsOpenSSLServerGet(server); Ns_DString ds; char *sessionCacheId = NULL; int id = 0; Ns_DStringInit(&ds); Ns_MutexLock(&thisServer->lock); id = thisServer->nextSessionCacheId; thisServer->nextSessionCacheId++; Ns_MutexUnlock(&thisServer->lock); Ns_DStringPrintf(&ds, "%s:%s:%d", MODULE, server, id); if (Ns_DStringLength(&ds) > SSL_MAX_SSL_SESSION_ID_LENGTH) { Ns_Log(Error, "%s (%s): session cache id generated is too big; truncating", MODULE, server); Ns_DStringTrunc(&ds, 0); Ns_DStringPrintf(&ds, "%s:%d", server, id); } sessionCacheId = Ns_DStringExport(&ds); Ns_DStringFree(&ds); return sessionCacheId; } /* *---------------------------------------------------------------------- * * SSLContextCertFileInit -- * * Load SSL context's key and certificate files. * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static int SSLContextCertFileInit(NsOpenSSLContext *sslcontext) { /* * Certificate is optional for clients as long as the server's they connect * to don't request and require them to provide one. Certificates are * required for SSL servers. */ Ns_Log(Debug, "KeyFile = %s; CertFile = %s", sslcontext->keyFile, sslcontext->certFile); if (sslcontext->keyFile == NULL || sslcontext->certFile == NULL) { if (sslcontext->role == SERVER_ROLE) { Ns_Log(Error, "%s (%s): certificate and key files must both be defined for server SSL context %s", MODULE, sslcontext->server, sslcontext->name); return NS_ERROR; } Ns_Log(Notice, "%s (%s): no cert or key defined for client SSL context '%s' (this may be ok)" MODULE, sslcontext->server, sslcontext->name); return NS_OK; } /* * Make sure we can get to the certificate and key files. */ if ((access(sslcontext->certFile, F_OK) != 0) || (access(sslcontext->certFile, R_OK) != 0)) { Ns_Log(Error, "%s (%s): '%s' certificate file is not readable or does not exist", MODULE, sslcontext->server, sslcontext->name); return NS_ERROR; } if ((access(sslcontext->keyFile, F_OK) != 0) || (access(sslcontext->keyFile, R_OK) != 0)) { Ns_Log(Error, "%s (%s): '%s' key file is not readable or does not exist", MODULE, sslcontext->server, sslcontext->name); return NS_ERROR; } /* * Load the certificate into the SSL context */ if (SSL_CTX_use_PrivateKey_file(sslcontext->sslctx, sslcontext->keyFile, SSL_FILETYPE_PEM) == 0) { Ns_Log(Error, "%s (%s): error loading key file '%s'", MODULE, sslcontext->server, sslcontext->keyFile); return NS_ERROR; } if (SSL_CTX_use_certificate_chain_file(sslcontext->sslctx, sslcontext->certFile) == 0) { Ns_Log(Error, "%s (%s): error loading certificate file '%s'", MODULE, sslcontext->server, sslcontext->certFile); return NS_ERROR; } if (SSL_CTX_check_private_key(sslcontext->sslctx) == 0) { Ns_Log(Error, "%s (%s): '%s' private key does not match certificate", MODULE, sslcontext->server, sslcontext->name); return NS_ERROR; } Ns_Log(Notice, "%s (%s): '%s' certificate and key loaded successfully", MODULE, sslcontext->server, sslcontext->name); return NS_OK; } /* *---------------------------------------------------------------------- * * SSLContextCAFileInit -- * * Loads SSL context's CA file * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static void SSLContextCAFileInit(NsOpenSSLContext *sslcontext) { if (sslcontext->caFile == NULL || SSL_CTX_load_verify_locations(sslcontext->sslctx, sslcontext->caFile, NULL) == 0) { Ns_Log(Notice, "%s (%s): '%s' failed to load CA certificate file '%s'", MODULE, sslcontext->server, sslcontext->name, sslcontext->caFile); if (sslcontext->peerVerify) Ns_Log(Error, "%s (%s): '%s' is set to verify peers; CA \ certificates are required to perform peer verification", MODULE, sslcontext->server, sslcontext->name); if ((access(sslcontext->caFile, F_OK) != 0) || (access(sslcontext->caFile, R_OK) != 0)) Ns_Log(Error, "%s (%s): '%s' CA certificate file is not readable or does not exist", MODULE, sslcontext->server, sslcontext->name); } else { Ns_Log(Notice, "%s (%s): '%s' CA file loaded successfully", MODULE, sslcontext->server, sslcontext->name); } } /* *---------------------------------------------------------------------- * * SSLContextCADirInit -- * * Initializes SSL context's CA directory * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static void SSLContextCADirInit(NsOpenSSLContext *sslcontext) { DIR *dirfp = NULL; if (sslcontext->caDir == NULL || SSL_CTX_load_verify_locations(sslcontext->sslctx, NULL, sslcontext->caDir) == 0) { Ns_Log(Warning, "%s (%s): '%s' error using CA directory '%s'", MODULE, sslcontext->server, sslcontext->name, sslcontext->caDir); dirfp = opendir(sslcontext->caDir); if (dirfp == NULL) { Ns_Log(Warning, "%s (%s): '%s' cannot open CA certificate directory", MODULE, sslcontext->server, sslcontext->name); } closedir(dirfp); } } /* *---------------------------------------------------------------------- * * SSLContextCiphersInit -- * * Initialize cipher suite for an SSL context. * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static int SSLContextCiphersInit(NsOpenSSLContext *sslcontext) { if (SSL_CTX_set_cipher_list(sslcontext->sslctx, sslcontext->cipherSuite) == 0) { Ns_Log(Error, "%s (%s): '%s' error setting cipher suite to '%s'", MODULE, sslcontext->server, sslcontext->name, sslcontext->cipherSuite); return NS_ERROR; } Ns_Log(Notice, "%s (%s): '%s' ciphers loaded successfully", MODULE, sslcontext->server, sslcontext->name); return NS_OK; } /* *---------------------------------------------------------------------- * * SSLContextPeerVerifyInit -- * * Initialize peer veification. * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static void SSLContextPeerVerifyInit(NsOpenSSLContext *sslcontext) { if (sslcontext->peerVerify) { SSL_CTX_set_verify(sslcontext->sslctx, (SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE), PeerVerifyCallback); } else { SSL_CTX_set_verify(sslcontext->sslctx, SSL_VERIFY_NONE, NULL); } } /* *---------------------------------------------------------------------- * * SSLContextPeerVerifyDepthInit -- * * Initialize peer verification depth. A '0' value indicates infitite * depth. * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static void SSLContextPeerVerifyDepthInit(NsOpenSSLContext *sslcontext) { if (sslcontext->peerVerifyDepth == 0) { Ns_Log(Warning, "%s (%s): '%s' peer verify depth set to infinite", MODULE, sslcontext->server, sslcontext->name); } if (sslcontext->peerVerifyDepth >= 0) { SSL_CTX_set_verify_depth(sslcontext->sslctx, sslcontext->peerVerifyDepth); } else { Ns_Log(Warning, "%s (%s): '%s' peer verify parameter invalid; defaulting to %d", MODULE, sslcontext->server, sslcontext->name, DEFAULT_PEER_VERIFY_DEPTH); SSL_CTX_set_verify_depth(sslcontext->sslctx, DEFAULT_PEER_VERIFY_DEPTH); } } /* *---------------------------------------------------------------------- * * SSLContextSessionCacheInit -- * * Initialize the per-SSL context session cache. We use OpenSSL's * internal cache for storage and let it do the work. * * Results: * * Side effects: * *---------------------------------------------------------------------- */ static void SSLContextSessionCacheInit(NsOpenSSLContext *sslcontext) { if (sslcontext->sessionCache) { /* * Turn on session caching for this SSL context. */ if (sslcontext->role == SERVER_ROLE) { SSL_CTX_set_session_cache_mode(sslcontext->sslctx, SSL_SESS_CACHE_SERVER); } else { SSL_CTX_set_session_cache_mode(sslcontext->sslctx, SSL_SESS_CACHE_CLIENT); } /* * Create the session cache context id which must be unique to each SSL * context across the entire OpenSSL library. This means we need to * make it unique enough that another AOLserver module won't * inadvertently use the same session cache context id. */ SSL_CTX_set_session_id_context( sslcontext->sslctx, (void *) &sslcontext->sessionCacheId, sizeof(sslcontext->sessionCacheId) ); /* * Set the time to live for a session in this session cache. After this * time, a session will have expired. It will be flushed automatically * by OpenSSL sometime after expiration. If a session has expired and a * new connection comes in using that session before the session cache * has been flushed, this session in the cache is flushed immediately * and a new session cache is created. (XXX need to confirm this) */ SSL_CTX_set_timeout(sslcontext->sslctx, sslcontext->sessionCacheTimeout); /* * Set how many sessions can be cached in this session cache. */ SSL_CTX_sess_set_cache_size(sslcontext->sslctx, sslcontext->sessionCacheSize); Ns_Log(Notice, "%s (%s): session cache is turned on for sslcontext '%s'", sslcontext->name, MODULE, sslcontext->server); } else { SSL_CTX_set_session_cache_mode(sslcontext->sslctx, SSL_SESS_CACHE_OFF); Ns_Log(Notice, "%s (%s): session cache is turned off for sslcontext '%s'", sslcontext->name, MODULE, sslcontext->server); } } /* *---------------------------------------------------------------------- * * SSLContextTraceInit -- * * Initialize handshake tracing. * * Results: * * Side effects: * SSL handshake information may show up in the server log. You don't * want this to happen in normal production service. * *---------------------------------------------------------------------- */ static void SSLContextTraceInit(NsOpenSSLContext *sslcontext) { /* XXX lock */ if (sslcontext->trace) { SSL_CTX_set_info_callback(sslcontext->sslctx, (void *) OpenSSLTrace); } else { SSL_CTX_set_info_callback(sslcontext->sslctx, NULL); } /* XXX unlock */ } /* *---------------------------------------------------------------------- * * SSLContextProtocolsInit -- * * Initialize protocols for an SSL context. * * Results: * NS_OK or NS_ERROR * * Side effects: * *---------------------------------------------------------------------- */ static int SSLContextProtocolsInit(NsOpenSSLContext *sslcontext) { int bits = 0; char *lprotocols = NULL; bits = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; if (sslcontext->protocols == NULL) { Ns_Log(Notice, "%s (%s): '%s' protocol parameter not set; using all protocols: SSLv2, SSLv3 and TLSv1", MODULE, sslcontext->server, sslcontext->name); bits &= ~bits; } else { lprotocols = ns_strdup(sslcontext->protocols); lprotocols = Ns_StrToLower(lprotocols); if (strstr(lprotocols, "all") != NULL) { Ns_Log(Notice, "%s (%s): '%s' using all protocols: SSLv2, SSLv3 and TLSv1", MODULE, sslcontext->server, sslcontext->name); bits &= ~bits; } else { if (strstr(lprotocols, "sslv2") != NULL) { Ns_Log(Notice, "%s (%s): '%s' using SSLv2 protocol", MODULE, sslcontext->server, sslcontext->name); bits &= ~SSL_OP_NO_SSLv2; } if (strstr(lprotocols, "sslv3") != NULL) { Ns_Log(Notice, "%s (%s): '%s' using SSLv3 protocol", MODULE, sslcontext->server, sslcontext->name); bits &= ~SSL_OP_NO_SSLv3; } if (strstr(lprotocols, "tlsv1") != NULL) { Ns_Log(Notice, "%s (%s): '%s' using TLSv1 protocol", MODULE, sslcontext->server, sslcontext->name); bits &= ~SSL_OP_NO_TLSv1; } } ns_free(lprotocols); } if (SSL_CTX_set_options(sslcontext->sslctx, bits) == 0) { Ns_Log(Error, "%s (%s): protocol initialization failed", MODULE, sslcontext->server); return NS_ERROR; } return NS_OK; } /* *---------------------------------------------------------------------- * * OpenSSLTrace -- * * Log the progress of an SSL connection. * * Results: * None. * * Side effects: * Server log output. * *---------------------------------------------------------------------- */ static void OpenSSLTrace(SSL *ssl, int where, int rc) { NsOpenSSLConn *sslconn = (NsOpenSSLConn *) SSL_get_app_data(ssl); char *alertTypePrefix = NULL; char *alertType = NULL; char *alertDescPrefix = NULL; char *alertDesc = NULL; struct Tcl_Time previoustime; unsigned long seconds; unsigned long microseconds; if (where & SSL_CB_ALERT) { alertTypePrefix = "; alert type = "; alertType = (char *) SSL_alert_type_string_long(rc); alertDescPrefix = "; alert desc = "; alertDesc = (char *) SSL_alert_desc_string_long(rc); } else { alertTypePrefix = alertType = ""; alertDescPrefix = alertDesc = ""; } /* Get time since last timer update */ previoustime = sslconn->timer; /* Update the timer */ Tcl_GetTime(&sslconn->timer); /* Find the difference in seconds */ seconds = sslconn->timer.sec - previoustime.sec; /* Find the difference in microseconds */ microseconds = sslconn->timer.usec - previoustime.usec; /* Convert the difference in seconds to microseconds and add */ microseconds = microseconds + (seconds * 1000000); Ns_Log(Notice, "%s (%s): trace (%p): %8ld secs: %s%s%s%s%s", MODULE, sslconn->server, sslconn, microseconds, SSL_state_string_long(ssl), alertTypePrefix, alertType, alertDescPrefix, alertDesc ); } /* *---------------------------------------------------------------------- * * PeerVerifyCallback -- * * Called by the SSL library at each stage of client certificate * verification. * * Results: * * Always returns 1 to prevent verification errors from halting * the SSL handshake. We'd rather finish the handshake so we * can either authenticate by other means or return an HTTP error. * * Side effects: * None. * *---------------------------------------------------------------------- */ static int PeerVerifyCallback(int preverify_ok, X509_STORE_CTX *x509_ctx) { return 1; } aolserver4-nsopenssl-3.0beta26/ChangeLog0000644000175000017500000011702410263172426020072 0ustar frankiefrankie2004-11-20 tag v3_0beta26 2004-11-20 Dossy Shiobara * Makefile (1.44), nsopenssl.c (1.77), sslcontext.c (1.10): Fix memory leak in IssueTmpRSAKey, introducing new NsMakeTmpRSAKey and pre-generating 512-bit and 1024-bit temporary RSA keys at nsopenssl module initialization time. Closes SF Bug #1069595. 2004-11-02 tag v3_0beta25 2004-11-02 Dossy Shiobara * ssl.c (1.68, v3_0beta25): SSL_ERROR_ZERO_RETURN should not be treated as an error as it signifies a proper close notification from the peer in SSLv3/TLSv1 connections (and doing so causes outbound SSL client connections to misbehave). Closes SF Bug #1059171. 2004-10-27 Dossy Shiobara * nsopenssl.c (1.76), nsd.tcl (1.13): Fix crashing bug in SeedPRNG and update sample configs. The correct module-global config section is called "ns/module/nsopenssl". Closes SF Bug #1055417. 2004-09-21 tag v3_0beta23 2004-09-21 Dossy Shiobara * sslcontext.c (1.9): Fix IssueTmpRSAKey crash when nsopenssl serves Netscape Navigator 4.06 Export Edition with weaker RSA keys. Closes SF Bug #999089. 2004-08-25 tag v3_0beta22 2004-08-25 Dossy Shiobara * ssl.c (1.67), nsopenssl.c (1.75): Cleanup of code and fix a bug relating to "broken" SSL connections being returned to the connection pool for HTTP Keep-Alive when they should be shut down and discarded. This was resulting in connection threads using up 100% of the CPU, trying to read the next HTTP request from a dead socket, then returning it back to the Keep-Alive pool. Fixes a portion of the SF Bug #1012892. 2004-08-25 Dossy Shiobara * README (1.6), nsd.tcl (1.12): If SSLv2 isn't in the list of configured protocols, then it shouldn't be in the list of ciphersuites either -- this can cause the server to crash if a client tries to negotiate a SSLv2 connection (as it's advertised as available in ciphersuites). 2004-06-23 Scott Goodwin * sslcontext.c, defaults.h: Merged SSLContextCertInit, SSLContextKeyFileInit, and the key/cert validate function into one. Default certificate and key file paths no longer default to anything: you must specify them specifically. Client SSL contexts no longer fail to load if there is no certificate defined for it: SSL certs are optional for clients. 2004-06-16 Dossy Shiobara * https.tcl: throwback from old http.tcl - should use _ns_https_read and not _ns_http_read 2004-06-16 Dossy Shiobara * https.tcl: new optional 'body' arg to ns_httpspost so the POST data can be computed before calling ns_httpspost and sent as the request body. 2004-06-12 Scott Goodwin * https.tcl: fixed to use separate read and write handles again. We don't really need a separate read and write sockets (which are both wrapped in Tcl channels) since sockets can be bidirectional. I decided I'd clean up that code so you'd only need one socket, and thus would only be using up one file descriptor instead of two. But doing this made the code more complex and would change the number of args. Ah well, live and learn. 2004-04-14 Scott Goodwin * nsopenssl.h, ssl.c, tclcmd.c: Restructured Tcl channel process; now uses ChanInfo structs to maintain state and to ensure proper closure and freeing of sockets, channels and structures. 2004-04-13 Scott Goodwin * x509.c: Added x509.c to hold certificate management functions. 2004-04-09 Scott Goodwin * All: Lot's of cleanup, deleting old comments, restructing some code. * nsopenssl.h, ssl.c: Merged NsOpenSSLConnSend, NsOpenSSLConnRecv and NsOpenSSLConnHandshake into one function, NsOpenSSLConnOp. * defaults.h, nsopenssl.h: Pulled default defines out of nsopenssl.h and into separate defaults.h file. * nsopenssl.h, ssl.c: Added DEBUG_NSOPENSSL define: uncomment it to dump more info to log; comment out for production runs. Recompilation necessary. * sslcontext: fixed SSLContextCacheInit to take into account the context's server / client role. 2004-04-04 Scott Goodwin * Tagged: 3_0beta19 * nsopenssl.h, ssl.c, tclcmds.c, https.tcl: Cleaned up CreateTclChannel. This affects calls to ns_openssl_sockopen and friends: the number of list values is the same and so is there meaning, but list items 1 and 2 are now the same socket descriptors where in the past they used to be separate, one for read and one for write. The benefits besides simpler C code are that your web server will use 1 less fd for each connection you make using nsopenssl's Tcl API. * ssl.c, tclcmds.c: Fixed fd leak when using nsopenssl Tcl API. Have to do a shutdown on the socket for both read/write sides, then ns_sockclose on the socket. 2004-03-27 Scott Goodwin * nsopenssl.c, tclcmds.c: Fixed fd leak where I wasn't properly cleaning up after ns_sockopen etc. Changes involved adding readchan and writechan to NsOpenSSLConn struct, unregistering and closing the sockets in ChanCloseProc. Thanks to Noah Robin (sitz@aol.net) for reporting the problem. 2004-03-12 Scott Goodwin * nsopenssl.c: Changed couple of STREQ's for STRIEQ's so sslcontext could be upper, lower or mixed case. Thanks to Noah Robin (sitz AT aol.net). 2004-03-02 Scott Goodwin * ssl.c: Changed SSL handshake failure to be a warning instead of an error. 2004-02-29 Scott Goodwin * nsopenssl.h, nsopenssl.c, ssl.c: Added code to wait on sockets that have nothing ready to read or write. Stops these conns from hogging the CPU. 2004-02-16 Scott Goodwin * nsopenssl.h, ssl.c: Fixed SSL read loop problem; improved processing path. 2004-02-15 Scott Goodwin * ssl.c: Restructured SSL read and write loops; both now take into account incomplete reads and track buffer length. * nsopenssl.h, ssl.c, sslcontext.c: Added microsecond timers to the connection structure for OpenSSLTrace to output. This incurs no overhead when SSL handshake tracing is turned off. When handshake tracing is turned on, the trace output includes times in microseconds for each step relative to when the previous step completed. This will assist in doing performance analysis and tuning for a particular installation. * ssl.c, nsopenssl.h: Removed read/write attempt counts. 2004-02-13 Scott Goodwin * ssl.c, nsopenssl.h: Added read and write attempt counts; if a conn read or write fails 1,000 in a row, we consider the conn dead and return an error. Hopefully this is a temporary fix until I can identify a better way to resolve this issue in the core server. 2004-01-19 Scott Goodwin * Makefile: added kerberos headers to CFLAGS to resolve issue when OpenSSL has been compile with Kerberos support. This appears to be true if you're running RedHat 9, and possibly Fedora. Thanks to C.R. Oldham for this fix. * nsopenssl.h, nsopenssl.c, ssl.c: Removed rest of BIO stuff in lieu of straight SSL calls. Cleaned up NsOpenSSLConnHandshake(). 2004-01-17 Scott Goodwin * nsopenssl.h, ssl.c, tclcmds.c: Ripped out use of BIOs in NsOpenSSLConnSend and NsOpenSSLConnRecv and replaced them with straight SSL methods. 2004-01-16 Scott Goodwin * ssl.c, nsopenssl.h: Merged NsOpenSSLConnConnect and NsOpenSSLConnAccept into a single NsOpenSSLConnHandshake function. Also converted the function to use SSL_* calls directly instead of using OpenSSL's BIO abstraction. Subjectively, the handshake does appear to be quicker. 2004-01-08 Scott Goodwin * ssl.c: Fixed bug in Recv and Send procs so that larger files could be downloaded and uploaded. 2003-12-27 Scott Goodwin * nsopenssl.c, nsopenssl.h: Added changes from Jamie Rasmussen for Windows build. 2003-12-24 Scott Goodwin * all: Lots of cleanup, initializing function variables. * sslcontext.c, nsopenssl.c, nsopenssl.h: Added names to mutexes. Added MODULE_SHORT to nsopenssl.h as the mutex name length is limited. 2003-12-13 Scott Goodwin * ssl.c: Changed NsOpenSSLConnSend to use BIO_write instead of SSL_write. It's crucial that we call BIO_flush after every write or this won't work. 2003-11-24 Scott Goodwin * ssl.c, nsopenssl.c, tclcmds.c: Fixed problem with SSL conn reference counting; conns are now free'd properly when NsOpenSSLDestroy is called and the conn's reference count is 0. Tagged v3_0_beta_2. 2003-11-22 Scott Goodwin * all: Stopped passing *module to all functions; nsopenssl must now be called nsopenssl in the config file and nothing else. Merged sslconn.c and sslsock.c into ssl.c. Lots of other cleanups. 2003-11-09 Scott Goodwin * tclcmds.c: 'ns_openssl info' now returns a string instead of a list. 2003-10-25 Scott Goodwin * All: Cleaned up SSL context handling, reintegrated Ns_OpenSSLSockConnect, adding SSL context passing ability. 2003-10-23 Scott Goodwin * https.tcl, Makefile: Added back to the code, modified Makefile to install it. 2003-10-19 Scott Goodwin * All: Lots of fixes, added mutexs around SSL contexts structures, fixed session cache id generation to be specific to each virtual server. 2003-10-11 Scott Goodwin * All: Refactored entire codebase by abstracting public functions into libnsopenssl.so/dylib and an nsopenssl.so module. 2003-09-30 Scott Goodwin * All: ripped out all non-comm-driven stuff. nsopenssl module will now be focused only on core comm-driven connections. All the rest of the stuff (incoming and outgoing SSL conns generated by Tcl API itself) is moving into a new module called nshttps. 2003-09-29 Scott Goodwin * init.c, nsopenssl.h: added default SSL contexts section and modified to read from config file.. 2003-08-21 Scott Goodwin * all: Added Ns_OpenSSLContextInit, Ns_OpenSSLContextRelease and other C API functions. Refactored rest of C API and other functions. 2003-08-12 Scott Goodwin * nsopenssl.c: fixed bug when setting protocols; SSL handshake now completes successfully. 2003-08-10 Scott Goodwin * ssl.c: Got rid of SetNonBlocking(); calling Ns_SockSetNonBlocking and Ns_SockSetBlocking directly. 2003-08-08 Scott Goodwin * All: - Ripped out AOLserver 3.x comm driver stuff - Added Tcl hash to manage nsopenssl state for each virtual server - OpenSSL library is initialized at first virtual server init - New Ns_DriverInitData structure added (req'd AOLserver mod) - Replaced config.c/h by using AOLserver C API config stuff directly 2003-01-08 Scott Goodwin * All: Committing back to AOLserver SourceForge area prior to ripping out AOLserver 3.x support. nsopenssl 3.x will only be supporting AOLserver 4.x. 2002-12-22 Scott Goodwin * thread.c, nsopenss.c: Merged thread.c into nsopenssl.c. 2002-11-23 Scott Goodwin * all: Cleaned up the directory; moved some files to the test directory. * config.h, tclcmds.h, thread.h: Merged into nsopenssl.h * config.c, nsopenssl.h: Merged config.c into init.c 2002-11-21 Scott Goodwin * tclcmds.c: ns_sockdup, ns_sockioctl have gone away in AOLserver 4.x. Changed them to dup and ioctl for both 3.x and 4.x compiles. * Makefile: Add a definition for $(CP). AOLserver 4.x uses the install program instead of CP like AOLserver 3.x. Need to update nsopenssl to compile using the appropriate method for 3.x and 4.x. * nsopenssl.h: Moved Ns_OpenSSLConn's 'struct NsOpenSSLDriver sdPtr' so that it is used for both 3.x and 4.x * nsopenssl.c: Moved declaration of 'static NsOpenSSLDriver firstSSLDriverPtr' so it is compiled with both 3.x and 4.x. Moved OpenSSLProc's conn initializer outside of the switch statement so that it runs first (might be a problem as this should probably only pertain to DriverSends and Receives. Changed OpenSSLProc to use recvmsg and sendmsg the same way nssock's SockProc does. 2002-10-28 Scott Goodwin * https.tcl: Added ability to pass multiple files with ns_httpspost: "filesets" is a list of ns_sets, each ns_set contains information about each file to upload and has four keys: name: the name of the form element for this file filename: the name of the file content: the actual contents of the file content-type: the type of contents in the file, such as text/plain filesets are only used with multipart/form-data The filesets parameter has been added to the parameters passed in: ns_httpspost {url {rqset ""} {qsset ""} {type ""} {filesets ""} {timeout 30}} IF YOU USE timeout IN YOUR CURRENT ns_httpspost CODE, YOU MUST CHANGE YOUR CALL. Also modified ns_httpspost to accept "" to mean the default timeout. 2002-10-18 Scott Goodwin * ca, tests: Moved the tests and ca to a separate test harness. Decided it would be best to keep all tests in the test harness rather that the module, at least until tests for all the modules and the core server are written. A decision can be made at that time whether to integrate the testing framework into the core and modules. 2002-10-07 Scott Goodwin * ca: Created the 'ca' directory to generate and hold test CA certs, web server certs, and client certs. * tests: Added more files to the tests directory. 2002-10-04 Scott Goodwin * TODO: Added back to the repository. * Makefile: Updated with new targets for tagging and releasing code (stolen from Rob Mayoff's nscache Makefile :) 2002-05-28 Scott Goodwin * init.c: added some user-friendly error messages when key fails to load in LoadKey routine. 2002-05-27 Scott Goodwin * ssl.c: in NsOpenSSLDestroyConn, if socket is valid, we shut it down before freeing the ccPtr datastructures. Apparently, the BIO_free_all causes two bytes to be sent over the socket that confuses Win32 clients. Thanks to Piotr Szuca for this addition. 2002-04-23 Scott Goodwin * Makefile: added 'install: all' directive that overrides the 'install: all' directive in Makefile.module so that I can have the install process install https.tcl as well (I need to update the online docs). Thanks to Pierre Asselin. 2002-03-05 Scott S. Goodwin * nsopenssl.c, nsopenssl.c: cleaned up NsOpenSSLDriver and Ns_OpenSSLConn structures some more, and cleaned up the initialization of an Ns_OpenSSLConn in SockThread. Some items weren't being initialized (they aren't yet being used, but should still be initialized). Tag: nsopenssl-2_2_beta_4. * nsopenssl.c, nsopenssl.h, tclcmds.c: rearranged the NsOpenSSLDriver and Ns_OpenSSLConn structures to make it clearer what items needed to be freed by what routines. Also changed port to peerport in Ns_OpenSSLConn, and added the 'ns_openssl peerport' command, which is the same as the 'ns_openssl port' command. Tag: nsopenssl-2_2_beta_3. * all: ran *.c and *.h through indent: indent -i2 -br -brs -ce -hnl -psl -sob . 2002-03-04 Scott S. Goodwin * ssl.c, nsopenssl.c, nsopenssl.h, tclcmds.c: Added 'ns_openssl module name' and 'ns_openssl module port' commands. The first tells you what the name of the loaded module is for the current connection (you may have more than one copy of nsopenssl loaded). The second tells you which port your current connection's nsopenssl driver is listening on. This would allow you to do access control by determining what module name and or module port the connection is coming through. 2002-02-18 Scott S. Goodwin * tclcmds.c: added ns_openssl port command. It returns the scPtr->port value, which happens to be the local port the conn is using, not the port the server is listening on. I need to do a query on the port the server is listening on by grabbing the port number from the nsd.tcl based on the module name (as you may have multiple instances of nsopenssl up and running). 2002-02-15 Scott S. Goodwin * ssl.c: I was decrementing towrite by rc bytes. Problem is that if rc < 0 which it will be on SSL_write error, towrite will get bigger. This was a late night error. The #if'd out debug portion worked properly. Adam Zell pointed this out. * dumb: The previous two changes were made once before. The tclcmd.c fix reverted when I received a fix for some other problem in tclcmd.c; I neglected to do a diff between my copy and the fixed one that was sent to me. In the case of init.c, I'm uncertain how that creeped back in. Thanks to Adam Zell for pointing these out. Lessons learned: Always commit your changes right after your tests work. Always diff a file sent to you against your current working copy from the repository. There may be changes you made to your file that aren't in the contributed file. * init.c: Fixed NsOpenSSLCreateDriver; sdPtr->randomFile wasn't set before being possibly used. * tclcmds.c: Fixed SSLSockListenCallback so that the interp was initialized before it was possibly used. 2002-02-14 Scott S. Goodwin * nsopenssl 2.1 released. 2002-02-13 Scott S. Goodwin * ssl.c: The nonblocking socket fix in RunServerSSLHandshake fixed the Solaris problem. #if'd out debug code in NsOpenSSLSend function. Maybe I can go back and retry the BIO_handshake by unblocking the socket before I run it. 2002-02-12 Scott S. Goodwin * ssl.c: made the socket non-blocking at the beginning of RunServerSSLHandshake, and set it back to blocking before the function returns. * ssl.c: took out #if 0 for the SetNonBlocking function. It's not used right now, but it had nested #if 0's, which makes me nervous. * ssl.c: moved SSL_set_app_data call from before to after SSL_set_accept_state call, as it was in 1.1c. * ssl.c: added some debug code in NsOpenSSLSend to help debug Solaris problem. 2002-02-08 Scott S. Goodwin * ssl.c: reverted NsOpenSSLSend back to the way nsopenssl 1.1c worked to try and resolve a problem on Solaris where images and possibly pages of certain sizes fail to download correctly. Haven't tested yet. 2002-02-08 Scott S. Goodwin * ssl.c: added some debugging code to log errors when NsOpenSSLSend fails. This is to try and debug a Solaris issue. 2001-12-27 Scott S. Goodwin * https.tcl: cleaned up the ns_httpspost proc and got rid of the debugging statements. 2001-12-12 Scott S. Goodwin * https.tcl: form.tcl doesn't respect the fact that a boundary value declaration may be wrapped in double quotes. It will work with this: multipart/form-data, boundary=--123456789 but not this: multipart/form-data, boundary="--123456789" Changed https.tcl to work with form.tcl (for now). 2001-12-02 Scott S. Goodwin * config.h: Added the CONFIG_MODULEDIR to create the ModuleDir parameter name. * init.c: Altered CheckModuleDir so that the config parameter ModuleDir can be used to point to somewhere outside of the AOLserver directory area. If ModuleDir is specified, the CheckModuleDir does *not* try and create the directory for you but assumes if you've already got it set up. * config.c: Used Ns_MakePath in place of Ns_DStringVarAppend to ensure that slashes always appear between path elements when ConfigPathDefault constructs a path. * init.c: Renamed MakeModuleDir to CheckModuleDir, and instead of passing in *server, *module and **dirp, now only *sdPtr is passed in. 2001-11-29 Scott S. Goodwin * https.tcl: Added '--' to prepend the boundary markers in ns_httpspost. I should have read the MIME RFC, as a boundary marker that is declared as --myboundarymarkerhere looks like this when actually used: ----myboundarymarkerhere 2001-11-19 Scott S. Goodwin * https.tcl: Fixed boundary problem when doing multpart form data with ns_httpspost. This code: set qsset [ns_set new qsset] ns_set put $qsset user goodwin ns_set put $qsset pass blahblah set page [ns_httpspost \ "https://192.168.0.1:8001/test.cgi" "" \ $qsset "multipart/form-data"] Posts this content: -----------------------------16931435195472910531915358310 Content-Disposition: form-data; name="user" goodwin -----------------------------16931435195472910531915358310 Content-Disposition: form-data; name="pass" blahblah -----------------------------16931435195472910531915358310-- 2001-10-13 Scott S. Goodwin * https.tcl: Added ability for ns_httpspost to do multiport/form-data. (Not Yet Tested!!!). * ssl.c: The NsOpenSSLSend function simply did an SSL_write. Robert Spassky Cabacungan found that this wasn't working for long file transfers. Specifically, in his own words: The problem is the NsOpenSSLSend() function in ssl.c, in nsopenssl-2.0. BIO_write is returning a "resource not available, try again" error, but NsOpenSSLSend is not checking for that, and so behaves as though it were a non-recoverable error, aborting the write instead of trying again. A simple loop over the SSL_write() fixes this. Ironically, there is commented out code at the bottom of the function which would handle retries. However, the comment reads "this BIO_write loop doesn't work, but seems like it should". So it looks like Scott did consider this possibility, but it kind of slipped through the cracks in the final release. Indeed, NsOpenSSLRecv() does loop and handle retries. Rob change the SSL_write to be inside of a loop that checks BIO_should_retry on the write BIO. 2001-09-28 Scott S. Goodwin * Makefile: Added better make instructions when OPENSSL var isn't set. * Makefile: Cleaned up the Makefile considerably so it's easier to read and understand. 2001-09-27 Scott S. Goodwin * tclcmds.c: Bug fixes contributed by Rich Fredericks, AOL Local Technology group. In his own words: "As I said on the phone, All I really did was make sure NsTclOpenSSLCmd returns out (TCL_ERROR) if the argc req isn't met (line 205) and add a check for NULL on scPtr (lines 220-223). I also moved the "scPtr = NsOpenSSLGetConn(interp)" statement to below the code for the "info" option to the tcl ns_openssl command (previously it was executed first), since that specific variant of the command does not require an SSL conn context." Thanks, Rich! 2001-08-29 Scott S. Goodwin * https.tcl: made fixes to ns_httpspost per Rick Lansky at bom.com. He also suggested I allow the Content-type to be passed in as a parameter, so I've added that too. 2001-08-27 Soctt S. Goodwin * https.tcl: added ns_httpspost, that is called with url, rqset, qsset and timeout. The qsset is an ns_set with key/values that will be turned into user=scottg&pass=1234, for example, and passed as content in the POST. 2001-08-21 Scott S. Goodwin * https.tcl: removed some debugging statements; commented out the debug notice that the url was local. * https.tcl: fixed a bug that was fixed once before but apparently I got my files mixed up. rqset wasn't being passed from ns_httpsget to ns_httpsopen. 2001-08-20 Scott S. Goodwin * https.tcl: changed ns_httpsopen such that when it's passed a url that does not begin with "https://", it will first try to prepend the ServerLocation param, then the ServerHostname param, then the ServerAddress param. If all of them fail, it will log an error, but continue processing. Should probably make it a fatal error for nsopenssl to load if one or more of these parameters are not set. * ssl.c: bug fix: trying to use module and type from ccPtr after ccPtr had been destroyed. 2001-08-17 Scott S. Goodwin * ssl.c: tested the sock client and sockserver's ability to validate their peer's certificate. Tested nsdserver's ability to validate client's certificate. * nsopenssl.h: defined version for OpenSSL 0.9.6b. * https.tcl: made rqset the second to last arg passed to ns_httpsget. 2001-08-16 Scott S. Goodwin * init.c: turned on the peerVerify code for SSL sock server and sock clients (as opposed to NSD-driven conns). Need to test. * tclcmds.c: was using an interp before it was allocated. Pretty amazing. Adam Zell pointed this out. * init.c: boneheadedly #if 0'd out the client cert verification code for NSD-driven conns. Works now. Sean Yamamoto noticed the problem. * init.c: should have been initializin sdPtr->randomFile *before* calling SeedPRNG. Also should have been freeing randomFile when freein the driver. Thanks to Adam Zell for pointing these out. 2001-08-15 Scott S. Goodwin * https.tcl: add the rqset parameter to ns_httpsget so that cookies can be used. 2001-08-09 Scott S. Goodwin * nsopenssl 2.0 released. * tests: tested with OpenSSL 0.9.6a Solaris. * tests: tested with OpenSSL 0.9.6, 0.9.6a and 0.9.6b on Linux. * ssl.c: Had to return to the non-BIO method of running the SSL server handshake. Solaris *hates* the BIO method. Where normally it'll try to read the client cert twice (even if you've told it not to ask for a client cert!), with the BIO method, it attempts to read the client cert up to 60 times! 2001-08-08 Scott S. Goodwin * init.c: the OpenSSL PRNG is now seeded at start time when NsOpenSSLCreateDriver is run. This will have no effect on Linux, but it will hopefully ease the pain of Solaris and other users. * docs: go see http://scottg.net for the new documentation on nsopenssl. * All: added ns_openssl_sock* commands and C API. The C API isn't ready for prime time, and there's more work to do on how the Tcl commands are going to use cert validation, but it can make outgoing SSL conns and set up SSL ports to listen on, from Tcl or C. * TODO: removed this file from distribution. Will host it on http://scottg.net. 2001-08-08 Scott S. Goodwin * nsopenssl 1.1c released. * all: Bug fix. If a file upload is interrupted, the connection doesn't go away, and the thread continues consuming CPU and memory. This fix checks to see if the client has gone away and forces an error, at which point the connection is cleaned up properly. 2001-06-20 Scott S. Goodwin * init.c: changed SetProtocols to allow putting all protocols on one line instead of one per line. 2001-06-15 Scott S. Goodwin * nsopenssl 1.1b released. * all: Updated to allow the server to pass its certificate chain to the client. To use this feature, simply append your certificate chain to the end of your server certificate PEM file and start your server. 2001-06-08 Scott S. Goodwin * nsopenssl 1.1a released. * all: Bug fix to allow session caching and client certificate verification to work at the same time. Session caching is on by default in version 1.1, but client verify is not. 2001-05-05 Scott S. Goodwin * nsopenssl 1.1 released. 2001-05-04 Scott S. Goodwin * ssl.c: Now checking for errors returned by BIO_flush in NsOpenSSLFlush; we don't return an error though from the function though, we just report it in the log. * init.c, config.h: made seedbytes a config parameter, which allows you to set the number of bytes that will be used to seed the PRNG. 2001-05-03 Scott S. Goodwin * test.adp: renamed test.adp to nsopenssl-tests.adp. * init.c: cleanup code; moved PRNG functions to bottom; cleaned up NsOpenSSLInitSessionCache. 2001-05-01 Scott S. Goodwin * init.c: Solaris problem: SeedPRNG failed because I wasn't adding enough randomness; I bumped up the number of bytes from 16 to 1024 and it's working now. Yay! 2001-04-30 Scott S. Goodwin * nsd.tcl: added ns_param RandomFile. * 40-bit export browsers now work!!! The recent changes you see were to fix problems with 40-bit export browsers, which could not connect when your server certificate had 1024 public key (which most if not all server certs do). * TODO: added link to nsopenssl release info on my site. * tclcmds.c, test.adp: Added new Tcl commands to report protocol and cipher name, strength: ns_openssl protocol ns_openssl cipher name ns_openssl cipher strength * init.c: initialized sdPtr->randomFile from the RandomFile nsd.tcl parameter in NsOpenSSLCreateDriver(). Added AddEntropyFromRandomFile(), PRNGIsSeeded(), SeedPRNG() and IssueTmpRSAKey(). * config.h: added CONFIG_RANDOM parameter; uses "RandomFile" in nsd.tcl to allow you to specify a file to use as a source of random bits for seeding the PRNG. * config.c: modified ConfigPathDefault to allow NULL as a default. * nsopenssl.h: removed Ns_Cache pointer from nsopenssl driver structure. Added pointer to file specified with the RandomFile parameter. 2001-04-17 scottg * TODO: Updated. * config.h: Session caching is now on by default. * Makefile: Updated to reflect absence of cache.c. * cache.c, cache.h: Removed these files. See item below. * init.c, cache.c: Moved NsOpenSSLInitSessionCache from cache.c to init.c and removed the callback setups. We don't need to use our own caching mechanism; OpenSSL already has one built-in. Apparently when the nsopenssl module was created, it was based on mod_ssl which needs an external, disk-based session caching mechanism because Apache isn't multithreaded. 2001-03-12 root * tclcmds.c: Replaced Ns_GetConn with Ns_TclGetConn. Latest AOLserver 4.x no longer offers Ns_GetConn. 2000-12-12 root * nsopenssl.c: made it a fatal error to bind to a socket already in use. What sometimes happens is someone will forget to take out nsssl at startup and it'll bind to the https socket first. nsopenssl would just log the error but most people would assume it was working since they could connect via SSL. * sock.c: somehow this file was left lying around. All of these functions are in nsopenssl.c, so I removed sock.c. 2000-11-18 root * nsopenssl.h: changed library version to 0.9.6. Gotta find a way to automatically set this based on what version of OpenSSL you compile against. Right now, OpenSSL 0.9.6 is required. Shouldn't be difficult to make this module work with 0.9.5a if you really want. 2000-11-18 root * production-ready: Although there is still more to do (see below and the TODO file), I have done extensive load-testing on RedHat 6.2 and Debian 2.2 and consider this code to be production-ready. Note that I haven't had time to run load tests on a Solaris box yet. * init.c: fixed LoadCACerts; the CA directory was forced to NULL when calling SSL_CTX_load_verify_locations. * all: Rob Mayoff has taken the module and really done an excellent job of refactoring and cleaning it up. Most notable changes: Extensive reformatting to bring code (mostly) in line with AOLserver standards. Consolidated structures. Now there is a single struct representing the driver and a single struct for each connection, instead of two in each case. Driver initialization factored mostly into init.c. Implemented timeout during SSL handshake. Revised Makefile. Now detects OPENSSL not set in a way that doesn't involve bracketing the whole Makefile in an ifdef. I modified the cache code extensively; however, it probably doesn't manage reference counts quite right. See next item anyway. The cache is disabled by default. This code was (I assume) blindly modelled on mod_ssl's cache. The reason mod_ssl needs it is because the Apache children don't share one SSL_CTX. Since nsd threads do share one SSL_CTX, and the SSL_CTX has its own session cache anyway, there's no point in building our own in this way. Config utilities in config.c. All config #defines in config.h. Added thread (mutex) callbacks in thread.c. If the client sends an invalid certificate, the connection is still accepted. I added a new command, [ns_openssl clientcert valid], that returns 1 if the client sent a certificate AND we were able to verify it. 2000-11-05 root * tclcmds.c: added 'ns_openssl clientcert exists' command. * readme.txt: updated with Tcl interface and with parameters for nsd.tcl. * nsopenssl.c: left it in SSL_VERIFY_PEER mode by mistake. It now uses the config file parameter ClientVerify properly. Also did some cleanup. * sock.c: the 'ssl' command has been changed to 'ns_openssl'. 2000-11-04 root * tclcmds: Changed the tcl command names. All nsopenssl Tcl commands now start with 'ssl'; all new commands should also start with 'ssl': ssl info ssl clientcert version ssl clientcert serial ssl clientcert subject ssl clientcert issuer ssl clientcert notbefore ssl clientcert notafter ssl clientcert signature_algorithm ssl clientcert key_algorithm ssl clientcert pem * nsopenssl.c, sock.c: Moved default ca chain processing from nsopenssl.c to sock.c. * nsopenssl.c: took out FIONBIO ifdef'd code. 2000-10-30 [ ROOT I ] * nsd.tcl, nsopenssl.h, nsopenssl.c, sock.c: added CACertPath and CACertFile parameters to config file processing; these now get passed from nsd.tcl to Ns_SSLCreateServer. * nsopenssl.h, nsopenssl.c, sock.c: Created SSLConf data structure to hold config parameters that are passed from Ns_ModuleInit to Ns_SSLCreateServer. Changed all 'keyfile' variable name types to 'config->keyfile'. This seems like double-duty since the SSLServer data structure contains some of the same information. To get rid of SSLConf we'd have to move the code that's currently in Ns_SSLCreateServer into Ns_ModuleInit which wouldn't be wise. First, you want to factor out any code you can to keep each function as simple as possible. Second, we might want to create multiple servers using Ns_SSLCreateServer (say for virtual hosting); having it a separate function that you pass these parameters to is useful, I think. 2000-10-27 Scott S. Goodwin * tclcmds.c: added CertInfoCmd which is called in Tcl as: client_cert_info version client_cert_info serial client_cert_info subject client_cert_info issuer client_cert_info notbefore client_cert_info notafter client_cert_info sig_algorithm client_cert_info key_algorithm client_cert_info pem_certificate I also added three functions to support the above Tcl commands: ValidTime, which is used to return the notbefore and notafter strings in the format "Aug 28 20:00:38 2000 GMT"; SerialNumber which returns the serial number as a string (serial number is in hex); and PEMCertificate, which returns the PEM format of the client certificate. 2000-10-25 Scott S. Goodwin * tclcmds.c: added InfoCmd which returns a true Tcl list with the SSL library name (OpenSSL), version (0.9.5a), crypto library name (OpenSSL, but could be BSAFE/Crypto-C), and crypto library version (0.9.5a). It's called as 'openssl_info' from Tcl (though this may change to ssl_info later -- I didn't want to clash with nssock's ssl_info Tcl command). 2000-10-24 Scott S. Goodwin * all: ifdef'd all client verification specific code so they won't be included in a normal compile. Eventually all of this code should be conditionally run if the config file says to do client verification. To use it, set VERIFY_CLIENT=1 as a make argument. 2000-10-23 Scott S. Goodwin * nsopenssl.c: Went back to "standard" SSL handshaking loop. Freddie's explicit select loop *works* better in that it allows more connections before it gets balled up (probably due to a threading problem) but that problem still exists. I'm still learning C, and I'm using the Apache/mod_ssl combo as my reference, as well as RSA's SSL-C toolkit, so for now I want to use the "standard" method until I can get the other stuff fixed and I understand what's going on. I'll then check to see if Freddie's changes improve performance without sacrificing maintainability across platforms and put it back in. 2000-10-17 Scott S. Goodwin * nsopenssl.c: BIO_free'd conPtr->ssl_bio in NsSSLCreateConn. Should I be using SSL_free() here instead??? Nope, that makes it crash. Ok. 2000-10-13 Scott S. Goodwin * sock.c: Added protocol parameter parsing in NsModuleInit. You specify protocols thusly: "SSLv2, SSLv3, TLSv1, ALL". Case doesn't matter, order doesn't matter. Commas are necessary. When the protocol parser sees 'ALL', none of the others are parsed (unlike Apache -- see the comments in the code). * sock.c: Ns_ModuleInit was changing the config file ciphersuite parameter in memory -- that's probably a no-no. Now it mallocs if the ciphersuite paramater doesn't exist, and strdup's if it does. 2000-10-02 Scott S. Goodwin * .c, .h: replaced all ConfigGet calls with ConfigGetValue; ConfigGet is obsolete. * debug: Why were we using our own debug variable? I took out all of the 'if (debug)' statements so Debug to the log file works like the rest of aolserver's debug statements. 2000-09-30 Scott S. Goodwin * Split Files: I've split the code into four files: nsopenssl.h, nsopenssl.c, sock.c and cache.c. Fewer pages to print when all I really want to see right now is the SSL portion of the code :) 2000-21-12 Scott S. Goodwin * logging: Added more debug log statements 2000-09-12 Scott S. Goodwin * nsopenssl.c: Integrated changes made by Freddie Mendoza (avm@satori.com). These included: changed NsSSLRecv and NsSSLCreateConn to use OpenSSL BIO routines, added more debugging in the SSL negotiations, changes made to make caching work better, removed some redundant functions that are now part of the AOLserver core. He tested his changes with OpenSSL 0.9.5a and 0.9.4. I'm using it with 0.9.5a without any problems. * Makefile: Added ability to pass BSAFE directory as a parameter to make. This allows you to compile OpenSSL with BSAFE. See the readme.txt file. * readme.txt: Added notes on how to compile with BSAFE. 2000-08-20 Kriston J. Rehberg * readme.txt: Updated build and installation instructions. Added notes about -fPIC and no-asm options when building the OpenSSL library. * Added to $TOP/nsopenssl with new Makefile.