diff options
Diffstat (limited to 'usr.sbin/pkg_install')
-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) |