From 51c8db4fb18b69cc185a36e326a6cdba6f580bf0 Mon Sep 17 00:00:00 2001 From: Walter Doekes Date: Sun, 4 Dec 2011 10:03:31 +0000 Subject: [PATCH] For SIP REGISTER fix domain-only URIs and domain ACL bypass. The code that allowed admins to create users with domain-only uri's had stopped to work in 1.8 because of the reqresp parser rewrites. This is fixed now: if you have a [mydomain.com] sip user, you can register with useraddr sip:mydomain.com. Note that in that case -- if you're using domain ACLs (a configured domain list) -- mydomain.com must be in the allow list as well. Reviewboard r1606 shows a list of registration combinations and which SIP response codes are returned. Review: https://reviewboard.asterisk.org/r/1533/ Reviewed by: Terry Wilson (closes issue ASTERISK-18389) (closes issue ASTERISK-18741) ........ Merged revisions 346899 from http://svn.asterisk.org/svn/asterisk/branches/1.8 git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/10@346900 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_sip.c | 109 ++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 39 deletions(-) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 0d48cc46e7..5a80bc3c9e 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -13951,7 +13951,12 @@ static int parse_ok_contact(struct sip_pvt *pvt, struct sip_request *req) return TRUE; } -/*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled */ +/*! \brief parse uri in a way that allows semicolon stripping if legacy mode is enabled + * + * \note This calls parse_uri which has the unexpected property that passing more + * arguments results in more splitting. Most common is to leave out the pass + * argument, causing user to contain user:pass if available. + */ static int parse_uri_legacy_check(char *uri, const char *scheme, char **user, char **pass, char **hostport, char **transport) { int ret = parse_uri(uri, scheme, user, pass, hostport, transport); @@ -14819,7 +14824,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock int sendmwi = 0; struct sip_peer *peer; char tmp[256]; - char *name = NULL, *c, *domain = NULL, *dummy = NULL; + char *c, *name, *unused_password, *domain; char *uri2 = ast_strdupa(uri); terminate_uri(uri2); @@ -14829,7 +14834,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock c = get_in_brackets(tmp); c = remove_uri_parameters(c); - if (parse_uri_legacy_check(c, "sip:,sips:", &name, &dummy, &domain, NULL)) { + if (parse_uri_legacy_check(c, "sip:,sips:", &name, &unused_password, &domain, NULL)) { ast_log(LOG_NOTICE, "Invalid to address: '%s' from %s (missing sip:) trying to use anyway...\n", c, ast_sockaddr_stringify_addr(addr)); return -1; } @@ -14839,12 +14844,34 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock extract_host_from_hostport(&domain); - /*! \todo XXX here too we interpret a missing @domain as a name-only - * URI, whereas the RFC says this is a domain-only uri. - */ - if (!ast_strlen_zero(domain) && !AST_LIST_EMPTY(&domain_list)) { + if (ast_strlen_zero(domain)) { + /* , never good */ + transmit_response(p, "404 Not found", &p->initreq); + return AUTH_UNKNOWN_DOMAIN; + } + + if (ast_strlen_zero(name)) { + /* , unsure whether valid for + * registration. RFC 3261, 10.2 states: + * "The To header field and the Request-URI field typically + * differ, as the former contains a user name." + * But, Asterisk has always treated the domain-only uri as a + * username: we allow admins to create accounts described by + * domain name. */ + name = domain; + } + + /* This here differs from 1.4 and 1.6: the domain matching ACLs were + * skipped if it was a domain-only URI (used as username). Here we treat + * as and won't forget to test the + * domain ACLs against host. */ + if (!AST_LIST_EMPTY(&domain_list)) { if (!check_sip_domain(domain, NULL, 0)) { - transmit_response(p, "404 Not found (unknown domain)", &p->initreq); + if (sip_cfg.alwaysauthreject) { + transmit_fake_auth_response(p, SIP_REGISTER, &p->initreq, XMIT_UNRELIABLE); + } else { + transmit_response(p, "404 Not found (unknown domain)", &p->initreq); + } return AUTH_UNKNOWN_DOMAIN; } } @@ -15386,7 +15413,7 @@ static int get_rdnis(struct sip_pvt *p, struct sip_request *oreq, char **name, c */ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_request *oreq, int *cc_recall_core_id) { - char tmp[256] = "", *uri, *domain, *dummy = NULL; + char tmp[256] = "", *uri, *unused_password, *domain; char tmpf[256] = "", *from = NULL; struct sip_request *req; char *decoded_uri; @@ -15402,7 +15429,7 @@ static enum sip_get_dest_result get_destination(struct sip_pvt *p, struct sip_re uri = ast_strdupa(get_in_brackets(tmp)); - if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &dummy, &domain, NULL)) { + if (parse_uri_legacy_check(uri, "sip:,sips:", &uri, &unused_password, &domain, NULL)) { ast_log(LOG_WARNING, "Not a SIP header (%s)?\n", uri); return SIP_GET_DEST_INVALID_URI; } @@ -16188,10 +16215,7 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ int sipmethod, const char *uri, enum xmittype reliable, struct ast_sockaddr *addr, struct sip_peer **authpeer) { - char from[256] = { 0, }; - char *dummy = NULL; /* dummy return value for parse_uri */ - char *domain = NULL; /* dummy return value for parse_uri */ - char *of; + char from[256] = "", *of, *name, *unused_password, *domain; enum check_auth_result res = AUTH_DONT_KNOW; char calleridname[50]; char *uri2 = ast_strdupa(uri); @@ -16208,8 +16232,9 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ return res; } - if (calleridname[0]) + if (calleridname[0]) { ast_string_field_set(p, cid_name, calleridname); + } if (ast_strlen_zero(p->exten)) { char *t = uri2; @@ -16231,32 +16256,36 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ /* save the URI part of the From header */ ast_string_field_set(p, from, of); - /* ignore all fields but name */ - if (parse_uri_legacy_check(of, "sip:,sips:", &of, &dummy, &domain, NULL)) { + if (parse_uri_legacy_check(of, "sip:,sips:", &name, &unused_password, &domain, NULL)) { ast_log(LOG_NOTICE, "From address missing 'sip:', using it anyway\n"); } - SIP_PEDANTIC_DECODE(of); + SIP_PEDANTIC_DECODE(name); SIP_PEDANTIC_DECODE(domain); - if (ast_strlen_zero(of)) { - /* XXX note: the original code considered a missing @host - * as a username-only URI. The SIP RFC (19.1.1) says that - * this is wrong, and it should be considered as a domain-only URI. - * For backward compatibility, we keep this block, but it is - * really a mistake and should go away. - */ + extract_host_from_hostport(&domain); - extract_host_from_hostport(&domain); - of = domain; + if (ast_strlen_zero(domain)) { + /* , never good */ + ast_log(LOG_ERROR, "Empty domain name in FROM header\n"); + return res; + } + + if (ast_strlen_zero(name)) { + /* . Asterisk 1.4 and 1.6 have always + * treated that as a username, so we continue the tradition: + * uri is now . */ + name = domain; } else { - char *tmp = ast_strdupa(of); - /* We need to be able to handle auth-headers looking like + /* Non-empty name, try to get caller id from it */ + char *tmp = ast_strdupa(name); + /* We need to be able to handle from-headers looking like */ tmp = strsep(&tmp, ";"); - if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp)) + if (global_shrinkcallerid && ast_is_shrinkable_phonenumber(tmp)) { ast_shrink_phone_number(tmp); + } ast_string_field_set(p, cid_num, tmp); } @@ -16270,20 +16299,22 @@ static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_requ * pick one or another depending on the request ? XXX */ const char *hdr = sip_get_header(req, "Authorization"); - if (ast_strlen_zero(hdr)) + if (ast_strlen_zero(hdr)) { hdr = sip_get_header(req, "Proxy-Authorization"); + } - if ( !ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\"")) ) { + if (!ast_strlen_zero(hdr) && (hdr = strstr(hdr, "username=\""))) { ast_copy_string(from, hdr + strlen("username=\""), sizeof(from)); - of = from; - of = strsep(&of, "\""); + name = from; + name = strsep(&name, "\""); } } - res = check_peer_ok(p, of, req, sipmethod, addr, + res = check_peer_ok(p, name, req, sipmethod, addr, authpeer, reliable, calleridname, uri2); - if (res != AUTH_DONT_KNOW) + if (res != AUTH_DONT_KNOW) { return res; + } /* Finally, apply the guest policy */ if (sip_cfg.allowguest) { @@ -16296,11 +16327,11 @@ 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) + } else if (sip_cfg.alwaysauthreject) { res = AUTH_FAKE_AUTH; /* reject with fake authorization request */ - else + } else { res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */ - + } if (ast_test_flag(&p->flags[1], SIP_PAGE2_RPORT_PRESENT)) { ast_set_flag(&p->flags[0], SIP_NAT_RPORT_PRESENT);