Asterisk - The Open Source Telephony Project GIT-master-55f4e6d
crypto_utils.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2023, Sangoma Technologies Corporation
5 *
6 * George Joseph <gjoseph@sangoma.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19#include <openssl/err.h>
20#include <openssl/ssl.h>
21#include <openssl/evp.h>
22#include <openssl/md5.h>
23#include <openssl/sha.h>
24#include <openssl/bio.h>
25#include <openssl/obj_mac.h>
26#include <openssl/x509.h>
27#include <openssl/x509v3.h>
28#include <openssl/x509_vfy.h>
29
30#include "crypto_utils.h"
31
32#include "asterisk.h"
33#include "asterisk/logger.h"
34#include "asterisk/module.h"
36#include "asterisk/utils.h"
37#include "asterisk/vector.h"
38#include "asterisk/cli.h"
39
40void __attribute__((format(printf, 5, 6)))
41crypto_log_openssl(int level, char *file, int line, const char *function,
42 const char *fmt, ...)
43{
44 FILE *fp;
45 char *buffer;
46 size_t length;
47 va_list ap;
48 char *tmp_fmt;
49
50 fp = open_memstream(&buffer, &length);
51 if (!fp) {
52 return;
53 }
54
55 va_start(ap, fmt);
56 if (!ast_strlen_zero(fmt)) {
57 size_t fmt_len = strlen(fmt);
58 if (fmt[fmt_len - 1] == '\n') {
59 tmp_fmt = ast_strdupa(fmt);
60 tmp_fmt[fmt_len - 1] = '\0';
61 fmt = tmp_fmt;
62 }
63 }
64 vfprintf(fp, fmt, ap);
65 fputs(": ", fp);
66 ERR_print_errors_fp(fp);
67 fclose(fp);
68
69 if (length) {
70 ast_log(level, file, line, function, "%s\n", buffer);
71 }
72
73 ast_std_free(buffer);
74}
75
76int crypto_register_x509_extension(const char *oid, const char *short_name,
77 const char *long_name)
78{
79 int nid = 0;
80
81 if (ast_strlen_zero(oid) || ast_strlen_zero(short_name) ||
82 ast_strlen_zero(long_name)) {
83 ast_log(LOG_ERROR, "One or more of oid, short_name or long_name are NULL or empty\n");
84 return -1;
85 }
86
87 nid = OBJ_sn2nid(short_name);
88 if (nid != NID_undef) {
89 ast_log(LOG_NOTICE, "NID %d, object %s already registered\n", nid, short_name);
90 return nid;
91 }
92
93 nid = OBJ_create(oid, short_name, long_name);
94 if (nid == NID_undef) {
95 crypto_log_openssl(LOG_ERROR, "Couldn't register %s X509 extension\n", short_name);
96 return -1;
97 }
98 ast_log(LOG_NOTICE, "Registered object %s as NID %d\n", short_name, nid);
99
100 return nid;
101}
102
103ASN1_OCTET_STRING *crypto_get_cert_extension_data(X509 *cert,
104 int nid, const char *short_name)
105{
106 int ex_idx;
107 X509_EXTENSION *ex;
108
109 if (nid <= 0) {
110 nid = OBJ_sn2nid(short_name);
111 if (nid == NID_undef) {
112 ast_log(LOG_ERROR, "Extension object for %s not found\n", short_name);
113 return NULL;
114 }
115 } else {
116 const char *tmp = OBJ_nid2sn(nid);
117 if (!tmp) {
118 ast_log(LOG_ERROR, "Extension object for NID %d not found\n", nid);
119 return NULL;
120 }
121 }
122
123 ex_idx = X509_get_ext_by_NID(cert, nid, -1);
124 if (ex_idx < 0) {
125 ast_log(LOG_ERROR, "Extension index not found in certificate\n");
126 return NULL;
127 }
128 ex = X509_get_ext(cert, ex_idx);
129 if (!ex) {
130 ast_log(LOG_ERROR, "Extension not found in certificate\n");
131 return NULL;
132 }
133
134 return X509_EXTENSION_get_data(ex);
135}
136
137EVP_PKEY *crypto_load_privkey_from_file(const char *filename)
138{
139 EVP_PKEY *key = NULL;
140 FILE *fp;
141
142 if (ast_strlen_zero(filename)) {
143 ast_log(LOG_ERROR, "filename was null or empty\n");
144 return NULL;
145 }
146
147 fp = fopen(filename, "r");
148 if (!fp) {
149 ast_log(LOG_ERROR, "Failed to open %s: %s\n", filename, strerror(errno));
150 return NULL;
151 }
152
153 key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
154 fclose(fp);
155 if (!key) {
156 crypto_log_openssl(LOG_ERROR, "Failed to load private key from %s\n", filename);
157 }
158 return key;
159}
160
161X509 *crypto_load_cert_from_file(const char *filename)
162{
163 FILE *fp;
164 X509 *cert = NULL;
165
166 if (ast_strlen_zero(filename)) {
167 ast_log(LOG_ERROR, "filename was null or empty\n");
168 return NULL;
169 }
170
171 fp = fopen(filename, "r");
172 if (!fp) {
173 ast_log(LOG_ERROR, "Failed to open %s: %s\n", filename, strerror(errno));
174 return NULL;
175 }
176
177 cert = PEM_read_X509(fp, &cert, NULL, NULL);
178 fclose(fp);
179 if (!cert) {
180 crypto_log_openssl(LOG_ERROR, "Failed to create cert from %s\n", filename);
181 }
182 return cert;
183}
184
185X509 *crypto_load_cert_from_memory(const char *buffer, size_t size)
186{
187 RAII_VAR(BIO *, bio, NULL, BIO_free_all);
188 X509 *cert = NULL;
189
190 if (ast_strlen_zero(buffer) || size <= 0) {
191 ast_log(LOG_ERROR, "buffer was null or empty\n");
192 return NULL;
193 }
194
195 bio = BIO_new_mem_buf(buffer, size);
196 if (!bio) {
197 crypto_log_openssl(LOG_ERROR, "Unable to create memory BIO\n");
198 return NULL;
199 }
200
201 cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);
202 if (!cert) {
203 crypto_log_openssl(LOG_ERROR, "Failed to create cert from BIO\n");
204 }
205 return cert;
206}
207
208static EVP_PKEY *load_private_key_from_memory(const char *buffer, size_t size)
209{
210 RAII_VAR(BIO *, bio, NULL, BIO_free_all);
211 EVP_PKEY *key = NULL;
212
213 if (ast_strlen_zero(buffer) || size <= 0) {
214 ast_log(LOG_ERROR, "buffer was null or empty\n");
215 return NULL;
216 }
217
218 bio = BIO_new_mem_buf(buffer, size);
219 if (!bio) {
220 crypto_log_openssl(LOG_ERROR, "Unable to create memory BIO\n");
221 return NULL;
222 }
223
224 key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
225
226 return key;
227}
228
229EVP_PKEY *crypto_load_private_key_from_memory(const char *buffer, size_t size)
230{
231 EVP_PKEY *key = load_private_key_from_memory(buffer, size);
232 if (!key) {
233 crypto_log_openssl(LOG_ERROR, "Unable to load private key from memory\n");
234 }
235 return key;
236}
237
238int crypto_has_private_key_from_memory(const char *buffer, size_t size)
239{
240 RAII_VAR(EVP_PKEY *, key, load_private_key_from_memory(buffer, size), EVP_PKEY_free);
241
242 return key ? 1 : 0;
243}
244
245static int dump_mem_bio(BIO *bio, unsigned char **buffer)
246{
247 char *temp_ptr;
248 int raw_key_len;
249
250 raw_key_len = BIO_get_mem_data(bio, &temp_ptr);
251 if (raw_key_len <= 0) {
252 crypto_log_openssl(LOG_ERROR, "Unable to extract raw public key\n");
253 return -1;
254 }
255 *buffer = ast_malloc(raw_key_len);
256 if (!*buffer) {
257 ast_log(LOG_ERROR, "Unable to allocate memory for raw public key\n");
258 return -1;
259 }
260 memcpy(*buffer, temp_ptr, raw_key_len);
261
262 return raw_key_len;
263}
264
265int crypto_extract_raw_pubkey(EVP_PKEY *key, unsigned char **buffer)
266{
267 RAII_VAR(BIO *, bio, NULL, BIO_free_all);
268
269 bio = BIO_new(BIO_s_mem());
270
271 if (!bio || (PEM_write_bio_PUBKEY(bio, key) <= 0)) {
272 crypto_log_openssl(LOG_ERROR, "Unable to write pubkey to BIO\n");
273 return -1;
274 }
275
276 return dump_mem_bio(bio, buffer);
277}
278
280 unsigned char **buffer)
281{
282 RAII_VAR(EVP_PKEY *, public_key, X509_get_pubkey(cert), EVP_PKEY_free);
283
284 if (!public_key) {
285 crypto_log_openssl(LOG_ERROR, "Unable to retrieve pubkey from cert\n");
286 return -1;
287 }
288
289 return crypto_extract_raw_pubkey(public_key, buffer);
290}
291
292int crypto_extract_raw_privkey(EVP_PKEY *key, unsigned char **buffer)
293{
294 RAII_VAR(BIO *, bio, NULL, BIO_free_all);
295
296 bio = BIO_new(BIO_s_mem());
297
298 if (!bio || (PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL) <= 0)) {
299 crypto_log_openssl(LOG_ERROR, "Unable to write privkey to BIO\n");
300 return -1;
301 }
302
303 return dump_mem_bio(bio, buffer);
304}
305
306static void crypto_cert_store_destructor(void *obj)
307{
308 struct crypto_cert_store *store = obj;
309
310 if (store->store) {
311 X509_STORE_free(store->store);
312 }
313}
314
316{
318 if (!store) {
319 ast_log(LOG_ERROR, "Failed to create crypto_cert_store\n");
320 return NULL;
321 }
322 store->store = X509_STORE_new();
323
324 if (!store->store) {
325 crypto_log_openssl(LOG_ERROR, "Failed to create X509_STORE\n");
326 ao2_ref(store, -1);
327 return NULL;
328 }
329
330 return store;
331}
332
334 const char *path)
335{
336 if (ast_strlen_zero(file) && ast_strlen_zero(path)) {
337 ast_log(LOG_ERROR, "Both file and path can't be NULL");
338 return -1;
339 }
340
341 if (!store || !store->store) {
342 ast_log(LOG_ERROR, "store is NULL");
343 return -1;
344 }
345
346 /*
347 * If the file or path are empty strings, we need to pass NULL
348 * so openssl ignores it otherwise it'll try to open a file or
349 * path named ''.
350 */
351 if (!X509_STORE_load_locations(store->store, S_OR(file, NULL), S_OR(path, NULL))) {
352 crypto_log_openssl(LOG_ERROR, "Failed to load store from file '%s' or path '%s'\n",
353 S_OR(file, "N/A"), S_OR(path, "N/A"));
354 return -1;
355 }
356
357 return 0;
358}
359
361{
362#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
363 STACK_OF(X509_OBJECT) *certs = NULL;
364 int count = 0;
365 int i = 0;
366 char subj[1024];
367
368 certs = X509_STORE_get0_objects(store->store);
369 count = sk_X509_OBJECT_num(certs);
370 for (i = 0; i < count ; i++) {
371 X509_OBJECT *o = sk_X509_OBJECT_value(certs, i);
372 X509 *c = X509_OBJECT_get0_X509(o);
373 X509_NAME_oneline(X509_get_subject_name(c), subj, 1024);
374 ast_cli(fd, "%s\n", subj);
375 }
376 return count;
377#else
378 ast_cli(fd, "This command is not supported until OpenSSL 1.1.0\n");
379 return 0;
380#endif
381}
382
383int crypto_is_cert_time_valid(X509*cert, time_t reftime)
384{
385 ASN1_STRING *notbefore;
386 ASN1_STRING *notafter;
387
388 if (!reftime) {
389 reftime = time(NULL);
390 }
391 notbefore = X509_get_notBefore(cert);
392 notafter = X509_get_notAfter(cert);
393 if (!notbefore || !notafter) {
394 ast_log(LOG_ERROR, "Either notbefore or notafter were not present in the cert\n");
395 return 0;
396 }
397
398 return (X509_cmp_time(notbefore, &reftime) < 0 &&
399 X509_cmp_time(notafter, &reftime) > 0);
400}
401
402int crypto_is_cert_trusted(struct crypto_cert_store *store, X509 *cert, const char **err_msg)
403{
404 X509_STORE_CTX *verify_ctx = NULL;
405 int rc = 0;
406
407 if (!(verify_ctx = X509_STORE_CTX_new())) {
408 crypto_log_openssl(LOG_ERROR, "Unable to create verify_ctx\n");
409 return 0;
410 }
411
412 if (X509_STORE_CTX_init(verify_ctx, store->store, cert, NULL) != 1) {
413 X509_STORE_CTX_cleanup(verify_ctx);
414 X509_STORE_CTX_free(verify_ctx);
415 crypto_log_openssl(LOG_ERROR, "Unable to initialize verify_ctx\n");
416 return 0;
417 }
418
419 rc = X509_verify_cert(verify_ctx);
420 if (rc != 1 && err_msg != NULL) {
421 int err = X509_STORE_CTX_get_error(verify_ctx);
422 *err_msg = X509_verify_cert_error_string(err);
423 }
424 X509_STORE_CTX_cleanup(verify_ctx);
425 X509_STORE_CTX_free(verify_ctx);
426
427 return rc;
428}
429
430#define SECS_PER_DAY 86400
431time_t crypto_asn_time_as_time_t(ASN1_TIME *at)
432{
433 int pday;
434 int psec;
435 time_t rt = time(NULL);
436
437 if (!ASN1_TIME_diff(&pday, &psec, NULL, at)) {
438 crypto_log_openssl(LOG_ERROR, "Unable to calculate time diff\n");
439 return 0;
440 }
441
442 rt += ((pday * SECS_PER_DAY) + psec);
443
444 return rt;
445}
446#undef SECS_PER_DAY
447
448char *crypto_get_cert_subject(X509 *cert, const char *short_name)
449{
450 size_t len = 0;
451 RAII_VAR(char *, buffer, NULL, ast_std_free);
452 char *search_buff = NULL;
453 char *search = NULL;
454 size_t search_len = 0;
455 char *rtn = NULL;
456 char *line = NULL;
457 /*
458 * If short_name was supplied, we want a multiline subject
459 * with each component on a separate line. This makes it easier
460 * to iterate over the components to find the one we want.
461 * Otherwise, we just want the whole subject on one line.
462 */
463 unsigned long flags =
464 short_name ? XN_FLAG_FN_SN | XN_FLAG_SEP_MULTILINE : XN_FLAG_ONELINE;
465 FILE *fp = open_memstream(&buffer, &len);
466 BIO *bio = fp ? BIO_new_fp(fp, BIO_CLOSE) : NULL;
467 X509_NAME *subject = X509_get_subject_name(cert);
468 int rc = 0;
469
470 if (!fp || !bio || !subject) {
471 return NULL;
472 }
473
474 rc = X509_NAME_print_ex(bio, subject, 0, flags);
475 BIO_free(bio);
476 if (rc < 0) {
477 return NULL;
478 }
479
480 if (!short_name) {
481 rtn = ast_malloc(len + 1);
482 if (rtn) {
483 strcpy(rtn, buffer); /* Safe */
484 }
485 return rtn;
486 }
487
488 search_len = strlen(short_name) + 1;
489 rc = ast_asprintf(&search, "%s=", short_name);
490 if (rc != search_len) {
491 return NULL;
492 }
493
494 search_buff = buffer;
495 while((line = ast_read_line_from_buffer(&search_buff))) {
496 if (ast_begins_with(line, search)) {
497 rtn = ast_malloc(strlen(line) - search_len + 1);
498 if (rtn) {
499 strcpy(rtn, line + search_len); /* Safe */
500 }
501 break;
502 }
503 }
504
505 ast_std_free(search);
506 return rtn;
507}
508
509int crypto_load(void)
510{
512}
513
515{
516 return 0;
517}
518
Asterisk main include file. File version handling, generic pbx functions.
void ast_std_free(void *ptr)
Definition: astmm.c:1734
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static int tmp()
Definition: bt_open.c:389
Standard Command Line Interface.
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
X509 * crypto_load_cert_from_memory(const char *buffer, size_t size)
Load an X509 Cert from a NULL terminated buffer.
Definition: crypto_utils.c:185
static EVP_PKEY * load_private_key_from_memory(const char *buffer, size_t size)
Definition: crypto_utils.c:208
static void crypto_cert_store_destructor(void *obj)
Definition: crypto_utils.c:306
int crypto_is_cert_trusted(struct crypto_cert_store *store, X509 *cert, const char **err_msg)
Check if the cert is trusted.
Definition: crypto_utils.c:402
void crypto_log_openssl(int level, char *file, int line, const char *function, const char *fmt,...)
Print a log message with any OpenSSL errors appended.
Definition: crypto_utils.c:41
int crypto_get_raw_pubkey_from_cert(X509 *cert, unsigned char **buffer)
Retrieve RAW public key from cert.
Definition: crypto_utils.c:279
int crypto_extract_raw_pubkey(EVP_PKEY *key, unsigned char **buffer)
Extract raw public key from EVP_PKEY.
Definition: crypto_utils.c:265
time_t crypto_asn_time_as_time_t(ASN1_TIME *at)
Return a time_t for an ASN1_TIME.
Definition: crypto_utils.c:431
EVP_PKEY * crypto_load_privkey_from_file(const char *filename)
Load a private key from a file.
Definition: crypto_utils.c:137
int crypto_register_x509_extension(const char *oid, const char *short_name, const char *long_name)
Register a certificate extension to openssl.
Definition: crypto_utils.c:76
int crypto_extract_raw_privkey(EVP_PKEY *key, unsigned char **buffer)
Extract raw private key from EVP_PKEY.
Definition: crypto_utils.c:292
int crypto_load(void)
Initialize the crypto utils.
Definition: crypto_utils.c:509
char * crypto_get_cert_subject(X509 *cert, const char *short_name)
Returns the Subject (or component of Subject) from a certificate.
Definition: crypto_utils.c:448
int crypto_is_cert_time_valid(X509 *cert, time_t reftime)
Check if the reftime is within the cert's valid dates.
Definition: crypto_utils.c:383
struct crypto_cert_store * crypto_create_cert_store(void)
Create an empty X509 store.
Definition: crypto_utils.c:315
static int dump_mem_bio(BIO *bio, unsigned char **buffer)
Definition: crypto_utils.c:245
int crypto_has_private_key_from_memory(const char *buffer, size_t size)
Check if the supplied buffer has a private key.
Definition: crypto_utils.c:238
int crypto_unload(void)
Clean up the crypto utils.
Definition: crypto_utils.c:514
X509 * crypto_load_cert_from_file(const char *filename)
Load an X509 Cert from a file.
Definition: crypto_utils.c:161
#define SECS_PER_DAY
Definition: crypto_utils.c:430
EVP_PKEY * crypto_load_private_key_from_memory(const char *buffer, size_t size)
Load a private key from memory.
Definition: crypto_utils.c:229
int crypto_show_cli_store(struct crypto_cert_store *store, int fd)
Dump a cert store to the asterisk CLI.
Definition: crypto_utils.c:360
ASN1_OCTET_STRING * crypto_get_cert_extension_data(X509 *cert, int nid, const char *short_name)
Return the data from a specific extension in a cert.
Definition: crypto_utils.c:103
int crypto_load_cert_store(struct crypto_cert_store *store, const char *file, const char *path)
Load an X509 Store with either certificates or CRLs.
Definition: crypto_utils.c:333
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Support for logging to various files, console and syslog Configuration in file logger....
#define LOG_ERROR
#define LOG_NOTICE
int errno
Asterisk module definitions.
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
#define NULL
Definition: resample.c:96
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_read_line_from_buffer(char **buffer)
Read lines from a string buffer.
Definition: strings.c:371
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
ao2 object wrapper for X509_STORE that provides locking and refcounting
Definition: crypto_utils.h:170
X509_STORE * store
Definition: crypto_utils.h:171
static struct test_val c
Utility functions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
Vector container support.