/* Establish a listening socket for incomming connections */ static SOCKET socketListen(short port, int32 *err) { struct sockaddr_in addr; SOCKET fd; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { _psTrace("Error creating listen socket\n"); *err = SOCKET_ERRNO; return INVALID_SOCKET; } setSocketOptions(fd); addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { _psTrace("Can't bind socket. Port in use or insufficient privilege\n"); *err = SOCKET_ERRNO; return INVALID_SOCKET; } if (listen(fd, SOMAXCONN) < 0) { _psTrace("Error listening on socket\n"); *err = SOCKET_ERRNO; return INVALID_SOCKET; } _psTraceInt("Listening on port %d\n", port); return fd; }
/* Stub for a user-level certificate validator. Just using the default validation value here. */ static int32 certValidator(ssl_t *ssl, psX509Cert_t *cert, int32 alert) { /* Example to allow anonymous connections based on a define */ if (alert > 0) { if (ALLOW_ANON_CONNECTIONS) { /* Could be NULL if SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG */ if (cert) { _psTraceStr("Allowing anonymous connection for: %s.\n", cert->subject.commonName); } else { _psTrace("Client didn't send certificate. Reverting to standard handshake due to SERVER_WILL_ACCEPT_EMPTY_CLIENT_CERT_MSG\n"); } return SSL_ALLOW_ANON_CONNECTION; } _psTrace("Certificate callback returning fatal alert\n"); return alert; } _psTraceStr("Validated cert for: %s.\n", cert->subject.commonName); return 0; }
void psTraceBytes(char *tag, unsigned char *p, int l) { char s[17]; int i; s[16] = '\0'; _psTraceStr("psTraceBytes(%s, ", tag); _psTraceInt("%d);", l); for (i = 0; i < l; i++) { if (!(i & 0xF)) { if (i != 0) { mem2str(s, p - 16, 16); _psTraceStr(" %s", s); } _psTraceInt("\n0x%08x:", (int)p); } _psTraceInt("%02x ", *p++); } memset(s, 0x0, 16); i = l & 0xF; mem2str(s, p - i, (unsigned int)i); for (;i < 16; i++) { _psTrace(" "); } _psTraceStr(" %s", s); _psTrace("\n"); }
static SOCKET newUdpSocket(char *ip, short port, int *err) { struct sockaddr_in addr; SOCKET fd; if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { _psTraceInt("Error creating socket %d\n", SOCKET_ERRNO); *err = SOCKET_ERRNO; return INVALID_SOCKET; } setSocketOptions(fd); addr.sin_family = AF_INET; addr.sin_port = htons(port); if (ip == NULL) { addr.sin_addr.s_addr = INADDR_ANY; if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { _psTrace("Can't bind socket. Port in use or permission problem\n"); *err = SOCKET_ERRNO; return INVALID_SOCKET; } } return fd; }
/* ERROR FUNCTIONS Tap into platform trace and break execution if DEBUG compile Modules should tie themselves to these low levels with compile-time defines */ void _psError(char *msg) { _psTrace(msg); #ifdef HALT_ON_PS_ERROR osdepBreak(); #endif }
/* Open an outgoing blocking socket connection to a remote ip and port. Caller should always check *err value, even if a valid socket is returned */ static SOCKET socketConnect(char *ip, int32 port, int32 *err) { struct sockaddr_in addr; SOCKET fd; int32 rc; /* By default, this will produce a blocking socket */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { _psTrace("Error creating socket\n"); *err = SOCKET_ERRNO; return INVALID_SOCKET; } memset((char *) &addr, 0x0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons((short)port); addr.sin_addr.s_addr = inet_addr(ip); rc = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); if (rc < 0) { *err = SOCKET_ERRNO; } else { *err = 0; } return fd; }
int32 psMd5Test(void) { psDigestContext_t ctx; #if defined(USE_MATRIX_MD5) && !defined(PS_MD5_IMPROVE_PERF_INCREASE_CODESIZE) _psTrace("##########\n#\n# "); _psTrace("MD5 speeds can be improved by enabling\n# "); _psTrace("PS_MD5_IMPROVE_PERF_INCREASE_CODESIZE in cryptoConfig.h\n"); _psTrace("#\n#\n#########\n"); #endif psMd5Init(&ctx.md5); runDigestTime(&ctx, TINY_CHUNKS, MD5_ALG); runDigestTime(&ctx, SMALL_CHUNKS, MD5_ALG); runDigestTime(&ctx, MEDIUM_CHUNKS, MD5_ALG); runDigestTime(&ctx, LARGE_CHUNKS, MD5_ALG); runDigestTime(&ctx, HUGE_CHUNKS, MD5_ALG); return PS_SUCCESS; }
/* Main routine. Initialize SSL keys and structures, and make two SSL connections, the first with a blank session Id, and the second with a session ID populated during the first connection to do a much faster session resumption connection the second time. */ int32 main(int32 argc, char **argv) { int32 rc; sslKeys_t *keys; sslSessionId_t sid; #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif if ((rc = matrixSslOpen()) < 0) { _psTrace("MatrixSSL library init failure. Exiting\n"); return rc; } if (matrixSslNewKeys(&keys) < 0) { _psTrace("MatrixSSL library key init failure. Exiting\n"); return -1; } #ifdef USE_HEADER_KEYS /* In-memory based keys */ if ((rc = matrixSslLoadRsaKeysMem(keys, NULL, 0, NULL, 0, RSA1024CA, sizeof(RSA1024CA))) < 0) { _psTrace("No certificate material loaded. Exiting\n"); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #else /* USE_HEADER_KEYS */ /* File based keys */ if ((rc = matrixSslLoadRsaKeys(keys, NULL, NULL, NULL, rsaCAFile)) < 0){ _psTrace("No certificate material loaded. Exiting\n"); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif /* USE_HEADER_KEYS */ matrixSslInitSessionId(sid); _psTrace("=== INITIAL CLIENT SESSION ===\n"); httpsClientConnection(keys, &sid); _psTrace("\n=== CLIENT SESSION WITH CACHED SESSION ID ===\n"); httpsClientConnection(keys, &sid); matrixSslDeleteKeys(keys); matrixSslClose(); #ifdef WIN32 _psTrace("Press any key to close"); getchar(); #endif return 0; }
static int32 psAesTestCBC(void) { int32 err; psCipherContext_t eCtx; #if defined(USE_MATRIX_AES_CBC) && !defined(PS_AES_IMPROVE_PERF_INCREASE_CODESIZE) _psTrace("##########\n#\n# "); _psTrace("AES speeds can be improved by enabling\n# "); _psTrace("PS_AES_IMPROVE_PERF_INCREASE_CODESIZE in cryptoConfig.h\n"); _psTrace("#\n#\n#########\n"); #endif _psTrace("***** AES-128 CBC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 16, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } runTime(&eCtx, NULL, TINY_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, SMALL_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, MEDIUM_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, AES_DEC_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, AES_DEC_ALG); psAesClearCBC(&eCtx.aes); _psTrace("***** AES-192 CBC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 24, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } runTime(&eCtx, NULL, TINY_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, SMALL_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, MEDIUM_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, AES_DEC_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, AES_DEC_ALG); psAesClearCBC(&eCtx.aes); _psTrace("***** AES-256 CBC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 32, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } runTime(&eCtx, NULL, TINY_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, SMALL_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, MEDIUM_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, AES_DEC_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, AES_ENC_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, AES_DEC_ALG); psAesClearCBC(&eCtx.aes); return 0; }
int32 psAesTestGCM(void) { int32 err; psCipherContext_t eCtx; psCipherGivContext_t eCtxGiv; memset(&eCtxGiv, 0, sizeof(eCtxGiv)); #ifndef USE_LIBSODIUM_AES_GCM _psTrace("***** AES-GCM-128 *****\n"); if ((err = psAesInitGCM(&eCtx.aesgcm, key, 16)) != PS_SUCCESS) { _psTraceInt("FAILED: psAesInitGCM returned %d\n", err); return err; } psAesReadyGCM(&eCtx.aesgcm, iv, iv, 16); runTime(&eCtx, &eCtxGiv, TINY_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, SMALL_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, MEDIUM_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, LARGE_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, HUGE_CHUNKS, AES_GCM_ALG); #else _psTrace("***** Skipping AES-GCM-128 *****\n"); #endif /* !USE_LIBSODIUM */ _psTrace("***** AES-GCM-256 *****\n"); if ((err = psAesInitGCM(&eCtx.aesgcm, key, 32)) != PS_SUCCESS) { _psTraceInt("FAILED: psAesInitGCM returned %d\n", err); return err; } psAesReadyGCM(&eCtx.aesgcm, iv, iv, 16); runTime(&eCtx, &eCtxGiv, TINY_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, SMALL_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, MEDIUM_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, LARGE_CHUNKS, AES_GCM_ALG); runTime(&eCtx, &eCtxGiv, HUGE_CHUNKS, AES_GCM_ALG); psAesClearGCM(&eCtx.aesgcm); return PS_SUCCESS; }
/* Return a string representation of the socket address passed. The return * value is allocated with malloc() */ static unsigned char *getaddrstring(struct sockaddr *addr, int withport) { char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; char *retstring = NULL; int ret; unsigned int len; len = sizeof(struct sockaddr_storage); /* Some platforms such as Solaris 8 require that len is the length * of the specific structure. Some older linux systems (glibc 2.1.3 * such as debian potato) have sockaddr_storage.__ss_family instead * but we'll ignore them */ # ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY if (addr->ss_family == AF_INET) { len = sizeof(struct sockaddr_in); } # ifdef AF_INET6 if (addr->ss_family == AF_INET6) { len = sizeof(struct sockaddr_in6); } # endif # endif ret = getnameinfo((struct sockaddr *) addr, len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST); if (ret != 0) { /* This is a fairly bad failure - it'll fallback to IP if it * just can't resolve */ _psTrace("failed lookup for getaddrstring"); strcpy(hbuf, "UNKNOWN"); strcpy(sbuf, "?"); } if (withport) { len = strlen(hbuf) + 2 + strlen(sbuf); retstring = (char *) psMalloc(MATRIX_NO_POOL, len); snprintf(retstring, len, "%s:%s", hbuf, sbuf); } else { retstring = strdup(hbuf); } return (unsigned char *) retstring; }
int32 psDes3Test(void) { psCipherContext_t eCtx; #if defined(USE_MATRIX_3DES) && !defined(PS_3DES_IMPROVE_PERF_INCREASE_CODESIZE) _psTrace("##########\n#\n# "); _psTrace("3DES speeds can be improved by enabling\n# "); _psTrace("PS_3DES_IMPROVE_PERF_INCREASE_CODESIZE in cryptoConfig.h\n"); _psTrace("#\n#\n#########\n"); #endif psDes3Init(&eCtx.des3, iv, key); runTime(&eCtx, NULL, TINY_CHUNKS, DES3_ALG); runTime(&eCtx, NULL, SMALL_CHUNKS, DES3_ALG); runTime(&eCtx, NULL, MEDIUM_CHUNKS, DES3_ALG); runTime(&eCtx, NULL, LARGE_CHUNKS, DES3_ALG); runTime(&eCtx, NULL, HUGE_CHUNKS, DES3_ALG); psDes3Clear(&eCtx.des3); return 0; }
static void SNI_callback(void *ssl, char *hostname, int32 hostnameLen, sslKeys_t **newKeys) { ssl_t *lssl; lssl = (ssl_t*)ssl; #if CHANGE_KEYS_EXAMPLE matrixSslNewKeys(&sni_keys); if (matrixSslLoadEcKeys(sni_keys, "../sampleCerts/EC/256_EC.pem", "../sampleCerts/EC/256_EC_KEY.pem", NULL, "../sampleCerts/EC/ALL_EC_CAS.pem") < 0) { _psTrace("SNI key load failed. Exiting\n"); matrixSslDeleteKeys(sni_keys); sni_keys = NULL; } *newKeys = sni_keys; #else *newKeys = lssl->keys; /* Just use the currently loaded keys */ #endif return; }
/* Non-blocking socket event handler Wait one time in select for events on any socket This will accept new connections, read and write to sockets that are connected, and close sockets as required. */ static int32 selectLoop(sslKeys_t *keys, SOCKET lfd) { httpConn_t *cp; psTime_t now; DLListEntry connsTmp; DLListEntry *pList; fd_set readfd, writefd; struct timeval timeout; SOCKET fd, maxfd; unsigned char *buf; int32 rc, len, transferred, val; unsigned char rSanity, wSanity, acceptSanity; DLListInit(&connsTmp); rc = PS_SUCCESS; maxfd = INVALID_SOCKET; timeout.tv_sec = SELECT_TIME / 1000; timeout.tv_usec = (SELECT_TIME % 1000) * 1000; FD_ZERO(&readfd); FD_ZERO(&writefd); /* Always set readfd for listening socket */ FD_SET(lfd, &readfd); if (lfd > maxfd) { maxfd = lfd; } /* Check timeouts and set readfd and writefd for connections as required. We use connsTemp so that removal on error from the active iteration list doesn't interfere with list traversal */ psGetTime(&now); while (!DLListIsEmpty(&g_conns)) { pList = DLListGetHead(&g_conns); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&connsTmp, &cp->List); /* If timeout != 0 msec ith no new data, close */ if (cp->timeout && (psDiffMsecs(cp->time, now) > (int32)cp->timeout)) { closeConn(cp, PS_TIMEOUT_FAIL); continue; /* Next connection */ } /* Always select for read */ FD_SET(cp->fd, &readfd); /* Select for write if there's pending write data or connection */ if (matrixSslGetOutdata(cp->ssl, NULL) > 0) { FD_SET(cp->fd, &writefd); } /* Housekeeping for maxsock in select call */ if (cp->fd > maxfd) { maxfd = cp->fd; } } /* Use select to check for events on the sockets */ if ((val = select(maxfd + 1, &readfd, &writefd, NULL, &timeout)) <= 0) { /* On error, restore global connections list */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); } /* Select timeout */ if (val == 0) { return PS_TIMEOUT_FAIL; } /* Woke due to interrupt */ if (SOCKET_ERRNO == EINTR) { return PS_TIMEOUT_FAIL; } /* Should attempt to handle more errnos, such as EBADF */ return PS_PLATFORM_FAIL; } /* Check listener for new incoming socket connections */ if (FD_ISSET(lfd, &readfd)) { for (acceptSanity = 0; acceptSanity < ACCEPT_QUEUE; acceptSanity++) { fd = accept(lfd, NULL, NULL); if (fd == INVALID_SOCKET) { break; /* Nothing more to accept; next listener */ } setSocketOptions(fd); cp = malloc(sizeof(httpConn_t)); if ((rc = matrixSslNewServerSession(&cp->ssl, keys, certCb)) < 0) { close(fd); fd = INVALID_SOCKET; continue; } cp->fd = fd; fd = INVALID_SOCKET; cp->timeout = SSL_TIMEOUT; psGetTime(&cp->time); cp->parsebuf = NULL; cp->parsebuflen = 0; DLListInsertTail(&connsTmp, &cp->List); /* Fake that there is read data available, no harm if there isn't */ FD_SET(cp->fd, &readfd); /* _psTraceInt("=== New Client %d ===\n", cp->fd); */ } } /* Check each connection for read/write activity */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); rSanity = wSanity = 0; /* See if there's pending data to send on this connection We could use FD_ISSET, but this is more reliable for the current state of data to send. */ WRITE_MORE: if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) { /* Could get a EWOULDBLOCK since we don't check FD_ISSET */ transferred = send(cp->fd, buf, len, MSG_DONTWAIT); if (transferred <= 0) { #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); continue; /* Next connection */ } } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if (rc == MATRIXSSL_REQUEST_CLOSE) { closeConn(cp, MATRIXSSL_REQUEST_CLOSE); continue; /* Next connection */ } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If the protocol is server initiated, send data here */ #ifdef ENABLE_FALSE_START /* OR this could be a Chrome browser using FALSE_START and the application data is already waiting in our inbuf for processing */ if ((rc = matrixSslReceivedData(cp->ssl, 0, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } if (rc > 0) { /* There was leftover data */ goto PROCESS_MORE; } #endif /* ENABLE_FALSE_START */ } /* Update activity time */ psGetTime(&cp->time); /* Try to send again if more data to send */ if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) { if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; } } } else if (len < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } /* Check the file descriptor returned from select to see if the connection has data to be read */ if (FD_ISSET(cp->fd, &readfd)) { READ_MORE: /* Get the ssl buffer and how much data it can accept */ /* Note 0 is a return failure, unlike with matrixSslGetOutdata */ if ((len = matrixSslGetReadbuf(cp->ssl, &buf)) <= 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if ((transferred = recv(cp->fd, buf, len, MSG_DONTWAIT)) < 0) { /* We could get EWOULDBLOCK despite the FD_ISSET on goto */ #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); } continue; /* Next connection */ } /* If EOF, remote socket closed. This is semi-normal closure. Officially, we should close on closure alert. */ if (transferred == 0) { /* psTraceIntInfo("Closing connection %d on EOF\n", cp->fd); */ closeConn(cp, 0); continue; /* Next connection */ } /* Notify SSL state machine that we've received more data into the ssl buffer retreived with matrixSslGetReadbuf. */ if ((rc = matrixSslReceivedData(cp->ssl, (int32)transferred, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } /* Update activity time */ psGetTime(&cp->time); PROCESS_MORE: /* Process any incoming plaintext application data */ switch (rc) { case MATRIXSSL_HANDSHAKE_COMPLETE: /* If the protocol is server initiated, send data here */ goto READ_MORE; case MATRIXSSL_APP_DATA: /* Remember, must handle if len == 0! */ if ((rc = httpBasicParse(cp, buf, len)) < 0) { _psTrace("Couldn't parse HTTP data. Closing conn.\n"); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } if (rc == HTTPS_COMPLETE) { if (httpWriteResponse(cp->ssl) < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* For HTTP, we assume no pipelined requests, so we close after parsing a single HTTP request */ /* Ignore return of closure alert, it's optional */ matrixSslEncodeClosureAlert(cp->ssl); rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len); if (rc > 0) { /* Additional data is available, but we ignore it */ _psTrace("HTTP data parsing not supported, ignoring.\n"); closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } else if (rc < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* rc == 0, write out our response and closure alert */ goto WRITE_MORE; } /* We processed a partial HTTP message */ if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { goto READ_MORE; } goto PROCESS_MORE; case MATRIXSSL_REQUEST_SEND: /* Prevent us from reading again after the write, although that wouldn't be the end of the world */ FD_CLR(cp->fd, &readfd); if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; break; case MATRIXSSL_REQUEST_RECV: if (rSanity++ < GOTO_SANITY) goto READ_MORE; break; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*buf == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Fatal alert: %d, closing connection.\n", *(buf + 1)); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } psTraceIntInfo("Warning alert: %d\n", *(buf + 1)); if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { /* No more data in buffer. Might as well read for more. */ goto READ_MORE; } goto PROCESS_MORE; default: /* If rc <= 0 we fall here */ closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Always try to read more if we processed some data */ if (rSanity++ < GOTO_SANITY) goto READ_MORE; } /* readfd handling */ } /* connection loop */ return PS_SUCCESS; } /******************************************************************************/ /* Create an HTTP response and encode it to the SSL buffer */ #define TEST_SIZE 16000 static int32 httpWriteResponse(ssl_t *cp) { unsigned char *buf; int32 available; if ((available = matrixSslGetWritebuf(cp, &buf, strlen((char *)g_httpResponseHdr) + 1)) < 0) { return PS_MEM_FAIL; } strncpy((char *)buf, (char *)g_httpResponseHdr, available); if (matrixSslEncodeWritebuf(cp, strlen((char *)buf)) < 0) { return PS_MEM_FAIL; } return MATRIXSSL_REQUEST_SEND; } /******************************************************************************/ /* Main non-blocking SSL server Initialize MatrixSSL and sockets layer, and loop on select */ int32 main(int32 argc, char **argv) { sslKeys_t *keys; SOCKET lfd; int32 err, rc; #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif keys = NULL; DLListInit(&g_conns); g_exitFlag = 0; lfd = INVALID_SOCKET; #ifdef POSIX if (sighandlers() < 0) { return PS_PLATFORM_FAIL; } #endif /* POSIX */ if ((rc = matrixSslOpen()) < 0) { _psTrace("MatrixSSL library init failure. Exiting\n"); return rc; } if (matrixSslNewKeys(&keys) < 0) { _psTrace("MatrixSSL library key init failure. Exiting\n"); return -1; } #ifdef USE_HEADER_KEYS /* In-memory based keys */ if ((rc = matrixSslLoadRsaKeysMem(keys, certSrvBuf, sizeof(certSrvBuf), privkeySrvBuf, sizeof(privkeySrvBuf), NULL, 0)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #else /* USE_HEADER_KEYS */ /* File based keys */ if ((rc = matrixSslLoadRsaKeys(keys, certSrvFile, privkeySrvFile, NULL, NULL)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif /* USE_HEADER_KEYS */ /* Create the listening socket that will accept incoming connections */ if ((lfd = socketListen(HTTPS_PORT, &err)) == INVALID_SOCKET) { _psTraceInt("Can't listen on port %d\n", HTTPS_PORT); goto L_EXIT; } /* Main select loop to handle sockets events */ while (!g_exitFlag) { selectLoop(keys, lfd); } L_EXIT: if (lfd != INVALID_SOCKET) close(lfd); if (keys) matrixSslDeleteKeys(keys); matrixSslClose(); return 0; }
int main(int argc, char **argv) { int32 id; sslConn_t *svrConn, *clnConn; #ifdef ENABLE_PERF_TIMING int32 perfIter; uint32 clnTime, svrTime; #endif /* ENABLE_PERF_TIMING */ if (matrixSslOpen() < 0) { fprintf(stderr, "matrixSslOpen failed, exiting..."); } svrConn = psMalloc(PEERSEC_NO_POOL, sizeof(sslConn_t)); clnConn = psMalloc(PEERSEC_NO_POOL, sizeof(sslConn_t)); memset(svrConn, 0, sizeof(sslConn_t)); memset(clnConn, 0, sizeof(sslConn_t)); for (id = 0; ciphers[id].cipherId > 0; id++) { matrixSslInitSessionId(clientSessionId); _psTraceStr("Testing %s suite\n", ciphers[id].name); /* Standard Handshake */ _psTrace(" Standard handshake test\n"); #ifdef ENABLE_PERF_TIMING /* Each matrixSsl call in the handshake is wrapped by a timer. The data exchange phase is not being included in the time */ clnTime = svrTime = 0; for (perfIter = 0; perfIter < CONN_ITER; perfIter++) { #endif /* ENABLE_PERF_TIMING */ if (initializeHandshake(clnConn, svrConn, ciphers[id], &clientSessionId) < 0) { _psTrace(" FAILED: initializing Standard handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Standard handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Standard handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } #ifdef ENABLE_PERF_TIMING clnTime += clnConn->runningTime; svrTime += svrConn->runningTime; /* Have to reset conn for full handshake... except last time through */ if (perfIter + 1 != CONN_ITER) { matrixSslDeleteSession(clnConn->ssl); matrixSslDeleteSession(svrConn->ssl); matrixSslInitSessionId(clientSessionId); } } /* iteration loop close */ _psTraceInt("CLIENT: %d " TIME_UNITS, (int32)clnTime/CONN_ITER); _psTraceInt("SERVER: %d " TIME_UNITS, (int32)svrTime/CONN_ITER); // _psTrace("Press any key to continue tests"); _psTrace("\n==========\n"); // getchar(); #endif /* ENABLE_PERF_TIMING */ #ifdef SSL_REHANDSHAKES_ENABLED /* Re-Handshake (full handshake over existing connection) */ _psTrace(" Re-handshake test (client-initiated)\n"); if (initializeReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: initializing Re-handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } #else _psTrace(" Re-handshake tests are disabled (ENABLE_SECURE_REHANDSHAKES)\n"); #endif /* Resumed handshake (fast handshake over new connection) */ _psTrace(" Resumed handshake test (new connection)\n"); #ifdef ENABLE_PERF_TIMING clnTime = svrTime = 0; for (perfIter = 0; perfIter < CONN_ITER; perfIter++) { #endif /* ENABLE_PERF_TIMING */ if (initializeResumedHandshake(clnConn, svrConn, ciphers[id]) < 0) { _psTrace(" FAILED: initializing Resumed handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Resumed handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Resumed handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } #ifdef ENABLE_PERF_TIMING clnTime += clnConn->runningTime; svrTime += svrConn->runningTime; /* Have to reset conn for full handshake */ } /* iteration loop */ _psTraceInt("CLIENT: %d " TIME_UNITS, (int32)clnTime/CONN_ITER); _psTraceInt("SERVER: %d " TIME_UNITS, (int32)svrTime/CONN_ITER); _psTrace("Press any key to continue tests"); _psTrace("\n==========\n"); // getchar(); #endif /* ENABLE_PERF_TIMING */ #ifdef SSL_REHANDSHAKES_ENABLED /* Re-handshake initiated by server (full handshake over existing conn) */ _psTrace(" Re-handshake test (server initiated)\n"); if (initializeServerInitiatedReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: initializing Re-handshake\n"); goto LBL_FREE; } if (performHandshake(svrConn, clnConn) < 0) { _psTrace(" FAILED: Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } /* Resumed re-handshake (fast handshake over existing connection) */ _psTrace(" Resumed Re-handshake test (client initiated)\n"); if (initializeResumedReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: initializing Resumed Re-handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Resumed Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Resumed Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } /* Resumed re-handshake initiated by server (fast handshake over conn) */ _psTrace(" Resumed Re-handshake test (server initiated)\n"); if (initializeServerInitiatedResumedReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: initializing Resumed Re-handshake\n"); goto LBL_FREE; } if (performHandshake(svrConn, clnConn) < 0) { _psTrace(" FAILED: Resumed Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Resumed Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } /* Re-handshaking with "upgraded" parameters */ _psTrace(" Change cert callback Re-handshake test\n"); if (initializeUpgradeCertCbackReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: init upgrade certCback Re-handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Upgrade cert callback Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Upgrade cert callback Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } /* Upgraded keys */ _psTrace(" Change keys Re-handshake test\n"); if (initializeUpgradeKeysReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: init upgrade keys Re-handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Upgrade keys Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Upgrade keys Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } /* Change cipher spec test. Changing to a hardcoded RSA suite so this will not work on suites that don't have RSA material loaded */ if (ciphers[id].rsa == 1) { _psTrace(" Change cipher suite Re-handshake test\n"); if (initializeChangeCipherReHandshake(clnConn, svrConn, ciphers[id].cipherId) < 0) { _psTrace(" FAILED: init change cipher Re-handshake\n"); goto LBL_FREE; } if (performHandshake(clnConn, svrConn) < 0) { _psTrace(" FAILED: Change cipher suite Re-handshake\n"); goto LBL_FREE; } else { testTrace(" PASSED: Change cipher suite Re-handshake"); if (exchangeAppData(clnConn, svrConn) < 0) { _psTrace(" but FAILED to exchange application data\n"); } else { testTrace("\n"); } } } #endif /* !SSL_REHANDSHAKES_ENABLED */ LBL_FREE: freeSessionAndConnection(svrConn); freeSessionAndConnection(clnConn); } psFree(svrConn); psFree(clnConn); matrixSslClose(); #ifdef WIN32 _psTrace("Press any key to close"); getchar(); #endif return PS_SUCCESS; }
int main(int argc, char **argv) { psPool_t *pool, *misc; psDhParams_t dhParams; psDhKey_t dhKeyPriv, dhKeyPub; uint16_t pLen, gLen; unsigned char *p, *g; psTime_t start, end; uint16_t iter, i = 0; unsigned char out[512]; uint16_t outLen = sizeof(out); pool = misc = NULL; if (psCryptoOpen(PSCRYPTO_CONFIG) < PS_SUCCESS) { _psTrace("Failed to initialize library: psCryptoOpen failed\n"); return -1; } _psTraceStr("STARTING DHPERF\n", NULL); while (keys[i].key != NULL) { _psTraceStr("Test %s...\n", keys[i].name); pkcs3ParseDhParamBin(misc, (unsigned char*)keys[i].key, keys[i].len, &dhParams); iter = 0; #ifdef DO_GEN_INTS psGetTime(&start, NULL); while (iter < keys[i].iter) { if (psDhGenKeyInts(pool, dhParams.size, &dhParams.p, &dhParams.g, &dhKeyPriv, NULL) < 0) { _psTrace(" FAILED OPERATION\n"); } psDhClearKey(&dhKeyPriv); iter++; } psGetTime(&end, NULL); _psTraceInt(TIME_UNITS " genInts\n", psDiffMsecs(start, end, NULL)); #endif /* DO_GEN_INTS */ #ifdef DO_GEN_SECRET psDhExportParameters(misc, &dhParams, &p, &pLen, &g, &gLen); if (psDhGenKeyInts(misc, dhParams.size, &dhParams.p, &dhParams.g, &dhKeyPriv, NULL) < 0) { _psTrace(" FAILED OPERATION\n"); } if (psDhGenKeyInts(misc, dhParams.size, &dhParams.p, &dhParams.g, &dhKeyPub, NULL) < 0) { _psTrace(" FAILED OPERATION\n"); } iter = 0; outLen = sizeof(out); psGetTime(&start, NULL); while (iter < keys[i].iter) { if (psDhGenSharedSecret(pool, &dhKeyPriv, &dhKeyPub, p, pLen, out, &outLen, NULL) < 0) { _psTrace(" FAILED OPERATION\n"); } iter++; } psGetTime(&end, NULL); psDhClearKey(&dhKeyPriv); psDhClearKey(&dhKeyPub); _psTraceInt(TIME_UNITS " genSecret\n", psDiffMsecs(start, end, NULL)); #endif /* DO_GEN_SECRET */ psFree(p, misc); psFree(g, misc); pkcs3ClearDhParams(&dhParams); i++; } #ifdef WIN32 _psTrace("Press any key to close"); getchar(); #endif _psTraceStr("FINISHED DHPERF\n", NULL); psCryptoClose(); return 0; }
/* Make a secure HTTP request to a defined IP and port Connection is made in blocking socket mode The connection is considered successful if the SSL/TLS session is negotiated successfully, a request is sent, and a HTTP response is received. */ static int32 httpsClientConnection(sslKeys_t *keys, sslSessionId_t *sid) { int32 rc, transferred, len, complete; ssl_t *ssl; unsigned char *buf; httpConn_t cp; SOCKET fd; complete = 0; memset(&cp, 0x0, sizeof(httpConn_t)); fd = socketConnect(HTTPS_IP, HTTPS_PORT, &rc); if (fd == INVALID_SOCKET || rc != PS_SUCCESS) { _psTraceInt("Connect failed: %d. Exiting\n", rc); return PS_PLATFORM_FAIL; } rc = matrixSslNewClientSession(&ssl, keys, sid, 0, certCb, NULL, NULL); if (rc != MATRIXSSL_REQUEST_SEND) { _psTraceInt("New Client Session Failed: %d. Exiting\n", rc); close(fd); return PS_ARG_FAIL; } WRITE_MORE: while ((len = matrixSslGetOutdata(ssl, &buf)) > 0) { transferred = send(fd, buf, len, 0); if (transferred <= 0) { goto L_CLOSE_ERR; } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(ssl, transferred)) < 0) { goto L_CLOSE_ERR; } if (rc == MATRIXSSL_REQUEST_CLOSE) { closeConn(ssl, fd); return MATRIXSSL_SUCCESS; } if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If we sent the Finished SSL message, initiate the HTTP req */ /* (This occurs on a resumption handshake) */ if (httpWriteRequest(ssl) < 0) { goto L_CLOSE_ERR; } goto WRITE_MORE; } /* SSL_REQUEST_SEND is handled by loop logic */ } } READ_MORE: if ((len = matrixSslGetReadbuf(ssl, &buf)) <= 0) { goto L_CLOSE_ERR; } if ((transferred = recv(fd, buf, len, 0)) < 0) { goto L_CLOSE_ERR; } /* If EOF, remote socket closed. But we haven't received the HTTP response so we consider it an error in the case of an HTTP client */ if (transferred == 0) { goto L_CLOSE_ERR; } if ((rc = matrixSslReceivedData(ssl, (int32)transferred, &buf, (uint32*)&len)) < 0) { goto L_CLOSE_ERR; } PROCESS_MORE: switch (rc) { case MATRIXSSL_HANDSHAKE_COMPLETE: #ifdef REHANDSHAKE_TEST /* Test rehandshake capabilities of server. If a successful session resmption rehandshake occurs, this client will be last to send handshake data and MATRIXSSL_HANDSHAKE_COMPLETE will hit on the WRITE_MORE handler and httpWriteRequest will occur there. NOTE: If the server doesn't support session resumption it is possible to fall into an endless rehandshake loop */ if (matrixSslEncodeRehandshake(ssl, NULL, NULL, 0, 0) < 0) { goto L_CLOSE_ERR; } #else /* We got the Finished SSL message, initiate the HTTP req */ if (httpWriteRequest(ssl) < 0) { goto L_CLOSE_ERR; } #endif goto WRITE_MORE; case MATRIXSSL_APP_DATA: if ((rc = httpBasicParse(&cp, buf, len)) < 0) { closeConn(ssl, fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; return MATRIXSSL_ERROR; } if (rc == HTTPS_COMPLETE) { rc = matrixSslProcessedData(ssl, &buf, (uint32*)&len); closeConn(ssl, fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; if (rc < 0) { return MATRIXSSL_ERROR; } else { if (rc > 0) { _psTrace("HTTP data parsing not supported, ignoring.\n"); } _psTrace("SUCCESS: Received HTTP Response\n"); return MATRIXSSL_SUCCESS; } } /* We processed a partial HTTP message */ if ((rc = matrixSslProcessedData(ssl, &buf, (uint32*)&len)) == 0) { goto READ_MORE; } goto PROCESS_MORE; case MATRIXSSL_REQUEST_SEND: goto WRITE_MORE; case MATRIXSSL_REQUEST_RECV: goto READ_MORE; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*buf == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Fatal alert: %d, closing connection.\n", *(buf + 1)); goto L_CLOSE_ERR; } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { closeConn(ssl, fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; return MATRIXSSL_SUCCESS; } psTraceIntInfo("Warning alert: %d\n", *(buf + 1)); if ((rc = matrixSslProcessedData(ssl, &buf, (uint32*)&len)) == 0) { /* No more data in buffer. Might as well read for more. */ goto READ_MORE; } goto PROCESS_MORE; default: /* If rc <= 0 we fall here */ goto L_CLOSE_ERR; } L_CLOSE_ERR: _psTrace("FAIL: No HTTP Response\n"); matrixSslDeleteSession(ssl); close(fd); if (cp.parsebuf) free(cp.parsebuf); cp.parsebuf = NULL; cp.parsebuflen = 0; return MATRIXSSL_ERROR; }
/* Example callback to show possiblie outcomes of certificate validation. If this callback is not registered in matrixSslNewServerSession the connection will be accepted or closed based on the alert value. */ static int32 certCb(ssl_t *ssl, psX509Cert_t *cert, int32 alert) { #ifndef USE_ONLY_PSK_CIPHER_SUITE psX509Cert_t *next; /* Did we even find a CA that issued the certificate? */ if (alert == SSL_ALERT_UNKNOWN_CA) { /* Example to allow anonymous connections based on a define */ if (ALLOW_ANON_CONNECTIONS) { _psTraceStr("Allowing anonymous connection for: %s.\n", cert->subject.commonName); return SSL_ALLOW_ANON_CONNECTION; } _psTrace("ERROR: No matching CA found. Terminating connection\n"); } /* Test if the server certificate didn't match the name passed to expectedName in matrixSslNewClientSession */ if (alert == SSL_ALERT_CERTIFICATE_UNKNOWN) { _psTraceStr("ERROR: %s not found in cert subject names\n", ssl->expectedName); } if (alert == SSL_ALERT_CERTIFICATE_EXPIRED) { #ifdef POSIX _psTrace("ERROR: A cert did not fall within the notBefore/notAfter window\n"); #else _psTrace("WARNING: Certificate date window validation not implemented\n"); alert = 0; #endif } if (alert == SSL_ALERT_ILLEGAL_PARAMETER) { _psTrace("ERROR: Found correct CA but X.509 extension details are wrong\n"); } /* Key usage related problems */ next = cert; while (next) { if (next->authStatus == PS_CERT_AUTH_FAIL_EXTENSION) { if (cert->authFailFlags & PS_CERT_AUTH_FAIL_KEY_USAGE_FLAG) { _psTrace("CA keyUsage extension doesn't allow cert signing\n"); } if (cert->authFailFlags & PS_CERT_AUTH_FAIL_EKU_FLAG) { _psTrace("Cert extendedKeyUsage extension doesn't allow TLS\n"); } } next = next->next; } if (alert == SSL_ALERT_BAD_CERTIFICATE) { /* Should never let a connection happen if this is set. There was either a problem in the presented chain or in the final CA test */ _psTrace("ERROR: Problem in certificate validation. Exiting.\n"); } if (alert == 0) _psTraceStr("SUCCESS: Validated cert for: %s.\n", cert->subject.commonName); #endif /* !USE_ONLY_PSK_CIPHER_SUITE */ return alert; }
/* Example callback to do additional certificate validation. If this callback is not registered in matrixSslNewService, the connection will be accepted or closed based on the status flag. */ static int32 certCb(ssl_t *ssl, psX509Cert_t *cert, int32 alert) { #ifdef POSIX struct tm t; time_t rawtime; char *c; int y, m, d; #endif /* Example to allow anonymous connections based on a define */ if (alert > 0) { if (ALLOW_ANON_CONNECTIONS) { _psTraceStr("Allowing anonymous connection for: %s.\n", cert->subject.commonName); return SSL_ALLOW_ANON_CONNECTION; } _psTrace("Certificate callback returning fatal alert\n"); return alert; } #ifdef POSIX /* Validate the dates in the cert */ time(&rawtime); localtime_r(&rawtime, &t); /* Localtime does months from 0-11 and (year-1900)! Normalize it. */ t.tm_mon++; t.tm_year += 1900; /* Validate the 'not before' date */ if ((c = cert->notBefore) != NULL) { if (strlen(c) < 8) { return PS_FAILURE; } /* UTCTIME, defined in 1982, has just a 2 digit year */ if (cert->timeType == ASN_UTCTIME) { y = 2000 + 10 * (c[0] - '0') + (c[1] - '0'); c += 2; } else { y = 1000 * (c[0] - '0') + 100 * (c[1] - '0') + 10 * (c[2] - '0') + (c[3] - '0'); c += 4; } m = 10 * (c[0] - '0') + (c[1] - '0'); c += 2; d = 10 * (c[0] - '0') + (c[1] - '0'); if (t.tm_year < y) return PS_FAILURE; if (t.tm_year == y) { if (t.tm_mon < m) return PS_FAILURE; if (t.tm_mon == m && t.tm_mday < d) return PS_FAILURE; } /* _psTraceStr("Validated notBefore: %s\n", cert->notBefore); */ } /* Validate the 'not after' date */ if ((c = cert->notAfter) != NULL) { if (strlen(c) < 8) { return PS_FAILURE; } /* UTCTIME, defined in 1982 has just a 2 digit year */ if (cert->timeType == ASN_UTCTIME) { y = 2000 + 10 * (c[0] - '0') + (c[1] - '0'); c += 2; } else { y = 1000 * (c[0] - '0') + 100 * (c[1] - '0') + 10 * (c[2] - '0') + (c[3] - '0'); c += 4; } m = 10 * (c[0] - '0') + (c[1] - '0'); c += 2; d = 10 * (c[0] - '0') + (c[1] - '0'); if (t.tm_year > y) return PS_FAILURE; if (t.tm_year == y) { if (t.tm_mon > m) return PS_FAILURE; if (t.tm_mon == m && t.tm_mday > d) return PS_FAILURE; } /* _psTraceStr("Validated notAfter: %s\n", cert->notAfter); */ } #endif /* POSIX */ _psTraceStr("Validated cert for: %s.\n", cert->subject.commonName); return PS_SUCCESS; }
/* Non-blocking socket event handler Wait one time in select for events on any socket This will accept new connections, read and write to sockets that are connected, and close sockets as required. */ static int32 selectLoop(sslKeys_t *keys, SOCKET lfd) { httpConn_t *cp; psTime_t now; DLListEntry connsTmp; DLListEntry *pList; fd_set readfd, writefd; struct timeval timeout; SOCKET fd, maxfd; unsigned char *buf; int32 rc, len, transferred, val; unsigned char rSanity, wSanity, acceptSanity; sslSessOpts_t options; DLListInit(&connsTmp); rc = PS_SUCCESS; maxfd = INVALID_SOCKET; timeout.tv_sec = SELECT_TIME / 1000; timeout.tv_usec = (SELECT_TIME % 1000) * 1000; FD_ZERO(&readfd); FD_ZERO(&writefd); /* Always set readfd for listening socket */ FD_SET(lfd, &readfd); if (lfd > maxfd) { maxfd = lfd; } /* Check timeouts and set readfd and writefd for connections as required. We use connsTemp so that removal on error from the active iteration list doesn't interfere with list traversal */ psGetTime(&now, NULL); while (!DLListIsEmpty(&g_conns)) { pList = DLListGetHead(&g_conns); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&connsTmp, &cp->List); /* If timeout != 0 msec ith no new data, close */ if (cp->timeout && (psDiffMsecs(cp->time, now, NULL) > (int32)cp->timeout)) { closeConn(cp, PS_TIMEOUT_FAIL); continue; /* Next connection */ } /* Always select for read */ FD_SET(cp->fd, &readfd); /* Select for write if there's pending write data or connection */ if (matrixSslGetOutdata(cp->ssl, NULL) > 0) { FD_SET(cp->fd, &writefd); } /* Housekeeping for maxsock in select call */ if (cp->fd > maxfd) { maxfd = cp->fd; } } /* Use select to check for events on the sockets */ if ((val = select(maxfd + 1, &readfd, &writefd, NULL, &timeout)) <= 0) { /* On error, restore global connections list */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); } /* Select timeout */ if (val == 0) { return PS_TIMEOUT_FAIL; } /* Woke due to interrupt */ if (SOCKET_ERRNO == EINTR) { return PS_TIMEOUT_FAIL; } /* Should attempt to handle more errnos, such as EBADF */ return PS_PLATFORM_FAIL; } /* Check listener for new incoming socket connections */ if (FD_ISSET(lfd, &readfd)) { for (acceptSanity = 0; acceptSanity < ACCEPT_QUEUE; acceptSanity++) { fd = accept(lfd, NULL, NULL); if (fd == INVALID_SOCKET) { break; /* Nothing more to accept; next listener */ } setSocketOptions(fd); cp = malloc(sizeof(httpConn_t)); memset(cp, 0x0, sizeof(httpConn_t)); memset(&options, 0x0, sizeof(sslSessOpts_t)); options.versionFlag = g_proto; options.userPtr = keys; /* Just a test */ //options.truncHmac = -1; //options.maxFragLen = -1; //options.ecFlags |= SSL_OPT_SECP521R1; //options.ecFlags |= SSL_OPT_SECP224R1; //options.ecFlags |= SSL_OPT_SECP384R1; if ((rc = matrixSslNewServerSession(&cp->ssl, keys, certCb, &options)) < 0) { close(fd); fd = INVALID_SOCKET; continue; } #ifdef USE_SERVER_NAME_INDICATION /* Register extension callbacks to manage client connection opts */ matrixSslRegisterSNICallback(cp->ssl, SNI_callback); #endif #ifdef USE_ALPN matrixSslRegisterALPNCallback(cp->ssl, ALPN_callback); #endif cp->fd = fd; fd = INVALID_SOCKET; cp->timeout = SSL_TIMEOUT; psGetTime(&cp->time, NULL); cp->parsebuf = NULL; cp->parsebuflen = 0; DLListInsertTail(&connsTmp, &cp->List); /* Fake that there is read data available, no harm if there isn't */ FD_SET(cp->fd, &readfd); /* _psTraceInt("=== New Client %d ===\n", cp->fd); */ } } /* Check each connection for read/write activity */ while (!DLListIsEmpty(&connsTmp)) { pList = DLListGetHead(&connsTmp); cp = DLListGetContainer(pList, httpConn_t, List); DLListInsertTail(&g_conns, &cp->List); rSanity = wSanity = 0; /* See if there's pending data to send on this connection We could use FD_ISSET, but this is more reliable for the current state of data to send. */ WRITE_MORE: if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) { /* Could get a EWOULDBLOCK since we don't check FD_ISSET */ transferred = send(cp->fd, buf, len, MSG_DONTWAIT); if (transferred <= 0) { #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); continue; /* Next connection */ } } else { /* Indicate that we've written > 0 bytes of data */ if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if (rc == MATRIXSSL_REQUEST_CLOSE) { closeConn(cp, MATRIXSSL_REQUEST_CLOSE); continue; /* Next connection */ } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* If the protocol is server initiated, send data here */ #ifdef ENABLE_FALSE_START /* OR this could be a Chrome browser using FALSE_START and the application data is already waiting in our inbuf for processing */ if ((rc = matrixSslReceivedData(cp->ssl, 0, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } if (rc > 0) { /* There was leftover data */ goto PROCESS_MORE; } #endif /* ENABLE_FALSE_START */ } /* Update activity time */ psGetTime(&cp->time, NULL); /* Try to send again if more data to send */ if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) { if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; } } } else if (len < 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } /* Check the file descriptor returned from select to see if the connection has data to be read */ if (FD_ISSET(cp->fd, &readfd)) { READ_MORE: /* Get the ssl buffer and how much data it can accept */ /* Note 0 is a return failure, unlike with matrixSslGetOutdata */ if ((len = matrixSslGetReadbuf(cp->ssl, &buf)) <= 0) { closeConn(cp, PS_ARG_FAIL); continue; /* Next connection */ } if ((transferred = recv(cp->fd, buf, len, MSG_DONTWAIT)) < 0) { /* We could get EWOULDBLOCK despite the FD_ISSET on goto */ #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAEWOULDBLOCK) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif closeConn(cp, PS_PLATFORM_FAIL); } continue; /* Next connection */ } /* If EOF, remote socket closed. This is semi-normal closure. Officially, we should close on closure alert. */ if (transferred == 0) { /* psTraceIntInfo("Closing connection %d on EOF\n", cp->fd); */ closeConn(cp, 0); continue; /* Next connection */ } /* Notify SSL state machine that we've received more data into the ssl buffer retreived with matrixSslGetReadbuf. */ if ((rc = matrixSslReceivedData(cp->ssl, (int32)transferred, &buf, (uint32*)&len)) < 0) { closeConn(cp, 0); continue; /* Next connection */ } /* Update activity time */ psGetTime(&cp->time, NULL); PROCESS_MORE: /* Process any incoming plaintext application data */ switch (rc) { case MATRIXSSL_HANDSHAKE_COMPLETE: /* If the protocol is server initiated, send data here */ goto READ_MORE; case MATRIXSSL_APP_DATA: case MATRIXSSL_APP_DATA_COMPRESSED: //psTraceBytes("DATA", buf, len); /* Remember, must handle if len == 0! */ if ((rc = httpBasicParse(cp, buf, len, 0)) < 0) { _psTrace("Couldn't parse HTTP data. Closing conn.\n"); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } if (cp->parsebuf != NULL) { /* Test for one of our custom testing messages */ if (strncmp((const char*)cp->parsebuf, "MATRIX_SHUTDOWN", 15) == 0) { g_exitFlag = 1; matrixSslEncodeClosureAlert(cp->ssl); _psTrace("Got MATRIX_SHUTDOWN. Exiting\n"); goto WRITE_MORE; } } /* reply to /bytes?<byte count> syntax */ if (len > 11 && strncmp((char *)buf, "GET /bytes?", 11) == 0) { cp->bytes_requested = atoi((char *)buf + 11); if (cp->bytes_requested < strlen((char *)g_httpResponseHdr) || cp->bytes_requested > 1073741824) { cp->bytes_requested = strlen((char *)g_httpResponseHdr); } cp->bytes_sent = 0; } if (rc == HTTPS_COMPLETE) { if (httpWriteResponse(cp) < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* For HTTP, we assume no pipelined requests, so we close after parsing a single HTTP request */ /* Ignore return of closure alert, it's optional */ matrixSslEncodeClosureAlert(cp->ssl); rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len); if (rc > 0) { /* Additional data is available, but we ignore it */ _psTrace("HTTP data parsing not supported, ignoring.\n"); closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } else if (rc < 0) { closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* rc == 0, write out our response and closure alert */ goto WRITE_MORE; } /* We processed a partial HTTP message */ if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { goto READ_MORE; } goto PROCESS_MORE; case MATRIXSSL_REQUEST_SEND: /* Prevent us from reading again after the write, although that wouldn't be the end of the world */ FD_CLR(cp->fd, &readfd); if (wSanity++ < GOTO_SANITY) goto WRITE_MORE; break; case MATRIXSSL_REQUEST_RECV: if (rSanity++ < GOTO_SANITY) goto READ_MORE; break; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*buf == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Fatal alert: %d, closing connection.\n", *(buf + 1)); closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Closure alert is normal (and best) way to close */ if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) { closeConn(cp, PS_SUCCESS); continue; /* Next connection */ } psTraceIntInfo("Warning alert: %d\n", *(buf + 1)); if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len)) == 0) { /* No more data in buffer. Might as well read for more. */ goto READ_MORE; } goto PROCESS_MORE; default: /* If rc <= 0 we fall here */ closeConn(cp, PS_PROTOCOL_FAIL); continue; /* Next connection */ } /* Always try to read more if we processed some data */ if (rSanity++ < GOTO_SANITY) goto READ_MORE; } /* readfd handling */ } /* connection loop */ return PS_SUCCESS; } /******************************************************************************/ /* Create an HTTP response and encode it to the SSL buffer */ #define TEST_SIZE 16000 static int32 httpWriteResponse(httpConn_t *conn) { unsigned char *buf; ssl_t *cp; int32 available, len, rc; cp = conn->ssl; if (conn->bytes_requested) { /* The /bytes? syntax */ while (conn->bytes_sent < conn->bytes_requested) { len = conn->bytes_requested - conn->bytes_sent; if (len > RESPONSE_REC_LEN) { len = RESPONSE_REC_LEN; } psAssert(len > 0); rc = matrixSslGetWritebuf(cp, &buf, len); if (rc < len) { len = rc; /* could have been shortened due to max_frag */ } memset(buf, 'J', len); if (conn->bytes_sent == 0) { /* Overwrite first N bytes with HTTP header the first time */ strncpy((char *)buf, (char *)g_httpResponseHdr, strlen((char*)g_httpResponseHdr)); } if ((rc = matrixSslEncodeWritebuf(cp, len)) < 0) { printf("couldn't encode data %d\n", rc); } conn->bytes_sent += len; } return MATRIXSSL_REQUEST_SEND; } /* Usual reply */ if ((available = matrixSslGetWritebuf(cp, &buf, (uint32)strlen((char *)g_httpResponseHdr) + 1)) < 0) { return PS_MEM_FAIL; } strncpy((char *)buf, (char *)g_httpResponseHdr, available); //psTraceBytes("Replying", buf, (uint32)strlen((char *)buf)); if (matrixSslEncodeWritebuf(cp, (uint32)strlen((char *)buf)) < 0) { return PS_MEM_FAIL; } return MATRIXSSL_REQUEST_SEND; } /******************************************************************************/ /* Main non-blocking SSL server Initialize MatrixSSL and sockets layer, and loop on select */ int32 main(int32 argc, char **argv) { sslKeys_t *keys; SOCKET lfd; unsigned char *CAstream; int32 err, rc, CAstreamLen; #ifdef USE_STATELESS_SESSION_TICKETS unsigned char randKey[16]; #endif #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif keys = NULL; DLListInit(&g_conns); g_exitFlag = 0; lfd = INVALID_SOCKET; #ifdef POSIX if (sighandlers() < 0) { return PS_PLATFORM_FAIL; } #endif /* POSIX */ if ((rc = matrixSslOpen()) < 0) { _psTrace("MatrixSSL library init failure. Exiting\n"); return rc; } if (matrixSslNewKeys(&keys, NULL) < 0) { _psTrace("MatrixSSL library key init failure. Exiting\n"); return -1; } #ifdef USE_STATELESS_SESSION_TICKETS matrixSslSetSessionTicketCallback(keys, sessTicketCb); psGetEntropy(randKey, 16, NULL); if (matrixSslLoadSessionTicketKeys(keys, randKey, sessTicketSymKey, 32, sessTicketMacKey, 32) < 0) { _psTrace("Error loading session ticket encryption key\n"); } #endif #ifdef USE_HEADER_KEYS /* In-memory based keys Build the CA list first for potential client auth usage */ CAstreamLen = 0; #ifdef USE_RSA CAstreamLen += sizeof(RSACAS); #ifdef USE_ECC CAstreamLen += sizeof(ECDHRSACAS); #endif #endif #ifdef USE_ECC CAstreamLen += sizeof(ECCAS); #endif CAstream = psMalloc(NULL, CAstreamLen); CAstreamLen = 0; #ifdef USE_RSA memcpy(CAstream, RSACAS, sizeof(RSACAS)); CAstreamLen += sizeof(RSACAS); #ifdef USE_ECC memcpy(CAstream + CAstreamLen, ECDHRSACAS, sizeof(ECDHRSACAS)); CAstreamLen += sizeof(ECDHRSACAS); #endif #endif #ifdef USE_ECC memcpy(CAstream + CAstreamLen, ECCAS, sizeof(ECCAS)); CAstreamLen += sizeof(ECCAS); #endif #ifdef EXAMPLE_RSA_KEYS if ((rc = matrixSslLoadRsaKeysMem(keys, RSA1024, sizeof(RSA1024), RSA1024KEY, sizeof(RSA1024KEY), CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_ECDH_RSA_KEYS if ((rc = matrixSslLoadEcKeysMem(keys, ECDHRSA256, sizeof(ECDHRSA256), ECDHRSA256KEY, sizeof(ECDHRSA256KEY), CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_EC_KEYS if ((rc = matrixSslLoadEcKeysMem(keys, EC521, sizeof(EC521), EC521KEY, sizeof(EC521KEY), CAstream, CAstreamLen)) < 0) { // if ((rc = matrixSslLoadEcKeysMem(keys, EC256, sizeof(EC256), // EC256KEY, sizeof(EC256KEY), CAstream, CAstreamLen)) < 0) { // if ((rc = matrixSslLoadEcKeysMem(keys, EC192, sizeof(EC192), // EC192KEY, sizeof(EC192KEY), CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef REQUIRE_DH_PARAMS if (matrixSslLoadDhParamsMem(keys, dhParamBuf1024, sizeof(dhParamBuf1024)) < 0){ _psTrace("Unable to load DH parameters\n"); } #endif /* DH_PARAMS */ psFree(CAstream, NULL); #else /* USE_HEADER_KEYS */ /* File based keys Build the CA list first for potential client auth usage */ CAstreamLen = 0; #ifdef USE_RSA CAstreamLen += (int32)strlen(rsaCAFile) + 1; #ifdef USE_ECC CAstreamLen += (int32)strlen(ecdhRsaCAFile) + 1; #endif #endif #ifdef USE_ECC CAstreamLen += (int32)strlen(ecCAFile) + 1; #endif CAstream = psMalloc(NULL, CAstreamLen); memset(CAstream, 0x0, CAstreamLen); CAstreamLen = 0; #ifdef USE_RSA memcpy(CAstream, rsaCAFile, strlen(rsaCAFile)); CAstreamLen += strlen(rsaCAFile); #ifdef USE_ECC memcpy(CAstream + CAstreamLen, ";", 1); CAstreamLen++; memcpy(CAstream + CAstreamLen, ecdhRsaCAFile, strlen(ecdhRsaCAFile)); CAstreamLen += strlen(ecdhRsaCAFile); #endif #endif #ifdef USE_ECC if (CAstreamLen > 0) { memcpy(CAstream + CAstreamLen, ";", 1); CAstreamLen++; } memcpy(CAstream + CAstreamLen, ecCAFile, strlen(ecCAFile)); #endif /* Load Identiy */ #ifdef EXAMPLE_RSA_KEYS if ((rc = matrixSslLoadRsaKeys(keys, rsaCertFile, rsaPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_ECDH_RSA_KEYS if ((rc = matrixSslLoadEcKeys(keys, ecdhRsaCertFile, ecdhRsaPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_EC_KEYS if ((rc = matrixSslLoadEcKeys(keys, ecCertFile, ecPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef REQUIRE_DH_PARAMS if (matrixSslLoadDhParams(keys, dhParamFile) < 0){ _psTrace("Unable to load DH parameters\n"); } #endif psFree(CAstream, NULL); #endif /* USE_HEADER_KEYS */ #ifdef USE_PSK_CIPHER_SUITE /* The first one supports the 15-byte openssl PSK ID */ matrixSslLoadPsk(keys, pskTable[0].key, sizeof(pskTable[0].key), pskTable[rc].id, 15); for (rc = 0; rc < 8; rc++) { matrixSslLoadPsk(keys, pskTable[rc].key, sizeof(pskTable[rc].key), pskTable[rc].id, sizeof(pskTable[rc].id)); } #endif /* PSK */ if (argc == 2) { switch (atoi(argv[1])) { case 0: g_proto = SSL_FLAGS_SSLV3; break; case 1: g_proto = SSL_FLAGS_TLS_1_0; break; case 2: g_proto = SSL_FLAGS_TLS_1_1; break; case 3: g_proto = SSL_FLAGS_TLS_1_2; break; default: g_proto = SSL_FLAGS_TLS_1_0; break; } } else { g_proto = 0; } /* Create the listening socket that will accept incoming connections */ if ((lfd = socketListen(HTTPS_PORT, &err)) == INVALID_SOCKET) { _psTraceInt("Can't listen on port %d\n", HTTPS_PORT); goto L_EXIT; } /* Main select loop to handle sockets events */ while (!g_exitFlag) { selectLoop(keys, lfd); } L_EXIT: if (lfd != INVALID_SOCKET) close(lfd); if (keys) matrixSslDeleteKeys(keys); matrixSslClose(); return 0; }
/* catch any segvs */ static void sigsegv_handler(int arg) { _psTrace("Aiee, segfault! You should probably report " "this as a bug to the developer\n"); exit(EXIT_FAILURE); }
/* Main */ int main(int argc, char ** argv) { struct sockaddr_in inaddr; socklen_t inaddrlen; struct timeval timeout; ssl_t *ssl; serverDtls_t *dtlsCtx; SOCKET sock; fd_set readfd; unsigned char *sslBuf, *recvfromBuf, *CAstream; #ifdef USE_DTLS_DEBUG_TRACE unsigned char *addrstr; #endif #if !defined(ID_PSK) && !defined(ID_DHE_PSK) unsigned char *keyValue, *certValue; int32 keyLen, certLen; #endif sslKeys_t *keys; int32 freeBufLen, rc, val, recvLen, err, CAstreamLen; int32 sslBufLen, rcr, rcs, sendLen, recvfromBufLen; sslSessOpts_t options; #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif rc = 0; ssl = NULL; dtlsCtx = NULL; sock = INVALID_SOCKET; /* parse input arguments */ if (0 != process_cmd_options(argc, argv)) { usage(); return 0; } if (sigsetup() < 0) { _psTrace("Init error creating signal handlers\n"); return DTLS_FATAL; } if (matrixSslOpen() < 0) { _psTrace("Init error opening MatrixDTLS library\n"); return DTLS_FATAL; } if (matrixSslNewKeys(&keys, NULL) < 0) { _psTrace("Init error allocating key structure\n"); matrixSslClose(); return DTLS_FATAL; } if ((rc = initClientList(MAX_CLIENTS)) < 0) { _psTrace("Init error opening client list\n"); goto MATRIX_EXIT; } recvfromBufLen = matrixDtlsGetPmtu(); if ((recvfromBuf = psMalloc(MATRIX_NO_POOL, recvfromBufLen)) == NULL) { rc = PS_MEM_FAIL; _psTrace("Init error allocating receive buffer\n"); goto CLIENT_EXIT; } #ifdef USE_HEADER_KEYS /* In-memory based keys Build the CA list first for potential client auth usage */ CAstreamLen = 0; #ifdef USE_RSA CAstreamLen += sizeof(RSACAS); #ifdef USE_ECC CAstreamLen += sizeof(ECDHRSACAS); #endif #endif #ifdef USE_ECC CAstreamLen += sizeof(ECCAS); #endif CAstream = psMalloc(NULL, CAstreamLen); CAstreamLen = 0; #ifdef USE_RSA memcpy(CAstream, RSACAS, sizeof(RSACAS)); CAstreamLen += sizeof(RSACAS); #ifdef USE_ECC memcpy(CAstream + CAstreamLen, ECDHRSACAS, sizeof(ECDHRSACAS)); CAstreamLen += sizeof(ECDHRSACAS); #endif #endif #ifdef USE_ECC memcpy(CAstream + CAstreamLen, ECCAS, sizeof(ECCAS)); CAstreamLen += sizeof(ECCAS); #endif #ifdef EXAMPLE_RSA_KEYS switch (g_rsaKeySize) { case 1024: certValue = (unsigned char *)RSA1024; certLen = sizeof(RSA1024); keyValue = (unsigned char *)RSA1024KEY; keyLen = sizeof(RSA1024KEY); break; case 2048: certValue = (unsigned char *)RSA2048; certLen = sizeof(RSA2048); keyValue = (unsigned char *)RSA2048KEY; keyLen = sizeof(RSA2048KEY); break; case 3072: certValue = (unsigned char *)RSA3072; certLen = sizeof(RSA3072); keyValue = (unsigned char *)RSA3072KEY; keyLen = sizeof(RSA3072KEY); break; case 4096: certValue = (unsigned char *)RSA4096; certLen = sizeof(RSA4096); keyValue = (unsigned char *)RSA4096KEY; keyLen = sizeof(RSA4096KEY); break; default: _psTraceInt("Invalid RSA key length (%d)\n", g_rsaKeySize); return -1; } if ((rc = matrixSslLoadRsaKeysMem(keys, (const unsigned char *)certValue, certLen, (const unsigned char *)keyValue, keyLen, CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_ECDH_RSA_KEYS switch (g_ecdhKeySize) { case 256: certValue = (unsigned char *)ECDHRSA256; certLen = sizeof(ECDHRSA256); keyValue = (unsigned char *)ECDHRSA256KEY; keyLen = sizeof(ECDHRSA256KEY); break; case 521: certValue = (unsigned char *)ECDHRSA521; certLen = sizeof(ECDHRSA521); keyValue = (unsigned char *)ECDHRSA521KEY; keyLen = sizeof(ECDHRSA521KEY); break; default: _psTraceInt("Invalid ECDH_RSA key length (%d)\n", g_ecdhKeySize); return -1; } if ((rc = matrixSslLoadEcKeysMem(keys, (const unsigned char *)certValue, certLen, (const unsigned char *)keyValue, keyLen, CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_EC_KEYS switch (g_eccKeySize) { case 192: certValue = (unsigned char *)EC192; certLen = sizeof(EC192); keyValue = (unsigned char *)EC192KEY; keyLen = sizeof(EC192KEY); break; case 224: certValue = (unsigned char *)EC224; certLen = sizeof(EC224); keyValue = (unsigned char *)EC224KEY; keyLen = sizeof(EC224KEY); break; case 256: certValue = (unsigned char *)EC256; certLen = sizeof(EC256); keyValue = (unsigned char *)EC256KEY; keyLen = sizeof(EC256KEY); break; case 384: certValue = (unsigned char *)EC384; certLen = sizeof(EC384); keyValue = (unsigned char *)EC384KEY; keyLen = sizeof(EC384KEY); break; case 521: certValue = (unsigned char *)EC521; certLen = sizeof(EC521); keyValue = (unsigned char *)EC521KEY; keyLen = sizeof(EC521KEY); break; default: _psTraceInt("Invalid ECC key length (%d)\n", g_eccKeySize); return -1; } if ((rc = matrixSslLoadEcKeysMem(keys, certValue, certLen, keyValue, keyLen, CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream, NULL); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef REQUIRE_DH_PARAMS if (matrixSslLoadDhParamsMem(keys, DHPARAM2048, DHPARAM2048_SIZE) < 0) { _psTrace("Unable to load DH parameters\n"); } #endif /* DH_PARAMS */ psFree(CAstream, NULL); #else /* USE_HEADER_KEYS */ /* File based keys Build the CA list first for potential client auth usage */ CAstreamLen = 0; #ifdef USE_RSA if (g_rsaKeySize == 3072) CAstreamLen += (int32)strlen(rsaCA3072File) + 1; else CAstreamLen += (int32)strlen(rsaCAFile) + 1; #ifdef USE_ECC CAstreamLen += (int32)strlen(ecdhRsaCAFile) + 1; #endif #endif #ifdef USE_ECC CAstreamLen += (int32)strlen(ecCAFile) + 1; #endif CAstream = psMalloc(NULL, CAstreamLen); memset(CAstream, 0x0, CAstreamLen); CAstreamLen = 0; #ifdef USE_RSA if (g_rsaKeySize == 3072) { memcpy(CAstream, rsaCA3072File, strlen(rsaCA3072File)); CAstreamLen += strlen(rsaCA3072File); } else { memcpy(CAstream, rsaCAFile, strlen(rsaCAFile)); CAstreamLen += strlen(rsaCAFile); } #ifdef USE_ECC memcpy(CAstream + CAstreamLen, ";", 1); CAstreamLen++; memcpy(CAstream + CAstreamLen, ecdhRsaCAFile, strlen(ecdhRsaCAFile)); CAstreamLen += strlen(ecdhRsaCAFile); #endif #endif #ifdef USE_ECC if (CAstreamLen > 0) { memcpy(CAstream + CAstreamLen, ";", 1); CAstreamLen++; } memcpy(CAstream + CAstreamLen, ecCAFile, strlen(ecCAFile)); #endif /* Load Identiy */ #ifdef EXAMPLE_RSA_KEYS if ((rc = matrixSslLoadRsaKeys(keys, rsaCertFile, rsaPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_ECDH_RSA_KEYS if ((rc = matrixSslLoadEcKeys(keys, ecdhRsaCertFile, ecdhRsaPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef EXAMPLE_EC_KEYS if ((rc = matrixSslLoadEcKeys(keys, ecCertFile, ecPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif #ifdef REQUIRE_DH_PARAMS if (matrixSslLoadDhParams(keys, dhParamFile) < 0) { _psTrace("Unable to load DH parameters\n"); } #endif psFree(CAstream); #endif /* USE_HEADER_KEYS */ #ifdef USE_PSK_CIPHER_SUITE /* The first ID is considered as null-terminiated string for compatibility with OpenSSL's s_client default client identity "Client_identity" */ matrixSslLoadPsk(keys, PSK_HEADER_TABLE[0].key, sizeof(PSK_HEADER_TABLE[0].key), PSK_HEADER_TABLE[0].id, strlen((const char *)PSK_HEADER_TABLE[0].id)); for (rc = 1; rc < PSK_HEADER_TABLE_COUNT; rc++) { matrixSslLoadPsk(keys, PSK_HEADER_TABLE[rc].key, sizeof(PSK_HEADER_TABLE[rc].key), PSK_HEADER_TABLE[rc].id, sizeof(PSK_HEADER_TABLE[rc].id)); } #endif /* PSK */ if ((sock = newUdpSocket(NULL, DTLS_PORT, &err)) == INVALID_SOCKET) { _psTrace("Error creating UDP socket\n"); goto DTLS_EXIT; } _psTraceInt("DTLS server running on port %d\n", DTLS_PORT); /* Server loop */ for (exitFlag = 0; exitFlag == 0;) { timeout.tv_sec = 1; timeout.tv_usec = 0; FD_ZERO(&readfd); FD_SET(sock, &readfd); /* Always just wait a second for any incoming data. The primary loop mechanism reads data from one source and replies with handshake data if needed (that reply may be a resend if reading a repeat message). Individual client timeouts are then handled */ val = select(sock+1, &readfd, NULL, NULL, &timeout); if (val > 0 && FD_ISSET(sock, &readfd)) { psTraceIntDtls("Select woke %d\n", val); /* recvfrom data must always go into generic buffer becuase we don't yet know who it is from */ inaddrlen = sizeof(struct sockaddr_in); if ((recvLen = (int32)recvfrom(sock, recvfromBuf, recvfromBufLen, 0, (struct sockaddr *)&inaddr, &inaddrlen)) < 0) { #ifdef WIN32 if (SOCKET_ERRNO != EWOULDBLOCK && SOCKET_ERRNO != WSAECONNRESET) { #else if (SOCKET_ERRNO != EWOULDBLOCK) { #endif _psTraceInt("recvfrom error %d. Exiting\n", SOCKET_ERRNO); goto DTLS_EXIT; } continue; } #ifdef USE_DTLS_DEBUG_TRACE /* nice for debugging */ { const char *addrstr; addrstr = getaddrstring((struct sockaddr *)&inaddr, 1); psTraceIntDtls("Read %d bytes ", recvLen); psTraceStrDtls("from %s\n", (char*)addrstr); psFree(addrstr, NULL); } #endif /* Locate the SSL context of this receive and create a new session if not found */ if ((dtlsCtx = findClient(inaddr)) == NULL) { memset(&options, 0x0, sizeof(sslSessOpts_t)); options.versionFlag = SSL_FLAGS_DTLS; options.truncHmac = -1; if (matrixSslNewServerSession(&ssl, keys, certValidator, &options) < 0) { rc = DTLS_FATAL; goto DTLS_EXIT; } if ((dtlsCtx = registerClient(inaddr, sock, ssl)) == NULL) { /* Client list is full. Just have to ignore */ matrixSslDeleteSession(ssl); continue; } } ssl = dtlsCtx->ssl; /* Move socket data into internal buffer */ freeBufLen = matrixSslGetReadbuf(ssl, &sslBuf); psAssert(freeBufLen >= recvLen); psAssert(freeBufLen == matrixDtlsGetPmtu()); memcpy(sslBuf, recvfromBuf, recvLen); /* Notify SSL state machine that we've received more data into the ssl buffer retreived with matrixSslGetReadbuf. */ if ((rcr = matrixSslReceivedData(ssl, recvLen, &sslBuf, (uint32*)&freeBufLen)) < 0) { clearClient(dtlsCtx); continue; /* Next connection */ } /* Update last activity time and reset timeout*/ psGetTime(&dtlsCtx->lastRecvTime, NULL); dtlsCtx->timeout = MIN_WAIT_SECS; PROCESS_MORE_FROM_BUFFER: /* Process any incoming plaintext application data */ switch (rcr) { case MATRIXSSL_HANDSHAKE_COMPLETE: /* This is a resumed handshake case which means we are the last to receive handshake flights and we know the handshake is complete. However, the internal workings will not flag us officially complete until we receive application data from the peer so we need a local flag to handle this case so we are not resending our final flight */ dtlsCtx->connStatus = RESUMED_HANDSHAKE_COMPLETE; psTraceDtls("Got HANDSHAKE_COMPLETE out of ReceivedData\n"); break; case MATRIXSSL_APP_DATA: /* Now safe to clear the connStatus flag that was keeping track of the state between receiving the final flight of a resumed handshake and receiving application data. The reciept of app data has now internally disabled flight resends */ dtlsCtx->connStatus = 0; _psTrace("Client connected. Received...\n"); _psTraceStr("%s\n", (char*)sslBuf); break; case MATRIXSSL_REQUEST_SEND: /* Still handshaking with this particular client */ while ((sslBufLen = matrixDtlsGetOutdata(ssl, &sslBuf)) > 0) { if ((sendLen = udpSend(dtlsCtx->fd, sslBuf, sslBufLen, (struct sockaddr*)&inaddr, sizeof(struct sockaddr_in), dtlsCtx->timeout, packet_loss_prob, NULL)) < 0) { psTraceDtls("udpSend error. Ignoring\n"); } /* Always indicate the entire datagram was sent as there is no way for DTLS to handle partial records. Resends and timeouts will handle any problems */ rcs = matrixDtlsSentData(ssl, sslBufLen); if (rcs == MATRIXSSL_REQUEST_CLOSE) { psTraceDtls("Got REQUEST_CLOSE out of SentData\n"); clearClient(dtlsCtx); break; } if (rcs == MATRIXSSL_HANDSHAKE_COMPLETE) { /* This is the standard handshake case */ _psTrace("Got HANDSHAKE_COMPLETE from SentData\n"); break; } /* SSL_REQUEST_SEND is handled by loop logic */ } break; case MATRIXSSL_REQUEST_RECV: psTraceDtls("Got REQUEST_RECV from ReceivedData\n"); break; case MATRIXSSL_RECEIVED_ALERT: /* The first byte of the buffer is the level */ /* The second byte is the description */ if (*sslBuf == SSL_ALERT_LEVEL_FATAL) { psTraceIntDtls("Fatal alert: %d, closing connection.\n", *(sslBuf + 1)); clearClient(dtlsCtx); continue; /* Next connection */ } /* Closure alert is normal (and best) way to close */ if (*(sslBuf + 1) == SSL_ALERT_CLOSE_NOTIFY) { clearClient(dtlsCtx); continue; /* Next connection */ } psTraceIntDtls("Warning alert: %d\n", *(sslBuf + 1)); if ((rcr = matrixSslProcessedData(ssl, &sslBuf, (uint32*)&freeBufLen)) == 0) { continue; } goto PROCESS_MORE_FROM_BUFFER; default: continue; /* Next connection */ } } else if (val < 0) { if (SOCKET_ERRNO != EINTR) { psTraceIntDtls("unhandled error %d from select", SOCKET_ERRNO); } } /* Have either timed out waiting for a read or have processed a single recv. Now check to see if any timeout resends are required */ rc = handleResends(sock); } /* Main Select Loop */ DTLS_EXIT: psFree(recvfromBuf, NULL); CLIENT_EXIT: closeClientList(); MATRIX_EXIT: matrixSslDeleteKeys(keys); matrixSslClose(); if (sock != INVALID_SOCKET) close(sock); return rc; } /******************************************************************************/ /* Work through client list and resend handshake flight if haven't heard from them in a while */ static int32 handleResends(SOCKET sock) { serverDtls_t *dtlsCtx; ssl_t *ssl; psTime_t now; unsigned char *sslBuf; int16 i; int32 sendLen, rc; uint32 timeout, sslBufLen, clientCount; clientCount = 0; /* return code is number of active clients or < 0 on error */ psGetTime(&now, NULL); for (i = 0; i < tableSize; i++) { dtlsCtx = &clientTable[i]; if (dtlsCtx->ssl != NULL) { clientCount++; timeout = psDiffMsecs(dtlsCtx->lastRecvTime, now, NULL) / 1000; /* Haven't heard from this client in a while. Might need resend */ if (timeout > dtlsCtx->timeout) { /* if timeout is too great. clear conn */ if (dtlsCtx->timeout >= MAX_WAIT_SECS) { clearClient(dtlsCtx); clientCount--; break; } /* Increase the timeout for next pass */ dtlsCtx->timeout *= 2; /* If we are in a RESUMED_HANDSHAKE_COMPLETE state that means we are positive the handshake is complete so we don't want to resend no matter what. This is an interim state before the internal mechaism sees an application data record and flags us as complete officially */ if (dtlsCtx->connStatus == RESUMED_HANDSHAKE_COMPLETE) { psTraceDtls("Connected but awaiting data\n"); continue; } ssl = dtlsCtx->ssl; while ((sslBufLen = matrixDtlsGetOutdata(ssl, &sslBuf)) > 0) { if ((sendLen = udpSend(dtlsCtx->fd, sslBuf, sslBufLen, (struct sockaddr*)&dtlsCtx->addr, sizeof(struct sockaddr_in), dtlsCtx->timeout / 2, packet_loss_prob, NULL)) < 0) { psTraceDtls("udpSend error. Ignoring\n"); } /* Always indicate the entire datagram was sent as there is no way for DTLS to handle partial records. Resends and timeouts will handle any problems */ if ((rc = matrixDtlsSentData(ssl, sslBufLen)) < 0) { psTraceDtls("internal error\n"); clearClient(dtlsCtx); clientCount--; break; } if (rc == MATRIXSSL_REQUEST_CLOSE) { psTraceDtls("Got REQUEST_CLOSE out of SentData\n"); clearClient(dtlsCtx); clientCount--; break; } if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) { /* This is the standard handshake case */ psTraceDtls("Got HANDSHAKE_COMPLETE out of SentData\n"); break; } /* SSL_REQUEST_SEND is handled by loop logic */ } } } } return clientCount; }
/* EXAMPLE ONLY - SHOULD NOT BE USED FOR PRODUCTION CODE Process an HTTP request from a client. Very simple - we just print it, and return success. No HTTP validation at all is done on the data. */ int32 httpBasicParse(httpConn_t *cp, unsigned char *buf, uint32 len, int32 trace) { unsigned char *c, *end, *tmp; int32 l; /* SSL/TLS can provide zero length records, which we just ignore here because the code below assumes we have at least one byte */ if (len == 0) { return HTTPS_PARTIAL; } c = buf; end = c + len; /* If we have an existing partial HTTP buffer, append to it the data in buf up to the first newline, or 'len' data, if no newline is in buf. */ if (cp->parsebuf != NULL) { for (tmp = c; c < end && *c != '\n'; c++) { ; } /* We want c to point to 'end' or to the byte after \r\n */ if (*c == '\n') { c++; } l = (int32) (c - tmp); if (l > HTTPS_BUFFER_MAX) { return HTTPS_ERROR; } cp->parsebuf = realloc(cp->parsebuf, l + cp->parsebuflen); memcpy(cp->parsebuf + cp->parsebuflen, tmp, l); cp->parsebuflen += l; /* Parse the data out of the saved buffer first */ c = cp->parsebuf; end = c + cp->parsebuflen; /* We've "moved" some data from buf into parsebuf, so account for it */ buf += l; len -= l; } L_PARSE_LINE: for (tmp = c; c < end && *c != '\n'; c++) { ; } if (c < end) { if (*(c - 1) != '\r') { return HTTPS_ERROR; } /* If the \r\n started the line, we're done reading headers */ if (*tmp == '\r' && (tmp + 1 == c)) { /* if ((c + 1) != end) { _psTrace("HTTP data parsing not supported, ignoring.\n"); } */ if (cp->parsebuf != NULL) { free(cp->parsebuf); cp->parsebuf = NULL; cp->parsebuflen = 0; if (len != 0) { _psTrace("HTTP data parsing not supported, ignoring.\n"); } } if (trace) { _psTrace("RECV COMPLETE HTTP MESSAGE\n"); } return HTTPS_COMPLETE; } } else { /* If parsebuf is non-null, we have already saved it */ if (cp->parsebuf == NULL && (l = (int32) (end - tmp)) > 0) { cp->parsebuflen = l; cp->parsebuf = malloc(cp->parsebuflen); psAssert(cp->parsebuf != NULL); memcpy(cp->parsebuf, tmp, cp->parsebuflen); } return HTTPS_PARTIAL; } *(c - 1) = '\0'; /* Replace \r with \0 just for printing */ if (trace) { _psTraceStr("RECV PARSED: [%s]\n", (char *) tmp); } /* Finished parsing the saved buffer, now start parsing from incoming buf */ if (cp->parsebuf != NULL) { free(cp->parsebuf); cp->parsebuf = NULL; cp->parsebuflen = 0; c = buf; end = c + len; } else { c++; /* point c to the next char after \r\n */ } goto L_PARSE_LINE; return HTTPS_ERROR; }
static int32 psAesTestCBCHmac(void) { int32 err; psCipherContext_t eCtx; psHmac_t hCtx; #if defined(USE_MATRIX_AES_CBC) && !defined(PS_AES_IMPROVE_PERF_INCREASE_CODESIZE) _psTrace("##########\n#\n# "); _psTrace("AES speeds can be improved by enabling\n# "); _psTrace("PS_AES_IMPROVE_PERF_INCREASE_CODESIZE in cryptoConfig.h\n"); _psTrace("#\n#\n#########\n"); #endif #ifdef USE_HMAC_SHA1 _psTrace("***** AES-128 CBC + SHA1-HMAC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 16, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, TINY_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, SMALL_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, MEDIUM_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, LARGE_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, HUGE_CHUNKS, AES_HMAC_ALG); psAesClearCBC(&eCtx.aes); _psTrace("***** AES-256 CBC + SHA1-HMAC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 32, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, TINY_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, SMALL_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, MEDIUM_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, LARGE_CHUNKS, AES_HMAC_ALG); psHmacSha1Init(&hCtx.u.sha1, key, SHA1_HASH_SIZE); runWithHmac(&eCtx, &hCtx, 0, HUGE_CHUNKS, AES_HMAC_ALG); psAesClearCBC(&eCtx.aes); #endif #ifdef USE_HMAC_SHA256 _psTrace("***** AES-128 CBC + SHA256-HMAC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 16, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, TINY_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, SMALL_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, MEDIUM_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, LARGE_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, HUGE_CHUNKS, AES_HMAC256_ALG); psAesClearCBC(&eCtx.aes); _psTrace("***** AES-256 CBC + SHA256-HMAC *****\n"); if ((err = psAesInitCBC(&eCtx.aes, iv, key, 32, PS_AES_ENCRYPT)) != PS_SUCCESS) { _psTraceInt("FAILED: returned %d\n", err); return err; } psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, TINY_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, SMALL_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, MEDIUM_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, LARGE_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); runWithHmac(&eCtx, &hCtx, SHA256_HASH_SIZE, HUGE_CHUNKS, AES_HMAC256_ALG); psHmacSha256Init(&hCtx.u.sha256, key, 32); psAesClearCBC(&eCtx.aes); #endif return 0; }
/* Main routine. Initialize SSL keys and structures, and make two SSL connections, the first with a blank session Id, and the second with a session ID populated during the first connection to do a much faster session resumption connection the second time. */ int32 main(int32 argc, char **argv) { int32 rc, CAstreamLen; sslKeys_t *keys; sslSessionId_t *sid; char *CAstream; #ifdef USE_CRL int32 numLoaded; #endif #ifdef WIN32 WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif if ((rc = matrixSslOpen()) < 0) { _psTrace("MatrixSSL library init failure. Exiting\n"); return rc; } if (matrixSslNewKeys(&keys) < 0) { _psTrace("MatrixSSL library key init failure. Exiting\n"); return -1; } #ifdef USE_HEADER_KEYS /* In-memory based keys Build the CA list first for potential client auth usage */ CAstreamLen = 0; CAstreamLen += sizeof(RSACAS); if (CAstreamLen > 0) { CAstream = psMalloc(NULL, CAstreamLen); } else { CAstream = NULL; } CAstreamLen = 0; memcpy(CAstream, RSACAS, sizeof(RSACAS)); CAstreamLen += sizeof(RSACAS); #ifdef ID_RSA if ((rc = matrixSslLoadRsaKeysMem(keys, RSA2048, sizeof(RSA2048), RSA2048KEY, sizeof(RSA2048KEY), (unsigned char*)CAstream, CAstreamLen)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); if (CAstream) psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif if (CAstream) psFree(CAstream); #else /* File based keys */ CAstreamLen = 0; CAstreamLen += (int32)strlen(rsaCAFile) + 1; if (CAstreamLen > 0) { CAstream = psMalloc(NULL, CAstreamLen); memset(CAstream, 0x0, CAstreamLen); } else { CAstream = NULL; } CAstreamLen = 0; memcpy(CAstream, rsaCAFile, strlen(rsaCAFile)); CAstreamLen += strlen(rsaCAFile); /* Load Identiy */ #ifdef EXAMPLE_RSA_KEYS if ((rc = matrixSslLoadRsaKeys(keys, rsaCertFile, rsaPrivkeyFile, NULL, (char*)CAstream)) < 0) { _psTrace("No certificate material loaded. Exiting\n"); if (CAstream) psFree(CAstream); matrixSslDeleteKeys(keys); matrixSslClose(); return rc; } #endif if (CAstream) psFree(CAstream); #endif /* USE_HEADER_KEYS */ #ifdef USE_CRL if (matrixSslGetCRL(keys, crlCb, &numLoaded) < 0) { _psTrace("WARNING: A CRL failed to load\n"); } _psTraceInt("CRLs loaded: %d\n", numLoaded); #endif matrixSslNewSessionId(&sid); _psTrace("=== INITIAL CLIENT SESSION ===\n"); httpsClientConnection(keys, sid); _psTrace("\n=== CLIENT SESSION WITH CACHED SESSION ID ===\n"); httpsClientConnection(keys, sid); matrixSslDeleteSessionId(sid); matrixSslDeleteKeys(keys); matrixSslClose(); #ifdef WIN32 _psTrace("Press any key to close"); getchar(); #endif return 0; }
int32 crlCb(psPool_t *pool, psX509Cert_t *CA, int append, char *url, uint32 urlLen) { SOCKET fd; struct hostent *ip; struct in_addr intaddr; char *pageStart, *replyPtr, *ipAddr; char hostAddr[HOST_ADDR_LEN], getReq[GET_REQ_LEN]; char crlBuf[CRL_BUF_SIZE]; int hostAddrLen, getReqLen, pageLen; int32 transferred; int32 err, httpUriLen, port, offset; uint32 crlBinLen; /* Is URI in expected URL form? */ if (strstr(url, "http://") == NULL) { if (strstr(url, "https://") == NULL) { _psTraceStr("crlCb: Unsupported CRL URI: %s\n", url); return -1; } httpUriLen = 8; port = 80; /* No example yet of using SSL to fetch CRL */ } else { httpUriLen = 7; port = 80; } /* Parsing host and page and setting up IP address and GET request */ if ((pageStart = strchr(url + httpUriLen, '/')) == NULL) { _psTrace("crlCb: No host/page divider found\n"); return -1; } if ((hostAddrLen = (int)(pageStart - url) - httpUriLen) > HOST_ADDR_LEN) { _psTrace("crlCb: HOST_ADDR_LEN needs to be increased\n"); return -1; /* ipAddr too small to hold */ } memset(hostAddr, 0, HOST_ADDR_LEN); memcpy(hostAddr, url + httpUriLen, hostAddrLen); if ((ip = gethostbyname(hostAddr)) == NULL) { _psTrace("crlCb: gethostbyname failed\n"); return -1; } memcpy((char *) &intaddr, (char *) ip->h_addr_list[0], (size_t) ip->h_length); if ((ipAddr = inet_ntoa(intaddr)) == NULL) { _psTrace("crlCb: inet_ntoa failed\n"); return -1; } pageLen = (urlLen - hostAddrLen - httpUriLen); getReqLen = pageLen + hostAddrLen + GET_OH_LEN + HTTP_OH_LEN + HOST_OH_LEN + ACCEPT_OH_LEN; if (getReqLen > GET_REQ_LEN) { _psTrace("crlCb: GET_REQ_LEN needs to be increased\n"); return -1; } // Build the request: // // GET /page.crl HTTP/1.0 // Host: www.host.com // Accept: */* // memset(getReq, 0, GET_REQ_LEN); memcpy(getReq, crl_getHdr, GET_OH_LEN); offset = GET_OH_LEN; memcpy(getReq + offset, pageStart, pageLen); offset += pageLen; memcpy(getReq + offset, crl_httpHdr, HTTP_OH_LEN); offset += HTTP_OH_LEN; memcpy(getReq + offset, crl_hostHdr, HOST_OH_LEN); offset += HOST_OH_LEN; memcpy(getReq + offset, hostAddr, hostAddrLen); offset += hostAddrLen; memcpy(getReq + offset, crl_acceptHdr, ACCEPT_OH_LEN); /* Connect and send */ fd = socketConnect(ipAddr, port, &err); if (fd == INVALID_SOCKET || err != PS_SUCCESS) { _psTraceInt("crlCb: socketConnect failed: %d\n", err); return PS_PLATFORM_FAIL; } /* Send request and receive response */ offset = 0; while (getReqLen) { if ((transferred = send(fd, getReq + offset, getReqLen, 0)) < 0) { _psTraceInt("crlCb: socket send failed: %d\n", errno); close(fd); return PS_PLATFORM_FAIL; } getReqLen -= transferred; offset += transferred; } /* Not a good full recv */ if ((transferred = recv(fd, crlBuf, CRL_BUF_SIZE, 0)) <= 0) { _psTrace("crlCb: socket recv closed or failed\n"); close(fd); return PS_PLATFORM_FAIL; } if (transferred == CRL_BUF_SIZE) { /* CRL larger than max */ _psTrace("crlCb: CRL_BUF_SIZE needs to be increased\n"); close(fd); return -1; } close(fd); /* Did we get an OK response? */ if (strstr(crlBuf, "200 OK") == NULL) { _psTrace("crlCb: server reply was not '200 OK'\n"); return -1; } /* Length parse */ if ((replyPtr = strstr(crlBuf, "Content-Length: ")) == NULL) { return -1; } crlBinLen = (int)atoi(replyPtr + 16); /* Data begins after CRLF CRLF */ if ((replyPtr = strstr(crlBuf, "\r\n\r\n")) == NULL) { return -1; } /* A sanity test that the length matches the remainder */ if ((transferred - (replyPtr - crlBuf) - 4) != crlBinLen) { return -1; } /* Lastly, pass the CRL to matrixSslLoadCRL to parse, perform signature validation, and cache the revoked certificates for this CA */ return matrixSslLoadCRL(pool, CA, append, replyPtr + 4, crlBinLen); }