Wed Jan 27 20:02:40 2016

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

Defines

#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 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_LIST_HEAD_STATIC (global_curl_info, curl_settings)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"Load external URL",.load=load_module,.unload=unload_module,.load_pri=AST_MODPRI_REALTIME_DEPEND2,)
 AST_TEST_DEFINE (vulnerable_url)
 AST_THREADSTORAGE (thread_escapebuf)
 AST_THREADSTORAGE_CUSTOM (curl_instance, curl_instance_init, curl_instance_cleanup)
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_custom_function acf_curl
static struct ast_custom_function acf_curlopt
static struct ast_datastore_info curl_info
static const char *const global_useragent = "asterisk-libcurl-agent/1.0"

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   -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.

00195                 {
00196    OT_BOOLEAN,
00197    OT_INTEGER,
00198    OT_INTEGER_MS,
00199    OT_STRING,
00200    OT_ENUM,
00201 };


Function Documentation

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().

00738 {
00739    return acf_curl_helper(chan, cmd, info, NULL, buf, len);
00740 }

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().

00733 {
00734    return acf_curl_helper(chan, cmd, info, buf, NULL, len);
00735 }

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(), CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, LOG_ERROR, LOG_WARNING, name, pbx_builtin_setvar_helper(), S_OR, str, url, and url_is_vulnerable().

Referenced by acf_curl2_exec(), and acf_curl_exec().

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

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, LOG_ERROR, OT_BOOLEAN, OT_INTEGER, OT_INTEGER_MS, OT_STRING, and parse_curlopt_key().

Referenced by acf_curlopt_read(), and acf_curlopt_read2().

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

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().

00506 {
00507    return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
00508 }

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().

00511 {
00512    return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
00513 }

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, LOG_ERROR, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, OT_STRING, and parse_curlopt_key().

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

AST_LIST_HEAD_STATIC ( global_curl_info  ,
curl_settings   
)
AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_LOAD_ORDER  ,
"Load external URL"  ,
load = load_module,
unload = unload_module,
load_pri = AST_MODPRI_REALTIME_DEPEND2 
)
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().

00782 {
00783    const char *bad_urls [] = {
00784       "http://example.com\r\nDELETE http://example.com/everything",
00785       "http://example.com\rDELETE http://example.com/everything",
00786       "http://example.com\nDELETE http://example.com/everything",
00787       "\r\nhttp://example.com",
00788       "\rhttp://example.com",
00789       "\nhttp://example.com",
00790       "http://example.com\r\n",
00791       "http://example.com\r",
00792       "http://example.com\n",
00793    };
00794    const char *good_urls [] = {
00795       "http://example.com",
00796       "http://example.com/%5Cr%5Cn",
00797    };
00798    int i;
00799    enum ast_test_result_state res = AST_TEST_PASS;
00800 
00801    switch (cmd) {
00802    case TEST_INIT:
00803       info->name = "vulnerable_url";
00804       info->category = "/funcs/func_curl/";
00805       info->summary = "cURL vulnerable URL test";
00806       info->description =
00807          "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
00808    case TEST_EXECUTE:
00809       break;
00810    }
00811 
00812    for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
00813       if (!url_is_vulnerable(bad_urls[i])) {
00814          ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
00815          res = AST_TEST_FAIL;
00816       }
00817    }
00818 
00819    for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
00820       if (url_is_vulnerable(good_urls[i])) {
00821          ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
00822          res = AST_TEST_FAIL;
00823       }
00824    }
00825 
00826    return res;
00827 }

AST_THREADSTORAGE ( thread_escapebuf   ) 
AST_THREADSTORAGE_CUSTOM ( curl_instance  ,
curl_instance_init  ,
curl_instance_cleanup   
)
static void curl_instance_cleanup ( void *  data  )  [static]

Definition at line 546 of file func_curl.c.

References ast_free.

00547 {
00548    CURL **curl = data;
00549 
00550    curl_easy_cleanup(*curl);
00551 
00552    ast_free(data);
00553 }

static int curl_instance_init ( void *  data  )  [static]

Definition at line 531 of file func_curl.c.

References WriteMemoryCallback().

00532 {
00533    CURL **curl = data;
00534 
00535    if (!(*curl = curl_easy_init()))
00536       return -1;
00537 
00538    curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
00539    curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
00540    curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
00541    curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
00542 
00543    return 0;
00544 }

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.

00183 {
00184    AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
00185    struct curl_settings *setting;
00186    if (!list) {
00187       return;
00188    }
00189    while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
00190       free(setting);
00191    }
00192    AST_LIST_HEAD_DESTROY(list);
00193 }

static int load_module ( void   )  [static]

Definition at line 841 of file func_curl.c.

References 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.

00842 {
00843    int res;
00844 
00845    if (!ast_module_check("res_curl.so")) {
00846       if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00847          ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
00848          return AST_MODULE_LOAD_DECLINE;
00849       }
00850    }
00851 
00852    res = ast_custom_function_register(&acf_curl);
00853    res |= ast_custom_function_register(&acf_curlopt);
00854 
00855    AST_TEST_REGISTER(vulnerable_url);
00856 
00857    return res;
00858 }

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().

00204 {
00205    if (!strcasecmp(name, "header")) {
00206       *key = CURLOPT_HEADER;
00207       *ot = OT_BOOLEAN;
00208    } else if (!strcasecmp(name, "proxy")) {
00209       *key = CURLOPT_PROXY;
00210       *ot = OT_STRING;
00211    } else if (!strcasecmp(name, "proxyport")) {
00212       *key = CURLOPT_PROXYPORT;
00213       *ot = OT_INTEGER;
00214    } else if (!strcasecmp(name, "proxytype")) {
00215       *key = CURLOPT_PROXYTYPE;
00216       *ot = OT_ENUM;
00217    } else if (!strcasecmp(name, "dnstimeout")) {
00218       *key = CURLOPT_DNS_CACHE_TIMEOUT;
00219       *ot = OT_INTEGER;
00220    } else if (!strcasecmp(name, "userpwd")) {
00221       *key = CURLOPT_USERPWD;
00222       *ot = OT_STRING;
00223    } else if (!strcasecmp(name, "proxyuserpwd")) {
00224       *key = CURLOPT_PROXYUSERPWD;
00225       *ot = OT_STRING;
00226    } else if (!strcasecmp(name, "maxredirs")) {
00227       *key = CURLOPT_MAXREDIRS;
00228       *ot = OT_INTEGER;
00229    } else if (!strcasecmp(name, "referer")) {
00230       *key = CURLOPT_REFERER;
00231       *ot = OT_STRING;
00232    } else if (!strcasecmp(name, "useragent")) {
00233       *key = CURLOPT_USERAGENT;
00234       *ot = OT_STRING;
00235    } else if (!strcasecmp(name, "cookie")) {
00236       *key = CURLOPT_COOKIE;
00237       *ot = OT_STRING;
00238    } else if (!strcasecmp(name, "ftptimeout")) {
00239       *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
00240       *ot = OT_INTEGER;
00241    } else if (!strcasecmp(name, "httptimeout")) {
00242 #if CURLVERSION_ATLEAST(7,16,2)
00243       *key = CURLOPT_TIMEOUT_MS;
00244       *ot = OT_INTEGER_MS;
00245 #else
00246       *key = CURLOPT_TIMEOUT;
00247       *ot = OT_INTEGER;
00248 #endif
00249    } else if (!strcasecmp(name, "conntimeout")) {
00250 #if CURLVERSION_ATLEAST(7,16,2)
00251       *key = CURLOPT_CONNECTTIMEOUT_MS;
00252       *ot = OT_INTEGER_MS;
00253 #else
00254       *key = CURLOPT_CONNECTTIMEOUT;
00255       *ot = OT_INTEGER;
00256 #endif
00257    } else if (!strcasecmp(name, "ftptext")) {
00258       *key = CURLOPT_TRANSFERTEXT;
00259       *ot = OT_BOOLEAN;
00260    } else if (!strcasecmp(name, "ssl_verifypeer")) {
00261       *key = CURLOPT_SSL_VERIFYPEER;
00262       *ot = OT_BOOLEAN;
00263    } else if (!strcasecmp(name, "hashcompat")) {
00264       *key = CURLOPT_SPECIAL_HASHCOMPAT;
00265       *ot = OT_BOOLEAN;
00266    } else {
00267       return -1;
00268    }
00269    return 0;
00270 }

static int unload_module ( void   )  [static]

Definition at line 829 of file func_curl.c.

References ast_custom_function_unregister(), and AST_TEST_UNREGISTER.

00830 {
00831    int res;
00832 
00833    res = ast_custom_function_unregister(&acf_curl);
00834    res |= ast_custom_function_unregister(&acf_curlopt);
00835 
00836    AST_TEST_UNREGISTER(vulnerable_url);
00837 
00838    return res;
00839 }

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

Referenced by acf_curl_helper(), and AST_TEST_DEFINE().

00575 {
00576    if (strpbrk(url, "\r\n")) {
00577       return 1;
00578    }
00579 
00580    return 0;
00581 }

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().

00516 {
00517    register int realsize = size * nmemb;
00518    struct ast_str **pstr = (struct ast_str **)data;
00519 
00520    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));
00521 
00522    ast_str_append_substr(pstr, 0, ptr, realsize);
00523 
00524    ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
00525 
00526    return realsize;
00527 }


Variable Documentation

struct ast_custom_function acf_curl [static]

Definition at line 742 of file func_curl.c.

Definition at line 753 of file func_curl.c.

struct ast_datastore_info curl_info [static]
Initial value:
 {
   .type = "CURL",
   .destroy = curlds_free,
}

Definition at line 169 of file func_curl.c.

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

Definition at line 529 of file func_curl.c.


Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1