Example #1
0
/*
    Non-blocking write data. Return the number of bytes written or -1 on errors.
    Returns zero if part of the data was written.

    Encode caller's data buffer into an SSL record and write to socket. The encoded data will always be 
    bigger than the incoming data because of the record header (5 bytes) and MAC (16 bytes MD5 / 20 bytes SHA1)
    This would be fine if we were using blocking sockets, but non-blocking presents an interesting problem.  Example:

        A 100 byte input record is encoded to a 125 byte SSL record
        We can send 124 bytes without blocking, leaving one buffered byte
        We can't return 124 to the caller because it's more than they requested
        We can't return 100 to the caller because they would assume all data
        has been written, and we wouldn't get re-called to send the last byte

    We handle the above case by returning 0 to the caller if the entire encoded record could not be sent. Returning 
    0 will prompt us to select this socket for write events, and we'll be called again when the socket is writable.  
    We'll use this mechanism to flush the remaining encoded data, ignoring the bytes sent in, as they have already 
    been encoded.  When it is completely flushed, we return the originally requested length, and resume normal 
    processing.
 */
PUBLIC ssize sslWrite(Webs *wp, void *buf, ssize len)
{
    Ms      *ms;
    uchar   *obuf;
    ssize   encoded, nbytes, written;

    ms = (Ms*) wp->ssl;
    while (len > 0 || ms->outlen > 0) {
        if ((encoded = matrixSslGetOutdata(ms->handle, &obuf)) <= 0) {
            if (ms->outlen <= 0) {
                ms->outbuf = (char*) buf;
                ms->outlen = len;
                ms->written = 0;
                len = 0;
            }
            nbytes = min(ms->outlen, SSL_MAX_PLAINTEXT_LEN);
            if ((encoded = matrixSslEncodeToOutdata(ms->handle, (uchar*) buf, (int) nbytes)) < 0) {
                return encoded;
            }
            ms->outbuf += nbytes;
            ms->outlen -= nbytes;
            ms->written += nbytes;
        }
        if ((written = socketWrite(wp->sid, obuf, encoded)) < 0) {
            return written;
        } else if (written == 0) {
            break;
        }
        matrixSslSentData(ms->handle, (int) written);
    }
    /*
        Only signify all the data has been written if MatrixSSL has absorbed all the data
     */
    return ms->outlen == 0 ? ms->written : 0;
}
Example #2
0
PUBLIC void sslFree(Webs *wp)
{
    Ms          *ms;
    WebsSocket  *sp;
    uchar       *buf;
    int         len;
    
    ms = wp->ssl;
    if (ms) {
        assert(wp->sid >= 0);
        if ((sp = socketPtr(wp->sid)) == 0) {
            return;
        }
        if (!(sp->flags & SOCKET_EOF)) {
            /*
                Flush data. Append a closure alert to any buffered output data, and try to send it.
                Don't bother retrying or blocking, we're just closing anyway.
            */
            matrixSslEncodeClosureAlert(ms->handle);
            if ((len = matrixSslGetOutdata(ms->handle, &buf)) > 0) {
                sslWrite(wp, buf, len);
            }
        }
        if (ms->handle) {
            matrixSslDeleteSession(ms->handle);
        }
        wfree(ms);
        wp->ssl = 0;
    }
}
Example #3
0
/*
	Close a socket and free associated SSL context and buffers
 */
static void closeConn(httpConn_t *cp, int32 reason)
{
	unsigned char	*buf;
	int32			len;
	
	DLListRemove(&cp->List);
	/* Quick attempt to send a closure alert, don't worry about failure */
	if (matrixSslEncodeClosureAlert(cp->ssl) >= 0) {
		if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) {
			if ((len = send(cp->fd, buf, len, MSG_DONTWAIT)) > 0) {
				matrixSslSentData(cp->ssl, len);
			}
		}
	}
	if (cp->parsebuf != NULL) {
		psAssert(cp->parsebuflen > 0);
		free(cp->parsebuf);
		cp->parsebuflen = 0;
	}
	matrixSslDeleteSession(cp->ssl);
	if (cp->fd != INVALID_SOCKET) {
		close(cp->fd);
	}
	if (reason >= 0) {
/*		_psTraceInt("=== Closing Client %d ===\n", cp->fd); */
	} else {
		_psTraceInt("=== Closing Client %d on Error ===\n", cp->fd);
	}
	free(cp);
}
Example #4
0
CAMLprim value stub_get_out_data(value ssl)
{
  CAMLparam1(ssl);
  CAMLlocal1(v);
  unsigned char *str;

  int rc=matrixSslGetOutdata(ssl_t_val(ssl), &str);

  if(rc>0) {
    v=caml_alloc_string(rc);
    memcpy(String_val(v),str,rc);
  } else {
    caml_failwith("No data");
  }

  CAMLreturn(v);
}
int handleRequestSend(SSL_Conext*& sslContext) {
	unsigned char *sslData;
	int len = matrixSslGetOutdata(sslContext->SslContext, &sslData);
	int sent = SOCK_send(sslContext->SocketHandle, (const char*) (sslData), len,
			0);
	int rc = matrixSslSentData(sslContext->SslContext, sent);
	if (rc == MATRIXSSL_REQUEST_CLOSE || rc == MATRIXSSL_SUCCESS) {
		return MATRIXSSL_SUCCESS;
	}
	if (rc == MATRIXSSL_REQUEST_SEND) {
		MATRIXSSL_PDEBUG("WARNING: Untested function call\n");
		//TODO
		return handleRequestSend(sslContext);
	}
	PRINT_UNEXPECTED_RETURN_VALUE(rc);
	return rc;
}
/*
	Send a close alert
*/
void sslWriteClosureAlert(sslConn_t *cp)
{
    unsigned char	*buf;
    int				len;

    if (cp != NULL) {
        if (matrixSslEncodeClosureAlert(cp->ssl) >= 0) {
            if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) {
                /* Non-blocking hail-mary alert */
                setSocketNonblock(cp->fd);
                if ((len = send(cp->fd, buf, len, MSG_DONTWAIT)) > 0) {
                    matrixSslSentData(cp->ssl, len);
                }
            }
        }
    }
}
int ssl_write_internal(int socket, const char* data, size_t size) {
	unsigned char *sslData;
	int rc = 0;
	SSL* ssl = NULL;
	SSL_Conext* sslContext = g_SSL_Driver.GetSSLContextBySocketHandle(socket);
	if (sslContext != NULL && sslContext->SslContext != NULL) {
		ssl = (SSL*) sslContext->SslContext;
	} else {
		return SOCK_SOCKET_ERROR;
	}

	MATRIXSSL_PDEBUG_ALL("------------- called ----------------: Socket: %i\n", socket);
/*
	SSL* ssl = (SSL*) g_SSL_Driver.m_sslContextArray[0].SslContext;
	if (ssl == NULL) {
		return SOCK_SOCKET_ERROR;
	}*/

	int available = 0, requested = size, sent = 0;
	if ((available = matrixSslGetWritebuf(ssl, &sslData, size)) < 0) {
		return SOCK_SOCKET_ERROR;
	}
	requested = min(requested, available);
	TINYCLR_SSL_MEMCPY(sslData, data, requested);
	rc = matrixSslEncodeWritebuf(ssl, requested);
	//PRINT_RETURN_VALUE(rc); //if > 0 number of bytes encoded
	if (rc < 0) {
		PRINT_UNEXPECTED_RETURN_VALUE(rc);
		return SOCK_SOCKET_ERROR;
	}
	int len = matrixSslGetOutdata(ssl, &sslData);

	sent = SOCK_send(socket, (const char *) sslData, len, 0);
	rc = matrixSslSentData(ssl, sent);
	PRINT_RETURN_VALUE(rc);
	if (rc == MATRIXSSL_SUCCESS) {
		return sent;
	}
	PRINT_UNEXPECTED_RETURN_VALUE(rc);
	return SOCK_SOCKET_ERROR;
}
Example #8
0
/*
	Close a socket and free associated SSL context and buffers
	An attempt is made to send a closure alert
 */
static void closeConn(ssl_t *ssl, SOCKET fd)
{
	unsigned char	*buf;
	int32			len;
	
	/* Set the socket to non-blocking to flush remaining data */
#ifdef POSIX
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
#elif WIN32
	len = 1;		/* 1 for non-block, 0 for block */
    ioctlsocket(fd, FIONBIO, &len);
#endif
	/* Quick attempt to send a closure alert, don't worry about failure */
	if (matrixSslEncodeClosureAlert(ssl) >= 0) {
		if ((len = matrixSslGetOutdata(ssl, &buf)) > 0) {
			if ((len = send(fd, buf, len, MSG_DONTWAIT)) > 0) {
				matrixSslSentData(ssl, len);
			}
		}
	}
	matrixSslDeleteSession(ssl);
	if (fd != INVALID_SOCKET) close(fd);
}
Example #9
0
/*
	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;
}
/*
	sslWrite encodes 'data' as a single SSL record.

	Return codes:
	-1	Internal failure. caller should free sess and close socket
	0	WOULDBLOCK. caller should call back with same input later
	> 0	success status.  number of plaintext bytes encoded and sent

	In order for non-blocking sockets to work transparently to the upper
	layers in webs, we manage the buffering of outgoing data and fudge the
	number of bytes sent until the entire SSL record is sent.

	This is because the encoded SSL record will be longer than 'len' with the
	SSL header prepended and the MAC and padding appended. There is no easy way
	to indicate to the caller if only a partial record was sent via the webs
	socket API.

	For example, if the caller specifies one byte of 'data' ('len' == 1),
	the SSL record could be 33 bytes long. If the socket send is able to write
	10 bytes of data, how would we indicate this to the caller, which expects
	only 1 byte, 0 bytes or a negative error code as a result of this call?

	The solution here is to always return 0 from this function, until we have
	flushed out all the bytes in the SSL record (33 in this example), and when
	the record has all been sent, return the originally requested length, which
	is 1 in this example.

	This assumes that on a 0 return, the caller will wait for a writable event
	on the socket and re-call this api with the same 'data' and 'len' as
	previously sent. Essentially a 0 return means "retry the same sslWrite call
	later".
*/
int sslWrite(sslConn_t *cp, char *data, int len)
{
    unsigned char	*buf;
    int				rc, transferred, ctLen;
    /*
    	If sendBlocked is set, then the previous time into sslWrite with this cp
    	could not send all the requested data, and zero was returned to the caller.
    	In this case, the data has already been encoded into the SSL outdata buffer,
    	and we don't need to re-encode it here.
    	This assumes that the caller is sending the same 'len' and contents of
    	'data' as they did last time.
     */
    if (!cp->sendBlocked) {
        if (matrixSslGetWritebuf(cp->ssl, &buf, len) < len) {
            /* SSL buffer must hold requested plaintext + SSL overhead */
            return -1;
        }
        memcpy((char *)buf, data, len);
        if (matrixSslEncodeWritebuf(cp->ssl, len) < 0) {
            return -1;
        }
    } else {
        /*
        		Not all previously encoded data could be sent without blocking and
        		0 bytes was previously returned to caller as sent. Ensure caller is
        		retrying with same len (and presumably the same data).
         */
        if (len != cp->ptReqBytes) {
            a_assert(len != cp->ptReqBytes);
            return -1;
        }
    }
WRITE_MORE:
    /*
    	There is a small chance that we are here with sendBlocked set, and yet
    	there is no buffered outdata to send. This happens because a send can
    	also happen in sslRead, and that could have flushed the outgoing buffer
    	in addition to a handshake message reply.
     */
    ctLen = matrixSslGetOutdata(cp->ssl, &buf);
    if (ctLen > 0) {
        transferred = send(cp->fd, buf, ctLen, MSG_NOSIGNAL);
        if (transferred <= 0) {
#ifdef USE_NONBLOCKING_SSL_SOCKETS
            if (socketGetError() != EWOULDBLOCK) {
                return -1;
            }
            if (!cp->sendBlocked) {
                cp->sendBlocked = 1;
                cp->ptReqBytes = len;
            } else {
            }
            return 0;
#else
            return -1;
#endif
        }
        /* Update the SSL buffer that we've written > 0 bytes of data */
        if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) {
            return -1;
        }
        /* There is more data in the SSL buffer to send */
        if (rc == MATRIXSSL_REQUEST_SEND) {
            goto WRITE_MORE;
        }
    }
    cp->sendBlocked = 0;
    cp->ptReqBytes = 0;
    return len;
}
/*
	Primary MatrixSSL read function that transparently handles SSL handshakes
	and the subsequent incoming application data records.

	A NULL inbuf parameter is an indication that this is a call to perform a
	new SSL handshake with an incoming client and no out data is expected

	Params:
	inbuf	allocated storage for plaintext data to be copied to
	inlen	length of inbuf

	Return codes:
	-1	EOF or internal failure.  caller should free sess and close socket
	0	success status.  no data is being returned to the caller
	>0	success status.  number of plaintext bytes written to inbuf

	Note that unlike a standard "socket read", a read of an SSL record can
	produce data that must be written, for example, a response to a handshake
	message that must be sent before any more data is read. Also, data can
	be read from the network such as an SSL alert, that produces no data to
	pass back to caller.

	Because webs doesn't have the concept of a read forcing a write, we
	do the write here, inline. If we are non-blocking, this presents an issue
	because we can't block indefinitely on a send, and we also can't indicate
	that the send be done later. The workaround below uses select() to
	implement a "timed send", which will fail and indicate the connection be
	closed if not complete within a time threshold.

	This situation is extremely unlikely in normal operation, since the only
	records that must be sent as a result of a recv are handshake messages,
	and TCP buffers would not typically be full enough at that point to
	result in an EWOULDBLOCK on send. Conceivably, it could occur on a client
	initiated SSl re-handshake that is sent by a slow-reading client to a
	server with full TCP buffers. A non-malicious client in this situation
	would read immediately after a re-handshake request and fail anyway
	because a buffered appdata record would be read.
*/
int	sslRead(sslConn_t *cp, char *inbuf, int inlen)
{
    unsigned char	*buf;
    int				rc, len, transferred;

    /*
    	Always first look to see if any plaintext application data is waiting
    	We have two levels of buffer here, one for decoded data and one for
    	partial, still encoded SSL records. The partial SSL records are stored
    	transparently in MatrixSSL, but the plaintext is stored in 'cp'.
     */
    if (inbuf != NULL && inlen > 0 && cp->ptBytes > 0 && cp->pt) {
        if (cp->ptBytes < inlen) {
            inlen = cp->ptBytes;
        }
        memcpy(inbuf, cp->currPt, inlen);
        cp->currPt += inlen;
        cp->ptBytes -= inlen;
        /* Free buffer as we go if empty */
        if (cp->ptBytes == 0) {
            bfree(B_L, cp->pt);
            cp->pt = cp->currPt = NULL;
        }
        return inlen;
    }
    /*
    	If there is outgoing data buffered, just try to write it here before
    	doing our read on the socket. Because the read could produce data to
    	be written, this will ensure we have as much room as possible in that
    	case for the written record. Note that there may be data here to send
    	because of a previous sslWrite that got EWOULDBLOCK.
    */
WRITE_MORE:
    if ((len = matrixSslGetOutdata(cp->ssl, &buf)) > 0) {
        transferred = send(cp->fd, buf, len, MSG_NOSIGNAL);
        if (transferred <= 0) {
            if (socketGetError() != EWOULDBLOCK) {
                return -1;
            }
            if (waitForWriteEvent(cp->fd, MAX_WRITE_MSEC) == 0) {
                goto WRITE_MORE;
            }
            return -1;
        } else {
            /* Indicate that we've written > 0 bytes of data */
            if ((rc = matrixSslSentData(cp->ssl, transferred)) < 0) {
                return -1;
            }
            if (rc == MATRIXSSL_REQUEST_CLOSE) {
                return -1;
            } else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) {
                /* If called via sslAccept (NULL buf), then we can just leave */
                return 0;
            }
            /* Try to send again if more data to send */
            if (rc == MATRIXSSL_REQUEST_SEND || transferred < len) {
                goto WRITE_MORE;
            }
        }
    } else if (len < 0) {
        return -1;
    }

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) {
        return -1;
    }
    if ((transferred = recv(cp->fd, buf, len, MSG_NOSIGNAL)) < 0) {
        /* Support non-blocking sockets if turned on */
        if (socketGetError() == EWOULDBLOCK) {
            return 0;
        }
        trace(1, T("RECV error: %d\n"), socketGetError());
        return -1;
    }
    if (transferred == 0) {
        /* If EOF, remote socket closed. This is semi-normal closure. */
        trace(4, T("Closing connection %d on EOF\n"), cp->fd);
        return -1;
    }
    /*
    	Notify SSL state machine that we've received more data into the
    	ssl buffer retreived with matrixSslGetReadbuf.
     */
    if ((rc = matrixSslReceivedData(cp->ssl, transferred, &buf,
                                    (uint32*)&len)) < 0) {
        return -1;
    }

PROCESS_MORE:
    switch (rc) {
    case MATRIXSSL_REQUEST_SEND:
        /* There is a handshake response we must send */
        goto WRITE_MORE;
    case MATRIXSSL_REQUEST_RECV:
        goto READ_MORE;
    case MATRIXSSL_HANDSHAKE_COMPLETE:
        /* Session resumption handshake */
        goto READ_MORE;
    case MATRIXSSL_RECEIVED_ALERT:
        /* Any fatal alert will simply cause a read error and exit */
        if (*buf == SSL_ALERT_LEVEL_FATAL) {
            trace(1, T("Fatal alert: %d, closing connection.\n"),
                  *(buf + 1));
            return -1;
        }
        /* Closure alert is normal (and best) way to close */
        if (*(buf + 1) == SSL_ALERT_CLOSE_NOTIFY) {
            return -1;
        }
        /* Eating warning alerts */
        trace(4, T("Warning alert: %d\n"), *(buf + 1));
        if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len))
            == 0) {
            /* Possible there was plaintext before the alert */
            if (inbuf != NULL && inlen > 0 && cp->ptBytes > 0 && cp->pt) {
                if (cp->ptBytes < inlen) {
                    inlen = cp->ptBytes;
                }
                memcpy(inbuf, cp->currPt, inlen);
                cp->currPt += inlen;
                cp->ptBytes -= inlen;
                /* Free buffer as we go if empty */
                if (cp->ptBytes == 0) {
                    bfree(B_L, cp->pt);
                    cp->pt = cp->currPt = NULL;
                }
                return inlen;
            } else {
                return 0;
            }
        }
        goto PROCESS_MORE;

    case MATRIXSSL_APP_DATA:
        if (cp->ptBytes == 0) {
            /*
            				Catching here means this is new app data just grabbed off the
            				wire.
            */
            cp->ptBytes = len;
            cp->pt = balloc(B_L, len);
            memcpy(cp->pt, buf, len);
            cp->currPt = cp->pt;
        } else {
            /*
            				Multi-record.  This case should only ever be possible if no
            				data has already been read out of the 'pt' cache so it is
            				fine to assume an unprocessed buffer.
            */
            psAssert(cp->pt == cp->currPt);
            cp->pt = brealloc(B_L, cp->pt, cp->ptBytes + len);
            memcpy(cp->pt + cp->ptBytes, buf, len);
            cp->currPt = cp->pt;
            cp->ptBytes += len;
        }
        if ((rc = matrixSslProcessedData(cp->ssl, &buf, (uint32*)&len))
            < 0) {
            return -1;
        }
        /* Check for multi-record app data*/
        if (rc > 0) {
            goto PROCESS_MORE;
        }
        /*
        			Otherwise pass back how much the caller wants to read (if any)
        */
        if (inbuf != 0 && inlen > 0) {
            if (cp->ptBytes < inlen) {
                inlen = cp->ptBytes;
            }
            memcpy(inbuf, cp->currPt, inlen);
            cp->currPt += inlen;
            cp->ptBytes -= inlen;
            return inlen; /* Just a breakpoint holder */
        }
        return 0; /* Have it stored, but caller didn't want any data */
    default:
        return -1;
    }

    return 0; /* really can never hit this */
}
Example #12
0
/*
	return 0 on successful encryption/decryption communication
	return -1 on failed comm
*/
static int32 exchangeAppData(sslConn_t *sendingSide, sslConn_t *receivingSide)
{
	int32			writeBufLen, inBufLen, dataSent, rc;
	uint32			ptLen, totalProcessed, requestedLen;
	unsigned char	*writeBuf, *inBuf, *plaintextBuf;
/*
	Client sends the data
	
	TODO: randomize the requested length
*/
	requestedLen = 1234;
	writeBufLen = matrixSslGetWritebuf(sendingSide->ssl, &writeBuf,
		requestedLen);
		
	psAssert(writeBufLen >= (int32)requestedLen);	
	memset(writeBuf, 4, (size_t)requestedLen);
	
/*
	EncodeWriteBuf does not return the buffer.
*/	
	writeBufLen = matrixSslEncodeWritebuf(sendingSide->ssl, requestedLen);
		
	totalProcessed = 0;	
	
SEND_MORE:		
	writeBufLen = matrixSslGetOutdata(sendingSide->ssl, &writeBuf);	

/*
	Receiving side must ask for storage space to receive data into.
	
	A good optimization of the buffer management can be seen here if a
	second pass was required:  the inBufLen should exactly match the
	writeBufLen because when matrixSslReceivedData was called below the
	record length was parsed off and the buffer was reallocated to the
	exact necessary length
*/
	inBufLen = matrixSslGetReadbuf(receivingSide->ssl, &inBuf);
	
	dataSent = min(writeBufLen, inBufLen);
	memcpy(inBuf, writeBuf, dataSent);

/*
	Now update the sending side that data has been "sent"
*/
	matrixSslSentData(sendingSide->ssl, dataSent);
		
/*
	Received data
*/
	rc = matrixSslReceivedData(receivingSide->ssl, dataSent, &plaintextBuf,
		&ptLen);
		
	if (rc == MATRIXSSL_REQUEST_RECV) {
		goto SEND_MORE;
	} else if (rc == MATRIXSSL_APP_DATA) {
		if (matrixSslProcessedData(receivingSide->ssl, &plaintextBuf,
				&ptLen) != 0) {
			return PS_FAILURE;
		}
	} else {
		printf("Unexpected error in exchangeAppData: %d\n", rc);
		return PS_FAILURE;
	}
	
	return PS_SUCCESS;
}
Example #13
0
/*
	Recursive handshake
*/
static int32 performHandshake(sslConn_t *sendingSide, sslConn_t *receivingSide)
{
	unsigned char	*inbuf, *outbuf, *plaintextBuf;
	int32			inbufLen, outbufLen, rc, sdrc, dataSent;
	uint32			ptLen;
#ifdef ENABLE_PERF_TIMING	
	psTime_t		start, end;
#endif /* ENABLE_PERF_TIMING */	
	
/*
	Sending side will have outdata ready
*/
#ifdef ENABLE_PERF_TIMING
	psGetTime(&start);
#endif /* ENABLE_PERF_TIMING */	
	outbufLen = matrixSslGetOutdata(sendingSide->ssl, &outbuf);
#ifdef ENABLE_PERF_TIMING	
	psGetTime(&end);
	sendingSide->runningTime += psDiffMsecs(start, end);
#endif /* ENABLE_PERF_TIMING */	
	
/*
	Receiving side must ask for storage space to receive data into
*/
#ifdef ENABLE_PERF_TIMING
	psGetTime(&start);
#endif /* ENABLE_PERF_TIMING */	
	inbufLen = matrixSslGetReadbuf(receivingSide->ssl, &inbuf);
#ifdef ENABLE_PERF_TIMING	
	psGetTime(&end);
	receivingSide->runningTime += psDiffMsecs(start, end);
#endif /* ENABLE_PERF_TIMING */	
	
/*
	The indata is the outdata from the sending side.  copy it over
*/
	dataSent = min(outbufLen, inbufLen);
	memcpy(inbuf, outbuf, dataSent);
	
/*
	Now update the sending side that data has been "sent"
*/
#ifdef ENABLE_PERF_TIMING
	psGetTime(&start);
#endif /* ENABLE_PERF_TIMING */	
	sdrc = matrixSslSentData(sendingSide->ssl, dataSent);
#ifdef ENABLE_PERF_TIMING	
	psGetTime(&end);
	sendingSide->runningTime += psDiffMsecs(start, end);
#endif /* ENABLE_PERF_TIMING */

/*
	Received data
*/
#ifdef ENABLE_PERF_TIMING
	psGetTime(&start);
#endif /* ENABLE_PERF_TIMING */	
	rc = matrixSslReceivedData(receivingSide->ssl, dataSent, &plaintextBuf,
		&ptLen);
#ifdef ENABLE_PERF_TIMING	
	psGetTime(&end);
	receivingSide->runningTime += psDiffMsecs(start, end);
#endif /* ENABLE_PERF_TIMING */	
		
	if (rc == MATRIXSSL_REQUEST_SEND) {
/*
		Success case.  Switch roles and continue
*/
		return performHandshake(receivingSide, sendingSide);
		
	} else if (rc == MATRIXSSL_REQUEST_RECV) {
/*
		This pass didn't take care of it all.  Don't switch rolls and
		try again
*/
		return performHandshake(sendingSide, receivingSide);
		
	} else if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) {
		return PS_SUCCESS;

	} else if (rc == MATRIXSSL_RECEIVED_ALERT) {
/*
		Just continue if warning level alert
*/
		if (plaintextBuf[0] == SSL_ALERT_LEVEL_WARNING) {
			if (matrixSslProcessedData(receivingSide->ssl, &plaintextBuf,
					&ptLen) != 0) {
				return PS_FAILURE;	
			}
			return performHandshake(sendingSide, receivingSide);
		} else {
			return PS_FAILURE;
		}
	
	} else {
		printf("Unexpected error in performHandshake: %d\n", rc);
		return PS_FAILURE;
	}
	return PS_FAILURE; /* can't get here */
}
Example #14
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;
}
int ssl_connect_internal(int socket, const char* szTargetHost,
		int sslContextHandle) {
	static int status = MATRIXSSL_REQUEST_SEND;
	int ret = SOCK_SOCKET_ERROR;

	int nonblock = 0;
	int done = 0;

	SSL *ssl = NULL;
	SSL_Conext* sslContext = g_SSL_Driver.GetSSLContextBySslIndex(
			sslContextHandle);
	if (sslContext != NULL && sslContext->SslContext != NULL) {
		ssl = (SSL*) sslContext->SslContext;
	} else {
		MATRIXSSL_PERROR("Context not valid\n");
		return SOCK_SOCKET_ERROR;
	}

	unsigned char *sslData;

	// WARNING - SSL_Connect is asynchronous and will be called multiple times for 1 connection, therefore
	// we only want to set the CA store on the first call (when sslData == NULL)
	//
	// The first certificate is the device's outbound certificate

	int rc = 0;
	int sent = 0;
	int len = matrixSslGetOutdata(ssl, &sslData);
	do {
		if (len > 0) //(status == MATRIXSSL_REQUEST_SEND )
				{

				ret = SOCK_TRY_AGAIN;
				sent = SOCK_send(socket, (const char *) sslData, len, 0);
				//TODO check if sent
				rc = matrixSslSentData(ssl, sent);
				done = 1;
				if (rc == MATRIXSSL_SUCCESS) {
					status = MATRIXSSL_REQUEST_RECV;

			}
		} else //if(status == MATRIXSSL_REQUEST_RECV && done == 0)
		{
			int len = 1000;
			len = matrixSslGetReadbuf(ssl, &sslData);
			len = SOCK_recv(socket, (char*) sslData, len, 0);
			ret = SOCK_TRY_AGAIN;
			if (len > 0) {
				rc = matrixSslReceivedData(ssl, (int32) len, &sslData,
						(uint32*) &len);
				if (rc == MATRIXSSL_REQUEST_RECV) {
					status = MATRIXSSL_REQUEST_RECV;
				}
				if (rc == MATRIXSSL_REQUEST_SEND) {
					status = MATRIXSSL_REQUEST_SEND;
					ret = SOCK_TRY_AGAIN;
				}
				if (rc == MATRIXSSL_HANDSHAKE_COMPLETE) {
					ret = 0;
				}
			}
		}
		if (done == 0) {
			len = matrixSslGetOutdata(ssl, &sslData);
		} else {
			len = 0;
		}
	} while (len);


	g_SSL_Driver.AddSslSocketHandle(sslContextHandle, socket);


	return ret;
}
Example #16
0
/*
	return 0 on successful encryption/decryption communication
	return -1 on failed comm
*/
static int32 exchangeAppData(sslConn_t *sendingSide, sslConn_t *receivingSide)
{
	int32			writeBufLen, inBufLen, dataSent, rc, sentRc;
	uint32			ptLen, totalProcessed, requestedLen, copyLen, halfReqLen;
	unsigned char	*writeBuf, *inBuf, *plaintextBuf;
	unsigned char	copyByte;

/*
	Client sends the data
*/
	requestedLen = APP_DATA_LEN;
	copyByte = 0x0;	

/*
	Split the data into two records sends.  Exercises the API a bit more 
	having the extra buffer management for the second record
*/
	while (requestedLen > 1) {
		copyByte++;
		halfReqLen = requestedLen / 2;
		writeBufLen = matrixSslGetWritebuf(sendingSide->ssl, &writeBuf,
			halfReqLen);
		
		copyLen = min(halfReqLen, writeBufLen);	
		memset(writeBuf, copyByte, copyLen);
		requestedLen -= copyLen;
		//psTraceBytes("sending", writeBuf, copyLen);

		writeBufLen = matrixSslEncodeWritebuf(sendingSide->ssl, copyLen);
		
		copyByte++;
		writeBufLen = matrixSslGetWritebuf(sendingSide->ssl, &writeBuf,
			halfReqLen);
		
		copyLen = min(halfReqLen, writeBufLen);	
		memset(writeBuf, copyByte, copyLen);
		requestedLen -= copyLen;
		//psTraceBytes("sending", writeBuf, copyLen);

		writeBufLen = matrixSslEncodeWritebuf(sendingSide->ssl, copyLen);
	}		

	totalProcessed = 0;	
	
SEND_MORE:		
	writeBufLen = matrixSslGetOutdata(sendingSide->ssl, &writeBuf);	

/*
	Receiving side must ask for storage space to receive data into.
	
	A good optimization of the buffer management can be seen here if a
	second pass was required:  the inBufLen should exactly match the
	writeBufLen because when matrixSslReceivedData was called below the
	record length was parsed off and the buffer was reallocated to the
	exact necessary length
*/
	inBufLen = matrixSslGetReadbuf(receivingSide->ssl, &inBuf);
	
	dataSent = min(writeBufLen, inBufLen);
	memcpy(inBuf, writeBuf, dataSent);

/*
	Now update the sending side that data has been "sent"
*/
	sentRc = matrixSslSentData(sendingSide->ssl, dataSent);
		
/*
	Received data
*/
	rc = matrixSslReceivedData(receivingSide->ssl, dataSent, &plaintextBuf,
		&ptLen);
		
	if (rc == MATRIXSSL_REQUEST_RECV) {
		goto SEND_MORE;
	} else if (rc == MATRIXSSL_APP_DATA) {
		while (rc == MATRIXSSL_APP_DATA) {
			//psTraceBytes("received", plaintextBuf, ptLen);
			if ((rc = matrixSslProcessedData(receivingSide->ssl, &plaintextBuf,
					&ptLen)) != 0) {
				if (rc == MATRIXSSL_APP_DATA) {
					continue;
				} else if (rc == MATRIXSSL_REQUEST_RECV) {
					goto SEND_MORE;
				} else {
					return PS_FAILURE;
				}
			}
		}
		if (sentRc == MATRIXSSL_REQUEST_SEND) {
			goto SEND_MORE;
		}
	} else {
		printf("Unexpected error in exchangeAppData: %d\n", rc);
		return PS_FAILURE;
	}
	
	return PS_SUCCESS;
}
Example #17
0
/*
	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;
}
Example #18
0
static ssize processIncoming(Webs *wp, char *buf, ssize size, ssize nbytes, int *readMore)
{
    Ms      *ms;
    uchar   *data, *obuf;
    ssize   toWrite, written, copied, sofar;
    uint32  dlen;
    int     rc;

    ms = (Ms*) wp->ssl;
    *readMore = 0;
    sofar = 0;

    /*
        Process the received data. If there is application data, it is returned in data/dlen
     */
    rc = matrixSslReceivedData(ms->handle, (int) nbytes, &data, &dlen);

    while (1) {
        switch (rc) {
        case PS_SUCCESS:
            return sofar;

        case MATRIXSSL_REQUEST_SEND:
            toWrite = matrixSslGetOutdata(ms->handle, &obuf);
            if ((written = blockingWrite(wp, obuf, toWrite)) < 0) {
                error("MatrixSSL: Error in process");
                return -1;
            }
            matrixSslSentData(ms->handle, (int) written);
            if (ms->handle->err != SSL_ALERT_NONE && ms->handle->err != SSL_ALLOW_ANON_CONNECTION) {
                return -1;
            }
            *readMore = 1;
            return 0;

        case MATRIXSSL_REQUEST_RECV:
            /* Partial read. More read data required */
            *readMore = 1;
            ms->more = 1;
            return 0;

        case MATRIXSSL_HANDSHAKE_COMPLETE:
            *readMore = 0;
            return 0;

        case MATRIXSSL_RECEIVED_ALERT:
            assert(dlen == 2);
            if (data[0] == SSL_ALERT_LEVEL_FATAL) {
                return -1;
            } else if (data[1] == SSL_ALERT_CLOSE_NOTIFY) {
                //  ignore - graceful close
                return 0;
            }
            rc = matrixSslProcessedData(ms->handle, &data, &dlen);
            break;

        case MATRIXSSL_APP_DATA:
            copied = min((ssize) dlen, size);
            memcpy(buf, data, copied);
            buf += copied;
            size -= copied;
            data += copied;
            dlen = dlen - (int) copied;
            sofar += copied;
            ms->more = ((ssize) dlen > size) ? 1 : 0;
            if (!ms->more) {
                /* The MatrixSSL buffer has been consumed, see if we can get more data */
                rc = matrixSslProcessedData(ms->handle, &data, &dlen);
                break;
            }
            return sofar;

        default:
            return -1;
        }
    }
}
Example #19
0
void child_process(struct spead_client *cl, sslKeys_t *keys)
{
#define BUFF   1000 
//#define REPLY   "HTTP/1.1 200 OK\nServer: pshr\nConnection: Keep-Alive\nContent-Type: audio/mpeg\nCache-Control: no-cache\nPragma: no-cache\n\n"
#define REPLY   "HTTP/1.1 200 OK\nServer: pshr\nConnection: Keep-Alive\nContent-Type: text/plain\nCache-Control: no-cache\nPragma: no-cache\n\nhello world"
#define MODE_CONNECTING 0
#define MODE_SENDING    1  

  //int bytes, mode = MODE_CONNECTING;
  //unsigned char data[BUFF];
  
  unsigned char *data, *res;
  uint32 err, len ,rb, wb;
  ssl_t *ssl;
  //int fd = (-1), initial=1450-300;
  
  data = NULL;
  res  = NULL;
  ssl  = NULL;

  if (cl == NULL || keys == NULL){
#ifdef DEBUG
    fprintf(stderr, "%s: parameter error\n", __func__);
#endif
    exit(EXIT_FAILURE);
  }


#ifdef DEBUG
  fprintf(stderr, "%s: child created with fd[%d]\n", __func__, cl->c_fd);
#endif
  
  err = matrixSslNewServerSession(&ssl, keys, NULL, 0);
  if (err != PS_SUCCESS){
#ifdef DEBUG
    switch(err){
      case PS_ARG_FAIL:
        fprintf(stderr, "Bad input function parameter\n");
        break;
      case PS_FAILURE:
        fprintf(stderr, "Internal memory allocation failure\n");
        break;
    }
#endif
#if 0
    matrixSslDeleteKeys(keys);
    matrixSslClose();
#endif
    exit(EXIT_FAILURE);
  }

  while(run){

READ_STATE:
    len = matrixSslGetReadbuf(ssl, &data);
    if (rb < 0){
#ifdef DEBUG
      fprintf(stderr, "%s: matrixssl getreadbuf error\n", __func__);
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    }

#ifdef DEBUG
    fprintf(stderr, "%s: matrixssl getreadbuf rtn [%d]\n", __func__, len);
#endif

    rb = read(cl->c_fd, data, len);
    if (rb == 0){
#ifdef DEBUG
      fprintf(stderr, "%s: read EOF\n", __func__);
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    } else if (rb < 0){
#ifdef DEBUG
      fprintf(stderr, "%s: read error (%s)\n", __func__, strerror(errno));
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    }

#ifdef DEBUG
    fprintf(stderr, "%s: read rtn [%d]\n", __func__, rb);
#endif

    rb = matrixSslReceivedData(ssl, rb, &data, &len);
    if (rb < 0){
#ifdef DEBUG
      fprintf(stderr, "%s: matrixssl receiveddata error\n", __func__);
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    } else if (rb == 0){
#ifdef DEBUG
      fprintf(stderr, "%s: matrix ssl received 0 bytes (false start?)\n", __func__);
#endif
    } else if (rb > 0){
      switch(rb){
        case MATRIXSSL_REQUEST_SEND:
#ifdef DEBUG
          fprintf(stderr, "%s: RS req send\n", __func__);
#endif
          goto WRITE_STATE;
          break;
        case MATRIXSSL_REQUEST_RECV:
#ifdef DEBUG
          fprintf(stderr, "%s: RS req recv\n", __func__);
#endif  
          goto READ_STATE;
          break;
        case MATRIXSSL_HANDSHAKE_COMPLETE:
#ifdef DEBUG
          fprintf(stderr, "%s: RS handshake complete\n", __func__);
#endif
          goto READ_STATE;
          break;
        case MATRIXSSL_RECEIVED_ALERT:
#ifdef DEBUG
          fprintf(stderr, "%s: RS rec alert\n", __func__);
#endif
          break;
        case MATRIXSSL_APP_DATA:
#ifdef DEBUG
          fprintf(stderr, "%s: RS app data\n", __func__);
#endif

#ifdef DEBUG
          fprintf(stderr, "%s: RS got data [%s]\n", __func__, data);
#endif
          
          /*process client data here*/
  
          res = get_resource_str(data);
          if (res == NULL){
#ifdef DEBUG
            fprintf(stderr, "%s: NULL RESOURCE\n", __func__);        
#endif
            run = 0;
          }

#ifdef DEBUG
          fprintf(stderr, "%s: got resource [%s]\n", __func__, res); 
#endif
                    
          unsigned char *tbuf;
          int32 tbuflen;

          tbuflen = matrixSslGetWritebuf(ssl, &tbuf, strlen(REPLY));
          if (tbuflen < 0){
#ifdef DEBUG
            fprintf(stderr, "%s: matrixssl getwritebuf error\n", __func__);
#endif
            matrixSslDeleteSession(ssl);
            destroy_spead_client(cl);
            exit(EXIT_FAILURE);
          }
          
          strncpy((char *) tbuf, REPLY, tbuflen);
          
          if (matrixSslEncodeWritebuf(ssl, strlen((char *) tbuf)) < 0){
            matrixSslDeleteSession(ssl);
            destroy_spead_client(cl);
            exit(EXIT_FAILURE);
          }

          matrixSslEncodeClosureAlert(ssl);

          rb = matrixSslProcessedData(ssl, &data, &len);
          
#ifdef DEBUG
          fprintf(stderr, "%s: processed data rtn [%d]\n", __func__, rb);
#endif

          goto WRITE_STATE;
          
          break;
      }

    }

WRITE_STATE:
    len = matrixSslGetOutdata(ssl, &data);
    if (len < 0){
#ifdef DEBUG
      fprintf(stderr, "%s: matrixssl getoutdata error\n", __func__);
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    }

#ifdef DEBUG
    fprintf(stderr, "%s: getoutdata rtn [%d]\n", __func__, len);
#endif
    
    wb = write(cl->c_fd, data, len);
    if (wb == 0){
#ifdef DEBUG
      fprintf(stderr, "%s: write 0\n", __func__);
#endif
      goto READ_STATE;
    } else if (wb < 0){
#ifdef DEBUG
      fprintf(stderr, "%s: write error (%s)\n", __func__, strerror(errno));
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    }

#ifdef DEBUG
    fprintf(stderr, "%s: write rtn [%d]\n", __func__, wb);
#endif
    
    wb = matrixSslSentData(ssl, wb);
    if (wb < 0) {
      #ifdef DEBUG
      fprintf(stderr, "%s: matrixssl sentdata error\n", __func__);
#endif
      matrixSslDeleteSession(ssl);
      destroy_spead_client(cl);
      exit(EXIT_FAILURE);
    } else if (wb > 0){
      switch(wb){
        case MATRIXSSL_REQUEST_SEND:
#ifdef DEBUG
          fprintf(stderr, "%s: WS req send\n", __func__);
#endif
          goto WRITE_STATE;
          break;
        case MATRIXSSL_REQUEST_CLOSE:
#ifdef DEBUG
          fprintf(stderr, "%s: WS req close\n", __func__);
#endif  
          matrixSslDeleteSession(ssl);
          destroy_spead_client(cl);
          exit(EXIT_FAILURE);
          break;
        case MATRIXSSL_HANDSHAKE_COMPLETE:
#ifdef DEBUG
          fprintf(stderr, "%s: WS handshake complete\n", __func__);
#endif
          goto READ_STATE; /*note might need to jump to receiveddata*/
          break;
      }

    }

  }

  matrixSslDeleteSession(ssl);

#if 0
  const char *filename = "/home/adam/live_audio_streaming/tenc/agoria-scala_original_mixwww.mp3vip.org.mp3";

  //fd = open("/home/adam/build/lame-3.99.5/testcase.mp3", O_RDONLY);
  fd = open("/home/adam/live_audio_streaming/tenc/agoria-scala_original_mixwww.mp3vip.org.mp3", O_RDONLY);
  if (fd < 0){
#ifdef DEBUG
    fprintf(stderr, "%s: open error (%s)\n", __func__, strerror(errno));
#endif
    exit(EXIT_SUCCESS);
  }

  while((bytes = read_ssl(ssl, data, BUFF)) > 0){
#ifdef DEBUG
    fprintf(stderr, "%s: read [%d] [%s:%d] [%s]\n", __func__, bytes, get_client_address(cl), get_client_port(cl), data);
#endif
  }
#endif
  
#if 0
  while (run){

    switch (mode){
      
      case MODE_CONNECTING:
        
        bytes = read_ssl(ssl, data, BUFF);
        switch (bytes){
          case 0:
#ifdef DEBUG
            fprintf(stderr, "%s: read EOF client[%s:%d]\n", __func__, get_client_address(cl), get_client_port(cl));
#endif
            run = 0;
            break;

          case -1:
#ifdef DEBUG
            fprintf(stderr, "%s: read error client[%s:%d] (%s)\n", __func__, get_client_address(cl), get_client_port(cl), strerror(errno));
#endif
            run = 0;
            break;
        }

#ifdef DEBUG
        fprintf(stderr, "%s: [%s:%d] [%s]\n", __func__, get_client_address(cl), get_client_port(cl), data);
#endif
        
        res = get_resource_str(data);
        if (res == NULL){
#ifdef DEBUG
          fprintf(stderr, "%s: NULL RESOURCE\n", __func__);        
#endif
          run = 0;
        }
  
#ifdef DEBUG
       fprintf(stderr, "%s: got resource [%s]\n", __func__, res); 
#endif
       
       if (strncmp(res, "/sound", 6) == 0){
         bytes = write_ssl(ssl, REPLY, sizeof(REPLY));
         switch(bytes){
           case -1:
#ifdef DEBUG
             fprintf(stderr, "%s: write error client[%s:%d] (%s)\n", __func__, get_client_address(cl), get_client_port(cl), strerror(errno));
#endif
             run = 0;
             break;
         }
         mode = MODE_SENDING;
       } else {
         run = 0;
       }
       break;


      case MODE_SENDING:
#if 0
        bytes = sendfile(cl->c_fd, fd, NULL, BUFF+initial);
        if (bytes == 0){
          close(fd);
#if 0
          fd = open("/srv/beats/Meditations On Afrocentrism EP/03 Down The Line (It Takes A Number).mp3", O_RDONLY);
          if (fd < 0){
#ifdef DEBUG
            fprintf(stderr, "%s: open error (%s)\n", __func__, strerror(errno));
#endif
            exit(EXIT_SUCCESS);
          }
#endif
        } else if (bytes < 0){
#ifdef DEBUG
          fprintf(stderr, "%s: sendfile error client[%s:%d] (%s)\n", __func__, get_client_address(cl), get_client_port(cl), strerror(errno));
#endif
          exit(EXIT_SUCCESS);
        }

        if (initial > 1){
          initial--;
        } 

#if 0
def DEBUG
        fprintf(stderr, "%s: [%s:%d] sent %d bytes\n", __func__, get_client_address(cl), get_client_port(cl), bytes);
#endif
        usleep(9766/initial);
#endif    
        run = 0;
        break;
    }
  }
#endif

#ifdef DEBUG
  fprintf(stderr, "%s: child[%d] ending\n", __func__, getpid());
#endif

#if 0
  if (fd > 0){
    close(fd);
  } 
#endif

  destroy_spead_client(cl);
  //shutdown(cl->c_fd, SHUT_RDWR);
  exit(EXIT_SUCCESS);
}