STIR/SHAKEN: Fix certificate type and storage.

During OpenSIPit, we found out that the public certificates must be of
type X.509. When reading in public keys, we use the corresponding X.509
functions now.

We also discovered that we needed a better naming scheme for the
certificates since certificates with the same name would cause issues
(overwriting certs, etc.). Now when we download a public certificate, we
get the serial number from it and use that as the name of the cached
certificate.

The configuration option public_key_url in stir_shaken.conf has also
been renamed to public_cert_url, which better describes what the option
is for.

https://wiki.asterisk.org/wiki/display/AST/OpenSIPit+2021

Change-Id: Ia00b20835f5f976e3603797f2f2fb19672d8114d
This commit is contained in:
Ben Ford
2021-04-21 11:12:55 -05:00
committed by George Joseph
parent 09303e8e22
commit 259ecfa289
12 changed files with 376 additions and 163 deletions

View File

@@ -22,8 +22,10 @@
#include "asterisk/logger.h"
#include "curl.h"
#include "general.h"
#include "stir_shaken.h"
#include <curl/curl.h>
#include <sys/stat.h>
/* Used to check CURL headers */
#define MAX_HEADER_LENGTH 1023
@@ -148,33 +150,80 @@ static CURL *get_curl_instance(struct curl_cb_data *data)
return curl;
}
int curl_public_key(const char *public_key_url, const char *path, struct curl_cb_data *data)
/*!
* \brief Create a temporary file located at path
*
* \note This function assumes path does not end with a '/'
*
* \param path The directory path to create the file in
* \param filename Function allocates memory and stores full filename (including path) here
*
* \retval -1 on failure
* \retval file descriptor on success
*/
static int create_temp_file(const char *path, char **filename)
{
const char *template_name = "certXXXXXX";
int fd;
if (ast_asprintf(filename, "%s/%s", path, template_name) < 0) {
ast_log(LOG_ERROR, "Failed to set up temporary file path for CURL\n");
return -1;
}
ast_mkdir(path, 0644);
if ((fd = mkstemp(*filename)) < 0) {
ast_log(LOG_NOTICE, "Failed to create temporary file for CURL\n");
ast_free(*filename);
return -1;
}
return fd;
}
char *curl_public_key(const char *public_cert_url, const char *path, struct curl_cb_data *data)
{
FILE *public_key_file;
RAII_VAR(char *, tmp_filename, NULL, ast_free);
char *filename;
char *serial;
int fd;
long http_code;
CURL *curl;
char curl_errbuf[CURL_ERROR_SIZE + 1];
char hash[41];
ast_sha1_hash(hash, public_key_url);
curl_errbuf[CURL_ERROR_SIZE] = '\0';
public_key_file = fopen(path, "wb");
/* For now, it's fine to pass in path as is - it shouldn't end with a '/'. However,
* if we decide to change how certificates are stored in the future (configurable paths),
* then we will need to check to see if path ends with '/', copy everything up to the '/',
* and use this new variable for create_temp_file as well as for ast_asprintf below.
*/
fd = create_temp_file(path, &tmp_filename);
if (fd == -1) {
ast_log(LOG_ERROR, "Failed to get temporary file descriptor for CURL\n");
return NULL;
}
public_key_file = fdopen(fd, "wb");
if (!public_key_file) {
ast_log(LOG_ERROR, "Failed to open file '%s' to write public key from '%s': %s (%d)\n",
path, public_key_url, strerror(errno), errno);
return -1;
tmp_filename, public_cert_url, strerror(errno), errno);
close(fd);
remove(tmp_filename);
return NULL;
}
curl = get_curl_instance(data);
if (!curl) {
ast_log(LOG_ERROR, "Failed to set up CURL isntance for '%s'\n", public_key_url);
ast_log(LOG_ERROR, "Failed to set up CURL isntance for '%s'\n", public_cert_url);
fclose(public_key_file);
return -1;
remove(tmp_filename);
return NULL;
}
curl_easy_setopt(curl, CURLOPT_URL, public_key_url);
curl_easy_setopt(curl, CURLOPT_URL, public_cert_url);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, public_key_file);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
@@ -182,7 +231,8 @@ int curl_public_key(const char *public_key_url, const char *path, struct curl_cb
ast_log(LOG_ERROR, "%s\n", curl_errbuf);
curl_easy_cleanup(curl);
fclose(public_key_file);
return -1;
remove(tmp_filename);
return NULL;
}
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
@@ -191,9 +241,34 @@ int curl_public_key(const char *public_key_url, const char *path, struct curl_cb
fclose(public_key_file);
if (http_code / 100 != 2) {
ast_log(LOG_ERROR, "Failed to retrieve URL '%s': code %ld\n", public_key_url, http_code);
return -1;
ast_log(LOG_ERROR, "Failed to retrieve URL '%s': code %ld\n", public_cert_url, http_code);
remove(tmp_filename);
return NULL;
}
return 0;
serial = stir_shaken_get_serial_number_x509(tmp_filename);
if (!serial) {
ast_log(LOG_ERROR, "Failed to get serial from cert %s\n", tmp_filename);
remove(tmp_filename);
return NULL;
}
if (ast_asprintf(&filename, "%s/%s.pem", path, serial) < 0) {
ast_log(LOG_ERROR, "Failed to allocate memory for new filename for temporary "
"file %s after CURL\n", tmp_filename);
ast_free(serial);
remove(tmp_filename);
return NULL;
}
ast_free(serial);
if (rename(tmp_filename, filename)) {
ast_log(LOG_ERROR, "Failed to rename temporary file %s to %s after CURL\n", tmp_filename, filename);
ast_free(filename);
remove(tmp_filename);
return NULL;
}
return filename;
}