/* * mocana_ssl.c * * Mocana Embedded SSL Server * GoAhead integration layer * * Copyright Mocana Corp 2003. All Rights Reserved. * */ /******************************** Description *********************************/ /* * This module implements a patch into the Mocana Embedded SSL Server. * */ #ifdef __ENABLE_MOCANA_SSL_SERVER__ /********************************* Includes ***********************************/ #include "mocana_ssl/common/moptions.h" #include "mocana_ssl/common/mdefs.h" #include "mocana_ssl/common/mtypes.h" #include "mocana_ssl/common/merrors.h" #include "mocana_ssl/common/mrtos.h" #include "mocana_ssl/common/mtcp.h" #include "mocana_ssl/common/mocana.h" #include "mocana_ssl/common/random.h" #include "mocana_ssl/ssl/ssl.h" #include "wsIntrn.h" #include "webs.h" #include "websSSL.h" #include #include #include /******************************* Definitions **********************************/ #ifdef __RTOS_VXWORKS__ #define SSL_CERTIFICATE_DER_FILE "NVRAM:/ssl.der" #define SSL_RSA_HOST_KEYS "NVRAM:/sslkey.dat" #else #define SSL_CERTIFICATE_DER_FILE "ssl.der" #define SSL_RSA_HOST_KEYS "sslkey.dat" #endif #define SSL_PORT SSL_DEFAULT_TCPIP_PORT #define MAX_SSL_CONNECTIONS_ALLOWED (10) #define SSL_HELLO_TIMEOUT (15000) #define SSL_RECV_TIMEOUT (300000) #define SSL_EXAMPLE_KEY_SIZE (1024 + 256) static int sslListenSock = -1; /* Listen socket */ static char* pCertificate = NULL; static char* pRsaKeyBlob = NULL; /******************************* Prototypes **********************************/ int websSSLAccept(int sid, char *ipaddr, int port, int listenSid); static void websSSLSocketEvent(int sid, int mask, int iwp); static int websSSLReadEvent(webs_t wp); static int mocana_SSL_computeHostKeys(char** ppCertificate, unsigned int *pCertLength, char** ppRsaKeyBlob, unsigned int *pKeyBlobLength); static int mocana_SSL_releaseHostKeys(char **ppCertificate, char **ppRsaKeyBlob); /******************************************************************************/ /* * Start up the SSL Context for the application, and start a listen on the * SSL port (usually 443, and defined by SSL_PORT) * Return 0 on success, -1 on failure. */ int websSSLOpen() { unsigned int certLength; unsigned int rsaKeyBlobLength; if (0 > MOCANA_initMocana()) return -1; if (0 > mocana_SSL_computeHostKeys(&pCertificate, &certLength, &pRsaKeyBlob, &rsaKeyBlobLength)) return -1; if (0 > SSL_init(pCertificate, certLength, pRsaKeyBlob, rsaKeyBlobLength, MAX_SSL_CONNECTIONS_ALLOWED)) return -1; SSL_sslSettings()->sslTimeOutHello = SSL_HELLO_TIMEOUT; SSL_sslSettings()->sslTimeOutReceive = SSL_RECV_TIMEOUT; sslListenSock = socketOpenConnection(NULL, SSL_PORT, websSSLAccept, SOCKET_BLOCK); if (sslListenSock < 0) { trace(2, T("SSL: Unable to open SSL socket on port <%d>!\n"), SSL_PORT); return -1; } return 0; } /******************************************************************************/ /* * Accept a connection */ int websSSLAccept(int sid, char *ipaddr, int port, int listenSid) { webs_t wp; int wid; a_assert(ipaddr && *ipaddr); a_assert(sid >= 0); a_assert(port >= 0); /* * Allocate a new handle for this accepted connection. This will allocate * a webs_t structure in the webs[] list */ if ((wid = websAlloc(sid)) < 0) { return -1; } wp = webs[wid]; a_assert(wp); wp->listenSid = listenSid; ascToUni(wp->ipaddr, ipaddr, min(sizeof(wp->ipaddr), strlen(ipaddr)+1)); /* * Check if this is a request from a browser on this system. This is useful * to know for permitting administrative operations only for local access */ if (gstrcmp(wp->ipaddr, T("127.0.0.1")) == 0 || gstrcmp(wp->ipaddr, websIpaddr) == 0 || gstrcmp(wp->ipaddr, websHost) == 0) { wp->flags |= WEBS_LOCAL_REQUEST; } /* * Since the acceptance came in on this channel, it must be secure */ wp->flags |= WEBS_SECURE; /* * Arrange for websSocketEvent to be called when read data is available */ socketCreateHandler(sid, SOCKET_READABLE, websSSLSocketEvent, (int) wp); /* * Arrange for a timeout to kill hung requests */ wp->timeout = emfSchedCallback(WEBS_TIMEOUT, websTimeout, (void *) wp); trace(8, T("webs: accept request\n")); return 0; } /******************************************************************************/ /* * Perform a read of the SSL socket */ int websSSLRead(websSSL_t *wsp, char_t *buf, int len) { int numBytesReceived; int rc; a_assert(wsp); a_assert(buf); if ((rc = SSL_recv(wsp->mocanaConnectionInstance, buf, len, &numBytesReceived)) < 0) { if (0 > rc) rc = -1; return rc; } return numBytesReceived; } /******************************************************************************/ /* * The webs socket handler. Called in response to I/O. We just pass control * to the relevant read or write handler. A pointer to the webs structure * is passed as an (int) in iwp. */ static void websSSLSocketEvent(int sid, int mask, int iwp) { webs_t wp; wp = (webs_t) iwp; a_assert(wp); if (! websValid(wp)) { return; } if (mask & SOCKET_READABLE) { websSSLReadEvent(wp); } if (mask & SOCKET_WRITABLE) { if (wp->writeSocket) { (*wp->writeSocket)(wp); } } } /******************************************************************************/ /* * Handler for SSL Read Events */ static int websSSLReadEvent (webs_t wp) { int ret = 07, sock; socket_t *sptr; int connectionInstance; a_assert (wp); a_assert(websValid(wp)); sptr = socketPtr(wp->sid); a_assert(sptr); sock = sptr->sock; /* * Create a new SSL session for this web request */ connectionInstance = SSL_acceptConnection(sock); if (0 > connectionInstance) { #ifdef __ENABLE_ALL_DEBUGGING__ printf("websSSLReadEvent: SSL_acceptConnection failed. %d\n", connectionInstance); #endif /* SSL error: cleanup */ websTimeoutCancel(wp); socketCloseConnection(wp->sid); websFree(wp); return -1; } /* * Create the SSL data structure in the wp. */ wp->wsp = balloc(B_L, sizeof(websSSL_t)); a_assert (wp->wsp); (wp->wsp)->mocanaConnectionInstance = connectionInstance; (wp->wsp)->wp = wp; /* * Call the default Read Event */ websReadEvent(wp); return ret; } /******************************************************************************/ /* * Return TRUE if websSSL has been opened */ int websSSLIsOpen() { return (sslListenSock != -1); } /******************************************************************************/ /* * Perform a gets of the SSL socket, returning an balloc'ed string * * Get a string from a socket. This returns data in *buf in a malloced string * after trimming the '\n'. If there is zero bytes returned, *buf will be set * to NULL. If doing non-blocking I/O, it returns -1 for error, EOF or when * no complete line yet read. If doing blocking I/O, it will block until an * entire line is read. If a partial line is read socketInputBuffered or * socketEof can be used to distinguish between EOF and partial line still * buffered. This routine eats and ignores carriage returns. */ int websSSLGets(websSSL_t *wsp, char_t **buf) { socket_t *sp; ringq_t *lq; char c; int rc, len; webs_t wp; int sid; int mci; int numBytesReceived; a_assert(wsp); a_assert(buf); *buf = NULL; wp = wsp->wp; sid = wp->sid; mci = wsp->mocanaConnectionInstance; if ((sp = socketPtr(sid)) == NULL) { return -1; } lq = &sp->lineBuf; while (1) { if ((rc = SSL_recv(mci, &c, 1, &numBytesReceived)) < 0) { if (0 > rc) rc = -1; return rc; } if (numBytesReceived == 0) { /* * If there is a partial line and we are at EOF, pretend we saw a '\n' */ if (ringqLen(lq) > 0 && (sp->flags & SOCKET_EOF)) { c = '\n'; } else { continue; } } /* * If a newline is seen, return the data excluding the new line to the * caller. If carriage return is seen, just eat it. */ if (c == '\n') { len = ringqLen(lq); if (len > 0) { *buf = ballocAscToUni((char *)lq->servp, len); } else { *buf = NULL; } ringqFlush(lq); return len; } else if (c == '\r') { continue; } ringqPutcA(lq, c); } return 0; } /******************************************************************************/ /* * Perform a write to the SSL socket */ int websSSLWrite(websSSL_t *wsp, char_t *buf, int len) { int sslBytesSent; a_assert(wsp); a_assert(buf); if (wsp == NULL) { return -1; } sslBytesSent = SSL_send(wsp->mocanaConnectionInstance, buf, len); if (0 > sslBytesSent) sslBytesSent = -1; return sslBytesSent; } /******************************************************************************/ /* * Return Eof for the underlying socket */ int websSSLEof(websSSL_t *wsp) { webs_t wp; int sid; a_assert(wsp); wp = wsp->wp; sid = wp->sid; return socketEof(sid); } /******************************************************************************/ /* * Flush stub for compatibility */ int websSSLFlush(websSSL_t *wsp) { a_assert(wsp); /* Autoflush - do nothing */ return 0; } /******************************************************************************/ /* * Free SSL resources */ int websSSLFree(websSSL_t *wsp) { int status; if (NULL != wsp) { int mci; mci = wsp->mocanaConnectionInstance; status = SSL_closeConnection(mci); if (0 > status) status = -1; /* Free memory here.... */ bfree(B_L, wsp); } return status; } /******************************************************************************/ /* * Stops the SSL system */ void websSSLClose() { SSL_shutdown(); mocana_SSL_releaseHostKeys(&pCertificate, &pRsaKeyBlob); SSL_releaseTables(); MOCANA_freeMocana(); } /******************************************************************************/ static int mocana_SSL_testHostKeys(char** ppCertificate, unsigned int *pCertLength, char** ppRsaKeyBlob, unsigned int *pKeyBlobLength) { int status; if (0 > (status = MOCANA_readFile(SSL_CERTIFICATE_DER_FILE, ppCertificate, pCertLength))) goto exit; status = MOCANA_readFile(SSL_RSA_HOST_KEYS, ppRsaKeyBlob, pKeyBlobLength); exit: return status; } /******************************************************************************/ static int mocana_SSL_releaseHostKeys(char **ppCertificate, char **ppRsaKeyBlob) { MOCANA_freeReadFile(ppCertificate); MOCANA_freeReadFile(ppRsaKeyBlob); return 0; } /******************************************************************************/ static int mocana_SSL_computeHostKeys(char** ppCertificate, unsigned int *pCertLength, char** ppRsaKeyBlob, unsigned int *pKeyBlobLength) { int status; *ppCertificate = NULL; *ppRsaKeyBlob = NULL; /* check for pre-existing set of host keys */ if (0 > (status = mocana_SSL_testHostKeys(ppCertificate, pCertLength, ppRsaKeyBlob, pKeyBlobLength))) { #ifdef __ENABLE_ALL_DEBUGGING__ printf("mocana_SSL_computeHostKeys: host keys do not exist, computing new key pair.\n"); #endif /* if not, compute new host keys */ if (0 > (status = SSL_generateCertificate(ppCertificate, pCertLength, ppRsaKeyBlob, pKeyBlobLength, SSL_EXAMPLE_KEY_SIZE))) goto exit; if (0 > (status = MOCANA_writeFile(SSL_CERTIFICATE_DER_FILE, *ppCertificate, *pCertLength))) goto exit; status = MOCANA_writeFile(SSL_RSA_HOST_KEYS, *ppRsaKeyBlob, *pKeyBlobLength); #ifdef __ENABLE_ALL_DEBUGGING__ printf("mocana_SSL_computeHostKeys: host key computation completed.\n"); #endif } exit: if (0 > status) SSL_freeCertificate(ppCertificate, ppRsaKeyBlob); return status; } /******************************************************************************/ #endif