Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Radius attributes for authentication request from SIP Header #37

Open
twmobius opened this issue Oct 27, 2020 · 3 comments
Open

Get Radius attributes for authentication request from SIP Header #37

twmobius opened this issue Oct 27, 2020 · 3 comments

Comments

@twmobius
Copy link
Contributor

twmobius commented Oct 27, 2020

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):
@twmobius
Copy link
Contributor Author

I went a step further and added another parameter named parameter on the Routing h323-ivr-in response in the authentication that a user could supply a set of radius attribute-value pairs that b2bua later adds in the accounting messages.

So for example if the authorization response if of this sort:

h323-ivr-in = 'Routing:[email protected]:5060;credit-time=7200;expires=15;np_expires=5;bill-to=123@realm;parameters=h323-ivr-out=Test-Data:d=2,vr=1,ar=8'

both originate and answer, start, stop accounting will containing the header:

h323-ivr-out = 'Test-Data:d=2,vr=1,ar=8'

The idea is that when the billing engine performs authorisation for a call, it could provide information to the accounting events so that the engine could easily charge the call without having to recalculate everything from scratch.

Please let me know if you are interested in such feature additions on b2bua. :)

@sobomax
Copy link
Member

sobomax commented Oct 29, 2020 via email

@twmobius
Copy link
Contributor Author

@sobomax there you go, I've split them up into two PRs one for each new feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants