Probable bug in openpgp

gmail arbogast.cedric at gmail.com
Sat Mar 26 15:57:18 CET 2011


Hi all,

This is my first contact and mail with gnutls mailing list, to summarize 
i'm a coder maintening a personnal home server using several GNU tools 
since the late 90's.


I build GNUTLS-2.12.0 in a chroot jail (gcc 4.5.2/libc 2.13/binutils 
2.21/make 3.82) with an athlon architecture on ext3 FS and, as root,  
and notice tests/openpgp-auth freeze after few error messages :

     [root at pompomgalli] mkdir gnutls-2.12.0_build && cd gnutls-2.12.0_build

     [root at pompomgalli] ../gnutls-2.12.0/configure && make

     ...

     [root at pompomgalli] make check

     ...

     PASS: mini-x509
     PASS: mini-x509-rehandshake
     Self test `./rng-fork' finished with 0 errors
     successSelf test `./rng-fork' finished with 0 errors
     PASS: rng-fork
     Self test `./openssl' finished with 0 errors
     PASS: openssl
     server openpgp keys The request is invalid.
     client openpgp keys The request is invalid.
     server handshake Could not negotiate a supported cipher suite. (-21)

<[CTRL][C]>

     [root at pompomgalli]





I investigate by put on debugging in tests/openpgp-auth and add strace 
support relating to openpgp-auth in tests Makefile :




--- gnutls-2.12.0_build/tests/Makefile.orig     2011-03-26 
13:23:14.000000000 +0100
+++ gnutls-2.12.0_build/tests/Makefile  2011-03-26 09:52:44.000000000 +0100
@@ -1670,6 +1670,7 @@
             if test -f ./$$tst; then dir=./; \
             elif test -f $$tst; then dir=; \
             else dir="$(srcdir)/"; fi; \
+           if [ "$$tst" = "openpgp-auth" ] ; then tst="strace -o 
pgp.txt -ff $$tst" ; fi; \
             if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
               all=`expr $$all + 1`; \
               case " $(XFAIL_TESTS) " in \


--- gnutls-2.12.0/tests/openpgp-auth.c.orig     2011-03-16 
22:40:25.000000000 +0100
+++ gnutls-2.12.0/tests/openpgp-auth.c  2011-03-26 12:05:10.000000000 +0100
@@ -60,6 +60,7 @@
    const char *srcdir;
    char *pub_key_path, *priv_key_path;
    pid_t child;
+  debug=1;

    gnutls_global_init ();





And relaunch tests suite :




     [root at pompomgalli] make check

     ...

     [ 5569| 7] RB: Have 5 bytes into buffer. Adding 112 bytes.
     [ 5569| 7] RB: Requested 117 bytes
     [ 5569| 4] REC[0x8055e80]: Decrypted Packet[1] Alert(21) with length: 2
     [ 5569| 4] REC[0x8055e80]: Alert[1|0] - Close notify - was received
     Process 5777 attached
     [ 5569| 4] REC[0x8064250]: Allocating epoch #0
     [ 5569| 7] new stream `[temp]'
     [ 5569| 7] new stream fd=10
     [ 5569| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/armor.c:327
     [ 5569| 6] armor filter: decode
     [ 5569| 7] filter [temp] [read]: type=1 rc=0
     [ 5569| 7] replace stream fd=10 with fd=11
     [ 5569| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/misc.c:325
     [ 5569| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/misc.c:325
     [ 5569| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:1002
     [ 5569| 7] close stream ref=0 `[temp]'
     [ 5569| 7] close stream fd=11
     [ 5777| 4] REC[0x8064250]: Allocating epoch #0
     [ 5569| 6] free armor filter
     [ 5569| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:515
     [ 5569| 7] new stream `[temp]'
     [ 5569| 7] new stream fd=10
     [ 5569| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/armor.c:327
     [ 5777| 7] new stream `[temp]'
     [ 5777| 7] new stream fd=10
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/armor.c:327
     [ 5777| 6] armor filter: decode
     [ 5777| 7] filter [temp] [read]: type=1 rc=0
     [ 5777| 7] replace stream fd=10 with fd=11
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/misc.c:325
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/misc.c:325
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:1002
     [ 5777| 7] close stream ref=0 `[temp]'
     [ 5777| 7] close stream fd=11
     [ 5777| 6] free armor filter
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:515
     [ 5777| 7] new stream `[temp]'
     [ 5777| 7] new stream fd=10
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/armor.c:327
     [ 5777| 6] armor filter: decode
     [ 5777| 7] filter [temp] [read]: type=1 rc=0
     [ 5777| 7] replace stream fd=10 with fd=11
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:1002
     [ 5777| 7] close stream ref=0 `[temp]'
     [ 5777| 7] close stream fd=11
     [ 5777| 6] free armor filter
     [ 5777| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:515
     [ 5777| 2] ASSERT: ../../gnutls-2.12.0/lib/gnutls_str.c:496
     [ 5777| 2] Error converting hex string: f30fd423c143e7ba.
     [ 5777| 2] ASSERT: 
../../../gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c:414
     [ 5777| 2] ASSERT: 
../../../gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c:425
     [ 5777| 2] ASSERT: 
../../../gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c:509
     client openpgp keys The request is invalid.
     [ 5777| 2] ASSERT: ../../gnutls-2.12.0/lib/gnutls_constate.c:695

     ...

<[CTRL][C]>

     [root at pompomgalli]



I notice then something is probably wrong with the key id :    "[ 5777| 
2] Error converting hex string: f30fd423c143e7ba." triggered by the 
gnutls_str.c assert at line 496 which then trigger the three following 
asserts.
I have then checked the gnutls_openpgp.c method at the origin of the 
line 414 assert :


         @321 "gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c"

         static int
         get_keyid (gnutls_openpgp_keyid_t keyid, const char *str)
         {
           size_t keyid_size = sizeof (keyid);

           if (strlen (str) != 16)
             {
               _gnutls_debug_log
                 ("The OpenPGP subkey ID has to be 16 hexadecimal 
characters.\n");
               return GNUTLS_E_INVALID_REQUEST;
             }

           if (_gnutls_hex2bin (str, strlen (str), keyid, &keyid_size) < 0)
             {
               _gnutls_debug_log ("Error converting hex string: %s.\n", 
str);
               return GNUTLS_E_INVALID_REQUEST;
             }

           return 0;
         }


And the _gnutls_hex2bin method that triggers the converting error message :


           @476 "gnutls-2.12.0/lib/gnutls_str.c"
           int
           _gnutls_hex2bin (const opaque * hex_data, int hex_size, 
opaque * bin_data,
                            size_t * bin_size)
           {
             int i, j;
             opaque hex2_data[3];
             unsigned long val;

             hex2_data[2] = 0;

             for (i = j = 0; i < hex_size;)
               {
                 if (!isxdigit (hex_data[i]))      /* skip non-hex such 
as the ':' in 00:FF */
                   {
                     i++;
                     continue;
                   }

                 if (j > *bin_size)
                   {
                     gnutls_assert ();
                     return GNUTLS_E_SHORT_MEMORY_BUFFER;
                   }

                 hex2_data[0] = hex_data[i];
                 hex2_data[1] = hex_data[i + 1];
                 i += 2;

                 val = strtoul ((char *) hex2_data, NULL, 16);
                 if (val == ULONG_MAX)
                   {
                     gnutls_assert ();
                     return GNUTLS_E_PARSING_ERROR;
                   }
                 bin_data[j] = val;
                 j++;
               }
             *bin_size = j;

             return 0;
           }

The line 496 assert (if (j > *bin_size)  { gnutls_assert (); return 
GNUTLS_E_SHORT_MEMORY_BUFFER;  }) is normaly triggered by an unexpected 
numbers of digits in keyid requiring too many bytes in the target binary 
buffer.
I guess then there is something wrrong with the keyid str string or the 
keyid_size. The keyid str in the log above seems correct, with 16 digits 
forming a correct hexadecimal string.
I decide to check the keyid_size :



--- gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c.orig     2011-03-16 
20:53:46.000000000 +0100
+++ gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c  2011-03-26 
13:18:35.000000000 +0100
@@ -332,7 +332,7 @@

    if (_gnutls_hex2bin (str, strlen (str), keyid, &keyid_size) < 0)
      {
-      _gnutls_debug_log ("Error converting hex string: %s.\n", str);
+      _gnutls_debug_log ("Error converting hex string: %s.[%d]\n", 
str,keyid_size);
        return GNUTLS_E_INVALID_REQUEST;
      }





And relaunch tests suite :





     [root at pompomgalli] make check

     ...

     [13182| 7] RB: Requested 181 bytes
     [13182| 4] REC[0x8055e80]: Decrypted Packet[2] Alert(21) with length: 2
     [13182| 4] REC[0x8055e80]: Alert[1|0] - Close notify - was received
     Process 13182 suspended
     [13184| 6] armor filter: decode
     [13184| 7] filter [temp] [read]: type=1 rc=0
     [13184| 7] replace stream fd=10 with fd=11
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/misc.c:325
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/misc.c:325
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:1002
     [13184| 7] close stream ref=0 `[temp]'
     [13184| 7] close stream fd=11
     [13184| 6] free armor filter
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:515
     [13184| 7] new stream `[temp]'
     [13184| 7] new stream fd=10
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/armor.c:327
     [13184| 6] armor filter: decode
     [13184| 7] filter [temp] [read]: type=1 rc=0
     [13184| 7] replace stream fd=10 with fd=11
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:1002
     [13184| 7] close stream ref=0 `[temp]'
     [13184| 7] close stream fd=11
     [13184| 6] free armor filter
     [13184| 2] ASSERT: ../../../gnutls-2.12.0/lib/opencdk/stream.c:515
     [13184| 2] ASSERT: ../../gnutls-2.12.0/lib/gnutls_str.c:496
     [13184| 2] Error converting hex string: f30fd423c143e7ba.[4]
     [13184| 2] ASSERT: 
../../../gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c:414
     [13184| 2] ASSERT: 
../../../gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c:425
     [13184| 2] ASSERT: 
../../../gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c:509
     client openpgp keys The request is invalid.
     [13184| 2] ASSERT: ../../gnutls-2.12.0/lib/gnutls_constate.c:695
     [13184| 4] REC[0x8064250]: Allocating epoch #1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_DSS_AES_128_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_DSS_CAMELLIA_128_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_DSS_AES_256_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_DSS_CAMELLIA_256_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_DSS_3DES_EDE_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: DHE_DSS_ARCFOUR_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_RSA_AES_128_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_RSA_CAMELLIA_128_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_RSA_AES_256_CBC_SHA1
     [13184| 3] HSK[0x8064250]: Keeping ciphersuite: 
DHE_RSA_CAMELLIA_256_CBC_SHA1


<[CTRL][C]>

     [root at pompomgalli]



The ligne  "[13184| 2] Error converting hex string: 
f30fd423c143e7ba.[4]" is self explaining, hard to store 16 digits 
hexadecimal string in 4 bytes.
The gnutls_openpgp_keyid_t type is now my main suspect and i then check 
his definition  and stuff relating to it  :

         @56 "gnutls-2.12.0/lib/includes/gnutls/openpgp.h"
           typedef unsigned char gnutls_openpgp_keyid_t[8];


         @2874 "gnutls-2.12.0/NEWS"
         gnutls_openpgp_keyid_t: ADDED, instead of hard-coded 'unsigned 
char[8]'.

         (in Version 2.3.1, released 2008-02-21 )


Bingo !

      sizeof(unsigned char[8]) return 8

  but

     sizeof(*) return ...
             4 on a 32 bits architecture
             8 on a 64 bits architecture


The issue appears at the third pass of the openpgp-auth which provides 
directly a keyid, which drives openpgp to parse the keyid.
On a 64 bits architecture, the pointer size matchs the keyid buffer size 
and then get_keyid works without any troubles.
On a 32 bits architecture, it fails as show the openpgp-auth test.

The problem is that keyid_size in the get_keyid method has to  get the 
keyid BUFFER size and not the keyid POINTER size.

A way to correct that could be to alter openpgp.h to have sizeof(keyid) 
returning BUFFER size without broke ABI compatibility :

       typedef struct { unsigned char __gnutls_openpgp_keyid_t[8]; } 
gnutls_openpgp_keyid_t;

But everything in gnutls deal with gnutls_openpgp_keyid_t  as a pointer, 
so it's seems better to directly alter "size_t keyid_size = sizeof 
(keyid); " in
gnutls_openpgp.c to "size_t keyid_size = sizeof (unsigned char[8]);" and 
doing it properly with a constant.

Here is three suggested patchs for this bug :

Patch 1 :

--- gnutls-2.12.0/lib/includes/gnutls/gnutls.h.in.orig  2011-03-23 
19:53:44.000000000 +0100
+++ gnutls-2.12.0/lib/includes/gnutls/gnutls.h.in       2011-03-26 
14:11:46.000000000 +0100
@@ -1571,6 +1571,11 @@
  #define GNUTLS_KEY_CRL_SIGN            2
  #define GNUTLS_KEY_ENCIPHER_ONLY       1
  #define GNUTLS_KEY_DECIPHER_ONLY       32768
+
+  /* specifics contants relating to keys and subkeys :
+   */
+#define GNUTLS_KEY_ID_SIZE_IN_BYTES     8
+

    void
      gnutls_certificate_set_params_function 
(gnutls_certificate_credentials_t



Patch 2 :


--- gnutls-2.12.0/lib/includes/gnutls/openpgp.h.orig    2011-02-28 
17:12:24.000000000 +0100
+++ gnutls-2.12.0/lib/includes/gnutls/openpgp.h 2011-03-26 
14:13:53.000000000 +0100
@@ -53,7 +53,7 @@
      GNUTLS_OPENPGP_FMT_BASE64
    } gnutls_openpgp_crt_fmt_t;

-  typedef unsigned char gnutls_openpgp_keyid_t[8];
+  typedef unsigned char 
gnutls_openpgp_keyid_t[GNUTLS_KEY_ID_SIZE_IN_BYTES];

  /* gnutls_openpgp_cert_t should be defined in gnutls.h
   */



Patch 3 :


--- gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c.orig     2011-03-16 
20:53:46.000000000 +0100
+++ gnutls-2.12.0/lib/openpgp/gnutls_openpgp.c  2011-03-26 
14:16:04.000000000 +0100
@@ -321,7 +321,11 @@
  static int
  get_keyid (gnutls_openpgp_keyid_t keyid, const char *str)
  {
-  size_t keyid_size = sizeof (keyid);
+  /* keyid_size should get keyid buffer size,
+   * since 2008-02-21 keyid has become a pointer so
+   * sizeof(keyid) become wrong and
+   * keyid_size must be explicity given */
+  size_t keyid_size = sizeof (unsigned char[GNUTLS_KEY_ID_SIZE_IN_BYTES]);

    if (strlen (str) != 16)
      {
@@ -329,7 +333,6 @@
          ("The OpenPGP subkey ID has to be 16 hexadecimal characters.\n");
        return GNUTLS_E_INVALID_REQUEST;
      }
-
    if (_gnutls_hex2bin (str, strlen (str), keyid, &keyid_size) < 0)
      {
        _gnutls_debug_log ("Error converting hex string: %s.\n", str);



I have applied  this three patchs on my build and the openpgp-auth is 
now succesfull :


     [root at pompomgalli] make check

     ...

     PASS: mini-x509
     PASS: mini-x509-rehandshake
     Self test `./rng-fork' finished with 0 errors
     successSelf test `./rng-fork' finished with 0 errors
     PASS: rng-fork
     Self test `./openssl' finished with 0 errors
     PASS: openssl
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     Self test `./openpgp-auth' finished with 0 errors
     PASS: openpgp-auth
     Self test `./openpgp-keyring' finished with 0 errors
     PASS: openpgp-keyring
     PASS: pgps2kgnu
     Self test `./x509self' finished with 0 errors
     Self test `./x509self' finished with 0 errors

     ...


Hope this will help, i'm available for tests/infos if needed.


Regards, Cedric.





More information about the Gnutls-devel mailing list