From: Automerge script Date: Wed, 27 Mar 2013 15:29:09 +0000 (+0000) Subject: Multiple revisions 383977,383982 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ee20c58acc9a71fbf90201f3482993e3f8b6b130;p=thirdparty%2Fasterisk.git Multiple revisions 383977,383982 ........ r383977 | mjordan | 2013-03-27 09:36:22 -0500 (Wed, 27 Mar 2013) | 21 lines AST-2013-002: Prevent denial of service in HTTP server AST-2012-014, fixed in January of this year, contained a fix for Asterisk's HTTP server for a remotely-triggered crash. While the fix put in place fixed the possibility for the crash to be triggered, a denial of service vector still exists with that solution if an attacker sends one or more HTTP POST requests with very large Content-Length values. This patch resolves this by capping the Content-Length at 1024 bytes. Any attempt to send an HTTP POST with Content-Length greater than this cap will not result in any memory allocation. The POST will be responded to with an HTTP 413 "Request Entity Too Large" response. This issue was reported by Christoph Hebeisen of TELUS Security Labs (closes issue ASTERISK-20967) Reported by: Christoph Hebeisen patches: AST-2013-002-1.8.diff uploaded by mmichelson (License 5049) AST-2013-002-10.diff uploaded by mmichelson (License 5049) AST-2013-002-11.diff uploaded by mmichelson (License 5049) ........ r383982 | mjordan | 2013-03-27 09:55:16 -0500 (Wed, 27 Mar 2013) | 32 lines AST-2013-003: Prevent username disclosure in SIP channel driver When authenticating a SIP request with alwaysauthreject enabled, allowguest disabled, and autocreatepeer disabled, Asterisk discloses whether a user exists for INVITE, SUBSCRIBE, and REGISTER transactions in multiple ways. The information is disclosed when: * A "407 Proxy Authentication Required" response is sent instead of a "401 Unauthorized" response * The presence or absence of additional tags occurs at the end of "403 Forbidden" (such as "(Bad Auth)") * A "401 Unauthorized" response is sent instead of "403 Forbidden" response after a retransmission * Retransmission are sent when a matching peer did not exist, but not when a matching peer did exist. This patch resolves these various vectors by ensuring that the responses sent in all scenarios is the same, regardless of the presence of a matching peer. This issue was reported by Walter Doekes, OSSO B.V. A substantial portion of the testing and the solution to this problem was done by Walter as well - a huge thanks to his tireless efforts in finding all the ways in which this setting didn't work, providing automated tests, and working with Kinsey on getting this fixed. (closes issue ASTERISK-21013) Reported by: wdoekes Tested by: wdoekes, kmoore patches: AST-2013-003-1.8 uploaded by kmoore, wdoekes (License 6273, 5674) AST-2013-003-10 uploaded by kmoore, wdoekes (License 6273, 5674) AST-2013-003-11 uploaded by kmoore, wdoekes (License 6273, 5674) ........ Merged revisions 383977,383982 from file:///srv/subversion/repos/asterisk/branches/10 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10-digiumphones@384025 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 499d592ed9..21626e2602 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -1146,6 +1146,11 @@ static struct ao2_container *threadt; static struct ao2_container *peers; static struct ao2_container *peers_by_ip; +/*! \brief A bogus peer, to be used when authentication should fail */ +static struct sip_peer *bogus_peer; +/*! \brief We can recognise the bogus peer by this invalid MD5 hash */ +#define BOGUS_PEER_MD5SECRET "intentionally_invalid_md5_string" + /*! \brief The register list: Other SIP proxies we register with and receive calls from */ static struct ast_register_list { ASTOBJ_CONTAINER_COMPONENTS(struct sip_registry); @@ -1286,7 +1291,7 @@ static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *rand, enum xmittype reliable, const char *header, int stale); static int transmit_provisional_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, int with_sdp); static int transmit_response_with_allow(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); -static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable); +static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable); static int transmit_request(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, uint32_t seqno, enum xmittype reliable, int newbranch); static int transmit_publish(struct sip_epa_entry *epa_entry, enum sip_publish_type publish_type, const char * const explicit_uri); @@ -15370,6 +15375,7 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request * char a1_hash[256]; char resp_hash[256]=""; char *c; + int is_bogus_peer = 0; int wrongnonce = FALSE; int good_response; const char *usednonce = p->randdata; @@ -15441,8 +15447,14 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request * sip_digest_parser(c, keys); + /* We cannot rely on the bogus_peer having a bad md5 value. Someone could + * use it to construct valid auth. */ + if (md5secret && strcmp(md5secret, BOGUS_PEER_MD5SECRET) == 0) { + is_bogus_peer = 1; + } + /* Verify that digest username matches the username we auth as */ - if (strcmp(username, keys[K_USER].s)) { + if (strcmp(username, keys[K_USER].s) && !is_bogus_peer) { ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n", username, keys[K_USER].s); /* Oops, we're trying something here */ @@ -15481,7 +15493,8 @@ static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request * } good_response = keys[K_RESP].s && - !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)); + !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)) && + !is_bogus_peer; /* lastly, check that the peer isn't the fake peer */ if (wrongnonce) { if (good_response) { if (sipdebug) @@ -15644,13 +15657,13 @@ static int cb_extensionstate(char *context, char *exten, struct ast_state_cb_inf /*! \brief Send a fake 401 Unauthorized response when the administrator wants to hide the names of local devices from fishers */ -static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct sip_request *req, enum xmittype reliable) +static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, enum xmittype reliable) { /* We have to emulate EXACTLY what we'd get with a good peer * and a bad password, or else we leak information. */ - const char *response = "407 Proxy Authentication Required"; - const char *reqheader = "Proxy-Authorization"; - const char *respheader = "Proxy-Authenticate"; + const char *response = "401 Unauthorized"; + const char *reqheader = "Authorization"; + const char *respheader = "WWW-Authenticate"; const char *authtoken; struct ast_str *buf; char *c; @@ -15665,36 +15678,31 @@ static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct [K_LAST] = { NULL, NULL} }; - if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) { - response = "401 Unauthorized"; - reqheader = "Authorization"; - respheader = "WWW-Authenticate"; - } authtoken = sip_get_header(req, reqheader); if (req->ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) { /* This is a retransmitted invite/register/etc, don't reconstruct authentication * information */ - transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0); + transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); /* Schedule auto destroy in 32 seconds (according to RFC 3261) */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); return; } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) { /* We have no auth, so issue challenge and request authentication */ set_nonce_randdata(p, 1); - transmit_response_with_auth(p, response, req, p->randdata, 0, respheader, 0); + transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); /* Schedule auto destroy in 32 seconds */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); return; } if (!(buf = ast_str_thread_get(&check_auth_buf, CHECK_AUTH_BUF_INITLEN))) { - transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); + __transmit_response(p, "403 Forbidden", &p->initreq, reliable); return; } /* Make a copy of the response and parse it */ if (ast_str_set(&buf, 0, "%s", authtoken) == AST_DYNSTR_BUILD_FAILED) { - transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); + __transmit_response(p, "403 Forbidden", &p->initreq, reliable); return; } @@ -15732,7 +15740,7 @@ static void transmit_fake_auth_response(struct sip_pvt *p, int sipmethod, struct /* Schedule auto destroy in 32 seconds */ sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); } else { - transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); + __transmit_response(p, "403 Forbidden", &p->initreq, reliable); } } @@ -15842,7 +15850,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock if (!AST_LIST_EMPTY(&domain_list)) { if (!check_sip_domain(domain, NULL, 0)) { if (sip_cfg.alwaysauthreject) { - transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE); + transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE); } else { transmit_response(p, "404 Not found (unknown domain)", &p->initreq); } @@ -15869,6 +15877,13 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock } peer = sip_find_peer(name, NULL, TRUE, FINDPEERS, FALSE, 0); + /* If we don't want username disclosure, use the bogus_peer when a user + * is not found. */ + if (!peer && sip_cfg.alwaysauthreject && !sip_cfg.autocreatepeer) { + peer = bogus_peer; + sip_ref_peer(peer, "register_verify: ref the bogus_peer"); + } + if (!(peer && ast_apply_ha(peer->ha, addr))) { /* Peer fails ACL check */ if (peer) { @@ -15945,7 +15960,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock switch (parse_register_contact(p, peer, req)) { case PARSE_REGISTER_DENIED: ast_log(LOG_WARNING, "Registration denied because of contact ACL\n"); - transmit_response_with_date(p, "403 Forbidden (ACL)", req); + transmit_response_with_date(p, "403 Forbidden", req); res = 0; break; case PARSE_REGISTER_FAILED: @@ -15985,7 +16000,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock switch (res) { case AUTH_SECRET_FAILED: /* Wrong password in authentication. Go away, don't try again until you fixed it */ - transmit_response(p, "403 Forbidden (Bad auth)", &p->initreq); + transmit_response(p, "403 Forbidden", &p->initreq); if (global_authfailureevents) { const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr)); const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr)); @@ -16008,7 +16023,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock case AUTH_PEER_NOT_DYNAMIC: case AUTH_ACL_FAILED: if (sip_cfg.alwaysauthreject) { - transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE); + transmit_fake_auth_response(p, &p->initreq, XMIT_UNRELIABLE); if (global_authfailureevents) { const char *peer_addr = ast_strdupa(ast_sockaddr_stringify_addr(addr)); const char *peer_port = ast_strdupa(ast_sockaddr_stringify_port(addr)); @@ -17015,7 +17030,19 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, ast_verbose("No matching peer for '%s' from '%s'\n", of, ast_sockaddr_stringify(&p->recv)); } - return AUTH_DONT_KNOW; + + /* If you don't mind, we can return 404s for devices that do + * not exist: username disclosure. If we allow guests, there + * is no way around that. */ + if (sip_cfg.allowguest || !sip_cfg.alwaysauthreject) { + return AUTH_DONT_KNOW; + } + + /* If you do mind, we use a peer that will never authenticate. + * This ensures that we follow the same code path as regular + * auth: less chance for username disclosure. */ + peer = bogus_peer; + sip_ref_peer(peer, "sip_ref_peer: check_peer_ok: must ref bogus_peer so unreffing it does not fail"); } if (!ast_apply_ha(peer->ha, addr)) { @@ -17023,9 +17050,10 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of, sip_unref_peer(peer, "sip_unref_peer: check_peer_ok: from sip_find_peer call, early return of AUTH_ACL_FAILED"); return AUTH_ACL_FAILED; } - if (debug) + if (debug && peer != bogus_peer) { ast_verbose("Found peer '%s' for '%s' from %s\n", peer->name, of, ast_sockaddr_stringify(&p->recv)); + } /* XXX what about p->prefs = peer->prefs; ? */ /* Set Frame packetization */ @@ -17302,8 +17330,6 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ } else { res = AUTH_RTP_FAILED; } - } else if (sip_cfg.alwaysauthreject) { - res = AUTH_FAKE_AUTH; /* reject with fake authorization request */ } else { res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */ } @@ -17495,13 +17521,8 @@ static void receive_message(struct sip_pvt *p, struct sip_request *req, struct a return; } if (res < 0) { /* Something failed in authentication */ - if (res == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From")); - transmit_fake_auth_response(p, SIP_MESSAGE, req, XMIT_UNRELIABLE); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); - transmit_response(p, "403 Forbidden", req); - } + ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); + transmit_response(p, "403 Forbidden", req); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); return; } @@ -23351,13 +23372,8 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req, st return 0; } if (res < 0) { /* Something failed in authentication */ - if (res == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From")); - transmit_fake_auth_response(p, SIP_OPTIONS, req, XMIT_UNRELIABLE); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); - transmit_response(p, "403 Forbidden", req); - } + ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); + transmit_response(p, "403 Forbidden", req); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); return 0; } @@ -24015,13 +24031,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int goto request_invite_cleanup; } if (res < 0) { /* Something failed in authentication */ - if (res == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From")); - transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); - } + ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); + transmit_response_reliable(p, "403 Forbidden", req); p->invitestate = INV_COMPLETED; sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); goto request_invite_cleanup; @@ -26025,18 +26036,13 @@ static int handle_request_publish(struct sip_pvt *p, struct sip_request *req, st return -1; } - auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_RELIABLE, addr); + auth_result = check_user(p, req, SIP_PUBLISH, uri, XMIT_UNRELIABLE, addr); if (auth_result == AUTH_CHALLENGE_SENT) { p->lastinvite = seqno; return 0; } else if (auth_result < 0) { - if (auth_result == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From")); - transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); - } + ast_log(LOG_NOTICE, "Failed to authenticate device %s\n", sip_get_header(req, "From")); + transmit_response(p, "403 Forbidden", req); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); ast_string_field_set(p, theirtag, NULL); return 0; @@ -26248,19 +26254,14 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, * use if !req->ignore, because then we'll end up sending * a 200 OK if someone retransmits without sending auth */ if (p->subscribed == NONE || resubscribe) { - res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, addr, &authpeer); + res = check_user_full(p, req, SIP_SUBSCRIBE, e, XMIT_UNRELIABLE, addr, &authpeer); /* if an authentication response was sent, we are done here */ if (res == AUTH_CHALLENGE_SENT) /* authpeer = NULL here */ return 0; if (res != AUTH_SUCCESSFUL) { - if (res == AUTH_FAKE_AUTH) { - ast_log(LOG_NOTICE, "Sending fake auth rejection for device %s\n", sip_get_header(req, "From")); - transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE); - } else { - ast_log(LOG_NOTICE, "Failed to authenticate device %s for SUBSCRIBE\n", sip_get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); - } + ast_log(LOG_NOTICE, "Failed to authenticate device %s for SUBSCRIBE\n", sip_get_header(req, "From")); + transmit_response(p, "403 Forbidden", req); pvt_set_needdestroy(p, "authentication failed"); return 0; @@ -31528,6 +31529,7 @@ static int sip_do_reload(enum channelreloadreason reason) /*! \brief Force reload of module from cli */ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { + static struct sip_peer *tmp_peer, *new_peer; switch (cmd) { case CLI_INIT: @@ -31550,6 +31552,18 @@ static char *sip_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a ast_mutex_unlock(&sip_reload_lock); restart_monitor(); + tmp_peer = bogus_peer; + /* Create new bogus peer possibly with new global settings. */ + if ((new_peer = temp_peer("(bogus_peer)"))) { + ast_string_field_set(new_peer, md5secret, BOGUS_PEER_MD5SECRET); + ast_clear_flag(&new_peer->flags[0], SIP_INSECURE); + bogus_peer = new_peer; + ao2_t_ref(tmp_peer, -1, "unref the old bogus_peer during reload"); + } else { + ast_log(LOG_ERROR, "Could not update the fake authentication peer.\n"); + /* You probably have bigger (memory?) issues to worry about though.. */ + } + return CLI_SUCCESS; } @@ -32693,6 +32707,17 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } + /* Initialize bogus peer. Can be done first after reload_config() */ + if (!(bogus_peer = temp_peer("(bogus_peer)"))) { + ast_log(LOG_ERROR, "Unable to create bogus_peer for authentication\n"); + io_context_destroy(io); + ast_sched_context_destroy(sched); + return AST_MODULE_LOAD_FAILURE; + } + /* Make sure the auth will always fail. */ + ast_string_field_set(bogus_peer, md5secret, BOGUS_PEER_MD5SECRET); + ast_clear_flag(&bogus_peer->flags[0], SIP_INSECURE); + /* Prepare the version that does not require DTMF BEGIN frames. * We need to use tricks such as memcpy and casts because the variable * has const fields. @@ -32708,6 +32733,7 @@ static int load_module(void) /* Make sure we can register our sip channel type */ if (ast_channel_register(&sip_tech)) { ast_log(LOG_ERROR, "Unable to register channel type 'SIP'\n"); + ao2_t_ref(bogus_peer, -1, "unref the bogus_peer"); io_context_destroy(io); ast_sched_context_destroy(sched); return AST_MODULE_LOAD_FAILURE; @@ -32953,6 +32979,8 @@ static int unload_module(void) ast_debug(2, "TCP/TLS thread container did not become empty :(\n"); } + ao2_t_ref(bogus_peer, -1, "unref the bogus_peer"); + ao2_t_ref(peers, -1, "unref the peers table"); ao2_t_ref(peers_by_ip, -1, "unref the peers_by_ip table"); ao2_t_ref(dialogs, -1, "unref the dialogs table"); diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h index 16fa099063..d4a298e0a7 100644 --- a/channels/sip/include/sip.h +++ b/channels/sip/include/sip.h @@ -489,7 +489,6 @@ enum check_auth_result { AUTH_SECRET_FAILED = -1, AUTH_USERNAME_MISMATCH = -2, AUTH_NOT_FOUND = -3, /*!< returned by register_verify */ - AUTH_FAKE_AUTH = -4, AUTH_UNKNOWN_DOMAIN = -5, AUTH_PEER_NOT_DYNAMIC = -6, AUTH_ACL_FAILED = -7, diff --git a/channels/sip/security_events.c b/channels/sip/security_events.c index fc63d9f938..0af39aa1b4 100644 --- a/channels/sip/security_events.c +++ b/channels/sip/security_events.c @@ -368,9 +368,6 @@ int sip_report_security_event(const struct sip_pvt *p, const struct sip_request /* with sip_cfg.alwaysauthreject on, generates 2 events */ sip_report_invalid_peer(p); break; - case AUTH_FAKE_AUTH: - sip_report_invalid_peer(p); - break; case AUTH_UNKNOWN_DOMAIN: snprintf(aclname, sizeof(aclname), "domain_must_match"); sip_report_failed_acl(p, aclname); diff --git a/main/http.c b/main/http.c index 6f6bde80e4..b327425873 100644 --- a/main/http.c +++ b/main/http.c @@ -593,6 +593,8 @@ void ast_http_uri_unlink_all_with_key(const char *key) AST_RWLIST_UNLOCK(&uris); } +#define MAX_POST_CONTENT 1025 + /* * get post variables from client Request Entity-Body, if content type is * application/x-www-form-urlencoded @@ -625,6 +627,13 @@ struct ast_variable *ast_http_get_post_vars( return NULL; } + if (content_length > MAX_POST_CONTENT - 1) { + ast_log(LOG_WARNING, "Excessively long HTTP content. %d is greater than our max of %d\n", + content_length, MAX_POST_CONTENT); + ast_http_send(ser, AST_HTTP_POST, 413, "Request Entity Too Large", NULL, NULL, 0, 0); + return NULL; + } + buf = ast_malloc(content_length + 1); if (!buf) { return NULL;