Browse code

Check --ncp-ciphers list on startup

Currently, if --ncp-ciphers contains an invalid cipher, OpenVPN will only
error out when that cipher is selected by negotiation. That's not very
friendly to the user, so check the list on startup, and give a clear error
message immediately.

This patches changes the cipher_kt_get() to let the caller decide what
action to take if no valid cipher was found. This enables us to print all
invalid ciphers in the list, instead of just the first invalid cipher.

This should fix trac #737.

v2: improve tls_check_ncp_cipher_list() with Selva's review suggestions.

Signed-off-by: Steffan Karger <steffan@karger.me>
Acked-by: Selva Nair <selva.nair@gmail.com>
Message-Id: <1476257569-16301-1-git-send-email-steffan@karger.me>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12671.html
Trac: #737
Signed-off-by: David Sommerseth <davids@openvpn.net>

Steffan Karger authored on 2016/10/12 16:32:49
Showing 7 changed files
... ...
@@ -766,6 +766,11 @@ init_key_type (struct key_type *kt, const char *ciphername,
766 766
   if (strcmp (ciphername, "none") != 0)
767 767
     {
768 768
       kt->cipher = cipher_kt_get (translate_cipher_name_from_openvpn(ciphername));
769
+      if (!kt->cipher)
770
+	{
771
+	  msg (M_FATAL, "Cipher %s not supported", ciphername);
772
+	}
773
+
769 774
       kt->cipher_length = cipher_kt_key_size (kt->cipher);
770 775
       if (keysize > 0 && keysize <= MAX_CIPHER_KEY_LENGTH)
771 776
 	kt->cipher_length = keysize;
... ...
@@ -195,7 +195,8 @@ void cipher_des_encrypt_ecb (const unsigned char key[DES_KEY_LENGTH],
195 195
  * 			\c AES-128-CBC).
196 196
  *
197 197
  * @return		A statically allocated structure containing parameters
198
- * 			for the given cipher.
198
+ * 			for the given cipher, or NULL if no matching parameters
199
+ * 			were found.
199 200
  */
200 201
 const cipher_kt_t * cipher_kt_get (const char *ciphername);
201 202
 
... ...
@@ -384,13 +384,18 @@ cipher_kt_get (const char *ciphername)
384 384
   cipher = mbedtls_cipher_info_from_string(ciphername);
385 385
 
386 386
   if (NULL == cipher)
387
-    msg (M_FATAL, "Cipher algorithm '%s' not found", ciphername);
387
+    {
388
+      msg (D_LOW, "Cipher algorithm '%s' not found", ciphername);
389
+      return NULL;
390
+    }
388 391
 
389 392
   if (cipher->key_bitlen/8 > MAX_CIPHER_KEY_LENGTH)
390
-    msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)",
391
-	 ciphername,
392
-	 cipher->key_bitlen/8,
393
-	 MAX_CIPHER_KEY_LENGTH);
393
+    {
394
+      msg (D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) "
395
+	  "which is larger than " PACKAGE_NAME "'s current maximum key size "
396
+	  "(%d bytes)", ciphername, cipher->key_bitlen/8, MAX_CIPHER_KEY_LENGTH);
397
+      return NULL;
398
+    }
394 399
 
395 400
   return cipher;
396 401
 }
... ...
@@ -504,13 +504,20 @@ cipher_kt_get (const char *ciphername)
504 504
   cipher = EVP_get_cipherbyname (ciphername);
505 505
 
506 506
   if (NULL == cipher)
507
-    crypto_msg (M_FATAL, "Cipher algorithm '%s' not found", ciphername);
507
+    {
508
+      crypto_msg (D_LOW, "Cipher algorithm '%s' not found", ciphername);
509
+      return NULL;
510
+    }
511
+
508 512
 
509 513
   if (EVP_CIPHER_key_length (cipher) > MAX_CIPHER_KEY_LENGTH)
510
-    msg (M_FATAL, "Cipher algorithm '%s' uses a default key size (%d bytes) which is larger than " PACKAGE_NAME "'s current maximum key size (%d bytes)",
511
-	 ciphername,
512
-	 EVP_CIPHER_key_length (cipher),
513
-	 MAX_CIPHER_KEY_LENGTH);
514
+    {
515
+      msg (D_LOW, "Cipher algorithm '%s' uses a default key size (%d bytes) "
516
+	  "which is larger than " PACKAGE_NAME "'s current maximum key size "
517
+	  "(%d bytes)", ciphername, EVP_CIPHER_key_length (cipher),
518
+	  MAX_CIPHER_KEY_LENGTH);
519
+      return NULL;
520
+    }
514 521
 
515 522
   return cipher;
516 523
 }
... ...
@@ -2208,6 +2208,11 @@ options_postprocess_verify_ce (const struct options *options, const struct conne
2208 2208
 
2209 2209
 #ifdef ENABLE_CRYPTO
2210 2210
 
2211
+  if (options->ncp_enabled && !tls_check_ncp_cipher_list(options->ncp_ciphers))
2212
+    {
2213
+      msg (M_USAGE, "NCP cipher list contains unsupported ciphers.");
2214
+    }
2215
+
2211 2216
   /*
2212 2217
    * Check consistency of replay options
2213 2218
    */
... ...
@@ -3714,6 +3714,28 @@ tls_peer_info_ncp_ver(const char *peer_info)
3714 3714
   return 0;
3715 3715
 }
3716 3716
 
3717
+bool
3718
+tls_check_ncp_cipher_list(const char *list) {
3719
+  bool unsupported_cipher_found = false;
3720
+
3721
+  ASSERT (list);
3722
+
3723
+  char * const tmp_ciphers = string_alloc (list, NULL);
3724
+  const char *token = strtok (tmp_ciphers, ":");
3725
+  while (token)
3726
+    {
3727
+      if (!cipher_kt_get (translate_cipher_name_from_openvpn (token)))
3728
+	{
3729
+	  msg (M_WARN, "Unsupported cipher in --ncp-ciphers: %s", token);
3730
+	  unsupported_cipher_found = true;
3731
+	}
3732
+      token = strtok (NULL, ":");
3733
+    }
3734
+  free (tmp_ciphers);
3735
+
3736
+  return 0 < strlen(list) && !unsupported_cipher_found;
3737
+}
3738
+
3717 3739
 /*
3718 3740
  * Dump a human-readable rendition of an openvpn packet
3719 3741
  * into a garbage collectable string which is returned.
... ...
@@ -503,6 +503,15 @@ tls_get_peer_info(const struct tls_multi *multi)
503 503
  */
504 504
 int tls_peer_info_ncp_ver(const char *peer_info);
505 505
 
506
+/**
507
+ * Check whether the ciphers in the supplied list are supported.
508
+ *
509
+ * @param list		Colon-separated list of ciphers
510
+ *
511
+ * @returns true iff all ciphers in list are supported.
512
+ */
513
+bool tls_check_ncp_cipher_list(const char *list);
514
+
506 515
 /*
507 516
  * inline functions
508 517
  */