Skip to content

Commit

Permalink
Adds -x / --pre_auth_proc parameter which allows for a user to specify
Browse files Browse the repository at this point in the history
a routine (a class really) that B2BUA will execute after call controller
is initialized but before any actions are taken.

Examples of such routine is one that extracts a specific header,
does some simple parsing and adds its contents as radius attribute/value
pairs.

The syntax for that routine would be something like the following:

  --pre_auth_proc=HDR2Xattrs[X-foo-hdr]

PR: #38
  • Loading branch information
twmobius authored and sobomax committed Jul 31, 2024
1 parent af0b28f commit 925e96c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 3 deletions.
54 changes: 54 additions & 0 deletions sippy/B2BTransforms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright (c) 2024 Sippy Software, Inc. All rights reserved.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from sippy.SipRequest import SipRequest

class HDR2Xattrs():
# Source: https://github.com/sippy/b2bua/pull/38
# Author: @twmobius
hdr_name:str
def __init__(self, hdr_name:str):
self.hdr_name = hdr_name

def __call__(req:SipRequest, cc:'CallController'):
hfs = req.getHFs(self.hdr_name)

if len(hfs) == 0:
return

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))
if len(extra_attributes) == 0:
return
if cc.extra_attributes is None:
cc.extra_attributes = extra_attributes
else:
cc.extra_attributes.extend(extra_attributes)
11 changes: 11 additions & 0 deletions sippy/MyConfigParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
from configparser import RawConfigParser
_boolean_states = RawConfigParser.BOOLEAN_STATES
from sippy.SipConf import SipConf
import sippy.B2BTransforms as bts

SUPPORTED_OPTIONS = { \
'acct_enable': ('B', 'enable or disable Radius accounting'), \
Expand Down Expand Up @@ -103,6 +104,8 @@
'"host:port:cert_file:key_file", where "cert_file" ' \
'/ "key_file" are paths to the TLS certificate ' \
'and key file respectively in the X.509 PEM format'),
'pre_auth_proc': ('S', 'internal routine to be executed before authentication '\
'is being processed. E.g. "HDR2Xattrs[X-foo-hdr]."'), \
'xmpp_b2bua_id': ('I', 'ID passed to the XMPP socket server')}

class MyConfigParser(RawConfigParser):
Expand Down Expand Up @@ -216,6 +219,14 @@ def check_and_set(self, key, value, compat = True):
if _value <= 0 or _value > 65535:
raise ValueError('sip_port should be in the range 1-65535')
self['_sip_port'] = _value
elif key == 'pre_auth_proc':
rparts = value.split('[', 1)
if not len(rparts) == 2 or not value.endswith(']'):
raise ValueError('pre_auth_proc should be in the format `function(argument)`')
fname = rparts[0]
fclass = getattr(bts, fname)
farg = rparts[1][:-1]
self['_pre_auth_proc'] = fclass(farg)
self[key] = value

def options_help(self):
Expand Down
15 changes: 12 additions & 3 deletions sippy/b2bua_radius.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class CallController(object):
challenge = None
req_source: str
req_target: SipURL
extra_attributes = None

def __init__(self, remote_ip, source, req_source, req_target, global_config, pass_headers):
self.id = CallController.id
Expand Down Expand Up @@ -213,11 +214,12 @@ def recvEvent(self, event, ua):
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.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.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
Expand Down Expand Up @@ -511,6 +513,10 @@ def recvRequest(self, req, sip_t):
pass_headers.extend(hfs)
req_target = req.getRURI()
cc = CallController(remote_ip, source, req_source, req_target, self.global_config, pass_headers)

if '_pre_auth_proc' in self.global_config:
self.global_config['_pre_auth_proc'](req, cc)

cc.challenge = challenge
rval = cc.uaA.recvRequest(req, sip_t)
self.ccmap.append(cc)
Expand Down Expand Up @@ -715,7 +721,7 @@ def main_func():
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)
Expand Down Expand Up @@ -807,6 +813,9 @@ def main_func():
for a in a.split(','):
global_config.check_and_set('pass_header', a)
continue
if o == '-x':
global_config.check_and_set('pre_auth_proc', a)
continue
if o == '-c':
global_config.check_and_set('b2bua_socket', a)
continue
Expand Down

0 comments on commit 925e96c

Please sign in to comment.