Skip to content

Get Radius attributes for authentication request from SIP Header #37

@twmobius

Description

@twmobius

Hi @sobomax,

I wanted to add the sip realm in the authentication radius request (without perform digest authentication), and I've been looking through RadiusAuthorization; there is an extra_attributes parameter in the do_auth method which is not used anywhere and got me thinking.

Similarly to pass_headers parameters it would be nice to have an option to define a sip header from which b2bua will get specific radius attributes and add them to the Authentication request (via the extra_attributes property)

So I've modified the b2bua a bit to support exactly that. The patch adds an -x option, from which the user can add a sip header for b2bua to read radius attributes.

So for example, opensips might do something like:

append_hf('X-Test-Header: h323-ivr-out=SomeProperty=1\r\n');
$du = 'b2bua.ip'

route(RELAY);

sippy would have to run with -x x-test-header parameter and in the authentication request the header contents is added to the radius request. Obviously it's up to the administrator/ matching dictionary to supply valid radius key/ value pairs for this to work.

I am providing the patch in this comment, I don't know if you would be willing to add it to the b2bua source code. If you are, I'd be happy to supply you with a PR

Index: sippy/b2bua_radius.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- sippy/b2bua_radius.py	(revision f1c375b3454445b66b88c4b3cdc365306bb0ef18)
+++ sippy/b2bua_radius.py	(date 1603809097780)
@@ -119,11 +119,12 @@
     rtp_proxy_session = None
     huntstop_scodes = None
     pass_headers = None
+    extra_attributes = None
     auth_proc = None
     proxied = False
     challenge = None
 
-    def __init__(self, remote_ip, source, global_config, pass_headers):
+    def __init__(self, remote_ip, source, global_config, pass_headers, extra_attributes):
         self.id = CallController.id
         CallController.id += 1
         self.global_config = global_config
@@ -137,6 +138,7 @@
         self.remote_ip = remote_ip
         self.source = source
         self.pass_headers = pass_headers
+        self.extra_attributes = extra_attributes
 
     def recvEvent(self, event, ua):
         if ua == self.uaA:
@@ -191,11 +193,12 @@
                 elif auth == None or auth.username == None or len(auth.username) == 0:
                     self.username = self.remote_ip
                     self.auth_proc = self.global_config['_radius_client'].do_auth(self.remote_ip, self.cli, self.cld, self.cGUID, \
-                      self.cId, self.remote_ip, self.rDone)
+                      self.cId, self.remote_ip, self.rDone, extra_attributes=self.extra_attributes)
                 else:
                     self.username = auth.username
                     self.auth_proc = self.global_config['_radius_client'].do_auth(auth.username, self.cli, self.cld, self.cGUID, 
-                      self.cId, self.remote_ip, self.rDone, auth.realm, auth.nonce, auth.uri, auth.response)
+                      self.cId, self.remote_ip, self.rDone, auth.realm, auth.nonce, auth.uri, auth.response,
+                      extra_attributes=self.extra_attributes)
                 return
             if self.state not in (CCStateARComplete, CCStateConnected, CCStateDisconnecting) or self.uaO == None:
                 return
@@ -465,7 +468,27 @@
                 hfs = req.getHFs(header)
                 if len(hfs) > 0:
                     pass_headers.extend(hfs)
-            cc = CallController(remote_ip, source, self.global_config, pass_headers)
+
+            extra_attributes = None
+
+            if 'auth_extra_header' in self.global_config:
+                header = self.global_config['auth_extra_header']
+
+                hfs = req.getHFs(header)
+
+                if len(hfs) > 0:
+                    extra_attributes = []
+
+                    for header in hfs:
+                        kvPairs = header.body.body.split(';')
+
+                        for pair in kvPairs:
+                            [key, _, value] = pair.partition("=")
+
+                            if value != '':
+                                extra_attributes.append((key, value))
+
+            cc = CallController(remote_ip, source, self.global_config, pass_headers, extra_attributes)
             cc.challenge = challenge
             rval = cc.uaA.recvRequest(req, sip_t)
             self.ccmap.append(cc)
@@ -668,7 +691,7 @@
     global_config['_orig_argv'] = sys.argv[:]
     global_config['_orig_cwd'] = os.getcwd()
     try:
-        opts, args = getopt.getopt(sys.argv[1:], 'fDl:p:d:P:L:s:a:t:T:k:m:A:ur:F:R:h:c:M:HC:W:',
+        opts, args = getopt.getopt(sys.argv[1:], 'fDl:p:d:P:L:s:a:t:T:k:m:A:ur:F:R:h:c:M:HC:W:x:',
           global_config.get_longopts())
     except getopt.GetoptError:
         usage(global_config)
@@ -759,6 +782,9 @@
         if o == '-h':
             for a in a.split(','):
                 global_config.check_and_set('pass_header', a)
+            continue
+        if o == '-x':
+            global_config.check_and_set('auth_extra_header', a)
             continue
         if o == '-c':
             global_config.check_and_set('b2bua_socket', a)
Index: sippy/MyConfigParser.py
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- sippy/MyConfigParser.py	(revision f1c375b3454445b66b88c4b3cdc365306bb0ef18)
+++ sippy/MyConfigParser.py	(date 1603808883519)
@@ -98,6 +98,8 @@
                              'and "SUBSCRIBE" messages. Address in the format ' \
                              '"host[:port]"'),
  'nat_traversal':     ('B', 'enable NAT traversal for signalling'), \
+ 'auth_extra_header': ('S', 'sip header containing radius parameters to pass ' \
+                            'to authentication request'), \
  'xmpp_b2bua_id':     ('I', 'ID passed to the XMPP socket server')}
 
 class MyConfigParser(RawConfigParser):

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions