/src/FreeRDP/libfreerdp/emu/scard/smartcard_virtual_gids.c
Line  | Count  | Source (jump to first uncovered line)  | 
1  |  | /**  | 
2  |  |  * WinPR: Windows Portable Runtime  | 
3  |  |  * Virtual GIDS implementation  | 
4  |  |  *  | 
5  |  |  * Copyright 2021 Martin Fleisz <martin.fleisz@thincast.com>  | 
6  |  |  * Copyright 2023 Armin Novak <anovak@thincast.com>  | 
7  |  |  * Copyright 2021,2023 Thincast Technologies GmbH  | 
8  |  |  *  | 
9  |  |  * Licensed under the Apache License, Version 2.0 (the "License");  | 
10  |  |  * you may not use this file except in compliance with the License.  | 
11  |  |  * You may obtain a copy of the License at  | 
12  |  |  *  | 
13  |  |  *     http://www.apache.org/licenses/LICENSE-2.0  | 
14  |  |  *  | 
15  |  |  * Unless required by applicable law or agreed to in writing, software  | 
16  |  |  * distributed under the License is distributed on an "AS IS" BASIS,  | 
17  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  | 
18  |  |  * See the License for the specific language governing permissions and  | 
19  |  |  * limitations under the License.  | 
20  |  |  */  | 
21  |  |  | 
22  |  | #include <freerdp/config.h>  | 
23  |  |  | 
24  |  | #include <winpr/wlog.h>  | 
25  |  | #include <winpr/stream.h>  | 
26  |  | #include <winpr/collections.h>  | 
27  |  |  | 
28  |  | #include <freerdp/crypto/crypto.h>  | 
29  |  |  | 
30  |  | #include <zlib.h>  | 
31  |  |  | 
32  |  | #include "../../crypto/certificate.h"  | 
33  |  | #include "../../crypto/privatekey.h"  | 
34  |  | #include "smartcard_virtual_gids.h"  | 
35  |  |  | 
36  |  | #define TAG CHANNELS_TAG("smartcard.vgids") | 
37  |  |  | 
38  | 0  | #define VGIDS_EFID_MASTER 0xA000  | 
39  | 0  | #define VGIDS_EFID_COMMON 0xA010  | 
40  |  | #define VGIDS_EFID_CARDCF VGIDS_EFID_COMMON  | 
41  |  | #define VGIDS_EFID_CARDAPPS VGIDS_EFID_COMMON  | 
42  |  | #define VGIDS_EFID_CMAPFILE VGIDS_EFID_COMMON  | 
43  | 0  | #define VGIDS_EFID_CARDID 0xA012  | 
44  |  | #define VGIDS_EFID_KXC00 VGIDS_EFID_COMMON  | 
45  | 0  | #define VGIDS_EFID_CURRENTDF 0x3FFF  | 
46  |  |  | 
47  | 0  | #define VGIDS_DO_FILESYSTEMTABLE 0xDF1F  | 
48  | 0  | #define VGIDS_DO_KEYMAP 0xDF20  | 
49  | 0  | #define VGIDS_DO_CARDID 0xDF20  | 
50  | 0  | #define VGIDS_DO_CARDAPPS 0xDF21  | 
51  | 0  | #define VGIDS_DO_CARDCF 0xDF22  | 
52  | 0  | #define VGIDS_DO_CMAPFILE 0xDF23  | 
53  | 0  | #define VGIDS_DO_KXC00 0xDF24  | 
54  |  |  | 
55  |  | #define VGIDS_CARDID_SIZE 16  | 
56  | 0  | #define VGIDS_MAX_PIN_SIZE 127  | 
57  |  |  | 
58  | 0  | #define VGIDS_DEFAULT_RETRY_COUNTER 3  | 
59  |  |  | 
60  | 0  | #define VGIDS_KEY_TYPE_KEYEXCHANGE 0x9A  | 
61  |  | #define VGIDS_KEY_TYPE_SIGNATURE 0x9C  | 
62  |  |  | 
63  | 0  | #define VGIDS_ALGID_RSA_1024 0x06  | 
64  | 0  | #define VGIDS_ALGID_RSA_2048 0x07  | 
65  | 0  | #define VGIDS_ALGID_RSA_3072 0x08  | 
66  | 0  | #define VGIDS_ALGID_RSA_4096 0x09  | 
67  |  |  | 
68  |  | #define VGIDS_SE_CRT_AUTH 0xA4  | 
69  | 0  | #define VGIDS_SE_CRT_SIGN 0xB6  | 
70  | 0  | #define VGIDS_SE_CRT_CONF 0xB8  | 
71  |  |  | 
72  | 0  | #define VGIDS_SE_ALGOID_CT_PAD_PKCS1 0x40  | 
73  | 0  | #define VGIDS_SE_ALGOID_CT_PAD_OAEP 0x80  | 
74  |  | #define VGIDS_SE_ALGOID_CT_RSA_1024 0x06  | 
75  |  | #define VGIDS_SE_ALGOID_CT_RSA_2048 0x07  | 
76  |  | #define VGIDS_SE_ALGOID_CT_RSA_3072 0x08  | 
77  |  | #define VGIDS_SE_ALGOID_CT_RSA_4096 0x09  | 
78  |  |  | 
79  | 0  | #define VGIDS_SE_ALGOID_DST_PAD_PKCS1 0x40  | 
80  |  | #define VGIDS_SE_ALGOID_DST_RSA_1024 0x06  | 
81  |  | #define VGIDS_SE_ALGOID_DST_RSA_2048 0x07  | 
82  |  | #define VGIDS_SE_ALGOID_DST_RSA_3072 0x08  | 
83  |  | #define VGIDS_SE_ALGOID_DST_RSA_4096 0x09  | 
84  |  | #define VGIDS_SE_ALGOID_DST_ECDSA_P192 0x0A  | 
85  |  | #define VGIDS_SE_ALGOID_DST_ECDSA_P224 0x0B  | 
86  |  | #define VGIDS_SE_ALGOID_DST_ECDSA_P256 0x0C  | 
87  |  | #define VGIDS_SE_ALGOID_DST_ECDSA_P384 0x0D  | 
88  |  | #define VGIDS_SE_ALGOID_DST_ECDSA_P512 0x0E  | 
89  |  |  | 
90  | 0  | #define VGIDS_DEFAULT_KEY_REF 0x81  | 
91  |  |  | 
92  | 0  | #define ISO_INS_SELECT 0xA4  | 
93  | 0  | #define ISO_INS_GETDATA 0xCB  | 
94  | 0  | #define ISO_INS_GETRESPONSE 0xC0  | 
95  | 0  | #define ISO_INS_MSE 0x22  | 
96  | 0  | #define ISO_INS_PSO 0x2A  | 
97  | 0  | #define ISO_INS_VERIFY 0x20  | 
98  |  |  | 
99  | 0  | #define ISO_STATUS_MORE_DATA 0x6100  | 
100  | 0  | #define ISO_STATUS_VERIFYFAILED 0x6300  | 
101  | 0  | #define ISO_STATUS_WRONGLC 0x6700  | 
102  | 0  | #define ISO_STATUS_COMMANDNOTALLOWED 0x6900  | 
103  | 0  | #define ISO_STATUS_SECURITYSTATUSNOTSATISFIED 0x6982  | 
104  | 0  | #define ISO_STATUS_AUTHMETHODBLOCKED 0x6983  | 
105  | 0  | #define ISO_STATUS_INVALIDCOMMANDDATA 0x6A80  | 
106  | 0  | #define ISO_STATUS_FILENOTFOUND 0x6A82  | 
107  | 0  | #define ISO_STATUS_INVALIDP1P2 0x6A86  | 
108  | 0  | #define ISO_STATUS_INVALIDLC 0x6A87  | 
109  | 0  | #define ISO_STATUS_REFERENCEDATANOTFOUND 0x6A88  | 
110  | 0  | #define ISO_STATUS_SUCCESS 0x9000  | 
111  |  |  | 
112  | 0  | #define ISO_AID_MAX_SIZE 16  | 
113  |  |  | 
114  | 0  | #define ISO_FID_MF 0x3F00  | 
115  |  |  | 
116  |  | struct vgids_ef  | 
117  |  | { | 
118  |  |   UINT16 id;  | 
119  |  |   UINT16 dirID;  | 
120  |  |   wStream* data;  | 
121  |  | };  | 
122  |  | typedef struct vgids_ef vgidsEF;  | 
123  |  |  | 
124  |  | struct vgids_se  | 
125  |  | { | 
126  |  |   BYTE crt;    /* control reference template tag */  | 
127  |  |   BYTE algoId; /* Algorithm ID */  | 
128  |  |   BYTE keyRef; /* Key reference */  | 
129  |  | };  | 
130  |  | typedef struct vgids_se vgidsSE;  | 
131  |  |  | 
132  |  | struct vgids_context  | 
133  |  | { | 
134  |  |   UINT16 currentDF;  | 
135  |  |   char* pin;  | 
136  |  |   UINT16 curRetryCounter;  | 
137  |  |   UINT16 retryCounter;  | 
138  |  |   wStream* commandData;  | 
139  |  |   wStream* responseData;  | 
140  |  |   BOOL pinVerified;  | 
141  |  |   vgidsSE currentSE;  | 
142  |  |  | 
143  |  |   rdpCertificate* certificate;  | 
144  |  |   rdpPrivateKey* privateKey;  | 
145  |  |  | 
146  |  |   wArrayList* files;  | 
147  |  | };  | 
148  |  |  | 
149  |  | /* PKCS 1.5 DER encoded digest information */  | 
150  | 0  | #define VGIDS_MAX_DIGEST_INFO 7  | 
151  |  |  | 
152  |  | static const BYTE g_PKCS1_SHA1[] = { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, | 
153  |  |                                    0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };  | 
154  |  | static const BYTE g_PKCS1_SHA224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, | 
155  |  |                                      0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c };  | 
156  |  | static const BYTE g_PKCS1_SHA256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, | 
157  |  |                                      0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20 };  | 
158  |  | static const BYTE g_PKCS1_SHA384[] = { 0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, | 
159  |  |                                      0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30 };  | 
160  |  | static const BYTE g_PKCS1_SHA512[] = { 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, | 
161  |  |                                      0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40 };  | 
162  |  | static const BYTE g_PKCS1_SHA512_224[] = { 0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, | 
163  |  |                                          0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,  | 
164  |  |                                          0x05, 0x05, 0x00, 0x04, 0x1c };  | 
165  |  | static const BYTE g_PKCS1_SHA512_256[] = { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, | 
166  |  |                                          0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02,  | 
167  |  |                                          0x06, 0x05, 0x00, 0x04, 0x20 };  | 
168  |  |  | 
169  |  | /* Helper struct to map PKCS1.5 digest info to OpenSSL EVP_MD */  | 
170  |  | struct vgids_digest_info_map  | 
171  |  | { | 
172  |  |   const BYTE* info;  | 
173  |  |   size_t infoSize;  | 
174  |  |   const EVP_MD* digest;  | 
175  |  | };  | 
176  |  | typedef struct vgids_digest_info_map vgidsDigestInfoMap;  | 
177  |  |  | 
178  |  | /* MS GIDS AID */  | 
179  |  | /* xx: Used by the Windows smart card framework for the GIDS version number. This byte must be set  | 
180  |  |  * to the GIDS specification revision number which is either 0x01 or 0x02.  | 
181  |  |  * yy: Reserved for use by the card application (set to 01)  | 
182  |  |  */  | 
183  |  | static const BYTE g_MsGidsAID[] = { | 
184  |  |   0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, 0x54, 0x46, 0x59, 0x02, 0x01  | 
185  |  | };  | 
186  |  |  | 
187  |  | /* GIDS APP File Control Parameter:  | 
188  |  |    FD-Byte (82): 38 (not shareable-DF)  | 
189  |  |    Sec Attr (8C): 03 30 30  Create/Delete File(03) Ext/User-Auth (30)  | 
190  |  | */  | 
191  |  | static const BYTE g_GidsAppFCP[] = { 0x62, 0x08, 0x82, 0x01, 0x38, 0x8C, 0x03, 0x03, 0x30, 0x30 }; | 
192  |  | /* GIDS APP File Control Information:  | 
193  |  |    AppID (4F, Len 0B): A0 00 00 03 97 42 54 46 59 02 01  | 
194  |  |    Discretionary DOs (73, Len 03): 40 01 C0  | 
195  |  |       Supported Auth Protocols (40, Len 01): C0 Mutual/External-Auth  | 
196  |  |  */  | 
197  |  | static const BYTE g_GidsAppFCI[] = { 0x61, 0x12, 0x4F, 0x0B, 0xA0, 0x00, 0x00, 0x03, 0x97, 0x42, | 
198  |  |                                    0x54, 0x46, 0x59, 0x02, 0x01, 0x73, 0x03, 0x40, 0x01, 0xC0 };  | 
199  |  |  | 
200  |  | /*  | 
201  |  | typedef struct  | 
202  |  | { | 
203  |  |     BYTE bVersion; // Cache version  | 
204  |  |     BYTE bPinsFreshness; // Card PIN  | 
205  |  |     WORD wContainersFreshness;  | 
206  |  |     WORD wFilesFreshness;  | 
207  |  | } CARD_CACHE_FILE_FORMAT, *PCARD_CACHE_FILE_FORMAT; */  | 
208  |  | static const BYTE g_CardCFContents[] = { 0x00, 0x00, 0x01, 0x00, 0x04, 0x00 }; | 
209  |  |  | 
210  |  | /* {mscp,0,0,0,0} */ | 
211  |  | static const BYTE g_CardAppsContents[] = { 0x6d, 0x73, 0x63, 0x70, 0x00, 0x00, 0x00, 0x00 }; | 
212  |  |  | 
213  |  | #pragma pack(push, 1)  | 
214  |  |  | 
215  |  | /* Type: CONTAINER_MAP_RECORD (taken from Windows Smart Card Minidriver Specification)  | 
216  |  |  | 
217  |  |    This structure describes the format of the Base CSP's  | 
218  |  |    container map file, stored on the card. This is wellknown  | 
219  |  |    logical file wszCONTAINER_MAP_FILE. The file consists of  | 
220  |  |    zero or more of these records. */  | 
221  |  | #define MAX_CONTAINER_NAME_LEN 39  | 
222  |  |  | 
223  |  | /* This flag is set in the CONTAINER_MAP_RECORD bFlags  | 
224  |  |    member if the corresponding container is valid and currently  | 
225  |  |    exists on the card. // If the container is deleted, its  | 
226  |  |    bFlags field must be cleared. */  | 
227  | 0  | #define CONTAINER_MAP_VALID_CONTAINER 1  | 
228  |  |  | 
229  |  | /* This flag is set in the CONTAINER_MAP_RECORD bFlags  | 
230  |  |    member if the corresponding container is the default  | 
231  |  |    container on the card. */  | 
232  | 0  | #define CONTAINER_MAP_DEFAULT_CONTAINER 2  | 
233  |  |  | 
234  |  | struct vgids_container_map_entry  | 
235  |  | { | 
236  |  |   WCHAR wszGuid[MAX_CONTAINER_NAME_LEN + 1];  | 
237  |  |   BYTE bFlags;  | 
238  |  |   BYTE bReserved;  | 
239  |  |   WORD wSigKeySizeBits;  | 
240  |  |   WORD wKeyExchangeKeySizeBits;  | 
241  |  | };  | 
242  |  | typedef struct vgids_container_map_entry vgidsContainerMapEntry;  | 
243  |  |  | 
244  |  | struct vgids_filesys_table_entry  | 
245  |  | { | 
246  |  |   char directory[9];  | 
247  |  |   char filename[9];  | 
248  |  |   UINT16 pad0;  | 
249  |  |   UINT16 dataObjectIdentifier;  | 
250  |  |   UINT16 pad1;  | 
251  |  |   UINT16 fileIdentifier;  | 
252  |  |   UINT16 unknown;  | 
253  |  | };  | 
254  |  | typedef struct vgids_filesys_table_entry vgidsFilesysTableEntry;  | 
255  |  |  | 
256  |  | struct vgids_keymap_record  | 
257  |  | { | 
258  |  |   UINT32 state;  | 
259  |  |   BYTE algid;  | 
260  |  |   BYTE keytype;  | 
261  |  |   UINT16 keyref;  | 
262  |  |   UINT16 unknownWithFFFF;  | 
263  |  |   UINT16 unknownWith0000;  | 
264  |  | };  | 
265  |  | typedef struct vgids_keymap_record vgidsKeymapRecord;  | 
266  |  |  | 
267  |  | #pragma pack(pop)  | 
268  |  |  | 
269  |  | static void vgids_ef_free(void* ptr);  | 
270  |  |  | 
271  |  | static vgidsEF* vgids_ef_new(vgidsContext* ctx, USHORT id)  | 
272  | 0  | { | 
273  | 0  |   vgidsEF* ef = calloc(1, sizeof(vgidsEF));  | 
274  |  | 
  | 
275  | 0  |   ef->id = id;  | 
276  | 0  |   ef->data = Stream_New(NULL, 1024);  | 
277  | 0  |   if (!ef->data)  | 
278  | 0  |   { | 
279  | 0  |     WLog_ERR(TAG, "Failed to create file data stream");  | 
280  | 0  |     goto create_failed;  | 
281  | 0  |   }  | 
282  | 0  |   Stream_SetLength(ef->data, 0);  | 
283  |  | 
  | 
284  | 0  |   if (!ArrayList_Append(ctx->files, ef))  | 
285  | 0  |   { | 
286  | 0  |     WLog_ERR(TAG, "Failed to add new ef to file list");  | 
287  | 0  |     goto create_failed;  | 
288  | 0  |   }  | 
289  |  |  | 
290  | 0  |   return ef;  | 
291  |  |  | 
292  | 0  | create_failed:  | 
293  | 0  |   vgids_ef_free(ef);  | 
294  | 0  |   return NULL;  | 
295  | 0  | }  | 
296  |  |  | 
297  |  | static BOOL vgids_write_tlv(wStream* s, UINT16 tag, const void* data, DWORD dataSize)  | 
298  | 0  | { | 
299  |  |   /* A maximum of 5 additional bytes is needed */  | 
300  | 0  |   if (!Stream_EnsureRemainingCapacity(s, dataSize + 5))  | 
301  | 0  |   { | 
302  | 0  |     WLog_ERR(TAG, "Failed to ensure capacity of DO stream");  | 
303  | 0  |     return FALSE;  | 
304  | 0  |   }  | 
305  |  |  | 
306  |  |   /* BER encoding: If the most-significant bit is set (0x80) the length is encoded in the  | 
307  |  |    * remaining bits. So lengths < 128 bytes can be set directly, all others are encoded */  | 
308  | 0  |   if (tag > 0xFF)  | 
309  | 0  |     Stream_Write_UINT16_BE(s, tag);  | 
310  | 0  |   else  | 
311  | 0  |     Stream_Write_UINT8(s, (BYTE)tag);  | 
312  | 0  |   if (dataSize < 128)  | 
313  | 0  |   { | 
314  | 0  |     Stream_Write_UINT8(s, (BYTE)dataSize);  | 
315  | 0  |   }  | 
316  | 0  |   else if (dataSize < 256)  | 
317  | 0  |   { | 
318  | 0  |     Stream_Write_UINT8(s, 0x81);  | 
319  | 0  |     Stream_Write_UINT8(s, (BYTE)dataSize);  | 
320  | 0  |   }  | 
321  | 0  |   else  | 
322  | 0  |   { | 
323  | 0  |     Stream_Write_UINT8(s, 0x82);  | 
324  | 0  |     Stream_Write_UINT16_BE(s, (UINT16)dataSize);  | 
325  | 0  |   }  | 
326  | 0  |   Stream_Write(s, data, dataSize);  | 
327  | 0  |   Stream_SealLength(s);  | 
328  | 0  |   return TRUE;  | 
329  | 0  | }  | 
330  |  |  | 
331  |  | static BOOL vgids_ef_write_do(vgidsEF* ef, UINT16 doID, const void* data, DWORD dataSize)  | 
332  | 0  | { | 
333  |  |   /* Write DO to end of file: 2-Byte ID, 1-Byte Len, Data */  | 
334  | 0  |   return vgids_write_tlv(ef->data, doID, data, dataSize);  | 
335  | 0  | }  | 
336  |  |  | 
337  |  | static BOOL vgids_ef_read_do(vgidsEF* ef, UINT16 doID, BYTE** data, DWORD* dataSize)  | 
338  | 0  | { | 
339  |  |   /* Read the given DO from the file: 2-Byte ID, 1-Byte Len, Data */  | 
340  | 0  |   if (!Stream_SetPosition(ef->data, 0))  | 
341  | 0  |   { | 
342  | 0  |     WLog_ERR(TAG, "Failed to seek to front of file");  | 
343  | 0  |     return FALSE;  | 
344  | 0  |   }  | 
345  |  |  | 
346  |  |   /* Look for the requested DO */  | 
347  | 0  |   while (Stream_GetRemainingLength(ef->data) > 3)  | 
348  | 0  |   { | 
349  | 0  |     BYTE len = 0;  | 
350  | 0  |     size_t curPos = 0;  | 
351  | 0  |     UINT16 doSize = 0;  | 
352  | 0  |     UINT16 nextDOID = 0;  | 
353  |  | 
  | 
354  | 0  |     curPos = Stream_GetPosition(ef->data);  | 
355  | 0  |     Stream_Read_UINT16_BE(ef->data, nextDOID);  | 
356  | 0  |     Stream_Read_UINT8(ef->data, len);  | 
357  | 0  |     if ((len & 0x80))  | 
358  | 0  |     { | 
359  | 0  |       BYTE lenSize = len & 0x7F;  | 
360  | 0  |       if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, lenSize))  | 
361  | 0  |         return FALSE;  | 
362  |  |  | 
363  | 0  |       switch (lenSize)  | 
364  | 0  |       { | 
365  | 0  |         case 1:  | 
366  | 0  |           Stream_Read_UINT8(ef->data, doSize);  | 
367  | 0  |           break;  | 
368  | 0  |         case 2:  | 
369  | 0  |           Stream_Read_UINT16_BE(ef->data, doSize);  | 
370  | 0  |           break;  | 
371  | 0  |         default:  | 
372  | 0  |           WLog_ERR(TAG, "Unexpected tag length %" PRIu8, lenSize);  | 
373  | 0  |           return FALSE;  | 
374  | 0  |       }  | 
375  | 0  |     }  | 
376  | 0  |     else  | 
377  | 0  |       doSize = len;  | 
378  |  |  | 
379  | 0  |     if (!Stream_CheckAndLogRequiredLength(TAG, ef->data, doSize))  | 
380  | 0  |       return FALSE;  | 
381  |  |  | 
382  | 0  |     if (nextDOID == doID)  | 
383  | 0  |     { | 
384  | 0  |       BYTE* outData = NULL;  | 
385  |  |  | 
386  |  |       /* Include Tag and length in result */  | 
387  | 0  |       doSize += (UINT16)(Stream_GetPosition(ef->data) - curPos);  | 
388  | 0  |       outData = malloc(doSize);  | 
389  | 0  |       if (!outData)  | 
390  | 0  |       { | 
391  | 0  |         WLog_ERR(TAG, "Failed to allocate output buffer");  | 
392  | 0  |         return FALSE;  | 
393  | 0  |       }  | 
394  |  |  | 
395  | 0  |       Stream_SetPosition(ef->data, curPos);  | 
396  | 0  |       Stream_Read(ef->data, outData, doSize);  | 
397  | 0  |       *data = outData;  | 
398  | 0  |       *dataSize = doSize;  | 
399  | 0  |       return TRUE;  | 
400  | 0  |     }  | 
401  | 0  |     else  | 
402  | 0  |     { | 
403  |  |       /* Skip DO */  | 
404  | 0  |       Stream_SafeSeek(ef->data, doSize);  | 
405  | 0  |     }  | 
406  | 0  |   }  | 
407  |  |  | 
408  | 0  |   return FALSE;  | 
409  | 0  | }  | 
410  |  |  | 
411  |  | void vgids_ef_free(void* ptr)  | 
412  | 0  | { | 
413  | 0  |   vgidsEF* ef = ptr;  | 
414  | 0  |   if (ef)  | 
415  | 0  |   { | 
416  | 0  |     Stream_Free(ef->data, TRUE);  | 
417  | 0  |     free(ef);  | 
418  | 0  |   }  | 
419  | 0  | }  | 
420  |  |  | 
421  |  | static BOOL vgids_prepare_fstable(const vgidsFilesysTableEntry* fstable, DWORD numEntries,  | 
422  |  |                                   BYTE** outData, DWORD* outDataSize)  | 
423  | 0  | { | 
424  |  |   /* Filesystem table:  | 
425  |  |       BYTE unkonwn: 0x01  | 
426  |  |       Array of vgidsFilesysTableEntry  | 
427  |  |   */  | 
428  | 0  |   BYTE* data = malloc(sizeof(vgidsFilesysTableEntry) * numEntries + 1);  | 
429  | 0  |   if (!data)  | 
430  | 0  |   { | 
431  | 0  |     WLog_ERR(TAG, "Failed to allocate filesystem table data blob");  | 
432  | 0  |     return FALSE;  | 
433  | 0  |   }  | 
434  |  |  | 
435  | 0  |   *data = 0x01;  | 
436  | 0  |   for (UINT32 i = 0; i < numEntries; ++i)  | 
437  | 0  |     memcpy(data + 1 + (sizeof(vgidsFilesysTableEntry) * i), &fstable[i],  | 
438  | 0  |            sizeof(vgidsFilesysTableEntry));  | 
439  |  | 
  | 
440  | 0  |   *outData = data;  | 
441  | 0  |   *outDataSize = sizeof(vgidsFilesysTableEntry) * numEntries + 1;  | 
442  |  | 
  | 
443  | 0  |   return TRUE;  | 
444  | 0  | }  | 
445  |  |  | 
446  |  | static BOOL vgids_prepare_certificate(const rdpCertificate* cert, BYTE** kxc, DWORD* kxcSize)  | 
447  | 0  | { | 
448  |  |   /* Key exchange container:  | 
449  |  |       UINT16 compression version: 0001  | 
450  |  |       UINT16 source size  | 
451  |  |       ZLIB compressed cert  | 
452  |  |   */  | 
453  | 0  |   uLongf destSize = 0;  | 
454  | 0  |   wStream* s = NULL;  | 
455  | 0  |   BYTE* comprData = NULL;  | 
456  |  | 
  | 
457  | 0  |   WINPR_ASSERT(cert);  | 
458  |  |  | 
459  | 0  |   size_t certSize = 0;  | 
460  | 0  |   BYTE* certData = freerdp_certificate_get_der(cert, &certSize);  | 
461  | 0  |   if (!certData || (certSize == 0))  | 
462  | 0  |   { | 
463  | 0  |     WLog_ERR(TAG, "Failed to get certificate size");  | 
464  | 0  |     goto handle_error;  | 
465  | 0  |   }  | 
466  |  |  | 
467  | 0  |   comprData = malloc(certSize);  | 
468  | 0  |   if (!comprData)  | 
469  | 0  |   { | 
470  | 0  |     WLog_ERR(TAG, "Failed to allocate certificate buffer");  | 
471  | 0  |     goto handle_error;  | 
472  | 0  |   }  | 
473  |  |  | 
474  |  |   /* compress certificate data */  | 
475  | 0  |   destSize = certSize;  | 
476  | 0  |   if (compress(comprData, &destSize, certData, certSize) != Z_OK)  | 
477  | 0  |   { | 
478  | 0  |     WLog_ERR(TAG, "Failed to compress certificate data");  | 
479  | 0  |     goto handle_error;  | 
480  | 0  |   }  | 
481  |  |  | 
482  |  |   /* Write container data */  | 
483  | 0  |   s = Stream_New(NULL, destSize + 4);  | 
484  | 0  |   Stream_Write_UINT16(s, 0x0001);  | 
485  | 0  |   Stream_Write_UINT16(s, (UINT16)certSize);  | 
486  | 0  |   Stream_Write(s, comprData, destSize);  | 
487  | 0  |   Stream_SealLength(s);  | 
488  |  | 
  | 
489  | 0  |   *kxc = Stream_Buffer(s);  | 
490  | 0  |   *kxcSize = (DWORD)Stream_Length(s);  | 
491  |  | 
  | 
492  | 0  |   Stream_Free(s, FALSE);  | 
493  | 0  |   free(certData);  | 
494  | 0  |   free(comprData);  | 
495  | 0  |   return TRUE;  | 
496  |  |  | 
497  | 0  | handle_error:  | 
498  | 0  |   Stream_Free(s, TRUE);  | 
499  | 0  |   free(certData);  | 
500  | 0  |   free(comprData);  | 
501  | 0  |   return FALSE;  | 
502  | 0  | }  | 
503  |  |  | 
504  |  | static int get_rsa_key_size(const rdpPrivateKey* privateKey)  | 
505  | 0  | { | 
506  | 0  |   WINPR_ASSERT(privateKey);  | 
507  |  |  | 
508  | 0  |   return freerdp_key_get_bits(privateKey) / 8;  | 
509  | 0  | }  | 
510  |  |  | 
511  |  | static BYTE vgids_get_algid(vgidsContext* p_Ctx)  | 
512  | 0  | { | 
513  | 0  |   WINPR_ASSERT(p_Ctx);  | 
514  |  |  | 
515  | 0  |   switch (get_rsa_key_size(p_Ctx->privateKey))  | 
516  | 0  |   { | 
517  | 0  |     case (1024 / 8):  | 
518  | 0  |       return VGIDS_ALGID_RSA_1024;  | 
519  | 0  |     case (2048 / 8):  | 
520  | 0  |       return VGIDS_ALGID_RSA_2048;  | 
521  | 0  |     case (3072 / 8):  | 
522  | 0  |       return VGIDS_ALGID_RSA_3072;  | 
523  | 0  |     case (4096 / 8):  | 
524  | 0  |       return VGIDS_ALGID_RSA_4096;  | 
525  | 0  |     default:  | 
526  | 0  |       WLog_ERR(TAG, "Failed to determine algid for private key");  | 
527  | 0  |       break;  | 
528  | 0  |   }  | 
529  |  |  | 
530  | 0  |   return 0;  | 
531  | 0  | }  | 
532  |  |  | 
533  |  | static BOOL vgids_prepare_keymap(vgidsContext* context, BYTE** outData, DWORD* outDataSize)  | 
534  | 0  | { | 
535  |  |   /* Key map record table:  | 
536  |  |       BYTE unkonwn (count?): 0x01  | 
537  |  |       Array of vgidsKeymapRecord  | 
538  |  |   */  | 
539  | 0  |   BYTE* data = NULL;  | 
540  | 0  |   vgidsKeymapRecord record = { | 
541  | 0  |     1,                                /* state */  | 
542  | 0  |     0,                                /* algo */  | 
543  | 0  |     VGIDS_KEY_TYPE_KEYEXCHANGE,       /* keytpe */  | 
544  | 0  |     (0xB000 | VGIDS_DEFAULT_KEY_REF), /* keyref */  | 
545  | 0  |     0xFFFF,                           /* unknown FFFF */  | 
546  | 0  |     0x0000                            /* unknown 0000 */  | 
547  | 0  |   };  | 
548  |  |  | 
549  |  |   /* Determine algo */  | 
550  | 0  |   BYTE algid = vgids_get_algid(context);  | 
551  | 0  |   if (algid == 0)  | 
552  | 0  |     return FALSE;  | 
553  |  |  | 
554  | 0  |   data = malloc(sizeof(record) + 1);  | 
555  | 0  |   if (!data)  | 
556  | 0  |   { | 
557  | 0  |     WLog_ERR(TAG, "Failed to allocate filesystem table data blob");  | 
558  | 0  |     return FALSE;  | 
559  | 0  |   }  | 
560  |  |  | 
561  | 0  |   *data = 0x01;  | 
562  | 0  |   record.algid = algid;  | 
563  | 0  |   memcpy(data + 1, &record, sizeof(record));  | 
564  |  | 
  | 
565  | 0  |   *outData = data;  | 
566  | 0  |   *outDataSize = sizeof(record) + 1;  | 
567  |  | 
  | 
568  | 0  |   return TRUE;  | 
569  | 0  | }  | 
570  |  |  | 
571  |  | static BOOL vgids_parse_apdu_header(wStream* s, BYTE* cla, BYTE* ins, BYTE* p1, BYTE* p2, BYTE* lc,  | 
572  |  |                                     BYTE* le)  | 
573  | 0  | { | 
574  | 0  |   if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))  | 
575  | 0  |     return FALSE;  | 
576  |  |  | 
577  |  |   /* Read and verify APDU data */  | 
578  | 0  |   if (cla)  | 
579  | 0  |     Stream_Read_UINT8(s, *cla);  | 
580  | 0  |   else  | 
581  | 0  |     Stream_Seek(s, 1);  | 
582  | 0  |   if (ins)  | 
583  | 0  |     Stream_Read_UINT8(s, *ins);  | 
584  | 0  |   else  | 
585  | 0  |     Stream_Seek(s, 1);  | 
586  | 0  |   if (p1)  | 
587  | 0  |     Stream_Read_UINT8(s, *p1);  | 
588  | 0  |   else  | 
589  | 0  |     Stream_Seek(s, 1);  | 
590  | 0  |   if (p2)  | 
591  | 0  |     Stream_Read_UINT8(s, *p2);  | 
592  | 0  |   else  | 
593  | 0  |     Stream_Seek(s, 1);  | 
594  |  |  | 
595  |  |   /* If LC is requested - check remaining length and read as well */  | 
596  | 0  |   if (lc)  | 
597  | 0  |   { | 
598  | 0  |     if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))  | 
599  | 0  |       return FALSE;  | 
600  |  |  | 
601  | 0  |     Stream_Read_UINT8(s, *lc);  | 
602  | 0  |     if (!Stream_CheckAndLogRequiredLength(TAG, s, *lc))  | 
603  | 0  |       return FALSE;  | 
604  | 0  |   }  | 
605  |  |  | 
606  |  |   /* read LE */  | 
607  | 0  |   if (le)  | 
608  | 0  |   { | 
609  | 0  |     if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))  | 
610  | 0  |       return FALSE;  | 
611  | 0  |     Stream_Read_UINT8(s, *le);  | 
612  | 0  |   }  | 
613  |  |  | 
614  | 0  |   return TRUE;  | 
615  | 0  | }  | 
616  |  |  | 
617  |  | static BOOL vgids_create_response(UINT16 status, const BYTE* answer, DWORD answerSize,  | 
618  |  |                                   BYTE** outData, DWORD* outDataSize)  | 
619  | 0  | { | 
620  | 0  |   BYTE* out = malloc(answerSize + 2);  | 
621  | 0  |   if (!out)  | 
622  | 0  |   { | 
623  | 0  |     WLog_ERR(TAG, "Failed to allocate memory for response data");  | 
624  | 0  |     return FALSE;  | 
625  | 0  |   }  | 
626  |  |  | 
627  | 0  |   *outData = out;  | 
628  | 0  |   if (answer)  | 
629  | 0  |   { | 
630  | 0  |     memcpy(out, answer, answerSize);  | 
631  | 0  |     out += answerSize;  | 
632  | 0  |   }  | 
633  |  | 
  | 
634  | 0  |   *out = (BYTE)((status >> 8) & 0xFF);  | 
635  | 0  |   *(out + 1) = (BYTE)(status & 0xFF);  | 
636  | 0  |   *outDataSize = answerSize + 2;  | 
637  | 0  |   return TRUE;  | 
638  | 0  | }  | 
639  |  |  | 
640  |  | static BOOL vgids_read_do_fkt(void* data, size_t index, va_list ap)  | 
641  | 0  | { | 
642  | 0  |   BYTE* response = NULL;  | 
643  | 0  |   DWORD responseSize = 0;  | 
644  | 0  |   vgidsEF* file = (vgidsEF*)data;  | 
645  | 0  |   vgidsContext* context = va_arg(ap, vgidsContext*);  | 
646  | 0  |   UINT16 efID = (UINT16)va_arg(ap, unsigned);  | 
647  | 0  |   UINT16 doID = (UINT16)va_arg(ap, unsigned);  | 
648  | 0  |   WINPR_UNUSED(index);  | 
649  |  | 
  | 
650  | 0  |   if (efID == 0x3FFF || efID == file->id)  | 
651  | 0  |   { | 
652  |  |     /* If the DO was successfully read - abort file enum */  | 
653  | 0  |     if (vgids_ef_read_do(file, doID, &response, &responseSize))  | 
654  | 0  |     { | 
655  | 0  |       context->responseData = Stream_New(response, (size_t)responseSize);  | 
656  | 0  |       return FALSE;  | 
657  | 0  |     }  | 
658  | 0  |   }  | 
659  |  |  | 
660  | 0  |   return TRUE;  | 
661  | 0  | }  | 
662  |  |  | 
663  |  | static void vgids_read_do(vgidsContext* context, UINT16 efID, UINT16 doID)  | 
664  | 0  | { | 
665  | 0  |   ArrayList_ForEach(context->files, vgids_read_do_fkt, context, efID, doID);  | 
666  | 0  | }  | 
667  |  |  | 
668  |  | static void vgids_reset_context_response(vgidsContext* context)  | 
669  | 0  | { | 
670  | 0  |   Stream_Free(context->responseData, TRUE);  | 
671  | 0  |   context->responseData = NULL;  | 
672  | 0  | }  | 
673  |  |  | 
674  |  | static void vgids_reset_context_command_data(vgidsContext* context)  | 
675  | 0  | { | 
676  | 0  |   Stream_Free(context->commandData, TRUE);  | 
677  | 0  |   context->commandData = NULL;  | 
678  | 0  | }  | 
679  |  |  | 
680  |  | static BOOL vgids_ins_select(vgidsContext* context, wStream* s, BYTE** response,  | 
681  |  |                              DWORD* responseSize)  | 
682  | 0  | { | 
683  | 0  |   BYTE p1 = 0;  | 
684  | 0  |   BYTE p2 = 0;  | 
685  | 0  |   BYTE lc = 0;  | 
686  | 0  |   DWORD resultDataSize = 0;  | 
687  | 0  |   const BYTE* resultData = NULL;  | 
688  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
689  |  |  | 
690  |  |   /* The only select operations performed are either select by AID or select 3FFF (return  | 
691  |  |    * information about the currently selected DF) */  | 
692  | 0  |   if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))  | 
693  | 0  |     return FALSE;  | 
694  |  |  | 
695  |  |   /* Check P1 for selection mode */  | 
696  | 0  |   switch (p1)  | 
697  | 0  |   { | 
698  |  |     /* Select by AID */  | 
699  | 0  |     case 0x04:  | 
700  | 0  |     { | 
701  |  |       /* read AID from APDU */  | 
702  | 0  |       BYTE aid[ISO_AID_MAX_SIZE] = { 0 }; | 
703  | 0  |       if (lc > ISO_AID_MAX_SIZE)  | 
704  | 0  |       { | 
705  | 0  |         WLog_ERR(TAG, "The LC byte is greater than the maximum AID length");  | 
706  | 0  |         status = ISO_STATUS_INVALIDLC;  | 
707  | 0  |         break;  | 
708  | 0  |       }  | 
709  |  |  | 
710  |  |       /* Check if we select MS GIDS App (only one we know) */  | 
711  | 0  |       Stream_Read(s, aid, lc);  | 
712  | 0  |       if (memcmp(aid, g_MsGidsAID, lc) != 0)  | 
713  | 0  |       { | 
714  | 0  |         status = ISO_STATUS_FILENOTFOUND;  | 
715  | 0  |         break;  | 
716  | 0  |       }  | 
717  |  |  | 
718  |  |       /* Return FCI or FCP for MsGids App */  | 
719  | 0  |       switch (p2)  | 
720  | 0  |       { | 
721  |  |         /* Return FCI information */  | 
722  | 0  |         case 0x00:  | 
723  | 0  |         { | 
724  | 0  |           resultData = g_GidsAppFCI;  | 
725  | 0  |           resultDataSize = sizeof(g_GidsAppFCI);  | 
726  | 0  |           break;  | 
727  | 0  |         }  | 
728  |  |         /* Return FCP information */  | 
729  | 0  |         case 0x04:  | 
730  | 0  |         { | 
731  | 0  |           resultData = g_GidsAppFCP;  | 
732  | 0  |           resultDataSize = sizeof(g_GidsAppFCP);  | 
733  | 0  |           break;  | 
734  | 0  |         }  | 
735  | 0  |         default:  | 
736  | 0  |           status = ISO_STATUS_INVALIDP1P2;  | 
737  | 0  |           break;  | 
738  | 0  |       }  | 
739  |  |  | 
740  | 0  |       if (resultData)  | 
741  | 0  |         context->currentDF = ISO_FID_MF;  | 
742  | 0  |       break;  | 
743  | 0  |     }  | 
744  |  |     /* Select by FID */  | 
745  | 0  |     case 0x00:  | 
746  | 0  |     { | 
747  |  |       /* read FID from APDU */  | 
748  | 0  |       UINT16 fid = 0;  | 
749  | 0  |       if (lc > 2)  | 
750  | 0  |       { | 
751  | 0  |         WLog_ERR(TAG, "The LC byte for the file ID is greater than 2");  | 
752  | 0  |         status = ISO_STATUS_INVALIDLC;  | 
753  | 0  |         break;  | 
754  | 0  |       }  | 
755  |  |  | 
756  | 0  |       Stream_Read_UINT16_BE(s, fid);  | 
757  | 0  |       if (fid != VGIDS_EFID_CURRENTDF || context->currentDF == 0)  | 
758  | 0  |       { | 
759  | 0  |         status = ISO_STATUS_FILENOTFOUND;  | 
760  | 0  |         break;  | 
761  | 0  |       }  | 
762  | 0  |       break;  | 
763  | 0  |     }  | 
764  | 0  |     default:  | 
765  | 0  |     { | 
766  |  |       /* P1 P2 combination not supported */  | 
767  | 0  |       status = ISO_STATUS_INVALIDP1P2;  | 
768  | 0  |       break;  | 
769  | 0  |     }  | 
770  | 0  |   }  | 
771  |  |  | 
772  | 0  |   return vgids_create_response(status, resultData, resultDataSize, response, responseSize);  | 
773  | 0  | }  | 
774  |  |  | 
775  |  | static UINT16 vgids_handle_chained_response(vgidsContext* context, const BYTE** response,  | 
776  |  |                                             DWORD* responseSize)  | 
777  | 0  | { | 
778  |  |   /* Cap to a maximum of 256 bytes and set status to more data */  | 
779  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
780  | 0  |   DWORD remainingBytes = (DWORD)Stream_Length(context->responseData);  | 
781  | 0  |   if (remainingBytes > 256)  | 
782  | 0  |   { | 
783  | 0  |     status = ISO_STATUS_MORE_DATA;  | 
784  | 0  |     remainingBytes = 256;  | 
785  | 0  |   }  | 
786  |  | 
  | 
787  | 0  |   *response = Stream_Buffer(context->responseData);  | 
788  | 0  |   *responseSize = remainingBytes;  | 
789  | 0  |   Stream_Seek(context->responseData, remainingBytes);  | 
790  |  |  | 
791  |  |   /* Check if there are more than 256 bytes left or if we can already provide the remaining length  | 
792  |  |    * in the status word */  | 
793  | 0  |   remainingBytes = (DWORD)(Stream_Length(context->responseData) - remainingBytes);  | 
794  | 0  |   if (remainingBytes < 256 && remainingBytes != 0)  | 
795  | 0  |     status |= (remainingBytes & 0xFF);  | 
796  | 0  |   return status;  | 
797  | 0  | }  | 
798  |  |  | 
799  |  | static BOOL vgids_get_public_key(vgidsContext* context, UINT16 doTag)  | 
800  | 0  | { | 
801  | 0  |   BOOL rc = FALSE;  | 
802  | 0  |   wStream* pubKey = NULL;  | 
803  | 0  |   wStream* response = NULL;  | 
804  |  | 
  | 
805  | 0  |   WINPR_ASSERT(context);  | 
806  |  |  | 
807  |  |   /* Get key components */  | 
808  | 0  |   size_t nSize = 0;  | 
809  | 0  |   size_t eSize = 0;  | 
810  |  | 
  | 
811  | 0  |   char* n = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_N, &nSize);  | 
812  | 0  |   char* e = freerdp_certificate_get_param(context->certificate, FREERDP_CERT_RSA_E, &eSize);  | 
813  |  | 
  | 
814  | 0  |   if (!n || !e)  | 
815  | 0  |     goto handle_error;  | 
816  |  |  | 
817  | 0  |   pubKey = Stream_New(NULL, nSize + eSize + 0x10);  | 
818  | 0  |   if (!pubKey)  | 
819  | 0  |   { | 
820  | 0  |     WLog_ERR(TAG, "Failed to allocate public key stream");  | 
821  | 0  |     goto handle_error;  | 
822  | 0  |   }  | 
823  |  |  | 
824  | 0  |   response = Stream_New(NULL, Stream_Capacity(pubKey) + 0x10);  | 
825  | 0  |   if (!response)  | 
826  | 0  |   { | 
827  | 0  |     WLog_ERR(TAG, "Failed to allocate response stream");  | 
828  | 0  |     goto handle_error;  | 
829  | 0  |   }  | 
830  |  |  | 
831  |  |   /* write modulus and exponent DOs */  | 
832  | 0  |   if (!vgids_write_tlv(pubKey, 0x81, n, nSize))  | 
833  | 0  |     goto handle_error;  | 
834  |  |  | 
835  | 0  |   if (!vgids_write_tlv(pubKey, 0x82, e, eSize))  | 
836  | 0  |     goto handle_error;  | 
837  |  |  | 
838  |  |   /* write ISO public key template */  | 
839  | 0  |   if (!vgids_write_tlv(response, doTag, Stream_Buffer(pubKey), (DWORD)Stream_Length(pubKey)))  | 
840  | 0  |     goto handle_error;  | 
841  |  |  | 
842  |  |   /* set response data */  | 
843  | 0  |   Stream_SetPosition(response, 0);  | 
844  | 0  |   context->responseData = response;  | 
845  | 0  |   response = NULL;  | 
846  |  | 
  | 
847  | 0  |   rc = TRUE;  | 
848  | 0  | handle_error:  | 
849  | 0  |   free(n);  | 
850  | 0  |   free(e);  | 
851  | 0  |   Stream_Free(pubKey, TRUE);  | 
852  | 0  |   Stream_Free(response, TRUE);  | 
853  | 0  |   return rc;  | 
854  | 0  | }  | 
855  |  |  | 
856  |  | static BOOL vgids_ins_getdata(vgidsContext* context, wStream* s, BYTE** response,  | 
857  |  |                               DWORD* responseSize)  | 
858  | 0  | { | 
859  | 0  |   UINT16 doId = 0;  | 
860  | 0  |   UINT16 fileId = 0;  | 
861  | 0  |   BYTE p1 = 0;  | 
862  | 0  |   BYTE p2 = 0;  | 
863  | 0  |   BYTE lc = 0;  | 
864  | 0  |   DWORD resultDataSize = 0;  | 
865  | 0  |   const BYTE* resultData = NULL;  | 
866  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
867  |  |  | 
868  |  |   /* GetData is called a lot!  | 
869  |  |        - To retrieve DOs from files  | 
870  |  |        - To retrieve public key information  | 
871  |  |   */  | 
872  | 0  |   if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))  | 
873  | 0  |     return FALSE;  | 
874  |  |  | 
875  |  |   /* free any previous queried data */  | 
876  | 0  |   vgids_reset_context_response(context);  | 
877  |  |  | 
878  |  |   /* build up file identifier */  | 
879  | 0  |   fileId = ((UINT16)p1 << 8) | p2;  | 
880  |  |  | 
881  |  |   /* Do we have a DO reference? */  | 
882  | 0  |   switch (lc)  | 
883  | 0  |   { | 
884  | 0  |     case 4:  | 
885  | 0  |     { | 
886  | 0  |       BYTE tag = 0;  | 
887  | 0  |       BYTE length = 0;  | 
888  | 0  |       Stream_Read_UINT8(s, tag);  | 
889  | 0  |       Stream_Read_UINT8(s, length);  | 
890  | 0  |       if (tag != 0x5C && length != 0x02)  | 
891  | 0  |       { | 
892  | 0  |         status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
893  | 0  |         break;  | 
894  | 0  |       }  | 
895  |  |  | 
896  | 0  |       Stream_Read_UINT16_BE(s, doId);  | 
897  | 0  |       vgids_read_do(context, fileId, doId);  | 
898  | 0  |       break;  | 
899  | 0  |     }  | 
900  | 0  |     case 0xA:  | 
901  | 0  |     { | 
902  | 0  |       UINT16 pubKeyDO = 0;  | 
903  | 0  |       BYTE tag = 0;  | 
904  | 0  |       BYTE length = 0;  | 
905  | 0  |       BYTE keyRef = 0;  | 
906  |  |  | 
907  |  |       /* We want to retrieve the public key? */  | 
908  | 0  |       if (p1 != 0x3F && p2 != 0xFF)  | 
909  | 0  |       { | 
910  | 0  |         status = ISO_STATUS_INVALIDP1P2;  | 
911  | 0  |         break;  | 
912  | 0  |       }  | 
913  |  |  | 
914  |  |       /* read parent tag/length */  | 
915  | 0  |       Stream_Read_UINT8(s, tag);  | 
916  | 0  |       Stream_Read_UINT8(s, length);  | 
917  | 0  |       if (tag != 0x70 || length != 0x08)  | 
918  | 0  |       { | 
919  | 0  |         status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
920  | 0  |         break;  | 
921  | 0  |       }  | 
922  |  |  | 
923  |  |       /* read key reference TLV */  | 
924  | 0  |       Stream_Read_UINT8(s, tag);  | 
925  | 0  |       Stream_Read_UINT8(s, length);  | 
926  | 0  |       Stream_Read_UINT8(s, keyRef);  | 
927  | 0  |       if (tag != 0x84 || length != 0x01 || keyRef != VGIDS_DEFAULT_KEY_REF)  | 
928  | 0  |       { | 
929  | 0  |         status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
930  | 0  |         break;  | 
931  | 0  |       }  | 
932  |  |  | 
933  |  |       /* read key value template TLV */  | 
934  | 0  |       Stream_Read_UINT8(s, tag);  | 
935  | 0  |       Stream_Read_UINT8(s, length);  | 
936  | 0  |       if (tag != 0xA5 || length != 0x03)  | 
937  | 0  |       { | 
938  | 0  |         status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
939  | 0  |         break;  | 
940  | 0  |       }  | 
941  |  |  | 
942  | 0  |       Stream_Read_UINT16_BE(s, pubKeyDO);  | 
943  | 0  |       Stream_Read_UINT8(s, length);  | 
944  | 0  |       if (pubKeyDO != 0x7F49 || length != 0x80)  | 
945  | 0  |       { | 
946  | 0  |         status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
947  | 0  |         break;  | 
948  | 0  |       }  | 
949  |  |  | 
950  | 0  |       if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))  | 
951  | 0  |       { | 
952  | 0  |         status = ISO_STATUS_INVALIDLC;  | 
953  | 0  |         break;  | 
954  | 0  |       }  | 
955  |  |  | 
956  |  |       /* Return public key value */  | 
957  | 0  |       vgids_get_public_key(context, pubKeyDO);  | 
958  | 0  |       break;  | 
959  | 0  |     }  | 
960  | 0  |     default:  | 
961  | 0  |       status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
962  | 0  |       break;  | 
963  | 0  |   }  | 
964  |  |  | 
965  |  |   /* If we have response data, make it ready for return */  | 
966  | 0  |   if (context->responseData)  | 
967  | 0  |     status = vgids_handle_chained_response(context, &resultData, &resultDataSize);  | 
968  | 0  |   else if (status == ISO_STATUS_SUCCESS)  | 
969  | 0  |     status = ISO_STATUS_REFERENCEDATANOTFOUND;  | 
970  |  | 
  | 
971  | 0  |   return vgids_create_response(status, resultData, resultDataSize, response, responseSize);  | 
972  | 0  | }  | 
973  |  |  | 
974  |  | static BOOL vgids_ins_manage_security_environment(vgidsContext* context, wStream* s,  | 
975  |  |                                                   BYTE** response, DWORD* responseSize)  | 
976  | 0  | { | 
977  | 0  |   BYTE tag = 0;  | 
978  | 0  |   BYTE length = 0;  | 
979  | 0  |   BYTE p1 = 0;  | 
980  | 0  |   BYTE p2 = 0;  | 
981  | 0  |   BYTE lc = 0;  | 
982  | 0  |   DWORD resultDataSize = 0;  | 
983  | 0  |   const BYTE* resultData = NULL;  | 
984  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
985  |  | 
  | 
986  | 0  |   vgids_reset_context_command_data(context);  | 
987  | 0  |   vgids_reset_context_response(context);  | 
988  |  |  | 
989  |  |   /* Manage security environment prepares the card for performing crypto operations. */  | 
990  | 0  |   if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, &lc, NULL))  | 
991  | 0  |     return FALSE;  | 
992  |  |  | 
993  |  |   /* Check APDU params */  | 
994  |  |   /* P1: Set Computation, decipherment, Internal Auth */  | 
995  |  |   /* P2: Digital Signature (B6), Confidentiality (B8) */  | 
996  | 0  |   if (p1 != 0x41 && p2 != 0xB6 && p2 != 0xB8)  | 
997  | 0  |   { | 
998  | 0  |     status = ISO_STATUS_INVALIDP1P2;  | 
999  | 0  |     goto create_response;  | 
1000  | 0  |   }  | 
1001  |  |  | 
1002  | 0  |   if (lc != 6)  | 
1003  | 0  |   { | 
1004  | 0  |     status = ISO_STATUS_WRONGLC;  | 
1005  | 0  |     goto create_response;  | 
1006  | 0  |   }  | 
1007  |  |  | 
1008  | 0  |   context->currentSE.crt = p2;  | 
1009  |  |  | 
1010  |  |   /* parse command buffer */  | 
1011  |  |   /* Read algo ID */  | 
1012  | 0  |   Stream_Read_UINT8(s, tag);  | 
1013  | 0  |   Stream_Read_UINT8(s, length);  | 
1014  | 0  |   if (tag != 0x80 || length != 0x01)  | 
1015  | 0  |   { | 
1016  | 0  |     status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
1017  | 0  |     goto create_response;  | 
1018  | 0  |   }  | 
1019  | 0  |   Stream_Read_UINT8(s, context->currentSE.algoId);  | 
1020  |  |  | 
1021  |  |   /* Read private key reference */  | 
1022  | 0  |   Stream_Read_UINT8(s, tag);  | 
1023  | 0  |   Stream_Read_UINT8(s, length);  | 
1024  | 0  |   if (tag != 0x84 || length != 0x01)  | 
1025  | 0  |   { | 
1026  | 0  |     status = ISO_STATUS_INVALIDCOMMANDDATA;  | 
1027  | 0  |     goto create_response;  | 
1028  | 0  |   }  | 
1029  | 0  |   Stream_Read_UINT8(s, context->currentSE.keyRef);  | 
1030  |  | 
  | 
1031  | 0  | create_response:  | 
1032  |  |   /* If an error occured reset SE */  | 
1033  | 0  |   if (status != ISO_STATUS_SUCCESS)  | 
1034  | 0  |     memset(&context->currentSE, 0, sizeof(context->currentSE));  | 
1035  | 0  |   return vgids_create_response(status, resultData, resultDataSize, response, responseSize);  | 
1036  | 0  | }  | 
1037  |  |  | 
1038  |  | static BOOL vgids_perform_digital_signature(vgidsContext* context)  | 
1039  | 0  | { | 
1040  | 0  |   size_t sigSize = 0;  | 
1041  | 0  |   size_t msgSize = 0;  | 
1042  | 0  |   EVP_PKEY_CTX* ctx = NULL;  | 
1043  | 0  |   EVP_PKEY* pk = freerdp_key_get_evp_pkey(context->privateKey);  | 
1044  | 0  |   const vgidsDigestInfoMap gidsDigestInfo[VGIDS_MAX_DIGEST_INFO] = { | 
1045  | 0  |     { g_PKCS1_SHA1, sizeof(g_PKCS1_SHA1), EVP_sha1() }, | 
1046  | 0  |     { g_PKCS1_SHA224, sizeof(g_PKCS1_SHA224), EVP_sha224() }, | 
1047  | 0  |     { g_PKCS1_SHA256, sizeof(g_PKCS1_SHA256), EVP_sha256() }, | 
1048  | 0  |     { g_PKCS1_SHA384, sizeof(g_PKCS1_SHA384), EVP_sha384() }, | 
1049  | 0  |     { g_PKCS1_SHA512, sizeof(g_PKCS1_SHA512), EVP_sha512() }, | 
1050  | 0  | #if OPENSSL_VERSION_NUMBER >= 0x10101000L  | 
1051  | 0  |     { g_PKCS1_SHA512_224, sizeof(g_PKCS1_SHA512_224), EVP_sha512_224() }, | 
1052  | 0  |     { g_PKCS1_SHA512_256, sizeof(g_PKCS1_SHA512_256), EVP_sha512_256() } | 
1053  | 0  | #endif  | 
1054  | 0  |   };  | 
1055  |  | 
  | 
1056  | 0  |   if (!pk)  | 
1057  | 0  |   { | 
1058  | 0  |     WLog_ERR(TAG, "Failed to create PKEY");  | 
1059  | 0  |     return FALSE;  | 
1060  | 0  |   }  | 
1061  |  |  | 
1062  | 0  |   vgids_reset_context_response(context);  | 
1063  |  |  | 
1064  |  |   /* for each digest info */  | 
1065  | 0  |   Stream_SetPosition(context->commandData, 0);  | 
1066  | 0  |   for (int i = 0; i < VGIDS_MAX_DIGEST_INFO; ++i)  | 
1067  | 0  |   { | 
1068  |  |     /* have we found our digest? */  | 
1069  | 0  |     const vgidsDigestInfoMap* digest = &gidsDigestInfo[i];  | 
1070  | 0  |     if (Stream_Length(context->commandData) >= digest->infoSize &&  | 
1071  | 0  |         memcmp(Stream_Buffer(context->commandData), digest->info, digest->infoSize) == 0)  | 
1072  | 0  |     { | 
1073  |  |       /* skip digest info and calculate message size */  | 
1074  | 0  |       Stream_Seek(context->commandData, digest->infoSize);  | 
1075  | 0  |       if (!Stream_CheckAndLogRequiredLength(TAG, context->commandData, 2))  | 
1076  | 0  |         goto sign_failed;  | 
1077  | 0  |       msgSize = Stream_GetRemainingLength(context->commandData);  | 
1078  |  |  | 
1079  |  |       /* setup signing context */  | 
1080  | 0  |       ctx = EVP_PKEY_CTX_new(pk, NULL);  | 
1081  | 0  |       if (!ctx)  | 
1082  | 0  |       { | 
1083  | 0  |         WLog_ERR(TAG, "Failed to create signing context");  | 
1084  | 0  |         goto sign_failed;  | 
1085  | 0  |       }  | 
1086  |  |  | 
1087  | 0  |       if (EVP_PKEY_sign_init(ctx) <= 0)  | 
1088  | 0  |       { | 
1089  | 0  |         WLog_ERR(TAG, "Failed to init signing context");  | 
1090  | 0  |         goto sign_failed;  | 
1091  | 0  |       }  | 
1092  |  |  | 
1093  |  |       /* set padding and signature algo */  | 
1094  | 0  |       if (context->currentSE.algoId & VGIDS_SE_ALGOID_DST_PAD_PKCS1)  | 
1095  | 0  |       { | 
1096  | 0  |         if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING) <= 0)  | 
1097  | 0  |         { | 
1098  | 0  |           WLog_ERR(TAG, "Failed to set padding mode");  | 
1099  | 0  |           goto sign_failed;  | 
1100  | 0  |         }  | 
1101  | 0  |       }  | 
1102  |  |  | 
1103  | 0  |       if (EVP_PKEY_CTX_set_signature_md(ctx, digest->digest) <= 0)  | 
1104  | 0  |       { | 
1105  | 0  |         WLog_ERR(TAG, "Failed to set signing mode");  | 
1106  | 0  |         goto sign_failed;  | 
1107  | 0  |       }  | 
1108  |  |  | 
1109  |  |       /* Determine buffer length */  | 
1110  | 0  |       if (EVP_PKEY_sign(ctx, NULL, &sigSize, Stream_Pointer(context->commandData), msgSize) <=  | 
1111  | 0  |           0)  | 
1112  | 0  |       { | 
1113  | 0  |         WLog_ERR(TAG, "Failed to determine signature size");  | 
1114  | 0  |         goto sign_failed;  | 
1115  | 0  |       }  | 
1116  |  |  | 
1117  | 0  |       context->responseData = Stream_New(NULL, sigSize);  | 
1118  | 0  |       if (!context->responseData)  | 
1119  | 0  |       { | 
1120  | 0  |         WLog_ERR(TAG, "Failed to allocate signing buffer");  | 
1121  | 0  |         goto sign_failed;  | 
1122  | 0  |       }  | 
1123  |  |  | 
1124  |  |       /* sign */  | 
1125  | 0  |       if (EVP_PKEY_sign(ctx, Stream_Buffer(context->responseData), &sigSize,  | 
1126  | 0  |                         Stream_Pointer(context->commandData), msgSize) <= 0)  | 
1127  | 0  |       { | 
1128  | 0  |         WLog_ERR(TAG, "Failed to create signature");  | 
1129  | 0  |         goto sign_failed;  | 
1130  | 0  |       }  | 
1131  |  |  | 
1132  | 0  |       Stream_SetLength(context->responseData, sigSize);  | 
1133  | 0  |       EVP_PKEY_CTX_free(ctx);  | 
1134  | 0  |       break;  | 
1135  | 0  |     }  | 
1136  | 0  |   }  | 
1137  |  |  | 
1138  | 0  |   EVP_PKEY_free(pk);  | 
1139  | 0  |   vgids_reset_context_command_data(context);  | 
1140  | 0  |   return TRUE;  | 
1141  |  |  | 
1142  | 0  | sign_failed:  | 
1143  | 0  |   vgids_reset_context_command_data(context);  | 
1144  | 0  |   vgids_reset_context_response(context);  | 
1145  | 0  |   EVP_PKEY_CTX_free(ctx);  | 
1146  | 0  |   EVP_PKEY_free(pk);  | 
1147  | 0  |   return FALSE;  | 
1148  | 0  | }  | 
1149  |  |  | 
1150  |  | static BOOL vgids_perform_decrypt(vgidsContext* context)  | 
1151  | 0  | { | 
1152  | 0  |   EVP_PKEY_CTX* ctx = NULL;  | 
1153  | 0  |   BOOL rc = FALSE;  | 
1154  | 0  |   int res = 0;  | 
1155  | 0  |   int padding = RSA_NO_PADDING;  | 
1156  |  | 
  | 
1157  | 0  |   vgids_reset_context_response(context);  | 
1158  |  |  | 
1159  |  |   /* determine padding */  | 
1160  | 0  |   if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_PKCS1)  | 
1161  | 0  |     padding = RSA_PKCS1_PADDING;  | 
1162  | 0  |   else if (context->currentSE.algoId & VGIDS_SE_ALGOID_CT_PAD_OAEP)  | 
1163  | 0  |     padding = RSA_PKCS1_OAEP_PADDING;  | 
1164  |  |  | 
1165  |  |   /* init response buffer */  | 
1166  | 0  |   EVP_PKEY* pkey = freerdp_key_get_evp_pkey(context->privateKey);  | 
1167  | 0  |   if (!pkey)  | 
1168  | 0  |     goto decrypt_failed;  | 
1169  | 0  |   ctx = EVP_PKEY_CTX_new(pkey, NULL);  | 
1170  | 0  |   if (!ctx)  | 
1171  | 0  |     goto decrypt_failed;  | 
1172  | 0  |   if (EVP_PKEY_decrypt_init(ctx) <= 0)  | 
1173  | 0  |     goto decrypt_failed;  | 
1174  | 0  |   if (EVP_PKEY_CTX_set_rsa_padding(ctx, padding) <= 0)  | 
1175  | 0  |     goto decrypt_failed;  | 
1176  |  |  | 
1177  |  |   /* Determine buffer length */  | 
1178  | 0  |   const size_t inlen = Stream_Length(context->commandData);  | 
1179  | 0  |   size_t outlen = 0;  | 
1180  | 0  |   res = EVP_PKEY_decrypt(ctx, NULL, &outlen, Stream_Buffer(context->commandData), inlen);  | 
1181  | 0  |   if (res < 0)  | 
1182  | 0  |   { | 
1183  | 0  |     WLog_ERR(TAG, "Failed to decrypt data");  | 
1184  | 0  |     goto decrypt_failed;  | 
1185  | 0  |   }  | 
1186  |  |  | 
1187  |  |   /* Prepare output buffer */  | 
1188  | 0  |   context->responseData = Stream_New(NULL, outlen);  | 
1189  | 0  |   if (!context->responseData)  | 
1190  | 0  |   { | 
1191  | 0  |     WLog_ERR(TAG, "Failed to create decryption buffer");  | 
1192  | 0  |     goto decrypt_failed;  | 
1193  | 0  |   }  | 
1194  |  |  | 
1195  |  |   /* Decrypt */  | 
1196  | 0  |   res = EVP_PKEY_decrypt(ctx, Stream_Buffer(context->responseData), &outlen,  | 
1197  | 0  |                          Stream_Buffer(context->commandData), inlen);  | 
1198  |  | 
  | 
1199  | 0  |   if (res < 0)  | 
1200  | 0  |   { | 
1201  | 0  |     WLog_ERR(TAG, "Failed to decrypt data");  | 
1202  | 0  |     goto decrypt_failed;  | 
1203  | 0  |   }  | 
1204  |  |  | 
1205  | 0  |   Stream_SetLength(context->responseData, outlen);  | 
1206  | 0  |   rc = TRUE;  | 
1207  |  | 
  | 
1208  | 0  | decrypt_failed:  | 
1209  | 0  |   EVP_PKEY_CTX_free(ctx);  | 
1210  | 0  |   EVP_PKEY_free(pkey);  | 
1211  | 0  |   vgids_reset_context_command_data(context);  | 
1212  | 0  |   if (!rc)  | 
1213  | 0  |     vgids_reset_context_response(context);  | 
1214  | 0  |   return rc;  | 
1215  | 0  | }  | 
1216  |  |  | 
1217  |  | static BOOL vgids_ins_perform_security_operation(vgidsContext* context, wStream* s, BYTE** response,  | 
1218  |  |                                                  DWORD* responseSize)  | 
1219  | 0  | { | 
1220  | 0  |   BYTE cla = 0;  | 
1221  | 0  |   BYTE p1 = 0;  | 
1222  | 0  |   BYTE p2 = 0;  | 
1223  | 0  |   BYTE lc = 0;  | 
1224  | 0  |   DWORD resultDataSize = 0;  | 
1225  | 0  |   const BYTE* resultData = NULL;  | 
1226  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
1227  |  |  | 
1228  |  |   /* Perform security operation */  | 
1229  | 0  |   if (!vgids_parse_apdu_header(s, &cla, NULL, &p1, &p2, &lc, NULL))  | 
1230  | 0  |     return FALSE;  | 
1231  |  |  | 
1232  | 0  |   if (lc == 0)  | 
1233  | 0  |   { | 
1234  | 0  |     status = ISO_STATUS_WRONGLC;  | 
1235  | 0  |     goto create_response;  | 
1236  | 0  |   }  | 
1237  |  |  | 
1238  |  |   /* Is our default key referenced? */  | 
1239  | 0  |   if (context->currentSE.keyRef != VGIDS_DEFAULT_KEY_REF)  | 
1240  | 0  |   { | 
1241  | 0  |     status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;  | 
1242  | 0  |     goto create_response;  | 
1243  | 0  |   }  | 
1244  |  |  | 
1245  |  |   /* is the pin protecting the key verified? */  | 
1246  | 0  |   if (!context->pinVerified)  | 
1247  | 0  |   { | 
1248  | 0  |     status = ISO_STATUS_SECURITYSTATUSNOTSATISFIED;  | 
1249  | 0  |     goto create_response;  | 
1250  | 0  |   }  | 
1251  |  |  | 
1252  |  |   /* Append the data to the context command buffer (PSO might chain command data) */  | 
1253  | 0  |   if (!context->commandData)  | 
1254  | 0  |   { | 
1255  | 0  |     context->commandData = Stream_New(NULL, lc);  | 
1256  | 0  |     if (!context->commandData)  | 
1257  | 0  |       return FALSE;  | 
1258  | 0  |   }  | 
1259  | 0  |   else  | 
1260  | 0  |     Stream_EnsureRemainingCapacity(context->commandData, lc);  | 
1261  |  |  | 
1262  | 0  |   Stream_Write(context->commandData, Stream_Pointer(s), lc);  | 
1263  | 0  |   Stream_SealLength(context->commandData);  | 
1264  |  |  | 
1265  |  |   /* Check if the correct operation is requested for our current SE */  | 
1266  | 0  |   switch (context->currentSE.crt)  | 
1267  | 0  |   { | 
1268  | 0  |     case VGIDS_SE_CRT_SIGN:  | 
1269  | 0  |     { | 
1270  | 0  |       if (p1 != 0x9E || p2 != 0x9A)  | 
1271  | 0  |       { | 
1272  | 0  |         status = ISO_STATUS_INVALIDP1P2;  | 
1273  | 0  |         break;  | 
1274  | 0  |       }  | 
1275  |  |  | 
1276  |  |       /* If chaining is over perform op */  | 
1277  | 0  |       if (!(cla & 0x10))  | 
1278  | 0  |         vgids_perform_digital_signature(context);  | 
1279  | 0  |       break;  | 
1280  | 0  |     }  | 
1281  | 0  |     case VGIDS_SE_CRT_CONF:  | 
1282  | 0  |     { | 
1283  | 0  |       if ((p1 != 0x86 || p2 != 0x80) && (p1 != 0x80 || p2 != 0x86))  | 
1284  | 0  |       { | 
1285  | 0  |         status = ISO_STATUS_INVALIDP1P2;  | 
1286  | 0  |         break;  | 
1287  | 0  |       }  | 
1288  |  |  | 
1289  |  |       /* If chaining is over perform op */  | 
1290  | 0  |       if (!(cla & 0x10))  | 
1291  | 0  |         vgids_perform_decrypt(context);  | 
1292  | 0  |       break;  | 
1293  | 0  |     }  | 
1294  | 0  |     default:  | 
1295  | 0  |       status = ISO_STATUS_INVALIDP1P2;  | 
1296  | 0  |       break;  | 
1297  | 0  |   }  | 
1298  |  |  | 
1299  |  |   /* Do chaining of response data if necessary */  | 
1300  | 0  |   if (status == ISO_STATUS_SUCCESS && context->responseData)  | 
1301  | 0  |     status = vgids_handle_chained_response(context, &resultData, &resultDataSize);  | 
1302  |  |  | 
1303  |  |   /* Check APDU params */  | 
1304  | 0  | create_response:  | 
1305  | 0  |   return vgids_create_response(status, resultData, resultDataSize, response, responseSize);  | 
1306  | 0  | }  | 
1307  |  |  | 
1308  |  | static BOOL vgids_ins_getresponse(vgidsContext* context, wStream* s, BYTE** response,  | 
1309  |  |                                   DWORD* responseSize)  | 
1310  | 0  | { | 
1311  | 0  |   BYTE p1 = 0;  | 
1312  | 0  |   BYTE p2 = 0;  | 
1313  | 0  |   BYTE le = 0;  | 
1314  | 0  |   DWORD resultDataSize = 0;  | 
1315  | 0  |   const BYTE* resultData = NULL;  | 
1316  | 0  |   DWORD expectedLen = 0;  | 
1317  | 0  |   DWORD remainingSize = 0;  | 
1318  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
1319  |  |  | 
1320  |  |   /* Get response continues data transfer after a previous get data command */  | 
1321  |  |   /* Check if there is any data to transfer left */  | 
1322  | 0  |   if (!context->responseData || !Stream_CheckAndLogRequiredLength(TAG, context->responseData, 1))  | 
1323  | 0  |   { | 
1324  | 0  |     status = ISO_STATUS_COMMANDNOTALLOWED;  | 
1325  | 0  |     goto create_response;  | 
1326  | 0  |   }  | 
1327  |  |  | 
1328  | 0  |   if (!vgids_parse_apdu_header(s, NULL, NULL, &p1, &p2, NULL, &le))  | 
1329  | 0  |     return FALSE;  | 
1330  |  |  | 
1331  |  |   /* Check APDU params */  | 
1332  | 0  |   if (p1 != 00 || p2 != 0x00)  | 
1333  | 0  |   { | 
1334  | 0  |     status = ISO_STATUS_INVALIDP1P2;  | 
1335  | 0  |     goto create_response;  | 
1336  | 0  |   }  | 
1337  |  |  | 
1338  |  |   /* LE = 0 means 256 bytes expected */  | 
1339  | 0  |   expectedLen = le;  | 
1340  | 0  |   if (expectedLen == 0)  | 
1341  | 0  |     expectedLen = 256;  | 
1342  |  |  | 
1343  |  |   /* prepare response size and update offset */  | 
1344  | 0  |   remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);  | 
1345  | 0  |   if (remainingSize < expectedLen)  | 
1346  | 0  |     expectedLen = remainingSize;  | 
1347  |  | 
  | 
1348  | 0  |   resultData = Stream_Pointer(context->responseData);  | 
1349  | 0  |   resultDataSize = expectedLen;  | 
1350  | 0  |   Stream_Seek(context->responseData, expectedLen);  | 
1351  |  |  | 
1352  |  |   /* If more data is left return 61XX - otherwise 9000 */  | 
1353  | 0  |   remainingSize = (DWORD)Stream_GetRemainingLength(context->responseData);  | 
1354  | 0  |   if (remainingSize > 0)  | 
1355  | 0  |   { | 
1356  | 0  |     status = ISO_STATUS_MORE_DATA;  | 
1357  | 0  |     if (remainingSize < 256)  | 
1358  | 0  |       status |= (remainingSize & 0xFF);  | 
1359  | 0  |   }  | 
1360  |  | 
  | 
1361  | 0  | create_response:  | 
1362  | 0  |   return vgids_create_response(status, resultData, resultDataSize, response, responseSize);  | 
1363  | 0  | }  | 
1364  |  |  | 
1365  |  | static BOOL vgids_ins_verify(vgidsContext* context, wStream* s, BYTE** response,  | 
1366  |  |                              DWORD* responseSize)  | 
1367  | 0  | { | 
1368  | 0  |   BYTE ins = 0;  | 
1369  | 0  |   BYTE p1 = 0;  | 
1370  | 0  |   BYTE p2 = 0;  | 
1371  | 0  |   BYTE lc = 0;  | 
1372  | 0  |   UINT16 status = ISO_STATUS_SUCCESS;  | 
1373  | 0  |   char pin[VGIDS_MAX_PIN_SIZE + 1] = { 0 }; | 
1374  |  |  | 
1375  |  |   /* Verify is always called for the application password (PIN) P2=0x80 */  | 
1376  | 0  |   if (!vgids_parse_apdu_header(s, NULL, &ins, &p1, &p2, NULL, NULL))  | 
1377  | 0  |     return FALSE;  | 
1378  |  |  | 
1379  |  |   /* Check APDU params */  | 
1380  | 0  |   if (p1 != 00 && p2 != 0x80 && p2 != 0x82)  | 
1381  | 0  |   { | 
1382  | 0  |     status = ISO_STATUS_INVALIDP1P2;  | 
1383  | 0  |     goto create_response;  | 
1384  | 0  |   }  | 
1385  |  |  | 
1386  |  |   /* shall we reset the security state? */  | 
1387  | 0  |   if (p2 == 0x82)  | 
1388  | 0  |   { | 
1389  | 0  |     context->pinVerified = FALSE;  | 
1390  | 0  |     goto create_response;  | 
1391  | 0  |   }  | 
1392  |  |  | 
1393  |  |   /* Check if pin is not already blocked */  | 
1394  | 0  |   if (context->curRetryCounter == 0)  | 
1395  | 0  |   { | 
1396  | 0  |     status = ISO_STATUS_AUTHMETHODBLOCKED;  | 
1397  | 0  |     goto create_response;  | 
1398  | 0  |   }  | 
1399  |  |  | 
1400  |  |   /* Read and verify LC */  | 
1401  | 0  |   if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))  | 
1402  | 0  |   { | 
1403  | 0  |     status = ISO_STATUS_INVALIDLC;  | 
1404  | 0  |     goto create_response;  | 
1405  | 0  |   }  | 
1406  |  |  | 
1407  | 0  |   Stream_Read_UINT8(s, lc);  | 
1408  | 0  |   if (!Stream_CheckAndLogRequiredLength(TAG, s, lc) || (lc > VGIDS_MAX_PIN_SIZE))  | 
1409  | 0  |   { | 
1410  | 0  |     status = ISO_STATUS_INVALIDLC;  | 
1411  | 0  |     goto create_response;  | 
1412  | 0  |   }  | 
1413  |  |  | 
1414  |  |   /* read and verify pin */  | 
1415  | 0  |   Stream_Read(s, pin, lc);  | 
1416  | 0  |   if (strcmp(context->pin, pin) != 0)  | 
1417  | 0  |   { | 
1418  |  |     /* retries are encoded in the lowest 4-bit of the status code */  | 
1419  | 0  |     --context->curRetryCounter;  | 
1420  | 0  |     context->pinVerified = FALSE;  | 
1421  | 0  |     status = (ISO_STATUS_VERIFYFAILED | (context->curRetryCounter & 0xFF));  | 
1422  | 0  |   }  | 
1423  | 0  |   else  | 
1424  | 0  |   { | 
1425  |  |     /* reset retry counter and mark pin as verified */  | 
1426  | 0  |     context->curRetryCounter = context->retryCounter;  | 
1427  | 0  |     context->pinVerified = TRUE;  | 
1428  | 0  |   }  | 
1429  |  | 
  | 
1430  | 0  | create_response:  | 
1431  | 0  |   return vgids_create_response(status, NULL, 0, response, responseSize);  | 
1432  | 0  | }  | 
1433  |  |  | 
1434  |  | vgidsContext* vgids_new(void)  | 
1435  | 0  | { | 
1436  | 0  |   wObject* obj = NULL;  | 
1437  | 0  |   vgidsContext* ctx = calloc(1, sizeof(vgidsContext));  | 
1438  |  | 
  | 
1439  | 0  |   ctx->files = ArrayList_New(FALSE);  | 
1440  | 0  |   if (!ctx->files)  | 
1441  | 0  |   { | 
1442  | 0  |     WLog_ERR(TAG, "Failed to create files array list");  | 
1443  | 0  |     goto create_failed;  | 
1444  | 0  |   }  | 
1445  |  |  | 
1446  | 0  |   obj = ArrayList_Object(ctx->files);  | 
1447  | 0  |   obj->fnObjectFree = vgids_ef_free;  | 
1448  |  | 
  | 
1449  | 0  |   return ctx;  | 
1450  |  |  | 
1451  | 0  | create_failed:  | 
1452  | 0  |   vgids_free(ctx);  | 
1453  | 0  |   return NULL;  | 
1454  | 0  | }  | 
1455  |  |  | 
1456  |  | BOOL vgids_init(vgidsContext* ctx, const char* cert, const char* privateKey, const char* pin)  | 
1457  | 0  | { | 
1458  | 0  |   DWORD kxcSize = 0;  | 
1459  | 0  |   DWORD keymapSize = 0;  | 
1460  | 0  |   DWORD fsTableSize = 0;  | 
1461  | 0  |   BOOL rc = FALSE;  | 
1462  | 0  |   BYTE* kxc = NULL;  | 
1463  | 0  |   BYTE* keymap = NULL;  | 
1464  | 0  |   BYTE* fsTable = NULL;  | 
1465  | 0  |   vgidsEF* masterEF = NULL;  | 
1466  | 0  |   vgidsEF* cardidEF = NULL;  | 
1467  | 0  |   vgidsEF* commonEF = NULL;  | 
1468  | 0  |   BYTE cardid[VGIDS_CARDID_SIZE] = { 0 }; | 
1469  | 0  |   vgidsContainerMapEntry cmrec = { { 'P', 'r', 'i', 'v', 'a', 't', 'e', ' ', 'K', 'e', 'y', ' ', | 
1470  | 0  |                                    '0', '0' },  | 
1471  | 0  |                                  CONTAINER_MAP_VALID_CONTAINER |  | 
1472  | 0  |                                      CONTAINER_MAP_DEFAULT_CONTAINER,  | 
1473  | 0  |                                  0,  | 
1474  | 0  |                                  0,  | 
1475  | 0  |                                  0x00 /* key-size in bits - filled out later */ };  | 
1476  | 0  |   vgidsFilesysTableEntry filesys[] = { | 
1477  | 0  |     { "mscp", "", 0, 0, 0, 0xA000, 0 }, | 
1478  | 0  |     { "", "cardid", 0, 0xDF20, 0, 0xA012, 0 }, | 
1479  | 0  |     { "", "cardapps", 0, 0xDF21, 0, 0xA010, 0 }, | 
1480  | 0  |     { "", "cardcf", 0, 0xDF22, 0, 0xA010, 0 }, | 
1481  | 0  |     { "mscp", "cmapfile", 0, 0xDF23, 0, 0xA010, 0 }, | 
1482  | 0  |     { "mscp", "kxc00", 0, 0xDF24, 0, 0xA010, 0 }, | 
1483  | 0  |   };  | 
1484  |  |  | 
1485  |  |   /* Check params */  | 
1486  | 0  |   if (!cert || !privateKey || !pin)  | 
1487  | 0  |   { | 
1488  | 0  |     WLog_DBG(TAG, "Passed invalid NULL argument: cert=%p, privateKey=%p, pin=%p", cert,  | 
1489  | 0  |              privateKey, pin);  | 
1490  | 0  |     goto init_failed;  | 
1491  | 0  |   }  | 
1492  |  |  | 
1493  |  |   /* Convert PEM input to DER certificate/public key/private key */  | 
1494  | 0  |   ctx->certificate = freerdp_certificate_new_from_pem(cert);  | 
1495  | 0  |   if (!ctx->certificate)  | 
1496  | 0  |     goto init_failed;  | 
1497  |  |  | 
1498  | 0  |   ctx->privateKey = freerdp_key_new_from_pem(privateKey);  | 
1499  | 0  |   if (!ctx->privateKey)  | 
1500  | 0  |     goto init_failed;  | 
1501  |  |  | 
1502  |  |   /* create masterfile */  | 
1503  | 0  |   masterEF = vgids_ef_new(ctx, VGIDS_EFID_MASTER);  | 
1504  | 0  |   if (!masterEF)  | 
1505  | 0  |     goto init_failed;  | 
1506  |  |  | 
1507  |  |   /* create cardid file with cardid DO */  | 
1508  | 0  |   cardidEF = vgids_ef_new(ctx, VGIDS_EFID_CARDID);  | 
1509  | 0  |   if (!cardidEF)  | 
1510  | 0  |     goto init_failed;  | 
1511  | 0  |   winpr_RAND(cardid, sizeof(cardid));  | 
1512  | 0  |   if (!vgids_ef_write_do(cardidEF, VGIDS_DO_CARDID, cardid, sizeof(cardid)))  | 
1513  | 0  |     goto init_failed;  | 
1514  |  |  | 
1515  |  |   /* create user common file */  | 
1516  | 0  |   commonEF = vgids_ef_new(ctx, VGIDS_EFID_COMMON);  | 
1517  | 0  |   if (!commonEF)  | 
1518  | 0  |     goto init_failed;  | 
1519  |  |  | 
1520  |  |   /* write card cache DO */  | 
1521  | 0  |   if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDCF, g_CardCFContents, sizeof(g_CardCFContents)))  | 
1522  | 0  |     goto init_failed;  | 
1523  |  |  | 
1524  |  |   /* write container map DO */  | 
1525  | 0  |   const int size = get_rsa_key_size(ctx->privateKey);  | 
1526  | 0  |   if (size <= 0)  | 
1527  | 0  |     goto init_failed;  | 
1528  |  |  | 
1529  | 0  |   cmrec.wKeyExchangeKeySizeBits = (WORD)size * 8;  | 
1530  | 0  |   if (!vgids_ef_write_do(commonEF, VGIDS_DO_CMAPFILE, &cmrec, sizeof(cmrec)))  | 
1531  | 0  |     goto init_failed;  | 
1532  |  |  | 
1533  |  |   /* write cardapps DO */  | 
1534  | 0  |   if (!vgids_ef_write_do(commonEF, VGIDS_DO_CARDAPPS, g_CardAppsContents,  | 
1535  | 0  |                          sizeof(g_CardAppsContents)))  | 
1536  | 0  |     goto init_failed;  | 
1537  |  |  | 
1538  |  |   /* convert and write certificate to key exchange container */  | 
1539  | 0  |   if (!vgids_prepare_certificate(ctx->certificate, &kxc, &kxcSize))  | 
1540  | 0  |     goto init_failed;  | 
1541  | 0  |   if (!vgids_ef_write_do(commonEF, VGIDS_DO_KXC00, kxc, kxcSize))  | 
1542  | 0  |     goto init_failed;  | 
1543  |  |  | 
1544  |  |   /* prepare and write file system table */  | 
1545  | 0  |   if (!vgids_prepare_fstable(filesys, ARRAYSIZE(filesys), &fsTable, &fsTableSize))  | 
1546  | 0  |     goto init_failed;  | 
1547  | 0  |   if (!vgids_ef_write_do(masterEF, VGIDS_DO_FILESYSTEMTABLE, fsTable, fsTableSize))  | 
1548  | 0  |     goto init_failed;  | 
1549  |  |  | 
1550  |  |   /* vgids_prepare_keymap and write to masterEF */  | 
1551  | 0  |   if (!vgids_prepare_keymap(ctx, &keymap, &keymapSize))  | 
1552  | 0  |     goto init_failed;  | 
1553  | 0  |   if (!vgids_ef_write_do(masterEF, VGIDS_DO_KEYMAP, keymap, keymapSize))  | 
1554  | 0  |     goto init_failed;  | 
1555  |  |  | 
1556  |  |   /* store user pin */  | 
1557  | 0  |   ctx->curRetryCounter = ctx->retryCounter = VGIDS_DEFAULT_RETRY_COUNTER;  | 
1558  | 0  |   ctx->pin = _strdup(pin);  | 
1559  | 0  |   if (!ctx->pin)  | 
1560  | 0  |     goto init_failed;  | 
1561  |  |  | 
1562  | 0  |   rc = TRUE;  | 
1563  |  | 
  | 
1564  | 0  | init_failed:  | 
1565  |  |   // ArrayList_Append in vgids_ef_new takes ownership  | 
1566  |  |   // of cardidEF, commonEF, masterEF  | 
1567  |  |   // NOLINTNEXTLINE(clang-analyzer-unix.Malloc)  | 
1568  | 0  |   free(kxc);  | 
1569  | 0  |   free(keymap);  | 
1570  | 0  |   free(fsTable);  | 
1571  | 0  |   return rc;  | 
1572  | 0  | }  | 
1573  |  |  | 
1574  |  | BOOL vgids_process_apdu(vgidsContext* context, const BYTE* data, DWORD dataSize, BYTE** response,  | 
1575  |  |                         DWORD* responseSize)  | 
1576  | 0  | { | 
1577  | 0  |   wStream s;  | 
1578  | 0  |   static int x = 1;  | 
1579  |  |  | 
1580  |  |   /* Check params */  | 
1581  | 0  |   if (!context || !data || !response || !responseSize)  | 
1582  | 0  |   { | 
1583  | 0  |     WLog_ERR(TAG, "Invalid NULL pointer passed");  | 
1584  | 0  |     return FALSE;  | 
1585  | 0  |   }  | 
1586  |  |  | 
1587  | 0  |   if (dataSize < 4)  | 
1588  | 0  |   { | 
1589  | 0  |     WLog_ERR(TAG, "APDU buffer is less than 4 bytes: %" PRIu32, dataSize);  | 
1590  | 0  |     return FALSE;  | 
1591  | 0  |   }  | 
1592  |  |  | 
1593  |  |   /* Examine INS byte */  | 
1594  | 0  |   Stream_StaticConstInit(&s, data, dataSize);  | 
1595  | 0  |   if (x++ == 0xe)  | 
1596  | 0  |     x = 0xe + 1;  | 
1597  | 0  |   switch (data[1])  | 
1598  | 0  |   { | 
1599  | 0  |     case ISO_INS_SELECT:  | 
1600  | 0  |       return vgids_ins_select(context, &s, response, responseSize);  | 
1601  | 0  |     case ISO_INS_GETDATA:  | 
1602  | 0  |       return vgids_ins_getdata(context, &s, response, responseSize);  | 
1603  | 0  |     case ISO_INS_GETRESPONSE:  | 
1604  | 0  |       return vgids_ins_getresponse(context, &s, response, responseSize);  | 
1605  | 0  |     case ISO_INS_MSE:  | 
1606  | 0  |       return vgids_ins_manage_security_environment(context, &s, response, responseSize);  | 
1607  | 0  |     case ISO_INS_PSO:  | 
1608  | 0  |       return vgids_ins_perform_security_operation(context, &s, response, responseSize);  | 
1609  | 0  |     case ISO_INS_VERIFY:  | 
1610  | 0  |       return vgids_ins_verify(context, &s, response, responseSize);  | 
1611  | 0  |     default:  | 
1612  | 0  |       break;  | 
1613  | 0  |   }  | 
1614  |  |  | 
1615  |  |   /* return command not allowed */  | 
1616  | 0  |   return vgids_create_response(ISO_STATUS_COMMANDNOTALLOWED, NULL, 0, response, responseSize);  | 
1617  | 0  | }  | 
1618  |  |  | 
1619  |  | void vgids_free(vgidsContext* context)  | 
1620  | 0  | { | 
1621  | 0  |   if (context)  | 
1622  | 0  |   { | 
1623  | 0  |     freerdp_key_free(context->privateKey);  | 
1624  | 0  |     freerdp_certificate_free(context->certificate);  | 
1625  | 0  |     Stream_Free(context->commandData, TRUE);  | 
1626  | 0  |     Stream_Free(context->responseData, TRUE);  | 
1627  | 0  |     free(context->pin);  | 
1628  | 0  |     ArrayList_Free(context->files);  | 
1629  | 0  |     free(context);  | 
1630  | 0  |   }  | 
1631  | 0  | }  |