fixup sip
authorAndreas Eversberg <jolly@eversberg.eu>
Sat, 2 Dec 2017 10:49:09 +0000 (11:49 +0100)
committerAndreas Eversberg <jolly@eversberg.eu>
Sat, 2 Dec 2017 10:49:09 +0000 (11:49 +0100)
default/interface.conf
interface.c
interface.h
sip.cpp
sip.h

index 17fabc1..ab62ca8 100644 (file)
 #earlyb no
 #tones no
 
 #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
 #[sip]
 ## define source and destination IP to make a call
 #sip 192.168.0.55 sipgate.de
 #register <user> sipgate.de 300
 ##define RTP port range or use default
 #rtp-ports 30000 39999
 #register <user> sipgate.de 300
 ##define RTP port range or use default
 #rtp-ports 30000 39999
-## use authentication credentials
-#authenticate <user> <password>
+## use authentication credentials, use realm to authenticate remote
+#authenticate <user> <password> [<realm>]
 ## define keepalive timer to keep INVITE/REGISTER alive
 ## this is also required to keep the NAT router's table alive
 #options-interval 15
 ## define keepalive timer to keep INVITE/REGISTER alive
 ## this is also required to keep the NAT router's table alive
 #options-interval 15
 #stun stun.sipgate.net 300
 ## screen caller ID to SIP caller ID
 #screen-out % <callerID>
 #stun stun.sipgate.net 300
 ## screen caller ID to SIP caller ID
 #screen-out % <callerID>
-#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 <user> <password> <realm>
+## 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.
 
 
 # Hint: Enter "lcr interface" for quick help on interface options.
index b8eff05..02e1531 100644 (file)
@@ -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
        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);
 
        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);
        }
                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);
        SCPY(interface->sip_auth_user, value);
        SCPY(interface->sip_auth_password, p);
+       if (q[0])
+               SCPY(interface->sip_auth_realm, q);
 
        return(0);
 #endif
 
        return(0);
 #endif
@@ -1554,8 +1557,9 @@ struct interface_param interface_param[] = {
        {"register", &inter_register, "<user> <host> [options-interval]",
        "Registers to given SIP registrar.\n"
        "Optionally give SIP timer to send OPTIONS messages to keepalive REGISTER sessions."},
        {"register", &inter_register, "<user> <host> [options-interval]",
        "Registers to given SIP registrar.\n"
        "Optionally give SIP timer to send OPTIONS messages to keepalive REGISTER sessions."},
-       {"authenticate", &inter_authenticate, "<user> <password>",
-       "Defines SIP user and password for authentication."},
+       {"authenticate", &inter_authenticate, "<user> <password> [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, "<interval> | 0",
        "Defines SIP timer to send OPTIONS messages to keepalive INVITE sessions."},
        {"asserted-id", &options_asserted_id, "<caller-id>",
        {"options-interval", &options_interval, "<interval> | 0",
        "Defines SIP timer to send OPTIONS messages to keepalive INVITE sessions."},
        {"asserted-id", &options_asserted_id, "<caller-id>",
index f095bc6..040c412 100644 (file)
@@ -131,6 +131,7 @@ struct interface {
        char                    sip_asserted_id[128];
        char                    sip_auth_user[128];
        char                    sip_auth_password[128];
        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];
        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 (file)
--- a/sip.cpp
+++ b/sip.cpp
@@ -16,6 +16,7 @@
 #include <sofia-sip/sip_header.h>
 #include <sofia-sip/stun.h>
 #include <sofia-sip/stun_tag.h>
 #include <sofia-sip/sip_header.h>
 #include <sofia-sip/stun.h>
 #include <sofia-sip/stun_tag.h>
+#include <sofia-sip/su_md5.h>
 
 #ifndef SOFIA_SIP_GCC_4_8_PATCH_APLLIED
 #warning ********************************************************
 
 #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];
        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;
 
        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;
        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));
        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;
 }
 
        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
  */
 /*
  * 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);
        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);
 
        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;
        }
 
                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);
        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();
 
        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);
        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();
 
        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;
 }
        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;
        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) {
 
        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);
 
        }
        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) {
        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_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;
 
        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 (file)
--- 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;
        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 */
        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 */