Wed Jan 8 2020 09:50:12

Asterisk developer's documentation


func_curl.c File Reference

Curl - Load a URL. More...

#include "asterisk.h"
#include <curl/curl.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/cli.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
#include "asterisk/test.h"

Go to the source code of this file.

Data Structures

struct  curl_settings
 
struct  global_curl_info
 

Macros

#define CURLOPT_SPECIAL_HASHCOMPAT   -500
 
#define CURLVERSION_ATLEAST(a, b, c)   ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
 

Enumerations

enum  optiontype {
  OT_BOOLEAN, OT_INTEGER, OT_INTEGER_MS, OT_STRING,
  OT_ENUM
}
 

Functions

static void __init_curl_instance (void)
 
static void __init_thread_escapebuf (void)
 
static void __reg_module (void)
 
static void __unreg_module (void)
 
static int acf_curl2_exec (struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
 
static int acf_curl_exec (struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
 
static int acf_curl_helper (struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
 
static int acf_curlopt_helper (struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
 
static int acf_curlopt_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 
static int acf_curlopt_read2 (struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
 
static int acf_curlopt_write (struct ast_channel *chan, const char *cmd, char *name, const char *value)
 
 AST_TEST_DEFINE (vulnerable_url)
 
static void curl_instance_cleanup (void *data)
 
static int curl_instance_init (void *data)
 
static void curlds_free (void *data)
 
static int load_module (void)
 
static int parse_curlopt_key (const char *name, CURLoption *key, enum optiontype *ot)
 
static int unload_module (void)
 
static int url_is_vulnerable (const char *url)
 Check for potential HTTP injection risk. More...
 
static size_t WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Load external URL" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_REALTIME_DEPEND2, }
 
static struct ast_custom_function acf_curl
 
static struct ast_custom_function acf_curlopt
 
static struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_datastore_info curl_info
 
static struct ast_threadstorage curl_instance = { .once = PTHREAD_ONCE_INIT , .key_init = __init_curl_instance , .custom_init = curl_instance_init , }
 
struct global_curl_info global_curl_info = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } , }
 
static const char *const global_useragent = "asterisk-libcurl-agent/1.0"
 
static struct ast_threadstorage thread_escapebuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_thread_escapebuf , .custom_init = NULL , }
 

Detailed Description

Curl - Load a URL.

Author
Tilghman Lesher curl-.nosp@m.2005.nosp@m.0919@.nosp@m.the-.nosp@m.tilgh.nosp@m.man..nosp@m.com
Note
Brian Wilkins bwilk.nosp@m.ins@.nosp@m.cfl.r.nosp@m.r.co.nosp@m.m (Added POST option)
ExtRef:
Depends on the CURL library - http://curl.haxx.se/

Definition in file func_curl.c.

Macro Definition Documentation

#define CURLOPT_SPECIAL_HASHCOMPAT   -500

Definition at line 165 of file func_curl.c.

Referenced by acf_curl_helper(), and parse_curlopt_key().

#define CURLVERSION_ATLEAST (   a,
  b,
 
)    ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))

Definition at line 162 of file func_curl.c.

Enumeration Type Documentation

enum optiontype
Enumerator
OT_BOOLEAN 
OT_INTEGER 
OT_INTEGER_MS 
OT_STRING 
OT_ENUM 

Definition at line 195 of file func_curl.c.

195  {
196  OT_BOOLEAN,
197  OT_INTEGER,
199  OT_STRING,
200  OT_ENUM,
201 };

Function Documentation

static void __init_curl_instance ( void  )
static

Definition at line 555 of file func_curl.c.

575 {
static void __init_thread_escapebuf ( void  )
static

Definition at line 556 of file func_curl.c.

575 {
static void __reg_module ( void  )
static

Definition at line 864 of file func_curl.c.

static void __unreg_module ( void  )
static

Definition at line 864 of file func_curl.c.

static int acf_curl2_exec ( struct ast_channel chan,
const char *  cmd,
char *  info,
struct ast_str **  buf,
ssize_t  len 
)
static

Definition at line 737 of file func_curl.c.

References acf_curl_helper().

738 {
739  return acf_curl_helper(chan, cmd, info, NULL, buf, len);
740 }
static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
Definition: func_curl.c:583
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int acf_curl_exec ( struct ast_channel chan,
const char *  cmd,
char *  info,
char *  buf,
size_t  len 
)
static

Definition at line 732 of file func_curl.c.

References acf_curl_helper().

733 {
734  return acf_curl_helper(chan, cmd, info, buf, NULL, len);
735 }
static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
Definition: func_curl.c:583
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int acf_curl_helper ( struct ast_channel chan,
const char *  cmd,
char *  info,
char *  buf,
struct ast_str **  input_str,
ssize_t  len 
)
static

Definition at line 583 of file func_curl.c.

References args, AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_set(), ast_str_set_escapecommas(), ast_str_strlen(), ast_str_thread_get(), ast_str_trim_blanks(), ast_strlen_zero(), ast_threadstorage_get(), ast_uri_decode(), curl_instance, CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, curl_settings::key, curl_settings::list, LOG_ERROR, LOG_WARNING, name, pbx_builtin_setvar_helper(), S_OR, str, strsep(), thread_escapebuf, url, url_is_vulnerable(), and curl_settings::value.

Referenced by acf_curl2_exec(), and acf_curl_exec().

584 {
585  struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
586  struct ast_str *str = ast_str_create(16);
587  int ret = -1;
589  AST_APP_ARG(url);
590  AST_APP_ARG(postdata);
591  );
592  CURL **curl;
593  struct curl_settings *cur;
594  struct ast_datastore *store = NULL;
595  int hashcompat = 0;
597  char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
598 
599  if (buf) {
600  *buf = '\0';
601  }
602 
603  if (!str) {
604  return -1;
605  }
606 
607  if (!escapebuf) {
608  ast_free(str);
609  return -1;
610  }
611 
612  if (ast_strlen_zero(info)) {
613  ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
614  ast_free(str);
615  return -1;
616  }
617 
619 
620  if (url_is_vulnerable(args.url)) {
621  ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args.url);
622  return -1;
623  }
624 
625  if (chan) {
626  ast_autoservice_start(chan);
627  }
628 
629  if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
630  ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
631  ast_free(str);
632  return -1;
633  }
634 
636  AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
637  if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
638  hashcompat = (cur->value != NULL) ? 1 : 0;
639  } else {
640  curl_easy_setopt(*curl, cur->key, cur->value);
641  }
642  }
643 
644  if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
645  list = store->data;
646  AST_LIST_LOCK(list);
647  AST_LIST_TRAVERSE(list, cur, list) {
648  if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
649  hashcompat = (cur->value != NULL) ? 1 : 0;
650  } else {
651  curl_easy_setopt(*curl, cur->key, cur->value);
652  }
653  }
654  }
655 
656  curl_easy_setopt(*curl, CURLOPT_URL, args.url);
657  curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
658 
659  if (args.postdata) {
660  curl_easy_setopt(*curl, CURLOPT_POST, 1);
661  curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
662  }
663 
664  /* Temporarily assign a buffer for curl to write errors to. */
665  curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
666  curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
667 
668  if (curl_easy_perform(*curl) != 0) {
669  ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args.url);
670  }
671 
672  /* Reset buffer to NULL so curl doesn't try to write to it when the
673  * buffer is deallocated. Documentation is vague about allowing NULL
674  * here, but the source allows it. See: "typecheck: allow NULL to unset
675  * CURLOPT_ERRORBUFFER" (62bcf005f4678a93158358265ba905bace33b834). */
676  curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
677 
678  if (store) {
679  AST_LIST_UNLOCK(list);
680  }
682 
683  if (args.postdata) {
684  curl_easy_setopt(*curl, CURLOPT_POST, 0);
685  }
686 
687  if (ast_str_strlen(str)) {
688  ast_str_trim_blanks(str);
689 
690  ast_debug(3, "str='%s'\n", ast_str_buffer(str));
691  if (hashcompat) {
692  char *remainder = ast_str_buffer(str);
693  char *piece;
694  struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
695  struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
696  int rowcount = 0;
697  while (fields && values && (piece = strsep(&remainder, "&"))) {
698  char *name = strsep(&piece, "=");
699  if (piece) {
700  ast_uri_decode(piece);
701  }
702  ast_uri_decode(name);
703  ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
704  ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
705  rowcount++;
706  }
707  pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
708  if (buf) {
709  ast_copy_string(buf, ast_str_buffer(values), len);
710  } else {
711  ast_str_set(input_str, len, "%s", ast_str_buffer(values));
712  }
713  ast_free(fields);
714  ast_free(values);
715  } else {
716  if (buf) {
717  ast_copy_string(buf, ast_str_buffer(str), len);
718  } else {
719  ast_str_set(input_str, len, "%s", ast_str_buffer(str));
720  }
721  }
722  ret = 0;
723  }
724  ast_free(str);
725 
726  if (chan)
727  ast_autoservice_stop(chan);
728 
729  return ret;
730 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
char * strsep(char **str, const char *delims)
void ast_uri_decode(char *s)
Decode URI, URN, URL (overwrite string)
Definition: utils.c:484
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
static struct ast_threadstorage curl_instance
Definition: func_curl.c:555
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
struct ast_datastore_info * info
Definition: datastore.h:57
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:900
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
Structure for a data store object.
Definition: datastore.h:54
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2604
const char * str
Definition: app_jack.c:144
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
Definition: func_curl.c:574
CURLoption key
Definition: func_curl.c:176
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
void * value
Definition: func_curl.c:177
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
#define LOG_ERROR
Definition: logger.h:155
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static struct @350 args
static struct ast_threadstorage thread_escapebuf
Definition: func_curl.c:556
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
static const char name[]
#define ast_free(a)
Definition: astmm.h:97
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
char * ast_str_set_escapecommas(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring, with escaping of commas. ...
Definition: strings.h:830
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
void * data
Definition: datastore.h:56
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:669
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
Definition: strings.h:461
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
static struct ast_datastore_info curl_info
Definition: func_curl.c:169
static char url[512]
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:165
static int acf_curlopt_helper ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
struct ast_str **  bufstr,
ssize_t  len 
)
static

Definition at line 392 of file func_curl.c.

References ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_str_set(), ast_datastore::data, global_curl_info, curl_settings::key, curl_settings::list, LOG_ERROR, OT_BOOLEAN, OT_INTEGER, OT_INTEGER_MS, OT_STRING, parse_curlopt_key(), and curl_settings::value.

Referenced by acf_curlopt_read(), and acf_curlopt_read2().

393 {
394  struct ast_datastore *store;
395  struct global_curl_info *list[2] = { &global_curl_info, NULL };
396  struct curl_settings *cur = NULL;
397  CURLoption key;
398  enum optiontype ot;
399  int i;
400 
401  if (parse_curlopt_key(data, &key, &ot)) {
402  ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
403  return -1;
404  }
405 
406  if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
407  list[0] = store->data;
408  list[1] = &global_curl_info;
409  }
410 
411  for (i = 0; i < 2; i++) {
412  if (!list[i]) {
413  break;
414  }
415  AST_LIST_LOCK(list[i]);
416  AST_LIST_TRAVERSE(list[i], cur, list) {
417  if (cur->key == key) {
418  if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
419  if (buf) {
420  snprintf(buf, len, "%ld", (long) cur->value);
421  } else {
422  ast_str_set(bufstr, len, "%ld", (long) cur->value);
423  }
424  } else if (ot == OT_INTEGER_MS) {
425  if ((long) cur->value % 1000 == 0) {
426  if (buf) {
427  snprintf(buf, len, "%ld", (long)cur->value / 1000);
428  } else {
429  ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
430  }
431  } else {
432  if (buf) {
433  snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
434  } else {
435  ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
436  }
437  }
438  } else if (ot == OT_STRING) {
439  ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
440  if (buf) {
441  ast_copy_string(buf, cur->value, len);
442  } else {
443  ast_str_set(bufstr, 0, "%s", (char *) cur->value);
444  }
445  } else if (key == CURLOPT_PROXYTYPE) {
446  if (0) {
447 #if CURLVERSION_ATLEAST(7,15,2)
448  } else if ((long)cur->value == CURLPROXY_SOCKS4) {
449  if (buf) {
450  ast_copy_string(buf, "socks4", len);
451  } else {
452  ast_str_set(bufstr, 0, "socks4");
453  }
454 #endif
455 #if CURLVERSION_ATLEAST(7,18,0)
456  } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
457  if (buf) {
458  ast_copy_string(buf, "socks4a", len);
459  } else {
460  ast_str_set(bufstr, 0, "socks4a");
461  }
462 #endif
463  } else if ((long)cur->value == CURLPROXY_SOCKS5) {
464  if (buf) {
465  ast_copy_string(buf, "socks5", len);
466  } else {
467  ast_str_set(bufstr, 0, "socks5");
468  }
469 #if CURLVERSION_ATLEAST(7,18,0)
470  } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
471  if (buf) {
472  ast_copy_string(buf, "socks5hostname", len);
473  } else {
474  ast_str_set(bufstr, 0, "socks5hostname");
475  }
476 #endif
477 #if CURLVERSION_ATLEAST(7,10,0)
478  } else if ((long)cur->value == CURLPROXY_HTTP) {
479  if (buf) {
480  ast_copy_string(buf, "http", len);
481  } else {
482  ast_str_set(bufstr, 0, "http");
483  }
484 #endif
485  } else {
486  if (buf) {
487  ast_copy_string(buf, "unknown", len);
488  } else {
489  ast_str_set(bufstr, 0, "unknown");
490  }
491  }
492  }
493  break;
494  }
495  }
496  AST_LIST_UNLOCK(list[i]);
497  if (cur) {
498  break;
499  }
500  }
501 
502  return cur ? 0 : -1;
503 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
Definition: func_curl.c:203
Structure for a data store object.
Definition: datastore.h:54
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2604
struct global_curl_info global_curl_info
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
CURLoption key
Definition: func_curl.c:176
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
void * value
Definition: func_curl.c:177
#define LOG_ERROR
Definition: logger.h:155
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
void * data
Definition: datastore.h:56
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
static struct ast_datastore_info curl_info
Definition: func_curl.c:169
optiontype
Definition: func_curl.c:195
static int acf_curlopt_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
)
static

Definition at line 505 of file func_curl.c.

References acf_curlopt_helper().

506 {
507  return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
508 }
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
Definition: func_curl.c:392
static int acf_curlopt_read2 ( struct ast_channel chan,
const char *  cmd,
char *  data,
struct ast_str **  buf,
ssize_t  len 
)
static

Definition at line 510 of file func_curl.c.

References acf_curlopt_helper().

511 {
512  return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
513 }
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
Definition: func_curl.c:392
static int acf_curlopt_write ( struct ast_channel chan,
const char *  cmd,
char *  name,
const char *  value 
)
static

Definition at line 272 of file func_curl.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_datastore_alloc(), ast_datastore_free(), ast_debug, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_true(), ast_datastore::data, free, global_curl_info, curl_settings::key, curl_settings::list, LOG_ERROR, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, OT_STRING, and parse_curlopt_key().

273 {
274  struct ast_datastore *store;
275  struct global_curl_info *list;
276  struct curl_settings *cur, *new = NULL;
277  CURLoption key;
278  enum optiontype ot;
279 
280  if (chan) {
281  if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
282  /* Create a new datastore */
283  if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
284  ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
285  return -1;
286  }
287 
288  if (!(list = ast_calloc(1, sizeof(*list)))) {
289  ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
290  ast_datastore_free(store);
291  return -1;
292  }
293 
294  store->data = list;
295  AST_LIST_HEAD_INIT(list);
296  ast_channel_datastore_add(chan, store);
297  } else {
298  list = store->data;
299  }
300  } else {
301  /* Populate the global structure */
302  list = &global_curl_info;
303  }
304 
305  if (!parse_curlopt_key(name, &key, &ot)) {
306  if (ot == OT_BOOLEAN) {
307  if ((new = ast_calloc(1, sizeof(*new)))) {
308  new->value = (void *)((long) ast_true(value));
309  }
310  } else if (ot == OT_INTEGER) {
311  long tmp = atol(value);
312  if ((new = ast_calloc(1, sizeof(*new)))) {
313  new->value = (void *)tmp;
314  }
315  } else if (ot == OT_INTEGER_MS) {
316  long tmp = atof(value) * 1000.0;
317  if ((new = ast_calloc(1, sizeof(*new)))) {
318  new->value = (void *)tmp;
319  }
320  } else if (ot == OT_STRING) {
321  if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
322  new->value = (char *)new + sizeof(*new);
323  strcpy(new->value, value);
324  }
325  } else if (ot == OT_ENUM) {
326  if (key == CURLOPT_PROXYTYPE) {
327  long ptype =
328 #if CURLVERSION_ATLEAST(7,10,0)
329  CURLPROXY_HTTP;
330 #else
331  CURLPROXY_SOCKS5;
332 #endif
333  if (0) {
334 #if CURLVERSION_ATLEAST(7,15,2)
335  } else if (!strcasecmp(value, "socks4")) {
336  ptype = CURLPROXY_SOCKS4;
337 #endif
338 #if CURLVERSION_ATLEAST(7,18,0)
339  } else if (!strcasecmp(value, "socks4a")) {
340  ptype = CURLPROXY_SOCKS4A;
341 #endif
342 #if CURLVERSION_ATLEAST(7,18,0)
343  } else if (!strcasecmp(value, "socks5")) {
344  ptype = CURLPROXY_SOCKS5;
345 #endif
346 #if CURLVERSION_ATLEAST(7,18,0)
347  } else if (!strncasecmp(value, "socks5", 6)) {
348  ptype = CURLPROXY_SOCKS5_HOSTNAME;
349 #endif
350  }
351 
352  if ((new = ast_calloc(1, sizeof(*new)))) {
353  new->value = (void *)ptype;
354  }
355  } else {
356  /* Highly unlikely */
357  goto yuck;
358  }
359  }
360 
361  /* Memory allocation error */
362  if (!new) {
363  return -1;
364  }
365 
366  new->key = key;
367  } else {
368 yuck:
369  ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
370  return -1;
371  }
372 
373  /* Remove any existing entry */
374  AST_LIST_LOCK(list);
375  AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
376  if (cur->key == new->key) {
378  free(cur);
379  break;
380  }
381  }
383 
384  /* Insert new entry */
385  ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
386  AST_LIST_INSERT_TAIL(list, new, list);
387  AST_LIST_UNLOCK(list);
388 
389  return 0;
390 }
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
Definition: func_curl.c:203
Structure for a data store object.
Definition: datastore.h:54
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2604
int value
Definition: syslog.c:39
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
struct global_curl_info global_curl_info
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:65
struct curl_settings::@131 list
CURLoption key
Definition: func_curl.c:176
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
#define free(a)
Definition: astmm.h:94
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
struct ast_datastore * ast_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
Definition: datastore.c:98
static const char name[]
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:611
void * data
Definition: datastore.h:56
#define ast_calloc(a, b)
Definition: astmm.h:82
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
static struct ast_datastore_info curl_info
Definition: func_curl.c:169
optiontype
Definition: func_curl.c:195
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2590
AST_TEST_DEFINE ( vulnerable_url  )

Definition at line 781 of file func_curl.c.

References ARRAY_LEN, AST_TEST_FAIL, AST_TEST_PASS, ast_test_status_update, TEST_EXECUTE, TEST_INIT, and url_is_vulnerable().

782 {
783  const char *bad_urls [] = {
784  "http://example.com\r\nDELETE http://example.com/everything",
785  "http://example.com\rDELETE http://example.com/everything",
786  "http://example.com\nDELETE http://example.com/everything",
787  "\r\nhttp://example.com",
788  "\rhttp://example.com",
789  "\nhttp://example.com",
790  "http://example.com\r\n",
791  "http://example.com\r",
792  "http://example.com\n",
793  };
794  const char *good_urls [] = {
795  "http://example.com",
796  "http://example.com/%5Cr%5Cn",
797  };
798  int i;
800 
801  switch (cmd) {
802  case TEST_INIT:
803  info->name = "vulnerable_url";
804  info->category = "/funcs/func_curl/";
805  info->summary = "cURL vulnerable URL test";
806  info->description =
807  "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
808  case TEST_EXECUTE:
809  break;
810  }
811 
812  for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
813  if (!url_is_vulnerable(bad_urls[i])) {
814  ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
815  res = AST_TEST_FAIL;
816  }
817  }
818 
819  for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
820  if (url_is_vulnerable(good_urls[i])) {
821  ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
822  res = AST_TEST_FAIL;
823  }
824  }
825 
826  return res;
827 }
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
Definition: func_curl.c:574
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
ast_test_result_state
Definition: test.h:189
static void curl_instance_cleanup ( void *  data)
static

Definition at line 546 of file func_curl.c.

References ast_free.

547 {
548  CURL **curl = data;
549 
550  curl_easy_cleanup(*curl);
551 
552  ast_free(data);
553 }
#define ast_free(a)
Definition: astmm.h:97
static int curl_instance_init ( void *  data)
static

Definition at line 531 of file func_curl.c.

References global_useragent, and WriteMemoryCallback().

532 {
533  CURL **curl = data;
534 
535  if (!(*curl = curl_easy_init()))
536  return -1;
537 
538  curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
539  curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
540  curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
541  curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
542 
543  return 0;
544 }
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
Definition: func_curl.c:515
static const char *const global_useragent
Definition: func_curl.c:529
static void curlds_free ( void *  data)
static

Definition at line 182 of file func_curl.c.

References AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_REMOVE_HEAD, and free.

183 {
185  struct curl_settings *setting;
186  if (!list) {
187  return;
188  }
189  while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
190  free(setting);
191  }
193 }
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:172
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:638
struct curl_settings::@131 list
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
#define free(a)
Definition: astmm.h:94
static int load_module ( void  )
static

Definition at line 841 of file func_curl.c.

References acf_curl, acf_curlopt, ast_custom_function_register, ast_load_resource(), ast_log(), ast_module_check(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, AST_TEST_REGISTER, and LOG_ERROR.

842 {
843  int res;
844 
845  if (!ast_module_check("res_curl.so")) {
846  if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
847  ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
849  }
850  }
851 
854 
855  AST_TEST_REGISTER(vulnerable_url);
856 
857  return res;
858 }
enum ast_module_load_result ast_load_resource(const char *resource_name)
Load a module.
Definition: loader.c:947
static struct ast_custom_function acf_curlopt
Definition: func_curl.c:753
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
int ast_module_check(const char *name)
Check if module with the name given is loaded.
Definition: loader.c:1255
#define LOG_ERROR
Definition: logger.h:155
static struct ast_custom_function acf_curl
Definition: func_curl.c:742
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
static int parse_curlopt_key ( const char *  name,
CURLoption *  key,
enum optiontype ot 
)
static

Definition at line 203 of file func_curl.c.

References CURLOPT_SPECIAL_HASHCOMPAT, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, and OT_STRING.

Referenced by acf_curlopt_helper(), and acf_curlopt_write().

204 {
205  if (!strcasecmp(name, "header")) {
206  *key = CURLOPT_HEADER;
207  *ot = OT_BOOLEAN;
208  } else if (!strcasecmp(name, "proxy")) {
209  *key = CURLOPT_PROXY;
210  *ot = OT_STRING;
211  } else if (!strcasecmp(name, "proxyport")) {
212  *key = CURLOPT_PROXYPORT;
213  *ot = OT_INTEGER;
214  } else if (!strcasecmp(name, "proxytype")) {
215  *key = CURLOPT_PROXYTYPE;
216  *ot = OT_ENUM;
217  } else if (!strcasecmp(name, "dnstimeout")) {
218  *key = CURLOPT_DNS_CACHE_TIMEOUT;
219  *ot = OT_INTEGER;
220  } else if (!strcasecmp(name, "userpwd")) {
221  *key = CURLOPT_USERPWD;
222  *ot = OT_STRING;
223  } else if (!strcasecmp(name, "proxyuserpwd")) {
224  *key = CURLOPT_PROXYUSERPWD;
225  *ot = OT_STRING;
226  } else if (!strcasecmp(name, "maxredirs")) {
227  *key = CURLOPT_MAXREDIRS;
228  *ot = OT_INTEGER;
229  } else if (!strcasecmp(name, "referer")) {
230  *key = CURLOPT_REFERER;
231  *ot = OT_STRING;
232  } else if (!strcasecmp(name, "useragent")) {
233  *key = CURLOPT_USERAGENT;
234  *ot = OT_STRING;
235  } else if (!strcasecmp(name, "cookie")) {
236  *key = CURLOPT_COOKIE;
237  *ot = OT_STRING;
238  } else if (!strcasecmp(name, "ftptimeout")) {
239  *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
240  *ot = OT_INTEGER;
241  } else if (!strcasecmp(name, "httptimeout")) {
242 #if CURLVERSION_ATLEAST(7,16,2)
243  *key = CURLOPT_TIMEOUT_MS;
244  *ot = OT_INTEGER_MS;
245 #else
246  *key = CURLOPT_TIMEOUT;
247  *ot = OT_INTEGER;
248 #endif
249  } else if (!strcasecmp(name, "conntimeout")) {
250 #if CURLVERSION_ATLEAST(7,16,2)
251  *key = CURLOPT_CONNECTTIMEOUT_MS;
252  *ot = OT_INTEGER_MS;
253 #else
254  *key = CURLOPT_CONNECTTIMEOUT;
255  *ot = OT_INTEGER;
256 #endif
257  } else if (!strcasecmp(name, "ftptext")) {
258  *key = CURLOPT_TRANSFERTEXT;
259  *ot = OT_BOOLEAN;
260  } else if (!strcasecmp(name, "ssl_verifypeer")) {
261  *key = CURLOPT_SSL_VERIFYPEER;
262  *ot = OT_BOOLEAN;
263  } else if (!strcasecmp(name, "hashcompat")) {
265  *ot = OT_BOOLEAN;
266  } else {
267  return -1;
268  }
269  return 0;
270 }
CURLoption key
Definition: func_curl.c:176
static const char name[]
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:165
static int unload_module ( void  )
static

Definition at line 829 of file func_curl.c.

References acf_curl, acf_curlopt, ast_custom_function_unregister(), and AST_TEST_UNREGISTER.

830 {
831  int res;
832 
835 
836  AST_TEST_UNREGISTER(vulnerable_url);
837 
838  return res;
839 }
static struct ast_custom_function acf_curlopt
Definition: func_curl.c:753
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
static struct ast_custom_function acf_curl
Definition: func_curl.c:742
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
static int url_is_vulnerable ( const char *  url)
static

Check for potential HTTP injection risk.

CVE-2014-8150 brought up the fact that HTTP proxies are subject to injection attacks. An HTTP URL sent to a proxy contains a carriage-return linefeed combination, followed by a complete HTTP request. Proxies will handle this as two separate HTTP requests rather than as a malformed URL.

libcURL patched this vulnerability in version 7.40.0, but we have no guarantee that Asterisk systems will be using an up-to-date cURL library. Therefore, we implement the same fix as libcURL for determining if a URL is vulnerable to an injection attack.

Parameters
urlThe URL to check for vulnerability
Return values
0The URL is not vulnerable
1The URL is vulnerable.

Definition at line 574 of file func_curl.c.

Referenced by acf_curl_helper(), and AST_TEST_DEFINE().

575 {
576  if (strpbrk(url, "\r\n")) {
577  return 1;
578  }
579 
580  return 0;
581 }
static char url[512]
static size_t WriteMemoryCallback ( void *  ptr,
size_t  size,
size_t  nmemb,
void *  data 
)
static

Definition at line 515 of file func_curl.c.

References ast_debug, ast_str_append_substr(), ast_str_size(), and ast_str_strlen().

Referenced by curl_instance_init().

516 {
517  register int realsize = size * nmemb;
518  struct ast_str **pstr = (struct ast_str **)data;
519 
520  ast_debug(3, "Called with data=%p, str=%p, realsize=%d, len=%zu, used=%zu\n", data, *pstr, realsize, ast_str_size(*pstr), ast_str_strlen(*pstr));
521 
522  ast_str_append_substr(pstr, 0, ptr, realsize);
523 
524  ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
525 
526  return realsize;
527 }
size_t ast_str_size(const struct ast_str *buf)
Returns the current maximum length (without reallocation) of the current buffer.
Definition: strings.h:482
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition: strings.h:823
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Load external URL" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "ac1f6a56484a8820659555499174e588" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_REALTIME_DEPEND2, }
static

Definition at line 864 of file func_curl.c.

struct ast_custom_function acf_curl
static

Definition at line 742 of file func_curl.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function acf_curlopt
static

Definition at line 753 of file func_curl.c.

Referenced by load_module(), and unload_module().

Definition at line 864 of file func_curl.c.

struct ast_datastore_info curl_info
static
Initial value:
= {
.type = "CURL",
.destroy = curlds_free,
}
static void curlds_free(void *data)
Definition: func_curl.c:182

Definition at line 169 of file func_curl.c.

struct ast_threadstorage curl_instance = { .once = PTHREAD_ONCE_INIT , .key_init = __init_curl_instance , .custom_init = curl_instance_init , }
static

Definition at line 555 of file func_curl.c.

Referenced by acf_curl_helper().

struct global_curl_info global_curl_info = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, 1 } , }
const char* const global_useragent = "asterisk-libcurl-agent/1.0"
static

Definition at line 529 of file func_curl.c.

Referenced by curl_instance_init().

struct ast_threadstorage thread_escapebuf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_thread_escapebuf , .custom_init = NULL , }
static

Definition at line 556 of file func_curl.c.

Referenced by acf_curl_helper().