Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,62 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# libfreerdp-crypto cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
set(MODULE_NAME "freerdp-crypto")
set(MODULE_PREFIX "FREERDP_CRYPTO")
freerdp_module_add(
ber.c
per.c
base64.c
x509_utils.c
x509_utils.h
cert_common.h
cert_common.c
privatekey.c
privatekey.h
certificate.c
certificate.h
certificate_data.c
certificate_store.c
crypto.c
tls.c
tls.h
opensslcompat.c
)
if(NOT WITHOUT_FREERDP_3x_DEPRECATED)
freerdp_module_add(er.c der.c)
endif()
freerdp_include_directory_add(${OPENSSL_INCLUDE_DIR})
freerdp_library_add(${OPENSSL_LIBRARIES})
freerdp_pc_add_requires_private("libssl")
if(MBEDTLS_FOUND)
freerdp_include_directory_add(${MBEDTLS_INCLUDE_DIR})
freerdp_library_add(${MBEDTLS_LIBRARIES})
freerdp_pc_add_requires_private("mbedtls")
endif()
if(WIN32)
freerdp_library_add(ws2_32)
endif()
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,555 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Base64 Encoding & Decoding
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <freerdp/crypto/crypto.h>
static const BYTE enc_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const BYTE enc_base64url[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const signed char dec_base64url[] = {
-1, /* 0 000 00 00000000 NUL &#00; Null character */
-1, /* 1 001 01 00000001 SOH &#01; Start of Heading */
-1, /* 2 002 02 00000010 STX &#02; Start of Text */
-1, /* 3 003 03 00000011 ETX &#03; End of Text */
-1, /* 4 004 04 00000100 EOT &#04; End of Transmission */
-1, /* 5 005 05 00000101 ENQ &#05; Enquiry */
-1, /* 6 006 06 00000110 ACK &#06; Acknowledge */
-1, /* 7 007 07 00000111 BEL &#07; Bell, Alert */
-1, /* 8 010 08 00001000 BS &#08; Backspace */
-1, /* 9 011 09 00001001 HT &#09; Horizontal Tab */
-1, /* 10 012 0A 00001010 LF &#10; Line Feed */
-1, /* 11 013 0B 00001011 VT &#11; Vertical Tabulation */
-1, /* 12 014 0C 00001100 FF &#12; Form Feed */
-1, /* 13 015 0D 00001101 CR &#13; Carriage Return */
-1, /* 14 016 0E 00001110 SO &#14; Shift Out */
-1, /* 15 017 0F 00001111 SI &#15; Shift In */
-1, /* 16 020 10 00010000 DLE &#16; Data Link Escape */
-1, /* 17 021 11 00010001 DC1 &#17; Device Control One (XON) */
-1, /* 18 022 12 00010010 DC2 &#18; Device Control Two */
-1, /* 19 023 13 00010011 DC3 &#19; Device Control Three (XOFF) */
-1, /* 20 024 14 00010100 DC4 &#20; Device Control Four */
-1, /* 21 025 15 00010101 NAK &#21; Negative Acknowledge */
-1, /* 22 026 16 00010110 SYN &#22; Synchronous Idle */
-1, /* 23 027 17 00010111 ETB &#23; End of Transmission Block */
-1, /* 24 030 18 00011000 CAN &#24; Cancel */
-1, /* 25 031 19 00011001 EM &#25; End of medium */
-1, /* 26 032 1A 00011010 SUB &#26; Substitute */
-1, /* 27 033 1B 00011011 ESC &#27; Escape */
-1, /* 28 034 1C 00011100 FS &#28; File Separator */
-1, /* 29 035 1D 00011101 GS &#29; Group Separator */
-1, /* 30 036 1E 00011110 RS &#30; Record Separator */
-1, /* 31 037 1F 00011111 US &#31; Unit Separator */
-1, /* 32 040 20 00100000 SP &#32; Space */
-1, /* 33 041 21 00100001 ! &#33; &excl; Exclamation mark */
-1, /* 34 042 22 00100010 " &#34; &quot; Double quotes (or speech marks) */
-1, /* 35 043 23 00100011 # &#35; &num; Number sign */
-1, /* 36 044 24 00100100 $ &#36; &dollar; Dollar */
-1, /* 37 045 25 00100101 % &#37; &percnt; Per cent sign */
-1, /* 38 046 26 00100110 & &#38; &amp; Ampersand */
-1, /* 39 047 27 00100111 ' &#39; &apos; Single quote */
-1, /* 40 050 28 00101000 ( &#40; &lparen; Open parenthesis (or open
* bracket)
*/
-1, /* 41 051 29 00101001 ) &#41; &rparen; Close parenthesis (or close
* bracket)
*/
-1, /* 42 052 2A 00101010 * &#42; &ast; Asterisk */
-1, /* 43 053 2B 00101011 + &#43; &plus; Plus */
-1, /* 44 054 2C 00101100 , &#44; &comma; Comma */
62, /* 45 055 2D 00101101 - &#45; Hyphen-minus */
-1, /* 46 056 2E 00101110 . &#46; &period; Period, dot or full stop */
-1, /* 47 057 2F 00101111 / &#47; &sol; Slash or divide */
52, /* 48 060 30 00110000 0 &#48; Zero */
53, /* 49 061 31 00110001 1 &#49; One */
54, /* 50 062 32 00110010 2 &#50; Two */
55, /* 51 063 33 00110011 3 &#51; Three */
56, /* 52 064 34 00110100 4 &#52; Four */
57, /* 53 065 35 00110101 5 &#53; Five */
58, /* 54 066 36 00110110 6 &#54; Six */
59, /* 55 067 37 00110111 7 &#55; Seven */
60, /* 56 070 38 00111000 8 &#56; Eight */
61, /* 57 071 39 00111001 9 &#57; Nine */
-1, /* 58 072 3A 00111010 : &#58; &colon; Colon */
-1, /* 59 073 3B 00111011 ; &#59; &semi; Semicolon */
-1, /* 60 074 3C 00111100 < &#60; &lt; Less than (or open angled bracket)
*/
-1, /* 61 075 3D 00111101 = &#61; &equals; Equals */
-1, /* 62 076 3E 00111110 > &#62; &gt; Greater than (or close angled
* bracket)
*/
-1, /* 63 077 3F 00111111 ? &#63; &quest; Question mark */
-1, /* 64 100 40 01000000 @ &#64; &commat; At sign */
0, /* 65 101 41 01000001 A &#65; Uppercase A */
1, /* 66 102 42 01000010 B &#66; Uppercase B */
2, /* 67 103 43 01000011 C &#67; Uppercase C */
3, /* 68 104 44 01000100 D &#68; Uppercase D */
4, /* 69 105 45 01000101 E &#69; Uppercase E */
5, /* 70 106 46 01000110 F &#70; Uppercase F */
6, /* 71 107 47 01000111 G &#71; Uppercase G */
7, /* 72 110 48 01001000 H &#72; Uppercase H */
8, /* 73 111 49 01001001 I &#73; Uppercase I */
9, /* 74 112 4A 01001010 J &#74; Uppercase J */
10, /* 75 113 4B 01001011 K &#75; Uppercase K */
11, /* 76 114 4C 01001100 L &#76; Uppercase L */
12, /* 77 115 4D 01001101 M &#77; Uppercase M */
13, /* 78 116 4E 01001110 N &#78; Uppercase N */
14, /* 79 117 4F 01001111 O &#79; Uppercase O */
15, /* 80 120 50 01010000 P &#80; Uppercase P */
16, /* 81 121 51 01010001 Q &#81; Uppercase Q */
17, /* 82 122 52 01010010 R &#82; Uppercase R */
18, /* 83 123 53 01010011 S &#83; Uppercase S */
19, /* 84 124 54 01010100 T &#84; Uppercase T */
20, /* 85 125 55 01010101 U &#85; Uppercase U */
21, /* 86 126 56 01010110 V &#86; Uppercase V */
22, /* 87 127 57 01010111 W &#87; Uppercase W */
23, /* 88 130 58 01011000 X &#88; Uppercase X */
24, /* 89 131 59 01011001 Y &#89; Uppercase Y */
25, /* 90 132 5A 01011010 Z &#90; Uppercase Z */
-1, /* 91 133 5B 01011011 [ &#91; &lsqb; Opening bracket */
-1, /* 92 134 5C 01011100 \ &#92; &bsol; Backslash */
-1, /* 93 135 5D 01011101 ] &#93; &rsqb; Closing bracket */
-1, /* 94 136 5E 01011110 ^ &#94; &Hat; Caret - circumflex */
63, /* 95 137 5F 01011111 _ &#95; &lowbar; Underscore */
-1, /* 96 140 60 01100000 ` &#96; &grave; Grave accent */
26, /* 97 141 61 01100001 a &#97; Lowercase a */
27, /* 98 142 62 01100010 b &#98; Lowercase b */
28, /* 99 143 63 01100011 c &#99; Lowercase c */
29, /* 100 144 64 01100100 d &#100; Lowercase d */
30, /* 101 145 65 01100101 e &#101; Lowercase e */
31, /* 102 146 66 01100110 f &#102; Lowercase f */
32, /* 103 147 67 01100111 g &#103; Lowercase g */
33, /* 104 150 68 01101000 h &#104; Lowercase h */
34, /* 105 151 69 01101001 i &#105; Lowercase i */
35, /* 106 152 6A 01101010 j &#106; Lowercase j */
36, /* 107 153 6B 01101011 k &#107; Lowercase k */
37, /* 108 154 6C 01101100 l &#108; Lowercase l */
38, /* 109 155 6D 01101101 m &#109; Lowercase m */
39, /* 110 156 6E 01101110 n &#110; Lowercase n */
40, /* 111 157 6F 01101111 o &#111; Lowercase o */
41, /* 112 160 70 01110000 p &#112; Lowercase p */
42, /* 113 161 71 01110001 q &#113; Lowercase q */
43, /* 114 162 72 01110010 r &#114; Lowercase r */
44, /* 115 163 73 01110011 s &#115; Lowercase s */
45, /* 116 164 74 01110100 t &#116; Lowercase t */
46, /* 117 165 75 01110101 u &#117; Lowercase u */
47, /* 118 166 76 01110110 v &#118; Lowercase v */
48, /* 119 167 77 01110111 w &#119; Lowercase w */
49, /* 120 170 78 01111000 x &#120; Lowercase x */
50, /* 121 171 79 01111001 y &#121; Lowercase y */
51, /* 122 172 7A 01111010 z &#122; Lowercase z */
-1, /* 123 173 7B 01111011 { &#123; &lcub; Opening brace */
-1, /* 124 174 7C 01111100 | &#124; &verbar; Vertical bar */
-1, /* 125 175 7D 01111101 } &#125; &rcub; Closing brace */
-1, /* 126 176 7E 01111110 ~ &#126; &tilde; Equivalency sign - tilde */
-1, /* 127 177 7F 01111111 DEL &#127; Delete */
};
static const signed char dec_base64[] = {
-1, /* 0 000 00 00000000 NUL &#00; Null character */
-1, /* 1 001 01 00000001 SOH &#01; Start of Heading */
-1, /* 2 002 02 00000010 STX &#02; Start of Text */
-1, /* 3 003 03 00000011 ETX &#03; End of Text */
-1, /* 4 004 04 00000100 EOT &#04; End of Transmission */
-1, /* 5 005 05 00000101 ENQ &#05; Enquiry */
-1, /* 6 006 06 00000110 ACK &#06; Acknowledge */
-1, /* 7 007 07 00000111 BEL &#07; Bell, Alert */
-1, /* 8 010 08 00001000 BS &#08; Backspace */
-1, /* 9 011 09 00001001 HT &#09; Horizontal Tab */
-1, /* 10 012 0A 00001010 LF &#10; Line Feed */
-1, /* 11 013 0B 00001011 VT &#11; Vertical Tabulation */
-1, /* 12 014 0C 00001100 FF &#12; Form Feed */
-1, /* 13 015 0D 00001101 CR &#13; Carriage Return */
-1, /* 14 016 0E 00001110 SO &#14; Shift Out */
-1, /* 15 017 0F 00001111 SI &#15; Shift In */
-1, /* 16 020 10 00010000 DLE &#16; Data Link Escape */
-1, /* 17 021 11 00010001 DC1 &#17; Device Control One (XON) */
-1, /* 18 022 12 00010010 DC2 &#18; Device Control Two */
-1, /* 19 023 13 00010011 DC3 &#19; Device Control Three (XOFF) */
-1, /* 20 024 14 00010100 DC4 &#20; Device Control Four */
-1, /* 21 025 15 00010101 NAK &#21; Negative Acknowledge */
-1, /* 22 026 16 00010110 SYN &#22; Synchronous Idle */
-1, /* 23 027 17 00010111 ETB &#23; End of Transmission Block */
-1, /* 24 030 18 00011000 CAN &#24; Cancel */
-1, /* 25 031 19 00011001 EM &#25; End of medium */
-1, /* 26 032 1A 00011010 SUB &#26; Substitute */
-1, /* 27 033 1B 00011011 ESC &#27; Escape */
-1, /* 28 034 1C 00011100 FS &#28; File Separator */
-1, /* 29 035 1D 00011101 GS &#29; Group Separator */
-1, /* 30 036 1E 00011110 RS &#30; Record Separator */
-1, /* 31 037 1F 00011111 US &#31; Unit Separator */
-1, /* 32 040 20 00100000 SP &#32; Space */
-1, /* 33 041 21 00100001 ! &#33; &excl; Exclamation mark */
-1, /* 34 042 22 00100010 " &#34; &quot; Double quotes (or speech marks) */
-1, /* 35 043 23 00100011 # &#35; &num; Number sign */
-1, /* 36 044 24 00100100 $ &#36; &dollar; Dollar */
-1, /* 37 045 25 00100101 % &#37; &percnt; Per cent sign */
-1, /* 38 046 26 00100110 & &#38; &amp; Ampersand */
-1, /* 39 047 27 00100111 ' &#39; &apos; Single quote */
-1, /* 40 050 28 00101000 ( &#40; &lparen; Open parenthesis (or open
* bracket)
*/
-1, /* 41 051 29 00101001 ) &#41; &rparen; Close parenthesis (or close
* bracket)
*/
-1, /* 42 052 2A 00101010 * &#42; &ast; Asterisk */
62, /* 43 053 2B 00101011 + &#43; &plus; Plus */
-1, /* 44 054 2C 00101100 , &#44; &comma; Comma */
-1, /* 45 055 2D 00101101 - &#45; Hyphen-minus */
-1, /* 46 056 2E 00101110 . &#46; &period; Period, dot or full stop */
63, /* 47 057 2F 00101111 / &#47; &sol; Slash or divide */
52, /* 48 060 30 00110000 0 &#48; Zero */
53, /* 49 061 31 00110001 1 &#49; One */
54, /* 50 062 32 00110010 2 &#50; Two */
55, /* 51 063 33 00110011 3 &#51; Three */
56, /* 52 064 34 00110100 4 &#52; Four */
57, /* 53 065 35 00110101 5 &#53; Five */
58, /* 54 066 36 00110110 6 &#54; Six */
59, /* 55 067 37 00110111 7 &#55; Seven */
60, /* 56 070 38 00111000 8 &#56; Eight */
61, /* 57 071 39 00111001 9 &#57; Nine */
-1, /* 58 072 3A 00111010 : &#58; &colon; Colon */
-1, /* 59 073 3B 00111011 ; &#59; &semi; Semicolon */
-1, /* 60 074 3C 00111100 < &#60; &lt; Less than (or open angled bracket)
*/
-1, /* 61 075 3D 00111101 = &#61; &equals; Equals */
-1, /* 62 076 3E 00111110 > &#62; &gt; Greater than (or close angled
* bracket)
*/
-1, /* 63 077 3F 00111111 ? &#63; &quest; Question mark */
-1, /* 64 100 40 01000000 @ &#64; &commat; At sign */
0, /* 65 101 41 01000001 A &#65; Uppercase A */
1, /* 66 102 42 01000010 B &#66; Uppercase B */
2, /* 67 103 43 01000011 C &#67; Uppercase C */
3, /* 68 104 44 01000100 D &#68; Uppercase D */
4, /* 69 105 45 01000101 E &#69; Uppercase E */
5, /* 70 106 46 01000110 F &#70; Uppercase F */
6, /* 71 107 47 01000111 G &#71; Uppercase G */
7, /* 72 110 48 01001000 H &#72; Uppercase H */
8, /* 73 111 49 01001001 I &#73; Uppercase I */
9, /* 74 112 4A 01001010 J &#74; Uppercase J */
10, /* 75 113 4B 01001011 K &#75; Uppercase K */
11, /* 76 114 4C 01001100 L &#76; Uppercase L */
12, /* 77 115 4D 01001101 M &#77; Uppercase M */
13, /* 78 116 4E 01001110 N &#78; Uppercase N */
14, /* 79 117 4F 01001111 O &#79; Uppercase O */
15, /* 80 120 50 01010000 P &#80; Uppercase P */
16, /* 81 121 51 01010001 Q &#81; Uppercase Q */
17, /* 82 122 52 01010010 R &#82; Uppercase R */
18, /* 83 123 53 01010011 S &#83; Uppercase S */
19, /* 84 124 54 01010100 T &#84; Uppercase T */
20, /* 85 125 55 01010101 U &#85; Uppercase U */
21, /* 86 126 56 01010110 V &#86; Uppercase V */
22, /* 87 127 57 01010111 W &#87; Uppercase W */
23, /* 88 130 58 01011000 X &#88; Uppercase X */
24, /* 89 131 59 01011001 Y &#89; Uppercase Y */
25, /* 90 132 5A 01011010 Z &#90; Uppercase Z */
-1, /* 91 133 5B 01011011 [ &#91; &lsqb; Opening bracket */
-1, /* 92 134 5C 01011100 \ &#92; &bsol; Backslash */
-1, /* 93 135 5D 01011101 ] &#93; &rsqb; Closing bracket */
-1, /* 94 136 5E 01011110 ^ &#94; &Hat; Caret - circumflex */
-1, /* 95 137 5F 01011111 _ &#95; &lowbar; Underscore */
-1, /* 96 140 60 01100000 ` &#96; &grave; Grave accent */
26, /* 97 141 61 01100001 a &#97; Lowercase a */
27, /* 98 142 62 01100010 b &#98; Lowercase b */
28, /* 99 143 63 01100011 c &#99; Lowercase c */
29, /* 100 144 64 01100100 d &#100; Lowercase d */
30, /* 101 145 65 01100101 e &#101; Lowercase e */
31, /* 102 146 66 01100110 f &#102; Lowercase f */
32, /* 103 147 67 01100111 g &#103; Lowercase g */
33, /* 104 150 68 01101000 h &#104; Lowercase h */
34, /* 105 151 69 01101001 i &#105; Lowercase i */
35, /* 106 152 6A 01101010 j &#106; Lowercase j */
36, /* 107 153 6B 01101011 k &#107; Lowercase k */
37, /* 108 154 6C 01101100 l &#108; Lowercase l */
38, /* 109 155 6D 01101101 m &#109; Lowercase m */
39, /* 110 156 6E 01101110 n &#110; Lowercase n */
40, /* 111 157 6F 01101111 o &#111; Lowercase o */
41, /* 112 160 70 01110000 p &#112; Lowercase p */
42, /* 113 161 71 01110001 q &#113; Lowercase q */
43, /* 114 162 72 01110010 r &#114; Lowercase r */
44, /* 115 163 73 01110011 s &#115; Lowercase s */
45, /* 116 164 74 01110100 t &#116; Lowercase t */
46, /* 117 165 75 01110101 u &#117; Lowercase u */
47, /* 118 166 76 01110110 v &#118; Lowercase v */
48, /* 119 167 77 01110111 w &#119; Lowercase w */
49, /* 120 170 78 01111000 x &#120; Lowercase x */
50, /* 121 171 79 01111001 y &#121; Lowercase y */
51, /* 122 172 7A 01111010 z &#122; Lowercase z */
-1, /* 123 173 7B 01111011 { &#123; &lcub; Opening brace */
-1, /* 124 174 7C 01111100 | &#124; &verbar; Vertical bar */
-1, /* 125 175 7D 01111101 } &#125; &rcub; Closing brace */
-1, /* 126 176 7E 01111110 ~ &#126; &tilde; Equivalency sign - tilde */
-1, /* 127 177 7F 01111111 DEL &#127; Delete */
};
static inline char* base64_encode_ex(const BYTE* WINPR_RESTRICT alphabet,
WINPR_ATTR_UNUSED size_t alphabetCount,
const BYTE* WINPR_RESTRICT data, size_t length, BOOL pad,
BOOL crLf, size_t lineSize)
{
size_t blocks = 0;
size_t outLen = (length + 3) * 4 / 3;
size_t extra = 0;
if (crLf)
{
size_t nCrLf = (outLen + lineSize - 1) / lineSize;
extra = nCrLf * 2ull;
}
size_t outCounter = 0;
const BYTE* q = data;
BYTE* p = malloc(outLen + extra + 1ull);
if (!p)
return nullptr;
char* ret = (char*)p;
/* b1, b2, b3 are input bytes
*
* 0 1 2
* 012345678901234567890123
* | b1 | b2 | b3 |
*
* [ c1 ] [ c3 ]
* [ c2 ] [ c4 ]
*
* c1, c2, c3, c4 are output chars in base64
*/
/* first treat complete blocks */
blocks = length - (length % 3);
for (size_t i = 0; i < blocks; i += 3, q += 3)
{
const unsigned c = ((unsigned)q[0] << 16) + ((unsigned)q[1] << 8) + q[2];
const unsigned idx0 = (c & 0x00FC0000) >> 18;
const unsigned idx1 = (c & 0x0003F000) >> 12;
const unsigned idx2 = (c & 0x00000FC0) >> 6;
const unsigned idx3 = c & 0x0000003F;
WINPR_ASSERT(idx0 < alphabetCount);
WINPR_ASSERT(idx1 < alphabetCount);
WINPR_ASSERT(idx2 < alphabetCount);
WINPR_ASSERT(idx3 < alphabetCount);
*p++ = alphabet[idx0];
*p++ = alphabet[idx1];
*p++ = alphabet[idx2];
*p++ = alphabet[idx3];
outCounter += 4;
if (crLf && (outCounter % lineSize == 0))
{
*p++ = '\r';
*p++ = '\n';
}
}
/* then remainder */
switch (length % 3)
{
case 0:
break;
case 1:
{
const unsigned c = ((unsigned)q[0] << 16);
const unsigned idx0 = (c & 0x00FC0000) >> 18;
const unsigned idx1 = (c & 0x0003F000) >> 12;
WINPR_ASSERT(idx0 < alphabetCount);
WINPR_ASSERT(idx1 < alphabetCount);
*p++ = alphabet[idx0];
*p++ = alphabet[idx1];
if (pad)
{
*p++ = '=';
*p++ = '=';
}
}
break;
case 2:
{
const unsigned c = ((unsigned)q[0] << 16) + ((unsigned)q[1] << 8);
const unsigned idx0 = (c & 0x00FC0000) >> 18;
const unsigned idx1 = (c & 0x0003F000) >> 12;
const unsigned idx2 = (c & 0x00000FC0) >> 6;
WINPR_ASSERT(idx0 < alphabetCount);
WINPR_ASSERT(idx1 < alphabetCount);
WINPR_ASSERT(idx2 < alphabetCount);
*p++ = alphabet[idx0];
*p++ = alphabet[idx1];
*p++ = alphabet[idx2];
if (pad)
*p++ = '=';
}
break;
default:
break;
}
if (crLf && length % 3)
{
*p++ = '\r';
*p++ = '\n';
}
*p = 0;
return ret;
}
static inline char* base64_encode(const BYTE* WINPR_RESTRICT alphabet, size_t alphabetCount,
const BYTE* WINPR_RESTRICT data, size_t length, BOOL pad)
{
return base64_encode_ex(alphabet, alphabetCount, data, length, pad, FALSE, 64);
}
static inline int base64_decode_char(const signed char* WINPR_RESTRICT alphabet,
size_t alphabetCount, char c)
{
/* ensure char is signed for this check */
const int ic = (int)c;
if ((ic <= 0) || ((size_t)ic >= alphabetCount))
return -1;
return alphabet[(size_t)c];
}
static inline void* base64_decode(const signed char* WINPR_RESTRICT alphabet, size_t alphabetCount,
const char* WINPR_RESTRICT s, size_t length,
size_t* WINPR_RESTRICT data_len, BOOL pad)
{
int n[4] = WINPR_C_ARRAY_INIT;
BYTE* data = nullptr;
const size_t remainder = length % 4;
if ((pad && remainder > 0) || (remainder == 1))
return nullptr;
if (!pad && remainder)
length += 4 - remainder;
BYTE* q = data = (BYTE*)malloc(length / 4 * 3 + 1);
if (!q)
return nullptr;
/* first treat complete blocks */
const size_t nBlocks = (length / 4);
size_t outputLen = 0;
if (nBlocks < 1)
{
free(data);
return nullptr;
}
for (size_t i = 0; i < nBlocks - 1; i++, q += 3)
{
n[0] = base64_decode_char(alphabet, alphabetCount, *s++);
n[1] = base64_decode_char(alphabet, alphabetCount, *s++);
n[2] = base64_decode_char(alphabet, alphabetCount, *s++);
n[3] = base64_decode_char(alphabet, alphabetCount, *s++);
if ((n[0] == -1) || (n[1] == -1) || (n[2] == -1) || (n[3] == -1))
goto out_free;
q[0] = (BYTE)((n[0] << 2) + (n[1] >> 4));
q[1] = (BYTE)(((n[1] & 15) << 4) + (n[2] >> 2));
q[2] = (BYTE)(((n[2] & 3) << 6) + n[3]);
outputLen += 3;
}
/* treat last block */
n[0] = base64_decode_char(alphabet, alphabetCount, *s++);
n[1] = base64_decode_char(alphabet, alphabetCount, *s++);
if ((n[0] == -1) || (n[1] == -1))
goto out_free;
n[2] = remainder == 2 ? -1 : base64_decode_char(alphabet, alphabetCount, *s++);
n[3] = remainder >= 2 ? -1 : base64_decode_char(alphabet, alphabetCount, *s++);
q[0] = (BYTE)((n[0] << 2) + (n[1] >> 4));
if (n[2] == -1)
{
/* XX== */
outputLen += 1;
if (n[3] != -1)
goto out_free;
q[1] = (BYTE)((n[1] & 15) << 4);
}
else if (n[3] == -1)
{
/* yyy= */
outputLen += 2;
q[1] = (BYTE)(((n[1] & 15) << 4) + (n[2] >> 2));
q[2] = (BYTE)((n[2] & 3) << 6);
}
else
{
/* XXXX */
outputLen += 3;
q[0] = (BYTE)((n[0] << 2) + (n[1] >> 4));
q[1] = (BYTE)(((n[1] & 15) << 4) + (n[2] >> 2));
q[2] = (BYTE)(((n[2] & 3) << 6) + n[3]);
}
if (data_len)
*data_len = outputLen;
data[outputLen] = '\0';
return data;
out_free:
free(data);
return nullptr;
}
char* crypto_base64_encode_ex(const BYTE* WINPR_RESTRICT data, size_t length, BOOL withCrLf)
{
return base64_encode_ex(enc_base64, ARRAYSIZE(enc_base64), data, length, TRUE, withCrLf, 64);
}
char* crypto_base64_encode(const BYTE* WINPR_RESTRICT data, size_t length)
{
return base64_encode(enc_base64, ARRAYSIZE(enc_base64), data, length, TRUE);
}
void crypto_base64_decode(const char* WINPR_RESTRICT enc_data, size_t length,
BYTE** WINPR_RESTRICT dec_data, size_t* WINPR_RESTRICT res_length)
{
*dec_data =
base64_decode(dec_base64, ARRAYSIZE(dec_base64), enc_data, length, res_length, TRUE);
}
char* crypto_base64url_encode(const BYTE* WINPR_RESTRICT data, size_t length)
{
return base64_encode(enc_base64url, ARRAYSIZE(enc_base64url), data, length, FALSE);
}
void crypto_base64url_decode(const char* WINPR_RESTRICT enc_data, size_t length,
BYTE** WINPR_RESTRICT dec_data, size_t* WINPR_RESTRICT res_length)
{
*dec_data =
base64_decode(dec_base64url, ARRAYSIZE(dec_base64url), enc_data, length, res_length, FALSE);
}

View File

@@ -0,0 +1,745 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* ASN.1 Basic Encoding Rules (BER)
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <winpr/assert.h>
#include <winpr/cast.h>
#include <winpr/crt.h>
#include <winpr/string.h>
#include <freerdp/log.h>
#include <freerdp/crypto/ber.h>
#define TAG FREERDP_TAG("crypto")
BOOL ber_read_length(wStream* s, size_t* length)
{
BYTE byte = 0;
WINPR_ASSERT(s);
WINPR_ASSERT(length);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte & 0x80)
{
byte &= ~(0x80);
if (!Stream_CheckAndLogRequiredLength(TAG, s, byte))
return FALSE;
if (byte == 1)
Stream_Read_UINT8(s, *length);
else if (byte == 2)
Stream_Read_UINT16_BE(s, *length);
else
{
WLog_ERR(TAG, "ber: unexpected byte 0x%02" PRIx8 ", expected [1,2]", byte);
return FALSE;
}
}
else
{
*length = byte;
}
return TRUE;
}
/**
* Write BER length.
* @param s stream
* @param length length
*/
size_t ber_write_length(wStream* s, size_t length)
{
WINPR_ASSERT(s);
if (length > 0xFF)
{
WINPR_ASSERT(length <= UINT16_MAX);
WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 3);
Stream_Write_UINT8(s, 0x80 ^ 2);
Stream_Write_UINT16_BE(s, (UINT16)length);
return 3;
}
WINPR_ASSERT(length <= UINT8_MAX);
if (length > 0x7F)
{
WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 2);
Stream_Write_UINT8(s, 0x80 ^ 1);
Stream_Write_UINT8(s, (UINT8)length);
return 2;
}
WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
Stream_Write_UINT8(s, (UINT8)length);
return 1;
}
size_t _ber_sizeof_length(size_t length)
{
if (length > 0xFF)
return 3;
if (length > 0x7F)
return 2;
return 1;
}
/**
* Read BER Universal tag.
*
* @param s The stream to read from
* @param tag BER universally-defined tag
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL ber_read_universal_tag(wStream* s, BYTE tag, BOOL pc)
{
BYTE byte = 0;
const BYTE expect = (BER_CLASS_UNIV | BER_PC(pc) | (BER_TAG_MASK & tag));
WINPR_ASSERT(s);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte != expect)
{
WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
return FALSE;
}
return TRUE;
}
/**
* Write BER Universal tag.
* @param s stream
* @param tag BER universally-defined tag
* @param pc primitive (FALSE) or constructed (TRUE)
*/
size_t ber_write_universal_tag(wStream* s, BYTE tag, BOOL pc)
{
WINPR_ASSERT(s);
Stream_Write_UINT8(s, (BER_CLASS_UNIV | BER_PC(pc)) | (BER_TAG_MASK & tag));
return 1;
}
/**
* Read BER Application tag.
* @param s stream
* @param tag BER application-defined tag
* @param length length
*/
BOOL ber_read_application_tag(wStream* s, BYTE tag, size_t* length)
{
BYTE byte = 0;
WINPR_ASSERT(s);
WINPR_ASSERT(length);
if (tag > 30)
{
const BYTE expect = ((BER_CLASS_APPL | BER_CONSTRUCT) | BER_TAG_MASK);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte != expect)
{
WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
return FALSE;
}
Stream_Read_UINT8(s, byte);
if (byte != tag)
{
WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, tag);
return FALSE;
}
return ber_read_length(s, length);
}
else
{
const BYTE expect = ((BER_CLASS_APPL | BER_CONSTRUCT) | (BER_TAG_MASK & tag));
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte != expect)
{
WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
return FALSE;
}
return ber_read_length(s, length);
}
return TRUE;
}
/**
* Write BER Application tag.
* @param s stream
* @param tag BER application-defined tag
* @param length length
*/
void ber_write_application_tag(wStream* s, BYTE tag, size_t length)
{
WINPR_ASSERT(s);
if (tag > 30)
{
WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 2);
Stream_Write_UINT8(s, (BER_CLASS_APPL | BER_CONSTRUCT) | BER_TAG_MASK);
Stream_Write_UINT8(s, tag);
ber_write_length(s, length);
}
else
{
WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
Stream_Write_UINT8(s, (BER_CLASS_APPL | BER_CONSTRUCT) | (BER_TAG_MASK & tag));
ber_write_length(s, length);
}
}
BOOL ber_read_contextual_tag(wStream* s, BYTE tag, size_t* length, BOOL pc)
{
const BYTE expect = ((BER_CLASS_CTXT | BER_PC(pc)) | (BER_TAG_MASK & tag));
BYTE byte = 0;
WINPR_ASSERT(s);
WINPR_ASSERT(length);
if (Stream_GetRemainingLength(s) < 1)
{
WLog_VRB(TAG, "short data, got %" PRIuz ", expected %u", Stream_GetRemainingLength(s), 1u);
return FALSE;
}
Stream_Read_UINT8(s, byte);
if (byte != expect)
{
WLog_VRB(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
Stream_Rewind(s, 1);
return FALSE;
}
return ber_read_length(s, length);
}
size_t ber_write_contextual_tag(wStream* s, BYTE tag, size_t length, BOOL pc)
{
WINPR_ASSERT(s);
WINPR_ASSERT(Stream_GetRemainingCapacity(s) >= 1);
Stream_Write_UINT8(s, (BER_CLASS_CTXT | BER_PC(pc)) | (BER_TAG_MASK & tag));
return 1 + ber_write_length(s, length);
}
size_t ber_sizeof_contextual_tag(size_t length)
{
return 1 + _ber_sizeof_length(length);
}
BOOL ber_read_sequence_tag(wStream* s, size_t* length)
{
const BYTE expect = ((BER_CLASS_UNIV | BER_CONSTRUCT) | (BER_TAG_SEQUENCE_OF));
BYTE byte = 0;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte != expect)
{
WLog_WARN(TAG, "invalid tag, got 0x%02" PRIx8 ", expected 0x%02" PRIx8, byte, expect);
return FALSE;
}
return ber_read_length(s, length);
}
/**
* Write BER SEQUENCE tag.
* @param s stream
* @param length length
*/
size_t ber_write_sequence_tag(wStream* s, size_t length)
{
Stream_Write_UINT8(s, (BER_CLASS_UNIV | BER_CONSTRUCT) | (BER_TAG_MASK & BER_TAG_SEQUENCE));
return 1 + ber_write_length(s, length);
}
size_t ber_sizeof_sequence(size_t length)
{
return 1 + _ber_sizeof_length(length) + length;
}
size_t ber_sizeof_sequence_tag(size_t length)
{
return 1 + _ber_sizeof_length(length);
}
BOOL ber_read_enumerated(wStream* s, BYTE* enumerated, BYTE count)
{
size_t length = 0;
WINPR_ASSERT(enumerated);
if (!ber_read_universal_tag(s, BER_TAG_ENUMERATED, FALSE) || !ber_read_length(s, &length))
return FALSE;
if (length != 1)
{
WLog_WARN(TAG, "short data, got %" PRIuz ", expected %u", length, 1u);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, *enumerated);
/* check that enumerated value falls within expected range */
if (*enumerated + 1 > count)
{
WLog_WARN(TAG, "invalid data, expected %" PRIu8 " < %" PRIu8, *enumerated, count);
return FALSE;
}
return TRUE;
}
void ber_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count)
{
ber_write_universal_tag(s, BER_TAG_ENUMERATED, FALSE);
ber_write_length(s, 1);
Stream_Write_UINT8(s, enumerated);
}
BOOL ber_read_bit_string(wStream* s, size_t* length, BYTE* padding)
{
if (!ber_read_universal_tag(s, BER_TAG_BIT_STRING, FALSE) || !ber_read_length(s, length))
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, *padding);
return TRUE;
}
/**
* Write a BER OCTET_STRING
* @param s stream
* @param oct_str octet string
* @param length string length
*/
size_t ber_write_octet_string(wStream* s, const BYTE* oct_str, size_t length)
{
size_t size = 0;
WINPR_ASSERT(oct_str || (length == 0));
size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
size += ber_write_length(s, length);
Stream_Write(s, oct_str, length);
size += length;
return size;
}
size_t ber_write_contextual_octet_string(wStream* s, BYTE tag, const BYTE* oct_str, size_t length)
{
size_t inner = ber_sizeof_octet_string(length);
size_t ret = 0;
size_t r = 0;
ret = ber_write_contextual_tag(s, tag, inner, TRUE);
if (!ret)
return 0;
r = ber_write_octet_string(s, oct_str, length);
if (!r)
return 0;
return ret + r;
}
size_t ber_write_char_to_unicode_octet_string(wStream* s, const char* str)
{
WINPR_ASSERT(str);
size_t size = 0;
size_t length = strlen(str) + 1;
size += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
size += ber_write_length(s, length * sizeof(WCHAR));
if (Stream_Write_UTF16_String_From_UTF8(s, length, str, length, TRUE) < 0)
return 0;
return size + length * sizeof(WCHAR);
}
size_t ber_write_contextual_unicode_octet_string(wStream* s, BYTE tag, LPWSTR str)
{
WINPR_ASSERT(str);
size_t len = _wcslen(str) * sizeof(WCHAR);
size_t inner_len = ber_sizeof_octet_string(len);
size_t ret = 0;
ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
return ret + ber_write_octet_string(s, (const BYTE*)str, len);
}
size_t ber_write_contextual_char_to_unicode_octet_string(wStream* s, BYTE tag, const char* str)
{
size_t ret = 0;
size_t len = strlen(str);
size_t inner_len = ber_sizeof_octet_string(len * 2);
WINPR_ASSERT(Stream_GetRemainingCapacity(s) < ber_sizeof_contextual_tag(inner_len) + inner_len);
ret = ber_write_contextual_tag(s, tag, inner_len, TRUE);
ret += ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE);
ret += ber_write_length(s, len * sizeof(WCHAR));
if (Stream_Write_UTF16_String_From_UTF8(s, len, str, len, TRUE) < 0)
return 0;
return ret + len;
}
BOOL ber_read_unicode_octet_string(wStream* s, LPWSTR* str)
{
LPWSTR ret = nullptr;
size_t length = 0;
if (!ber_read_octet_string_tag(s, &length))
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
ret = calloc(1, length + 2);
if (!ret)
return FALSE;
memcpy(ret, Stream_ConstPointer(s), length);
ret[length / 2] = 0;
Stream_Seek(s, length);
*str = ret;
return TRUE;
}
BOOL ber_read_char_from_unicode_octet_string(wStream* s, char** str)
{
size_t length = 0;
char* ptr = nullptr;
*str = nullptr;
if (!ber_read_octet_string_tag(s, &length))
return FALSE;
ptr = Stream_Read_UTF16_String_As_UTF8(s, length / sizeof(WCHAR), nullptr);
if (!ptr)
return FALSE;
*str = ptr;
return TRUE;
}
BOOL ber_read_octet_string_tag(wStream* s, size_t* length)
{
return ber_read_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) && ber_read_length(s, length);
}
BOOL ber_read_octet_string(wStream* s, BYTE** content, size_t* length)
{
BYTE* ret = nullptr;
WINPR_ASSERT(s);
WINPR_ASSERT(content);
WINPR_ASSERT(length);
if (!ber_read_octet_string_tag(s, length))
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, *length))
return FALSE;
ret = malloc(*length);
if (!ret)
return FALSE;
Stream_Read(s, ret, *length);
*content = ret;
return TRUE;
}
size_t ber_write_octet_string_tag(wStream* s, size_t length)
{
if (ber_write_universal_tag(s, BER_TAG_OCTET_STRING, FALSE) == 0)
return 0;
if (ber_write_length(s, length) == 0)
return 0;
return 1 + _ber_sizeof_length(length);
}
size_t ber_sizeof_octet_string(size_t length)
{
return 1 + _ber_sizeof_length(length) + length;
}
size_t ber_sizeof_contextual_octet_string(size_t length)
{
size_t ret = ber_sizeof_octet_string(length);
return ber_sizeof_contextual_tag(ret) + ret;
}
/** \brief Read a BER BOOLEAN
*
* @param s The stream to read from.
* @param value A pointer to the value read, must not be nullptr
*
* \return \b TRUE for success, \b FALSE for any failure
*/
BOOL ber_read_BOOL(wStream* s, BOOL* value)
{
size_t length = 0;
BYTE v = 0;
WINPR_ASSERT(value);
if (!ber_read_universal_tag(s, BER_TAG_BOOLEAN, FALSE) || !ber_read_length(s, &length))
return FALSE;
if (length != 1)
{
WLog_WARN(TAG, "short data, got %" PRIuz ", expected %u", length, 1u);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, v);
*value = (v != 0);
return TRUE;
}
/**
* Write a BER BOOLEAN
*
* @param s A pointer to the stream to write to
* @param value The value to write
*/
void ber_write_BOOL(wStream* s, BOOL value)
{
ber_write_universal_tag(s, BER_TAG_BOOLEAN, FALSE);
ber_write_length(s, 1);
Stream_Write_UINT8(s, (value == TRUE) ? 0xFF : 0);
}
BOOL ber_read_integer(wStream* s, UINT32* value)
{
size_t length = 0;
WINPR_ASSERT(s);
if (!ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE))
return FALSE;
if (!ber_read_length(s, &length))
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
if (value == nullptr)
{
// even if we don't care the integer value, check the announced size
return Stream_SafeSeek(s, length);
}
if (length == 1)
{
Stream_Read_UINT8(s, *value);
}
else if (length == 2)
{
Stream_Read_UINT16_BE(s, *value);
}
else if (length == 3)
{
BYTE byte = 0;
Stream_Read_UINT8(s, byte);
Stream_Read_UINT16_BE(s, *value);
*value += (byte << 16) & 0xFF0000;
}
else if (length == 4)
{
Stream_Read_UINT32_BE(s, *value);
}
else if (length == 8)
{
WLog_ERR(TAG, "should implement reading an 8 bytes integer");
return FALSE;
}
else
{
WLog_ERR(TAG, "should implement reading an integer with length=%" PRIuz, length);
return FALSE;
}
return TRUE;
}
/**
* Write a BER INTEGER
*
* @param s A pointer to the stream to write to
* @param value The value to write
*
* @return The size in bytes that were written
*/
size_t ber_write_integer(wStream* s, UINT32 value)
{
WINPR_ASSERT(s);
if (value < 0x80)
{
if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
return 0;
if (ber_write_length(s, 1) == 0)
return 0;
Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value));
return 3;
}
else if (value < 0x8000)
{
if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
return 0;
if (ber_write_length(s, 2) == 0)
return 0;
Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
return 4;
}
else if (value < 0x800000)
{
if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
return 0;
if (ber_write_length(s, 3) == 0)
return 0;
Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, value >> 16));
Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, value));
return 5;
}
else if (value < 0x80000000)
{
if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
return 0;
if (ber_write_length(s, 4) == 0)
return 0;
Stream_Write_UINT32_BE(s, value);
return 6;
}
else
{
/* treat as signed integer i.e. NT/HRESULT error codes */
if (ber_write_universal_tag(s, BER_TAG_INTEGER, FALSE) == 0)
return 0;
if (ber_write_length(s, 4) == 0)
return 0;
Stream_Write_UINT32_BE(s, value);
return 6;
}
}
size_t ber_write_contextual_integer(wStream* s, BYTE tag, UINT32 value)
{
size_t len = ber_sizeof_integer(value);
WINPR_ASSERT(s);
WINPR_ASSERT(Stream_EnsureRemainingCapacity(s, len + 5));
len += ber_write_contextual_tag(s, tag, len, TRUE);
if (ber_write_integer(s, value) == 0)
return 0;
return len;
}
size_t ber_sizeof_integer(UINT32 value)
{
if (value < 0x80)
{
return 3;
}
else if (value < 0x8000)
{
return 4;
}
else if (value < 0x800000)
{
return 5;
}
else if (value < 0x80000000)
{
return 6;
}
else
{
/* treat as signed integer i.e. NT/HRESULT error codes */
return 6;
}
}
size_t ber_sizeof_contextual_integer(UINT32 value)
{
size_t intSize = ber_sizeof_integer(value);
return ber_sizeof_contextual_tag(intSize) + intSize;
}
BOOL ber_read_integer_length(wStream* s, size_t* length)
{
return ber_read_universal_tag(s, BER_TAG_INTEGER, FALSE) && ber_read_length(s, length);
}

View File

@@ -0,0 +1,245 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate Handling
*
* Copyright 2011 Jiten Pathy
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <freerdp/config.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <winpr/assert.h>
#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/crypto.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include "cert_common.h"
#include "crypto.h"
#include "opensslcompat.h"
#define TAG FREERDP_TAG("core")
static BOOL cert_info_allocate(rdpCertInfo* info, size_t size);
BOOL read_bignum(BYTE** dst, DWORD* length, const BIGNUM* num, BOOL alloc)
{
WINPR_ASSERT(dst);
WINPR_ASSERT(length);
WINPR_ASSERT(num);
if (alloc)
{
free(*dst);
*dst = nullptr;
*length = 0;
}
const int len = BN_num_bytes(num);
if (len < 0)
return FALSE;
if (!alloc)
{
if (*length < (UINT32)len)
return FALSE;
}
if (len > 0)
{
if (alloc)
{
*dst = malloc((size_t)len);
if (!*dst)
return FALSE;
}
BN_bn2bin(num, *dst);
crypto_reverse(*dst, (size_t)len);
*length = (UINT32)len;
}
return TRUE;
}
BOOL cert_info_create(rdpCertInfo* dst, const BIGNUM* rsa, const BIGNUM* rsa_e)
{
const rdpCertInfo empty = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(dst);
WINPR_ASSERT(rsa);
*dst = empty;
if (!read_bignum(&dst->Modulus, &dst->ModulusLength, rsa, TRUE))
goto fail;
{
DWORD len = sizeof(dst->exponent);
BYTE* ptr = &dst->exponent[0];
if (!read_bignum(&ptr, &len, rsa_e, FALSE))
goto fail;
}
return TRUE;
fail:
cert_info_free(dst);
return FALSE;
}
BOOL cert_info_clone(rdpCertInfo* dst, const rdpCertInfo* src)
{
WINPR_ASSERT(dst);
WINPR_ASSERT(src);
*dst = *src;
dst->Modulus = nullptr;
dst->ModulusLength = 0;
if (src->ModulusLength > 0)
{
dst->Modulus = malloc(src->ModulusLength);
if (!dst->Modulus)
return FALSE;
memcpy(dst->Modulus, src->Modulus, src->ModulusLength);
dst->ModulusLength = src->ModulusLength;
}
return TRUE;
}
void cert_info_free(rdpCertInfo* info)
{
WINPR_ASSERT(info);
free(info->Modulus);
info->ModulusLength = 0;
info->Modulus = nullptr;
}
BOOL cert_info_allocate(rdpCertInfo* info, size_t size)
{
WINPR_ASSERT(info);
cert_info_free(info);
info->Modulus = (BYTE*)malloc(size);
if (!info->Modulus && (size > 0))
{
WLog_ERR(TAG, "Failed to allocate info->Modulus of size %" PRIuz, size);
return FALSE;
}
info->ModulusLength = (UINT32)size;
return TRUE;
}
BOOL cert_info_read_modulus(rdpCertInfo* info, size_t size, wStream* s)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
return FALSE;
if (size > UINT32_MAX)
{
WLog_ERR(TAG, "modulus size %" PRIuz " exceeds limit of %" PRIu32, size, UINT32_MAX);
return FALSE;
}
if (!cert_info_allocate(info, size))
return FALSE;
Stream_Read(s, info->Modulus, info->ModulusLength);
return TRUE;
}
BOOL cert_info_read_exponent(rdpCertInfo* info, size_t size, wStream* s)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
return FALSE;
if (size > 4)
{
WLog_ERR(TAG, "exponent size %" PRIuz " exceeds limit of %" PRIu32, size, 4u);
return FALSE;
}
if (!info->Modulus || (info->ModulusLength == 0))
{
WLog_ERR(TAG, "invalid modulus=%p [%" PRIu32 "]",
WINPR_CXX_COMPAT_CAST(const void*, info->Modulus), info->ModulusLength);
return FALSE;
}
Stream_Read(s, &info->exponent[4 - size], size);
crypto_reverse(info->Modulus, info->ModulusLength);
crypto_reverse(info->exponent, 4);
return TRUE;
}
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
X509* x509_from_rsa(const RSA* rsa)
{
EVP_PKEY* pubkey = nullptr;
X509* x509 = nullptr;
BIO* bio = BIO_new(
#if defined(LIBRESSL_VERSION_NUMBER)
BIO_s_mem()
#else
BIO_s_secmem()
#endif
);
if (!bio)
{
WLog_ERR(TAG, "BIO_new() failed");
return nullptr;
}
const int rc = PEM_write_bio_RSA_PUBKEY(bio, (RSA*)rsa);
if (rc != 1)
{
WLog_ERR(TAG, "PEM_write_bio_RSA_PUBKEY(bio, (RSA*)rsa) failed");
goto fail;
}
pubkey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
if (!pubkey)
{
WLog_ERR(TAG, "PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr) failed");
goto fail;
}
x509 = X509_new();
if (!x509)
{
WLog_ERR(TAG, "X509_new() failed");
goto fail;
}
const int res = X509_set_pubkey(x509, pubkey);
if (res != 1)
{
WLog_ERR(TAG, "X509_set_pubkey(x509, pubkey) failed");
X509_free(x509);
x509 = nullptr;
goto fail;
}
fail:
BIO_free_all(bio);
EVP_PKEY_free(pubkey);
return x509;
}
#endif

View File

@@ -0,0 +1,64 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate and private key helpers
*
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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_LIB_CORE_CERT_COMMON_H
#define FREERDP_LIB_CORE_CERT_COMMON_H
#include <freerdp/crypto/ber.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/settings.h>
#include <freerdp/log.h>
#include <freerdp/api.h>
#include "opensslcompat.h"
#ifdef __cplusplus
extern "C"
{
#endif
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL cert_info_create(rdpCertInfo* dst, const BIGNUM* rsa, const BIGNUM* rsa_e);
FREERDP_LOCAL void cert_info_free(rdpCertInfo* info);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL cert_info_clone(rdpCertInfo* dst, const rdpCertInfo* src);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL cert_info_read_modulus(rdpCertInfo* info, size_t size, wStream* s);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL cert_info_read_exponent(rdpCertInfo* info, size_t size, wStream* s);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL read_bignum(BYTE** dst, DWORD* length, const BIGNUM* num, BOOL alloc);
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL X509* x509_from_rsa(const RSA* rsa);
#endif
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_CORE_CERT_COMMON_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,80 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate Handling
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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_LIB_CORE_CERTIFICATE_H
#define FREERDP_LIB_CORE_CERTIFICATE_H
#include <freerdp/crypto/crypto.h>
#include <freerdp/crypto/certificate.h>
#include <openssl/x509.h>
/* Certificate Version */
#define CERT_CHAIN_VERSION_1 0x00000001
#define CERT_CHAIN_VERSION_2 0x00000002
#define CERT_CHAIN_VERSION_MASK 0x7FFFFFFF
#define CERT_PERMANENTLY_ISSUED 0x00000000
#define CERT_TEMPORARILY_ISSUED 0x80000000
#define SIGNATURE_ALG_RSA 0x00000001
#define KEY_EXCHANGE_ALG_RSA 0x00000001
#define BB_RSA_KEY_BLOB 6
#define BB_RSA_SIGNATURE_BLOB 8
WINPR_ATTR_MALLOC(freerdp_certificate_free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpCertificate* freerdp_certificate_new_from_x509(const X509* xcert,
const STACK_OF(X509) * chain);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL freerdp_certificate_read_server_cert(rdpCertificate* certificate,
const BYTE* server_cert, size_t length);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL SSIZE_T freerdp_certificate_write_server_cert(const rdpCertificate* certificate,
UINT32 dwVersion, wStream* s);
WINPR_ATTR_MALLOC(freerdp_certificate_free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpCertificate* freerdp_certificate_clone(const rdpCertificate* certificate);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL const rdpCertInfo* freerdp_certificate_get_info(const rdpCertificate* certificate);
/** \brief returns a pointer to a X509 structure.
* Call X509_free when done.
*/
WINPR_ATTR_NODISCARD
FREERDP_LOCAL X509* freerdp_certificate_get_x509(rdpCertificate* certificate);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL size_t freerdp_certificate_get_chain_len(rdpCertificate* certificate);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL X509* freerdp_certificate_get_chain_at(rdpCertificate* certificate, size_t offset);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL freerdp_certificate_publickey_encrypt(const rdpCertificate* cert,
const BYTE* input, size_t cbInput,
BYTE** poutput, size_t* pcbOutput);
#endif /* FREERDP_LIB_CORE_CERTIFICATE_H */

View File

@@ -0,0 +1,311 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate Handling
*
* Copyright 2011 Jiten Pathy
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <ctype.h>
#include <freerdp/config.h>
#include <winpr/assert.h>
#include <winpr/path.h>
#include <freerdp/settings.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/crypto/certificate_data.h>
#include "certificate.h"
#include <freerdp/log.h>
#define TAG FREERDP_TAG("crypto.certificate_data")
struct rdp_certificate_data
{
char* hostname;
UINT16 port;
rdpCertificate* cert;
char cached_hash[MAX_PATH + 10];
char* cached_subject;
char* cached_issuer;
char* cached_fingerprint;
char* cached_pem;
char* cached_pem_chain;
};
/* ensure our hostnames (and therefore filenames) always use the same capitalization.
* the user might have input random case, but we always need to have a sane
* baseline to compare against. */
static char* ensure_lowercase(char* str, size_t length)
{
const size_t len = strnlen(str, length);
for (size_t x = 0; x < len; x++)
str[x] = (char)tolower(str[x]);
return str;
}
static char* ensure_valid_charset(char* str, size_t length)
{
const size_t len = strnlen(str, length);
for (size_t x = 0; x < len; x++)
{
char cur = str[x];
switch (cur)
{
case ':':
str[x] = '.';
break;
case '/':
case '\\':
str[x] = '_';
break;
default:
break;
}
}
return str;
}
static const char* freerdp_certificate_data_hash_(const char* hostname, UINT16 port, char* name,
size_t length)
{
(void)_snprintf(name, length, "%s_%" PRIu16 ".pem", hostname, port);
return ensure_lowercase(ensure_valid_charset(name, length), length);
}
static BOOL freerdp_certificate_data_load_cache(rdpCertificateData* data)
{
BOOL rc = FALSE;
WINPR_ASSERT(data);
freerdp_certificate_data_hash_(data->hostname, data->port, data->cached_hash,
sizeof(data->cached_hash) - 1);
const size_t len = strnlen(data->cached_hash, sizeof(data->cached_hash));
if ((len == 0) || (len >= sizeof(data->cached_hash)))
goto fail;
data->cached_subject = freerdp_certificate_get_subject(data->cert);
if (!data->cached_subject)
data->cached_subject = calloc(1, 1);
{
size_t pemlen = 0;
data->cached_pem = freerdp_certificate_get_pem_ex(data->cert, &pemlen, FALSE);
}
if (!data->cached_pem)
goto fail;
{
size_t pemchainlen = 0;
data->cached_pem_chain = freerdp_certificate_get_pem_ex(data->cert, &pemchainlen, TRUE);
if (!data->cached_pem_chain)
goto fail;
}
data->cached_fingerprint = freerdp_certificate_get_fingerprint(data->cert);
if (!data->cached_fingerprint)
goto fail;
data->cached_issuer = freerdp_certificate_get_issuer(data->cert);
if (!data->cached_issuer)
data->cached_issuer = calloc(1, 1);
rc = TRUE;
fail:
return rc;
}
static rdpCertificateData* freerdp_certificate_data_new_nocopy(const char* hostname, UINT16 port,
rdpCertificate* xcert)
{
rdpCertificateData* certdata = nullptr;
if (!hostname || !xcert)
goto fail;
if (strnlen(hostname, MAX_PATH) >= MAX_PATH)
{
WLog_ERR(TAG, "hostname exceeds length limits");
goto fail;
}
certdata = (rdpCertificateData*)calloc(1, sizeof(rdpCertificateData));
if (!certdata)
goto fail;
certdata->port = port;
certdata->hostname = _strdup(hostname);
if (!certdata->hostname)
goto fail;
ensure_lowercase(certdata->hostname, strlen(certdata->hostname));
certdata->cert = xcert;
if (!freerdp_certificate_data_load_cache(certdata))
{
certdata->cert = nullptr;
goto fail;
}
return certdata;
fail:
freerdp_certificate_data_free(certdata);
return nullptr;
}
rdpCertificateData* freerdp_certificate_data_new(const char* hostname, UINT16 port,
const rdpCertificate* xcert)
{
rdpCertificate* copy = freerdp_certificate_clone(xcert);
rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, copy);
if (!data)
freerdp_certificate_free(copy);
return data;
}
rdpCertificateData* freerdp_certificate_data_new_from_pem(const char* hostname, UINT16 port,
const char* pem, size_t length)
{
if (!pem || (length == 0))
return nullptr;
rdpCertificate* cert = freerdp_certificate_new_from_pem(pem);
rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
if (!data)
freerdp_certificate_free(cert);
return data;
}
rdpCertificateData* freerdp_certificate_data_new_from_file(const char* hostname, UINT16 port,
const char* file)
{
if (!file)
return nullptr;
rdpCertificate* cert = freerdp_certificate_new_from_file(file);
rdpCertificateData* data = freerdp_certificate_data_new_nocopy(hostname, port, cert);
if (!data)
freerdp_certificate_free(cert);
return data;
}
void freerdp_certificate_data_free(rdpCertificateData* data)
{
if (data == nullptr)
return;
free(data->hostname);
freerdp_certificate_free(data->cert);
free(data->cached_subject);
free(data->cached_issuer);
free(data->cached_fingerprint);
free(data->cached_pem);
free(data->cached_pem_chain);
free(data);
}
const char* freerdp_certificate_data_get_host(const rdpCertificateData* cert)
{
if (!cert)
return nullptr;
return cert->hostname;
}
UINT16 freerdp_certificate_data_get_port(const rdpCertificateData* cert)
{
if (!cert)
return 0;
return cert->port;
}
const char* freerdp_certificate_data_get_pem(const rdpCertificateData* cert)
{
return freerdp_certificate_data_get_pem_ex(cert, TRUE);
}
const char* freerdp_certificate_data_get_pem_ex(const rdpCertificateData* cert, BOOL withFullChain)
{
if (!cert)
return nullptr;
if (withFullChain)
return cert->cached_pem_chain;
return cert->cached_pem;
}
const char* freerdp_certificate_data_get_subject(const rdpCertificateData* cert)
{
if (!cert)
return nullptr;
return cert->cached_subject;
}
const char* freerdp_certificate_data_get_issuer(const rdpCertificateData* cert)
{
if (!cert)
return nullptr;
return cert->cached_issuer;
}
const char* freerdp_certificate_data_get_fingerprint(const rdpCertificateData* cert)
{
if (!cert)
return nullptr;
return cert->cached_fingerprint;
}
BOOL freerdp_certificate_data_equal(const rdpCertificateData* a, const rdpCertificateData* b)
{
BOOL rc = FALSE;
WINPR_ASSERT(a);
WINPR_ASSERT(b);
if (strcmp(a->hostname, b->hostname) != 0)
return FALSE;
if (a->port != b->port)
return FALSE;
const char* pem1 = freerdp_certificate_data_get_fingerprint(a);
const char* pem2 = freerdp_certificate_data_get_fingerprint(b);
if (pem1 && pem2)
rc = strcmp(pem1, pem2) == 0;
else
rc = pem1 == pem2;
return rc;
}
const char* freerdp_certificate_data_get_hash(const rdpCertificateData* cert)
{
if (!cert)
return nullptr;
return cert->cached_hash;
}
char* freerdp_certificate_data_hash(const char* hostname, UINT16 port)
{
char name[MAX_PATH + 10] = WINPR_C_ARRAY_INIT;
freerdp_certificate_data_hash_(hostname, port, name, sizeof(name));
return strndup(name, sizeof(name));
}

View File

@@ -0,0 +1,206 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Certificate Handling
*
* Copyright 2011 Jiten Pathy
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/assert.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <winpr/crypto.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/path.h>
#include <freerdp/settings.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/crypto/certificate_store.h>
#include <freerdp/log.h>
struct rdp_certificate_store
{
char* certs_path;
char* server_path;
};
static const char certificate_store_dir[] = "certs";
static const char certificate_server_dir[] = "server";
static char* freerdp_certificate_store_file_path(const rdpCertificateStore* store, const char* hash)
{
const char* hosts = freerdp_certificate_store_get_hosts_path(store);
if (!hosts || !hash)
return nullptr;
return GetCombinedPath(hosts, hash);
}
freerdp_certificate_store_result
freerdp_certificate_store_contains_data(rdpCertificateStore* store, const rdpCertificateData* data)
{
freerdp_certificate_store_result rc = CERT_STORE_NOT_FOUND;
const char* host = freerdp_certificate_data_get_host(data);
const UINT16 port = freerdp_certificate_data_get_port(data);
rdpCertificateData* loaded = freerdp_certificate_store_load_data(store, host, port);
if (!loaded)
goto fail;
rc = freerdp_certificate_data_equal(data, loaded) ? CERT_STORE_MATCH : CERT_STORE_MISMATCH;
fail:
freerdp_certificate_data_free(loaded);
return rc;
}
BOOL freerdp_certificate_store_remove_data(rdpCertificateStore* store,
const rdpCertificateData* data)
{
BOOL rc = TRUE;
WINPR_ASSERT(store);
const char* hash = freerdp_certificate_data_get_hash(data);
if (!hash)
return FALSE;
char* path = freerdp_certificate_store_file_path(store, hash);
if (!path)
return FALSE;
if (winpr_PathFileExists(path))
rc = winpr_DeleteFile(path);
free(path);
return rc;
}
BOOL freerdp_certificate_store_save_data(rdpCertificateStore* store, const rdpCertificateData* data)
{
BOOL rc = FALSE;
const char* base = freerdp_certificate_store_get_hosts_path(store);
const char* hash = freerdp_certificate_data_get_hash(data);
char* path = freerdp_certificate_store_file_path(store, hash);
FILE* fp = nullptr;
if (!winpr_PathFileExists(base))
{
if (!winpr_PathMakePath(base, nullptr))
goto fail;
}
fp = winpr_fopen(path, "w");
if (!fp)
goto fail;
(void)fprintf(fp, "%s", freerdp_certificate_data_get_pem_ex(data, FALSE));
rc = TRUE;
fail:
if (fp)
(void)fclose(fp);
free(path);
return rc;
}
rdpCertificateData* freerdp_certificate_store_load_data(rdpCertificateStore* store,
const char* host, UINT16 port)
{
char* path = nullptr;
rdpCertificateData* data = nullptr;
WINPR_ASSERT(store);
path = freerdp_certificate_store_get_cert_path(store, host, port);
if (!path)
goto fail;
data = freerdp_certificate_data_new_from_file(host, port, path);
fail:
free(path);
return data;
}
rdpCertificateStore* freerdp_certificate_store_new(const rdpSettings* settings)
{
rdpCertificateStore* store = (rdpCertificateStore*)calloc(1, sizeof(rdpCertificateStore));
if (!store)
return nullptr;
const char* base = freerdp_settings_get_string(settings, FreeRDP_ConfigPath);
if (!base)
goto fail;
store->certs_path = GetCombinedPath(base, certificate_store_dir);
store->server_path = GetCombinedPath(base, certificate_server_dir);
if (!store->certs_path || !store->server_path)
goto fail;
return store;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
freerdp_certificate_store_free(store);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void freerdp_certificate_store_free(rdpCertificateStore* store)
{
if (!store)
return;
free(store->certs_path);
free(store->server_path);
free(store);
}
const char* freerdp_certificate_store_get_certs_path(const rdpCertificateStore* store)
{
WINPR_ASSERT(store);
return store->certs_path;
}
const char* freerdp_certificate_store_get_hosts_path(const rdpCertificateStore* store)
{
WINPR_ASSERT(store);
return store->server_path;
}
char* freerdp_certificate_store_get_cert_path(const rdpCertificateStore* store, const char* host,
UINT16 port)
{
WINPR_ASSERT(store);
char* hash = freerdp_certificate_data_hash(host, port);
if (!hash)
return nullptr;
char* path = freerdp_certificate_store_file_path(store, hash);
free(hash);
return path;
}

View File

@@ -0,0 +1,272 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Cryptographic Abstraction Layer
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <errno.h>
#include <openssl/objects.h>
#include <openssl/bn.h>
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <freerdp/log.h>
#include <freerdp/crypto/crypto.h>
#include "crypto.h"
#include "privatekey.h"
#define TAG FREERDP_TAG("crypto")
static SSIZE_T crypto_rsa_common(const BYTE* input, size_t length, UINT32 key_length,
const BYTE* modulus, const BYTE* exponent, size_t exponent_size,
BYTE* output, size_t out_length)
{
BN_CTX* ctx = nullptr;
int output_length = -1;
BYTE* input_reverse = nullptr;
BYTE* modulus_reverse = nullptr;
BYTE* exponent_reverse = nullptr;
BIGNUM* mod = nullptr;
BIGNUM* exp = nullptr;
BIGNUM* x = nullptr;
BIGNUM* y = nullptr;
size_t bufferSize = 0;
if (!input || !modulus || !exponent || !output)
return -1;
if (exponent_size > INT_MAX / 2)
return -1;
if (key_length >= INT_MAX / 2 - exponent_size)
return -1;
bufferSize = 2ULL * key_length + exponent_size;
if (length > bufferSize)
bufferSize = length;
input_reverse = (BYTE*)calloc(bufferSize, 1);
if (!input_reverse)
return -1;
modulus_reverse = input_reverse + key_length;
exponent_reverse = modulus_reverse + key_length;
memcpy(modulus_reverse, modulus, key_length);
crypto_reverse(modulus_reverse, key_length);
memcpy(exponent_reverse, exponent, exponent_size);
crypto_reverse(exponent_reverse, exponent_size);
memcpy(input_reverse, input, length);
crypto_reverse(input_reverse, length);
if (!(ctx = BN_CTX_new()))
goto fail;
if (!(mod = BN_new()))
goto fail;
if (!(exp = BN_new()))
goto fail;
if (!(x = BN_new()))
goto fail;
if (!(y = BN_new()))
goto fail;
if (!BN_bin2bn(modulus_reverse, (int)key_length, mod))
goto fail;
if (!BN_bin2bn(exponent_reverse, (int)exponent_size, exp))
goto fail;
if (!BN_bin2bn(input_reverse, (int)length, x))
goto fail;
if (BN_mod_exp(y, x, exp, mod, ctx) != 1)
goto fail;
output_length = BN_bn2bin(y, output);
if (output_length < 0)
goto fail;
if (WINPR_ASSERTING_INT_CAST(size_t, output_length) > out_length)
goto fail;
crypto_reverse(output, WINPR_ASSERTING_INT_CAST(size_t, output_length));
if ((size_t)output_length < key_length)
{
size_t diff = key_length - WINPR_ASSERTING_INT_CAST(size_t, output_length);
if ((size_t)output_length + diff > out_length)
diff = out_length - (size_t)output_length;
memset(output + output_length, 0, diff);
}
fail:
BN_free(y);
BN_clear_free(x);
BN_free(exp);
BN_free(mod);
BN_CTX_free(ctx);
free(input_reverse);
return output_length;
}
static SSIZE_T crypto_rsa_public(const BYTE* input, size_t length, const rdpCertInfo* cert,
BYTE* output, size_t output_length)
{
WINPR_ASSERT(cert);
return crypto_rsa_common(input, length, cert->ModulusLength, cert->Modulus, cert->exponent,
sizeof(cert->exponent), output, output_length);
}
static SSIZE_T crypto_rsa_private(const BYTE* input, size_t length, const rdpPrivateKey* key,
BYTE* output, size_t output_length)
{
WINPR_ASSERT(key);
const rdpCertInfo* info = freerdp_key_get_info(key);
WINPR_ASSERT(info);
size_t PrivateExponentLength = 0;
const BYTE* PrivateExponent = freerdp_key_get_exponent(key, &PrivateExponentLength);
return crypto_rsa_common(input, length, info->ModulusLength, info->Modulus, PrivateExponent,
PrivateExponentLength, output, output_length);
}
SSIZE_T crypto_rsa_public_encrypt(const BYTE* input, size_t length, const rdpCertInfo* cert,
BYTE* output, size_t output_length)
{
return crypto_rsa_public(input, length, cert, output, output_length);
}
SSIZE_T crypto_rsa_public_decrypt(const BYTE* input, size_t length, const rdpCertInfo* cert,
BYTE* output, size_t output_length)
{
return crypto_rsa_public(input, length, cert, output, output_length);
}
SSIZE_T crypto_rsa_private_encrypt(const BYTE* input, size_t length, const rdpPrivateKey* key,
BYTE* output, size_t output_length)
{
return crypto_rsa_private(input, length, key, output, output_length);
}
SSIZE_T crypto_rsa_private_decrypt(const BYTE* input, size_t length, const rdpPrivateKey* key,
BYTE* output, size_t output_length)
{
return crypto_rsa_private(input, length, key, output, output_length);
}
void crypto_reverse(BYTE* data, size_t length)
{
if (length < 1)
return;
for (size_t i = 0, j = length - 1; i < j; i++, j--)
{
const BYTE temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
char* crypto_read_pem(const char* WINPR_RESTRICT filename, size_t* WINPR_RESTRICT plength)
{
char* pem = nullptr;
FILE* fp = nullptr;
WINPR_ASSERT(filename);
if (plength)
*plength = 0;
fp = winpr_fopen(filename, "r");
if (!fp)
goto fail;
{
const int rs = _fseeki64(fp, 0, SEEK_END);
if (rs < 0)
goto fail;
}
{
const int64_t size = _ftelli64(fp);
if (size < 0)
goto fail;
{
const int rc = _fseeki64(fp, 0, SEEK_SET);
if (rc < 0)
goto fail;
}
pem = calloc(WINPR_ASSERTING_INT_CAST(size_t, size) + 1, sizeof(char));
if (!pem)
goto fail;
{
const size_t fr = fread(pem, (size_t)size, 1, fp);
if (fr != 1)
goto fail;
}
if (plength)
*plength = strnlen(pem, WINPR_ASSERTING_INT_CAST(size_t, size));
}
(void)fclose(fp);
return pem;
fail:
{
char buffer[8192] = WINPR_C_ARRAY_INIT;
WLog_WARN(TAG, "Failed to read PEM from file '%s' [%s]", filename,
winpr_strerror(errno, buffer, sizeof(buffer)));
}
if (fp)
(void)fclose(fp);
free(pem);
return nullptr;
}
BOOL crypto_write_pem(const char* WINPR_RESTRICT filename, const char* WINPR_RESTRICT pem,
size_t length)
{
WINPR_ASSERT(filename);
WINPR_ASSERT(pem || (length == 0));
WINPR_ASSERT(filename);
WINPR_ASSERT(pem);
const size_t size = strnlen(pem, length) + 1;
size_t rc = 0;
FILE* fp = winpr_fopen(filename, "w");
if (!fp)
goto fail;
rc = fwrite(pem, 1, size, fp);
(void)fclose(fp);
fail:
if (rc == 0)
{
char buffer[8192] = WINPR_C_ARRAY_INIT;
WLog_WARN(TAG, "Failed to write PEM [%" PRIuz "] to file '%s' [%s]", length, filename,
winpr_strerror(errno, buffer, sizeof(buffer)));
}
return rc == size;
}

View File

@@ -0,0 +1,62 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Cryptographic Abstraction Layer
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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_LIB_CRYPTO_H
#define FREERDP_LIB_CRYPTO_H
/* OpenSSL includes windows.h */
#include <winpr/windows.h>
#include <winpr/custom-crypto.h>
#include <freerdp/api.h>
#include <freerdp/freerdp.h>
#include <freerdp/crypto/crypto.h>
#ifdef __cplusplus
extern "C"
{
#endif
WINPR_ATTR_NODISCARD
FREERDP_LOCAL SSIZE_T crypto_rsa_public_encrypt(const BYTE* input, size_t length,
const rdpCertInfo* cert, BYTE* output,
size_t output_length);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL SSIZE_T crypto_rsa_public_decrypt(const BYTE* input, size_t length,
const rdpCertInfo* cert, BYTE* output,
size_t output_length);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL SSIZE_T crypto_rsa_private_encrypt(const BYTE* input, size_t length,
const rdpPrivateKey* key, BYTE* output,
size_t output_length);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL SSIZE_T crypto_rsa_private_decrypt(const BYTE* input, size_t length,
const rdpPrivateKey* key, BYTE* output,
size_t output_length);
FREERDP_LOCAL void crypto_reverse(BYTE* data, size_t length);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_CRYPTO_H */

View File

@@ -0,0 +1,106 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* ASN.1 Basic Encoding Rules (DER)
*
* Copyright 2011 Samsung, Author Jiten Pathy
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/cast.h>
#include <freerdp/crypto/der.h>
int _der_skip_length(int length)
{
if (length > 0x7F && length <= 0xFF)
return 2;
else if (length > 0xFF)
return 3;
else
return 1;
}
int der_write_length(wStream* s, int length)
{
if (length > 0x7F && length <= 0xFF)
{
Stream_Write_UINT8(s, 0x81);
Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, length));
return 2;
}
else if (length > 0xFF)
{
Stream_Write_UINT8(s, 0x82);
Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, length));
return 3;
}
else
{
Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, length));
return 1;
}
}
int der_get_content_length(int length)
{
if (length > 0x81 && length <= 0x102)
return length - 3;
else if (length > 0x102)
return length - 4;
else
return length - 2;
}
int der_skip_contextual_tag(int length)
{
return _der_skip_length(length) + 1;
}
int der_write_contextual_tag(wStream* s, BYTE tag, int length, BOOL pc)
{
Stream_Write_UINT8(s, (ER_CLASS_CTXT | ER_PC(pc)) | (ER_TAG_MASK & tag));
return der_write_length(s, length) + 1;
}
static void der_write_universal_tag(wStream* s, BYTE tag, BOOL pc)
{
Stream_Write_UINT8(s, (ER_CLASS_UNIV | ER_PC(pc)) | (ER_TAG_MASK & tag));
}
int der_skip_octet_string(int length)
{
return 1 + _der_skip_length(length) + length;
}
void der_write_octet_string(wStream* s, BYTE* oct_str, int length)
{
der_write_universal_tag(s, ER_TAG_OCTET_STRING, FALSE);
der_write_length(s, length);
Stream_Write(s, oct_str, WINPR_ASSERTING_INT_CAST(size_t, length));
}
int der_skip_sequence_tag(int length)
{
return 1 + _der_skip_length(length);
}
int der_write_sequence_tag(wStream* s, int length)
{
Stream_Write_UINT8(s, (ER_CLASS_UNIV | ER_CONSTRUCT) | (ER_TAG_MASK & ER_TAG_SEQUENCE));
return der_write_length(s, length) + 1;
}

View File

@@ -0,0 +1,445 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* ASN.1 Encoding Rules (BER/DER common functions)
*
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Modified by Jiten Pathy
*
* 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.
*/
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/cast.h>
#include <freerdp/crypto/er.h>
#include <freerdp/crypto/ber.h>
#include <freerdp/crypto/der.h>
void er_read_length(wStream* s, int* length)
{
BYTE byte = 0;
Stream_Read_UINT8(s, byte);
if (!length)
return;
*length = 0;
if (!s)
return;
if (byte & 0x80)
{
byte &= ~(0x80);
if (byte == 1)
Stream_Read_UINT8(s, *length);
if (byte == 2)
Stream_Read_UINT16_BE(s, *length);
}
else
{
*length = byte;
}
}
/**
* Write er length.
* @param s stream
* @param length length
*/
int er_write_length(wStream* s, int length, BOOL flag)
{
WINPR_ASSERT(length >= 0);
if (flag)
return der_write_length(s, length);
else
return (int)ber_write_length(s, (size_t)length);
}
int _er_skip_length(int length)
{
if (length > 0x7F)
return 3;
else
return 1;
}
int er_get_content_length(int length)
{
if (length - 1 > 0x7F)
return length - 4;
else
return length - 2;
}
/**
* Read er Universal tag.
* @param s A pointer to the stream to write to
* @param tag er universally-defined tag
* @return \b TRUE for success
*/
BOOL er_read_universal_tag(wStream* s, BYTE tag, BOOL pc)
{
BYTE byte = 0;
Stream_Read_UINT8(s, byte);
return (byte == (ER_CLASS_UNIV | ER_PC(pc) | (ER_TAG_MASK & tag)));
}
/**
* Write er Universal tag.
* @param s stream
* @param tag er universally-defined tag
* @param pc primitive (FALSE) or constructed (TRUE)
*/
void er_write_universal_tag(wStream* s, BYTE tag, BOOL pc)
{
Stream_Write_UINT8(s, (ER_CLASS_UNIV | ER_PC(pc)) | (ER_TAG_MASK & tag));
}
/**
* Read er Application tag.
* @param s stream
* @param tag er application-defined tag
* @param length length
*/
BOOL er_read_application_tag(wStream* s, BYTE tag, int* length)
{
BYTE byte = 0;
if (tag > 30)
{
Stream_Read_UINT8(s, byte);
if (byte != ((ER_CLASS_APPL | ER_CONSTRUCT) | ER_TAG_MASK))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte != tag)
return FALSE;
er_read_length(s, length);
}
else
{
Stream_Read_UINT8(s, byte);
if (byte != ((ER_CLASS_APPL | ER_CONSTRUCT) | (ER_TAG_MASK & tag)))
return FALSE;
er_read_length(s, length);
}
return TRUE;
}
/**
* Write er Application tag.
* @param s stream
* @param tag er application-defined tag
* @param length length
*/
void er_write_application_tag(wStream* s, BYTE tag, int length, BOOL flag)
{
if (tag > 30)
{
Stream_Write_UINT8(s, (ER_CLASS_APPL | ER_CONSTRUCT) | ER_TAG_MASK);
Stream_Write_UINT8(s, tag);
er_write_length(s, length, flag);
}
else
{
Stream_Write_UINT8(s, (ER_CLASS_APPL | ER_CONSTRUCT) | (ER_TAG_MASK & tag));
er_write_length(s, length, flag);
}
}
BOOL er_read_contextual_tag(wStream* s, BYTE tag, int* length, BOOL pc)
{
BYTE byte = 0;
Stream_Read_UINT8(s, byte);
if (byte != ((ER_CLASS_CTXT | ER_PC(pc)) | (ER_TAG_MASK & tag)))
{
Stream_Rewind(s, 1);
return FALSE;
}
er_read_length(s, length);
return TRUE;
}
int er_write_contextual_tag(wStream* s, BYTE tag, int length, BOOL pc, BOOL flag)
{
Stream_Write_UINT8(s, (ER_CLASS_CTXT | ER_PC(pc)) | (ER_TAG_MASK & tag));
return er_write_length(s, length, flag) + 1;
}
int er_skip_contextual_tag(int length)
{
return _er_skip_length(length) + 1;
}
BOOL er_read_sequence_tag(wStream* s, int* length)
{
BYTE byte = 0;
Stream_Read_UINT8(s, byte);
if (byte != ((ER_CLASS_UNIV | ER_CONSTRUCT) | (ER_TAG_SEQUENCE_OF)))
return FALSE;
er_read_length(s, length);
return TRUE;
}
/**
* Write er SEQUENCE tag.
* @param s stream
* @param length length
*/
int er_write_sequence_tag(wStream* s, int length, BOOL flag)
{
Stream_Write_UINT8(s, (ER_CLASS_UNIV | ER_CONSTRUCT) | (ER_TAG_MASK & ER_TAG_SEQUENCE));
return er_write_length(s, length, flag) + 1;
}
int er_skip_sequence(int length)
{
return 1 + _er_skip_length(length) + length;
}
int er_skip_sequence_tag(int length)
{
return 1 + _er_skip_length(length);
}
BOOL er_read_enumerated(wStream* s, BYTE* enumerated, BYTE count)
{
int length = 0;
er_read_universal_tag(s, ER_TAG_ENUMERATED, FALSE);
er_read_length(s, &length);
if (length == 1)
Stream_Read_UINT8(s, *enumerated);
else
return FALSE;
/* check that enumerated value falls within expected range */
if (*enumerated + 1 > count)
return FALSE;
return TRUE;
}
void er_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count, BOOL flag)
{
er_write_universal_tag(s, ER_TAG_ENUMERATED, FALSE);
er_write_length(s, 1, flag);
Stream_Write_UINT8(s, enumerated);
}
BOOL er_read_bit_string(wStream* s, int* length, BYTE* padding)
{
er_read_universal_tag(s, ER_TAG_BIT_STRING, FALSE);
er_read_length(s, length);
Stream_Read_UINT8(s, *padding);
return TRUE;
}
BOOL er_write_bit_string_tag(wStream* s, UINT32 length, BYTE padding, BOOL flag)
{
er_write_universal_tag(s, ER_TAG_BIT_STRING, FALSE);
er_write_length(s, WINPR_ASSERTING_INT_CAST(int, length), flag);
Stream_Write_UINT8(s, padding);
return TRUE;
}
BOOL er_read_octet_string(wStream* s, int* length)
{
if (!er_read_universal_tag(s, ER_TAG_OCTET_STRING, FALSE))
return FALSE;
er_read_length(s, length);
return TRUE;
}
/**
* Write a er OCTET_STRING
* @param s stream
* @param oct_str octet string
* @param length string length
*/
void er_write_octet_string(wStream* s, BYTE* oct_str, int length, BOOL flag)
{
er_write_universal_tag(s, ER_TAG_OCTET_STRING, FALSE);
er_write_length(s, length, flag);
Stream_Write(s, oct_str, WINPR_ASSERTING_INT_CAST(size_t, length));
}
int er_write_octet_string_tag(wStream* s, int length, BOOL flag)
{
er_write_universal_tag(s, ER_TAG_OCTET_STRING, FALSE);
er_write_length(s, length, flag);
return 1 + _er_skip_length(length);
}
int er_skip_octet_string(int length)
{
return 1 + _er_skip_length(length) + length;
}
/**
* Read a er BOOLEAN
* @param s A pointer to the stream to read from
* @param value A pointer to read the data to
*/
BOOL er_read_BOOL(wStream* s, BOOL* value)
{
int length = 0;
BYTE v = 0;
if (!er_read_universal_tag(s, ER_TAG_BOOLEAN, FALSE))
return FALSE;
er_read_length(s, &length);
if (length != 1)
return FALSE;
Stream_Read_UINT8(s, v);
*value = v != 0;
return TRUE;
}
/**
* Write a er BOOLEAN
* @param s A pointer to the stream to write to
* @param value The value to write
*/
void er_write_BOOL(wStream* s, BOOL value)
{
er_write_universal_tag(s, ER_TAG_BOOLEAN, FALSE);
er_write_length(s, 1, FALSE);
Stream_Write_UINT8(s, (value == TRUE) ? 0xFF : 0);
}
BOOL er_read_integer(wStream* s, UINT32* value)
{
int length = 0;
er_read_universal_tag(s, ER_TAG_INTEGER, FALSE);
er_read_length(s, &length);
if (value == nullptr)
{
Stream_Seek(s, WINPR_ASSERTING_INT_CAST(size_t, length));
return TRUE;
}
if (length == 1)
{
Stream_Read_UINT8(s, *value);
}
else if (length == 2)
{
Stream_Read_UINT16_BE(s, *value);
}
else if (length == 3)
{
BYTE byte = 0;
Stream_Read_UINT8(s, byte);
Stream_Read_UINT16_BE(s, *value);
*value += (byte << 16) & 0xFF0000;
}
else if (length == 4)
{
Stream_Read_UINT32_BE(s, *value);
}
else
{
return FALSE;
}
return TRUE;
}
/**
* Write a er INTEGER
* @param s A pointer to the stream to write to
* @param value the value to write
*/
int er_write_integer(wStream* s, INT32 value)
{
er_write_universal_tag(s, ER_TAG_INTEGER, FALSE);
if (value <= 127 && value >= -128)
{
er_write_length(s, 1, FALSE);
Stream_Write_INT8(s, WINPR_ASSERTING_INT_CAST(INT8, value));
return 2;
}
else if (value <= 32767 && value >= -32768)
{
er_write_length(s, 2, FALSE);
Stream_Write_INT16_BE(s, WINPR_ASSERTING_INT_CAST(INT16, value));
return 3;
}
else
{
er_write_length(s, 4, FALSE);
Stream_Write_INT32_BE(s, value);
return 5;
}
}
int er_skip_integer(INT32 value)
{
if (value <= 127 && value >= -128)
{
return _er_skip_length(1) + 2;
}
else if (value <= 32767 && value >= -32768)
{
return _er_skip_length(2) + 3;
}
else
{
return _er_skip_length(4) + 5;
}
}
BOOL er_read_integer_length(wStream* s, int* length)
{
er_read_universal_tag(s, ER_TAG_INTEGER, FALSE);
er_read_length(s, length);
return TRUE;
}

View File

@@ -0,0 +1,45 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* OpenSSL Compatibility
*
* Copyright (C) 2016 Norbert Federa <norbert.federa@thincast.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.
*/
#include "opensslcompat.h"
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
BIO_METHOD* BIO_meth_new(int type, const char* name)
{
BIO_METHOD* m;
if (!(m = calloc(1, sizeof(BIO_METHOD))))
return nullptr;
m->type = type;
m->name = name;
return m;
}
void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d)
{
if (n != nullptr)
*n = r->n;
if (e != nullptr)
*e = r->e;
if (d != nullptr)
*d = r->d;
}
#endif /* OPENSSL < 1.1.0 || LIBRESSL */

View File

@@ -0,0 +1,119 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* OpenSSL Compatibility
*
* Copyright (C) 2016 Norbert Federa <norbert.federa@thincast.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_LIB_CRYPTO_OPENSSLCOMPAT_H
#define FREERDP_LIB_CRYPTO_OPENSSLCOMPAT_H
#include <winpr/winpr.h>
#include <winpr/assert.h>
#include <freerdp/config.h>
#include <freerdp/api.h>
#include <openssl/x509.h>
#ifdef WITH_OPENSSL
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#define BIO_get_data(b) (b)->ptr
#define BIO_set_data(b, v) (b)->ptr = v
#define BIO_get_init(b) (b)->init
#define BIO_set_init(b, v) (b)->init = v
#define BIO_get_next(b, v) (b)->next_bio
#define BIO_set_next(b, v) (b)->next_bio = v
#define BIO_get_shutdown(b) (b)->shutdown
#define BIO_set_shutdown(b, v) (b)->shutdown = v
#define BIO_get_retry_reason(b) (b)->retry_reason
#define BIO_set_retry_reason(b, v) (b)->retry_reason = v
#define BIO_meth_set_write(b, f) (b)->bwrite = (f)
#define BIO_meth_set_read(b, f) (b)->bread = (f)
#define BIO_meth_set_puts(b, f) (b)->bputs = (f)
#define BIO_meth_set_gets(b, f) (b)->bgets = (f)
#define BIO_meth_set_ctrl(b, f) (b)->ctrl = (f)
#define BIO_meth_set_create(b, f) (b)->create = (f)
#define BIO_meth_set_destroy(b, f) (b)->destroy = (f)
#define BIO_meth_set_callback_ctrl(b, f) (b)->callback_ctrl = (f)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL
BIO_METHOD* BIO_meth_new(int type, const char* name);
FREERDP_LOCAL void RSA_get0_key(const RSA* r, const BIGNUM** n, const BIGNUM** e, const BIGNUM** d);
#endif /* OPENSSL < 1.1.0 || LIBRESSL */
#endif /* WITH_OPENSSL */
/* Drop in replacement for older OpenSSL and LibRESSL */
#if defined(LIBRESSL_VERSION_NUMBER) || \
(defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER < 0x1010000fL))
WINPR_ATTR_NODISCARD
static inline STACK_OF(X509) * sk_X509_deep_copy(const STACK_OF(X509) * sk,
X509* (*copyfunc)(const X509*),
void (*freefunc)(X509*))
{
WINPR_ASSERT(copyfunc);
WINPR_ASSERT(freefunc);
STACK_OF(X509)* stack = sk_X509_new_null();
if (!stack)
return nullptr;
if (sk)
{
for (int i = 0; i < sk_X509_num(sk); i++)
{
X509* cert = sk_X509_value(sk, i);
X509* copy = copyfunc(cert);
if (!copy)
goto fail;
const int rc = sk_X509_push(stack, copy);
if (rc <= 0)
goto fail;
}
}
return stack;
fail:
sk_X509_pop_free(stack, freefunc);
return nullptr;
}
#endif
/* OpenSSL API is not const consistent.
* While the TYPE_dup function take non const arguments
* the TYPE_sk versions require the arguments to be const...
*/
WINPR_ATTR_NODISCARD
static inline X509* X509_const_dup(const X509* x509)
{
X509* ptr = WINPR_CAST_CONST_PTR_AWAY(x509, X509*);
return X509_dup(ptr);
}
#endif /* FREERDP_LIB_CRYPTO_OPENSSLCOMPAT_H */

View File

@@ -0,0 +1,591 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* ASN.1 Packed Encoding Rules (BER)
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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.
*/
#include <winpr/assert.h>
#include <winpr/cast.h>
#include <winpr/print.h>
#include <freerdp/config.h>
#include <freerdp/crypto/per.h>
#include <freerdp/log.h>
#define TAG FREERDP_TAG("crypto.per")
/**
* Read PER length.
*
* @param s stream to read from
* @param length A pointer to return the length read, must not be nullptr
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_length(wStream* s, UINT16* length)
{
BYTE byte = 0;
WINPR_ASSERT(length);
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, byte);
if (byte & 0x80)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
byte &= ~(0x80);
*length = WINPR_ASSERTING_INT_CAST(UINT16, byte << 8);
Stream_Read_UINT8(s, byte);
*length += byte;
}
else
{
*length = byte;
}
return TRUE;
}
/**
* Write PER length.
* @param s stream
* @param length length
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_length(wStream* s, UINT16 length)
{
if (length > 0x7F)
{
if (!Stream_EnsureRemainingCapacity(s, 2))
return FALSE;
Stream_Write_UINT16_BE(s, (length | 0x8000));
}
else
{
if (!Stream_EnsureRemainingCapacity(s, 1))
return FALSE;
Stream_Write_UINT8(s, (UINT8)length);
}
return TRUE;
}
/**
* Read PER choice.
* @param s stream
* @param choice choice
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_choice(wStream* s, BYTE* choice)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
Stream_Read_UINT8(s, *choice);
return TRUE;
}
/**
* Write PER CHOICE.
* @param s stream
* @param choice index of chosen field
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_choice(wStream* s, BYTE choice)
{
if (!Stream_EnsureRemainingCapacity(s, 1))
return FALSE;
Stream_Write_UINT8(s, choice);
return TRUE;
}
/**
* Read PER selection.
* @param s stream
* @param selection selection
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_selection(wStream* s, BYTE* selection)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
WINPR_ASSERT(selection);
Stream_Read_UINT8(s, *selection);
return TRUE;
}
/**
* Write PER selection for OPTIONAL fields.
* @param s stream
* @param selection bit map of selected fields
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_selection(wStream* s, BYTE selection)
{
if (!Stream_EnsureRemainingCapacity(s, 1))
return FALSE;
Stream_Write_UINT8(s, selection);
return TRUE;
}
/**
* Read PER number of sets.
* @param s stream
* @param number number of sets
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_number_of_sets(wStream* s, BYTE* number)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
WINPR_ASSERT(number);
Stream_Read_UINT8(s, *number);
return TRUE;
}
/**
* Write PER number of sets for SET OF.
*
* @param s stream
* @param number number of sets
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_number_of_sets(wStream* s, BYTE number)
{
if (!Stream_EnsureRemainingCapacity(s, 1))
return FALSE;
Stream_Write_UINT8(s, number);
return TRUE;
}
/**
* Read PER padding with zeros.
*
* @param s A stream to read from
* @param length the data to write
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_padding(wStream* s, UINT16 length)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
Stream_Seek(s, length);
return TRUE;
}
/**
* Write PER padding with zeros.
* @param s A stream to write to
* @param length the data to write
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_padding(wStream* s, UINT16 length)
{
if (!Stream_EnsureRemainingCapacity(s, length))
return FALSE;
Stream_Zero(s, length);
return TRUE;
}
/**
* Read PER INTEGER.
* @param s stream
* @param integer integer
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_integer(wStream* s, UINT32* integer)
{
UINT16 length = 0;
WINPR_ASSERT(integer);
if (!per_read_length(s, &length))
return FALSE;
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
if (length == 0)
*integer = 0;
else if (length == 1)
Stream_Read_UINT8(s, *integer);
else if (length == 2)
Stream_Read_UINT16_BE(s, *integer);
else
return FALSE;
return TRUE;
}
/**
* Write PER INTEGER.
* @param s stream
* @param integer integer
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_integer(wStream* s, UINT32 integer)
{
if (integer <= UINT8_MAX)
{
if (!per_write_length(s, 1))
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, 1))
return FALSE;
Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, integer));
}
else if (integer <= UINT16_MAX)
{
if (!per_write_length(s, 2))
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, 2))
return FALSE;
Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, integer));
}
else if (integer <= UINT32_MAX)
{
if (!per_write_length(s, 4))
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, 4))
return FALSE;
Stream_Write_UINT32_BE(s, integer);
}
return TRUE;
}
/**
* Read PER INTEGER (UINT16).
*
* @param s The stream to read from
* @param integer The integer result variable pointer, must not be nullptr
* @param min minimum value
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL per_read_integer16(wStream* s, UINT16* integer, UINT16 min)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
return FALSE;
Stream_Read_UINT16_BE(s, *integer);
if (*integer > UINT16_MAX - min)
{
WLog_WARN(TAG, "PER uint16 invalid value %" PRIu16 " > %d", *integer, UINT16_MAX - min);
return FALSE;
}
*integer += min;
return TRUE;
}
/**
* Write PER INTEGER (UINT16).
* @param s stream
* @param integer integer
* @param min minimum value
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_integer16(wStream* s, UINT16 integer, UINT16 min)
{
if (min > integer)
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, 2))
return FALSE;
Stream_Write_UINT16_BE(s, integer - min);
return TRUE;
}
/**
* Read PER ENUMERATED.
*
* @param s The stream to read from
* @param enumerated enumerated result variable, must not be nullptr
* @param count enumeration count
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL per_read_enumerated(wStream* s, BYTE* enumerated, BYTE count)
{
if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
return FALSE;
WINPR_ASSERT(enumerated);
Stream_Read_UINT8(s, *enumerated);
/* check that enumerated value falls within expected range */
if (*enumerated + 1 > count)
{
WLog_WARN(TAG, "PER invalid data, expected %" PRIu8 " < %" PRIu8, *enumerated, count);
return FALSE;
}
return TRUE;
}
/**
* Write PER ENUMERATED.
*
* @param s The stream to write to
* @param enumerated enumerated
* @param count enumeration count
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL per_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count)
{
if (!Stream_EnsureRemainingCapacity(s, 1))
return FALSE;
Stream_Write_UINT8(s, enumerated);
return TRUE;
}
static BOOL per_check_oid_and_log_mismatch(const BYTE* got, const BYTE* expect, size_t length)
{
if (memcmp(got, expect, length) == 0)
{
return TRUE;
}
else
{
char* got_str = winpr_BinToHexString(got, length, TRUE);
char* expect_str = winpr_BinToHexString(expect, length, TRUE);
WLog_WARN(TAG, "PER OID mismatch, got %s, expected %s", got_str, expect_str);
free(got_str);
free(expect_str);
return FALSE;
}
}
/**
* Read PER OBJECT_IDENTIFIER (OID).
*
* @param s The stream to read from
* @param oid object identifier (OID)
* @warning It works correctly only for limited set of OIDs.
*
* @return \b TRUE for success, \b FALSE otherwise
*/
BOOL per_read_object_identifier(wStream* s, const BYTE oid[6])
{
BYTE t12 = 0;
UINT16 length = 0;
BYTE a_oid[6] = WINPR_C_ARRAY_INIT;
if (!per_read_length(s, &length))
return FALSE;
if (length != 5)
{
WLog_WARN(TAG, "PER length, got %" PRIu16 ", expected 5", length);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
Stream_Read_UINT8(s, t12); /* first two tuples */
a_oid[0] = t12 / 40;
a_oid[1] = t12 % 40;
Stream_Read_UINT8(s, a_oid[2]); /* tuple 3 */
Stream_Read_UINT8(s, a_oid[3]); /* tuple 4 */
Stream_Read_UINT8(s, a_oid[4]); /* tuple 5 */
Stream_Read_UINT8(s, a_oid[5]); /* tuple 6 */
return per_check_oid_and_log_mismatch(a_oid, oid, sizeof(a_oid));
}
/**
* Write PER OBJECT_IDENTIFIER (OID)
* @param s stream
* @param oid object identifier (oid)
* @warning It works correctly only for limited set of OIDs.
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_object_identifier(wStream* s, const BYTE oid[6])
{
BYTE t12 = oid[0] * 40 + oid[1];
if (!Stream_EnsureRemainingCapacity(s, 6))
return FALSE;
Stream_Write_UINT8(s, 5); /* length */
Stream_Write_UINT8(s, t12); /* first two tuples */
Stream_Write_UINT8(s, oid[2]); /* tuple 3 */
Stream_Write_UINT8(s, oid[3]); /* tuple 4 */
Stream_Write_UINT8(s, oid[4]); /* tuple 5 */
Stream_Write_UINT8(s, oid[5]); /* tuple 6 */
return TRUE;
}
/**
* Read PER OCTET_STRING.
*
* @param s The stream to read from
* @param oct_str octet string
* @param length string length
* @param min minimum length
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_octet_string(wStream* s, const BYTE* oct_str, UINT16 length, UINT16 min)
{
UINT16 mlength = 0;
if (!per_read_length(s, &mlength))
return FALSE;
if (mlength + min != length)
{
WLog_ERR(TAG, "length mismatch: %d!= %" PRIu16, mlength + min, length);
return FALSE;
}
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
const BYTE* a_oct_str = Stream_ConstPointer(s);
Stream_Seek(s, length);
return per_check_oid_and_log_mismatch(a_oct_str, oct_str, length);
}
/**
* Write PER OCTET_STRING
* @param s stream
* @param oct_str octet string
* @param length string length
* @param min minimum string length
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_octet_string(wStream* s, const BYTE* oct_str, UINT16 length, UINT16 min)
{
UINT16 mlength = 0;
mlength = (length >= min) ? length - min : min;
if (!per_write_length(s, mlength))
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, length))
return FALSE;
for (UINT16 i = 0; i < length; i++)
Stream_Write_UINT8(s, oct_str[i]);
return TRUE;
}
/**
* Read PER NumericString.
* @param s stream
* @param min minimum string length
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_read_numeric_string(wStream* s, UINT16 min)
{
size_t length = 0;
UINT16 mlength = 0;
if (!per_read_length(s, &mlength))
return FALSE;
length = (mlength + min + 1) / 2;
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
return FALSE;
Stream_Seek(s, length);
return TRUE;
}
/**
* Write PER NumericString.
* @param s stream
* @param num_str numeric string
* @param length string length
* @param min minimum string length
*
* @return \b TRUE for success, \b FALSE otherwise.
*/
BOOL per_write_numeric_string(wStream* s, const BYTE* num_str, UINT16 length, UINT16 min)
{
WINPR_ASSERT(num_str || (length == 0));
const UINT16 mlength = (length >= min) ? length - min : min;
if (!per_write_length(s, mlength))
return FALSE;
if (!Stream_EnsureRemainingCapacity(s, length))
return FALSE;
for (UINT16 i = 0; i < length; i += 2)
{
BYTE c1 = num_str[i];
BYTE c2 = ((i + 1) < length) ? num_str[i + 1] : 0x30;
if ((c1 < 0x30) || (c2 < 0x30))
return FALSE;
c1 = (c1 - 0x30) % 10;
c2 = (c2 - 0x30) % 10;
const BYTE num = WINPR_ASSERTING_INT_CAST(BYTE, (c1 << 4) | c2);
Stream_Write_UINT8(s, num); /* string */
}
return TRUE;
}

View File

@@ -0,0 +1,684 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Private key Handling
*
* Copyright 2011 Jiten Pathy
* Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <freerdp/config.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <winpr/assert.h>
#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/crypto.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/err.h>
#include "privatekey.h"
#include "cert_common.h"
#include <freerdp/crypto/privatekey.h>
#include <openssl/evp.h>
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
#include <openssl/core_names.h>
#endif
#include "x509_utils.h"
#include "crypto.h"
#include "opensslcompat.h"
#define TAG FREERDP_TAG("crypto")
struct rdp_private_key
{
EVP_PKEY* evp;
rdpCertInfo cert;
BYTE* PrivateExponent;
DWORD PrivateExponentLength;
};
/*
* Terminal Services Signing Keys.
* Yes, Terminal Services Private Key is publicly available.
*/
static BYTE tssk_modulus[] = { 0x3d, 0x3a, 0x5e, 0xbd, 0x72, 0x43, 0x3e, 0xc9, 0x4d, 0xbb, 0xc1,
0x1e, 0x4a, 0xba, 0x5f, 0xcb, 0x3e, 0x88, 0x20, 0x87, 0xef, 0xf5,
0xc1, 0xe2, 0xd7, 0xb7, 0x6b, 0x9a, 0xf2, 0x52, 0x45, 0x95, 0xce,
0x63, 0x65, 0x6b, 0x58, 0x3a, 0xfe, 0xef, 0x7c, 0xe7, 0xbf, 0xfe,
0x3d, 0xf6, 0x5c, 0x7d, 0x6c, 0x5e, 0x06, 0x09, 0x1a, 0xf5, 0x61,
0xbb, 0x20, 0x93, 0x09, 0x5f, 0x05, 0x6d, 0xea, 0x87 };
static BYTE tssk_privateExponent[] = {
0x87, 0xa7, 0x19, 0x32, 0xda, 0x11, 0x87, 0x55, 0x58, 0x00, 0x16, 0x16, 0x25, 0x65, 0x68, 0xf8,
0x24, 0x3e, 0xe6, 0xfa, 0xe9, 0x67, 0x49, 0x94, 0xcf, 0x92, 0xcc, 0x33, 0x99, 0xe8, 0x08, 0x60,
0x17, 0x9a, 0x12, 0x9f, 0x24, 0xdd, 0xb1, 0x24, 0x99, 0xc7, 0x3a, 0xb8, 0x0a, 0x7b, 0x0d, 0xdd,
0x35, 0x07, 0x79, 0x17, 0x0b, 0x51, 0x9b, 0xb3, 0xc7, 0x10, 0x01, 0x13, 0xe7, 0x3f, 0xf3, 0x5f
};
static const rdpPrivateKey tssk = { .PrivateExponent = tssk_privateExponent,
.PrivateExponentLength = sizeof(tssk_privateExponent),
.cert = { .Modulus = tssk_modulus,
.ModulusLength = sizeof(tssk_modulus) } };
const rdpPrivateKey* priv_key_tssk = &tssk;
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
static RSA* evp_pkey_to_rsa(const rdpPrivateKey* key)
{
if (!freerdp_key_is_rsa(key))
{
WLog_WARN(TAG, "Key is no RSA key");
return nullptr;
}
RSA* rsa = nullptr;
BIO* bio = BIO_new(
#if defined(LIBRESSL_VERSION_NUMBER)
BIO_s_mem()
#else
BIO_s_secmem()
#endif
);
if (!bio)
return nullptr;
const int rc = PEM_write_bio_PrivateKey(bio, key->evp, nullptr, nullptr, 0, nullptr, nullptr);
if (rc != 1)
goto fail;
rsa = PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, nullptr);
fail:
BIO_free_all(bio);
return rsa;
}
#endif
static EVP_PKEY* evp_pkey_utils_from_pem(const char* data, size_t len, BOOL fromFile,
const char* password)
{
EVP_PKEY* evp = nullptr;
BIO* bio = nullptr;
if (fromFile)
bio = BIO_new_file(data, "rb");
else
{
if (len > INT_MAX)
return nullptr;
bio = BIO_new_mem_buf(data, (int)len);
}
if (!bio)
{
WLog_ERR(TAG, "BIO_new failed for private key");
return nullptr;
}
evp =
PEM_read_bio_PrivateKey(bio, nullptr, nullptr, WINPR_CAST_CONST_PTR_AWAY(password, void*));
BIO_free_all(bio);
if (!evp)
WLog_ERR(TAG, "PEM_read_bio_PrivateKey returned nullptr [input length %" PRIuz "]", len);
return evp;
}
static BOOL key_read_private(rdpPrivateKey* key)
{
BOOL rc = FALSE;
WINPR_ASSERT(key);
WINPR_ASSERT(key->evp);
/* The key is not an RSA key, that means we just return success. */
if (!freerdp_key_is_rsa(key))
return TRUE;
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
RSA* rsa = evp_pkey_to_rsa(key);
if (!rsa)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "unable to load RSA key: %s.",
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
goto fail;
}
switch (RSA_check_key(rsa))
{
case 0:
WLog_ERR(TAG, "invalid RSA key");
goto fail;
case 1:
/* Valid key. */
break;
default:
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_ERR(TAG, "unexpected error when checking RSA key: %s.",
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
goto fail;
}
}
const BIGNUM* rsa_e = nullptr;
const BIGNUM* rsa_n = nullptr;
const BIGNUM* rsa_d = nullptr;
RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
#else
BIGNUM* rsa_e = nullptr;
BIGNUM* rsa_n = nullptr;
BIGNUM* rsa_d = nullptr;
if (!EVP_PKEY_get_bn_param(key->evp, OSSL_PKEY_PARAM_RSA_N, &rsa_n))
goto fail;
if (!EVP_PKEY_get_bn_param(key->evp, OSSL_PKEY_PARAM_RSA_E, &rsa_e))
goto fail;
if (!EVP_PKEY_get_bn_param(key->evp, OSSL_PKEY_PARAM_RSA_D, &rsa_d))
goto fail;
#endif
if (BN_num_bytes(rsa_e) > 4)
{
WLog_ERR(TAG, "RSA public exponent too large");
goto fail;
}
if (!read_bignum(&key->PrivateExponent, &key->PrivateExponentLength, rsa_d, TRUE))
goto fail;
if (!cert_info_create(&key->cert, rsa_n, rsa_e))
goto fail;
rc = TRUE;
fail:
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
RSA_free(rsa);
#else
BN_free(rsa_d);
BN_free(rsa_e);
BN_free(rsa_n);
#endif
return rc;
}
rdpPrivateKey* freerdp_key_new_from_pem(const char* pem)
{
return freerdp_key_new_from_pem_enc(pem, nullptr);
}
rdpPrivateKey* freerdp_key_new_from_pem_enc(const char* pem, const char* password)
{
rdpPrivateKey* key = freerdp_key_new();
if (!key || !pem)
goto fail;
key->evp = evp_pkey_utils_from_pem(pem, strlen(pem), FALSE, password);
if (!key->evp)
goto fail;
if (!key_read_private(key))
goto fail;
return key;
fail:
freerdp_key_free(key);
return nullptr;
}
rdpPrivateKey* freerdp_key_new_from_file(const char* keyfile)
{
return freerdp_key_new_from_file_enc(keyfile, nullptr);
}
rdpPrivateKey* freerdp_key_new_from_file_enc(const char* keyfile, const char* password)
{
rdpPrivateKey* key = freerdp_key_new();
if (!key || !keyfile)
goto fail;
key->evp = evp_pkey_utils_from_pem(keyfile, strlen(keyfile), TRUE, password);
if (!key->evp)
goto fail;
if (!key_read_private(key))
goto fail;
return key;
fail:
freerdp_key_free(key);
return nullptr;
}
rdpPrivateKey* freerdp_key_new(void)
{
return calloc(1, sizeof(rdpPrivateKey));
}
rdpPrivateKey* freerdp_key_clone(const rdpPrivateKey* key)
{
if (!key)
return nullptr;
rdpPrivateKey* _key = (rdpPrivateKey*)calloc(1, sizeof(rdpPrivateKey));
if (!_key)
return nullptr;
if (key->evp)
{
_key->evp = key->evp;
if (!_key->evp)
goto out_fail;
EVP_PKEY_up_ref(_key->evp);
}
if (key->PrivateExponent)
{
_key->PrivateExponent = (BYTE*)malloc(key->PrivateExponentLength);
if (!_key->PrivateExponent)
goto out_fail;
CopyMemory(_key->PrivateExponent, key->PrivateExponent, key->PrivateExponentLength);
_key->PrivateExponentLength = key->PrivateExponentLength;
}
if (!cert_info_clone(&_key->cert, &key->cert))
goto out_fail;
return _key;
out_fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
freerdp_key_free(_key);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void freerdp_key_free(rdpPrivateKey* key)
{
if (!key)
return;
EVP_PKEY_free(key->evp);
if (key->PrivateExponent)
memset(key->PrivateExponent, 0, key->PrivateExponentLength);
free(key->PrivateExponent);
cert_info_free(&key->cert);
free(key);
}
const rdpCertInfo* freerdp_key_get_info(const rdpPrivateKey* key)
{
WINPR_ASSERT(key);
if (!freerdp_key_is_rsa(key))
return nullptr;
return &key->cert;
}
const BYTE* freerdp_key_get_exponent(const rdpPrivateKey* key, size_t* plength)
{
WINPR_ASSERT(key);
if (!freerdp_key_is_rsa(key))
{
if (plength)
*plength = 0;
return nullptr;
}
if (plength)
*plength = key->PrivateExponentLength;
return key->PrivateExponent;
}
EVP_PKEY* freerdp_key_get_evp_pkey(const rdpPrivateKey* key)
{
WINPR_ASSERT(key);
EVP_PKEY* evp = key->evp;
WINPR_ASSERT(evp);
EVP_PKEY_up_ref(evp);
return evp;
}
BOOL freerdp_key_is_rsa(const rdpPrivateKey* key)
{
WINPR_ASSERT(key);
if (key == priv_key_tssk)
return TRUE;
WINPR_ASSERT(key->evp);
return (EVP_PKEY_id(key->evp) == EVP_PKEY_RSA);
}
size_t freerdp_key_get_bits(const rdpPrivateKey* key)
{
int rc = -1;
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
RSA* rsa = evp_pkey_to_rsa(key);
if (rsa)
{
rc = RSA_bits(rsa);
RSA_free(rsa);
}
#else
rc = EVP_PKEY_get_bits(key->evp);
#endif
return WINPR_ASSERTING_INT_CAST(size_t, rc);
}
BOOL freerdp_key_generate(rdpPrivateKey* key, const char* type, size_t count, ...)
{
BOOL rc = FALSE;
if (!type)
{
WLog_ERR(TAG, "Invalid argument type=%s", type);
return FALSE;
}
if (strncmp("RSA", type, 4) != 0)
{
WLog_ERR(TAG, "Argument type=%s is currently not supported, aborting", type);
return FALSE;
}
if (count != 1)
{
WLog_ERR(TAG, "Argument type=%s requires count=1, got %" PRIuz ", aborting", type, count);
return FALSE;
}
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, count);
const int key_length = va_arg(ap, int);
va_end(ap);
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
RSA* rsa = nullptr;
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
rsa = RSA_generate_key(key_length, RSA_F4, nullptr, nullptr);
#else
{
BIGNUM* bn = BN_secure_new();
if (!bn)
return FALSE;
rsa = RSA_new();
if (!rsa)
{
BN_clear_free(bn);
return FALSE;
}
BN_set_word(bn, RSA_F4);
const int res = RSA_generate_key_ex(rsa, key_length, bn, nullptr);
BN_clear_free(bn);
if (res != 1)
return FALSE;
}
#endif
EVP_PKEY_free(key->evp);
key->evp = EVP_PKEY_new();
if (!EVP_PKEY_assign_RSA(key->evp, rsa))
{
EVP_PKEY_free(key->evp);
key->evp = nullptr;
RSA_free(rsa);
return FALSE;
}
rc = TRUE;
#else
EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(nullptr, type, nullptr);
if (!pctx)
return FALSE;
if (EVP_PKEY_keygen_init(pctx) != 1)
goto fail;
if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, key_length) != 1)
goto fail;
EVP_PKEY_free(key->evp);
key->evp = nullptr;
if (EVP_PKEY_generate(pctx, &key->evp) != 1)
goto fail;
rc = TRUE;
fail:
EVP_PKEY_CTX_free(pctx);
#endif
return rc;
}
BYTE* freerdp_key_get_param(const rdpPrivateKey* key, enum FREERDP_KEY_PARAM param, size_t* plength)
{
BYTE* buf = nullptr;
WINPR_ASSERT(key);
WINPR_ASSERT(plength);
*plength = 0;
BIGNUM* bn = nullptr;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
const char* pk = nullptr;
switch (param)
{
case FREERDP_KEY_PARAM_RSA_D:
pk = OSSL_PKEY_PARAM_RSA_D;
break;
case FREERDP_KEY_PARAM_RSA_E:
pk = OSSL_PKEY_PARAM_RSA_E;
break;
case FREERDP_KEY_PARAM_RSA_N:
pk = OSSL_PKEY_PARAM_RSA_N;
break;
default:
return nullptr;
}
if (!EVP_PKEY_get_bn_param(key->evp, pk, &bn))
return nullptr;
#else
{
const RSA* rsa = EVP_PKEY_get0_RSA(key->evp);
if (!rsa)
return nullptr;
const BIGNUM* cbn = nullptr;
switch (param)
{
case FREERDP_KEY_PARAM_RSA_D:
#if OPENSSL_VERSION_NUMBER >= 0x10101007L
cbn = RSA_get0_d(rsa);
#endif
break;
case FREERDP_KEY_PARAM_RSA_E:
#if OPENSSL_VERSION_NUMBER >= 0x10101007L
cbn = RSA_get0_e(rsa);
#endif
break;
case FREERDP_KEY_PARAM_RSA_N:
#if OPENSSL_VERSION_NUMBER >= 0x10101007L
cbn = RSA_get0_n(rsa);
#endif
break;
default:
return nullptr;
}
if (!cbn)
return nullptr;
bn = BN_dup(cbn);
if (!bn)
return nullptr;
}
#endif
const int length = BN_num_bytes(bn);
if (length < 0)
goto fail;
{
const size_t alloc_size = (size_t)length + 1ull;
buf = calloc(alloc_size, sizeof(BYTE));
}
if (!buf)
goto fail;
{
const int bnlen = BN_bn2bin(bn, buf);
if (bnlen != length)
{
free(buf);
buf = nullptr;
}
else
*plength = WINPR_ASSERTING_INT_CAST(size_t, length);
}
fail:
BN_free(bn);
return buf;
}
WINPR_DIGEST_CTX* freerdp_key_digest_sign(rdpPrivateKey* key, WINPR_MD_TYPE digest)
{
WINPR_DIGEST_CTX* md_ctx = winpr_Digest_New();
if (!md_ctx)
return nullptr;
if (!winpr_DigestSign_Init(md_ctx, digest, key->evp))
{
winpr_Digest_Free(md_ctx);
return nullptr;
}
return md_ctx;
}
static BOOL bio_read_pem(BIO* bio, char** ppem, size_t* plength)
{
BOOL rc = FALSE;
WINPR_ASSERT(bio);
WINPR_ASSERT(ppem);
const size_t blocksize = 2048;
size_t offset = 0;
size_t length = blocksize;
char* pem = nullptr;
*ppem = nullptr;
if (plength)
*plength = 0;
while (offset < length)
{
char* tmp = realloc(pem, length + 1);
if (!tmp)
goto fail;
pem = tmp;
ERR_clear_error();
const int status = BIO_read(bio, &pem[offset], (int)(length - offset));
if (status < 0)
{
WLog_ERR(TAG, "failed to read certificate");
goto fail;
}
if (status == 0)
break;
offset += (size_t)status;
if (length - offset > 0)
break;
length += blocksize;
}
if (pem)
{
if (offset >= length)
goto fail;
pem[offset] = '\0';
}
*ppem = pem;
if (plength)
*plength = offset;
rc = TRUE;
fail:
if (!rc)
free(pem);
return rc;
}
char* freerdp_key_get_pem(const rdpPrivateKey* key, size_t* plen, const char* password)
{
WINPR_ASSERT(key);
if (!key->evp)
return nullptr;
/**
* Don't manage certificates internally, leave it up entirely to the external client
* implementation
*/
BIO* bio = BIO_new(BIO_s_mem());
if (!bio)
{
WLog_ERR(TAG, "BIO_new() failure");
return nullptr;
}
char* pem = nullptr;
const EVP_CIPHER* enc = nullptr;
if (password)
enc = EVP_aes_256_xts();
const int status = PEM_write_bio_PrivateKey(bio, key->evp, enc, nullptr, 0, nullptr,
WINPR_CAST_CONST_PTR_AWAY(password, void*));
if (status < 0)
{
WLog_ERR(TAG, "PEM_write_bio_PrivateKey failure: %d", status);
goto fail;
}
(void)bio_read_pem(bio, &pem, plen);
fail:
BIO_free_all(bio);
return pem;
}

View File

@@ -0,0 +1,76 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Private key Handling
*
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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_LIB_CORE_PRIVATEKEY_H
#define FREERDP_LIB_CORE_PRIVATEKEY_H
#include <freerdp/api.h>
#include <freerdp/crypto/crypto.h>
#include <freerdp/crypto/privatekey.h>
#include <openssl/rsa.h>
#include <openssl/evp.h>
#ifdef __cplusplus
extern "C"
{
#endif
enum FREERDP_KEY_PARAM
{
FREERDP_KEY_PARAM_RSA_D,
FREERDP_KEY_PARAM_RSA_E,
FREERDP_KEY_PARAM_RSA_N
};
WINPR_ATTR_MALLOC(freerdp_key_free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpPrivateKey* freerdp_key_clone(const rdpPrivateKey* key);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL const rdpCertInfo* freerdp_key_get_info(const rdpPrivateKey* key);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL const BYTE* freerdp_key_get_exponent(const rdpPrivateKey* key, size_t* plength);
/** \brief returns a pointer to a EVP_PKEY structure.
* Call EVP_PKEY_free when done.
*/
WINPR_ATTR_MALLOC(EVP_PKEY_free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL EVP_PKEY* freerdp_key_get_evp_pkey(const rdpPrivateKey* key);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BYTE* freerdp_key_get_param(const rdpPrivateKey* key,
enum FREERDP_KEY_PARAM param, size_t* plength);
WINPR_ATTR_NODISCARD
WINPR_ATTR_MALLOC(winpr_Digest_Free, 1)
FREERDP_LOCAL WINPR_DIGEST_CTX* freerdp_key_digest_sign(rdpPrivateKey* key,
WINPR_MD_TYPE digest);
FREERDP_LOCAL extern const rdpPrivateKey* priv_key_tssk;
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_CORE_PRIVATEKEY_H */

View File

@@ -0,0 +1,32 @@
set(MODULE_NAME "TestFreeRDPCrypto")
set(MODULE_PREFIX "TEST_FREERDP_CRYPTO")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(DRIVER ${MODULE_NAME}.c)
set(TESTS TestKnownHosts.c TestBase64.c)
if(BUILD_TESTING_INTERNAL)
list(APPEND TESTS Test_x509_utils.c)
endif()
create_test_sourcelist(SRCS ${DRIVER} ${TESTS})
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
add_executable(${MODULE_NAME} ${SRCS})
set(TEST_PATH ${CMAKE_CURRENT_SOURCE_DIR})
add_compile_definitions(TEST_SOURCE_DIR="${TEST_PATH}")
target_link_libraries(${MODULE_NAME} freerdp winpr ${OPENSSL_LIBRARIES})
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "FreeRDP/Test")

View File

@@ -0,0 +1,178 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2014 Thincast Technologies GmbH
* Copyright 2014 Hardening <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.
*/
#include <freerdp/crypto/crypto.h>
struct Encode64test
{
const char* input;
size_t len;
const char* output;
};
static const struct Encode64test encodeTests_base64[] = {
{ "\x00", 1, "AA==" },
{ "\x00\x00", 2, "AAA=" },
{ "\x00\x00\x00", 3, "AAAA" },
{ "0123456", 7, "MDEyMzQ1Ng==" },
{ "90123456", 8, "OTAxMjM0NTY=" },
{ "890123456", 9, "ODkwMTIzNDU2" },
{ "7890123456", 10, "Nzg5MDEyMzQ1Ng==" },
{ nullptr, -1, nullptr }, /* /!\ last one /!\ */
};
static const struct Encode64test encodeTests_base64url[] = {
{ "\x00", 1, "AA" },
{ "\x00\x00", 2, "AAA" },
{ "\x00\x00\x00", 3, "AAAA" },
{ "01?34>6", 7, "MDE_MzQ-Ng" },
{ "90123456", 8, "OTAxMjM0NTY" },
{ "890123456", 9, "ODkwMTIzNDU2" },
{ "78?01>3456", 10, "Nzg_MDE-MzQ1Ng" },
{ nullptr, -1, nullptr }, /* /!\ last one /!\ */
};
int TestBase64(int argc, char* argv[])
{
int testNb = 0;
size_t outLen = 0;
BYTE* decoded = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
testNb++;
(void)fprintf(stderr, "%d:encode base64...", testNb);
for (int i = 0; encodeTests_base64[i].input; i++)
{
char* encoded = crypto_base64_encode((const BYTE*)encodeTests_base64[i].input,
encodeTests_base64[i].len);
if (strcmp(encodeTests_base64[i].output, encoded) != 0)
{
(void)fprintf(stderr, "ko, error for string %d\n", i);
return -1;
}
free(encoded);
}
(void)fprintf(stderr, "ok\n");
testNb++;
(void)fprintf(stderr, "%d:encode base64url...", testNb);
for (int i = 0; encodeTests_base64url[i].input; i++)
{
char* encoded = crypto_base64url_encode((const BYTE*)encodeTests_base64url[i].input,
encodeTests_base64url[i].len);
if (strcmp(encodeTests_base64url[i].output, encoded) != 0)
{
(void)fprintf(stderr, "ko, error for string %d\n", i);
return -1;
}
free(encoded);
}
(void)fprintf(stderr, "ok\n");
testNb++;
(void)fprintf(stderr, "%d:decode base64...", testNb);
for (int i = 0; encodeTests_base64[i].input; i++)
{
crypto_base64_decode(encodeTests_base64[i].output, strlen(encodeTests_base64[i].output),
&decoded, &outLen);
if (!decoded || (outLen != encodeTests_base64[i].len) ||
memcmp(encodeTests_base64[i].input, decoded, outLen) != 0)
{
(void)fprintf(stderr, "ko, error for string %d\n", i);
return -1;
}
free(decoded);
}
(void)fprintf(stderr, "ok\n");
testNb++;
(void)fprintf(stderr, "%d:decode base64url...", testNb);
for (int i = 0; encodeTests_base64url[i].input; i++)
{
crypto_base64url_decode(encodeTests_base64url[i].output,
strlen(encodeTests_base64url[i].output), &decoded, &outLen);
if (!decoded || (outLen != encodeTests_base64url[i].len) ||
memcmp(encodeTests_base64url[i].input, decoded, outLen) != 0)
{
(void)fprintf(stderr, "ko, error for string %d\n", i);
return -1;
}
free(decoded);
}
(void)fprintf(stderr, "ok\n");
testNb++;
(void)fprintf(stderr, "%d:decode base64 errors...", testNb);
crypto_base64_decode("000", 3, &decoded, &outLen);
if (decoded)
{
(void)fprintf(stderr, "ko, badly padded string\n");
return -1;
}
crypto_base64_decode("0=00", 4, &decoded, &outLen);
if (decoded)
{
(void)fprintf(stderr, "ko, = in a wrong place\n");
return -1;
}
crypto_base64_decode("00=0", 4, &decoded, &outLen);
if (decoded)
{
(void)fprintf(stderr, "ko, = in a wrong place\n");
return -1;
}
(void)fprintf(stderr, "ok\n");
testNb++;
/* test the encode_ex version that will add \r\n */
(void)fprintf(stderr, "%d:encode base64 with crLf...", testNb);
const char* longStr = "01234567890123456789012345678901234567890123456789";
const char* longStrExpected =
"MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3\r\nODk=\r\n";
char* encoded = crypto_base64_encode_ex((const BYTE*)longStr, strlen(longStr), TRUE);
if (!encoded || strcmp(encoded, longStrExpected) != 0)
{
(void)fprintf(stderr, "problem with encode with CRLF\n");
return -1;
}
free(encoded);
(void)fprintf(stderr, "ok\n");
return 0;
}

View File

@@ -0,0 +1,394 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
*
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 Armin Novak <armin.novak@thincast.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.
*/
#include <winpr/path.h>
#include <winpr/file.h>
#include <winpr/sysinfo.h>
#include <freerdp/crypto/certificate_store.h>
/* Some certificates copied from /usr/share/ca-certificates */
static const char pem1[] = "-----BEGIN CERTIFICATE-----\n"
"MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH\n"
"MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\n"
"QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy\n"
"MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl\n"
"cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB\n"
"AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM\n"
"f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX\n"
"mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7\n"
"zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P\n"
"fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc\n"
"vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4\n"
"Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp\n"
"zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO\n"
"Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW\n"
"k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+\n"
"DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF\n"
"lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n"
"HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW\n"
"Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1\n"
"d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z\n"
"XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR\n"
"gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3\n"
"d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv\n"
"J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg\n"
"DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM\n"
"+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy\n"
"F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9\n"
"SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws\n"
"E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl\n"
"-----END CERTIFICATE-----";
static const char pem2[] = "-----BEGIN CERTIFICATE-----\n"
"MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH\n"
"MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\n"
"QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy\n"
"MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl\n"
"cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB\n"
"AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv\n"
"CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg\n"
"GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu\n"
"XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd\n"
"re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu\n"
"PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1\n"
"mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K\n"
"8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj\n"
"x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR\n"
"nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0\n"
"kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok\n"
"twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\n"
"HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp\n"
"8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT\n"
"vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT\n"
"z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA\n"
"pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb\n"
"pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB\n"
"R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R\n"
"RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk\n"
"0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC\n"
"5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF\n"
"izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn\n"
"yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC\n"
"-----END CERTIFICATE-----";
static const char pem3[] = "-----BEGIN CERTIFICATE-----\n"
"MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw\n"
"CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n"
"MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n"
"MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n"
"Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA\n"
"IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout\n"
"736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A\n"
"DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"
"DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk\n"
"fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA\n"
"njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd\n"
"-----END CERTIFICATE-----";
static const char pem4[] = "-----BEGIN CERTIFICATE-----\n"
"MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw\n"
"CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\n"
"MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\n"
"MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\n"
"Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA\n"
"IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu\n"
"hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l\n"
"xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud\n"
"DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0\n"
"CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx\n"
"sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w==\n"
"-----END CERTIFICATE-----";
static int prepare(const char* currentFileV2)
{
int rc = -1;
const char* hosts[] = { "#somecomment\r\n"
"someurl 3389 ff:11:22:dd c3ViamVjdA== aXNzdWVy\r\n"
" \t#anothercomment\r\n"
"otherurl\t3389\taa:bb:cc:dd\tsubject2\tissuer2\r" };
FILE* fc = nullptr;
fc = winpr_fopen(currentFileV2, "w+");
if (!fc)
goto finish;
for (size_t i = 0; i < ARRAYSIZE(hosts); i++)
{
if (fwrite(hosts[i], strlen(hosts[i]), 1, fc) != 1)
goto finish;
}
rc = 0;
finish:
if (fc)
(void)fclose(fc);
return rc;
}
static BOOL setup_config(rdpSettings** settings)
{
BOOL rc = FALSE;
char* path = nullptr;
char sname[8192];
SYSTEMTIME systemTime;
if (!settings)
goto fail;
*settings = freerdp_settings_new(0);
if (!*settings)
goto fail;
GetSystemTime(&systemTime);
(void)sprintf_s(sname, sizeof(sname),
"TestKnownHostsCurrent-%04" PRIu16 "%02" PRIu16 "%02" PRIu16 "%02" PRIu16
"%02" PRIu16 "%02" PRIu16 "%04" PRIu16,
systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour,
systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
path = GetKnownSubPath(KNOWN_PATH_TEMP, sname);
if (!path)
goto fail;
if (!winpr_PathFileExists(path))
{
if (!CreateDirectoryA(path, nullptr))
{
(void)fprintf(stderr, "Could not create %s!\n", path);
goto fail;
}
}
rc = freerdp_settings_set_string(*settings, FreeRDP_ConfigPath, path);
fail:
free(path);
return rc;
}
static BOOL equal(const char* a, const char* b)
{
if (!a && !b)
return TRUE;
if (!a || !b)
return FALSE;
return strcmp(a, b) == 0;
}
static BOOL compare(const rdpCertificateData* data, const rdpCertificateData* stored)
{
if (!data || !stored)
return FALSE;
if (!equal(freerdp_certificate_data_get_subject(data),
freerdp_certificate_data_get_subject(stored)))
return FALSE;
if (!equal(freerdp_certificate_data_get_issuer(data),
freerdp_certificate_data_get_issuer(stored)))
return FALSE;
if (!equal(freerdp_certificate_data_get_fingerprint(data),
freerdp_certificate_data_get_fingerprint(stored)))
return FALSE;
return TRUE;
}
static BOOL pem_equal(const char* a, const char* b)
{
return strcmp(a, b) == 0;
}
static BOOL compare_ex(const rdpCertificateData* data, const rdpCertificateData* stored)
{
if (!compare(data, stored))
return FALSE;
if (!pem_equal(freerdp_certificate_data_get_pem(data),
freerdp_certificate_data_get_pem(stored)))
return FALSE;
return TRUE;
}
static BOOL test_get_data(rdpCertificateStore* store, const rdpCertificateData* data)
{
BOOL res = 0;
rdpCertificateData* stored = freerdp_certificate_store_load_data(
store, freerdp_certificate_data_get_host(data), freerdp_certificate_data_get_port(data));
if (!stored)
return FALSE;
res = compare(data, stored);
freerdp_certificate_data_free(stored);
return res;
}
static BOOL test_get_data_ex(rdpCertificateStore* store, const rdpCertificateData* data)
{
BOOL res = 0;
rdpCertificateData* stored = freerdp_certificate_store_load_data(
store, freerdp_certificate_data_get_host(data), freerdp_certificate_data_get_port(data));
if (!stored)
return FALSE;
res = compare_ex(data, stored);
freerdp_certificate_data_free(stored);
return res;
}
static BOOL test_certs_dir(void)
{
BOOL rc = FALSE;
rdpSettings* settings = nullptr;
rdpCertificateStore* store = nullptr;
rdpCertificateData* data1 = nullptr;
rdpCertificateData* data2 = nullptr;
rdpCertificateData* data3 = nullptr;
rdpCertificateData* data4 = nullptr;
printf("%s\n", __func__);
if (!setup_config(&settings))
goto fail;
printf("freerdp_certificate_store_new()\n");
store = freerdp_certificate_store_new(settings);
if (!store)
goto fail;
{
printf("freerdp_certificate_data_new()\n");
data1 = freerdp_certificate_data_new_from_pem("somehost", 1234, pem1, strlen(pem1));
data2 = freerdp_certificate_data_new_from_pem("otherhost", 4321, pem2, strlen(pem2));
data3 = freerdp_certificate_data_new_from_pem("otherhost4", 444, pem3, strlen(pem3));
data4 = freerdp_certificate_data_new_from_pem("otherhost", 4321, pem4, strlen(pem4));
if (!data1 || !data2 || !data3 || !data4)
goto fail;
/* Find non existing in empty store */
printf("freerdp_certificate_store_load_data on empty store\n");
if (test_get_data(store, data1))
goto fail;
if (test_get_data_ex(store, data1))
goto fail;
if (test_get_data(store, data2))
goto fail;
if (test_get_data_ex(store, data2))
goto fail;
if (test_get_data(store, data3))
goto fail;
if (test_get_data_ex(store, data3))
goto fail;
/* Add certificates */
printf("freerdp_certificate_store_save_data\n");
if (!freerdp_certificate_store_save_data(store, data1))
goto fail;
if (!freerdp_certificate_store_save_data(store, data2))
goto fail;
/* Find non existing in non empty store */
printf("freerdp_certificate_store_load_data on filled store, non existing value\n");
if (test_get_data(store, data3))
goto fail;
if (test_get_data_ex(store, data3))
goto fail;
/* Add remaining certs */
printf("freerdp_certificate_store_save_data\n");
if (!freerdp_certificate_store_save_data(store, data3))
goto fail;
/* Check existing can all be found */
printf("freerdp_certificate_store_load_data on filled store, existing value\n");
if (!test_get_data(store, data1))
goto fail;
if (!test_get_data_ex(store, data1))
goto fail;
if (!test_get_data(store, data2))
goto fail;
if (!test_get_data_ex(store, data2))
goto fail;
if (!test_get_data(store, data3))
goto fail;
if (!test_get_data_ex(store, data3))
goto fail;
/* Modify existing entry */
printf("freerdp_certificate_store_save_data modify data\n");
if (!freerdp_certificate_store_save_data(store, data4))
goto fail;
/* Check new data is in store */
printf("freerdp_certificate_store_load_data check modified data can be loaded\n");
if (!test_get_data(store, data4))
goto fail;
if (!test_get_data_ex(store, data4))
goto fail;
/* Check old data is no longer valid */
printf("freerdp_certificate_store_load_data check original data no longer there\n");
if (test_get_data(store, data2))
goto fail;
if (test_get_data_ex(store, data2))
goto fail;
/* Delete a cert */
printf("freerdp_certificate_store_remove_data\n");
if (!freerdp_certificate_store_remove_data(store, data3))
goto fail;
/* Delete non existing, should succeed */
printf("freerdp_certificate_store_remove_data missing value\n");
if (!freerdp_certificate_store_remove_data(store, data3))
goto fail;
printf("freerdp_certificate_store_load_data on filled store, existing value\n");
if (!test_get_data(store, data1))
goto fail;
if (!test_get_data_ex(store, data1))
goto fail;
if (!test_get_data(store, data4))
goto fail;
if (!test_get_data_ex(store, data4))
goto fail;
printf("freerdp_certificate_store_load_data on filled store, removed value\n");
if (test_get_data(store, data3))
goto fail;
if (test_get_data_ex(store, data3))
goto fail;
}
rc = TRUE;
fail:
printf("freerdp_certificate_data_free %d\n", rc);
freerdp_certificate_data_free(data1);
freerdp_certificate_data_free(data2);
freerdp_certificate_data_free(data3);
freerdp_certificate_data_free(data4);
freerdp_certificate_store_free(store);
freerdp_settings_free(settings);
return rc;
}
int TestKnownHosts(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!test_certs_dir())
return -1;
return 0;
}

View File

@@ -0,0 +1,41 @@
-----BEGIN CERTIFICATE-----
MIIHNzCCBR+gAwIBAgICEAAwDQYJKoZIhvcNAQEFBQAwcDEqMCgGA1UEAwwhQURN
SU5JU1RSQVRJT04gQ0VOVFJBTEUgREVTIFRFU1RTMQswCQYDVQQGEwJGUjEcMBoG
A1UECgwTTUlOSVNURVJFIERFUyBURVNUUzEXMBUGA1UECwwOMDAwMiAxMTAwMTQw
MTYwHhcNMTgwNTE4MDkyNTU1WhcNMTkwNTEzMDkyNTU1WjCBvzEkMCIGA1UEAwwb
VEVTVEpFQU4gVEVTVE1BUlRJTiA5OTk5OTk5MQswCQYDVQQGEwJGUjEcMBoGA1UE
CgwTTUlOSVNURVJFIERFUyBURVNUUzEXMBUGA1UECwwOMDAwMiAxMTAwMTQwMTYx
EjAQBgNVBAsMCVBFUlNPTk5FUzEXMBUGCgmSJomT8ixkAQEMBzk5OTk5OTkxETAP
BgNVBCoMCFRFU1RKRUFOMRMwEQYDVQQEDApURVNUTUFSVElOMIICIjANBgkqhkiG
9w0BAQEFAAOCAg8AMIICCgKCAgEA3yc22RDYc+Vc6F26/LONvaYkdTVDiCgbh9Ik
6pLF5izNpfdQ/YZU25h/UPECdchYX31UEErVOYudOBOHtU4fNjTO0oK5Va/DoFln
LnfwNpAlBZfogG+yy8fK4yLxG+raoSKDR/5P3hmTqKJqw1WpkwcVE2EDqkP1clMZ
L5cvJj6gLJa2q0JCdoKe7NntZkgpIk5ZHUZm2JYC30xL7XHfvvb/i0OZLpPOIekT
DCzxr9HTjbqe+BRZix2UiGpXzjIlDm6EEQNebZqf5kKgcbkxIDWcVraE0kO3TqJI
P4FBUeuxLqGwQ0AMKrZ+j8U7KAoM9WUoIFcmm8nYGo4hT6ugNIQ9nwQSgyH3yGH1
PU2k12Ovv2Ft8C/IFuusXxTOJprcFxtjE7qYZ44tmvlozlDOBOJYjLiURAh3r5LL
TadgArZ3XVMyWlwlTEy9qX59izY9Zz27kd5H11DOz5ezopHAWwP6sgCvWeNDyx8Q
I3jY8TYzJHahN2bknP2fqwwdGqFCrHItJx2DhDe2ruTk6vvbnwGgYqGzv+RtdNbW
CL4IMEQQKG9AM40WCz9pu32/vOaQ+hrYyCQMCtli0DSauB+K2IFPsAcz5OAaITJv
LenMt8mUP9NWHWfr5WYm0tuUCCU4dUT38MqkkdQv7oly1LHkvUdMU+Nk/Ki0Q83U
9gMvaPcCAwEAAaOCAYkwggGFMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgXg
MDIGA1UdJQQrMCkGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQCAgYHKwYB
BQIDBDAdBgNVHQ4EFgQUXs4RKN+vUVsZjEW/J6qo6EZTLZUwHwYDVR0jBBgwFoAU
fUXj4k7OA3d8KylcprhMptiOL10wgbIGA1UdEQSBqjCBp6A9BgYrBgEFAgKgMzAx
oBYbFGtwbi50ZXN0LmV4YW1wbGUuY29toRcwFRsTdGVzdGplYW4udGVzdG1hcnRp
bqBABgorBgEEAYI3FAIDoDIMMHRlc3RqZWFuLnRlc3RtYXJ0aW4uOTk5OTk5OUB1
cG4udGVzdC5leGFtcGxlLmNvbYEkdGVzdGplYW4udGVzdG1hcnRpbkB0ZXN0LmV4
YW1wbGUuY29tMDEGA1UdEgQqMCiCEnJvb3RjYS5leGFtcGxlLmNvbYISaW50ZWNh
LmV4YW1wbGUuY29tMAkGA1UdIAQCMAAwDQYJKoZIhvcNAQEFBQADggIBAKRDovf+
CCnV2mtXnzH5JlduOjPWJmGB5a8HLPvakfAm4wQ0YyAViE1tar0V9lhG6nCogWWa
28D+eM5vLPjVE8ebq5UjIv76x6gWoJkQ3HtfVJvn9UfXwax6IqT7hb1fAHBqu0rj
uSnSxf1wIzPMp9Lb5x3jBu9ryNMiLUzeY1slBvosOXKlmprPhGWfPYYNCZo2bGJI
1w5alGDgTBcWKl7icJjAIuCpyRTnKCsaN3kyDU7C5aUhsm9AriPiNErzRI+l5+eu
Ywg3MZ7Yfjd3rXb6JleT0ZnCh/nFtVLIccWaI4phCrYTGz6odNIqrZ6X23Pt6Rx3
ZbQjtj4ipMdvbvJbS90aFMrTyfqhVLOxHy+setDcmPOixUgXlx8ZjFI9vgFUeJbo
OKrkLw4ITUduO+9MplBX7Kt/iCS/CbTfPlHMv03Xb6rbjqHxTJZCCu5QMNHiBeHV
l8FK5R6gv+9FuCl8uPHwGh/jelQp51cVORlQWeKpqWdwTi0Q3VeVeQAG5RR34xgT
cQa8h9AqkxYajhxKUmbUlaoYGd8TwUQLrS2jZxp/9geyApVQLAQ27CyAK5HyHSCA
uqCKsM0gFQyCL4IbXQyFMWgjXZYaorHFjVuMhYEkgWui/9sv+7sMAV5JzROeAw3l
4+D7yhywwuRzH2SzoavzGpWGMUveVsdLMRk9
-----END CERTIFICATE-----

View File

@@ -0,0 +1,241 @@
#include <winpr/file.h>
#include <winpr/string.h>
#include "../x509_utils.h"
typedef char* (*get_field_pr)(const X509*);
typedef struct
{
enum
{
DISABLED,
ENABLED,
} status;
const char* field_description;
get_field_pr get_field;
const char* expected_result;
} certificate_test_t;
static char* x509_utils_subject_common_name_wo_length(const X509* xcert)
{
size_t length = 0;
return x509_utils_get_common_name(xcert, &length);
}
static char* certificate_path(const char* filename)
{
/*
Assume the .pem file is in the same directory as this source file.
Assume that __FILE__ will be a valid path to this file, even from the current working directory
where the tests are run. (ie. no chdir occurs between compilation and test running, or __FILE__
is an absolute path).
*/
static const char dirsep = '/';
#ifdef TEST_SOURCE_DIR
const char* file = TEST_SOURCE_DIR;
const size_t flen = strlen(file) + sizeof(dirsep) + strlen(filename) + sizeof(char);
char* result = calloc(1, flen);
if (!result)
return nullptr;
(void)_snprintf(result, flen, "%s%c%s", file, dirsep, filename);
return result;
#else
const char* file = __FILE__;
const char* last_dirsep = strrchr(file, dirsep);
if (last_dirsep)
{
const size_t filenameLen = strlen(filename);
const size_t dirsepLen = last_dirsep - file + 1;
char* result = malloc(dirsepLen + filenameLen + 1);
if (!result)
return nullptr;
strncpy(result, file, dirsepLen);
strncpy(result + dirsepLen, filename, filenameLen + 1);
return result;
}
else
{
/* No dirsep => relative path in same directory */
return _strdup(filename);
}
#endif
}
static const certificate_test_t certificate_tests[] = {
{ ENABLED, "Certificate Common Name", x509_utils_subject_common_name_wo_length,
"TESTJEAN TESTMARTIN 9999999" },
{ ENABLED, "Certificate subject", x509_utils_get_subject,
"CN = TESTJEAN TESTMARTIN 9999999, C = FR, O = MINISTERE DES TESTS, OU = 0002 110014016, OU "
"= PERSONNES, UID = 9999999, GN = TESTJEAN, SN = TESTMARTIN" },
{ DISABLED, "Kerberos principal name", nullptr, "testjean.testmartin@kpn.test.example.com" },
{ ENABLED, "Certificate e-mail", x509_utils_get_email, "testjean.testmartin@test.example.com"
},
{ ENABLED, "Microsoft's Universal Principal Name", x509_utils_get_upn,
"testjean.testmartin.9999999@upn.test.example.com" },
{ ENABLED, "Certificate issuer", x509_utils_get_issuer,
"CN = ADMINISTRATION CENTRALE DES TESTS, C = FR, O = MINISTERE DES TESTS, OU = 0002 "
"110014016" },
};
static int TestCertificateFile(const char* certificate_path,
const certificate_test_t* ccertificate_tests, size_t count)
{
int success = 0;
X509* certificate = x509_utils_from_pem(certificate_path, strlen(certificate_path), TRUE);
if (!certificate)
{
printf("%s: failure: cannot read certificate file '%s'\n", __func__, certificate_path);
success = -1;
goto fail;
}
for (size_t i = 0; i < count; i++)
{
const certificate_test_t* test = &ccertificate_tests[i];
char* result = nullptr;
if (test->status == DISABLED)
{
continue;
}
result = (test->get_field ? test->get_field(certificate) : nullptr);
if (result)
{
printf("%s: crypto got %-40s -> \"%s\"\n", __func__, test->field_description, result);
if (0 != strcmp(result, test->expected_result))
{
printf("%s: failure: for %s, actual: \"%s\", expected \"%s\"\n", __func__,
test->field_description, result, test->expected_result);
success = -1;
}
free(result);
}
else
{
printf("%s: failure: cannot get %s\n", __func__, test->field_description);
}
}
fail:
X509_free(certificate);
return success;
}
/* clang-format off */
/*
These certificates were generated with the following commands:
openssl ecparam -name P-256 -out /tmp/p256.pem
openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha1_cert.pem -sha1
openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha256_cert.pem -sha256
openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha384_cert.pem -sha384
openssl req -x509 -newkey ec:/tmp/p256.pem -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out ecdsa_sha512_cert.pem -sha512
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha1_cert.pem -sha1
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha256_cert.pem -sha256
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha384_cert.pem -sha384
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pkcs1_sha512_cert.pem -sha512
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha1_cert.pem -sha1 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha256_cert.pem -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha384_cert.pem -sha384 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha512_cert.pem -sha512 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest
openssl req -x509 -newkey rsa:2048 -keyout /dev/null -days 3650 -nodes -subj "/CN=Test" -out rsa_pss_sha256_mgf1_sha384_cert.pem -sha256 -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest -sigopt rsa_mgf1_md:sha384
*/
/* clang-format on */
typedef struct
{
const char* filename;
WINPR_MD_TYPE expected;
} signature_alg_test_t;
static const signature_alg_test_t signature_alg_tests[] = {
{ "rsa_pkcs1_sha1_cert.pem", WINPR_MD_SHA1 },
{ "rsa_pkcs1_sha256_cert.pem", WINPR_MD_SHA256 },
{ "rsa_pkcs1_sha384_cert.pem", WINPR_MD_SHA384 },
{ "rsa_pkcs1_sha512_cert.pem", WINPR_MD_SHA512 },
{ "ecdsa_sha1_cert.pem", WINPR_MD_SHA1 },
{ "ecdsa_sha256_cert.pem", WINPR_MD_SHA256 },
{ "ecdsa_sha384_cert.pem", WINPR_MD_SHA384 },
{ "ecdsa_sha512_cert.pem", WINPR_MD_SHA512 },
{ "rsa_pss_sha1_cert.pem", WINPR_MD_SHA1 },
{ "rsa_pss_sha256_cert.pem", WINPR_MD_SHA256 },
{ "rsa_pss_sha384_cert.pem", WINPR_MD_SHA384 },
{ "rsa_pss_sha512_cert.pem", WINPR_MD_SHA512 },
/*
PSS may use different digests for the message hash and MGF-1 hash. In this case, RFC 5929
leaves the tls-server-end-point hash unspecified, so it should return WINPR_MD_NONE.
*/
{ "rsa_pss_sha256_mgf1_sha384_cert.pem", WINPR_MD_NONE },
};
static int TestSignatureAlgorithm(const signature_alg_test_t* test)
{
int success = 0;
WINPR_MD_TYPE signature_alg = WINPR_MD_NONE;
char* path = certificate_path(test->filename);
X509* certificate = x509_utils_from_pem(path, strlen(path), TRUE);
if (!certificate)
{
printf("%s: failure: cannot read certificate file '%s'\n", __func__, path);
success = -1;
goto fail;
}
signature_alg = x509_utils_get_signature_alg(certificate);
if (signature_alg != test->expected)
{
const char* signature_alg_string =
signature_alg == WINPR_MD_NONE ? "none" : winpr_md_type_to_string(signature_alg);
const char* expected_string =
test->expected == WINPR_MD_NONE ? "none" : winpr_md_type_to_string(test->expected);
printf("%s: failure: for \"%s\", actual: %s, expected %s\n", __func__, test->filename,
signature_alg_string, expected_string);
success = -1;
goto fail;
}
fail:
X509_free(certificate);
free(path);
return success;
}
int Test_x509_utils(int argc, char* argv[])
{
char* cert_path = certificate_path("Test_x509_cert_info.pem");
int ret = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
ret = TestCertificateFile(cert_path, certificate_tests, ARRAYSIZE(certificate_tests));
free(cert_path);
if (ret != 0)
return ret;
for (size_t i = 0; i < ARRAYSIZE(signature_alg_tests); i++)
{
ret = TestSignatureAlgorithm(&signature_alg_tests[i]);
if (ret != 0)
return ret;
}
return ret;
}

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBcjCCARigAwIBAgIUP9Q+so71qPtrK898RUJibDMRSYQwCQYHKoZIzj0EATAP
MQ0wCwYDVQQDDARUZXN0MB4XDTI0MDIwNjAzMDkzNFoXDTM0MDIwMzAzMDkzNFow
DzENMAsGA1UEAwwEVGVzdDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHj9x4Vr
7pGzpilUY799+mWmOsJwtxFZ3lPNRy+wsfxibRE6e2T0Gk2Ifysl8Vya6Ynwrd2d
7ztAk+b6HF+1lgqjUzBRMB0GA1UdDgQWBBSX66LoFThh5RCXaeAS+sjGPmLxKTAf
BgNVHSMEGDAWgBSX66LoFThh5RCXaeAS+sjGPmLxKTAPBgNVHRMBAf8EBTADAQH/
MAkGByqGSM49BAEDSQAwRgIhAJf3H7PWAZ/5G2SbBKF5jzBVlmWLiVmfanLOvttf
9DFUAiEA3CnntihpfkAGjUCav7CojYfz8hqe0d6F9ZStfzV4t3g=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBdDCCARmgAwIBAgIUUDFppYHwhd7smJSH6W8QSLttoNEwCgYIKoZIzj0EAwIw
DzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA5MzRaFw0zNDAyMDMwMzA5MzRa
MA8xDTALBgNVBAMMBFRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATfQ2ox
CF1xh6Dwcsi3BqyUIlKxgY3J2qOSmOzepOMLWhPpiDsneKskpKx4b5JM92mmIyiq
UMMR7mXlclDHyQtro1MwUTAdBgNVHQ4EFgQUgoV/fxICc75gTRslwgvs/I1YbOUw
HwYDVR0jBBgwFoAUgoV/fxICc75gTRslwgvs/I1YbOUwDwYDVR0TAQH/BAUwAwEB
/zAKBggqhkjOPQQDAgNJADBGAiEAyVInWgy3JVEUPDSpjNseJKPie/hINfO6KbrK
IqGQ0+ACIQDk/oXOIwFZr26TTghYKOn12aOuPCxOqeBu5ObeFMf91Q==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBczCCARmgAwIBAgIUDT9Rw/q4CH5WmNCTbGbNI964MQwwCgYIKoZIzj0EAwMw
DzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA5MzRaFw0zNDAyMDMwMzA5MzRa
MA8xDTALBgNVBAMMBFRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR0oA7y
QeXAp65otDob8Uqmtthdub5T7fbzMr/qnUTxNYoUpXKnde28Cvan4QPCuepHmVPw
sVx94UX8RIlrXAhdo1MwUTAdBgNVHQ4EFgQUFfghIBL0wxknjd9I8+Wub61VJk4w
HwYDVR0jBBgwFoAUFfghIBL0wxknjd9I8+Wub61VJk4wDwYDVR0TAQH/BAUwAwEB
/zAKBggqhkjOPQQDAwNIADBFAiB66sAH30kMoOsMHu5vb1hUl3DPRLb30WbtVSBC
ZHEDyQIhAK1xgDA005XqcC77o8gzQFFsxIkQrCHTqre2LEGndxLA
-----END CERTIFICATE-----

View File

@@ -0,0 +1,10 @@
-----BEGIN CERTIFICATE-----
MIIBdDCCARmgAwIBAgIUfb77WvmuJ7r/9aLrhvfHymxssoQwCgYIKoZIzj0EAwQw
DzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA5MzRaFw0zNDAyMDMwMzA5MzRa
MA8xDTALBgNVBAMMBFRlc3QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARewOb8
HMJXad76YWUSaPLMUH8IKpzO0iZkQ2d1SSCylEMdrPJKhi54r7/y6m5LXMejyQzi
eB2eiNju1yfs1tkoo1MwUTAdBgNVHQ4EFgQUGSveQiJxuzwWX1jIRXdHCzdvj7Ew
HwYDVR0jBBgwFoAUGSveQiJxuzwWX1jIRXdHCzdvj7EwDwYDVR0TAQH/BAUwAwEB
/zAKBggqhkjOPQQDBANJADBGAiEAspDRGKH6Nlp+XUxyHKc3IGN5WVIg5ezGHJDR
9+Q8RAkCIQDVWMxflgAII4D+t2Z8nT4bavUImHD26kbaGtR2/DYPVw==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,2 @@
someurl ff:11:22:dd
otherurl aa:bb:cc:dd

View File

@@ -0,0 +1,2 @@
someurl 3389 ff:11:22:dd
otherurl 3389 aa:bb:cc:dd

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUNveSXnRIoI24dM6PxYjhcXQhsgkwDQYJKoZIhvcNAQEF
BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA2MzVaFw0zNDAyMDMwMzA2
MzVaMA8xDTALBgNVBAMMBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCy23VMgwiNO32ovOxt+7CJCR5Ep1qn1tV5//ONhLEoz+VhEbMTYQNHK1WY
E9isGrcRUVLsBehIFP02ImgOGv1Yep/P1pY+A/fLpy4NhHoLYxmvdhAKQG3TB5P1
s7GuXTaK4/Kp8CzVYP7xZu7zI2TWWolkCDYZvkewR5QOuyiAstvZp5IoIx0J9mo2
rI5DqnSmK+zzaYTMaGyWFLXOQJZi+k+RUB3XUFZSid69thW0rfi7tC0fyUm7fP7H
72/27aBmW1S/8hUYSfu88kCCmEEu+KGXbmyNPEVMJcM9cZ43TVMGUPodOXuDydm/
IKnsZaRnGPi8IBzn0y6k/8ZdmJojAgMBAAGjUzBRMB0GA1UdDgQWBBRsqgoFZ83u
IMZzUjsVI5N73Izq1DAfBgNVHSMEGDAWgBRsqgoFZ83uIMZzUjsVI5N73Izq1DAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCIFhCbHy6U6EYHogXE
xnoHdtzitHTUU+mymWeQxWxSvZjWX8xdJdbWQyktSCChKrRnQE+P/e5+HOZFn9q5
jwgj3HwZZwFqt/0nSX7pjEvTOmwEXTo/QBlyHaLSdrxbd85gahkXP1br6vI0yWcT
kkKZCFiqbsGGqcoErRyZjYfJBgaZ5AMYXEKkKCgRJ59Sln+mW+fpta7dmgmnPIdX
Jl/ovEHr0X+PgwLby8BxCb5pOyb6CQvUNWfngtzgm76vricszpmDl4HeBAb0IkZ1
0hlnHEBNgP46/2EhPvD1QehYOmr5HRi2xSPC9gOW8pkbRRFuCDZwoSfav023xim6
lOS3
-----END CERTIFICATE-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUK9L1Ajn7PiMRVvR6YUoATBxgD2wwDQYJKoZIhvcNAQEL
BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA2MzVaFw0zNDAyMDMwMzA2
MzVaMA8xDTALBgNVBAMMBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQC73wUpONU4o0abFykUC08A2deojU/+SGomnN8V51VDVeC2BGUljLabjABm
iluh/yUJ0zDHp6x1spr6wdDVzRMvjjxC/G2HVzYRn/3zXhZ0q9avPnCe1hgawHZY
FG4vgysJ1jtPSR97E4MvSg6v6mATCU2ttvceENFwo5FQ6nfBv+rHpepRyOKUtfsa
gxRCWU0uHwyahNsYzWOrbkEcpoQAowAoHZh9EbjyNNbCX0/C3yew/GX8mNBz1UOV
X4taJsOW52LKQ2xgSwl31m6VYmNqqfzTA6pr96PpYqsuAT3WYBBnIk+XT+Yhq3Cs
n96PvHAovUiBwWptvzsICvtAUwCjAgMBAAGjUzBRMB0GA1UdDgQWBBQGUaK7joEk
yFR6FtsA4UjF+FPrRTAfBgNVHSMEGDAWgBQGUaK7joEkyFR6FtsA4UjF+FPrRTAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAkYpNiExDwuqfFCHpz
jWFSkL9drTxMuasGfmq634FSe050TGKl8yUZhz215PoEDHcutGAw6JV59DJMI/wo
Xq4MpJiCLlUu8BOixU41rlyc9XkV6twcnQlkVhYWV07390SP4uKGXRR03yGwjN7C
RDv/4z4t9iMQhiIYkN0EXKvymjRMhb9GocfTQFSXq6vy0fr4MuwnuOkr6EcU41Hr
pQ4nNLuj2P7cAaaFo5RaD/eXzsE4CfgoltmTp+Ir3deXG/RKrJ9V/YTx5d6pqssp
5f7nDKmhiYn6wtF4TlOnujppT1Mr4UZF7lJAfwUX80bYQBbpfczHBKSYQ/Th5Mtx
ediM
-----END CERTIFICATE-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUHpfVpdCFW4e5gmPMyNN/CZou028wDQYJKoZIhvcNAQEM
BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA2MzVaFw0zNDAyMDMwMzA2
MzVaMA8xDTALBgNVBAMMBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCMlHin4N8tMbfDqIahZlyOhBX392Pcyo4bUM7ipUEdLdz1SSCf7/gh4SgH
ihCbN/NWiYoobnR7iS6vBgUMQgzdivwOia0jydcifpf2UR2D1KjBnolALqaRYpDB
zdEOnJ4Nm21sOlfCM/QoiMdPWZlbzisXyGBYHR8E8G5Snfy13cRvfV+M9/epbvwV
1o4m/wPu2H+Q+XVRHJY1H7jAtjUtLSUwii0jc134c9nHOdqXB9fgcn7etkQKXWPv
w7Dg/OWXURpABGjflS6w4UhECzlPInmd9fBtLngf7n9Sa4b0rcdWEWcTbWORmEbj
xoHyWNiRbdzIc0zUb42DFuFgeUcFAgMBAAGjUzBRMB0GA1UdDgQWBBRm5loY68pA
VVlVVcIL3zTv1sCYWjAfBgNVHSMEGDAWgBRm5loY68pAVVlVVcIL3zTv1sCYWjAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4IBAQAJFNU9dUNpIbbsgyty
RRd5SVD/4ziOOPoKO6jobBenPxNQNbaOQmENImW0WbgOHWW1kX15afCThMocibKl
OIVoOf2tcrseAYdfsOzEMwDc1hI5y68vNRV32rJamix1223wPhs1xuoX8bn0t0VB
RK4kgb9U1mxSKERjcgtp2Pph5DeflCQDeNayBGAA5PCL4ydO61DEKbVo1Cyqtw9n
yhae7AUR4zzRnZEh1eePd06cXSIYwmTkiJLSF7ILsZnGcqy4bO8yJPrqXT01Iq0S
RzQ2hgyldKD0kJ3EBmki3mIrnswCzNqyXux1CXIR1FjIA5SnwycqiiPUUPLckinq
DtQX
-----END CERTIFICATE-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUTAXZy2Zojh8wh4YSdc2vMBiytGIwDQYJKoZIhvcNAQEN
BQAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA2MzVaFw0zNDAyMDMwMzA2
MzVaMA8xDTALBgNVBAMMBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQDH2av9vKvlzlSB63ylb8OyacC6zkhoiH4ECkVt7DRvBBnHYX39eeXg1KNk
yHBaLA+9ASXucckqAQ8hWlIW7y9KLYYsK8+ajDUnWkzPj+eOonSc2oTbSBtEP8V4
3TihNGRgyLSZYj6HxUrFWW6pc0Kz7yAG83YNzR8uWCEZV8wYAdBqZDO1RAOd2tLr
DFLI+DJwMtgDquT1Uo/xDsx0p9+tSGXm4eXjlZQguhA4tuP4eJP7qvwR+khJNN9X
u2igfO3zMY26wX4m/kPdljxh5vRc2DVIePfKbTKLyfpsydQUSoKVzlSQG7QJAUmC
jcqgIGQqYy8f6Nq4fRzgiSQOULmPAgMBAAGjUzBRMB0GA1UdDgQWBBQMYhVmmR0c
9DJmJZOqHawdWDVFJDAfBgNVHSMEGDAWgBQMYhVmmR0c9DJmJZOqHawdWDVFJDAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDQUAA4IBAQBr2I9uvQ8Ccn5yw1Ig
7k2Qmg/HOCFGh42Ufz1PAc9dc1CzBzMlFucJ42shES98Spyh4R+ZCJmlZ9PneViA
s9NW+mHLAFJ2Fuct4XY8RnyDYqTne0in6eQykF33gl79sdYSPuBfAxMfJqUoQo+b
tF0N0DA00i5z69wcF5LQJLRbeTh7T1qikkay3ODMJYpCDVb7GOhWCt4hOkxOszuL
SoJ2gFMGoEKbt19IzcMctsSPgpQCNYZirU9x3l/Ptw5zgUQMEngwuutuhDMxb5Ht
2QHTGt3jwzlOi+lvHFH1AMW3e8/XRZa7jFUa+KCr8vD3yfDimx9J0ESmlUrLxOZG
ayG7
-----END CERTIFICATE-----

View File

@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIC/zCCAeegAwIBAgIUDZuL7xQULUgo1YKeDIJDFdsVCuMwDQYJKoZIhvcNAQEK
MAAwDzENMAsGA1UEAwwEVGVzdDAeFw0yNDAyMDYwMzA2MzVaFw0zNDAyMDMwMzA2
MzVaMA8xDTALBgNVBAMMBFRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQCxLekSxv8aVwB5p9UImN+jipKNJUb8fOrtOVHjeO5jK9G920vneZsubpq/
NngzpjT2A+HOkee9oNG92O8U7DGyGXiZugmeFE6kunPQ+GP5za3XfZqwvAu53+Pg
oyrBGl5ssowRimRDXOH3/x7WSKD2lvQNgLWOS5NTIZethXx29Xj+/nKJ33im8ra/
YcEB6CANHmo2bDCz8Q54iHjIOro+MiP10oQM2ERzuZREn/+xCFuYYGhXKb3fqy+r
Ze/tIVqEr7yMVpp76RxFM3I4PadHt8T9Q5Oat9FXWaqG/1fBtBKXt6jElkqnd+vv
KtRMEqQ4XNeMJi6Q4oyLsmxGB8q7AgMBAAGjUzBRMB0GA1UdDgQWBBQuCV/GISEn
D18xN3d1KEOo13c/2zAfBgNVHSMEGDAWgBQuCV/GISEnD18xN3d1KEOo13c/2zAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCjAAA4IBAQA+nnvGYJwVP5byl+XB
1KoFLv4SOS9kDypjv+4Mjo3/houn7DAf1+6ewfmAN7edkElTngpz6rGzJgARULbO
djKi6AJDDqF9NzWbJZC/UEVqqVY1Znw1v8Hwz31YFaoARLWwuKQrKjLZUHphsMok
iWdwusebfQ1SK9b8QTW7s8CncC+PbOHH1UnHhM1XWGz49sWZo/KnHCGL0pGROSro
gT2zEDDk/bpslalcavEGcw2wttaBiYovdpgUHzOIcTxLz/yQGAfaCk/cO8x5mS9y
w6CZVaH6K1TrmXGWBoHtLQ+JMC7msms3PmBZ4XCc8zAqjhVI4o71Jlbrd/Bag0lm
X+89
-----END CERTIFICATE-----

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZzCCAhugAwIBAgIUChf3gsm9kG9E05UbcYnEzQ/fSfkwQQYJKoZIhvcNAQEK
MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF
AKIDAgEgMA8xDTALBgNVBAMMBFRlc3QwHhcNMjQwMjA2MDMwNjM1WhcNMzQwMjAz
MDMwNjM1WjAPMQ0wCwYDVQQDDARUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAtk22hPAmTxSTSs8MUJInf7N6byDo1LlEgw5juF8y1bVxhoF0U4Ug
u0zqCVtar/JBPmcVofM4IU4+GoIRfI9qyyyD5EQup2e8exUn/Bku+7XTmlCSKfUI
T4u/zpe/qJCyRwT6pLP4POtoqsmrRf+u7zVvS7VHafPioxLLrOIbB750AYmi7y/n
h8MreJHrQ901IQV4Ktf2QgaFrjJFgXD307iTGoiRt+UxkmGsOOzFt+N91lM30ad+
sCgmc1NVVyo6p4RByl7ilxlQGCATOQ9wTVwehgmtFGpU0denhqViNUU8yuPZ3pTD
w1o9uWTAKdUuMySSUiKmmlE+qnlFfR9uPwIDAQABo1MwUTAdBgNVHQ4EFgQUQPus
AuQ9cNE9E2+vZXKtKAtfDIUwHwYDVR0jBBgwFoAUQPusAuQ9cNE9E2+vZXKtKAtf
DIUwDwYDVR0TAQH/BAUwAwEB/zBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQC
AQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASADggEBAGpIH7ic
GwD2vzTGgFhHrh4NkGu9OwI8WA342DKfJq0Cx+BjaFsglhjLVkbqP74XjgQUSqt+
yp09xgrZdIP8yMJr7mjqQpV7tolB1yrD0h0jtoMe0Pc1+wVwjkMQjsewwSDJvV9O
RjXqmzoIjUAXmASk5JdZ5oDBK6bg7m8/hJyEJnWdOuiVUJTyPz+Y1/6rpzwH/+xX
x3sO6OaWMSIc/ovF1Y7kQ4mwgPjLuq1X8Dx7xM9rcJak5cGAwUwQqvOaD3y9ZQzg
E+kH4mtWw29xRTejbiMSbnbfR8LfZdS2+10ESK66SXChVz3Q7shkR9Gs6kTJiXTu
FUtRtO0G5aoSHeA=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZzCCAhugAwIBAgIUVvhEAv0+q7jndH6xf2RJjaiaIVswQQYJKoZIhvcNAQEK
MDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIF
AKIDAgEgMA8xDTALBgNVBAMMBFRlc3QwHhcNMjQwMjA2MDMwNjM2WhcNMzQwMjAz
MDMwNjM2WjAPMQ0wCwYDVQQDDARUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAsC6mgspqVdpZrhHUz5zoY4Sklg+spYAz8axxEyJf3Tdg427ildSH
BLi62qZ+gTXLHgUvdkMWEv8NWODHY5DNuTiL65LjV0gN7eIzVbzc0alTdbp+nc7H
HHZHBedD3CRT4LHqlK9LVTYcQk8qNtnKJ7vxNJHN3vpmr0zxxJf3nle7Ymnx2FiK
mXDu3vQEDSU6eyOu5dk0IsEWlMOCYu5w8dpbdGaE4az3IaHXsDnmfLfIWUaILQxw
Mj0UhNg+vnZsanB7CWiLZOIawEpa6dx2Zcasyi8V6lZV1sxYQrskRbAj9uGtDmtd
kTKeCxtj7Bgj9e6aIN2rh7keH5+pqissxwIDAQABo1MwUTAdBgNVHQ4EFgQUpXlx
cN8y7RSFQjPjO3Kb1x0GWcYwHwYDVR0jBBgwFoAUpXlxcN8y7RSFQjPjO3Kb1x0G
WcYwDwYDVR0TAQH/BAUwAwEB/zBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQC
AQUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCASADggEBAKcp+pEt
hei70aV9+j+p/cjChDSqQyipIvyqt0wdHiM/5WwGO4LnR/JxWqHHGtlQjgphKVpV
bjRMBivrbb//LNb0cx5z3o9hx2H6ITwpwrRA9dvfE1C+hOBbxhUYNtk4jpH1DneX
8l/A9Lm6kmZ02KrJok8VEGthtZqypkRAQFxStY2EuopzWVzdceJYa4AOB26r7F0T
Z5BzO/Piy39lQtZGyMZ2R71ppEDWXVrSaqxV64aFnh3/2aCgFUzuV2FYr1NZ+zQu
EBJZIdd0IvhcgHkiUD6qtlYNJ1H7Jf9hSRDM7d7AblhgxVB1eRU+7Ve1o47LfQqz
iVybceMUSCyMFLk=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZzCCAhugAwIBAgIUI7KZPJ4nCR/qFZWD2bagetWGJRQwQQYJKoZIhvcNAQEK
MDSgDzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIF
AKIDAgEwMA8xDTALBgNVBAMMBFRlc3QwHhcNMjQwMjA2MDMwNjM2WhcNMzQwMjAz
MDMwNjM2WjAPMQ0wCwYDVQQDDARUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA3/WFdrzn9QQjowwZqCmgIJcCWy+8aIjHLlE4OLWyv4rVAdQluFRA
Af1lsKY+ZD6gvWOKhnSNT7z4WfH03vUNpkqOt1kqtjMsuRsy4Zh2n54LYM2IHkVh
+oZpq8dBESkffAHOLwkl6Be/iZE3t4Z2hdrzhFEt1iGxtfwduIcku/geciLKM0D0
1UHtVzb0762hx94IX506vxAdvNAK48gTSobBBWQJhHz2a2tvt2ON3eRian41qwxW
Nsl7OUyss6PmVXYS3JbmZgyzT0nSHQWEVlEcQOI3UxjNqeK2HMyMDYLgIvVD+IER
1nMT4AUyWDGkRR6bFITS2BmW7JbxQ40ZiwIDAQABo1MwUTAdBgNVHQ4EFgQUn9rP
CfGH3OFxdNXCFbAQrYlgXd4wHwYDVR0jBBgwFoAUn9rPCfGH3OFxdNXCFbAQrYlg
Xd4wDwYDVR0TAQH/BAUwAwEB/zBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQC
AgUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATADggEBADJ6+6Us
KXbcpyzapxqQGr0rGkGOPf45Vo2EY0H2A0YY9CHYrU/jq2xijHgnw89I3z9Z3h4q
kSD51Rr/3V/e5ffcYwRabC6S515anOgxtCE4cgHEqgIdzrwD3EFGm74D+MnJRTy8
UpE5pZTaHNRno80rz1kRYN2MHO/sHqQCpFoZ8SI3+Nik5m3FSe2Umb3FLTvrOAcm
biGgj4cF52w1D7XAAo/3bAH9Rt0/FRK46nULoEX54RJHlFN8f3kzBucNNfoHNPBR
2lMQJM1VzpPkjR5rLOwAYKEfvIvwDVEgDMpD4+P5/FMX/fnSm4kVaiZMwLLrtJlX
pS9N0mDlr4wk9hw=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDZzCCAhugAwIBAgIUCJHdINzBcxL45k+BWvKYcpH8vzQwQQYJKoZIhvcNAQEK
MDSgDzANBglghkgBZQMEAgMFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgMF
AKIDAgFAMA8xDTALBgNVBAMMBFRlc3QwHhcNMjQwMjA2MDMwNjM2WhcNMzQwMjAz
MDMwNjM2WjAPMQ0wCwYDVQQDDARUZXN0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA6jzuBGtb+D/0ZWRqZAcZh6bkClCbHdiz1yOPuh4APfyfsFGDSRBt
ArMH+l2/Mr7TXeAfdDHX//bNBclPzu0mKWtVamh7s3WWoPBX+r10ie94YxErFY+6
Po5pGDnSabVawdnXd5FqCdFVpmXP12Ii9qKuRD13XAPJML0Cz9z4pOL0ioWvvUqn
MyWwa8zT8pfK4AJe4XGilQ2uxwJV922XQxv6rY8aOFuwozkNa0/ceN1A2EKIwShB
P3/3Z+9maz4YMg/VgmJfEY/xekawDZ7MC8CY9G/alE1YqHERvR5BwekrnFHSErM4
ArSUJnavXm7rdB1OCp4kAKXkLvu2H/8AMQIDAQABo1MwUTAdBgNVHQ4EFgQUrLTl
C9kRXzElbsDUCc/ePO75qNkwHwYDVR0jBBgwFoAUrLTlC9kRXzElbsDUCc/ePO75
qNkwDwYDVR0TAQH/BAUwAwEB/zBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQC
AwUAoRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAwUAogMCAUADggEBAGMyu2fZ
NpvIJQxXPSfhgf8idvGwu7YFdZ/Ct0/HIHJ1h+j2WFwubr/Rcwqu+u6Nq09oMq+H
5EWDtOona78WIQ/RrIs6ltVJBDpirIGjra0IKpYGqYHUEj00u1OZkiQzmMLRT80W
jEe38fATXbpmLhXA8bqlOHuMot2OTWzKEtST4knAAYUCFPIS94mODR4faeqDBwIB
JpYBj2sRwZDU4QbERvLTQMD27kE2ynF4duI4NB6k9w3fJSe60ki5m4avYmiQgj5/
304UD/AEf+xlMCLh3R8ZGST10zV5M1Wm7czuQ0AKsv45pSltDvWl52OjL3W5CLAJ
iu1eLWBVbFPTK68=
-----END CERTIFICATE-----

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Transport Layer Security
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.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_LIB_CRYPTO_TLS_H
#define FREERDP_LIB_CRYPTO_TLS_H
#include <winpr/crt.h>
#include <winpr/sspi.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <freerdp/api.h>
#include <freerdp/types.h>
#include <freerdp/crypto/certificate_store.h>
#include <winpr/stream.h>
#define TLS_ALERT_LEVEL_WARNING 1
#define TLS_ALERT_LEVEL_FATAL 2
#define TLS_ALERT_DESCRIPTION_CLOSE_NOTIFY 0
#define TLS_ALERT_DESCRIPTION_UNEXPECTED_MESSAGE 10
#define TLS_ALERT_DESCRIPTION_BAD_RECORD_MAC 20
#define TLS_ALERT_DESCRIPTION_DECRYPTION_FAILED 21
#define TLS_ALERT_DESCRIPTION_RECORD_OVERFLOW 22
#define TLS_ALERT_DESCRIPTION_DECOMPRESSION_FAILURE 30
#define TLS_ALERT_DESCRIPTION_HANSHAKE_FAILURE 40
#define TLS_ALERT_DESCRIPTION_NO_CERTIFICATE 41
#define TLS_ALERT_DESCRIPTION_BAD_CERTIFICATE 42
#define TLS_ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE 43
#define TLS_ALERT_DESCRIPTION_CERTIFICATE_REVOKED 44
#define TLS_ALERT_DESCRIPTION_CERTIFICATE_EXPIRED 45
#define TLS_ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN 46
#define TLS_ALERT_DESCRIPTION_ILLEGAL_PARAMETER 47
#define TLS_ALERT_DESCRIPTION_UNKNOWN_CA 48
#define TLS_ALERT_DESCRIPTION_ACCESS_DENIED 49
#define TLS_ALERT_DESCRIPTION_DECODE_ERROR 50
#define TLS_ALERT_DESCRIPTION_DECRYPT_ERROR 51
#define TLS_ALERT_DESCRIPTION_EXPORT_RESTRICTION 60
#define TLS_ALERT_DESCRIPTION_PROTOCOL_VERSION 70
#define TLS_ALERT_DESCRIPTION_INSUFFICIENT_SECURITY 71
#define TLS_ALERT_DESCRIPTION_INTERNAL_ERROR 80
#define TLS_ALERT_DESCRIPTION_USER_CANCELED 90
#define TLS_ALERT_DESCRIPTION_NO_RENEGOTIATION 100
#define TLS_ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION 110
typedef struct rdp_tls rdpTls;
struct rdp_tls
{
SSL* ssl;
BIO* bio;
void* tsg;
SSL_CTX* ctx;
BYTE* PublicKey;
DWORD PublicKeyLength;
rdpContext* context;
SecPkgContext_Bindings* Bindings;
rdpCertificateStore* certificate_store;
BIO* underlying;
const char* hostname;
const char* serverName;
int port;
int alertLevel;
int alertDescription;
BOOL isGatewayTransport;
BOOL isClientMode;
};
/** @brief result of a handshake operation */
typedef enum
{
TLS_HANDSHAKE_SUCCESS, /*!< handshake was successful */
TLS_HANDSHAKE_CONTINUE, /*!< handshake is not completed */
TLS_HANDSHAKE_ERROR, /*!< an error (probably IO error) happened */
TLS_HANDSHAKE_VERIFY_ERROR /*!< Certificate verification failed (client mode) */
} TlsHandshakeResult;
#ifdef __cplusplus
extern "C"
{
#endif
WINPR_ATTR_NODISCARD
FREERDP_LOCAL const SSL_METHOD* freerdp_tls_get_ssl_method(BOOL isDtls, BOOL isClient);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL int freerdp_tls_connect(rdpTls* tls, BIO* underlying);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL TlsHandshakeResult freerdp_tls_connect_ex(rdpTls* tls, BIO* underlying,
const SSL_METHOD* methods);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL freerdp_tls_accept(rdpTls* tls, BIO* underlying, rdpSettings* settings);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL TlsHandshakeResult freerdp_tls_accept_ex(rdpTls* tls, BIO* underlying,
rdpSettings* settings,
const SSL_METHOD* methods);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL TlsHandshakeResult freerdp_tls_handshake(rdpTls* tls);
FREERDP_LOCAL BOOL freerdp_tls_send_alert(rdpTls* tls);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL int freerdp_tls_write_all(rdpTls* tls, const BYTE* data, size_t length);
FREERDP_LOCAL int freerdp_tls_set_alert_code(rdpTls* tls, int level, int description);
FREERDP_LOCAL void freerdp_tls_free(rdpTls* tls);
WINPR_ATTR_MALLOC(freerdp_tls_free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL rdpTls* freerdp_tls_new(rdpContext* context);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_CRYPTO_TLS_H */

View File

@@ -0,0 +1,934 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Cryptographic Abstraction Layer
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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.
*/
#include <openssl/objects.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/err.h>
#include <freerdp/config.h>
#include <winpr/crt.h>
#include <winpr/string.h>
#include <winpr/assert.h>
#include <freerdp/log.h>
#include "x509_utils.h"
#define TAG FREERDP_TAG("crypto")
BYTE* x509_utils_get_hash(const X509* xcert, const char* hash, size_t* length)
{
UINT32 fp_len = EVP_MAX_MD_SIZE;
BYTE* fp = nullptr;
const EVP_MD* md = EVP_get_digestbyname(hash);
if (!md)
{
WLog_ERR(TAG, "System does not support %s hash!", hash);
return nullptr;
}
if (!xcert || !length)
{
WLog_ERR(TAG, "Invalid arguments: xcert=%p, length=%p",
WINPR_CXX_COMPAT_CAST(const void*, xcert),
WINPR_CXX_COMPAT_CAST(const void*, length));
return nullptr;
}
fp = calloc(fp_len + 1, sizeof(BYTE));
if (!fp)
{
WLog_ERR(TAG, "could not allocate %" PRIu32 " bytes", fp_len);
return nullptr;
}
if (X509_digest(xcert, md, fp, &fp_len) != 1)
{
free(fp);
WLog_ERR(TAG, "certificate does not have a %s hash!", hash);
return nullptr;
}
*length = fp_len;
return fp;
}
static char* crypto_print_name(const X509_NAME* name)
{
char* buffer = nullptr;
BIO* outBIO = BIO_new(BIO_s_mem());
if (X509_NAME_print_ex(outBIO, name, 0, XN_FLAG_ONELINE) > 0)
{
UINT64 size = BIO_number_written(outBIO);
if (size > INT_MAX)
goto fail;
buffer = calloc(1, (size_t)size + 1);
if (!buffer)
goto fail;
ERR_clear_error();
const int rc = BIO_read(outBIO, buffer, (int)size);
if (rc <= 0)
{
free(buffer);
buffer = nullptr;
goto fail;
}
}
fail:
BIO_free_all(outBIO);
return buffer;
}
char* x509_utils_get_subject(const X509* xcert)
{
char* subject = nullptr;
if (!xcert)
{
WLog_ERR(TAG, "Invalid certificate nullptr");
return nullptr;
}
subject = crypto_print_name(X509_get_subject_name(xcert));
if (!subject)
WLog_WARN(TAG, "certificate does not have a subject!");
return subject;
}
/* GENERAL_NAME type labels */
static const char* general_name_type_labels[] = { "OTHERNAME", "EMAIL ", "DNS ",
"X400 ", "DIRNAME ", "EDIPARTY ",
"URI ", "IPADD ", "RID " };
static const char* general_name_type_label(int general_name_type)
{
if ((0 <= general_name_type) &&
((size_t)general_name_type < ARRAYSIZE(general_name_type_labels)))
{
return general_name_type_labels[general_name_type];
}
else
{
static char buffer[80] = WINPR_C_ARRAY_INIT;
(void)snprintf(buffer, sizeof(buffer), "Unknown general name type (%d)", general_name_type);
return buffer;
}
}
/*
map_subject_alt_name(x509, general_name_type, mapper, data)
Call the function mapper with subjectAltNames found in the x509
certificate and data. if generate_name_type is GEN_ALL, the the
mapper is called for all the names, else it's called only for names
of the given type.
We implement two extractors:
- a string extractor that can be used to get the subjectAltNames of
the following types: GEN_URI, GEN_DNS, GEN_EMAIL
- a ASN1_OBJECT filter/extractor that can be used to get the
subjectAltNames of OTHERNAME type.
Note: usually, it's a string, but some type of otherNames can be
associated with different classes of objects. eg. a KPN may be a
sequence of realm and principal name, instead of a single string
object.
Not implemented yet: extractors for the types: GEN_X400, GEN_DIRNAME,
GEN_EDIPARTY, GEN_RID, GEN_IPADD (the later can contain nul-bytes).
mapper(name, data, index, count)
The mapper is passed:
- the GENERAL_NAME selected,
- the data,
- the index of the general name in the subjectAltNames,
- the total number of names in the subjectAltNames.
The last parameter let's the mapper allocate arrays to collect objects.
Note: if names are filtered, not all the indices from 0 to count-1 are
passed to mapper, only the indices selected.
When the mapper returns 0, map_subject_alt_name stops the iteration immediately.
*/
#define GEN_ALL (-1)
typedef int (*general_name_mapper_pr)(GENERAL_NAME* name, void* data, int index, int count);
static void map_subject_alt_name(const X509* x509, int general_name_type,
general_name_mapper_pr mapper, void* data)
{
int num = 0;
STACK_OF(GENERAL_NAME)* gens = nullptr;
gens = X509_get_ext_d2i(x509, NID_subject_alt_name, nullptr, nullptr);
if (!gens)
{
return;
}
num = sk_GENERAL_NAME_num(gens);
for (int i = 0; (i < num); i++)
{
GENERAL_NAME* name = sk_GENERAL_NAME_value(gens, i);
if (name)
{
if ((general_name_type == GEN_ALL) || (general_name_type == name->type))
{
if (!mapper(name, data, i, num))
{
break;
}
}
}
}
sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
}
/*
extract_string -- string extractor
- the strings array is allocated lazily, when we first have to store a
string.
- allocated contains the size of the strings array, or -1 if
allocation failed.
- count contains the actual count of strings in the strings array.
- maximum limits the number of strings we can store in the strings
array: beyond, the extractor returns 0 to short-cut the search.
extract_string stores in the string list OPENSSL strings,
that must be freed with OPENSSL_free.
*/
typedef struct string_list
{
char** strings;
size_t allocated;
size_t count;
size_t maximum;
} string_list;
static void string_list_initialize(string_list* list)
{
list->strings = nullptr;
list->allocated = 0;
list->count = 0;
list->maximum = INT_MAX;
}
static void string_list_allocate(string_list* list, size_t allocate_count)
{
if (!list->strings && list->allocated == 0)
{
list->strings = (char**)calloc(allocate_count, sizeof(char*));
list->allocated = list->strings ? allocate_count : 0;
list->count = 0;
}
}
static void string_list_free(string_list* list)
{
/* Note: we don't free the contents of the strings array: this */
/* is handled by the caller, either by returning this */
/* content, or freeing it itself. */
free((void*)list->strings);
}
static int extract_string(GENERAL_NAME* name, void* data, int index, int count)
{
string_list* list = data;
unsigned char* cstring = nullptr;
ASN1_STRING* str = nullptr;
WINPR_UNUSED(index);
switch (name->type)
{
case GEN_URI:
str = name->d.uniformResourceIdentifier;
break;
case GEN_DNS:
str = name->d.dNSName;
break;
case GEN_EMAIL:
str = name->d.rfc822Name;
break;
default:
return 1;
}
if ((ASN1_STRING_to_UTF8(&cstring, str)) < 0)
{
WLog_ERR(TAG, "ASN1_STRING_to_UTF8() failed for %s: %s",
general_name_type_label(name->type), ERR_error_string(ERR_get_error(), nullptr));
return 1;
}
string_list_allocate(list, WINPR_ASSERTING_INT_CAST(WINPR_CIPHER_TYPE, count));
if (list->allocated <= 0)
{
OPENSSL_free(cstring);
return 0;
}
list->strings[list->count] = (char*)cstring;
list->count++;
if (list->count >= list->maximum)
{
return 0;
}
return 1;
}
/*
extract_othername_object -- object extractor.
- the objects array is allocated lazily, when we first have to store a
string.
- allocated contains the size of the objects array, or -1 if
allocation failed.
- count contains the actual count of objects in the objects array.
- maximum limits the number of objects we can store in the objects
array: beyond, the extractor returns 0 to short-cut the search.
extract_othername_objects stores in the objects array ASN1_TYPE *
pointers directly obtained from the GENERAL_NAME.
*/
typedef struct object_list
{
ASN1_OBJECT* type_id;
char** strings;
size_t allocated;
size_t count;
size_t maximum;
} object_list;
static void object_list_initialize(object_list* list)
{
list->type_id = nullptr;
list->strings = nullptr;
list->allocated = 0;
list->count = 0;
list->maximum = INT_MAX;
}
static void object_list_allocate(object_list* list, size_t allocate_count)
{
if (!list->strings && (list->allocated == 0) && (allocate_count > 0))
{
list->strings = (char**)calloc(allocate_count, sizeof(list->strings[0]));
list->allocated = list->strings ? allocate_count : 0;
list->count = 0;
}
}
static char* object_string(ASN1_TYPE* object)
{
char* result = nullptr;
unsigned char* utf8String = nullptr;
/* TODO: check that object.type is a string type. */
const int length = ASN1_STRING_to_UTF8(&utf8String, object->value.asn1_string);
if (length < 0)
{
return nullptr;
}
result = strndup((char*)utf8String, WINPR_ASSERTING_INT_CAST(size_t, length));
OPENSSL_free(utf8String);
return result;
}
static void object_list_free(object_list* list)
{
WINPR_ASSERT(list);
free((void*)list->strings);
}
static int extract_othername_object_as_string(GENERAL_NAME* name, void* data, int index, int count)
{
object_list* list = data;
WINPR_UNUSED(index);
if (count < 0)
return -1;
if (name->type != GEN_OTHERNAME)
{
return 1;
}
if (0 != OBJ_cmp(name->d.otherName->type_id, list->type_id))
{
return 1;
}
object_list_allocate(list, WINPR_ASSERTING_INT_CAST(size_t, count));
if (list->allocated <= 0)
{
return 0;
}
list->strings[list->count] = object_string(name->d.otherName->value);
if (list->strings[list->count])
{
list->count++;
}
if (list->count >= list->maximum)
{
return 0;
}
return 1;
}
char* x509_utils_get_email(const X509* x509)
{
char* result = nullptr;
string_list list;
string_list_initialize(&list);
list.maximum = 1;
map_subject_alt_name(x509, GEN_EMAIL, extract_string, &list);
if (list.count == 0)
{
string_list_free(&list);
return nullptr;
}
result = _strdup(list.strings[0]);
OPENSSL_free(list.strings[0]);
string_list_free(&list);
return result;
}
char* x509_utils_get_upn(const X509* x509)
{
char* result = nullptr;
object_list list = WINPR_C_ARRAY_INIT;
object_list_initialize(&list);
list.type_id = OBJ_nid2obj(NID_ms_upn);
list.maximum = 1;
map_subject_alt_name(x509, GEN_OTHERNAME, extract_othername_object_as_string, &list);
if (list.count == 0)
{
object_list_free(&list);
return nullptr;
}
result = list.strings[0];
object_list_free(&list);
return result;
}
char* x509_utils_get_date(const X509* x509, BOOL startDate)
{
WINPR_ASSERT(x509);
const ASN1_TIME* date = startDate ? X509_get0_notBefore(x509) : X509_get0_notAfter(x509);
if (!date)
return nullptr;
BIO* bmem = BIO_new(BIO_s_mem());
if (!bmem)
return nullptr;
char* str = nullptr;
if (ASN1_TIME_print(bmem, date))
{
BUF_MEM* bptr = nullptr;
BIO_get_mem_ptr(bmem, &bptr);
str = strndup(bptr->data, bptr->length);
}
else
{ // Log error
}
BIO_free_all(bmem);
return str;
}
void x509_utils_dns_names_free(size_t count, size_t* lengths, char** dns_names)
{
free(lengths);
if (dns_names)
{
for (size_t i = 0; i < count; i++)
{
if (dns_names[i])
{
OPENSSL_free(dns_names[i]);
}
}
free((void*)dns_names);
}
}
char** x509_utils_get_dns_names(const X509* xcert, size_t* count, size_t** lengths)
{
char** result = nullptr;
string_list list = WINPR_C_ARRAY_INIT;
string_list_initialize(&list);
map_subject_alt_name(xcert, GEN_DNS, extract_string, &list);
(*count) = list.count;
if (list.count <= 0)
{
string_list_free(&list);
return nullptr;
}
/* lengths are not useful, since we converted the
strings to utf-8, there cannot be nul-bytes in them. */
result = (char**)calloc(list.count, sizeof(*result));
(*lengths) = calloc(list.count, sizeof(**lengths));
if (!result || !(*lengths))
{
string_list_free(&list);
free((void*)result);
free(*lengths);
(*lengths) = nullptr;
(*count) = 0;
return nullptr;
}
for (size_t i = 0; i < list.count; i++)
{
result[i] = list.strings[i];
(*lengths)[i] = strlen(result[i]);
}
string_list_free(&list);
return result;
}
char* x509_utils_get_issuer(const X509* xcert)
{
char* issuer = nullptr;
if (!xcert)
{
WLog_ERR(TAG, "Invalid certificate nullptr");
return nullptr;
}
issuer = crypto_print_name(X509_get_issuer_name(xcert));
if (!issuer)
WLog_WARN(TAG, "certificate does not have an issuer!");
return issuer;
}
static int asn1_object_cmp(const ASN1_OBJECT* const* a, const ASN1_OBJECT* const* b)
{
if (!a || !b)
return (a == b) ? 0 : (a ? 1 : -1);
if (!*a || !*b)
return (*a == *b) ? 0 : (*a ? 1 : -1);
return OBJ_cmp(*a, *b);
}
BOOL x509_utils_check_eku(const X509* xcert, int nid)
{
BOOL ret = FALSE;
STACK_OF(ASN1_OBJECT)* oid_stack = nullptr;
ASN1_OBJECT* oid = nullptr;
if (!xcert)
return FALSE;
oid = OBJ_nid2obj(nid);
if (!oid)
return FALSE;
oid_stack = X509_get_ext_d2i(xcert, NID_ext_key_usage, nullptr, nullptr);
if (!oid_stack)
return FALSE;
sk_ASN1_OBJECT_set_cmp_func(oid_stack, asn1_object_cmp);
if (sk_ASN1_OBJECT_find(oid_stack, oid) >= 0)
ret = TRUE;
sk_ASN1_OBJECT_pop_free(oid_stack, ASN1_OBJECT_free);
return ret;
}
void x509_utils_print_info(const X509* xcert)
{
char* fp = nullptr;
char* issuer = nullptr;
char* subject = nullptr;
subject = x509_utils_get_subject(xcert);
issuer = x509_utils_get_issuer(xcert);
fp = (char*)x509_utils_get_hash(xcert, "sha256", nullptr);
if (!fp)
{
WLog_ERR(TAG, "error computing fingerprint");
goto out_free_issuer;
}
WLog_INFO(TAG, "Certificate details:");
WLog_INFO(TAG, "\tSubject: %s", subject);
WLog_INFO(TAG, "\tIssuer: %s", issuer);
WLog_INFO(TAG, "\tThumbprint: %s", fp);
WLog_INFO(TAG,
"The above X.509 certificate could not be verified, possibly because you do not have "
"the CA certificate in your certificate store, or the certificate has expired. "
"Please look at the OpenSSL documentation on how to add a private CA to the store.");
free(fp);
out_free_issuer:
free(issuer);
free(subject);
}
X509* x509_utils_from_pem(const char* data, size_t len, BOOL fromFile)
{
X509* x509 = nullptr;
BIO* bio = nullptr;
if (fromFile)
bio = BIO_new_file(data, "rb");
else
{
if (len > INT_MAX)
return nullptr;
bio = BIO_new_mem_buf(data, (int)len);
}
if (!bio)
{
WLog_ERR(TAG, "BIO_new failed for certificate");
return nullptr;
}
x509 = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
BIO_free_all(bio);
if (!x509)
WLog_ERR(TAG, "PEM_read_bio_X509 returned nullptr [input length %" PRIuz "]", len);
return x509;
}
static WINPR_MD_TYPE hash_nid_to_winpr(int hash_nid)
{
switch (hash_nid)
{
case NID_md2:
return WINPR_MD_MD2;
case NID_md4:
return WINPR_MD_MD4;
case NID_md5:
return WINPR_MD_MD5;
case NID_sha1:
return WINPR_MD_SHA1;
case NID_sha224:
return WINPR_MD_SHA224;
case NID_sha256:
return WINPR_MD_SHA256;
case NID_sha384:
return WINPR_MD_SHA384;
case NID_sha512:
return WINPR_MD_SHA512;
case NID_ripemd160:
return WINPR_MD_RIPEMD160;
#if (OPENSSL_VERSION_NUMBER >= 0x1010101fL) && !defined(LIBRESSL_VERSION_NUMBER)
case NID_sha3_224:
return WINPR_MD_SHA3_224;
case NID_sha3_256:
return WINPR_MD_SHA3_256;
case NID_sha3_384:
return WINPR_MD_SHA3_384;
case NID_sha3_512:
return WINPR_MD_SHA3_512;
case NID_shake128:
return WINPR_MD_SHAKE128;
case NID_shake256:
return WINPR_MD_SHAKE256;
#endif
case NID_undef:
default:
return WINPR_MD_NONE;
}
}
static WINPR_MD_TYPE get_rsa_pss_digest(const X509_ALGOR* alg)
{
WINPR_MD_TYPE ret = WINPR_MD_NONE;
WINPR_MD_TYPE message_digest = WINPR_MD_NONE;
WINPR_MD_TYPE mgf1_digest = WINPR_MD_NONE;
int param_type = 0;
const void* param_value = nullptr;
const ASN1_STRING* sequence = nullptr;
const unsigned char* inp = nullptr;
RSA_PSS_PARAMS* params = nullptr;
X509_ALGOR* mgf1_digest_alg = nullptr;
/* The RSA-PSS digest is encoded in a complex structure, defined in
https://www.rfc-editor.org/rfc/rfc4055.html. */
X509_ALGOR_get0(nullptr, &param_type, &param_value, alg);
/* param_type and param_value the parameter in ASN1_TYPE form, but split into two parameters. A
SEQUENCE is has type V_ASN1_SEQUENCE, and the value is an ASN1_STRING with the encoded
structure. */
if (param_type != V_ASN1_SEQUENCE)
goto end;
sequence = param_value;
/* Decode the structure. */
inp = ASN1_STRING_get0_data(sequence);
params = d2i_RSA_PSS_PARAMS(nullptr, &inp, ASN1_STRING_length(sequence));
if (params == nullptr)
goto end;
/* RSA-PSS uses two hash algorithms, a message digest and also an MGF function which is, itself,
parameterized by a hash function. Both fields default to SHA-1, so we must also check for the
value being nullptr. */
message_digest = WINPR_MD_SHA1;
if (params->hashAlgorithm != nullptr)
{
const ASN1_OBJECT* obj = nullptr;
X509_ALGOR_get0(&obj, nullptr, nullptr, params->hashAlgorithm);
message_digest = hash_nid_to_winpr(OBJ_obj2nid(obj));
if (message_digest == WINPR_MD_NONE)
goto end;
}
mgf1_digest = WINPR_MD_SHA1;
if (params->maskGenAlgorithm != nullptr)
{
const ASN1_OBJECT* obj = nullptr;
int mgf_param_type = 0;
const void* mgf_param_value = nullptr;
const ASN1_STRING* mgf_param_sequence = nullptr;
/* First, check this is MGF-1, the only one ever defined. */
X509_ALGOR_get0(&obj, &mgf_param_type, &mgf_param_value, params->maskGenAlgorithm);
if (OBJ_obj2nid(obj) != NID_mgf1)
goto end;
/* MGF-1 is, itself, parameterized by a hash function, encoded as an AlgorithmIdentifier. */
if (mgf_param_type != V_ASN1_SEQUENCE)
goto end;
mgf_param_sequence = mgf_param_value;
inp = ASN1_STRING_get0_data(mgf_param_sequence);
mgf1_digest_alg = d2i_X509_ALGOR(nullptr, &inp, ASN1_STRING_length(mgf_param_sequence));
if (mgf1_digest_alg == nullptr)
goto end;
/* Finally, extract the digest. */
X509_ALGOR_get0(&obj, nullptr, nullptr, mgf1_digest_alg);
mgf1_digest = hash_nid_to_winpr(OBJ_obj2nid(obj));
if (mgf1_digest == WINPR_MD_NONE)
goto end;
}
/* If the two digests do not match, it is ambiguous which to return. tls-server-end-point leaves
it undefined, so return none.
https://www.rfc-editor.org/rfc/rfc5929.html#section-4.1 */
if (message_digest != mgf1_digest)
goto end;
ret = message_digest;
end:
RSA_PSS_PARAMS_free(params);
X509_ALGOR_free(mgf1_digest_alg);
return ret;
}
WINPR_MD_TYPE x509_utils_get_signature_alg(const X509* xcert)
{
WINPR_ASSERT(xcert);
const int nid = X509_get_signature_nid(xcert);
if (nid == NID_rsassaPss)
{
const X509_ALGOR* alg = nullptr;
X509_get0_signature(nullptr, &alg, xcert);
return get_rsa_pss_digest(alg);
}
int hash_nid = 0;
if (OBJ_find_sigid_algs(nid, &hash_nid, nullptr) != 1)
return WINPR_MD_NONE;
return hash_nid_to_winpr(hash_nid);
}
char* x509_utils_get_common_name(const X509* xcert, size_t* plength)
{
X509_NAME* subject_name = X509_get_subject_name(xcert);
if (subject_name == nullptr)
return nullptr;
const int index = X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
if (index < 0)
return nullptr;
const X509_NAME_ENTRY* entry = X509_NAME_get_entry(subject_name, index);
if (entry == nullptr)
return nullptr;
const ASN1_STRING* entry_data = X509_NAME_ENTRY_get_data(entry);
if (entry_data == nullptr)
return nullptr;
BYTE* common_name_raw = nullptr;
const int length = ASN1_STRING_to_UTF8(&common_name_raw, entry_data);
if (length < 0)
return nullptr;
if (plength)
*plength = (size_t)length;
char* common_name = _strdup((char*)common_name_raw);
OPENSSL_free(common_name_raw);
return common_name;
}
static int verify_cb(int ok, X509_STORE_CTX* csc)
{
if (ok != 1)
{
WINPR_ASSERT(csc);
int err = X509_STORE_CTX_get_error(csc);
int derr = X509_STORE_CTX_get_error_depth(csc);
X509* where = X509_STORE_CTX_get_current_cert(csc);
const char* what = X509_verify_cert_error_string(err);
char* name = x509_utils_get_subject(where);
WLog_WARN(TAG, "Certificate verification failure '%s (%d)' at stack position %d", what, err,
derr);
WLog_WARN(TAG, "%s", name);
free(name);
}
return ok;
}
BOOL x509_utils_verify(X509* xcert, STACK_OF(X509) * chain, const char* certificate_store_path)
{
const int purposes[3] = { X509_PURPOSE_SSL_SERVER, X509_PURPOSE_SSL_CLIENT, X509_PURPOSE_ANY };
X509_STORE_CTX* csc = nullptr;
BOOL status = FALSE;
X509_LOOKUP* lookup = nullptr;
if (!xcert)
return FALSE;
X509_STORE* cert_ctx = X509_STORE_new();
if (cert_ctx == nullptr)
goto end;
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
OpenSSL_add_all_algorithms();
#else
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
OPENSSL_INIT_LOAD_CONFIG,
nullptr);
#endif
if (X509_STORE_set_default_paths(cert_ctx) != 1)
goto end;
lookup = X509_STORE_add_lookup(cert_ctx, X509_LOOKUP_hash_dir());
if (lookup == nullptr)
goto end;
X509_LOOKUP_add_dir(lookup, nullptr, X509_FILETYPE_DEFAULT);
if (certificate_store_path != nullptr)
{
X509_LOOKUP_add_dir(lookup, certificate_store_path, X509_FILETYPE_PEM);
}
X509_STORE_set_flags(cert_ctx, 0);
for (size_t i = 0; i < ARRAYSIZE(purposes); i++)
{
int err = -1;
int rc = -1;
int purpose = purposes[i];
csc = X509_STORE_CTX_new();
if (csc == nullptr)
goto skip;
if (!X509_STORE_CTX_init(csc, cert_ctx, xcert, chain))
goto skip;
X509_STORE_CTX_set_purpose(csc, purpose);
X509_STORE_CTX_set_verify_cb(csc, verify_cb);
rc = X509_verify_cert(csc);
err = X509_STORE_CTX_get_error(csc);
skip:
X509_STORE_CTX_free(csc);
if (rc == 1)
{
status = TRUE;
break;
}
else if (err != X509_V_ERR_INVALID_PURPOSE)
break;
}
X509_STORE_free(cert_ctx);
end:
return status;
}

View File

@@ -0,0 +1,96 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Cryptographic Abstraction Layer
*
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2023 Armin Novak <anovak@thincast.com>
* Copyright 2023 Thincast Technologies GmbH
*
* 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_LIB_X509_UTILS_H
#define FREERDP_LIB_X509_UTILS_H
#include <winpr/custom-crypto.h>
#include <openssl/x509.h>
#include <freerdp/api.h>
#ifdef __cplusplus
extern "C"
{
#endif
WINPR_ATTR_NODISCARD
FREERDP_LOCAL WINPR_MD_TYPE x509_utils_get_signature_alg(const X509* xcert);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BYTE* x509_utils_get_hash(const X509* xcert, const char* hash, size_t* length);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BYTE* x509_utils_to_pem(const X509* xcert, const STACK_OF(X509) * chain,
size_t* length);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL X509* x509_utils_from_pem(const char* data, size_t length, BOOL fromFile);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char* x509_utils_get_subject(const X509* xcert);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char* x509_utils_get_issuer(const X509* xcert);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char* x509_utils_get_email(const X509* x509);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char* x509_utils_get_upn(const X509* x509);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char* x509_utils_get_date(const X509* x509, BOOL startDate);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char* x509_utils_get_common_name(const X509* xcert, size_t* plength);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
FREERDP_LOCAL char** x509_utils_get_dns_names(const X509* xcert, size_t* count,
size_t** pplengths);
FREERDP_LOCAL void x509_utils_dns_names_free(size_t count, size_t* lengths, char** dns_names);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL x509_utils_check_eku(const X509* xcert, int nid);
FREERDP_LOCAL void x509_utils_print_info(const X509* xcert);
WINPR_ATTR_NODISCARD
FREERDP_LOCAL BOOL x509_utils_verify(X509* xcert, STACK_OF(X509) * chain,
const char* certificate_store_path);
#ifdef __cplusplus
}
#endif
#endif /* FREERDP_LIB_X509_UTILS_H */