/src/FreeRDP/libfreerdp/crypto/per.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * FreeRDP: A Remote Desktop Protocol Implementation |
3 | | * ASN.1 Packed Encoding Rules (BER) |
4 | | * |
5 | | * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> |
6 | | * |
7 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | * you may not use this file except in compliance with the License. |
9 | | * You may obtain a copy of the License at |
10 | | * |
11 | | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | | * |
13 | | * Unless required by applicable law or agreed to in writing, software |
14 | | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | * See the License for the specific language governing permissions and |
17 | | * limitations under the License. |
18 | | */ |
19 | | |
20 | | #include <winpr/assert.h> |
21 | | #include <winpr/cast.h> |
22 | | #include <winpr/print.h> |
23 | | |
24 | | #include <freerdp/config.h> |
25 | | #include <freerdp/crypto/per.h> |
26 | | |
27 | | #include <freerdp/log.h> |
28 | | #define TAG FREERDP_TAG("crypto.per") |
29 | | |
30 | | /** |
31 | | * Read PER length. |
32 | | * |
33 | | * @param s stream to read from |
34 | | * @param length A pointer to return the length read, must not be NULL |
35 | | * |
36 | | * @return \b TRUE for success, \b FALSE otherwise. |
37 | | */ |
38 | | |
39 | | BOOL per_read_length(wStream* s, UINT16* length) |
40 | 0 | { |
41 | 0 | BYTE byte = 0; |
42 | |
|
43 | 0 | WINPR_ASSERT(length); |
44 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) |
45 | 0 | return FALSE; |
46 | | |
47 | 0 | Stream_Read_UINT8(s, byte); |
48 | |
|
49 | 0 | if (byte & 0x80) |
50 | 0 | { |
51 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) |
52 | 0 | return FALSE; |
53 | | |
54 | 0 | byte &= ~(0x80); |
55 | 0 | *length = WINPR_ASSERTING_INT_CAST(UINT16, byte << 8); |
56 | 0 | Stream_Read_UINT8(s, byte); |
57 | 0 | *length += byte; |
58 | 0 | } |
59 | 0 | else |
60 | 0 | { |
61 | 0 | *length = byte; |
62 | 0 | } |
63 | | |
64 | 0 | return TRUE; |
65 | 0 | } |
66 | | |
67 | | /** |
68 | | * Write PER length. |
69 | | * @param s stream |
70 | | * @param length length |
71 | | * |
72 | | * @return \b TRUE for success, \b FALSE otherwise. |
73 | | */ |
74 | | |
75 | | BOOL per_write_length(wStream* s, UINT16 length) |
76 | 0 | { |
77 | 0 | if (length > 0x7F) |
78 | 0 | { |
79 | 0 | if (!Stream_EnsureRemainingCapacity(s, 2)) |
80 | 0 | return FALSE; |
81 | 0 | Stream_Write_UINT16_BE(s, (length | 0x8000)); |
82 | 0 | } |
83 | 0 | else |
84 | 0 | { |
85 | 0 | if (!Stream_EnsureRemainingCapacity(s, 1)) |
86 | 0 | return FALSE; |
87 | 0 | Stream_Write_UINT8(s, (UINT8)length); |
88 | 0 | } |
89 | 0 | return TRUE; |
90 | 0 | } |
91 | | |
92 | | /** |
93 | | * Read PER choice. |
94 | | * @param s stream |
95 | | * @param choice choice |
96 | | * |
97 | | * @return \b TRUE for success, \b FALSE otherwise. |
98 | | */ |
99 | | |
100 | | BOOL per_read_choice(wStream* s, BYTE* choice) |
101 | 0 | { |
102 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) |
103 | 0 | return FALSE; |
104 | | |
105 | 0 | Stream_Read_UINT8(s, *choice); |
106 | 0 | return TRUE; |
107 | 0 | } |
108 | | |
109 | | /** |
110 | | * Write PER CHOICE. |
111 | | * @param s stream |
112 | | * @param choice index of chosen field |
113 | | * |
114 | | * @return \b TRUE for success, \b FALSE otherwise. |
115 | | */ |
116 | | |
117 | | BOOL per_write_choice(wStream* s, BYTE choice) |
118 | 0 | { |
119 | 0 | if (!Stream_EnsureRemainingCapacity(s, 1)) |
120 | 0 | return FALSE; |
121 | 0 | Stream_Write_UINT8(s, choice); |
122 | 0 | return TRUE; |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | * Read PER selection. |
127 | | * @param s stream |
128 | | * @param selection selection |
129 | | * |
130 | | * @return \b TRUE for success, \b FALSE otherwise. |
131 | | */ |
132 | | |
133 | | BOOL per_read_selection(wStream* s, BYTE* selection) |
134 | 0 | { |
135 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) |
136 | 0 | return FALSE; |
137 | | |
138 | 0 | WINPR_ASSERT(selection); |
139 | 0 | Stream_Read_UINT8(s, *selection); |
140 | 0 | return TRUE; |
141 | 0 | } |
142 | | |
143 | | /** |
144 | | * Write PER selection for OPTIONAL fields. |
145 | | * @param s stream |
146 | | * @param selection bit map of selected fields |
147 | | * |
148 | | * @return \b TRUE for success, \b FALSE otherwise. |
149 | | */ |
150 | | |
151 | | BOOL per_write_selection(wStream* s, BYTE selection) |
152 | 0 | { |
153 | 0 | if (!Stream_EnsureRemainingCapacity(s, 1)) |
154 | 0 | return FALSE; |
155 | 0 | Stream_Write_UINT8(s, selection); |
156 | 0 | return TRUE; |
157 | 0 | } |
158 | | |
159 | | /** |
160 | | * Read PER number of sets. |
161 | | * @param s stream |
162 | | * @param number number of sets |
163 | | * |
164 | | * @return \b TRUE for success, \b FALSE otherwise. |
165 | | */ |
166 | | |
167 | | BOOL per_read_number_of_sets(wStream* s, BYTE* number) |
168 | 0 | { |
169 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) |
170 | 0 | return FALSE; |
171 | | |
172 | 0 | WINPR_ASSERT(number); |
173 | 0 | Stream_Read_UINT8(s, *number); |
174 | 0 | return TRUE; |
175 | 0 | } |
176 | | |
177 | | /** |
178 | | * Write PER number of sets for SET OF. |
179 | | * |
180 | | * @param s stream |
181 | | * @param number number of sets |
182 | | * |
183 | | * @return \b TRUE for success, \b FALSE otherwise. |
184 | | */ |
185 | | |
186 | | BOOL per_write_number_of_sets(wStream* s, BYTE number) |
187 | 0 | { |
188 | 0 | if (!Stream_EnsureRemainingCapacity(s, 1)) |
189 | 0 | return FALSE; |
190 | 0 | Stream_Write_UINT8(s, number); |
191 | 0 | return TRUE; |
192 | 0 | } |
193 | | |
194 | | /** |
195 | | * Read PER padding with zeros. |
196 | | * |
197 | | * @param s A stream to read from |
198 | | * @param length the data to write |
199 | | * |
200 | | * @return \b TRUE for success, \b FALSE otherwise. |
201 | | */ |
202 | | |
203 | | BOOL per_read_padding(wStream* s, UINT16 length) |
204 | 0 | { |
205 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) |
206 | 0 | return FALSE; |
207 | | |
208 | 0 | Stream_Seek(s, length); |
209 | 0 | return TRUE; |
210 | 0 | } |
211 | | |
212 | | /** |
213 | | * Write PER padding with zeros. |
214 | | * @param s A stream to write to |
215 | | * @param length the data to write |
216 | | * |
217 | | * @return \b TRUE for success, \b FALSE otherwise. |
218 | | */ |
219 | | |
220 | | BOOL per_write_padding(wStream* s, UINT16 length) |
221 | 0 | { |
222 | 0 | if (!Stream_EnsureRemainingCapacity(s, length)) |
223 | 0 | return FALSE; |
224 | 0 | Stream_Zero(s, length); |
225 | 0 | return TRUE; |
226 | 0 | } |
227 | | |
228 | | /** |
229 | | * Read PER INTEGER. |
230 | | * @param s stream |
231 | | * @param integer integer |
232 | | * |
233 | | * @return \b TRUE for success, \b FALSE otherwise. |
234 | | */ |
235 | | |
236 | | BOOL per_read_integer(wStream* s, UINT32* integer) |
237 | 0 | { |
238 | 0 | UINT16 length = 0; |
239 | |
|
240 | 0 | WINPR_ASSERT(integer); |
241 | | |
242 | 0 | if (!per_read_length(s, &length)) |
243 | 0 | return FALSE; |
244 | | |
245 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) |
246 | 0 | return FALSE; |
247 | | |
248 | 0 | if (length == 0) |
249 | 0 | *integer = 0; |
250 | 0 | else if (length == 1) |
251 | 0 | Stream_Read_UINT8(s, *integer); |
252 | 0 | else if (length == 2) |
253 | 0 | Stream_Read_UINT16_BE(s, *integer); |
254 | 0 | else |
255 | 0 | return FALSE; |
256 | | |
257 | 0 | return TRUE; |
258 | 0 | } |
259 | | |
260 | | /** |
261 | | * Write PER INTEGER. |
262 | | * @param s stream |
263 | | * @param integer integer |
264 | | * |
265 | | * @return \b TRUE for success, \b FALSE otherwise. |
266 | | */ |
267 | | |
268 | | BOOL per_write_integer(wStream* s, UINT32 integer) |
269 | 0 | { |
270 | 0 | if (integer <= UINT8_MAX) |
271 | 0 | { |
272 | 0 | if (!per_write_length(s, 1)) |
273 | 0 | return FALSE; |
274 | 0 | if (!Stream_EnsureRemainingCapacity(s, 1)) |
275 | 0 | return FALSE; |
276 | 0 | Stream_Write_UINT8(s, WINPR_ASSERTING_INT_CAST(UINT8, integer)); |
277 | 0 | } |
278 | 0 | else if (integer <= UINT16_MAX) |
279 | 0 | { |
280 | 0 | if (!per_write_length(s, 2)) |
281 | 0 | return FALSE; |
282 | 0 | if (!Stream_EnsureRemainingCapacity(s, 2)) |
283 | 0 | return FALSE; |
284 | 0 | Stream_Write_UINT16_BE(s, WINPR_ASSERTING_INT_CAST(UINT16, integer)); |
285 | 0 | } |
286 | 0 | else if (integer <= UINT32_MAX) |
287 | 0 | { |
288 | 0 | if (!per_write_length(s, 4)) |
289 | 0 | return FALSE; |
290 | 0 | if (!Stream_EnsureRemainingCapacity(s, 4)) |
291 | 0 | return FALSE; |
292 | 0 | Stream_Write_UINT32_BE(s, integer); |
293 | 0 | } |
294 | 0 | return TRUE; |
295 | 0 | } |
296 | | |
297 | | /** |
298 | | * Read PER INTEGER (UINT16). |
299 | | * |
300 | | * @param s The stream to read from |
301 | | * @param integer The integer result variable pointer, must not be NULL |
302 | | * @param min minimum value |
303 | | * |
304 | | * @return \b TRUE for success, \b FALSE otherwise |
305 | | */ |
306 | | |
307 | | BOOL per_read_integer16(wStream* s, UINT16* integer, UINT16 min) |
308 | 0 | { |
309 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 2)) |
310 | 0 | return FALSE; |
311 | | |
312 | 0 | Stream_Read_UINT16_BE(s, *integer); |
313 | |
|
314 | 0 | if (*integer > UINT16_MAX - min) |
315 | 0 | { |
316 | 0 | WLog_WARN(TAG, "PER uint16 invalid value %" PRIu16 " > %" PRIu16, *integer, |
317 | 0 | UINT16_MAX - min); |
318 | 0 | return FALSE; |
319 | 0 | } |
320 | | |
321 | 0 | *integer += min; |
322 | |
|
323 | 0 | return TRUE; |
324 | 0 | } |
325 | | |
326 | | /** |
327 | | * Write PER INTEGER (UINT16). |
328 | | * @param s stream |
329 | | * @param integer integer |
330 | | * @param min minimum value |
331 | | * |
332 | | * @return \b TRUE for success, \b FALSE otherwise. |
333 | | */ |
334 | | |
335 | | BOOL per_write_integer16(wStream* s, UINT16 integer, UINT16 min) |
336 | 0 | { |
337 | 0 | if (min > integer) |
338 | 0 | return FALSE; |
339 | 0 | if (!Stream_EnsureRemainingCapacity(s, 2)) |
340 | 0 | return FALSE; |
341 | 0 | Stream_Write_UINT16_BE(s, integer - min); |
342 | 0 | return TRUE; |
343 | 0 | } |
344 | | |
345 | | /** |
346 | | * Read PER ENUMERATED. |
347 | | * |
348 | | * @param s The stream to read from |
349 | | * @param enumerated enumerated result variable, must not be NULL |
350 | | * @param count enumeration count |
351 | | * |
352 | | * @return \b TRUE for success, \b FALSE otherwise |
353 | | */ |
354 | | |
355 | | BOOL per_read_enumerated(wStream* s, BYTE* enumerated, BYTE count) |
356 | 0 | { |
357 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, 1)) |
358 | 0 | return FALSE; |
359 | | |
360 | 0 | WINPR_ASSERT(enumerated); |
361 | 0 | Stream_Read_UINT8(s, *enumerated); |
362 | | |
363 | | /* check that enumerated value falls within expected range */ |
364 | 0 | if (*enumerated + 1 > count) |
365 | 0 | { |
366 | 0 | WLog_WARN(TAG, "PER invalid data, expected %" PRIu8 " < %" PRIu8, *enumerated, count); |
367 | 0 | return FALSE; |
368 | 0 | } |
369 | | |
370 | 0 | return TRUE; |
371 | 0 | } |
372 | | |
373 | | /** |
374 | | * Write PER ENUMERATED. |
375 | | * |
376 | | * @param s The stream to write to |
377 | | * @param enumerated enumerated |
378 | | * @param count enumeration count |
379 | | * |
380 | | * @return \b TRUE for success, \b FALSE otherwise |
381 | | */ |
382 | | |
383 | | BOOL per_write_enumerated(wStream* s, BYTE enumerated, WINPR_ATTR_UNUSED BYTE count) |
384 | 0 | { |
385 | 0 | if (!Stream_EnsureRemainingCapacity(s, 1)) |
386 | 0 | return FALSE; |
387 | 0 | Stream_Write_UINT8(s, enumerated); |
388 | 0 | return TRUE; |
389 | 0 | } |
390 | | |
391 | | static BOOL per_check_oid_and_log_mismatch(const BYTE* got, const BYTE* expect, size_t length) |
392 | 0 | { |
393 | 0 | if (memcmp(got, expect, length) == 0) |
394 | 0 | { |
395 | 0 | return TRUE; |
396 | 0 | } |
397 | 0 | else |
398 | 0 | { |
399 | 0 | char* got_str = winpr_BinToHexString(got, length, TRUE); |
400 | 0 | char* expect_str = winpr_BinToHexString(expect, length, TRUE); |
401 | |
|
402 | 0 | WLog_WARN(TAG, "PER OID mismatch, got %s, expected %s", got_str, expect_str); |
403 | 0 | free(got_str); |
404 | 0 | free(expect_str); |
405 | 0 | return FALSE; |
406 | 0 | } |
407 | 0 | } |
408 | | |
409 | | /** |
410 | | * Read PER OBJECT_IDENTIFIER (OID). |
411 | | * |
412 | | * @param s The stream to read from |
413 | | * @param oid object identifier (OID) |
414 | | * @warning It works correctly only for limited set of OIDs. |
415 | | * |
416 | | * @return \b TRUE for success, \b FALSE otherwise |
417 | | */ |
418 | | |
419 | | BOOL per_read_object_identifier(wStream* s, const BYTE oid[6]) |
420 | 0 | { |
421 | 0 | BYTE t12 = 0; |
422 | 0 | UINT16 length = 0; |
423 | 0 | BYTE a_oid[6] = { 0 }; |
424 | |
|
425 | 0 | if (!per_read_length(s, &length)) |
426 | 0 | return FALSE; |
427 | | |
428 | 0 | if (length != 5) |
429 | 0 | { |
430 | 0 | WLog_WARN(TAG, "PER length, got %" PRIu16 ", expected 5", length); |
431 | 0 | return FALSE; |
432 | 0 | } |
433 | | |
434 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) |
435 | 0 | return FALSE; |
436 | | |
437 | 0 | Stream_Read_UINT8(s, t12); /* first two tuples */ |
438 | 0 | a_oid[0] = t12 / 40; |
439 | 0 | a_oid[1] = t12 % 40; |
440 | |
|
441 | 0 | Stream_Read_UINT8(s, a_oid[2]); /* tuple 3 */ |
442 | 0 | Stream_Read_UINT8(s, a_oid[3]); /* tuple 4 */ |
443 | 0 | Stream_Read_UINT8(s, a_oid[4]); /* tuple 5 */ |
444 | 0 | Stream_Read_UINT8(s, a_oid[5]); /* tuple 6 */ |
445 | |
|
446 | 0 | return per_check_oid_and_log_mismatch(a_oid, oid, sizeof(a_oid)); |
447 | 0 | } |
448 | | |
449 | | /** |
450 | | * Write PER OBJECT_IDENTIFIER (OID) |
451 | | * @param s stream |
452 | | * @param oid object identifier (oid) |
453 | | * @warning It works correctly only for limited set of OIDs. |
454 | | * |
455 | | * @return \b TRUE for success, \b FALSE otherwise. |
456 | | */ |
457 | | |
458 | | BOOL per_write_object_identifier(wStream* s, const BYTE oid[6]) |
459 | 0 | { |
460 | 0 | BYTE t12 = oid[0] * 40 + oid[1]; |
461 | 0 | if (!Stream_EnsureRemainingCapacity(s, 6)) |
462 | 0 | return FALSE; |
463 | 0 | Stream_Write_UINT8(s, 5); /* length */ |
464 | 0 | Stream_Write_UINT8(s, t12); /* first two tuples */ |
465 | 0 | Stream_Write_UINT8(s, oid[2]); /* tuple 3 */ |
466 | 0 | Stream_Write_UINT8(s, oid[3]); /* tuple 4 */ |
467 | 0 | Stream_Write_UINT8(s, oid[4]); /* tuple 5 */ |
468 | 0 | Stream_Write_UINT8(s, oid[5]); /* tuple 6 */ |
469 | 0 | return TRUE; |
470 | 0 | } |
471 | | |
472 | | /** |
473 | | * Read PER OCTET_STRING. |
474 | | * |
475 | | * @param s The stream to read from |
476 | | * @param oct_str octet string |
477 | | * @param length string length |
478 | | * @param min minimum length |
479 | | * |
480 | | * @return \b TRUE for success, \b FALSE otherwise. |
481 | | */ |
482 | | |
483 | | BOOL per_read_octet_string(wStream* s, const BYTE* oct_str, UINT16 length, UINT16 min) |
484 | 0 | { |
485 | 0 | UINT16 mlength = 0; |
486 | |
|
487 | 0 | if (!per_read_length(s, &mlength)) |
488 | 0 | return FALSE; |
489 | | |
490 | 0 | if (mlength + min != length) |
491 | 0 | { |
492 | 0 | WLog_ERR(TAG, "length mismatch: %" PRIu16 "!= %" PRIu16, mlength + min, length); |
493 | 0 | return FALSE; |
494 | 0 | } |
495 | | |
496 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) |
497 | 0 | return FALSE; |
498 | | |
499 | 0 | const BYTE* a_oct_str = Stream_ConstPointer(s); |
500 | 0 | Stream_Seek(s, length); |
501 | |
|
502 | 0 | return per_check_oid_and_log_mismatch(a_oct_str, oct_str, length); |
503 | 0 | } |
504 | | |
505 | | /** |
506 | | * Write PER OCTET_STRING |
507 | | * @param s stream |
508 | | * @param oct_str octet string |
509 | | * @param length string length |
510 | | * @param min minimum string length |
511 | | * |
512 | | * @return \b TRUE for success, \b FALSE otherwise. |
513 | | */ |
514 | | |
515 | | BOOL per_write_octet_string(wStream* s, const BYTE* oct_str, UINT16 length, UINT16 min) |
516 | 0 | { |
517 | 0 | UINT16 mlength = 0; |
518 | |
|
519 | 0 | mlength = (length >= min) ? length - min : min; |
520 | |
|
521 | 0 | if (!per_write_length(s, mlength)) |
522 | 0 | return FALSE; |
523 | | |
524 | 0 | if (!Stream_EnsureRemainingCapacity(s, length)) |
525 | 0 | return FALSE; |
526 | 0 | for (UINT16 i = 0; i < length; i++) |
527 | 0 | Stream_Write_UINT8(s, oct_str[i]); |
528 | 0 | return TRUE; |
529 | 0 | } |
530 | | |
531 | | /** |
532 | | * Read PER NumericString. |
533 | | * @param s stream |
534 | | * @param min minimum string length |
535 | | * |
536 | | * @return \b TRUE for success, \b FALSE otherwise. |
537 | | */ |
538 | | |
539 | | BOOL per_read_numeric_string(wStream* s, UINT16 min) |
540 | 0 | { |
541 | 0 | size_t length = 0; |
542 | 0 | UINT16 mlength = 0; |
543 | |
|
544 | 0 | if (!per_read_length(s, &mlength)) |
545 | 0 | return FALSE; |
546 | | |
547 | 0 | length = (mlength + min + 1) / 2; |
548 | |
|
549 | 0 | if (!Stream_CheckAndLogRequiredLength(TAG, s, length)) |
550 | 0 | return FALSE; |
551 | | |
552 | 0 | Stream_Seek(s, length); |
553 | 0 | return TRUE; |
554 | 0 | } |
555 | | |
556 | | /** |
557 | | * Write PER NumericString. |
558 | | * @param s stream |
559 | | * @param num_str numeric string |
560 | | * @param length string length |
561 | | * @param min minimum string length |
562 | | * |
563 | | * @return \b TRUE for success, \b FALSE otherwise. |
564 | | */ |
565 | | |
566 | | BOOL per_write_numeric_string(wStream* s, const BYTE* num_str, UINT16 length, UINT16 min) |
567 | 0 | { |
568 | 0 | WINPR_ASSERT(num_str || (length == 0)); |
569 | | |
570 | 0 | const UINT16 mlength = (length >= min) ? length - min : min; |
571 | |
|
572 | 0 | if (!per_write_length(s, mlength)) |
573 | 0 | return FALSE; |
574 | | |
575 | 0 | if (!Stream_EnsureRemainingCapacity(s, length)) |
576 | 0 | return FALSE; |
577 | 0 | for (UINT16 i = 0; i < length; i += 2) |
578 | 0 | { |
579 | 0 | BYTE c1 = num_str[i]; |
580 | 0 | BYTE c2 = ((i + 1) < length) ? num_str[i + 1] : 0x30; |
581 | |
|
582 | 0 | if ((c1 < 0x30) || (c2 < 0x30)) |
583 | 0 | return FALSE; |
584 | | |
585 | 0 | c1 = (c1 - 0x30) % 10; |
586 | 0 | c2 = (c2 - 0x30) % 10; |
587 | 0 | const BYTE num = WINPR_ASSERTING_INT_CAST(BYTE, (c1 << 4) | c2); |
588 | | |
589 | 0 | Stream_Write_UINT8(s, num); /* string */ |
590 | 0 | } |
591 | 0 | return TRUE; |
592 | 0 | } |