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