From 056c05d59db742469fbbfcddbd5da0d92a38bbf8 Mon Sep 17 00:00:00 2001 From: Wes Peters Date: Tue, 6 Feb 2001 06:46:42 +0000 Subject: [PATCH] Add package signing utilities; somebody might actually want them. These are not enabled in the pkg_install Makefile as of yet; adding the "sign" directory to the SUBDIR list will enable building of sign. Submitted by: Wes Peters Obtained from: Original framework from OpenBSD 2.7, X.509 bits from DoBox. --- usr.sbin/pkg_install/sign/Makefile | 14 + usr.sbin/pkg_install/sign/README | 55 ++++ usr.sbin/pkg_install/sign/check.c | 117 +++++++ usr.sbin/pkg_install/sign/common.c | 88 ++++++ usr.sbin/pkg_install/sign/extern.h | 99 ++++++ usr.sbin/pkg_install/sign/gzip.c | 315 +++++++++++++++++++ usr.sbin/pkg_install/sign/gzip.h | 92 ++++++ usr.sbin/pkg_install/sign/main.c | 183 +++++++++++ usr.sbin/pkg_install/sign/pgp.h | 25 ++ usr.sbin/pkg_install/sign/pgp_check.c | 194 ++++++++++++ usr.sbin/pkg_install/sign/pgp_sign.c | 275 +++++++++++++++++ usr.sbin/pkg_install/sign/pkg_sign.1 | 186 +++++++++++ usr.sbin/pkg_install/sign/sha1.c | 221 ++++++++++++++ usr.sbin/pkg_install/sign/sign.c | 142 +++++++++ usr.sbin/pkg_install/sign/stand.c | 56 ++++ usr.sbin/pkg_install/sign/stand.h | 28 ++ usr.sbin/pkg_install/sign/x509.c | 424 ++++++++++++++++++++++++++ 17 files changed, 2514 insertions(+) create mode 100644 usr.sbin/pkg_install/sign/Makefile create mode 100644 usr.sbin/pkg_install/sign/README create mode 100644 usr.sbin/pkg_install/sign/check.c create mode 100644 usr.sbin/pkg_install/sign/common.c create mode 100644 usr.sbin/pkg_install/sign/extern.h create mode 100644 usr.sbin/pkg_install/sign/gzip.c create mode 100644 usr.sbin/pkg_install/sign/gzip.h create mode 100644 usr.sbin/pkg_install/sign/main.c create mode 100644 usr.sbin/pkg_install/sign/pgp.h create mode 100644 usr.sbin/pkg_install/sign/pgp_check.c create mode 100644 usr.sbin/pkg_install/sign/pgp_sign.c create mode 100644 usr.sbin/pkg_install/sign/pkg_sign.1 create mode 100644 usr.sbin/pkg_install/sign/sha1.c create mode 100644 usr.sbin/pkg_install/sign/sign.c create mode 100644 usr.sbin/pkg_install/sign/stand.c create mode 100644 usr.sbin/pkg_install/sign/stand.h create mode 100644 usr.sbin/pkg_install/sign/x509.c diff --git a/usr.sbin/pkg_install/sign/Makefile b/usr.sbin/pkg_install/sign/Makefile new file mode 100644 index 000000000000..ffb1b246f743 --- /dev/null +++ b/usr.sbin/pkg_install/sign/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ +# $OpenBSD: Makefile.bsd-wrapper,v 1.2 1999/10/07 16:30:32 espie Exp $ + +PROG= pkg_sign +SRCS= main.c check.c common.c gzip.c pgp_check.c pgp_sign.c sha1.c sign.c stand.c x509.c + +DPADD= ${LIBINSTALL} +LDADD= ${LIBINSTALL} -lcrypto + +LINKS= ${BINDIR}/pkg_sign ${BINDIR}/pkg_check +MLINKS= pkg_sign.1 pkg_check.1 + +.include + diff --git a/usr.sbin/pkg_install/sign/README b/usr.sbin/pkg_install/sign/README new file mode 100644 index 000000000000..8e81fcdf052b --- /dev/null +++ b/usr.sbin/pkg_install/sign/README @@ -0,0 +1,55 @@ +To sign packages in a transparent way: +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 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. + +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 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 $ + +-- + +X.509 notes: + +I added the ability to sign a package with an X.509 key, and to check +against a stack of X.509 certificates. This allows a "vendor" to +distribute a system with one or more certificates pre-installed, and +to add certificates in a signed package by appending them to the +default certficiate stack. + +The X.509 signatures are stored in the gzip header in the same manner +as other signatures. This is known to compile against OpenSSL +libraries on OpenBSD 2.7 and FreeBSD 5.0, your mileage may vary. + +-- + + Wes Peters, Dec 2000 + $FreeBSD$ diff --git a/usr.sbin/pkg_install/sign/check.c b/usr.sbin/pkg_install/sign/check.c new file mode 100644 index 000000000000..d92353863660 --- /dev/null +++ b/usr.sbin/pkg_install/sign/check.c @@ -0,0 +1,117 @@ +/* $FreeBSD$ */ +/* $OpenBSD: check.c,v 1.2 1999/10/04 21:46:27 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. + */ + +/* Simple code for a stand-alone package checker */ +#include +#include +#include +#include +#include "stand.h" +#include "pgp.h" +#include "gzip.h" +#include "extern.h" + +struct checker { + void *context; + void (*add)(void *, const char *, size_t); + int (*get)(void *); + int status; +}; + +#define MAX_CHECKERS 20 + +int +check_signature(file, userid, envp, filename) + /*@dependent@*/FILE *file; + const char *userid; + char *envp[]; + /*@observer@*/const char *filename; +{ + struct signature *sign; + struct mygzip_header h; + int status; + 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); + if (status != 1) + return PKG_UNSIGNED; + + 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_X509: + checker[i].context = new_x509_checker(&h, sweep, userid, envp, filename); + checker[i].add = x509_add; + checker[i].get = x509_sign_ok; + 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(); + } + } + 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 new file mode 100644 index 000000000000..8b41bcda17ef --- /dev/null +++ b/usr.sbin/pkg_install/sign/common.c @@ -0,0 +1,88 @@ +/* $FreeBSD$ */ +/* $OpenBSD: common.c,v 1.3 1999/10/07 16:30:32 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 +#include +#include +#include +#include +#include +#include "stand.h" +#include "gzip.h" +#include "pgp.h" +#include "extern.h" + +/* Ensure consistent diagnostics */ +int +read_header_and_diagnose(file, h, sign, filename) + FILE *file; + struct mygzip_header *h; + struct signature **sign; + const char *filename; +{ + switch(gzip_read_header(file, h, sign)) { + case GZIP_SIGNED: + if (sign == NULL) { + fprintf(stderr, "File %s is already signed\n", filename); + return 0; + } else + return 1; + case GZIP_UNSIGNED: + if (sign != NULL) { + fprintf(stderr, "File %s is not a signed gzip file\n", filename); + return 0; + } else + return 1; + case GZIP_NOT_GZIP: + fprintf(stderr, "File %s is not a gzip file\n", filename); + return 0; + case GZIP_NOT_PGPSIGNED: + fprintf(stderr, "File %s contains an unknown extension\n", filename); + return 0; + default: + /* this should not happen */ + abort(); + } +} + +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; +} + diff --git a/usr.sbin/pkg_install/sign/extern.h b/usr.sbin/pkg_install/sign/extern.h new file mode 100644 index 000000000000..760d39cd3788 --- /dev/null +++ b/usr.sbin/pkg_install/sign/extern.h @@ -0,0 +1,99 @@ +/* $FreeBSD$ */ +/* $OpenBSD: extern.h,v 1.3 1999/10/07 16:30:32 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. + */ + +/* 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; + +/* main.c */ +extern int verbose; +extern int quiet; +extern char *userkey; + +/* 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)); + +/* 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 retrieve_sha1_marker __P((const char *filename, \ + struct signature **sign, const char *userid)); + +/* x509.c */ +#define X509_DB_NAME "/var/db/pkg/X509" + +extern void *new_x509_checker __P((struct mygzip_header *h, \ + struct signature *sign, const char *userid, char *envp[], \ + const char *filename)); + +extern void x509_add __P((void *arg, const char *buffer, \ + size_t length)); + +extern int x509_sign_ok __P((void *arg)); + +extern int retrieve_x509_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 new file mode 100644 index 000000000000..4150bcc8ae66 --- /dev/null +++ b/usr.sbin/pkg_install/sign/gzip.c @@ -0,0 +1,315 @@ +/* $FreeBSD$ */ +/* $OpenBSD: gzip.c,v 1.3 1999/10/04 21:46:28 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 +#include +#include +#include +#include +#include +#include "stand.h" +#include "gzip.h" +#include "pgp.h" + +/* Signatures follow a simple format + (endianess was chosen to conform to gzip header format) + */ + +SIGNTAG known_tags[KNOWN_TAGS] = { + {'S', 'I', 'G', 'P', 'G', 'P', 0, 0 }, + {'C', 'K', 'S', 'H', 'A', '1', 0, 0 }, + {'C', 'R', 'X', '5', '0', '9', 0, 0 }, + {'S', 'i', 'g', 'P', 'G', 'P', 0, 0 } /* old format */ +}; + +void +sign_fill_tag(sign) + struct signature *sign; +{ + 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]; +} + +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; + } + 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; +} + + +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 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_GZIP; + h->remaining = (unsigned)c; + c = fgetc(f); + if (c == EOF) + return GZIP_NOT_PGPSIGNED; + 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; + } + 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 signatures */ +int +gzip_write_header(f, h, sign) + FILE *f; + const struct mygzip_header *h; + 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; + + 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) + 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 new file mode 100644 index 000000000000..bc64aab1445e --- /dev/null +++ b/usr.sbin/pkg_install/sign/gzip.h @@ -0,0 +1,92 @@ +/* $FreeBSD$ */ +/* $OpenBSD: gzip.h,v 1.2 1999/10/04 21:46:28 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. + */ + +#define GZIP_MAGIC0 '\037' +#define GZIP_MAGIC1 '\213' +/* flags values */ +#define CONTINUATION 0x02 +#define EXTRA_FIELD 0x04 + +/* meaningful fields in a gzip header, see gzip proper for details. + This structure should not be fiddled with outside of gzip_read_header + and gzip_write_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@*/struct signature **sign)); +/* gzip_write_header returns 1 for success */ +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 4 +#define TAG_PGP 0 +#define TAG_SHA1 1 +#define TAG_X509 2 +#define TAG_OLD 3 +#define TAG_ANY -1 +#define pgptag (known_tags[TAG_PGP]) +#define sha1tag (known_tags[TAG_SHA1]) +#define x509tag (known_tags[TAG_X509]) +extern SIGNTAG known_tags[KNOWN_TAGS]; diff --git a/usr.sbin/pkg_install/sign/main.c b/usr.sbin/pkg_install/sign/main.c new file mode 100644 index 000000000000..09df2d1db0cd --- /dev/null +++ b/usr.sbin/pkg_install/sign/main.c @@ -0,0 +1,183 @@ +/* $FreeBSD$ */ +/* $OpenBSD: main.c,v 1.2 1999/10/04 21:46:28 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 +#include +#include +#include +#include +#include +#include "stand.h" +#include "gzip.h" +#include "pgp.h" +#include "extern.h" + +#ifdef __OpenBSD__ +extern char *__progname; +#define argv0 __progname +#else +static char *argv0; +#endif + +#define NM_SIGN "pkg_sign" + +int verbose = 0; +int quiet = 0; +char *userkey = NULL; + +static void +usage() +{ + fprintf(stderr, "usage: %s [-sc] [-t type] [-u userid] [-k keyfile] pkg1 ...\n", argv0); + exit(EXIT_FAILURE); +} + +#define SIGN 0 +#define CHECK 1 + +/* wrapper for the check_signature function (open file if needed) */ +static int +check(filename, type, userid, envp) + /*@observer@*/const char *filename; + int type; + /*@null@*/const char *userid; + char *envp[]; +{ + int result; + FILE *file; + + if (strcmp(filename, "-") == 0) + return check_signature(stdin, userid, envp, "stdin"); + file = fopen(filename, "r"); + if (file == NULL) { + fprintf(stderr, "Can't open %s\n", filename); + return 0; + } + result = check_signature(file, userid, envp, filename); + if (fclose(file) == 0) { + if (result == PKG_BADSIG || result == PKG_SIGERROR) + return 0; + else + return 1; + } else + return 0; +} + +int +main(argc, argv, envp) + int argc; + char *argv[]; + char *envp[]; +{ + int success = 1; + int ch; + 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 +#ifndef __OpenBSD__ + if ((argv0 = strrchr(argv[0], '/')) != NULL) + argv0++; + else + argv0 = argv[0]; +#endif + if (strcmp(argv0, NM_SIGN) == 0) + mode = SIGN; + else + mode = CHECK; +#endif + + while ((ch = getopt(argc, argv, "t:u:k:qscv")) != -1) { + switch(ch) { + case 't': + if (strcmp(optarg, "pgp") == 0) + type = TAG_PGP; + else if (strcmp(optarg, "sha1") == 0) + type = TAG_SHA1; + else if (strcmp(optarg, "x509") == 0) + type = TAG_X509; + else + usage(); + break; + case 'u': + userid = strdup(optarg); + break; + + case 'k': + userkey = optarg; + break; + + case 'q': + quiet = 1; + break; + +#ifndef CHECKER_ONLY + case 's': + mode = SIGN; + break; +#endif + case 'c': + mode = CHECK; + break; + + case 'v': + verbose = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + if (argc == 0) { + if (mode == CHECK) + success &= check("-", 0, userid, envp); + else + usage(); + } + +#ifndef CHECKER_ONLY + 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], 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 new file mode 100644 index 000000000000..99476f2f6811 --- /dev/null +++ b/usr.sbin/pkg_install/sign/pgp.h @@ -0,0 +1,25 @@ +/* $FreeBSD$ */ +/* $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 000000000000..d1fef13d5b49 --- /dev/null +++ b/usr.sbin/pkg_install/sign/pgp_check.c @@ -0,0 +1,194 @@ +/* $FreeBSD$ */ +/* $OpenBSD: pgp_check.c,v 1.2 1999/10/07 16:30:32 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 +#include +#include +#include +#include +#include +#include +#include +#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 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(n->id = 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]; + /* so that subsequent fork() won't duplicate it inadvertently */ + (void)fcntl(n->fdout, F_SETFD, FD_CLOEXEC); +#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 (close(n->fdout) != 0) + status = PKG_SIGERROR; + if (reap(n->id) != 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 000000000000..1fada956f2de --- /dev/null +++ b/usr.sbin/pkg_install/sign/pgp_sign.c @@ -0,0 +1,275 @@ +/* $FreeBSD$ */ +/* $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 +#include +#include +#include +#include +#include +#include +#include +#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"; + argv[argc++] = "-zAthlon"; + + 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; + +printf("Short-circuiting %s\n", __FUNCTION__); +return; + + /* 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. + */ + printf("Child process %d stuffing passphrase in pipe:\n", getpid()); + for(;;) { + char c = '\n'; + (void)write(fd[1], p, strlen(p)); + (void)write(fd[1], &c, 1); + putchar('.'); fflush(stdout); + } + } + default: + { + char buf[10]; + + sleep(1); + (void)close(fd[1]); + (void)sprintf(buf, "%d", fd[0]); + (void)setenv("PGPPASSFD", buf, 1); + printf("Parent process PGPPASSFD=%d.\n", fd[0]); + } + } +} + diff --git a/usr.sbin/pkg_install/sign/pkg_sign.1 b/usr.sbin/pkg_install/sign/pkg_sign.1 new file mode 100644 index 000000000000..b6d4c3eada25 --- /dev/null +++ b/usr.sbin/pkg_install/sign/pkg_sign.1 @@ -0,0 +1,186 @@ +.\" $FreeBSD$ +.\" $OpenBSD: pkg_sign.1,v 1.6 2000/04/15 02:15:20 aaron 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. + +.Dd September 24, 1999 +.Dt PKG_SIGN 1 +.Os +.Sh NAME +.Nm pkg_sign , +.Nm check_sign +.Nd handle package signatures +.Sh SYNOPSIS +.Nm pkg_sign +.Op Fl sc +.Op Fl t Ar type +.Op Fl u Ar id +.Op Fl k Ar key +.Op Ar +.Nm pkg_check +.Op Fl sc +.Op Fl u Ar id +.Op Fl k Ar cert +.Op Ar +.Sh DESCRIPTION +.Nm pkg_sign +embeds a cryptographic signature within a gzip file +.Ar file . +.Ar type +can be +.Li pgp +(default), +.Li sha1 , +or +.Li x509 . +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 +.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 +and +.Fl c +can be used to force package signing or signature checking mode. +.Pp +For pgp, the +.Ar id +to use to sign the package or verify the signature can be forced with +.Fl u . +.Pp +For X.509, the signing key or verification certificate may be +specified with the +.Fl k +option. If not specified, packages are signed or verified with the +default keys and certificates documented below. +.Pp +If +.Ar file +is a single dash +.Pq Sq \&- +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 +.Nm pkg_check +return with an exit code > 0 if anything went wrong for any +.Ar file . +For +.Nm pkg_check , +this usually indicates that the package is not signed, or that the +signature is forged. +.Sh DIAGNOSTICS +.Bl -diag +.It "File %s is already signed" +There is a signature embedded within the gzip file already. +.Nm pkg_sign +currently does not handle multiple signatures. +.It "File %s is not a signed gzip file" +This is an unsigned package. +.It "File %s is not a gzip file" +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 +is an ill-designed program, which is hard to interface with. +For instance, the `separate signing scheme' it pretends to offer is +useless, as it can't be used with pipes, so that +.Nm pgp_sign +needs to kludge it by knowing the length of a pgp signature, and invoking +pgp in `seamless' signature mode, without compression of the main file, +and just retrieving the signature. +.Pp +The checking scheme is little less convoluted, namely we rebuild the file +that pgp expects on the fly. +.Pp +Paths to +.Nm pgp +and +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 +Temporary file built by +.Nm pkg_sign +from +.Ar file . +.It Pa /usr/local/bin/pgp +Default path to +.Xr pgp 1 . +.It Pa /var/db/pkgs/SHA1 +Recorded checksums. +.It Pa /etc/ssl/pkg.key +Default package signing key. +.It Pa /etc/ssl/pkg.crt +Default package verification certificate(s). +.El +.Sh SEE ALSO +.Xr gzip 1 , +.Xr pgp 1 , +.Xr pkg_add 1 , +.Xr sha1 1 +.Sh AUTHORS +.Nm pkg_sign +was created by Marc Espie for the OpenBSD Project. X.509 signatures +and FreeBSD support added by Wes Peters . diff --git a/usr.sbin/pkg_install/sign/sha1.c b/usr.sbin/pkg_install/sign/sha1.c new file mode 100644 index 000000000000..c16eb028084b --- /dev/null +++ b/usr.sbin/pkg_install/sign/sha1.c @@ -0,0 +1,221 @@ +/* $FreeBSD$ */ +/* $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 +#include +#include +#include +#include +#include +#include "stand.h" +#include "gzip.h" +#include "extern.h" + +/* private context for sha1 signature checker */ +struct sha1_checker { + SHA_CTX context; + const char *id; + const char *filename; +}; + + +#define SHA1_TEMPLATE "SHA1 (%s) = " +#define BUFSIZE (MAXID+sizeof(SHA1_TEMPLATE)+2*SHA_DIGEST_LENGTH+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); + SHA1_Final(result + length, &n->context); + 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; + } + SHA1_Init(&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; + SHA1_Update(&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; + + /* + * Create a blank signature and fill it with the userid. + */ + 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); + + /* + * Read the gzip header and add our "userid" signature to it. + */ + 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; + + /* + * Calculate the SHA1 of the remaining data and write it to stderr. + */ + 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 new file mode 100644 index 000000000000..1bcbcf5ff641 --- /dev/null +++ b/usr.sbin/pkg_install/sign/sign.c @@ -0,0 +1,142 @@ +/* $FreeBSD$ */ +/* $OpenBSD: sign.c,v 1.3 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 +#include +#include +#include +#include +#include +#include +#include +#include "stand.h" +#include "pgp.h" +#include "gzip.h" +#include "extern.h" + +#define COPY_TEMPLATE "%s.sign" + +static int +embed_signature_FILE(orig, dest, sign, filename) + /*@temp@*/FILE *orig; + /*@temp@*/FILE *dest; + struct signature *sign; + const char *filename; +{ + struct mygzip_header h; + int c; + + if (gzip_read_header(orig, &h, NULL) == GZIP_NOT_GZIP) + return 0; + + if (gzip_write_header(dest, &h, sign) == 0) + return 0; + while ((c = fgetc(orig)) != EOF && fputc(c, dest) != EOF) + ; + if (ferror(dest) != 0) + return 0; + return 1; +} + +static int +embed_signature(filename, copy, sign) + const char *filename; + const char *copy; + struct signature *sign; +{ + FILE *orig, *dest; + int success; + + success = 0; + orig= fopen(filename, "r"); + if (orig) { + dest = fopen(copy, "w"); + if (dest) { + success = embed_signature_FILE(orig, dest, sign, filename); + if (fclose(dest) != 0) + success = 0; + } + if (fclose(orig) != 0) + success = 0; + } + return success; +} + +int +sign(filename, type, userid, envp) + const char *filename; + const char *userid; + int type; + char *envp[]; +{ + 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; + case TAG_X509: + success = retrieve_x509_marker(filename, &sign, userid); + break; + } + + 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); + result = embed_signature(filename, copy, sign); + if (result == 0) { + fprintf(stderr, "Can't embed signature in %s\n", filename); + } else if (unlink(filename) != 0) { + fprintf(stderr, "Can't unlink original %s\n", filename); + result = 0; + } else if (rename(copy, filename) != 0) { + fprintf(stderr, "Can't rename new file %s\n", copy); + result = 0; + } + free(copy); + free_signature(sign); + return result; +} + diff --git a/usr.sbin/pkg_install/sign/stand.c b/usr.sbin/pkg_install/sign/stand.c new file mode 100644 index 000000000000..7fa84d24d8d8 --- /dev/null +++ b/usr.sbin/pkg_install/sign/stand.c @@ -0,0 +1,56 @@ +/* $FreeBSD$ */ + +#include "stand.h" + +#ifdef BSD4_4 +#include +#include +#include +#include + +/* 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 new file mode 100644 index 000000000000..bc8de2f96b35 --- /dev/null +++ b/usr.sbin/pkg_install/sign/stand.h @@ -0,0 +1,28 @@ +/* $FreeBSD$ */ +/* $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 +#endif + +#ifndef __P +#ifdef __STDC__ +#define __P(x) x +#else +#define __P(x) () +#endif +#endif + +#if defined(BSD4_4) +#include +#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) +#endif diff --git a/usr.sbin/pkg_install/sign/x509.c b/usr.sbin/pkg_install/sign/x509.c new file mode 100644 index 000000000000..27ab10a80564 --- /dev/null +++ b/usr.sbin/pkg_install/sign/x509.c @@ -0,0 +1,424 @@ +/* $FreeBSD$ */ +/*- + * Copyright (c) 2000 Softweyr LLC, South Jordan, Utah, USA. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY Softweyr LLC ``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 Softweyr LLC 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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "stand.h" +#include "gzip.h" +#include "extern.h" + + +/* + * Default names for the signing key and certificate(s) for verification. + */ +#define CERTFILE "/etc/ssl/pkg.crt" +#define KEYFILE "/etc/ssl/pkg.key" + + +/* + * Private context for X.509 signature checker + */ +struct x509_checker +{ + const char * id; + const char * filename; + + struct signature * signature; + + STACK_OF(X509) * certs; + EVP_MD_CTX rsa_ctx, dsa_ctx; + int has_rsa, has_dsa; +}; + + +static void key_from_name(char *, const char *); + +/* + * Initialize an X.509 "checker" context. + */ +void * +new_x509_checker(h, sign, userid, envp, filename) + struct mygzip_header *h; + struct signature *sign; + const char *userid; + char *envp[]; + /*@observer@*/const char *filename; +{ + FILE * fp; + struct x509_checker * me; + char certfile[PATH_MAX + 1] = CERTFILE; + char * cp; + X509 * x509; + + assert(sign->type == TAG_X509); + + /* + * Make sure data conforms to what we can handle. We do not write a + * trailing null onto the signature like some other types, because + * the X.509 signature is binary data. + */ + if (sign->length > MAXID) { + warnx("Corrupted X.509 header in %s", filename); + return 0; + } + + me = malloc(sizeof *me); + if (me == NULL) { + warn("Cannot allocate x509_checker"); + return 0; + } + me->id = sign->data; + me->filename = filename; + me->signature = sign; + me->has_rsa = 0; + me->has_dsa = 0; + + key_from_name(certfile, userkey); + + /* + * Load just the crypto library error strings. + */ + ERR_load_crypto_strings(); + + /* + * Load the stack of X.509 certs we will compare against. + * + * KLUDGE: this needs to be fleshed out a bit. We can do better + * than hard-coding the location of the cert key file. + */ + me->certs = sk_X509_new_null(); + + fp = fopen(certfile, "r"); + if (fp == NULL) { + warnx("Cannot open public key %s", certfile); + return 0; + } + + if (verbose) + printf("Loading certificates from %s:\n", certfile); + + while (x509 = PEM_read_X509(fp, NULL, NULL, 0)) { + sk_X509_push(me->certs, x509); + + switch (EVP_PKEY_type(X509_get_pubkey(x509)->type)) + { + case EVP_PKEY_RSA: + me->has_rsa = 1; + break; + + case EVP_PKEY_DSA: + me->has_dsa = 1; + break; + + default: + warnx("Uknown certificate type"); + return 0; + } + + /* + * By default, print the contents of the cert we matched so the + * user can decide if she is willing to accept a package from + * whoever signed this. + */ + if (!quiet) + X509_print_fp(stdout, x509); + } + fclose(fp); + + /* + * Initialize the verification contexts for both RSA and DSA. + */ + if (me->has_rsa) EVP_VerifyInit(&me->rsa_ctx, EVP_sha1()); + if (me->has_dsa) EVP_VerifyInit(&me->dsa_ctx, EVP_dss1()); + + return me; +} + + +/* + * "Add" another data block to an existing checker. + */ +void +x509_add(arg, buffer, length) + void *arg; + const char *buffer; + size_t length; +{ + struct x509_checker * me = arg; + + if (me->has_rsa) EVP_VerifyUpdate(&me->rsa_ctx, buffer, length); + if (me->has_dsa) EVP_VerifyUpdate(&me->dsa_ctx, buffer, length); +} + + +/* + * Finalize an existing checker and verify the signature matches one of the + * certs in our stack. + */ +int +x509_sign_ok(arg) + void *arg; +{ + struct x509_checker * n = arg; + X509 * x509; + EVP_PKEY * pkey; + EVP_MD_CTX * md_ctx; + int status; + + if (verbose) + printf("\n\n-------\n\nChecking package signature:\n"); + + while ((x509 = sk_X509_pop(n->certs)) != NULL) { + /* + * Get public key from cert. + */ + pkey = X509_get_pubkey(x509); + if (pkey == NULL) { + warnx("Getting public key:"); + ERR_print_errors_fp(stderr); + continue; + } + + if (verbose) + X509_print_fp(stdout, x509); + + switch (EVP_PKEY_type(pkey->type)) + { + case EVP_PKEY_RSA: + md_ctx = &n->rsa_ctx; + break; + + case EVP_PKEY_DSA: + md_ctx = &n->dsa_ctx; + break; + + default: + } + + status = EVP_VerifyFinal(md_ctx, + n->signature->data, + n->signature->length, + pkey); + + EVP_PKEY_free(pkey); + X509_free(x509); + + if (status == 1) { + fprintf(stderr, "X.509 signature matched\n"); + + /* + * KLUDGE: Does this free the rest of the certs, or just the + * stack itself? Enquiring minds want to know. + */ + sk_X509_free(n->certs); + return PKG_GOODSIG; + } + } + + warnx("Verifying signature:"); + ERR_print_errors_fp(stderr); + sk_X509_free(n->certs); + return PKG_BADSIG; +} + + +/* + * Sign the specified filename into sign. + */ +int +retrieve_x509_marker(filename, sign, userid) + const char * filename; + struct signature ** sign; + const char * userid; +{ + struct signature * n; + struct mygzip_header h; + FILE * f, * keyf; + char buffer[1024]; + ssize_t length; + int err; + int sig_len = 4096; + unsigned char * sig_buf; + EVP_MD_CTX md_ctx; + EVP_MD * md_type; + EVP_PKEY * pkey; + + char keyfile[PATH_MAX + 1] = KEYFILE; + char * kp; + + key_from_name(keyfile, userkey); + + 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; + } + + /* + * Sign the remaining data: + * Load just the crypto library error strings. + */ + ERR_load_crypto_strings(); + + /* + * Read private key. + */ + keyf = fopen(keyfile, "r"); + if (keyf == NULL) + { + warnx("Cannot open private key %s.", keyfile); + return 0; + } + + pkey = PEM_read_PrivateKey(keyf, NULL, NULL, 0); + fclose(keyf); + + if (pkey == NULL) + { + warnx("Reading private key %s:", keyfile); + ERR_print_errors_fp(stderr); + return 0; + } + + /* + * Do the signature. The remaining bytes of the GZIP file are the + * compressed tar image, which is what we are signing. + */ + switch (EVP_PKEY_type(pkey->type)) + { + case EVP_PKEY_RSA: + md_type = EVP_sha1(); +printf("*** It's an RSA key.\n"); + break; + + case EVP_PKEY_DSA: + md_type = EVP_dss1(); +printf("@@@ It's a DSA key, yippee!\n"); + break; + + default: + warnx("Uknown key type"); + return 0; + } + + EVP_SignInit(&md_ctx, md_type); + + while ((length = fread(buffer, 1, sizeof buffer, f)) > 0) + EVP_SignUpdate(&md_ctx, buffer, length); + + sig_buf = malloc(sig_len); + if (sig_buf == NULL) { + warnx("Cannot allocated %u bytes for signature buffer", sig_len); + return 0; + } + + err = EVP_SignFinal(&md_ctx, sig_buf, &sig_len, pkey); + + if (err != 1) + { + warnx("Creating signature:"); + ERR_print_errors_fp(stderr); + return 0; + } + + EVP_PKEY_free(pkey); + + /* + * Stuff the signature onto the head of the chain of signatures in + * the package. + */ + n = malloc(sizeof *n); + if (n == NULL) { + warnx("Cannot allocate %u bytes for new signature", sizeof *n); + return 0; + } + n->data = sig_buf; + n->length = sig_len; + n->type = TAG_X509; + memcpy(n->tag, x509tag, sizeof x509tag); + sign_fill_tag(n); + n->next = *sign; + *sign = n; + + /* + * Report our success. + */ + return 1; +} + + +static void +key_from_name(char * filename, const char * ident) +{ + char * cp; + + /* + * If an alternate keyfile was specified, treat it as the name of an + * alternate private key with which to sign or verify the package. + */ + if (ident) { + printf("Using alternate key/cert \"%s\".\n", ident); + if (strchr(ident, '/')) { + /* + * The user specified a path, take it verbatim. + */ + strncpy(filename, ident, PATH_MAX); + } else { + cp = dirname(filename); + if (cp == NULL) { + warnx("Key directory not correctly specified."); + return; + } + snprintf(filename, PATH_MAX, "%s/%s", cp, ident); + } + } + + if (verbose) + printf("Key is \"%s\".\n", filename); +}