Asterisk - The Open Source Telephony Project  18.5.0
stir_shaken.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2020, Sangoma Technologies Corporation
5  *
6  * Kevin Harwell <[email protected]>
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 /*! \file
20  *
21  * \brief Internal stir/shaken utilities
22  */
23 
24 #include "asterisk.h"
25 
26 #include <openssl/evp.h>
27 #include <openssl/pem.h>
28 
29 #include "asterisk/cli.h"
30 #include "asterisk/sorcery.h"
31 
32 #include "stir_shaken.h"
34 
35 int stir_shaken_cli_show(void *obj, void *arg, int flags)
36 {
37  struct ast_cli_args *a = arg;
38  struct ast_variable *options;
39  struct ast_variable *i;
40 
41  if (!obj) {
42  ast_cli(a->fd, "No stir/shaken configuration found\n");
43  return 0;
44  }
45 
48  if (!options) {
49  return 0;
50  }
51 
52  ast_cli(a->fd, "%s: %s\n", ast_sorcery_object_get_type(obj),
54 
55  for (i = options; i; i = i->next) {
56  ast_cli(a->fd, "\t%s: %s\n", i->name, i->value);
57  }
58 
59  ast_cli(a->fd, "\n");
60 
61  ast_variables_destroy(options);
62 
63  return 0;
64 }
65 
67 {
68  void *obj;
69  struct ao2_iterator it;
70  int wordlen = strlen(word);
71  int ret;
72 
73  it = ao2_iterator_init(container, 0);
74  while ((obj = ao2_iterator_next(&it))) {
75  if (!strncasecmp(word, ast_sorcery_object_get_id(obj), wordlen)) {
77  if (ret) {
78  ao2_ref(obj, -1);
79  break;
80  }
81  }
82  ao2_ref(obj, -1);
83  }
85 
86  return NULL;
87 }
88 
89 EVP_PKEY *stir_shaken_read_key(const char *path, int priv)
90 {
91  EVP_PKEY *key = NULL;
92  FILE *fp;
93  X509 *cert = NULL;
94 
95  fp = fopen(path, "r");
96  if (!fp) {
97  ast_log(LOG_ERROR, "Failed to read %s key file '%s'\n", priv ? "private" : "public", path);
98  return NULL;
99  }
100 
101  /* If this is to get the private key, the file will be ECDSA or RSA, with the former eventually
102  * replacing the latter. For the public key, the file will be X.509.
103  */
104  if (priv) {
105  key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
106  } else {
107  cert = PEM_read_X509(fp, NULL, NULL, NULL);
108  if (!cert) {
109  ast_log(LOG_ERROR, "Failed to read X.509 cert from file '%s'\n", path);
110  fclose(fp);
111  return NULL;
112  }
113  key = X509_get_pubkey(cert);
114  /* It's fine to free the cert after we get the key because they are 2
115  * independent objects; you don't need a X509 object to be in memory
116  * in order to have an EVP_PKEY, and it doesn't rely on it being there.
117  */
118  X509_free(cert);
119  }
120 
121  if (!key) {
122  ast_log(LOG_ERROR, "Failed to read %s key from file '%s'\n", priv ? "private" : "public", path);
123  fclose(fp);
124  return NULL;
125  }
126 
127  if (EVP_PKEY_id(key) != EVP_PKEY_EC && EVP_PKEY_id(key) != EVP_PKEY_RSA) {
128  ast_log(LOG_ERROR, "%s key from '%s' must be of type EVP_PKEY_EC or EVP_PKEY_RSA\n",
129  priv ? "Private" : "Public", path);
130  fclose(fp);
131  EVP_PKEY_free(key);
132  return NULL;
133  }
134 
135  fclose(fp);
136 
137  return key;
138 }
139 
140 char *stir_shaken_get_serial_number_x509(const char *path)
141 {
142  FILE *fp;
143  X509 *cert;
144  ASN1_INTEGER *serial;
145  BIGNUM *bignum;
146  char *serial_hex;
147  char *ret;
148 
149  fp = fopen(path, "r");
150  if (!fp) {
151  ast_log(LOG_ERROR, "Failed to open file %s\n", path);
152  return NULL;
153  }
154 
155  cert = PEM_read_X509(fp, NULL, NULL, NULL);
156  if (!cert) {
157  ast_log(LOG_ERROR, "Failed to read X.509 cert from file %s\n", path);
158  fclose(fp);
159  return NULL;
160  }
161 
162  serial = X509_get_serialNumber(cert);
163  if (!serial) {
164  ast_log(LOG_ERROR, "Failed to get serial number from certificate %s\n", path);
165  X509_free(cert);
166  fclose(fp);
167  return NULL;
168  }
169 
170  bignum = ASN1_INTEGER_to_BN(serial, NULL);
171  if (bignum == NULL) {
172  ast_log(LOG_ERROR, "Failed to convert serial to bignum for certificate %s\n", path);
173  X509_free(cert);
174  fclose(fp);
175  return NULL;
176  }
177 
178  /* This will return a string with memory allocated. After we get the string,
179  * we don't need the cert, file, or bignum references anymore, so free them
180  * and return the string, if BN_bn2hex was a success.
181  */
182  serial_hex = BN_bn2hex(bignum);
183  X509_free(cert);
184  fclose(fp);
185  BN_free(bignum);
186 
187  if (!serial_hex) {
188  ast_log(LOG_ERROR, "Failed to convert bignum to hex for certificate %s\n", path);
189  return NULL;
190  }
191 
192  ret = ast_strdup(serial_hex);
193  OPENSSL_free(serial_hex);
194  if (!ret) {
195  ast_log(LOG_ERROR, "Failed to dup serial from openssl for certificate %s\n", path);
196  return NULL;
197  }
198 
199  return ret;
200 }
char * stir_shaken_get_serial_number_x509(const char *path)
Gets the serial number in hex form from the X509 certificate at path.
Definition: stir_shaken.c:140
struct ast_variable * next
struct ast_variable * ast_variable_list_sort(struct ast_variable *head)
Performs an in-place sort on the variable list by ascending name.
Definition: main/config.c:622
struct ast_variable * ast_sorcery_objectset_create2(const struct ast_sorcery *sorcery, const void *object, enum ast_sorcery_field_handler_flags flags)
Create an object set (KVP list) for an object.
Definition: sorcery.c:1511
Asterisk main include file. File version handling, generic pbx functions.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
Structure for variables, used for configurations and for channel variables.
EVP_PKEY * stir_shaken_read_key(const char *path, int priv)
Reads the public (or private) key from the specified path.
Definition: stir_shaken.c:89
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
#define NULL
Definition: resample.c:96
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
char * stir_shaken_tab_complete_name(const char *word, struct ao2_container *container)
Tab completion for name matching with STIR/SHAKEN CLI commands.
Definition: stir_shaken.c:66
const char * ast_sorcery_object_get_type(const void *object)
Get the type of a sorcery object.
Definition: sorcery.c:2324
#define ast_log
Definition: astobj2.c:42
const int fd
Definition: cli.h:159
#define ao2_ref(o, delta)
Definition: astobj2.h:464
Use string handler only.
Definition: sorcery.h:137
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2312
struct ao2_container * container
Definition: res_fax.c:502
int stir_shaken_cli_show(void *obj, void *arg, int flags)
Output configuration settings to the Asterisk CLI.
Definition: stir_shaken.c:35
#define LOG_ERROR
Definition: logger.h:285
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
struct ast_sorcery * ast_stir_shaken_sorcery(void)
Retrieve the stir/shaken sorcery context.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
Standard Command Line Interface.
Generic container type.
static struct test_options options
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2726
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
short word
Sorcery Data Access Layer API.
static struct test_val a