X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=sip.cpp;h=d8b1124b6b19f25a43246f921e51ce9744167c90;hp=b5d96359b75c632ce091512637350eccd69f0e75;hb=487bd4bcb2fd2a405081b21b17d2f8f2e2eb53dc;hpb=dd298160ec62b7d50a2764d472c906f6b1f353ab diff --git a/sip.cpp b/sip.cpp index b5d9635..d8b1124 100644 --- a/sip.cpp +++ b/sip.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #ifndef SOFIA_SIP_GCC_4_8_PATCH_APLLIED #warning ******************************************************** @@ -66,6 +67,8 @@ struct sip_inst { int options_interval; char auth_user[128]; char auth_password[128]; + char auth_realm[128]; + char auth_nonce[128]; su_root_t *root; nua_t *nua; @@ -100,6 +103,7 @@ Psip::Psip(int type, char *portname, struct port_settings *settings, struct inte add_work(&p_s_delete, delete_event, this, 0); p_s_handle = 0; p_s_magic = 0; + memset(&p_s_auth_nonce, 0, sizeof(p_s_auth_nonce)); memset(&p_s_rtp_fd, 0, sizeof(p_s_rtp_fd)); memset(&p_s_rtcp_fd, 0, sizeof(p_s_rtcp_fd)); memset(&p_s_rtp_sin_local, 0, sizeof(p_s_rtp_sin_local)); @@ -880,6 +884,158 @@ unsigned int Psip::get_local_ip(unsigned int ip) return ip; } +/* some simple nonce generator */ +static void generate_nonce(char *result) +{ + UPRINT(result, "%08x", (unsigned int)random()); + result += 8; + UPRINT(result, "%08x", (unsigned int)random()); + result += 8; + UPRINT(result, "%08x", (unsigned int)random()); + result += 8; + UPRINT(result, "%08x", (unsigned int)random()); +} + +/* check authorization */ +static int check_authorization(sip_authorization_t const *authorization, const char *regstr, const char *check_user, const char *check_pass, const char *check_realm, const char *check_nonce, const char **auth_text) +{ + int ret = 500; + *auth_text = "Internal Server Error"; + + char *username = NULL; + char *realm = NULL; + char *nonce = NULL; + char *uri = NULL; + char *qop = NULL; + char *cnonce = NULL; + char *nc = NULL; + char *response = NULL; + + int indexnum; + const char *cur; + + char temp[256], first_digest[2 * SU_MD5_DIGEST_SIZE + 1], second_digest[2 * SU_MD5_DIGEST_SIZE + 1], third_digest[2 * SU_MD5_DIGEST_SIZE + 1]; + su_md5_t md5_ctx; + + if (!check_nonce || !check_nonce[0] || !authorization || !authorization->au_params) { + if (!strcmp(regstr, "REGISTER")) { + *auth_text = "Unauthorized"; + ret = 401; + } else { + *auth_text = "Proxy Authentication Required"; + ret = 407; + } + goto end; + } + + /* parse header (stolen from freeswitch) */ + for (indexnum = 0; (cur = authorization->au_params[indexnum]); indexnum++) { + char *var, *val, *p, *work; + var = val = work = NULL; + if ((work = strdup(cur))) { + var = work; + if ((val = strchr(var, '='))) { + *val++ = '\0'; + while (*val == '"') { + *val++ = '\0'; + } + if ((p = strchr(val, '"'))) { + *p = '\0'; + } + + PDEBUG(DEBUG_SIP, "Found in Auth header: %s = %s\n", var, val); + if (!strcasecmp(var, "username")) { + username = strdup(val); + } else if (!strcasecmp(var, "realm")) { + realm = strdup(val); + } else if (!strcasecmp(var, "nonce")) { + nonce = strdup(val); + } else if (!strcasecmp(var, "uri")) { + uri = strdup(val); + } else if (!strcasecmp(var, "qop")) { + qop = strdup(val); + } else if (!strcasecmp(var, "cnonce")) { + cnonce = strdup(val); + } else if (!strcasecmp(var, "response")) { + response = strdup(val); + } else if (!strcasecmp(var, "nc")) { + nc = strdup(val); + } + } + + free(work); + } + } + + if (!username || !realm || !nonce || ! uri || !response) { + *auth_text = "Authorization header incomplete"; + ret = 400; + goto end; + } + + if (!!strcmp(username, check_user)) { + *auth_text = "Authorization Username Missmatch"; + ret = 403; + goto end; + } + if (!!strcmp(realm, check_realm)) { + *auth_text = "Authorization Realm Missmatch"; + ret = 403; + goto end; + } + if (!!strcmp(nonce, check_nonce)) { + *auth_text = "Authorization Nonce Missmatch"; + ret = 403; + goto end; + } + + /* perform hash */ + SPRINT(temp, "%s:%s:%s", check_user, realm, check_pass); + PDEBUG(DEBUG_SIP, "First hash: %s\n", temp); + su_md5_init(&md5_ctx); + su_md5_strupdate(&md5_ctx, temp); + su_md5_hexdigest(&md5_ctx, first_digest); + su_md5_deinit(&md5_ctx); + + SPRINT(temp, "%s:%s", regstr, uri); + PDEBUG(DEBUG_SIP, "First hash: %s\n", temp); + su_md5_init(&md5_ctx); + su_md5_strupdate(&md5_ctx, temp); + su_md5_hexdigest(&md5_ctx, second_digest); + su_md5_deinit(&md5_ctx); + + if (nc && cnonce && qop) + SPRINT(temp, "%s:%s:%s:%s:%s:%s", first_digest, nonce, nc, cnonce, qop, second_digest); + else + SPRINT(temp, "%s:%s:%s", first_digest, nonce, second_digest); + PDEBUG(DEBUG_SIP, "Third hash: %s\n", temp); + su_md5_init(&md5_ctx); + su_md5_strupdate(&md5_ctx, temp); + su_md5_hexdigest(&md5_ctx, third_digest); + su_md5_deinit(&md5_ctx); + + if (!!strcmp(response, third_digest)) { + *auth_text = "Authorization Failed"; + ret = 403; + goto end; + } + + *auth_text = "Authorization Success"; + ret = 200; + +end: + free(username); + free(realm); + free(nonce); + free(uri); + free(qop); + free(cnonce); + free(nc); + free(response); + + return ret; +} + /* * endpoint sends messages to the SIP port */ @@ -1549,6 +1705,17 @@ static void i_register(struct sip_inst *inst, int status, char const *phrase, nu contact = sip->sip_contact; nua_save_event(nua, saved); nua_event_data_t const *data = nua_event_data(saved); + sip_authorization_t const *authorization; + char uri[256] = ""; + const char *auth_text = NULL; + char auth_str[256] = ""; + + if (contact->m_url->url_host) + SCPY(uri, contact->m_url->url_host); + if (contact->m_url->url_port && contact->m_url->url_port[0]) { + SCAT(uri, ":"); + SCAT(uri, contact->m_url->url_port); + } if (!inst->allow_register) { sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_IN); @@ -1560,23 +1727,36 @@ static void i_register(struct sip_inst *inst, int status, char const *phrase, nu return; } - if (contact->m_url->url_host) - SCPY(inst->remote_peer, contact->m_url->url_host); - if (contact->m_url->url_port && contact->m_url->url_port[0]) { - SCAT(inst->remote_peer, ":"); - SCAT(inst->remote_peer, contact->m_url->url_port); - } - sip_trace_header(NULL, inst->interface_name, "REGISTER", DIRECTION_IN); - add_trace("contact", "uri", "%s", inst->remote_peer); + add_trace("contact", "uri", "%s", uri); + end_trace(); + + sip_trace_header(NULL, inst->interface_name, "Authorization", DIRECTION_IN); + if (inst->auth_realm[0]) { + authorization = sip->sip_authorization; + status = check_authorization(authorization, "REGISTER", inst->auth_user, inst->auth_password, inst->auth_realm, inst->auth_nonce, &auth_text); + if (status == 401) { + generate_nonce(inst->auth_nonce); + SPRINT(auth_str, "Digest realm=\"%s\", nonce=\"%s\", algorithm=MD5, qop=\"auth\"", inst->auth_realm, inst->auth_nonce); + } + } else { + status = 200; + auth_text = "Authentication not required"; + } + add_trace("result", NULL, "%s", auth_text); end_trace(); + if (status == 200) { + SCPY(inst->remote_peer, uri); + inst->auth_nonce[0] = '\0'; + } + sip_trace_header(NULL, inst->interface_name, "RESPOND", DIRECTION_OUT); - add_trace("respond", "value", "200 OK"); - add_trace("reason", NULL, "peer registered"); + add_trace("respond", "value", "%d", status); + add_trace("reason", NULL, "peer registers"); end_trace(); - nua_respond(nh, SIP_200_OK, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(data->e_msg), TAG_END()); + nua_respond(nh, status, auth_text, SIPTAG_CONTACT(sip->sip_contact), NUTAG_WITH_THIS_MSG(data->e_msg), TAG_IF(auth_str[0], SIPTAG_WWW_AUTHENTICATE_STR(auth_str)), TAG_END()); nua_handle_destroy(nh); inst->register_handle = NULL; } @@ -1643,6 +1823,9 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag int payloads = 0; unsigned char payload_type; int media_type; + sip_authorization_t const *authorization; + const char *auth_text = NULL; + char auth_str[256] = ""; interface = getinterfacebyname(inst->interface_name); if (!interface) { @@ -1668,6 +1851,36 @@ void Psip::i_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag } PDEBUG(DEBUG_SIP, "invite received (%s->%s)\n", from, to); + sip_trace_header(this, inst->interface_name, "Authorization", DIRECTION_IN); + if (inst->auth_realm[0] || p_state != PORT_STATE_IDLE) { + /* only authenticate remote, if we have a realm set and we don't have re-invite */ + authorization = sip->sip_proxy_authorization; + status = check_authorization(authorization, "INVITE", inst->auth_user, inst->auth_password, inst->auth_realm, inst->auth_nonce, &auth_text); + if (status == 407) { + generate_nonce(inst->auth_nonce); + SPRINT(auth_str, "Digest realm=\"%s\", nonce=\"%s\", algorithm=MD5, qop=\"auth\"", inst->auth_realm, inst->auth_nonce); + } + } else { + status = 200; + auth_text = "Authentication not required"; + } + add_trace("result", NULL, "%s", auth_text); + end_trace(); + + if (status == 200) { + inst->auth_nonce[0] = '\0'; + } else { + sip_trace_header(this, inst->interface_name, "RESPOND", DIRECTION_OUT); + add_trace("respond", "value", "%d", status); + add_trace("reason", NULL, "peer invited"); + end_trace(); + + nua_respond(nh, status, auth_text, SIPTAG_CONTACT(sip->sip_contact), TAG_IF(auth_str[0], SIPTAG_PROXY_AUTHENTICATE_STR(auth_str)), TAG_END()); + new_state(PORT_STATE_RELEASE); + trigger_work(&p_s_delete); + return; + } + sip_trace_header(this, inst->interface_name, "Payload received", DIRECTION_NONE); ret = parse_sdp(sip, &p_s_rtp_ip_remote, &p_s_rtp_port_remote, payload_types, media_types, &payloads, sizeof(payload_types)); if (!ret) { @@ -2015,6 +2228,12 @@ void Psip::r_invite(int status, char const *phrase, nua_t *nua, nua_magic_t *mag add_trace("respond", "value", "%d", status); end_trace(); + if (status == 401 || status == 407) { + PDEBUG(DEBUG_SIP, "Invite challenge received\n"); + challenge(inst, this, status, phrase, nua, magic, nh, hmagic, sip, tags); + return; + } + /* connect audio */ if (status == 183 || (status >= 200 && status <= 299)) { int ret; @@ -2189,7 +2408,6 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_ struct sip_inst *inst = (struct sip_inst *) magic; class Port *port; class Psip *psip = NULL; - int rc; PDEBUG(DEBUG_SIP, "Event %d from SIP stack received (handle=%p)\n", event, nh); if (!nh) @@ -2278,14 +2496,6 @@ static void sip_callback(nua_event_t event, int status, char const *phrase, nua_ return; } - switch (status) { - case 401: - case 407: - rc = challenge(inst, psip, status, phrase, nua, magic, nh, hmagic, sip, tags); - if (rc >= 0) - return; - } - switch (event) { case nua_r_set_params: PDEBUG(DEBUG_SIP, "setparam response\n"); @@ -2469,6 +2679,7 @@ int sip_init_inst(struct interface *interface) } SCPY(inst->auth_user, interface->sip_auth_user); SCPY(inst->auth_password, interface->sip_auth_password); + SCPY(inst->auth_realm, interface->sip_auth_realm); inst->register_interval = interface->sip_register_interval; inst->options_interval = interface->sip_options_interval;