summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--INSTALL27
-rw-r--r--Makefile86
-rw-r--r--README229
-rw-r--r--TODO15
-rw-r--r--VERSIONS39
-rw-r--r--configs/astmanproxy.conf (renamed from astmanproxy.conf)45
-rw-r--r--configs/astmanproxy.users10
-rw-r--r--configs/ssl.conf154
-rw-r--r--doc/README.csv (renamed from README.csv)0
-rw-r--r--doc/README.http (renamed from README.http)0
-rw-r--r--doc/README.standard (renamed from README.standard)0
-rw-r--r--doc/README.xml (renamed from README.xml)0
-rw-r--r--http.c137
-rw-r--r--samples/httpast2.html12
-rw-r--r--src/astmanproxy.c (renamed from astmanproxy.c)121
-rw-r--r--src/common.c (renamed from common.c)122
-rw-r--r--src/config.c (renamed from config.c)49
-rw-r--r--src/config_perms.c125
-rw-r--r--src/csv.c (renamed from csv.c)7
-rw-r--r--src/dlfcn.c (renamed from dlfcn.c)0
-rw-r--r--src/http.c155
-rw-r--r--src/include/astmanproxy.h (renamed from astmanproxy.h)54
-rw-r--r--src/include/dlfcn-compat.h (renamed from dlfcn-compat.h)0
-rw-r--r--src/include/endian.h60
-rw-r--r--src/include/md5.h18
-rw-r--r--src/include/poll-compat.h (renamed from poll-compat.h)0
-rw-r--r--src/include/ssl.h89
-rw-r--r--src/log.c (renamed from log.c)0
-rw-r--r--src/md5.c260
-rw-r--r--src/poll.c (renamed from poll.c)0
-rw-r--r--src/proxyfunc.c (renamed from proxyfunc.c)160
-rw-r--r--src/ssl.c438
-rw-r--r--src/standard.c (renamed from standard.c)13
-rw-r--r--src/xml.c (renamed from xml.c)11
34 files changed, 2045 insertions, 391 deletions
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..1842a67
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,27 @@
+Quick Install Instructions
+--------------------------
+
+Requirements: openssl, openssl-devel
+
+Installation is easy:
+
+1. make
+2. make install
+3. To run:
+
+astmanproxy -d (for debug mode)
+astmanproxy -dddddd (for more debug info)
+astmanproxy (for background mode)
+
+Other make actions:
+
+make cert Makes initial proxy server certificate
+make certificate Forces remake of proxy server certificate)
+make clean Remove all binary object files from source tree
+
+Please read README and docs/README.* for info on specific modules.
+
+Enjoy!
+
+--------------------------------------------
+(C) 2005-2006 David C. Troy, dave@popvox.com
diff --git a/Makefile b/Makefile
index 66ec1b8..4b22697 100644
--- a/Makefile
+++ b/Makefile
@@ -3,15 +3,20 @@
OSARCH=$(shell uname -s)
OSREV=$(shell uname -r)
-VERSION := 1.13
-DESTDIR :=
+VERSION := 1.20pre
+DESTDIR ?=
CONFDIR:=/etc/asterisk
CONFDIR_REAL := $(DESTDIR)/etc/asterisk
+PERMDIR:=/etc/asterisk
+PERMDIR_REAL := $(DESTDIR)/etc/asterisk
LIBDIR := $(DESTDIR)/usr/lib/astmanproxy
CONFFILE := astmanproxy.conf
+PERMFILE := astmanproxy.users
DISTDIR := /var/www/html/astmanproxy
-
+CERTDIR := /var/lib/asterisk/certs
+PROXYCERT := $(CERTDIR)/proxy-server.pem
+PROXYSSLCONF := $(CONFDIR)/proxy-ssl.conf
CC := gcc
#LIBS := -lpthread
@@ -20,25 +25,27 @@ PREFIX:= /usr/local
BINDIR := $(DESTDIR)$(PREFIX)/sbin
# For compilation dependencies
-MODS := astmanproxy config common proxyfunc log
+MODS := astmanproxy config config_perms common proxyfunc log ssl md5
HANDLERS := xml standard csv http
SOBJS := $(HANDLERS:%=%.so)
+LIBS := -lssl
ifeq (${OSARCH},Darwin)
- LIBS=-lresolv
+ LIBS+=-lresolv
CFLAGS+=-D__Darwin_
-# -DFINK_BUILD
BINDIR=/opt/sbin
LIBDIR=/opt/lib/astmanproxy
CONFDIR=/opt/etc/asterisk
CONFDIR_REAL=/opt/etc/asterisk
+ PERMDIR=/opt/etc/asterisk
+ PERMDIR_REAL=/opt/etc/asterisk
LOGDIR=/opt/log/asterisk
OBJS+=dlfcn.o poll.o
ASTLINK=-Wl,-force_flat_namespace,-dynamic
SOLINK=-dynamic -bundle -undefined suppress -force_flat_namespace
else
#These are used for all but Darwin
- LIBS=-ldl -pthread
+ LIBS+=-ldl -pthread
ASTLINK=-Wl,-E
SOLINK=-shared -Xlinker -x
LOGDIR=/var/log/asterisk
@@ -47,17 +54,19 @@ endif
OBJS += $(MODS:%=%.o)
MODDIR := $(LIBDIR)/modules
CONF_TARGET:= $(CONFDIR_REAL)/$(CONFFILE)
-DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"' '-DMDIR="$(MODDIR)"'
+PERM_TARGET:= $(PERMDIR_REAL)/$(PERMFILE)
+DEFINES:='-DPROXY_VERSION="$(VERSION)"' '-DCDIR="$(CONFDIR)"' '-DCFILE="$(CONFFILE)"'
+DEFINES+='-DMDIR="$(MODDIR)"' '-DPDIR="$(PERMDIR)"' '-DPFILE="$(PERMFILE)"'
+VPATH = src
# Add -g below for debug/GDB symbols
-#CFLAGS+= $(DEFINES) -Wall -O2 -dynamic -D_REENTRANT -fPIC
-CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC
+CFLAGS+= $(DEFINES) -Wall -O2 -D_REENTRANT -fPIC -Isrc/include -I/usr/include/openssl -I-
# For printing only
SRCS := $(MODS:%=%.c)
HDRS := astmanproxy.h
-all: astmanproxy
+all: astmanproxy cert
astmanproxy: $(OBJS) $(SOBJS)
$(CC) $(CFLAGS) -o $@ $(ASTLINK) $(OBJS) $(LIBS)
@@ -68,6 +77,56 @@ $(OBJS): %.o: %.c
$(SOBJS): %.so: %.o
$(CC) $(SOLINK) $< -o $@
+SERIAL=`date "+%Y%m%d%H%M%S"`
+
+cert:
+ if [ ! -f $(PROXYCERT) ]; then \
+ umask 77 ; \
+ PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
+ PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
+ if [ ! -f $(PROXYSSLCONF) ]; then \
+ install ./configs/ssl.conf $(PROXYSSLCONF); \
+ fi; \
+ /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config $(PROXYSSLCONF) ; \
+ mkdir -p $(CERTDIR); \
+ cat $$PEM1 > $(PROXYCERT) ; \
+ echo "" >> $(PROXYCERT) ; \
+ cat $$PEM2 >> $(PROXYCERT) ; \
+ rm $$PEM1 $$PEM2; \
+ fi
+
+certificate:
+ createcert="1"; \
+ if [ -f $(PROXYCERT) ]; then \
+ echo -n "The certificate already exists, Do you really want to create new one(yes/no)?"; \
+ read answer; \
+ if [ "$$answer" = "yes" ]; then \
+ echo "I am creating a new certificate, Old one is copied as server.pem.old ";\
+ sudo cp /var/lib/asterisk/certs/server.pem /var/lib/asterisk/certs/server.pem.old; \
+ elif [ "$$answer" = "no" ]; then \
+ echo "Certificate already exists, I am not creating a new certificate,";\
+ createcert="0"; \
+ else \
+ echo "You need to enter either yes or no"; \
+ createcert="0"; \
+ fi; \
+ fi; \
+ if [ "$$createcert" = "1" ]; then \
+ umask 77 ; \
+ PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
+ PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \
+ if [ ! -f $(PROXYSSLCONF) ]; then \
+ install ./configs/ssl.conf $(PROXYSSLCONF); \
+ fi; \
+ /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config $(PROXYSSLCONF) ; \
+ mkdir -p $(CERTDIR); \
+ cat $$PEM1 > $(PROXYCERT) ; \
+ echo "" >> $(PROXYCERT) ; \
+ cat $$PEM2 >> $(PROXYCERT) ; \
+ rm $$PEM1 $$PEM2; \
+ fi
+
+
install: uninstall all
install -d $(BINDIR)
install astmanproxy $(BINDIR)
@@ -78,6 +137,9 @@ install: uninstall all
if [ ! -f $(CONF_TARGET) ]; then \
install $(CONFFILE) $(CONF_TARGET); \
fi
+ if [ ! -f $(PERM_TARGET) ]; then \
+ install $(PERMFILE) $(PERM_TARGET); \
+ fi
@echo "Installation Complete!"
uninstall:
@@ -92,7 +154,7 @@ dist: clean
/usr/bin/ssh www.popvox.com "ln -sf $(DISTDIR)/astmanproxy-${VERSION}-*.tgz $(DISTDIR)/astmanproxy-latest.tgz"
clean:
- rm -f *.o *.so core *~ astmanproxy;
+ rm -f *.o *.so core *~ astmanproxy proxy-server.pem;
print:
more Makefile $(HDRS) $(SRCS) | enscript -Ec -2r -j; exit 0
diff --git a/README b/README
index 7369dd2..0e8c350 100644
--- a/README
+++ b/README
@@ -1,9 +1,10 @@
-astmanproxy README (c) 2005 David C. Troy
+astmanproxy README
+(c) 2005-2006 David C. Troy, dave@popvox.com
------------------------------------------------------------------
FOREWORD & QUICK START
The need for a proxy to Asterisk's manager interface has been
-clear; almost all GUI's and other interfaces to asterisk implement a
+clear; almost all GUIs and other interfaces to asterisk implement a
proxy of some kind. Why? A proxy offers:
- A single persistent connection to asterisk
@@ -12,13 +13,15 @@ proxy of some kind. Why? A proxy offers:
- Less connections and networking load for asterisk
This proxy began as the the perl/select based "simpleproxy.pl" and
-has since evolved into a full multithreaded quasi-stateful proxy
-based in c/pthreads. It is now capable of serving as the basis for
-an extensible application framework for multiple Asterisk servers.
+has since evolved into a full multithreaded stateful proxy written
+in c/pthreads. It can serve as the basis for an extensible
+application framework for communication with multiple Asterisk
+servers.
-New features in version 1.1 include:
+Features include:
- Multiple Input/Output formats: HTTP, XML, CSV, and Standard
+ - SSL Support for clients & servers (including HTTPS clients)
- API for addition of new, modular I/O formats
- Ability to support communication with multiple Asterisk Servers
- I/O Formats selectable on a per-client basis
@@ -26,9 +29,10 @@ New features in version 1.1 include:
For example, you can use Astmanproxy as a single point of contact to
communicate with multiple Asterisk servers.
-You can use Astmanproxy as the basis for a web-based application:
-send it data using HTTP POST or HTTP GET, and receive XML output.
-No web server required!
+You can use Astmanproxy as the basis for a web-based application:
+send it data using HTTP POST or HTTP GET, and receive XML output.
+Or use HTTP POST and get Standard (text/plain) output back!
+Astmanproxy speaks HTTP internally, so no web server is required!
You can use Astmanproxy as an XML feed for a .NET program that keeps
track of Asterisk's state. Or as an interface for injecting quick
@@ -42,50 +46,37 @@ To get started quickly, simply:
Edit the configuration file:
vi /etc/asterisk/astmanproxy.conf
+Optionally edit the other config files:
+ vi /etc/asterisk/astmanproxy.users
+ vi /etc/asterisk/ssl.conf
+
Start the program:
astmanproxy
To view debug output, start astmanproxy in debug mode:
astmanproxy -d
+For more debug output, add more -d's:
+ astmanproxy -ddddddd
+
You may want to start astmanproxy at boot. In that case, you might
place it in /etc/rc.d/rc.local:
/usr/local/sbin/astmanproxy
Please send your feedback! We are looking for contributors to add
-support for new I/O formats and to help debug the more complicated
-HTTP and XML IO Handlers!
+support for new I/O formats and add new features!
Contributions:
Paypal via dave@toad.net; beer accepted at Astricon events
===================================================================
-INSTALLATION:
-
- make
- make install
-
-The default configuration file location is
-/etc/asterisk/astmanproxy.conf, and that file will be automatically
-created upon installation if it does not already exist.
-
-TO RUN:
-
-Launch from /etc/rc.d/rc.local or startup script.
-
-/usr/local/sbin/astmanproxy (or whatever your path is)
-
-Launch with no options to run as daemon. To debug, use option "-d",
-and to display version information, use option "-v".
-
-===================================================================
Additional Proxy Features
In addition to exposing the entire Asterisk Manager API as a
-pass-through, non-interpreting proxy, 'astmanproxy' can parse client
-input where desired; this could be used in the future to add new
-features that make sense to be in a proxy but that don't necessarily
-need to be in Asterisk proper.
+pass-through, non-interpreting proxy, 'astmanproxy' can parse client
+input where desired; this could be used in the future to add new
+features that should exist in a proxy but that don't
+necessarily need to be in Asterisk proper.
There are some proxy-specific headers that you can specify in your
applications now:
@@ -134,18 +125,34 @@ Server: (x.x.x.x|hostname)
should exactly match the entry in your config host= section,
or whatever name you used with ProxyAction: AddServer.
+ProxyKey: secret
+Action: Originate
+...
+ActionID: ...
+ You can use this as a simple authentication mechanism.
+ Rather than have to login with a username & password,
+ you can specify a ProxyKey that must be passed from
+ a client before requests are processed. This is helpful
+ in situations where you would like to authenticate and
+ execute an action in a single step. See the sample
+ config file for more information.
+
The proxy also intercepts the following Actions:
Action: Login
- Since the proxy logs in on behalf of your clients, and you are
- using key-based or network-level security to secure the proxy,
- logins should not be passed to asterisk. Ideally, your apps
- will be written not to issue Logins in the first place, but
- if you do, the proxy will intercept them and NOT pass them
- to Asterisk. Instead, it will respond with an Authentication
- Successful message, just as Asterisk would upon successful
- authentication. Note it will do this regardless of information
- supplied.
+ You can login to astmanproxy just as you would the Asterisk
+ Manager Interface. The user credentials are stored in
+ astmanproxy.users.
+
+Action: Challenge
+ Astmanproxy now supports the MD5 challenge authentication
+ mechanism. See section below for more information on
+ this authentication mechanism and how you can use it
+ in your applications to avoid having to send a password
+ over the internet, and instead use a MD5 challenge to
+ hash your password before sending. Note that this is
+ somewhat less of an issue with SSL support now enabled,
+ however, some apps require this mechanism, and we support it.
Action: Logoff
You don't want your applications logging the proxy off of
@@ -160,23 +167,139 @@ Blank Commands
blank command blocks.
===================================================================
-Author Contact Info
+On the 'Action: Challenge' Authentication Mechanism
+
+John Todd wrote this excellent summary of the Action: Challenge
+Authentication Mechanism, and it accurately describes the
+implementation included in astmanproxy:
+
+While the SSL encryption of the AMI is great, it's always a good
+policy to never send passwords at all if you have an alternative.
+
+ After connecting to the AMI port, send this message:
+
+ Action: Challenge
+ AuthType: MD5
+
+ You should receive a challenge string:
+
+ Response: Success
+ Challenge: 125065091
+
+Then, assuming that the manager username is "joebob" and the
+password is "yoyodyne11", perform this on a shell line of a handy
+UNIX system (you programmers will figure out how to do this with a
+library call, I'm sure):
+
+ bash-3.00# md5 -s 125065091yoyodyne11
+ MD5 ("125065091yoyodyne11") = e83a9e59e7c8d1bb6554982275d05016
+ bash-3.00#
+
+ Now use this key to log in, so type this to the AMI:
+
+ Action: Login
+ AuthType: MD5
+ Username: joebob
+ Key: e83a9e59e7c8d1bb6554982275d05016
+
+ ...and you'll get:
+
+ Response: Success
+ Message: Authentication accepted
+
+===================================================================
+On Astmanproxy's SSL Support
-This code is intended primarily as a foundation for others to add
-new features and capabilities going forward. While I will attempt
-to keep up with it and add to it, I make no guarantees that I'll be
-able to do that. My hope is that the wonderful asterisk community
-will start making changes, and that those changes can be managed
-through Digium's great CVS process.
+Support for SSL on the Asterisk Manager Interface has recently been
+contributed to the Asterisk project (see Digium #6812).
-However, you can concact me at dave@popvox.com, and I will endeavor
-to post the latest code here:
+This SSL implementation has been tested by several people and seems
+to work fine. While it is not in a mainline Asterisk distribution
+yet (in SVN Trunk only right now), it is likely that AMI will soon
+support SSL natively.
+I felt that it was important that Astmanproxy support the same SSL
+mechanism as Asterisk; we have been talking about adding SSL/TLS
+for some time. So, now it's been incorporated.
+
+This means you can implement scenarios like:
+ client <-> proxy <-> n*asterisk
+with end-to-end SSL security.
+
+To make Astmanproxy talk to asterisk, turn on the 'usessl' option
+in the server host specification (see astmanproxy.conf).
+
+To have Astmanproxy talk to clients via SSL, be sure to enable
+'allowencryptedconnections' in the astmanproxy.conf file.
+
+To have Astmanproxy accept ONLY SSL connections, you should
+enable 'allowencryptedconnections' and disable
+'allowunencryptedconnections'. We've endeavored to use the same
+configuration setting names as in manager.conf with the SSL
+implementation in #6812.
+
+===================================================================
+Now Supports HTTPS Natively!
+
+One really interesting side effect of having both SSL and HTTP support
+natively is that we in fact now support HTTPS!
+
+With the proxy configured on localhost:1234, you can do things
+along these lines:
+
+https://localhost:1234/?Action=ShowChannels&ActionID=Foo
+
+This has been tested fairly extensively with good results. The
+HTTP handler supports both GET and POST and can properly deal
+with XML or Standard output formats. With Autofilter=on,
+this paradigm is ideal for creating a simple REST-like interface
+into Asterisk (even multiple boxes!) with no web servers needed.
+
+===================================================================
+Software Updates, Author Info, and How to Contribute
+
+Digium has graciously agreed to host the development of AstManProxy
+on their Subversion Community Server.
+
+Latest releases can be found here:
+http://svncommunity.digium.com/view/astmanproxy
+
+For development branches & experimental features:
+http://svncommunity.digium.com/view/astmanproxy/branches
+
+For current development/stable snapshot:
+http://svncommunity.digium.com/view/astmanproxy/trunk
+
+For stable release versions:
+http://svncommunity.digium.com/view/astmanproxy/tags
+
+To download from these repositories:
+
+ - Install Subversion (yum -y install subversion -- or equivalent)
+ - svn checkout http://svncommunity.digium.com/svn/astmanproxy/trunk
+
+Be sure to use the full URL path to the version you wish to check out;
+for example, do not checkout the 'branches' tree, but instead choose
+which branch to checkout, as in:
+
+http://svncommunity.digium.com/view/astmanproxy/branches/1.2x
+
+I will also try to post current tarballs here:
http://www.popvox.com/astmanproxy
-Donations are accepted via paypal to dave@toad.net.
+Donations are accepted via paypal to dave@toad.net; beer is also
+accepted at Astricon events. :)
+
+To contact me about contributing to the project, please email:
+dave@popvox.com
+
+I acknowledge all contributions and encourage your experimentation!
+AstManProxy would not be where it is without your support!!
===================================================================
+AstManProxy Background Information
+----------------------------------
+
Developing web-based realtime applications for the asterisk
open-source PBX often requires interacting with asterisk's Manager
interface. The Asterisk Manager runs on port 5038 by default and
diff --git a/TODO b/TODO
index be076d0..a94b30e 100644
--- a/TODO
+++ b/TODO
@@ -1,15 +1,12 @@
-'connected' flag; do not set until we get "authentication accepted' back from server
+write SOAP methods for http.c to use
+clean up reconnect to lost asterisk server (socket reuse)
+clean up debugmsg instances to add if (debug)
+deal with http non-conformity better, and act on POST MIME-type inputs
+
Check for module versions; do not run without modules installed
use a key? see loader.c in *
State maintenance modules
code formatting
-tcpwrappers/access control/connect mask
-tls?
-better dir structure in source tree
-subversion with digium
+tcpwrappers/access control/connect mask?
libtool/autoconf/automake support
SetInputFormat proxyaction?
-detect old config file fmt and puke
-
-kill http clients better?
-snmp?
diff --git a/VERSIONS b/VERSIONS
index 8a29a2b..72c7d89 100644
--- a/VERSIONS
+++ b/VERSIONS
@@ -1,17 +1,16 @@
-0.99a Initial Beta Release
-0.99b Moved WriteXMLClient into xml.c
- Fixed XML tag bug (Bryan Ballard)
- Fixed ast_log instances
-1.0 First major release
-1.0a Added minor Makefile changes
- Added Debian package support (Tzafrir Cohen)
-1.0b Action: logoff (lowercase) was not intercepted properly (Steven Blatchford)
- Fixed SetOutputFormat documentation error (Steven Blatchford)
-1.0c Intercept empty command blocks; do not pass to Asterisk
- Added call for proxyerror routine, previously unreferenced
- Intercept Action: Login; always authenticates and does not pass to *
-1.0d Properly intercept SIGPIPE (Brian Jones)
+1.20pre Now using ast_carefulwrite for non-blocking writes to all sockets
+ Added asteriskwritetimeout config setting for asterisk write timeout
+ Added clientwritetimeout config setting for client write timeout
+ Added support for astmanproxy.users user authentication (Steve Davies)
+ Added support for Action: Challenge/AuthType: MD5 authentication
+ Added challenge field to mansession structure for use by Action: Challenge
+ Added writetimeout var to mansession structure for use by ast_carefulwrite
+ Added SSL Support (Remco Treffkorn, Mahesh Karoshi, John Todd; Tello Corp)
+ Added 'usessl' option for connecting to asterisk servers
+ Fixed proxykey pointer bug (Steve Davies)
+ Ditched autodisconnect handler property in favor of inputcomplete/outputcomplete
+------------------------------------------------------------------------------------------------
1.1pre2 Completely Modularized and Abstracted Input/Output formats
Cleaned up session write mutex code (Peter Loeppky)
Fixed message initialization bug -- using wrong sizeof
@@ -42,3 +41,17 @@
Added support for Mac OS X (Tested on 10.3.9); BSD may also work
Aborts on old config file format (detects incomplete server spec)
+------------------------------------------------------------------------------------------------
+0.99a Initial Beta Release
+0.99b Moved WriteXMLClient into xml.c
+ Fixed XML tag bug (Bryan Ballard)
+ Fixed ast_log instances
+1.0 First major release
+1.0a Added minor Makefile changes
+ Added Debian package support (Tzafrir Cohen)
+1.0b Action: logoff (lowercase) was not intercepted properly (Steven Blatchford)
+ Fixed SetOutputFormat documentation error (Steven Blatchford)
+1.0c Intercept empty command blocks; do not pass to Asterisk
+ Added call for proxyerror routine, previously unreferenced
+ Intercept Action: Login; always authenticates and does not pass to *
+1.0d Properly intercept SIGPIPE (Brian Jones)
diff --git a/astmanproxy.conf b/configs/astmanproxy.conf
index ec19621..be28edf 100644
--- a/astmanproxy.conf
+++ b/configs/astmanproxy.conf
@@ -1,10 +1,12 @@
; astmanproxy.conf
; Asterisk Manager Proxy Configuration Sample
-; (C) 2005 David C. Troy
+; (C) 2005-2006 David C. Troy - dave@popvox.com
; List of asterisk host(s) you want to proxy
-; host = ip_addr, port, user, secret, events
-host = localhost, 5038, dave, moo, on
+; host = ip_addr, port, user, secret, events, use_ssl
+host = localhost, 5038, dave, moo, on, off
+
+;host = 192.168.1.173, 5038, dave, moo, on, on
;host = 127.0.0.2, 5038, user, secret, on
;host = otherhost, 5038, user, secret, on
;host = newhost, 5030, user, secret, off
@@ -19,6 +21,24 @@ retryinterval = 2
; use 0 for infinitely, or some finite number
maxretries = 10
+; How long do we wait on the manager port for an SSL session start? (ms)
+sslclienthellotimeout = 200
+
+; Do we accept encrypted SSL manager connections?
+acceptencryptedconnection = yes
+
+; Do we accept unencrypted manager connections?
+acceptunencryptedconnection = yes
+
+; Amount of time to wait before timing out on writes to asterisk
+asteriskwritetimeout=100
+
+; Amount of time to wait before timing out on writes to clients
+clientwritetimeout=200
+
+; Our server-side SSL certificate; what we use when answering clients
+certfile = /var/lib/asterisk/certs/proxy-server.pem
+
; Address for proxy to listen on, can be set to * or x.x.x.x format
; recommend that you listen only on 127.0.0.1 or on an interface that
; is otherwise locked down to a trusted host, since the proxy
@@ -28,6 +48,10 @@ listenaddress = *
; Port for proxy to listen on
listenport = 1234
+; Do we require authentication (either proxykey or astmanproxy.users entry)?
+; See README and astmanproxy.users for more info
+authrequired = no
+
; Setting a proxy key requires proxy client connections to
; specify a ProxyKey: keyvalue header in the first incoming request
; to the proxy. Once this is done the client remains authenticated.
@@ -37,7 +61,7 @@ listenport = 1234
; in place and well understood.
; proxykey = foobar
-; user and group for proxy to run as; will NOT run as root!
+; local user and group for proxy to run as; will NOT run as root!
proc_user = nobody
proc_group = nobody
@@ -47,6 +71,15 @@ proc_group = nobody
inputformat = standard
outputformat = standard
+; to enable REST/XMLRPC-like functionality, try this combo.
+; this gives you http input (POST or GET) and either
+; text/xml or text/plain output with NO webserver required!
+; to access: http://[host]:1234/?Action=Ping&ActionID=Foo
+;
+; inputformat = http
+; outputfomat = xml|standard
+; autofilter = on
+
; set autofilter to be on or off by default
; with autofilter on, you can automatically filter responses
; to include only messages related to a specific actionid,
@@ -58,6 +91,6 @@ outputformat = standard
autofilter = off
; location of logfile -- will be owned by proc_user/proc_group
-;logfile = /var/log/asterisk/astmanproxy.log
-logfile = /opt/log/asterisk/astmanproxy.log
+;logfile = /opt/log/asterisk/astmanproxy.log
+logfile = /var/log/asterisk/astmanproxy.log
diff --git a/configs/astmanproxy.users b/configs/astmanproxy.users
new file mode 100644
index 0000000..9e31056
--- /dev/null
+++ b/configs/astmanproxy.users
@@ -0,0 +1,10 @@
+; Astmanproxy user list
+;
+; Reload permissions by sending a SIGHUP
+;
+; "user" is the username, secret is the password, and the (optional)
+; channel setting causes filtering of events only for the specified
+; channel to be sent to this user.
+;
+; user=secret,channel,out_context (to Asterisk),in_context (From Asterisk)
+steve=steve,SIP/snom190,local,
diff --git a/configs/ssl.conf b/configs/ssl.conf
new file mode 100644
index 0000000..d10d9a1
--- /dev/null
+++ b/configs/ssl.conf
@@ -0,0 +1,154 @@
+# Asterisk SSL configuration
+#
+# OpenSSL configuration file for custom Certificate Authority. Use a
+# different openssl.cnf file to generate certificate signing requests;
+# this one is for use only in Certificate Authority operations (csr ->
+# cert, cert revocation, revocation list generation).
+#
+# Be sure to customize this file prior to use, e.g. the commonName and
+# other options under the root_ca_distinguished_name section.
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+
+[ ca ]
+default_ca = MyAsteriskCA
+
+[ MyAsteriskCA ]
+dir = .
+# unsed at present, and my limited certs can be kept in current dir
+#certs = $dir/certs
+new_certs_dir = $dir/newcerts
+crl_dir = $dir/crl
+database = $dir/index
+
+certificate = $dir/ca-cert.pem
+serial = $dir/serial
+crl = $dir/ca-crl.pem
+private_key = $dir/private/ca-key.pem
+RANDFILE = $dir/private/.rand
+
+x509_extensions = usr_cert
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt = ca_default
+cert_opt = ca_default
+
+default_crl_days= 30
+default_days = 7300
+# if need to be compatible with older software, use weaker md5
+default_md = sha1
+# MSIE may need following set to yes?
+preserve = no
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = US
+stateOrProvinceName = CA
+organizationName = XYZ
+organizationalUnitName = XYZ
+commonName = asterisk
+emailAddress = root@localhost
+
+# For the 'anything' policy
+# At this point in time, you must list all acceptable 'object'
+# types.
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = ./private/ca-key.pem
+default_md = sha1
+
+prompt = no
+distinguished_name = root_ca_distinguished_name
+
+x509_extensions = v3_ca
+
+# Passwords for private keys if not present they will be prompted for
+# input_password = secret
+# output_password = secret
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+string_mask = nombstr
+
+# req_extensions = v3_req
+
+[ root_ca_distinguished_name ]
+commonName = NoSuchCA CA
+countryName = US
+stateOrProvinceName = California
+localityName = San Mateo
+0.organizationName = domain.net
+emailAddress = nobody@localhost
+
+[ usr_cert ]
+
+# These extensions are added when 'ca' signs a request.
+
+# This goes against PKIX guidelines but some CAs do it and some software
+# requires this to avoid interpreting an end user certificate as a CA.
+
+basicConstraints=CA:FALSE
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer:always
+
+nsCaRevocationUrl = https://www.sial.org/ca-crl.pem
+#nsBaseUrl
+#nsRevocationUrl
+#nsRenewalUrl
+#nsCaPolicyUrl
+#nsSslServerName
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+[ v3_ca ]
+
+
+# Extensions for a typical CA
+
+# PKIX recommendation.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+[ crl_ext ]
+
+# CRL extensions.
+# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always,issuer:always
diff --git a/README.csv b/doc/README.csv
index 5f77c09..5f77c09 100644
--- a/README.csv
+++ b/doc/README.csv
diff --git a/README.http b/doc/README.http
index 63268a9..63268a9 100644
--- a/README.http
+++ b/doc/README.http
diff --git a/README.standard b/doc/README.standard
index 40699d7..40699d7 100644
--- a/README.standard
+++ b/doc/README.standard
diff --git a/README.xml b/doc/README.xml
index 0e34c97..0e34c97 100644
--- a/README.xml
+++ b/doc/README.xml
diff --git a/http.c b/http.c
deleted file mode 100644
index 6b5580d..0000000
--- a/http.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/* Asterisk Manager Proxy
- Copyright (c) 2005 David C. Troy <dave@popvox.com>
-
- This program is free software, distributed under the terms of
- the GNU General Public License.
-
- HTTP Input Handler
-*/
-
-#include "astmanproxy.h"
-
-int ParseHTTPInput(char *buf, struct message *m) {
- char *n, *v;
-
- /* just an empty block; go home
- if ( !(*buf) )
- return 0; */
-
- /* initialize message block */
- memset(m, 0, sizeof (struct message) );
-
- n = buf;
- while ( (v = strstr(n, "=")) ) {
- v += 1;
- debugmsg("n: %s, v: %s", n, v);
- strncat(m->headers[m->hdrcount], n, v-n-1);
- strcat(m->headers[m->hdrcount], ": ");
-
- if ( (n = strstr(v, "&")) ) {
- n += 1;
- } else {
- n = (v + strlen(v) + 1);
- }
- strncat(m->headers[m->hdrcount], v, n-v-1);
- debugmsg("got hdr: %s", m->headers[m->hdrcount]);
- m->hdrcount++;
- }
-
- return (m->hdrcount > 0);
-}
-
-int BuildHTTPHeader(char *hdr) {
-
-
- time_t t;
- struct tm tm;
- char date[80];
-
- time(&t);
- localtime_r(&t, &tm);
- strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
-
- sprintf(hdr,
- "HTTP/1.1 200 OK\r\n"
- "Date: %s\r\n"
- "Content-Type: text/xml\r\n"
- "Connection: close\r\n"
- "Server: %s/%s\r\n\r\n",
- date, PROXY_BANNER, PROXY_VERSION);
-
- return 0;
-}
-
-int _read(struct mansession *s, struct message *m) {
-
- /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */
- /* No HTTP Input may be longer than BUFSIZE */
-
- char line[MAX_LEN], method[10], formdata[MAX_LEN], header[MAX_LEN];
- int res, clength = 0;
-
- if (s->inputcomplete)
- return 0;
-
- memset(method, 0, sizeof method);
- memset(formdata, 0, sizeof formdata);
-
- /* for http, don't do get_input forever */
- for (;;) {
- memset(line, 0, sizeof line);
- res = get_input(s, line);
-
- if (res > 0) {
- if (*line == '\0' ) {
- if (*method == '\0')
- break;
- else {
- if ( !strcasecmp(method, "POST") ) {
- pthread_mutex_lock(&s->lock);
- strncpy(formdata, s->inbuf, clength);
- /* Move remaining data back to the front */
- memmove(s->inbuf, s->inbuf + clength+1, s->inlen - clength);
- s->inlen -= clength;
- pthread_mutex_unlock(&s->lock);
- }
- debugmsg("method: %s", method);
- debugmsg("clength: %d", clength);
- debugmsg("formdata: %s", formdata);
- debugmsg("s->inbuf: %s", s->inbuf);
- debugmsg("s->inlen: %d", s->inlen);
-
- BuildHTTPHeader(header);
- pthread_mutex_lock(&s->lock);
- s->inputcomplete = 1;
- write(s->fd, header, strlen(header));
- pthread_mutex_unlock(&s->lock);
- debugmsg("header: %s", header);
-
- /* now, let's transform and copy into a standard message block */
- res = ParseHTTPInput(formdata, m);
- return res;
- }
- } else {
- debugmsg("Got http: %s", line);
- /* Do meaningful things here */
- if ( !strncmp(line,"POST",4) ) {
- strncpy(method, line, 4);
- } else if ( !strncmp(line,"GET",3) ) {
- /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */
- strncpy(method, line, 3);
- memcpy(formdata, line+6, strstr(line, " HTTP")-line-6);
- } else if ( !strncasecmp(line, "Content-Length: ", 16) ) {
- clength = atoi(line+16);
- }
- }
- } else if (res < 0)
- return res;
- }
-
- return -1;
-}
-
-int _autodisconnect() {
- return 1;
-}
-
-/* We do not define a _write or _onconnect method */
diff --git a/samples/httpast2.html b/samples/httpast2.html
new file mode 100644
index 0000000..66baa2e
--- /dev/null
+++ b/samples/httpast2.html
@@ -0,0 +1,12 @@
+<HTML>
+<h3>Sample Astmanproxy HTTP Input</h3>
+This version uses the GET method with an HTTPS action. Be sure
+you have acceptencryptedconnections=yes in astmnanproxy.conf.
+<p>
+<FORM ACTION="https://localhost:1234" METHOD=GET>
+Server: <input type=text name=Server value="127.0.0.1"><br>
+Action: <input name="Action" type="text" value="Ping"><br>
+ActionID: <input name="ActionID" type="text" value="Foo"><br>
+<input type=submit><br>
+</FORM>
+</HTML>
diff --git a/astmanproxy.c b/src/astmanproxy.c
index c0a8470..b84fdad 100644
--- a/astmanproxy.c
+++ b/src/astmanproxy.c
@@ -10,12 +10,14 @@
extern int LoadHandlers( void );
extern void ReadConfig( void );
+extern void ReadPerms( void );
extern FILE *OpenLogfile( void );
extern int SetProcUID( void );
extern void *proxyaction_do(char *proxyaction, struct message *m, struct mansession *s);
-extern void *ProxyLogin(struct mansession *s);
+extern void *ProxyLogin(struct mansession *s, struct message *m);
extern void *ProxyLogoff(struct mansession *s);
+extern int ValidateAction(struct message *m, struct mansession *s, int inbound);
int ConnectAsterisk(struct mansession *s);
@@ -25,6 +27,7 @@ struct iohandler *iohandlers = NULL;
pthread_mutex_t sessionlock;
pthread_mutex_t serverlock;
+pthread_mutex_t userslock;
pthread_mutex_t loglock;
pthread_mutex_t debuglock;
static int asock = -1;
@@ -38,6 +41,8 @@ void hup(int sig) {
}
proxylog = OpenLogfile();
logmsg("Received HUP -- reopened log");
+ ReadPerms();
+ logmsg("Received HUP -- reread permissions");
}
void leave(int sig) {
@@ -73,7 +78,7 @@ void leave(int sig) {
c->output->write(c, &cm);
logmsg("Shutdown, closed client %s", ast_inet_ntoa(iabuf, sizeof(iabuf), c->sin.sin_addr));
}
- close(c->fd);
+ close_sock(c->fd); /* close tcp & ssl socket */
pthread_mutex_destroy(&c->lock);
free(c);
}
@@ -90,7 +95,7 @@ void leave(int sig) {
if (debug)
debugmsg("Closing listener socket");
- close(asock);
+ close_sock(asock); /* close tcp & ssl socket */
/* unload io handlers */
while (iohandlers) {
@@ -115,7 +120,7 @@ void leave(int sig) {
void Version( void )
{
- printf("astmanproxy: Version %s, (C) David C. Troy\n", PROXY_VERSION);
+ printf("astmanproxy: Version %s, (C) David C. Troy 2005-2006\n", PROXY_VERSION);
return;
}
@@ -148,8 +153,7 @@ void destroy_session(struct mansession *s)
else
sessions = cur->next;
debugmsg("Connection closed: %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
- if (s->fd > -1)
- close(s->fd);
+ close_sock(s->fd); /* close tcp/ssl socket */
pthread_mutex_destroy(&s->lock);
free(s);
} else
@@ -169,16 +173,19 @@ int WriteClients(struct message *m) {
c = sessions;
while (c) {
- if ( !c->server && m->hdrcount>1 ) {
+ if ( !c->server && m->hdrcount>1 && ValidateAction(m, c, 1) ) {
if (c->autofilter && c->actionid) {
- actionid = astman_get_header(m, "ActionID");
+ actionid = astman_get_header(m, ACTION_ID);
if ( !strcmp(actionid, c->actionid) ) {
c->output->write(c, m);
}
} else
c->output->write(c, m);
- if ( c->input->autodisconnect && c->input->autodisconnect() )
- close(c->fd);
+ if (c->inputcomplete) {
+ pthread_mutex_lock(&c->lock);
+ c->outputcomplete = 1;
+ pthread_mutex_unlock(&c->lock);
+ }
}
c = c->next;
}
@@ -220,10 +227,10 @@ int WriteAsterisk(struct message *m) {
for (i=0; i<m->hdrcount; i++) {
if (strcasecmp(m->headers[i], "Server:") ) {
sprintf(outstring, "%s\r\n", m->headers[i]);
- write(s->fd, outstring, strlen(outstring) );
+ ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout );
}
}
- write(s->fd, "\r\n", 2);
+ ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout);
pthread_mutex_unlock(&s->lock);
return 1;
}
@@ -250,12 +257,16 @@ void *session_do(struct mansession *s)
for (;;) {
/* Get a complete message block from input handler */
memset(&m, 0, sizeof(struct message) );
+ if (debug > 3)
+ debugmsg("calling %s_read...", s->input->formatname);
res = s->input->read(s, &m);
+ if (debug > 3)
+ debugmsg("%s_read result = %d", s->input->formatname, res);
m.session = s;
if (res > 0) {
/* Check for anything that requires proxy-side processing */
- if (pc.key && !s->authenticated) {
+ if (pc.key[0] != '\0' && !s->authenticated) {
key = astman_get_header(&m, "ProxyKey");
if (!strcmp(key, pc.key) ) {
pthread_mutex_lock(&s->lock);
@@ -266,19 +277,26 @@ void *session_do(struct mansession *s)
}
proxyaction = astman_get_header(&m, "ProxyAction");
- actionid = astman_get_header(&m, "ActionID");
+ actionid = astman_get_header(&m, ACTION_ID);
action = astman_get_header(&m, "Action");
if ( !strcasecmp(action, "Login") )
- ProxyLogin(s);
+ if (!s->authenticated)
+ ProxyLogin(s, &m);
+ else
+ break;
else if ( !strcasecmp(action, "Logoff") )
ProxyLogoff(s);
+ else if ( !strcasecmp(action, "Challenge") )
+ ProxyChallenge(s, &m);
else if ( !(*proxyaction == '\0') )
proxyaction_do(proxyaction, &m, s);
- else {
+ else if ( ValidateAction(&m, s, 0) ) {
if ( !(*actionid == '\0') )
setactionid(actionid, &m, s);
if ( !WriteAsterisk(&m) )
break;
+ } else {
+ SendError(s, "Action Filtered");
}
} else if (res < 0)
break;
@@ -286,7 +304,7 @@ void *session_do(struct mansession *s)
destroy_session(s);
if (debug)
- debugmsg("Exiting session_do thread");
+ debugmsg("--- exiting session_do thread ---");
pthread_exit(NULL);
return NULL;
}
@@ -329,10 +347,9 @@ void *HandleAsterisk(struct mansession *s)
if (!WriteClients(&m))
break;
- /* TODO: does it make any sense to abort * conn if we cannot write to clients? I don't think so.
- Do we even get a return code back that means anything? I don't think so. */
} else if (res < 0) {
- if (debug)
+ /* TODO: do we need to do more than this here? or something different? */
+ if ( debug )
debugmsg("asterisk@%s: Not connected", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr));
if ( ConnectAsterisk(s) )
break;
@@ -359,8 +376,8 @@ int ConnectAsterisk(struct mansession *s) {
s->connected = 0;
if (debug)
- debugmsg("asterisk@%s: Connecting (u=%s, p=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr),
- s->server->ast_user, s->server->ast_pass);
+ debugmsg("asterisk@%s: Connecting (u=%s, p=%s, ssl=%s)", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr),
+ s->server->ast_user, s->server->ast_pass, s->server->use_ssl ? "on" : "off");
/* Construct auth message just once */
memset( &m, 0, sizeof(struct message) );
@@ -370,15 +387,10 @@ int ConnectAsterisk(struct mansession *s) {
AddHeader(&m, "Events: %s", s->server->ast_events);
for ( ;; ) {
- if ( connect_nonb(s->fd, (struct sockaddr *) &s->sin, sizeof(s->sin), 2) < 0 ) {
- if (errno == EISCONN) {
- pthread_mutex_lock(&s->lock);
- s->fd = socket(AF_INET, SOCK_STREAM, 0);
- pthread_mutex_unlock(&s->lock);
- }
+ if ( ast_connect(s) == -1 ) {
if (debug)
- debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s",
- ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno) );
+ debugmsg("asterisk@%s: Connect failed, Retrying (%d) %s [%d]",
+ ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), r, strerror(errno), errno );
if (pc.maxretries && (++r>pc.maxretries) ) {
res = 1;
break;
@@ -442,7 +454,6 @@ int StartServer(struct ast_server *srv) {
debugmsg("Set %s output format to %s", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->output->formatname);
}
-
if (pthread_create(&s->t, &attr, (void *)HandleAsterisk, s))
destroy_session(s);
else
@@ -481,14 +492,13 @@ int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt)
io = io->next;
}
+ /* set default handlers if non match was found */
if (!s->output) {
- /* TODO: Output debug that default/first handler was used */
s->output = iohandlers;
res = 1;
}
if (!s->input) {
- /* TODO: Output debug that default/first handler was used */
s->input = iohandlers;
res = 1;
}
@@ -508,6 +518,7 @@ static void *accept_thread()
int flags;
pthread_attr_t attr;
char iabuf[INET_ADDRSTRLEN];
+ int is_encrypted;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
@@ -525,6 +536,36 @@ static void *accept_thread()
logmsg("Failed to set listener tcp connection to TCP_NODELAY mode: %s\n", strerror(errno));
}
}
+
+ /* SSL stuff below */
+ is_encrypted = is_encrypt_request(pc.sslclhellotimeout, as);
+ debugmsg("is_encrypted: %d", is_encrypted);
+ if (is_encrypted > 0) {
+ if (!pc.acceptencryptedconnection) {
+ if( debug )
+ debugmsg("Accepting encrypted connection disabled, closing the connection \n");
+ close_sock(as);
+ continue;
+ } else {
+ if((as = saccept(as)) >= 0 ) {
+ if( debug )
+ debugmsg("Can't accept the ssl connection, since SSL init has failed for certificate reason\n");
+ close_sock(as);
+ continue;
+ }
+ }
+ } else if (is_encrypted == -1) {
+ logmsg("SSL version 2 is unsecure, we don't support it\n");
+ close_sock(as);
+ continue;
+ }
+ if ( (! pc.acceptunencryptedconnection) && (as >= 0)) {
+ logmsg("Unencrypted connections are not accepted and we received an unencrypted connection request\n");
+ close_sock(as);
+ continue;
+ }
+ /* SSL stuff end */
+
s = malloc(sizeof(struct mansession));
if ( !s ) {
logmsg("Failed to allocate listener session: %s\n", strerror(errno));
@@ -534,14 +575,13 @@ static void *accept_thread()
memcpy(&s->sin, &sin, sizeof(sin));
/* For safety, make sure socket is non-blocking */
- flags = fcntl(as, F_GETFL);
- fcntl(as, F_SETFL, flags | O_NONBLOCK);
+ flags = fcntl(get_real_fd(as), F_GETFL);
+ fcntl(get_real_fd(as), F_SETFL, flags | O_NONBLOCK);
pthread_mutex_init(&s->lock, NULL);
s->fd = as;
SetIOHandlers(s, pc.inputformat, pc.outputformat);
s->autofilter = pc.autofilter;
- s->inputcomplete = 0;
s->server = NULL;
pthread_mutex_lock(&sessionlock);
@@ -577,7 +617,7 @@ int main(int argc, char *argv[])
{
switch( i ) {
case 'd':
- debug = 1;
+ debug++;
break;
case 'h':
Usage();
@@ -617,9 +657,16 @@ int main(int argc, char *argv[])
/* Initialize global mutexes */
pthread_mutex_init(&sessionlock, NULL);
+ pthread_mutex_init(&userslock, NULL);
pthread_mutex_init(&loglock, NULL);
pthread_mutex_init(&debuglock, NULL);
+ /* Read initial state for user permissions */
+ ReadPerms();
+
+ /* Initialize SSL Client-Side Context */
+ client_init_secure();
+
/* Initialize global client/server list */
sessions = NULL;
LaunchAsteriskThreads();
diff --git a/common.c b/src/common.c
index 5f63c15..d6bea7a 100644
--- a/common.c
+++ b/src/common.c
@@ -11,8 +11,6 @@ int get_input(struct mansession *s, char *output)
struct pollfd fds[1];
char iabuf[INET_ADDRSTRLEN];
- debugmsg("in get_input");
-
/* Look for \r\n from the front, our preferred end of line */
for (x=0;x<s->inlen;x++) {
int xtra = 0;
@@ -36,25 +34,37 @@ int get_input(struct mansession *s, char *output)
debugmsg("Warning: Got long line with no end from %s: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), s->sin.sin_addr), s->inbuf);
s->inlen = 0;
}
- debugmsg("attempting poll operation");
- fds[0].fd = s->fd;
+ /* get actual fd, even if a negative SSL fd */
+ fds[0].fd = get_real_fd(s->fd);
+
fds[0].events = POLLIN;
- res = poll(fds, 1, -1);
- debugmsg("returned from poll op");
- if (res < 0 && debug) {
- debugmsg("Select returned error");
- } else if (res > 0) {
- pthread_mutex_lock(&s->lock);
- debugmsg("attempting socket read in get_input...");
- res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen);
- pthread_mutex_unlock(&s->lock);
- if (res < 1)
- return -1;
- }
+ do {
+ res = poll(fds, 1, -1);
+ if (res < 0) {
+ if (errno == EINTR) {
+ if (s->dead)
+ return -1;
+ continue;
+ }
+ if (debug)
+ debugmsg("Select returned error");
+ return -1;
+ } else if (res > 0) {
+ pthread_mutex_lock(&s->lock);
+ /* read from socket; SSL or otherwise */
+ res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0);
+ pthread_mutex_unlock(&s->lock);
+ if (res < 1)
+ return -1;
+ break;
+
+ }
+ } while(1);
+
+ /* We have some input, but it's not ready for processing */
s->inlen += res;
s->inbuf[s->inlen] = '\0';
return 0;
- /* We have some input, but it's not ready for processing */
}
char *astman_get_header(struct message *m, char *var)
@@ -92,56 +102,34 @@ const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia)
}
-int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec)
+/*! If you are calling ast_carefulwrite, it is assumed that you are calling
+ it on a file descriptor that _DOES_ have NONBLOCK set. This way,
+ there is only one system call made to do a write, unless we actually
+ have a need to wait. This way, we get better performance. */
+int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
{
- int flags, n, error;
- socklen_t len;
- fd_set rset, wset;
- struct timeval tval;
-
- flags = fcntl(sockfd, F_GETFL, 0);
- fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
-
- error = 0;
- if ( (n = connect(sockfd, (struct sockaddr *) saptr, salen)) < 0)
- if (errno != EINPROGRESS)
- return(-1);
-
- /* Do whatever we want while the connect is taking place. */
-
- if (n == 0)
- goto done; /* connect completed immediately */
-
- FD_ZERO(&rset);
- FD_SET(sockfd, &rset);
- wset = rset;
- tval.tv_sec = nsec;
- tval.tv_usec = 0;
-
- if ( (n = select(sockfd+1, &rset, &wset, NULL,
- nsec ? &tval : NULL)) == 0) {
- /*close(sockfd);*/ /* we want to retry */
- errno = ETIMEDOUT;
- return(-1);
- }
-
- if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
- len = sizeof(error);
- if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
- return(-1); /* Solaris pending error */
- } else {
- /*err_quit("select error: sockfd not set");*/
- logmsg("select error: sockfd not set");
- return(-1);
+ /* Try to write string, but wait no more than ms milliseconds
+ before timing out */
+ int res=0;
+ struct pollfd fds[1];
+ while(len) {
+ res = m_send(fd, s, len);
+ if ((res < 0) && (errno != EAGAIN)) {
+ return -1;
+ }
+ if (res < 0) res = 0;
+ len -= res;
+ s += res;
+ res = 0;
+ if (len) {
+ fds[0].fd = get_real_fd(fd);
+ fds[0].events = POLLOUT;
+ /* Wait until writable again */
+ res = poll(fds, 1, timeoutms);
+ if (res < 1)
+ return -1;
+ }
}
-
-done:
- fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
-
- if (error) {
- /* close(sockfd); */ /* disable for now, we want to retry... */
- errno = error;
- return(-1);
- }
- return(0);
+ return res;
}
+
diff --git a/config.c b/src/config.c
index f9d3b9f..f642aa6 100644
--- a/config.c
+++ b/src/config.c
@@ -9,6 +9,7 @@ void *add_server(char *srvspec) {
int ccount = 0;
struct ast_server *srv;
char *s;
+ char usessl[10];
/* malloc ourselves a server credentials structure */
srv = malloc(sizeof(struct ast_server));
@@ -17,6 +18,7 @@ void *add_server(char *srvspec) {
exit(1);
}
memset(srv, 0, sizeof (struct ast_server) );
+ memset(usessl, 0, sizeof (usessl) );
s = srvspec;
do {
@@ -41,20 +43,24 @@ void *add_server(char *srvspec) {
case 4:
strncat(srv->ast_events, s, 1);
break;
+ case 5:
+ strncat(usessl, s, 1);
+ break;
}
} while (*(s++));
- if (!srv->ast_host || !srv->ast_port || !srv->ast_user || !srv->ast_pass || !srv->ast_events) {
- fprintf(stderr, "Aborting! Server spec incomplete: %s!\n", srvspec);
+
+ if (!*srv->ast_host || !*srv->ast_port || !*srv->ast_user || !*srv->ast_pass || !*srv->ast_events || !*usessl) {
+ fprintf(stderr, "Aborting: server spec incomplete: %s\n", srvspec);
free(srv);
exit(1);
- } else {
- srv->next = pc.serverlist;
- pc.serverlist = srv;
}
+ srv->use_ssl = (!strcmp(usessl,"on"));
+ srv->next = pc.serverlist;
+ pc.serverlist = srv;
+
return 0;
- /* TODO: make sure server credentials are freed at leave */
}
void *processline(char *s) {
@@ -95,6 +101,20 @@ void *processline(char *s) {
strcpy(pc.listen_addr, value);
else if (!strcmp(name,"listenport") )
pc.listen_port = atoi(value);
+ else if (!strcmp(name,"asteriskwritetimeout") )
+ pc.asteriskwritetimeout = atoi(value);
+ else if (!strcmp(name,"clientwritetimeout") )
+ pc.clientwritetimeout = atoi(value);
+ else if (!strcmp(name,"sslclienthellotimeout") )
+ pc.sslclhellotimeout = atoi(value);
+ else if (!strcmp(name,"authrequired") )
+ pc.authrequired = strcmp(value,"yes") ? 0 : 1;
+ else if (!strcmp(name,"acceptencryptedconnection") )
+ pc.acceptencryptedconnection = strcmp(value,"yes") ? 0 : 1;
+ else if (!strcmp(name,"acceptunencryptedconnection") )
+ pc.acceptunencryptedconnection = strcmp(value,"yes") ? 0 : 1;
+ else if (!strcmp(name,"certfile") )
+ strcpy(pc.certfile, value);
else if (!strcmp(name,"proxykey") )
strcpy(pc.key, value);
else if (!strcmp(name,"proc_user") )
@@ -172,11 +192,7 @@ int LoadHandlers() {
io->write = wh;
if (och)
io->onconnect = och;
- io->autodisconnect = dlsym(dlhandle, "_autodisconnect");
- if ((error = dlerror()) != NULL) {
- if (debug)
- debugmsg("loading: note, %s_autodisconnect not defined; ignoring", fmt);
- }
+
io->dlhandle = dlhandle;
io->next = iohandlers;
iohandlers = io;
@@ -203,6 +219,12 @@ int ReadConfig() {
memset( &pc, 0, sizeof pc );
+
+ /* Set nonzero config defaults */
+ pc.asteriskwritetimeout = 100;
+ pc.clientwritetimeout = 100;
+ pc.sslclhellotimeout = 500;
+
sprintf(cfn, "%s/%s", CDIR, CFILE);
FP = fopen( cfn, "r" );
@@ -222,6 +244,9 @@ int ReadConfig() {
fclose(FP);
+ /* initialize SSL layer with our server certfile */
+ init_secure(pc.certfile);
+
return 0;
}
@@ -229,7 +254,7 @@ FILE *OpenLogfile() {
FILE *FP;
FP = fopen( pc.logfile, "a" );
if ( !FP ) {
- fprintf(stderr, "Unable to open logfile: %s!", pc.logfile);
+ fprintf(stderr, "Unable to open logfile: %s!\n", pc.logfile);
exit( 1 );
}
diff --git a/src/config_perms.c b/src/config_perms.c
new file mode 100644
index 0000000..4dbeeb0
--- /dev/null
+++ b/src/config_perms.c
@@ -0,0 +1,125 @@
+#include "astmanproxy.h"
+
+extern pthread_mutex_t userslock;
+
+void *free_userperm(struct proxy_user *pu) {
+ struct proxy_user *next_pu;
+
+ while( pu ) {
+ next_pu = pu->next;
+ free( pu );
+ pu = next_pu;
+ }
+ return 0;
+}
+
+void *add_userperm(char* username, char *userspec, struct proxy_user **pu) {
+
+ int ccount = 0;
+ struct proxy_user *user;
+ char *s;
+
+ /* malloc ourselves a server credentials structure */
+ user = malloc(sizeof(struct proxy_user));
+ if ( !user ) {
+ fprintf(stderr, "Failed to allocate user credentials: %s\n", strerror(errno));
+ exit(1);
+ }
+ memset(user, 0, sizeof (struct proxy_user) );
+
+ s = userspec;
+ strncpy(user->username, username, sizeof(user->username)-1 );
+ do {
+ if ( *s == ',' ) {
+ ccount++;
+ continue;
+ }
+ switch(ccount) {
+ case 0:
+ strncat(user->secret, s, 1);
+ break;
+ case 1:
+ strncat(user->channel, s, 1);
+ break;
+ case 2:
+ strncat(user->ocontext, s, 1);
+ break;
+ case 3:
+ strncat(user->icontext, s, 1);
+ break;
+ }
+ } while (*(s++));
+
+ user->next = *pu;
+ *pu = user;
+
+ return 0;
+}
+
+void *processperm(char *s, struct proxy_user **pu) {
+ char name[80],value[80];
+ int nvstate = 0;
+
+
+ memset (name,0,sizeof name);
+ memset (value,0,sizeof value);
+
+ do {
+ *s = tolower(*s);
+
+ if ( *s == ' ' || *s == '\t')
+ continue;
+ if ( *s == ';' || *s == '#' || *s == '\r' || *s == '\n' )
+ break;
+ if ( *s == '=' ) {
+ nvstate = 1;
+ continue;
+ }
+ if (!nvstate)
+ strncat(name, s, 1);
+ else
+ strncat(value, s, 1);
+ } while (*(s++));
+
+ if (debug)
+ debugmsg("perm: %s, %s", name, value);
+
+ add_userperm(name,value,pu);
+
+ return 0;
+}
+
+int ReadPerms() {
+ FILE *FP;
+ char buf[1024];
+ char cfn[80];
+ struct proxy_user *pu;
+
+ pu=0;
+ sprintf(cfn, "%s/%s", PDIR, PFILE);
+ FP = fopen( cfn, "r" );
+
+ if ( !FP )
+ {
+ fprintf(stderr, "Unable to open permissions file: %s/%s!\n", PDIR, PFILE);
+ exit( 1 );
+ }
+
+ if (debug)
+ debugmsg("config: parsing configuration file: %s", cfn);
+
+ while ( fgets( buf, sizeof buf, FP ) ) {
+ if (*buf == ';' || *buf == '\r' || *buf == '\n' || *buf == '#') continue;
+ processperm(buf,&pu);
+ }
+
+ fclose(FP);
+
+ pthread_mutex_lock(&userslock);
+ free_userperm(pc.userlist);
+ pc.userlist=pu;
+ pthread_mutex_unlock(&userslock);
+
+ return 0;
+}
+
diff --git a/csv.c b/src/csv.c
index bf8e800..94d9a00 100644
--- a/csv.c
+++ b/src/csv.c
@@ -20,14 +20,11 @@ int _write(struct mansession *s, struct message *m) {
sprintf(outstring, "\"%s\"", m->headers[i]);
if (i<m->hdrcount-1)
strcat(outstring, ", ");
- write(s->fd, outstring, strlen(outstring));
+ ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout);
}
- write(s->fd, "\r\n\r\n", 4);
+ ast_carefulwrite(s->fd, "\r\n\r\n", 4, s->writetimeout);
pthread_mutex_unlock(&s->lock);
return 0;
}
-int _autodisconnect() {
- return 0;
-}
diff --git a/dlfcn.c b/src/dlfcn.c
index 98d2373..98d2373 100644
--- a/dlfcn.c
+++ b/src/dlfcn.c
diff --git a/src/http.c b/src/http.c
new file mode 100644
index 0000000..4f107db
--- /dev/null
+++ b/src/http.c
@@ -0,0 +1,155 @@
+/* Asterisk Manager Proxy
+ Copyright (c) 2005 David C. Troy <dave@popvox.com>
+
+ This program is free software, distributed under the terms of
+ the GNU General Public License.
+
+ HTTP Input Handler
+*/
+
+#include "astmanproxy.h"
+
+int ParseHTTPInput(char *buf, struct message *m) {
+ char *n, *v;
+
+ n = buf;
+ while ( (v = strstr(n, "=")) ) {
+ v += 1;
+ debugmsg("n: %s, v: %s", n, v);
+ strncat(m->headers[m->hdrcount], n, v-n-1);
+ strcat(m->headers[m->hdrcount], ": ");
+
+ if ( (n = strstr(v, "&")) ) {
+ n += 1;
+ } else {
+ n = (v + strlen(v) + 1);
+ }
+ strncat(m->headers[m->hdrcount], v, n-v-1);
+ debugmsg("got hdr: %s", m->headers[m->hdrcount]);
+ m->hdrcount++;
+ }
+
+ return (m->hdrcount > 0);
+}
+
+int HTTPHeader(struct mansession *s, char *status) {
+
+
+ time_t t;
+ struct tm tm;
+ char date[80];
+ char ctype[15], hdr[MAX_LEN];
+
+ time(&t);
+ localtime_r(&t, &tm);
+ strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
+
+ if ( !strcasecmp("xml", s->output->formatname) )
+ sprintf(ctype, "text/xml");
+ else
+ sprintf(ctype, "text/plain");
+
+ if (!strcmp("200 OK", status) )
+ sprintf(hdr,
+ "HTTP/1.1 %s\r\n"
+ "Date: %s\r\n"
+ "Content-Type: %s\r\n"
+ "Connection: close\r\n"
+ "Server: %s/%s\r\n\r\n", status,
+ date, ctype, PROXY_BANNER, PROXY_VERSION);
+ else
+ sprintf(hdr,
+ "HTTP/1.1 %s\r\n"
+ "Date: %s\r\n"
+ "Status: %s\r\n"
+ "Server: %s/%s\r\n\r\n", status, date, status, PROXY_BANNER, PROXY_VERSION);
+
+ pthread_mutex_lock(&s->lock);
+ s->inputcomplete = 1;
+ ast_carefulwrite(s->fd, hdr, strlen(hdr), s->writetimeout);
+ pthread_mutex_unlock(&s->lock);
+ debugmsg("http header: %s", hdr);
+
+ return 0;
+}
+
+int _read(struct mansession *s, struct message *m) {
+
+ /* Note: No single line may be longer than MAX_LEN/s->inbuf, as per get_input */
+ /* No HTTP Input may be longer than BUFSIZE */
+
+ char line[MAX_LEN], method[10], formdata[MAX_LEN], status[15];
+ int res, clength = 0;
+
+ memset(method, 0, sizeof method);
+ memset(formdata, 0, sizeof formdata);
+ memset(status, 0, sizeof status);
+
+ /* for http, don't do get_input forever */
+ for (;;) {
+
+ if (s->inputcomplete && !s->outputcomplete)
+ continue;
+ else if (s->inputcomplete && s->outputcomplete)
+ return -1;
+
+ memset(line, 0, sizeof line);
+ res = get_input(s, line);
+ debugmsg("res=%d, line: %s",res, line);
+
+ if (res > 0) {
+ debugmsg("Got http: %s", line);
+
+ if ( !clength && !strncasecmp(line, "Content-Length: ", 16) )
+ clength = atoi(line+16);
+
+ if (!*method) {
+ if ( !strncmp(line,"POST",4) ) {
+ strncpy(method, line, 4);
+ } else if ( !strncmp(line,"GET",3)) {
+ if ( strlen(line) > 14 ) {
+ /* GET / HTTP/1.1 ---- this is bad */
+ /* GET /?Action=Ping&ActionID=Foo HTTP/1.1 */
+ strncpy(method, line, 3);
+ memcpy(formdata, line+6, strstr(line, " HTTP")-line-6);
+ sprintf(status, "200 OK");
+ } else
+ sprintf(status, "501 Not Implemented");
+ }
+ }
+ } else if (res == 0) {
+ /* x-www-form-urlencoded handler */
+ /* Content-Type: application/x-www-form-urlencoded */
+ if (*method && !*formdata) {
+ if ( !strcasecmp(method, "POST") && clength && s->inlen==clength) {
+ pthread_mutex_lock(&s->lock);
+ strncpy(formdata, s->inbuf, clength);
+ s->inlen = 0;
+ pthread_mutex_unlock(&s->lock);
+ sprintf(status, "200 OK");
+ }
+ }
+ }
+
+ if (res < 0)
+ break;
+
+ if (*status) {
+ HTTPHeader(s, status);
+
+ /* now, let's transform and copy into a standard message block */
+ if (!strcmp("200 OK", status) ) {
+ res = ParseHTTPInput(formdata, m);
+ return res;
+ } else {
+ pthread_mutex_lock(&s->lock);
+ s->outputcomplete = 1;
+ pthread_mutex_unlock(&s->lock);
+ return 0;
+ }
+ }
+ }
+ return -1;
+}
+
+/* We do not define a _write or _onconnect method */
diff --git a/astmanproxy.h b/src/include/astmanproxy.h
index 566ae69..dc7ac87 100644
--- a/astmanproxy.h
+++ b/src/include/astmanproxy.h
@@ -25,12 +25,13 @@
#include <sys/poll.h>
#endif
-#define BUFSIZE 150
+#define BUFSIZE 1024
#define MAX_HEADERS 256
-#define MAX_LEN 150
+#define MAX_LEN 1024
#define PROXY_BANNER "Asterisk Call Manager Proxy"
#define PROXY_SHUTDOWN "ProxyMessage: Proxy Shutting Down"
+#define ACTION_ID "ActionID"
struct ast_server {
char nickname[80];
@@ -39,30 +40,47 @@ struct ast_server {
char ast_user[80];
char ast_pass[80];
char ast_events[10];
+ int use_ssl; /* Use SSL when Connecting to Server? */
int status; /* TODO: have this mean something */
struct ast_server *next;
};
+struct proxy_user {
+ char username[80];
+ char secret[80];
+ char channel[80];
+ char icontext[80];
+ char ocontext[80];
+ struct proxy_user *next;
+};
+
struct proxyconfig {
struct ast_server *serverlist;
+ struct proxy_user *userlist;
char listen_addr[INET_ADDRSTRLEN];
int listen_port;
char inputformat[80];
char outputformat[80];
- int autofilter;
+ int autofilter; /* enable autofiltering? */
+ int authrequired; /* is authentication required? */
char key[80];
- char proc_user[30];
- char proc_group[30];
- char logfile[80];
+ char proc_user[40];
+ char proc_group[40];
+ char logfile[256];
int retryinterval;
int maxretries;
+ int asteriskwritetimeout; /* ms to wait when writing to asteriskfor ast_carefulwrite */
+ int clientwritetimeout; /* ms to wait when writing to client ast_carefulwrite */
+ int sslclhellotimeout; /* ssl client hello timeout -- how long to wait before assuming not ssl */
+ int acceptencryptedconnection; /* accept encrypted connections? */
+ int acceptunencryptedconnection; /* accept unencrypted connections? */
+ char certfile[256]; /* our SERVER-side SSL certificate file */
};
struct iohandler {
int (*read) ();
int (*write) ();
int (*onconnect) ();
- int *(*autodisconnect)(void);
char formatname[80];
void *dlhandle;
struct iohandler *next;
@@ -78,11 +96,17 @@ struct mansession {
struct iohandler *input;
struct iohandler *output;
int autofilter;
- int inputcomplete;
int authenticated;
int connected;
+ int dead; /* Whether we are dead */
+ int busy; /* Whether we are busy */
+ int inputcomplete; /* Whether we want any more input from this session (http) */
+ int outputcomplete; /* Whether output to this session is done (http) */
struct ast_server *server;
+ struct proxy_user user;
char actionid[MAX_LEN];
+ char challenge[10]; /*! Authentication challenge */
+ int writetimeout; /* Timeout for ast_carefulwrite() */
struct mansession *next;
};
@@ -110,4 +134,16 @@ int proxyerror_do(struct mansession *s, char *err);
int get_input(struct mansession *s, char *output);
int SetIOHandlers(struct mansession *s, char *ifmt, char *ofmt);
void destroy_session(struct mansession *s);
-int connect_nonb(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec);
+int ast_carefulwrite(int fd, char *s, int len, int timeoutms);
+extern void *SendError(struct mansession *s, char *errmsg);
+
+int close_sock(int socket);
+int ProxyChallenge(struct mansession *s, struct message *m);
+int ast_connect(struct mansession *a);
+int is_encrypt_request(int sslclhellotimeout, int fd);
+int saccept(int s);
+int get_real_fd(int fd);
+int client_init_secure(void);
+int init_secure(char *certfile);
+int m_send(int fd, const void *data, size_t len);
+int m_recv(int s, void *buf, size_t len, int flags);
diff --git a/dlfcn-compat.h b/src/include/dlfcn-compat.h
index 7c5e87f..7c5e87f 100644
--- a/dlfcn-compat.h
+++ b/src/include/dlfcn-compat.h
diff --git a/src/include/endian.h b/src/include/endian.h
new file mode 100644
index 0000000..f5e20fb
--- /dev/null
+++ b/src/include/endian.h
@@ -0,0 +1,60 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Asterisk architecture endianess compatibility definitions
+ *
+ * Copyright (C) 1999 - 2005, Digium, Inc.
+ *
+ * Mark Spencer <markster@digium.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU Lesser General Public License. Other components of
+ * Asterisk are distributed under The GNU General Public License
+ * only.
+ */
+
+#ifndef _ASTERISK_ENDIAN_H
+#define _ASTERISK_ENDIAN_H
+
+/*
+ * Autodetect system endianess
+ */
+
+#ifdef SOLARIS
+#include "solaris-compat/compat.h"
+#endif
+
+#ifndef __BYTE_ORDER
+#ifdef __linux__
+#include <endian.h>
+#elif defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+#if defined(__OpenBSD__)
+#include <machine/types.h>
+#endif /* __OpenBSD__ */
+#include <machine/endian.h>
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#define __BIG_ENDIAN BIG_ENDIAN
+#else
+#ifdef __LITTLE_ENDIAN__
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif /* __LITTLE_ENDIAN */
+
+#if defined(i386) || defined(__i386__)
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif /* defined i386 */
+
+#if defined(sun) && defined(unix) && defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif /* sun unix sparc */
+
+#endif /* linux */
+
+#endif /* __BYTE_ORDER */
+
+#ifndef __BYTE_ORDER
+#error Need to know endianess
+#endif /* __BYTE_ORDER */
+
+#endif /* _ASTERISK_ENDIAN_H */
+
diff --git a/src/include/md5.h b/src/include/md5.h
new file mode 100644
index 0000000..30ac30c
--- /dev/null
+++ b/src/include/md5.h
@@ -0,0 +1,18 @@
+#ifndef MD5_H
+#define MD5_H
+
+#include <inttypes.h>
+
+struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+ unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+
+#endif /* !MD5_H */
diff --git a/poll-compat.h b/src/include/poll-compat.h
index 79eab15..79eab15 100644
--- a/poll-compat.h
+++ b/src/include/poll-compat.h
diff --git a/src/include/ssl.h b/src/include/ssl.h
new file mode 100644
index 0000000..123bd43
--- /dev/null
+++ b/src/include/ssl.h
@@ -0,0 +1,89 @@
+/*
+ * ssl_addon: Encrypts the asterisk management interface
+ *
+ * Copyrights:
+ * Copyright (C) 2005-2006, Tello Corporation, Inc.
+ *
+ * Contributors:
+ * Remco Treffkorn(Architect) and Mahesh Karoshi
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU Lesser (Library) General Public License
+ *
+ * Copyright on this file is disclaimed to Digium for inclusion in Asterisk
+ */
+
+#ifndef _SSL_ADDON_H_
+#define _SSL_ADDON_H_
+
+#include <openssl/ssl.h>
+#include "astmanproxy.h"
+
+int connect_nonb(struct mansession *a);
+
+/*! \brief
+ This data structure holds the additional SSL data needed to use the ssl functions.
+ The negative fd is used as an index into this data structure (after processing).
+ Choose SEC_MAX to be impossibly large for the application.
+*/
+#define SEC_MAX 16
+struct {
+ int fd;
+ SSL* ssl;
+} sec_channel[SEC_MAX];
+
+/*! \brief
+ this has to be called before any other function dealing with ssl.
+*/
+int init_secure(char* certfile);
+
+/*! \brief
+ Returns the real fd, that is received from os, when we accept the connection.
+*/
+int get_real_fd(int fd);
+
+/*! \brief
+ Returns the ssl structure from the fd.
+*/
+SSL *get_ssl(int fd);
+
+/*! \brief
+ Returns the availabe security slot. This restricts the maximun number of security connection,
+ the asterisk server can have for AMI.
+*/
+int sec_getslot(void);
+
+/*! \brief
+ Accepts the connection, if the security is enabled it returns the negative fd. -1 is flase, -2, -3
+ etc are ssl connections.
+*/
+int saccept(int s);
+
+/*! \brief
+ Sends the data over secured or unsecured connections.
+*/
+int m_send(int fd, const void *data, size_t len);
+
+
+/*! \brief
+ Receives the connection from either ssl or fd.
+*/
+int m_recv(int s, void *buf, size_t len, int flags);
+
+
+/*! \brief
+ Needs to be called instead of close() to close a socket.
+ It also closes the ssl meta connection.
+*/
+
+int close_sock(int socket);
+
+int errexit(char s[]);
+
+int is_encrypt_request(int sslclhellotimeout, int fd);
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
diff --git a/log.c b/src/log.c
index d3f8d15..d3f8d15 100644
--- a/log.c
+++ b/src/log.c
diff --git a/src/md5.c b/src/md5.c
new file mode 100644
index 0000000..cda3441
--- /dev/null
+++ b/src/md5.c
@@ -0,0 +1,260 @@
+/* MD5 checksum routines used for authentication. Not covered by GPL, but
+ in the public domain as per the copyright below */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "endian.h"
+#include "astmanproxy.h"
+#include "md5.h"
+
+# if __BYTE_ORDER == __BIG_ENDIAN
+# define HIGHFIRST 1
+# endif
+#ifndef HIGHFIRST
+#define byteReverse(buf, len) /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+ uint32_t t;
+ do {
+ t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+ ((unsigned) buf[1] << 8 | buf[0]);
+ *(uint32_t *) buf = t;
+ buf += 4;
+ } while (--longs);
+}
+#endif
+#endif
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+ ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+ byteReverse((unsigned char *) ctx->buf, 4);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif
diff --git a/poll.c b/src/poll.c
index a36539a..a36539a 100644
--- a/poll.c
+++ b/src/poll.c
diff --git a/proxyfunc.c b/src/proxyfunc.c
index 11132ff..434baff 100644
--- a/proxyfunc.c
+++ b/src/proxyfunc.c
@@ -1,8 +1,10 @@
#include "astmanproxy.h"
+#include "md5.h"
extern struct mansession *sessions;
extern struct iohandler *iohandlers;
extern pthread_mutex_t serverlock;
+extern pthread_mutex_t userslock;
void *ProxyListIOHandlers(struct mansession *s) {
struct message m;
@@ -52,7 +54,6 @@ void *ProxySetOutputFormat(struct mansession *s, struct message *m) {
value = astman_get_header(m, "OutputFormat");
SetIOHandlers(s, s->input->formatname, value);
- /* TODO: this is retarded */
memset(&mo, 0, sizeof(struct message));
AddHeader(&mo, "ProxyResponse: Success");
@@ -63,6 +64,25 @@ void *ProxySetOutputFormat(struct mansession *s, struct message *m) {
return 0;
}
+int ProxyChallenge(struct mansession *s, struct message *m) {
+ struct message mo;
+
+ if ( strcasecmp("MD5", astman_get_header(m, "AuthType")) ) {
+ SendError(s, "Must specify AuthType");
+ return 1;
+ }
+
+ if (!*s->challenge)
+ snprintf(s->challenge, sizeof(s->challenge), "%d", rand());
+
+ memset(&mo, 0, sizeof(struct message));
+ AddHeader(&mo, "Response: Success");
+ AddHeader(&mo, "Challenge: %s", s->challenge);
+
+ s->output->write(s, &mo);
+ return 0;
+}
+
void *ProxySetAutoFilter(struct mansession *s, struct message *m) {
struct message mo;
char *value;
@@ -86,20 +106,80 @@ void *ProxySetAutoFilter(struct mansession *s, struct message *m) {
return 0;
}
-void *ProxyLogin(struct mansession *s) {
- struct message m;
+int AuthMD5(char *key, char *challenge, char *password) {
+ int x;
+ int len=0;
+ char md5key[256] = "";
+ struct MD5Context md5;
+ unsigned char digest[16];
- /* Send back dummy output for clients that insist on authenticating the old way */
- /* Mostly, we want to avoid sending this to Asterisk */
+ if (!*key || !*challenge || !*password )
+ return 1;
- /* Response: Success */
- /* Message: Authentication accepted */
+ if (debug)
+ debugmsg("MD5 password=%s, challenge=%s", password, challenge);
+
+ MD5Init(&md5);
+ MD5Update(&md5, (unsigned char *) challenge, strlen(challenge));
+ MD5Update(&md5, (unsigned char *) password, strlen(password));
+ MD5Final(digest, &md5);
+ for (x=0;x<16;x++)
+ len += sprintf(md5key + len, "%2.2x", digest[x]);
+ if( debug ) {
+ debugmsg("MD5 computed=%s, received=%s", md5key, key);
+ }
+ if (!strcmp(md5key, key))
+ return 0;
+ else
+ return 1;
+}
- memset(&m, 0, sizeof(struct message));
- AddHeader(&m, "Response: Success");
- AddHeader(&m, "Message: Authentication accepted");
+void *ProxyLogin(struct mansession *s, struct message *m) {
+ struct message mo;
+ struct proxy_user *pu;
+ char *user, *secret, *key;
+
+ user = astman_get_header(m, "Username");
+ secret = astman_get_header(m, "Secret");
+ key = astman_get_header(m, "Key");
+
+ memset(&mo, 0, sizeof(struct message));
+ if( debug )
+ debugmsg("Login attempt as: %s/%s", user, secret);
+
+ pthread_mutex_lock(&userslock);
+ pu = pc.userlist;
+ while( pu ) {
+ if ( !strcmp(user, pu->username) ) {
+ if (!AuthMD5(key, s->challenge, pu->secret) ||
+ !strcmp(secret, pu->secret) ) {
+ AddHeader(&mo, "Response: Success");
+ AddHeader(&mo, "Message: Authentication accepted");
+ s->output->write(s, &mo);
+ pthread_mutex_lock(&s->lock);
+ s->authenticated = 1;
+ strcpy(s->user.channel, pu->channel);
+ strcpy(s->user.icontext, pu->icontext);
+ strcpy(s->user.ocontext, pu->ocontext);
+ pthread_mutex_unlock(&s->lock);
+ if( debug )
+ debugmsg("Login as: %s", user);
+ break;
+ }
+ }
+ pu = pu->next;
+ }
+ pthread_mutex_unlock(&userslock);
+
+ if( !pu ) {
+ SendError(s, "Authentication failed");
+ pthread_mutex_lock(&s->lock);
+ s->authenticated = 0;
+ pthread_mutex_unlock(&s->lock);
+ if( debug )
+ debugmsg("Login failed as: %s/%s", user, secret);
+ }
- s->output->write(s, &m);
return 0;
}
@@ -130,10 +210,7 @@ int ProxyAddServer(struct mansession *s, struct message *m) {
fprintf(stderr, "Failed to allocate server credentials: %s\n", strerror(errno));
exit(1);
}
- memset(srv, 0, sizeof (struct ast_server) );
-
- /* TODO: Disallow adding of duplicate servers? Or not, I suppose that could be useful (events on/off) */
memset(srv, 0, sizeof(struct ast_server) );
memset(&mo, 0, sizeof(struct message));
strcpy(srv->ast_host, astman_get_header(m, "Server"));
@@ -256,3 +333,58 @@ int proxyerror_do(struct mansession *s, char *err)
return 0;
}
+int ValidateAction(struct message *m, struct mansession *s, int inbound) {
+ char *channel, *channel1, *channel2;
+ char *context;
+ char *uchannel;
+ char *ucontext;
+
+ if( pc.authrequired && !s->authenticated )
+ return 0;
+
+ if( inbound )
+ ucontext = s->user.icontext;
+ else
+ ucontext = s->user.ocontext;
+ uchannel = s->user.channel;
+
+ channel = astman_get_header(m, "Channel");
+ if( channel[0] != '\0' && uchannel[0] != '\0' )
+ if( strncasecmp( channel, uchannel, strlen(uchannel) ) ) {
+ if( debug )
+ debugmsg("Message filtered (chan): %s != %s", channel, uchannel);
+ return 0;
+ }
+
+ channel1 = astman_get_header(m, "Channel1");
+ channel2 = astman_get_header(m, "Channel2");
+ if( (channel1[0] != '\0' || channel2[0] != '\0') && uchannel[0] != '\0' )
+ if( !(strncasecmp( channel1, uchannel, strlen(uchannel) ) == 0 ||
+ strncasecmp( channel2, uchannel, strlen(uchannel) ) == 0) ) {
+ if( debug )
+ debugmsg("Message filtered (chan): %s/%s != %s", channel1, channel2, uchannel);
+ return 0;
+ }
+
+ context = astman_get_header(m, "Context");
+ if( context[0] != '\0' && ucontext[0] != '\0' )
+ if( strcasecmp( context, ucontext ) ) {
+ if( debug )
+ debugmsg("Message filtered (ctxt): %s != %s", context, ucontext);
+ return 0;
+ }
+
+ return 1;
+}
+
+void *SendError(struct mansession *s, char *errmsg) {
+ struct message m;
+
+ memset(&m, 0, sizeof(struct message));
+ AddHeader(&m, "Response: Error");
+ AddHeader(&m, "Message: %s", errmsg);
+
+ s->output->write(s, &m);
+
+ return 0;
+}
diff --git a/src/ssl.c b/src/ssl.c
new file mode 100644
index 0000000..9f5b341
--- /dev/null
+++ b/src/ssl.c
@@ -0,0 +1,438 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Tello Corporation, Inc.
+ *
+ * Remco Treffkorn(Architect) and Mahesh Karoshi(Senior Software Developer)
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief SSL for The Asterisk Management Interface - AMI
+ *
+ * Channel Management and more
+ *
+ * \author Remco Treffkorn(Architect) and Mahesh Karoshi(Senior Software Developer)
+ * \ref amiconf
+ */
+
+/*! \addtogroup Group_AMI AMI functions
+*/
+/*! @{
+ Doxygen group */
+
+/*! \note We use negative file descriptors for secure channels. The file descriptor
+ -1 is reseved for errors. -2 to -... are secure file descriptors. 0 to ...
+ are regular file descriptors.
+
+ NOTE: Commonly error checks for routines returning fd's are done with (value<0).
+ You must check for (value==-1) instead, since all other negative fd's now
+ are valid fd's.
+*/
+#include <sys/types.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include "ssl.h"
+
+SSL_CTX *sctx;
+SSL_CTX *cctx;
+static long rec_bytes;
+static long sent_bytes;
+static int ssl_initialized;
+
+
+/*! \brief this has to be called before any other function dealing with ssl.
+ Initializes all the ssl related stuff here. */
+int init_secure(char *certfile)
+{
+ SSL_METHOD *meth;
+
+ SSLeay_add_ssl_algorithms();
+ SSL_load_error_strings();
+
+ /* server init */
+ meth = SSLv23_server_method();
+ sctx = SSL_CTX_new(meth);
+
+ if (!sctx) {
+ return errexit("Failed to create a server ssl context!");
+ }
+
+ if (SSL_CTX_use_certificate_file(sctx, certfile, SSL_FILETYPE_PEM) <= 0) {
+ return errexit("Failed to use the certificate file!");
+ }
+
+ if (SSL_CTX_use_PrivateKey_file(sctx, certfile, SSL_FILETYPE_PEM) <= 0) {
+ return errexit("Failed to use the key file!\n");
+ }
+
+ if (!SSL_CTX_check_private_key(sctx)) {
+ return errexit("Private key does not match the certificate public key");
+ }
+ ssl_initialized = 1;
+ return 0;
+}
+
+
+/* Initializes all the client-side ssl related stuff here.
+*/
+int client_init_secure(void)
+{
+ SSL_METHOD *meth;
+
+ /* client init */
+ SSLeay_add_ssl_algorithms();
+ meth = SSLv23_client_method();
+ SSL_load_error_strings();
+ cctx = SSL_CTX_new (meth);
+
+ if (!cctx)
+ debugmsg("Failed to create a client ssl context!");
+ else
+ debugmsg("Client SSL Context Initialized");
+ return 0;
+}
+
+/*! \brief Takes the negative ssl fd and returns the positive fd recieved from the os.
+ * It goes through arrray of fixed maximum number of secured channels.
+*/
+int get_real_fd(int fd)
+{
+ if (fd<-1) {
+ fd = -fd - 2;
+ if (fd>=0 && fd <SEC_MAX)
+ fd = sec_channel[fd].fd;
+ else fd = -1;
+
+ }
+ return fd;
+}
+
+/*! \brief Returns the SSL pointer from the fd. This structure is filled when we accept
+ * the ssl connection and used
+ * for reading and writing through ssl.
+*/
+SSL *get_ssl(int fd)
+{
+ SSL *ssl = NULL;
+
+ fd = -fd - 2;
+
+ if (fd>=0 && fd <SEC_MAX)
+ ssl = sec_channel[fd].ssl;
+
+ return ssl;
+}
+
+/*! \brief Returns the empty ssl slot. Used to save ssl information.
+*/
+int sec_getslot(void)
+{
+ int i;
+
+ for (i=0; i<SEC_MAX; i++) {
+ if(sec_channel[i].ssl==NULL)
+ break;
+ }
+
+ if (i==SEC_MAX)
+ return -1;
+ return i;
+}
+
+/*! \brief Accepts the ssl connection. Returns the negative fd. negative fd's are
+ * chosen to differentiate between ssl and non-ssl connections. Positive
+ * fd's are used for non-ssl connections and negative fd's are used for ssl
+ * connections. So we purposefully calculate and return negative fds.
+ * You can always get positive fd by calling get_real_fd(negative fd).
+ * The positive fd's are required for system calls.
+ *
+*/
+int saccept(int s)
+{
+ int fd, err;
+ SSL* ssl;
+
+ if (!ssl_initialized)
+ return s;
+
+ if (((fd=sec_getslot())!=-1)) {
+ ssl=SSL_new(sctx);
+ SSL_set_fd(ssl, s);
+ sec_channel[fd].ssl = ssl; /* remember ssl */
+ sec_channel[fd].fd = s; /* remember the real fd */
+ do {
+ err = SSL_accept(ssl);
+ err = SSL_get_error(ssl, err);
+ } while( err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE);
+
+ SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ debugmsg("ssl_addon: Connection accepted");
+
+ err=1;
+
+ fd = -(fd+2);
+
+ if (err!=1 || !ssl) {
+ /* it did not work */
+ sec_channel[fd].ssl = NULL; /* free the slot */
+ fd = -1;
+ }
+ }
+ return fd;
+}
+
+/*!
+ * \brief Writes through secured ssl connection
+*/
+int m_send(int fd, const void *data, size_t len)
+{
+ sent_bytes += len;
+
+ if (fd < -1) {
+ SSL* ssl = get_ssl(fd);
+ return SSL_write(ssl, data, len);
+ }
+ return write(fd, data, len);
+}
+
+/*!
+ * \brief Receives data from the SSL connection.
+*/
+int m_recv(int s, void *buf, size_t len, int flags)
+{
+ int ret = 0;
+
+ if (s<-1) {
+ SSL* ssl = get_ssl(s);
+ ret = SSL_read (ssl, buf, len);
+ } else
+ ret = recv(s, buf, len, flags);
+
+ if (ret > 0)
+ rec_bytes += ret;
+
+ if (debug && s<-1)
+ debugmsg("Received %d bytes from SSL socket", ret);
+ return ret;
+}
+
+
+/*! \brief
+ Needs to be called instead of close() to close a socket.
+ It also closes the SSL meta connection.
+*/
+
+int close_sock(int socket)
+{
+ int ret=0;
+ SSL* ssl = NULL;
+
+ if (socket < -1) {
+ socket = - socket - 2;
+
+ ssl = sec_channel[socket].ssl;
+ sec_channel[socket].ssl = NULL;
+ socket = sec_channel[socket].fd;
+ }
+
+ ret= close(socket);
+
+ if (ssl)
+ SSL_free (ssl);
+
+ return(ret);
+}
+
+/*! \brief This process cannot continue without fixing this error.
+*/
+int errexit(char s[])
+{
+ debugmsg("SSL critical error: %s", s);
+ return -1;
+}
+
+/*! \brief Checks whether the client is requesting an ssl encrypted connection or not. If its encrypted
+ * request we expect "Client Hello" in the beginning of the message and ssl version 2.
+ * This can be verified by checking buf[0x02], buf[0x03] and buf[0x04]. If the contents are
+ * 0x01, 0x00, 0x02, then its an ssl packet with content "Client Hello", "SSL version 2".
+ * For SSL version 3, we might need to check for 0x01, 0x00, 0x03.
+ *
+*/
+int is_encrypt_request(int sslclhellotimeout, int fd)
+{
+ fd_set listeners;
+ struct timeval tv;
+ char buf[1024];
+ int ready_fdescriptors;
+ int ret;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = sslclhellotimeout * 1000;
+
+ FD_ZERO(&listeners);
+ FD_SET(fd, &listeners);
+
+ ready_fdescriptors = select (fd + 1, &listeners, NULL, NULL, &tv);
+
+ if (ready_fdescriptors < 0 ) {
+ debugmsg("is_encrypt_request: select returned error, This should not happen:");
+ return 0;
+ } else if (ready_fdescriptors == 0) {
+ return 0;
+ }
+ ret = recv(fd, buf, 100, MSG_PEEK);
+ if(ret > 0) {
+ /* check for sslv3 or tls*/
+ if ((buf[0x00] == 0x16) && (buf[0x01] == 0x03) &&
+ /* for tls buf[0x02] = 0x01 and ssl v3 buf[0x02] = 0x02 */
+ ((buf[0x02] == 0x00) || (buf[0x02] == 0x01))) {
+ if (debug)
+ debugmsg("Received a SSL request");
+ return 1;
+ /* check for sslv23_client_method */
+ } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) {
+ if (debug)
+ debugmsg("Received a SSL request for SSLv23_client_method()");
+ return 1;
+ }
+ /* check for sslv2 and return -1 */
+ else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x00) && (buf[0x04] == 0x02)) {
+ if (debug)
+ debugmsg("Received a SSLv2 request()");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+/* Connects to an asterisk server either plain or SSL as appropriate
+*/
+int ast_connect(struct mansession *a) {
+ int s, err=-1, fd;
+ SSL* ssl;
+
+ fd = connect_nonb(a);
+ if ( fd < 0 )
+ return -1;
+
+ if (a->server->use_ssl) {
+ debugmsg("initiating ssl connection");
+ if ((s=sec_getslot())!=-1) { /* find a slot for the ssl handle */
+ sec_channel[s].fd = fd; /* remember the real fd */
+
+ if((ssl=SSL_new(cctx))) { /* get a new ssl */
+ sec_channel[s].ssl = ssl;
+ SSL_set_fd(ssl, fd); /* and attach the real fd */
+ err = SSL_connect(ssl); /* now try and connect */
+ } else
+ debugmsg("couldn't create ssl client context");
+ fd = -(s+2); /* offset by two and negate */
+ /* this tells us it is a ssl fd */
+ } else
+ debugmsg("couldn't get SSL slot!");
+
+ if (err==-1) {
+ close_sock(fd); /* that frees the ssl too */
+ fd = -1;
+ }
+ }
+
+ debugmsg("returning ast_connect with %d", fd);
+ pthread_mutex_lock(&a->lock);
+ a->fd = fd;
+ pthread_mutex_unlock(&a->lock);
+
+ return fd;
+}
+
+int connect_nonb(struct mansession *a)
+{
+ int flags, n, error;
+ socklen_t len;
+ fd_set rset, wset;
+ struct timeval tval;
+ int nsec = 1, sockfd;
+
+ sockfd = get_real_fd(a->fd);
+
+ flags = fcntl(sockfd, F_GETFL, 0);
+ fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+
+ error = 0;
+ if ( (n = connect(sockfd, (struct sockaddr *) &a->sin, sizeof(a->sin)) ) < 0 ) {
+ /* TODO: This seems like the nine pound hammer to me... */
+ /* perhaps something a bit more elegant; errno seems to change too */
+ if (errno == EISCONN || errno == 103 || errno==111) {
+ debugmsg("connect_nonb: error %d, closing old fd and grabbing a new one...", errno);
+ /* looks like our old socket died, let's round up a new one and try again */
+ close_sock(a->fd);
+ pthread_mutex_lock(&a->lock);
+ a->fd = socket(AF_INET, SOCK_STREAM, 0);
+ pthread_mutex_unlock(&a->lock);
+ return(-1);
+ }
+ if (errno != EINPROGRESS)
+ return(-1);
+ }
+
+ /* Do whatever we want while the connect is taking place. */
+
+ if (n == 0)
+ goto done; /* connect completed immediately */
+
+ FD_ZERO(&rset);
+ FD_SET(sockfd, &rset);
+ wset = rset;
+ tval.tv_sec = nsec;
+ tval.tv_usec = 0;
+
+ if ( (n = select(sockfd+1, &rset, &wset, NULL,
+ nsec ? &tval : NULL)) == 0) {
+ /*close(sockfd);*/ /* we want to retry */
+ errno = ETIMEDOUT;
+ return(-1);
+ }
+
+ if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
+ len = sizeof(error);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+ return(-1); /* Solaris pending error */
+ } else {
+ /*err_quit("select error: sockfd not set");*/
+ logmsg("select error: sockfd not set");
+ return(-1);
+ }
+
+done:
+ fcntl(sockfd, F_SETFL, flags); /* restore file status flags */
+
+ if (error) {
+ /* close(sockfd); */ /* disable for now, we want to retry... */
+ errno = error;
+ return(-1);
+ }
+ return(sockfd);
+}
diff --git a/standard.c b/src/standard.c
index de62cf7..9e8f200 100644
--- a/standard.c
+++ b/src/standard.c
@@ -15,7 +15,6 @@ extern struct mansession *sessions;
int _read(struct mansession *s, struct message *m) {
int res;
- if (debug) debugmsg("in standard_read module...");
for (;;) {
res = get_input(s, m->headers[m->hdrcount]);
@@ -46,13 +45,12 @@ int _read(struct mansession *s, struct message *m) {
int _write(struct mansession *s, struct message *m) {
int i;
- if (debug) debugmsg("in standard_write module...");
pthread_mutex_lock(&s->lock);
for (i=0; i<m->hdrcount; i++) {
- write(s->fd, m->headers[i], strlen(m->headers[i]) );
- write(s->fd, "\r\n", 2);
+ ast_carefulwrite(s->fd, m->headers[i], strlen(m->headers[i]) , s->writetimeout);
+ ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout);
}
- write(s->fd, "\r\n", 2);
+ ast_carefulwrite(s->fd, "\r\n", 2, s->writetimeout);
pthread_mutex_unlock(&s->lock);
return 0;
@@ -64,12 +62,9 @@ int _onconnect(struct mansession *s, struct message *m) {
sprintf(banner, "%s/%s\r\n", PROXY_BANNER, PROXY_VERSION);
pthread_mutex_lock(&s->lock);
- write(s->fd, banner, strlen(banner));
+ ast_carefulwrite(s->fd, banner, strlen(banner), s->writetimeout);
pthread_mutex_unlock(&s->lock);
return 0;
}
-int _autodisconnect() {
- return 0;
-}
diff --git a/xml.c b/src/xml.c
index 0d6737b..72dc4fb 100644
--- a/xml.c
+++ b/src/xml.c
@@ -74,7 +74,7 @@ int _write(struct mansession *s, struct message *m) {
sprintf(buf, "<%s>\r\n", xmldoctag);
pthread_mutex_lock(&s->lock);
- write(s->fd, buf, strlen(buf));
+ ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout);
for (i=0; i<m->hdrcount; i++) {
memset(xmlescaped, 0, sizeof xmlescaped);
@@ -89,20 +89,15 @@ int _write(struct mansession *s, struct message *m) {
strcat(outstring, "\"/>\r\n");
} else
sprintf(outstring, " <%s Value=\"%s\"/>\r\n", XML_UNPARSED, lpos);
- write(s->fd, outstring, strlen(outstring) );
+ ast_carefulwrite(s->fd, outstring, strlen(outstring), s->writetimeout);
}
sprintf(buf, "</%s>\r\n\r\n", xmldoctag);
- write(s->fd, buf, strlen(buf));
+ ast_carefulwrite(s->fd, buf, strlen(buf), s->writetimeout);
pthread_mutex_unlock(&s->lock);
return 0;
}
-int _autodisconnect() {
- return 0;
-}
-
-
/* Takes a single manager header line and converts xml entities */
void xml_quote_string(char *s, char *o) {