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 #include #include +#include #include #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);) { - if ( ferror (parm->fp) ) - return -1; - if (n) - break; /* return what we have before an EOF */ - return -1; + 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; + } } - *(byte *)buffer++ = c; + 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 + { + 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; + } + } + 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 /* 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) {