summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Spencer <markster@digium.com>2005-03-24 23:06:21 +0000
committerMark Spencer <markster@digium.com>2005-03-24 23:06:21 +0000
commit03f5290b89d04a04254ab73a7e7c7da68dd8f768 (patch)
tree6a4327ccdca6bce451eaecd41062d3f87b44634d
parent78d9eee14173cfd9f2ffb2a448319563913773d3 (diff)
Add SIP real authentication (bug #3782)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5256 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rwxr-xr-xchannels/chan_sip.c178
-rwxr-xr-xconfigs/sip.conf.sample17
2 files changed, 189 insertions, 6 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 236b5bad5..6b4a0b06e 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -291,11 +291,21 @@ struct sip_route {
char hop[0];
};
+/* sip_history: Structure for saving transactions within a SIP dialog */
struct sip_history {
char event[80];
struct sip_history *next;
};
+/* sip_auth: Creadentials for authentication to other SIP services */
+struct sip_auth {
+ char realm[AST_MAX_EXTENSION]; /* Realm in which these credentials are valid */
+ char username[256]; /* Username */
+ char secret[256]; /* Secret */
+ char md5secret[256]; /* MD5Secret */
+ struct sip_auth *next; /* Next auth structure in list */
+};
+
#define SIP_ALREADYGONE (1 << 0) /* Whether or not we've already been destroyed by our peer */
#define SIP_NEEDDESTROY (1 << 1) /* if we need to be destroyed */
#define SIP_NOVIDEO (1 << 2) /* Didn't get video in invite, don't offer */
@@ -410,6 +420,7 @@ static struct sip_pvt {
char okcontacturi[256]; /* URI from the 200 OK on INVITE */
char peersecret[256]; /* Password */
char peermd5secret[256];
+ struct sip_auth *peerauth; /* Realm authentication */
char cid_num[256]; /* Caller*ID */
char cid_name[256]; /* Caller*ID */
char via[256]; /* Via: header */
@@ -505,6 +516,7 @@ struct sip_peer {
/* peer->name is the unique name of this object */
char secret[80]; /* Password */
char md5secret[80]; /* Password in MD5 */
+ struct sip_auth *auth; /* Realm authentication list */
char context[80]; /* Default context for incoming calls */
char username[80]; /* Temporary username until registration */
char accountcode[20]; /* Account code */
@@ -630,6 +642,9 @@ static struct ast_ha *localaddr;
/* The list of manual NOTIFY types we know how to send */
struct ast_config *notify_types;
+static struct sip_auth *authl; /* Authentication list */
+
+
static struct ast_frame *sip_read(struct ast_channel *ast);
static int transmit_response(struct sip_pvt *p, char *msg, struct sip_request *req);
static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_request *req, int retrans);
@@ -667,6 +682,9 @@ static int sip_transfer(struct ast_channel *ast, char *dest);
static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int sip_senddigit(struct ast_channel *ast, char digit);
static int sip_sendtext(struct ast_channel *ast, char *text);
+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, char *realm); /* Find authentication for a specific realm */
/* Definition of this channel for channel registration */
static const struct ast_channel_tech sip_tech = {
@@ -716,6 +734,7 @@ static inline int sip_debug_test_addr(struct sockaddr_in *addr)
return 1;
}
+/*--- sip_debug_test_pvt: Test PVT for debugging output */
static inline int sip_debug_test_pvt(struct sip_pvt *p)
{
if (sipdebug == 0)
@@ -1234,6 +1253,8 @@ static void sip_destroy_peer(struct sip_peer *peer)
rpeerobjs--;
else
speerobjs--;
+ clear_realm_authentication(peer->auth);
+ peer->auth = (struct sip_auth *) NULL;
free(peer);
}
@@ -6513,6 +6534,7 @@ static int _sip_show_peer(int type, int fd, struct mansession *s, struct message
char codec_buf[512];
struct ast_codec_pref *pref;
struct ast_variable *v;
+ struct sip_auth *auth;
int x = 0, codec = 0, load_realtime = 0;
if (argc < 4)
@@ -6536,6 +6558,12 @@ static int _sip_show_peer(int type, int fd, struct mansession *s, struct message
ast_cli(fd, " * Name : %s\n", peer->name);
ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"<Not set>":"<Set>");
ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"<Not set>":"<Set>");
+ auth = peer->auth;
+ while(auth) {
+ ast_cli(fd, " Realm-auth : Realm %-15.15s User %-10.20s ", auth->realm, auth->username);
+ ast_cli(fd, "%s\n", !ast_strlen_zero(auth->secret)?"<Secret set>":(!ast_strlen_zero(auth->md5secret)?"<MD5secret set>" : "<Not set>"));
+ auth = auth->next;
+ }
ast_cli(fd, " Context : %s\n", peer->context);
ast_cli(fd, " Language : %s\n", peer->language);
if (!ast_strlen_zero(peer->accountcode))
@@ -7458,6 +7486,10 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
char uri[256] = "";
char cnonce[80];
char iabuf[INET_ADDRSTRLEN];
+ char *username;
+ char *secret;
+ char *md5secret;
+ struct sip_auth *auth = (struct sip_auth *) NULL; /* Realm authentication */
if (!ast_strlen_zero(p->domain))
strncpy(uri, p->domain, sizeof(uri) - 1);
@@ -7468,10 +7500,25 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
snprintf(cnonce, sizeof(cnonce), "%08x", rand());
- snprintf(a1,sizeof(a1),"%s:%s:%s",p->authname,p->realm,p->peersecret);
+ /* Check if we have separate auth credentials */
+ if ((auth = find_realm_authentication(authl, p->realm))) {
+ username = auth->username;
+ secret = auth->secret;
+ md5secret = auth->md5secret;
+ ast_log(LOG_NOTICE,"Using realm %s authentication for this call\n", p->realm);
+ } else {
+ /* No authentication, use peer or register= config */
+ username = p->authname;
+ secret = p->peersecret;
+ md5secret = p->peermd5secret;
+ }
+
+
+ /* Calculate SIP digest response */
+ snprintf(a1,sizeof(a1),"%s:%s:%s",username,p->realm,secret);
snprintf(a2,sizeof(a2),"%s:%s", sip_methods[method].text, uri);
- if (!ast_strlen_zero(p->peermd5secret))
- strncpy(a1_hash, p->peermd5secret, sizeof(a1_hash) - 1);
+ if (!ast_strlen_zero(md5secret))
+ strncpy(a1_hash, md5secret, sizeof(a1_hash) - 1);
else
ast_md5_hash(a1_hash,a1);
ast_md5_hash(a2_hash,a2);
@@ -7484,9 +7531,9 @@ static int build_reply_digest(struct sip_pvt *p, int method, char* digest, int d
ast_md5_hash(resp_hash,resp);
/* XXX We hard code our qop to "auth" for now. XXX */
if (!ast_strlen_zero(p->qop))
- snprintf(digest,digest_len,"Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\", qop=\"%s\", cnonce=\"%s\", nc=%s",p->authname,p->realm,uri,p->nonce,resp_hash, p->opaque, "auth", cnonce, "00000001");
+ snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\", qop=\"%s\", cnonce=\"%s\", nc=%s", username, p->realm, uri, p->nonce, resp_hash, p->opaque, "auth", cnonce, "00000001");
else
- snprintf(digest,digest_len,"Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\"",p->authname,p->realm,uri,p->nonce,resp_hash, p->opaque);
+ snprintf(digest, digest_len, "Digest username=\"%s\", realm=\"%s\", algorithm=MD5, uri=\"%s\", nonce=\"%s\", response=\"%s\", opaque=\"%s\"", username, p->realm, uri, p->nonce, resp_hash, p->opaque);
return 0;
}
@@ -9525,6 +9572,105 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
return res;
}
+/*--- add_realm_authentication: Add realm authentication in list ---*/
+static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno)
+{
+ char authcopy[256] = "";
+ char *username=NULL, *realm=NULL, *secret=NULL, *md5secret=NULL;
+ char *stringp;
+ struct sip_auth *auth;
+ struct sip_auth *b = NULL, *a = authlist;
+
+ if (!configuration || ast_strlen_zero(configuration))
+ return (authlist);
+
+ ast_log(LOG_DEBUG, "Auth config :: %s\n", configuration);
+
+ strncpy(authcopy, configuration, sizeof(authcopy)-1);
+ stringp = authcopy;
+
+ username = stringp;
+ realm = strrchr(stringp, '@');
+ if (realm) {
+ *realm = '\0';
+ realm++;
+ }
+ if (!username || ast_strlen_zero(username) || !realm || ast_strlen_zero(realm)) {
+ ast_log(LOG_WARNING, "Format for authentication entry is user[:secret]@realm at line %d", lineno);
+ return (authlist);
+ }
+ stringp = username;
+ username = strsep(&stringp, ":");
+ if (username) {
+ secret = strsep(&stringp, ":");
+ if (!secret) {
+ stringp = username;
+ md5secret = strsep(&stringp,"#");
+ }
+ }
+ auth = malloc(sizeof(struct sip_auth));
+ if (auth) {
+ memset(auth, 0, sizeof(struct sip_auth));
+ strncpy(auth->realm, realm, sizeof(auth->realm)-1);
+ strncpy(auth->username, username, sizeof(auth->username)-1);
+ if (secret)
+ strncpy(auth->secret, secret, sizeof(auth->secret)-1);
+ if (md5secret)
+ strncpy(auth->md5secret, md5secret, sizeof(auth->md5secret)-1);
+ } else {
+ ast_log(LOG_ERROR, "Allocation of auth structure failed, Out of memory\n");
+ return (authlist);
+ }
+
+ /* Add authentication to authl */
+ if (!authlist) { /* No existing list */
+ return(auth);
+ } else {
+ while(a) {
+ b = a;
+ a = a->next;
+ }
+ b->next = auth; /* Add structure add end of list */
+ }
+
+ if (option_verbose > 2)
+ ast_verbose("Added authentication for realm %s\n", realm);
+
+ return(authlist);
+
+}
+
+/*--- clear_realm_authentication: Clear realm authentication list (at reload) ---*/
+static int clear_realm_authentication(struct sip_auth *authlist)
+{
+ struct sip_auth *a = authlist;
+ struct sip_auth *b;
+
+ while(a) {
+ b = a;
+ a = a->next;
+ free(b);
+ }
+
+
+ return(1);
+}
+
+/*--- find_realm_authentication: Find authentication for a specific realm ---*/
+static struct sip_auth *find_realm_authentication(struct sip_auth *authlist, char *realm)
+{
+ struct sip_auth *a = authlist; /* First entry in auth list */
+
+ while (a) {
+ if (!strcasecmp(a->realm, realm)){
+ break;
+ }
+ a = a->next;
+ }
+
+ return(a);
+}
+
/*--- build_user: Initiate a SIP user structure from sip.conf ---*/
static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime)
{
@@ -9754,6 +9900,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int
strncpy(peer->secret, v->value, sizeof(peer->secret)-1);
else if (!strcasecmp(v->name, "md5secret"))
strncpy(peer->md5secret, v->value, sizeof(peer->md5secret)-1);
+ else if (!strcasecmp(v->name, "auth"))
+ peer->auth = add_realm_authentication(peer->auth, v->value, v->lineno);
else if (!strcasecmp(v->name, "callerid")) {
ast_callerid_split(v->value, peer->cid_name, sizeof(peer->cid_name), peer->cid_num, sizeof(peer->cid_num));
} else if (!strcasecmp(v->name, "context"))
@@ -10166,10 +10314,21 @@ static int reload_config(void)
v = v->next;
}
+ /* Build list of authentication to various SIP realms, i.e. service providers */
+ v = ast_variable_browse(cfg, "authentication");
+ while(v) {
+ /* Format for authentication is auth = username:password@realm */
+ if (!strcasecmp(v->name, "auth")) {
+ authl = add_realm_authentication(authl, v->value, v->lineno);
+ }
+ v = v->next;
+ }
+
+
/* Load peers, users and friends */
cat = ast_category_browse(cfg, NULL);
while(cat) {
- if (strcasecmp(cat, "general")) {
+ if (strcasecmp(cat, "general") && strcasecmp(cat, "authentication")) {
utype = ast_variable_retrieve(cfg, cat, "type");
if (utype) {
if (!strcasecmp(utype, "user") || !strcasecmp(utype, "friend")) {
@@ -10611,6 +10770,9 @@ static void sip_send_all_registers(void)
/*--- sip_do_reload: Reload module */
static int sip_do_reload(void)
{
+ clear_realm_authentication(authl);
+ authl = (struct sip_auth *) NULL;
+
delete_users();
reload_config();
prune_peers();
@@ -10735,6 +10897,8 @@ int unload_module()
ast_manager_unregister("SIPpeers");
ast_manager_unregister("SIPshowpeer");
ast_channel_unregister(&sip_tech);
+
+
if (!ast_mutex_lock(&iflock)) {
/* Hangup all interfaces if they have an owner */
p = iflist;
@@ -10787,6 +10951,8 @@ int unload_module()
ASTOBJ_CONTAINER_DESTROY(&userl);
ASTOBJ_CONTAINER_DESTROY(&peerl);
ASTOBJ_CONTAINER_DESTROY(&regl);
+
+ clear_realm_authentication(authl);
return 0;
}
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 16230850e..c1926c179 100755
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -172,6 +172,23 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; same as the registration interval
+[authentication]
+; Global credentials for outbound calls, i.e. when a proxy challenges your
+; Asterisk server for authentication. These credentials override
+; any credentials in peer/register definition if realm is matched.
+;
+; This way, Asterisk can authenticate for outbound calls to other
+; realms. We match realm on the proxy challenge and pick an set of
+; credentials from this list
+; Syntax:
+; auth = <user>:<secret>@<realm>
+; auth = <user>#<md5secret>@<realm>
+; Example:
+;auth=mark:topsecret@digium.com
+;
+; You may also add auth= statements to [peer] definitions
+; Peer auth= override all other authentication settings if we match on realm
+
;-----------------------------------------------------------------------------------
; Users and peers have different settings available. Friends have all settings,
; since a friend is both a peer and a user