[asterisk-commits] trunk r29903 - in /trunk: channels/chan_sip.c
configs/sip.conf.sample
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Tue May 23 20:28:50 MST 2006
Author: kpfleming
Date: Tue May 23 22:28:49 2006
New Revision: 29903
URL: http://svn.digium.com/view/asterisk?rev=29903&view=rev
Log:
add a new option for 'obscuring' SIP user/peer names from fishers
use an enum for authentication results and clean up code
fix a bug where SUBSCRIBE for an unknown user/peer would not generate a response
Modified:
trunk/channels/chan_sip.c
trunk/configs/sip.conf.sample
Modified: trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/trunk/channels/chan_sip.c?rev=29903&r1=29902&r2=29903&view=diff
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Tue May 23 22:28:49 2006
@@ -444,6 +444,7 @@
/* Global settings only apply to the channel */
static int global_rtautoclear;
static int global_notifyringing; /*!< Send notifications on ringing */
+static int global_alwaysauthreject; /*!< Send 401 Unauthorized for all failing requests */
static int srvlookup; /*!< SRV Lookup on or off. Default is off, RFC behavior is on */
static int pedanticsipchecking; /*!< Extra checking ? Default off */
static int autocreatepeer; /*!< Auto creation of peers at registration? Default off. */
@@ -1031,6 +1032,16 @@
static struct ast_config *notify_types; /*!< The list of manual NOTIFY types we know how to send */
+enum check_auth_result {
+ AUTH_SUCCESSFUL = 0,
+ AUTH_CHALLENGE_SENT = 1,
+ AUTH_SECRET_FAILED = -1,
+ AUTH_USERNAME_MISMATCH = -2,
+ AUTH_NOT_FOUND = -3,
+ AUTH_FAKE_AUTH = -4,
+ AUTH_UNKNOWN_DOMAIN = -5,
+};
+
/*---------------------------- Forward declarations of functions in chan_sip.c */
/*! \note Sorted up from start to build_rpid.... Will continue categorization in order to
split up chan_sip.c into several files */
@@ -1114,9 +1125,9 @@
static int clear_realm_authentication(struct sip_auth *authlist); /* Clear realm authentication list (at reload) */
static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno); /* Add realm authentication in list */
static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, const char *realm); /* Find authentication for a specific realm */
-static int check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
- const char *secret, const char *md5secret, int sipmethod,
- char *uri, enum xmittype reliable, int ignore);
+static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
+ const char *secret, const char *md5secret, int sipmethod,
+ char *uri, enum xmittype reliable, int ignore);
/*--- Domain handling */
static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */
@@ -6635,12 +6646,11 @@
/*! \brief Check user authorization from peer definition
Some actions, like REGISTER and INVITEs from peers require
authentication (if peer have secret set)
- \return -1 on Error, 0 on success, 1 on challenge sent
-
+ \return 0 on success, non-zero on error
*/
-static int check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
- const char *secret, const char *md5secret, int sipmethod,
- char *uri, enum xmittype reliable, int ignore)
+static enum check_auth_result check_auth(struct sip_pvt *p, struct sip_request *req, const char *username,
+ const char *secret, const char *md5secret, int sipmethod,
+ char *uri, enum xmittype reliable, int ignore)
{
const char *response = "407 Proxy Authentication Required";
const char *reqheader = "Proxy-Authorization";
@@ -6649,7 +6659,7 @@
/* Always OK if no secret */
if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret))
- return 0;
+ return AUTH_SUCCESSFUL;
if (sipmethod == SIP_REGISTER || sipmethod == SIP_SUBSCRIBE) {
/* On a REGISTER, we have to use 401 and its family of headers instead of 407 and its family
of headers -- GO SIP! Whoo hoo! Two things that do the same thing but are used in
@@ -6669,14 +6679,14 @@
/* Schedule auto destroy in 32 seconds (according to RFC 3261) */
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
}
- return 1; /* Auth sent */
+ return AUTH_CHALLENGE_SENT;
} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
/* We have no auth, so issue challenge and request authentication */
ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
/* Schedule auto destroy in 32 seconds */
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- return 1; /* Auth sent */
+ return AUTH_CHALLENGE_SENT;
} else { /* We have auth, so check it */
/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
an example in the spec of just what it is you're doing a hash on. */
@@ -6729,7 +6739,7 @@
ast_log(LOG_WARNING, "username mismatch, have <%s>, digest has <%s>\n",
username, keys[K_USER].s);
/* Oops, we're trying something here */
- return -2;
+ return AUTH_USERNAME_MISMATCH;
}
/* Verify nonce from request matches our nonce. If not, send 401 with new nonce */
@@ -6777,18 +6787,16 @@
/* Schedule auto destroy in 32 seconds */
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- return 1; /* XXX should it be -1 ? */
+ return AUTH_CHALLENGE_SENT;
}
- if (good_response) /* Auth is OK */
- return 0;
-
- /* XXX is this needed ? */
+ if (good_response)
+ return AUTH_SUCCESSFUL;
+
/* Ok, we have a bad username/secret pair */
/* Challenge again, and again, and again */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
- return 1; /* Challenge sent */
-
+ return AUTH_CHALLENGE_SENT;
}
}
@@ -6821,10 +6829,20 @@
return 0;
}
+/*! \brief Send a fake 401 Unauthorized response when the administrator
+ wants to hide the names of local users/peers from fishers
+ */
+static void transmit_fake_auth_response(struct sip_pvt *p, struct sip_request *req, int reliable)
+{
+ ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
+ transmit_response_with_auth(p, "401 Unauthorized", req, p->randdata, reliable, "WWW-Authenticate", 0);
+}
+
/*! \brief Verify registration of user */
-static int register_verify(struct sip_pvt *p, struct sockaddr_in *sin, struct sip_request *req, char *uri)
-{
- int res = -3;
+static enum check_auth_result register_verify(struct sip_pvt *p, struct sockaddr_in *sin,
+ struct sip_request *req, char *uri)
+{
+ enum check_auth_result res = AUTH_NOT_FOUND;
struct sip_peer *peer;
char tmp[256];
char iabuf[INET_ADDRSTRLEN];
@@ -6861,7 +6879,7 @@
if (!AST_LIST_EMPTY(&domain_list)) {
if (!check_sip_domain(domain, NULL, 0)) {
transmit_response(p, "404 Not found (unknown domain)", &p->initreq);
- return -3;
+ return AUTH_UNKNOWN_DOMAIN;
}
}
}
@@ -6938,28 +6956,46 @@
}
if (res < 0) {
switch (res) {
- case -1:
+ 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);
break;
- case -2:
+ case AUTH_USERNAME_MISMATCH:
/* Username and digest username does not match.
Asterisk uses the From: username for authentication. We need the
users to use the same authentication user name until we support
proper authentication by digest auth name */
transmit_response(p, "403 Authentication user name does not match account name", &p->initreq);
break;
- case -3:
- /* URI not found */
- transmit_response(p, "404 Not found", &p->initreq);
- /* Set res back to -2 because we don't want to return an invalid domain message. That check already happened up above. */
- res = -2;
+ case AUTH_NOT_FOUND:
+ if (global_alwaysauthreject) {
+ transmit_fake_auth_response(p, &p->initreq, 1);
+ } else {
+ /* URI not found */
+ transmit_response(p, "404 Not found", &p->initreq);
+ }
break;
+ default:
+ break;
}
if (option_debug > 1) {
+ const char *reason = "";
+
+ switch (res) {
+ case AUTH_SECRET_FAILED:
+ reason = "Bad password";
+ break;
+ case AUTH_USERNAME_MISMATCH:
+ reason = "Bad digest user";
+ break;
+ case AUTH_NOT_FOUND:
+ reason = "Peer not found";
+ break;
+ default:
+ break;
+ }
ast_log(LOG_DEBUG, "SIP REGISTER attempt failed for %s : %s\n",
- peer->name,
- (res == -1) ? "Bad password" : ((res == -2 ) ? "Bad digest user" : "Peer not found"));
+ peer->name, reason);
}
}
if (peer)
@@ -7455,10 +7491,11 @@
/*! \brief Check if matching user or peer is defined
Match user on From: user name and peer on IP/port
This is used on first invite (not re-invites) and subscribe requests
- \return 0 on success, -1 on failure, and 1 on challenge sent
- -2 on authentication error from chedck_auth()
+ \return 0 on success, non-zero on failure
*/
-static int check_user_full(struct sip_pvt *p, struct sip_request *req, int sipmethod, char *uri, enum xmittype reliable, struct sockaddr_in *sin, struct sip_peer **authpeer)
+static enum check_auth_result check_user_full(struct sip_pvt *p, struct sip_request *req,
+ int sipmethod, char *uri, enum xmittype reliable,
+ struct sockaddr_in *sin, struct sip_peer **authpeer)
{
struct sip_user *user = NULL;
struct sip_peer *peer;
@@ -7467,7 +7504,7 @@
char rpid_num[50];
const char *rpid;
char iabuf[INET_ADDRSTRLEN];
- int res = 0;
+ enum check_auth_result res = AUTH_SUCCESSFUL;
char *t;
char calleridname[50];
int debug=sip_debug_test_addr(sin);
@@ -7523,7 +7560,7 @@
ast_string_field_set(p, cid_num, tmp);
}
if (ast_strlen_zero(of))
- return 0;
+ return AUTH_SUCCESSFUL;
if (!authpeer) /* If we are looking for a peer, don't check the user objects (or realtime) */
user = find_user(of, 1);
@@ -7550,7 +7587,6 @@
ast_shrink_phone_number(tmp);
ast_string_field_set(p, cid_num, tmp);
}
-
usenatroute = ast_test_flag(&p->flags[0], SIP_NAT_ROUTE);
@@ -7742,8 +7778,12 @@
ast_verbose("Found no matching peer or user for '%s:%d'\n", ast_inet_ntoa(iabuf, sizeof(iabuf), p->recv.sin_addr), ntohs(p->recv.sin_port));
/* do we allow guests? */
- if (!global_allowguest)
- res = -1; /* we don't want any guests, authentication will fail */
+ if (!global_allowguest) {
+ if (global_alwaysauthreject)
+ res = AUTH_FAKE_AUTH; /* reject with fake authorization request */
+ else
+ res = AUTH_SECRET_FAILED; /* we don't want any guests, authentication will fail */
+ }
}
}
@@ -8789,6 +8829,7 @@
ast_cli(fd, " URI user is phone no: %s\n", ast_test_flag(&global_flags[0], SIP_USEREQPHONE) ? "Yes" : "No");
ast_cli(fd, " Our auth realm %s\n", global_realm);
ast_cli(fd, " Realm. auth: %s\n", authl ? "Yes": "No");
+ ast_cli(fd, " Always auth rejects: %s\n", global_alwaysauthreject ? "Yes" : "No");
ast_cli(fd, " User Agent: %s\n", global_useragent);
ast_cli(fd, " MWI checking interval: %d secs\n", global_mwitime);
ast_cli(fd, " Reg. context: %s\n", S_OR(global_regcontext, "(not set)"));
@@ -11248,11 +11289,16 @@
/* Handle authentication if this is our first invite */
res = check_user(p, req, SIP_INVITE, e, XMIT_RELIABLE, sin);
- if (res > 0) /* We have challenged the user for auth */
+ if (res == AUTH_CHALLENGE_SENT)
return 0;
if (res < 0) { /* Something failed in authentication */
- ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
- transmit_response_reliable(p, "403 Forbidden", req);
+ if (res == AUTH_FAKE_AUTH) {
+ ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
+ transmit_fake_auth_response(p, req, 1);
+ } else {
+ ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From"));
+ transmit_response_reliable(p, "403 Forbidden", req);
+ }
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
ast_string_field_free(p, theirtag);
return 0;
@@ -11678,11 +11724,18 @@
/* Handle authentication if this is our first subscribe */
res = check_user_full(p, req, SIP_SUBSCRIBE, e, 0, sin, &authpeer);
- if (res) {
- if (res < 0) {
+ /* if an authentication response was sent, we are done here */
+ if (res == AUTH_CHALLENGE_SENT)
+ return 0;
+ if (res < 0) {
+ if (res == AUTH_FAKE_AUTH) {
+ ast_log(LOG_NOTICE, "Sending fake auth rejection for user %s\n", get_header(req, "From"));
+ transmit_fake_auth_response(p, req, 1);
+ } else {
ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From"));
- ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
- }
+ transmit_response_reliable(p, "403 Forbidden", req);
+ }
+ ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
return 0;
}
@@ -11856,7 +11909,7 @@
/*! \brief Handle incoming REGISTER request */
static int handle_request_register(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, char *e)
{
- int res = 0;
+ enum check_auth_result res;
char iabuf[INET_ADDRSTRLEN];
/* Use this as the basis */
@@ -11864,12 +11917,33 @@
ast_verbose("Using latest REGISTER request as basis request\n");
copy_request(&p->initreq, req);
check_via(p, req);
- if ((res = register_verify(p, sin, req, e)) < 0)
- ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n", get_header(req, "To"), ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr), (res == -1) ? "Wrong password" : (res == -2 ? "Username/auth name mismatch" : "Not a local SIP domain"));
+ if ((res = register_verify(p, sin, req, e)) < 0) {
+ const char *reason = "";
+
+ switch (res) {
+ case AUTH_SECRET_FAILED:
+ reason = "Wrong password";
+ break;
+ case AUTH_USERNAME_MISMATCH:
+ reason = "Username/auth name mismatch";
+ break;
+ case AUTH_NOT_FOUND:
+ reason = "No matching peer found";
+ break;
+ case AUTH_UNKNOWN_DOMAIN:
+ reason = "Not a local domain";
+ break;
+ default:
+ break;
+ }
+ ast_log(LOG_NOTICE, "Registration from '%s' failed for '%s' - %s\n",
+ get_header(req, "To"), ast_inet_ntoa(iabuf, sizeof(iabuf), sin->sin_addr),
+ reason);
+ }
if (res < 1) {
/* Destroy the session, but keep us around for just a bit in case they don't
get our 200 OK */
- sip_scheddestroy(p, 15*1000);
+ sip_scheddestroy(p, 15000);
}
return res;
}
@@ -13392,6 +13466,7 @@
global_regcontext[0] = '\0';
expiry = DEFAULT_EXPIRY;
global_notifyringing = DEFAULT_NOTIFYRINGING;
+ global_alwaysauthreject = 0;
global_allowsubscribe = FALSE;
ast_copy_string(global_useragent, DEFAULT_USERAGENT, sizeof(global_useragent));
ast_copy_string(default_notifymime, DEFAULT_NOTIFYMIME, sizeof(default_notifymime));
@@ -13503,6 +13578,8 @@
ast_copy_string(default_notifymime, v->value, sizeof(default_notifymime));
} else if (!strcasecmp(v->name, "notifyringing")) {
global_notifyringing = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "alwaysauthreject")) {
+ global_alwaysauthreject = ast_true(v->value);
} else if (!strcasecmp(v->name, "musicclass") || !strcasecmp(v->name, "musiconhold")) {
ast_copy_string(default_musicclass, v->value, sizeof(default_musicclass));
} else if (!strcasecmp(v->name, "language")) {
Modified: trunk/configs/sip.conf.sample
URL: http://svn.digium.com/view/asterisk/trunk/configs/sip.conf.sample?rev=29903&r1=29902&r2=29903&view=diff
==============================================================================
--- trunk/configs/sip.conf.sample (original)
+++ trunk/configs/sip.conf.sample Tue May 23 22:28:49 2006
@@ -136,6 +136,10 @@
; Useful to limit subscriptions to local extensions
; Settable per peer/user also
;notifyringing = yes ; Notify subscriptions on RINGING state
+;alwaysauthreject = yes ; When an incoming INVITE or REGISTER is to be rejected,
+ ; for any reason, always reject with '401 Unauthorized'
+ ; instead of letting the requester know whether there was
+ ; a matching user or peer for their request
;
; If regcontext is specified, Asterisk will dynamically create and destroy a
; NoOp priority 1 extension for a given peer who registers or unregisters with
More information about the asterisk-commits
mailing list