debsig-verify-0.8/0000755000000000000000000000000007647041603011015 5ustar debsig-verify-0.8/debsig-verify.c0000644000000000000000000002735707272331401013726 0ustar /* * debsig-verify - Debian package signature verification tool * * Copyright (c) 2000 by Ben Collins * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* $Id: debsig-verify.c,v 1.24 2001/04/27 17:57:21 bcollins Exp $ * main routines */ #include #include #include #include #include #include #include #include "debsig.h" char originID[2048]; char *deb = NULL; FILE *deb_fs = NULL; char *ver_members[] = { "debian-binary", "control.tar.gz", "data.tar.gz", 0 }; static char *prog_name = NULL; static int checkSelRules(struct group *grp, const char *deb) { int opt_count = 0; struct match *mtc; int len; for (mtc = grp->matches; mtc; mtc = mtc->next) { ds_printf(DS_LEV_VER, " Processing `%s' key...", mtc->name); /* If we have an ID for this match, check to make sure it exists, and * matches the signature we are about to check. */ if (mtc->id) { char *m_id = getKeyID(mtc); char *d_id = getSigKeyID(deb, mtc->name); if (m_id == NULL || d_id == NULL || strcmp(m_id, d_id)) return 0; } /* XXX: If the match doesn't specify an ID, we need to check to * make sure the ID of the signature exists in the keyring * specified, don't we? */ len = checkSigExist(mtc->name); /* If the member exists and we reject it, fail now. Also, if it * doesn't exist, and we require it, fail aswell. */ if ((!len && mtc->type == REQUIRED_MATCH) || (len && mtc->type == REJECT_MATCH)) { return 0; } /* This would mean this is Optional, so we ignore it for now */ if (!len) continue; /* Kick up the count once for checking later */ if (mtc->type == OPTIONAL_MATCH) opt_count++; } if (opt_count < grp->min_opt) { ds_printf(DS_LEV_DEBUG, "checkSelRules: opt passed - %d, opt required %d", opt_count, grp->min_opt); return 0; } return 1; } static int verifyGroupRules(struct group *grp, const char *deb) { FILE *fp; char buf[2048], tmp_sig[32] = {'\0'}, tmp_data[32] = {'\0'}; int opt_count = 0, t, i, fd; struct match *mtc; int len; /* If we don't have any matches, we fail. We don't want blank, * take-all rules. This actually gets checked while we parse the * policy file, but we check again for good measure. */ if (grp->matches == NULL) return 0; /* Go ahead and write out our data to a temp file */ strncpy(tmp_data, "/tmp/debsig-data.XXXXXX", sizeof(tmp_data)); if ((fd = mkstemp(tmp_data)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { ds_printf(DS_LEV_ERR, "error creating temp file %s: %s\n", tmp_data, strerror(errno)); if (fd != -1) { close(fd); unlink(tmp_data); } return 0; } /* Now, let's find all the members we need to check and cat them into a * single temp file. This is what we pass to gpg. */ for (i = 0; ver_members[i]; i++) { if (!(len = findMember(ver_members[i]))) goto fail_and_close; while(len > 0) { t = fread(buf, 1, sizeof(buf), deb_fs); fwrite(buf, 1, (t > len) ? len : t, fp); len -= t; } } fclose(fp); fd = -1; for (mtc = grp->matches; mtc; mtc = mtc->next) { ds_printf(DS_LEV_VER, " Processing `%s' key...", mtc->name); /* If we have an ID for this match, check to make sure it exists, and * matches the signature we are about to check. */ if (mtc->id) { char *m_id = getKeyID(mtc); char *d_id = getSigKeyID(deb, mtc->name); if (m_id == NULL || d_id == NULL || strcmp(m_id, d_id)) goto fail_and_close; } /* This will also position deb_fs to the start of the member */ len = checkSigExist(mtc->name); /* If the member exists and we reject it, die now. Also, if it * doesn't exist, and we require it, die aswell. */ if ((!len && mtc->type == REQUIRED_MATCH) || (len && mtc->type == REJECT_MATCH)) { goto fail_and_close; } /* This would mean this is Optional, so we ignore it for now */ if (!len) continue; /* let's get our temp file */ strncpy(tmp_sig, "/tmp/debsig-sig.XXXXXX", sizeof(tmp_sig)); if ((fd = mkstemp(tmp_sig)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { ds_printf(DS_LEV_ERR, "error creating temp file %s: %s\n", tmp_sig, strerror(errno)); goto fail_and_close; } while(len > 0) { t = fread(buf, 1, sizeof(buf), deb_fs); fwrite(buf, 1, (t > len) ? len : t, fp); len -= t; } fclose(fp); /* Now, let's check with gpg on this one */ t = gpgVerify(tmp_data, mtc, tmp_sig); fd = -1; unlink(tmp_sig); /* We fail no matter what now. Even if this is an optional match * rule, by now, we know that the sig exists, so we must fail */ if (!t) { ds_printf(DS_LEV_DEBUG, "verifyGroupRules: failed for %s", mtc->name); goto fail_and_close; } /* Kick up the count once for checking later */ if (mtc->type == OPTIONAL_MATCH) opt_count++; } if (opt_count < grp->min_opt) { ds_printf(DS_LEV_DEBUG, "verifyGroupRules: opt passed - %d, opt required %d", opt_count, grp->min_opt); goto fail_and_close; } unlink(tmp_data); return 1; fail_and_close: unlink(tmp_data); if (fd != -1) { close(fd); unlink(tmp_sig); } return 0; } static int checkIsDeb(void) { int i; if (!findMember("debian-binary")) return 0; for (i = 0; ver_members[i]; i++) if (!findMember(ver_members[i])) return 0; return 1; } static void outputVersion(void) { fprintf(stderr, "\ Debsig Program Version - "VERSION"\n\ Signature Version - "SIG_VERSION"\n\ Signature Namespace - "DEBSIG_NS"\n\ Policies Directory - "DEBSIG_POLICIES_DIR"\n\ Keyrings Directory - "DEBSIG_KEYRINGS_DIR"\n"); return; } static void outputUsage(void) { fprintf(stderr, "\ Usage: %s [ options ] \n\n\ -q Quiet, only output fatal errors\n\ -v Verbose output (mainly debug)\n\ -d Debug output aswell\n\ --version Output version info, and exit\n\ --list-policies Only list policies that can be used to\n\ validate this sig. This runs through\n\ `Selection' block of the policies only.\n\ --use-policy Used in conjunction with the above\n\ option. This allows you to specify the\n\ short name of the policy you wish to try.\n", prog_name); exit(1); } int main(int argc, char *argv[]) { struct policy *pol = NULL; char buf[8192], pol_file[8192], *tmpID, *force_file = NULL; DIR *pd = NULL; struct dirent *pd_ent; struct group *grp; int i, list_only = 0; if ((prog_name = strrchr(argv[0], '/')) == NULL) prog_name = strdup(argv[0]); else prog_name = strdup(prog_name + 1); if (argc < 2) outputUsage(); for (i = 1; i < argc && argv[i][0] == '-'; i++) { if (!strcmp(argv[i], "-q")) ds_debug_level = DS_LEV_ERR; else if (!strcmp(argv[i], "-v")) ds_debug_level = DS_LEV_VER; else if (!strcmp(argv[i], "-d")) ds_debug_level = DS_LEV_DEBUG; else if (!strcmp(argv[i], "--version")) { outputVersion(); /* Make sure we exit non-zero if there are any more args. This * makes sure someone doesn't do something stupid like pass * --version and a .deb, and expect it to return a validation * exit status. */ if (argc > 2) exit(1); else exit(0); } else if (!strcmp(argv[i], "--list-policies")) { /* Just create a list of policies we can use */ list_only = 1; ds_printf(DS_LEV_ALWAYS, "Listing usable policies"); } else if (!strcmp(argv[i], "--use-policy")) { /* We take one arg */ force_file = argv[++i]; if (i == argc || force_file[0] == '-') { ds_printf(DS_LEV_ERR, "--use-policy requires an argument"); outputUsage(); } } else outputUsage(); } if (i + 1 != argc) /* There should only be one arg left */ outputUsage(); deb = argv[i]; if ((deb_fs = fopen(deb, "r")) == NULL) ds_fail_printf(DS_FAIL_INTERNAL, "could not open %s (%s)", deb, strerror(errno)); if (!list_only) ds_printf(DS_LEV_VER, "Starting verification for: %s", deb); if (!checkIsDeb()) ds_fail_printf(DS_FAIL_INTERNAL, "%s does not appear to be a deb format package", deb); if ((tmpID = getSigKeyID(deb, "origin")) == NULL) ds_fail_printf(DS_FAIL_NOSIGS, "Origin Signature check failed. This deb might not be signed.\n"); strncpy(originID, tmpID, sizeof(originID)); /* Now we have an ID, let's check the policy to use */ snprintf(buf, sizeof(buf) - 1, DEBSIG_POLICIES_DIR_FMT, originID); if ((pd = opendir(buf)) == NULL) ds_fail_printf(DS_FAIL_UNKNOWN_ORIGIN, "Could not open Origin dir %s: %s\n", buf, strerror(errno)); ds_printf(DS_LEV_VER, "Using policy directory: %s", buf); if (list_only) ds_printf(DS_LEV_ALWAYS, " Policies in: %s", buf); while ((pd_ent = readdir(pd)) != NULL && (pol == NULL || list_only)) { char *ext = strstr(pd_ent->d_name, ".pol"); /* Make sure we have the right name format */ if (ext == NULL || (ext - pd_ent->d_name) + 4 != strlen(pd_ent->d_name)) continue; if (force_file != NULL && strcmp(pd_ent->d_name, force_file)) continue; /* Now try to parse the file */ snprintf(pol_file, sizeof(pol_file) - 1, "%s/%s", buf, pd_ent->d_name); ds_printf(DS_LEV_VER, " Parsing policy file: %s", pol_file); pol = parsePolicyFile(pol_file); if (pol == NULL) continue; /* Now let's see if this policy's selection is useful for this .deb */ ds_printf(DS_LEV_VER, " Checking Selection group(s)."); for (grp = pol->sels; grp != NULL; grp = grp->next) { if (!checkSelRules(grp, deb)) { clear_policy(); ds_printf(DS_LEV_VER, " Selection group failed checks."); pol = NULL; break; } } if (pol && list_only) { ds_printf(DS_LEV_ALWAYS, " Usable: %s", pd_ent->d_name); list_only++; } else if (pol) ds_printf(DS_LEV_VER, " Selection group(s) passed, policy is usable."); } closedir(pd); if ((pol == NULL && !list_only) || list_only == 1) /* Damn, can't verify this one */ ds_fail_printf(DS_FAIL_NOPOLICIES, "No applicable policy found."); if (list_only) exit(0); /* our job is done */ ds_printf(DS_LEV_VER, "Using policy file: %s", pol_file); /* This should actually be caught in the xml-parsing. */ if (pol->vers == NULL) ds_fail_printf(DS_FAIL_NOPOLICIES, "Failed, no Verification groups in policy."); /* Now the final test */ ds_printf(DS_LEV_VER, " Checking Verification group(s)."); for (grp = pol->vers; grp; grp = grp->next) { if (!verifyGroupRules(grp, deb)) { ds_printf(DS_LEV_VER, " Verification group failed checks."); ds_fail_printf(DS_FAIL_BADSIG, "Failed verification for %s.", deb); } } ds_printf(DS_LEV_VER, " Verification group(s) passed, deb is validated."); ds_printf(DS_LEV_INFO, "Verified package from `%s' (%s)", pol->description, pol->name); /* If we get here, then things passed just fine */ exit(DS_SUCCESS); } debsig-verify-0.8/Makefile0000644000000000000000000000233407232627723012462 0ustar CC = gcc CFLAGS = -Wall -g -O2 LDFLAGS = -lxmltok -lxmlparse #TESTING=1 ifndef TESTING DEBSIG_KEYRINGS_DIR=/usr/share/debsig/keyrings DEBSIG_POLICIES_DIR=/etc/debsig/policies else DEBSIG_KEYRINGS_DIR=$(shell pwd)/testing/keyrings DEBSIG_POLICIES_DIR=$(shell pwd)/testing/policies endif PROGRAM = debsig-verify OBJS = xml-parse.o ar-parse.o gpg-parse.o debsig-verify.o misc.o CFLAGS += -DDEBSIG_POLICIES_DIR=\"$(DEBSIG_POLICIES_DIR)\" \ -DDEBSIG_KEYRINGS_DIR=\"$(DEBSIG_KEYRINGS_DIR)\" MANPAGES = debsig-verify.1 all: $(PROGRAM) $(MANPAGES) $(PROGRAM): $(OBJS) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $@ install: all install -d -m755 $(DESTDIR)/usr/bin install -m755 $(PROGRAM) $(DESTDIR)/usr/bin/$(PROGRAM) install -d -m755 $(DESTDIR)$(DEBSIG_POLICIES_DIR) install -d -m755 $(DESTDIR)$(DEBSIG_KEYRINGS_DIR) for mpage in $(MANPAGES); do \ num=`echo $$mpage | sed 's,.*\.,,'`; \ install -d -m755 $(DESTDIR)/usr/share/man/man$$num; \ install $$mpage $(DESTDIR)/usr/share/man/man$$num/$$mpage; \ done clean: rm -f debsig-verify $(OBJS) $(MANPAGES) %.o: %.c debsig.h $(CC) $(CFLAGS) -c $< -o $@ %.1: docs/%.1.in sed -e 's,@POLICIES_DIR@,$(DEBSIG_POLICIES_DIR),g' \ -e 's,@KEYRINGS_DIR@,$(DEBSIG_KEYRINGS_DIR),g' < $< > $@ debsig-verify-0.8/testing/0000755000000000000000000000000007272331711012466 5ustar debsig-verify-0.8/testing/policies/0000755000000000000000000000000007272331711014275 5ustar debsig-verify-0.8/testing/policies/7CD73F641E04EC2D/0000755000000000000000000000000007272331711016234 5ustar debsig-verify-0.8/testing/policies/7CD73F641E04EC2D/generic.pol0000644000000000000000000000162007213522006020355 0ustar debsig-verify-0.8/testing/policies/7CD73F641E04EC2D/potato-release.pol0000644000000000000000000000142507213522104021667 0ustar debsig-verify-0.8/testing/debs/0000755000000000000000000000000007272331711013403 5ustar debsig-verify-0.8/testing/debs/sigtest1_1.0-1_all.deb0000644000000000000000000000163607213466760017204 0ustar ! debian-binary/ 975967825 0 0 100644 4 ` 2.0 control.tar.gz/ 975967825 0 0 100644 253 ` Q,:JD!`}}HDt `{$PsfjR[PzU/!)T+4?-Ug"S;uOu+b),|̿q#ͻvv]J~͛mn:ls)$;q&eL76m Bs^إ.-&.`zjo[ 2 Nyn( data.tar.gz/ 975967825 0 0 100644 228 ` Q,:ҽ af\A93bԊ?1D]{Bhus|8bMݐ~_sEN mbT޻? z4m.b SzL v7Qg7Wl9hÜgNyʊnѽ/J6~M:P#"SP& =(_gpgorigin/ 975968023 1000 1000 100644 65 ` ?:,|?d-^#%Av71 0IOs("' _gpgmaint/ 976121316 1000 1000 100644 65 ` ?:,|?d-^#%Av71 0IOs("' debsig-verify-0.8/testing/debs/sigtest2_2.0-1_all.deb0000644000000000000000000000203407213036701017163 0ustar ! debian-binary/ 975967834 0 0 100644 4 ` 2.0 control.tar.gz/ 975967834 0 0 100644 254 ` Z,:J!p}ͪ;ѿ 6wL u?g% ~ GTn'D/ahu{=G%B8Jh``WD,T{wV&ŚSX2)y3?_Ε@{];;QRWk.c'R8AX۲y6c9^إ.-&o.1=Y5}K^S >:OI(data.tar.gz/ 975967834 0 0 100644 228 ` Z,:ҽ af\A93bԊ?1D]{Bhus|8bMݐ~_sEN mbT޻? z4m.b SzL v7Qg7Wl9hÜgNyʊnѽ/J6~M:P#"SP& =(_gpgmaint/ 975977921 1000 1000 100644 65 ` ?:,=|?d-Q*ś0pVrNN_Yeaf3av _gpgorigin/ 975968030 1000 1000 100644 65 ` ?:,|?d-pC.&FQ))a >?zF@| _gpgrelease/ 975968959 1000 1000 100644 65 ` ?:,|?d-kCzA*F7x"ɔZgdr[: ?Tm粅О0 debsig-verify-0.8/testing/keyrings/0000755000000000000000000000000007272331711014321 5ustar debsig-verify-0.8/testing/keyrings/7CD73F641E04EC2D/0000755000000000000000000000000007272331711016260 5ustar debsig-verify-0.8/testing/keyrings/7CD73F641E04EC2D/debian-debsig.gpg0000644000000000000000000000405007212630555021435 0ustar 7+˃ V}\O`&0uiAXkt8x6^Lcv!R3Lj5zsJ?1uZ@>$_4 J"~ѻlq:MW-NkhO>;w)lT F:s2Y߳Bqi۠7)5 ]ybdf(#Ď3כ$8ޙll~A !Ben Collins U7  |?d-*.jq}lW42Qqzn/> #-a#Ben Collins U7X  |?d-t oZӾq40;nH*Dm<҈F8_ Ћ,m *{BƮJzkPQLmKNBen Collins U7  |?d-%\nƌr=w&6ZpR·\ӈF8_ Ћ,m k$1V9pb  |?d-C1|ٟzJ[x#L;֨/&Ben Collins F8_ Ћ,m F8_ Ћ,m @wt5Ԩ&y(W&DOe&6NkC޹;x]7;*=ZAf aOgEV#~]S5 dD\, Ci:@pǎ - 62=YwRϭ 0)Ə ޖ 3]t\ȍ! \{pF9F[ sTCNex/S;}b=#ޟ2q9YDQsRU7  |?d-^yej´YjEӬYm:,7F8^ o JZIϞ͙pFtuaKT?1Ykq2fݲ 7j/PlfwO:3~~邒eeJ#:f鷑bSF|F=@0p@3YWUfS&PjdllI^sR>afWD7`QˌY6QtfD_Ovw/<"Wo-[ˉvkv 03qq@K1&G:X-\;>k0_+NF q؆4 ӭf[O#RvF7j |?d-W4Z6n.[\Bg]LLhe ]debsig-verify-0.8/testing/keyrings/7CD73F641E04EC2D/debian-keyring.gpg0000777000000000000000000000000007647041203031060 2/usr/share/keyrings/debian-keyring.gpgustar debsig-verify-0.8/ar-parse.c0000644000000000000000000001050007272331401012657 0ustar /* * debsig-verify - Debian package signature verification tool * * Copyright (c) 2000 by Ben Collins * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* $Id: ar-parse.c,v 1.6 2001/04/27 17:57:21 bcollins Exp $ * processes ar style archives (the format of a .deb package) */ #include #include #include #include #include #include #include "debsig.h" /* borrowed from dpkg */ static unsigned long parseLength(const char *inh, size_t len) { char buf[16]; unsigned long r; char *endp; if (memchr(inh, 0, len)) ds_fail_printf(DS_FAIL_INTERNAL, "parseLength: member lenght contains NULL's"); assert(sizeof(buf) > len); memcpy(buf, inh, len); buf[len]= ' '; *strchr(buf,' ') = 0; r = strtoul(buf,&endp,10); if (*endp) ds_fail_printf(DS_FAIL_INTERNAL, "parseLength: archive is corrupt - bad digit `%c' member length", *endp); return r; } /* This function takes a member name as an argument. It then goes through * the archive trying to find it. If it does, it returns the size of the * member's data, and leaves the deb_fs file pointer at the start of that * data. Yes, we may have a zero length member in here somewhere, but * nothing important is going to be zero length anyway, so we treat it as * "non-existant". */ size_t findMember(const char *name) { char magic[SARMAG+1]; struct ar_hdr arh; long mem_len; int len = strlen(name); if (len > sizeof(arh.ar_name)) { ds_printf(DS_LEV_DEBUG, "findMember: `%s' is too long to be an archive member name", name); return 0; } /* This shouldn't happen, but... */ if (deb_fs == NULL) ds_fail_printf(DS_FAIL_INTERNAL, "findMember: called while deb_fs == NULL"); rewind(deb_fs); if (!fgets(magic,sizeof(magic),deb_fs)) ds_fail_printf(DS_FAIL_INTERNAL, "findMember: failure to read package (%s)", strerror(errno)); /* We will fail in main() with this one */ if (strcmp(magic,ARMAG)) { ds_printf(DS_LEV_DEBUG, "findMember: archive has bad magic"); return 0; } while(!feof(deb_fs)) { if (fread(&arh, 1, sizeof(arh),deb_fs) != sizeof(arh)) { if (ferror(deb_fs)) ds_fail_printf(DS_FAIL_INTERNAL, "findMember: error while parsing archive header (%s)", strerror(errno)); return 0; } if (memcmp(arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag))) ds_fail_printf(DS_FAIL_INTERNAL, "findMember: archive appears to be corrupt, fmag incorrect"); if ((mem_len = parseLength(arh.ar_size, sizeof(arh.ar_size))) < 0) ds_fail_printf(DS_FAIL_INTERNAL, "findMember: archive appears to be corrupt, negative member length"); /* * If all looks well, then we return the length of the member, and * leave the file pointer where it is (at the start of the data). * The logic here is based on the ar spec. The ar_name field is * padded with spaces to get the full length. The actual name may * also be suffixed with '/' (dpkg-deb creates .deb's without the * trailing '/' in the member names, but binutils ar does, so we * try to be compatible, like dpkg does). We don't support the * "extended naming" scheme that binutils does. */ if (!strncmp(arh.ar_name, name, len) && (len == sizeof(arh.ar_name) || arh.ar_name[len] == '/' || arh.ar_name[len] == ' ')) return (size_t)mem_len; /* fseek to the start of the next member, and try again */ if (fseek(deb_fs, mem_len + (mem_len & 1), SEEK_CUR) == -1 && ferror(deb_fs)) ds_fail_printf(DS_FAIL_INTERNAL, "findMember: error during file seek (%s)", strerror(errno)); } /* well, nothing found, so let's pass on the bad news */ return 0; } debsig-verify-0.8/gpg-parse.c0000644000000000000000000001252007272331565013051 0ustar /* * debsig-verify - Debian package signature verification tool * * Copyright (c) 2000 by Ben Collins * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* $Id: gpg-parse.c,v 1.11 2001/04/27 17:59:17 bcollins Exp $ * routines to parse gpg output */ #include #include #include #include #include #include #include #include "debsig.h" static int gpg_inited = 0; /* Crazy damn hack to make sure gpg has created ~/.gnupg, else it will * fail first time called */ static void gpg_init(void) { if (gpg_inited) return; system(GPG_PROG" --options /dev/null < /dev/null > /dev/null 2>&1"); gpg_inited = 1; } char *getKeyID (const struct match *mtc) { static char buf[2048]; FILE *ds; char *c, *d, *ret = mtc->id; if (ret == NULL) return NULL; gpg_init(); snprintf(buf, sizeof(buf) - 1, GPG_PROG" "GPG_ARGS_FMT" --list-packets -q "DEBSIG_KEYRINGS_FMT, GPG_ARGS, originID, mtc->file); if ((ds = popen(buf, "r")) == NULL) { perror("gpg"); return NULL; } c = fgets(buf, sizeof(buf), ds); while (c != NULL) { if (!strncmp(buf, USER_MAGIC, strlen(USER_MAGIC))) { if ((c = strchr(buf, '"')) == NULL) continue; d = c + 1; if ((c = strchr(d, '"')) == NULL) continue; *c = '\0'; if (!strcmp(d, mtc->id)) { c = fgets(buf, sizeof(buf), ds); if (c == NULL) continue; if (!strncmp(buf, SIG_MAGIC, strlen(SIG_MAGIC))) { if ((c = strchr(buf, '\n')) != NULL) *c = '\0'; d = strstr(buf, "keyid"); if (d) { ret = d + 6; break; } } } } c = fgets(buf, sizeof(buf), ds); } pclose(ds); if (ret == NULL) ds_printf(DS_LEV_DEBUG, " getKeyID: failed for %s", mtc->id); else ds_printf(DS_LEV_DEBUG, " getKeyID: mapped %s -> %s", mtc->id, ret); return ret; } char *getSigKeyID (const char *deb, const char *type) { static char buf[2048]; int pread[2], pwrite[2], len = checkSigExist(type), t; pid_t pid; FILE *ds_read, *ds_write; char *c, *ret = NULL; if (!len) return NULL; gpg_init(); /* Fork for gpg, keeping a nice pipe to read/write from. */ pipe(pread);pipe(pwrite); /* I like file streams, so sue me :P */ if ((ds_read = fdopen(pread[0], "r")) == NULL || (ds_write = fdopen(pwrite[1], "w")) == NULL) ds_fail_printf(DS_FAIL_INTERNAL, "error opening file stream for gpg"); if (!(pid = fork())) { /* Here we go */ dup2(pread[1],1); close(pread[0]); close(pread[1]); dup2(pwrite[0],0); close(pwrite[0]); close(pwrite[1]); execl(GPG_PROG, "gpg", GPG_ARGS, "--list-packets", "-q", "-", NULL); exit(1); } close(pread[1]); close(pwrite[0]); /* First, let's feed gpg our signature. Don't forget, our call to * checkSigExist() above positioned the deb_fs file pointer already. */ t = fread(buf, 1, sizeof(buf), deb_fs); while(len > 0) { if (t > len) fwrite(buf, 1, len, ds_write); else fwrite(buf, 1, t, ds_write); len -= t; t = fread(buf, 1, sizeof(buf), deb_fs); } if (ferror(ds_write)) ds_fail_printf(DS_FAIL_INTERNAL, "error writing to gpg"); fclose(ds_write); /* Now, let's see what gpg has to say about all this */ c = fgets(buf, sizeof(buf), ds_read); while (c != NULL) { if (!strncmp(buf, SIG_MAGIC, strlen(SIG_MAGIC))) { if ((c = strchr(buf, '\n')) != NULL) *c = '\0'; /* This is the only line we care about */ ret = strstr(buf, "keyid"); if (ret) { ret += 6; break; } } c = fgets(buf, sizeof(buf), ds_read); } if (ferror(ds_read)) ds_fail_printf(DS_FAIL_INTERNAL, "error reading from gpg"); fclose(ds_read); waitpid(pid, NULL, 0); if (ret == NULL) ds_printf(DS_LEV_DEBUG, " getSigKeyID: failed for %s", type); else ds_printf(DS_LEV_DEBUG, " getSigKeyID: got %s for %s key", ret, type); return ret; } int gpgVerify(const char *data, struct match *mtc, const char *sig) { char keyring[8192]; int status; pid_t pid; struct stat st; gpg_init(); snprintf(keyring, sizeof(keyring) - 1, DEBSIG_KEYRINGS_FMT, originID, mtc->file); if (stat(keyring, &st)) { ds_printf(DS_LEV_DEBUG, "gpgVerify: could not stat %s", keyring); return 0; } if (!(pid = fork())) { if (DS_LEV_DEBUG < ds_debug_level) { close(0); close(1); close(2); } execl(GPG_PROG, "gpg", GPG_ARGS, "--keyring", keyring, "--verify", sig, data, NULL); exit(1); } waitpid(pid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status)) { ds_printf(DS_LEV_DEBUG, "gpgVerify: gpg exited abnormally or with non-zero exit status"); return 0; } return 1; } debsig-verify-0.8/debian/0000755000000000000000000000000011756254375012247 5ustar debsig-verify-0.8/debian/changelog0000644000000000000000000000452611756147775014135 0ustar debsig-verify (0.8) unstable; urgency=low * QA upload. * Maintainer field set to QA Group. * Standards-Version bumped to 3.9.3. * Add dependency on ${misc:Depends}. * Debhelper compatibility level set to 9. Build dependency on debhelper updated accordingly. * build-{arch,indep} targets added to debian/rules. * Deprecated dh_clean -k replaced with dh_prep. -- Emanuele Rocca Sun, 20 May 2012 09:25:43 +0000 debsig-verify (0.7) unstable; urgency=low * Upload to main proper -- Ben Collins Tue, 15 Apr 2003 13:37:34 -0400 debsig-verify (0.6) unstable; urgency=low * Redesigned ds_fail_printf * Initialize gpg before using it -- Ben Collins Fri, 27 Apr 2001 13:55:52 -0400 debsig-verify (0.5) unstable; urgency=low * Some formatting changes * add "debian-binary" to members that are part of the signed data * For the selection phase, do not actually check the signatures with gpg. Just make sure they exist, and match if the ID is specified * If -d is specified, allow gpg output to stdout/stderr -- Ben Collins Sat, 14 Apr 2001 00:41:03 -0400 debsig-verify (0.4) unstable; urgency=low * Fix execl in gpgVerify() * Make sure files end in .pol * Add some more debug output -- Ben Collins Thu, 8 Mar 2001 10:50:01 -0500 debsig-verify (0.3) unstable; urgency=low * debian/control: Suggests debian-keyring, Section non-US/main -- Ben Collins Sun, 21 Jan 2001 13:48:08 -0500 debsig-verify (0.2) unstable; urgency=low * Added --list-policies, to get a list of applicable policies for a .deb (do not verify the contents). Also added a --use-policy so the user can specify one of the shortnames in the list from the list option. * Added manpage for debsig-verify * Added some code to free up alloc'd memory used by parsePolicy() * Lots of code cleanups * Added a -d option for debug output * Change to use "gpg --verify " intead of stdin. The newest gpg changed this behavior to fix a security issue. * Use obstack to alloc policy data in xml-parts.c -- Ben Collins Tue, 12 Dec 2000 17:41:53 -0500 debsig-verify (0.1) unstable; urgency=low * Original setup -- Ben Collins Mon, 4 Dec 2000 20:21:32 -0500 debsig-verify-0.8/debian/copyright0000644000000000000000000000020207235347473014173 0ustar This work is Copyright 2001 by Ben Collins It is licensed under the GPL v2 /usr/share/common-licenses/GPL debsig-verify-0.8/debian/rules0000755000000000000000000000140011756253465013321 0ustar #!/usr/bin/make -f STAMP_DIR = debian build: build-arch build-indep build-arch: build-stamp build-indep: build-stamp build-stamp: $(STAMP_DIR)/build-stamp $(STAMP_DIR)/build-stamp: dh_testdir $(MAKE) touch $(STAMP_DIR)/build-stamp clean: dh_testdir rm -f $(STAMP_DIR)/*-stamp $(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_prep dh_installdirs $(MAKE) DESTDIR="$(shell pwd)/debian/debsig-verify" install binary-indep: binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs docs/policy-syntax.txt docs/policy.dtd dh_installexamples testing/policies/*/*.pol dh_installdeb dh_strip dh_compress dh_fixperms dh_md5sums dh_shlibdeps dh_gencontrol dh_builddeb binary: binary-indep binary-arch debsig-verify-0.8/debian/compat0000644000000000000000000000000211756134565013443 0ustar 9 debsig-verify-0.8/debian/control0000644000000000000000000000067111756134572013652 0ustar Source: debsig-verify Section: admin Priority: standard Maintainer: Debian QA Group Build-Depends: libxmltok1-dev, debhelper (>=9) Standards-Version: 3.9.3 Package: debsig-verify Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, gnupg Suggests: debian-keyring Description: Debian Package Signature Verification Tool This tool inspects and verifies package signatures based on predetermined policies. debsig-verify-0.8/misc.c0000644000000000000000000000302007265750736012121 0ustar /* * debsig-verify - Debian package signature verification tool * * Copyright (c) 2000 by Ben Collins * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* $Id: misc.c,v 1.4 2000/12/12 22:44:10 bcollins Exp $ * miscellaneous functions */ #include #include #include "debsig.h" int ds_debug_level = 1; void ds_printf(int level, const char *fmt, ...) { va_list ap; char buf[8096]; if (level >= ds_debug_level) { va_start(ap, fmt); snprintf(buf, sizeof(buf) - 1, "debsig: %s\n", fmt); (void) vprintf (buf, ap); va_end(ap); } return; } int checkSigExist(const char *name) { char buf[16]; if (name == NULL) { ds_printf(DS_LEV_DEBUG, "checkSigExist: NULL values passed"); return 0; } snprintf(buf, sizeof(buf) - 1, "_gpg%s", name); return findMember(buf); } debsig-verify-0.8/debsig.h0000644000000000000000000000544407272331401012422 0ustar /* * debsig-verify - Debian package signature verification tool * * Copyright (c) 2000 by Ben Collins * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* $Id: debsig.h,v 1.12 2001/04/27 17:57:21 bcollins Exp $ */ #define DEBSIG_POLICIES_DIR_FMT DEBSIG_POLICIES_DIR"/%s" #define DEBSIG_KEYRINGS_FMT DEBSIG_KEYRINGS_DIR"/%s/%s" #define GPG_PROG "/usr/bin/gpg" /* This is so ugly, but easy */ #define GPG_ARGS_FMT "%s %s %s" #define GPG_ARGS "--no-options", "--no-default-keyring", "--batch" #define SIG_MAGIC ":signature packet:" #define USER_MAGIC ":user ID packet:" #define OPTIONAL_MATCH 1 #define REQUIRED_MATCH 2 #define REJECT_MATCH 3 #define VERSION "0.6" #define SIG_VERSION "1.0" #define DEBSIG_NS "http://www.debian.org/debsig/"SIG_VERSION"/" struct match { struct match *next; int type; char *name; char *file; char *id; int day_expiry; }; struct group { struct group *next; struct match *matches; int min_opt; }; struct policy { char *name; char *id; char *description; struct group *sels; struct group *vers; }; struct policy *parsePolicyFile(const char *filename); size_t findMember(const char *name); int checkSigExist(const char *name); char *getKeyID (const struct match *mtc); char *getSigKeyID (const char *deb, const char *type); int gpgVerify(const char *data, struct match *mtc, const char *sig); void clear_policy(void); /* Debugging and failures */ #define DS_LEV_ALWAYS 3 #define DS_LEV_ERR 2 #define DS_LEV_INFO 1 #define DS_LEV_VER 0 #define DS_LEV_DEBUG -1 #define DS_SUCCESS 0 #define DS_FAIL_NOSIGS 10 #define DS_FAIL_UNKNOWN_ORIGIN 11 #define DS_FAIL_NOPOLICIES 12 #define DS_FAIL_BADSIG 13 #define DS_FAIL_INTERNAL 14 extern void ds_printf(int level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); #define ds_fail_printf(myexit, fmt, args...) \ do { \ ds_printf(DS_LEV_ERR, fmt, ##args); \ exit(myexit); \ } while(0) extern int ds_debug_level; extern FILE *deb_fs; extern char *deb; extern char originID[]; extern char *ver_members[]; debsig-verify-0.8/xml-parse.c0000644000000000000000000002100407272331401013056 0ustar /* * debsig-verify - Debian package signature verification tool * * Copyright (c) 2000 by Ben Collins * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This Program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this Program; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ /* $Id: xml-parse.c,v 1.8 2001/04/27 17:57:21 bcollins Exp $ * provides the XML parsing code for policy files, via expat (xmltok) */ #include #include #include #include #include #include #include #include #include "debsig.h" #define obstack_chunk_alloc xmalloc #define obstack_chunk_free free static int parse_err_cnt; static struct policy ret; static struct group *cur_grp = NULL; static XML_Parser parser; static struct obstack deb_obs; static int deb_obs_init = 0; #define parse_error(fmt, args...) \ { \ parse_err_cnt++; \ ds_printf(DS_LEV_DEBUG , "%d: " fmt , XML_GetCurrentLineNumber(parser) , ## args); \ } static void *xmalloc(size_t size) { register void *p = malloc(size); if (p == NULL) ds_fail_printf(DS_FAIL_INTERNAL, "out of memory"); return p; } static void startElement(void *userData, const char *name, const char **atts) { int i, depth; int *depthPtr = userData; /* save the current and increment the userdata */ depth = *depthPtr; *depthPtr = depth + 1; if (!strcmp(name,"Policy")) { if (depth != 0) parse_error("policy parse error: `Policy' found at wrong level"); for (i = 0; atts[i]; i += 2) { if (!strcmp(atts[i], "xmlns")) { if (strcmp(atts[i+1], DEBSIG_NS)) parse_error("policy name space != " DEBSIG_NS); } else parse_error("Policy element contains unknown attribute `%s'", atts[i]); } } else if (!strcmp(name,"Origin")) { if (depth != 1) parse_error("policy parse error: `Origin' found at wrong level"); for (i = 0; atts[i]; i += 2) { if (!strcmp(atts[i], "id")) ret.id = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1])); else if (!strcmp(atts[i], "Name")) ret.name = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1])); else if (!strcmp(atts[i], "Description")) ret.description = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1])); else parse_error("Origin element contains unknown attribute `%s'", atts[i]); } if (ret.id == NULL || ret.name == NULL) parse_error("Origin element missing Name or ID attribute"); } else if (!strcmp(name,"Selection") || !strcmp(name,"Verification")) { struct group *g = NULL; if (depth != 1) parse_error("policy parse error: `Selection/Verification' found at wrong level"); /* create a new entry, make it the current */ cur_grp = (struct group *)obstack_alloc(&deb_obs, sizeof(struct group)); if (cur_grp == NULL) ds_fail_printf(DS_FAIL_INTERNAL, "out of memory"); memset(cur_grp, 0, sizeof(struct group)); if (!strcmp(name,"Selection")) { if (ret.sels == NULL) ret.sels = cur_grp; else g = ret.sels; } else { if (ret.vers == NULL) ret.vers = cur_grp; else g = ret.vers; } if (g) { for ( ; g->next; g = g->next) ; /* find the end of the chain */ g->next = cur_grp; } for (i = 0; atts[i]; i += 2) { if (!strcmp(atts[i], "MinOptional")) { int t; const char *c = atts[i+1]; for (t = 0; c[t]; t++) { if (!isdigit(c[t])) parse_error("MinOptional requires a numerical value"); } cur_grp->min_opt = atoi(c); } else { parse_error("Selection/Verification element contains unknown attribute `%s'", atts[i]); } } } else if (!strcmp(name,"Required") || !strcmp(name,"Reject") || !strcmp(name,"Optional")) { struct match *m = NULL, *cur_m = NULL; if (depth != 2) parse_error("policy parse error: Match element found at wrong level"); /* This should never happen with the other checks in place */ if (cur_grp == NULL) { parse_error("policy parse error: No current group for match element"); return; } /* create a new entry, make it the current */ cur_m = (struct match *)obstack_alloc(&deb_obs, sizeof(struct match)); if (cur_m == NULL) ds_fail_printf(DS_FAIL_INTERNAL, "out of memory"); memset(cur_m, 0, sizeof(struct match)); if (cur_grp->matches == NULL) cur_grp->matches = cur_m; else { for (m = cur_grp->matches; m->next; m = m->next) ; /* find the end of the chain */ m->next = cur_m; } /* Set the attributes first, so we can sanity check the type after */ for (i = 0; atts[i]; i += 2) { if (!strcmp(atts[i], "Type")) { cur_m->name = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1])); } else if (!strcmp(atts[i], "File")) { cur_m->file = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));; } else if (!strcmp(atts[i], "id")) { cur_m->id = obstack_copy0(&deb_obs, atts[i+1], strlen(atts[i+1]));; } else if (!strcmp(atts[i], "Expiry")) { int t; const char *c = atts[i+1]; for (t = 0; c[t]; t++) { if (!isdigit(c[t])) parse_error("Expiry requires a numerical value"); } cur_m->day_expiry = atoi(c); } else { parse_error("Match element contains unknown attribute `%s'", atts[i]); } } if (!strcmp(name,"Required")) { cur_m->type = REQUIRED_MATCH; if (cur_m->name == NULL || cur_m->file == NULL) parse_error("Required must have a Type and File attribute"); } else if (!strcmp(name,"Optional")) { cur_m->type = OPTIONAL_MATCH; if (cur_m->name == NULL || cur_m->file == NULL) parse_error("Optional must have a Type and File attribute"); } else { /* Reject */ cur_m->type = REJECT_MATCH; if (cur_m->name == NULL) parse_error("Reject must have a Type attribute"); } } } static void endElement(void *userData, const char *name) { int *depthPtr = userData; *depthPtr -= 1; if (!strcmp(name,"Selection") || !strcmp(name,"Verification")) { struct match *m; int i = 0; /* sanity check this block */ for (m = cur_grp->matches; m; m = m->next) { if (m->type == OPTIONAL_MATCH || m->type == REQUIRED_MATCH) i++; } if (!i) { parse_error("Selection/Verification block does not contain any " "Required or Optional matches."); } cur_grp = NULL; /* just to make sure */ } } void clear_policy(void) { if (deb_obs_init) { obstack_free(&deb_obs, 0); deb_obs_init = 0; } memset(&ret, '\0', sizeof(struct policy)); return; } struct policy *parsePolicyFile(const char *filename) { char buf[BUFSIZ]; int done, depth = 0; FILE *pol_fs; struct stat st; /* clear and initialize */ parser = XML_ParserCreate(NULL); //clear_policy(); obstack_init(&deb_obs); deb_obs_init = 1; ds_printf(DS_LEV_DEBUG, " parsePolicyFile: parsing `%s'", filename); if (stat(filename, &st)) { ds_printf(DS_LEV_ERR, "parsePolicyFile: could not stat %s", filename); return NULL; } if (!S_ISREG(st.st_mode)) { ds_printf(DS_LEV_ERR, "parsePolicyFile: %s is not a regular file", filename); return NULL; } if ((pol_fs = fopen(filename, "r")) == NULL) { ds_printf(DS_LEV_ERR, "parsePolicyFile: could not open `%s' (%s)", filename, strerror(errno)); return NULL; } XML_SetUserData(parser, &depth); XML_SetElementHandler(parser, startElement, endElement); parse_err_cnt = 0; do { size_t len = fread(buf, 1, sizeof(buf), pol_fs); done = len < sizeof(buf); if (!XML_Parse(parser, buf, len, done)) { ds_printf(DS_LEV_DEBUG, "%s at line %d", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); parse_err_cnt++; break; } } while (!done); XML_ParserFree(parser); fclose(pol_fs); ds_printf(DS_LEV_DEBUG, " parsePolicyFile: completed"); if (parse_err_cnt) { ds_printf(DS_LEV_DEBUG, " parsePolicyFile: %d errors during parsing, failed", parse_err_cnt); clear_policy(); return NULL; } return &ret; } debsig-verify-0.8/COPYING0000644000000000000000000004311007213514143012040 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. debsig-verify-0.8/ChangeLog0000644000000000000000000000611107272331226012563 0ustar Fri Apr 27 13:53:32 EDT 2001 Ben Collins * debsig.h: Redefine ds_fail_printf with exit values, and macros. Also, add __attributes__ to ds_printf so gcc will warn us on misuse * ar-parse.c: Convert to new ds_fail_printf * debsig-verify.c: ditto * gpg-parse.c: ditto * xml-parse.c: ditto * docs/policy-syntax.txt: Update terms for handling policy selection Thu Mar 8 10:50:14 EST 2001 Ben Collins * gpg-parse.c (gpgVerify): Make sure we add NULL to the end of execl call. Also add some better debug output. * debsig-verify.c (main): Extra check to be sure policy files end in .pol. Also add some better debug output. Tue Dec 12 17:39:55 EST 2000 Ben Collins * Add a -d (debug) command line option. * debsig-verify.1: Document it * debsig.h: Add a DS_LEV define for it * debsig-verify.c: Some better formatting, and use the DEBUG level for some. * .c: use the DEBUG level for lots of the function info Mon Dec 11 14:35:49 EST 2000 Ben Collins * debsig-verify.c: main(): Better formatting for the Selection/Verification verbose output. * gpg-parse.c: getKeyID()/getSigKeyID(): Extra verbose output, plus formatting. Mon Dec 11 11:18:56 EST 2000 Ben Collins * xml-parse.c: increment depthPtr at start of function to get rid of the goto * debsig-verify.c: checkIsDeb(): new function, functionality moved from main(). Mon Dec 11 10:47:49 EST 2000 Ben Collins * debsig-verify.c: outputUsage(): new function * debsig-verify.c: main(): Use it. Also, handle argv[0] better (prog_name). * gpg-parse.c: gpgVerify(): do not pass --always-trust to gpg. Fri Dec 8 09:22:05 EST 2000 Ben Collins * debsig.h:ds_fail_printf: new macro to print DS_LEV_ERR message and exit(1). Used throughout now. * xml-parse.c: Convert PARSE_ERROR macro to parse_error using proper stdarg format. Thu Dec 7 21:28:40 EST 2000 Ben Collins * xml-parse.c:clear_policy(): Used to free the memory used by the policy struct. Call it after a parse error, and before start of parse. * debsig-verify.c:main(): call clear_policy() if we fail, even though parsing succeeded. Thu Dec 7 16:18:56 EST 2000 Ben Collins * docs/debsig-verify.1.in: Move to here, and generate at build so we get the right paths in the file. Thu Dec 7 13:30:43 EST 2000 Ben Collins * docs/debsig-verify.1: new file, man page. Thu Dec 7 10:50:17 EST 2000 Ben Collins * Added a --use-policy option, which takes the shortname of a policy that was listed with --list-policies. Allows the user to specify the policy they want to use. Thu Dec 7 02:24:50 EST 2000 Ben Collins * debsig-verify.c: Added --list-policies option. Mon Dec 4 19:17:46 EST 2000 Ben Collins * Added parsing to pull out the keyID from a full name. E.g. "Name " -> 28219193292JSAJALS debsig-verify-0.8/docs/0000755000000000000000000000000007272331711011741 5ustar debsig-verify-0.8/docs/policy.dtd0000644000000000000000000000171307213636703013743 0ustar debsig-verify-0.8/docs/debsig-verify.1.in0000644000000000000000000001033207215525072015170 0ustar .\" Copyright (c) 2000 by Ben Collins .\" .\" Covered under the GPL v2 .\" .\" $Id: debsig-verify.1.in,v 1.3 2000/12/12 22:44:10 bcollins Exp $ .\" .TH DEBSIG-VERIFY 1 .SH NAME debsig-verify \- Verify signatures for a Debian format package .SH SYNOPSIS .TP 6 \fBdebsig-verify\fR [\fBoptions\fR] <\fBdeb\fR> .SH DESCRIPTION This program is part of a security model that verifies the source and validity of a Debian format package (commonly refered to as a \fBdeb\fR). .PP This program implements the verification specs defined in the document, "\fBPackage Verification with dpkg: Implementation\fR", which is a more complete reference for the verification procedure. .PP The program generally takes one argument, the \fBdeb\fR file to be verified. It will then check the \fBorigin\fR signature of the \fBdeb\fR, find its Public Key ID (long format), and use that as the name for a policy subdirectory. If this subdirectory does not exist, then the verification fails immediately. .PP In this subdirectory, the program finds one or more files named with the \fB.pol\fR file extension, which signifies an XML format policy definition. This file contains three main parts. .PP .TP .BR Origin Information about the origin of this policy. .TP .BR Selection Rules used to decide if this policy is pertinent to this \fBdeb\fR's verification. .TP .BR Verification Rules that are used to actually verify the \fBdeb\fR. .PP The policy files will reference keyrings by a filename. These keyrings will be looked for in a subdirectory of the keyring directory. The subdirectory has the same name as the policy subdirectory (previously determined by the Origin's Public Key ID). .PP The program will, after first parsing the entire file, check the Origin ID against the Public Key ID of the \fBorigin\fR signature in the \fBdeb\fR. If these match (which they should, else something is really wrong), then it will proceed to the \fBSelection\fR rules. .PP The \fBSelection\fR rules decide whether this policy is suitable for verifying this \fBdeb\fR. If these rules fail, then the program will proceed to the next policy. If it passes, then the program commits to using this policy for verification, and no other policies will be referenced. .PP The last verification step relies on the \fBVerification\fR rules. These are similar in format to the \fBSelection\fR rules, but are usually more constrained. If these rules fail, the program exits with a non-zero status. If they pass, then it exits with a zero status. .SH OPTIONS .TP .BR -q Causes the program to send no output, other than fatal errors. This is useful when being called from another program, where you rely on the exit value only. .TP .BR -v Causes the program to send more output on execution, so as to follow the steps it is taking while trying to verify the \fBdeb\fR. .TP .BR -d Outputs even more info than the \fB-v\fR option. This is mainly for debugging. .TP .BR --version Outputs the version information for the program. This includes the policy format version. This option does not require any other arguments. .TP .BR --list-policies Outputs a list of the policies that passed the \fBSelection\fR phase of the verification process. In other words, those that could potentially verify the \fBdeb\fR. The output is one line showing the directory selected by the \fBorigin\fR signature, and then a single line for any policy files in that directory that pass the \fBSelection\fR rules. This option will \fBNOT\fR verify the \fBdeb\fR. .TP .BR --use-policy\ This option takes one argument, which is the name of the policy file (as shown by the \fB--list-policies\fR option). Note, this is just a file, and not a full path. You cannot specifiy arbitrary policies. This option is useful if more than one policy applies to potentially verifying the \fBdeb\fR. The program will then use this policy, and only this policy, to try and verify the \fBdeb\fR. .SH FILES .TP .BR @POLICIES_DIR@/ Directory containing the policy (.pol) definitions. .TP .BR @POLICIES_DIR@/*/*.pol XML format policy files. .TP .BR @KEYRINGS_DIR@/ Directory containing the keyrings that coincide with the policies. .TP .BR @KEYRINGS_DIR@/*/*.gpg GPG format keyrings for use by the policies. .SH SEE ALSO .BR deb (5), .SH AUTHOR Ben Collins debsig-verify-0.8/docs/TODO0000644000000000000000000000266107232577372012451 0ustar - Is there a GnuPG library we can link against instead of execing gpg? * Yes, there is PGG, but it is merely a wrapper around the GPG binary. A very good wrapper, but it is hugely overweight for what I need. Basically this may be a dead issue. Otherwise, I should probably start using the --with-colon and --status-fd output for better parsing of the verify and keyring output. - Figure out how to integrate this with the package tools (apt, dpkg etc..) - Expiry still needs to be handled - Add some more info to the verbose output. STATUS: in progress - Testing setup. This is the way I envision it. Basically we have directories with sample data. Each directory contains some related parts of a Debian file (control.tar.gz, data.tar.gz), signatures and policy's/keyrings. We then have some XML files (god I love XML :) that describe the tests. This includes which parts to use to build the debs, which policies to configure for checking it, and the expected outcome of the checks. Need a program that parses this and performs the jobs. One drawback is, how can we tell debsig-verify where to find the polices and keyrings, since it is hardcoded? Should we allow command line options to override this? - Obviously this needs some serious auditing, not matter how good I think my coding is. Generally I've used static buffers and length constrained functions (snprintf, strncmp) where ever possible. - i18n debsig-verify-0.8/docs/policy-syntax.txt0000644000000000000000000000743007265753133015341 0ustar The .pol files are in XML format. The main node is the Policy node. Each .pol file can only contain one Policy node. Under that, there are three main nodes of the Policy, which are Origin, Selection and Verification. The Origin node describes the supplier of this policy, and subsequent packages verified by it. E.g. "debian" is one type of origin, and "progeny" may be another. The Selection node is used to determine the rules to decide if this policy will apply to this package. Since an origin can supply more than one policy, this is useful. E.g. Debian could supply a "generic" policy and a "Potato Release" policy. Debian CD's may also include a policy for verifying that particular set of packages. This node is a list of matches that describe how to evaluate the signatures in the package. If the Selection block does not pass, then the verification tool will continue to other policies. The Verification block is the same format as the Selection block. It is only used if the Selection block passes. If the Selection block passes, and the Verification block does not, then the package is assumed to not be verified, and the verification program will exit with a non-zero status, indicating failure. There can be more than one Verification or Selection block. However, there must be atleast one of each, else the policy will fail irregardless. All blocks of the particular type must pass, or all are considered to fail. Origin - Description of this policy. This *must* be the first node in the Policy file. Name - short name Description - descriptive text (duh :) ID - The GPG keyID of this Policy's Origin key, sanity check Selection - The matching rules which decide if we want to use this rule set. Basically the rules in this group decide if we use this set. If they pass, we commit to using this Policy. If it fails, the checker will go and try another Policy. The signatures in this set are not actually verified via gpg. They are merely checked for existence. If an ID is specified for a match, then that ID is also checked. Verification - Once we commit to using this Policy, the matching rules in this block must verify without error before we declare the package to be "ok". If this fails, no further policies will be checked and the package is considered to be insecure. MinOptional - This is the minimum number of Optional types that must be present and verified for this group. This is given as an attribute to the Verfication and Selection nodes. There are three types of matching rules within the two blocks. Required - The Type must be present, and must verify. Optional - If the Type is present, it must verify. If it isn't present, we ignore this match. Being "present" means that the "Type" exists in the .deb. If the ID is included, then the sig in the deb must contain the same ID for it to be considered "present". Reject - If this type is present, we fail. This is mostly useful for Selection groups, to manage several policies under one Origin. Type - short string that matches the name of the sig file in the deb. File - the name of the file (sans path) that contains the public key for this signature. Expiry - Number of days old since this sig was created (not yet enforced). The sig creation can be no older than this. ID - If given, the specific keyID to validate against. Otherwise, any key in the keyring specified by "File" will suffice. This is useful if you want several important keys in one keyring, and also for specifying a "group" of keys for one type (like the maintainer keyring). For the "Required" and "Optional" matches, the "Type" and "File" attribute must be given. ID is optional for both cases. For the "Reject" match, only the "Type" attribute is used.