diff options
-rw-r--r-- | channels/chan_sip.c | 249 |
1 files changed, 108 insertions, 141 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 9724fea3e..4aade99dc 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -897,7 +897,7 @@ struct ast_config *notify_types; /*!< The list of manual NOTIFY types we know h 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); static int transmit_response_with_unsupported(struct sip_pvt *p, char *msg, struct sip_request *req, char *unsupported); -static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, const char *rand, int reliable, char *header, int stale); +static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, struct sip_request *req, const char *rand, int reliable, const char *header, int stale); static int transmit_request(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch); static int transmit_request_with_auth(struct sip_pvt *p, int sipmethod, int inc, int reliable, int newbranch); static int transmit_invite(struct sip_pvt *p, int sipmethod, int sendsdp, int init); @@ -916,7 +916,6 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int static struct sip_user *build_user(const char *name, struct ast_variable *v, int realtime); static int sip_do_reload(enum channelreloadreason reason); static int expire_register(void *data); - static struct ast_channel *sip_request_call(const char *type, int format, void *data, int *cause); static int sip_devicestate(void *data); static int sip_sendtext(struct ast_channel *ast, const char *text); @@ -932,6 +931,9 @@ static int sip_senddigit(struct ast_channel *ast, char digit); 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, int reliable, int ignore); static int check_sip_domain(const char *domain, char *context, size_t len); /* Check if domain is one of our local domains */ static void append_date(struct sip_request *req); /* Append date to SIP packet */ static int determine_firstline_parts(struct sip_request *req); @@ -943,7 +945,7 @@ static int find_sip_method(char *msg); static unsigned int parse_sip_options(struct sip_pvt *pvt, char *supported); static void sip_destroy(struct sip_pvt *p); static void parse_request(struct sip_request *req); -static char *get_header(struct sip_request *req, char *name); +static char *get_header(struct sip_request *req, const char *name); static void copy_request(struct sip_request *dst,struct sip_request *src); static int transmit_response_reliable(struct sip_pvt *p, char *msg, struct sip_request *req, int fatal); static int transmit_register(struct sip_registry *r, int sipmethod, char *auth, char *authheader); @@ -2968,7 +2970,7 @@ static char *find_alias(const char *name, char *_default) return _default; } -static char *__get_header(struct sip_request *req, char *name, int *start) +static char *__get_header(struct sip_request *req, const char *name, int *start) { int pass; @@ -3004,7 +3006,7 @@ static char *__get_header(struct sip_request *req, char *name, int *start) } /*! \brief Get header from SIP request */ -static char *get_header(struct sip_request *req, char *name) +static char *get_header(struct sip_request *req, const char *name) { int start = 0; return __get_header(req, name, &start); @@ -4043,7 +4045,7 @@ static void set_destination(struct sip_pvt *p, char *uri) } /*! \brief Initialize SIP response, based on SIP request */ -static int init_resp(struct sip_request *req, char *resp, struct sip_request *orig) +static int init_resp(struct sip_request *req, const char *resp, struct sip_request *orig) { /* Initialize a response */ if (req->headers || req->len) { @@ -4076,7 +4078,7 @@ static int init_req(struct sip_request *req, int sipmethod, const char *recip) /*! \brief Prepare SIP response packet */ -static int respprep(struct sip_request *resp, struct sip_pvt *p, char *msg, struct sip_request *req) +static int respprep(struct sip_request *resp, struct sip_pvt *p, const char *msg, struct sip_request *req) { char newto[256], *ot; @@ -4307,7 +4309,7 @@ static int transmit_response_with_allow(struct sip_pvt *p, char *msg, struct sip } /*! \brief Respond with authorization request */ -static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_request *req, const char *randdata, int reliable, char *header, int stale) +static int transmit_response_with_auth(struct sip_pvt *p, const char *msg, struct sip_request *req, const char *randdata, int reliable, const char *header, int stale) { struct sip_request resp; char tmp[256]; @@ -6182,19 +6184,19 @@ static int check_osptoken (struct sip_pvt *p, char *token) /*! \brief Check user authorization from peer definition Some actions, like REGISTER and INVITEs from peers require - authentication (if peer have secret set) */ + authentication (if peer have secret set) + \return -1 on Error, 0 on success, 1 on challenge sent + +*/ 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, int reliable, int ignore) { - int res = -1; - char *response = "407 Proxy Authentication Required"; - char *reqheader = "Proxy-Authorization"; - char *respheader = "Proxy-Authenticate"; - char *authtoken; -#ifdef OSP_SUPPORT - char *osptoken; -#endif + const char *response = "407 Proxy Authentication Required"; + const char *reqheader = "Proxy-Authorization"; + const char *respheader = "Proxy-Authenticate"; + const char *authtoken; + /* Always OK if no secret */ if (ast_strlen_zero(secret) && ast_strlen_zero(md5secret) #ifdef OSP_SUPPORT @@ -6213,36 +6215,30 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, const char *us } #ifdef OSP_SUPPORT else { - ast_log (LOG_DEBUG, "Checking OSP Authentication!\n"); + char *osptoken; + if (option_debug) + ast_log (LOG_DEBUG, "Checking OSP Authentication!\n"); osptoken = get_header (req, "P-OSP-Auth-Token"); switch (ast_test_flag (p, SIP_OSPAUTH)) { case SIP_OSPAUTH_NO: break; case SIP_OSPAUTH_GATEWAY: - if (ast_strlen_zero (osptoken)) { - if (ast_strlen_zero (secret) && ast_strlen_zero (md5secret)) { + if (ast_strlen_zero(osptoken)) { + if (ast_strlen_zero(secret) && ast_strlen_zero (md5secret)) return 0; - } - } - else { - return check_osptoken (p, osptoken); + } else { + return check_osptoken(p, osptoken); } break; case SIP_OSPAUTH_PROXY: - if (ast_strlen_zero (osptoken)) { + if (ast_strlen_zero(osptoken)) return 0; - } - else { - return check_osptoken (p, osptoken); - } + return check_osptoken(p, osptoken); break; case SIP_OSPAUTH_EXCLUSIVE: - if (ast_strlen_zero (osptoken)) { + if (ast_strlen_zero(osptoken)) return -1; - } - else { - return check_osptoken (p, osptoken); - } + return check_osptoken(p, osptoken); break; default: return -1; @@ -6253,134 +6249,108 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, const char *us if (ignore && !ast_strlen_zero(p->randdata) && ast_strlen_zero(authtoken)) { /* This is a retransmitted invite/register/etc, don't reconstruct authentication information */ - if (!ast_strlen_zero(p->randdata)) { - if (!reliable) { - /* Resend message if this was NOT a reliable delivery. Otherwise the - retransmission should get it */ - transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); - /* Schedule auto destroy in 15 seconds */ - sip_scheddestroy(p, 15000); - } - res = 1; - } + if (!reliable) { + /* Resend message if this was NOT a reliable delivery. Otherwise the + retransmission should get it */ + transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); + /* Schedule auto destroy in 32 seconds (according to RFC 3261) */ + sip_scheddestroy(p, 32000); + } + return 1; /* Auth sent */ } else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) { - ast_string_field_build(p, randdata, "%08x", thread_safe_rand()); + /* We have no auth, so issue challenge and request authentication */ + ast_string_field_build(p, randdata, "%08x", thread_safe_rand()); /* Create nonce for challenge */ transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); - /* Schedule auto destroy in 15 seconds */ - sip_scheddestroy(p, 15000); - res = 1; - } else { + /* Schedule auto destroy in 32 seconds */ + sip_scheddestroy(p, 32000); + return 1; /* Auth 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. */ - char a1[256]; - char a2[256]; + an example in the spec of just what it is you're doing a hash on. */ char a1_hash[256]; - char a2_hash[256]; - char resp[256]; char resp_hash[256]=""; char tmp[256]; char *c; - char *z; - char *ua_hash =""; - char *resp_uri =""; - char *nonce = ""; - char *digestusername = ""; int wrongnonce = FALSE; - const char *usednonce = p->randdata; - - /* Find their response among the mess that we'r sent for comparison */ + int good_response; + const char *usednonce = p->randdata; /* XXX check */ + + /* table of recognised keywords, and their value in the digest */ + enum keys { K_RESP, K_URI, K_USER, K_NONCE, K_LAST }; + struct x { + const char *key; + const char *s; + } *i, keys[] = { + [K_RESP] = { "response=", "" }, + [K_URI] = { "uri=", "" }, + [K_USER] = { "username=", "" }, + [K_NONCE] = { "nonce=", "" }, + [K_LAST] = { NULL, NULL} + }; + + /* Make a copy of the response and parse it */ ast_copy_string(tmp, authtoken, sizeof(tmp)); c = tmp; - while(c) { - c = ast_skip_blanks(c); - if (!*c) - break; - if (!strncasecmp(c, "response=", strlen("response="))) { - c+= strlen("response="); - if ((*c == '\"')) { - ua_hash=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - - } else { - ua_hash=c; - if ((c = strchr(c,','))) - *c = '\0'; - } - - } else if (!strncasecmp(c, "uri=", strlen("uri="))) { - c+= strlen("uri="); - if ((*c == '\"')) { - resp_uri=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - } else { - resp_uri=c; - if ((c = strchr(c,','))) - *c = '\0'; - } + while(c && *(c = ast_skip_blanks(c)) ) { /* lookup for keys */ + for (i = keys; i->key != NULL; i++) { + const char *separator = ","; /* default */ - } else if (!strncasecmp(c, "username=", strlen("username="))) { - c+= strlen("username="); - if ((*c == '\"')) { - digestusername=++c; - if((c = strchr(c,'\"'))) - *c = '\0'; - } else { - digestusername=c; - if((c = strchr(c,','))) - *c = '\0'; - } - } else if (!strncasecmp(c, "nonce=", strlen("nonce="))) { - c+= strlen("nonce="); - if ((*c == '\"')) { - nonce=++c; - if ((c = strchr(c,'\"'))) - *c = '\0'; - } else { - nonce=c; - if ((c = strchr(c,','))) - *c = '\0'; + if (strncasecmp(c, i->key, strlen(i->key)) != 0) + continue; + /* Found. Skip keyword, take text in quotes or up to the separator. */ + c += strlen(i->key); + if (*c == '"') { /* in quotes. Skip first and look for last */ + c++; + separator = "\""; } - - } else - if ((z = strchr(c,' ')) || (z = strchr(c,','))) c=z; - if (c) - c++; + i->s = c; + strsep(&c, separator); + break; + } + if (i->key == NULL) /* not found, jump after space or comma */ + strsep(&c, " ,"); } /* Verify that digest username matches the username we auth as */ - if (strcmp(username, digestusername)) { + if (strcmp(username, keys[K_USER].s)) { + 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; } /* Verify nonce from request matches our nonce. If not, send 401 with new nonce */ - if (strcasecmp(p->randdata, nonce)) { + if (strcasecmp(p->randdata, keys[K_NONCE].s)) { /* XXX it was 'n'casecmp ? */ wrongnonce = TRUE; - usednonce = nonce; + usednonce = keys[K_NONCE].s; } - snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret); - - if (!ast_strlen_zero(resp_uri)) - snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, resp_uri); - else - snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, uri); - if (!ast_strlen_zero(md5secret)) - snprintf(a1_hash, sizeof(a1_hash), "%s", md5secret); - else + ast_copy_string(a1_hash, md5secret, sizeof(a1_hash)); + else { + char a1[256]; + snprintf(a1, sizeof(a1), "%s:%s:%s", username, global_realm, secret); ast_md5_hash(a1_hash, a1); + } - ast_md5_hash(a2_hash, a2); + /* compute the expected response to compare with what we received */ + { + char a2[256]; + char a2_hash[256]; + char resp[256]; - snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash); - ast_md5_hash(resp_hash, resp); + snprintf(a2, sizeof(a2), "%s:%s", sip_methods[sipmethod].text, + !ast_strlen_zero(keys[K_URI].s) ? keys[K_URI].s : uri); + ast_md5_hash(a2_hash, a2); + snprintf(resp, sizeof(resp), "%s:%s:%s", a1_hash, usednonce, a2_hash); + ast_md5_hash(resp_hash, resp); + } + good_response = keys[K_RESP].s && + !strncasecmp(keys[K_RESP].s, resp_hash, strlen(resp_hash)); if (wrongnonce) { ast_string_field_build(p, randdata, "%08x", thread_safe_rand()); - if (ua_hash && !strncasecmp(ua_hash, resp_hash, strlen(resp_hash))) { + if (good_response) { if (sipdebug) ast_log(LOG_NOTICE, "stale nonce received from '%s'\n", get_header(req, "To")); /* We got working auth token, based on stale nonce . */ @@ -6392,18 +6362,15 @@ static int check_auth(struct sip_pvt *p, struct sip_request *req, const char *us transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0); } - /* Schedule auto destroy in 15 seconds */ - sip_scheddestroy(p, 15000); - return 1; + /* Schedule auto destroy in 32 seconds */ + sip_scheddestroy(p, 32000); + return 1; /* Challenge sent */ } - /* resp_hash now has the expected response, compare the two */ - if (ua_hash && !strncasecmp(ua_hash, resp_hash, strlen(resp_hash))) { - /* Auth is OK */ - res = 0; - } + if (good_response) /* Auth is OK */ + return 0; } /* Failure */ - return res; + return -1; } /*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem |