diff options
author | Marc Espie <espie@cvs.openbsd.org> | 1999-10-04 21:46:31 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 1999-10-04 21:46:31 +0000 |
commit | 5dfe2b009511b15f852b0bc0512140ae1e9371a3 (patch) | |
tree | 6fa4684c7daf902f4709d0217155c0916d1c885a | |
parent | cd21bf5152aa75fa8a13091c4ba85f3c16b9d1a9 (diff) |
Synch with current development:
* signatures no longer deal with zcat. Instead, we sign the gzip file
itself (stripped of the signature part of the header, of course). Thanks
Angelos. Niels seems to think passing the header itself to sign is not
a problem, even though no-one cares about checking it ?
* gzip header handling revamped: can write to memory. Will eliminate some
pipes later on. Can stack signatures.
* taken out specific signature schemes (e.g., pgp and sha1). Code is now
signature scheme independent, mostly, and writes with client data from
memory, e.g., check.c can invoke several checks in parallel without needing
to fork.
* need the full set of popen-like functionalities (keep track of opened
file descriptors to avoid passing them down to children)
* remove simple_check.c, functionality absorbed elsewhere.
To do:
* re-check message output and what to do with unsigned/unchecked/verified
packages,
* check pkg_add implementation and remove extra-pipe in asynchronous
checking,
* control over what to do when several signatures are stacked... Simple
way would be to disable that for now (possibility for release)
* get the code through a linter again.
-rw-r--r-- | usr.sbin/pkg_install/sign/INSTALL | 4 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/Makefile | 1 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/README | 31 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/check.c | 198 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/common.c | 89 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/extern.h | 53 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/gzip.c | 317 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/gzip.h | 50 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/main.c | 41 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/pgp.h | 23 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/pgp_check.c | 193 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/pgp_sign.c | 266 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/pkg_sign.1 | 73 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/sha1.c | 212 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/sign.c | 110 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/simple_check.c | 72 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/stand.c | 54 | ||||
-rw-r--r-- | usr.sbin/pkg_install/sign/stand.h | 13 |
18 files changed, 1337 insertions, 463 deletions
diff --git a/usr.sbin/pkg_install/sign/INSTALL b/usr.sbin/pkg_install/sign/INSTALL index 1577272fc8c..bc0a530fcee 100644 --- a/usr.sbin/pkg_install/sign/INSTALL +++ b/usr.sbin/pkg_install/sign/INSTALL @@ -4,8 +4,8 @@ Normally, `make' should be enough to build these tools. A simpler version can be built by using `make check_sign' (does not depend on the existence of getpass()) -You should define PGP and GZCAT in the Makefile if the defaults -(/usr/local/bin/pgp and /usr/bin/gzcat) don't apply. +You should define PGP in the Makefile if the default +(/usr/local/bin/pgp) doesn't apply. There is no install target, just copy pkg_sign and/or check_sign where you want, along with the manpage. diff --git a/usr.sbin/pkg_install/sign/Makefile b/usr.sbin/pkg_install/sign/Makefile index b3adf4d287d..72b2e5c506c 100644 --- a/usr.sbin/pkg_install/sign/Makefile +++ b/usr.sbin/pkg_install/sign/Makefile @@ -1,5 +1,4 @@ # define PGP to be the path to pgp (default: /usr/local/bin/pgp) -# and GZCAT to be the path to gzcat (default: /usr/bin/gzcat) # Use the check_sign target if your system can't handle pipes or getpass all: pkg_sign diff --git a/usr.sbin/pkg_install/sign/README b/usr.sbin/pkg_install/sign/README index 57a6656c8a4..952dec5a40a 100644 --- a/usr.sbin/pkg_install/sign/README +++ b/usr.sbin/pkg_install/sign/README @@ -1,21 +1,36 @@ To sign packages in a transparent way: -gzip files can handle an EXTRA_FIELD at the beginning that +gzip files can handle an extra field at the beginning that stores anything we wish. So it's just a question to choose a format for the signature, and to embed it there. -We put a specific cookie `S, i, g, P, G, P, length' -at the beginning of the extra-field, so that eventually we can put -more information in that field (like several signatures), -followed by the signature output by PGP. +We use the extra field to store signatures. Each signature consists +of a 6 bytes type marker, a 2 bytes length, followed by the signature +itself. We can potentially stack signatures: resign a signed archive +by just prepending the new signature to the extra field. -The checker just needs to extract the signature, pass it off to PGP, -followed by the uncompressed archive. +To check the first signature, the checker just needs to extract it, pass it +off to the checking protocol (e.g. PGP), followed by the unsigned archive +(e.g., regenerate the gzip header without the first signature, then put +the gzip data). -* Signed archives that just look like normal .tar.gz files, +* Signed archives just look like normal .tar.gz files, except for programs +that use the extra field for their own purpose, * Possibility to grab the files off the net and extract stuff/verify signatures on the fly (just need to wedge the checker as an intermediate pipe) * Pretty simple, small portable code to be able to check signatures everywhere (the signer itself needs getpass and corresponding functionality) + +The scheme should be extensible to any compressed format which allows for +extended headers. + + +Thanks to Angelos D. Keromytis for pointing out I did not need to +uncompress the archive to sign it, and to other members of the OpenBSD +project for various reasons. + +-- + Marc Espie, 1999 + $OpenBSD: README,v 1.2 1999/10/04 21:46:27 espie Exp $ diff --git a/usr.sbin/pkg_install/sign/check.c b/usr.sbin/pkg_install/sign/check.c index 1799b438ff3..435a03026d7 100644 --- a/usr.sbin/pkg_install/sign/check.c +++ b/usr.sbin/pkg_install/sign/check.c @@ -1,4 +1,4 @@ -/* $OpenBSD: check.c,v 1.1 1999/09/27 21:40:03 espie Exp $ */ +/* $OpenBSD: check.c,v 1.2 1999/10/04 21:46:27 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -32,77 +32,20 @@ #include <sys/types.h> #include <sys/wait.h> #include <stdlib.h> -#include <unistd.h> #include <stdio.h> -#include <fcntl.h> -#include <paths.h> -#include <errno.h> #include "stand.h" #include "pgp.h" #include "gzip.h" #include "extern.h" -#ifndef _PATH_DEVNULL -#define _PATH_DEVNULL "/dev/null" -#endif - -typedef /*@observer@*/char *pchar; - -static void -gzcat(fdin, fdout, envp) - int fdin, fdout; - char *envp[]; -{ - pchar argv[2]; - - argv[0] = GZCAT; - argv[1] = NULL; - if (dup2(fdin, fileno(stdin)) == -1 || - dup2(fdout, fileno(stdout)) == -1 || - execve(GZCAT, argv, envp) == -1) - exit(errno); -} - -static void -pgpcheck(fd, userid, envp) - int fd; - const char *userid; - char *envp[]; -{ - int fdnull; - pchar argv[6]; - - argv[0] = PGP; - argv[1] = "+batchmode"; - argv[2] = "-f"; - - if (userid) { - argv[3] = "-u"; - argv[4] = (char *)userid; - argv[5] = NULL; - } else - argv[3] = NULL; - - fdnull = open(_PATH_DEVNULL, O_RDWR); - if (fdnull == -1 || - dup2(fd, fileno(stdin)) == -1 || - dup2(fdnull, fileno(stdout)) == -1 || - execve(PGP, argv, envp) == -1) - exit(errno); -} - -static int -reap(pid) - pid_t pid; -{ - pid_t result; - int pstat; +struct checker { + void *context; + void (*add)(void *, const char *, size_t); + int (*get)(void *); + int status; +}; - do { - result = waitpid(pid, &pstat, 0); - } while (result == -1 && errno == EINTR); - return result == -1 ? -1 : pstat; -} +#define MAX_CHECKERS 20 int check_signature(file, userid, envp, filename) @@ -111,96 +54,53 @@ check_signature(file, userid, envp, filename) char *envp[]; /*@observer@*/const char *filename; { - FILE *file2; - int c; - char sign[SIGNSIZE]; + struct signature *sign; struct mygzip_header h; int status; - int togzcat[2], topgpcheck[2]; - pid_t pgpid, gzcatid; + char buffer[1024]; + size_t length; + struct checker checker[MAX_CHECKERS]; + struct signature *sweep; + int i, j; - status = read_header_and_diagnose(file, &h, sign, filename); + status = read_header_and_diagnose(file, &h, &sign, filename); if (status != 1) return PKG_UNSIGNED; - if (pipe(topgpcheck) == -1) { - fprintf(stderr, "Error creating pipe\n"); - return PKG_SIGERROR; - } - switch(pgpid = fork()) { - case -1: - fprintf(stderr, "Error creating pgp process\n"); - return PKG_SIGERROR; - case 0: - if (close(topgpcheck[1]) == -1) - exit(errno); - pgpcheck(topgpcheck[0], userid, envp); - /*@notreached@*/ - break; - default: - (void)close(topgpcheck[0]); - break; - } - if (write(topgpcheck[1], sign, sizeof(sign)) != sizeof(sign)) { - fprintf(stderr, "Error writing to pgp pipe\n"); - (void)close(topgpcheck[1]); - (void)reap(pgpid); - return PKG_SIGERROR; - } - if (pipe(togzcat) == -1) { - fprintf(stderr, "Error creating pipe\n"); - (void)close(topgpcheck[1]); - (void)reap(pgpid); - return PKG_SIGERROR; - } - switch (gzcatid=fork()) { - case -1: - fprintf(stderr, "Error creating gzcat process\n"); - (void)reap(pgpid); - return PKG_SIGERROR; - case 0: - if (close(togzcat[1]) == -1) - exit(errno); - gzcat(togzcat[0], topgpcheck[1], envp); - /*@notreached@*/ - break; - default: - (void)close(topgpcheck[1]); - (void)close(togzcat[0]); - } - - file2 = fdopen(togzcat[1], "w"); - if (file2 == NULL) { - (void)close(togzcat[1]); - (void)reap(gzcatid); - (void)reap(pgpid); - fprintf(stderr, "Error turning fd into FILE *\n"); - return PKG_SIGERROR; - } - - if (gzip_write_header(file2, &h, NULL) != 1) { - (void)fclose(file2); - (void)reap(pgpid); - (void)reap(gzcatid); - fprintf(stderr, "Error writing gzip header\n"); - return PKG_SIGERROR; - } - while((c = fgetc(file)) != EOF) { - if (fputc(c, file2) == EOF) { - fprintf(stderr, "Problem writing to zcat\n"); - (void)fclose(file2); - (void)reap(pgpid); - (void)reap(gzcatid); - return PKG_SIGERROR; + for (sweep = sign, i = 0; + sweep != NULL && i < MAX_CHECKERS; + sweep=sweep->next, i++) { + switch(sweep->type) { + case TAG_OLD: + fprintf(stderr, "File %s uses old signatures, no longer supported\n", + filename); + checker[i].context = NULL; + break; + case TAG_SHA1: + checker[i].context = new_sha1_checker(&h, sweep, userid, envp, filename); + checker[i].add = sha1_add; + checker[i].get = sha1_sign_ok; + break; + case TAG_PGP: + checker[i].context = new_pgp_checker(&h, sweep, userid, envp, filename); + checker[i].add = pgp_add; + checker[i].get = pgp_sign_ok; + break; + default: + abort(); } - } - status = PKG_GOODSIG; - if (fclose(file2) != 0) - status = PKG_SIGERROR; - if (reap(gzcatid) != 0) - status = PKG_SIGERROR; - if (reap(pgpid) != 0) - status = PKG_BADSIG; - return status; + while ((length = fread(buffer, 1, sizeof buffer, file)) > 0) + for (j = 0; j < i; j++) + if (checker[j].context) + (*checker[j].add)(checker[j].context, buffer, length); +// for (j = i-1; j >= 0; j--) + for (j = 0; j < i; j++) + if (checker[j].context) + checker[j].status = (*checker[j].get)(checker[j].context); + else + checker[j].status = PKG_SIGERROR; + free_signature(sign); + return checker[0].status; } + diff --git a/usr.sbin/pkg_install/sign/common.c b/usr.sbin/pkg_install/sign/common.c index adc58b69565..d46629e57ff 100644 --- a/usr.sbin/pkg_install/sign/common.c +++ b/usr.sbin/pkg_install/sign/common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: common.c,v 1.1 1999/09/27 21:40:03 espie Exp $ */ +/* $OpenBSD: common.c,v 1.2 1999/10/04 21:46:27 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -29,8 +29,11 @@ */ #include <sys/types.h> +#include <sys/wait.h> #include <sys/stat.h> #include <stdio.h> +#include <stdlib.h> +#include <errno.h> #include "stand.h" #include "gzip.h" #include "pgp.h" @@ -41,7 +44,7 @@ int read_header_and_diagnose(file, h, sign, filename) FILE *file; struct mygzip_header *h; - char sign[]; + struct signature **sign; const char *filename; { switch(gzip_read_header(file, h, sign)) { @@ -69,20 +72,82 @@ read_header_and_diagnose(file, h, sign, filename) } } -/* Check command existence */ -int check_helpers() +struct reg_fd { + int fd; + pid_t pid; + struct reg_fd *next; +}; + +static struct reg_fd *first = NULL; + +void +register_pipe(fd, pid) + int fd; + pid_t pid; { - struct stat sbuf; + struct reg_fd *n; - if (stat(GZCAT, &sbuf) == -1) { - fprintf(stderr, "Tool %s does not exist\n", GZCAT); - return 0; + n = malloc(sizeof *n); + if (n) { + n->fd = fd; + n->pid = pid; + n->next = first; + first = n; } - if (stat(PGP, &sbuf) == -1) { - fprintf(stderr, "Tool %s does not exist\n", PGP); - return 0; +} + +void +close_dangling_pipes() +{ + while (first) { + close(first->fd); + first = first->next; } - return 1; } +static struct reg_fd * +retrieve_reg(fd) + int fd; +{ + struct reg_fd **i, *cur; + + for (i = &first; *i ; i = &((*i)->next)) + if ((*i)->fd == fd) + break; + cur = *i; + *i = cur->next; + return cur; +} + +int +reap(pid) + pid_t pid; +{ + int pstat; + pid_t result; + + do { + result = waitpid(pid, &pstat, 0); + } while (result == -1 && errno == EINTR); + return result == -1 ? -1 : pstat; +} + +/* kill process and reap status + */ +int +terminate_pipe(fd) + int fd; +{ + pid_t result; + int close_result; + struct reg_fd *cur; + + cur = retrieve_reg(fd); + if (!cur) + return -1; + close_result = close(cur->fd); + result = reap(cur->pid); + free(cur); + return close_result == -1 ? -1 : result; +} diff --git a/usr.sbin/pkg_install/sign/extern.h b/usr.sbin/pkg_install/sign/extern.h index 7cc86239a6b..202f7c378bc 100644 --- a/usr.sbin/pkg_install/sign/extern.h +++ b/usr.sbin/pkg_install/sign/extern.h @@ -1,4 +1,4 @@ -/* $OpenBSD: extern.h,v 1.1 1999/09/27 21:40:03 espie Exp $ */ +/* $OpenBSD: extern.h,v 1.2 1999/10/04 21:46:27 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -28,23 +28,56 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +/* Convention: all functions that operate on a FILE * also take a filename + for diagnostic purposes. The file can be connected to a pipe, so + - don't rewind + - don't reopen from filename. + */ + struct mygzip_header; +struct signature; + +/* common.c */ +extern int read_header_and_diagnose __P((FILE *file, \ + /*@out@*/struct mygzip_header *h, /*@null@*/struct signature **sign, \ + const char *filename)); +extern int reap __P((pid_t pid)); -extern int read_header_and_diagnose - __P((FILE *file, /*@out@*/struct mygzip_header *h, /*@null@*/char sign[], const char *filename)); -extern int check_helpers __P((void)); +extern int terminate_pipe __P((int fd)); +extern void close_dangling_pipes __P((void)); +extern void register_pipe __P((int fd, pid_t pid)); -extern int sign - __P((/*@observer@*/const char *filename, /*@null@*/const char *userid, char *envp[])); -extern int check_signature - __P((/*@dependent@*/FILE *file, /*@null@*/const char *userid, char *envp[], /*@observer@*/const char *filename)); -extern void handle_passphrase __P((void)); +/* sign.c */ +extern int sign __P((/*@observer@*/const char *filename, int type, \ + /*@null@*/const char *userid, char *envp[])); + +/* check.c */ +extern int check_signature __P((/*@dependent@*/FILE *file, \ + /*@null@*/const char *userid, char *envp[], \ + /*@observer@*/const char *filename)); #define PKG_BADSIG 0 #define PKG_GOODSIG 1 #define PKG_UNSIGNED 2 #define PKG_SIGNED 4 #define PKG_SIGERROR 8 +#define PKG_SIGUNKNOWN 16 + +typedef /*@observer@*/char *pchar; + +#define MAXID 512 +/* sha1.c */ +#define SHA1_DB_NAME "/var/db/pkg/SHA1" + +extern void *new_sha1_checker __P((struct mygzip_header *h, \ + struct signature *sign, const char *userid, char *envp[], \ + const char *filename)); + +extern void sha1_add __P((void *arg, const char *buffer, \ + size_t length)); + +extern int sha1_sign_ok __P((void *arg)); -extern int simple_check __P((const char *pkg_name)); +extern int retrieve_sha1_marker __P((const char *filename, \ + struct signature **sign, const char *userid)); diff --git a/usr.sbin/pkg_install/sign/gzip.c b/usr.sbin/pkg_install/sign/gzip.c index 03132d83bf6..abebee5a767 100644 --- a/usr.sbin/pkg_install/sign/gzip.c +++ b/usr.sbin/pkg_install/sign/gzip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gzip.c,v 1.2 1999/10/01 01:14:38 espie Exp $ */ +/* $OpenBSD: gzip.c,v 1.3 1999/10/04 21:46:28 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -30,113 +30,284 @@ #include <sys/types.h> #include <sys/wait.h> #include <stdio.h> +#include <stdlib.h> +#include <assert.h> #include <string.h> #include "stand.h" #include "gzip.h" #include "pgp.h" -/* For now, signatures follow a hardcoded format +/* Signatures follow a simple format (endianess was chosen to conform to gzip header format) */ -static char tagsign[] = - {'S', 'i', 'g', 'P', 'G', 'P', - (char)(SIGNSIZE /256), (char)(SIGNSIZE & 255) }; -/* retrieve a gzip header, including PGP signatures */ -int -gzip_read_header(f, h, sign) - FILE *f; - struct mygzip_header *h; - char sign[]; +SIGNTAG known_tags[KNOWN_TAGS] = { + {'S', 'I', 'G', 'P', 'G', 'P', 0, 0 }, + {'C', 'K', 'S', 'H', 'A', '1', 0, 0 }, + {'S', 'i', 'g', 'P', 'G', 'P', 0, 0 } /* old format */ +}; + +void +sign_fill_tag(sign) + struct signature *sign; { - { - int c, d; + sign->tag[6] = sign->length % 256; + sign->tag[7] = sign->length / 256; +} + +void +sign_fill_length(sign) + struct signature *sign; +{ + sign->length = sign->tag[6] + 256 * sign->tag[7]; +} - c = fgetc(f); - d = fgetc(f); - if ((unsigned char)c != (unsigned char)GZIP_MAGIC0 - || (unsigned char)d != (unsigned char)GZIP_MAGIC1) - return GZIP_NOT_GZIP; +static size_t +stack_sign(match, t, f, sign) + SIGNTAG match; + int t; + FILE *f; + struct signature **sign; +{ + struct signature *new_sign; + size_t length; + + new_sign = malloc(sizeof *new_sign); + if (new_sign == NULL) + return 0; + new_sign->type = t; + new_sign->next = NULL; + memcpy(new_sign->tag, match, sizeof(SIGNTAG)); + sign_fill_length(new_sign); + new_sign->data = malloc(new_sign->length); + if (new_sign->data == NULL || + fread(new_sign->data, 1, new_sign->length, f) != new_sign->length) { + free_signature(new_sign); + return 0; } - { - int method, flags; - - method = fgetc(f); - flags = fgetc(f); + length = new_sign->length; + if (sign != NULL) { + if (!*sign) + *sign = new_sign; + else { + while ((*sign)->next != NULL) + sign = &((*sign)->next); + (*sign)->next = new_sign; + } + } else + free_signature(new_sign); + return length; +} - if (method == EOF || flags == EOF || fread(h->stamp, 1, 6, f) != 6) - return GZIP_NOT_GZIP; - h->method = (char)method; - h->flags = (char)flags; + +static int +add_sign(f, sign) + FILE *f; + struct signature **sign; +{ + SIGNTAG match; + int i; + + if (fread(match, 1, sizeof(SIGNTAG), f) != sizeof(SIGNTAG)) + return -1; + for (i = 0; i < KNOWN_TAGS; i++) { + if (memcmp(match, known_tags[i], TAGCHECK) == 0) { + unsigned int sign_length = stack_sign(match, i, f, sign); + if (sign_length > 0) + return sign_length + sizeof(SIGNTAG); + else + return -1; + } } + return 0; +} + +static int +gzip_magic(f) + FILE *f; +{ + int c, d; + + c = fgetc(f); + d = fgetc(f); + if ((unsigned char)c != (unsigned char)GZIP_MAGIC0 + || (unsigned char)d != (unsigned char)GZIP_MAGIC1) + return 0; + else + return 1; +} + +static int +fill_gzip_fields(f, h) + FILE *f; + struct mygzip_header *h; +{ + int method, flags; + + method = fgetc(f); + flags = fgetc(f); + if (method == EOF || flags == EOF || fread(h->stamp, 1, 6, f) != 6) + return 0; + h->method = (char)method; + h->flags = (char)flags; if ((h->flags & CONTINUATION) != 0) if (fread(h->part, 1, 2, f) != 2) - return GZIP_NOT_GZIP; - if ((h->flags & EXTRA_FIELD) != 0) { - char match[sizeof(tagsign)]; - unsigned int len; + return 0; + return 1; +} + +/* retrieve a gzip header, including signatures */ +int +gzip_read_header(f, h, sign) + FILE *f; + struct mygzip_header *h; + struct signature **sign; +{ + if (sign != NULL) + *sign = NULL; + if (!gzip_magic(f) || !fill_gzip_fields(f, h)) + return GZIP_NOT_GZIP; + + if ((h->flags & EXTRA_FIELD) == 0) { + h->remaining = 0; + return GZIP_UNSIGNED; + } + else { int c; c = fgetc(f); if (c == EOF) - return GZIP_NOT_PGPSIGNED; - len = (unsigned)c; + return GZIP_NOT_GZIP; + h->remaining = (unsigned)c; c = fgetc(f); if (c == EOF) return GZIP_NOT_PGPSIGNED; - len |= ((unsigned) c) << 8; - if (len != sizeof(tagsign) + SIGNSIZE) - return GZIP_NOT_PGPSIGNED; - if (fread(match, 1, sizeof(match), f) != sizeof(match) || - memcmp(match, tagsign, sizeof(match)) != 0) - return GZIP_NOT_PGPSIGNED; - if (sign != NULL) { - if (fread(sign, 1, SIGNSIZE, f) == SIGNSIZE) - return GZIP_SIGNED; - else - return GZIP_NOT_PGPSIGNED; - } else { - if (fseek(f, SIGNSIZE, SEEK_CUR) != -1) + h->remaining += ((unsigned) c) << 8; + while (h->remaining >= sizeof(SIGNTAG)) { + int sign_length = add_sign(f, sign); + if (sign_length > 0) + h->remaining -= sign_length; + if (sign_length < 0) + return GZIP_NOT_GZIP; + if (sign_length == 0) return GZIP_SIGNED; - else - return GZIP_NOT_PGPSIGNED; } - } else - return GZIP_UNSIGNED; + return GZIP_SIGNED; + } +} + +static unsigned +sign_length(sign) + struct signature *sign; +{ + unsigned total = 0; + + while (sign != NULL) { + total += sizeof(SIGNTAG) + sign->length; + sign = sign->next; + } + return total; +} + +struct mydata { + FILE *file; + int ok; +}; + +static void myadd(arg, buffer, size) + void *arg; + const char *buffer; + size_t size; +{ + struct mydata *d = arg; + + if (fwrite(buffer, 1, size, d->file) == size) + d->ok = 1; + else + d->ok = 0; } -/* write a gzip header, including PGP signature */ +/* write a gzip header, including signatures */ int gzip_write_header(f, h, sign) FILE *f; const struct mygzip_header *h; - const char sign[]; + struct signature *sign; +{ + struct mydata d; + d.file = f; + if (gzip_copy_header(h, sign, myadd, &d) == 0) + return 0; + return d.ok; +} + +int +gzip_copy_header(h, sign, add, data) + const struct mygzip_header *h; + struct signature *sign; + void (*add)(void *, const char *, size_t); + void *data; { char flags; + size_t length; + size_t buflength; + size_t i; + char *buffer; - flags = h->flags; - - if (sign != NULL) - flags |= EXTRA_FIELD; - else - flags &= ~EXTRA_FIELD; - if (fputc(GZIP_MAGIC0, f) == EOF || - fputc(GZIP_MAGIC1, f) == EOF || - fputc(h->method, f) == EOF || - fputc(flags, f) == EOF || - fwrite(h->stamp, 1, 6, f) != 6) - return 0; + length = h->remaining + sign_length(sign); + if (length) { + buflength = length + 2; + flags = h->flags | EXTRA_FIELD; + } else { + flags = h->flags & ~EXTRA_FIELD; + buflength = 0; + } + buflength += 10; if ((h->flags & CONTINUATION) != 0) - if (fwrite(h->part, 1, 2, f) != 2) - return 0; - if (sign != NULL) { - unsigned short len = sizeof(tagsign) + SIGNSIZE; - if (fputc(len & 255, f) == EOF || - fputc(len/256, f) == EOF || - fwrite(tagsign, 1, sizeof(tagsign), f) != sizeof(tagsign) || - fwrite(sign, 1, SIGNSIZE, f) != SIGNSIZE) - return 0; + buflength += 2; + + buffer = malloc(buflength); + if (buffer == NULL) + return 0; + + i = 0; + buffer[i++] = GZIP_MAGIC0; + buffer[i++] = GZIP_MAGIC1; + buffer[i++] = h->method; + buffer[i++] = flags; + memcpy(buffer+i, h->stamp, 6); + i += 6; + if ((flags & CONTINUATION) != 0) { + memcpy(buffer+i, h->part, 2); + i += 2; } + if (length) { + buffer[i++] = (char)(length % 256); + buffer[i++] = (char)(length / 256); + while (sign != NULL) { + memcpy(buffer+i, sign->tag, sizeof(SIGNTAG)); + i += sizeof(SIGNTAG); + memcpy(buffer+i, sign->data, sign->length); + i += sign->length; + sign = sign->next; + } + } + (*add)(data, buffer, buflength); + free(buffer); return 1; } + +void +free_signature(sign) + struct signature *sign; +{ + struct signature *next; + + while (sign != NULL) { + next = sign->next; + free(sign->data); + free(sign); + sign = next; + } +} diff --git a/usr.sbin/pkg_install/sign/gzip.h b/usr.sbin/pkg_install/sign/gzip.h index 13db86fa40d..dc3267ea5c8 100644 --- a/usr.sbin/pkg_install/sign/gzip.h +++ b/usr.sbin/pkg_install/sign/gzip.h @@ -1,4 +1,4 @@ -/* $OpenBSD: gzip.h,v 1.1 1999/09/27 21:40:04 espie Exp $ */ +/* $OpenBSD: gzip.h,v 1.2 1999/10/04 21:46:28 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -38,26 +38,52 @@ This structure should not be fiddled with outside of gzip_read_header and gzip_write_header */ -struct mygzip_header - { +struct mygzip_header { char method; char flags; char stamp[6]; char part[2]; - }; + /* remaining extra, after know signs have been read */ + unsigned int remaining; +}; +#define TAGSIZE 8 +#define TAGCHECK 6 + +typedef unsigned char SIGNTAG[8]; + +/* stack of signatures */ +struct signature { + SIGNTAG tag; + int type; + int length; + char *data; + struct signature *next; +}; + /* returns from gzip_read_header */ #define GZIP_UNSIGNED 0 /* gzip file, no signature */ #define GZIP_SIGNED 1 /* gzip file, signature parsed ok */ #define GZIP_NOT_GZIP 2 /* not a proper gzip file */ #define GZIP_NOT_PGPSIGNED 3 /* gzip file, unknown extension */ -extern int gzip_read_header - __P((FILE *f, /*@out@*/struct mygzip_header *h, /*@null@*/char sign[])); +extern int gzip_read_header __P((FILE *f, /*@out@*/struct mygzip_header *h, \ + /*@null@*/struct signature **sign)); /* gzip_write_header returns 1 for success */ -extern int gzip_write_header - __P((FILE *f, const struct mygzip_header *h, /*@null@*/const char sign[])); - -#ifndef GZCAT -#define GZCAT "/usr/bin/gzcat" -#endif +extern int gzip_write_header __P((FILE *f, const struct mygzip_header *h, \ + /*@null@*/struct signature *sign)); +/* writing header to memory. Returns size needed, or 0 if buffer too small + buffer must be at least 14 characters */ +extern int gzip_copy_header __P((const struct mygzip_header *h, \ + /*@null@*/struct signature *sign, \ + void (*add)(void *, const char *, size_t), void *data)); +extern void free_signature __P((/*@null@*/struct signature *sign)); +extern void sign_fill_tag __P((struct signature *sign)); +#define KNOWN_TAGS 3 +#define TAG_PGP 0 +#define TAG_SHA1 1 +#define TAG_OLD 2 +#define TAG_ANY -1 +#define pgptag (known_tags[TAG_PGP]) +#define sha1tag (known_tags[TAG_SHA1]) +extern SIGNTAG known_tags[KNOWN_TAGS]; diff --git a/usr.sbin/pkg_install/sign/main.c b/usr.sbin/pkg_install/sign/main.c index 2792f1ad816..bb444a9919c 100644 --- a/usr.sbin/pkg_install/sign/main.c +++ b/usr.sbin/pkg_install/sign/main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: main.c,v 1.1 1999/09/27 21:40:04 espie Exp $ */ +/* $OpenBSD: main.c,v 1.2 1999/10/04 21:46:28 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -29,12 +29,14 @@ */ #include <sys/types.h> +#include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include "stand.h" #include "gzip.h" +#include "pgp.h" #include "extern.h" #ifdef __OpenBSD__ @@ -56,9 +58,11 @@ usage() #define SIGN 0 #define CHECK 1 +/* wrapper for the check_signature function (open file if needed) */ static int -check(filename, userid, envp) +check(filename, type, userid, envp) /*@observer@*/const char *filename; + int type; /*@null@*/const char *userid; char *envp[]; { @@ -73,9 +77,12 @@ check(filename, userid, envp) return 0; } result = check_signature(file, userid, envp, filename); - if (fclose(file) == 0) - return result; - else + if (fclose(file) == 0) { + if (result == PKG_BADSIG || result == PKG_SIGERROR) + return 0; + else + return 1; + } else return 0; } @@ -90,7 +97,11 @@ main(argc, argv, envp) char *userid = NULL; int mode; int i; + int type = TAG_ANY; +#ifndef BSD4_4 + set_program_name(argv[0]); +#endif #ifdef CHECKER_ONLY mode = CHECK; #else @@ -106,10 +117,16 @@ main(argc, argv, envp) mode = CHECK; #endif - if (check_helpers() == 0) - exit(EXIT_FAILURE); - while ((ch = getopt(argc, argv, "u:sc")) != -1) { + while ((ch = getopt(argc, argv, "u:t:sc")) != -1) { switch(ch) { + case 't': + if (strcmp(optarg, "pgp") == 0) + type = TAG_PGP; + else if (strcmp(optarg, "sha1") == 0) + type = TAG_SHA1; + else + usage(); + break; case 'u': userid = strdup(optarg); break; @@ -135,10 +152,12 @@ main(argc, argv, envp) } #ifndef CHECKER_ONLY - if (mode == SIGN) - handle_passphrase(); + if (mode == SIGN && type == TAG_ANY) + type = TAG_PGP; + if (mode == SIGN && type == TAG_PGP) + handle_pgp_passphrase(); #endif for (i = 0; i < argc; i++) - success &= (mode == SIGN ? sign : check)(argv[i], userid, envp); + success &= (mode == SIGN ? sign : check)(argv[i], type, userid, envp); exit(success == 1 ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/usr.sbin/pkg_install/sign/pgp.h b/usr.sbin/pkg_install/sign/pgp.h index cf91474e1c9..4487ce413aa 100644 --- a/usr.sbin/pkg_install/sign/pgp.h +++ b/usr.sbin/pkg_install/sign/pgp.h @@ -1,7 +1,24 @@ -/* $OpenBSD: pgp.h,v 1.1 1999/09/27 21:40:04 espie Exp $ */ -/* Hardcode size of a pgp signature */ -#define SIGNSIZE 168 +/* $OpenBSD: pgp.h,v 1.2 1999/10/04 21:46:28 espie Exp $ */ +/* Estimate size of pgp signature */ +#define MAXPGPSIGNSIZE 1024 #ifndef PGP #define PGP "/usr/local/bin/pgp" #endif + +struct mygzip_header; +struct signature; + +extern void *new_pgp_checker __P((struct mygzip_header *h, \ + struct signature *sign, const char *userid, char *envp[], \ + const char *filename)); + +extern void pgp_add __P((void *arg, const char *buffer, \ + size_t length)); + +extern int pgp_sign_ok __P((void *arg)); + +extern void handle_pgp_passphrase __P((void)); + +extern int retrieve_pgp_signature __P((const char *filename, \ +struct signature **sign, const char *userid, char *envp[])); diff --git a/usr.sbin/pkg_install/sign/pgp_check.c b/usr.sbin/pkg_install/sign/pgp_check.c new file mode 100644 index 00000000000..7615615a162 --- /dev/null +++ b/usr.sbin/pkg_install/sign/pgp_check.c @@ -0,0 +1,193 @@ +/* $OpenBSD: pgp_check.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */ +/*- + * Copyright (c) 1999 Marc Espie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Marc Espie for the OpenBSD + * Project. + * + * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD + * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <errno.h> +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <paths.h> +#include <stdlib.h> +#include <sys/stat.h> +#include "stand.h" +#include "pgp.h" +#include "gzip.h" +#include "extern.h" + +#ifndef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#endif + +/* transform current process into pgp signature checker -u userid <fd */ +static void +pgpcheck(fd, userid, envp) + int fd; + const char *userid; + char *envp[]; +{ + int fdnull; + pchar argv[6]; + int argc = 0; + + close_dangling_pipes(); + setsid(); + argv[argc++] = PGP; + argv[argc++] = "+batchmode"; + argv[argc++] = "-f"; + + if (userid) { + argv[argc++] = "-u"; + argv[argc++] = (char *)userid; + } + argv[argc++] = NULL; + + assert(argc <= sizeof argv / sizeof(pchar)); + + fdnull = open(_PATH_DEVNULL, O_RDWR); + if (fdnull == -1 || + dup2(fd, fileno(stdin)) == -1 || + dup2(fdnull, fileno(stdout)) == -1 || + close(fdnull) == -1 || close(fd) == -1 || + execve(PGP, argv, envp) == -1) + perror("launching pgp"); + exit(errno); +} + +struct pgp_checker { + pid_t id; + int fdout; + int status; +#ifdef DEBUG_DUMP + FILE *out; +#endif +}; + +void * +new_pgp_checker(h, sign, userid, envp, filename) + struct mygzip_header *h; + struct signature *sign; + const char *userid; + char *envp[]; + /*@observer@*/const char *filename; +{ + struct pgp_checker *n; + int topgpcheck[2]; + pid_t pgpid; + + assert(sign->type == TAG_PGP); + n = malloc(sizeof *n); + + { + struct stat sbuf; + + if (stat(PGP, &sbuf) == -1) { + warnx("%s does not exist", PGP); + return NULL; + } + } + if (n == NULL) { + warnx("Can't allocate pgp_checker"); + return NULL; + } + + if (pipe(topgpcheck) == -1) { + warn("Pgp checker pipe"); + free(n); + return NULL; + } + switch(pgpid = fork()) { + case -1: + warn("Pgp checker process"); + free(n); + return NULL; + case 0: + if (close(topgpcheck[1]) == -1) + exit(errno); + pgpcheck(topgpcheck[0], userid, envp); + /*@notreached@*/ + break; + default: + (void)close(topgpcheck[0]); + break; + } + n->fdout = topgpcheck[1]; + register_pipe(n->fdout, pgpid); +#ifdef DEBUG_DUMP + n->out = fopen("compare", "w"); +#endif + n->status = PKG_GOODSIG; + + pgp_add(n, sign->data, sign->length); + if (gzip_copy_header(h, sign->next, pgp_add, n) == 0) { + warnx("Unexpected header in %s", filename); + n->status = PKG_SIGERROR; + } + return n; +} + +void +pgp_add(arg, buffer, length) + void *arg; + const char *buffer; + size_t length; +{ + struct pgp_checker *n = arg; + + if (n->status == PKG_GOODSIG) { +#ifdef DEBUG_DUMP + fwrite(buffer, 1, length, n->out); +#endif + while (length > 0) { + ssize_t l = write(n->fdout, buffer, length); + if (l == -1) { + n->status = PKG_SIGERROR; + break; + } + length -= l; + buffer += l; + } + } +} + +int +pgp_sign_ok(arg) + void *arg; +{ + struct pgp_checker *n = arg; + int status = n->status; + +#ifdef DEBUG_DUMP + fclose(n->out); +#endif + if (terminate_pipe(n->fdout) != 0) + status = PKG_BADSIG; + free(n); + return status; +} diff --git a/usr.sbin/pkg_install/sign/pgp_sign.c b/usr.sbin/pkg_install/sign/pgp_sign.c new file mode 100644 index 00000000000..8997769450a --- /dev/null +++ b/usr.sbin/pkg_install/sign/pgp_sign.c @@ -0,0 +1,266 @@ +/* $OpenBSD: pgp_sign.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */ +/*- + * Copyright (c) 1999 Marc Espie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Marc Espie for the OpenBSD + * Project. + * + * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD + * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <signal.h> +#include <pwd.h> +#include <assert.h> +#include "stand.h" +#include "pgp.h" +#include "gzip.h" +#include "extern.h" + +static void +pgpsign(fdin, fdout, userid, envp) + int fdin, fdout; + const char *userid; + char *envp[]; +{ + pchar argv[10]; + int argc = 0; + + argv[argc++] = PGP; + argv[argc++] = "+batchmode"; + argv[argc++] = "+compress=off"; + argv[argc++] = "-f"; + argv[argc++] = "-s"; + + if (userid) { + argv[argc++] = "-u"; + argv[argc++] = (char *)userid; + } + argv[argc++] = NULL; + assert(argc <= sizeof argv / sizeof(pchar)); + + if (dup2(fdin, fileno(stdin)) == -1 || + dup2(fdout, fileno(stdout)) == -1 || + execve(PGP, argv, envp) == -1) + exit(errno); +} + +static struct signature * +new_pgpsignature(old) + struct signature *old; +{ + struct signature *n; + + n = malloc(sizeof(*n)); + if (n != NULL) { + n->data = malloc(MAXPGPSIGNSIZE); + if (n->data == NULL) { + free(n); + return NULL; + } + n->length = 0; + n->next = old; + n->type = TAG_PGP; + memcpy(n->tag, pgptag, sizeof pgptag); + } + return n; +} + +int +retrieve_pgp_signature(filename, sign, userid, envp) + const char *filename; + struct signature **sign; + const char *userid; + char *envp[]; +{ + int topgp[2], frompgp[2]; + pid_t pgpid; + struct mygzip_header h; + int success; + + FILE *orig, *dest, *signin; + struct signature *old; + + orig = fopen(filename, "r"); + if (orig == NULL) + return 0; + if (gzip_read_header(orig, &h, &old) == GZIP_NOT_GZIP) { + warnx("File %s is not a gzip file\n", filename); + fclose(orig); + return 0; + } + + if (pipe(topgp) == -1) { + fclose(orig); + return 0; + } + if (pipe(frompgp) == -1) { + fclose(orig); + (void)close(topgp[0]); + (void)close(topgp[1]); + return 0; + } + switch(pgpid = fork()) { + case 0: + (void)close(topgp[1]); + (void)close(frompgp[0]); + pgpsign(topgp[0], frompgp[1], userid, envp); + /*NOT REACHED */ + case -1: + (void)close(topgp[0]); + (void)close(topgp[1]); + (void)close(frompgp[0]); + (void)close(frompgp[1]); + fclose(orig); + return 0; + default: + (void)close(topgp[0]); + (void)close(frompgp[1]); + } + + dest = fdopen(topgp[1], "w"); + if (dest == NULL) { + (void)close(topgp[1]); + (void)close(frompgp[0]); + (void)reap(pgpid); + return 0; + } + + success = 1; + if (gzip_write_header(dest, &h, old) == 0) + success = 0; + else { + int c; + + while ((c = fgetc(orig)) != EOF && fputc(c, dest) != EOF) + ; + if (ferror(dest)) + success = 0; + } + if (fclose(dest) != 0) + success = 0; + + if (fclose(orig) != 0) + success = 0; + + signin = fdopen(frompgp[0], "r"); + if (signin == NULL) { + (void)close(frompgp[0]); + } else { + enum { NONE, FIRST, DONE, COPY} magic = NONE; + int c; +#ifdef DEBUG_DUMP + FILE *out = fopen("dump", "w"); +#endif + + if ((*sign = new_pgpsignature(old)) == NULL) + success = 0; + else { + while ((c = fgetc(signin)) != EOF && magic != DONE && + (*sign)->length < MAXPGPSIGNSIZE) { + switch(magic) { + case NONE: + (*sign)->data[(*sign)->length++] = c; + if ((unsigned char)c == (unsigned char)GZIP_MAGIC0) + magic = FIRST; + break; + case FIRST: + (*sign)->data[(*sign)->length++] = c; + if ((unsigned char)c == (unsigned char)GZIP_MAGIC1) +#ifdef DEBUG_DUMP + magic = COPY; +#else + magic = DONE; +#endif + else if ((unsigned char)c != (unsigned char)GZIP_MAGIC0) + magic = NONE; + break; + case DONE: + case COPY: + break; + } +#ifdef DEBUG_DUMP + fputc(c, out); +#endif + } + if ((*sign)->length == MAXPGPSIGNSIZE) + success = 0; + (*sign)->length -= 2; + sign_fill_tag(*sign); + } + fclose(signin); +#ifdef DEBUG_DUMP + fclose(out); +#endif + reap(pgpid); + } + return success; +} + +void +handle_pgp_passphrase() +{ + pid_t pid; + int fd[2]; + char *p; + + /* Retrieve the pgp passphrase */ + p = getpass("Enter passphrase:"); + + /* somewhat kludgy code to get the passphrase to pgp, see + pgp documentation for the gore + */ + if (pipe(fd) != 0) { + perror("pkg_sign"); + exit(EXIT_FAILURE); + } + switch(pid = fork()) { + case -1: + perror("pkg_sign"); + exit(EXIT_FAILURE); + case 0: + { + (void)close(fd[0]); + /* the child fills the pipe with copies of the passphrase. + Expect violent death when father exits. + */ + for(;;) { + char c = '\n'; + (void)write(fd[1], p, strlen(p)); + (void)write(fd[1], &c, 1); + } + } + default: + { + char buf[10]; + + (void)close(fd[1]); + (void)sprintf(buf, "%d", fd[0]); + (void)setenv("PGPPASSFD", buf, 1); + } + } +} + diff --git a/usr.sbin/pkg_install/sign/pkg_sign.1 b/usr.sbin/pkg_install/sign/pkg_sign.1 index 2f46b9ca094..a3b19eae961 100644 --- a/usr.sbin/pkg_install/sign/pkg_sign.1 +++ b/usr.sbin/pkg_install/sign/pkg_sign.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: pkg_sign.1,v 1.1 1999/09/27 21:40:04 espie Exp $ +.\" $OpenBSD: pkg_sign.1,v 1.2 1999/10/04 21:46:29 espie Exp $ .\" Copyright (c) 1999 Marc Espie. .\" .\" Redistribution and use in source and binary forms, with or without @@ -36,28 +36,44 @@ .Sh SYNOPSIS .Nm pkg_sign .Op Fl sc -.Op Fl u Ar userid +.Op Fl t Ar type +.Op Fl u Ar id .Op Ar .Nm pkg_check .Op Fl sc -.Op Fl u Ar userid +.Op Fl u Ar id .Op Ar .Sh DESCRIPTION .Nm pkg_sign -embeds a cryptographic signature (currently PGP) within a gzip file +embeds a cryptographic signature within a gzip file .Ar file . -It will always prompt you for a passphrase to unlock your private pgp key, -even if you don't use a passphrase (which is a bad idea, anyway). -.Nm pkg_check -cheks that cryptographic signature. +.Ar type +can be +.Li pgp +(default) or +.Li +sha1 . +If +.Ar type +is +.Li pgp , +it will always prompt you for a passphrase to unlock your private +pgp key, even if you don't use a passphrase (which is a bad idea, anyway). +If +.Ar type +is +.Li sha1 , +you must supply an +.Ar id, +which will be recorded as the name of the package, and printed as the +SHA1 checksum. .Pp -This uses a feature of the gzip format, namely that one can set a flag -.Dv EXTRA_FIELD -in the gzip header and store extra data between the gzip header and the -compressed file proper. -The OpenBSD signing scheme uses `SigPGP\\0\\xa8' -as a magic number for its signature (this marker is conveniently 8 bytes -long, and the `\\0\\xa8' is the length of the pgp signature proper). +.Nm pkg_check +checks that cryptographic signature. It currently disregards +.Ar type +and checks only the topmost signature. For sha1, it checksums the file +and verifies that the result matches the list of checksums recorded in +.Pa /var/db/pkg/SHA1 . .Pp Options .Fl s @@ -65,8 +81,8 @@ and .Fl c can be used to force package signing or signature checking mode. .Pp -The -.Ar userid +For pgp, the +.Ar id to use to sign the package or verify the signature can be forced with .Fl u . .Pp @@ -77,6 +93,15 @@ is a single dash or absent, .Nm check_sign reads from the standard input. +.Pp +Package signing uses a feature of the gzip format, namely that one can +set a flag +.Dv EXTRA_FIELD +in the gzip header and store extra data between the gzip header and the +compressed file proper. +The OpenBSD signing scheme uses eight bytes markers such `SIGPGP' \+ length +or `CKSHA1' \+ length for its signatures (those markers are conveniently +eight bytes long). .Sh RESULTS .Nm pkg_sign and @@ -99,6 +124,9 @@ This is an unsigned package. The program couldn't find a proper gzip header. .It "File %s contains an unknown extension" The extended area of the gzip file has been used for an unknown purpose. +.It "File %s uses old signatures, no longer supported" +The gzip file uses a very early version of package signing that was +substantially slower. .El .Sh BUGS .Xr pgp 1 @@ -116,8 +144,7 @@ that pgp expects on the fly. Paths to .Nm pgp and -.Nm gzcat -are hard-coded to avoid tampering and hinder flexibility. +the checksum file are hard-coded to avoid tampering and hinder flexibility. .Sh FILES .Bl -tag -width "/usr/local/bin/pgp" -compact .It Pa file.sign @@ -128,11 +155,11 @@ from .It Pa /usr/local/bin/pgp Default path to .Xr pgp 1 . -.It Pa /usr/bin/gzcat -Default path to -.Xr gzcat 1 . +.It Pa /var/db/pkgs/SHA1 +Recorded checksums. .El .Sh SEE ALSO +.Xr gzip 1 , .Xr pgp 1 , .Xr pkg_add 1 , -.Xr gzip 1 +.Xr sha1 1 diff --git a/usr.sbin/pkg_install/sign/sha1.c b/usr.sbin/pkg_install/sign/sha1.c new file mode 100644 index 00000000000..796b8cce664 --- /dev/null +++ b/usr.sbin/pkg_install/sign/sha1.c @@ -0,0 +1,212 @@ +/* $OpenBSD: sha1.c,v 1.1 1999/10/04 21:46:29 espie Exp $ */ +/*- + * Copyright (c) 1999 Marc Espie. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Marc Espie for the OpenBSD + * Project. + * + * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD + * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <sha1.h> +#include "stand.h" +#include "gzip.h" +#include "extern.h" + + +/* private context for sha1 signature checker */ +struct sha1_checker { + SHA1_CTX context; + const char *id; + const char *filename; +}; + + +#define SHA1_TEMPLATE "SHA1 (%s) = " +#define BUFSIZE (MAXID+sizeof(SHA1_TEMPLATE)+2*SHA1_DIGESTSIZE+1) + +/* Finalize SHA1 checksum for our sha1_context into result + (size at least BUFSIZE). Returns the length of the checksum + marker, e.g., SHA1 (id) = xxxxxxxxx + ^here + Return 0 for errors. + */ +size_t +sha1_build_checksum(result, n) + char *result; + struct sha1_checker *n; +{ + size_t length; + + sprintf(result, "SHA1 (%s) = ", n->id); + length = strlen(result); + SHA1End(&n->context, result + length); + strcat(result, "\n"); + free(n); + return length; +} + +void * +new_sha1_checker(h, sign, userid, envp, filename) + struct mygzip_header *h; + struct signature *sign; + const char *userid; + char *envp[]; + /*@observer@*/const char *filename; +{ + struct sha1_checker *n; + + assert(sign->type == TAG_SHA1); + /* make sure data conforms to what we can handle */ + if (sign->length > MAXID || sign->data[sign->length-1] != '\0') { + warnx("Corrupted SHA1 header in %s", filename); + return 0; + } + + n = malloc(sizeof *n); + if (n == NULL) { + warnx("Can't allocate sha1_checker"); + return NULL; + } + SHA1Init(&n->context); + n->id = sign->data; + n->filename = filename; + + /* copy header, as this is a checksum, we don't strip our own marker */ + if (gzip_copy_header(h, sign, sha1_add, n) == 0) { + warnx("Unexpected header in %s", filename); + free(n); + return 0; + } + return n; +} + +void +sha1_add(arg, buffer, length) + void *arg; + const char *buffer; + size_t length; +{ + struct sha1_checker *n = arg; + SHA1Update(&n->context, buffer, length); +} + +int +sha1_sign_ok(arg) + void *arg; +{ + struct sha1_checker *n = arg; + char buffer[BUFSIZE]; + char scan[BUFSIZE]; + size_t length; + FILE *f; + int tag_found; + + length = sha1_build_checksum(buffer, n); + f= fopen(SHA1_DB_NAME, "r"); + tag_found = 0; + + if (f == NULL) { + warn("Can't access checksum file %s", SHA1_DB_NAME); + return PKG_BADSIG; + } + while (fgets(scan, sizeof(scan), f) != NULL) { + if (strcmp(scan, buffer) == 0) { + fprintf(stderr, "Checksum ok\n"); + return PKG_GOODSIG; + } + if (strncmp(scan, buffer, length) == 0) + tag_found = 1; + } + + if (tag_found) { + warnx("Checksum incorrect for %s (%s)", n->filename, n->id); + return PKG_BADSIG; + } else { + warnx("No checksum found for %s (%s)", n->filename, n->id); + return PKG_SIGUNKNOWN; + } +} + +int +retrieve_sha1_marker(filename, sign, userid) + const char *filename; + struct signature **sign; + const char *userid; +{ + struct signature *n; + struct mygzip_header h; + FILE *f; + char buffer[1024]; + char result[BUFSIZE]; + ssize_t length; + struct sha1_checker *checker; + struct signature *old; + + *sign = NULL; + if (userid == NULL) + return 0; + + n = malloc(sizeof *n); + if (n == NULL) + return 0; + n->data = (char *)userid; + n->length = strlen(n->data)+1; + n->type = TAG_SHA1; + memcpy(n->tag, sha1tag, sizeof sha1tag); + sign_fill_tag(n); + + f = fopen(filename, "r"); + if (f == NULL) { + free(n); + return 0; + } + if (gzip_read_header(f, &h, sign) == GZIP_NOT_GZIP) { + warnx("File %s is not a gzip file\n", filename); + fclose(f); + free(n); + return 0; + } + n->next = *sign; + *sign = n; + + checker = new_sha1_checker(&h, *sign, NULL, NULL, filename); + while ((length = fread(buffer, 1, sizeof buffer, f)) > 0) + sha1_add(checker, buffer, length); + if (fclose(f) != 0 || length == -1) { + warn("Problem checksumming %s", filename); + *sign = n->next; + free(n); + return 0; + } + + (void)sha1_build_checksum(result, checker); + fputs(result, stderr); + return 1; +} + diff --git a/usr.sbin/pkg_install/sign/sign.c b/usr.sbin/pkg_install/sign/sign.c index 953dd5e867e..ea1cf554971 100644 --- a/usr.sbin/pkg_install/sign/sign.c +++ b/usr.sbin/pkg_install/sign/sign.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sign.c,v 1.2 1999/09/28 21:31:23 espie Exp $ */ +/* $OpenBSD: sign.c,v 1.3 1999/10/04 21:46:29 espie Exp $ */ /*- * Copyright (c) 1999 Marc Espie. * @@ -31,60 +31,28 @@ #include <stdlib.h> #include <unistd.h> #include <stdio.h> +#include <errno.h> #include <signal.h> #include <pwd.h> +#include <assert.h> #include "stand.h" #include "pgp.h" #include "gzip.h" #include "extern.h" -#define SIGN_TEMPLATE "%s %s | %s +batchmode +compress=off -f -s" -#define SIGN2_TEMPLATE "%s %s | %s +batchmode +compress=off -f -u %s -s" #define COPY_TEMPLATE "%s.sign" -static int -retrieve_signature(filename, sign, userid) - const char *filename; - char sign[]; - const char *userid; -{ - char *buffer; - FILE *cmd; - - if (userid) { - buffer = malloc(strlen(GZCAT) + strlen(filename) + - strlen(PGP) + strlen(userid) + sizeof(SIGN2_TEMPLATE)); - if (!buffer) - return 0; - sprintf(buffer, SIGN2_TEMPLATE, GZCAT, filename, PGP, userid); - } else { - buffer = malloc(strlen(GZCAT) + strlen(filename) + - strlen(PGP) + sizeof(SIGN_TEMPLATE)); - if (!buffer) - return 0; - sprintf(buffer, SIGN_TEMPLATE, GZCAT, filename, PGP); - } - cmd = popen(buffer, "r"); - free(buffer); - if (!cmd) - return 0; - if (fread(sign, 1, SIGNSIZE, cmd) != SIGNSIZE) - return 0; - (void)pclose(cmd); - return 1; -} - static int embed_signature_FILE(orig, dest, sign, filename) /*@temp@*/FILE *orig; /*@temp@*/FILE *dest; - const char sign[]; + struct signature *sign; const char *filename; { struct mygzip_header h; int c; - if (read_header_and_diagnose(orig, &h, NULL, filename) == 0) + if (gzip_read_header(orig, &h, NULL) == GZIP_NOT_GZIP) return 0; if (gzip_write_header(dest, &h, sign) == 0) @@ -100,7 +68,7 @@ static int embed_signature(filename, copy, sign) const char *filename; const char *copy; - const char sign[]; + struct signature *sign; { FILE *orig, *dest; int success; @@ -121,22 +89,35 @@ embed_signature(filename, copy, sign) } int -sign(filename, userid, envp) +sign(filename, type, userid, envp) const char *filename; const char *userid; - /*@unused@*/char *envp[] __attribute__((unused)); + int type; + char *envp[]; { - char sign[SIGNSIZE]; char *copy; int result; + struct signature *sign; + int success; + + switch(type) { + case TAG_PGP: + success = retrieve_pgp_signature(filename, &sign, userid, envp); + break; + case TAG_SHA1: + success =retrieve_sha1_marker(filename, &sign, userid); + break; + } - if (retrieve_signature(filename, sign, userid) == 0) { + if (!success) { fprintf(stderr, "Problem signing %s\n", filename); + free_signature(sign); return 0; } copy = malloc(strlen(filename)+sizeof(COPY_TEMPLATE)); if (copy == NULL) { fprintf(stderr, "Can't allocate memory\n"); + free_signature(sign); return 0; } sprintf(copy, COPY_TEMPLATE, filename); @@ -151,50 +132,7 @@ sign(filename, userid, envp) result = 0; } free(copy); + free_signature(sign); return result; } -void -handle_passphrase() -{ - pid_t pid; - int fd[2]; - char *p; - - /* Retrieve the pgp passphrase */ - p = getpass("Enter passphrase:"); - - /* somewhat kludgy code to get the passphrase to pgp, see - pgp documentation for the gore - */ - if (pipe(fd) != 0) { - perror("pkg_sign"); - exit(EXIT_FAILURE); - } - switch(pid = fork()) { - case -1: - perror("pkg_sign"); - exit(EXIT_FAILURE); - case 0: - { - (void)close(fd[0]); - /* the child fills the pipe with copies of the passphrase. - Expect violent death when father exits. - */ - for(;;) { - char c = '\n'; - (void)write(fd[1], p, strlen(p)); - (void)write(fd[1], &c, 1); - } - } - default: - { - char buf[10]; - - (void)close(fd[1]); - (void)sprintf(buf, "%d", fd[0]); - (void)setenv("PGPPASSFD", buf, 1); - } - } -} - diff --git a/usr.sbin/pkg_install/sign/simple_check.c b/usr.sbin/pkg_install/sign/simple_check.c deleted file mode 100644 index 4156042701f..00000000000 --- a/usr.sbin/pkg_install/sign/simple_check.c +++ /dev/null @@ -1,72 +0,0 @@ -/* $OpenBSD: simple_check.c,v 1.1 1999/09/27 21:40:04 espie Exp $ */ -/*- - * Copyright (c) 1999 Marc Espie. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Marc Espie for the OpenBSD - * Project. - * - * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS - * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD - * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <libgen.h> -#include "extern.h" - -#define CHECKER_STRING "/usr/bin/fgrep \"`cd %s && /bin/sha1 %s`\" /var/db/pkg/SHA1" -#define CHECKER2_STRING "/usr/bin/fgrep \\(%s\\) /var/db/pkg/SHA1" - -int -simple_check(pkg_name) - const char *pkg_name; -{ - int result; - char *buffer; - char *dir, *file; - - dir = dirname(pkg_name); - file = basename(pkg_name); - if (dir == NULL || file == NULL) - return PKG_SIGERROR; - - buffer = malloc(sizeof(CHECKER_STRING)+strlen(dir)+strlen(file)); - if (!buffer) - return PKG_SIGERROR; - sprintf(buffer, CHECKER_STRING, dir, file); - result = system(buffer); - free(buffer); - if (result == 0) - return PKG_GOODSIG; - buffer = malloc(sizeof(CHECKER2_STRING)+strlen(file)); - if (!buffer) - return PKG_SIGERROR; - sprintf(buffer, CHECKER2_STRING, file); - result = system(buffer); - free(buffer); - if (result == 0) - return PKG_BADSIG; - else - return PKG_UNSIGNED; -} - - diff --git a/usr.sbin/pkg_install/sign/stand.c b/usr.sbin/pkg_install/sign/stand.c new file mode 100644 index 00000000000..4788a79ced3 --- /dev/null +++ b/usr.sbin/pkg_install/sign/stand.c @@ -0,0 +1,54 @@ +#include "stand.h" + +#ifdef BSD4_4 +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> + +/* shortened version of warn */ +static const char *program_name; + +void +set_program_name(n) + const char *n; +{ + if ((program_name = strrchr(n, '/')) != NULL) + program_name++; + else + program_name = n; +} + +void +warn(const char *fmt, ...) +{ + va_list ap; + int interrno; + + va_start(ap, fmt); + + interrno = errno; + (void)fprintf(stderr, "%s", program_name); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(interrno)); + + va_end(ap); +} + +void +warnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fprintf(stderr, "%s", program_name); + if (fmt != NULL) + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, "\n"); + va_end(ap); +} + +#endif diff --git a/usr.sbin/pkg_install/sign/stand.h b/usr.sbin/pkg_install/sign/stand.h index f90d9e53477..c411ad3d2ef 100644 --- a/usr.sbin/pkg_install/sign/stand.h +++ b/usr.sbin/pkg_install/sign/stand.h @@ -1,7 +1,11 @@ -/* $OpenBSD: stand.h,v 1.1 1999/09/27 21:40:04 espie Exp $ */ +/* $OpenBSD: stand.h,v 1.2 1999/10/04 21:46:30 espie Exp $ */ /* provided to cater for BSD idiosyncrasies */ +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include <sys/param.h> +#endif + #ifndef __P #ifdef __STDC__ #define __P(x) x @@ -10,6 +14,13 @@ #endif #endif +#if defined(BSD4_4) +#include <err.h> +#else +extern void set_program_name __P((const char * name)); +extern void warn __P((const char *fmt, ...)); +extern void warnx __P((const char *fmt, ...)); +#endif #ifndef __GNUC__ #define __attribute__(x) |