/* 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; }
/* Server initiated rehandshake. Builds and sends the HELLO_REQUEST message */ void sslRehandshake(sslConn_t *cp) { matrixSslEncodeHelloRequest(cp->ssl, &cp->outsock); psSocketWrite(cp->fd, &cp->outsock); cp->outsock.start = cp->outsock.end = cp->outsock.buf; }
/* 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; }