* (c) 2000 Straylight/Edgeware
*/
-/*----- Licensing notice --------------------------------------------------*
+/*----- Licensing notice --------------------------------------------------*
*
* This file is part of Catacomb.
*
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
- *
+ *
* Catacomb 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 Library General Public License for more details.
- *
+ *
* You should have received a copy of the GNU Library General Public
* License along with Catacomb; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
/*----- Header files ------------------------------------------------------*/
+#define _FILE_OFFSET_BITS 64
+
#include "config.h"
#include <ctype.h>
uint32 k; /* Keyid */
} block;
-/* --- @getstring@ --- *
- *
- * Arguments: @FILE *fp@ = stream from which to read
- * @dstr *d@ = destination string
- * @unsigned raw@ = raw or cooked read
- *
- * Returns: Zero if OK, nonzero on end-of-file.
- *
- * Use: Reads a filename (or something similar) from a stream.
- */
-
-static int getstring(FILE *fp, dstr *d, unsigned raw)
-{
- int ch;
- int q = 0;
-
- /* --- Raw: just read exactly what's written up to a null byte --- */
-
- if (raw) {
- if ((ch = getc(fp)) == EOF)
- return (EOF);
- for (;;) {
- if (!ch)
- break;
- DPUTC(d, ch);
- if ((ch = getc(fp)) == EOF)
- break;
- }
- DPUTZ(d);
- return (0);
- }
-
- /* --- Skip as far as whitespace --- *
- *
- * Also skip past comments.
- */
-
-again:
- ch = getc(fp);
- while (isspace(ch))
- ch = getc(fp);
- if (ch == '#') {
- do ch = getc(fp); while (ch != '\n' && ch != EOF);
- goto again;
- }
- if (ch == EOF)
- return (EOF);
-
- /* --- If the character is a quote then read a quoted string --- */
-
- switch (ch) {
- case '`':
- ch = '\'';
- case '\'':
- case '\"':
- q = ch;
- ch = getc(fp);
- break;
- }
-
- /* --- Now read all sorts of interesting things --- */
-
- for (;;) {
-
- /* --- Handle an escaped thing --- */
-
- if (ch == '\\') {
- ch = getc(fp);
- if (ch == EOF)
- break;
- switch (ch) {
- case 'a': ch = '\a'; break;
- case 'b': ch = '\b'; break;
- case 'f': ch = '\f'; break;
- case 'n': ch = '\n'; break;
- case 'r': ch = '\r'; break;
- case 't': ch = '\t'; break;
- case 'v': ch = '\v'; break;
- }
- DPUTC(d, ch);
- ch = getc(fp);
- continue;
- }
-
- /* --- If it's a quote or some other end marker then stop --- */
-
- if (ch == q || (!q && isspace((unsigned char)ch)))
- break;
-
- /* --- Otherwise contribute and continue --- */
-
- DPUTC(d, ch);
- if ((ch = getc(fp)) == EOF)
- break;
- }
-
- /* --- Done --- */
-
- DPUTZ(d);
- return (0);
-}
-
-/* --- @putstring@ --- *
- *
- * Arguments: @FILE *fp@ = stream to write on
- * @const char *p@ = pointer to text
- * @unsigned raw@ = whether the string is to be written raw
- *
- * Returns: ---
- *
- * Use: Emits a string to a stream.
- */
-
-static void putstring(FILE *fp, const char *p, unsigned raw)
-{
- size_t sz = strlen(p);
- unsigned qq;
- const char *q;
-
- /* --- Just write the string null terminated if raw --- */
-
- if (raw) {
- fwrite(p, 1, sz + 1, fp);
- return;
- }
-
- /* --- Check for any dodgy characters --- */
-
- qq = 0;
- for (q = p; *q; q++) {
- if (isspace((unsigned char)*q)) {
- qq = '\"';
- break;
- }
- }
-
- if (qq)
- putc(qq, fp);
-
- /* --- Emit the string --- */
-
- for (q = p; *q; q++) {
- switch (*q) {
- case '\a': fputc('\\', fp); fputc('a', fp); break;
- case '\b': fputc('\\', fp); fputc('b', fp); break;
- case '\f': fputc('\\', fp); fputc('f', fp); break;
- case '\n': fputc('\\', fp); fputc('n', fp); break;
- case '\r': fputc('\\', fp); fputc('r', fp); break;
- case '\t': fputc('\\', fp); fputc('t', fp); break;
- case '\v': fputc('\\', fp); fputc('v', fp); break;
- case '`': fputc('\\', fp); fputc('`', fp); break;
- case '\'': fputc('\\', fp); fputc('\'', fp); break;
- case '\"': fputc('\\', fp); fputc('\"', fp); break;
- default:
- putc(*q, fp);
- break;
- }
- }
-
- /* --- Done --- */
-
- if (qq)
- putc(qq, fp);
-}
-
/* --- @timestring@ --- *
*
* Arguments: @time_t t@ = a timestamp
tag = getc(fp);
else {
dstr d = DSTR_INIT;
- if (getstring(fp, &d, 0))
+ if (getstring(fp, &d, GSF_FILE))
return (E_EOF);
for (tag = 0; tagtab[tag]; tag++) {
if (strcmp(tagtab[tag], d.buf) == 0)
case T_IDENT:
case T_COMMENT:
- if (getstring(fp, &b->d, bin))
+ if (getstring(fp, &b->d, GSF_FILE | (bin ? GSF_RAW : 0)))
return (E_EOF);
break;
b->t = ((time_t)(((LOAD32(buf + 0) << 16) << 16) & ~MASK32) |
(time_t)LOAD32(buf + 4));
} else {
- if (getstring(fp, &b->d, 0))
+ if (getstring(fp, &b->d, GSF_FILE))
return (E_EOF);
if (strcmp(b->d.buf, "forever") == 0)
b->t = KEXP_FOREVER;
return (E_EOF);
b->k = LOAD32(buf);
} else {
- if (getstring(fp, &b->d, 0))
+ if (getstring(fp, &b->d, GSF_FILE))
return (E_EOF);
b->k = strtoul(b->d.buf, 0, 16);
}
b->b.len += sz;
} else {
base64_ctx b64;
- if (getstring(fp, &b->d, 0))
+ if (getstring(fp, &b->d, GSF_FILE))
return (E_EOF);
base64_init(&b64);
base64_decode(&b64, b->d.buf, b->d.len, &b->b);
base64_decode(&b64, 0, 0, &b->b);
DRESET(&b->d);
}
- if (tag == T_FILE && getstring(fp, &b->d, bin))
+ if (tag == T_FILE &&
+ getstring(fp, &b->d, GSF_FILE | (bin ? GSF_RAW : 0)))
return (E_EOF);
break;
if (fp && !bin)
bwrite(b, fp);
}
-
+
/*----- Static variables --------------------------------------------------*/
static const char *keyring = "keyring";
/*----- Other shared functions --------------------------------------------*/
-/* --- @keyreport@ --- *
- *
- * Arguments: @const char *file@ = filename containing the error
- * @int line@ = line number in file
- * @const char *err@ = error text message
- * @void *p@ = unimportant pointer
- *
- * Returns: ---
- *
- * Use: Reports errors during the opening of a key file.
- */
-
-static void keyreport(const char *file, int line, const char *err, void *p)
-{
- moan("error in keyring `%s' at line `%s': %s", file, line, err);
-}
-
-/* --- @fhash@ --- *
- *
- * Arguments: @const gchash *c@ = pointer to hash class
- * @const char *file@ = file to hash
- * @void *b@ = pointer to output buffer
- *
- * Returns: Zero if it worked, or nonzero for a system error.
- *
- * Use: Hashes a file.
- */
-
-static int fhash(const gchash *c, const char *file, void *b)
-{
- FILE *fp = fopen(file, "rb");
- ghash *h = GH_INIT(c);
- char buf[4096];
- size_t sz;
- int rc = 0;
-
- if (!fp)
- return (-1);
- while ((sz = fread(buf, 1, sizeof(buf), fp)) > 0)
- GH_HASH(h, buf, sz);
- if (ferror(fp))
- rc = -1;
- GH_DONE(h, b);
- GH_DESTROY(h);
- fclose(fp);
- return (rc);
-}
-
/* --- @fhex@ --- *
*
* Arguments: @FILE *fp@ = file to write on
if (!sz)
break;
}
-}
+}
/*----- Signature generation ----------------------------------------------*/
static int sign(int argc, char *argv[])
{
-#define f_raw 1u
+#define f_bogus 1u
#define f_bin 2u
-#define f_bogus 4u
+#define f_nocheck 4u
unsigned f = 0;
const char *ki = "dsig";
{ "null", 0, 0, '0' },
{ "binary", 0, 0, 'b' },
{ "verbose", 0, 0, 'v' },
+ { "progress", 0, 0, 'p' },
{ "quiet", 0, 0, 'q' },
{ "comment", OPTF_ARGREQ, 0, 'c' },
{ "file", OPTF_ARGREQ, 0, 'f' },
{ "output", OPTF_ARGREQ, 0, 'o' },
{ "key", OPTF_ARGREQ, 0, 'k' },
{ "expire", OPTF_ARGREQ, 0, 'e' },
+ { "nocheck", OPTF_ARGREQ, 0, 'C' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+0vqb" "c:" "f:o:" "k:e:", opts, 0, 0, 0);
+ int i = mdwopt(argc, argv, "+0vpqbC" "c:" "f:o:" "k:e:", opts, 0, 0, 0);
if (i < 0)
break;
switch (i) {
case '0':
- f |= f_raw;
+ f |= GSF_RAW;
break;
case 'b':
f |= f_bin;
case 'v':
verb++;
break;
+ case 'p':
+ f |= FHF_PROGRESS;
+ break;
case 'q':
if (verb > 0)
verb--;
break;
+ case 'C':
+ f |= f_nocheck;
+ break;
case 'c':
c = optarg;
break;
/* --- Locate the signing key --- */
- if (key_open(&kf, keyring, KOPEN_WRITE, keyreport, 0))
+ if (key_open(&kf, keyring, KOPEN_WRITE, key_moan, 0))
die(EXIT_FAILURE, "couldn't open keyring `%s'", keyring);
if ((k = key_bytag(&kf, ki)) == 0)
die(EXIT_FAILURE, "couldn't find key `%s'", ki);
/* --- Check the key --- */
- if ((err = s->ops->check(s)) != 0)
+ if (!(f & f_nocheck) && (err = s->ops->check(s)) != 0)
moan("key `%s' fails check: %s", d.buf, err);
/* --- Open files --- */
- if (!ifile)
+ if (!ifile || strcmp(ifile, "-") == 0)
ifp = stdin;
- else if ((ifp = fopen(ifile, (f & f_raw) ? "rb" : "r")) == 0) {
+ else if ((ifp = fopen(ifile, (f & f_bin) ? "rb" : "r")) == 0) {
die(EXIT_FAILURE, "couldn't open input file `%s': %s",
ifile, strerror(errno));
}
- if (!ofile)
+ if (!ofile || strcmp(ofile, "-") == 0)
ofp = stdout;
else if ((ofp = fopen(ofile, (f & f_bin) ? "wb" : "w")) == 0) {
die(EXIT_FAILURE, "couldn't open output file `%s': %s",
binit(&b); b.tag = T_IDENT;
dstr_putf(&b.d, "%s, Catacomb version " VERSION, QUIS);
bemit(&b, ofp, 0, f & f_bin);
-
+
breset(&b); b.tag = T_KEYID; b.k = k->id;
bemit(&b, ofp, 0, f & f_bin);
/* --- Read the next filename to hash --- */
breset(&b);
- if (getstring(ifp, &b.d, f & f_raw))
+ if (getstring(ifp, &b.d, GSF_FILE | f))
break;
b.tag = T_FILE;
DENSURE(&b.b, GH_CLASS(s->h)->hashsz);
- if (fhash(GH_CLASS(s->h), b.d.buf, b.b.buf)) {
+ if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, b.b.buf)) {
moan("Error reading `%s': %s", b.d.buf, strerror(errno));
f |= f_bogus;
} else {
die(EXIT_FAILURE, "error(s) occurred while creating signature");
return (EXIT_SUCCESS);
-#undef f_raw
#undef f_bin
#undef f_bogus
+#undef f_nocheck
}
/*----- Signature verification --------------------------------------------*/
#define f_bogus 1u
#define f_bin 2u
#define f_ok 4u
+#define f_nocheck 8u
unsigned f = 0;
unsigned verb = 1;
for (;;) {
static struct option opts[] = {
{ "verbose", 0, 0, 'v' },
+ { "progress", 0, 0, 'p' },
{ "quiet", 0, 0, 'q' },
+ { "nocheck", 0, 0, 'C' },
{ 0, 0, 0, 0 }
};
- int i = mdwopt(argc, argv, "+vq", opts, 0, 0, 0);
+ int i = mdwopt(argc, argv, "+vpqC", opts, 0, 0, 0);
if (i < 0)
break;
switch (i) {
case 'v':
verb++;
break;
+ case 'p':
+ f |= FHF_PROGRESS;
+ break;
case 'q':
if (verb)
verb--;
break;
+ case 'C':
+ f |= f_nocheck;
+ break;
default:
f |= f_bogus;
break;
argc -= optind;
argv += optind;
if ((f & f_bogus) || argc > 1)
- die(EXIT_FAILURE, "Usage: verify [-qv] [FILE]");
+ die(EXIT_FAILURE, "Usage: verify [-qvC] [FILE]");
/* --- Open the key file, and start reading the input file --- */
- if (key_open(&kf, keyring, KOPEN_READ, keyreport, 0))
+ if (key_open(&kf, keyring, KOPEN_READ, key_moan, 0))
die(EXIT_FAILURE, "couldn't open keyring `%s'\n", keyring);
if (argc < 1)
fp = stdin;
}
s = getsig(k, "dsig", 0);
- if (verb && (err = s->ops->check(s)) != 0)
+ if (!(f & f_nocheck) && verb && (err = s->ops->check(s)) != 0)
printf("WARN public key fails check: %s", err);
for (;;) {
case T_FILE:
DRESET(&d);
DENSURE(&d, GH_CLASS(s->h)->hashsz);
- if (fhash(GH_CLASS(s->h), b.d.buf, d.buf)) {
+ if (fhash(GH_CLASS(s->h), f | FHF_BINARY, b.d.buf, d.buf)) {
if (verb > 1) {
printf("BAD error reading file `%s': %s\n",
b.d.buf, strerror(errno));
#undef f_bogus
#undef f_bin
#undef f_ok
+#undef f_nocheck
}
/*----- Main code ---------------------------------------------------------*/
{ "help", cmd_help, "help [COMMAND...]" },
{ "show", cmd_show, "show [ITEM...]" },
{ "sign", sign,
- "sign [-0bqv] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\
+ "sign [-0bpqvC] [-c COMMENT] [-k TAG] [-e EXPIRE]\n\t\
[-f FILE] [-o OUTPUT]",
"\
Options:\n\
-b, --binary Produce a binary output file.\n\
-q, --quiet Produce fewer messages while working.\n\
-v, --verbose Produce more messages while working.\n\
+-p, --progress Show progress on large files.\n\
+-C, --nocheck Don't check the private key.\n\
-c, --comment=COMMENT Include COMMENT in the output file.\n\
-f, --file=FILE Read filenames to hash from FILE.\n\
-o, --output=FILE Write the signed result to FILE.\n\
-e, --expire=TIME The signature should expire after TIME.\n\
" },
{ "verify", verify,
- "verify [-qv] [FILE]", "\
+ "verify [-pqvC] [FILE]", "\
Options:\n\
\n\
-q, --quiet Produce fewer messages while working.\n\
-v, --verbose Produce more messages while working.\n\
+-p, --progress Show progress on large files.\n\
+-C, --nocheck Don't check the public key.\n\
" },
{ 0, 0, 0 }
};