Release 0.2.1

This commit is contained in:
Thomas Ries 2002-09-27 23:26:44 +00:00
parent b31f9c7b86
commit d9173e19b6
25 changed files with 1080 additions and 93 deletions

View File

@ -1,3 +1,12 @@
0.2.1
=====
- 28-Sep-2002: - Released version 0.2.1
- 27-Sep-2002: - first implementation of proxy authentication (for REGISTER)
- 24-Sep-2002: - if daemonized, log to syslog
- 22-Sep-2002: - added access list support for registration and incomming
SIP packets.
- 21-Sep-2002: - added RPM support (Spec File)
0.2.0
=====
- 17-Sep-2002: - Released version 0.2.0

View File

@ -21,4 +21,4 @@
SUBDIRS = src doc scripts
EXTRA_DIST = TODO RELNOTES
EXTRA_DIST = TODO RELNOTES siproxd.spec

7
README
View File

@ -130,7 +130,7 @@ IMPORTANT NOTICE
The gethostbyname() function leaks memory in glibc 2.1.1 (-> RedHat 6.0).
The quick fix is to delete the nisplus service from hosts entry in
/etc/nsswitch.conf.
In my tests, memory use remained stable after I made the mentioned change.
In my tests, memory usage remained stable after I made the mentioned change.
(source: http://www.squid-cache.org/Doc/FAQ/FAQ-14.html)
@ -145,7 +145,7 @@ Please feel free to contact the author to:
and visit the website at http://siproxd.sourceforge.net/
There also is a siproxd mailinglist available on sourceforge.
There is a siproxd mailinglist available on sourceforge.
Thomas Ries (tries@gmx.net)
GnuPG Public Key:
@ -160,3 +160,6 @@ CREDITS
Thanks to sourceforge.net for providing the distribution platform and
infrastructure.
Also credits to the maintainers of linphone from where I have taken some
code parts for MD5 proxy authentication.

View File

@ -1,18 +1,29 @@
Release Notes for siproxd-0.2.1
===============================
TO BE ADAPTED!!
- SIP Proxy for SIP based softphones hidden behind a masquerading firewall
- Includes an RTP data stream proxy for incomming audio data
- Includes an RTP data stream proxy for *incomming* audio data
(outgoing RTP data should be handled by IP masquerading by the firewall)
- Port range to be used for incomming RTP traffic is configurable
(-> easy to set up apropriate firewall rules for incomming traffic)
- Multiple local users/hosts can be masqueraded simultaneously
- Supports running in a chroot jail (configurable)
- Supports changing user ID after startup (if started as root)
- All configuration done via config file
- Supports changing user-ID after startup (if started as root)
- All configuration done via one simple ascii configuration file
- Proxy Authentication for registration of local clients (User Agents)
- Logging to syslog in Daemon mode
- Access control (IP based) for incomming traffic
- RPM support (spec file)
Requirements:
- pthreads
- libosip 0.8.8
- currently tested on Linux 2.2.x (Redhat 6.0) and 2.4.x (Redhat 7.2)
however, should run on any other
Currently tested on Linux 2.2.x (Redhat 6.0) and 2.4.x (Redhat 7.2),
should run on others Linux distributions as well.
Interoperability (tested with softphones):
- Linphone (http://www.linphone.org)
- Kphone (http://www.wirlab.net/kphone/)
-----
md5sum for siproxd-0.2.1.tar.gz:

14
TODO
View File

@ -1,20 +1,22 @@
TODOs, in random order:
=======================
- re-think the registration mechanism (mapping and decision criteria
for in- and outgoing stuff...
- client authentication for registration - multiple user feature
- logging via syslog if running in daemon mode
- redo return status (make then at least DEFINED constants)
- get_ip_by_host: reduce DNS timeouts
- Documentation (yeah, yeah...)
- Documentation
- general security issues
- automagically create a proper config file during install
- client authentication
- get_ip_by_host: reduce DNS timeouts
- support for "full duplex" RTP proxying
- portability to other platforms / operating systems
first goal: other Unixes

View File

@ -87,9 +87,15 @@
/* Define if you have the strstr function. */
#undef HAVE_STRSTR
/* Define if you have the syslog function. */
#undef HAVE_SYSLOG
/* Define if you have the vfprintf function. */
#undef HAVE_VFPRINTF
/* Define if you have the vsnprintf function. */
#undef HAVE_VSNPRINTF
/* Define if you have the <errno.h> header file. */
#undef HAVE_ERRNO_H

View File

@ -15,7 +15,7 @@ dnl ******************************************************************
dnl
SPD_MAJOR_VERSION=0
SPD_MINOR_VERSION=2
SPD_MICRO_VERSION=0
SPD_MICRO_VERSION=1
SPD_VERSION=$SPD_MAJOR_VERSION.$SPD_MINOR_VERSION.$SPD_MICRO_VERSION
dnl *********************************************************************
@ -51,10 +51,11 @@ AC_FUNC_MEMCMP
AC_FUNC_VPRINTF
AC_CHECK_FUNCS(strerror)
AC_CHECK_FUNCS(gethostbyname)
AC_CHECK_FUNCS(getopt_long_only daemon)
AC_CHECK_FUNCS(getopt_long_only daemon syslog)
AC_CHECK_FUNCS(getuid setuid getgid setgid getpwnam chroot)
AC_CHECK_FUNCS(socket bind select read send sendto)
AC_CHECK_FUNCS(strncpy strchr strstr sprintf vfprintf fgets sscanf)
AC_CHECK_FUNCS(strncpy strchr strstr sprintf vfprintf vsnprintf)
AC_CHECK_FUNCS(fgets sscanf)
AC_OUTPUT(Makefile \

116
doc/FAQ Normal file
View File

@ -0,0 +1,116 @@
Still under construction...
---------------------------------------------------------------------------
Q: What softphone work with siproxd?
A: The goal is that every softphone (that is SIP compliant) should be
able to work via siproxd. Tested and/or reported to work so far:
- linphone (0.9.0)
- kphone (1.0.2)
---------------------------------------------------------------------------
Q: Siproxd's RTP proxying does only work for incomming RTP audio data.
Should it not also proxy outgoing RTP data?
A: Curently (0.2.0) that is the correct behaviour. Incomming RTP traffic
is handled by siproxd's RTP proxy. However, outgoing RTP traffic has
to be handled by the firewall (IP masquerading).
---------------------------------------------------------------------------
Q: How do I setup IP masquerading for the outgoing RTP traffic?
A: if you are using 'ipchains' it is a firewall rule like the following:
# ipchains -A forward -i ppp0 -j MASQ -s 10.0.0.0/24 -d 0.0.0.0/0
This will set up IP masquerading for all local hostx (10.x.x.x) to
the Internet (connected on ppp0). Read the ipchains documentation
for details.
More recent Linux Kernels (2.4.x) may use 'iptables' instead of
'ipchains'. Check the corresponding documentation for details
how to configure IP masquerading there.
---------------------------------------------------------------------------
Q: Is it possible from a remote computer to call the inbound computer?
A: Yes, see also next question.
---------------------------------------------------------------------------
Q: What SIP address must the remote computer use to make a call?
A: Scenario
--------
private IP address range : Internet
10.0.0.x : (publich IP address range)
:
: foo.bar.org xxx.org
+-------------+ +--------------+ +-------------+
! !.10 .1 ! masquerading ! publicIP ! !
! IntHost !-------------! Firewall !------------>>! externalHost!
! ! eth0! !ppp0 ! !
+-------------+ +--------------+ +-------------+
user: johndoe user: test
- IntHost is running an SIP softphone (like linphone, kphone)
- The SIP address used by IntHost is sip:johndoe@foo.bar.org
- The softphone on IntHost is configured to register at siproxd
running on the firewall host (10.0.0.1) as sip:johndoe@foo.bar.org
- foo.bar.org is the domain name corresponding to the public IP address
of the firewall (eg use some dynamic DNS service [1])
- externalHost does *not* register at siproxd running on the firewall host.
The relevant part of the configuration (linphone) of IntHost
then looks like ($HOME/gnome/linphone):
[sip]
sip_port=5060
use_registrar=1
username=johndoe
hostname=foo.bar.org
registrar=sip:10.0.0.1
reg_passwd=
addr_of_rec=sip:johndoe@foo.bar.org
reg_expires=900
as_proxy=1
as_redirect=0
as_outbound=1
To make an outgoing call from IntHost simply use the SIP address of the
target ( -> sip:test@xxx.org).
test@xxx.org can make a incomming calls - it simply has to use the registered
SIP address of the softphone running on IntHost (sip:johndoe@foo.bar.org).
Siproxd will then rewrite and forward the incomming request to Inthost.
The externalHost does not need to know anything about the proxy. For the
user sip:test@xxx.org it looks as he directly sends the traffic to
foo.bar.org, siproxd then takes care about where to send it from there.
---------------------------------------------------------------------------
Q: How does the registration and mapping of inbound clients work?
A: The mapping mechanism of SIP addresses works basically like:
Inthost sends a registration to siproxd with:
- a 'To:' address of the address to be registered (sip:johndoe@foo.bar.org)
(lets call this address the 'masqueraded' or 'public' address)
- a 'Contact:' address of the *true* address (sip:johndoe@10.0.0.10)
Siproxd then will basically 'just' substitute the true address by the
masqueraded address and vice versa. That means you can have multiple
IntHosts (each of them using a different user name) running at the
same time.
For an incomming call, siproxd will search its registration table for
the requested SIP address and so finds the internal host that belong to it.
This of course *requires* that the username part of the SIP address is
unique for each softphone that registers a the proxy (So this is more or
less the mechanism that you mentioned in your mail).
---------------------------------------------------------------------------

View File

@ -19,4 +19,4 @@
#
EXTRA_DIST = siproxd.conf.example
EXTRA_DIST = siproxd.conf.example FAQ

View File

@ -3,6 +3,8 @@
#
# !! This is a sample file, adapt it to your needs before using it
#
# !! Strings MUST NOT contain spaces in between !!
#
######################################################################
# The IP addresses of the INBOUND and OUTBOUND interface can
@ -12,14 +14,36 @@
#
# or as dotted decimal IP:
#
# host_inbound=10.0.0.1
#
host_inbound = 192.168.1.1
host_outbound = ries.homeip.net
host_inbound = 10.0.0.1
host_outbound = my.external-ip.address
######################################################################
# Port to listen for incomming SIP messages.
# Access lists in the form: IP/mask (ex. 10.0.0.1/24)
# multiple entries may be separated by commas NO SPACES ARE ALLOWED!!
# Empty list means 'does not apply' - no filtering is done then.
# For *allow* lists this means: always allow, for *deny* lists that
# this means never deny.
#
# hosts_allow_reg: defines nets where we accept registrations from
# hosts_allow_sip: defines nets where we accept SIP traffic from
# hosts_deny_sip: defines nets where we deny SIP traffic from
#
# - The deny list takes precedence over the allow lists.
# - The allow_reg list imples also allowance for sip.
#
# Example for usage:
# local private net -> allow_reg list
# external nets (where we accept incomming calls from) -> allow_sip
#
#hosts_allow_reg = 192.168.1.8/24
#hosts_allow_sip = 123.45.0.0/16,123.46.0.0/16
#hosts_deny_sip = 10.0.0.0/8,11.0.0.0/8
######################################################################
# Access control.
# 5060 is usually the correct choise - don't change is unless you
# know what you're doing
#
@ -58,7 +82,22 @@ rtp_port_high = 7080
rtp_timeout = 60
######################################################################
# DBCLASS_BABBLE 0x00000001 // babble (like entering/leaving fnc)
# Proxy authentication
# If proxy_auth_realm is defined (a string), clients will be forced
# to authenticate themselfes at the proxy (for registration only).
# To disable Authentication, simply comment out this line.
#
#proxy_auth_realm = Authentication_Realm
#
# the password to use (right now, only one global password for
# registration is supported -> same for all local clients)
#
#proxy_auth_passwd = password
######################################################################
# Debug level... (setting to -1 will enable everything)
#
# DBCLASS_BABBLE 0x00000001 // babble (like entering/leaving func)
# DBCLASS_NET 0x00000002 // network
# DBCLASS_SIP 0x00000004 // SIP manipulations
# DBCLASS_REG 0x00000008 // Client registration
@ -68,6 +107,8 @@ rtp_timeout = 60
# DBCLASS_NETTRAF 0x00000080 // network traffic
# DBCLASS_CONFIG 0x00000100 // configuration
# DBCLASS_RTP 0x00000200 // RTP proxy
# DBCLASS_ACCESS 0x00000400 // Access list evaluation
# DBCLASS_AUTH 0x00000800 // Authentication
#
debug_level = 0x00000318
debug_level = 0x00000000

58
siproxd.spec Normal file
View File

@ -0,0 +1,58 @@
%define name siproxd
%define ver 0.2.1
%define release 1
%define serial 1
%define prefix %{_prefix}
%define sysconfdir %{_sysconfdir}
Name: %{name}
Summary: A SIP masquerading proxy with RTP support
Version: %{ver}
Release: %{release}
Copyright: GPL
Group: Applications/Communications
Source0: %{name}-%{ver}.tar.gz
URL: http://siproxd.sourceforge.net/
BuildRoot: %{_tmppath}/%{name}-%{ver}-root
Docdir: %{_docdir}
Requires: libosip >= 0.8.0
BuildRequires: libosip >= 0.8.0
Vendor: Thomas Ries
Packager: Thomas Ries <tries@gmx.net>
%description
Siprox is an proxy/masquerading daemon for the SIP protocol.
It handles registrations of SIP clients on a private IP network
and performs rewriting of the SIP message bodies to make SIP
connections possible via an masquerading firewall.
It allows SIP clients (like kphone, linphone) to work behind
an IP masquerading firewall or router.
%prep
%setup -q
%build
CFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} --sysconfdir=%{sysconfdir}
make
%install
make prefix=$RPM_BUILD_ROOT%{prefix} PIXDESTDIR=$RPM_BUILD_ROOT sysconfdir=$RPM_BUILD_ROOT%{sysconfdir} install
%clean
rm -rf $RPM_BUILD_ROOT
%files
%defattr(-, root, root)
%doc COPYING README AUTHORS INSTALL NEWS ChangeLog
%{_bindir}/siproxd
%changelog
* Sat Sep 21 2002 Thomas Ries <tries@gmx.net>
- first RPM support

View File

@ -23,9 +23,10 @@ CFLAGS =@CFLAGS@ -Wall -DBUILDSTR=\"`cat .buildno`\" -D_GNU_SOURCE
bin_PROGRAMS = siproxd
siproxd_SOURCES = siproxd.c proxy.c register.c sock.c utils.c \
log.c readconf.c rtpproxy.c
log.c readconf.c rtpproxy.c accessctl.c \
security.c auth.c
noinst_HEADERS = log.h siproxd.h
noinst_HEADERS = log.h siproxd.h digcalc.h
EXTRA_DIST = .buildno

166
src/accessctl.c Normal file
View File

@ -0,0 +1,166 @@
/*
Copyright (C) 2002 Thomas Ries <tries@gmx.net>
This file is part of Siproxd.
Siproxd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Siproxd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Siproxd; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <osip/smsg.h>
#include "siproxd.h"
#include "log.h"
static char const ident[]="$Id: " __FILE__ ": " PACKAGE "-" VERSION "-"\
BUILDSTR " $";
/* configuration storage */
struct siproxd_config configuration;
/* prototypes used locally only */
int process_aclist (char *aclist, struct sockaddr_in from);
/*
* verifies the from address agains the access lists
* defined in the configuration file.
*
* returns a bitmask with ACCESSCTL_SIP, ACCESSCTL_REG
*/
int check_accesslist (struct sockaddr_in from) {
int access = 0;
DEBUGC(DBCLASS_ACCESS,"deny list (SIP):%s",configuration.hosts_deny_sip);
DEBUGC(DBCLASS_ACCESS,"allow list (SIP):%s",configuration.hosts_allow_sip);
DEBUGC(DBCLASS_ACCESS,"allow list (REG):%s",configuration.hosts_allow_reg);
/*
* check DENY list
*/
if ( (configuration.hosts_deny_sip !=NULL) &&
(strcmp(configuration.hosts_deny_sip,"")!=0) ) {
/* non-empty list -> check agains it */
if (process_aclist(configuration.hosts_deny_sip, from)) {
/* yup - this one is blacklisted */
DEBUGC(DBCLASS_ACCESS,"caught by deny list");
return 0;
}
}
/*
* check SIP allow list
*/
if ( (configuration.hosts_allow_sip !=NULL) &&
(strcmp(configuration.hosts_allow_sip,"")!=0) ) {
/* non-empty list -> check agains it */
if (process_aclist(configuration.hosts_allow_sip, from)) {
/* SIP access granted */
DEBUGC(DBCLASS_ACCESS,"granted SIP access");
access |= ACCESSCTL_SIP;
}
} else {
access |= ACCESSCTL_SIP;
}
/*
* check SIP registration allow list
*/
if ( (configuration.hosts_allow_reg !=NULL) &&
(strcmp(configuration.hosts_allow_reg,"")!=0) ) {
/* non-empty list -> check agains it */
if (process_aclist(configuration.hosts_allow_reg, from)) {
/* SIP registration access granted */
DEBUGC(DBCLASS_ACCESS,"granted REG/SIP access");
access |= ACCESSCTL_REG | ACCESSCTL_SIP;
}
} else {
access |= ACCESSCTL_REG;
}
return (access);
}
/*
* checks for a match of the 'from' address with the supplies
* access list.
*
* return 1 for MATCH, 0 otherwise
*/
int process_aclist (char *aclist, struct sockaddr_in from) {
int i;
int lastentry;
char *p1, *p2;
char address[32]; /* dotted decimal IP - max 15 chars*/
char mask[8]; /* mask - max 2 digits */
struct in_addr inaddr;
unsigned int bitmask;
for (i=0, p1=aclist, lastentry=0;
!lastentry; i++) {
/*
* extract one entry from the access list
*/
/* address */
p2=strchr(p1,'/');
if (!p2) {
ERROR("CONFIG: hosts_deny_sip - no mask separator found");
return 0;
}
memset(address,0,sizeof(address));
memcpy(address,p1,p2-p1);
/* mask */
p1=strchr(p2,',');
p1=p2+1;
p2=strchr(p1,',');
if (!p2) { /* then this must be the last entry in the list */
p2=strchr(p1,'\0');
lastentry=1;
}
memset(mask,0,sizeof(mask));
memcpy(mask,p1,p2-p1);
p1=p2+1;
DEBUGC(DBCLASS_ACCESS,"[%i] extracted address=%s", i, address);
DEBUGC(DBCLASS_ACCESS,"[%i] extracted mask =%s", i, mask);
/*
* check for a match
*/
get_ip_by_host(address, &inaddr);
bitmask=~(0xffffffff>>atoi(mask));
DEBUGC(DBCLASS_ACCESS,"[%i] (%p) <-> (%p)", i,
ntohl(inaddr.s_addr) & bitmask,
ntohl(from.sin_addr.s_addr) & bitmask);
if ( (ntohl(inaddr.s_addr) & bitmask) ==
(ntohl(from.sin_addr.s_addr) & bitmask) ) return 1;
}
return 0;
}

351
src/auth.c Normal file
View File

@ -0,0 +1,351 @@
/*
Copyright (C) 2002 Thomas Ries <tries@gmx.net>
This file is part of Siproxd.
Siproxd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Siproxd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Siproxd; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <osip/smsg.h>
#include <osip/port.h>
#include <osip/global.h>
#include <osip/md5.h>
#include "digcalc.h"
#include "siproxd.h"
#include "log.h"
static char const ident[]="$Id: " __FILE__ ": " PACKAGE "-" VERSION "-"\
BUILDSTR " $";
/* configuration storage */
extern struct siproxd_config configuration;
/* local protorypes */
static char *auth_generate_nonce(void);
static int auth_check(proxy_authorization_t *proxy_auth);
/*
* perform proxy authentication
*
* sts = 0 : authentication ok / not needed
* sts = 1 : authentication failed
* sts = 2 : authentication needed
*/
int authenticate_proxy(sip_t *request) {
proxy_authorization_t *proxy_auth;
/* required by config ? (if not, return 0)*/
if (configuration.proxy_auth_realm==NULL) {
return 0;
}
/* supplied by UA? (if not, return 1)*/
msg_getproxy_authorization(request, 0, &proxy_auth);
if (proxy_auth == NULL) {
DEBUGC(DBCLASS_AUTH,"proxy-auth required, not supplied by UA");
return 2;
}
/* verify supplied authentication */
if (auth_check(proxy_auth) == 0) {
DEBUGC(DBCLASS_AUTH,"proxy-auth succeeded");
return 0;
}
/* authentication failed */
DEBUGC(DBCLASS_AUTH,"proxy-auth failed");
return 1;
}
int auth_include_authrq(sip_t *response) {
int sts;
char str[256];
/*
Example of an Proxy-Authenticate header:
Proxy-Authenticate: Digest realm="atlanta.com",
domain="sip:ss1.carrier.com", qop="auth",
nonce="f84f1cec41e6cbe5aea9c8e88d359",
opaque="", stale=FALSE, algorithm=MD5
*/
/* 40static + 32nonce + \0 -> max 183 */
sprintf(str, "Digest realm=\"%.180s\", "
"nonce=\"%s\", "
"algorithm=MD5",
configuration.proxy_auth_realm,
auth_generate_nonce());
sts = msg_setproxy_authenticate(response, str);
DEBUGC(DBCLASS_AUTH," msg_setproxy_authenticate sts=%i",sts);
return 0;
}
static char *auth_generate_nonce() {
static char nonce[40];
struct timeval tv;
gettimeofday (&tv, NULL);
/* yeah, I know... should be a better algorithm */
sprintf(nonce, "%8.8lx%8.8lx%8.8x%8.8x",
tv.tv_sec, tv.tv_usec, rand(), rand() );
DEBUGC(DBCLASS_AUTH," created nonce=\"%s\"",nonce);
return nonce;
}
/*
* verify the supplied authentication information from UA
*
* returns 0 if succeeded
* returns 1 if failed
*/
static int auth_check(proxy_authorization_t *proxy_auth) {
char *password=NULL;
int sts;
HASHHEX HA1;
HASHHEX HA2 = "";
HASHHEX Lcl_Response;
char *Username = NULL;
char *Realm = NULL;
char *Nonce = NULL;
char *CNonce = NULL;
char *NonceCount = NULL;
char *Qpop = NULL;
char *Uri = NULL;
char *Response = NULL;
/* if item exists, allocate& copy string without quotes */
if (proxy_auth->username)
Username=sgetcopy_unquoted_string(proxy_auth->username);
if (proxy_auth->realm)
Realm=sgetcopy_unquoted_string(proxy_auth->realm);
if (proxy_auth->nonce)
Nonce=sgetcopy_unquoted_string(proxy_auth->nonce);
if (proxy_auth->cnonce)
CNonce=sgetcopy_unquoted_string(proxy_auth->cnonce);
if (proxy_auth->nonce_count)
NonceCount=sgetcopy_unquoted_string(proxy_auth->nonce_count);
if (proxy_auth->message_qop)
Qpop=sgetcopy_unquoted_string(proxy_auth->message_qop);
if (proxy_auth->uri)
Uri=sgetcopy_unquoted_string(proxy_auth->uri);
if (proxy_auth->response)
Response=sgetcopy_unquoted_string(proxy_auth->response);
/* get password from configuration */
if (configuration.proxy_auth_passwd)
password=configuration.proxy_auth_passwd;
else
password="";
DEBUGC(DBCLASS_BABBLE," username=\"%s\"",Username );
DEBUGC(DBCLASS_BABBLE," realm =\"%s\"",Realm );
DEBUGC(DBCLASS_BABBLE," nonce =\"%s\"",Nonce );
DEBUGC(DBCLASS_BABBLE," cnonce =\"%s\"",CNonce );
DEBUGC(DBCLASS_BABBLE," nonce_cn=\"%s\"",NonceCount);
DEBUGC(DBCLASS_BABBLE," qpop =\"%s\"",Qpop );
DEBUGC(DBCLASS_BABBLE," uri =\"%s\"",Uri );
DEBUGC(DBCLASS_BABBLE," response=\"%s\"",Response );
/* calculate the MD5 digest (heavily inspired from linphone code) */
DigestCalcHA1("MD5", Username, Realm, password, Nonce, CNonce, HA1);
DigestCalcResponse(HA1, Nonce, NonceCount, CNonce, Qpop,
"REGISTER", Uri, HA2, Lcl_Response);
DEBUGC(DBCLASS_BABBLE," calculated Response=\"%s\"", Lcl_Response);
if (strcmp(Lcl_Response, Response)==0) {
DEBUGC(DBCLASS_AUTH," Authentication succeeded");
sts = 0;
} else {
DEBUGC(DBCLASS_AUTH," Authentication failed");
sts = 1;
}
/* free allocated memory from above */
if (Username) free(Username);
if (Realm) free(Realm);
if (Nonce) free(Nonce);
if (CNonce) free(CNonce);
if (NonceCount) free(NonceCount);
if (Qpop) free(Qpop);
if (Uri) free(Uri);
if (Response) free(Response);
return sts;
}
/*-------------------------------------------------------------------------
-------------------------------------------------------------------------
The routines below have been taken from linphone
(osipua/src/authentication.c)
-------------------------------------------------------------------------
-------------------------------------------------------------------------*/
void CvtHex(
IN HASH Bin,
OUT HASHHEX Hex
)
{
unsigned short i;
unsigned char j;
for (i = 0; i < HASHLEN; i++) {
j = (Bin[i] >> 4) & 0xf;
if (j <= 9)
Hex[i*2] = (j + '0');
else
Hex[i*2] = (j + 'a' - 10);
j = Bin[i] & 0xf;
if (j <= 9)
Hex[i*2+1] = (j + '0');
else
Hex[i*2+1] = (j + 'a' - 10);
};
Hex[HASHHEXLEN] = '\0';
};
/* calculate H(A1) as per spec */
void DigestCalcHA1(
IN char * pszAlg,
IN char * pszUserName,
IN char * pszRealm,
IN char * pszPassword,
IN char * pszNonce,
IN char * pszCNonce,
OUT HASHHEX SessionKey
)
{
MD5_CTX Md5Ctx;
HASH HA1;
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, pszUserName, strlen(pszUserName));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszRealm, strlen(pszRealm));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszPassword, strlen(pszPassword));
MD5Final(HA1, &Md5Ctx);
if ((pszAlg!=NULL)&&strcasecmp(pszAlg, "md5-sess") == 0) {
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, HA1, HASHLEN);
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
MD5Final(HA1, &Md5Ctx);
};
CvtHex(HA1, SessionKey);
};
/* calculate request-digest/response-digest as per HTTP Digest spec */
void DigestCalcResponse(
IN HASHHEX HA1, /* H(A1) */
IN char * pszNonce, /* nonce from server */
IN char * pszNonceCount, /* 8 hex digits */
IN char * pszCNonce, /* client nonce */
IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
IN char * pszMethod, /* method from the request */
IN char * pszDigestUri, /* requested URL */
IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
OUT HASHHEX Response /* request-digest or response-digest */
)
{
MD5_CTX Md5Ctx;
HASH HA2;
HASH RespHash;
HASHHEX HA2Hex;
// calculate H(A2)
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, pszMethod, strlen(pszMethod));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszDigestUri, strlen(pszDigestUri));
if (pszQop!=NULL) {
goto auth_withqop;
};
// auth_withoutqop:
MD5Final(HA2, &Md5Ctx);
CvtHex(HA2, HA2Hex);
// calculate response
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
MD5Update(&Md5Ctx, ":", 1);
goto end;
auth_withqop:
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, HEntity, HASHHEXLEN);
MD5Final(HA2, &Md5Ctx);
CvtHex(HA2, HA2Hex);
// calculate response
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, HA1, HASHHEXLEN);
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszNonce, strlen(pszNonce));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszNonceCount, strlen(pszNonceCount));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszCNonce, strlen(pszCNonce));
MD5Update(&Md5Ctx, ":", 1);
MD5Update(&Md5Ctx, pszQop, strlen(pszQop));
MD5Update(&Md5Ctx, ":", 1);
end:
MD5Update(&Md5Ctx, HA2Hex, HASHHEXLEN);
MD5Final(RespHash, &Md5Ctx);
CvtHex(RespHash, Response);
};

40
src/digcalc.h Normal file
View File

@ -0,0 +1,40 @@
/* TAKEN from rcf2617.txt */
#ifndef _DIGCALC_H_
#define _DIGCALC_H_
#define HASHLEN 16
typedef char HASH[HASHLEN];
#define HASHHEXLEN 32
typedef char HASHHEX[HASHHEXLEN+1];
#define IN
#define OUT
/* calculate H(A1) as per HTTP Digest spec */
void DigestCalcHA1(
IN char * pszAlg,
IN char * pszUserName,
IN char * pszRealm,
IN char * pszPassword,
IN char * pszNonce,
IN char * pszCNonce,
OUT HASHHEX SessionKey
);
/* calculate request-digest/response-digest as per HTTP Digest spec */
void DigestCalcResponse(
IN HASHHEX HA1, /* H(A1) */
IN char * pszNonce, /* nonce from server */
IN char * pszNonceCount, /* 8 hex digits */
IN char * pszCNonce, /* client nonce */
IN char * pszQop, /* qop-value: "", "auth", "auth-int" */
IN char * pszMethod, /* method from the request */
IN char * pszDigestUri, /* requested URL */
IN HASHHEX HEntity, /* H(entity body) if qop="auth-int" */
OUT HASHHEX Response /* request-digest or response-digest */
);
#endif

View File

@ -25,11 +25,14 @@
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <syslog.h>
static char const ident[]="$Id: " __FILE__ ": " PACKAGE "-" VERSION "-"\
BUILDSTR " $";
int log_to_syslog=0;
int debug_pattern=0;
@ -37,6 +40,9 @@ void log_set_pattern(int pattern) {
debug_pattern=pattern;
}
void log_set_tosyslog(int tosyslog) {
log_to_syslog=tosyslog;
}
/* for all the LOGGING routines:
They should figure out if we are running as a daemon, then write
@ -48,17 +54,26 @@ void log_debug(int class, char *file, int line, const char *format, ...) {
va_list ap;
time_t t;
struct tm *tim;
char string[128];
if ((debug_pattern & class) == 0) return;
va_start(ap, format);
if (! log_to_syslog) {
/* not running as daemon - log to STDERR */
time(&t);
tim=localtime(&t);
fprintf(stderr,"%2.2i:%2.2i:%2.2i %s:%i ", tim->tm_hour,
tim->tm_min, tim->tm_sec, file, line);
vfprintf(stderr, format, ap);
fprintf(stderr,"\n");
} else {
/* running as daemon - log via SYSLOG facility */
vsnprintf(string, sizeof(string), format, ap);
syslog(LOG_USER|LOG_DEBUG, "%s:%i %s", file, line, string);
}
va_end(ap);
fflush(stderr);
@ -71,15 +86,23 @@ void log_error(char *file, int line, const char *format, ...) {
va_list ap;
time_t t;
struct tm *tim;
char string[128];
va_start(ap, format);
if (! log_to_syslog) {
/* not running as daemon - log to STDERR */
time(&t);
tim=localtime(&t);
fprintf(stderr,"%2.2i:%2.2i:%2.2i ERROR:%s:%i ",tim->tm_hour,
tim->tm_min, tim->tm_sec,file,line);
tim->tm_min, tim->tm_sec, file, line);
vfprintf(stderr, format, ap);
fprintf(stderr,"\n");
} else {
/* running as daemon - log via SYSLOG facility */
vsnprintf(string, sizeof(string), format, ap);
syslog(LOG_USER|LOG_WARNING, "%s:%i %s", file, line, string);
}
va_end(ap);
fflush(stderr);
@ -92,15 +115,23 @@ void log_warn(char *file, int line, const char *format, ...) {
va_list ap;
time_t t;
struct tm *tim;
char string[128];
va_start(ap, format);
if (! log_to_syslog) {
/* not running as daemon - log to STDERR */
time(&t);
tim=localtime(&t);
fprintf(stderr,"%2.2i:%2.2i:%2.2i WARNING:%s:%i ",tim->tm_hour,
tim->tm_min, tim->tm_sec,file,line);
vfprintf(stderr, format, ap);
fprintf(stderr,"\n");
} else {
/* running as daemon - log via SYSLOG facility */
vsnprintf(string, sizeof(string), format, ap);
syslog(LOG_USER|LOG_NOTICE, "%s:%i %s", file, line, string);
}
va_end(ap);
fflush(stderr);
@ -114,6 +145,7 @@ void log_dump_buffer(int class, char *file, int line,
int i;
if ((debug_pattern & class) == 0) return;
if (log_to_syslog) return;
fprintf(stderr,"---BUFFER DUMP follows---\n");
for (i=0;i<length;i++) {

View File

@ -30,10 +30,14 @@
#define DBCLASS_NETTRAF 0x00000080 // network traffic
#define DBCLASS_CONFIG 0x00000100 // configuration
#define DBCLASS_RTP 0x00000200 // RTP proxy
#define DBCLASS_ACCESS 0x00000400 // Access list evaluation
#define DBCLASS_AUTH 0x00000800 // Authentication
void log_set_pattern(int pattern);
void log_set_tosyslog(int tosyslog);
#define DEBUG(F...) log_debug(1,__FILE__, __LINE__,F)
void log_set_pattern(int pattern);
#define DEBUGC(C,F...) log_debug(C,__FILE__, __LINE__,F)
void log_debug(int class, char *file, int line, const char *format, ...);

View File

@ -188,6 +188,9 @@ int proxy_request (sip_t *request) {
default:
DEBUGC(DBCLASS_PROXY,"request: refuse to proxy - UA not registered?");
WARN("request from/to unregistered UA (%s@%s)",
request->from->url->username,
request->from->url->host);
/* some clients seem to run amok when passing back a negative response */
// proxy_gen_response(request, 403 /*forbidden*/);
return 1;
@ -201,7 +204,7 @@ int proxy_request (sip_t *request) {
/* linphone-0.9.0pre4
take To address and place it into URI (at least the host part)
Linphone-0.9.0pre4 puts the proxy host in the request URI
if OUTBOUNT proxy is activated!
if OUTBOUND proxy is activated!
This is only a hack to recreate the proper final request URI.
This issue has been fixed in 0.9.1pre1
*/
@ -504,6 +507,8 @@ int proxy_del_myvia (sip_t *response) {
/*
* PROXY_REWRITE_INVITATION_BODY
*
* rewrites the outgoing INVITATION packet
*
*/
int proxy_rewrite_invitation_body(sip_t *mymsg){
body_t *body;

View File

@ -122,6 +122,12 @@ static int parse_config (FILE *configfile) {
{ "rtp_proxy_enable", TYP_INT4, &configuration.rtp_proxy_enable },
{ "user", TYP_STRING, &configuration.user },
{ "chrootjail", TYP_STRING, &configuration.chrootjail },
{ "hosts_allow_reg", TYP_STRING, &configuration.hosts_allow_reg },
{ "hosts_allow_sip", TYP_STRING, &configuration.hosts_allow_sip },
{ "hosts_deny_sip", TYP_STRING, &configuration.hosts_deny_sip },
{ "hosts_deny_sip", TYP_STRING, &configuration.hosts_deny_sip },
{ "proxy_auth_realm", TYP_STRING, &configuration.proxy_auth_realm },
{ "proxy_auth_passwd", TYP_STRING, &configuration.proxy_auth_passwd },
{0, 0, 0}
};
@ -168,6 +174,7 @@ static int parse_config (FILE *configfile) {
break;
case TYP_STRING:
// num=sscanf(ptr,"%a[^#]",(char**)configoptions[j].dest);
num=sscanf(ptr,"%as",(char**)configoptions[j].dest);
DEBUGC(DBCLASS_BABBLE,"STRING=%s",*(char**)configoptions[j].dest);
break;

View File

@ -54,15 +54,32 @@ void register_init(void) {
* handles register requests and updates the URL mapping table
* sts = 0 : successfully registered
* sts = 1 : registration failed
* sts = 2 : authentication needed
*/
int register_client(sip_t *my_msg) {
int i,j;
int i, j, sts;
int expires;
time_t time_now;
url_t *url1_to, *url1_contact;
url_t *url2_to, *url2_contact;
header_t *expires_hdr;
/*
do proxy authentication
*/
sts = authenticate_proxy(my_msg);
if (sts == 1) {
/* failed */
WARN("proxy authentication failed for %s@%s",
my_msg->to->url->username,my_msg->to->url->host);
return (1);
} else if (sts == 2) {
/* needed */
DEBUGC(DBCLASS_REG,"proxy authentication needed for %s@%s",
my_msg->to->url->username,my_msg->to->url->host);
return (2);
}
/*
fetch 1st Via entry and remember this address. Incomming requests
for the registered address have to be passed on to that host.
@ -181,8 +198,9 @@ void register_agemap(void) {
/*
* send answer to a registration request.
* flag = 0 -> positive answer
* flag != 0 -> negative answer
* flag = 0 -> positive answer (200)
* flag = 1 -> negative answer (503)
* flag = 2 -> proxy authentication needed (407)
*/
int register_response(sip_t *request, int flag) {
sip_t *response;
@ -195,8 +213,20 @@ int register_response(sip_t *request, int flag) {
header_t *expires_hdr;
/* ok -> 200, fail -> 503 */
if (flag == 0) code = 200;
else code = 503;
switch (flag) {
case 0:
code = 200; /* OK */
break;
case 1:
code = 503; /* failed */
break;
case 2:
code = 407; /* proxy authentication needed */
break;
default:
code = 503; /* failed */
break;
}
/* create the response template */
if ((response=msg_make_template_reply(request, code))==NULL) {
@ -210,6 +240,11 @@ int register_response(sip_t *request, int flag) {
msg_setexpires(response, expires_hdr->hvalue);
}
/* if we send back an proxy authentication needed,
include the Proxy-Authenticate field */
if (code == 407) {
auth_include_authrq(response);
}
/* get the IP address from existing VIA header */
msg_getvia (response, 0, &via);

46
src/security.c Normal file
View File

@ -0,0 +1,46 @@
/*
Copyright (C) 2002 Thomas Ries <tries@gmx.net>
This file is part of Siproxd.
Siproxd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Siproxd is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Siproxd; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <osip/smsg.h>
#include "siproxd.h"
#include "log.h"
/*
* do security and integrity checks on the received packet
*
* returns >0 if ok
* 0 if the packed did not pass the checks
*/
int securitycheck(char *sip_buffer, int size){
/* TODO: still way to go here ... */
return 1;
}

View File

@ -26,6 +26,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <getopt.h>
@ -60,6 +61,8 @@ int main (int argc, char *argv[])
{
int sts;
int i;
int access;
struct sockaddr_in from;
char buff [BUFFER_SIZE];
sip_t *my_msg=NULL;
@ -69,13 +72,11 @@ int main (int argc, char *argv[])
char configfile[64]="siproxd"; /* basename of configfile */
int config_search=1; /* search the config file */
/* prepare default configuration */
configuration.debuglevel=0;
configuration.daemonize=0;
/*
* prepare default configuration
*/
memset (&configuration, 0, sizeof(configuration));
configuration.sip_listen_port=SIP_PORT;
configuration.inboundhost=NULL;
configuration.outboundhost=NULL;
configuration.user=NULL;
log_set_pattern(configuration.debuglevel);
@ -161,6 +162,7 @@ int main (int argc, char *argv[])
/* close STDIN, STDOUT, STDERR */
close(0);close(1);close(2);
#endif
log_set_tosyslog(1);
}
@ -178,17 +180,28 @@ int main (int argc, char *argv[])
/* got input, process */
DEBUGC(DBCLASS_BABBLE,"back from sip_wait");
i=sipsock_read(&buff, sizeof(buff));
/*
* more integrity checks of received packet needed !!
* it's possible to crash msg_parse with some crap-input.
*/
i=sipsock_read(&buff, sizeof(buff), &from);
/* evaluate the access lists */
access=check_accesslist(from);
if (access == 0) continue; /* there are no resources to free */
/* integrity checks */
sts=securitycheck(buff, i);
if (sts == 0) continue; /* there are no resources to free */
/* parse the received message */
sts=msg_init(&my_msg);
sts=msg_parse( my_msg, buff);
/*
* if message parsing was ok go on - otherwise skip
*/
if (sts != 0) continue;
if (sts != 0) {
ERROR("msg_init() failed... this is not good");
continue; /* skip, there are no resources to free */
}
sts=msg_parse(my_msg, buff);
if (sts != 0) {
ERROR("msg_parse() failed... this is not good");
goto end_loop; /* skip and free resources */
}
DEBUGC(DBCLASS_SIP,"received SIP type %s:%s",
(MSG_IS_REQUEST(my_msg))? "REQ" : "RES",
@ -196,19 +209,34 @@ int main (int argc, char *argv[])
/* if RQ REGISTER, just register and send an answer */
if (MSG_IS_REGISTER(my_msg) && MSG_IS_REQUEST(my_msg)) {
if (access & ACCESSCTL_REG) {
sts = register_client(my_msg);
sts = register_response(my_msg, sts);
} else {
WARN("non-authorized registration attempt from %s",
inet_ntoa(from.sin_addr));
}
/* MSG is a request, add current via entry,
* do a lookup in the URLMAP table and
* send to the final destination */
} else if (MSG_IS_REQUEST(my_msg)) {
if (access & ACCESSCTL_SIP) {
sts = proxy_request(my_msg);
} else {
WARN("non-authorized request received from %s",
inet_ntoa(from.sin_addr));
}
/* MSG is a response, remove current via and
* send to next via in chain */
} else if (MSG_IS_RESPONSE(my_msg)) {
if (access & ACCESSCTL_SIP) {
sts = proxy_response(my_msg);
} else {
WARN("non-authorized response received from %s",
inet_ntoa(from.sin_addr));
}
/* unsupported message */
} else {
@ -221,6 +249,7 @@ int main (int argc, char *argv[])
/*
* free the SIP message buffers
*/
end_loop:
msg_free(my_msg);
free(my_msg);

View File

@ -23,7 +23,7 @@
/* sock.c */
int sipsock_listen (void);
int sipsock_wait(void);
int sipsock_read(void *buf, size_t bufsize);
int sipsock_read(void *buf, size_t bufsize, struct sockaddr_in *from);
int sipsock_send_udp(int *sock, struct in_addr addr, int port,
char *buffer, int size, int allowdump);
int sockbind(struct in_addr ipaddr, int localport);
@ -50,7 +50,7 @@ int get_ip_by_host(char *hostname, struct in_addr *addr);
int compare_url(url_t *url1, url_t *url2);
void secure_enviroment (void);
/* config.c */
/* readconf.c */
int read_config(char *name, int search);
/* rtpproxy.c */
@ -60,6 +60,17 @@ int rtp_start_fwd (call_id_t *callid,
struct in_addr lcl_client_ipaddr, int lcl_clientport);
int rtp_stop_fwd (call_id_t *callid);
/* accessctl.c */
int check_accesslist (struct sockaddr_in from);
/* security.c */
int securitycheck(char *sip_buffer, int size);
/* auth.c */
int authenticate_proxy(sip_t *request);
int auth_include_authrq(sip_t *response);
/*
* table to hold the client registrations
@ -88,6 +99,11 @@ struct siproxd_config {
int rtp_proxy_enable;
char *user;
char *chrootjail;
char *hosts_allow_reg;
char *hosts_allow_sip;
char *hosts_deny_sip;
char *proxy_auth_realm;
char *proxy_auth_passwd;
};
@ -107,6 +123,11 @@ struct siproxd_config {
#define DNS_MAX_AGE 60 // maximum age of an cache entry (sec)
#define HOSTNAME_SIZE 32 // max string length of a hostname
#define ACCESSCTL_SIP 1 // for access control - SIP allowed
#define ACCESSCTL_REG 2 // --"-- - registrations allowed
/*
* optional hacks
*/

View File

@ -78,9 +78,12 @@ int sipsock_wait(void) {
return sts;
}
int sipsock_read(void *buf, size_t bufsize) {
int sipsock_read(void *buf, size_t bufsize, struct sockaddr_in *from) {
int count;
count=read(listen_socket, buf, bufsize);
socklen_t fromlen;
fromlen=sizeof(struct sockaddr);
count=recvfrom(listen_socket, buf, bufsize, 0, from, &fromlen);
DEBUGC(DBCLASS_NET,"received UDP packet, count=%i", count);
DUMP_BUFFER(DBCLASS_NETTRAF, buf, count);

View File

@ -179,7 +179,7 @@ int get_ip_by_host(char *hostname, struct in_addr *addr) {
for (i=0; i<DNS_CACHE_SIZE; i++) {
if (dns_cache[i].hostname[0]=='\0') continue;
if ( (dns_cache[i].timestamp+DNS_MAX_AGE) < t ) {
DEBUGC(DBCLASS_DNS, "cleaning DNS cache, entry %i)", i);
DEBUGC(DBCLASS_DNS, "cleaning DNS cache (entry %i)", i);
memset (&dns_cache[i], 0, sizeof(dns_cache[0]));
}
}