aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sm/gpgsm.c71
-rw-r--r--sm/gpgsm.h7
-rw-r--r--sm/import.c188
-rw-r--r--sm/server.c2
-rw-r--r--sm/util.h2
-rw-r--r--sm/verify.c60
6 files changed, 305 insertions, 25 deletions
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 53b8dcd01..329e80d9f 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -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;
+}
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index d60eb035a..9c0c93375 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -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);
diff --git a/sm/import.c b/sm/import.c
index 8dc36c1ad..8913f8092 100644
--- a/sm/import.c
+++ b/sm/import.c
@@ -36,32 +36,200 @@
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;
diff --git a/sm/server.c b/sm/server.c
index 7c4318bb2..c3b9f1559 100644
--- a/sm/server.c
+++ b/sm/server.c
@@ -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;
}
diff --git a/sm/util.h b/sm/util.h
index c0fc6667e..ffd4a4030 100644
--- 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"
diff --git a/sm/verify.c b/sm/verify.c
index 31e7fcf8f..ecbbba4f0 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -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);
+}
+/* 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)
{