Asterisk - The Open Source Telephony Project  18.5.0
Data Structures | Macros | Functions
curl.c File Reference
#include "asterisk.h"
#include "asterisk/utils.h"
#include "asterisk/logger.h"
#include "curl.h"
#include "general.h"
#include "stir_shaken.h"
#include <curl/curl.h>
#include <sys/stat.h>
Include dependency graph for curl.c:

Go to the source code of this file.

Data Structures

struct  curl_cb_data
 

Macros

#define GLOBAL_USERAGENT   "asterisk-libcurl-agent/1.0"
 
#define MAX_HEADER_LENGTH   1023
 

Functions

static int create_temp_file (const char *path, char **filename)
 Create a temporary file located at path. More...
 
struct curl_cb_datacurl_cb_data_create (void)
 Allocate memory for a curl_cb_data struct. More...
 
void curl_cb_data_free (struct curl_cb_data *data)
 Free a curl_cb_data struct. More...
 
char * curl_cb_data_get_cache_control (const struct curl_cb_data *data)
 Get the cache_control field from a curl_cb_data struct. More...
 
char * curl_cb_data_get_expires (const struct curl_cb_data *data)
 Get the expires field from a curl_cb_data struct. More...
 
static size_t curl_header_callback (char *buffer, size_t size, size_t nitems, void *data)
 Called when a CURL request completes. More...
 
char * curl_public_key (const char *public_cert_url, const char *path, struct curl_cb_data *data)
 CURL the public key from the provided URL to the specified path. More...
 
static CURL * get_curl_instance (struct curl_cb_data *data)
 Prepare a CURL instance to use. More...
 

Macro Definition Documentation

◆ GLOBAL_USERAGENT

#define GLOBAL_USERAGENT   "asterisk-libcurl-agent/1.0"

Definition at line 34 of file curl.c.

Referenced by get_curl_instance().

◆ MAX_HEADER_LENGTH

#define MAX_HEADER_LENGTH   1023

Definition at line 31 of file curl.c.

Referenced by curl_header_callback().

Function Documentation

◆ create_temp_file()

static int create_temp_file ( const char *  path,
char **  filename 
)
static

Create a temporary file located at path.

Note
This function assumes path does not end with a '/'
Parameters
pathThe directory path to create the file in
filenameFunction allocates memory and stores full filename (including path) here
Return values
-1on failure
filedescriptor on success

Definition at line 164 of file curl.c.

References ast_asprintf, ast_free, ast_log, ast_mkdir(), LOG_ERROR, and LOG_NOTICE.

Referenced by curl_public_key().

165 {
166  const char *template_name = "certXXXXXX";
167  int fd;
168 
169  if (ast_asprintf(filename, "%s/%s", path, template_name) < 0) {
170  ast_log(LOG_ERROR, "Failed to set up temporary file path for CURL\n");
171  return -1;
172  }
173 
174  ast_mkdir(path, 0644);
175 
176  if ((fd = mkstemp(*filename)) < 0) {
177  ast_log(LOG_NOTICE, "Failed to create temporary file for CURL\n");
178  ast_free(*filename);
179  return -1;
180  }
181 
182  return fd;
183 }
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
#define ast_log
Definition: astobj2.c:42
#define LOG_ERROR
Definition: logger.h:285
#define LOG_NOTICE
Definition: logger.h:263
#define ast_free(a)
Definition: astmm.h:182
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: main/utils.c:2231

◆ curl_cb_data_create()

struct curl_cb_data* curl_cb_data_create ( void  )

Allocate memory for a curl_cb_data struct.

Note
This will need to be freed by the consumer using curl_cb_data_free
Return values
NULLon failure
curl_cb_structon success

Definition at line 42 of file curl.c.

References ast_calloc.

Referenced by run_curl().

43 {
44  struct curl_cb_data *data;
45 
46  data = ast_calloc(1, sizeof(*data));
47 
48  return data;
49 }
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204

◆ curl_cb_data_free()

void curl_cb_data_free ( struct curl_cb_data data)

Free a curl_cb_data struct.

Parameters
dataThe curl_cb_data struct to free

Definition at line 51 of file curl.c.

References ast_free, curl_cb_data::cache_control, and curl_cb_data::expires.

Referenced by run_curl().

52 {
53  if (!data) {
54  return;
55  }
56 
57  ast_free(data->cache_control);
58  ast_free(data->expires);
59 
60  ast_free(data);
61 }
#define ast_free(a)
Definition: astmm.h:182
char * expires
Definition: curl.c:39
char * cache_control
Definition: curl.c:38

◆ curl_cb_data_get_cache_control()

char* curl_cb_data_get_cache_control ( const struct curl_cb_data data)

Get the cache_control field from a curl_cb_data struct.

Parameters
dataThe curl_cb_data
Return values
cache_controlon success
NULLotherwise

Definition at line 63 of file curl.c.

References curl_cb_data::cache_control, and NULL.

Referenced by set_public_key_expiration().

64 {
65  if (!data) {
66  return NULL;
67  }
68 
69  return data->cache_control;
70 }
#define NULL
Definition: resample.c:96
char * cache_control
Definition: curl.c:38

◆ curl_cb_data_get_expires()

char* curl_cb_data_get_expires ( const struct curl_cb_data data)

Get the expires field from a curl_cb_data struct.

Parameters
dataThe curl_cb_data
Return values
expireson success
NULLotherwise

Definition at line 72 of file curl.c.

References curl_cb_data::expires, and NULL.

Referenced by set_public_key_expiration().

73 {
74  if (!data) {
75  return NULL;
76  }
77 
78  return data->expires;
79 }
#define NULL
Definition: resample.c:96
char * expires
Definition: curl.c:39

◆ curl_header_callback()

static size_t curl_header_callback ( char *  buffer,
size_t  size,
size_t  nitems,
void *  data 
)
static

Called when a CURL request completes.

Parameters
dataThe curl_cb_data structure to store expiration info

Definition at line 86 of file curl.c.

References ast_alloca, ast_log, ast_skip_blanks(), ast_strdup, ast_trim_blanks(), curl_cb_data::cache_control, curl_cb_data::expires, LOG_WARNING, MAX_HEADER_LENGTH, and value.

Referenced by get_curl_instance().

87 {
88  struct curl_cb_data *cb_data = data;
89  size_t realsize;
90  char *header;
91  char *value;
92 
93  realsize = size * nitems;
94 
95  if (realsize > MAX_HEADER_LENGTH) {
96  ast_log(LOG_WARNING, "CURL header length is too large (size: '%zu' | max: '%d')\n",
97  realsize, MAX_HEADER_LENGTH);
98  return 0;
99  }
100 
101  header = ast_alloca(realsize + 1);
102  memcpy(header, buffer, realsize);
103  header[realsize] = '\0';
104  value = strchr(header, ':');
105  if (!value) {
106  return realsize;
107  }
108  *value++ = '\0';
109  value = ast_trim_blanks(ast_skip_blanks(value));
110 
111  if (!strcasecmp(header, "Cache-Control")) {
112  cb_data->cache_control = ast_strdup(value);
113  } else if (!strcasecmp(header, "Expires")) {
114  cb_data->expires = ast_strdup(value);
115  }
116 
117  return realsize;
118 }
#define MAX_HEADER_LENGTH
Definition: curl.c:31
#define LOG_WARNING
Definition: logger.h:274
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
int value
Definition: syslog.c:37
#define ast_log
Definition: astobj2.c:42
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
char * ast_skip_blanks(const char *str)
Gets a pointer to the first non-whitespace character in a string.
Definition: strings.h:157
char * ast_trim_blanks(char *str)
Trims trailing whitespace characters from a string.
Definition: strings.h:182
char * expires
Definition: curl.c:39
char * cache_control
Definition: curl.c:38

◆ curl_public_key()

char* curl_public_key ( const char *  public_cert_url,
const char *  path,
struct curl_cb_data data 
)

CURL the public key from the provided URL to the specified path.

Note
The returned string will need to be freed by the caller
Parameters
public_cert_urlThe public cert URL
pathThe path to download the file to
dataThe curl_cb_data
Return values
NULLon failure
fullpath filename on success

Definition at line 185 of file curl.c.

References ast_asprintf, ast_free, ast_log, create_temp_file(), errno, get_curl_instance(), LOG_ERROR, NULL, RAII_VAR, and stir_shaken_get_serial_number_x509().

Referenced by run_curl().

186 {
187  FILE *public_key_file;
188  RAII_VAR(char *, tmp_filename, NULL, ast_free);
189  char *filename;
190  char *serial;
191  int fd;
192  long http_code;
193  CURL *curl;
194  char curl_errbuf[CURL_ERROR_SIZE + 1];
195 
196  curl_errbuf[CURL_ERROR_SIZE] = '\0';
197 
198  /* For now, it's fine to pass in path as is - it shouldn't end with a '/'. However,
199  * if we decide to change how certificates are stored in the future (configurable paths),
200  * then we will need to check to see if path ends with '/', copy everything up to the '/',
201  * and use this new variable for create_temp_file as well as for ast_asprintf below.
202  */
203  fd = create_temp_file(path, &tmp_filename);
204  if (fd == -1) {
205  ast_log(LOG_ERROR, "Failed to get temporary file descriptor for CURL\n");
206  return NULL;
207  }
208 
209  public_key_file = fdopen(fd, "wb");
210  if (!public_key_file) {
211  ast_log(LOG_ERROR, "Failed to open file '%s' to write public key from '%s': %s (%d)\n",
212  tmp_filename, public_cert_url, strerror(errno), errno);
213  close(fd);
214  remove(tmp_filename);
215  return NULL;
216  }
217 
218  curl = get_curl_instance(data);
219  if (!curl) {
220  ast_log(LOG_ERROR, "Failed to set up CURL isntance for '%s'\n", public_cert_url);
221  fclose(public_key_file);
222  remove(tmp_filename);
223  return NULL;
224  }
225 
226  curl_easy_setopt(curl, CURLOPT_URL, public_cert_url);
227  curl_easy_setopt(curl, CURLOPT_WRITEDATA, public_key_file);
228  curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
229 
230  if (curl_easy_perform(curl)) {
231  ast_log(LOG_ERROR, "%s\n", curl_errbuf);
232  curl_easy_cleanup(curl);
233  fclose(public_key_file);
234  remove(tmp_filename);
235  return NULL;
236  }
237 
238  curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
239 
240  curl_easy_cleanup(curl);
241  fclose(public_key_file);
242 
243  if (http_code / 100 != 2) {
244  ast_log(LOG_ERROR, "Failed to retrieve URL '%s': code %ld\n", public_cert_url, http_code);
245  remove(tmp_filename);
246  return NULL;
247  }
248 
249  serial = stir_shaken_get_serial_number_x509(tmp_filename);
250  if (!serial) {
251  ast_log(LOG_ERROR, "Failed to get serial from cert %s\n", tmp_filename);
252  remove(tmp_filename);
253  return NULL;
254  }
255 
256  if (ast_asprintf(&filename, "%s/%s.pem", path, serial) < 0) {
257  ast_log(LOG_ERROR, "Failed to allocate memory for new filename for temporary "
258  "file %s after CURL\n", tmp_filename);
259  ast_free(serial);
260  remove(tmp_filename);
261  return NULL;
262  }
263 
264  ast_free(serial);
265 
266  if (rename(tmp_filename, filename)) {
267  ast_log(LOG_ERROR, "Failed to rename temporary file %s to %s after CURL\n", tmp_filename, filename);
268  ast_free(filename);
269  remove(tmp_filename);
270  return NULL;
271  }
272 
273  return filename;
274 }
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
#define NULL
Definition: resample.c:96
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
#define ast_log
Definition: astobj2.c:42
#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:911
static CURL * get_curl_instance(struct curl_cb_data *data)
Prepare a CURL instance to use.
Definition: curl.c:128
#define LOG_ERROR
Definition: logger.h:285
int errno
#define ast_free(a)
Definition: astmm.h:182
static int create_temp_file(const char *path, char **filename)
Create a temporary file located at path.
Definition: curl.c:164

◆ get_curl_instance()

static CURL* get_curl_instance ( struct curl_cb_data data)
static

Prepare a CURL instance to use.

Parameters
dataThe CURL callback data
Return values
NULLon failure
CURLinstance on success

Definition at line 128 of file curl.c.

References ao2_cleanup, ast_stir_shaken_curl_timeout(), curl_header_callback(), stir_shaken_general::curl_timeout, GLOBAL_USERAGENT, NULL, and stir_shaken_general_get().

Referenced by curl_public_key().

129 {
130  CURL *curl;
131  struct stir_shaken_general *cfg;
132  unsigned int curl_timeout;
133 
134  cfg = stir_shaken_general_get();
135  curl_timeout = ast_stir_shaken_curl_timeout(cfg);
136  ao2_cleanup(cfg);
137 
138  curl = curl_easy_init();
139  if (!curl) {
140  return NULL;
141  }
142 
143  curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
144  curl_easy_setopt(curl, CURLOPT_TIMEOUT, curl_timeout);
145  curl_easy_setopt(curl, CURLOPT_USERAGENT, GLOBAL_USERAGENT);
146  curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
147  curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
148  curl_easy_setopt(curl, CURLOPT_HEADERDATA, data);
149 
150  return curl;
151 }
struct stir_shaken_general * stir_shaken_general_get()
Retrieve the stir/shaken &#39;general&#39; configuration object.
Definition: general.c:54
static size_t curl_header_callback(char *buffer, size_t size, size_t nitems, void *data)
Called when a CURL request completes.
Definition: curl.c:86
#define NULL
Definition: resample.c:96
#define GLOBAL_USERAGENT
Definition: curl.c:34
unsigned int curl_timeout
Definition: general.c:47
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
unsigned int ast_stir_shaken_curl_timeout(const struct stir_shaken_general *cfg)
Retrieve the &#39;curl_timeout&#39; general configuration option value.
Definition: general.c:87