Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.400 diff -u -r1.400 chan_sip.c --- channels/chan_sip.c 27 May 2004 05:06:32 -0000 1.400 +++ channels/chan_sip.c 27 May 2004 17:48:16 -0000 @@ -229,11 +229,22 @@ char hop[0]; }; +/* sip_history: Structure for saving transactions within a SIP dialog */ struct sip_history { - char event[80]; + char event[80]; /* Event code and direction */ 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 */ +}; + + /* sip_pvt: PVT structures are used for each SIP conversation, ie. a call */ static struct sip_pvt { ast_mutex_t lock; /* Channel private lock */ @@ -293,6 +304,7 @@ char uri[256]; /* Original requested URI */ char peersecret[256]; char peermd5secret[256]; + struct sip_auth *peerauth; /* Realm Authentication (from peer) */ char callerid[256]; /* Caller*ID */ int restrictcid; /* hide presentation from remote user */ char via[256]; @@ -378,42 +390,43 @@ /* Structure for SIP peer data, we place calls to peers if registred or fixed IP address (host) */ struct sip_peer { - char name[80]; - char secret[80]; - char md5secret[80]; - char context[80]; /* JK02: peers need context too to allow parking etc */ - char username[80]; - char tohost[80]; - char fromuser[80]; - char fromdomain[80]; - char mailbox[AST_MAX_EXTENSION]; - char language[MAX_LANGUAGE]; + char name[80]; /* Peer [name] in sip.conf */ + char secret[80]; /* Secret, unencrypted, for digest auth */ + char md5secret[80]; /* MD5 encoded secret */ + struct sip_auth *auth; /* Realm authentication list */ + char context[80]; /* Context too to allow parking etc */ + char username[80]; /* Username */ + char tohost[80]; /* IP address (host= option in sip.conf) */ + char fromuser[80]; /* From: user for outbound calling */ + char fromdomain[80]; /* From: domain part */ + char mailbox[AST_MAX_EXTENSION]; /* Voicemail mailbox@vm-context for notification */ + char language[MAX_LANGUAGE]; /* Language for voice prompts */ char musicclass[MAX_LANGUAGE]; /* Music on Hold class */ char useragent[256]; /* User agent in SIP request */ int lastmsgssent; time_t lastmsgcheck; - int dynamic; + int dynamic; /* Whether or not allowed to register */ int expire; int expiry; - int capability; + int capability; /* Codecs we support for this peer */ int insecure; - int nat; - int canreinvite; - unsigned int callgroup; - unsigned int pickupgroup; - int dtmfmode; - struct sockaddr_in addr; + int nat; /* NAT support for SIP & RTP */ + int canreinvite; /* If we can let the media stream go */ + unsigned int callgroup; /* Call groups */ + unsigned int pickupgroup; /* Pickup groups */ + int dtmfmode; /* DTMF mode for sending TO this peer */ + struct sockaddr_in addr; /* Peers IPv4 location */ struct in_addr mask; - /* Qualification */ + /* Qualification (if qualify= in sip.conf) */ struct sip_pvt *call; /* Call pointer */ - int pokeexpire; /* When to expire poke */ - int lastms; /* How long last response took (in ms), or -1 for no response */ - int maxms; /* Max ms we will accept for the host to be up, 0 to not monitor */ - struct timeval ps; /* Ping send time */ + int pokeexpire; /* When to expire poke */ + int lastms; /* How long last response took (in ms), or -1 for no response */ + int maxms; /* Max ms we will accept for the host to be up, 0 to not monitor */ + struct timeval ps; /* Ping send time */ - struct sockaddr_in defaddr; - struct ast_ha *ha; + struct sockaddr_in defaddr; /* Default IP until registration */ + struct ast_ha *ha; /* ACL */ int delme; int selfdestruct; int lastmsg; @@ -488,6 +501,9 @@ static struct sockaddr_in externip; static struct ast_ha *localaddr; +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); @@ -508,6 +524,10 @@ static int sip_do_reload(void); static int sip_debug_test_addr(struct sockaddr_in *addr); static int sip_debug_test_pvt(struct sip_pvt *p); +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 */ + /*--- __sip_xmit: Transmit SIP message ---*/ static int __sip_xmit(struct sip_pvt *p, char *data, int len) @@ -5078,6 +5098,7 @@ { char status[30]; struct sip_peer *peer; + struct sip_auth *auth; if (argc != 4) return RESULT_SHOWUSAGE; @@ -5088,6 +5109,12 @@ ast_cli(fd, " * Name : %s\n", peer->name); ast_cli(fd, " Secret : %s\n", ast_strlen_zero(peer->secret)?"":""); ast_cli(fd, " MD5Secret : %s\n", ast_strlen_zero(peer->md5secret)?"":""); + 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)?"":(!ast_strlen_zero(auth->md5secret)?"" : "")); + auth=auth->next; + } ast_cli(fd, " Context : %s\n", peer->context); ast_cli(fd, " Language : %s\n", peer->language); ast_cli(fd, " FromUser : %s\n", peer->fromuser); @@ -5563,7 +5590,7 @@ return transmit_register(p->registry,"REGISTER",digest, respheader); } -/*--- do_proxy_auth: Challenge user ---*/ +/*--- do_proxy_auth: Respons to challenge from service ---*/ static int do_proxy_auth(struct sip_pvt *p, struct sip_request *req, char *header, char *respheader, char *msg, int init) { char digest[1024]; p->authtries++; @@ -5684,6 +5711,10 @@ char resp_hash[256]; char uri[256] = ""; char cnonce[80]; + char *username; + char *secret; + char *md5secret; + struct sip_auth *auth = (struct sip_auth *) NULL; /* Authentication for realms */ if (!ast_strlen_zero(p->domain)) strncpy(uri, p->domain, sizeof(uri) - 1); @@ -5694,10 +5725,25 @@ 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",orig_header,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 md5_hash(a1_hash,a1); md5_hash(a2_hash,a2); @@ -5710,9 +5756,9 @@ 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; } @@ -7136,6 +7182,101 @@ return tmpc; } +/*--- add_realm_authentication: Add realm authentication in list ---*/ +static struct sip_auth *add_realm_authentication(struct sip_auth *authlist, char *configuration, int lineno) +{ + char copy[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) + return (authlist); + ast_log(LOG_DEBUG, "Auth config :: %s\n", configuration); + + strncpy(copy, configuration, sizeof(copy)-1); + stringp=copy; + + 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); + } + /* Lägg auth till authl */ + if (!authlist) { + return(auth); + } else { + while(a) { + b = a; + a = a->next; + } + b->next = auth; /* Add structure add end of list */ + } + + ast_log(LOG_NOTICE, "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(char *name, struct ast_variable *v) { @@ -7625,9 +7766,22 @@ v = v->next; } + + + /* Build list of authentication to various 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; + } + + /* Now, build peers, friends and users */ 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")) { @@ -7899,6 +8053,9 @@ ast_sched_del(sched, peer->expire); if (peer->pokeexpire > -1) ast_sched_del(sched, peer->pokeexpire); + clear_realm_authentication(peer->auth); + if(peer->ha) + ast_free_ha(peer->ha); free(peer); if (peerlast) peerlast->next = peernext; @@ -7916,8 +8073,10 @@ { struct sip_registry *reg; struct sip_peer *peer; + delete_users(); reload_config(); + clear_realm_authentication(authl); prune_peers(); /* And start the monitor for the first time */ @@ -8042,6 +8201,9 @@ ast_cli_unregister(&cli_inuse_show); ast_rtp_proto_unregister(&sip_rtp); ast_channel_unregister(type); + + clear_realm_authentication(authl); + if (!ast_mutex_lock(&iflock)) { /* Hangup all interfaces if they have an owner */ p = iflist; Index: configs/sip.conf.sample =================================================================== RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v retrieving revision 1.28 diff -u -r1.28 sip.conf.sample --- configs/sip.conf.sample 24 May 2004 15:09:34 -0000 1.28 +++ configs/sip.conf.sample 27 May 2004 17:48:16 -0000 @@ -129,6 +129,22 @@ ; qualify ; defaultip +[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 = :@ +; auth = #@ +; Example: +;auth=mark:topsecret@digium.com +; +; You may also add auth= statements to [peer] definitions +; Peer auth= override all other settings if we match on realm ;[sip_proxy] ; For incoming calls only. Example: FWD (Free World Dialup)