gpgsm --verify does now work like gpg including the
authorWerner Koch <wk@gnupg.org>
Mon, 19 Nov 2001 10:25:00 +0000 (10:25 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 19 Nov 2001 10:25:00 +0000 (10:25 +0000)
--enable-special-filenames option.

sm/gpgsm.c
sm/gpgsm.h
sm/import.c
sm/server.c
sm/util.h
sm/verify.c

index 53b8dcd..329e80d 100644 (file)
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
+#include <fcntl.h>
 
 #include <gcrypt.h>
 #include "gpgsm.h"
@@ -75,6 +76,9 @@ enum cmd_and_opt_values {
   aCheckKeys,
   aServer,                        
 
+  oEnableSpecialFilenames,
+
+
   oTextmode,
   oFingerprint,
   oWithFingerprint,
@@ -263,6 +267,10 @@ static ARGPARSE_OPTS opts[] = {
 
   /* hidden options */
     { oNoVerbose, "no-verbose", 0, "@"},
+
+    { oEnableSpecialFilenames, "enable-special-filenames", 0, "@" },
+
+
     { oTrustDBName, "trustdb-name", 2, "@" },
     { oNoSecmemWarn, "no-secmem-warning", 0, "@" }, /* used only by regression tests */
     { oNoArmor, "no-armor",   0, "@"},
@@ -299,13 +307,20 @@ static ARGPARSE_OPTS opts[] = {
 
 int gpgsm_errors_seen = 0;
 
+/* It is possible that we are currentlu running under setuid permissions */
 static int maybe_setuid = 1;
 
+/* Option --enable-special-filenames */
+static int allow_special_filenames;
+
+
 static char *build_list (const char *text,
                         const char *(*mapf)(int), int (*chkf)(int));
 static void set_cmd (enum cmd_and_opt_values *ret_cmd,
                      enum cmd_and_opt_values new_cmd );
 
+static int open_read (const char *filename);
+
 
 static int
 our_pk_test_algo (int algo)
@@ -747,6 +762,8 @@ main ( int argc, char **argv)
 
         case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
         case oNoRandomSeedFile: use_random_seed = 0; break;
+
+        case oEnableSpecialFilenames: allow_special_filenames =1; break;
           
         default: 
           pargs.err = configfp? 1:2; 
@@ -912,9 +929,14 @@ main ( int argc, char **argv)
       break;
 
     case aVerify:
-      gpgsm_verify (0);
-/*        if ((rc = verify_signatures( argc, argv ) )) */
-/*          log_error ("verify signatures failed: %s\n", gpg_errstr(rc) ); */
+      if (!argc)
+        gpgsm_verify (0, -1); /* normal signature from stdin */
+      else if (argc == 1)
+        gpgsm_verify (open_read (*argv), -1); /* normal signature */
+      else if (argc == 2) /* detached signature (sig, detached) */
+        gpgsm_verify (open_read (*argv), open_read (argv[1])); 
+      else
+        wrong_args (_("--verify [signature [detached_data]]"));
       break;
 
     case aVerifyFiles:
@@ -1038,3 +1060,46 @@ gpgsm_exit (int rc)
   rc = rc? rc : log_get_errorcount(0)? 2 : gpgsm_errors_seen? 1 : 0;
   exit (rc);
 }
+
+
+/* Check whether the filename has the form "-&nnnn", where n is a
+   non-zero number.  Returns this number or -1 if it is not the case.  */
+static int
+check_special_filename (const char *fname)
+{
+  if (allow_special_filenames
+      && fname && *fname == '-' && fname[1] == '&' ) {
+    int i;
+    
+    fname += 2;
+    for (i=0; isdigit (fname[i]); i++ )
+      ;
+    if ( !fname[i] ) 
+      return atoi (fname);
+  }
+  return -1;
+}
+
+
+
+/* Open the FILENAME for read and return the fieldescriptor.  Stop
+   with an error message in case of problems.  "-" denotes stdin and
+   if special filenames are allowed the given fd is opend instead. */
+static int 
+open_read (const char *filename)
+{
+  int fd;
+
+  if (filename[0] == '-' && !filename[1])
+    return 0; /* stdin */
+  fd = check_special_filename (filename);
+  if (fd != -1)
+    return fd;
+  fd = open (filename, O_RDONLY);
+  if (fd == -1)
+    {
+      log_error (_("can't open `%s': %s\n"), filename, strerror (errno));
+      gpgsm_exit (2);
+    }
+  return fd;
+}
index d60eb03..9c0c933 100644 (file)
@@ -37,7 +37,10 @@ enum {
   GPGSM_Bad_Certificate = 7,
   GPGSM_Bad_Certificate_Path = 8,
   GPGSM_Missing_Certificate = 9,
-
+  GPGSM_No_Data = 10,
+  GPGSM_Bad_Signature = 11,
+  GPGSM_Not_Implemented = 12,
+  GPGSM_Conflict = 13,
 };
 
 #define MAX_DIGEST_LEN 24 
@@ -124,7 +127,7 @@ int gpgsm_validate_path (KsbaCert cert);
 int gpgsm_import (int in_fd);
 
 /*-- verify.c --*/
-int gpgsm_verify (int in_fd);
+int gpgsm_verify (int in_fd, int data_fd);
 
 
 
index 8dc36c1..8913f80 100644 (file)
 
 struct reader_cb_parm_s {
   FILE *fp;
+  unsigned char line[1024];
+  int linelen;
+  int readpos;
+  int have_lf;
+  unsigned long line_counter;
+
+  int identified;
+  int is_pem;
+  int stop_seen;
+
+  struct {
+    int idx;
+    unsigned char val;
+    int stop_seen;
+  } base64;
+
+
+};
+
+/*  static unsigned char bintoasc[] =  */
+/*  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" */
+/*  "abcdefghijklmnopqrstuvwxyz" */
+/*  "0123456789+/"; */
+
+static unsigned char asctobin[256] = {
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, 
+  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 
+  0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 
+  0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 
+  0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
+  0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
+  0xff, 0xff, 0xff, 0xff
 };
 
 
+
 static int
 reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
 {
   struct reader_cb_parm_s *parm = cb_value;
   size_t n;
-  int c = 0;
+  int c, c2;
 
   *nread = 0;
   if (!buffer)
     return -1; /* not supported */
 
-  for (n=0; n < count; n++)
+ next:
+  if (!parm->linelen)
     {
-      c = getc (parm->fp);
-      if (c == EOF)
+      /* read an entire line or up to the size of the buffer */
+      parm->line_counter++;
+      parm->have_lf = 0;
+      for (n=0; n < DIM(parm->line);)
+        {
+          c = getc (parm->fp);
+          if (c == EOF)
+            {
+              if (ferror (parm->fp))
+                return -1;
+              break; 
+            }
+          parm->line[n++] = c;
+          if (c == '\n')
+            {
+              parm->have_lf = 1;
+              /* FIXME: we need to skip overlong lines while detecting
+                 the dashed lines */
+              break;
+            }
+        }
+      parm->linelen = n;
+      if (!n)
+        return -1; /* eof */
+      parm->readpos = 0;
+    }
+
+  if (!parm->identified)
+    {
+      if (parm->line_counter == 1 && !parm->have_lf)
+        {
+          /* first line too long - assume DER encoding */
+          parm->is_pem = 0;
+        }
+      else if (parm->line_counter == 1 && parm->linelen && *parm->line == 0x30)
+        {
+          /* the very first bytes does pretty much look like a SEQUENCE tag*/
+          parm->is_pem = 0;
+        }
+      else if ( parm->have_lf && !strncmp (parm->line, "-----BEGIN ", 11)
+                && strncmp (parm->line+11, "PGP ", 4) )
+        {
+          /* Fixme: we must only compare if the line really starts at
+             the beginning */
+          parm->is_pem = 1;
+          parm->linelen = parm->readpos = 0;
+        }
+      else
+        {
+          parm->linelen = parm->readpos = 0;
+          goto next;
+        }
+      parm->identified = 1;
+      parm->base64.stop_seen = 0;
+      parm->base64.idx = 0;
+    }
+  
+
+  n = 0;
+  if (parm->is_pem)
+    {  
+      if (parm->have_lf && !strncmp (parm->line, "-----END ", 9))
+        { 
+          parm->identified = 0;
+          parm->linelen = parm->readpos = 0;
+          /* let us return 0 */
+        }
+      else if (parm->stop_seen)
+        { /* skip the rest of the line */
+          parm->linelen = parm->readpos = 0;
+        }
+      else
         {
-          if ( ferror (parm->fp) )
-            return -1;
-          if (n)
-            break; /* return what we have before an EOF */
-          return -1;
+          int idx = parm->base64.idx;
+          unsigned char val = parm->base64.val;
+
+          while (n < count && parm->readpos < parm->linelen )
+            {
+              c = parm->line[parm->readpos++];
+              if (c == '\n' || c == ' ' || c == '\r' || c == '\t')
+                continue;
+              if (c == '=')
+                { /* pad character: stop */
+                  if (idx == 1)
+                    buffer[n++] = val; 
+                  parm->stop_seen = 1;
+                  break;
+                }
+              if( (c = asctobin[(c2=c)]) == 255 ) 
+                {
+                  log_error (_("invalid radix64 character %02x skipped\n"),
+                             c2);
+                  continue;
+                }
+              switch (idx) 
+                {
+                case 0: 
+                  val = c << 2;
+                  break;
+                case 1: 
+                  val |= (c>>4)&3;
+                  buffer[n++] = val;
+                  val = (c<<4)&0xf0;
+                  break;
+                case 2: 
+                  val |= (c>>2)&15;
+                  buffer[n++] = val;
+                  val = (c<<6)&0xc0;
+                  break;
+                case 3: 
+                  val |= c&0x3f;
+                  buffer[n++] = val;
+                  break;
+                }
+              idx = (idx+1) % 4;
+            }
+          if (parm->readpos == parm->linelen)
+            parm->linelen = parm->readpos = 0;
+
+          parm->base64.idx = idx;
+          parm->base64.val = val;
         }
-      *(byte *)buffer++ = c;
+    }
+  else
+    { /* DER encoded */
+      while (n < count && parm->readpos < parm->linelen)
+          buffer[n++] = parm->line[parm->readpos++];
+      if (parm->readpos == parm->linelen)
+        parm->linelen = parm->readpos = 0;
     }
 
   *nread = n;
index 7c4318b..c3b9f15 100644 (file)
@@ -109,7 +109,7 @@ cmd_verify (ASSUAN_CONTEXT ctx, char *line)
   if (fd == -1)
     return set_error (No_Input, NULL);
 
-  gpgsm_verify (fd);
+  gpgsm_verify (fd, -1);
 
   return 0;
 }
index c0fc666..ffd4a40 100644 (file)
--- a/sm/util.h
+++ b/sm/util.h
@@ -24,7 +24,7 @@
 #include <gcrypt.h> /* we need this for the memory function protos */
 
 /* to pass the fucntion to libksba we need to cast it */
-#define HASH_FNC ((void (*)(void *, const byte*,size_t))gcry_md_write)
+#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
 
 
 #include "../jnlib/logging.h"
index 31e7fcf..ecbbba4 100644 (file)
@@ -112,11 +112,37 @@ print_integer (unsigned char *p)
 }
 
 
+static void
+hash_data (int fd, GCRY_MD_HD md)
+{
+  FILE *fp;
+  char buffer[4096];
+  int nread;
+
+  fp = fdopen ( dup (fd), "rb");
+  if (!fp)
+    {
+      log_error ("fdopen(%d) failed: %s\n", fd, strerror (errno));
+      return;
+    }
+
+  do 
+    {
+      nread = fread (buffer, 1, DIM(buffer), fp);
+      gcry_md_write (md, buffer, nread);
+    }
+  while (nread);
+  if (ferror (fp))
+      log_error ("read error on fd %d: %s\n", fd, strerror (errno));
+  fclose (fp);
+}
 
 
 \f
+/* Perform a verify operation.  To verify detached signatures, data_fd
+   must be different than -1 */
 int
-gpgsm_verify (int in_fd)
+gpgsm_verify (int in_fd, int data_fd)
 {
   int i, rc;
   KsbaError err;
@@ -210,7 +236,11 @@ gpgsm_verify (int in_fd)
           log_debug ("Detached signature\n");
         }
       if (stopreason == KSBA_SR_BEGIN_DATA)
-        log_error ("error: only detached signatuires are supportted\n");
+        {
+          log_error ("error: only detached signatures are supportted\n");
+          rc = GPGSM_Not_Implemented;
+          goto leave;
+        }
 
       if (stopreason == KSBA_SR_NEED_HASH
           || stopreason == KSBA_SR_BEGIN_DATA)
@@ -220,12 +250,27 @@ gpgsm_verify (int in_fd)
               if (algo)
                 gcry_md_enable (data_md, algo);
             }
+          if (is_detached)
+            {
+              if (data_fd == -1)
+                {
+                  log_error ("detached signature but no data given\n");
+                  rc = GPGSM_Bad_Signature;
+                  goto leave;
+                }
+              hash_data (data_fd, data_md);  
+            }
         }
-
-
     }
   while (stopreason != KSBA_SR_READY);   
 
+  if (data_fd != -1 && !is_detached)
+    {
+      log_error ("data given for a non-detached signature");
+      rc = GPGSM_Conflict;
+      goto leave;
+    }
+
   for (i=0; (cert=ksba_cms_get_cert (cms, i)); i++)
     {
       log_debug ("storing certifcate %d\n", i);
@@ -301,7 +346,7 @@ gpgsm_verify (int in_fd)
             {
               log_error ("message digest attribute does not "
                          "match calculated one\n");
-              /*goto next_signer; FIXME: only for debugging commented*/
+              goto next_signer; 
             }
             
           md = gcry_md_open (algo, 0);
@@ -310,7 +355,7 @@ gpgsm_verify (int in_fd)
               log_error ("md_open failed: %s\n", gcry_strerror (-1));
               goto next_signer;
             }
-          ksba_cms_set_hash_function (cms, HASH_FNC, md);
+          ksba_cms_set_hash_function (cms, gcry_md_write, md);
           rc = ksba_cms_hash_signed_attrs (cms, signer);
           if (rc)
             {
@@ -332,8 +377,7 @@ gpgsm_verify (int in_fd)
           log_error ("invalid signature: %s\n", gpgsm_strerror (rc));
           goto next_signer;
         }
-      log_debug ("signature is good - checking certs\n");
-      /* FIXME: validate_path */
+      log_debug ("signature okay - checking certs\n");
       rc = gpgsm_validate_path (cert);
       if (rc)
         {