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" |
Curl - Load a URL.
Definition in file func_curl.c.
#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, | |||||
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 162 of file func_curl.c.
enum optiontype |
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 };
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.
url | The URL to check for vulnerability |
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 }
struct ast_custom_function acf_curl [static] |
Definition at line 742 of file func_curl.c.
struct ast_custom_function acf_curlopt [static] |
Definition at line 753 of file func_curl.c.
struct ast_datastore_info curl_info [static] |
{ .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.