2001-12-25 21:12:07 +00:00
|
|
|
/*
|
2005-09-14 20:46:50 +00:00
|
|
|
* Asterisk -- An open source telephony toolkit.
|
2001-12-25 21:12:07 +00:00
|
|
|
*
|
2006-08-21 02:11:39 +00:00
|
|
|
* Copyright (C) 1999 - 2006, Digium, Inc.
|
2001-12-25 21:12:07 +00:00
|
|
|
*
|
2004-09-18 23:39:39 +00:00
|
|
|
* Mark Spencer <markster@digium.com>
|
2001-12-25 21:12:07 +00:00
|
|
|
*
|
2005-09-14 20:46:50 +00:00
|
|
|
* 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.
|
|
|
|
*
|
2001-12-25 21:12:07 +00:00
|
|
|
* This program is free software, distributed under the terms of
|
2005-09-14 20:46:50 +00:00
|
|
|
* the GNU General Public License Version 2. See the LICENSE file
|
|
|
|
* at the top of the source tree.
|
|
|
|
*/
|
|
|
|
|
2005-10-24 20:12:06 +00:00
|
|
|
/*! \file
|
2005-09-14 20:46:50 +00:00
|
|
|
*
|
2005-10-24 20:12:06 +00:00
|
|
|
* \brief Provide Cryptographic Signature capability
|
2005-12-30 21:18:06 +00:00
|
|
|
*
|
2010-07-14 20:48:59 +00:00
|
|
|
* \author Mark Spencer <markster@digium.com>
|
2007-02-24 19:27:50 +00:00
|
|
|
*
|
2012-09-21 17:14:59 +00:00
|
|
|
* Uses the OpenSSL library, available at
|
2007-02-24 19:27:50 +00:00
|
|
|
* http://www.openssl.org/
|
2001-12-25 21:12:07 +00:00
|
|
|
*/
|
|
|
|
|
2006-04-24 17:11:45 +00:00
|
|
|
/*** MODULEINFO
|
2008-11-04 15:07:54 +00:00
|
|
|
<depend>openssl</depend>
|
2011-07-14 20:28:54 +00:00
|
|
|
<support_level>core</support_level>
|
2006-04-24 17:11:45 +00:00
|
|
|
***/
|
|
|
|
|
2006-06-07 18:54:56 +00:00
|
|
|
#include "asterisk.h"
|
|
|
|
|
git migration: Refactor the ASTERISK_FILE_VERSION macro
Git does not support the ability to replace a token with a version
string during check-in. While it does have support for replacing a
token on clone, this is somewhat sub-optimal: the token is replaced
with the object hash, which is not particularly easy for human
consumption. What's more, in practice, the source file version was often
not terribly useful. Generally, when triaging bugs, the overall version
of Asterisk is far more useful than an individual SVN version of a file. As a
result, this patch removes Asterisk's support for showing source file
versions.
Specifically, it does the following:
* Rename ASTERISK_FILE_VERSION macro to ASTERISK_REGISTER_FILE, and
remove passing the version in with the macro. Other facilities
than 'core show file version' make use of the file names, such as
setting a debug level only on a specific file. As such, the act of
registering source files with the Asterisk core still has use. The
macro rename now reflects the new macro purpose.
* main/asterisk:
- Refactor the file_version structure to reflect that it no longer
tracks a version field.
- Remove the "core show file version" CLI command. Without the file
version, it is no longer useful.
- Remove the ast_file_version_find function. The file version is no
longer tracked.
- Rename ast_register_file_version/ast_unregister_file_version to
ast_register_file/ast_unregister_file, respectively.
* main/manager: Remove value from the Version key of the ModuleCheck
Action. The actual key itself has not been removed, as doing so would
absolutely constitute a backwards incompatible change. However, since
the file version is no longer tracked, there is no need to attempt to
include it in the Version key.
* UPGRADE: Add notes for:
- Modification to the ModuleCheck AMI Action
- Removal of the "core show file version" CLI command
Change-Id: I6cf0ff280e1668bf4957dc21f32a5ff43444a40e
2015-04-12 02:38:22 +00:00
|
|
|
ASTERISK_REGISTER_FILE()
|
2006-06-07 18:54:56 +00:00
|
|
|
|
2007-11-20 23:16:15 +00:00
|
|
|
#include "asterisk/paths.h" /* use ast_config_AST_KEY_DIR */
|
2005-06-06 22:12:19 +00:00
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <openssl/err.h>
|
2010-07-21 19:11:32 +00:00
|
|
|
#include <openssl/aes.h>
|
2005-06-06 22:12:19 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
|
2005-04-21 06:02:45 +00:00
|
|
|
#include "asterisk/module.h"
|
|
|
|
#include "asterisk/md5.h"
|
|
|
|
#include "asterisk/cli.h"
|
|
|
|
#include "asterisk/io.h"
|
|
|
|
#include "asterisk/lock.h"
|
|
|
|
#include "asterisk/utils.h"
|
2001-12-25 21:12:07 +00:00
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
#define AST_API_MODULE
|
|
|
|
#include "asterisk/crypto.h"
|
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
/*
|
|
|
|
* 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
|
2010-07-14 20:48:59 +00:00
|
|
|
* on the recently discovered collisions in MD5's compression
|
2001-12-25 21:12:07 +00:00
|
|
|
* 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
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define KEY_NEEDS_PASSCODE (1 << 16)
|
|
|
|
|
|
|
|
struct ast_key {
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! Name of entity */
|
2001-12-25 21:12:07 +00:00
|
|
|
char name[80];
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! File name */
|
2001-12-25 21:12:07 +00:00
|
|
|
char fn[256];
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! Key type (AST_KEY_PUB or AST_KEY_PRIV, along with flags from above) */
|
2001-12-25 21:12:07 +00:00
|
|
|
int ktype;
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! RSA structure (if successfully loaded) */
|
2001-12-25 21:12:07 +00:00
|
|
|
RSA *rsa;
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! Whether we should be deleted */
|
2001-12-25 21:12:07 +00:00
|
|
|
int delme;
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! FD for input (or -1 if no input allowed, or -2 if we needed input) */
|
2001-12-25 21:12:07 +00:00
|
|
|
int infd;
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! FD for output */
|
2001-12-25 21:12:07 +00:00
|
|
|
int outfd;
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! Last MD5 Digest */
|
2001-12-25 21:12:07 +00:00
|
|
|
unsigned char digest[16];
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_ENTRY(ast_key) list;
|
2001-12-25 21:12:07 +00:00
|
|
|
};
|
|
|
|
|
2007-07-23 21:42:43 +00:00
|
|
|
static AST_RWLIST_HEAD_STATIC(keys, ast_key);
|
2007-07-23 14:32:04 +00:00
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief setting of priv key
|
|
|
|
* \param buf
|
|
|
|
* \param size
|
|
|
|
* \param rwflag
|
|
|
|
* \param userdata
|
|
|
|
* \return length of string,-1 on failure
|
|
|
|
*/
|
2001-12-25 21:12:07 +00:00
|
|
|
static int pw_cb(char *buf, int size, int rwflag, void *userdata)
|
|
|
|
{
|
|
|
|
struct ast_key *key = (struct ast_key *)userdata;
|
|
|
|
char prompt[256];
|
2011-05-05 22:44:52 +00:00
|
|
|
int tmp;
|
|
|
|
int res;
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
if (key->infd < 0) {
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Note that we were at least called */
|
|
|
|
key->infd = -2;
|
2007-07-23 21:42:43 +00:00
|
|
|
return -1;
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
2010-07-14 20:48:59 +00:00
|
|
|
|
2007-07-23 21:42:43 +00:00
|
|
|
snprintf(prompt, sizeof(prompt), ">>>> passcode for %s key '%s': ",
|
|
|
|
key->ktype == AST_KEY_PRIVATE ? "PRIVATE" : "PUBLIC", key->name);
|
2008-11-02 18:52:13 +00:00
|
|
|
if (write(key->outfd, prompt, strlen(prompt)) < 0) {
|
|
|
|
ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
|
|
|
|
key->infd = -2;
|
|
|
|
return -1;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
tmp = ast_hide_password(key->infd);
|
|
|
|
memset(buf, 0, size);
|
|
|
|
res = read(key->infd, buf, size);
|
2011-05-05 22:44:52 +00:00
|
|
|
if (res == -1) {
|
|
|
|
ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
ast_restore_tty(key->infd, tmp);
|
2010-07-14 20:48:59 +00:00
|
|
|
if (buf[strlen(buf) -1] == '\n') {
|
2007-07-23 21:42:43 +00:00
|
|
|
buf[strlen(buf) - 1] = '\0';
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
return strlen(buf);
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief return the ast_key structure for name
|
|
|
|
* \see ast_key_get
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
struct ast_key * AST_OPTIONAL_API_NAME(ast_key_get)(const char *kname, int ktype)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
struct ast_key *key;
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
AST_RWLIST_RDLOCK(&keys);
|
|
|
|
AST_RWLIST_TRAVERSE(&keys, key, list) {
|
2001-12-25 21:12:07 +00:00
|
|
|
if (!strcmp(kname, key->name) &&
|
2010-07-14 20:48:59 +00:00
|
|
|
(ktype == key->ktype)) {
|
2001-12-25 21:12:07 +00:00
|
|
|
break;
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_UNLOCK(&keys);
|
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
return key;
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief load RSA key from file
|
|
|
|
* \param dir directory string
|
|
|
|
* \param fname name of file
|
|
|
|
* \param ifd incoming file descriptor
|
|
|
|
* \param ofd outgoing file descriptor
|
|
|
|
* \param not2
|
|
|
|
* \retval key on success.
|
|
|
|
* \retval NULL on failure.
|
|
|
|
*/
|
2007-12-18 10:24:58 +00:00
|
|
|
static struct ast_key *try_load_key(const char *dir, const char *fname, int ifd, int ofd, int *not2)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
2007-07-23 21:42:43 +00:00
|
|
|
int ktype = 0, found = 0;
|
|
|
|
char *c = NULL, ffname[256];
|
2005-08-05 16:29:30 +00:00
|
|
|
unsigned char digest[16];
|
2001-12-25 21:12:07 +00:00
|
|
|
FILE *f;
|
|
|
|
struct MD5Context md5;
|
|
|
|
struct ast_key *key;
|
|
|
|
static int notice = 0;
|
|
|
|
|
|
|
|
/* Make sure its name is a public or private key */
|
2010-07-14 20:48:59 +00:00
|
|
|
if ((c = strstr(fname, ".pub")) && !strcmp(c, ".pub")) {
|
2001-12-25 21:12:07 +00:00
|
|
|
ktype = AST_KEY_PUBLIC;
|
2010-07-14 20:48:59 +00:00
|
|
|
} else if ((c = strstr(fname, ".key")) && !strcmp(c, ".key")) {
|
2001-12-25 21:12:07 +00:00
|
|
|
ktype = AST_KEY_PRIVATE;
|
2010-07-14 20:48:59 +00:00
|
|
|
} else {
|
2001-12-25 21:12:07 +00:00
|
|
|
return NULL;
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
|
|
|
|
/* Get actual filename */
|
|
|
|
snprintf(ffname, sizeof(ffname), "%s/%s", dir, fname);
|
|
|
|
|
|
|
|
/* Open file */
|
2007-07-23 21:42:43 +00:00
|
|
|
if (!(f = fopen(ffname, "r"))) {
|
2001-12-25 21:12:07 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to open key file %s: %s\n", ffname, strerror(errno));
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
MD5Init(&md5);
|
2010-07-14 20:48:59 +00:00
|
|
|
while (!feof(f)) {
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Calculate a "whatever" quality md5sum of the key */
|
2007-07-23 21:42:43 +00:00
|
|
|
char buf[256] = "";
|
2008-11-02 18:52:13 +00:00
|
|
|
if (!fgets(buf, sizeof(buf), f)) {
|
|
|
|
continue;
|
|
|
|
}
|
2010-07-14 20:48:59 +00:00
|
|
|
if (!feof(f)) {
|
2005-08-05 16:29:30 +00:00
|
|
|
MD5Update(&md5, (unsigned char *) buf, strlen(buf));
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
MD5Final(digest, &md5);
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
/* Look for an existing key */
|
|
|
|
AST_RWLIST_TRAVERSE(&keys, key, list) {
|
2010-07-14 20:48:59 +00:00
|
|
|
if (!strcasecmp(key->fn, ffname)) {
|
2007-07-23 21:42:43 +00:00
|
|
|
break;
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
}
|
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
if (key) {
|
2010-07-14 20:48:59 +00:00
|
|
|
/* If the MD5 sum is the same, and it isn't awaiting a passcode
|
2001-12-25 21:12:07 +00:00
|
|
|
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) {
|
2006-01-21 22:09:06 +00:00
|
|
|
if (!(key = ast_calloc(1, sizeof(*key)))) {
|
2001-12-25 21:12:07 +00:00
|
|
|
fclose(f);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* First the filename */
|
2005-07-10 23:21:39 +00:00
|
|
|
ast_copy_string(key->fn, ffname, sizeof(key->fn));
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Then the name */
|
2005-07-10 23:21:39 +00:00
|
|
|
ast_copy_string(key->name, fname, sizeof(key->name));
|
2001-12-25 21:12:07 +00:00
|
|
|
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 */
|
2010-07-14 20:48:59 +00:00
|
|
|
if (ktype == AST_KEY_PUBLIC) {
|
2001-12-25 21:12:07 +00:00
|
|
|
key->rsa = PEM_read_RSA_PUBKEY(f, NULL, pw_cb, key);
|
2010-07-14 20:48:59 +00:00
|
|
|
} else {
|
2001-12-25 21:12:07 +00:00
|
|
|
key->rsa = PEM_read_RSAPrivateKey(f, NULL, pw_cb, key);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
fclose(f);
|
|
|
|
if (key->rsa) {
|
2004-09-18 03:59:51 +00:00
|
|
|
if (RSA_size(key->rsa) == 128) {
|
|
|
|
/* Key loaded okay */
|
|
|
|
key->ktype &= ~KEY_NEEDS_PASSCODE;
|
2007-07-26 15:49:18 +00:00
|
|
|
ast_verb(3, "Loaded %s key '%s'\n", key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name);
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Key '%s' loaded OK\n", key->name);
|
2004-09-18 03:59:51 +00:00
|
|
|
key->delme = 0;
|
2010-07-14 20:48:59 +00:00
|
|
|
} else {
|
2004-09-18 03:59:51 +00:00
|
|
|
ast_log(LOG_NOTICE, "Key '%s' is not expected size.\n", key->name);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
} else if (key->infd != -2) {
|
|
|
|
ast_log(LOG_WARNING, "Key load %s '%s' failed\n",key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name);
|
2010-07-14 20:48:59 +00:00
|
|
|
if (ofd > -1) {
|
2001-12-25 21:12:07 +00:00
|
|
|
ERR_print_errors_fp(stderr);
|
2010-07-14 20:48:59 +00:00
|
|
|
} else {
|
2001-12-25 21:12:07 +00:00
|
|
|
ERR_print_errors_fp(stderr);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
} else {
|
|
|
|
ast_log(LOG_NOTICE, "Key '%s' needs passcode.\n", key->name);
|
|
|
|
key->ktype |= KEY_NEEDS_PASSCODE;
|
|
|
|
if (!notice) {
|
2010-07-14 20:48:59 +00:00
|
|
|
if (!ast_opt_init_keys) {
|
2001-12-25 21:12:07 +00:00
|
|
|
ast_log(LOG_NOTICE, "Add the '-i' flag to the asterisk command line if you want to automatically initialize passcodes at launch.\n");
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
notice++;
|
|
|
|
}
|
|
|
|
/* Keep it anyway */
|
|
|
|
key->delme = 0;
|
2010-07-28 13:02:11 +00:00
|
|
|
/* Print final notice about "keys init" when done */
|
2001-12-25 21:12:07 +00:00
|
|
|
*not2 = 1;
|
|
|
|
}
|
|
|
|
|
2007-07-23 21:42:43 +00:00
|
|
|
/* If this is a new key add it to the list */
|
2010-07-14 20:48:59 +00:00
|
|
|
if (!found) {
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_INSERT_TAIL(&keys, key, list);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
|
2007-07-23 21:42:43 +00:00
|
|
|
return key;
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief signs outgoing message with public key
|
|
|
|
* \see ast_sign_bin
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_sign_bin)(struct ast_key *key, const char *msg, int msglen, unsigned char *dsig)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
unsigned char digest[20];
|
2005-08-05 16:29:30 +00:00
|
|
|
unsigned int siglen = 128;
|
2001-12-25 21:12:07 +00:00
|
|
|
int res;
|
|
|
|
|
|
|
|
if (key->ktype != AST_KEY_PRIVATE) {
|
2004-09-18 03:59:51 +00:00
|
|
|
ast_log(LOG_WARNING, "Cannot sign with a public key\n");
|
2001-12-25 21:12:07 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate digest of message */
|
2004-08-14 21:28:30 +00:00
|
|
|
SHA1((unsigned char *)msg, msglen, digest);
|
2001-12-25 21:12:07 +00:00
|
|
|
|
|
|
|
/* Verify signature */
|
2007-07-23 21:42:43 +00:00
|
|
|
if (!(res = RSA_sign(NID_sha1, digest, sizeof(digest), dsig, &siglen, key->rsa))) {
|
2001-12-25 21:12:07 +00:00
|
|
|
ast_log(LOG_WARNING, "RSA Signature (key %s) failed\n", key->name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-08-15 01:01:34 +00:00
|
|
|
if (siglen != 128) {
|
|
|
|
ast_log(LOG_WARNING, "Unexpected signature length %d, expecting %d\n", (int)siglen, (int)128);
|
2001-12-25 21:12:07 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief decrypt a message
|
|
|
|
* \see ast_decrypt_bin
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_decrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
|
2004-09-18 03:59:51 +00:00
|
|
|
{
|
2007-07-23 21:42:43 +00:00
|
|
|
int res, pos = 0;
|
|
|
|
|
2004-09-18 03:59:51 +00:00
|
|
|
if (key->ktype != AST_KEY_PRIVATE) {
|
|
|
|
ast_log(LOG_WARNING, "Cannot decrypt with a public key\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (srclen % 128) {
|
|
|
|
ast_log(LOG_NOTICE, "Tried to decrypt something not a multiple of 128 bytes\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
while (srclen) {
|
2004-09-18 03:59:51 +00:00
|
|
|
/* Process chunks 128 bytes at a time */
|
2010-07-14 20:48:59 +00:00
|
|
|
if ((res = RSA_private_decrypt(128, src, dst, key->rsa, RSA_PKCS1_OAEP_PADDING)) < 0) {
|
2004-09-18 03:59:51 +00:00
|
|
|
return -1;
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2004-09-18 03:59:51 +00:00
|
|
|
pos += res;
|
|
|
|
src += 128;
|
|
|
|
srclen -= 128;
|
|
|
|
dst += res;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2004-09-18 03:59:51 +00:00
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief encrypt a message
|
|
|
|
* \see ast_encrypt_bin
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_encrypt_bin)(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
|
2004-09-18 03:59:51 +00:00
|
|
|
{
|
2007-07-23 21:42:43 +00:00
|
|
|
int res, bytes, pos = 0;
|
|
|
|
|
2004-09-18 03:59:51 +00:00
|
|
|
if (key->ktype != AST_KEY_PUBLIC) {
|
|
|
|
ast_log(LOG_WARNING, "Cannot encrypt with a private key\n");
|
|
|
|
return -1;
|
|
|
|
}
|
2010-07-14 20:48:59 +00:00
|
|
|
|
|
|
|
while (srclen) {
|
2004-09-18 03:59:51 +00:00
|
|
|
bytes = srclen;
|
2010-07-14 20:48:59 +00:00
|
|
|
if (bytes > 128 - 41) {
|
2004-09-18 03:59:51 +00:00
|
|
|
bytes = 128 - 41;
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2004-09-18 23:39:39 +00:00
|
|
|
/* Process chunks 128-41 bytes at a time */
|
2007-07-23 21:42:43 +00:00
|
|
|
if ((res = RSA_public_encrypt(bytes, src, dst, key->rsa, RSA_PKCS1_OAEP_PADDING)) != 128) {
|
2004-09-18 23:39:39 +00:00
|
|
|
ast_log(LOG_NOTICE, "How odd, encrypted size is %d\n", res);
|
2004-09-18 03:59:51 +00:00
|
|
|
return -1;
|
2004-09-18 23:39:39 +00:00
|
|
|
}
|
|
|
|
src += bytes;
|
|
|
|
srclen -= bytes;
|
2004-09-18 03:59:51 +00:00
|
|
|
pos += res;
|
|
|
|
dst += res;
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief wrapper for __ast_sign_bin then base64 encode it
|
|
|
|
* \see ast_sign
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_sign)(struct ast_key *key, char *msg, char *sig)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
unsigned char dsig[128];
|
2007-07-23 21:42:43 +00:00
|
|
|
int siglen = sizeof(dsig), res;
|
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
if (!(res = ast_sign_bin(key, msg, strlen(msg), dsig))) {
|
2004-08-14 20:19:38 +00:00
|
|
|
/* Success -- encode (256 bytes max as documented) */
|
|
|
|
ast_base64encode(sig, dsig, siglen, 256);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2004-08-14 20:19:38 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief check signature of a message
|
|
|
|
* \see ast_check_signature_bin
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_check_signature_bin)(struct ast_key *key, const char *msg, int msglen, const unsigned char *dsig)
|
2004-08-14 20:19:38 +00:00
|
|
|
{
|
|
|
|
unsigned char digest[20];
|
2001-12-25 21:12:07 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Calculate digest of message */
|
2004-08-14 21:28:30 +00:00
|
|
|
SHA1((unsigned char *)msg, msglen, digest);
|
2001-12-25 21:12:07 +00:00
|
|
|
|
|
|
|
/* Verify signature */
|
2007-07-23 21:42:43 +00:00
|
|
|
if (!(res = RSA_verify(NID_sha1, digest, sizeof(digest), (unsigned char *)dsig, 128, key->rsa))) {
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Key failed verification: %s\n", key->name);
|
2001-12-25 21:12:07 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Pass */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief base64 decode then sent to __ast_check_signature_bin
|
|
|
|
* \see ast_check_signature
|
|
|
|
*/
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_check_signature)(struct ast_key *key, const char *msg, const char *sig)
|
2004-08-14 20:19:38 +00:00
|
|
|
{
|
|
|
|
unsigned char dsig[128];
|
|
|
|
int res;
|
|
|
|
|
|
|
|
/* Decode signature */
|
2007-07-23 21:42:43 +00:00
|
|
|
if ((res = ast_base64decode(dsig, sig, sizeof(dsig))) != sizeof(dsig)) {
|
2004-08-14 20:19:38 +00:00
|
|
|
ast_log(LOG_WARNING, "Signature improper length (expect %d, got %d)\n", (int)sizeof(dsig), (int)res);
|
|
|
|
return -1;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2004-08-14 21:28:30 +00:00
|
|
|
res = ast_check_signature_bin(key, msg, strlen(msg), dsig);
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2004-08-14 20:19:38 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_crypto_loaded)(void)
|
|
|
|
{
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2010-07-21 19:11:32 +00:00
|
|
|
int AST_OPTIONAL_API_NAME(ast_aes_set_encrypt_key)(const unsigned char *key, ast_aes_encrypt_key *ctx)
|
|
|
|
{
|
|
|
|
return AES_set_encrypt_key(key, 128, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
int AST_OPTIONAL_API_NAME(ast_aes_set_decrypt_key)(const unsigned char *key, ast_aes_decrypt_key *ctx)
|
|
|
|
{
|
|
|
|
return AES_set_decrypt_key(key, 128, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AST_OPTIONAL_API_NAME(ast_aes_encrypt)(const unsigned char *in, unsigned char *out, const ast_aes_encrypt_key *ctx)
|
|
|
|
{
|
|
|
|
return AES_encrypt(in, out, ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void AST_OPTIONAL_API_NAME(ast_aes_decrypt)(const unsigned char *in, unsigned char *out, const ast_aes_decrypt_key *ctx)
|
|
|
|
{
|
|
|
|
return AES_decrypt(in, out, ctx);
|
|
|
|
}
|
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*!
|
|
|
|
* \brief refresh RSA keys from file
|
|
|
|
* \param ifd file descriptor
|
|
|
|
* \param ofd file descriptor
|
|
|
|
* \return void
|
|
|
|
*/
|
2001-12-25 21:12:07 +00:00
|
|
|
static void crypto_load(int ifd, int ofd)
|
|
|
|
{
|
2007-07-23 21:42:43 +00:00
|
|
|
struct ast_key *key;
|
2004-11-15 00:33:07 +00:00
|
|
|
DIR *dir = NULL;
|
2001-12-25 21:12:07 +00:00
|
|
|
struct dirent *ent;
|
|
|
|
int note = 0;
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
AST_RWLIST_WRLOCK(&keys);
|
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Mark all keys for deletion */
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_TRAVERSE(&keys, key, list) {
|
2001-12-25 21:12:07 +00:00
|
|
|
key->delme = 1;
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Load new keys */
|
2007-12-18 09:46:18 +00:00
|
|
|
if ((dir = opendir(ast_config_AST_KEY_DIR))) {
|
2010-07-14 20:48:59 +00:00
|
|
|
while ((ent = readdir(dir))) {
|
2007-12-18 09:46:18 +00:00
|
|
|
try_load_key(ast_config_AST_KEY_DIR, ent->d_name, ifd, ofd, ¬e);
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
closedir(dir);
|
2010-07-14 20:48:59 +00:00
|
|
|
} else {
|
2007-12-18 09:46:18 +00:00
|
|
|
ast_log(LOG_WARNING, "Unable to open key directory '%s'\n", ast_config_AST_KEY_DIR);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
if (note) {
|
2010-07-28 13:02:11 +00:00
|
|
|
ast_log(LOG_NOTICE, "Please run the command 'keys init' to enter the passcodes for the keys\n");
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
/* Delete any keys that are no longer present */
|
|
|
|
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&keys, key, list) {
|
2001-12-25 21:12:07 +00:00
|
|
|
if (key->delme) {
|
2007-06-14 19:39:12 +00:00
|
|
|
ast_debug(1, "Deleting key %s type %d\n", key->name, key->ktype);
|
2007-11-08 05:28:47 +00:00
|
|
|
AST_RWLIST_REMOVE_CURRENT(list);
|
2010-07-14 20:48:59 +00:00
|
|
|
if (key->rsa) {
|
2001-12-25 21:12:07 +00:00
|
|
|
RSA_free(key->rsa);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
ast_free(key);
|
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
2007-11-08 05:28:47 +00:00
|
|
|
AST_RWLIST_TRAVERSE_SAFE_END;
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
AST_RWLIST_UNLOCK(&keys);
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void md52sum(char *sum, unsigned char *md5)
|
|
|
|
{
|
|
|
|
int x;
|
2010-07-14 20:48:59 +00:00
|
|
|
for (x = 0; x < 16; x++) {
|
2014-12-17 10:23:32 +00:00
|
|
|
sum += sprintf(sum, "%02hhx", *(md5++));
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
/*!
|
|
|
|
* \brief show the list of RSA keys
|
2007-11-12 23:44:20 +00:00
|
|
|
* \param e CLI command
|
|
|
|
* \param cmd
|
|
|
|
* \param a list of CLI arguments
|
2007-11-28 20:27:40 +00:00
|
|
|
* \return CLI_SUCCESS
|
2007-07-23 14:32:04 +00:00
|
|
|
*/
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_keys_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
2007-10-11 19:03:06 +00:00
|
|
|
#define FORMAT "%-18s %-8s %-16s %-33s\n"
|
|
|
|
|
2001-12-25 21:12:07 +00:00
|
|
|
struct ast_key *key;
|
|
|
|
char sum[16 * 2 + 1];
|
2005-08-23 15:41:17 +00:00
|
|
|
int count_keys = 0;
|
2001-12-25 21:12:07 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "keys show";
|
|
|
|
e->usage =
|
|
|
|
"Usage: keys show\n"
|
|
|
|
" Displays information about RSA keys known by Asterisk\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ast_cli(a->fd, FORMAT, "Key Name", "Type", "Status", "Sum");
|
|
|
|
ast_cli(a->fd, FORMAT, "------------------", "--------", "----------------", "--------------------------------");
|
2007-07-23 21:42:43 +00:00
|
|
|
|
|
|
|
AST_RWLIST_RDLOCK(&keys);
|
|
|
|
AST_RWLIST_TRAVERSE(&keys, key, list) {
|
2001-12-25 21:12:07 +00:00
|
|
|
md52sum(sum, key->digest);
|
2010-07-14 20:48:59 +00:00
|
|
|
ast_cli(a->fd, FORMAT, key->name,
|
2001-12-25 21:12:07 +00:00
|
|
|
(key->ktype & 0xf) == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE",
|
|
|
|
key->ktype & KEY_NEEDS_PASSCODE ? "[Needs Passcode]" : "[Loaded]", sum);
|
2005-08-23 15:41:17 +00:00
|
|
|
count_keys++;
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_UNLOCK(&keys);
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
ast_cli(a->fd, "\n%d known RSA keys.\n", count_keys);
|
|
|
|
|
|
|
|
return CLI_SUCCESS;
|
2007-07-23 21:42:43 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
#undef FORMAT
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
/*!
|
|
|
|
* \brief initialize all RSA keys
|
2007-11-12 23:44:20 +00:00
|
|
|
* \param e CLI command
|
2010-07-14 20:48:59 +00:00
|
|
|
* \param cmd
|
2007-11-12 23:44:20 +00:00
|
|
|
* \param a list of CLI arguments
|
2007-11-28 20:27:40 +00:00
|
|
|
* \return CLI_SUCCESS
|
2007-07-23 14:32:04 +00:00
|
|
|
*/
|
2007-10-11 19:03:06 +00:00
|
|
|
static char *handle_cli_keys_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
struct ast_key *key;
|
|
|
|
int ign;
|
2007-07-23 21:42:43 +00:00
|
|
|
char *kn, tmp[256] = "";
|
2001-12-25 21:12:07 +00:00
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
switch (cmd) {
|
|
|
|
case CLI_INIT:
|
|
|
|
e->command = "keys init";
|
|
|
|
e->usage =
|
|
|
|
"Usage: keys init\n"
|
|
|
|
" Initializes private keys (by reading in pass code from\n"
|
|
|
|
" the user)\n";
|
|
|
|
return NULL;
|
|
|
|
case CLI_GENERATE:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-07-14 20:48:59 +00:00
|
|
|
if (a->argc != 2) {
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SHOWUSAGE;
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-10-11 19:03:06 +00:00
|
|
|
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_WRLOCK(&keys);
|
|
|
|
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&keys, key, list) {
|
2001-12-25 21:12:07 +00:00
|
|
|
/* Reload keys that need pass codes now */
|
|
|
|
if (key->ktype & KEY_NEEDS_PASSCODE) {
|
2003-02-04 15:48:42 +00:00
|
|
|
kn = key->fn + strlen(ast_config_AST_KEY_DIR) + 1;
|
2005-07-10 23:21:39 +00:00
|
|
|
ast_copy_string(tmp, kn, sizeof(tmp));
|
2007-12-18 09:46:18 +00:00
|
|
|
try_load_key(ast_config_AST_KEY_DIR, tmp, a->fd, a->fd, &ign);
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
}
|
2007-07-23 21:42:43 +00:00
|
|
|
AST_RWLIST_TRAVERSE_SAFE_END
|
|
|
|
AST_RWLIST_UNLOCK(&keys);
|
|
|
|
|
2007-10-11 19:03:06 +00:00
|
|
|
return CLI_SUCCESS;
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
2006-09-18 19:54:18 +00:00
|
|
|
static struct ast_cli_entry cli_crypto[] = {
|
2007-10-22 20:05:18 +00:00
|
|
|
AST_CLI_DEFINE(handle_cli_keys_show, "Displays RSA key information"),
|
|
|
|
AST_CLI_DEFINE(handle_cli_keys_init, "Initialize RSA key passcodes")
|
2006-09-18 19:54:18 +00:00
|
|
|
};
|
2001-12-25 21:12:07 +00:00
|
|
|
|
2007-07-23 14:32:04 +00:00
|
|
|
/*! \brief initialise the res_crypto module */
|
2001-12-25 21:12:07 +00:00
|
|
|
static int crypto_init(void)
|
|
|
|
{
|
2008-12-05 10:31:25 +00:00
|
|
|
ast_cli_register_multiple(cli_crypto, ARRAY_LEN(cli_crypto));
|
2001-12-25 21:12:07 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int reload(void)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
crypto_load(-1, -1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int load_module(void)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
crypto_init();
|
2010-07-14 20:48:59 +00:00
|
|
|
if (ast_opt_init_keys) {
|
2001-12-25 21:12:07 +00:00
|
|
|
crypto_load(STDIN_FILENO, STDOUT_FILENO);
|
2010-07-14 20:48:59 +00:00
|
|
|
} else {
|
2001-12-25 21:12:07 +00:00
|
|
|
crypto_load(-1, -1);
|
2010-07-14 20:48:59 +00:00
|
|
|
}
|
2007-10-26 21:37:02 +00:00
|
|
|
return AST_MODULE_LOAD_SUCCESS;
|
2001-12-25 21:12:07 +00:00
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
static int unload_module(void)
|
2001-12-25 21:12:07 +00:00
|
|
|
{
|
|
|
|
/* Can't unload this once we're loaded */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2006-08-21 02:11:39 +00:00
|
|
|
/* needs usecount semantics defined */
|
2010-07-20 19:35:02 +00:00
|
|
|
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Cryptographic Digital Signatures",
|
2015-05-06 00:49:04 +00:00
|
|
|
.support_level = AST_MODULE_SUPPORT_CORE,
|
|
|
|
.load = load_module,
|
|
|
|
.unload = unload_module,
|
|
|
|
.reload = reload,
|
|
|
|
.load_pri = AST_MODPRI_CHANNEL_DEPEND, /*!< Since we don't have a config file, we could move up to REALTIME_DEPEND, if necessary */
|
|
|
|
);
|