Collected changes - see ChangeLogs
[libgcrypt.git] / src / sexp.c
index f2f93f7..80fed53 100644 (file)
@@ -1,5 +1,6 @@
 /* sexp.c  -  S-Expression handling
- *     Copyright (C) 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003,
+ *               2004, 2006 Free Software Foundation, Inc.
  *
  * This file is part of Libgcrypt.
  *
@@ -25,7 +26,7 @@
 #include <string.h>
 #include <stdarg.h>
 #include <ctype.h>
-#include <assert.h>
+#include <errno.h>
 
 #define GCRYPT_NO_MPI_MACROS 1
 #include "g10lib.h"
@@ -53,11 +54,25 @@ struct gcry_sexp
 
 #define TOKEN_SPECIALS  "-./_:*+="
 
-static gpg_error_t
+static gcry_error_t
 sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
            const char *buffer, size_t length, int argflag,
            va_list arg_ptr, void **arg_list);
 
+/* Return true if P points to a byte containing a whitespace according
+   to the S-expressions definition. */
+#undef whitespacep
+static GPG_ERR_INLINE int
+whitespacep (const unsigned char *p)
+{ 
+  switch (*p)
+    {
+    case ' ': case '\t': case '\v': case '\f': case '\r': case '\n': return 1;
+    default: return 0;
+    }
+}
+
+
 #if 0
 static void
 dump_mpi( gcry_mpi_t a )
@@ -187,19 +202,19 @@ normalize ( gcry_sexp_t list )
   
    This function returns 0 and and the pointer to the new object in
    RETSEXP or an error code in which case RETSEXP is set to NULL.  */
-gpg_error_t
+gcry_error_t
 gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
                   int autodetect, void (*freefnc)(void*) )
 {
-  gpg_error_t errcode;
+  gcry_error_t errcode;
   gcry_sexp_t se;
   volatile va_list dummy_arg_ptr;
 
   if (!retsexp)
-    return gpg_error (GPG_ERR_INV_ARG);
+    return gcry_error (GPG_ERR_INV_ARG);
   *retsexp = NULL;
   if (autodetect < 0 || autodetect > 1 || !buffer)
-    return gpg_error (GPG_ERR_INV_ARG);
+    return gcry_error (GPG_ERR_INV_ARG);
 
   if (!length && !autodetect)
     { /* What a brave caller to assume that there is really a canonical
@@ -227,11 +242,11 @@ gcry_sexp_create (gcry_sexp_t *retsexp, void *buffer, size_t length,
          GCRYSEXP object and use the BUFFER directly */
       freefnc (buffer);
     }
-  return gpg_error (GPG_ERR_NO_ERROR);
+  return gcry_error (GPG_ERR_NO_ERROR);
 }
 
 /* Same as gcry_sexp_create but don't transfer ownership */
-gpg_error_t
+gcry_error_t
 gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
                int autodetect)
 {
@@ -245,7 +260,39 @@ gcry_sexp_new (gcry_sexp_t *retsexp, const void *buffer, size_t length,
 void
 gcry_sexp_release( gcry_sexp_t sexp )
 {
-    gcry_free ( sexp );
+  if (sexp)
+    {
+      if (gcry_is_secure (sexp))
+        {
+          /* Extra paranoid wiping. */
+          const byte *p = sexp->d;
+          int type;
+
+          while ( (type = *p) != ST_STOP )
+            {
+              p++;
+              switch ( type )
+                {
+                case ST_OPEN:
+                  break;
+                case ST_CLOSE:
+                  break;
+                case ST_DATA: 
+                  {
+                    DATALEN n;
+                    memcpy ( &n, p, sizeof n );
+                    p += sizeof n;
+                    p += n;
+                  }
+                  break;
+                default:
+                  break;
+                }
+            }
+          wipememory (sexp->d, p - sexp->d);
+        }
+      gcry_free ( sexp );
+    }
 }
 
 
@@ -257,22 +304,24 @@ gcry_sexp_release( gcry_sexp_t sexp )
 gcry_sexp_t
 gcry_sexp_cons( const gcry_sexp_t a, const gcry_sexp_t b )
 {
-    /* NYI: Implementation should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation */
+  BUG ();
+  return NULL;
 }
 
 
 /****************
  * Make a list from all items in the array the end of the array is marked
- * with a NULL.                                                                      y a NULL
+ * with a NULL.
  */
 gcry_sexp_t
 gcry_sexp_alist( const gcry_sexp_t *array )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 /****************
@@ -281,9 +330,10 @@ gcry_sexp_alist( const gcry_sexp_t *array )
 gcry_sexp_t
 gcry_sexp_vlist( const gcry_sexp_t a, ... )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 
@@ -294,17 +344,19 @@ gcry_sexp_vlist( const gcry_sexp_t a, ... )
 gcry_sexp_t
 gcry_sexp_append( const gcry_sexp_t a, const gcry_sexp_t n )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 gcry_sexp_t
 gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
 {
-    /* NYI: Implementaion should be quite easy with our new data representation */
-    BUG ();
-    return NULL;
+  /* NYI: Implementation should be quite easy with our new data
+     representation. */
+  BUG ();
+  return NULL;
 }
 
 
@@ -316,61 +368,78 @@ gcry_sexp_prepend( const gcry_sexp_t a, const gcry_sexp_t n )
 gcry_sexp_t
 gcry_sexp_find_token( const gcry_sexp_t list, const char *tok, size_t toklen )
 {
-    const byte *p;
-    DATALEN n;
+  const byte *p;
+  DATALEN n;
+  
+  if ( !list )
+    return NULL;
 
-    if ( !list )
-       return NULL;
+  if ( !toklen )
+    toklen = strlen(tok);
 
-    if( !toklen )
-       toklen = strlen(tok);
-    p = list->d;
-    while ( *p != ST_STOP ) {
-       if ( *p == ST_OPEN && p[1] == ST_DATA ) {
-           const byte *head = p;
-
-           p += 2;
-           memcpy ( &n, p, sizeof n ); p += sizeof n;
-           if ( n == toklen && !memcmp( p, tok, toklen ) ) { /* found it */
-               gcry_sexp_t newlist;
-               byte *d;
-               int level = 1;
-
-               /* look for the end of the list */
-               for ( p += n; level; p++ ) {
-                   if ( *p == ST_DATA ) {
+  p = list->d;
+  while ( *p != ST_STOP )
+    {
+      if ( *p == ST_OPEN && p[1] == ST_DATA ) 
+        {
+          const byte *head = p;
+
+          p += 2;
+          memcpy ( &n, p, sizeof n );
+          p += sizeof n;
+          if ( n == toklen && !memcmp( p, tok, toklen ) )
+            { /* found it */
+              gcry_sexp_t newlist;
+              byte *d;
+              int level = 1;
+
+              /* Look for the end of the list.  */
+              for ( p += n; level; p++ ) 
+                {
+                  if ( *p == ST_DATA )
+                    {
                        memcpy ( &n, ++p, sizeof n );
                        p += sizeof n + n;
-                       p--; /* compensate for later increment */
+                       p--; /* Compensate for later increment. */
                    }
-                   else if ( *p == ST_OPEN ) {
-                       level++;
+                  else if ( *p == ST_OPEN ) 
+                    {
+                      level++;
                    }
-                   else if ( *p == ST_CLOSE ) {
-                       level--;
+                  else if ( *p == ST_CLOSE ) 
+                    {
+                      level--;
                    }
-                   else if ( *p == ST_STOP ) {
-                       BUG ();
+                  else if ( *p == ST_STOP ) 
+                    {
+                      BUG ();
                    }
-               } while ( level );
-               n = p - head;
-
-               newlist = gcry_xmalloc ( sizeof *newlist + n );
-               d = newlist->d;
-               memcpy ( d, head, n ); d += n;
-               *d++ = ST_STOP;
-               return normalize ( newlist );
+               }
+              n = p - head;
+
+              newlist = gcry_malloc ( sizeof *newlist + n );
+              if (!newlist)
+                {
+                  /* No way to return an error code, so we can only
+                     return Not Found. */
+                  return NULL;
+                }
+              d = newlist->d;
+              memcpy ( d, head, n ); d += n;
+              *d++ = ST_STOP;
+              return normalize ( newlist );
            }
-           p += n;
+          p += n;
        }
-       else if ( *p == ST_DATA ) {
-           memcpy ( &n, ++p, sizeof n ); p += sizeof n;
-           p += n;
+      else if ( *p == ST_DATA )
+        {
+          memcpy ( &n, ++p, sizeof n ); p += sizeof n;
+          p += n;
        }
-       else
-           p++;
+      else
+        p++;
     }
-    return NULL;
+  return NULL;
 }
 
 /****************
@@ -411,9 +480,8 @@ gcry_sexp_length( const gcry_sexp_t list )
 
 
 
-/****************
- * Extract the CAR of the given list
- */
+/* Extract the CAR of the given list.  May return NULL for bad lists
+   or memory failure.  */
 gcry_sexp_t
 gcry_sexp_nth( const gcry_sexp_t list, int number )
 {
@@ -452,7 +520,9 @@ gcry_sexp_nth( const gcry_sexp_t list, int number )
 
     if ( *p == ST_DATA ) {
        memcpy ( &n, p, sizeof n ); p += sizeof n;
-       newlist = gcry_xmalloc ( sizeof *newlist + n + 1 );
+       newlist = gcry_malloc ( sizeof *newlist + n + 1 );
+        if (!newlist)
+          return NULL;
        d = newlist->d;
        memcpy ( d, p, n ); d += n;
        *d++ = ST_STOP;
@@ -480,7 +550,9 @@ gcry_sexp_nth( const gcry_sexp_t list, int number )
        } while ( level );
        n = p + 1 - head;
 
-       newlist = gcry_xmalloc ( sizeof *newlist + n );
+       newlist = gcry_malloc ( sizeof *newlist + n );
+        if (!newlist)
+          return NULL;
        d = newlist->d;
        memcpy ( d, head, n ); d += n;
        *d++ = ST_STOP;
@@ -596,13 +668,13 @@ gcry_sexp_nth_mpi( gcry_sexp_t list, int number, int mpifmt )
     }
 
     if ( *p == ST_DATA ) {
-       MPI a;
+       gcry_mpi_t a;
        size_t nbytes;
 
        memcpy ( &n, ++p, sizeof n );
        p += sizeof n;
        nbytes = n;
-       if( !gcry_mpi_scan( &a, mpifmt, p, &nbytes ) )
+       if( !gcry_mpi_scan( &a, mpifmt, p, n, &nbytes ) )
            return a;
     }
 
@@ -672,7 +744,9 @@ gcry_sexp_cdr( const gcry_sexp_t list )
     } while ( level );
     n = p - head;
 
-    newlist = gcry_xmalloc ( sizeof *newlist + n + 2 );
+    newlist = gcry_malloc ( sizeof *newlist + n + 2 );
+    if (!newlist)
+      return NULL;
     d = newlist->d;
     *d++ = ST_OPEN;
     memcpy ( d, head, n ); d += n;
@@ -724,21 +798,29 @@ struct make_space_ctx {
     byte *pos;
 };
 
-static void
+static gpg_err_code_t
 make_space ( struct make_space_ctx *c, size_t n )
 {
-    size_t used = c->pos - c->sexp->d;
-
-    if ( used + n + sizeof(DATALEN) + 1 >= c->allocated ) {
-       gcry_sexp_t newsexp;
-       byte *newhead;
-
-       c->allocated += 2*(n+sizeof(DATALEN)+1);
-       newsexp = gcry_xrealloc ( c->sexp, sizeof *newsexp + c->allocated - 1 );
-       newhead = newsexp->d;
-       c->pos = newhead + used;
-       c->sexp = newsexp;
+  size_t used = c->pos - c->sexp->d;
+  
+  if ( used + n + sizeof(DATALEN) + 1 >= c->allocated )
+    {
+      gcry_sexp_t newsexp;
+      byte *newhead;
+      size_t newsize;
+      
+      newsize = c->allocated + 2*(n+sizeof(DATALEN)+1);
+      if (newsize <= c->allocated)
+        return GPG_ERR_TOO_LARGE;
+      newsexp = gcry_realloc ( c->sexp, sizeof *newsexp + newsize - 1);
+      if (!newsexp)
+        return gpg_err_code_from_errno (errno);
+      c->allocated = newsize;
+      newhead = newsexp->d;
+      c->pos = newhead + used;
+      c->sexp = newsexp;
     }
+  return 0;
 }
 
 
@@ -823,25 +905,28 @@ unquote_string (const unsigned char *string, size_t length, unsigned char *buf)
  *     %m - MPI
  *     %s - string (no autoswitch to secure allocation)
  *     %d - integer stored as string (no autoswitch to secure allocation)
+ *      %b - memory buffer; this takes _two_ arguments: an integer with the 
+ *           length of the buffer and a pointer to the buffer.
  *  all other format elements are currently not defined and return an error.
  *  this includes the "%%" sequence becauce the percent sign is not an
  *  allowed character.
- * FIXME: We should find a way to store the secure-MPIS not in the string
+ * FIXME: We should find a way to store the secure-MPIs not in the string
  * but as reference to somewhere - this can help us to save huge amounts
  * of secure memory.  The problem is, that if only one element is secure, all
- * other elements are automagicaly copied to secure meory too, so the most
+ * other elements are automagicaly copied to secure memory too, so the most
  * common operation gcry_sexp_cdr_mpi() will always return a secure MPI
  * regardless whether it is needed or not.
  */
-static gpg_error_t
+static gcry_error_t
 sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
            const char *buffer, size_t length, int argflag,
            va_list arg_ptr, void **arg_list)
 {
-  static const char tokenchars[] = "\
-abcdefghijklmnopqrstuvwxyz\
-ABCDEFGHIJKLMNOPQRSTUVWXYZ\
-0123456789-./_:*+=";
+  gcry_err_code_t err = 0;
+  static const char tokenchars[] =
+    "abcdefghijklmnopqrstuvwxyz"
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "0123456789-./_:*+=";
   const char *p;
   size_t n;
   const char *digptr = NULL;
@@ -857,341 +942,505 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ\
   size_t dummy_erroff;
   struct make_space_ctx c;
   int arg_counter = 0;
+  int level = 0;
 
-  if (! erroff)
+  if (!erroff)
     erroff = &dummy_erroff;
 
   /* Depending on wether ARG_LIST is non-zero or not, this macro gives
      us the next argument, either from the variable argument list as
-     specified by ARG_PTR or from the arugment array ARG_LIST.  */
+     specified by ARG_PTR or from the argument array ARG_LIST.  */
 #define ARG_NEXT(storage, type)                          \
   do                                                     \
     {                                                    \
-      if (! arg_list)                                    \
+      if (!arg_list)                                    \
        storage = va_arg (arg_ptr, type);                \
       else                                               \
        storage = *((type *) (arg_list[arg_counter++])); \
     }                                                    \
   while (0)
 
-  /* FIXME: replace all the returns by a jump to the leave label
-   * and invent better error codes. Make sure that everything is cleaned up*/
-#define MAKE_SPACE(n)  do { make_space ( &c, (n) ); } while (0)
+  /* The MAKE_SPACE macro is used before each store operation to
+     ensure that the buffer is large enough.  It requires a global
+     context named C and jumps out to the label LEAVE on error! It
+     also sets ERROFF using the variables BUFFER and P.  */
+#define MAKE_SPACE(n)  do {                                                \
+                            gpg_err_code_t _ms_err = make_space (&c, (n)); \
+                            if (_ms_err)                                   \
+                              {                                            \
+                                err = _ms_err;                             \
+                                *erroff = p - buffer;                      \
+                                goto leave;                                \
+                              }                                            \
+                       } while (0)
+
+  /* The STORE_LEN macro is used to store the length N at buffer P. */
 #define STORE_LEN(p,n) do {                                               \
                            DATALEN ashort = (n);                          \
                            memcpy ( (p), &ashort, sizeof(ashort) );       \
                            (p) += sizeof (ashort);                        \
                        } while (0)
 
-  /* We assume that the internal representation takes less memory
-   * than the provided one.  However, we add space for one extra datalen
-   * so that the code which does the ST_CLOSE can use MAKE_SPACE */
+  /* We assume that the internal representation takes less memory than
+     the provided one.  However, we add space for one extra datalen so
+     that the code which does the ST_CLOSE can use MAKE_SPACE */
   c.allocated = length + sizeof(DATALEN);
-  c.sexp = gcry_xmalloc ( sizeof *c.sexp + c.allocated - 1 );
+  if (buffer && length && gcry_is_secure (buffer))
+    c.sexp = gcry_malloc_secure (sizeof *c.sexp + c.allocated - 1);
+  else
+    c.sexp = gcry_malloc (sizeof *c.sexp + c.allocated - 1);
+  if (!c.sexp)
+    {
+      err = gpg_err_code_from_errno (errno);
+      *erroff = 0;
+      goto leave;
+    }
   c.pos = c.sexp->d;
 
-  for(p=buffer,n=length; n; p++, n-- ) {
-    if( tokenp && !hexfmt ) {
-      if( strchr( tokenchars, *p ) )
-       continue;
-      datalen = p - tokenp;
-      MAKE_SPACE ( datalen );
-      *c.pos++ = ST_DATA;
-      STORE_LEN ( c.pos, datalen );
-      memcpy ( c.pos, tokenp, datalen );
-      c.pos += datalen;
-      tokenp = NULL;
-    }
-    if( quoted ) {
-      if( quoted_esc ) {
-       switch( *p ) {
-       case 'b': case 't': case 'v': case 'n': case 'f':
-       case 'r': case '"': case '\'': case '\\':
-         quoted_esc = 0;
-         break;
-       case '0': case '1': case '2': case '3': case '4':
-       case '5': case '6': case '7':
-         if( !(n > 2 && p[1] >= '0' && p[1] <= '7'
-               && p[2] >= '0' && p[2] <= '7') ) {
-           *erroff = p - buffer;
-           /* Invalid octal value.  */
-           return gpg_error (GPG_ERR_SEXP_BAD_QUOTATION);
-         }
-         p += 2; n -= 2;
-         quoted_esc = 0;
-         break;
-       case 'x':
-         if( !(n > 2 && isxdigit(p[1]) && isxdigit(p[2]) ) ) {
-           *erroff = p - buffer;
-           /* Invalid hex value.  */
-           return gpg_error (GPG_ERR_SEXP_BAD_QUOTATION);
-         }
-         p += 2; n -= 2;
-         quoted_esc = 0;
-         break;
-       case '\r':  /* ignore CR[,LF] */
-         if( n && p[1] == '\n' ) {
-           p++; n--;
-         }
-         quoted_esc = 0;
-         break;
-       case '\n':  /* ignore LF[,CR] */
-         if( n && p[1] == '\r' ) {
-           p++; n--;
-         }
-         quoted_esc = 0;
-         break;
-       default:
-         *erroff = p - buffer;
-         /* Invalid quoted string escape.  */
-         return gpg_error (GPG_ERR_SEXP_BAD_QUOTATION);
+  for (p = buffer, n = length; n; p++, n--)
+    {
+      if (tokenp && !hexfmt)
+       {
+         if (strchr (tokenchars, *p))
+           continue;
+         else
+           {
+             datalen = p - tokenp;
+             MAKE_SPACE (datalen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, datalen);
+             memcpy (c.pos, tokenp, datalen);
+             c.pos += datalen;
+             tokenp = NULL;
+           }
        }
-      }
-      else if( *p == '\\' )
-       quoted_esc = 1;
-      else if( *p == '\"' ) {
-       /* keep it easy - we know that the unquoted string will
-          never be larger */
-       char *save;
-       size_t len;
-                
-       quoted++; /* skip leading quote */
-       MAKE_SPACE (p - quoted);
-       *c.pos++ = ST_DATA;
-       save = c.pos;
-       STORE_LEN (c.pos, 0); /* will be fixed up later */
-       len = unquote_string (quoted, p - quoted, c.pos);
-       c.pos += len;
-       STORE_LEN (save, len);
-       quoted = NULL;
-      }
-    }
-    else if( hexfmt ) {
-      if( isxdigit( *p ) )
-       hexcount++;
-      else if( *p == '#' ) {
-       if( (hexcount & 1) ) {
-         *erroff = p - buffer;
-         return gpg_error (GPG_ERR_SEXP_ODD_HEX_NUMBERS);
+
+      if (quoted)
+       {
+         if (quoted_esc)
+           {
+             switch (*p)
+               {
+               case 'b': case 't': case 'v': case 'n': case 'f':
+               case 'r': case '"': case '\'': case '\\':
+                 quoted_esc = 0;
+                 break;
+
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7':
+                 if (!((n > 2)
+                        && (p[1] >= '0') && (p[1] <= '7')
+                        && (p[2] >= '0') && (p[2] <= '7')))
+                   {
+                     *erroff = p - buffer;
+                     /* Invalid octal value.  */
+                     err = GPG_ERR_SEXP_BAD_QUOTATION;
+                      goto leave;
+                   }
+                 p += 2;
+                 n -= 2;
+                 quoted_esc = 0;
+                 break;
+                 
+               case 'x':
+                 if (!((n > 2) && hexdigitp (p+1) && hexdigitp (p+2)))
+                   {
+                     *erroff = p - buffer;
+                     /* Invalid hex value.  */
+                     err = GPG_ERR_SEXP_BAD_QUOTATION;
+                      goto leave;
+                   }
+                 p += 2;
+                 n -= 2;
+                 quoted_esc = 0;
+                 break;
+
+               case '\r':
+                 /* ignore CR[,LF] */
+                 if (n && (p[1] == '\n'))
+                   {
+                     p++;
+                     n--;
+                   }
+                 quoted_esc = 0;
+                 break;
+
+               case '\n':
+                 /* ignore LF[,CR] */
+                 if (n && (p[1] == '\r'))
+                   {
+                     p++;
+                     n--;
+                   }
+                 quoted_esc = 0;
+                 break;
+
+               default:
+                 *erroff = p - buffer;
+                 /* Invalid quoted string escape.  */
+                 err = GPG_ERR_SEXP_BAD_QUOTATION;
+                  goto leave;
+               }
+           }
+         else if (*p == '\\')
+           quoted_esc = 1;
+         else if (*p == '\"')
+           {
+             /* Keep it easy - we know that the unquoted string will
+                never be larger. */
+             char *save;
+             size_t len;
+             
+             quoted++; /* Skip leading quote.  */
+             MAKE_SPACE (p - quoted);
+             *c.pos++ = ST_DATA;
+             save = c.pos;
+             STORE_LEN (c.pos, 0); /* Will be fixed up later.  */
+             len = unquote_string (quoted, p - quoted, c.pos);
+             c.pos += len;
+             STORE_LEN (save, len);
+             quoted = NULL;
+           }
+       }
+      else if (hexfmt)
+       {
+         if (isxdigit (*p))
+           hexcount++;
+         else if (*p == '#')
+           {
+             if ((hexcount & 1))
+               {
+                 *erroff = p - buffer;
+                 err = GPG_ERR_SEXP_ODD_HEX_NUMBERS;
+                  goto leave;
+               }
+
+             datalen = hexcount / 2;
+             MAKE_SPACE (datalen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, datalen);
+             for (hexfmt++; hexfmt < p; hexfmt++)
+               {
+                 if (whitespacep (hexfmt))
+                   continue;
+                 *c.pos++ = hextobyte (hexfmt);
+                 hexfmt++;
+               }
+             hexfmt = NULL;
+           }
+         else if (!whitespacep (p))
+           {
+             *erroff = p - buffer;
+             err = GPG_ERR_SEXP_BAD_HEX_CHAR;
+              goto leave;
+           }
+       }
+      else if (base64)
+       {
+         if (*p == '|')
+           base64 = NULL;
        }
+      else if (digptr)
+       {
+         if (digitp (p))
+           ;
+         else if (*p == ':')
+           {
+             datalen = atoi (digptr); /* FIXME: check for overflow.  */
+             digptr = NULL;
+             if (datalen > n - 1)
+               {
+                 *erroff = p - buffer;
+                 /* Buffer too short.  */
+                 err = GPG_ERR_SEXP_STRING_TOO_LONG;
+                  goto leave;
+               }
+             /* Make a new list entry.  */
+             MAKE_SPACE (datalen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, datalen);
+             memcpy (c.pos, p + 1, datalen);
+             c.pos += datalen;
+             n -= datalen;
+             p += datalen;
+           }
+         else if (*p == '\"')
+           {
+             digptr = NULL; /* We ignore the optional length.  */
+             quoted = p;
+             quoted_esc = 0;
+           }
+         else if (*p == '#')
+           {
+             digptr = NULL; /* We ignore the optional length.  */
+             hexfmt = p;
+             hexcount = 0;
+           }
+         else if (*p == '|')
+           {
+             digptr = NULL; /* We ignore the optional length.  */
+             base64 = p;
+           }
+         else
+           {
+             *erroff = p - buffer;
+             err = GPG_ERR_SEXP_INV_LEN_SPEC;
+              goto leave;
+           }
+       }
+      else if (percent)
+       {
+         if (*p == 'm')
+           {
+             /* Insert an MPI.  */
+             gcry_mpi_t m;
+             size_t nm = 0;
+
+             ARG_NEXT (m, gcry_mpi_t);
+             
+             if (gcry_mpi_print (GCRYMPI_FMT_STD, NULL, 0, &nm, m))
+               BUG ();
 
-       datalen = hexcount/2;
-       MAKE_SPACE (datalen);
-       *c.pos++ = ST_DATA;
-       STORE_LEN (c.pos, datalen);
-       for( hexfmt++; hexfmt < p; hexfmt++ ) {
-         if( isspace( *hexfmt ) )
-           continue;
-         *c.pos++ = hextobyte( hexfmt );
-         hexfmt++;
+             MAKE_SPACE (nm);
+             if (!gcry_is_secure (c.sexp->d)
+                 && gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE))
+               {
+                 /* We have to switch to secure allocation.  */
+                 gcry_sexp_t newsexp;
+                 byte *newhead;
+
+                 newsexp = gcry_malloc_secure (sizeof *newsexp
+                                                + c.allocated - 1);
+                  if (!newsexp)
+                    {
+                      err = gpg_err_code_from_errno (errno);
+                      goto leave;
+                    }
+                 newhead = newsexp->d;
+                 memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
+                 c.pos = newhead + (c.pos - c.sexp->d);
+                 gcry_free (c.sexp);
+                 c.sexp = newsexp;
+               }
+
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, nm);
+             if (gcry_mpi_print (GCRYMPI_FMT_STD, c.pos, nm, &nm, m))
+               BUG ();
+             c.pos += nm;
+           }
+         else if (*p == 's')
+           {
+             /* Insert an string.  */
+             const char *astr;
+             size_t alen;
+
+             ARG_NEXT (astr, const char *);
+             alen = strlen (astr);
+             
+             MAKE_SPACE (alen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, astr, alen);
+             c.pos += alen;
+           }
+         else if (*p == 'b')
+           {
+             /* Insert a memory buffer.  */
+             const char *astr;
+             int alen;
+
+             ARG_NEXT (alen, int);
+             ARG_NEXT (astr, const char *);
+             
+             MAKE_SPACE (alen);
+             if (alen
+                  && !gcry_is_secure (c.sexp->d)
+                 && gcry_is_secure (astr))
+              {
+                 /* We have to switch to secure allocation.  */
+                 gcry_sexp_t newsexp;
+                 byte *newhead;
+
+                 newsexp = gcry_malloc_secure (sizeof *newsexp
+                                                + c.allocated - 1);
+                  if (!newsexp)
+                    {
+                      err = gpg_err_code_from_errno (errno);
+                      goto leave;
+                    }
+                 newhead = newsexp->d;
+                 memcpy (newhead, c.sexp->d, (c.pos - c.sexp->d));
+                 c.pos = newhead + (c.pos - c.sexp->d);
+                 gcry_free (c.sexp);
+                 c.sexp = newsexp;
+               }
+
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, astr, alen);
+             c.pos += alen;
+           }
+         else if (*p == 'd')
+           {
+             /* Insert an integer as string.  */
+             int aint;
+             size_t alen;
+             char buf[20];
+             
+             ARG_NEXT (aint, int);
+             sprintf (buf, "%d", aint);
+             alen = strlen (buf);
+             MAKE_SPACE (alen);
+             *c.pos++ = ST_DATA;
+             STORE_LEN (c.pos, alen);
+             memcpy (c.pos, buf, alen);
+             c.pos += alen;
+           }
+         else
+           {
+             *erroff = p - buffer;
+             /* Invalid format specifier.  */
+             err = GPG_ERR_SEXP_INV_LEN_SPEC;
+              goto leave;
+           }
+         percent = NULL;
        }
-       hexfmt = NULL;
-      }
-      else if( !isspace( *p ) ) {
-       *erroff = p - buffer;
-       return gpg_error (GPG_ERR_SEXP_BAD_HEX_CHAR);
-      }
-    }
-    else if( base64 ) {
-      if( *p == '|' )
-       base64 = NULL;
-    }
-    else if( digptr ) {
-      if( isdigit(*p) )
+      else if (*p == '(')
+       {
+         if (disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_UNMATCHED_DH;
+              goto leave;
+           }
+         MAKE_SPACE (0);
+         *c.pos++ = ST_OPEN;
+         level++;
+       }
+      else if (*p == ')')
+       {
+         /* Walk up.  */
+         if (disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_UNMATCHED_DH;
+              goto leave;
+           }
+         MAKE_SPACE (0);
+         *c.pos++ = ST_CLOSE;
+         level--;
+       }
+      else if (*p == '\"')
+       {
+         quoted = p;
+         quoted_esc = 0;
+       }
+      else if (*p == '#')
+       {
+         hexfmt = p;
+         hexcount = 0;
+       }
+      else if (*p == '|')
+       base64 = p;
+      else if (*p == '[')
+       {
+         if (disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_NESTED_DH;
+              goto leave;
+           }
+         disphint = p;
+       }
+      else if (*p == ']')
+       {
+         if (!disphint)
+           {
+             *erroff = p - buffer;
+             /* Open display hint.  */
+             err = GPG_ERR_SEXP_UNMATCHED_DH;
+              goto leave;
+           }
+         disphint = NULL;
+       }
+      else if (digitp (p))
+       {
+         if (*p == '0')
+           {
+             /* A length may not begin with zero.  */
+             *erroff = p - buffer;
+             err = GPG_ERR_SEXP_ZERO_PREFIX;
+              goto leave;
+           }
+         digptr = p;
+       }
+      else if (strchr (tokenchars, *p))
+       tokenp = p;
+      else if (whitespacep (p))
        ;
-      else if( *p == ':' ) {
-       datalen = atoi( digptr ); /* fixme: check for overflow */
-       digptr = NULL;
-       if( datalen > n-1 ) {
+      else if (*p == '{')
+       {
+         /* fixme: handle rescanning: we can do this by saving our
+            current state and start over at p+1 -- Hmmm. At this
+            point here we are in a well defined state, so we don't
+            need to save it.  Great.  */
          *erroff = p - buffer;
-         /* Buffer too short.  */
-         return gpg_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+         err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
+          goto leave;
        }
-       /* make a new list entry */
-       MAKE_SPACE (datalen);
-       *c.pos++ = ST_DATA;
-       STORE_LEN (c.pos, datalen);
-       memcpy (c.pos, p+1, datalen );
-       c.pos += datalen;
-       n -= datalen;
-       p += datalen;
-      }
-      else if( *p == '\"' ) {
-       digptr = NULL; /* we ignore the optional length */
-       quoted = p;
-       quoted_esc = 0;
-      }
-      else if( *p == '#' ) {
-       digptr = NULL; /* we ignore the optional length */
-       hexfmt = p;
-       hexcount = 0;
-      }
-      else if( *p == '|' ) {
-       digptr = NULL; /* we ignore the optional length */
-       base64 = p;
-      }
-      else {
-       *erroff = p - buffer;
-       return gpg_error (GPG_ERR_SEXP_INV_LEN_SPEC);
-      }
-    }
-    else if ( percent ) {
-      if ( *p == 'm' ) { /* insert an MPI */
-       gcry_mpi_t m;
-       size_t nm = 0;
-
-       ARG_NEXT (m, gcry_mpi_t);
-
-       if ( gcry_mpi_print( GCRYMPI_FMT_STD, NULL, &nm, m ) )
-         BUG ();
-
-       MAKE_SPACE (nm);
-       if ( !gcry_is_secure ( c.sexp->d )
-            &&  gcry_mpi_get_flag ( m, GCRYMPI_FLAG_SECURE ) ) {
-         /* we have to switch to secure allocation */
-         gcry_sexp_t newsexp;
-         byte *newhead;
-
-         newsexp = gcry_xmalloc_secure ( sizeof *newsexp
-                                         + c.allocated - 1 );
-         newhead = newsexp->d;
-         memcpy ( newhead, c.sexp->d, (c.pos - c.sexp->d) );
-         c.pos = newhead + ( c.pos - c.sexp->d );
-         gcry_free ( c.sexp );
-         c.sexp = newsexp;
+      else if (strchr ("&\\", *p))
+       {
+         /* Reserved punctuation.  */
+         *erroff = p - buffer;
+         err = GPG_ERR_SEXP_UNEXPECTED_PUNC;
+          goto leave;
+       }
+      else if (argflag && (*p == '%'))
+       percent = p;
+      else
+       {
+         /* Bad or unavailable.  */
+         *erroff = p - buffer;
+         err = GPG_ERR_SEXP_BAD_CHARACTER;
+          goto leave;
        }
-
-       *c.pos++ = ST_DATA;
-       STORE_LEN (c.pos, nm);
-       if ( gcry_mpi_print( GCRYMPI_FMT_STD, c.pos, &nm, m ) )
-         BUG ();
-       c.pos += nm;
-      }
-      else if ( *p == 's' ) { /* insert an string */
-       const char *astr;
-       size_t alen;
-
-       ARG_NEXT (astr, const char *);
-       alen = strlen (astr);
-
-       MAKE_SPACE (alen);
-       *c.pos++ = ST_DATA;
-       STORE_LEN (c.pos, alen);
-       memcpy ( c.pos, astr, alen );
-       c.pos += alen;
-      }
-      else if ( *p == 'd' ) { /* insert an integer as string */
-       int aint;
-       size_t alen;
-       char buf[20];
-
-       ARG_NEXT (aint, int);
-       sprintf ( buf, "%d", aint );
-       alen = strlen ( buf );
-       MAKE_SPACE (alen);
-       *c.pos++ = ST_DATA;
-       STORE_LEN (c.pos, alen);
-       memcpy ( c.pos, buf, alen );
-       c.pos += alen;
-      }
-      else {
-       *erroff = p - buffer;
-       /* Invalid format specifier.  */
-       return gpg_error (GPG_ERR_SEXP_INV_LEN_SPEC);
-      }
-      percent = NULL;
-    }
-    else if( *p == '(' ) {
-      if( disphint ) {
-       *erroff = p - buffer;
-       /* Open display hint.  */
-       return gpg_error (GPG_ERR_SEXP_UNMATCHED_DH);
-      }
-      MAKE_SPACE (0);
-      *c.pos++ = ST_OPEN;
-    }
-    else if( *p == ')' ) { /* walk up */
-      if( disphint ) {
-       *erroff = p - buffer;
-       /* Open display hint.  */
-       return gpg_error (GPG_ERR_SEXP_UNMATCHED_DH);
-      }
-      MAKE_SPACE (0);
-      *c.pos++ = ST_CLOSE;
-    }
-    else if( *p == '\"' ) {
-      quoted = p;
-      quoted_esc = 0;
-    }
-    else if( *p == '#' ) {
-      hexfmt = p;
-      hexcount = 0;
-    }
-    else if( *p == '|' )
-      base64 = p;
-    else if( *p == '[' ) {
-      if( disphint ) {
-       *erroff = p - buffer;
-       /* Open display hint.  */
-       return gpg_error (GPG_ERR_SEXP_NESTED_DH);
-      }
-      disphint = p;
-    }
-    else if( *p == ']' ) {
-      if( !disphint ) {
-       *erroff = p - buffer;
-       /* Open display hint.  */
-       return gpg_error (GPG_ERR_SEXP_UNMATCHED_DH);
-      }
-      disphint = NULL;
-    }
-    else if( isdigit(*p) ) {
-      if( *p == '0' ) { /* a length may not begin with zero */
-       *erroff = p - buffer;
-       return gpg_error (GPG_ERR_SEXP_ZERO_PREFIX);
-      }
-      digptr = p;
-    }
-    else if( strchr( tokenchars, *p ) )
-      tokenp = p;
-    else if( isspace(*p) )
-      ;
-    else if( *p == '{' ) {
-      /* fixme: handle rescanning:
-       * we can do this by saving our current state
-       * and start over at p+1 -- Hmmm. At this point here
-       * we are in a well defined state, so we don't need to save
-       * it.  Great.
-       */
-      *erroff = p - buffer;
-      return gpg_error (GPG_ERR_SEXP_UNEXPECTED_PUNC);
-    }
-    else if( strchr( "&\\", *p ) ) { /*reserved punctuation*/
-      *erroff = p - buffer;
-      return gpg_error (GPG_ERR_SEXP_UNEXPECTED_PUNC);
-    }
-    else if( argflag && *p == '%' ) {
-      percent = p;
-    }
-    else { /* bad or unavailable*/
-      *erroff = p - buffer;
-      return gpg_error (GPG_ERR_SEXP_BAD_CHARACTER);
     }
-
-  }
   MAKE_SPACE (0);
   *c.pos++ = ST_STOP;
 
-  *retsexp = normalize ( c.sexp );
-  return gpg_error (GPG_ERR_NO_ERROR);
+  if (level && !err)
+    err = GPG_ERR_SEXP_UNMATCHED_PAREN;
+
+ leave:
+  if (err)
+    {
+      /* Error -> deallocate.  */
+      if (c.sexp)
+        {
+          /* Extra paranoid wipe on error. */
+          if (gcry_is_secure (c.sexp))
+            wipememory (c.sexp, sizeof (struct gcry_sexp) + c.allocated - 1);
+          gcry_free (c.sexp);
+        }
+      /* This might be expected by existing code...  */
+      *retsexp = NULL;
+    }
+  else
+    *retsexp = normalize (c.sexp);
+
+  return gcry_error (err);
 #undef MAKE_SPACE
 #undef STORE_LEN
 }
 
-gpg_error_t
+gcry_error_t
 gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
 {
-  gpg_error_t rc;
+  gcry_error_t rc;
   va_list arg_ptr;
   
   va_start (arg_ptr, format);
@@ -1204,20 +1453,25 @@ gcry_sexp_build (gcry_sexp_t *retsexp, size_t *erroff, const char *format, ...)
 
 /* Like gcry_sexp_build, but uses an array instead of variable
    function arguments.  */
-gpg_error_t
+gcry_error_t
 gcry_sexp_build_array (gcry_sexp_t *retsexp, size_t *erroff,
                       const char *format, void **arg_list)
 {
+  /* We don't need the va_list because it is controlled by the
+     following flag, however we have to pass it but can't initialize
+     it as there is no portable way to do so.  volatile is needed to
+     suppress the compiler warning */
+  volatile va_list dummy_arg_ptr;
   
-  gpg_error_t rc;
+  gcry_error_t rc;
 
   rc = sexp_sscan (retsexp, erroff, format, strlen(format), 1,
-                  NULL, arg_list);
+                  dummy_arg_ptr, arg_list);
 
   return rc;
 }
 
-gpg_error_t
+gcry_error_t
 gcry_sexp_sscan (gcry_sexp_t *retsexp, size_t *erroff,
                 const char *buffer, size_t length)
 {
@@ -1507,13 +1761,13 @@ gcry_sexp_sprint( const gcry_sexp_t list, int mode,
    NULL.  */
 size_t
 gcry_sexp_canon_len (const unsigned char *buffer, size_t length, 
-                     size_t *erroff, gpg_error_t *errcode)
+                     size_t *erroff, gcry_error_t *errcode)
 {
   const unsigned char *p;
   const char *disphint=NULL;
   unsigned int datalen = 0;
   size_t dummy_erroff;
-  gpg_error_t dummy_errcode;
+  gcry_error_t dummy_errcode;
   size_t count = 0;
   int level = 0;
 
@@ -1522,13 +1776,13 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
   if (!errcode)
     errcode = &dummy_errcode;
 
-  *errcode = gpg_error (GPG_ERR_NO_ERROR);
+  *errcode = gcry_error (GPG_ERR_NO_ERROR);
   *erroff = 0;
   if (!buffer)
     return 0;
   if (*buffer != '(')
     {
-      *errcode = gpg_error (GPG_ERR_SEXP_NOT_CANONICAL);
+      *errcode = gcry_error (GPG_ERR_SEXP_NOT_CANONICAL);
       return 0;
     }
 
@@ -1537,7 +1791,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
       if (length && count >= length)
         {
           *erroff = count;
-          *errcode = gpg_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+          *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
           return 0;
         }
       
@@ -1548,7 +1802,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
               if (length && (count+datalen) >= length)
                 {
                   *erroff = count;
-                  *errcode = gpg_error (GPG_ERR_SEXP_STRING_TOO_LONG);
+                  *errcode = gcry_error (GPG_ERR_SEXP_STRING_TOO_LONG);
                   return 0;
                 }
               count += datalen;
@@ -1560,7 +1814,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
           else 
             {
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_INV_LEN_SPEC);
+              *errcode = gcry_error (GPG_ERR_SEXP_INV_LEN_SPEC);
               return 0;
            }
        }
@@ -1569,7 +1823,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
           if (disphint)
             {
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_UNMATCHED_DH);
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
               return 0;
            }
           level++;
@@ -1579,13 +1833,13 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
           if (!level)
             {
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_UNMATCHED_PAREN);
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_PAREN);
               return 0;
            }
           if (disphint)
             {
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_UNMATCHED_DH);
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
               return 0;
            }
           if (!--level)
@@ -1596,7 +1850,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
           if (disphint) 
             {
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_NESTED_DH);
+              *errcode = gcry_error (GPG_ERR_SEXP_NESTED_DH);
               return 0;
             }
           disphint = p;
@@ -1606,7 +1860,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
           if( !disphint ) 
             {
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_UNMATCHED_DH);
+              *errcode = gcry_error (GPG_ERR_SEXP_UNMATCHED_DH);
               return 0;
            }
           disphint = NULL;
@@ -1616,7 +1870,7 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
           if (*p == '0')
             { 
               *erroff = count;
-              *errcode = gpg_error (GPG_ERR_SEXP_ZERO_PREFIX);
+              *errcode = gcry_error (GPG_ERR_SEXP_ZERO_PREFIX);
               return 0;
            }
           datalen = atoi_1 (p);
@@ -1624,13 +1878,13 @@ gcry_sexp_canon_len (const unsigned char *buffer, size_t length,
       else if (*p == '&' || *p == '\\')
         {
           *erroff = count;
-          *errcode = gpg_error (GPG_ERR_SEXP_UNEXPECTED_PUNC);
+          *errcode = gcry_error (GPG_ERR_SEXP_UNEXPECTED_PUNC);
           return 0;
        }
       else
         { 
           *erroff = count;
-          *errcode = gpg_error (GPG_ERR_SEXP_BAD_CHARACTER);
+          *errcode = gcry_error (GPG_ERR_SEXP_BAD_CHARACTER);
           return 0;
        }
     }