diff options
author | Bernhard Schmidt <berni@debian.org> | 2020-08-15 21:29:50 +0200 |
---|---|---|
committer | Bernhard Schmidt <berni@debian.org> | 2020-08-15 21:29:50 +0200 |
commit | 1079962e4c06f88a54e50d997c1b7e84303d30b4 (patch) | |
tree | 4d019426928435425214ccedd6f89b70dbdf035d /src/openvpn/crypto.c | |
parent | 620785fe268a1221c1ba7a9cb5a70f3140a4f1ca (diff) |
New upstream version 2.5~beta1upstream/2.5_beta1
Diffstat (limited to 'src/openvpn/crypto.c')
-rw-r--r-- | src/openvpn/crypto.c | 344 |
1 files changed, 213 insertions, 131 deletions
diff --git a/src/openvpn/crypto.c b/src/openvpn/crypto.c index 7e7dead..3a0bfbe 100644 --- a/src/openvpn/crypto.c +++ b/src/openvpn/crypto.c @@ -30,8 +30,6 @@ #include "syshead.h" -#ifdef ENABLE_CRYPTO - #include "crypto.h" #include "error.h" #include "integer.h" @@ -66,7 +64,6 @@ static void openvpn_encrypt_aead(struct buffer *buf, struct buffer work, struct crypto_options *opt) { -#ifdef HAVE_AEAD_CIPHER_MODES struct gc_arena gc; int outlen = 0; const struct key_ctx *ctx = &opt->key_ctx_bi.encrypt; @@ -77,7 +74,6 @@ openvpn_encrypt_aead(struct buffer *buf, struct buffer work, /* IV, packet-ID and implicit IV required for this mode. */ ASSERT(ctx->cipher); ASSERT(cipher_kt_mode_aead(cipher_kt)); - ASSERT(opt->flags & CO_USE_IV); ASSERT(packet_id_initialized(&opt->packet_id)); gc_init(&gc); @@ -155,9 +151,6 @@ err: buf->len = 0; gc_free(&gc); return; -#else /* HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); -#endif /* ifdef HAVE_AEAD_CIPHER_MODES */ } static void @@ -192,10 +185,7 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work, if (cipher_kt_mode_cbc(cipher_kt)) { /* generate pseudo-random IV */ - if (opt->flags & CO_USE_IV) - { - prng_bytes(iv_buf, iv_size); - } + prng_bytes(iv_buf, iv_size); /* Put packet ID in plaintext buffer */ if (packet_id_initialized(&opt->packet_id) @@ -211,8 +201,7 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work, { struct buffer b; - /* IV and packet-ID required for this mode. */ - ASSERT(opt->flags & CO_USE_IV); + /* packet-ID required for this mode. */ ASSERT(packet_id_initialized(&opt->packet_id)); buf_set_write(&b, iv_buf, iv_size); @@ -224,11 +213,8 @@ openvpn_encrypt_v1(struct buffer *buf, struct buffer work, } /* set the IV pseudo-randomly */ - if (opt->flags & CO_USE_IV) - { - ASSERT(buf_write(&work, iv_buf, iv_size)); - dmsg(D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc)); - } + ASSERT(buf_write(&work, iv_buf, iv_size)); + dmsg(D_PACKET_CONTENT, "ENCRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc)); dmsg(D_PACKET_CONTENT, "ENCRYPT FROM: %s", format_hex(BPTR(buf), BLEN(buf), 80, &gc)); @@ -358,20 +344,19 @@ crypto_check_replay(struct crypto_options *opt, return ret; } -/* - * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. +/** + * Unwrap (authenticate, decrypt and check replay protection) AEAD-mode data + * channel packets. * * Set buf->len to 0 and return false on decrypt error. * - * On success, buf is set to point to plaintext, true - * is returned. + * On success, buf is set to point to plaintext, true is returned. */ static bool openvpn_decrypt_aead(struct buffer *buf, struct buffer work, struct crypto_options *opt, const struct frame *frame, const uint8_t *ad_start) { -#ifdef HAVE_AEAD_CIPHER_MODES static const char error_prefix[] = "AEAD Decrypt error"; struct packet_id_net pin = { 0 }; const struct key_ctx *ctx = &opt->key_ctx_bi.decrypt; @@ -398,7 +383,6 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work, /* IV and Packet ID required for this mode */ ASSERT(packet_id_initialized(&opt->packet_id)); - ASSERT(opt->flags & CO_USE_IV); /* Combine IV from explicit part from packet and implicit part from context */ { @@ -439,13 +423,6 @@ openvpn_decrypt_aead(struct buffer *buf, struct buffer work, tag_ptr = BPTR(buf); ASSERT(buf_advance(buf, tag_size)); dmsg(D_PACKET_CONTENT, "DECRYPT MAC: %s", format_hex(tag_ptr, tag_size, 0, &gc)); -#if defined(ENABLE_CRYPTO_OPENSSL) && OPENSSL_VERSION_NUMBER < 0x10001040L - /* OpenSSL <= 1.0.1c bug requires set tag before processing ciphertext */ - if (!EVP_CIPHER_CTX_ctrl(ctx->cipher, EVP_CTRL_GCM_SET_TAG, tag_size, tag_ptr)) - { - CRYPT_ERROR("setting tag failed"); - } -#endif if (buf->len < 1) { @@ -500,19 +477,15 @@ error_exit: buf->len = 0; gc_free(&gc); return false; -#else /* HAVE_AEAD_CIPHER_MODES */ - ASSERT(0); - return false; -#endif /* ifdef HAVE_AEAD_CIPHER_MODES */ } /* - * If (opt->flags & CO_USE_IV) is not NULL, we will read an IV from the packet. + * Unwrap (authenticate, decrypt and check replay protection) CBC, OFB or CFB + * mode data channel packets. * * Set buf->len to 0 and return false on decrypt error. * - * On success, buf is set to point to plaintext, true - * is returned. + * On success, buf is set to point to plaintext, true is returned. */ static bool openvpn_decrypt_v1(struct buffer *buf, struct buffer work, @@ -572,22 +545,14 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work, /* initialize work buffer with FRAME_HEADROOM bytes of prepend capacity */ ASSERT(buf_init(&work, FRAME_HEADROOM_ADJ(frame, FRAME_HEADROOM_MARKER_DECRYPT))); - /* use IV if user requested it */ - if (opt->flags & CO_USE_IV) - { - if (buf->len < iv_size) - { - CRYPT_ERROR("missing IV info"); - } - memcpy(iv_buf, BPTR(buf), iv_size); - ASSERT(buf_advance(buf, iv_size)); - } - - /* show the IV's initial state */ - if (opt->flags & CO_USE_IV) + /* read the IV from the packet */ + if (buf->len < iv_size) { - dmsg(D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc)); + CRYPT_ERROR("missing IV info"); } + memcpy(iv_buf, BPTR(buf), iv_size); + ASSERT(buf_advance(buf, iv_size)); + dmsg(D_PACKET_CONTENT, "DECRYPT IV: %s", format_hex(iv_buf, iv_size, 0, &gc)); if (buf->len < 1) { @@ -640,8 +605,7 @@ openvpn_decrypt_v1(struct buffer *buf, struct buffer work, { struct buffer b; - /* IV and packet-ID required for this mode. */ - ASSERT(opt->flags & CO_USE_IV); + /* packet-ID required for this mode. */ ASSERT(packet_id_initialized(&opt->packet_id)); buf_set_read(&b, iv_buf, iv_size); @@ -717,7 +681,6 @@ openvpn_decrypt(struct buffer *buf, struct buffer work, void crypto_adjust_frame_parameters(struct frame *frame, const struct key_type *kt, - bool use_iv, bool packet_id, bool packet_id_long_form) { @@ -730,10 +693,7 @@ crypto_adjust_frame_parameters(struct frame *frame, if (kt->cipher) { - if (use_iv) - { - crypto_overhead += cipher_kt_iv_size(kt->cipher); - } + crypto_overhead += cipher_kt_iv_size(kt->cipher); if (cipher_kt_mode_aead(kt->cipher)) { @@ -760,6 +720,20 @@ crypto_max_overhead(void) +max_int(OPENVPN_MAX_HMAC_SIZE, OPENVPN_AEAD_TAG_LENGTH); } +static void +warn_insecure_key_type(const char *ciphername, const cipher_kt_t *cipher) +{ + if (cipher_kt_insecure(cipher)) + { + msg(M_WARN, "WARNING: INSECURE cipher (%s) with block size less than 128" + " bit (%d bit). This allows attacks like SWEET32. Mitigate by " + "using a --cipher with a larger block size (e.g. AES-256-CBC). " + "Support for these insecure ciphers will be removed in " + "OpenVPN 2.6.", + ciphername, cipher_kt_block_size(cipher)*8); + } +} + /* * Build a struct key_type. */ @@ -775,7 +749,7 @@ init_key_type(struct key_type *kt, const char *ciphername, CLEAR(*kt); if (strcmp(ciphername, "none") != 0) { - kt->cipher = cipher_kt_get(translate_cipher_name_from_openvpn(ciphername)); + kt->cipher = cipher_kt_get(ciphername); if (!kt->cipher) { msg(M_FATAL, "Cipher %s not supported", ciphername); @@ -803,6 +777,10 @@ init_key_type(struct key_type *kt, const char *ciphername, { msg(M_FATAL, "Cipher '%s' not allowed: block size too big.", ciphername); } + if (warn) + { + warn_insecure_key_type(ciphername, kt->cipher); + } } else { @@ -855,9 +833,10 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key, cipher_ctx_init(ctx->cipher, key->cipher, kt->cipher_length, kt->cipher, enc); + const char *ciphername = cipher_kt_name(kt->cipher); msg(D_HANDSHAKE, "%s: Cipher '%s' initialized with %d bit key", prefix, - translate_cipher_name_to_openvpn(cipher_kt_name(kt->cipher)), + ciphername, kt->cipher_length *8); dmsg(D_SHOW_KEYS, "%s: CIPHER KEY: %s", prefix, @@ -865,13 +844,7 @@ init_key_ctx(struct key_ctx *ctx, const struct key *key, dmsg(D_CRYPTO_DEBUG, "%s: CIPHER block_size=%d iv_size=%d", prefix, cipher_kt_block_size(kt->cipher), cipher_kt_iv_size(kt->cipher)); - if (cipher_kt_block_size(kt->cipher) < 128/8) - { - msg(M_WARN, "WARNING: INSECURE cipher with block size less than 128" - " bit (%d bit). This allows attacks like SWEET32. Mitigate by " - "using a --cipher with a larger block size (e.g. AES-256-CBC).", - cipher_kt_block_size(kt->cipher)*8); - } + warn_insecure_key_type(ciphername, kt->cipher); } if (kt->digest && kt->hmac_length > 0) { @@ -943,10 +916,12 @@ key_is_zero(struct key *key, const struct key_type *kt) { int i; for (i = 0; i < kt->cipher_length; ++i) + { if (key->cipher[i]) { return false; } + } msg(D_CRYPT_ERRORS, "CRYPTO INFO: WARNING: zero key detected"); return true; } @@ -1025,15 +1000,14 @@ fixup_key(struct key *key, const struct key_type *kt) } void -check_replay_iv_consistency(const struct key_type *kt, bool packet_id, bool use_iv) +check_replay_consistency(const struct key_type *kt, bool packet_id) { ASSERT(kt); - if (!(packet_id && use_iv) && (cipher_kt_mode_ofb_cfb(kt->cipher) - || cipher_kt_mode_aead(kt->cipher))) + if (!packet_id && (cipher_kt_mode_ofb_cfb(kt->cipher) + || cipher_kt_mode_aead(kt->cipher))) { - msg(M_FATAL, "--no-replay or --no-iv cannot be used with a CFB, OFB or " - "AEAD mode cipher"); + msg(M_FATAL, "--no-replay cannot be used with a CFB, OFB or AEAD mode cipher"); } } @@ -1123,7 +1097,6 @@ test_crypto(struct crypto_options *co, struct frame *frame) /* init work */ ASSERT(buf_init(&work, FRAME_HEADROOM(frame))); -#ifdef HAVE_AEAD_CIPHER_MODES /* init implicit IV */ { const cipher_kt_t *cipher = @@ -1145,7 +1118,6 @@ test_crypto(struct crypto_options *co, struct frame *frame) co->key_ctx_bi.decrypt.implicit_iv_len = impl_iv_len; } } -#endif /* ifdef HAVE_AEAD_CIPHER_MODES */ msg(M_INFO, "Entering " PACKAGE_NAME " crypto self-test mode."); for (i = 1; i <= TUN_MTU_SIZE(frame); ++i) @@ -1196,27 +1168,38 @@ test_crypto(struct crypto_options *co, struct frame *frame) gc_free(&gc); } +const char * +print_key_filename(const char *str, bool is_inline) +{ + if (is_inline) + { + return "[[INLINE]]"; + } + + return np(str); +} + void crypto_read_openvpn_key(const struct key_type *key_type, - struct key_ctx_bi *ctx, const char *key_file, const char *key_inline, - const int key_direction, const char *key_name, const char *opt_name) + struct key_ctx_bi *ctx, const char *key_file, + bool key_inline, const int key_direction, + const char *key_name, const char *opt_name) { struct key2 key2; struct key_direction_state kds; + unsigned int flags = RKF_MUST_SUCCEED; if (key_inline) { - read_key_file(&key2, key_inline, RKF_MUST_SUCCEED|RKF_INLINE); - } - else - { - read_key_file(&key2, key_file, RKF_MUST_SUCCEED); + flags |= RKF_INLINE; } + read_key_file(&key2, key_file, flags); if (key2.n != 2) { msg(M_ERR, "File '%s' does not have OpenVPN Static Key format. Using " - "free-form passphrase file is not supported anymore.", key_file); + "free-form passphrase file is not supported anymore.", + print_key_filename(key_file, key_inline)); } /* check for and fix highly unlikely key problems */ @@ -1248,9 +1231,8 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { struct gc_arena gc = gc_new(); struct buffer in; - int fd, size; + int size; uint8_t hex_byte[3] = {0, 0, 0}; - const char *error_filename = file; /* parse info */ const unsigned char *cp; @@ -1288,26 +1270,16 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { size = strlen(file) + 1; buf_set_read(&in, (const uint8_t *)file, size); - error_filename = INLINE_FILE_TAG; } else /* 'file' is a filename which refers to a file containing the ascii key */ { - in = alloc_buf_gc(2048, &gc); - fd = platform_open(file, O_RDONLY, 0); - if (fd == -1) - { - msg(M_ERR, "Cannot open key file '%s'", file); - } - size = read(fd, in.data, in.capacity); - if (size < 0) + in = buffer_read_from_file(file, &gc); + if (!buf_valid(&in)) { msg(M_FATAL, "Read error on key file ('%s')", file); } - if (size == in.capacity) - { - msg(M_FATAL, "Key file ('%s') can be a maximum of %d bytes", file, (int)in.capacity); - } - close(fd); + + size = in.len; } cp = (unsigned char *)in.data; @@ -1393,7 +1365,9 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) { msg(M_FATAL, (isprint(c) ? printable_char_fmt : unprintable_char_fmt), - c, line_num, error_filename, count, onekeylen, keylen); + c, line_num, + print_key_filename(file, flags & RKF_INLINE), count, + onekeylen, keylen); } } ++line_index; @@ -1414,13 +1388,15 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) if (!key2->n) { msg(M_FATAL, "Insufficient key material or header text not found in file '%s' (%d/%d/%d bytes found/min/max)", - error_filename, count, onekeylen, keylen); + print_key_filename(file, flags & RKF_INLINE), count, onekeylen, + keylen); } if (state != PARSE_FINISHED) { msg(M_FATAL, "Footer text not found in file '%s' (%d/%d/%d bytes found/min/max)", - error_filename, count, onekeylen, keylen); + print_key_filename(file, flags & RKF_INLINE), count, onekeylen, + keylen); } } @@ -1453,36 +1429,24 @@ read_key_file(struct key2 *key2, const char *file, const unsigned int flags) gc_free(&gc); } -/* - * Write key to file, return number of random bits - * written. - */ int write_key_file(const int nkeys, const char *filename) { struct gc_arena gc = gc_new(); - int fd, i; - int nbits = 0; + int nbits = nkeys * sizeof(struct key) * 8; /* must be large enough to hold full key file */ struct buffer out = alloc_buf_gc(2048, &gc); - struct buffer nbits_head_text = alloc_buf_gc(128, &gc); /* how to format the ascii file representation of key */ const int bytes_per_line = 16; - /* open key file */ - fd = platform_open(filename, O_CREAT | O_TRUNC | O_WRONLY, S_IRUSR | S_IWUSR); - - if (fd == -1) - { - msg(M_ERR, "Cannot open shared secret file '%s' for write", filename); - } - + /* write header */ + buf_printf(&out, "#\n# %d bit OpenVPN static key\n#\n", nbits); buf_printf(&out, "%s\n", static_key_head); - for (i = 0; i < nkeys; ++i) + for (int i = 0; i < nkeys; ++i) { struct key key; char *fmt; @@ -1498,9 +1462,6 @@ write_key_file(const int nkeys, const char *filename) "\n", &gc); - /* increment random bits counter */ - nbits += sizeof(key) * 8; - /* write to holding buffer */ buf_printf(&out, "%s\n", fmt); @@ -1511,16 +1472,15 @@ write_key_file(const int nkeys, const char *filename) buf_printf(&out, "%s\n", static_key_foot); - /* write number of bits */ - buf_printf(&nbits_head_text, "#\n# %d bit OpenVPN static key\n#\n", nbits); - buf_write_string_file(&nbits_head_text, filename, fd); - + /* write key file to stdout if no filename given */ + if (!filename || strcmp(filename, "")==0) + { + printf("%.*s\n", BLEN(&out), BPTR(&out)); + } /* write key file, now formatted in out, to file */ - buf_write_string_file(&out, filename, fd); - - if (close(fd)) + else if (!buffer_write_file(filename, &out)) { - msg(M_ERR, "Close error on shared secret file %s", filename); + nbits = -1; } /* zero memory which held file content (memory will be freed by GC) */ @@ -1540,7 +1500,7 @@ must_have_n_keys(const char *filename, const char *option, const struct key2 *ke #ifdef ENABLE_SMALL msg(M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d]", filename, option, key2->n, n); #else - msg(M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with '" PACKAGE " --genkey --secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter", filename, option, key2->n, n, option); + msg(M_FATAL, "Key file '%s' used in --%s contains insufficient key material [keys found=%d required=%d] -- try generating a new key file with '" PACKAGE " --genkey secret [file]', or use the existing key file in bidirectional mode by specifying --%s without a key direction parameter", filename, option, key2->n, n, option); #endif } } @@ -1748,7 +1708,9 @@ prng_reset_nonce(void) { int i; for (i = 0; i < size; ++i) + { nonce_data[i] = (uint8_t) i; + } } #endif } @@ -1825,6 +1787,33 @@ get_random(void) return l; } +void +print_cipher(const cipher_kt_t *cipher) +{ + const char *var_key_size = cipher_kt_var_key_size(cipher) ? + " by default" : ""; + + printf("%s (%d bit key%s, ", + cipher_kt_name(cipher), + cipher_kt_key_size(cipher) * 8, var_key_size); + + if (cipher_kt_block_size(cipher) == 1) + { + printf("stream cipher"); + } + else + { + printf("%d bit block", cipher_kt_block_size(cipher) * 8); + } + + if (!cipher_kt_mode_cbc(cipher)) + { + printf(", TLS client/server mode only"); + } + + printf(")\n"); +} + static const cipher_name_pair * get_cipher_name_pair(const char *cipher_name) { @@ -1872,4 +1861,97 @@ translate_cipher_name_to_openvpn(const char *cipher_name) return pair->openvpn_name; } -#endif /* ENABLE_CRYPTO */ +void +write_pem_key_file(const char *filename, const char *pem_name) +{ + struct gc_arena gc = gc_new(); + struct key server_key = { 0 }; + struct buffer server_key_buf = clear_buf(); + struct buffer server_key_pem = clear_buf(); + + if (!rand_bytes((void *)&server_key, sizeof(server_key))) + { + msg(M_NONFATAL, "ERROR: could not generate random key"); + goto cleanup; + } + buf_set_read(&server_key_buf, (void *)&server_key, sizeof(server_key)); + if (!crypto_pem_encode(pem_name, &server_key_pem, + &server_key_buf, &gc)) + { + msg(M_WARN, "ERROR: could not PEM-encode key"); + goto cleanup; + } + + if (!filename || strcmp(filename, "")==0) + { + printf("%.*s", BLEN(&server_key_pem), BPTR(&server_key_pem)); + } + else if (!buffer_write_file(filename, &server_key_pem)) + { + msg(M_ERR, "ERROR: could not write key file"); + goto cleanup; + } + +cleanup: + secure_memzero(&server_key, sizeof(server_key)); + buf_clear(&server_key_pem); + gc_free(&gc); + return; +} + +bool +generate_ephemeral_key(struct buffer *key, const char *key_name) +{ + const int len = BCAP(key); + + msg(M_INFO, "Using random %s.", key_name); + + if (!rand_bytes(BEND(key), len)) + { + msg(M_WARN, "ERROR: could not generate random key"); + return false; + } + + buf_inc_len(key, len); + + return true; +} + +bool +read_pem_key_file(struct buffer *key, const char *pem_name, + const char *key_file, bool key_inline) +{ + bool ret = false; + struct buffer key_pem = { 0 }; + struct gc_arena gc = gc_new(); + + if (!key_inline) + { + key_pem = buffer_read_from_file(key_file, &gc); + if (!buf_valid(&key_pem)) + { + msg(M_WARN, "ERROR: failed to read %s file (%s)", + pem_name, key_file); + goto cleanup; + } + } + else + { + buf_set_read(&key_pem, (const void *)key_file, strlen(key_file) + 1); + } + + if (!crypto_pem_decode(pem_name, key, &key_pem)) + { + msg(M_WARN, "ERROR: %s pem decode failed", pem_name); + goto cleanup; + } + + ret = true; +cleanup: + if (!key_inline) + { + buf_clear(&key_pem); + } + gc_free(&gc); + return ret; +} |