blob: 4a85b89187ea05b71a29a5e9fce5a823a4056ca7 [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>
Tim Duesterhus2a0688a2022-05-14 22:15:26 +020020#include <haproxy/ssl_utils.h>
William Lallemand6a66a5e2020-05-15 12:01:17 +020021
22/* fill a buffer with the algorithm and size of a public key */
23int cert_get_pkey_algo(X509 *crt, struct buffer *out)
24{
25 int bits = 0;
26 int sig = TLSEXT_signature_anonymous;
27 int len = -1;
28 EVP_PKEY *pkey;
29
30 pkey = X509_get_pubkey(crt);
31 if (pkey) {
32 bits = EVP_PKEY_bits(pkey);
33 switch(EVP_PKEY_base_id(pkey)) {
34 case EVP_PKEY_RSA:
35 sig = TLSEXT_signature_rsa;
36 break;
37 case EVP_PKEY_EC:
38 sig = TLSEXT_signature_ecdsa;
39 break;
40 case EVP_PKEY_DSA:
41 sig = TLSEXT_signature_dsa;
42 break;
43 }
44 EVP_PKEY_free(pkey);
45 }
46
47 switch(sig) {
48 case TLSEXT_signature_rsa:
49 len = chunk_printf(out, "RSA%d", bits);
50 break;
51 case TLSEXT_signature_ecdsa:
52 len = chunk_printf(out, "EC%d", bits);
53 break;
54 case TLSEXT_signature_dsa:
55 len = chunk_printf(out, "DSA%d", bits);
56 break;
57 default:
58 return 0;
59 }
60 if (len < 0)
61 return 0;
62 return 1;
63}
64
65/* Extract a serial from a cert, and copy it to a chunk.
66 * Returns 1 if serial is found and copied, 0 if no serial found and
67 * -1 if output is not large enough.
68 */
69int ssl_sock_get_serial(X509 *crt, struct buffer *out)
70{
71 ASN1_INTEGER *serial;
72
73 serial = X509_get_serialNumber(crt);
74 if (!serial)
75 return 0;
76
77 if (out->size < serial->length)
78 return -1;
79
80 memcpy(out->area, serial->data, serial->length);
81 out->data = serial->length;
82 return 1;
83}
84
85/* Extract a cert to der, and copy it to a chunk.
86 * Returns 1 if the cert is found and copied, 0 on der conversion failure
87 * and -1 if the output is not large enough.
88 */
89int ssl_sock_crt2der(X509 *crt, struct buffer *out)
90{
91 int len;
William Dauchy98c35042020-08-06 18:11:37 +020092 unsigned char *p = (unsigned char *) out->area;
William Lallemand6a66a5e2020-05-15 12:01:17 +020093
William Dauchy98c35042020-08-06 18:11:37 +020094 len = i2d_X509(crt, NULL);
William Lallemand6a66a5e2020-05-15 12:01:17 +020095 if (len <= 0)
96 return 1;
97
98 if (out->size < len)
99 return -1;
100
William Dauchy98c35042020-08-06 18:11:37 +0200101 i2d_X509(crt, &p);
William Lallemand6a66a5e2020-05-15 12:01:17 +0200102 out->data = len;
103 return 1;
104}
105
106
107/* Copy Date in ASN1_UTCTIME format in struct buffer out.
108 * Returns 1 if serial is found and copied, 0 if no valid time found
109 * and -1 if output is not large enough.
110 */
111int ssl_sock_get_time(ASN1_TIME *tm, struct buffer *out)
112{
113 if (tm->type == V_ASN1_GENERALIZEDTIME) {
114 ASN1_GENERALIZEDTIME *gentm = (ASN1_GENERALIZEDTIME *)tm;
115
116 if (gentm->length < 12)
117 return 0;
118 if (gentm->data[0] != 0x32 || gentm->data[1] != 0x30)
119 return 0;
120 if (out->size < gentm->length-2)
121 return -1;
122
123 memcpy(out->area, gentm->data+2, gentm->length-2);
124 out->data = gentm->length-2;
125 return 1;
126 }
127 else if (tm->type == V_ASN1_UTCTIME) {
128 ASN1_UTCTIME *utctm = (ASN1_UTCTIME *)tm;
129
130 if (utctm->length < 10)
131 return 0;
132 if (utctm->data[0] >= 0x35)
133 return 0;
134 if (out->size < utctm->length)
135 return -1;
136
137 memcpy(out->area, utctm->data, utctm->length);
138 out->data = utctm->length;
139 return 1;
140 }
141
142 return 0;
143}
144
145/* Extract an entry from a X509_NAME and copy its value to an output chunk.
146 * Returns 1 if entry found, 0 if entry not found, or -1 if output not large enough.
147 */
148int ssl_sock_get_dn_entry(X509_NAME *a, const struct buffer *entry, int pos,
149 struct buffer *out)
150{
151 X509_NAME_ENTRY *ne;
152 ASN1_OBJECT *obj;
153 ASN1_STRING *data;
154 const unsigned char *data_ptr;
155 int data_len;
156 int i, j, n;
157 int cur = 0;
158 const char *s;
159 char tmp[128];
160 int name_count;
161
162 name_count = X509_NAME_entry_count(a);
163
164 out->data = 0;
165 for (i = 0; i < name_count; i++) {
166 if (pos < 0)
167 j = (name_count-1) - i;
168 else
169 j = i;
170
171 ne = X509_NAME_get_entry(a, j);
172 obj = X509_NAME_ENTRY_get_object(ne);
173 data = X509_NAME_ENTRY_get_data(ne);
174 data_ptr = ASN1_STRING_get0_data(data);
175 data_len = ASN1_STRING_length(data);
176 n = OBJ_obj2nid(obj);
177 if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
178 i2t_ASN1_OBJECT(tmp, sizeof(tmp), obj);
179 s = tmp;
180 }
181
182 if (chunk_strcasecmp(entry, s) != 0)
183 continue;
184
185 if (pos < 0)
186 cur--;
187 else
188 cur++;
189
190 if (cur != pos)
191 continue;
192
193 if (data_len > out->size)
194 return -1;
195
196 memcpy(out->area, data_ptr, data_len);
197 out->data = data_len;
198 return 1;
199 }
200
201 return 0;
202
203}
204
205/*
206 * Extract the DN in the specified format from the X509_NAME and copy result to a chunk.
207 * Currently supports rfc2253 for returning LDAP V3 DNs.
208 * Returns 1 if dn entries exist, 0 if no dn entry was found.
209 */
210int ssl_sock_get_dn_formatted(X509_NAME *a, const struct buffer *format, struct buffer *out)
211{
212 BIO *bio = NULL;
213 int ret = 0;
214 int data_len = 0;
215
216 if (chunk_strcmp(format, "rfc2253") == 0) {
217 bio = BIO_new(BIO_s_mem());
218 if (bio == NULL)
219 goto out;
220
221 if (X509_NAME_print_ex(bio, a, 0, XN_FLAG_RFC2253) < 0)
222 goto out;
223
224 if ((data_len = BIO_read(bio, out->area, out->size)) <= 0)
225 goto out;
226
227 out->data = data_len;
228
229 ret = 1;
230 }
231out:
232 if (bio)
233 BIO_free(bio);
234 return ret;
235}
236
237/* Extract and format full DN from a X509_NAME and copy result into a chunk
238 * Returns 1 if dn entries exits, 0 if no dn entry found or -1 if output is not large enough.
239 */
240int ssl_sock_get_dn_oneline(X509_NAME *a, struct buffer *out)
241{
242 X509_NAME_ENTRY *ne;
243 ASN1_OBJECT *obj;
244 ASN1_STRING *data;
245 const unsigned char *data_ptr;
246 int data_len;
247 int i, n, ln;
248 int l = 0;
249 const char *s;
250 char *p;
251 char tmp[128];
252 int name_count;
253
254
255 name_count = X509_NAME_entry_count(a);
256
257 out->data = 0;
258 p = out->area;
259 for (i = 0; i < name_count; i++) {
260 ne = X509_NAME_get_entry(a, i);
261 obj = X509_NAME_ENTRY_get_object(ne);
262 data = X509_NAME_ENTRY_get_data(ne);
263 data_ptr = ASN1_STRING_get0_data(data);
264 data_len = ASN1_STRING_length(data);
265 n = OBJ_obj2nid(obj);
266 if ((n == NID_undef) || ((s = OBJ_nid2sn(n)) == NULL)) {
267 i2t_ASN1_OBJECT(tmp, sizeof(tmp), obj);
268 s = tmp;
269 }
270 ln = strlen(s);
271
272 l += 1 + ln + 1 + data_len;
273 if (l > out->size)
274 return -1;
275 out->data = l;
276
277 *(p++)='/';
278 memcpy(p, s, ln);
279 p += ln;
280 *(p++)='=';
281 memcpy(p, data_ptr, data_len);
282 p += data_len;
283 }
284
285 if (!out->data)
286 return 0;
287
288 return 1;
289}
290
Remi Tricot-Le Breton74f6ab62021-08-19 18:06:30 +0200291
292extern int ssl_client_crt_ref_index;
293
294/*
295 * This function fetches the SSL certificate for a specific connection (either
296 * client certificate or server certificate depending on the cert_peer
297 * parameter).
298 * When trying to get the peer certificate from the server side, we first try to
299 * use the dedicated SSL_get_peer_certificate function, but we fall back to
300 * trying to get the client certificate reference that might have been stored in
301 * the SSL structure's ex_data during the verification process.
302 * Returns NULL in case of failure.
303 */
304X509* ssl_sock_get_peer_certificate(SSL *ssl)
305{
306 X509* cert;
307
308 cert = SSL_get_peer_certificate(ssl);
309 /* Get the client certificate reference stored in the SSL
310 * structure's ex_data during the verification process. */
311 if (!cert) {
312 cert = SSL_get_ex_data(ssl, ssl_client_crt_ref_index);
313 if (cert)
314 X509_up_ref(cert);
315 }
316
317 return cert;
318}
William Lallemand44d862d2021-08-21 23:16:06 +0200319
320/*
Abhijeet Rastogidf97f472023-05-13 20:04:45 -0700321 * This function fetches the x509* for the root CA of client certificate
322 * from the verified chain. We use the SSL_get0_verified_chain and get the
323 * last certificate in the x509 stack.
324 *
325 * Returns NULL in case of failure.
326*/
William Lallemand6e0c39d2023-05-15 12:05:55 +0200327#ifdef HAVE_SSL_get0_verified_chain
Abhijeet Rastogidf97f472023-05-13 20:04:45 -0700328X509* ssl_sock_get_verified_chain_root(SSL *ssl)
329{
330 STACK_OF(X509) *chain = NULL;
331 X509 *crt = NULL;
332 int i;
333
334 chain = SSL_get0_verified_chain(ssl);
335 if (!chain)
336 return NULL;
337
338 for (i = 0; i < sk_X509_num(chain); i++) {
339 crt = sk_X509_value(chain, i);
340
341 if (X509_check_issued(crt, crt) == X509_V_OK)
342 break;
343 }
344
345 return crt;
346}
William Lallemand6e0c39d2023-05-15 12:05:55 +0200347#endif
Abhijeet Rastogidf97f472023-05-13 20:04:45 -0700348
349/*
William Lallemand44d862d2021-08-21 23:16:06 +0200350 * Take an OpenSSL version in text format and return a numeric openssl version
351 * Return 0 if it failed to parse the version
352 *
353 * https://www.openssl.org/docs/man1.1.1/man3/OPENSSL_VERSION_NUMBER.html
354 *
355 * MNNFFPPS: major minor fix patch status
356 *
357 * The status nibble has one of the values 0 for development, 1 to e for betas
358 * 1 to 14, and f for release.
359 *
360 * for example
361 *
362 * 0x0090821f 0.9.8zh
363 * 0x1000215f 1.0.2u
364 * 0x30000000 3.0.0-alpha17
365 * 0x30000002 3.0.0-beta2
366 * 0x3000000e 3.0.0-beta14
367 * 0x3000000f 3.0.0
368 */
369unsigned int openssl_version_parser(const char *version)
370{
371 unsigned int numversion;
372 unsigned int major = 0, minor = 0, fix = 0, patch = 0, status = 0;
373 char *p, *end;
374
375 p = (char *)version;
376
377 if (!p || !*p)
378 return 0;
379
380 major = strtol(p, &end, 10);
381 if (*end != '.' || major > 0xf)
382 goto error;
383 p = end + 1;
384
385 minor = strtol(p, &end, 10);
386 if (*end != '.' || minor > 0xff)
387 goto error;
388 p = end + 1;
389
390 fix = strtol(p, &end, 10);
391 if (fix > 0xff)
392 goto error;
393 p = end;
394
395 if (!*p) {
396 /* end of the string, that's a release */
397 status = 0xf;
398 } else if (*p == '-') {
399 /* after the hyphen, only the beta will increment the status
400 * counter, all others versions will be considered as "dev" and
401 * does not increment anything */
402 p++;
403
404 if (!strncmp(p, "beta", 4)) {
405 p += 4;
William Lallemand8b673f02021-08-22 13:36:11 +0200406 status = strtol(p, &end, 10);
407 if (status > 14)
408 goto error;
William Lallemand44d862d2021-08-21 23:16:06 +0200409 }
410 } else {
411 /* that's a patch release */
412 patch = 1;
413
414 /* add the value of each letter */
415 while (*p) {
416 patch += (*p & ~0x20) - 'A';
417 p++;
418 }
419 status = 0xf;
420 }
421
422end:
423 numversion = ((major & 0xf) << 28) | ((minor & 0xff) << 20) | ((fix & 0xff) << 12) | ((patch & 0xff) << 4) | (status & 0xf);
424 return numversion;
425
426error:
427 return 0;
428
429}
Marcin Deranek959a48c2021-07-13 15:14:21 +0200430
431/* Exclude GREASE (RFC8701) values from input buffer */
432void exclude_tls_grease(char *input, int len, struct buffer *output)
433{
434 int ptr = 0;
435
436 while (ptr < len - 1) {
437 if (input[ptr] != input[ptr+1] || (input[ptr] & 0x0f) != 0x0a) {
438 if (output->data <= output->size - 2) {
439 memcpy(output->area + output->data, input + ptr, 2);
440 output->data += 2;
441 } else
442 break;
443 }
444 ptr += 2;
445 }
446 if (output->size - output->data > 0 && len - ptr > 0)
447 output->area[output->data++] = input[ptr];
448}
William Lallemand960fb742022-11-03 16:31:50 +0100449
450/*
451 * The following generates an array <x509_v_codes> in which the X509_V_ERR_*
452 * codes are populated with there string equivalent. Depending on the version
453 * of the SSL library, some code does not exist, these will be populated as
454 * "-1" in the array.
455 *
456 * The list was taken from
457 * https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
458 * and must be updated when new constant are introduced.
459 */
460
William Lallemand960fb742022-11-03 16:31:50 +0100461#undef _Q
Willy Tarreaua8a83bc2022-11-14 08:11:32 +0100462#define _Q(x) (#x)
William Lallemand960fb742022-11-03 16:31:50 +0100463#undef V
Willy Tarreaua8a83bc2022-11-14 08:11:32 +0100464#define V(x) { .code = -1, .value = _Q(x), .string = #x }
William Lallemand960fb742022-11-03 16:31:50 +0100465
Willy Tarreaua8a83bc2022-11-14 08:11:32 +0100466static struct x509_v_codes {
467 int code; // integer value of the code or -1 if undefined
468 const char *value; // value of the macro as a string or its name
469 const char *string; // name of the macro
William Lallemand960fb742022-11-03 16:31:50 +0100470} x509_v_codes[] = {
471 V(X509_V_OK),
472 V(X509_V_ERR_UNSPECIFIED),
473 V(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT),
474 V(X509_V_ERR_UNABLE_TO_GET_CRL),
475 V(X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE),
476 V(X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE),
477 V(X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY),
478 V(X509_V_ERR_CERT_SIGNATURE_FAILURE),
479 V(X509_V_ERR_CRL_SIGNATURE_FAILURE),
480 V(X509_V_ERR_CERT_NOT_YET_VALID),
481 V(X509_V_ERR_CERT_HAS_EXPIRED),
482 V(X509_V_ERR_CRL_NOT_YET_VALID),
483 V(X509_V_ERR_CRL_HAS_EXPIRED),
484 V(X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD),
485 V(X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD),
486 V(X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD),
487 V(X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD),
488 V(X509_V_ERR_OUT_OF_MEM),
489 V(X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT),
490 V(X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN),
491 V(X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY),
492 V(X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE),
493 V(X509_V_ERR_CERT_CHAIN_TOO_LONG),
494 V(X509_V_ERR_CERT_REVOKED),
495 V(X509_V_ERR_NO_ISSUER_PUBLIC_KEY),
496 V(X509_V_ERR_PATH_LENGTH_EXCEEDED),
497 V(X509_V_ERR_INVALID_PURPOSE),
498 V(X509_V_ERR_CERT_UNTRUSTED),
499 V(X509_V_ERR_CERT_REJECTED),
500 V(X509_V_ERR_SUBJECT_ISSUER_MISMATCH),
501 V(X509_V_ERR_AKID_SKID_MISMATCH),
502 V(X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH),
503 V(X509_V_ERR_KEYUSAGE_NO_CERTSIGN),
504 V(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER),
505 V(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION),
506 V(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN),
507 V(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION),
508 V(X509_V_ERR_INVALID_NON_CA),
509 V(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED),
510 V(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE),
511 V(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED),
512 V(X509_V_ERR_INVALID_EXTENSION),
513 V(X509_V_ERR_INVALID_POLICY_EXTENSION),
514 V(X509_V_ERR_NO_EXPLICIT_POLICY),
515 V(X509_V_ERR_DIFFERENT_CRL_SCOPE),
516 V(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE),
517 V(X509_V_ERR_UNNESTED_RESOURCE),
518 V(X509_V_ERR_PERMITTED_VIOLATION),
519 V(X509_V_ERR_EXCLUDED_VIOLATION),
520 V(X509_V_ERR_SUBTREE_MINMAX),
521 V(X509_V_ERR_APPLICATION_VERIFICATION),
522 V(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE),
523 V(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX),
524 V(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX),
525 V(X509_V_ERR_CRL_PATH_VALIDATION_ERROR),
526 V(X509_V_ERR_PATH_LOOP),
527 V(X509_V_ERR_SUITE_B_INVALID_VERSION),
528 V(X509_V_ERR_SUITE_B_INVALID_ALGORITHM),
529 V(X509_V_ERR_SUITE_B_INVALID_CURVE),
530 V(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM),
531 V(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED),
532 V(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256),
533 V(X509_V_ERR_HOSTNAME_MISMATCH),
534 V(X509_V_ERR_EMAIL_MISMATCH),
535 V(X509_V_ERR_IP_ADDRESS_MISMATCH),
536 V(X509_V_ERR_DANE_NO_MATCH),
537 V(X509_V_ERR_EE_KEY_TOO_SMALL),
538 V(X509_V_ERR_CA_KEY_TOO_SMALL),
539 V(X509_V_ERR_CA_MD_TOO_WEAK),
540 V(X509_V_ERR_INVALID_CALL),
541 V(X509_V_ERR_STORE_LOOKUP),
542 V(X509_V_ERR_NO_VALID_SCTS),
543 V(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION),
544 V(X509_V_ERR_OCSP_VERIFY_NEEDED),
545 V(X509_V_ERR_OCSP_VERIFY_FAILED),
546 V(X509_V_ERR_OCSP_CERT_UNKNOWN),
547 V(X509_V_ERR_UNSUPPORTED_SIGNATURE_ALGORITHM),
548 V(X509_V_ERR_SIGNATURE_ALGORITHM_MISMATCH),
549 V(X509_V_ERR_SIGNATURE_ALGORITHM_INCONSISTENCY),
550 V(X509_V_ERR_INVALID_CA),
551 V(X509_V_ERR_PATHLEN_INVALID_FOR_NON_CA),
552 V(X509_V_ERR_PATHLEN_WITHOUT_KU_KEY_CERT_SIGN),
553 V(X509_V_ERR_KU_KEY_CERT_SIGN_INVALID_FOR_NON_CA),
554 V(X509_V_ERR_ISSUER_NAME_EMPTY),
555 V(X509_V_ERR_SUBJECT_NAME_EMPTY),
556 V(X509_V_ERR_MISSING_AUTHORITY_KEY_IDENTIFIER),
557 V(X509_V_ERR_MISSING_SUBJECT_KEY_IDENTIFIER),
558 V(X509_V_ERR_EMPTY_SUBJECT_ALT_NAME),
559 V(X509_V_ERR_EMPTY_SUBJECT_SAN_NOT_CRITICAL),
560 V(X509_V_ERR_CA_BCONS_NOT_CRITICAL),
561 V(X509_V_ERR_AUTHORITY_KEY_IDENTIFIER_CRITICAL),
562 V(X509_V_ERR_SUBJECT_KEY_IDENTIFIER_CRITICAL),
563 V(X509_V_ERR_CA_CERT_MISSING_KEY_USAGE),
564 V(X509_V_ERR_EXTENSIONS_REQUIRE_VERSION_3),
565 V(X509_V_ERR_EC_KEY_EXPLICIT_PARAMS),
Willy Tarreaua8a83bc2022-11-14 08:11:32 +0100566 { 0, NULL, NULL },
William Lallemand960fb742022-11-03 16:31:50 +0100567};
568
569/*
570 * Return the X509_V_ERR code corresponding to the name of the constant.
571 * See https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
572 * If not found, return -1
573 */
574int x509_v_err_str_to_int(const char *str)
575{
576 int i;
577
578 for (i = 0; x509_v_codes[i].string; i++) {
579 if (strcmp(str, x509_v_codes[i].string) == 0) {
580 return x509_v_codes[i].code;
581 }
582 }
583
584 return -1;
585}
586
587/*
588 * Return the constant name corresponding to the X509_V_ERR code
589 * See https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
590 * If not found, return NULL;
591 */
592const char *x509_v_err_int_to_str(int code)
593{
594 int i;
595
596 if (code == -1)
597 return NULL;
598
599 for (i = 0; x509_v_codes[i].string; i++) {
600 if (x509_v_codes[i].code == code) {
601 return x509_v_codes[i].string;
602 }
603 }
604 return NULL;
605}
Willy Tarreaua8a83bc2022-11-14 08:11:32 +0100606
607void init_x509_v_err_tab(void)
608{
609 int i;
610
611 for (i = 0; x509_v_codes[i].string; i++) {
612 /* either the macro exists or it's equal to its own name */
613 if (strcmp(x509_v_codes[i].string, x509_v_codes[i].value) == 0)
614 continue;
615 x509_v_codes[i].code = atoi(x509_v_codes[i].value);
616 }
617}
618
619INITCALL0(STG_REGISTER, init_x509_v_err_tab);
Remi Tricot-Le Bretonc8d814e2022-12-20 11:11:17 +0100620
621
622/*
623 * This function returns the number of seconds elapsed
624 * since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the
625 * date presented un ASN1_GENERALIZEDTIME.
626 *
627 * In parsing error case, it returns -1.
628 */
629long asn1_generalizedtime_to_epoch(ASN1_GENERALIZEDTIME *d)
630{
631 long epoch;
632 char *p, *end;
633 const unsigned short month_offset[12] = {
634 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
635 };
636 unsigned long year, month;
637
638 if (!d || (d->type != V_ASN1_GENERALIZEDTIME)) return -1;
639
640 p = (char *)d->data;
641 end = p + d->length;
642
643 if (end - p < 4) return -1;
644 year = 1000 * (p[0] - '0') + 100 * (p[1] - '0') + 10 * (p[2] - '0') + p[3] - '0';
645 p += 4;
646 if (end - p < 2) return -1;
647 month = 10 * (p[0] - '0') + p[1] - '0';
648 if (month < 1 || month > 12) return -1;
649 /* Compute the number of seconds since 1 jan 1970 and the beginning of current month
650 We consider leap years and the current month (<marsh or not) */
651 epoch = ( ((year - 1970) * 365)
652 + ((year - (month < 3)) / 4 - (year - (month < 3)) / 100 + (year - (month < 3)) / 400)
653 - ((1970 - 1) / 4 - (1970 - 1) / 100 + (1970 - 1) / 400)
654 + month_offset[month-1]
655 ) * 24 * 60 * 60;
656 p += 2;
657 if (end - p < 2) return -1;
658 /* Add the number of seconds of completed days of current month */
659 epoch += (10 * (p[0] - '0') + p[1] - '0' - 1) * 24 * 60 * 60;
660 p += 2;
661 if (end - p < 2) return -1;
662 /* Add the completed hours of the current day */
663 epoch += (10 * (p[0] - '0') + p[1] - '0') * 60 * 60;
664 p += 2;
665 if (end - p < 2) return -1;
666 /* Add the completed minutes of the current hour */
667 epoch += (10 * (p[0] - '0') + p[1] - '0') * 60;
668 p += 2;
669 if (p == end) return -1;
670 /* Test if there is available seconds */
671 if (p[0] < '0' || p[0] > '9')
672 goto nosec;
673 if (end - p < 2) return -1;
674 /* Add the seconds of the current minute */
675 epoch += 10 * (p[0] - '0') + p[1] - '0';
676 p += 2;
677 if (p == end) return -1;
678 /* Ignore seconds float part if present */
679 if (p[0] == '.') {
680 do {
681 if (++p == end) return -1;
682 } while (p[0] >= '0' && p[0] <= '9');
683 }
684
685nosec:
686 if (p[0] == 'Z') {
687 if (end - p != 1) return -1;
688 return epoch;
689 }
690 else if (p[0] == '+') {
691 if (end - p != 5) return -1;
692 /* Apply timezone offset */
693 return epoch - ((10 * (p[1] - '0') + p[2] - '0') * 60 * 60 + (10 * (p[3] - '0') + p[4] - '0')) * 60;
694 }
695 else if (p[0] == '-') {
696 if (end - p != 5) return -1;
697 /* Apply timezone offset */
698 return epoch + ((10 * (p[1] - '0') + p[2] - '0') * 60 * 60 + (10 * (p[3] - '0') + p[4] - '0')) * 60;
699 }
700
701 return -1;
702}