license: implement server-side management
authorDavid Fort <contact@hardening-consulting.com>
Thu, 1 Nov 2018 10:02:54 +0000 (11:02 +0100)
committerDavid Fort <contact@hardening-consulting.com>
Mon, 21 Jan 2019 08:57:15 +0000 (09:57 +0100)
Add server-side management of the licensing workflow. The default
behaviour is to accept the client, but if a server wants to implement
full licensing support as in MS-RDPELE it is possible by defining a callback.

include/freerdp/license.h [new file with mode: 0644]
include/freerdp/peer.h
libfreerdp/core/license.c
libfreerdp/core/license.h
libfreerdp/core/peer.c

diff --git a/include/freerdp/license.h b/include/freerdp/license.h
new file mode 100644 (file)
index 0000000..ca68335
--- /dev/null
@@ -0,0 +1,160 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * Licensing API
+ *
+ * Copyright 2018 David Fort <contact@hardening-consulting.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_LICENSE_H
+#define FREERDP_LICENSE_H
+
+#include <freerdp/api.h>
+
+
+typedef struct rdp_license rdpLicense;
+
+/** @brief Licensing Packet Types */
+enum
+{
+       LICENSE_REQUEST         = 0x01,
+       PLATFORM_CHALLENGE      = 0x02,
+       NEW_LICENSE             = 0x03,
+       UPGRADE_LICENSE         = 0x04,
+       LICENSE_INFO            = 0x12,
+       NEW_LICENSE_REQUEST     = 0x13,
+       PLATFORM_CHALLENGE_RESPONSE     = 0x15,
+       ERROR_ALERT                     = 0xFF
+};
+
+#define LICENSE_PKT_CS_MASK    (LICENSE_INFO | NEW_LICENSE_REQUEST | PLATFORM_CHALLENGE_RESPONSE | ERROR_ALERT)
+#define LICENSE_PKT_SC_MASK    (LICENSE_REQUEST | PLATFORM_CHALLENGE | NEW_LICENSE | UPGRADE_LICENSE | ERROR_ALERT)
+#define LICENSE_PKT_MASK       (LICENSE_PKT_CS_MASK | LICENSE_PKT_SC_MASK)
+
+#define LICENSE_PREAMBLE_LENGTH                        4
+
+/* Cryptographic Lengths */
+
+#define CLIENT_RANDOM_LENGTH                   32
+#define SERVER_RANDOM_LENGTH                   32
+#define MASTER_SECRET_LENGTH                   48
+#define PREMASTER_SECRET_LENGTH                        48
+#define SESSION_KEY_BLOB_LENGTH                        48
+#define MAC_SALT_KEY_LENGTH                    16
+#define LICENSING_ENCRYPTION_KEY_LENGTH                16
+#define HWID_PLATFORM_ID_LENGTH                        4
+#define HWID_UNIQUE_DATA_LENGTH                        16
+#define HWID_LENGTH                            20
+#define LICENSING_PADDING_SIZE                 8
+
+/* Preamble Flags */
+
+#define PREAMBLE_VERSION_2_0                   0x02
+#define PREAMBLE_VERSION_3_0                   0x03
+#define LicenseProtocolVersionMask             0x0F
+#define EXTENDED_ERROR_MSG_SUPPORTED           0x80
+
+/** @brief binary Blob Types */
+enum
+{
+       BB_ANY_BLOB                                     = 0x0000,
+       BB_DATA_BLOB                            = 0x0001,
+       BB_RANDOM_BLOB                          = 0x0002,
+       BB_CERTIFICATE_BLOB                     = 0x0003,
+       BB_ERROR_BLOB                           = 0x0004,
+       BB_ENCRYPTED_DATA_BLOB          = 0x0009,
+       BB_KEY_EXCHG_ALG_BLOB           = 0x000D,
+       BB_SCOPE_BLOB                           = 0x000E,
+       BB_CLIENT_USER_NAME_BLOB        = 0x000F,
+       BB_CLIENT_MACHINE_NAME_BLOB     = 0x0010
+};
+
+/* License Key Exchange Algorithms */
+
+#define KEY_EXCHANGE_ALG_RSA                   0x00000001
+
+/** @brief license Error Codes */
+enum
+{
+       ERR_INVALID_SERVER_CERTIFICATE  = 0x00000001,
+       ERR_NO_LICENSE                                  = 0x00000002,
+       ERR_INVALID_MAC                                 = 0x00000003,
+       ERR_INVALID_SCOPE                               = 0x00000004,
+       ERR_NO_LICENSE_SERVER                   = 0x00000006,
+       STATUS_VALID_CLIENT                             = 0x00000007,
+       ERR_INVALID_CLIENT                              = 0x00000008,
+       ERR_INVALID_PRODUCT_ID                  = 0x0000000B,
+       ERR_INVALID_MESSAGE_LENGTH              = 0x0000000C
+};
+
+/** @brief state Transition Codes */
+enum
+{
+       ST_TOTAL_ABORT                  = 0x00000001,
+       ST_NO_TRANSITION                = 0x00000002,
+       ST_RESET_PHASE_TO_START = 0x00000003,
+       ST_RESEND_LAST_MESSAGE  = 0x00000004
+};
+
+/** @brief Platform Challenge Types */
+enum
+{
+       WIN32_PLATFORM_CHALLENGE_TYPE = 0x0100,
+       WIN16_PLATFORM_CHALLENGE_TYPE = 0x0200,
+       WINCE_PLATFORM_CHALLENGE_TYPE = 0x0300,
+       OTHER_PLATFORM_CHALLENGE_TYPE = 0xFF00
+};
+
+/** @brief License Detail Levels */
+enum
+{
+       LICENSE_DETAIL_SIMPLE   = 0x0001,
+       LICENSE_DETAIL_MODERATE = 0x0002,
+       LICENSE_DETAIL_DETAIL   = 0x0003
+};
+
+/*
+ * PlatformId:
+ *
+ * The most significant byte of the PlatformId field contains the operating system version of the client.
+ * The second most significant byte of the PlatformId field identifies the ISV that provided the client image.
+ * The remaining two bytes in the PlatformId field are used by the ISV to identify the build number of the operating system.
+ *
+ * 0x04010000:
+ *
+ * CLIENT_OS_ID_WINNT_POST_52  (0x04000000)
+ * CLIENT_IMAGE_ID_MICROSOFT   (0x00010000)
+ */
+enum
+{
+       CLIENT_OS_ID_WINNT_351          = 0x01000000,
+       CLIENT_OS_ID_WINNT_40           = 0x02000000,
+       CLIENT_OS_ID_WINNT_50           = 0x03000000,
+       CLIENT_OS_ID_WINNT_POST_52      = 0x04000000,
+
+       CLIENT_IMAGE_ID_MICROSOFT       = 0x00010000,
+       CLIENT_IMAGE_ID_CITRIX          = 0x00020000,
+};
+
+#ifdef __cpluscplus
+extern "C" {
+#endif
+
+FREERDP_API BOOL license_send_valid_client_error_packet(rdpRdp* rdp);
+
+#ifdef __cpluscplus
+}
+#endif
+
+#endif /* FREERDP_LICENSE_H */
index 27961f1..b231e99 100644 (file)
@@ -67,6 +67,17 @@ typedef int (*psPeerVirtualChannelWrite)(freerdp_peer* peer, HANDLE hChannel, BY
 typedef void* (*psPeerVirtualChannelGetData)(freerdp_peer* peer, HANDLE hChannel);
 typedef int (*psPeerVirtualChannelSetData)(freerdp_peer* peer, HANDLE hChannel, void* data);
 
+/** @brief the result of the license callback */
+typedef enum
+{
+       LICENSE_CB_INTERNAL_ERROR,      /** an internal error happened in the callback */
+       LICENSE_CB_ABORT,                       /** licensing process failed, abort the connection */
+       LICENSE_CB_IN_PROGRESS,         /** incoming packet has been treated, we're waiting for further packets to complete the workflow */
+       LICENSE_CB_COMPLETED            /** the licensing workflow has completed, go to next step */
+} LicenseCallbackResult;
+
+typedef LicenseCallbackResult (*psPeerLicenseCallback)(freerdp_peer* peer, wStream* s);
+
 
 struct rdp_freerdp_peer
 {
@@ -123,6 +134,7 @@ struct rdp_freerdp_peer
        psPeerAdjustMonitorsLayout AdjustMonitorsLayout;
        psPeerClientCapabilities ClientCapabilities;
        psPeerComputeNtlmHash ComputeNtlmHash;
+       psPeerLicenseCallback LicenseCallback;
 };
 
 #ifdef __cplusplus
index cf91a3a..58de332 100644 (file)
@@ -605,7 +605,12 @@ BOOL license_generate_hwid(rdpLicense* license)
        }
        else
        {
+               wStream s;
                const char *hostname = license->rdp->settings->ClientHostname;
+               Stream_StaticInit(&s, license->HardwareId, 4);
+               Stream_Write_UINT32(&s, PLATFORMID);
+               Stream_Free(&s, TRUE);
+
                hashTarget = (const BYTE *)hostname;
                targetLen = strlen(hostname);
        }
@@ -1551,8 +1556,9 @@ BOOL license_send_platform_challenge_response_packet(rdpLicense* license)
  * @param license license module
  */
 
-BOOL license_send_valid_client_error_packet(rdpLicense* license)
+BOOL license_send_valid_client_error_packet(rdpRdp* rdp)
 {
+       rdpLicense *license = rdp->license;
        wStream* s = license_send_stream_init(license);
        if (!s)
                return FALSE;
index 2f1c284..ff1b151 100644 (file)
@@ -30,116 +30,10 @@ typedef struct rdp_license rdpLicense;
 #include <freerdp/freerdp.h>
 #include <freerdp/log.h>
 #include <freerdp/api.h>
+#include <freerdp/license.h>
 
 #include <winpr/stream.h>
 
-/* Licensing Packet Types */
-
-#define LICENSE_REQUEST                                0x01
-#define PLATFORM_CHALLENGE                     0x02
-#define NEW_LICENSE                            0x03
-#define UPGRADE_LICENSE                                0x04
-#define LICENSE_INFO                           0x12
-#define NEW_LICENSE_REQUEST                    0x13
-#define PLATFORM_CHALLENGE_RESPONSE            0x15
-#define ERROR_ALERT                            0xFF
-
-#define LICENSE_PKT_CS_MASK                    (LICENSE_INFO | NEW_LICENSE_REQUEST | PLATFORM_CHALLENGE_RESPONSE | ERROR_ALERT)
-#define LICENSE_PKT_SC_MASK                    (LICENSE_REQUEST | PLATFORM_CHALLENGE | NEW_LICENSE | UPGRADE_LICENSE | ERROR_ALERT)
-#define LICENSE_PKT_MASK                       (LICENSE_PKT_CS_MASK | LICENSE_PKT_SC_MASK)
-
-#define LICENSE_PREAMBLE_LENGTH                        4
-
-/* Cryptographic Lengths */
-
-#define CLIENT_RANDOM_LENGTH                   32
-#define SERVER_RANDOM_LENGTH                   32
-#define MASTER_SECRET_LENGTH                   48
-#define PREMASTER_SECRET_LENGTH                        48
-#define SESSION_KEY_BLOB_LENGTH                        48
-#define MAC_SALT_KEY_LENGTH                    16
-#define LICENSING_ENCRYPTION_KEY_LENGTH                16
-#define HWID_PLATFORM_ID_LENGTH                        4
-#define HWID_UNIQUE_DATA_LENGTH                        16
-#define HWID_LENGTH                            20
-#define LICENSING_PADDING_SIZE                 8
-
-/* Preamble Flags */
-
-#define PREAMBLE_VERSION_2_0                   0x02
-#define PREAMBLE_VERSION_3_0                   0x03
-#define LicenseProtocolVersionMask             0x0F
-#define EXTENDED_ERROR_MSG_SUPPORTED           0x80
-
-/* Binary Blob Types */
-
-#define BB_ANY_BLOB                            0x0000
-#define BB_DATA_BLOB                           0x0001
-#define BB_RANDOM_BLOB                         0x0002
-#define BB_CERTIFICATE_BLOB                    0x0003
-#define BB_ERROR_BLOB                          0x0004
-#define BB_ENCRYPTED_DATA_BLOB                 0x0009
-#define BB_KEY_EXCHG_ALG_BLOB                  0x000D
-#define BB_SCOPE_BLOB                          0x000E
-#define BB_CLIENT_USER_NAME_BLOB               0x000F
-#define BB_CLIENT_MACHINE_NAME_BLOB            0x0010
-
-/* License Key Exchange Algorithms */
-
-#define KEY_EXCHANGE_ALG_RSA                   0x00000001
-
-/* License Error Codes */
-
-#define ERR_INVALID_SERVER_CERTIFICATE         0x00000001
-#define ERR_NO_LICENSE                         0x00000002
-#define ERR_INVALID_MAC                                0x00000003
-#define ERR_INVALID_SCOPE                      0x00000004
-#define ERR_NO_LICENSE_SERVER                  0x00000006
-#define STATUS_VALID_CLIENT                    0x00000007
-#define ERR_INVALID_CLIENT                     0x00000008
-#define ERR_INVALID_PRODUCT_ID                 0x0000000B
-#define ERR_INVALID_MESSAGE_LENGTH             0x0000000C
-
-/* State Transition Codes */
-
-#define ST_TOTAL_ABORT                         0x00000001
-#define ST_NO_TRANSITION                       0x00000002
-#define ST_RESET_PHASE_TO_START                        0x00000003
-#define ST_RESEND_LAST_MESSAGE                 0x00000004
-
-/* Platform Challenge Types */
-
-#define WIN32_PLATFORM_CHALLENGE_TYPE          0x0100
-#define WIN16_PLATFORM_CHALLENGE_TYPE          0x0200
-#define WINCE_PLATFORM_CHALLENGE_TYPE          0x0300
-#define OTHER_PLATFORM_CHALLENGE_TYPE          0xFF00
-
-/* License Detail Levels */
-
-#define LICENSE_DETAIL_SIMPLE                  0x0001
-#define LICENSE_DETAIL_MODERATE                        0x0002
-#define LICENSE_DETAIL_DETAIL                  0x0003
-
-/*
- * PlatformId:
- *
- * The most significant byte of the PlatformId field contains the operating system version of the client.
- * The second most significant byte of the PlatformId field identifies the ISV that provided the client image.
- * The remaining two bytes in the PlatformId field are used by the ISV to identify the build number of the operating system.
- *
- * 0x04010000:
- *
- * CLIENT_OS_ID_WINNT_POST_52  (0x04000000)
- * CLIENT_IMAGE_ID_MICROSOFT   (0x00010000)
- */
-
-#define CLIENT_OS_ID_WINNT_351                 0x01000000
-#define CLIENT_OS_ID_WINNT_40                  0x02000000
-#define CLIENT_OS_ID_WINNT_50                  0x03000000
-#define CLIENT_OS_ID_WINNT_POST_52             0x04000000
-
-#define CLIENT_IMAGE_ID_MICROSOFT              0x00010000
-#define CLIENT_IMAGE_ID_CITRIX                 0x00020000
 
 typedef struct
 {
@@ -204,7 +98,6 @@ struct rdp_license
 
 FREERDP_LOCAL int license_recv(rdpLicense* license, wStream* s);
 
-FREERDP_LOCAL BOOL license_send_valid_client_error_packet(rdpLicense* license);
 
 FREERDP_LOCAL rdpLicense* license_new(rdpRdp* rdp);
 FREERDP_LOCAL void license_free(rdpLicense* license);
index 65be268..c2561c4 100644 (file)
@@ -567,16 +567,34 @@ static int peer_recv_callback(rdpTransport* transport, wStream* s, void* extra)
                        break;
 
                case CONNECTION_STATE_LICENSING:
-                       if (!license_send_valid_client_error_packet(rdp->license))
+               {
+                       LicenseCallbackResult res;
+                       if (!client->LicenseCallback)
                        {
-                               WLog_ERR(TAG,
-                                        "peer_recv_callback: CONNECTION_STATE_LICENSING - license_send_valid_client_error_packet() fail");
+                               WLog_ERR(TAG, "peer_recv_callback: LicenseCallback has been removed, assuming licensing is ok (please fix your app)");
+                               res = LICENSE_CB_COMPLETED;
+                       }
+                       else
+                               res = client->LicenseCallback(client, s);
+
+                       switch(res) {
+                       case LICENSE_CB_INTERNAL_ERROR:
+                               WLog_ERR(TAG, "peer_recv_callback: CONNECTION_STATE_LICENSING - callback internal error, aborting");
                                return -1;
+                       case LICENSE_CB_ABORT:
+                               return -1;
+                       case LICENSE_CB_IN_PROGRESS:
+                               break;
+                       case LICENSE_CB_COMPLETED:
+                               rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE);
+                               return peer_recv_callback(transport, NULL, extra);
+                       default:
+                               WLog_ERR(TAG, "peer_recv_callback: CONNECTION_STATE_LICENSING - unknown license callback result %d", (int)res);
+                               break;
                        }
 
-                       rdp_server_transition_to_state(rdp, CONNECTION_STATE_CAPABILITIES_EXCHANGE);
-                       return peer_recv_callback(transport, NULL, extra);
                        break;
+               }
 
                case CONNECTION_STATE_CAPABILITIES_EXCHANGE:
                        if (!rdp->AwaitCapabilities)
@@ -699,6 +717,19 @@ static BOOL freerdp_peer_has_more_to_read(freerdp_peer* peer)
        return peer->context->rdp->transport->haveMoreBytesToRead;
 }
 
+static LicenseCallbackResult freerdp_peer_nolicense(freerdp_peer* peer, wStream *s)
+{
+       rdpRdp *rdp = peer->context->rdp;
+
+       if (!license_send_valid_client_error_packet(rdp))
+       {
+               WLog_ERR(TAG, "freerdp_peer_nolicense: license_send_valid_client_error_packet() failed");
+               return LICENSE_CB_ABORT;
+       }
+
+       return LICENSE_CB_COMPLETED;
+}
+
 BOOL freerdp_peer_context_new(freerdp_peer* client)
 {
        rdpRdp* rdp;
@@ -752,6 +783,7 @@ BOOL freerdp_peer_context_new(freerdp_peer* client)
        client->IsWriteBlocked = freerdp_peer_is_write_blocked;
        client->DrainOutputBuffer = freerdp_peer_drain_output_buffer;
        client->HasMoreToRead = freerdp_peer_has_more_to_read;
+       client->LicenseCallback = freerdp_peer_nolicense;
        IFCALLRET(client->ContextNew, ret, client, client->context);
 
        if (ret)