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"

Include dependency graph for func_curl.c:

Go to the source code of this file.

Data Structures

struct  curl_settings
struct  global_curl_info

Defines

#define CURLOPT_SPECIAL_HASHCOMPAT   ((CURLoption) -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  hashcompat { HASHCOMPAT_NO = 0, HASHCOMPAT_YES, HASHCOMPAT_LEGACY }
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.
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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .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 , }
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-20050919@the-tilghman.com>
Note:
Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
ExtRef:
Depends on the CURL library - http://curl.haxx.se/

Definition in file func_curl.c.


Define Documentation

#define CURLOPT_SPECIAL_HASHCOMPAT   ((CURLoption) -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))))

Definition at line 171 of file func_curl.c.


Enumeration Type Documentation

enum hashcompat

Enumerator:
HASHCOMPAT_NO 
HASHCOMPAT_YES 
HASHCOMPAT_LEGACY 

Definition at line 213 of file func_curl.c.

00213                 {
00214    HASHCOMPAT_NO = 0,
00215    HASHCOMPAT_YES,
00216    HASHCOMPAT_LEGACY,
00217 };

enum optiontype

Enumerator:
OT_BOOLEAN 
OT_INTEGER 
OT_INTEGER_MS 
OT_STRING 
OT_ENUM 

Definition at line 205 of file func_curl.c.

00205                 {
00206    OT_BOOLEAN,
00207    OT_INTEGER,
00208    OT_INTEGER_MS,
00209    OT_STRING,
00210    OT_ENUM,
00211 };


Function Documentation

static void __init_curl_instance ( void   )  [static]

Definition at line 569 of file func_curl.c.

00589 {

static void __init_thread_escapebuf ( void   )  [static]

Definition at line 570 of file func_curl.c.

00589 {

static void __reg_module ( void   )  [static]

Definition at line 881 of file func_curl.c.

static void __unreg_module ( void   )  [static]

Definition at line 881 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 752 of file func_curl.c.

References acf_curl_helper(), and NULL.

00753 {
00754    return acf_curl_helper(chan, cmd, info, NULL, buf, len);
00755 }

static int acf_curl_exec ( struct ast_channel chan,
const char *  cmd,
char *  info,
char *  buf,
size_t  len 
) [static]

Definition at line 747 of file func_curl.c.

References acf_curl_helper(), and NULL.

00748 {
00749    return acf_curl_helper(chan, cmd, info, buf, NULL, len);
00750 }

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 597 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(), ast_uri_http, ast_uri_http_legacy, curl_instance, CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, curl_settings::key, curl_settings::list, LOG_ERROR, LOG_WARNING, name, NULL, 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().

00598 {
00599    struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
00600    struct ast_str *str = ast_str_create(16);
00601    int ret = -1;
00602    AST_DECLARE_APP_ARGS(args,
00603       AST_APP_ARG(url);
00604       AST_APP_ARG(postdata);
00605    );
00606    CURL **curl;
00607    struct curl_settings *cur;
00608    struct ast_datastore *store = NULL;
00609    int hashcompat = 0;
00610    AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
00611    char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
00612 
00613    if (buf) {
00614       *buf = '\0';
00615    }
00616 
00617    if (!str) {
00618       return -1;
00619    }
00620 
00621    if (!escapebuf) {
00622       ast_free(str);
00623       return -1;
00624    }
00625 
00626    if (ast_strlen_zero(info)) {
00627       ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
00628       ast_free(str);
00629       return -1;
00630    }
00631 
00632    AST_STANDARD_APP_ARGS(args, info);
00633 
00634    if (url_is_vulnerable(args.url)) {
00635       ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args.url);
00636       return -1;
00637    }
00638 
00639    if (chan) {
00640       ast_autoservice_start(chan);
00641    }
00642 
00643    if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
00644       ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
00645       ast_free(str);
00646       return -1;
00647    }
00648 
00649    AST_LIST_LOCK(&global_curl_info);
00650    AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
00651       if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00652          hashcompat = (long) cur->value;
00653       } else {
00654          curl_easy_setopt(*curl, cur->key, cur->value);
00655       }
00656    }
00657    AST_LIST_UNLOCK(&global_curl_info);
00658 
00659    if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00660       list = store->data;
00661       AST_LIST_LOCK(list);
00662       AST_LIST_TRAVERSE(list, cur, list) {
00663          if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00664             hashcompat = (long) cur->value;
00665          } else {
00666             curl_easy_setopt(*curl, cur->key, cur->value);
00667          }
00668       }
00669    }
00670 
00671    curl_easy_setopt(*curl, CURLOPT_URL, args.url);
00672    curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
00673 
00674    if (args.postdata) {
00675       curl_easy_setopt(*curl, CURLOPT_POST, 1);
00676       curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
00677    }
00678 
00679    /* Temporarily assign a buffer for curl to write errors to. */
00680    curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
00681    curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
00682 
00683    if (curl_easy_perform(*curl) != 0) {
00684       ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args.url);
00685    }
00686 
00687    /* Reset buffer to NULL so curl doesn't try to write to it when the
00688     * buffer is deallocated. Documentation is vague about allowing NULL
00689     * here, but the source allows it. See: "typecheck: allow NULL to unset
00690     * CURLOPT_ERRORBUFFER" (62bcf005f4678a93158358265ba905bace33b834). */
00691    curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
00692 
00693    if (store) {
00694       AST_LIST_UNLOCK(list);
00695    }
00696 
00697    if (args.postdata) {
00698       curl_easy_setopt(*curl, CURLOPT_POST, 0);
00699    }
00700 
00701    if (ast_str_strlen(str)) {
00702       ast_str_trim_blanks(str);
00703 
00704       ast_debug(3, "str='%s'\n", ast_str_buffer(str));
00705       if (hashcompat) {
00706          char *remainder = ast_str_buffer(str);
00707          char *piece;
00708          struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
00709          struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
00710          int rowcount = 0;
00711          while (fields && values && (piece = strsep(&remainder, "&"))) {
00712             char *name = strsep(&piece, "=");
00713             struct ast_flags mode = (hashcompat == HASHCOMPAT_LEGACY ? ast_uri_http_legacy : ast_uri_http);
00714             if (piece) {
00715                ast_uri_decode(piece, mode);
00716             }
00717             ast_uri_decode(name, mode);
00718             ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
00719             ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
00720             rowcount++;
00721          }
00722          pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
00723          if (buf) {
00724             ast_copy_string(buf, ast_str_buffer(values), len);
00725          } else {
00726             ast_str_set(input_str, len, "%s", ast_str_buffer(values));
00727          }
00728          ast_free(fields);
00729          ast_free(values);
00730       } else {
00731          if (buf) {
00732             ast_copy_string(buf, ast_str_buffer(str), len);
00733          } else {
00734             ast_str_set(input_str, len, "%s", ast_str_buffer(str));
00735          }
00736       }
00737       ret = 0;
00738    }
00739    ast_free(str);
00740 
00741    if (chan)
00742       ast_autoservice_stop(chan);
00743 
00744    return ret;
00745 }

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 412 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(), CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, HASHCOMPAT_NO, HASHCOMPAT_YES, curl_settings::key, curl_settings::list, LOG_ERROR, NULL, 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().

00413 {
00414    struct ast_datastore *store;
00415    struct global_curl_info *list[2] = { &global_curl_info, NULL };
00416    struct curl_settings *cur = NULL;
00417    CURLoption key;
00418    enum optiontype ot;
00419    int i;
00420 
00421    if (parse_curlopt_key(data, &key, &ot)) {
00422       ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
00423       return -1;
00424    }
00425 
00426    if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00427       list[0] = store->data;
00428       list[1] = &global_curl_info;
00429    }
00430 
00431    for (i = 0; i < 2; i++) {
00432       if (!list[i]) {
00433          break;
00434       }
00435       AST_LIST_LOCK(list[i]);
00436       AST_LIST_TRAVERSE(list[i], cur, list) {
00437          if (cur->key == key) {
00438             if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
00439                if (buf) {
00440                   snprintf(buf, len, "%ld", (long) cur->value);
00441                } else {
00442                   ast_str_set(bufstr, len, "%ld", (long) cur->value);
00443                }
00444             } else if (ot == OT_INTEGER_MS) {
00445                if ((long) cur->value % 1000 == 0) {
00446                   if (buf) {
00447                      snprintf(buf, len, "%ld", (long)cur->value / 1000);
00448                   } else {
00449                      ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
00450                   }
00451                } else {
00452                   if (buf) {
00453                      snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00454                   } else {
00455                      ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00456                   }
00457                }
00458             } else if (ot == OT_STRING) {
00459                ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
00460                if (buf) {
00461                   ast_copy_string(buf, cur->value, len);
00462                } else {
00463                   ast_str_set(bufstr, 0, "%s", (char *) cur->value);
00464                }
00465             } else if (key == CURLOPT_PROXYTYPE) {
00466                const char *strval = "unknown";
00467                if (0) {
00468 #if CURLVERSION_ATLEAST(7,15,2)
00469                } else if ((long)cur->value == CURLPROXY_SOCKS4) {
00470                   strval = "socks4";
00471 #endif
00472 #if CURLVERSION_ATLEAST(7,18,0)
00473                } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
00474                   strval = "socks4a";
00475 #endif
00476                } else if ((long)cur->value == CURLPROXY_SOCKS5) {
00477                   strval = "socks5";
00478 #if CURLVERSION_ATLEAST(7,18,0)
00479                } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
00480                   strval = "socks5hostname";
00481 #endif
00482 #if CURLVERSION_ATLEAST(7,10,0)
00483                } else if ((long)cur->value == CURLPROXY_HTTP) {
00484                   strval = "http";
00485 #endif
00486                }
00487                if (buf) {
00488                   ast_copy_string(buf, strval, len);
00489                } else {
00490                   ast_str_set(bufstr, 0, "%s", strval);
00491                }
00492             } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
00493                const char *strval = "unknown";
00494                if ((long) cur->value == HASHCOMPAT_LEGACY) {
00495                   strval = "legacy";
00496                } else if ((long) cur->value == HASHCOMPAT_YES) {
00497                   strval = "yes";
00498                } else if ((long) cur->value == HASHCOMPAT_NO) {
00499                   strval = "no";
00500                }
00501                if (buf) {
00502                   ast_copy_string(buf, strval, len);
00503                } else {
00504                   ast_str_set(bufstr, 0, "%s", strval);
00505                }
00506             }
00507             break;
00508          }
00509       }
00510       AST_LIST_UNLOCK(list[i]);
00511       if (cur) {
00512          break;
00513       }
00514    }
00515 
00516    return cur ? 0 : -1;
00517 }

static int acf_curlopt_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 519 of file func_curl.c.

References acf_curlopt_helper(), and NULL.

00520 {
00521    return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
00522 }

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 524 of file func_curl.c.

References acf_curlopt_helper(), and NULL.

00525 {
00526    return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
00527 }

static int acf_curlopt_write ( struct ast_channel chan,
const char *  cmd,
char *  name,
const char *  value 
) [static]

Definition at line 288 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_free, 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(), CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, HASHCOMPAT_LEGACY, HASHCOMPAT_NO, HASHCOMPAT_YES, curl_settings::key, curl_settings::list, LOG_ERROR, NULL, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, OT_STRING, parse_curlopt_key(), and tmp().

00289 {
00290    struct ast_datastore *store;
00291    struct global_curl_info *list;
00292    struct curl_settings *cur, *new = NULL;
00293    CURLoption key;
00294    enum optiontype ot;
00295 
00296    if (chan) {
00297       if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00298          /* Create a new datastore */
00299          if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
00300             ast_log(LOG_ERROR, "Unable to allocate new datastore.  Cannot set any CURL options\n");
00301             return -1;
00302          }
00303 
00304          if (!(list = ast_calloc(1, sizeof(*list)))) {
00305             ast_log(LOG_ERROR, "Unable to allocate list head.  Cannot set any CURL options\n");
00306             ast_datastore_free(store);
00307             return -1;
00308          }
00309 
00310          store->data = list;
00311          AST_LIST_HEAD_INIT(list);
00312          ast_channel_datastore_add(chan, store);
00313       } else {
00314          list = store->data;
00315       }
00316    } else {
00317       /* Populate the global structure */
00318       list = &global_curl_info;
00319    }
00320 
00321    if (!parse_curlopt_key(name, &key, &ot)) {
00322       if (ot == OT_BOOLEAN) {
00323          if ((new = ast_calloc(1, sizeof(*new)))) {
00324             new->value = (void *)((long) ast_true(value));
00325          }
00326       } else if (ot == OT_INTEGER) {
00327          long tmp = atol(value);
00328          if ((new = ast_calloc(1, sizeof(*new)))) {
00329             new->value = (void *)tmp;
00330          }
00331       } else if (ot == OT_INTEGER_MS) {
00332          long tmp = atof(value) * 1000.0;
00333          if ((new = ast_calloc(1, sizeof(*new)))) {
00334             new->value = (void *)tmp;
00335          }
00336       } else if (ot == OT_STRING) {
00337          if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
00338             new->value = (char *)new + sizeof(*new);
00339             strcpy(new->value, value);
00340          }
00341       } else if (ot == OT_ENUM) {
00342          if (key == CURLOPT_PROXYTYPE) {
00343             long ptype =
00344 #if CURLVERSION_ATLEAST(7,10,0)
00345                CURLPROXY_HTTP;
00346 #else
00347                CURLPROXY_SOCKS5;
00348 #endif
00349             if (0) {
00350 #if CURLVERSION_ATLEAST(7,15,2)
00351             } else if (!strcasecmp(value, "socks4")) {
00352                ptype = CURLPROXY_SOCKS4;
00353 #endif
00354 #if CURLVERSION_ATLEAST(7,18,0)
00355             } else if (!strcasecmp(value, "socks4a")) {
00356                ptype = CURLPROXY_SOCKS4A;
00357 #endif
00358 #if CURLVERSION_ATLEAST(7,18,0)
00359             } else if (!strcasecmp(value, "socks5")) {
00360                ptype = CURLPROXY_SOCKS5;
00361 #endif
00362 #if CURLVERSION_ATLEAST(7,18,0)
00363             } else if (!strncasecmp(value, "socks5", 6)) {
00364                ptype = CURLPROXY_SOCKS5_HOSTNAME;
00365 #endif
00366             }
00367 
00368             if ((new = ast_calloc(1, sizeof(*new)))) {
00369                new->value = (void *)ptype;
00370             }
00371          } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
00372             if ((new = ast_calloc(1, sizeof(*new)))) {
00373                new->value = (void *) (long) (!strcasecmp(value, "legacy") ? HASHCOMPAT_LEGACY : ast_true(value) ? HASHCOMPAT_YES : HASHCOMPAT_NO);
00374             }
00375          } else {
00376             /* Highly unlikely */
00377             goto yuck;
00378          }
00379       }
00380 
00381       /* Memory allocation error */
00382       if (!new) {
00383          return -1;
00384       }
00385 
00386       new->key = key;
00387    } else {
00388 yuck:
00389       ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
00390       return -1;
00391    }
00392 
00393    /* Remove any existing entry */
00394    AST_LIST_LOCK(list);
00395    AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
00396       if (cur->key == new->key) {
00397          AST_LIST_REMOVE_CURRENT(list);
00398          ast_free(cur);
00399          break;
00400       }
00401    }
00402    AST_LIST_TRAVERSE_SAFE_END
00403 
00404    /* Insert new entry */
00405    ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
00406    AST_LIST_INSERT_TAIL(list, new, list);
00407    AST_LIST_UNLOCK(list);
00408 
00409    return 0;
00410 }

AST_TEST_DEFINE ( vulnerable_url   ) 

Definition at line 797 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().

00798 {
00799    const char *bad_urls [] = {
00800       "http://example.com\r\nDELETE http://example.com/everything",
00801       "http://example.com\rDELETE http://example.com/everything",
00802       "http://example.com\nDELETE http://example.com/everything",
00803       "\r\nhttp://example.com",
00804       "\rhttp://example.com",
00805       "\nhttp://example.com",
00806       "http://example.com\r\n",
00807       "http://example.com\r",
00808       "http://example.com\n",
00809    };
00810    const char *good_urls [] = {
00811       "http://example.com",
00812       "http://example.com/%5Cr%5Cn",
00813    };
00814    int i;
00815    enum ast_test_result_state res = AST_TEST_PASS;
00816 
00817    switch (cmd) {
00818    case TEST_INIT:
00819       info->name = "vulnerable_url";
00820       info->category = "/funcs/func_curl/";
00821       info->summary = "cURL vulnerable URL test";
00822       info->description =
00823          "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
00824    case TEST_EXECUTE:
00825       break;
00826    }
00827 
00828    for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
00829       if (!url_is_vulnerable(bad_urls[i])) {
00830          ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
00831          res = AST_TEST_FAIL;
00832       }
00833    }
00834 
00835    for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
00836       if (url_is_vulnerable(good_urls[i])) {
00837          ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
00838          res = AST_TEST_FAIL;
00839       }
00840    }
00841 
00842    return res;
00843 }

static void curl_instance_cleanup ( void *  data  )  [static]

Definition at line 560 of file func_curl.c.

References ast_free.

00561 {
00562    CURL **curl = data;
00563 
00564    curl_easy_cleanup(*curl);
00565 
00566    ast_free(data);
00567 }

static int curl_instance_init ( void *  data  )  [static]

Definition at line 545 of file func_curl.c.

References global_useragent, and WriteMemoryCallback().

00546 {
00547    CURL **curl = data;
00548 
00549    if (!(*curl = curl_easy_init()))
00550       return -1;
00551 
00552    curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
00553    curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
00554    curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
00555    curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
00556 
00557    return 0;
00558 }

static void curlds_free ( void *  data  )  [static]

Definition at line 191 of file func_curl.c.

References ast_free, AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, and AST_LIST_REMOVE_HEAD.

00192 {
00193    AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
00194    struct curl_settings *setting;
00195    if (!list) {
00196       return;
00197    }
00198    while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
00199       ast_free(setting);
00200    }
00201    AST_LIST_HEAD_DESTROY(list);
00202    ast_free(list);
00203 }

static int load_module ( void   )  [static]

Definition at line 857 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.

00858 {
00859    int res;
00860 
00861    if (!ast_module_check("res_curl.so")) {
00862       if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00863          ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
00864          return AST_MODULE_LOAD_DECLINE;
00865       }
00866    }
00867 
00868    res = ast_custom_function_register(&acf_curl);
00869    res |= ast_custom_function_register(&acf_curlopt);
00870 
00871    AST_TEST_REGISTER(vulnerable_url);
00872 
00873    return res;
00874 }

static int parse_curlopt_key ( const char *  name,
CURLoption *  key,
enum optiontype ot 
) [static]

Definition at line 219 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().

00220 {
00221    if (!strcasecmp(name, "header")) {
00222       *key = CURLOPT_HEADER;
00223       *ot = OT_BOOLEAN;
00224    } else if (!strcasecmp(name, "proxy")) {
00225       *key = CURLOPT_PROXY;
00226       *ot = OT_STRING;
00227    } else if (!strcasecmp(name, "proxyport")) {
00228       *key = CURLOPT_PROXYPORT;
00229       *ot = OT_INTEGER;
00230    } else if (!strcasecmp(name, "proxytype")) {
00231       *key = CURLOPT_PROXYTYPE;
00232       *ot = OT_ENUM;
00233    } else if (!strcasecmp(name, "dnstimeout")) {
00234       *key = CURLOPT_DNS_CACHE_TIMEOUT;
00235       *ot = OT_INTEGER;
00236    } else if (!strcasecmp(name, "userpwd")) {
00237       *key = CURLOPT_USERPWD;
00238       *ot = OT_STRING;
00239    } else if (!strcasecmp(name, "proxyuserpwd")) {
00240       *key = CURLOPT_PROXYUSERPWD;
00241       *ot = OT_STRING;
00242    } else if (!strcasecmp(name, "maxredirs")) {
00243       *key = CURLOPT_MAXREDIRS;
00244       *ot = OT_INTEGER;
00245    } else if (!strcasecmp(name, "referer")) {
00246       *key = CURLOPT_REFERER;
00247       *ot = OT_STRING;
00248    } else if (!strcasecmp(name, "useragent")) {
00249       *key = CURLOPT_USERAGENT;
00250       *ot = OT_STRING;
00251    } else if (!strcasecmp(name, "cookie")) {
00252       *key = CURLOPT_COOKIE;
00253       *ot = OT_STRING;
00254    } else if (!strcasecmp(name, "ftptimeout")) {
00255       *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
00256       *ot = OT_INTEGER;
00257    } else if (!strcasecmp(name, "httptimeout")) {
00258 #if CURLVERSION_ATLEAST(7,16,2)
00259       *key = CURLOPT_TIMEOUT_MS;
00260       *ot = OT_INTEGER_MS;
00261 #else
00262       *key = CURLOPT_TIMEOUT;
00263       *ot = OT_INTEGER;
00264 #endif
00265    } else if (!strcasecmp(name, "conntimeout")) {
00266 #if CURLVERSION_ATLEAST(7,16,2)
00267       *key = CURLOPT_CONNECTTIMEOUT_MS;
00268       *ot = OT_INTEGER_MS;
00269 #else
00270       *key = CURLOPT_CONNECTTIMEOUT;
00271       *ot = OT_INTEGER;
00272 #endif
00273    } else if (!strcasecmp(name, "ftptext")) {
00274       *key = CURLOPT_TRANSFERTEXT;
00275       *ot = OT_BOOLEAN;
00276    } else if (!strcasecmp(name, "ssl_verifypeer")) {
00277       *key = CURLOPT_SSL_VERIFYPEER;
00278       *ot = OT_BOOLEAN;
00279    } else if (!strcasecmp(name, "hashcompat")) {
00280       *key = CURLOPT_SPECIAL_HASHCOMPAT;
00281       *ot = OT_ENUM;
00282    } else {
00283       return -1;
00284    }
00285    return 0;
00286 }

static int unload_module ( void   )  [static]

Definition at line 845 of file func_curl.c.

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

00846 {
00847    int res;
00848 
00849    res = ast_custom_function_unregister(&acf_curl);
00850    res |= ast_custom_function_unregister(&acf_curlopt);
00851 
00852    AST_TEST_UNREGISTER(vulnerable_url);
00853 
00854    return res;
00855 }

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:
url The URL to check for vulnerability
Return values:
0 The URL is not vulnerable
1 The URL is vulnerable.

Definition at line 588 of file func_curl.c.

Referenced by acf_curl_helper(), and AST_TEST_DEFINE().

00589 {
00590    if (strpbrk(url, "\r\n")) {
00591       return 1;
00592    }
00593 
00594    return 0;
00595 }

static size_t WriteMemoryCallback ( void *  ptr,
size_t  size,
size_t  nmemb,
void *  data 
) [static]

Definition at line 529 of file func_curl.c.

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

Referenced by curl_instance_init().

00530 {
00531    register int realsize = size * nmemb;
00532    struct ast_str **pstr = (struct ast_str **)data;
00533 
00534    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));
00535 
00536    ast_str_append_substr(pstr, 0, ptr, realsize);
00537 
00538    ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
00539 
00540    return realsize;
00541 }


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 = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_REALTIME_DEPEND2, } [static]

Definition at line 881 of file func_curl.c.

struct ast_custom_function acf_curl [static]

Definition at line 757 of file func_curl.c.

Referenced by load_module(), and unload_module().

Definition at line 768 of file func_curl.c.

Referenced by load_module(), and unload_module().

Definition at line 881 of file func_curl.c.

struct ast_datastore_info curl_info [static]

Initial value:

 {
   .type = "CURL",
   .destroy = curlds_free,
}

Definition at line 178 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 569 of file func_curl.c.

Referenced by acf_curl_helper().

const char* const global_useragent = "asterisk-libcurl-agent/1.0" [static]

Definition at line 543 of file func_curl.c.

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

Definition at line 570 of file func_curl.c.

Referenced by acf_curl_helper().


Generated on Thu Apr 16 06:31:14 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6