Wed Oct 28 11:51:07 2009

Asterisk developer's documentation


res_crypto.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Provide Cryptographic Signature capability
00022  *
00023  * \author Mark Spencer <markster@digium.com> 
00024  *
00025  * \extref Uses the OpenSSL library, available at
00026  * http://www.openssl.org/
00027  */
00028 
00029 /*** MODULEINFO
00030    <depend>ssl</depend>
00031  ***/
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 205147 $")
00036 
00037 #include "asterisk/paths.h"   /* use ast_config_AST_KEY_DIR */
00038 #include <openssl/ssl.h>
00039 #include <openssl/err.h>
00040 #include <dirent.h>
00041 
00042 #include "asterisk/module.h"
00043 #include "asterisk/crypto.h"
00044 #include "asterisk/md5.h"
00045 #include "asterisk/cli.h"
00046 #include "asterisk/io.h"
00047 #include "asterisk/lock.h"
00048 #include "asterisk/utils.h"
00049 
00050 /*
00051  * Asterisk uses RSA keys with SHA-1 message digests for its
00052  * digital signatures.  The choice of RSA is due to its higher
00053  * throughput on verification, and the choice of SHA-1 based
00054  * on the recently discovered collisions in MD5's compression 
00055  * algorithm and recommendations of avoiding MD5 in new schemes
00056  * from various industry experts.
00057  *
00058  * We use OpenSSL to provide our crypto routines, although we never
00059  * actually use full-up SSL
00060  *
00061  */
00062 
00063 #define KEY_NEEDS_PASSCODE (1 << 16)
00064 
00065 struct ast_key {
00066    /*! Name of entity */
00067    char name[80];
00068    /*! File name */
00069    char fn[256];
00070    /*! Key type (AST_KEY_PUB or AST_KEY_PRIV, along with flags from above) */
00071    int ktype;
00072    /*! RSA structure (if successfully loaded) */
00073    RSA *rsa;
00074    /*! Whether we should be deleted */
00075    int delme;
00076    /*! FD for input (or -1 if no input allowed, or -2 if we needed input) */
00077    int infd;
00078    /*! FD for output */
00079    int outfd;
00080    /*! Last MD5 Digest */
00081    unsigned char digest[16];
00082    AST_RWLIST_ENTRY(ast_key) list;
00083 };
00084 
00085 static AST_RWLIST_HEAD_STATIC(keys, ast_key);
00086 
00087 /*!
00088  * \brief setting of priv key
00089  * \param buf
00090  * \param size
00091  * \param rwflag
00092  * \param userdata
00093  * \return length of string,-1 on failure
00094 */
00095 static int pw_cb(char *buf, int size, int rwflag, void *userdata)
00096 {
00097    struct ast_key *key = (struct ast_key *)userdata;
00098    char prompt[256];
00099    int res, tmp;
00100 
00101    if (key->infd < 0) {
00102       /* Note that we were at least called */
00103       key->infd = -2;
00104       return -1;
00105    }
00106    
00107    snprintf(prompt, sizeof(prompt), ">>>> passcode for %s key '%s': ",
00108        key->ktype == AST_KEY_PRIVATE ? "PRIVATE" : "PUBLIC", key->name);
00109    if (write(key->outfd, prompt, strlen(prompt)) < 0) {
00110       ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00111       key->infd = -2;
00112       return -1;
00113    }
00114    memset(buf, 0, sizeof(buf));
00115    tmp = ast_hide_password(key->infd);
00116    memset(buf, 0, size);
00117    res = read(key->infd, buf, size);
00118    ast_restore_tty(key->infd, tmp);
00119    if (buf[strlen(buf) -1] == '\n')
00120       buf[strlen(buf) - 1] = '\0';
00121    return strlen(buf);
00122 }
00123 
00124 /*!
00125  * \brief return the ast_key structure for name
00126  * \see ast_key_get
00127 */
00128 static struct ast_key *__ast_key_get(const char *kname, int ktype)
00129 {
00130    struct ast_key *key;
00131 
00132    AST_RWLIST_RDLOCK(&keys);
00133    AST_RWLIST_TRAVERSE(&keys, key, list) {
00134       if (!strcmp(kname, key->name) &&
00135           (ktype == key->ktype))
00136          break;
00137    }
00138    AST_RWLIST_UNLOCK(&keys);
00139 
00140    return key;
00141 }
00142 
00143 /*!
00144  * \brief load RSA key from file
00145  * \param dir directory string
00146  * \param fname name of file
00147  * \param ifd incoming file descriptor
00148  * \param ofd outgoing file descriptor
00149  * \param not2
00150  * \retval key on success.
00151  * \retval NULL on failure.
00152 */
00153 static struct ast_key *try_load_key(const char *dir, const char *fname, int ifd, int ofd, int *not2)
00154 {
00155    int ktype = 0, found = 0;
00156    char *c = NULL, ffname[256];
00157    unsigned char digest[16];
00158    FILE *f;
00159    struct MD5Context md5;
00160    struct ast_key *key;
00161    static int notice = 0;
00162 
00163    /* Make sure its name is a public or private key */
00164    if ((c = strstr(fname, ".pub")) && !strcmp(c, ".pub"))
00165       ktype = AST_KEY_PUBLIC;
00166    else if ((c = strstr(fname, ".key")) && !strcmp(c, ".key"))
00167       ktype = AST_KEY_PRIVATE;
00168    else
00169       return NULL;
00170 
00171    /* Get actual filename */
00172    snprintf(ffname, sizeof(ffname), "%s/%s", dir, fname);
00173 
00174    /* Open file */
00175    if (!(f = fopen(ffname, "r"))) {
00176       ast_log(LOG_WARNING, "Unable to open key file %s: %s\n", ffname, strerror(errno));
00177       return NULL;
00178    }
00179 
00180    MD5Init(&md5);
00181    while(!feof(f)) {
00182       /* Calculate a "whatever" quality md5sum of the key */
00183       char buf[256] = "";
00184       if (!fgets(buf, sizeof(buf), f)) {
00185          continue;
00186       }
00187       if (!feof(f))
00188          MD5Update(&md5, (unsigned char *) buf, strlen(buf));
00189    }
00190    MD5Final(digest, &md5);
00191 
00192    /* Look for an existing key */
00193    AST_RWLIST_TRAVERSE(&keys, key, list) {
00194       if (!strcasecmp(key->fn, ffname))
00195          break;
00196    }
00197 
00198    if (key) {
00199       /* If the MD5 sum is the same, and it isn't awaiting a passcode 
00200          then this is far enough */
00201       if (!memcmp(digest, key->digest, 16) &&
00202           !(key->ktype & KEY_NEEDS_PASSCODE)) {
00203          fclose(f);
00204          key->delme = 0;
00205          return NULL;
00206       } else {
00207          /* Preserve keytype */
00208          ktype = key->ktype;
00209          /* Recycle the same structure */
00210          found++;
00211       }
00212    }
00213 
00214    /* Make fname just be the normal name now */
00215    *c = '\0';
00216    if (!key) {
00217       if (!(key = ast_calloc(1, sizeof(*key)))) {
00218          fclose(f);
00219          return NULL;
00220       }
00221    }
00222    /* First the filename */
00223    ast_copy_string(key->fn, ffname, sizeof(key->fn));
00224    /* Then the name */
00225    ast_copy_string(key->name, fname, sizeof(key->name));
00226    key->ktype = ktype;
00227    /* Yes, assume we're going to be deleted */
00228    key->delme = 1;
00229    /* Keep the key type */
00230    memcpy(key->digest, digest, 16);
00231    /* Can I/O takes the FD we're given */
00232    key->infd = ifd;
00233    key->outfd = ofd;
00234    /* Reset the file back to the beginning */
00235    rewind(f);
00236    /* Now load the key with the right method */
00237    if (ktype == AST_KEY_PUBLIC)
00238       key->rsa = PEM_read_RSA_PUBKEY(f, NULL, pw_cb, key);
00239    else
00240       key->rsa = PEM_read_RSAPrivateKey(f, NULL, pw_cb, key);
00241    fclose(f);
00242    if (key->rsa) {
00243       if (RSA_size(key->rsa) == 128) {
00244          /* Key loaded okay */
00245          key->ktype &= ~KEY_NEEDS_PASSCODE;
00246          ast_verb(3, "Loaded %s key '%s'\n", key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name);
00247          ast_debug(1, "Key '%s' loaded OK\n", key->name);
00248          key->delme = 0;
00249       } else
00250          ast_log(LOG_NOTICE, "Key '%s' is not expected size.\n", key->name);
00251    } else if (key->infd != -2) {
00252       ast_log(LOG_WARNING, "Key load %s '%s' failed\n",key->ktype == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE", key->name);
00253       if (ofd > -1)
00254          ERR_print_errors_fp(stderr);
00255       else
00256          ERR_print_errors_fp(stderr);
00257    } else {
00258       ast_log(LOG_NOTICE, "Key '%s' needs passcode.\n", key->name);
00259       key->ktype |= KEY_NEEDS_PASSCODE;
00260       if (!notice) {
00261          if (!ast_opt_init_keys) 
00262             ast_log(LOG_NOTICE, "Add the '-i' flag to the asterisk command line if you want to automatically initialize passcodes at launch.\n");
00263          notice++;
00264       }
00265       /* Keep it anyway */
00266       key->delme = 0;
00267       /* Print final notice about "init keys" when done */
00268       *not2 = 1;
00269    }
00270 
00271    /* If this is a new key add it to the list */
00272    if (!found)
00273       AST_RWLIST_INSERT_TAIL(&keys, key, list);
00274 
00275    return key;
00276 }
00277 
00278 /*!
00279  * \brief signs outgoing message with public key
00280  * \see ast_sign_bin
00281 */
00282 static int __ast_sign_bin(struct ast_key *key, const char *msg, int msglen, unsigned char *dsig)
00283 {
00284    unsigned char digest[20];
00285    unsigned int siglen = 128;
00286    int res;
00287 
00288    if (key->ktype != AST_KEY_PRIVATE) {
00289       ast_log(LOG_WARNING, "Cannot sign with a public key\n");
00290       return -1;
00291    }
00292 
00293    /* Calculate digest of message */
00294    SHA1((unsigned char *)msg, msglen, digest);
00295 
00296    /* Verify signature */
00297    if (!(res = RSA_sign(NID_sha1, digest, sizeof(digest), dsig, &siglen, key->rsa))) {
00298       ast_log(LOG_WARNING, "RSA Signature (key %s) failed\n", key->name);
00299       return -1;
00300    }
00301 
00302    if (siglen != 128) {
00303       ast_log(LOG_WARNING, "Unexpected signature length %d, expecting %d\n", (int)siglen, (int)128);
00304       return -1;
00305    }
00306 
00307    return 0;
00308    
00309 }
00310 
00311 /*!
00312  * \brief decrypt a message
00313  * \see ast_decrypt_bin
00314 */
00315 static int __ast_decrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
00316 {
00317    int res, pos = 0;
00318 
00319    if (key->ktype != AST_KEY_PRIVATE) {
00320       ast_log(LOG_WARNING, "Cannot decrypt with a public key\n");
00321       return -1;
00322    }
00323 
00324    if (srclen % 128) {
00325       ast_log(LOG_NOTICE, "Tried to decrypt something not a multiple of 128 bytes\n");
00326       return -1;
00327    }
00328 
00329    while(srclen) {
00330       /* Process chunks 128 bytes at a time */
00331       if ((res = RSA_private_decrypt(128, src, dst, key->rsa, RSA_PKCS1_OAEP_PADDING)) < 0)
00332          return -1;
00333       pos += res;
00334       src += 128;
00335       srclen -= 128;
00336       dst += res;
00337    }
00338 
00339    return pos;
00340 }
00341 
00342 /*!
00343  * \brief encrypt a message
00344  * \see ast_encrypt_bin
00345 */
00346 static int __ast_encrypt_bin(unsigned char *dst, const unsigned char *src, int srclen, struct ast_key *key)
00347 {
00348    int res, bytes, pos = 0;
00349 
00350    if (key->ktype != AST_KEY_PUBLIC) {
00351       ast_log(LOG_WARNING, "Cannot encrypt with a private key\n");
00352       return -1;
00353    }
00354    
00355    while(srclen) {
00356       bytes = srclen;
00357       if (bytes > 128 - 41)
00358          bytes = 128 - 41;
00359       /* Process chunks 128-41 bytes at a time */
00360       if ((res = RSA_public_encrypt(bytes, src, dst, key->rsa, RSA_PKCS1_OAEP_PADDING)) != 128) {
00361          ast_log(LOG_NOTICE, "How odd, encrypted size is %d\n", res);
00362          return -1;
00363       }
00364       src += bytes;
00365       srclen -= bytes;
00366       pos += res;
00367       dst += res;
00368    }
00369    return pos;
00370 }
00371 
00372 /*!
00373  * \brief wrapper for __ast_sign_bin then base64 encode it
00374  * \see ast_sign
00375 */
00376 static int __ast_sign(struct ast_key *key, char *msg, char *sig)
00377 {
00378    unsigned char dsig[128];
00379    int siglen = sizeof(dsig), res;
00380 
00381    if (!(res = ast_sign_bin(key, msg, strlen(msg), dsig)))
00382       /* Success -- encode (256 bytes max as documented) */
00383       ast_base64encode(sig, dsig, siglen, 256);
00384 
00385    return res;
00386 }
00387 
00388 /*!
00389  * \brief check signature of a message
00390  * \see ast_check_signature_bin
00391 */
00392 static int __ast_check_signature_bin(struct ast_key *key, const char *msg, int msglen, const unsigned char *dsig)
00393 {
00394    unsigned char digest[20];
00395    int res;
00396 
00397    if (key->ktype != AST_KEY_PUBLIC) {
00398       /* Okay, so of course you really *can* but for our purposes
00399          we're going to say you can't */
00400       ast_log(LOG_WARNING, "Cannot check message signature with a private key\n");
00401       return -1;
00402    }
00403 
00404    /* Calculate digest of message */
00405    SHA1((unsigned char *)msg, msglen, digest);
00406 
00407    /* Verify signature */
00408    if (!(res = RSA_verify(NID_sha1, digest, sizeof(digest), (unsigned char *)dsig, 128, key->rsa))) {
00409       ast_debug(1, "Key failed verification: %s\n", key->name);
00410       return -1;
00411    }
00412 
00413    /* Pass */
00414    return 0;
00415 }
00416 
00417 /*!
00418  * \brief base64 decode then sent to __ast_check_signature_bin
00419  * \see ast_check_signature
00420 */
00421 static int __ast_check_signature(struct ast_key *key, const char *msg, const char *sig)
00422 {
00423    unsigned char dsig[128];
00424    int res;
00425 
00426    /* Decode signature */
00427    if ((res = ast_base64decode(dsig, sig, sizeof(dsig))) != sizeof(dsig)) {
00428       ast_log(LOG_WARNING, "Signature improper length (expect %d, got %d)\n", (int)sizeof(dsig), (int)res);
00429       return -1;
00430    }
00431 
00432    res = ast_check_signature_bin(key, msg, strlen(msg), dsig);
00433 
00434    return res;
00435 }
00436 
00437 /*!
00438  * \brief refresh RSA keys from file
00439  * \param ifd file descriptor
00440  * \param ofd file descriptor
00441  * \return void
00442 */
00443 static void crypto_load(int ifd, int ofd)
00444 {
00445    struct ast_key *key;
00446    DIR *dir = NULL;
00447    struct dirent *ent;
00448    int note = 0;
00449 
00450    AST_RWLIST_WRLOCK(&keys);
00451 
00452    /* Mark all keys for deletion */
00453    AST_RWLIST_TRAVERSE(&keys, key, list) {
00454       key->delme = 1;
00455    }
00456 
00457    /* Load new keys */
00458    if ((dir = opendir(ast_config_AST_KEY_DIR))) {
00459       while((ent = readdir(dir))) {
00460          try_load_key(ast_config_AST_KEY_DIR, ent->d_name, ifd, ofd, &note);
00461       }
00462       closedir(dir);
00463    } else
00464       ast_log(LOG_WARNING, "Unable to open key directory '%s'\n", ast_config_AST_KEY_DIR);
00465 
00466    if (note)
00467       ast_log(LOG_NOTICE, "Please run the command 'init keys' to enter the passcodes for the keys\n");
00468 
00469    /* Delete any keys that are no longer present */
00470    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&keys, key, list) {
00471       if (key->delme) {
00472          ast_debug(1, "Deleting key %s type %d\n", key->name, key->ktype);
00473          AST_RWLIST_REMOVE_CURRENT(list);
00474          if (key->rsa)
00475             RSA_free(key->rsa);
00476          ast_free(key);
00477       }
00478    }
00479    AST_RWLIST_TRAVERSE_SAFE_END;
00480 
00481    AST_RWLIST_UNLOCK(&keys);
00482 }
00483 
00484 static void md52sum(char *sum, unsigned char *md5)
00485 {
00486    int x;
00487    for (x = 0; x < 16; x++) 
00488       sum += sprintf(sum, "%02x", *(md5++));
00489 }
00490 
00491 /*! 
00492  * \brief show the list of RSA keys 
00493  * \param e CLI command
00494  * \param cmd
00495  * \param a list of CLI arguments
00496  * \return CLI_SUCCESS
00497 */
00498 static char *handle_cli_keys_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00499 {
00500 #define FORMAT "%-18s %-8s %-16s %-33s\n"
00501 
00502    struct ast_key *key;
00503    char sum[16 * 2 + 1];
00504    int count_keys = 0;
00505 
00506    switch (cmd) {
00507    case CLI_INIT:
00508       e->command = "keys show";
00509       e->usage =
00510          "Usage: keys show\n"
00511          "       Displays information about RSA keys known by Asterisk\n";
00512       return NULL;
00513    case CLI_GENERATE:
00514       return NULL;
00515    }
00516 
00517    ast_cli(a->fd, FORMAT, "Key Name", "Type", "Status", "Sum");
00518    ast_cli(a->fd, FORMAT, "------------------", "--------", "----------------", "--------------------------------");
00519 
00520    AST_RWLIST_RDLOCK(&keys);
00521    AST_RWLIST_TRAVERSE(&keys, key, list) {
00522       md52sum(sum, key->digest);
00523       ast_cli(a->fd, FORMAT, key->name, 
00524          (key->ktype & 0xf) == AST_KEY_PUBLIC ? "PUBLIC" : "PRIVATE",
00525          key->ktype & KEY_NEEDS_PASSCODE ? "[Needs Passcode]" : "[Loaded]", sum);
00526       count_keys++;
00527    }
00528    AST_RWLIST_UNLOCK(&keys);
00529 
00530    ast_cli(a->fd, "\n%d known RSA keys.\n", count_keys);
00531 
00532    return CLI_SUCCESS;
00533 
00534 #undef FORMAT
00535 }
00536 
00537 /*! 
00538  * \brief initialize all RSA keys  
00539  * \param e CLI command
00540  * \param cmd 
00541  * \param a list of CLI arguments
00542  * \return CLI_SUCCESS
00543 */
00544 static char *handle_cli_keys_init(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00545 {
00546    struct ast_key *key;
00547    int ign;
00548    char *kn, tmp[256] = "";
00549 
00550    switch (cmd) {
00551    case CLI_INIT:
00552       e->command = "keys init";
00553       e->usage =
00554          "Usage: keys init\n"
00555          "       Initializes private keys (by reading in pass code from\n"
00556          "       the user)\n";
00557       return NULL;
00558    case CLI_GENERATE:
00559       return NULL;
00560    }
00561 
00562    if (a->argc != 2)
00563       return CLI_SHOWUSAGE;
00564 
00565    AST_RWLIST_WRLOCK(&keys);
00566    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&keys, key, list) {
00567       /* Reload keys that need pass codes now */
00568       if (key->ktype & KEY_NEEDS_PASSCODE) {
00569          kn = key->fn + strlen(ast_config_AST_KEY_DIR) + 1;
00570          ast_copy_string(tmp, kn, sizeof(tmp));
00571          try_load_key(ast_config_AST_KEY_DIR, tmp, a->fd, a->fd, &ign);
00572       }
00573    }
00574    AST_RWLIST_TRAVERSE_SAFE_END
00575    AST_RWLIST_UNLOCK(&keys);
00576 
00577    return CLI_SUCCESS;
00578 }
00579 
00580 static struct ast_cli_entry cli_crypto[] = {
00581    AST_CLI_DEFINE(handle_cli_keys_show, "Displays RSA key information"),
00582    AST_CLI_DEFINE(handle_cli_keys_init, "Initialize RSA key passcodes")
00583 };
00584 
00585 /*! \brief initialise the res_crypto module */
00586 static int crypto_init(void)
00587 {
00588    ast_cli_register_multiple(cli_crypto, sizeof(cli_crypto) / sizeof(struct ast_cli_entry));
00589 
00590    /* Install ourselves into stubs */
00591    ast_key_get = __ast_key_get;
00592    ast_check_signature = __ast_check_signature;
00593    ast_check_signature_bin = __ast_check_signature_bin;
00594    ast_sign = __ast_sign;
00595    ast_sign_bin = __ast_sign_bin;
00596    ast_encrypt_bin = __ast_encrypt_bin;
00597    ast_decrypt_bin = __ast_decrypt_bin;
00598    return 0;
00599 }
00600 
00601 static int reload(void)
00602 {
00603    crypto_load(-1, -1);
00604    return 0;
00605 }
00606 
00607 static int load_module(void)
00608 {
00609    crypto_init();
00610    if (ast_opt_init_keys)
00611       crypto_load(STDIN_FILENO, STDOUT_FILENO);
00612    else
00613       crypto_load(-1, -1);
00614    return AST_MODULE_LOAD_SUCCESS;
00615 }
00616 
00617 static int unload_module(void)
00618 {
00619    /* Can't unload this once we're loaded */
00620    return -1;
00621 }
00622 
00623 /* needs usecount semantics defined */
00624 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Cryptographic Digital Signatures",
00625       .load = load_module,
00626       .unload = unload_module,
00627       .reload = reload
00628    );

Generated on Wed Oct 28 11:51:07 2009 for Asterisk - the Open Source PBX by  doxygen 1.5.6