Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/crypto/cert_common.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Certificate Handling
4
 *
5
 * Copyright 2011 Jiten Pathy
6
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
7
 * Copyright 2015 Thincast Technologies GmbH
8
 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
9
 * Copyright 2023 Armin Novak <anovak@thincast.com>
10
 * Copyright 2023 Thincast Technologies GmbH
11
 *
12
 * Licensed under the Apache License, Version 2.0 (the "License");
13
 * you may not use this file except in compliance with the License.
14
 * You may obtain a copy of the License at
15
 *
16
 *     http://www.apache.org/licenses/LICENSE-2.0
17
 *
18
 * Unless required by applicable law or agreed to in writing, software
19
 * distributed under the License is distributed on an "AS IS" BASIS,
20
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21
 * See the License for the specific language governing permissions and
22
 * limitations under the License.
23
 */
24
25
#include <freerdp/config.h>
26
27
#include <errno.h>
28
#include <stdio.h>
29
#include <string.h>
30
31
#include <winpr/assert.h>
32
#include <winpr/wtypes.h>
33
#include <winpr/crt.h>
34
#include <winpr/file.h>
35
#include <winpr/crypto.h>
36
37
#include <openssl/pem.h>
38
#include <openssl/rsa.h>
39
#include <openssl/bn.h>
40
41
#include "cert_common.h"
42
#include "crypto.h"
43
#include "opensslcompat.h"
44
45
#define TAG FREERDP_TAG("core")
46
47
static BOOL cert_info_allocate(rdpCertInfo* info, size_t size);
48
49
BOOL read_bignum(BYTE** dst, UINT32* length, const BIGNUM* num, BOOL alloc)
50
0
{
51
0
  WINPR_ASSERT(dst);
52
0
  WINPR_ASSERT(length);
53
0
  WINPR_ASSERT(num);
54
55
0
  if (alloc)
56
0
  {
57
0
    free(*dst);
58
0
    *dst = NULL;
59
0
    *length = 0;
60
0
  }
61
62
0
  const int len = BN_num_bytes(num);
63
0
  if (len < 0)
64
0
    return FALSE;
65
66
0
  if (!alloc)
67
0
  {
68
0
    if (*length < (UINT32)len)
69
0
      return FALSE;
70
0
  }
71
72
0
  if (len > 0)
73
0
  {
74
0
    if (alloc)
75
0
    {
76
0
      *dst = malloc((size_t)len);
77
0
      if (!*dst)
78
0
        return FALSE;
79
0
    }
80
0
    BN_bn2bin(num, *dst);
81
0
    crypto_reverse(*dst, (size_t)len);
82
0
    *length = (UINT32)len;
83
0
  }
84
85
0
  return TRUE;
86
0
}
87
88
BOOL cert_info_create(rdpCertInfo* dst, const BIGNUM* rsa, const BIGNUM* rsa_e)
89
0
{
90
0
  const rdpCertInfo empty = { 0 };
91
92
0
  WINPR_ASSERT(dst);
93
0
  WINPR_ASSERT(rsa);
94
95
0
  *dst = empty;
96
97
0
  if (!read_bignum(&dst->Modulus, &dst->ModulusLength, rsa, TRUE))
98
0
    goto fail;
99
100
0
  UINT32 len = sizeof(dst->exponent);
101
0
  BYTE* ptr = &dst->exponent[0];
102
0
  if (!read_bignum(&ptr, &len, rsa_e, FALSE))
103
0
    goto fail;
104
0
  return TRUE;
105
106
0
fail:
107
0
  cert_info_free(dst);
108
0
  return FALSE;
109
0
}
110
111
BOOL cert_info_clone(rdpCertInfo* dst, const rdpCertInfo* src)
112
0
{
113
0
  WINPR_ASSERT(dst);
114
0
  WINPR_ASSERT(src);
115
116
0
  *dst = *src;
117
118
0
  dst->Modulus = NULL;
119
0
  dst->ModulusLength = 0;
120
0
  if (src->ModulusLength > 0)
121
0
  {
122
0
    dst->Modulus = malloc(src->ModulusLength);
123
0
    if (!dst->Modulus)
124
0
      return FALSE;
125
0
    memcpy(dst->Modulus, src->Modulus, src->ModulusLength);
126
0
    dst->ModulusLength = src->ModulusLength;
127
0
  }
128
0
  return TRUE;
129
0
}
130
131
void cert_info_free(rdpCertInfo* info)
132
0
{
133
0
  WINPR_ASSERT(info);
134
0
  free(info->Modulus);
135
0
  info->ModulusLength = 0;
136
0
  info->Modulus = NULL;
137
0
}
138
139
BOOL cert_info_allocate(rdpCertInfo* info, size_t size)
140
0
{
141
0
  WINPR_ASSERT(info);
142
0
  cert_info_free(info);
143
144
0
  info->Modulus = (BYTE*)malloc(size);
145
146
0
  if (!info->Modulus && (size > 0))
147
0
  {
148
0
    WLog_ERR(TAG, "Failed to allocate info->Modulus of size %" PRIuz, size);
149
0
    return FALSE;
150
0
  }
151
0
  info->ModulusLength = (UINT32)size;
152
0
  return TRUE;
153
0
}
154
155
BOOL cert_info_read_modulus(rdpCertInfo* info, size_t size, wStream* s)
156
0
{
157
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
158
0
    return FALSE;
159
0
  if (size > UINT32_MAX)
160
0
  {
161
0
    WLog_ERR(TAG, "modulus size %" PRIuz " exceeds limit of %" PRIu32, size, UINT32_MAX);
162
0
    return FALSE;
163
0
  }
164
0
  if (!cert_info_allocate(info, size))
165
0
    return FALSE;
166
0
  Stream_Read(s, info->Modulus, info->ModulusLength);
167
0
  return TRUE;
168
0
}
169
170
BOOL cert_info_read_exponent(rdpCertInfo* info, size_t size, wStream* s)
171
0
{
172
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
173
0
    return FALSE;
174
0
  if (size > 4)
175
0
  {
176
0
    WLog_ERR(TAG, "exponent size %" PRIuz " exceeds limit of %" PRIu32, size, 4);
177
0
    return FALSE;
178
0
  }
179
0
  if (!info->Modulus || (info->ModulusLength == 0))
180
0
  {
181
0
    WLog_ERR(TAG, "invalid modulus=%p [%" PRIu32 "]", info->Modulus, info->ModulusLength);
182
0
    return FALSE;
183
0
  }
184
0
  Stream_Read(s, &info->exponent[4 - size], size);
185
0
  crypto_reverse(info->Modulus, info->ModulusLength);
186
0
  crypto_reverse(info->exponent, 4);
187
0
  return TRUE;
188
0
}
189
190
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
191
X509* x509_from_rsa(const RSA* rsa)
192
0
{
193
0
  EVP_PKEY* pubkey = NULL;
194
0
  X509* x509 = NULL;
195
0
  BIO* bio = BIO_new(
196
#if defined(LIBRESSL_VERSION_NUMBER)
197
      BIO_s_mem()
198
#else
199
0
      BIO_s_secmem()
200
0
#endif
201
0
  );
202
0
  if (!bio)
203
0
  {
204
0
    WLog_ERR(TAG, "BIO_new() failed");
205
0
    return NULL;
206
0
  }
207
208
0
  const int rc = PEM_write_bio_RSA_PUBKEY(bio, (RSA*)rsa);
209
0
  if (rc != 1)
210
0
  {
211
0
    WLog_ERR(TAG, "PEM_write_bio_RSA_PUBKEY(bio, (RSA*)rsa) failed");
212
0
    goto fail;
213
0
  }
214
215
0
  pubkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
216
0
  if (!pubkey)
217
0
  {
218
0
    WLog_ERR(TAG, "PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL) failed");
219
0
    goto fail;
220
0
  }
221
222
0
  x509 = X509_new();
223
0
  if (!x509)
224
0
  {
225
0
    WLog_ERR(TAG, "X509_new() failed");
226
0
    goto fail;
227
0
  }
228
229
0
  const int res = X509_set_pubkey(x509, pubkey);
230
0
  if (res != 1)
231
0
  {
232
0
    WLog_ERR(TAG, "X509_set_pubkey(x509, pubkey) failed");
233
0
    X509_free(x509);
234
0
    x509 = NULL;
235
0
    goto fail;
236
0
  }
237
0
fail:
238
0
  BIO_free_all(bio);
239
0
  EVP_PKEY_free(pubkey);
240
0
  return x509;
241
0
}
242
#endif