libwinpr-sspi: start implementing ISC and ASC for Schannel
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Thu, 3 Jan 2013 18:36:19 +0000 (13:36 -0500)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Thu, 3 Jan 2013 18:36:19 +0000 (13:36 -0500)
winpr/libwinpr/sspi/Schannel/schannel.c
winpr/libwinpr/sspi/Schannel/schannel_openssl.c
winpr/libwinpr/sspi/Schannel/schannel_openssl.h

index 3757e26..d6b5501 100644 (file)
@@ -206,6 +206,7 @@ SECURITY_STATUS SEC_ENTRY schannel_InitializeSecurityContextW(PCredHandle phCred
                PSecBufferDesc pInput, ULONG Reserved2, PCtxtHandle phNewContext,
                PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
 {
+       SECURITY_STATUS status;
        SCHANNEL_CONTEXT* context;
        SCHANNEL_CREDENTIALS* credentials;
 
@@ -227,6 +228,10 @@ SECURITY_STATUS SEC_ENTRY schannel_InitializeSecurityContextW(PCredHandle phCred
                sspi_SecureHandleSetUpperPointer(phNewContext, (void*) SCHANNEL_PACKAGE_NAME);
 
                schannel_openssl_client_init(context->openssl);
+
+               status = schannel_openssl_client_process_tokens(context->openssl, pInput, pOutput);
+
+               return status;
        }
 
        return SEC_E_OK;
@@ -258,6 +263,7 @@ SECURITY_STATUS SEC_ENTRY schannel_AcceptSecurityContext(PCredHandle phCredentia
                PSecBufferDesc pInput, ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext,
                PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp)
 {
+       SECURITY_STATUS status;
        SCHANNEL_CONTEXT* context;
        SCHANNEL_CREDENTIALS* credentials;
 
@@ -276,6 +282,12 @@ SECURITY_STATUS SEC_ENTRY schannel_AcceptSecurityContext(PCredHandle phCredentia
 
                sspi_SecureHandleSetLowerPointer(phNewContext, context);
                sspi_SecureHandleSetUpperPointer(phNewContext, (void*) SCHANNEL_PACKAGE_NAME);
+
+               schannel_openssl_server_init(context->openssl);
+
+               status = schannel_openssl_server_process_tokens(context->openssl, pInput, pOutput);
+
+               return status;
        }
 
        return SEC_E_OK;
index b8a3805..c33e33a 100644 (file)
@@ -53,9 +53,6 @@ char* openssl_get_ssl_error_string(int ssl_error)
 int schannel_openssl_client_init(SCHANNEL_OPENSSL* context)
 {
        int status;
-       int ssl_error;
-       BYTE* ReadBuffer;
-       BYTE* WriteBuffer;
        long options = 0;
 
        context->ctx = SSL_CTX_new(TLSv1_client_method());
@@ -129,30 +126,225 @@ int schannel_openssl_client_init(SCHANNEL_OPENSSL* context)
 
        SSL_set_bio(context->ssl, context->bioRead, context->bioWrite);
 
-       status = SSL_connect(context->ssl);
+       context->ReadBuffer = (BYTE*) malloc(SCHANNEL_CB_MAX_TOKEN);
+       context->WriteBuffer = (BYTE*) malloc(SCHANNEL_CB_MAX_TOKEN);
+
+       return 0;
+}
+
+int schannel_openssl_server_init(SCHANNEL_OPENSSL* context)
+{
+       int status;
+       long options = 0;
+
+       context->ctx = SSL_CTX_new(SSLv23_server_method());
 
-       if (status < 0)
+       if (!context->ctx)
        {
-               ssl_error = SSL_get_error(context->ssl, status);
-               printf("SSL_connect error: %s\n", openssl_get_ssl_error_string(ssl_error));
+               printf("SSL_CTX_new failed\n");
+               return -1;
        }
 
-       ReadBuffer = (BYTE*) malloc(SCHANNEL_CB_MAX_TOKEN);
-       WriteBuffer = (BYTE*) malloc(SCHANNEL_CB_MAX_TOKEN);
+       /*
+        * SSL_OP_NO_SSLv2:
+        *
+        * We only want SSLv3 and TLSv1, so disable SSLv2.
+        * SSLv3 is used by, eg. Microsoft RDC for Mac OS X.
+        */
+       options |= SSL_OP_NO_SSLv2;
 
-       status = BIO_read(context->bioWrite, ReadBuffer, SCHANNEL_CB_MAX_TOKEN);
+       /**
+        * SSL_OP_NO_COMPRESSION:
+        *
+        * The Microsoft RDP server does not advertise support
+        * for TLS compression, but alternative servers may support it.
+        * This was observed between early versions of the FreeRDP server
+        * and the FreeRDP client, and caused major performance issues,
+        * which is why we're disabling it.
+        */
+#ifdef SSL_OP_NO_COMPRESSION
+       options |= SSL_OP_NO_COMPRESSION;
+#endif
 
-       if (status >= 0)
+       /**
+        * SSL_OP_TLS_BLOCK_PADDING_BUG:
+        *
+        * The Microsoft RDP server does *not* support TLS padding.
+        * It absolutely needs to be disabled otherwise it won't work.
+        */
+       options |= SSL_OP_TLS_BLOCK_PADDING_BUG;
+
+       /**
+        * SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS:
+        *
+        * Just like TLS padding, the Microsoft RDP server does not
+        * support empty fragments. This needs to be disabled.
+        */
+       options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+
+       SSL_CTX_set_options(context->ctx, options);
+
+       context->ssl = SSL_new(context->ctx);
+
+       if (!context->ssl)
+       {
+               printf("SSL_new failed\n");
+               return -1;
+       }
+
+#if 0
+       if (SSL_CTX_use_RSAPrivateKey_file(context->ctx, privatekey_file, SSL_FILETYPE_PEM) <= 0)
+       {
+               printf("SSL_CTX_use_RSAPrivateKey_file failed\n");
+               return -1;
+       }
+
+       if (SSL_use_certificate_file(context->ssl, certificate_file, SSL_FILETYPE_PEM) <= 0)
        {
-               winpr_HexDump(ReadBuffer, status);
+               printf("SSL_use_certificate_file failed\n");
+               return -1;
        }
+#endif
+
+       context->bioRead = BIO_new(BIO_s_mem());
 
-       free(ReadBuffer);
-       free(WriteBuffer);
+       if (!context->bioRead)
+       {
+               printf("BIO_new failed\n");
+               return -1;
+       }
+
+       status = BIO_set_write_buf_size(context->bioRead, SCHANNEL_CB_MAX_TOKEN);
+
+       context->bioWrite = BIO_new(BIO_s_mem());
+
+       if (!context->bioWrite)
+       {
+               printf("BIO_new failed\n");
+               return -1;
+       }
+
+       status = BIO_set_write_buf_size(context->bioWrite, SCHANNEL_CB_MAX_TOKEN);
+
+       status = BIO_make_bio_pair(context->bioRead, context->bioWrite);
+
+       SSL_set_bio(context->ssl, context->bioRead, context->bioWrite);
+
+       context->ReadBuffer = (BYTE*) malloc(SCHANNEL_CB_MAX_TOKEN);
+       context->WriteBuffer = (BYTE*) malloc(SCHANNEL_CB_MAX_TOKEN);
 
        return 0;
 }
 
+SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context, PSecBufferDesc pInput, PSecBufferDesc pOutput)
+{
+       int status;
+       int ssl_error;
+       PSecBuffer pBuffer;
+
+       if (!context->connected)
+       {
+               if (pInput)
+               {
+                       if (pInput->cBuffers < 1)
+                               return SEC_E_INVALID_TOKEN;
+
+                       pBuffer = &pInput->pBuffers[0];
+
+                       if (pBuffer->BufferType != SECBUFFER_TOKEN)
+                               return SEC_E_INVALID_TOKEN;
+
+                       status = BIO_write(context->bioRead, pBuffer->pvBuffer, pBuffer->cbBuffer);
+               }
+
+               status = SSL_connect(context->ssl);
+
+               if (status < 0)
+               {
+                       ssl_error = SSL_get_error(context->ssl, status);
+                       printf("SSL_connect error: %s\n", openssl_get_ssl_error_string(ssl_error));
+               }
+
+               status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN);
+
+               if (status >= 0)
+               {
+                       winpr_HexDump(context->ReadBuffer, status);
+               }
+
+               if (pOutput->cBuffers < 1)
+                       return SEC_E_INVALID_TOKEN;
+
+               pBuffer = &pOutput->pBuffers[0];
+
+               if (pBuffer->BufferType != SECBUFFER_TOKEN)
+                       return SEC_E_INVALID_TOKEN;
+
+               if (pBuffer->cbBuffer < status)
+                       return SEC_E_INSUFFICIENT_MEMORY;
+
+               CopyMemory(pBuffer->pvBuffer, context->ReadBuffer, status);
+               pBuffer->cbBuffer = status;
+
+               return SEC_I_CONTINUE_NEEDED;
+       }
+
+       return SEC_E_OK;
+}
+
+SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context, PSecBufferDesc pInput, PSecBufferDesc pOutput)
+{
+       int status;
+       int ssl_error;
+       PSecBuffer pBuffer;
+
+       if (!context->connected)
+       {
+               if (pInput->cBuffers < 1)
+                       return SEC_E_INVALID_TOKEN;
+
+               pBuffer = &pInput->pBuffers[0];
+
+               if (pBuffer->BufferType != SECBUFFER_TOKEN)
+                       return SEC_E_INVALID_TOKEN;
+
+               status = BIO_write(context->bioRead, pBuffer->pvBuffer, pBuffer->cbBuffer);
+
+               status = SSL_accept(context->ssl);
+
+               if (status < 0)
+               {
+                       ssl_error = SSL_get_error(context->ssl, status);
+                       printf("SSL_accept error: %s\n", openssl_get_ssl_error_string(ssl_error));
+               }
+
+               status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN);
+
+               if (status >= 0)
+               {
+                       winpr_HexDump(context->ReadBuffer, status);
+               }
+
+               if (pOutput->cBuffers < 1)
+                       return SEC_E_INVALID_TOKEN;
+
+               pBuffer = &pOutput->pBuffers[0];
+
+               if (pBuffer->BufferType != SECBUFFER_TOKEN)
+                       return SEC_E_INVALID_TOKEN;
+
+               if (pBuffer->cbBuffer < status)
+                       return SEC_E_INSUFFICIENT_MEMORY;
+
+               CopyMemory(pBuffer->pvBuffer, context->ReadBuffer, status);
+               pBuffer->cbBuffer = status;
+
+               return SEC_I_CONTINUE_NEEDED;
+       }
+
+       return SEC_E_OK;
+}
+
 SCHANNEL_OPENSSL* schannel_openssl_new()
 {
        SCHANNEL_OPENSSL* context;
@@ -165,6 +357,8 @@ SCHANNEL_OPENSSL* schannel_openssl_new()
 
                SSL_load_error_strings();
                SSL_library_init();
+
+               context->connected = FALSE;
        }
 
        return context;
@@ -172,8 +366,11 @@ SCHANNEL_OPENSSL* schannel_openssl_new()
 
 void schannel_openssl_free(SCHANNEL_OPENSSL* context)
 {
-       if (!context)
-               return;
+       if (context)
+       {
+               free(context->ReadBuffer);
+               free(context->WriteBuffer);
 
-       free(context);
+               free(context);
+       }
 }
index a023b79..9d6624c 100644 (file)
@@ -35,12 +35,19 @@ struct _SCHANNEL_OPENSSL
 {
        SSL* ssl;
        SSL_CTX* ctx;
+       BOOL connected;
        BIO* bioRead;
        BIO* bioWrite;
+       BYTE* ReadBuffer;
+       BYTE* WriteBuffer;
 };
 typedef struct _SCHANNEL_OPENSSL SCHANNEL_OPENSSL;
 
 int schannel_openssl_client_init(SCHANNEL_OPENSSL* context);
+int schannel_openssl_server_init(SCHANNEL_OPENSSL* context);
+
+SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context, PSecBufferDesc pInput, PSecBufferDesc pOutput);
+SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context, PSecBufferDesc pInput, PSecBufferDesc pOutput);
 
 SCHANNEL_OPENSSL* schannel_openssl_new();
 void schannel_openssl_free(SCHANNEL_OPENSSL* context);