From: Andreas Eversberg Date: Sat, 2 Dec 2017 10:49:09 +0000 (+0100) Subject: fixup sip X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=commitdiff_plain;h=ef08f1213998f3bfd7bc3d95ab7c4917725fb3e2;ds=sidebyside fixup sip --- diff --git a/default/interface.conf b/default/interface.conf index 17fabc1..ab62ca8 100644 --- a/default/interface.conf +++ b/default/interface.conf @@ -208,7 +208,7 @@ #earlyb no #tones no -# Use Sofia-SIP as SIP interface to register to a SIP gateway/proxy +# Use Sofia-SIP as SIP client to register to a SIP gateway/proxy #[sip] ## define source and destination IP to make a call #sip 192.168.0.55 sipgate.de @@ -216,8 +216,8 @@ #register sipgate.de 300 ##define RTP port range or use default #rtp-ports 30000 39999 -## use authentication credentials -#authenticate +## use authentication credentials, use realm to authenticate remote +#authenticate [] ## define keepalive timer to keep INVITE/REGISTER alive ## this is also required to keep the NAT router's table alive #options-interval 15 @@ -229,8 +229,26 @@ #stun stun.sipgate.net 300 ## screen caller ID to SIP caller ID #screen-out % -#tones no -#earlyb no +#tones yes +#earlyb yes + +# Use Sofia-SIP as SIP gateway/proxy to allow SIP clients to register +#[sip] +## define source +#sip 192.168.0.55 +##define RTP port range or use default +#rtp-ports 30000 39999 +## use authentication credentials and realm to authenticate remote +#authenticate +## define keepalive timer to keep INVITE/REGISTER alive +## this is also required to keep the NAT router's table alive +#options-interval 15 +## define public IP (if behind NAT firewall) +#public 123.45.67.89 +## OR define stun server and resolving interval +#stun stun.sipgate.net 300 +#tones yes +#earlyb yes # Hint: Enter "lcr interface" for quick help on interface options. diff --git a/interface.c b/interface.c index b8eff05..02e1531 100644 --- a/interface.c +++ b/interface.c @@ -990,7 +990,7 @@ static int inter_authenticate(struct interface *interface, char *filename, int l SPRINT(interface_error, "Error in %s (line %d): SIP not compiled in.\n", filename, line); return(-1); #else - char *p; + char *p, *q; if (!interface->sip) { SPRINT(interface_error, "Error in %s (line %d): This is not a SIP interface.\n", filename, line); @@ -1007,8 +1007,11 @@ static int inter_authenticate(struct interface *interface, char *filename, int l SPRINT(interface_error, "Error in %s (line %d): Missing SIP password.\n", filename, line); return(-1); } + q = get_seperated(p); SCPY(interface->sip_auth_user, value); SCPY(interface->sip_auth_password, p); + if (q[0]) + SCPY(interface->sip_auth_realm, q); return(0); #endif @@ -1554,8 +1557,9 @@ struct interface_param interface_param[] = { {"register", &inter_register, " [options-interval]", "Registers to given SIP registrar.\n" "Optionally give SIP timer to send OPTIONS messages to keepalive REGISTER sessions."}, - {"authenticate", &inter_authenticate, " ", - "Defines SIP user and password for authentication."}, + {"authenticate", &inter_authenticate, " [realm]", + "Defines SIP user and password for authentication.\n" + "If no remote IP was give, we are SIP gateway, so realm must be given also."}, {"options-interval", &options_interval, " | 0", "Defines SIP timer to send OPTIONS messages to keepalive INVITE sessions."}, {"asserted-id", &options_asserted_id, "", diff --git a/interface.h b/interface.h index f095bc6..040c412 100644 --- a/interface.h +++ b/interface.h @@ -131,6 +131,7 @@ struct interface { char sip_asserted_id[128]; char sip_auth_user[128]; char sip_auth_password[128]; + char sip_auth_realm[128]; int sip_register; char sip_register_user[128]; char sip_register_host[128]; diff --git a/sip.cpp b/sip.cpp index b5d9635..aa28f59 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,25 @@ 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]) { + authorization = sip->sip_proxy_authorization; + status = check_authorization(authorization, "INVITE", inst->auth_user, inst->auth_password, inst->auth_realm, p_s_auth_nonce, &auth_text); + if (status == 401) { + generate_nonce(p_s_auth_nonce); + SPRINT(auth_str, "Digest realm=\"%s\", nonce=\"%s\", algorithm=MD5, qop=\"auth\"", inst->auth_realm, p_s_auth_nonce); + } + } else { + status = 200; + auth_text = "Authentication not required"; + } + add_trace("result", NULL, "%s", auth_text); + end_trace(); + + if (status == 200) { + p_s_auth_nonce[0] = '\0'; + } + 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) { @@ -2469,6 +2671,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; diff --git a/sip.h b/sip.h index 1e13aee..b3537e5 100644 --- a/sip.h +++ b/sip.h @@ -40,6 +40,7 @@ class Psip : public Port struct lcr_work p_s_delete; nua_handle_t *p_s_handle; nua_magic_t *p_s_magic; + char p_s_auth_nonce[64]; struct lcr_timer p_s_invite_option_timer; /* time to send OPTION to invite transaction */ int p_s_invite_direction; /* DIRECTION_* of invite */ int p_s_rtp_bridge; /* bridge RTP instead of having a local RTP peer */