AST-2013-003: Prevent username disclosure in SIP channel driver
authorMatthew Jordan <mjordan@digium.com>
Wed, 27 Mar 2013 15:27:31 +0000 (15:27 +0000)
committerMatthew Jordan <mjordan@digium.com>
Wed, 27 Mar 2013 15:27:31 +0000 (15:27 +0000)
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 384003 from http://svn.asterisk.org/svn/asterisk/branches/11

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@384019 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c
channels/sip/include/sip.h
channels/sip/security_events.c

index 82a8519..eb26020 100644 (file)
@@ -1009,6 +1009,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);
@@ -1157,7 +1162,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);
@@ -16479,6 +16484,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->nonce;
@@ -16550,8 +16556,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 */
@@ -16590,7 +16602,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)
@@ -16821,13 +16834,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;
@@ -16842,36 +16855,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->nonce) && ast_strlen_zero(authtoken)) {
                /* This is a retransmitted invite/register/etc, don't reconstruct authentication
                 * information */
-               transmit_response_with_auth(p, response, req, p->nonce, 0, respheader, 0);
+               transmit_response_with_auth(p, response, req, p->nonce, 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->nonce) || ast_strlen_zero(authtoken)) {
                /* We have no auth, so issue challenge and request authentication */
                build_nonce(p, 1);
-               transmit_response_with_auth(p, response, req, p->nonce, 0, respheader, 0);
+               transmit_response_with_auth(p, response, req, p->nonce, 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;
        }
 
@@ -16909,7 +16917,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);
        }
 }
 
@@ -17019,7 +17027,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);
                        }
@@ -17046,6 +17054,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 == AUTOPEERS_DISABLED) {
+               peer = bogus_peer;
+               sip_ref_peer(peer, "register_verify: ref the bogus_peer");
+       }
+
        if (!(peer && ast_apply_acl(peer->acl, addr, "SIP Peer ACL: "))) {
                /* Peer fails ACL check */
                if (peer) {
@@ -17137,7 +17152,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:
@@ -17177,7 +17192,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));
@@ -17200,7 +17215,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));
@@ -18239,7 +18254,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");
        }
 
        /*  build_peer, called through sip_find_peer, is not able to check the
@@ -18262,9 +18289,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 */
@@ -18547,8 +18575,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 */
        }
@@ -18683,13 +18709,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;
                }
@@ -24847,13 +24868,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;
                }
@@ -25498,13 +25514,8 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
                        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;
@@ -27547,18 +27558,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;
@@ -27770,19 +27776,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;
@@ -33315,6 +33316,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:
@@ -33337,6 +33339,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;
 }
 
@@ -34564,6 +34578,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.
@@ -34579,6 +34604,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;
@@ -34841,6 +34867,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");
index 6eb2f29..e8f629b 100644 (file)
@@ -507,7 +507,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,
index 4045ae1..77dfa2e 100644 (file)
@@ -342,9 +342,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);