diff -Naur asteriskorg/client_ssl.c asterisk/client_ssl.c --- asteriskorg/client_ssl.c 1969-12-31 16:00:00.000000000 -0800 +++ asterisk/client_ssl.c 2006-02-20 17:32:54.000000000 -0800 @@ -0,0 +1,344 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2005 - 2006, Tello Corporation, Inc. + * + * Remco Treffkorn(Architect), 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 + * + * \ref amiconf + */ + +/*! \addtogroup Group_AMI AMI functions +*/ +/*! @{ + Doxygen group */ + +/*! 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. + + The routines in here demonstrate the use of secure fd's. + + 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 +#include +#include +#include +#include +#include +#include +#include +#include +//#include "asterisk/logger.h" + +#define BUFSIZE 1024 + +#define EVENTBUFSIZE 10*1024 + +#if !defined (INADDR_NONE) +#define INADDR_NONE ((in_addr_t)-1) +#endif + +#include "client_ssl.h" + +SSL_CTX *cctx; + +static long rec_bytes = 0; +static long sent_bytes = 0; +static int sslfd; + +/*! This has to be called before any other function dealing with ssl. + * Initializes all the 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) { + fprintf(stderr, "Failed to create a client ssl context!\n"); + } + return 0; +} + +/*! 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 =0 && fd 0) + rec_bytes += ret; + + return ret; +} + +/*! + * 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; + } + + //printf("closing the socket: %d\n", socket); + ret= close(socket); + + if (ssl) + SSL_free (ssl); + + return(ret); +} + + +/*! + * Provides the regular TCP connection. +*/ + +int connectsock(const char *host, const char *service, const char *protocol) +{ + struct hostent *phe; + struct servent *pse; + struct protoent *ppe; + struct sockaddr_in sin; + int s, type, one = 1; + + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + if ((pse = getservbyname(service, protocol))) + sin.sin_port = htons(ntohs((unsigned short) pse->s_port)); + else if ((sin.sin_port = htons((unsigned short) atoi(service))) == 0) + return -1; + if ((phe = gethostbyname(host))) + memcpy((char *) &sin.sin_addr, phe->h_addr, phe->h_length); + else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) + return -1; + if ((ppe = getprotobyname(protocol)) == 0) + return -1; + if (strcmp(protocol, "udp") == 0) + type = SOCK_DGRAM; + else + type = SOCK_STREAM; + + if ((s = socket(PF_INET, type, ppe->p_proto)) < 0) + return -1; + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))==-1) { + (void)close(s); + return -1; + } + if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + (void)close(s); + return -1; + } + return s; +} + +/*! +* Connects securly to the remote end. Initializes lots of ssl stuff. +* Returns the negative fd. Purposefully negative fd's are returned in order to +* differentiate between ssl and non-ssl connections. +*/ +int sec_connectTCP(char *host, char *service) +{ + int s, fd, err=-1; + SSL* ssl; + + if ((fd=connectsock(host, service, "tcp"))==-1) + return -1; + //printf("socket created: %d\n", fd); + + 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 */ + } + fd = -(s+2); /* offset by two and negate */ + /* this tells us it is a ssl fd */ + } + + if (err==-1) { + close_sock(fd); /* that frees the ssl too */ + fd = -1; + } + + return fd; +} + +int main(int argc, char* argv[]) +{ + char recvBuffer[BUFSIZE]; + char eventBuffer[EVENTBUFSIZE]; + char event[EVENTBUFSIZE]; + char actionBuf[500]; + char *strptr; + char hostname[100], port[100], username[100], password[100]; + int pos; + fd_set listeners; + struct timeval tv; + + if (argc == 5){ + strcpy(hostname,argv[1]); + strcpy(port,argv[2]); + strcpy(username,argv[3]); + strcpy(password, argv[4]); + } else { + printf("Usage: ./client_ssl asteriskhostname portnumber username password\n"); + exit(0); + } + signal(SIGINT, catch_int); + client_init_secure(); + if((sslfd = sec_connectTCP(hostname, port)) == -1) { + printf("SSL connection to asterisk failed : \n"); + exit(0); + } + strcpy(actionBuf, "Action: Login\r\n"); + strcat(actionBuf, "Username: "); + strcat(actionBuf, username); + strcat(actionBuf, "\r\n"); + strcat(actionBuf, "Secret: "); + strcat(actionBuf, password); + strcat(actionBuf, "\r\n\r\n"); + m_recv(sslfd, &recvBuffer, BUFSIZE - 1, 0); + if ((strptr = strstr(recvBuffer, "\r\n\r\n"))) { + pos = strptr - recvBuffer; // Length of event text... + strncpy(event,recvBuffer,pos); + } + printf("%s\n", event); + m_send(sslfd, actionBuf, BUFSIZE); + printf("The event sent to asterisk:\n %s", actionBuf); + tv.tv_sec = 1; + tv.tv_usec = 0; + while(1) { + FD_ZERO(&listeners); + FD_SET(get_real_fd(sslfd), &listeners); + int ready_fdescriptors = select (get_real_fd(sslfd) + 1, &listeners, NULL, NULL, &tv); + + if (ready_fdescriptors < 0 ) { + printf("select returned error, This should not happen: \n"); + close(get_real_fd(sslfd)); + exit(0); + } else if (ready_fdescriptors == 0) { + continue; + } + if(m_recv(sslfd, &recvBuffer, BUFSIZE - 1, 0) == 0) { + exit(0); + } else { + if ((strptr = strstr(recvBuffer, "\r\n\r\n"))) { + pos = strptr - recvBuffer; // Length of event text... + strncpy(event,recvBuffer,pos); + } + printf("%s\r\n", event); + } + } +} + +void catch_int(int sig_num) +{ + /* re-set the signal handler again to catch_int, for next time */ + signal(SIGINT, catch_int); + printf("Ctrl C pressed, closing the socket connection fd = %d\n", sslfd); + close_sock(sslfd); + exit(0); +} diff -Naur asteriskorg/client_ssl.h asterisk/client_ssl.h --- asteriskorg/client_ssl.h 1969-12-31 16:00:00.000000000 -0800 +++ asterisk/client_ssl.h 2006-02-09 15:08:30.000000000 -0800 @@ -0,0 +1,80 @@ +/* + * client_ssl: encrypts the asterisk management interface + * + * Copyrights: + * + * Contributors: + * Mahesh Karoshi and Remco Treffkorn(Architect) + * + * 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 _CLIENT_SSL_H_ +#define _CLIENT_SSL_H_ +#define SEC_MAX 8 + +#include +//SSL_CTX *cctx; + +/* +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. +*/ + +struct { + int fd; + SSL* ssl; +} sec_channel[SEC_MAX]; +/* + this has to be called before any other function dealing with ssl. +*/ + +int client_init_secure(void); + +// Returns the real fd, that is received from os, when we accept the connection. + +int get_real_fd(int fd); + +/* + Sends the data over secured or unsecured connections. +*/ +int m_send(int fd, const void *data, size_t len); + +/* + Receives the connection from either ssl or fd. +*/ +int m_recv(int s, void *buf, size_t len, int flags); + +/* + Needs to be called instead of close() to close a socket. + It also closes the ssl meta connection. +*/ +int close_sock(int socket); + +/* + provides the ssl connect api +*/ +int sec_connectTCP(char *host, char *service); + +// returns the ssl structure from the fd. +SSL* get_ssl(int fd); + +/* + Returns the availabe security slot. This restricts the maximun number of security connection, + the asterisk server can have for AMI. +*/ +int sec_getslot(void); + +/* + * connect with SSL +*/ +int connectsock(const char *host, const char *service, const char *protocol); + +void catch_int(int sig_num); +#endif + diff -Naur asteriskorg/configs/manager.conf.sample asterisk/configs/manager.conf.sample --- asteriskorg/configs/manager.conf.sample 2006-03-28 03:08:13.000000000 +0000 +++ asterisk/configs/manager.conf.sample 2006-03-28 03:09:58.000000000 +0000 @@ -24,7 +24,24 @@ ; Add a Unix epoch timestamp to events (not action responses) ; ;timestampevents = yes - +; +; +; For SSL encryption, where is the cert file? +; +certfile = /var/lib/asterisk/certs/server.pem +; +; How long do we wait on the manager port for an SSL session start? (ms) +; +sslclienthellotimeout = 500 +; +; Do we accept encrypted SSL manager connections? +; +acceptencryptedconnection = yes +; +; Do we accept unencrypted manager connections? +; +acceptunencryptedconnection = yes +; ;[mark] ;secret = mysecret diff -Naur asteriskorg/configs/ssl.conf.sample asterisk/configs/ssl.conf.sample --- asteriskorg/configs/ssl.conf.sample 1969-12-31 16:00:00.000000000 -0800 +++ asterisk/configs/ssl.conf.sample 2006-02-13 11:00:11.000000000 -0800 @@ -0,0 +1,154 @@ +# $Id: openssl.cnf,v 1.2 2004/01/22 19:27:32 jmates Exp $ +# +# 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 = CA_default + +[ CA_default ] +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 -Naur asteriskorg/Makefile asterisk/Makefile --- asteriskorg/Makefile 2006-02-19 21:51:39.000000000 -0800 +++ asterisk/Makefile 2006-02-19 22:17:16.000000000 -0800 @@ -135,7 +135,7 @@ AGI_DIR=$(ASTVARLIBDIR)/agi-bin endif -ASTCFLAGS= +ASTCFLAGS=-I/usr/kerberos/include # Pentium Pro Optimize #PROC=i686 @@ -218,7 +218,7 @@ endif INCLUDE+=-Iinclude -I../include -ASTCFLAGS+=-pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE #-DMAKE_VALGRIND_HAPPY +ASTCFLAGS+=-pipe -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations $(DEBUG) $(INCLUDE) -D_REENTRANT -D_GNU_SOURCE #-DAMI_WITH_SSL #-DMAKE_VALGRIND_HAPPY ASTCFLAGS+=$(OPTIMIZE) ASTOBJ=-o asterisk @@ -338,7 +338,7 @@ OBJS=io.o sched.o logger.o frame.o loader.o config.o channel.o \ translate.o file.o say.o pbx.o cli.o md5.o term.o \ ulaw.o alaw.o callerid.o fskmodem.o image.o app.o \ - cdr.o tdd.o acl.o rtp.o udptl.o manager.o asterisk.o \ + cdr.o tdd.o acl.o rtp.o udptl.o manager.o ssl_addon.o asterisk.o \ dsp.o chanvars.o indications.o autoservice.o db.o privacy.o \ astmm.o enum.o srv.o dns.o aescrypt.o aestab.o aeskey.o \ utils.o plc.o jitterbuf.o dnsmgr.o devicestate.o \ @@ -405,6 +405,10 @@ HAVEDOT=no endif +ifeq ($(findstring AMI_WITH_SSL, $(ASTCFLAGS)), AMI_WITH_SSL) + LIBS+=-lssl +endif + INSTALL=install _all: all @@ -685,6 +689,15 @@ @echo " + **Note** This requires that you have +" @echo " + doxygen installed on your local system +" @echo " +-------------------------------------------+" + @echo " + +" + @echo " + AMI can be encrypted or unencrypted. For +" + @echo " + encrypted you can either create your own +" + @echo " + certificate or use the one provided by +" + @echo " + the asterisk. If you want to create your +" + @echo " + own certificate, please read the ssl.txt +" + @echo " + documentation in the doc directory. +" + @echo " + +" + @echo " +-------------------------------------------+" @$(MAKE) -s oldmodcheck NEWMODS=$(notdir $(wildcard */*.so)) @@ -708,7 +721,7 @@ echo " WARNING WARNING WARNING" ;\ fi -install: all datafiles bininstall +install: all datafiles cert bininstall @if [ -x /usr/sbin/asterisk-post-install ]; then \ /usr/sbin/asterisk-post-install $(DESTDIR) . ; \ fi @@ -888,6 +901,59 @@ env: env + +UTF8 := $(shell locale -c LC_CTYPE -k | grep -q charmap.*UTF-8 && echo -utf8) +SERIAL=0 + +cert: + if [ ! -f /var/lib/asterisk/certs/server.pem ]; then \ + umask 77 ; \ + PEM1=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ + PEM2=`/bin/mktemp /tmp/openssl.XXXXXX` ; \ + sudo cp ./configs/ssl.conf.sample /etc/asterisk/ssl.conf; \ + /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config /etc/asterisk/ssl.conf; \ + cat $$PEM1 > $@ ; \ + echo "" >> $@ ; \ + cat $$PEM2 >> $@ ; \ + cat $@ > server.pem ;\ + sudo mkdir -p /var/lib/asterisk/certs; \ + sudo mv $@ /var/lib/asterisk/certs/server.pem; \ + $(RM) $$PEM1 $$PEM2 $@; \ + fi + + + +certificate: + createcert="1"; \ + if [ -f /var/lib/asterisk/certs/server.pem ]; 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` ; \ + sudo cp ./configs/ssl.conf.sample /etc/asterisk/ssl.conf; \ + /usr/bin/openssl req $(UTF8) -newkey rsa:1024 -keyout $$PEM1 -nodes -x509 -days 365 -out $$PEM2 -set_serial $(SERIAL) -config /etc/asterisk/ssl.conf; \ + cat $$PEM1 > $@ ; \ + echo "" >> $@ ; \ + cat $$PEM2 >> $@ ; \ + cat $@ > server.pem ;\ + sudo mkdir -p /var/lib/asterisk/certs; \ + sudo mv $@ /var/lib/asterisk/certs/server.pem; \ + $(RM) $$PEM1 $$PEM2 $@; \ + fi + # If the cleancount has been changed, force a make clean. # .cleancount is the global clean count, and .lastclean is the # last clean count we had diff -Naur asteriskorg/manager.c asterisk/manager.c --- asteriskorg/manager.c 2006-02-09 15:11:00.000000000 -0800 +++ asterisk/manager.c 2006-02-20 19:24:14.000000000 -0800 @@ -62,6 +62,9 @@ #include "asterisk/md5.h" #include "asterisk/acl.h" #include "asterisk/utils.h" +#ifdef AMI_WITH_SSL +#include "ssl_addon.h" +#endif struct fast_originate_helper { char tech[256]; @@ -78,6 +81,11 @@ struct ast_variable *vars; }; +#ifdef AMI_WITH_SSL +static int sslclhellotimeout = 200; +static int acceptencryptedconnection = 0; +static int acceptunencryptedconnection = 0; +#endif static int enabled = 0; static int portno = DEFAULT_MANAGER_PORT; static int asock = -1; @@ -117,7 +125,11 @@ int res=0; struct pollfd fds[1]; while(len) { +#ifdef AMI_WITH_SSL + res = m_send(fd, s, len); +#else res = write(fd, s, len); +#endif if ((res < 0) && (errno != EAGAIN)) { return -1; } @@ -126,7 +138,11 @@ s += res; res = 0; if (len) { +#ifdef AMI_WITH_SSL + fds[0].fd = get_real_fd(fd); +#else fds[0].fd = fd; +#endif fds[0].events = POLLOUT; /* Wait until writable again */ res = poll(fds, 1, timeoutms); @@ -267,8 +283,12 @@ static void free_session(struct mansession *s) { struct eventqent *eqe; +#ifdef AMI_WITH_SSL + close_sock(s->fd); +#else if (s->fd > -1) close(s->fd); +#endif ast_mutex_destroy(&s->__lock); while(s->eventq) { eqe = s->eventq; @@ -1358,6 +1378,10 @@ s->inlen = 0; } fds[0].fd = s->fd; +#ifdef AMI_WITH_SSL + fds[0].fd = get_real_fd(s->fd); +#endif + fds[0].events = POLLIN; do { res = poll(fds, 1, -1); @@ -1371,7 +1395,11 @@ return -1; } else if (res > 0) { ast_mutex_lock(&s->__lock); +#ifdef AMI_WITH_SSL + res = m_recv(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen, 0); +#else res = read(s->fd, s->inbuf + s->inlen, sizeof(s->inbuf) - 1 - s->inlen); +#endif ast_mutex_unlock(&s->__lock); if (res < 1) return -1; @@ -1454,6 +1482,31 @@ ast_log(LOG_WARNING, "Failed to set manager tcp connection to TCP_NODELAY mode: %s\n", strerror(errno)); } } +#ifdef AMI_WITH_SSL + int is_encrytped = is_encrypt_request(sslclhellotimeout, as); + if( is_encrytped > 0) { + if (! acceptencryptedconnection ) { + ast_log(LOG_NOTICE, "Accepting encrypted connection disabled, closing the connection \n"); + close_sock(as); + continue; + } else { + if((as = saccept(as)) >= 0 ) { + ast_log(LOG_NOTICE, "Can't accept the ssl connection, since SSL init has failed for certificate reason\n"); + close_sock(as); + continue; + } + } + } else if (is_encrytped == -1) { + ast_log(LOG_NOTICE, "SSL version 2 is unsecured, we don't support it\n"); + close_sock(as); + continue; + } + if ( (! acceptunencryptedconnection) && (as >= 0)) { + ast_log(LOG_NOTICE, "Unencrypted connections are not accepted and we received an unencrypted connection request\n"); + close_sock(as); + continue; + } +#endif s = malloc(sizeof(struct mansession)); if (!s) { ast_log(LOG_WARNING, "Failed to allocate management session: %s\n", strerror(errno)); @@ -1465,8 +1518,13 @@ if(! block_sockets) { /* For safety, make sure socket is non-blocking */ +#ifdef AMI_WITH_SSL + flags = fcntl(get_real_fd(as), F_GETFL); + fcntl(get_real_fd(as), F_SETFL, flags | O_NONBLOCK); +#else flags = fcntl(as, F_GETFL); fcntl(as, F_SETFL, flags | O_NONBLOCK); +#endif } ast_mutex_init(&s->__lock); s->fd = as; @@ -1649,6 +1707,9 @@ { struct ast_config *cfg; char *val; +#ifdef AMI_WITH_SSL + char certfile[1000], ssltimeout[1000]; +#endif int oldportno = portno; static struct sockaddr_in ba; int x = 1; @@ -1730,6 +1791,44 @@ ast_log(LOG_WARNING, "Unable to change management port / enabled\n"); #endif } + +#ifdef AMI_WITH_SSL + /* Parsing the certificate directory */ + if ((val = ast_variable_retrieve(cfg, "general", "certfile"))) { + if (sscanf(val, "%s", (char *)certfile) != 1) { + ast_log(LOG_WARNING, "Certificate directory not found, assigning default directory\n"); + strcpy((char *)certfile, "/var/lib/asterisk/certs/server.pem"); + } + } else { + strcpy((char *)certfile, "/var/lib/asterisk/certs/server.pem"); + } + if ((val = ast_variable_retrieve(cfg, "general", "sslclienthellotimeout"))) { + if (sscanf(val, "%s", (char *)ssltimeout) != 1) { + sslclhellotimeout = 500; + ast_log(LOG_NOTICE, "Failed to read sslclhellotime\n"); + } else { + sslclhellotimeout = atoi(ssltimeout); + ast_log(LOG_NOTICE, "sslclhellotime = %d", sslclhellotimeout); + } + } else { + sslclhellotimeout = 500; + } + val = ast_variable_retrieve(cfg, "general", "acceptencryptedconnection"); + if (val) { + acceptencryptedconnection = ast_true(val); + ast_log(LOG_NOTICE, "acceptencryptedconnection = %d", acceptencryptedconnection); + } else { + acceptencryptedconnection = -1; + } + val = ast_variable_retrieve(cfg, "general", "acceptunencryptedconnection"); + if (val) { + acceptunencryptedconnection = ast_true(val); + ast_log(LOG_NOTICE, "acceptunencryptedconnection = %d", acceptunencryptedconnection); + } else { + acceptunencryptedconnection = -1; + } + init_secure(certfile); +#endif ast_config_destroy(cfg); /* If not enabled, do nothing */ @@ -1767,3 +1866,4 @@ manager_event(EVENT_FLAG_SYSTEM, "Reload", "Message: Reload Requested\r\n"); return init_manager(); } + diff -Naur asteriskorg/ssl_addon.c asterisk/ssl_addon.c --- asteriskorg/ssl_addon.c 1969-12-31 16:00:00.000000000 -0800 +++ asterisk/ssl_addon.c 2006-02-21 20:10:19.000000000 -0800 @@ -0,0 +1,306 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 1999 - 2005, 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 + * + * \ref amiconf + */ + +/*! \addtogroup Group_AMI AMI functions +*/ +/*! @{ + Doxygen group */ + +/*! 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. + + The routines in here demonstrate the use of secure fd's. + + 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. +*/ +#ifdef AMI_WITH_SSL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "asterisk/logger.h" +#include "asterisk/config.h" + +#include "asterisk.h" +ASTERISK_FILE_VERSION(__FILE__, "$Revision: 7451 $") + +#include "ssl_addon.h" +SSL_CTX *sctx; +static long rec_bytes; +static long sent_bytes; +static int ssl_initialized; + + +/*! 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; +} + +/*! 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 =0 && fd 0) + rec_bytes += ret; + + ast_log(LOG_DEBUG, "ssl_addon: Received data from ssl\n"); + return ret; +} + + +/*! + 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); +} + +/*! This process cannot continue without fixing this error. +*/ +int errexit(char s[]) +{ + ast_log(LOG_NOTICE, "ssl_addon: %s", s); + return -1; +} + +/*! 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]; + + tv.tv_sec = 0; + tv.tv_usec = sslclhellotimeout * 1000; + + FD_ZERO(&listeners); + FD_SET(fd, &listeners); + + int ready_fdescriptors = select (fd + 1, &listeners, NULL, NULL, &tv); + + if (ready_fdescriptors < 0 ) { + ast_log(LOG_WARNING,"select returned error, This should not happen: \n"); + return 0; + } else if (ready_fdescriptors == 0) { + return 0; + } + int 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))) { + ast_log(LOG_DEBUG, "Received a SSL request\n"); + return 1; + /* check for sslv23_client_method */ + } else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x03) && (buf[0x04] == 0x01)) { + ast_log(LOG_DEBUG, "Received a SSL request for SSLv23_client_method()\n"); + return 1; + } + /* check for sslv2 and return -1 */ + else if ((buf[0x02] == 0x01) && (buf[0x03] == 0x00) && (buf[0x04] == 0x02)) { + return -1; + } + } + return 0; +} +#endif diff -Naur asteriskorg/ssl_addon.h asterisk/ssl_addon.h --- asteriskorg/ssl_addon.h 1969-12-31 16:00:00.000000000 -0800 +++ asterisk/ssl_addon.h 2006-02-13 15:42:23.000000000 -0800 @@ -0,0 +1,91 @@ +/* + * 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 + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + 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 8 +struct { + int fd; + SSL* ssl; +} sec_channel[SEC_MAX]; + +/*! + this has to be called before any other function dealing with ssl. +*/ +int init_secure(char* certfile); + +/*! + Returns the real fd, that is received from os, when we accept the connection. +*/ +int get_real_fd(int fd); + +/*! + Returns the ssl structure from the fd. +*/ +SSL *get_ssl(int fd); + +/*! + Returns the availabe security slot. This restricts the maximun number of security connection, + the asterisk server can have for AMI. +*/ +int sec_getslot(void); + +/*! + 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); + +/*! + Sends the data over secured or unsecured connections. +*/ +int m_send(int fd, const void *data, size_t len); + + +/*! + Receives the connection from either ssl or fd. +*/ +int m_recv(int s, void *buf, size_t len, int flags); + + +/*! + 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