blob: 35c06f73d631dce15939459ac81ebf03e7ec9719 [file] [log] [blame]
William Lallemand6a66a5e2020-05-15 12:01:17 +02001/*
2 * Utility functions for SSL:
3 * Mostly generic functions that retrieve information from certificates
4 *
5 * Copyright (C) 2012 EXCELIANCE, Emeric Brun <ebrun@exceliance.fr>
6 * Copyright (C) 2020 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version
11 * 2 of the License, or (at your option) any later version.
12 */
13
14
Willy Tarreau2741c8c2020-06-02 11:28:02 +020015#include <haproxy/api.h>
16#include <haproxy/buf-t.h>
Willy Tarreaucb72b7e2021-05-08 12:52:56 +020017#include <haproxy/chunk.h>
Willy Tarreau6019fab2020-05-27 16:26:00 +020018#include <haproxy/openssl-compat.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020019#include <haproxy/ssl_sock.h>
William Lallemand6a66a5e2020-05-15 12:01:17 +020020
21/* fill a buffer with the algorithm and size of a public key */
22int cert_get_pkey_algo(X509 *crt, struct buffer *out)
23{
24 int bits = 0;
25 int sig = TLSEXT_signature_anonymous;
26 int len = -1;
27 EVP_PKEY *pkey;
28
29 pkey = X509_get_pubkey(crt);
30 if (pkey) {
31 bits = EVP_PKEY_bits(pkey);
32 switch(EVP_PKEY_base_id(pkey)) {
33 case EVP_PKEY_RSA:
34 sig = TLSEXT_signature_rsa;
35 break;
36 case EVP_PKEY_EC:
37 sig = TLSEXT_signature_ecdsa;
38 break;
39 case EVP_PKEY_DSA:
40 sig = TLSEXT_signature_dsa;
41 break;
42 }
43 EVP_PKEY_free(pkey);
44 }
45
46 switch(sig) {
47 case TLSEXT_signature_rsa:
48 len = chunk_printf(out, "RSA%d", bits);
49 break;
50 case TLSEXT_signature_ecdsa:
51 len = chunk_printf(out, "EC%d", bits);
52 break;
53 case TLSEXT_signature_dsa:
54 len = chunk_printf(out, "DSA%d", bits);
55 break;
56 default:
57 return 0;
58 }
59 if (len < 0)
60 return 0;
61 return 1;
62}
63
64/* Extract a serial from a cert, and copy it to a chunk.
65 * Returns 1 if serial is found and copied, 0 if no serial found and
66 * -1 if output is not large enough.
67 */
68int ssl_sock_get_serial(X509 *crt, struct buffer *out)
69{
70 ASN1_INTEGER *serial;
71
72 serial = X509_get_serialNumber(crt);
73 if (!serial)
74 return 0;
75
76 if (out->size < serial->length)
77 return -1;
78
79 memcpy(out->area, serial->data, serial->length);
80 out->data = serial->length;
81 return 1;
82}
83
84/* Extract a cert to der, and copy it to a chunk.
85 * Returns 1 if the cert is found and copied, 0 on der conversion failure
86 * and -1 if the output is not large enough.
87 */
88int ssl_sock_crt2der(X509 *crt, struct buffer *out)
89{
90 int len;
William Dauchy98c35042020-08-06 18:11:37 +020091 unsigned char *p = (unsigned char *) out->area;
William Lallemand6a66a5e2020-05-15 12:01:17 +020092
William Dauchy98c35042020-08-06 18:11:37 +020093 len = i2d_X509(crt, NULL);
William Lallemand6a66a5e2020-05-15 12:01:17 +020094 if (len <= 0)
95 return 1;
96
97 if (out->size < len)
98 return -1;
99
William Dauchy98c35042020-08-06 18:11:37 +0200100 i2d_X509(crt, &p);
William Lallemand6a66a5e2020-05-15 12:01:17 +0200101 out->data = len;
102 return 1;
103}
104
105
106/* Copy Date in ASN1_UTCTIME format in struct buffer out.
107 * Returns 1 if serial is found and copied, 0 if no valid time found
108 * and -1 if output is not large enough.
109 */
110int ssl_sock_get_time(ASN1_TIME *tm, struct buffer *out)
111{
112 if (tm->type == V_ASN1_GENERALIZEDTIME) {
113 ASN1_GENERALIZEDTIME *gentm = (ASN1_GENERALIZEDTIME *)tm;
114
115 if (gentm->length < 12)
116 return 0;
117 if (gentm->data[0] != 0x32 || gentm->data[1] != 0x30)
118 return 0;
119 if (out->size < gentm->length-2)
120 return -1;
121
122 memcpy(out->area, gentm->data+2, gentm->length-2);
123 out->data = gentm->length-2;
124 return 1;
125 }
126 else if (tm->type == V_ASN1_UTCTIME) {
127 ASN1_UTCTIME *utctm = (ASN1_UTCTIME *)tm;
128
129 if (utctm->length < 10)
130 return 0;
131 if (utctm->data[0] >= 0x35)
132 return 0;
133 if (out->size < utctm->length)
134 return -1;
135
136 memcpy(out->area, utctm->data, utctm->length);
137 out->data = utctm->length;
138 return 1;
139 }
140
141 return 0;
142}
143
144/* Extract an entry from a X509_NAME and copy its value to an output chunk.
145 * Returns 1 if entry found, 0 if entry not found, or -1 if output not large enough.
146 */
147int ssl_sock_get_dn_entry(X509_NAME *a, const struct buffer *entry, int pos,
148 struct buffer *out)
149{
150 X509_NAME_ENTRY *ne;
151 ASN1_OBJECT *obj;
152 ASN1_STRING *data;
153 const unsigned char *data_ptr;
154 int data_len;
155 int i, j, n;
156 int cur = 0;
157 const char *s;
158 char tmp[128];
159 int name_count;
160
161 name_count = X509_NAME_entry_count(a);
162
163 out->data = 0;
164 for (i = 0; i < name_count; i++) {
165 if (pos < 0)
166 j = (name_count-1) - i;
167 else
168 j = i;
169
170 ne = X509_NAME_get_entry(a, j);
171 obj = X509_NAME_ENTRY_get_object(ne);
172 data = X509_NAME_ENTRY_get_data(ne);
173 data_ptr = ASN1_STRING_get0_data(data);
174 data_len = ASN1_STRING_length(data);
175 n = OBJ_obj2nid(obj);
176 if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
177 i2t_ASN1_OBJECT(tmp, sizeof(tmp), obj);
178 s = tmp;
179 }
180
181 if (chunk_strcasecmp(entry, s) != 0)
182 continue;
183
184 if (pos < 0)
185 cur--;
186 else
187 cur++;
188
189 if (cur != pos)
190 continue;
191
192 if (data_len > out->size)
193 return -1;
194
195 memcpy(out->area, data_ptr, data_len);
196 out->data = data_len;
197 return 1;
198 }
199
200 return 0;
201
202}
203
204/*
205 * Extract the DN in the specified format from the X509_NAME and copy result to a chunk.
206 * Currently supports rfc2253 for returning LDAP V3 DNs.
207 * Returns 1 if dn entries exist, 0 if no dn entry was found.
208 */
209int ssl_sock_get_dn_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out)
210{
211 BIO *bio = NULL;
212 int ret = 0;
213 int data_len = 0;
214
215 if (chunk_strcmp(format, "rfc2253") == 0) {
216 bio = BIO_new(BIO_s_mem());
217 if (bio == NULL)
218 goto out;
219
220 if (X509_NAME_print_ex(bio, a, 0, XN_FLAG_RFC2253) < 0)
221 goto out;
222
223 if ((data_len = BIO_read(bio, out->area, out->size)) <= 0)
224 goto out;
225
226 out->data = data_len;
227
228 ret = 1;
229 }
230out:
231 if (bio)
232 BIO_free(bio);
233 return ret;
234}
235
236/* Extract and format full DN from a X509_NAME and copy result into a chunk
237 * Returns 1 if dn entries exits, 0 if no dn entry found or -1 if output is not large enough.
238 */
239int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out)
240{
241 X509_NAME_ENTRY *ne;
242 ASN1_OBJECT *obj;
243 ASN1_STRING *data;
244 const unsigned char *data_ptr;
245 int data_len;
246 int i, n, ln;
247 int l = 0;
248 const char *s;
249 char *p;
250 char tmp[128];
251 int name_count;
252
253
254 name_count = X509_NAME_entry_count(a);
255
256 out->data = 0;
257 p = out->area;
258 for (i = 0; i < name_count; i++) {
259 ne = X509_NAME_get_entry(a, i);
260 obj = X509_NAME_ENTRY_get_object(ne);
261 data = X509_NAME_ENTRY_get_data(ne);
262 data_ptr = ASN1_STRING_get0_data(data);
263 data_len = ASN1_STRING_length(data);
264 n = OBJ_obj2nid(obj);
265 if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
266 i2t_ASN1_OBJECT(tmp, sizeof(tmp), obj);
267 s = tmp;
268 }
269 ln = strlen(s);
270
271 l += 1 + ln + 1 + data_len;
272 if (l > out->size)
273 return -1;
274 out->data = l;
275
276 *(p++)='/';
277 memcpy(p, s, ln);
278 p += ln;
279 *(p++)='=';
280 memcpy(p, data_ptr, data_len);
281 p += data_len;
282 }
283
284 if (!out->data)
285 return 0;
286
287 return 1;
288}
289
Remi Tricot-Le Breton74f6ab62021-08-19 18:06:30 +0200290
291extern int ssl_client_crt_ref_index;
292
293/*
294 * This function fetches the SSL certificate for a specific connection (either
295 * client certificate or server certificate depending on the cert_peer
296 * parameter).
297 * When trying to get the peer certificate from the server side, we first try to
298 * use the dedicated SSL_get_peer_certificate function, but we fall back to
299 * trying to get the client certificate reference that might have been stored in
300 * the SSL structure's ex_data during the verification process.
301 * Returns NULL in case of failure.
302 */
303X509* ssl_sock_get_peer_certificate(SSL *ssl)
304{
305 X509* cert;
306
307 cert = SSL_get_peer_certificate(ssl);
308 /* Get the client certificate reference stored in the SSL
309 * structure's ex_data during the verification process. */
310 if (!cert) {
311 cert = SSL_get_ex_data(ssl, ssl_client_crt_ref_index);
312 if (cert)
313 X509_up_ref(cert);
314 }
315
316 return cert;
317}
William Lallemand44d862d2021-08-21 23:16:06 +0200318
319/*
320 * Take an OpenSSL version in text format and return a numeric openssl version
321 * Return 0 if it failed to parse the version
322 *
323 * https://www.openssl.org/docs/man1.1.1/man3/OPENSSL_VERSION_NUMBER.html
324 *
325 * MNNFFPPS: major minor fix patch status
326 *
327 * The status nibble has one of the values 0 for development, 1 to e for betas
328 * 1 to 14, and f for release.
329 *
330 * for example
331 *
332 * 0x0090821f 0.9.8zh
333 * 0x1000215f 1.0.2u
334 * 0x30000000 3.0.0-alpha17
335 * 0x30000002 3.0.0-beta2
336 * 0x3000000e 3.0.0-beta14
337 * 0x3000000f 3.0.0
338 */
339unsigned int openssl_version_parser(const char *version)
340{
341 unsigned int numversion;
342 unsigned int major = 0, minor = 0, fix = 0, patch = 0, status = 0;
343 char *p, *end;
344
345 p = (char *)version;
346
347 if (!p || !*p)
348 return 0;
349
350 major = strtol(p, &end, 10);
351 if (*end != '.' || major > 0xf)
352 goto error;
353 p = end + 1;
354
355 minor = strtol(p, &end, 10);
356 if (*end != '.' || minor > 0xff)
357 goto error;
358 p = end + 1;
359
360 fix = strtol(p, &end, 10);
361 if (fix > 0xff)
362 goto error;
363 p = end;
364
365 if (!*p) {
366 /* end of the string, that's a release */
367 status = 0xf;
368 } else if (*p == '-') {
369 /* after the hyphen, only the beta will increment the status
370 * counter, all others versions will be considered as "dev" and
371 * does not increment anything */
372 p++;
373
374 if (!strncmp(p, "beta", 4)) {
375 p += 4;
William Lallemand8b673f02021-08-22 13:36:11 +0200376 status = strtol(p, &end, 10);
377 if (status > 14)
378 goto error;
William Lallemand44d862d2021-08-21 23:16:06 +0200379 }
380 } else {
381 /* that's a patch release */
382 patch = 1;
383
384 /* add the value of each letter */
385 while (*p) {
386 patch += (*p & ~0x20) - 'A';
387 p++;
388 }
389 status = 0xf;
390 }
391
392end:
393 numversion = ((major & 0xf) << 28) | ((minor & 0xff) << 20) | ((fix & 0xff) << 12) | ((patch & 0xff) << 4) | (status & 0xf);
394 return numversion;
395
396error:
397 return 0;
398
399}
Marcin Deranek959a48c2021-07-13 15:14:21 +0200400
401/* Exclude GREASE (RFC8701) values from input buffer */
402void exclude_tls_grease(char *input, int len, struct buffer *output)
403{
404 int ptr = 0;
405
406 while (ptr < len - 1) {
407 if (input[ptr] != input[ptr+1] || (input[ptr] & 0x0f) != 0x0a) {
408 if (output->data <= output->size - 2) {
409 memcpy(output->area + output->data, input + ptr, 2);
410 output->data += 2;
411 } else
412 break;
413 }
414 ptr += 2;
415 }
416 if (output->size - output->data > 0 && len - ptr > 0)
417 output->area[output->data++] = input[ptr];
418}