Esempio n. 1
0
/*
  Client side.  Make a socket connection and go through the SSL handshake
  phase in blocking mode.  The second parameter is an optional function
  callback for user-level certificate validation which should be NULL if
  not needed. Third parameter is an optional second argument for the validation
  function.

*/
int SSL_connect(SSL *ssl, int (*certValidator)(sslCertInfo_t *t, void *arg), void *certValidatorArgs) {
  if (!(ssl->ssl)) {
    SSL_free(ssl);
    return -1;
  }

  if(certValidator != NULL) {
    matrixSslSetCertValidator(ssl->ssl, certValidator, certValidatorArgs);
  } else {
    matrixSslSetCertValidator(ssl->ssl, _always_true_validator, certValidatorArgs);
  }
  int ret = _ssl_doHandshake(ssl);

  return ret;
}
Esempio n. 2
0
/*
	Client side.  Make a socket connection and go through the SSL handshake
	phase in blocking mode.  The last parameter is an optional function
	callback for user-level certificate validation.  NULL if not needed.
*/
int sslConnect(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys, 
			   sslSessionId_t *id, short cipherSuite, 
			   int (*certValidator)(sslCertInfo_t *t, void *arg))
{
	sslConn_t	*conn;

/*
	Create a new SSL session for the new socket and register the
	user certificate validator 
*/
	conn = calloc(sizeof(sslConn_t), 1);
	conn->fd = fd;
	if (matrixSslNewSession(&conn->ssl, keys, id, 0) < 0) {
		sslFreeConnection(&conn);
		return -1;
	}
	matrixSslSetCertValidator(conn->ssl, certValidator, NULL);

	*cpp = sslDoHandshake(conn, cipherSuite);
	
	if (*cpp == NULL) {
		return -1;
	}
	return 0;
}
Esempio n. 3
0
int sslConnect(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys, 
	       sslSessionId_t *id, short cipherSuite, 
	       int (*certValidator)(sslCertInfo_t *t, void *arg))
{
  sslConn_t *conn;

  conn = calloc(sizeof(sslConn_t), 1);
  conn->fd = fd;

  if (matrixSslNewSession(&conn->ssl, keys, id, 0) < 0) {
    fprintf(stderr, "error %s:%d\n",__FILE__,__LINE__);
    sslFreeConnection(&conn);
    return -1;
  }
  
  matrixSslSetCertValidator(conn->ssl, certValidator, keys);
  
  *cpp = sslDoHandshake(conn, cipherSuite);
  
  if (*cpp == NULL) {
    fprintf(stderr, "error %s:%d\n",__FILE__,__LINE__);
    return -1;
  }

  return 0;
}
Esempio n. 4
0
int32 matrixSslNewServerSession(ssl_t **ssl, const sslKeys_t *keys,
				sslCertCb_t certCb,
				sslSessOpts_t *options)
{
	ssl_t		*lssl;

	if (!ssl) {
		return PS_ARG_FAIL;
	}
	if (options == NULL) {
		return PS_ARG_FAIL;
	}

	/* Add SERVER_FLAGS to versionFlag member of options */
	options->versionFlag |= SSL_FLAGS_SERVER;
	*ssl = NULL;
	lssl = NULL;

#ifdef USE_CLIENT_AUTH
	if (certCb) {
		options->versionFlag |= SSL_FLAGS_CLIENT_AUTH;
		if (matrixSslNewSession(&lssl, keys, NULL, options) < 0) {
			goto NEW_SVR_ERROR;
		}
		matrixSslSetCertValidator(lssl, (sslCertCb_t)certCb);
	} else if (matrixSslNewSession(&lssl, keys, NULL, options) < 0) {
		goto NEW_SVR_ERROR;
	}
#else
	psAssert(certCb == NULL);
	if (matrixSslNewSession(&lssl, keys, NULL, options) < 0) {
		goto NEW_SVR_ERROR;
	}
#endif /* USE_CLIENT_AUTH */

	lssl->userPtr = options->userPtr;
	if (options->maxFragLen < 0) {
		/* User wants to deny a client request for changing max frag len */
		lssl->extFlags.deny_max_fragment_len = 1;
	}
	lssl->maxPtFrag = SSL_MAX_PLAINTEXT_LEN;

	if (options->truncHmac < 0) {
		lssl->extFlags.deny_truncated_hmac = 1;
	}
	
	/* Extended master secret is enabled by default.  If user sets to 1 this
		is a flag to REQUIRE its use */
	if (options->extendedMasterSecret > 0) {
		lssl->extFlags.require_extended_master_secret = 1;
	}

	*ssl = lssl;
	return MATRIXSSL_SUCCESS;

NEW_SVR_ERROR:
	if (lssl) matrixSslDeleteSession(lssl);
	return PS_FAILURE;
}
Esempio n. 5
0
int sslAccept(sslConn_t **cpp, SOCKET fd, sslKeys_t *keys,
	      int (*certValidator)(sslCertInfo_t *t, void *arg), int flags)
{
  sslConn_t		*conn;
  unsigned char	buf[1024];
  int				status, rc;
  conn = calloc(sizeof(sslConn_t), 1);
  conn->fd = fd;
  if (matrixSslNewSession(&conn->ssl, keys, NULL,
			  SSL_FLAGS_SERVER | flags) < 0) {
    sslFreeConnection(&conn);
    return -1;
  }
  
#ifdef USE_CLIENT_AUTH
  matrixSslSetCertValidator(conn->ssl, certValidator, keys);
#endif /* USE_CLIENT_AUTH */
  memset(&conn->inbuf, 0x0, sizeof(sslBuf_t));
  conn->insock.size = 1024;
  conn->insock.start = conn->insock.end = conn->insock.buf = 
    (unsigned char *)malloc(conn->insock.size);
  conn->outsock.size = 1024;
  conn->outsock.start = conn->outsock.end = conn->outsock.buf = 
    (unsigned char *)malloc(conn->outsock.size);
  conn->inbuf.size = 0;
  conn->inbuf.start = conn->inbuf.end = conn->inbuf.buf = NULL;
  *cpp = conn;
  
 readMore:
  rc = sslRead(conn, buf, sizeof(buf), &status);
  if (rc == 0) {
    if (status == SSLSOCKET_EOF || status == SSLSOCKET_CLOSE_NOTIFY) {
      sslFreeConnection(&conn);
      return -1;
    }
    if (matrixSslHandshakeIsComplete(conn->ssl) == 0) {
      goto readMore;
    }
  } else if (rc > 0) {
    socketAssert(0);
    return -1;
  } else {
    fprintf(stderr, "sslRead error in sslAccept\n");
    sslFreeConnection(&conn);
    return -1;
  }
  *cpp = conn;
  
  return 0;
}
Esempio n. 6
0
/*
	Create a new server SSL session
	This creates internal SSL buffers and cipher structures
	Internal SSL state is set to expect an incoming 'HelloRequest'

	Return	MATRIXSSL_SUCCESS on success
			< 0 on error
 */
int32 matrixSslNewServerSession(ssl_t **ssl, sslKeys_t *keys,
			int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert),
			int32 flags)
{
	ssl_t		*lssl;
	int32		lflags = SSL_FLAGS_SERVER;
	
	if (!ssl) {
		return PS_ARG_FAIL;
	}
	
	lflags |= flags;
	*ssl = NULL;
	lssl = NULL;
	
#ifdef USE_CLIENT_AUTH	
	if (certCb) {
		lflags |= SSL_FLAGS_CLIENT_AUTH;
		if (matrixSslNewSession(&lssl, keys, NULL, lflags) < 0) {
			goto NEW_SVR_ERROR;
		}
		matrixSslSetCertValidator(lssl, (sslCertCb_t)certCb);
	} else if (matrixSslNewSession(&lssl, keys, NULL, lflags) < 0) {
		goto NEW_SVR_ERROR;
	}
#else
	psAssert(certCb == NULL);
	if (matrixSslNewSession(&lssl, keys, NULL, lflags) < 0) {
		goto NEW_SVR_ERROR;
	}
#endif /* USE_CLIENT_AUTH */
	
	lssl->maxPtFrag = SSL_MAX_PLAINTEXT_LEN;
	*ssl = lssl;
	return MATRIXSSL_SUCCESS;
	
NEW_SVR_ERROR:
	if (lssl) matrixSslDeleteSession(lssl);
	return PS_FAILURE;
}
Esempio n. 7
0
openssl_con *
openssl_accept_fd(openssl_env *env, int fd, int timeout, struct redir_conn_t *conn) {
  openssl_con *c = (openssl_con *)calloc(1, sizeof(openssl_con));
  int rc;

  if (!c) return 0;

  if (!env || !env->ready) {
    syslog(LOG_ERR, "SSL not available!");
    openssl_free(c);
    return 0;
  }

  c->env = env;
#ifdef HAVE_OPENSSL
  c->con = (SSL *)SSL_new(env->ctx);
#elif  HAVE_MATRIXSSL
  c->con = (SSL *)SSL_new(env->keys, SSL_FLAGS_SERVER);
#endif
  c->sock = fd;
  c->timeout = timeout;

  SSL_set_fd(c->con, c->sock);

#ifdef HAVE_OPENSSL
#ifdef HAVE_OPENSSL_ENGINE
  SSL_clear(c->con);
#endif

#ifdef HAVE_OPENSSL_ENGINE
  SSL_set_app_data(c->con, c);
#endif
  SSL_set_accept_state(c->con);

#ifdef HAVE_OPENSSL_ENGINE
  SSL_set_verify_result(c->con, X509_V_OK);
#endif

  if ((rc = openssl_check_accept(c, conn)) < 0) {
    SSL_set_shutdown(c->con, SSL_RECEIVED_SHUTDOWN);
    openssl_free(c);
    return 0;
  }

#elif  HAVE_MATRIXSSL

  /* ndelay_off(c->sock); */

  matrixSslSetCertValidator(c->con->ssl, certValidator, c->con->keys);

  if ((rc = SSL_accept2(c->con)) < 0) {
    syslog(LOG_ERR, "%s: SSL accept failure %s", strerror(errno), c->con->status);
    openssl_free(c);
    return 0;
  }

  SSL_is_init_finished(c->con);

  /* ndelay_on(c->sock);*/

#else
#error NO SSL SUPPORT
#endif

  return c;
}
Esempio n. 8
0
int32_t matrixSslNewClientSession(ssl_t **ssl, const sslKeys_t *keys,
				sslSessionId_t *sid,
				const uint16_t cipherSpec[], uint8_t cipherSpecLen,
				sslCertCb_t certCb,
				const char *expectedName, tlsExtension_t *extensions,
				sslExtCb_t extCb,
				sslSessOpts_t *options)
{
	ssl_t		*lssl;
	psBuf_t		tmp;
	uint32		len;
	int32		rc, i;

	if (!ssl) {
		return PS_ARG_FAIL;
	}
	if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) {
		return PS_ARG_FAIL;
	}
	if (options == NULL) {
		return PS_ARG_FAIL;
	}

	*ssl = NULL;
	lssl = NULL;

	/* Give priority to cipher suite if session id is provided and doesn't match */
	if (cipherSpec != NULL && cipherSpec[0] != 0 && sid != NULL &&
			sid->cipherId != 0) {
		rc = 1;
		for (i = 0; i < cipherSpecLen; i++) {
			if (cipherSpec[i] == sid->cipherId) {
				rc = 0;
			}
		}
		if (rc) {
			psTraceInfo("Explicit cipher suite will override session cache\n");
			memset(sid->id, 0, SSL_MAX_SESSION_ID_SIZE);
			memset(sid->masterSecret, 0, SSL_HS_MASTER_SIZE);
			sid->cipherId = 0;
		}
	}

	if ((rc = matrixSslNewSession(&lssl, keys, sid, options)) < 0) {
		return rc;
	}
	lssl->userPtr = options->userPtr;

#ifndef USE_ONLY_PSK_CIPHER_SUITE
	if (expectedName) {
		if (psX509ValidateGeneralName((char*)expectedName) < 0) {
			matrixSslDeleteSession(lssl);
			return rc;
		}
		rc = strlen(expectedName);
		lssl->expectedName = psMalloc(lssl->sPool, rc + 1);
		strcpy(lssl->expectedName, expectedName);
	}
	if (certCb) {
		matrixSslSetCertValidator(lssl, certCb);
	}
#endif
	if (extCb) {
		lssl->extCb = extCb;
	}
RETRY_HELLO:
	tmp.size = lssl->outsize;
	tmp.buf = tmp.start = tmp.end = lssl->outbuf;
	if ((rc = matrixSslEncodeClientHello(lssl, &tmp, cipherSpec, cipherSpecLen,
			&len, extensions, options)) < 0) {
		if (rc == SSL_FULL) {
			if ((tmp.buf = psRealloc(lssl->outbuf, len, lssl->bufferPool))
					== NULL) {
				matrixSslDeleteSession(lssl);
				return PS_MEM_FAIL;
			}
			lssl->outbuf = tmp.buf;
			lssl->outsize = len;
			goto RETRY_HELLO;
		} else {
			matrixSslDeleteSession(lssl);
			return rc;
		}
	}
	psAssert(tmp.start == tmp.buf);
	lssl->outlen = tmp.end - tmp.start;
	*ssl = lssl;
	return MATRIXSSL_REQUEST_SEND;
}
Esempio n. 9
0
/*
	Encode a CLIENT_HELLO or HELLO_REQUEST to re-handshake an existing
	connection.

	Can't "downgrade" the re-handshake.  This means if keys or certCb are
	NULL we stick with whatever the session already has loaded.

	keys should be NULL if no change in key material is being made

	cipherSpec is only used by clients
 */
int32_t matrixSslEncodeRehandshake(ssl_t *ssl, sslKeys_t *keys,
				int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert),
				uint32 sessionOption,
				const uint16_t cipherSpec[], uint8_t cipherSpecLen)
{
	sslBuf_t		sbuf;
	int32			rc, i;
	uint32			reqLen, newLen;
	unsigned char	*p;
	sslSessOpts_t	options;

	/* Clear extFlags for rehandshakes */
	ssl->extFlags.truncated_hmac = 0;
	ssl->extFlags.sni = 0;
	
	if (!ssl) {
		return PS_ARG_FAIL;
	}
	if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) {
		return PS_ARG_FAIL;
	}
	if (ssl->bFlags & BFLAG_CLOSE_AFTER_SENT) {
		return PS_PROTOCOL_FAIL;
	}
	psAssert(ssl->outsize > 0 && ssl->outbuf != NULL);

#ifdef DISABLE_DTLS_CLIENT_CHANGE_CIPHER_FROM_GCM_TO_GCM
#endif /* DISABLE_DTLS_CLIENT_CHANGE_CIPHER_FROM_GCM_TO_GCM */

#ifdef USE_ZLIB_COMPRESSION
	/* Re-handshakes are not currently supported for compressed sessions. */
	if (ssl->compression > 0) {
		psTraceInfo("Re-handshakes not supported for compressed sessions\n");
		return PS_UNSUPPORTED_FAIL;
	}
#endif
/*
	The only explicit option that can be passsed in is
	SSL_OPTION_FULL_HANDSHAKE to indicate no resumption is allowed
*/
	if (sessionOption == SSL_OPTION_FULL_HANDSHAKE) {
		matrixSslSetSessionOption(ssl, sessionOption, NULL);
	}
/*
	If the key material or cert callback are provided we have to assume it
	was intentional to "upgrade" the re-handshake and we force full handshake
	No big overhead calling SetSessionOption with FULL_HS multiple times.
*/
	if (keys != NULL) {
		ssl->keys = keys;
		matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL);
	}

#ifndef USE_ONLY_PSK_CIPHER_SUITE
	if (certCb != NULL) {
		matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL);
#if defined(USE_CLIENT_AUTH) || defined(USE_CLIENT_SIDE_SSL)
		matrixSslSetCertValidator(ssl, certCb);
#endif /* USE_CLIENT_AUTH || USE_CLIENT_SIDE_SSL */
#if defined(USE_CLIENT_AUTH) && defined(USE_SERVER_SIDE_SSL)
/*
		If server, a certCb is an explicit flag to set client auth just as
		it is in matrixSslNewServerSession
*/
		if (ssl->flags & SSL_FLAGS_SERVER) {
			matrixSslSetSessionOption(ssl, SSL_OPTION_ENABLE_CLIENT_AUTH, NULL);
		}
#endif /* USE_CLIENT_AUTH && USE_SERVER_SIDE_SSL */
	}
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */

/*
	If cipher spec is explicitly different from current, force a full handshake
*/
	if (!(ssl->flags & SSL_FLAGS_SERVER)) {
		rc = 0;
		if (cipherSpecLen > 0) {
			rc = 1;
			for (i = 0; i < cipherSpecLen; i++) {
				if (cipherSpec[i] == ssl->cipher->ident) {
					rc = 0;
				}
			}
		}
		if (rc) {
			matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL);
		}
	}
#ifdef USE_DTLS
	if (ssl->flags & SSL_FLAGS_DTLS) {
		/* Resend epoch should be brought up-to-date with new epoch */
		ssl->resendEpoch[0] = ssl->epoch[0];
		ssl->resendEpoch[1] = ssl->epoch[1];

		ssl->msn = ssl->resendMsn = 0;
	}
#endif /* USE_DTLS */
/*
	Options are set.  Encode the HELLO message
*/
	newLen = 0;
L_REHANDSHAKE:
	if (ssl->flags & SSL_FLAGS_SERVER) {
		sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen;
		sbuf.size = ssl->outsize - ssl->outlen;
		if ((rc = matrixSslEncodeHelloRequest(ssl, &sbuf, &reqLen)) < 0) {
			if (rc == SSL_FULL && newLen == 0) {
				newLen = ssl->outlen + reqLen;
				if (newLen < SSL_MAX_BUF_SIZE) {
					if ((p = psRealloc(ssl->outbuf, newLen, ssl->bufferPool))
							== NULL){
						return PS_MEM_FAIL;
					}
					ssl->outbuf = p;
					ssl->outsize = newLen;
					goto L_REHANDSHAKE;
				}
			}
			return rc;
		}
	} else {
		sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen;
		sbuf.size = ssl->outsize - ssl->outlen;
		memset(&options, 0x0, sizeof(sslSessOpts_t));
#ifdef USE_ECC_CIPHER_SUITE
		options.ecFlags = ssl->ecInfo.ecFlags;
#endif
		/* Use extended master secret if original connection used it */
		if (ssl->extFlags.extended_master_secret == 1) {
			options.extendedMasterSecret = 1;
			ssl->extFlags.extended_master_secret = 0;
		} else {
			options.extendedMasterSecret = -1;
		}
	
		if ((rc = matrixSslEncodeClientHello(ssl, &sbuf, cipherSpec,
				cipherSpecLen, &reqLen, NULL, &options)) < 0) {
			if (rc == SSL_FULL && newLen == 0) {
				newLen = ssl->outlen + reqLen;
				if (newLen < SSL_MAX_BUF_SIZE) {
					if ((p = psRealloc(ssl->outbuf, newLen, ssl->bufferPool))
							== NULL) {
						return PS_MEM_FAIL;
					}
					ssl->outbuf = p;
					ssl->outsize = newLen;
					goto L_REHANDSHAKE;
				}
			}
			return rc;
		}
	}
	ssl->outlen += sbuf.end - sbuf.start;
	return MATRIXSSL_SUCCESS;
}
Esempio n. 10
0
int ssl_io(unsigned int newsession, const char **prog) {
  if (client) { fdstdin =6; fdstdou =7; }
  bad_certificate = env_get("SSLIO_BAD_CERTIFICATE");
  if ((s =env_get("SSLIO_BUFIN"))) scan_ulong(s, &bufsizein);
  if ((s =env_get("SSLIO_BUFOU"))) scan_ulong(s, &bufsizeou);
  if (bufsizein < 64) bufsizein =64;
  if (bufsizeou < 64) bufsizeou =64;
  if ((s =env_get("SSLIO_HANDSHAKE_TIMEOUT")))
    scan_ulong(s, &handshake_timeout);
  if (handshake_timeout < 1) handshake_timeout =1;

  if (pipe(encpipe) == -1) fatalm("unable to create pipe for encoding");
  if (pipe(decpipe) == -1) fatalm("unable to create pipe for decoding");
  if ((pid =fork()) == -1) fatalm("unable to fork");
  if (pid == 0) {
    if (close(encpipe[1]) == -1)
      fatalm("unable to close encoding pipe output");
    if (close(decpipe[0]) == -1)
      fatalm("unable to close decoding pipe input");
    if (newsession) if (matrixSslOpen() < 0) fatalm("unable to initialize ssl");
    if (root) {
      if (chdir(root) == -1) fatalm("unable to change to new root directory");
      if (chroot(".") == -1) fatalm("unable to chroot");
    }
    if (ssluser) {
      /* drop permissions */
      if (setgroups(sslugid.gids, sslugid.gid) == -1)
        fatal("unable to set groups");
      if (setgid(*sslugid.gid) == -1) fatal("unable to set gid");
      if (prot_uid(sslugid.uid) == -1) fatalm("unable to set uid");
    }
    if (newsession) {
      if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) {
        if (client) fatalm("unable to read cert, key, or ca file");
        fatalm("unable to read cert or key file");
      }
      if (matrixSslNewSession(&ssl, keys, 0, client?0:SSL_FLAGS_SERVER) < 0)
        fatalmx("unable to create ssl session");
    }
    if (client)
      if (ca || bad_certificate) matrixSslSetCertValidator(ssl, &validate, 0);

    sig_catch(sig_term, sig_term_handler);
    sig_ignore(sig_pipe);
    doio();
    finish();
    _exit(0);
  }
  if (close(encpipe[0]) == -1) fatalm("unable to close encoding pipe input");
  if (close(decpipe[1]) == -1) fatalm("unable to close decoding pipe output");
  if (fd_move(fdstdin, decpipe[0]) == -1)
    fatalm("unable to setup filedescriptor for decoding");
  if (fd_move(fdstdou, encpipe[1]) == -1)
    fatalm("unable to setup filedescriptor for encoding");
  sslCloseOsdep();
  if (svuser) {
    if (setgroups(ugid.gids, ugid.gid) == -1)
      fatal("unable to set groups for prog");
    if (setgid(*ugid.gid) == -1) fatal("unable to set gid for prog");
    if (prot_uid(ugid.uid) == -1) fatalm("unable to set uid for prog");
  }
  pathexec(prog);
  fatalm("unable to run prog");
  return(111);
}
Esempio n. 11
0
/*
	Encode a CLIENT_HELLO or HELLO_REQUEST to re-handshake an existing
	connection.
		
	Can't "downgrade" the re-handshake.  This means if keys or certCb are
	NULL we stick with whatever the session already has loaded.
	
	keys should be NULL if no change in key material is being made
	
	cipherSpec is only used by clients
 */
int32 matrixSslEncodeRehandshake(ssl_t *ssl, sslKeys_t *keys,
				int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert),
				uint32 sessionOption, uint32 cipherSpec[], uint16 cipherSpecLen)
{
	sslBuf_t		sbuf;
	int32			rc, i;
	uint32			reqLen, newLen;
	unsigned char	*p;
	
	if (!ssl) {
		return PS_ARG_FAIL;
	}
	if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) {
		return PS_ARG_FAIL;
	}
	if (ssl->bFlags & BFLAG_CLOSE_AFTER_SENT) {
		return PS_PROTOCOL_FAIL;
	}	
	psAssert(ssl->outsize > 0 && ssl->outbuf != NULL);
	
#ifdef USE_ZLIB_COMPRESSION
	/* Re-handshakes are not currently supported for compressed sessions. */
	if (ssl->compression > 0) {
		psTraceInfo("Re-handshakes not supported for compressed sessions\n");
		return PS_UNSUPPORTED_FAIL;
	}
#endif
/*
	The only explicit option that can be passsed in is
	SSL_OPTION_FULL_HANDSHAKE to indicate no resumption is allowed
*/
	if (sessionOption == SSL_OPTION_FULL_HANDSHAKE) {
		matrixSslSetSessionOption(ssl, sessionOption, NULL);
	}
/*
	If the key material or cert callback are provided we have to assume it
	was intentional to "upgrade" the re-handshake and we force full handshake
	No big overhead calling SetSessionOption with FULL_HS multiple times.
*/
	if (keys != NULL) {
		ssl->keys = keys;
		matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL);
	}

#ifndef USE_ONLY_PSK_CIPHER_SUITE
	if (certCb != NULL) { 	
		matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL);
#if defined(USE_CLIENT_AUTH) || defined(USE_CLIENT_SIDE_SSL)
		matrixSslSetCertValidator(ssl, (sslCertCb_t)certCb);
#endif /* USE_CLIENT_AUTH || USE_CLIENT_SIDE_SSL */
#if defined(USE_CLIENT_AUTH) && defined(USE_SERVER_SIDE_SSL) 		
/*
		If server, a certCb is an explicit flag to set client auth just as
		it is in matrixSslNewServerSession
*/		
		if (ssl->flags & SSL_FLAGS_SERVER) {
			matrixSslSetSessionOption(ssl, SSL_OPTION_ENABLE_CLIENT_AUTH, NULL);
		}
#endif /* USE_CLIENT_AUTH && USE_SERVER_SIDE_SSL */
	}	
#endif /* !USE_ONLY_PSK_CIPHER_SUITE */
	
/*
	If cipher spec is explicitly different from current, force a full handshake
*/
	if (!(ssl->flags & SSL_FLAGS_SERVER)) {
		rc = 0;
		if (cipherSpecLen > 0) {
			rc = 1;
			for (i = 0; i < cipherSpecLen; i++) {
				if (cipherSpec[i] == ssl->cipher->ident) {
					rc = 0;
				}
			}
		}
		if (rc) {
			matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL);
		}
	}
/*
	Options are set.  Encode the HELLO message
*/
	newLen = 0;
L_REHANDSHAKE:
	if (ssl->flags & SSL_FLAGS_SERVER) {
		sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen;
		sbuf.size = ssl->outsize - ssl->outlen;
		if ((rc = matrixSslEncodeHelloRequest(ssl, &sbuf, &reqLen)) < 0) {
			if (rc == SSL_FULL && newLen == 0) {
				newLen = ssl->outlen + reqLen;
				if (newLen < SSL_MAX_BUF_SIZE) {
					if ((p = psRealloc(ssl->outbuf, newLen)) == NULL){
						return PS_MEM_FAIL;
					}
					ssl->outbuf = p;
					ssl->outsize = newLen;
					goto L_REHANDSHAKE;
				}
			}
			return rc;
		}
	} else {
		sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen;
		sbuf.size = ssl->outsize - ssl->outlen;
		if ((rc = matrixSslEncodeClientHello(ssl, &sbuf, cipherSpec,
				cipherSpecLen, &reqLen, NULL)) < 0) {
			if (rc == SSL_FULL && newLen == 0) {
				newLen = ssl->outlen + reqLen;
				if (newLen < SSL_MAX_BUF_SIZE) {
					if ((p = psRealloc(ssl->outbuf, newLen)) == NULL) {
						return PS_MEM_FAIL;
					}
					ssl->outbuf = p;
					ssl->outsize = newLen;
					goto L_REHANDSHAKE;
				}
			}
			return rc;
		}
	}
	ssl->outlen += sbuf.end - sbuf.start;
	return MATRIXSSL_SUCCESS;
}