/* * 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. */ /* * EXPORT NOTICE * * This source code is subject to the U.S. Export Administration * Regulations and other U.S. law, and may not be exported or * re-exported to certain countries (currently Afghanistan * (Taliban-controlled areas), Cuba, Iran, Iraq, Libya, North Korea, * Serbia (except Kosovo), Sudan and Syria) or to persons or entities * prohibited from receiving U.S. exports (including Denied Parties, * Specially Designated Nationals, and entities on the Bureau of * Export Administration Entity List). */ /* * ssl.c -- * * Encryption and I/O routines for SSL v2. * */ static const char *RCSID = "@(#): $Header: /cvsroot/aolserver/aolserver/nsssl/ssl.c,v 1.2 2001/04/24 17:10:07 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__; #include "ns.h" #include "ssl.h" #include "ssltcl.h" #include "x509.h" #include /* * Constants and macros */ #define ATOU16(X) (((((unsigned char*)X)[0])<<8)|(((unsigned char*)X)[1])) #define ATOU24(X) ((((unsigned char*)X)[0]<<16)|ATOU16(((unsigned char*)X)+1)) /* * BSAFE algorithm chooser * */ B_ALGORITHM_METHOD *ALGORITHM_CHOOSER[] = { #ifndef SSL_EXPORT &AM_DES_CBC_DECRYPT, &AM_DES_CBC_ENCRYPT, &AM_DES_EDE3_CBC_DECRYPT, &AM_DES_EDE3_CBC_ENCRYPT, #endif &AM_MD5, &AM_MD5_RANDOM, &AM_MD, &AM_RC2_CBC_DECRYPT, &AM_RC2_CBC_ENCRYPT, &AM_RC4_DECRYPT, &AM_RC4_ENCRYPT, #ifdef HAVE_SWIFT &AM_SwiftRSA_CRT_DECRYPT, &AM_SwiftRSA_CRT_ENCRYPT, #endif &AM_RSA_CRT_DECRYPT, &AM_RSA_CRT_ENCRYPT, &AM_RSA_KEY_GEN, (B_ALGORITHM_METHOD *) NULL }; /* * BSAFE message digest chooser */ B_ALGORITHM_METHOD *DIGEST_CHOOSER[] = { &AM_MD5, (B_ALGORITHM_METHOD *) NULL }; /* * ClientHello data structure * * Used for setting up the client's side of the connection. */ typedef struct { unsigned char msg; unsigned char clientVersion[2]; unsigned char cipherSpecsLength[2]; unsigned char sessionIdLength[2]; unsigned char challengeLength[2]; unsigned char data; } ClientHello; /* * ServerHello data structure * * Used for setting up the server's side of the connection. */ typedef struct { unsigned char msg; unsigned char sessionIdHit; unsigned char certificateType; unsigned char serverVersion[2]; unsigned char certificateLength[2]; unsigned char cipherSpecsLength[2]; unsigned char connectionIdLength[2]; unsigned char data; } ServerHello; /* * ClientMasterKey data structure * * Used to hold and protect the server's private RSA key. */ typedef struct { unsigned char msg; unsigned char cipherKind[3]; unsigned char clearKeyLength[2]; unsigned char encryptedKeyLength[2]; unsigned char keyArgLength[2]; unsigned char data; } ClientMasterKey; /* * surrenderCtx callback * * The random object has to be shared to keep it as well seeded as * possible. However, some of the functions that call it take a long * time to run, such as key generation. The surrenderCtx allows these * functions to let another function get some work done too. We * register the function Surrender so that BSAFE yields the thread * occasionally. */ static A_SURRENDER_CTX surrenderCtx; static Ns_Cs csRandom; static B_ALGORITHM_OBJ randomObject = NULL; /* * Static functions defined in this file. */ static int Recv(SSLConn *cPtr, void *vbuf, int toread); static int Decrypt(SSLConn * con); static int Encrypt(SSLConn * con); static int RecvRecord(SSLConn * con); static int SendRecord(SSLConn * con); static int SetupDigester(SSLConn * con); static int KeyMaterial(SSLConn * con, unsigned char *dest, char *num); static int DetermineSessionKeys(SSLConn * con); static int VerifyKeyArgs(SSLConn * con); static int SetupEncryption(SSLConn * con); static int EncryptInit(SSLConn * con, int fRead); static void EncryptFinal(SSLConn * con, int fRead, unsigned char *data, int length); static void DescribeError(unsigned char *errorcode); static int Surrender(POINTER ignored); static void RandomCleanup(void *ignored); static int CheckForAlgorithm(int trialck); static char *DescribeAlgorithm(int trialck); static int GenerateRandomBytes(unsigned char *output, int outputLength); static int NewSessionID(unsigned char *buf); static void U32TOA(unsigned u, unsigned char *dest); static void U24TOA(unsigned u, unsigned char *dest); static void U24TOA(unsigned u, unsigned char *dest); static void U16TOA(unsigned u, unsigned char *dest); static unsigned char f4Data[3] = {0x01, 0x00, 0x01}; /* *---------------------------------------------------------------------- * * NsSSLGenerateKeypair -- * * Generates an RSA key pair of modulusBits size. If * publicExponent!=NULL it will use it instead of the normal F4 * prime. * * input: &publicKey, &privateKey (uninitialized key objects) * * Note: This is used in stand-alone mode (keygen.tcl) only. See * readme.txt for more information. * * Results: * A keypair is created and placed in publicKey, privateKey. * * Side effects: * Memory is allocated to hold the publicKey, privateKey. * *---------------------------------------------------------------------- */ int NsSSLGenerateKeypair(unsigned int modulusBits, ITEM * publicExponent, B_KEY_OBJ * publicKey, B_KEY_OBJ * privateKey) { int status; B_ALGORITHM_OBJ keypairGenerator; int err; status = NS_ERROR; keypairGenerator = (B_ALGORITHM_OBJ) NULL; err = 0; *publicKey = (B_KEY_OBJ) NULL; *privateKey = (B_KEY_OBJ) NULL; do { A_RSA_KEY_GEN_PARAMS params; if ((modulusBits < 256) || (modulusBits > 1024)) { Ns_Log(Error, "nsssl: invalid key size"); break; } params.modulusBits = modulusBits; if (publicExponent != NULL) { memcpy(¶ms.publicExponent, publicExponent, sizeof(ITEM)); } else { params.publicExponent.data = f4Data; params.publicExponent.len = 3; } err = B_CreateAlgorithmObject(&keypairGenerator); if (err != 0) { break; } err = B_SetAlgorithmInfo(keypairGenerator, AI_RSAKeyGen, (POINTER) & params); if (err != 0) { break; } err = B_GenerateInit(keypairGenerator, ALGORITHM_CHOOSER, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } err = B_CreateKeyObject(publicKey); if (err != 0) { break; } err = B_CreateKeyObject(privateKey); if (err != 0) { break; } Ns_CsEnter(&csRandom); err = B_GenerateKeypair(keypairGenerator, *publicKey, *privateKey, randomObject, &surrenderCtx); Ns_CsLeave(&csRandom); if (err != 0) { break; } status = NS_OK; } while (0); if (err != 0) { Ns_Log(Error, "nsssl: bsafe error %d", err); B_DestroyKeyObject(publicKey); B_DestroyKeyObject(privateKey); } B_DestroyAlgorithmObject(&keypairGenerator); return status; } /* *---------------------------------------------------------------------- * * NsSSLInitialize -- * * Set up SSL data structures at server startup. * Random number is generated, shutdown handle is registered, Tcl * commands (if any) are registered. * * Results: * NS_OK * * Side effects: * See Ns_GenSeeds, RandomCleanup, and NSSSLInterpInit. * * Variable "initialized" is global. * *---------------------------------------------------------------------- */ int NsSSLInitialize(char *server, char *module) { unsigned long seeds[NSEEDS]; static int initialized; if (!initialized) { Ns_GenSeeds(seeds, NSEEDS); if (B_CreateAlgorithmObject(&randomObject) != 0 || B_SetAlgorithmInfo(randomObject, AI_MD5Random, NULL) != 0 || B_RandomInit(randomObject, ALGORITHM_CHOOSER, NULL) != 0 || B_RandomUpdate(randomObject, (unsigned char *) seeds, NSEEDS * (sizeof(long)), NULL) != 0) { return NS_ERROR; } Ns_CsInit(&csRandom); surrenderCtx.Surrender = Surrender; surrenderCtx.handle = NULL; if (server != NULL) { Ns_RegisterShutdown(RandomCleanup, NULL); } else { Ns_Log(Notice, "nsssl: running in stand-alone mode"); } initialized = 1; } if (server != NULL) { Ns_TclInitInterps(server, NsSSLInterpInit, NULL); } return NS_OK; } /* *---------------------------------------------------------------------- * * NsSSLCreateServer -- * * Sets up SSL data structures at server startup. * The server's private key and certificate are loaded * * Results: * The SSLServer context (sPtr). * * Side effects: * SSLServer context is created. * *---------------------------------------------------------------------- */ void * NsSSLCreateServer(char *cert, char *key) { SSLServer *sPtr; sPtr = ns_calloc(1, sizeof(SSLServer)); if (key != NULL && GetPrivateKey(&sPtr->privateKey, key) != NS_OK) { Ns_Log(Error, "nsssl: " "failed to fetch private key from file '%s'", key); ns_free(sPtr); return NULL; } if (GetCertificate(&sPtr->certificate, &sPtr->certificateLength, cert) != NS_OK) { Ns_Log(Error, "nsssl: " "failed to fetch server certificate from file '%s'", cert); ns_free(sPtr); return NULL; } return (void *) sPtr; } /* *---------------------------------------------------------------------- * * NsSSLDestroyServer -- * * Cleans up after the server and frees up the SSLServer * context. This is only called when AOLserver is shutting down. * * Results: * None. * * Side effects: * SSLServer context is freed. * *---------------------------------------------------------------------- */ void NsSSLDestroyServer(void *server) { SSLServer * ctx = server; if (ctx->privateKey != NULL) { B_DestroyKeyObject(&ctx->privateKey); } ns_free(ctx->certificate); ns_free(ctx); } /* *---------------------------------------------------------------------- * * NsSSLCreateConn -- * * Create the ubiquitous conn structure for a new SSL connection. * In order to create a conn for SSL, lots of work is done for * public key exchange, private session key exchange, and random * number generation. Note that RSA is only used for the private * session key exchange, after which a conventional cipher with * a message digest (if requested) is used. * * Results: * Pointer to the conn. * * Side effects: * The conn is created. * Encryption objects are created and (hopefully) destroyed. * *---------------------------------------------------------------------- */ void * NsSSLCreateConn(SOCKET socket, int timeout, void *server) { B_ALGORITHM_OBJ rsaDecryptor; SSLRecord *rec; int err; SSLConn *con; SSLServer *ctx = server; con = ns_calloc(1, sizeof(SSLConn)); con->timeout = timeout; con->socket = socket; con->ctx = ctx; err = 0; rsaDecryptor = NULL; rec = &con->rec; do { unsigned char common_ciphers[3 * 25]; int num_common; ServerHello *serverHello; int i; unsigned char *p; /* * At the moment we don't do client certificates. * (I think that's why this variable is disabled.) */ /* unsigned char certificateChallengeData[32]; */ /* * Get CLIENT-HELLO. */ if (RecvRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "client dropped connection before CLIENT-HELLO"); /* * This is not something to get concerned about. */ break; } else { ClientHello *clientHello = (ClientHello *) rec->data; int iClientVersion; int iCipherSpecsLength; int iSessionIdLength; int iChallengeLength; int i; iClientVersion = ATOU16(clientHello->clientVersion); iCipherSpecsLength = ATOU16(clientHello->cipherSpecsLength); iSessionIdLength = ATOU16(clientHello->sessionIdLength); iChallengeLength = ATOU16(clientHello->challengeLength); if (clientHello->msg == SSL_MT_ERROR) { DescribeError((unsigned char *) (&clientHello->msg + 1)); break; } if ((clientHello->msg != SSL_MT_CLIENT_HELLO) || ((iCipherSpecsLength % 3) != 0) || ((iSessionIdLength != 0) && (iSessionIdLength != 16)) || (iChallengeLength < 16) || (iChallengeLength > 32)) { Ns_Log(Debug, "nsssl: client sent malformed CLIENT-HELLO"); /* * This is not something to get worried about. If the * browser is that messed up it will drop the * connection, anyway. */ break; } num_common = 0; for (i = 0; i < iCipherSpecsLength; i += 3) { int ck = ATOU24(&clientHello->data + i) | 0x01000000; if ( CheckForAlgorithm(ck) == NS_OK) { U24TOA(ck, &common_ciphers[num_common * 3]); num_common++; } } if (num_common == 0) { Ns_Log(Warning, "nsssl: " "client and server failed to agree on cipher"); rec = &con->rec; rec->input[0] = SSL_MT_ERROR; U16TOA(SSL_PE_NO_CIPHER, &(rec->input)[1]); rec->nRecordLength = 3; rec->fIsEscape = 0; (void) SendRecord(con); break; } con->challengeLength = iChallengeLength; memcpy(con->challenge, &clientHello->data + iCipherSpecsLength + iSessionIdLength, iChallengeLength); } /* * Get a new session id (a random number). */ NewSessionID(con->connId); /* * Make the SERVER-HELLO. */ serverHello = (ServerHello *) rec->input; serverHello->msg = SSL_MT_SERVER_HELLO; serverHello->sessionIdHit = 0; serverHello->certificateType = SSL_CT_X509_CERTIFICATE; U16TOA(SSL_SERVER_VERSION, serverHello->serverVersion); U16TOA(ctx->certificateLength, serverHello->certificateLength); i = num_common * 3; U16TOA(i, serverHello->cipherSpecsLength); U16TOA(SSL_SESSION_ID_LENGTH, serverHello->connectionIdLength); p = &serverHello->data; memcpy(p, ctx->certificate, ctx->certificateLength); p += ctx->certificateLength; memcpy(p, common_ciphers, i); p += i; memcpy(p, con->connId, SSL_SESSION_ID_LENGTH); rec->nRecordLength = (sizeof(ServerHello) - 1) + ctx->certificateLength + i + SSL_SESSION_ID_LENGTH; rec->fIsEscape = 0; /* * Send the SERVER-HELLO. */ if (SendRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "client failed to receive SERVER-HELLO"); break; } /* * Get the CLIENT-MASTER-KEY. */ if (RecvRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "client failed to send CLIENT-MASTER-KEY"); break; } else { /* * Decode CLIENT-MASTER-KEY. */ ClientMasterKey *clientMasterKey = (ClientMasterKey *) rec->data; int iClearKeyLength; int iEncryptedKeyLength; int iKeyArgLength; unsigned int outputLenUpdate; unsigned int outputLenFinal; unsigned char *out; unsigned int outMax; con->cipherKind = ATOU24(clientMasterKey->cipherKind) | 0x01000000; iClearKeyLength = ATOU16(clientMasterKey->clearKeyLength); iEncryptedKeyLength = ATOU16(clientMasterKey->encryptedKeyLength); iKeyArgLength = ATOU16(clientMasterKey->keyArgLength); if (clientMasterKey->msg == SSL_MT_ERROR) { DescribeError(&clientMasterKey->msg + 1); break; } if ( (clientMasterKey->msg != SSL_MT_CLIENT_MASTER_KEY) || (CheckForAlgorithm(con->cipherKind) != NS_OK) ) { Ns_Log(Debug, "nsssl: " "client sent malformed CLIENT-MASTER-KEY"); break; } p = &(clientMasterKey->data); memcpy(con->masterKey, p, iClearKeyLength); p += iClearKeyLength; /* * Decrypt secret session key. You know the BSAFE drill by now. */ err = B_CreateAlgorithmObject(&rsaDecryptor); if (err != 0) { break; } err = B_SetAlgorithmInfo(rsaDecryptor, AI_PKCS_RSAPrivate, NULL); if (err != 0) { break; } err = B_DecryptInit(rsaDecryptor, ctx->privateKey, ALGORITHM_CHOOSER, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } out = (unsigned char *) (con->masterKey + iClearKeyLength); outMax = sizeof(con->masterKey) - iClearKeyLength; err = B_DecryptUpdate(rsaDecryptor, out, &outputLenUpdate, outMax, p, iEncryptedKeyLength, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } out += outputLenUpdate; outMax -= outputLenUpdate; err = B_DecryptFinal(rsaDecryptor, out, &outputLenFinal, outMax, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { Ns_Log(Debug, "nsssl: " "failed to decrypt secret session key"); break; } outputLenFinal += outputLenUpdate; con->masterKeyLength = iClearKeyLength + outputLenFinal; p += iEncryptedKeyLength; /* * Get the KEY-ARG-DATA if there is any. */ con->keyArgLength = iKeyArgLength; if (VerifyKeyArgs(con) != NS_OK) { break; } else if (iKeyArgLength > 0) { memcpy(con->readKeyArgData, p, iKeyArgLength); memcpy(con->writeKeyArgData, p, iKeyArgLength); } if ( (SetupDigester(con) != NS_OK) || (DetermineSessionKeys(con) != NS_OK) || (SetupEncryption(con) != NS_OK) ) { break; } /* * Figure out the cipher block size. */ switch (con->cipherKind) { case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: con->blockSize = 0; break; case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: con->blockSize = 8; break; case SSL_CK_NULL_WITH_MD5: default: con->blockSize = 0; } con->fEncryptionActive = 1; } /* * Send the SERVER-VERIFY message. */ p = rec->input; *p++ = SSL_MT_SERVER_VERIFY; memcpy(p, con->challenge, con->challengeLength); rec->nRecordLength = con->challengeLength + 1; rec->fIsEscape = 0; if (SendRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "client did not receive SERVER-VERIFY"); break; } /* * Get the CLIENT-FINISHED-V2 or CLIENT-FINISHED message. */ if (RecvRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "client did not receive CLIENT-FINISHED"); break; } else { p = rec->data; if (*p == SSL_MT_ERROR) { DescribeError(p + 1); break; } if ((*p != SSL_MT_CLIENT_FINISHED) && (*p != SSL_MT_CLIENT_FINISHED_V2)) { Ns_Log(Debug, "nsssl: " "client sent malformed CLIENT-FINISHED"); break; } /* * We probably should verify that the CLIENT-FINISHED * message is correct at this point but we don't because it * doesn't really matter once the connection was closed. */ } /* * Create a new session ID. */ NewSessionID(con->sessionId); /* * Send the SERVER-FINISHED-V2 message. */ rec->input[0] = SSL_MT_SERVER_FINISHED_V2; memcpy(&rec->input[1], con->sessionId, SSL_SESSION_ID_LENGTH); rec->nRecordLength = SSL_SESSION_ID_LENGTH + 1; rec->fIsEscape = 0; if (SendRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "client did not receive SERVER-FINISHED-V2"); break; } } while (0); /* * Clean up the RSA object if it hasn't been cleaned up already. */ if (rsaDecryptor != NULL) { B_DestroyAlgorithmObject(&rsaDecryptor); } if (err != 0) { Ns_Log(Error, "nsssl: " "ssl connection failed, bsafe error %d", err); NsSSLDestroyConn(con); con = NULL; } return (void *) con; } /* *---------------------------------------------------------------------- * * NsSSLDestroyConn -- * * Destroy the ubiquitous conn structure for the SSL connection. * This code enters a critical section at the beginning to update * the random number so it isn't used again in another session. * * Results: * None * * Side effects: * The conn and all its data structures are (hopefully) freed. * Encryption objects are created and (hopefully) destroyed. * *---------------------------------------------------------------------- */ void NsSSLDestroyConn(void *conn) { SSLConn *cPtr = conn; /* * Make a new random number. */ Ns_CsEnter(&csRandom); (void) B_RandomUpdate(randomObject, (unsigned char *) cPtr, sizeof(SSLConn), &surrenderCtx); Ns_CsLeave(&csRandom); /* * Burn all the algorithm objects for the digester, encryptor, and * decryptor if they haven't been destroyed already. */ if (cPtr->digester != NULL) { B_DestroyAlgorithmObject(&cPtr->digester); } if (cPtr->encryptor != NULL) { B_DestroyAlgorithmObject(&cPtr->encryptor); } if (cPtr->decryptor != NULL) { B_DestroyAlgorithmObject(&cPtr->decryptor); } /* * Burn the key objects so they can't be found in memory again if * something hasn't already done so. */ if (cPtr->readKeyObj != NULL) { B_DestroyKeyObject(&cPtr->readKeyObj); } if (cPtr->writeKeyObj != NULL) { B_DestroyKeyObject(&cPtr->writeKeyObj); } ns_free(cPtr); } /* *---------------------------------------------------------------------- * * NsSSLSend -- * * Send data out to the connection. * * Results: * The number of bytes sent. * * Notice: -1 is returned on error. * * Side effects: * None. * *---------------------------------------------------------------------- */ int NsSSLSend(void *conn, void *vbuf, int towrite) { int nwrote, ncopy; unsigned char *buf = vbuf; SSLConn *con = conn; nwrote = towrite; while (towrite) { ncopy = SSL_MAX_RECORD_LENGTH_2_BYTE_HEADER - con->outgoingLength - con->macSize; if (con->blockSize > 0) { /* * Note: macSize % blockSize == 0 */ ncopy -= (ncopy + con->outgoingLength) % con->blockSize; } if (towrite < ncopy) { memcpy(&(con->outgoing)[con->outgoingLength], buf, towrite); con->outgoingLength += towrite; towrite = 0; } else { memcpy(con->rec.input, con->outgoing, con->outgoingLength); memcpy(&(con->rec.input)[con->outgoingLength], buf, ncopy); con->rec.nRecordLength = con->outgoingLength + ncopy; con->rec.fIsEscape = 0; con->outgoingLength = 0; towrite -= ncopy; buf += ncopy; if (SendRecord(con) != NS_OK) { Ns_Log(Debug, "nsssl: " "failed sending record to client"); /* * Note that we're returning -1 here. */ return -1; } } } return nwrote; } /* *---------------------------------------------------------------------- * * NsSSLRecv -- * * Read data from the connection. * * Results: * The number of bytes received. * * Notice: -1 is returned on error. * * Side effects: * vbuf contains the data read. * *---------------------------------------------------------------------- */ int NsSSLRecv(void *conn, void *vbuf, int toread) { int ncopy; SSLConn *cPtr = conn; unsigned char *buf = vbuf; if (cPtr->incomingLength == 0) { if ((NsSSLFlush(conn) != NS_OK) || (RecvRecord(cPtr) != NS_OK)) { Ns_Log(Debug, "nsssl: " "failed receiving record from client"); /* * Note that we're returning -1 here. */ return -1; } cPtr->incomingLength = cPtr->rec.nRecordLength; cPtr->incomingNext = cPtr->incoming; memcpy(cPtr->incoming, cPtr->rec.data, cPtr->incomingLength); } ncopy = cPtr->incomingLength; if (ncopy > toread) { ncopy = toread; } memcpy(buf, cPtr->incomingNext, ncopy); cPtr->incomingLength -= ncopy; cPtr->incomingNext += ncopy; return ncopy; } /* *---------------------------------------------------------------------- * * NsSSLFlush -- * * Flush data waiting to be sent. * * Results: * NS_OK or NS_ERROR. * * Side effects: * * *---------------------------------------------------------------------- */ int NsSSLFlush(void *conn) { int towrite; SSLConn *cPtr = conn; while (cPtr->outgoingLength > 0) { towrite = cPtr->outgoingLength; if (cPtr->blockSize > 0 && ((towrite + cPtr->macSize) > (SSL_MAX_RECORD_LENGTH_3_BYTE_HEADER - SSL_MAXPADDING))) { /* * Note: macSize % blockSize == 0 */ towrite -= towrite % cPtr->blockSize; } cPtr->rec.nRecordLength = towrite; cPtr->rec.fIsEscape = 0; memcpy(cPtr->rec.input, cPtr->outgoing, towrite); if (towrite != cPtr->outgoingLength) { memcpy(cPtr->outgoing, &(cPtr->outgoing)[towrite], cPtr->outgoingLength - towrite); } cPtr->outgoingLength -= towrite; if (SendRecord(cPtr) != NS_OK) { Ns_Log(Debug, "nsssl: failed flushing record to client"); return NS_ERROR; } } return NS_OK; } /* *---------------------------------------------------------------------- * * Recv -- * * Reads data from the connection using Ns_SockRecv. * * Results: * NS_OK * * Side effects: * *vbuf contains the data. * *---------------------------------------------------------------------- */ static int Recv(SSLConn *cPtr, void *vbuf, int toread) { char *buf = (char *) vbuf; int tocopy; while (toread > 0) { if (cPtr->cnt > 0) { if (toread < cPtr->cnt) { tocopy = toread; } else { tocopy = cPtr->cnt; } memcpy(buf, cPtr->base, tocopy); cPtr->base += tocopy; cPtr->cnt -= tocopy; buf += tocopy; toread -= tocopy; } if (toread > 0) { cPtr->base = cPtr->buf; cPtr->cnt = Ns_SockRecv(cPtr->socket, cPtr->base, sizeof(cPtr->buf), cPtr->timeout); if (cPtr->cnt <= 0) { /* * This happens when a user drops the connection by * hitting "stop" or rejects the server's master key * and/or certificate. */ Ns_Log(Debug, "nsssl: client dropped connection"); return NS_ERROR; } } } return NS_OK; } /* *---------------------------------------------------------------------- * * Decrypt -- * * Takes rec->nRecordLength bytes with rec->nPadding padding from * rec->input * and decrypt it into rec->output. It also checks * the MAC and sets * rec->data to point to the actual data. * rec->nRecordLength is adjusted, if * there was padding or a * MAC. * * Results: * NS_OK or NS_ERROR * * Side effects: * rec->output contains the decrypted data. * *---------------------------------------------------------------------- */ static int Decrypt(SSLConn * con) { int status; int err; unsigned char *out; unsigned int outMax; unsigned int length; unsigned int updateLength; SSLRecord *rec; if (EncryptInit(con, 1) != NS_OK) { Ns_Log(Error, "nsssl: encryptinit failed"); return NS_ERROR; } rec = &con->rec; status = NS_ERROR; err = 0; /* * Decryption step. */ switch (con->cipherKind) { case SSL_CK_NULL_WITH_MD5: /* * No encryption with just a message digest. */ rec->data = rec->input; status = NS_OK; break; case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: /* * Encryption with a message digest. */ out = rec->data = rec->output; outMax = SSL_MAXRECSIZE; err = B_DecryptUpdate(con->decryptor, out, &updateLength, outMax, rec->input, rec->nRecordLength, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } length = updateLength; if ((con->cipherKind != SSL_CK_RC4_128_WITH_MD5) && (con->cipherKind != SSL_CK_RC4_128_EXPORT40_WITH_MD5)) { out += updateLength; outMax -= updateLength; err = B_DecryptFinal(con->decryptor, out, &updateLength, outMax, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } length += updateLength; EncryptFinal(con, 1, rec->input, rec->nRecordLength); } rec->nRecordLength = length - rec->nPadding; status = NS_OK; break; default: Ns_Log(Warning, "nsssl: unsupported cipher"); } if (err != 0) { Ns_Log(Error, "nsssl: failed to decrypt, bsafe error %d", err); } if (status != NS_OK) { return NS_ERROR; } else { status = NS_ERROR; } /* * Message digest step. */ do { unsigned char buf[4]; rec->nRecordLength -= con->macSize; rec->mac = rec->data; rec->data += con->macSize; err = B_DigestUpdate(con->digester, con->readKey, con->ReadWriteKeyLength, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } err = B_DigestUpdate(con->digester, rec->data, rec->nRecordLength + rec->nPadding, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } U32TOA(con->nReadSequence, buf); err = B_DigestUpdate(con->digester, buf, 4, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } err = B_DigestFinal(con->digester, rec->macBuf, &updateLength, 16, (A_SURRENDER_CTX *) NULL); if ((err != 0) || (updateLength != 16)) { break; } if (memcmp(rec->mac, rec->macBuf, con->macSize) != 0) { Ns_Log(Error, "nsssl: invalid message authentication code"); break; } status = NS_OK; } while (0); if (err != 0) { Ns_Log(Error, "nsssl: decrypt failed, bsafe error %d", err); } return status; } /* *---------------------------------------------------------------------- * * Encrypt -- * * Makes a MAC for (rec->nRecordLength + rec->nPadding) bytes in * rec->input. Encrypts MAC + rec->input and stores the result * at rec->data. * * Results: * NS_OK or NS_ERROR * * Side effects: * con->rec (in the SSLConn context) contains the * encrypted data. * *---------------------------------------------------------------------- */ static int Encrypt(SSLConn * con) { int status; int err; unsigned char *out; unsigned int outMax; unsigned int length; unsigned int updateLength; SSLRecord *rec; status = NS_ERROR; err = 0; rec = &con->rec; /* * Message digest step. * Note: We must always do MD5 with encryption. */ do { unsigned char buf[4]; err = B_DigestUpdate(con->digester, con->writeKey, con->ReadWriteKeyLength, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } err = B_DigestUpdate(con->digester, rec->input, rec->nRecordLength + rec->nPadding, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } U32TOA(con->nWriteSequence, buf); err = B_DigestUpdate(con->digester, buf, 4, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } err = B_DigestFinal(con->digester, rec->macBuf, &updateLength, 16, (A_SURRENDER_CTX *) NULL); if ((err != 0) || (updateLength != 16)) { break; } if (EncryptInit(con, 0) != NS_OK) { break; } status = NS_OK; } while (0); if (err != 0) { Ns_Log(Error, "nsssl: encrypt failed, bsafe error %d", err); } /* * Return immediately on error or re-initialize status and continue. */ if (status != NS_OK) { return NS_ERROR; } else { status = NS_ERROR; } /* * Encryption step. */ switch (con->cipherKind) { case SSL_CK_NULL_WITH_MD5: /* * No encryption -- just do a message digest and copy the data. */ memcpy(rec->data, rec->macBuf, con->macSize); memcpy(rec->data + con->macSize, rec->input, rec->nRecordLength); rec->nRecordLength += con->macSize; status = NS_OK; break; case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: /* * Encryption with a message digest. */ out = rec->data; outMax = SSL_MAXRECSIZE; err = B_EncryptUpdate(con->encryptor, out, &updateLength, outMax, rec->macBuf, con->macSize, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } length = updateLength; out += updateLength; outMax -= updateLength; err = B_EncryptUpdate(con->encryptor, out, &updateLength, outMax, rec->input, rec->nRecordLength + rec->nPadding, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } length += updateLength; if ((con->cipherKind != SSL_CK_RC4_128_WITH_MD5) && (con->cipherKind != SSL_CK_RC4_128_EXPORT40_WITH_MD5)) { out += updateLength; outMax -= updateLength; err = B_EncryptFinal(con->encryptor, out, &updateLength, outMax, NULL, (A_SURRENDER_CTX *) NULL); if (err != 0) { break; } length += updateLength; EncryptFinal(con, 0, rec->data, length); } rec->nRecordLength = length; status = NS_OK; break; default: Ns_Log(Warning, "nsssl: unsupported cipher"); } if (err != 0) { Ns_Log(Error, "nsssl: encrypt failed, bsafe error %d", err); } return status; } /* *---------------------------------------------------------------------- * * RecvRecord -- * * Reads an ssl record from con->socket. If appropriate it * decrypts and checks the MAC. It sets rec->data to the * received data of rec->nRecordLength bytes. * * Results: * NS_OK or NS_ERROR * * Side effects: * con->rec (in the SSLConn context) contains the * record received. * *---------------------------------------------------------------------- */ static int RecvRecord(SSLConn * con) { int toread; unsigned char buf[3]; unsigned char *dest; SSLRecord *rec; if (Recv(con, buf, 3) != NS_OK) { return NS_ERROR; } rec = &con->rec; if (buf[0] & 0x80) { /* * 2 byte length header */ rec->nRecordLength = ((buf[0] & 0x7f) << 8) | buf[1]; rec->fIsEscape = 0; rec->nPadding = 0; rec->input[0] = buf[2]; dest = &rec->input[1]; toread = rec->nRecordLength - 1; } else { /* * 3 byte length header */ rec->nRecordLength = ((buf[0] & 0x3f) << 8) | buf[1]; rec->fIsEscape = (buf[0] & 0x40) != 0; rec->nPadding = buf[2]; dest = (unsigned char *) rec->input; toread = rec->nRecordLength; } if (toread == 2) { /* * Client cancelled due to server certificate rejection. */ Ns_Log(Debug, "nsssl: client rejected cert and dropped connection"); return NS_ERROR; } if (Recv(con, dest, toread) != NS_OK) { Ns_Log(Debug, "nsssl: client dropped connection"); return NS_ERROR; } if (!con->fEncryptionActive) { rec->data = rec->input; } else if (Decrypt(con) != NS_OK) { return NS_ERROR; } con->nReadSequence++; return NS_OK; } /* *---------------------------------------------------------------------- * * SendRecord -- * * Creates and writes an ssl record to con->socket. If * appropriate it makes a MAC and encrypts the MAC + data + * padding. * * input: rec->nRecordLength bytes in rec->input, * rec->fIsEscape denotes an escape record (unused and untested). * * Results: * NS_OK or NS_ERROR * * Side effects: * con->socket (in the SSLConn context) contains the ssl * record created. * *---------------------------------------------------------------------- */ static int SendRecord(SSLConn * con) { SSLRecord *rec = &con->rec; int towrite, nwrote; int fThreeByteHeader; char *buf; /* * Calculate the padding. */ if (con->blockSize != 0) { rec->nPadding = (rec->nRecordLength + con->macSize) % con->blockSize; if (rec->nPadding != 0) { rec->nPadding = con->blockSize - rec->nPadding; } } else { rec->nPadding = 0; } /* * Decide the header size. */ if (!rec->fIsEscape && (rec->nPadding == 0)) { /* * We can use the 2 byte record header. */ fThreeByteHeader = 0; rec->data = &rec->output[2]; } else { /* * We need the 3 byte record header. */ fThreeByteHeader = 1; rec->data = &rec->output[3]; } if (!con->fEncryptionActive) { memcpy(rec->data, rec->input, rec->nRecordLength); } else if (Encrypt(con) != NS_OK) { return NS_ERROR; } if (!fThreeByteHeader) { towrite = 2; U16TOA(rec->nRecordLength, rec->output); rec->output[0] |= 0x80; } else { /* * 3 byte record header. */ towrite = 3; U16TOA(rec->nRecordLength, rec->output); rec->output[2] = rec->nPadding; if (rec->fIsEscape) { rec->output[0] |= 0x40; } } towrite += rec->nRecordLength; buf = (char *) rec->output; while (towrite > 0) { nwrote = Ns_SockSend(con->socket, buf, towrite, con->timeout); if (nwrote < 0) { return NS_ERROR; } towrite -= nwrote; buf += nwrote; } con->nWriteSequence++; return NS_OK; } /* *---------------------------------------------------------------------- * * SetupDigester -- * * Sets up up the message digester based on con->cipherKind. * Currently only MD5 is used. * * Results: * NS_OK or NS_ERROR * * Side effects: * Algorithm object in con->digester is created and initialized * by the BSAFE library. * *---------------------------------------------------------------------- */ static int SetupDigester(SSLConn * con) { switch (con->cipherKind) { case SSL_CK_NULL_WITH_MD5: case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: con->macSize = 16; if (B_CreateAlgorithmObject(&con->digester) != 0 || B_SetAlgorithmInfo(con->digester, AI_MD5, NULL) != 0 || B_DigestInit(con->digester, (B_KEY_OBJ) NULL, DIGEST_CHOOSER, NULL) != 0) { Ns_Log(Error, "nsssl: failed initializing digester"); return NS_ERROR; } break; default: /* * No digester in use. */ con->macSize = 0; } return NS_OK; } /* *---------------------------------------------------------------------- * * KeyMaterial * * This generates the key material used to generate the read and * write keys during RSA public key exchange. * * Results: * NS_OK or NS_ERROR * * Side effects: * con->masterKey, con->masterKeyLength, * con->challenge, and con->challengeLength are populated. * *---------------------------------------------------------------------- */ static int KeyMaterial(SSLConn * con, unsigned char *dest, char *num) { unsigned int outputLengthFinal; if (B_DigestUpdate(con->digester, con->masterKey, con->masterKeyLength, NULL) != 0) { return NS_ERROR; } if (num != NULL && B_DigestUpdate(con->digester, (unsigned char *)num, strlen(num), NULL) != 0) { return NS_ERROR; } if (B_DigestUpdate(con->digester, con->challenge, con->challengeLength, NULL) != 0 || B_DigestUpdate(con->digester, con->connId, SSL_SESSION_ID_LENGTH, NULL) != 0 || B_DigestFinal(con->digester, dest, &outputLengthFinal, 16, NULL) != 0 || outputLengthFinal != 16) { return NS_ERROR; } return NS_OK; } /* *---------------------------------------------------------------------- * * DetermineSessionKeys * * Determines the session keys according to con->cipherKind * * input: * con->masterKey, con->challenge, con->connId, con->cipherKind * * output: * con->readKey, con->writeKey, con->ReadWriteKeyLength * * Results: * NS_OK or NS_ERROR * * Side effects: * None. * *---------------------------------------------------------------------- */ static int DetermineSessionKeys(SSLConn * con) { unsigned char keyMaterial[16]; switch (con->cipherKind) { case SSL_CK_NULL_WITH_MD5: case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: /* * With MD5 message digest. */ con->ReadWriteKeyLength = 16; /* WRITE KEY = MD5[ MASTER-KEY, "0", CHALLENGE, CONNECTION-ID ] */ /* READ KEY = MD5[ MASTER-KEY, "1", CHALLENGE, CONNECTION-ID ] */ if ( (KeyMaterial(con, con->writeKey, "0") != NS_OK) || (KeyMaterial(con, con->readKey, "1") != NS_OK) ) { return NS_ERROR; } break; case SSL_CK_DES_64_CBC_WITH_MD5: con->ReadWriteKeyLength = 8; /* * WRITE KEY = HASH[ MASTER-KEY, CHALLENGE, CONNECTION-ID ] [0-7] */ if (KeyMaterial(con, con->writeKey, "0") != NS_OK) { return NS_ERROR; } /* * WRITE KEY = HASH[ MASTER-KEY, CHALLENGE, CONNECTION-ID ] [8-15] */ memcpy(con->readKey, &con->writeKey[8], 8); break; case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: con->ReadWriteKeyLength = 24; /* * 3 * 8 byte keys. */ if ( (KeyMaterial(con, con->writeKey, "0") != NS_OK) || (KeyMaterial(con, keyMaterial, "1") != NS_OK) ) { return NS_ERROR; } memcpy(&con->writeKey[16], keyMaterial, 8); memcpy(con->readKey, &keyMaterial[8], 8); if (KeyMaterial(con, &con->readKey[8], "2") != NS_OK) { return NS_ERROR; } break; default: /* * Note: We do not support SHA message digests at this time, * so in those extremely rare cases when a browser is set up * to refuse MD5 and use SHA, this error will appear in * addition to the standard error cases. That kind of * situation would be extremely rare, since all browsers * are set up to accept MD5 and only crazies turn it off. */ Ns_Log(Error, "nsssl: unsupported message digest algorithm"); return NS_ERROR; } return NS_OK; } /* *---------------------------------------------------------------------- * * VerifyKeyArgs * * Verifies the master key length and key arg length after the * server receives the CLIENT-MASTER-KEY. * * input: con->cipherKind, con->masterKeyLength, con->keyArgLength * * Results: * NS_OK or NS_ERROR * * Side effects: * None. * *---------------------------------------------------------------------- */ static int VerifyKeyArgs(SSLConn * con) { int masterKeyLength; int keyArgLength; int status; status = NS_ERROR; /* * This switch is for the masterKeyLength. * Note: there are two switch statements. */ switch (con->cipherKind) { case SSL_CK_NULL_WITH_MD5: case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: masterKeyLength = 16; break; case SSL_CK_DES_64_CBC_WITH_MD5: masterKeyLength = 8; break; case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: masterKeyLength = 24; break; default: /* * This is an error condition. */ masterKeyLength = -1; } /* * This switch is for the keyArgLength. */ switch (con->cipherKind) { case SSL_CK_NULL_WITH_MD5: case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: keyArgLength = 0; break; case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: keyArgLength = 8; break; default: /* * This is an error condition. */ keyArgLength = -1; } /* * This looks like a lame way to detect an error condition. */ if ((con->masterKeyLength == masterKeyLength) && (con->keyArgLength == keyArgLength)) { status = NS_OK; } return status; } /* *---------------------------------------------------------------------- * * VerifyKeyArgs * * Sets up encryption algorithm objects and key objects. * * input: * con->cipherKind, con->readKey, con->writeKey, * con->ReadWriteKeyLength * * output: * con->encryptor, con->decryptor, con->readKeyObj, * con->writeKeyObj * * Results: * NS_OK or NS_ERROR * * Side effects: * * *---------------------------------------------------------------------- */ static int SetupEncryption(SSLConn * con) { ITEM item; int rc4; /* * This is the Bizarro-world way of choosing export/domestic RC4. * (I believe it's because we have to use SetAlgorithmInfo for * RC4 and nothing else). If you're going to add SSL v3 to this * you'll be using SetAlgorithmInfo a whole lot more. */ rc4 = 0; switch (con->cipherKind) { case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: rc4 = 1; if (B_CreateAlgorithmObject(&con->encryptor) != 0 || B_CreateAlgorithmObject(&con->decryptor) != 0) { return NS_ERROR; } /* Fallthrough */ case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: if (B_CreateKeyObject(&con->readKeyObj) != 0 || B_CreateKeyObject(&con->writeKeyObj) != 0) { return NS_ERROR; } } /* * Setup the RC4 algorithm. * Note: Other algorithms used in SSL v3 will need to use this, too. */ if (rc4 && (B_SetAlgorithmInfo(con->encryptor, AI_RC4, NULL) != 0 || B_SetAlgorithmInfo(con->decryptor, AI_RC4, NULL) != 0)) { return NS_ERROR; } /* * Initialize the key objects. */ switch (con->cipherKind) { case SSL_CK_DES_64_CBC_WITH_MD5: if (B_SetKeyInfo(con->writeKeyObj, KI_DES8, con->writeKey) != 0 || B_SetKeyInfo(con->readKeyObj, KI_DES8, con->readKey) != 0) { return NS_ERROR; } break; case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: item.data = con->writeKey; item.len = con->ReadWriteKeyLength; if (B_SetKeyInfo(con->writeKeyObj, KI_Item, (POINTER) &item) != 0) { return NS_ERROR; } item.data = con->readKey; if (B_SetKeyInfo(con->readKeyObj, KI_Item, (POINTER) &item)) { return NS_ERROR; } } /* * Extra setup for RC4. */ if (rc4 && (B_EncryptInit(con->encryptor, con->writeKeyObj, ALGORITHM_CHOOSER, NULL) != 0 || B_DecryptInit(con->decryptor, con->readKeyObj, ALGORITHM_CHOOSER, NULL) != 0)) { return NS_ERROR; } return NS_OK; } /* *---------------------------------------------------------------------- * * EncryptInit -- * * Sets up the encryption/decryption algorithm for another * round of doing it's thing. * * (Note the dual switch(con->cipherKind) statements.) * * input: fRead, con->cipherKind, { con->decryptor | con->encryptor } * con->keyArgData, con->keyArgLength, con->ReadWriteKeyLength * { con->readKeyObj | con->writeKeyObj } * output: { con->encryptor | con->decryptor } * * Results: * NS_OK or NS_ERROR * * Side effects: * * *---------------------------------------------------------------------- */ static int EncryptInit(SSLConn * con, int fRead) { A_RC2_CBC_PARAMS rc2Params; B_ALGORITHM_OBJ *algObj; unsigned char *keyArgData; int err; algObj = fRead ? &con->decryptor : &con->encryptor; switch (con->cipherKind) { case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: if (B_CreateAlgorithmObject(algObj) != 0) { return NS_ERROR; } } if (con->keyArgLength > 0) { keyArgData = fRead ? con->readKeyArgData : con->writeKeyArgData; } /* * Setup the algorithms. */ switch (con->cipherKind) { case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: rc2Params.effectiveKeyBits = con->ReadWriteKeyLength * 8; rc2Params.iv = keyArgData; if (B_SetAlgorithmInfo(*algObj, AI_RC2_CBC, (POINTER) &rc2Params) != 0) { return NS_ERROR; } break; case SSL_CK_DES_64_CBC_WITH_MD5: if (B_SetAlgorithmInfo(*algObj, AI_DES_CBC_IV8, (POINTER) keyArgData) != 0) { return NS_ERROR; } break; case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: if (B_SetAlgorithmInfo(*algObj, AI_DES_EDE3_CBC_IV8, (POINTER) keyArgData) != 0) { return NS_ERROR; } /* Fallthrough */ case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: break; default: Ns_Log(Warning, "nsssl: unsupported cipher"); return NS_ERROR; } /* * Still setting up the algorithms. */ switch (con->cipherKind) { case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: case SSL_CK_IDEA_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: if (!fRead) { err = B_EncryptInit(*algObj, con->writeKeyObj, ALGORITHM_CHOOSER, NULL); } else { err = B_DecryptInit(*algObj, con->readKeyObj, ALGORITHM_CHOOSER, NULL); } if (err != 0) { return NS_ERROR; } /* Fallthrough */ case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC4_128_EXPORT40_WITH_MD5: break; default: Ns_Log(Warning, "nsssl: unsupported cipher"); return NS_ERROR; } return NS_OK; } /* *---------------------------------------------------------------------- * * EncryptFinal -- * * Update the key arg data and destroy the encryption/decryption * object. * * Note: This is not called for RC4. * * Results: * None. * * Side effects: * * *---------------------------------------------------------------------- */ static void EncryptFinal(SSLConn * con, int fRead, unsigned char *data, int length) { B_ALGORITHM_OBJ *algObj; if (con->keyArgLength > 0) { unsigned char *dest; dest = fRead ? con->readKeyArgData : con->writeKeyArgData; memcpy(dest, &data[length - 8], con->keyArgLength); } algObj = fRead ? &con->decryptor : &con->encryptor; B_DestroyAlgorithmObject(algObj); *algObj = NULL; } /* *---------------------------------------------------------------------- * * DescribeError -- * * Translate error code into English. * * Note: This is only used for the server log. * * Results: * None. * * Side effects: * * *---------------------------------------------------------------------- */ static void DescribeError(unsigned char *errorcode) { unsigned err; char *msg; err = ATOU16(errorcode); switch (err) { case SSL_PE_NO_CIPHER: msg = "No Cipher"; break; case SSL_PE_NO_CERTIFICATE: msg = "No Certificate"; break; case SSL_PE_BAD_CERTIFICATE: msg = "Bad Certificate"; break; case SSL_PE_UNSUPPORTED_CERTIFICATE_TYPE: msg = "Unsupported Certificate Type"; break; default: msg = "Unknown Error"; } Ns_Log(Debug, "nsssl: client sent this error: '%s'", msg); } /* *---------------------------------------------------------------------- * * Surrender -- * * Cede control to allow other threads to work while we're still * busy running the random number generator. * * Results: * None. * * Side effects: * * *---------------------------------------------------------------------- */ static int Surrender(POINTER ignored) { Ns_CsLeave(&csRandom); Ns_ThreadYield(); Ns_CsEnter(&csRandom); return NS_OK; } /* *---------------------------------------------------------------------- * * RandomCleanup -- * * Destroy algorithm object and the critical section used for * the random number generator. * * Results: * None. * * Side effects: * * *---------------------------------------------------------------------- */ static void RandomCleanup(void *ignored) { B_DestroyAlgorithmObject(&randomObject); randomObject = NULL; Ns_CsDestroy(&csRandom); } /* *---------------------------------------------------------------------- * * CheckForAlgorithm -- * * Report on ciphers/digests supported. * * Results: * NS_OK if your cipher is supported. * NS_ERROR if your cipher isn't. * * Side effects: * * *---------------------------------------------------------------------- */ static int CheckForAlgorithm(int trialck) { switch (trialck) { case SSL_CK_RC4_128_EXPORT40_WITH_MD5: case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: #ifndef SSL_EXPORT case SSL_CK_RC4_128_WITH_MD5: case SSL_CK_RC2_128_CBC_WITH_MD5: case SSL_CK_DES_64_CBC_WITH_MD5: case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: #endif return NS_OK; } return NS_ERROR; } /* *---------------------------------------------------------------------- * * DescribeAlgorithm -- * * Describe ciphers/digests supported. * * Results: * char pointer to the ASCII name of the cipher. * * Side effects: * None. * *---------------------------------------------------------------------- */ static char * DescribeAlgorithm(int trialck) { char *desc; switch (trialck) { case SSL_CK_RC4_128_EXPORT40_WITH_MD5: desc = "SSL_CK_RC4_128_EXPORT40_WITH_MD5"; break; case SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5: desc = "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"; break; #ifndef SSL_EXPORT case SSL_CK_RC4_128_WITH_MD5: desc = "SSL_CK_RC4_128_WITH_MD5"; break; case SSL_CK_RC2_128_CBC_WITH_MD5: desc = "SSL_CK_RC2_128_CBC_WITH_MD5"; break; case SSL_CK_DES_64_CBC_WITH_MD5: desc = "SSL_CK_DES_64_CBC_WITH_MD5"; break; case SSL_CK_DES_192_EDE3_CBC_WITH_MD5: desc = "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"; break; #endif default: desc = "unknown cipher"; } return desc; } /* *---------------------------------------------------------------------- * * GenerateRandomBytes -- * * Returns the output of the random number generator. * Note the use of critical sections. This function necessarily * takes a little while to execute, so we define surrenderCtx to * point to our Surrender function, above. * * Results: * NS_OK or NS_ERROR. * * Side effects: * A random number is put into the *output variable. * *---------------------------------------------------------------------- */ static int GenerateRandomBytes(unsigned char *output, int outputLength) { int retval = NS_ERROR; int err = -1; Ns_CsEnter(&csRandom); if ( (err = (B_GenerateRandomBytes(randomObject, output, outputLength, &surrenderCtx))) != 0 ) { Ns_Log(Error, "nsssl: " "failed to generate random bytes, bsafe error %d", err); retval = NS_ERROR; } else { retval = NS_OK; } Ns_CsLeave(&csRandom); return retval; } /* *---------------------------------------------------------------------- * * NewSessionID -- * * Generate a session id using the random number generator. * Note: This actually used for both conn ids and session ids. * * Results: * NS_OK or NS_ERROR from GenerateRandomBytes. * * Side effects: * See GenerateRandomBytes. * *---------------------------------------------------------------------- */ static int NewSessionID(unsigned char *buf) { return GenerateRandomBytes(buf, SSL_SESSION_ID_LENGTH); } /* *---------------------------------------------------------------------- * * U32TOA * * Shift to unsigned character (32-bit). * * Results: * dest points to the converted value. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void U32TOA(unsigned u, unsigned char *dest) { dest[0] = (u & 0xFF000000) >> 24; dest[1] = (u & 0x00FF0000) >> 16; dest[2] = (u & 0x0000FF00) >> 8; dest[3] = u & 0x000000FF; } /* *---------------------------------------------------------------------- * * U24TOA * * Shift to unsigned character (24-bit). * * Results: * dest points to the converted value. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void U24TOA(unsigned u, unsigned char *dest) { dest[0] = (u & 0x00FF0000) >> 16; dest[1] = (u & 0x0000FF00) >> 8; dest[2] = u & 0x000000FF; } /* *---------------------------------------------------------------------- * * U24TOA * * Shift to unsigned character (16-bit). * * Results: * dest points to the converted value. * * Side effects: * None. * *---------------------------------------------------------------------- */ static void U16TOA(unsigned u, unsigned char *dest) { dest[0] = (u & 0x0000FF00) >> 8; dest[1] = u & 0x000000FF; }