[pacman-dev] [PATCH] RFC: sample/rough implementation to verify detached sigs for pkg.tar.gz
This code will attempt to fire every time we call pkg_load in libalpm, which happens every time we open a package file (during sync, upgrades, and -Qp). It checks for the existence of either a .sig or .asc file, and if present, attempts to verify them using gpgv. Right now a keyring file is hardcoded to /tmp/pacman.keyring; add any keys here that are to be seen as 'valid'. This is rough, there isn't enough error checking or any way of automatically prompting to ask if a key should be considered valid, etc. We also use the lighter weight gpgv rather than the full blown gpg. I'm not the biggest fan of all this process spawning code we have using popen and friends, but I don't know of a lot of other ways around it besides implementing something like git has to spawn child processes. Signed-off-by: Dan McGee <dan@archlinux.org> --- lib/libalpm/alpm.h | 1 + lib/libalpm/be_package.c | 102 +++++++++++++++++++++++++++++++++++++++++----- lib/libalpm/error.c | 2 + lib/libalpm/package.c | 2 +- src/pacman/query.c | 4 +- 5 files changed, 98 insertions(+), 13 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 62a517b..30f9a97 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -503,6 +503,7 @@ enum _pmerrno_t { /* Packages */ PM_ERR_PKG_NOT_FOUND, PM_ERR_PKG_INVALID, + PM_ERR_PKG_BAD_SIG, PM_ERR_PKG_OPEN, PM_ERR_PKG_LOAD, PM_ERR_PKG_CANT_FRESH, diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index 85112fd..6eb597c 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -19,12 +19,15 @@ #include "config.h" +#include <sys/types.h> +#include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <ctype.h> #include <locale.h> /* setlocale */ +#include <errno.h> /* libarchive */ #include <archive.h> @@ -122,6 +125,74 @@ static int parse_descfile(struct archive *a, pmpkg_t *newpkg) return(0); } +const char *keyring = "/tmp/pacman.keyring"; + +/** + * Check for the existence of a PGP signature file and verify it if it is + * found. Either a .sig or .asc file can be checked and verified. + * pm_errno is set to PM_ERR_PKG_BAD_SIG in addition to returning 1 if a + * signature is invalid. + * @param pkgfile path to the package file + * @return -1 if no signature, 0 on success, 1 on verification failure + */ +static int check_pgp(const char *pkgfile) +{ + char *pgpfile; + struct stat pgpst; + pid_t pid; + int ret = 0; + + /* check for a PGP .sig or .asc file */ + MALLOC(pgpfile, strlen(pkgfile) + 5, RET_ERR(PM_ERR_MEMORY, 1)); + sprintf(pgpfile, "%s.sig", pkgfile); + if(stat(pgpfile, &pgpst) != 0) { + sprintf(pgpfile, "%s.asc", pkgfile); + if(stat(pgpfile, &pgpst) != 0) { + /* we had neither a .sig or .asc file present */ + FREE(pgpfile); + return(-1); + } + } + + /* we found a signature, verify it */ + /* fork and execute */ + pid = fork(); + if(pid == -1) { + /* fork failure code */ + _alpm_log(PM_LOG_ERROR, _("could not fork a new process (%s)\n"), + strerror(errno)); + ret = 1; + } else if(pid == 0) { + /* child process code */ + int retval; + /* hide all output */ + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + retval = execlp("gpgv", "", "--keyring", keyring, + pgpfile, (char *)NULL); + exit(retval); + } else { + /* parent process code */ + pid_t retpid; + int status; + retpid = waitpid(pid, &status, 0); + if(retpid == -1) { + _alpm_log(PM_LOG_ERROR, _("call to waitpid failed (%s)\n"), + strerror(errno)); + ret = 1; + } else { + /* check the return status, make sure it is 0 (success) */ + if(WIFEXITED(status) && WEXITSTATUS(status) != 0) { + pm_errno = PM_ERR_PKG_BAD_SIG; + ret = 1; + } + } + } + + FREE(pgpfile); + return(ret); +} + /** * Load a package and create the corresponding pmpkg_t struct. * @param pkgfile path to the package file @@ -131,7 +202,7 @@ static int parse_descfile(struct archive *a, pmpkg_t *newpkg) */ static pmpkg_t *pkg_load(const char *pkgfile, unsigned short full) { - int ret = ARCHIVE_OK; + int ret; int config = 0; struct archive *archive; struct archive_entry *entry; @@ -144,6 +215,25 @@ static pmpkg_t *pkg_load(const char *pkgfile, unsigned short full) RET_ERR(PM_ERR_WRONG_ARGS, NULL); } + /* attempt to stat the file, ensure it exists */ + if(stat(pkgfile, &st) == 0) { + + newpkg = _alpm_pkg_new(); + if(newpkg == NULL) { + RET_ERR(PM_ERR_MEMORY, NULL); + } + newpkg->size = st.st_size; + + ret = check_pgp(pkgfile); + if(ret == 1) { + /* failed verification */ + RET_ERR(PM_ERR_PKG_BAD_SIG, NULL); + } + } else { + /* couldn't stat the pkgfile, return an error */ + RET_ERR(PM_ERR_PKG_OPEN, NULL); + } + if((archive = archive_read_new()) == NULL) { RET_ERR(PM_ERR_LIBARCHIVE, NULL); } @@ -156,16 +246,6 @@ static pmpkg_t *pkg_load(const char *pkgfile, unsigned short full) RET_ERR(PM_ERR_PKG_OPEN, NULL); } - newpkg = _alpm_pkg_new(); - if(newpkg == NULL) { - archive_read_finish(archive); - RET_ERR(PM_ERR_MEMORY, NULL); - } - - if(stat(pkgfile, &st) == 0) { - newpkg->size = st.st_size; - } - /* If full is false, only read through the archive until we find our needed * metadata. If it is true, read through the entire archive, which serves * as a verfication of integrity and allows us to create the filelist. */ diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 05caf8e..884b0b6 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -111,6 +111,8 @@ const char SYMEXPORT *alpm_strerror(int err) return _("could not find or read package"); case PM_ERR_PKG_INVALID: return _("invalid or corrupted package"); + case PM_ERR_PKG_BAD_SIG: + return _("invalid PGP signature"); case PM_ERR_PKG_OPEN: return _("cannot open package file"); case PM_ERR_PKG_LOAD: diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index 685a411..0af43c3 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -895,7 +895,7 @@ pmpkg_t *_alpm_pkg_find(alpm_list_t *haystack, const char *needle) return(info); } } - return(NULL); + RET_ERR(PM_ERR_PKG_NOT_FOUND, NULL); } /** Test if a package should be ignored. diff --git a/src/pacman/query.c b/src/pacman/query.c index 74d3ff2..52e66d9 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -416,7 +416,9 @@ int pacman_query(alpm_list_t *targets) } if(pkg == NULL) { - pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname); + pm_fprintf(stderr, PM_LOG_ERROR, + _("could not query package \"%s\" (%s)\n"), + strname, alpm_strerrorlast()); ret++; continue; } -- 1.5.5.3
participants (1)
-
Dan McGee