int main(int argc, char *argv[]) { char *port = NULL; BIO *in = NULL; BIO *ssl_bio, *tmp; SSL_CTX *ctx; char buf[512]; int ret = 1, i; if (argc <= 1) port = "*:4433"; else port = argv[1]; SSL_load_error_strings(); /* Add ciphers and message digests */ OpenSSL_add_ssl_algorithms(); ctx = SSL_CTX_new(TLS_server_method()); if (!SSL_CTX_use_certificate_chain_file(ctx, CERT_FILE)) goto err; if (!SSL_CTX_use_PrivateKey_file(ctx, CERT_FILE, SSL_FILETYPE_PEM)) goto err; if (!SSL_CTX_check_private_key(ctx)) goto err; /* Setup server side SSL bio */ ssl_bio = BIO_new_ssl(ctx, 0); if ((in = BIO_new_accept(port)) == NULL) goto err; /* * This means that when a new connection is accepted on 'in', The ssl_bio * will be 'duplicated' and have the new socket BIO push into it. * Basically it means the SSL BIO will be automatically setup */ BIO_set_accept_bios(in, ssl_bio); /* Arrange to leave server loop on interrupt */ sigsetup(); again: /* * The first call will setup the accept socket, and the second will get a * socket. In this loop, the first actual accept will occur in the * BIO_read() function. */ if (BIO_do_accept(in) <= 0) goto err; while (!done) { i = BIO_read(in, buf, 512); if (i == 0) { /* * If we have finished, remove the underlying BIO stack so the * next time we call any function for this BIO, it will attempt * to do an accept */ printf("Done\n"); tmp = BIO_pop(in); BIO_free_all(tmp); goto again; } if (i < 0) goto err; fwrite(buf, 1, i, stdout); fflush(stdout); } ret = 0; err: if (ret) { ERR_print_errors_fp(stderr); } BIO_free(in); exit(ret); return (!ret); }
void _envsetup(void) { int dfd, fdinited, n, nd, m, i, j, f, nohandle, psize, cnt; char *ps, *p; char **pp; char name[NAME_MAX+5]; Dir *d9, *d9a; static char **emptyenvp = 0; environ = emptyenvp; /* pessimism */ nohandle = 0; fdinited = 0; cnt = 0; strcpy(name, "#e"); dfd = _OPEN(name, 0); if(dfd < 0) return; name[2] = '/'; ps = p = malloc(Envhunk); if(p == 0) return; psize = Envhunk; nd = _dirreadall(dfd, &d9a); _CLOSE(dfd); for(j=0; j<nd; j++) { d9 = &d9a[j]; n = strlen(d9->name); if(n >= sizeof name - 4) continue; /* shouldn't be possible */ m = d9->length; i = p - ps; if(i+n+1+m+1 > psize) { psize += (n+m+2 < Envhunk)? Envhunk : n+m+2; ps = realloc(ps, psize); if (ps == 0) { free(d9a); return; } p = ps + i; } memcpy(p, d9->name, n); p[n] = '='; strcpy(name+3, d9->name); f = _OPEN(name, O_RDONLY); if(f < 0 || _READ(f, p+n+1, m) != m) m = 0; _CLOSE(f); if(p[n+m] == 0) m--; for(i=0; i<m; i++) if(p[n+1+i] == 0) p[n+1+i] = 1; p[n+1+m] = 0; if(strcmp(d9->name, "_fdinfo") == 0) { _fdinit(p+n+1, p+n+1+m); fdinited = 1; } else if(strcmp(d9->name, "_sighdlr") == 0) sigsetup(p+n+1, p+n+1+m); else if(strcmp(d9->name, "nohandle") == 0) nohandle = 1; p += n+m+2; cnt++; } free(d9a); if(!fdinited) _fdinit(0, 0); pp = malloc((1+cnt)*sizeof(char *)); if (pp == 0) return; environ = pp; p = ps; for(i = 0; i < cnt; i++) { *pp++ = p; p = memchr(p, 0, ps+psize-p); if (!p) break; p++; } *pp = 0; if(!nohandle) _NOTIFY(_notehandler); }
/* 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; }