Browse Source

Properly parse the s-expressions.

* src/agent.c (pksign_parse_result): New function.
(scute_agent_sign): Use the new function to parse the s-expression.
* src/sexp-parse.h: New file copied from GnuPG.

Signed-off-by: Justus Winter <justus@g10code.com>
Justus Winter 9 years ago
parent
commit
055ae9e741
2 changed files with 201 additions and 35 deletions
  1. 64 35
      src/agent.c
  2. 137 0
      src/sexp-parse.h

+ 64 - 35
src/agent.c

@@ -1,5 +1,5 @@
 /* agent.c - Talking to gpg-agent.
 /* agent.c - Talking to gpg-agent.
-   Copyright (C) 2006, 2007, 2008 g10 Code GmbH
+   Copyright (C) 2006, 2007, 2008, 2015 g10 Code GmbH
 
 
    This file is part of Scute.
    This file is part of Scute.
  
  
@@ -51,6 +51,7 @@
 
 
 #include "debug.h"
 #include "debug.h"
 #include "support.h"
 #include "support.h"
+#include "sexp-parse.h"
 #include "cert.h"
 #include "cert.h"
 #include "agent.h"
 #include "agent.h"
 
 
@@ -980,14 +981,66 @@ pksign_cb (void *opaque, const void *buffer, size_t length)
   return 0;
   return 0;
 }
 }
 
 
+/* Parse the result of an pksign operation which is a s-expression in
+   normal form that looks like (7:sig-val(3:rsa(1:s<LENGTH>:<DATA>))).
+   The raw result is stored in RESULT of size *LEN, and *LEN is
+   adjusted to the actual size.  */
+static gpg_error_t
+pksign_parse_result (const struct signature *sig,
+                     unsigned char *result, unsigned int *len)
+{
+  gpg_error_t err;
+  const unsigned char *s = sig->data;
+  size_t n;
+  int depth;
+
+  if (*s++ != '(')
+    gpg_error (GPG_ERR_INV_SEXP);
+
+  n = snext (&s);
+  if (! n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  if (! smatch (&s, n, "sig-val"))
+    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+
+  if (*s++ != '(')
+    gpg_error (GPG_ERR_UNKNOWN_SEXP);
+
+  n = snext (&s);
+  if (! n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  if (! smatch (&s, n, "rsa"))
+    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+
+  if (*s++ != '(')
+    gpg_error (GPG_ERR_UNKNOWN_SEXP);
+
+  n = snext (&s);
+  if (! n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  if (! smatch (&s, n, "s"))
+    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
+
+  n = snext (&s);
+  if (! n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+
+  if (*len < (unsigned int) n)
+    return gpg_error (GPG_ERR_INV_LENGTH);
+
+  *len = (unsigned int) n;
+  memcpy (result, s, n);
+  s += n;
+
+  depth = 3;
+  err = sskip (&s, &depth);
+  if (err)
+    return err;
+  if (s - sig->data != sig->len || depth != 0)
+    return gpg_error (GPG_ERR_INV_SEXP);
 
 
-#define SIG_PREFIX   "(7:sig-val(3:rsa(1:s128:"
-#define SIG_PREFIX_2 "(7:sig-val(3:rsa(1:s256:"
-#define SIG_PREFIX_LEN (sizeof (SIG_PREFIX) - 1)
-#define SIG_POSTFIX ")))"
-#define SIG_POSTFIX_LEN (sizeof (SIG_POSTFIX) - 1)
-#define SIG_LEN 128
-#define SIG_LEN_2 256
+  return 0;
+}
 
 
 /* Call the agent to learn about a smartcard.  */
 /* Call the agent to learn about a smartcard.  */
 gpg_error_t
 gpg_error_t
@@ -1016,7 +1069,7 @@ scute_agent_sign (char *grip, unsigned char *data, int len,
   if (len > MAX_DATA_LEN)
   if (len > MAX_DATA_LEN)
     return gpg_error (GPG_ERR_INV_ARG);
     return gpg_error (GPG_ERR_INV_ARG);
 
 
-  if (grip == NULL || sig_result == NULL || *sig_len < SIG_LEN)
+  if (grip == NULL || sig_result == NULL)
     return gpg_error (GPG_ERR_INV_ARG);
     return gpg_error (GPG_ERR_INV_ARG);
 
 
   snprintf (cmd, sizeof (cmd), "SIGKEY %s", grip);
   snprintf (cmd, sizeof (cmd), "SIGKEY %s", grip);
@@ -1040,32 +1093,8 @@ scute_agent_sign (char *grip, unsigned char *data, int len,
   if (err)
   if (err)
     return err;
     return err;
 
 
-  /* FIXME: we need a real parser to cope with all kind of S-expressions.  */
-  if (sig.len == SIG_PREFIX_LEN + SIG_LEN_2 + SIG_POSTFIX_LEN)
-    {
-      if (memcmp (sig.data, SIG_PREFIX_2, SIG_PREFIX_LEN))
-        return gpg_error (GPG_ERR_BAD_SIGNATURE);
-      if (memcmp (sig.data + sig.len - SIG_POSTFIX_LEN,
-                  SIG_POSTFIX, SIG_POSTFIX_LEN))
-        return gpg_error (GPG_ERR_BAD_SIGNATURE);
-      memcpy (sig_result, sig.data + SIG_PREFIX_LEN, SIG_LEN_2);
-      *sig_len = SIG_LEN_2;
-    }
-  else
-    {
-      if (sig.len != SIG_PREFIX_LEN + SIG_LEN + SIG_POSTFIX_LEN)
-        return gpg_error (GPG_ERR_BAD_SIGNATURE);
-      if (memcmp (sig.data, SIG_PREFIX, SIG_PREFIX_LEN))
-        return gpg_error (GPG_ERR_BAD_SIGNATURE);
-      if (memcmp (sig.data + sig.len - SIG_POSTFIX_LEN,
-                  SIG_POSTFIX, SIG_POSTFIX_LEN))
-        return gpg_error (GPG_ERR_BAD_SIGNATURE);
-      memcpy (sig_result, sig.data + SIG_PREFIX_LEN, SIG_LEN);
-      *sig_len = SIG_LEN;
-    }
-  
-  
-  return 0;
+  err = pksign_parse_result (&sig, sig_result, sig_len);
+  return err;
 }
 }
 
 
 
 

+ 137 - 0
src/sexp-parse.h

@@ -0,0 +1,137 @@
+/* sexp-parse.h - S-expression helper functions
+ * Copyright (C) 2002, 2003, 2007 Free Software Foundation, Inc.
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
+ *
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SEXP_PARSE_H
+#define SEXP_PARSE_H
+
+#include <gpg-error.h>
+
+
+/* Return the length of the next S-Exp part and update the pointer to
+   the first data byte.  0 is returned on error */
+static inline size_t
+snext (unsigned char const **buf)
+{
+  const unsigned char *s;
+  int n;
+
+  s = *buf;
+  for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++)
+    n = n*10 + (*s - '0');
+  if (!n || *s != ':')
+    return 0; /* we don't allow empty lengths */
+  *buf = s+1;
+  return n;
+}
+
+/* Skip over the S-Expression BUF points to and update BUF to point to
+   the chacter right behind.  DEPTH gives the initial number of open
+   lists and may be passed as a positive number to skip over the
+   remainder of an S-Expression if the current position is somewhere
+   in an S-Expression.  The function may return an error code if it
+   encounters an impossible condition.  */
+static inline gpg_error_t
+sskip (unsigned char const **buf, int *depth)
+{
+  const unsigned char *s = *buf;
+  size_t n;
+  int d = *depth;
+
+  while (d > 0)
+    {
+      if (*s == '(')
+        {
+          d++;
+          s++;
+        }
+      else if (*s == ')')
+        {
+          d--;
+          s++;
+        }
+      else
+        {
+          if (!d)
+            return gpg_error (GPG_ERR_INV_SEXP);
+          n = snext (&s);
+          if (!n)
+            return gpg_error (GPG_ERR_INV_SEXP);
+          s += n;
+        }
+    }
+  *buf = s;
+  *depth = d;
+  return 0;
+}
+
+
+/* Check whether the the string at the address BUF points to matches
+   the token.  Return true on match and update BUF to point behind the
+   token.  Return false and do not update the buffer if it does not
+   match. */
+static inline int
+smatch (unsigned char const **buf, size_t buflen, const char *token)
+{
+  size_t toklen = strlen (token);
+
+  if (buflen != toklen || memcmp (*buf, token, toklen))
+    return 0;
+  *buf += toklen;
+  return 1;
+}
+
+/* Format VALUE for use as the length indicatior of an S-expression.
+   The caller needs to provide a buffer HELP_BUFFER wth a length of
+   HELP_BUFLEN.  The return value is a pointer into HELP_BUFFER with
+   the formatted length string.  The colon and a trailing nul are
+   appended.  HELP_BUFLEN must be at least 3 - a more useful value is
+   15.  If LENGTH is not NULL, the LENGTH of the resulting string
+   (excluding the terminating nul) is stored at that address. */
+static inline char *
+smklen (char *help_buffer, size_t help_buflen, size_t value, size_t *length)
+{
+  char *p = help_buffer + help_buflen;
+
+  if (help_buflen >= 3)
+    {
+      *--p = 0;
+      *--p = ':';
+      do
+        {
+          *--p = '0' + (value % 10);
+          value /= 10;
+        }
+      while (value && p > help_buffer);
+    }
+
+  if (length)
+    *length = (help_buffer + help_buflen) - p;
+  return p;
+}
+
+
+#endif /*SEXP_PARSE_H*/