From eb97d576eb4499a18e9dd8fb9b23b77a9d0bc2df Mon Sep 17 00:00:00 2001 From: Mark Spencer Date: Tue, 25 Dec 2001 21:12:07 +0000 Subject: [PATCH] Version 0.1.10 from FTP git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@396 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- asterisk.c | 45 +- asterisk.h | 1 + channels/Makefile | 9 +- include/asterisk/crypto.h | 71 +++ include/asterisk/io.h | 70 ++- include/asterisk/md5.h | 5 - include/asterisk/options.h | 2 + io.c | 40 ++ keys/iaxtel.pub | 6 + res/Makefile | 32 ++ res/res_adsi.c | 992 +++++++++++++++++++++++++++++++++++++ res/res_crypto.c | 633 +++++++++++++++++++++++ 12 files changed, 1877 insertions(+), 29 deletions(-) create mode 100755 include/asterisk/crypto.h create mode 100755 keys/iaxtel.pub create mode 100755 res/Makefile create mode 100755 res/res_adsi.c create mode 100755 res/res_crypto.c diff --git a/asterisk.c b/asterisk.c index 83a2e4635f..005cf33846 100755 --- a/asterisk.c +++ b/asterisk.c @@ -18,13 +18,16 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -46,6 +49,7 @@ int option_console=0; int option_highpriority=0; int option_remote=0; int option_exec=0; +int option_initcrypto=0; int fully_booted = 0; static int ast_socket = -1; /* UNIX Socket for allowing remote control */ @@ -87,7 +91,7 @@ static void *netconsole(void *vconsole) fd_set rfds; if (gethostname(hostname, sizeof(hostname))) - strncpy(hostname, "", sizeof(hostname)); + strncpy(hostname, "", sizeof(hostname)-1); snprintf(tmp, sizeof(tmp), "%s/%d/%s\n", hostname, mainpid, ASTERISK_VERSION); fdprint(con->fd, tmp); for(;;) { @@ -196,7 +200,7 @@ static int ast_makesocket(void) } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; - strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path)); + strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path)-1); res = bind(ast_socket, (struct sockaddr *)&sun, sizeof(sun)); if (res) { ast_log(LOG_WARNING, "Unable to bind socket to %s: %s\n", AST_SOCKET, strerror(errno)); @@ -227,7 +231,7 @@ static int ast_tryconnect(void) } memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_LOCAL; - strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path)); + strncpy(sun.sun_path, AST_SOCKET, sizeof(sun.sun_path)-1); res = connect(ast_consock, (struct sockaddr *)&sun, sizeof(sun)); if (res) { close(ast_consock); @@ -328,7 +332,7 @@ static void console_verboser(char *s, int pos, int replace, int complete) /* Return to the beginning of the line */ if (!pos) fprintf(stdout, "\r"); - fprintf(stdout, s + pos); + fputs(s + pos,stdout); fflush(stdout); if (complete) /* Wake up a select()ing console */ @@ -371,7 +375,7 @@ static void remoteconsolehandler(char *s) else system(getenv("SHELL") ? getenv("SHELL") : "/bin/sh"); } else - strncpy(cmd, s, sizeof(cmd)); + strncpy(cmd, s, sizeof(cmd)-1); if (!strcasecmp(s, "help")) fprintf(stdout, " ! Executes a given shell command\n"); if (!strcasecmp(s, "quit")) @@ -441,6 +445,8 @@ static void ast_remotecontrol(char * data) char *cpid; char *version; int pid; + int lastclear=0; + int oldstatus=0; char tmp[80]; read(ast_consock, buf, sizeof(buf)); if (data) { @@ -499,6 +505,18 @@ static void ast_remotecontrol(char * data) if (res < 1) break; buf[res] = 0; + /* If someone asks for a pass code, hide the password */ + if (!memcmp(buf, ">>>>", 4)) { + printf("Ooh, i should hide password!\n"); + if (!lastclear) { + oldstatus = ast_hide_password(STDIN_FILENO); + printf("Oldstatus = %d\n", oldstatus); + } + lastclear = 1; + } else if (lastclear) { + ast_restore_tty(STDIN_FILENO, oldstatus); + lastclear = 0; + } if (!lastpos) write(STDOUT_FILENO, "\r", 2); write(STDOUT_FILENO, buf, res); @@ -525,10 +543,12 @@ int main(int argc, char *argv[]) sigset_t sigs; if (gethostname(hostname, sizeof(hostname))) - strncpy(hostname, "", sizeof(hostname)); + strncpy(hostname, "", sizeof(hostname)-1); mainpid = getpid(); ast_ulaw_init(); + ast_alaw_init(); callerid_init(); + tdd_init(); if (getenv("HOME")) snprintf(filename, sizeof(filename), "%s/.asterisk_history", getenv("HOME")); /* Check if we're root */ @@ -537,7 +557,7 @@ int main(int argc, char *argv[]) exit(1); } /* Check for options */ - while((c=getopt(argc, argv, "fdvqprcx:")) != EOF) { + while((c=getopt(argc, argv, "fdvqprcix:")) != EOF) { switch(c) { case 'd': option_debug++; @@ -568,6 +588,9 @@ int main(int argc, char *argv[]) option_exec++; xarg = optarg; break; + case 'i': + option_initcrypto++; + break; case '?': exit(1); } @@ -596,7 +619,8 @@ int main(int argc, char *argv[]) ast_log(LOG_ERROR, "Unable to connect to remote asterisk\n"); exit(1); } - if (!option_verbose && !option_console && !option_debug) { + + if (!option_verbose && !option_debug && !option_nofork && !option_console) { pid = fork(); if (pid < 0) { ast_log(LOG_ERROR, "Unable to fork(): %s\n", strerror(errno)); @@ -605,6 +629,7 @@ int main(int argc, char *argv[]) if (pid) exit(0); } + ast_makesocket(); sigemptyset(&sigs); sigaddset(&sigs, SIGHUP); @@ -648,7 +673,7 @@ int main(int argc, char *argv[]) ast_verbose( "Asterisk Ready.\n"); fully_booted = 1; pthread_sigmask(SIG_UNBLOCK, &sigs, NULL); - ast_cli_register(&astshutdown); + ast_cli_register(&astshutdown); if (option_console) { /* Console stuff now... */ /* Register our quit function */ @@ -674,7 +699,7 @@ int main(int argc, char *argv[]) } } else { - /* Do nothing */ + /* Do nothing */ select(0,NULL,NULL,NULL,NULL); } return 0; diff --git a/asterisk.h b/asterisk.h index 583ee7a64c..34dae49c86 100755 --- a/asterisk.h +++ b/asterisk.h @@ -23,6 +23,7 @@ #define AST_VAR_DIR "/var/lib/asterisk" #define AST_LOG_DIR "/var/log/asterisk" #define AST_AGI_DIR "/var/lib/asterisk/agi-bin" +#define AST_KEY_DIR "/var/lib/asterisk/keys" #define AST_CONFIG_FILE "asterisk.conf" diff --git a/channels/Makefile b/channels/Makefile index 69f1a64010..06d751e38a 100755 --- a/channels/Makefile +++ b/channels/Makefile @@ -22,6 +22,8 @@ CFLAGS+=$(shell [ ! -f /usr/include/linux/if_wanpipe.h ] && echo " -DOLD_SANGOMA CFLAGS+=$(shell [ -f /usr/lib/libpri.so.1 ] && echo " -DZAPATA_PRI") ZAPPRI=$(shell [ -f /usr/lib/libpri.so.1 ] && echo "-lpri") +CFLAGS+=-DCRYPTO + CFLAGS+=#-DVOFRDUMPER ZAPDIR=/usr/lib @@ -30,11 +32,11 @@ CHANNEL_LIBS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "chan_zap.so") CFLAGS+=$(shell [ -f $(ZAPDIR)/libzap.a ] && echo "-I$(ZAPDIR)") -all: $(CHANNEL_LIBS) +all: $(CHANNEL_LIBS) clean: rm -f *.so *.o - rm -f busy.h ringtone.h gentone + rm -f busy.h ringtone.h gentone gentone-ulaw %.so : %.o $(CC) -shared -Xlinker -x -o $@ $< @@ -42,6 +44,9 @@ clean: gentone: gentone.c $(CC) -o gentone gentone.c -lm +gentone-ulaw: gentone-ulaw.c + $(CC) -o gentone-ulaw gentone-ulaw.c -lm + busy.h: gentone ./gentone busy 480 620 diff --git a/include/asterisk/crypto.h b/include/asterisk/crypto.h new file mode 100755 index 0000000000..4aac31b1df --- /dev/null +++ b/include/asterisk/crypto.h @@ -0,0 +1,71 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Provide cryptographic signature routines + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_CRYPTO_H +#define _ASTERISK_CRYPTO_H + +#include +#include + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#define AST_KEY_PUBLIC (1 << 0) +#define AST_KEY_PRIVATE (1 << 1) + +struct ast_key; + +//! Retrieve a key +/*! + * \param name of the key we are retrieving + * \param int type of key (AST_KEY_PUBLIC or AST_KEY_PRIVATE) + * + * Returns the key on success or NULL on failure + */ +extern struct ast_key *ast_key_get(char *key, int type); + +//! Initialize keys (that is, retrieve pass codes for all private keys) +/*! + * \param fd a file descriptor for I/O for passwords + * + */ +extern int ast_key_init(int fd); + +//! Check the authenticity of a message signature using a given public key +/*! + * \param key a public key to use to verify + * \param msg the message that has been signed + * \param sig the proposed valid signature in mime64-like encoding + * + * Returns 0 if the signature is valid, or -1 otherwise + * + */ +extern int ast_check_signature(struct ast_key *key, char *msg, char *sig); + +/*! + * \param key a private key to use to create the signature + * \param msg the message to sign + * \param sig a pointer to a buffer of at least 256 bytes in which the + * mime64-like encoded signature will be stored + * + * Returns 0 on success or -1 on failure. + * + */ +extern int ast_sign(struct ast_key *key, char *msg, char *sig); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif diff --git a/include/asterisk/io.h b/include/asterisk/io.h index 82060c20dd..d8d8d46e14 100755 --- a/include/asterisk/io.h +++ b/include/asterisk/io.h @@ -20,14 +20,20 @@ extern "C" { #endif -#define AST_IO_IN POLLIN /* Input ready */ -#define AST_IO_OUT POLLOUT /* Output ready */ -#define AST_IO_PRI POLLPRI /* Priority input ready */ +/*! Input ready */ +#define AST_IO_IN POLLIN +/*! Output ready */ +#define AST_IO_OUT POLLOUT +/*! Priority input ready */ +#define AST_IO_PRI POLLPRI /* Implicitly polled for */ -#define AST_IO_ERR POLLERR /* Error condition (errno or getsockopt) */ -#define AST_IO_HUP POLLHUP /* Hangup */ -#define AST_IO_NVAL POLLNVAL /* Invalid fd */ +/*! Error condition (errno or getsockopt) */ +#define AST_IO_ERR POLLERR +/*! Hangup */ +#define AST_IO_HUP POLLHUP +/*! Invalid fd */ +#define AST_IO_NVAL POLLNVAL /* * An Asterisk IO callback takes its id, a file descriptor, list of events, and @@ -37,34 +43,63 @@ extern "C" { struct io_context; -/* Create a context for I/O operations */ +//! Creates a context +/*! + * Create a context for I/O operations + * Basically mallocs an IO structure and sets up some default values. + * Returns an allocated io_context structure + */ extern struct io_context *io_context_create(void); -/* Destroy a context for I/O operations */ +//! Destroys a context +/* + * \param ioc structure to destroy + * Destroy a context for I/O operations + * Frees all memory associated with the given io_context structure along with the structure itself + */ extern void io_context_destroy(struct io_context *ioc); typedef int (*ast_io_cb)(int *id, int fd, short events, void *cbdata); #define AST_IO_CB(a) ((ast_io_cb)(a)) -/* +//! Adds an IO context +/*! + * \param ioc which context to use + * \param fd which fd to monitor + * \param callback callback function to run + * \param events event mask of events to wait for + * \param data data to pass to the callback * Watch for any of revents activites on fd, calling callback with data as * callback data. Returns a pointer to ID of the IO event, or NULL on failure. */ extern int *ast_io_add(struct io_context *ioc, int fd, ast_io_cb callback, short events, void *data); -/* +//! Changes an IO handler +/*! + * \param ioc which context to use + * \param id + * \param fd the fd you wish it to contain now + * \param callback new callback function + * \param events event mask to wait for + * \param data data to pass to the callback function * Change an i/o handler, updating fd if > -1, callback if non-null, and revents * if >-1, and data if non-null. Returns a pointero to the ID of the IO event, * or NULL on failure. */ extern int *ast_io_change(struct io_context *ioc, int *id, int fd, ast_io_cb callback, short events, void *data); -/* +//! Removes an IO context +/*! + * \param ioc which io_context to remove it from + * \param id which ID to remove * Remove an I/O id from consideration Returns 0 on success or -1 on failure. */ extern int ast_io_remove(struct io_context *ioc, int *id); -/* +//! Waits for IO +/*! + * \param ioc which context to act upon + * \param howlong how many milliseconds to wait * Wait for I/O to happen, returning after * howlong milliseconds, and after processing * any necessary I/O. Returns the number of @@ -72,11 +107,22 @@ extern int ast_io_remove(struct io_context *ioc, int *id); */ extern int ast_io_wait(struct io_context *ioc, int howlong); +//! Dumps the IO array /* * Debugging: Dump everything in the I/O array */ extern void ast_io_dump(struct io_context *ioc); +//! Set fd into non-echoing mode (if fd is a tty) + +extern int ast_hide_password(int fd); + +//! Restores TTY mode +/* + * Call with result from previous ast_hide_password + */ +extern int ast_restore_tty(int fd, int oldstatus); + #if defined(__cplusplus) || defined(c_plusplus) } #endif diff --git a/include/asterisk/md5.h b/include/asterisk/md5.h index e264f686db..53a40c2720 100755 --- a/include/asterisk/md5.h +++ b/include/asterisk/md5.h @@ -19,9 +19,4 @@ void MD5Update(struct MD5Context *context, unsigned char const *buf, void MD5Final(unsigned char digest[16], struct MD5Context *context); void MD5Transform(uint32 buf[4], uint32 const in[16]); -/* - * This is needed to make RSAREF happy on some MS-DOS compilers. - */ -typedef struct MD5Context MD5_CTX; - #endif /* !MD5_H */ diff --git a/include/asterisk/options.h b/include/asterisk/options.h index 9cd0b5ef90..375b216a18 100755 --- a/include/asterisk/options.h +++ b/include/asterisk/options.h @@ -23,12 +23,14 @@ extern int option_debug; extern int option_nofork; extern int option_quiet; extern int option_console; +extern int option_initcrypto; extern int fully_booted; extern char defaultlanguage[]; #define VERBOSE_PREFIX_1 " " #define VERBOSE_PREFIX_2 " == " #define VERBOSE_PREFIX_3 " -- " +#define VERBOSE_PREFIX_4 " > " #if defined(__cplusplus) || defined(c_plusplus) } diff --git a/io.c b/io.c index aef361c642..91aec690b4 100755 --- a/io.c +++ b/io.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -257,3 +258,42 @@ void ast_io_dump(struct io_context *ioc) } ast_log(LOG_DEBUG, "================================================\n"); } + +/* Unrelated I/O functions */ + +int ast_hide_password(int fd) +{ + struct termios tios; + int res; + int old; + if (!isatty(fd)) + return -1; + res = tcgetattr(fd, &tios); + if (res < 0) + return -1; + old = tios.c_lflag & (ECHO | ECHONL); + tios.c_lflag &= ~ECHO; + tios.c_lflag |= ECHONL; + res = tcsetattr(fd, TCSAFLUSH, &tios); + if (res < 0) + return -1; + return old; +} + +int ast_restore_tty(int fd, int oldstate) +{ + int res; + struct termios tios; + if (oldstate < 0) + return 0; + res = tcgetattr(fd, &tios); + if (res < 0) + return -1; + tios.c_lflag &= ~(ECHO | ECHONL); + tios.c_lflag |= oldstate; + res = tcsetattr(fd, TCSAFLUSH, &tios); + if (res < 0) + return -1; + return 0; +} + diff --git a/keys/iaxtel.pub b/keys/iaxtel.pub new file mode 100755 index 0000000000..d7c8f6cc8d --- /dev/null +++ b/keys/iaxtel.pub @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC3+joshldCUFgjj5DeYOgRLSPS +0t7gxTHiy1BTfynadPzgn447dy9iIQfBykE0pEdIPgaPMEt+ZPqPtln1P4dX3Ynx +I+RYKgtjsmYYnyJRMGHfHuLXwkbFkxyiEg0KDvbdBWbz7GUNZlp49BLXewWGY9DJ +MauE7FXVyCVDchn0YQIDAQAB +-----END PUBLIC KEY----- diff --git a/res/Makefile b/res/Makefile new file mode 100755 index 0000000000..9c4bffb6c4 --- /dev/null +++ b/res/Makefile @@ -0,0 +1,32 @@ +# +# Asterisk -- A telephony toolkit for Linux. +# +# Makefile for CDR backends (dynamically loaded) +# +# Copyright (C) 1999, Mark Spencer +# +# Mark Spencer +# +# This program is free software, distributed under the terms of +# the GNU General Public License +# + +MODS=res_adsi.so res_parking.so res_crypto.so + +CRYPTO_LIBS=-lssl -lcrypto + +CFLAGS+= + +all: $(MODS) + +install: all + for x in $(MODS); do $(INSTALL) -m 755 $$x $(MODULES_DIR) ; done + +res_crypto.so: res_crypto.o + $(CC) -shared -Xlinker -x -o $@ $< $(CRYPTO_LIBS) + +clean: + rm -f *.so *.o + +%.so : %.o + $(CC) -shared -Xlinker -x -o $@ $< diff --git a/res/res_adsi.c b/res/res_adsi.c new file mode 100755 index 0000000000..c7aad7b481 --- /dev/null +++ b/res/res_adsi.c @@ -0,0 +1,992 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * ADSI support + * + * Copyright (C) 2001, Linux Support Services, Inc. + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU General Public License. + * + * Includes code and algorithms from the Zapata library. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_ADSI_MAX_RETRIES 3 + +#define ADSI_MAX_INTRO 20 +#define ADSI_MAX_SPEED_DIAL 6 + +#define ADSI_FLAG_DATAMODE (1 << 8) + +static int maxretries = DEFAULT_ADSI_MAX_RETRIES; + +/* Asterisk ADSI button definitions */ +#define ADSI_SPEED_DIAL 10 /* 10-15 are reserved for speed dial */ + +static char intro[ADSI_MAX_INTRO][20]; +static int aligns[ADSI_MAX_INTRO]; + +static char speeddial[ADSI_MAX_SPEED_DIAL][3][20]; + +static int alignment = 0; + +static int adsi_generate(unsigned char *buf, int msgtype, char *msg, int msglen, int msgnum, int last) +{ + int sum; + int x; + int bytes=0; + /* Initial carrier (imaginary) */ + float cr = 1.0; + float ci = 0.0; + float scont = 0.0; + + if (msglen > 255) + msglen = 255; + + /* If first message, Send 150ms of MARK's */ + if (msgnum == 1) { + for (x=0;x<150;x++) /* was 150 */ + PUT_CLID_MARKMS; + } + /* Put message type */ + PUT_CLID(msgtype); + sum = msgtype; + + /* Put message length (plus one for the message number) */ + PUT_CLID(msglen + 1); + sum += msglen + 1; + + /* Put message number */ + PUT_CLID(msgnum); + sum += msgnum; + + /* Put actual message */ + for (x=0;xfds[0], &fds); + select(chan->fds[0] + 1, NULL, &fds, NULL, NULL); + res = write(chan->fds[0], buf, len); + if (res < 1) { + ast_log(LOG_WARNING, "Failed to write: %s\n", strerror(errno)); + return -1; + } + buf += res; + len -= res; + } + return 0; +} + +#else +static int adsi_careful_send(struct ast_channel *chan, unsigned char *buf, int len, int *remainder) +{ + /* Sends carefully on a full duplex channel by using reading for + timing */ + struct ast_frame *inf, outf; + int amt; + + /* Zero out our outgoing frame */ + memset(&outf, 0, sizeof(outf)); + + if (remainder && *remainder) { + amt = len; + + /* Send remainder if provided */ + if (amt > *remainder) + amt = *remainder; + else + *remainder = *remainder - amt; + outf.frametype = AST_FRAME_VOICE; + outf.subclass = AST_FORMAT_ULAW; + outf.data = buf; + outf.datalen = amt; + outf.timelen = amt * 8; + if (ast_write(chan, &outf)) { + ast_log(LOG_WARNING, "Failed to carefully write frame\n"); + return -1; + } + /* Update pointers and lengths */ + buf += amt; + len -= amt; + } + + while(len) { + amt = len; + /* If we don't get anything at all back in a second, forget + about it */ + if (ast_waitfor(chan, 1000) < 1) + return -1; + inf = ast_read(chan); + /* Detect hangup */ + if (!inf) + return -1; + if (inf->frametype == AST_FRAME_VOICE) { + /* Read a voice frame */ + if (inf->subclass != AST_FORMAT_ULAW) { + ast_log(LOG_WARNING, "Channel not in ulaw?\n"); + return -1; + } + /* Send no more than they sent us */ + if (amt > inf->datalen) + amt = inf->datalen; + else if (remainder) + *remainder = inf->datalen - amt; + outf.frametype = AST_FRAME_VOICE; + outf.subclass = AST_FORMAT_ULAW; + outf.data = buf; + outf.datalen = amt; + outf.timelen = amt * 8; + if (ast_write(chan, &outf)) { + ast_log(LOG_WARNING, "Failed to carefully write frame\n"); + return -1; + } + /* Update pointers and lengths */ + buf += amt; + len -= amt; + } + ast_frfree(inf); + } + return 0; +} +#endif + +static int __adsi_transmit_messages(struct ast_channel *chan, unsigned char **msg, int *msglen, int *msgtype) +{ + /* msglen must be no more than 256 bits, each */ + unsigned char buf[24000 * 5]; + int pos = 0, res; + int x; + int start=0; + int retries = 0; + + char ack[3]; + + /* Wait up to 500 ms for initial ACK */ + int waittime; + struct ast_frame *f; + int rem = 0; + + if (chan->adsicpe == AST_ADSI_UNAVAILABLE) { + /* Don't bother if we know they don't support ADSI */ + errno = ENOSYS; + return -1; + } + + while(retries < maxretries) { + if (!(chan->adsicpe & ADSI_FLAG_DATAMODE)) { + /* Generate CAS (no SAS) */ + ast_gen_cas(buf, 0, 680); + + /* Send CAS */ + if (adsi_careful_send(chan, buf, 680, NULL)) { + ast_log(LOG_WARNING, "Unable to send CAS\n"); + } + /* Wait For DTMF result */ + waittime = 500; + for(;;) { + if (((res = ast_waitfor(chan, waittime)) < 1)) { + /* Didn't get back DTMF A in time */ + ast_log(LOG_DEBUG, "No ADSI CPE detected (%d)\n", res); + if (!chan->adsicpe) + chan->adsicpe = AST_ADSI_UNAVAILABLE; + errno = ENOSYS; + return -1; + } + waittime = res; + f = ast_read(chan); + if (!f) { + ast_log(LOG_DEBUG, "Hangup in ADSI\n"); + return -1; + } + if (f->frametype == AST_FRAME_DTMF) { + if (f->subclass == 'A') { + /* Okay, this is an ADSI CPE. Note this for future reference, too */ + if (!chan->adsicpe) + chan->adsicpe = AST_ADSI_AVAILABLE; + break; + } else { + if (f->subclass == 'D') { + ast_log(LOG_DEBUG, "Off-hook capable CPE only, not ADSI\n"); + } else + ast_log(LOG_WARNING, "Unknown ADSI response '%c'\n", f->subclass); + if (!chan->adsicpe) + chan->adsicpe = AST_ADSI_UNAVAILABLE; + errno = ENOSYS; + return -1; + } + } + ast_frfree(f); + } + + ast_log(LOG_DEBUG, "ADSI Compatible CPE Detected\n"); + } else + ast_log(LOG_DEBUG, "Already in data mode\n"); + + x = 0; + pos = 0; +#if 0 + def= ast_channel_defer_dtmf(chan); +#endif + while((x < 6) && msg[x]) { + res = adsi_generate(buf + pos, msgtype[x], msg[x], msglen[x], x+1 - start, (x == 5) || !msg[x+1]); + if (res < 0) { + ast_log(LOG_WARNING, "Failed to generate ADSI message %d on channel %s\n", x + 1, chan->name); + return -1; + } + ast_log(LOG_DEBUG, "Message %d, of %d input bytes, %d output bytes\n", + x + 1, msglen[x], res); + pos += res; + x++; + } + + + rem = 0; + if (adsi_careful_send(chan, buf, pos, &rem)) + return -1; + + ast_log(LOG_DEBUG, "Sent total spill of %d bytes\n", pos); + + memset(ack, 0, sizeof(ack)); + /* Get real result */ + res = ast_readstring(chan, ack, 3, 1000, 1000, ""); + /* Check for hangup */ + if (res < 0) + return -1; + if (ack[0] == 'D') { + ast_log(LOG_DEBUG, "Acked up to message %d\n", atoi(ack + 1)); + start += atoi(ack + 1); + if (start >= x) + break; + else { + retries++; + ast_log(LOG_DEBUG, "Retransmitting (%d), from %d\n", start + 1); + } + } else { + retries++; + ast_log(LOG_WARNING, "Unexpected response to ack: %s (retry %d)\n", ack, retries); + } + } + if (retries >= maxretries) { + ast_log(LOG_WARNING, "Maximum ADSI Retries (%d) exceeded\n", maxretries); + errno = ETIMEDOUT; + return -1; + } + return 0; + +} + +int adsi_begin_download(struct ast_channel *chan, char *service, char *fdn, char *sec, int version) +{ + int bytes; + unsigned char buf[256]; + char ack[2]; + bytes = 0; + /* Setup the resident soft key stuff, a piece at a time */ + /* Upload what scripts we can for voicemail ahead of time */ + bytes += adsi_download_connect(buf + bytes, service, fdn, sec, version); + if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) + return -1; + if (ast_readstring(chan, ack, 1, 10000, 10000, "")) + return -1; + if (ack[0] == 'B') + return 0; + ast_log(LOG_DEBUG, "Download was denied by CPE\n"); + return -1; +} + +int adsi_end_download(struct ast_channel *chan) +{ + int bytes; + unsigned char buf[256]; + bytes = 0; + /* Setup the resident soft key stuff, a piece at a time */ + /* Upload what scripts we can for voicemail ahead of time */ + bytes += adsi_download_disconnect(buf + bytes); + if (adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD)) + return -1; + return 0; +} + +int adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype) +{ + unsigned char *msgs[5] = { NULL, NULL, NULL, NULL, NULL }; + int msglens[5]; + int msgtypes[5]; + int newdatamode; + int res; + int x; + int writeformat, readformat; + + writeformat = chan->writeformat; + readformat = chan->readformat; + + newdatamode = chan->adsicpe & ADSI_FLAG_DATAMODE; + + for (x=0;x 253) { + ast_log(LOG_WARNING, "Can't send ADSI message of %d bytes, too large\n", msglen); + return -1; + } + + ast_stopstream(chan); + + if (ast_set_write_format(chan, AST_FORMAT_ULAW)) { + ast_log(LOG_WARNING, "Unable to set write format to ULAW\n"); + return -1; + } + + if (ast_set_read_format(chan, AST_FORMAT_ULAW)) { + ast_log(LOG_WARNING, "Unable to set read format to ULAW\n"); + if (writeformat) { + if (ast_set_write_format(chan, writeformat)) + ast_log(LOG_WARNING, "Unable to restore write format to %d\n", writeformat); + } + return -1; + } + res = __adsi_transmit_messages(chan, msgs, msglens, msgtypes); + if (!res) + chan->adsicpe = (chan->adsicpe & ~ADSI_FLAG_DATAMODE) | newdatamode; + + if (writeformat) + ast_set_write_format(chan, writeformat); + if (readformat) + ast_set_read_format(chan, readformat); + + return res; +} + +static inline int ccopy(unsigned char *dst, unsigned char *src, int max) +{ + int x=0; + /* Carefully copy the requested data */ + while ((x < max) && src[x] && (src[x] != 0xff)) { + dst[x] = src[x]; + x++; + } + return x; +} + +int adsi_load_soft_key(unsigned char *buf, int key, unsigned char *llabel, unsigned char *slabel, unsigned char *ret, int data) +{ + int bytes=0; + + /* Abort if invalid key specified */ + if ((key < 2) || (key > 33)) + return -1; + buf[bytes++] = ADSI_LOAD_SOFTKEY; + /* Reserve for length */ + bytes++; + /* Which key */ + buf[bytes++] = key; + + /* Carefully copy long label */ + bytes += ccopy(buf + bytes, llabel, 18); + + /* Place delimiter */ + buf[bytes++] = 0xff; + + /* Short label */ + bytes += ccopy(buf + bytes, slabel, 7); + + + /* If specified, copy return string */ + if (ret) { + /* Place delimiter */ + buf[bytes++] = 0xff; + if (data) + buf[bytes++] = ADSI_SWITCH_TO_DATA2; + /* Carefully copy return string */ + bytes += ccopy(buf + bytes, ret, 20); + + } + /* Replace parameter length */ + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_connect_session(unsigned char *buf, unsigned char *fdn, int ver) +{ + int bytes=0; + int x; + + /* Message type */ + buf[bytes++] = ADSI_CONNECT_SESSION; + + /* Reserve space for length */ + bytes++; + + if (fdn) { + for (x=0;x<4;x++) + buf[bytes++] = fdn[x]; + if (ver > -1) + buf[bytes++] = ver & 0xff; + } + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_download_connect(unsigned char *buf, unsigned char *service, unsigned char *fdn, unsigned char *sec, int ver) +{ + int bytes=0; + int x; + + /* Message type */ + buf[bytes++] = ADSI_DOWNLOAD_CONNECT; + + /* Reserve space for length */ + bytes++; + + /* Primary column */ + bytes+= ccopy(buf + bytes, service, 18); + + /* Delimiter */ + buf[bytes++] = 0xff; + + for (x=0;x<4;x++) + buf[bytes++] = fdn[x]; + for (x=0;x<4;x++) + buf[bytes++] = sec[x]; + if (ver > -1) + buf[bytes++] = ver & 0xff; + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_disconnect_session(unsigned char *buf) +{ + int bytes=0; + + /* Message type */ + buf[bytes++] = ADSI_DISC_SESSION; + + /* Reserve space for length */ + bytes++; + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_data_mode(unsigned char *buf) +{ + int bytes=0; + + /* Message type */ + buf[bytes++] = ADSI_SWITCH_TO_DATA; + + /* Reserve space for length */ + bytes++; + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_voice_mode(unsigned char *buf, int when) +{ + int bytes=0; + + /* Message type */ + buf[bytes++] = ADSI_SWITCH_TO_VOICE; + + /* Reserve space for length */ + bytes++; + + buf[bytes++] = when & 0x7f; + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_available(struct ast_channel *chan) +{ + int cpe = chan->adsicpe & 0xff; + if ((cpe == AST_ADSI_AVAILABLE) || + (cpe == AST_ADSI_UNKNOWN)) + return 1; + return 0; +} + +int adsi_download_disconnect(unsigned char *buf) +{ + int bytes=0; + + /* Message type */ + buf[bytes++] = ADSI_DOWNLOAD_DISC; + + /* Reserve space for length */ + bytes++; + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_display(unsigned char *buf, int page, int line, int just, int wrap, + unsigned char *col1, unsigned char *col2) +{ + int bytes=0; + + /* Sanity check line number */ + + if (page) { + if (line > 4) return -1; + } else { + if (line > 33) return -1; + } + + if (line < 1) + return -1; + /* Parameter type */ + buf[bytes++] = ADSI_LOAD_VIRTUAL_DISP; + + /* Reserve space for size */ + bytes++; + + /* Page and wrap indicator */ + buf[bytes++] = ((page & 0x1) << 7) | ((wrap & 0x1) << 6) | (line & 0x3f); + + /* Justification */ + buf[bytes++] = (just & 0x3) << 5; + + /* Omit highlight mode definition */ + buf[bytes++] = 0xff; + + /* Primary column */ + bytes+= ccopy(buf + bytes, col1, 20); + + /* Delimiter */ + buf[bytes++] = 0xff; + + /* Secondary column */ + bytes += ccopy(buf + bytes, col2, 20); + + /* Update length */ + buf[1] = bytes - 2; + + return bytes; + +} + +int adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just) +{ + int bytes=0; + + if (page) { + if (line > 4) return -1; + } else { + if (line > 33) return -1; + } + + if (line < 1) + return -1; + + buf[bytes++] = ADSI_INPUT_CONTROL; + bytes++; + buf[bytes++] = ((page & 1) << 7) | (line & 0x3f); + buf[bytes++] = ((display & 1) << 7) | ((just & 0x3) << 4) | (format & 0x7); + + buf[1] = bytes - 2; + return bytes; + +} + +int adsi_input_format(unsigned char *buf, int num, int dir, int wrap, unsigned char *format1, unsigned char *format2) +{ + int bytes = 0; + + if (!strlen(format1)) + return -1; + + buf[bytes++] = ADSI_INPUT_FORMAT; + bytes++; + buf[bytes++] = ((dir & 1) << 7) | ((wrap & 1) << 6) | (num & 0x7); + bytes += ccopy(buf + bytes, format1, 20); + buf[bytes++] = 0xff; + if (format2 && strlen(format2)) { + bytes += ccopy(buf + bytes, format2, 20); + } + buf[1] = bytes - 2; + return bytes; +} + +int adsi_set_keys(unsigned char *buf, unsigned char *keys) +{ + int bytes=0; + int x; + /* Message type */ + buf[bytes++] = ADSI_INIT_SOFTKEY_LINE; + /* Space for size */ + bytes++; + /* Key definitions */ + for (x=0;x<6;x++) + buf[bytes++] = (keys[x] & 0x3f) ? keys[x] : (keys[x] | 0x1); + buf[1] = bytes - 2; + return bytes; +} + +int adsi_set_line(unsigned char *buf, int page, int line) +{ + int bytes=0; + + /* Sanity check line number */ + + if (page) { + if (line > 4) return -1; + } else { + if (line > 33) return -1; + } + + if (line < 1) + return -1; + /* Parameter type */ + buf[bytes++] = ADSI_LINE_CONTROL; + + /* Reserve space for size */ + bytes++; + + /* Page and line */ + buf[bytes++] = ((page & 0x1) << 7) | (line & 0x3f); + + buf[1] = bytes - 2; + return bytes; + +}; + +static int total = 0; +static int speeds = 0; + +#if 0 +int adsi_channel_init(struct ast_channel *chan) +{ + char dsp[256]; + char keys[256]; + int bytes; + int x; + unsigned char *msgs[3]; + unsigned char keyd[6]; + int lens[3]; + int types[3]; + memset(dsp, 0, sizeof(dsp)); + memset(keys, 0, sizeof(keys)); + memset(msgs, 0, sizeof(msgs)); + + /* Start with initial display setup */ + bytes = 0; + bytes += adsi_connect_session(dsp, NULL, -1); + for (x=0;xname, "alignment")) + alignment = str2align(v->value); + else if (!strcasecmp(v->name, "greeting")) { + if (x < ADSI_MAX_INTRO) { + aligns[x] = alignment; + strncpy(intro[x], v->value, 20); + x++; + } + } else if (!strcasecmp(v->name, "maxretries")) { + if (atoi(v->value) > 0) + maxretries = atoi(v->value); + } + v = v->next; + } + v = ast_variable_browse(conf, "speeddial"); + if (x) + total = x; + x = 0; + while(v) { + name = strtok(v->value, ","); + sname = strtok(NULL, ","); + if (!sname) + sname = name; + if (x < ADSI_MAX_SPEED_DIAL) { + /* Up to 20 digits */ + strncpy(speeddial[x][0], v->name, 20); + strncpy(speeddial[x][1], name, 18); + strncpy(speeddial[x][2], sname, 7); + x++; + } + v = v->next; + + } + if (x) + speeds = x; + ast_destroy(conf); + } +} + +int reload(void) +{ + adsi_load(); + return 0; +} + +int load_module(void) +{ + adsi_load(); + return 0; +} + +int unload_module(void) +{ + /* Can't unload this once we're loaded */ + return -1; +} + +char *description(void) +{ + return "Call Parking Resource"; +} + +int usecount(void) +{ + /* We should never be unloaded */ + return 1; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff --git a/res/res_crypto.c b/res/res_crypto.c new file mode 100755 index 0000000000..8aa4e9d6ea --- /dev/null +++ b/res/res_crypto.c @@ -0,0 +1,633 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Provide Cryptographic Signature capability + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../asterisk.h" + +/* + * Asterisk uses RSA keys with SHA-1 message digests for its + * digital signatures. The choice of RSA is due to its higher + * throughput on verification, and the choice of SHA-1 based + * on the recently discovered collisions in MD5's compression + * algorithm and recommendations of avoiding MD5 in new schemes + * from various industry experts. + * + * We use OpenSSL to provide our crypto routines, although we never + * actually use full-up SSL + * + */ + +/* + * XXX This module is not very thread-safe. It is for everyday stuff + * like reading keys and stuff, but there are all kinds of weird + * races with people running reload and key init at the same time + * for example + * + * XXXX + */ + +static char base64[64]; +static char b2a[256]; + +static pthread_mutex_t keylock = PTHREAD_MUTEX_INITIALIZER; + +#define KEY_NEEDS_PASSCODE (1 << 16) + +struct ast_key { + /* Name of entity */ + char name[80]; + /* File name */ + char fn[256]; + /* Key type (AST_KEY_PUB or AST_KEY_PRIV, along with flags from above) */ + int ktype; + /* RSA structure (if successfully loaded) */ + RSA *rsa; + /* Whether we should be deleted */ + int delme; + /* FD for input (or -1 if no input allowed, or -2 if we needed input) */ + int infd; + /* FD for output */ + int outfd; + /* Last MD5 Digest */ + unsigned char digest[16]; + struct ast_key *next; +}; + +static struct ast_key *keys = NULL; + +static int fdprint(int fd, char *s) +{ + return write(fd, s, strlen(s) + 1); +} + +static int pw_cb(char *buf, int size, int rwflag, void *userdata) +{ + struct ast_key *key = (struct ast_key *)userdata; + char prompt[256]; + int res; + int tmp; + if (key->infd > -1) { + snprintf(prompt, sizeof(prompt), ">>>> passcode for %s key '%s': ", + key->ktype == AST_KEY_PRIVATE ? "PRIVATE" : "PUBLIC", key->name); + write(key->outfd, prompt, strlen(prompt)); + memset(buf, 0, sizeof(buf)); + tmp = ast_hide_password(key->infd); + memset(buf, 0, size); + res = read(key->infd, buf, size); + ast_restore_tty(key->infd, tmp); + if (buf[strlen(buf) -1] == '\n') + buf[strlen(buf) - 1] = '\0'; + return strlen(buf); + } else { + /* Note that we were at least called */ + key->infd = -2; + } + return -1; +} + +struct ast_key *ast_key_get(char *kname, int ktype) +{ + struct ast_key *key; + ast_pthread_mutex_lock(&keylock); + key = keys; + while(key) { + if (!strcmp(kname, key->name) && + (ktype == key->ktype)) + break; + key = key->next; + } + ast_pthread_mutex_unlock(&keylock); + return key; +} + +static struct ast_key *try_load_key (char *dir, char *fname, int ifd, int ofd, int *not2) +{ + int ktype = 0; + char *c = NULL; + char ffname[256]; + char digest[16]; + FILE *f; + struct MD5Context md5; + struct ast_key *key; + static int notice = 0; + int found = 0; + + /* Make sure its name is a public or private key */ + + if ((c = strstr(fname, ".pub")) && !strcmp(c, ".pub")) { + ktype = AST_KEY_PUBLIC; + } else if ((c = strstr(fname, ".key")) && !strcmp(c, ".key")) { + ktype = AST_KEY_PRIVATE; + } else + return NULL; + + /* Get actual filename */ + snprintf(ffname, sizeof(ffname), "%s/%s", dir, fname); + + ast_pthread_mutex_lock(&keylock); + key = keys; + while(key) { + /* Look for an existing version already */ + if (!strcasecmp(key->fn, ffname)) + break; + key = key->next; + } + ast_pthread_mutex_unlock(&keylock); + + /* Open file */ + f = fopen(ffname, "r"); + if (!f) { + ast_log(LOG_WARNING, "Unable to open key file %s: %s\n", ffname, strerror(errno)); + return NULL; + } + MD5Init(&md5); + while(!feof(f)) { + /* Calculate a "whatever" quality md5sum of the key */ + char buf[256]; + fgets(buf, sizeof(buf), f); + if (!feof(f)) { + MD5Update(&md5, buf, strlen(buf)); + } + } + MD5Final(digest, &md5); + if (key) { + /* If the MD5 sum is the same, and it isn't awaiting a passcode + then this is far enough */ + if (!memcmp(digest, key->digest, 16) && + !(key->ktype & KEY_NEEDS_PASSCODE)) { + fclose(f); + key->delme = 0; + return NULL; + } else { + /* Preserve keytype */ + ktype = key->ktype; + /* Recycle the same structure */ + found++; + } + } + + /* Make fname just be the normal name now */ + *c = '\0'; + if (!key) { + key = (struct ast_key *)malloc(sizeof(struct ast_key)); + if (!key) { + ast_log(LOG_WARNING, "Out of memory\n"); + fclose(f); + return NULL; + } + memset(key, 0, sizeof(struct ast_key)); + } + /* At this point we have a key structure (old or new). Time to + fill it with what we know */ + /* Gotta lock if this one already exists */ + if (found) + ast_pthread_mutex_lock(&keylock); + /* First the filename */ + strncpy(key->fn, ffname, sizeof(key->fn)); + /* Then the name */ + strncpy(key->name, fname, sizeof(key->name)); + key->ktype = ktype; + /* Yes, assume we're going to be deleted */ + key->delme = 1; + /* Keep the key type */ + memcpy(key->digest, digest, 16); + /* Can I/O takes the FD we're given */ + key->infd = ifd; + key->outfd = ofd; + /* Reset the file back to the beginning */ + rewind(f); + /* Now load the key with the right method */ + if (ktype == AST_KEY_PUBLIC) + key->rsa = PEM_read_RSA_PUBKEY(f, NULL, pw_cb, key); + else + key->rsa = PEM_read_RSAPrivateKey(f, NULL, pw_cb, key); + fclose(f); + if (key->rsa) { + /* Key loaded okay */ + key->ktype &= ~KEY_NEEDS_PASSCODE; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Loaded %s key '%s'\n", key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name); + if (option_debug) + ast_log(LOG_DEBUG, "Key '%s' loaded OK\n", key->name); + key->delme = 0; + } else if (key->infd != -2) { + ast_log(LOG_WARNING, "Key load %s '%s' failed\n",key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name); + if (ofd > -1) { + ERR_print_errors_fp(stderr); + } else + ERR_print_errors_fp(stderr); + } else { + ast_log(LOG_NOTICE, "Key '%s' needs passcode.\n", key->name); + key->ktype |= KEY_NEEDS_PASSCODE; + if (!notice) { + if (!option_initcrypto) + ast_log(LOG_NOTICE, "Add the '-i' flag to the asterisk command line if you want to automatically initialize passcodes at launch.\n"); + notice++; + } + /* Keep it anyway */ + key->delme = 0; + /* Print final notice about "init keys" when done */ + *not2 = 1; + } + if (found) + ast_pthread_mutex_unlock(&keylock); + if (!found) { + ast_pthread_mutex_lock(&keylock); + key->next = keys; + keys = key; + ast_pthread_mutex_unlock(&keylock); + } + return key; +} + +#if 0 + +static void dump(unsigned char *src, int len) +{ + int x; + for (x=0;x= 8) { + bits -= 8; + *dst = (byte >> bits) & 0xff; +#if 0 + printf("Remove: %02x %s\n", *dst, binary(*dst, 8)); +#endif + dst++; + cnt++; + } + } +#if 0 + dump(odst, cnt); +#endif + /* Dont worry about left over bits, they're extra anyway */ + return cnt; +} + +static int base64encode(char *dst, unsigned char *src, int srclen, int max) +{ + int cnt = 0; + unsigned int byte = 0; + int bits = 0; + int index; + int cntin = 0; +#if 0 + char *odst = dst; + dump(src, srclen); +#endif + /* Reserve one bit for end */ + max--; + while((cntin < srclen) && (cnt < max)) { + byte <<= 8; +#if 0 + printf("Add: %02x %s\n", *src, binary(*src, 8)); +#endif + byte |= *(src++); + bits += 8; + cntin++; + while((bits >= 6) && (cnt < max)) { + bits -= 6; + /* We want only the top */ + index = (byte >> bits) & 0x3f; + *dst = base64[index]; +#if 0 + printf("Remove: %c %s\n", *dst, binary(index, 6)); +#endif + dst++; + cnt++; + } + } + if (bits && (cnt < max)) { + /* Add one last character for the remaining bits, + padding the rest with 0 */ + byte <<= (6 - bits); + index = (byte) & 0x3f; + *(dst++) = base64[index]; + cnt++; + } + *dst = '\0'; + return cnt; +} + +int ast_sign(struct ast_key *key, char *msg, char *sig) +{ + unsigned char digest[20]; + unsigned char dsig[128]; + int siglen = sizeof(dsig); + int res; + + if (key->ktype != AST_KEY_PRIVATE) { + ast_log(LOG_WARNING, "Cannot sign with a private key\n"); + return -1; + } + + /* Calculate digest of message */ + SHA1((unsigned char *)msg, strlen(msg), digest); + + /* Verify signature */ + res = RSA_sign(NID_sha1, digest, sizeof(digest), dsig, &siglen, key->rsa); + + if (!res) { + ast_log(LOG_WARNING, "RSA Signature (key %s) failed\n", key->name); + return -1; + } + + if (siglen != sizeof(dsig)) { + ast_log(LOG_WARNING, "Unexpected signature length %d, expecting %d\n", siglen, sizeof(dsig)); + return -1; + } + + /* Success -- encode (256 bytes max as documented) */ + base64encode(sig, dsig, siglen, 256); + return 0; + +} + +int ast_check_signature(struct ast_key *key, char *msg, char *sig) +{ + unsigned char digest[20]; + unsigned char dsig[128]; + int res; + + if (key->ktype != AST_KEY_PUBLIC) { + /* Okay, so of course you really *can* but for our purposes + we're going to say you can't */ + ast_log(LOG_WARNING, "Cannot check message signature with a private key\n"); + return -1; + } + + /* Decode signature */ + res = base64decode(dsig, sig, sizeof(dsig)); + if (res != sizeof(dsig)) { + ast_log(LOG_WARNING, "Signature improper length (expect %d, got %d)\n", sizeof(dsig), res); + return -1; + } + + /* Calculate digest of message */ + SHA1((unsigned char *)msg, strlen(msg), digest); + + /* Verify signature */ + res = RSA_verify(NID_sha1, digest, sizeof(digest), dsig, sizeof(dsig), key->rsa); + + if (!res) { + ast_log(LOG_DEBUG, "Key failed verification\n"); + return -1; + } + /* Pass */ + return 0; +} + +static void crypto_load(int ifd, int ofd) +{ + struct ast_key *key, *nkey, *last; + DIR *dir; + struct dirent *ent; + int note = 0; + /* Mark all keys for deletion */ + ast_pthread_mutex_lock(&keylock); + key = keys; + while(key) { + key->delme = 1; + key = key->next; + } + ast_pthread_mutex_unlock(&keylock); + /* Load new keys */ + dir = opendir(AST_KEY_DIR); + if (dir) { + while((ent = readdir(dir))) { + try_load_key(AST_KEY_DIR, ent->d_name, ifd, ofd, ¬e); + } + closedir(dir); + } else + ast_log(LOG_WARNING, "Unable to open key directory '%s'\n", AST_KEY_DIR); + if (note) { + ast_log(LOG_NOTICE, "Please run the command 'init keys' to enter the passcodes for the keys\n"); + } + ast_pthread_mutex_lock(&keylock); + key = keys; + last = NULL; + while(key) { + nkey = key->next; + if (key->delme) { + ast_log(LOG_DEBUG, "Deleting key %s type %d\n", key->name, key->ktype); + /* Do the delete */ + if (last) + last->next = nkey; + else + keys = nkey; + if (key->rsa) + RSA_free(key->rsa); + free(key); + } else + last = key; + key = nkey; + } + ast_pthread_mutex_unlock(&keylock); +} + +static void md52sum(char *sum, unsigned char *md5) +{ + int x; + for (x=0;x<16;x++) + sum += sprintf(sum, "%02x", *(md5++)); +} + +static int show_keys(int fd, int argc, char *argv[]) +{ + struct ast_key *key; + char sum[16 * 2 + 1]; + + ast_pthread_mutex_lock(&keylock); + key = keys; + ast_cli(fd, "%-18s %-8s %-16s %-33s\n", "Key Name", "Type", "Status", "Sum"); + while(key) { + md52sum(sum, key->digest); + ast_cli(fd, "%-18s %-8s %-16s %-33s\n", key->name, + (key->ktype & 0xf) == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", + key->ktype & KEY_NEEDS_PASSCODE ? "[Needs Passcode]" : "[Loaded]", sum); + + key = key->next; + } + ast_pthread_mutex_unlock(&keylock); + return RESULT_SUCCESS; +} + +static int init_keys(int fd, int argc, char *argv[]) +{ + struct ast_key *key; + int ign; + char *kn; + char tmp[256]; + + key = keys; + while(key) { + /* Reload keys that need pass codes now */ + if (key->ktype & KEY_NEEDS_PASSCODE) { + kn = key->fn + strlen(AST_KEY_DIR) + 1; + strncpy(tmp, kn, sizeof(tmp)); + try_load_key(AST_KEY_DIR, tmp, fd, fd, &ign); + } + key = key->next; + } + return RESULT_SUCCESS; +} + +static char show_key_usage[] = +"Usage: show keys\n" +" Displays information about RSA keys known by Asterisk\n"; + +static char init_keys_usage[] = +"Usage: init keys\n" +" Initializes private keys (by reading in pass code from the user)\n"; + +static struct ast_cli_entry cli_show_keys = +{ { "show", "keys", NULL }, show_keys, "Displays RSA key information", show_key_usage }; + +static struct ast_cli_entry cli_init_keys = +{ { "init", "keys", NULL }, init_keys, "Initialize RSA key passcodes", init_keys_usage }; + +static void base64_init(void) +{ + int x; + memset(b2a, -1, sizeof(b2a)); + /* Initialize base-64 Conversion table */ + for (x=0;x<26;x++) { + /* A-Z */ + base64[x] = 'A' + x; + b2a['A' + x] = x; + /* a-z */ + base64[x + 26] = 'a' + x; + b2a['a' + x] = x + 26; + /* 0-9 */ + if (x < 10) { + base64[x + 52] = '0' + x; + b2a['0' + x] = x + 52; + } + } + base64[62] = '+'; + base64[63] = '/'; + b2a[(int)'+'] = 62; + b2a[(int)'/'] = 63; +#if 0 + for (x=0;x<64;x++) { + if (b2a[(int)base64[x]] != x) { + fprintf(stderr, "!!! %d failed\n", x); + } else + fprintf(stderr, "--- %d passed\n", x); + } +#endif +} + +static int crypto_init(void) +{ + base64_init(); + SSL_library_init(); + ERR_load_crypto_strings(); + ast_cli_register(&cli_show_keys); + ast_cli_register(&cli_init_keys); + return 0; +} + +int reload(void) +{ + crypto_load(-1, -1); + return 0; +} + +int load_module(void) +{ + crypto_init(); + if (option_initcrypto) + crypto_load(STDIN_FILENO, STDOUT_FILENO); + else + crypto_load(-1, -1); + return 0; +} + +int unload_module(void) +{ + /* Can't unload this once we're loaded */ + return -1; +} + +char *description(void) +{ + return "Cryptographic Digital Signatures"; +} + +int usecount(void) +{ + /* We should never be unloaded */ + return 1; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +}