From 7af0ab1cde9398c938a7a221aca5787934a16121 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Mon, 27 Jun 2011 16:29:49 -0500 Subject: signing: move to new signing verification and return scheme This gives us more granularity than the former Never/Optional/Always trifecta. The frontend still uses these values temporarily but that will be changed in a future patch. * Use 'siglevel' consistenly in method names, 'level' as variable name * The level becomes an enum bitmask value for flexibility * Signature check methods now return a array of status codes rather than a simple integer success/failure value. This allows callers to determine whether things such as an unknown signature are valid. * Specific signature error codes mostly disappear in favor of the above returned status code; pm_errno is now set only to PKG_INVALID_SIG or DB_INVALID_SIG as appropriate. Signed-off-by: Dan McGee --- lib/libalpm/alpm.h | 67 +++++++++++++------ lib/libalpm/be_package.c | 20 +++--- lib/libalpm/be_sync.c | 30 ++++----- lib/libalpm/db.c | 16 ++--- lib/libalpm/db.h | 4 +- lib/libalpm/dload.c | 5 +- lib/libalpm/error.c | 12 ++-- lib/libalpm/handle.c | 15 +++-- lib/libalpm/handle.h | 2 +- lib/libalpm/package.h | 2 +- lib/libalpm/signing.c | 168 +++++++++++++++++++++++++++++++++++++---------- lib/libalpm/signing.h | 5 +- lib/libalpm/sync.c | 6 +- src/pacman/conf.c | 38 ++++++----- src/pacman/conf.h | 2 +- src/pacman/query.c | 2 +- src/pacman/sync.c | 2 +- src/pacman/upgrade.c | 4 +- src/util/cleanupdelta.c | 3 +- src/util/pactree.c | 3 +- src/util/testdb.c | 3 +- src/util/testpkg.c | 3 +- 22 files changed, 270 insertions(+), 142 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index e733eb2d..049eae2f 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -87,14 +87,31 @@ typedef enum _alpm_fileconflicttype_t { } alpm_fileconflicttype_t; /** - * GPG signature verification options + * PGP signature verification options */ -typedef enum _pgp_verify_t { - PM_PGP_VERIFY_UNKNOWN, - PM_PGP_VERIFY_NEVER, - PM_PGP_VERIFY_OPTIONAL, - PM_PGP_VERIFY_ALWAYS -} pgp_verify_t; +typedef enum _alpm_siglevel_t { + ALPM_SIG_PACKAGE = (1 << 0), + ALPM_SIG_PACKAGE_OPTIONAL = (1 << 1), + ALPM_SIG_PACKAGE_MARGINAL_OK = (1 << 2), + ALPM_SIG_PACKAGE_UNKNOWN_OK = (1 << 3), + + ALPM_SIG_DATABASE = (1 << 10), + ALPM_SIG_DATABASE_OPTIONAL = (1 << 11), + ALPM_SIG_DATABASE_MARGINAL_OK = (1 << 12), + ALPM_SIG_DATABASE_UNKNOWN_OK = (1 << 13), + + ALPM_SIG_USE_DEFAULT = (1 << 31) +} alpm_siglevel_t; + +/** + * PGP signature verification return codes + */ +typedef enum _alpm_sigstatus_t { + ALPM_SIGSTATUS_VALID = 0, + ALPM_SIGSTATUS_MARGINAL, + ALPM_SIGSTATUS_UNKNOWN, + ALPM_SIGSTATUS_BAD +} alpm_sigstatus_t; /* * Structures @@ -173,6 +190,15 @@ typedef struct _alpm_backup_t { char *hash; } alpm_backup_t; +/** Signature result. Contains the number of signatures found and pointers to + * arrays containing key and status info. All contained arrays have size + * #count.*/ +typedef struct _alpm_sigresult_t { + int count; + alpm_sigstatus_t *status; + char **uid; +} alpm_sigresult_t; + /* * Logging facilities */ @@ -335,8 +361,8 @@ int alpm_option_set_usedelta(alpm_handle_t *handle, int usedelta); int alpm_option_get_checkspace(alpm_handle_t *handle); int alpm_option_set_checkspace(alpm_handle_t *handle, int checkspace); -pgp_verify_t alpm_option_get_default_sigverify(alpm_handle_t *handle); -int alpm_option_set_default_sigverify(alpm_handle_t *handle, pgp_verify_t level); +alpm_siglevel_t alpm_option_get_default_siglevel(alpm_handle_t *handle); +int alpm_option_set_default_siglevel(alpm_handle_t *handle, alpm_siglevel_t level); /** @} */ @@ -364,12 +390,12 @@ alpm_list_t *alpm_option_get_syncdbs(alpm_handle_t *handle); /** Register a sync database of packages. * @param handle the context handle * @param treename the name of the sync repository - * @param check_sig what level of signature checking to perform on the + * @param level what level of signature checking to perform on the * database; note that this must be a '.sig' file type verification - * @return a alpm_db_t* on success (the value), NULL on error + * @return an alpm_db_t* on success (the value), NULL on error */ alpm_db_t *alpm_db_register_sync(alpm_handle_t *handle, const char *treename, - pgp_verify_t check_sig); + alpm_siglevel_t level); /** Unregister a package database. * @param db pointer to the package database to unregister @@ -391,11 +417,11 @@ const char *alpm_db_get_name(const alpm_db_t *db); /** Get the signature verification level for a database. * Will return the default verification level if this database is set up - * with PM_PGP_VERIFY_UNKNOWN. + * with ALPM_SIG_USE_DEFAULT. * @param db pointer to the package database * @return the signature verification level */ -pgp_verify_t alpm_db_get_sigverify_level(alpm_db_t *db); +alpm_siglevel_t alpm_db_get_siglevel(alpm_db_t *db); /** Check the validity of a database. * This is most useful for sync databases and verifying signature status. @@ -473,13 +499,13 @@ int alpm_db_set_pkgreason(alpm_db_t *db, const char *name, alpm_pkgreason_t reas * @param filename location of the package tarball * @param full whether to stop the load after metadata is read or continue * through the full archive - * @param check_sig what level of package signature checking to perform on the + * @param level what level of package signature checking to perform on the * package; note that this must be a '.sig' file type verification * @param pkg address of the package pointer * @return 0 on success, -1 on error (pm_errno is set accordingly) */ int alpm_pkg_load(alpm_handle_t *handle, const char *filename, int full, - pgp_verify_t check_sig, alpm_pkg_t **pkg); + alpm_siglevel_t, alpm_pkg_t **pkg); /** Free a package. * @param pkg package pointer to free @@ -715,9 +741,9 @@ alpm_list_t *alpm_pkg_unused_deltas(alpm_pkg_t *pkg); * Signatures */ -int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg); +int alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, alpm_sigresult_t *result); -int alpm_db_check_pgp_signature(alpm_db_t *db); +int alpm_db_check_pgp_signature(alpm_db_t *db, alpm_sigresult_t *result); /* * Groups @@ -1025,6 +1051,7 @@ enum _alpm_errno_t { ALPM_ERR_DB_NOT_NULL, ALPM_ERR_DB_NOT_FOUND, ALPM_ERR_DB_INVALID, + ALPM_ERR_DB_INVALID_SIG, ALPM_ERR_DB_VERSION, ALPM_ERR_DB_WRITE, ALPM_ERR_DB_REMOVE, @@ -1044,15 +1071,15 @@ enum _alpm_errno_t { ALPM_ERR_PKG_NOT_FOUND, ALPM_ERR_PKG_IGNORED, ALPM_ERR_PKG_INVALID, + ALPM_ERR_PKG_INVALID_SIG, ALPM_ERR_PKG_OPEN, ALPM_ERR_PKG_CANT_REMOVE, ALPM_ERR_PKG_INVALID_NAME, ALPM_ERR_PKG_INVALID_ARCH, ALPM_ERR_PKG_REPO_NOT_FOUND, /* Signatures */ - ALPM_ERR_SIG_MISSINGDIR, + ALPM_ERR_SIG_MISSING, ALPM_ERR_SIG_INVALID, - ALPM_ERR_SIG_UNKNOWN, /* Deltas */ ALPM_ERR_DLT_INVALID, ALPM_ERR_DLT_PATCHFAILED, diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index af213241..46bdaed0 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -233,7 +233,7 @@ static int parse_descfile(alpm_handle_t *handle, struct archive *a, alpm_pkg_t * */ alpm_pkg_t *_alpm_pkg_load_internal(alpm_handle_t *handle, const char *pkgfile, int full, const char *md5sum, const char *base64_sig, - pgp_verify_t check_sig) + alpm_siglevel_t level) { int ret; int config = 0; @@ -271,14 +271,12 @@ alpm_pkg_t *_alpm_pkg_load_internal(alpm_handle_t *handle, const char *pkgfile, } _alpm_log(handle, ALPM_LOG_DEBUG, "base64_sig: %s\n", base64_sig); - if(check_sig != PM_PGP_VERIFY_NEVER) { - _alpm_log(handle, ALPM_LOG_DEBUG, "checking signature for %s\n", pkgfile); - ret = _alpm_gpgme_checksig(handle, pkgfile, base64_sig); - if((check_sig == PM_PGP_VERIFY_ALWAYS && ret != 0) || - (check_sig == PM_PGP_VERIFY_OPTIONAL && ret == 1)) { - alpm_pkg_free(newpkg); - RET_ERR(handle, ALPM_ERR_SIG_INVALID, NULL); - } + if(level & ALPM_SIG_PACKAGE && + _alpm_check_pgp_helper(handle, pkgfile, base64_sig, + level & ALPM_SIG_PACKAGE_OPTIONAL, level & ALPM_SIG_PACKAGE_MARGINAL_OK, + level & ALPM_SIG_PACKAGE_UNKNOWN_OK, ALPM_ERR_PKG_INVALID_SIG)) { + _alpm_pkg_free(newpkg); + return NULL; } /* next- try to create an archive object to read in the package */ @@ -396,12 +394,12 @@ error: } int SYMEXPORT alpm_pkg_load(alpm_handle_t *handle, const char *filename, int full, - pgp_verify_t check_sig, alpm_pkg_t **pkg) + alpm_siglevel_t level, alpm_pkg_t **pkg) { CHECK_HANDLE(handle, return -1); ASSERT(pkg != NULL, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1)); - *pkg = _alpm_pkg_load_internal(handle, filename, full, NULL, NULL, check_sig); + *pkg = _alpm_pkg_load_internal(handle, filename, full, NULL, NULL, level); if(*pkg == NULL) { /* pm_errno is set by pkg_load */ return -1; diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c index 368accea..d5d797cf 100644 --- a/lib/libalpm/be_sync.c +++ b/lib/libalpm/be_sync.c @@ -69,7 +69,7 @@ static char *get_sync_dir(alpm_handle_t *handle) static int sync_db_validate(alpm_db_t *db) { - pgp_verify_t check_sig; + alpm_siglevel_t level; if(db->status & DB_STATUS_VALID) { return 0; @@ -77,10 +77,9 @@ static int sync_db_validate(alpm_db_t *db) /* this takes into account the default verification level if UNKNOWN * was assigned to this db */ - check_sig = alpm_db_get_sigverify_level(db); + level = alpm_db_get_siglevel(db); - if(check_sig != PM_PGP_VERIFY_NEVER) { - int ret; + if(level & ALPM_SIG_DATABASE) { const char *dbpath = _alpm_db_path(db); if(!dbpath) { /* pm_errno set in _alpm_db_path() */ @@ -93,12 +92,10 @@ static int sync_db_validate(alpm_db_t *db) return 0; } - _alpm_log(db->handle, ALPM_LOG_DEBUG, "checking signature for %s\n", - db->treename); - ret = _alpm_gpgme_checksig(db->handle, dbpath, NULL); - if((check_sig == PM_PGP_VERIFY_ALWAYS && ret != 0) || - (check_sig == PM_PGP_VERIFY_OPTIONAL && ret == 1)) { - RET_ERR(db->handle, ALPM_ERR_SIG_INVALID, -1); + if(_alpm_check_pgp_helper(db->handle, dbpath, NULL, + level & ALPM_SIG_DATABASE_OPTIONAL, level & ALPM_SIG_DATABASE_MARGINAL_OK, + level & ALPM_SIG_DATABASE_UNKNOWN_OK, ALPM_ERR_DB_INVALID_SIG)) { + return 1; } } @@ -149,7 +146,7 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db) int ret = -1; mode_t oldmask; alpm_handle_t *handle; - pgp_verify_t check_sig; + alpm_siglevel_t level; /* Sanity checks */ ASSERT(db != NULL, return -1); @@ -166,7 +163,7 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db) /* make sure we have a sane umask */ oldmask = umask(0022); - check_sig = alpm_db_get_sigverify_level(db); + level = alpm_db_get_siglevel(db); /* attempt to grab a lock */ if(_alpm_handle_lock(handle)) { @@ -186,8 +183,7 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db) ret = _alpm_download(handle, fileurl, syncpath, force, 0, 0); - if(ret == 0 && (check_sig == PM_PGP_VERIFY_ALWAYS || - check_sig == PM_PGP_VERIFY_OPTIONAL)) { + if(ret == 0 && (level & ALPM_SIG_DATABASE)) { /* an existing sig file is no good at this point */ char *sigpath = _alpm_db_sig_path(db); if(!sigpath) { @@ -197,7 +193,7 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db) unlink(sigpath); free(sigpath); - int errors_ok = (check_sig == PM_PGP_VERIFY_OPTIONAL); + int errors_ok = (level & ALPM_SIG_DATABASE_OPTIONAL); /* if we downloaded a DB, we want the .sig from the same server */ snprintf(fileurl, len, "%s/%s.db.sig", server, db->treename); @@ -586,7 +582,7 @@ struct db_operations sync_db_ops = { }; alpm_db_t *_alpm_db_register_sync(alpm_handle_t *handle, const char *treename, - pgp_verify_t level) + alpm_siglevel_t level) { alpm_db_t *db; @@ -598,7 +594,7 @@ alpm_db_t *_alpm_db_register_sync(alpm_handle_t *handle, const char *treename, } db->ops = &sync_db_ops; db->handle = handle; - db->pgp_verify = level; + db->siglevel = level; if(sync_db_validate(db)) { _alpm_db_free(db); diff --git a/lib/libalpm/db.c b/lib/libalpm/db.c index 17f26e90..af27047f 100644 --- a/lib/libalpm/db.c +++ b/lib/libalpm/db.c @@ -45,8 +45,8 @@ */ /** Register a sync database of packages. */ -alpm_db_t SYMEXPORT *alpm_db_register_sync(alpm_handle_t *handle, const char *treename, - pgp_verify_t check_sig) +alpm_db_t SYMEXPORT *alpm_db_register_sync(alpm_handle_t *handle, + const char *treename, alpm_siglevel_t level) { /* Sanity checks */ CHECK_HANDLE(handle, return NULL); @@ -55,7 +55,7 @@ alpm_db_t SYMEXPORT *alpm_db_register_sync(alpm_handle_t *handle, const char *tr /* Do not register a database if a transaction is on-going */ ASSERT(handle->trans == NULL, RET_ERR(handle, ALPM_ERR_TRANS_NOT_NULL, NULL)); - return _alpm_db_register_sync(handle, treename, check_sig); + return _alpm_db_register_sync(handle, treename, level); } /* Helper function for alpm_db_unregister{_all} */ @@ -220,13 +220,13 @@ const char SYMEXPORT *alpm_db_get_name(const alpm_db_t *db) } /** Get the signature verification level for a database. */ -pgp_verify_t SYMEXPORT alpm_db_get_sigverify_level(alpm_db_t *db) +alpm_siglevel_t SYMEXPORT alpm_db_get_siglevel(alpm_db_t *db) { ASSERT(db != NULL, return -1); - if(db->pgp_verify == PM_PGP_VERIFY_UNKNOWN) { - return alpm_option_get_default_sigverify(db->handle); + if(db->siglevel & ALPM_SIG_USE_DEFAULT) { + return alpm_option_get_default_siglevel(db->handle); } else { - return db->pgp_verify; + return db->siglevel; } } @@ -323,7 +323,7 @@ alpm_db_t *_alpm_db_new(const char *treename, int is_local) CALLOC(db, 1, sizeof(alpm_db_t), return NULL); STRDUP(db->treename, treename, return NULL); db->is_local = is_local; - db->pgp_verify = PM_PGP_VERIFY_UNKNOWN; + db->siglevel = 0; return db; } diff --git a/lib/libalpm/db.h b/lib/libalpm/db.h index 0f00f683..a950130e 100644 --- a/lib/libalpm/db.h +++ b/lib/libalpm/db.h @@ -69,7 +69,7 @@ struct __alpm_db_t { alpm_pkghash_t *pkgcache; alpm_list_t *grpcache; alpm_list_t *servers; - pgp_verify_t pgp_verify; + alpm_siglevel_t siglevel; struct db_operations *ops; }; @@ -84,7 +84,7 @@ int _alpm_db_cmp(const void *d1, const void *d2); alpm_list_t *_alpm_db_search(alpm_db_t *db, const alpm_list_t *needles); alpm_db_t *_alpm_db_register_local(alpm_handle_t *handle); alpm_db_t *_alpm_db_register_sync(alpm_handle_t *handle, const char *treename, - pgp_verify_t level); + alpm_siglevel_t level); void _alpm_db_unregister(alpm_db_t *db); /* be_*.c, backend specific calls */ diff --git a/lib/libalpm/dload.c b/lib/libalpm/dload.c index c7a1c2b2..4b4d8dff 100644 --- a/lib/libalpm/dload.c +++ b/lib/libalpm/dload.c @@ -363,11 +363,10 @@ char SYMEXPORT *alpm_fetch_pkgurl(alpm_handle_t *handle, const char *url) _alpm_log(handle, ALPM_LOG_DEBUG, "successfully downloaded %s\n", url); /* attempt to download the signature */ - if(ret == 0 && (handle->sigverify == PM_PGP_VERIFY_ALWAYS || - handle->sigverify == PM_PGP_VERIFY_OPTIONAL)) { + if(ret == 0 && (handle->siglevel & ALPM_SIG_PACKAGE)) { char *sig_url; size_t len; - int errors_ok = (handle->sigverify == PM_PGP_VERIFY_OPTIONAL); + int errors_ok = (handle->siglevel & ALPM_SIG_PACKAGE_OPTIONAL); len = strlen(url) + 5; CALLOC(sig_url, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, NULL)); diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index 8716109f..7e0e5c85 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -72,6 +72,8 @@ const char SYMEXPORT *alpm_strerror(enum _alpm_errno_t err) return _("could not find database"); case ALPM_ERR_DB_INVALID: return _("invalid or corrupted database"); + case ALPM_ERR_DB_INVALID_SIG: + return _("invalid or corrupted database (PGP signature)"); case ALPM_ERR_DB_VERSION: return _("database is incorrect version"); case ALPM_ERR_DB_WRITE: @@ -106,7 +108,9 @@ const char SYMEXPORT *alpm_strerror(enum _alpm_errno_t err) case ALPM_ERR_PKG_IGNORED: return _("operation cancelled due to ignorepkg"); case ALPM_ERR_PKG_INVALID: - return _("invalid or corrupted package"); + return _("invalid or corrupted package (checksum)"); + case ALPM_ERR_PKG_INVALID_SIG: + return _("invalid or corrupted package (PGP signature)"); case ALPM_ERR_PKG_OPEN: return _("cannot open package file"); case ALPM_ERR_PKG_CANT_REMOVE: @@ -118,12 +122,10 @@ const char SYMEXPORT *alpm_strerror(enum _alpm_errno_t err) case ALPM_ERR_PKG_REPO_NOT_FOUND: return _("could not find repository for target"); /* Signatures */ - case ALPM_ERR_SIG_MISSINGDIR: - return _("signature directory not configured correctly"); + case ALPM_ERR_SIG_MISSING: + return _("missing PGP signature"); case ALPM_ERR_SIG_INVALID: return _("invalid PGP signature"); - case ALPM_ERR_SIG_UNKNOWN: - return _("unknown PGP signature"); /* Deltas */ case ALPM_ERR_DLT_INVALID: return _("invalid or corrupted delta"); diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c index 22f3fc8f..ddd76a25 100644 --- a/lib/libalpm/handle.c +++ b/lib/libalpm/handle.c @@ -45,7 +45,8 @@ alpm_handle_t *_alpm_handle_new() CALLOC(handle, 1, sizeof(alpm_handle_t), return NULL); - handle->sigverify = PM_PGP_VERIFY_OPTIONAL; + handle->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL | + ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; return handle; } @@ -574,18 +575,18 @@ int SYMEXPORT alpm_option_set_checkspace(alpm_handle_t *handle, int checkspace) return 0; } -int SYMEXPORT alpm_option_set_default_sigverify(alpm_handle_t *handle, pgp_verify_t level) +int SYMEXPORT alpm_option_set_default_siglevel(alpm_handle_t *handle, + alpm_siglevel_t level) { CHECK_HANDLE(handle, return -1); - ASSERT(level != PM_PGP_VERIFY_UNKNOWN, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1)); - handle->sigverify = level; + handle->siglevel = level; return 0; } -pgp_verify_t SYMEXPORT alpm_option_get_default_sigverify(alpm_handle_t *handle) +alpm_siglevel_t SYMEXPORT alpm_option_get_default_siglevel(alpm_handle_t *handle) { - CHECK_HANDLE(handle, return PM_PGP_VERIFY_UNKNOWN); - return handle->sigverify; + CHECK_HANDLE(handle, return -1); + return handle->siglevel; } /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/handle.h b/lib/libalpm/handle.h index ae8c7c8d..9e038581 100644 --- a/lib/libalpm/handle.h +++ b/lib/libalpm/handle.h @@ -69,7 +69,7 @@ struct __alpm_handle_t { char *arch; /* Architecture of packages we should allow */ int usedelta; /* Download deltas if possible */ int checkspace; /* Check disk space before installing */ - pgp_verify_t sigverify; /* Default signature verification level */ + alpm_siglevel_t siglevel; /* Default signature verification level */ /* error code */ enum _alpm_errno_t pm_errno; diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h index b6021939..f76812b5 100644 --- a/lib/libalpm/package.h +++ b/lib/libalpm/package.h @@ -151,7 +151,7 @@ void _alpm_pkg_free_trans(alpm_pkg_t *pkg); alpm_pkg_t *_alpm_pkg_load_internal(alpm_handle_t *handle, const char *pkgfile, int full, const char *md5sum, const char *base64_sig, - pgp_verify_t check_sig); + alpm_siglevel_t level); int _alpm_pkg_cmp(const void *p1, const void *p2); int _alpm_pkg_compare_versions(alpm_pkg_t *local_pkg, alpm_pkg_t *pkg); diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c index 1ac9963a..42429e2d 100644 --- a/lib/libalpm/signing.c +++ b/lib/libalpm/signing.c @@ -117,9 +117,6 @@ static int init_gpgme(alpm_handle_t *handle) } sigdir = alpm_option_get_gpgdir(handle); - if(!sigdir) { - RET_ERR(handle, ALPM_ERR_SIG_MISSINGDIR, 1); - } /* calling gpgme_check_version() returns the current version and runs * some internal library setup code */ @@ -196,23 +193,27 @@ error: * Check the PGP signature for the given file path. * If base64_sig is provided, it will be used as the signature data after * decoding. If base64_sig is NULL, expect a signature file next to path - * (e.g. "%s.sig"). The return value will be 0 if all checked signatures are - * valid, 1 if there was some sort of problem (but not necessarily rejection), - * and -1 if an error occurred while checking signatures. If 1 is returned, - * pm_errno should be checked to see why the signatures did not pass muster. + * (e.g. "%s.sig"). + * + * The return value will be 0 if nothing abnormal happened during the signature + * check, and -1 if an error occurred while checking signatures or if a + * signature could not be found; pm_errno will be set. Note that "abnormal" + * does not include a failed signature; the value in #result should be checked + * to determine if the signature(s) are good. * @param handle the context handle * @param path the full path to a file * @param base64_sig optional PGP signature data in base64 encoding - * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred) + * @result + * @return 0 in normal cases, -1 if the something failed in the check process */ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, - const char *base64_sig) + const char *base64_sig, alpm_sigresult_t *result) { - int ret = 0; + int ret = -1, sigcount; gpgme_error_t err; gpgme_ctx_t ctx; gpgme_data_t filedata, sigdata; - gpgme_verify_result_t result; + gpgme_verify_result_t verify_result; gpgme_signature_t gpgsig; char *sigpath = NULL; unsigned char *decoded_sigdata = NULL; @@ -222,14 +223,18 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, RET_ERR(handle, ALPM_ERR_NOT_A_FILE, -1); } + if(!result) { + RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1); + } + result->count = 0; + if(!base64_sig) { size_t len = strlen(path) + 5; CALLOC(sigpath, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(sigpath, len, "%s.sig", path); if(!access(sigpath, R_OK) == 0) { - FREE(sigpath); - RET_ERR(handle, ALPM_ERR_SIG_UNKNOWN, -1); + /* sigcount is 0 */ } } @@ -251,7 +256,6 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, file = fopen(path, "rb"); if(file == NULL) { handle->pm_errno = ALPM_ERR_NOT_A_FILE; - ret = -1; goto error; } err = gpgme_data_new_from_stream(&filedata, file); @@ -264,7 +268,7 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, int decode_ret = decode_signature(base64_sig, &decoded_sigdata, &data_len); if(decode_ret) { - ret = -1; + handle->pm_errno = ALPM_ERR_SIG_INVALID; goto error; } err = gpgme_data_new_from_mem(&sigdata, @@ -273,8 +277,7 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, /* file-based, it is on disk */ sigfile = fopen(sigpath, "rb"); if(sigfile == NULL) { - handle->pm_errno = ALPM_ERR_NOT_A_FILE; - ret = -1; + handle->pm_errno = ALPM_ERR_SIG_MISSING; goto error; } err = gpgme_data_new_from_stream(&sigdata, sigfile); @@ -284,16 +287,29 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, /* here's where the magic happens */ err = gpgme_op_verify(ctx, sigdata, filedata, NULL); CHECK_ERR(); - result = gpgme_op_verify_result(ctx); - gpgsig = result->signatures; - if(!gpgsig) { + verify_result = gpgme_op_verify_result(ctx); + CHECK_ERR(); + if(!verify_result || !verify_result->signatures) { _alpm_log(handle, ALPM_LOG_DEBUG, "no signatures returned\n"); - ret = -1; + handle->pm_errno = ALPM_ERR_SIG_MISSING; + goto error; + } + for(gpgsig = verify_result->signatures, sigcount = 0; + gpgsig; gpgsig = gpgsig->next, sigcount++); + _alpm_log(handle, ALPM_LOG_DEBUG, "%d signatures returned\n", sigcount); + + result->status = calloc(sigcount, sizeof(alpm_sigstatus_t)); + result->uid = calloc(sigcount, sizeof(char*)); + if(!result->status || !result->uid) { + handle->pm_errno = ALPM_ERR_MEMORY; goto error; } + result->count = sigcount; - while(gpgsig) { + for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig; + gpgsig = gpgsig->next, sigcount++) { alpm_list_t *summary_list, *summary; + alpm_sigstatus_t status; _alpm_log(handle, ALPM_LOG_DEBUG, "fingerprint: %s\n", gpgsig->fpr); summary_list = list_sigsum(gpgsig->summary); @@ -308,34 +324,53 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, string_validity(gpgsig->validity), gpgme_strerror(gpgsig->validity_reason)); - /* Note: this is structured so any bad signature will set the return code - * to a bad one, but good ones just leave the default value in place; e.g. - * worst case wins out. */ + err = gpgme_get_key(ctx, gpgsig->fpr, &key, 0); + if(gpg_err_code(err) == GPG_ERR_EOF) { + _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); + err = GPG_ERR_NO_ERROR; + } else { + CHECK_ERR(); + if(key->uids) { + const char *uid = key->uids->uid; + STRDUP(result->uid[sigcount], uid, + handle->pm_errno = ALPM_ERR_MEMORY; goto error); + _alpm_log(handle, ALPM_LOG_DEBUG, "key user: %s\n", uid); + } + gpgme_key_unref(key); + } + if(gpgsig->summary & GPGME_SIGSUM_VALID) { /* definite good signature */ _alpm_log(handle, ALPM_LOG_DEBUG, "result: valid signature\n"); + status = ALPM_SIGSTATUS_VALID; } else if(gpgsig->summary & GPGME_SIGSUM_GREEN) { /* good signature */ _alpm_log(handle, ALPM_LOG_DEBUG, "result: green signature\n"); + status = ALPM_SIGSTATUS_VALID; } else if(gpgsig->summary & GPGME_SIGSUM_RED) { /* definite bad signature, error */ _alpm_log(handle, ALPM_LOG_DEBUG, "result: red signature\n"); - handle->pm_errno = ALPM_ERR_SIG_INVALID; - ret = 1; + status = ALPM_SIGSTATUS_BAD; } else if(gpgsig->summary & GPGME_SIGSUM_KEY_MISSING) { _alpm_log(handle, ALPM_LOG_DEBUG, "result: signature from unknown key\n"); - handle->pm_errno = ALPM_ERR_SIG_UNKNOWN; - ret = 1; + status = ALPM_SIGSTATUS_UNKNOWN; + } else if(gpgsig->summary & GPGME_SIGSUM_KEY_EXPIRED) { + _alpm_log(handle, ALPM_LOG_DEBUG, "result: key expired\n"); + status = ALPM_SIGSTATUS_BAD; + } else if(gpgsig->summary & GPGME_SIGSUM_SIG_EXPIRED) { + _alpm_log(handle, ALPM_LOG_DEBUG, "result: signature expired\n"); + status = ALPM_SIGSTATUS_BAD; } else { /* we'll capture everything else here */ _alpm_log(handle, ALPM_LOG_DEBUG, "result: invalid signature\n"); - handle->pm_errno = ALPM_ERR_SIG_INVALID; - ret = 1; + status = ALPM_SIGSTATUS_BAD; } - gpgsig = gpgsig->next; + result->status[sigcount] = status; } + ret = 0; + error: gpgme_data_release(sigdata); gpgme_data_release(filedata); @@ -362,18 +397,77 @@ int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, } #endif +int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, + const char *base64_sig, int optional, int marginal, int unknown, + enum _alpm_errno_t invalid_err) +{ + alpm_sigresult_t result; + int ret; + + memset(&result, 0, sizeof(result)); + + _alpm_log(handle, ALPM_LOG_DEBUG, "checking signatures for %s\n", path); + ret = _alpm_gpgme_checksig(handle, path, base64_sig, &result); + if(ret && handle->pm_errno == ALPM_ERR_SIG_MISSING) { + if(optional) { + _alpm_log(handle, ALPM_LOG_DEBUG, "missing optional signature\n"); + handle->pm_errno = 0; + ret = 0; + } else { + _alpm_log(handle, ALPM_LOG_DEBUG, "missing required signature\n"); + /* ret will already be -1 */ + } + } else if(ret) { + _alpm_log(handle, ALPM_LOG_DEBUG, "signature check failed\n"); + /* ret will already be -1 */ + } else { + int num; + for(num = 0; num < result.count; num++) { + /* fallthrough in this case block is on purpose. if one allows unknown + * signatures, then a marginal signature should be allowed as well, and + * if neither of these are allowed we fall all the way through to bad. */ + switch(result.status[num]) { + case ALPM_SIGSTATUS_VALID: + _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n"); + break; + case ALPM_SIGSTATUS_MARGINAL: + if(marginal) { + _alpm_log(handle, ALPM_LOG_DEBUG, "allowing marginal signature\n"); + break; + } + case ALPM_SIGSTATUS_UNKNOWN: + if(unknown) { + _alpm_log(handle, ALPM_LOG_DEBUG, "allowing unknown signature\n"); + break; + } + case ALPM_SIGSTATUS_BAD: + default: + _alpm_log(handle, ALPM_LOG_DEBUG, "signature is invalid\n"); + handle->pm_errno = invalid_err; + ret = -1; + } + } + } + + free(result.status); + free(result.uid); + return ret; +} + /** * Check the PGP signature for the given package file. * @param pkg the package to check * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred) */ -int SYMEXPORT alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg) +int SYMEXPORT alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, + alpm_sigresult_t *result) { ASSERT(pkg != NULL, return -1); + ASSERT(result != NULL, RET_ERR(pkg->handle, ALPM_ERR_WRONG_ARGS, -1)); pkg->handle->pm_errno = 0; return _alpm_gpgme_checksig(pkg->handle, alpm_pkg_get_filename(pkg), - pkg->base64_sig); + pkg->base64_sig, result); } /** @@ -381,12 +475,14 @@ int SYMEXPORT alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg) * @param db the database to check * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred) */ -int SYMEXPORT alpm_db_check_pgp_signature(alpm_db_t *db) +int SYMEXPORT alpm_db_check_pgp_signature(alpm_db_t *db, + alpm_sigresult_t *result) { ASSERT(db != NULL, return -1); + ASSERT(result != NULL, RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, -1)); db->handle->pm_errno = 0; - return _alpm_gpgme_checksig(db->handle, _alpm_db_path(db), NULL); + return _alpm_gpgme_checksig(db->handle, _alpm_db_path(db), NULL, result); } /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/signing.h b/lib/libalpm/signing.h index b1b90d77..22f6357e 100644 --- a/lib/libalpm/signing.h +++ b/lib/libalpm/signing.h @@ -22,7 +22,10 @@ #include "alpm.h" int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, - const char *base64_sig); + const char *base64_sig, alpm_sigresult_t *result); +int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, + const char *base64_sig, int optional, int marginal, int unknown, + enum _alpm_errno_t invalid_err); #endif /* _ALPM_SIGNING_H */ diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index d6b54ba0..783c7280 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -863,7 +863,7 @@ int _alpm_sync_commit(alpm_handle_t *handle, alpm_list_t **data) int percent = (current * 100) / numtargs; const char *filename; char *filepath; - pgp_verify_t check_sig; + alpm_siglevel_t level; PROGRESS(trans, ALPM_TRANS_PROGRESS_INTEGRITY_START, "", percent, numtargs, current); @@ -874,7 +874,7 @@ int _alpm_sync_commit(alpm_handle_t *handle, alpm_list_t **data) filename = alpm_pkg_get_filename(spkg); filepath = _alpm_filecache_find(handle, filename); alpm_db_t *sdb = alpm_pkg_get_db(spkg); - check_sig = alpm_db_get_sigverify_level(sdb); + level = alpm_db_get_siglevel(sdb); /* load the package file and replace pkgcache entry with it in the target list */ /* TODO: alpm_pkg_get_db() will not work on this target anymore */ @@ -882,7 +882,7 @@ int _alpm_sync_commit(alpm_handle_t *handle, alpm_list_t **data) "replacing pkgcache entry with package file for target %s\n", spkg->name); alpm_pkg_t *pkgfile =_alpm_pkg_load_internal(handle, filepath, 1, spkg->md5sum, - spkg->base64_sig, check_sig); + spkg->base64_sig, level); if(!pkgfile) { errors++; *data = alpm_list_add(*data, strdup(filename)); diff --git a/src/pacman/conf.c b/src/pacman/conf.c index f2df260e..3af3fa5b 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -52,7 +52,7 @@ config_t *config_new(void) newconfig->op = PM_OP_MAIN; newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING; newconfig->configfile = strdup(CONFFILE); - newconfig->sigverify = PM_PGP_VERIFY_UNKNOWN; + newconfig->siglevel = ALPM_SIG_USE_DEFAULT; return newconfig; } @@ -222,17 +222,18 @@ int config_set_arch(const char *arch) return 0; } -static pgp_verify_t option_verifysig(const char *value) +static alpm_siglevel_t option_verifysig(const char *value) { - pgp_verify_t level; + alpm_siglevel_t level; if(strcmp(value, "Always") == 0) { - level = PM_PGP_VERIFY_ALWAYS; + level = ALPM_SIG_PACKAGE | ALPM_SIG_DATABASE; } else if(strcmp(value, "Optional") == 0) { - level = PM_PGP_VERIFY_OPTIONAL; + level = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL | + ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; } else if(strcmp(value, "Never") == 0) { - level = PM_PGP_VERIFY_NEVER; + level = 0; } else { - level = PM_PGP_VERIFY_UNKNOWN; + return -1; } pm_printf(ALPM_LOG_DEBUG, "config: VerifySig = %s (%d)\n", value, level); return level; @@ -359,9 +360,9 @@ static int _parse_options(const char *key, char *value, } FREELIST(methods); } else if(strcmp(key, "VerifySig") == 0) { - pgp_verify_t level = option_verifysig(value); - if(level != PM_PGP_VERIFY_UNKNOWN) { - config->sigverify = level; + alpm_siglevel_t level = option_verifysig(value); + if(level != -1) { + config->siglevel = level; } else { pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' has invalid value '%s'\n"), @@ -484,8 +485,8 @@ static int setup_libalpm(void) alpm_option_set_cachedirs(handle, config->cachedirs); } - if(config->sigverify != PM_PGP_VERIFY_UNKNOWN) { - alpm_option_set_default_sigverify(handle, config->sigverify); + if(config->siglevel != ALPM_SIG_USE_DEFAULT) { + alpm_option_set_default_siglevel(handle, config->siglevel); } if(config->xfercommand) { @@ -518,7 +519,7 @@ struct section_t { char *name; int is_options; /* db section option gathering */ - pgp_verify_t sigverify; + alpm_siglevel_t siglevel; alpm_list_t *servers; }; @@ -545,7 +546,7 @@ static int finish_section(struct section_t *section, int parse_options) } /* if we are not looking at options sections only, register a db */ - db = alpm_db_register_sync(config->handle, section->name, section->sigverify); + db = alpm_db_register_sync(config->handle, section->name, section->siglevel); if(db == NULL) { pm_printf(ALPM_LOG_ERROR, _("could not register '%s' database (%s)\n"), section->name, alpm_strerror(alpm_errno(config->handle))); @@ -568,7 +569,7 @@ static int finish_section(struct section_t *section, int parse_options) cleanup: alpm_list_free(section->servers); section->servers = NULL; - section->sigverify = 0; + section->siglevel = ALPM_SIG_USE_DEFAULT; free(section->name); section->name = NULL; return ret; @@ -726,9 +727,9 @@ static int _parseconfig(const char *file, struct section_t *section, } section->servers = alpm_list_add(section->servers, strdup(value)); } else if(strcmp(key, "VerifySig") == 0) { - pgp_verify_t level = option_verifysig(value); - if(level != PM_PGP_VERIFY_UNKNOWN) { - section->sigverify = level; + alpm_siglevel_t level = option_verifysig(value); + if(level != -1) { + section->siglevel = level; } else { pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' has invalid value '%s'\n"), @@ -763,6 +764,7 @@ int parseconfig(const char *file) int ret; struct section_t section; memset(§ion, 0, sizeof(struct section_t)); + section.siglevel = ALPM_SIG_USE_DEFAULT; /* the config parse is a two-pass affair. We first parse the entire thing for * the [options] section so we can get all default and path options set. * Next, we go back and parse everything but [options]. */ diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 64b911ab..bce42ab5 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -71,7 +71,7 @@ typedef struct __config_t { unsigned short noask; unsigned int ask; alpm_transflag_t flags; - pgp_verify_t sigverify; + alpm_siglevel_t siglevel; /* conf file options */ /* I Love Candy! */ diff --git a/src/pacman/query.c b/src/pacman/query.c index 045dc7f0..5dff03ff 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -551,7 +551,7 @@ int pacman_query(alpm_list_t *targets) char *strname = alpm_list_getdata(i); if(config->op_q_isfile) { - alpm_pkg_load(config->handle, strname, 1, PM_PGP_VERIFY_OPTIONAL, &pkg); + alpm_pkg_load(config->handle, strname, 1, 0, &pkg); } else { pkg = alpm_db_get_pkg(db_local, strname); } diff --git a/src/pacman/sync.c b/src/pacman/sync.c index ad6d5e5c..5f67236d 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -220,7 +220,7 @@ static int sync_cleancache(int level) /* attempt to load the package, prompt removal on failures as we may have * files here that aren't valid packages. we also don't need a full * load of the package, just the metadata. */ - if(alpm_pkg_load(config->handle, path, 0, PM_PGP_VERIFY_NEVER, &localpkg) != 0 + if(alpm_pkg_load(config->handle, path, 0, 0, &localpkg) != 0 || localpkg == NULL) { if(yesno(_("File %s does not seem to be a valid package, remove it?"), path)) { diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c index 95b17cc1..11d00e73 100644 --- a/src/pacman/upgrade.c +++ b/src/pacman/upgrade.c @@ -42,7 +42,7 @@ int pacman_upgrade(alpm_list_t *targets) { alpm_list_t *i, *data = NULL; - pgp_verify_t check_sig = alpm_option_get_default_sigverify(config->handle); + alpm_siglevel_t level = alpm_option_get_default_siglevel(config->handle); int retval = 0; if(targets == NULL) { @@ -76,7 +76,7 @@ int pacman_upgrade(alpm_list_t *targets) char *targ = alpm_list_getdata(i); alpm_pkg_t *pkg; - if(alpm_pkg_load(config->handle, targ, 1, check_sig, &pkg) != 0) { + if(alpm_pkg_load(config->handle, targ, 1, level, &pkg) != 0) { pm_fprintf(stderr, ALPM_LOG_ERROR, "'%s': %s\n", targ, alpm_strerror(alpm_errno(config->handle))); trans_release(); diff --git a/src/util/cleanupdelta.c b/src/util/cleanupdelta.c index 08d8a557..a45efdcc 100644 --- a/src/util/cleanupdelta.c +++ b/src/util/cleanupdelta.c @@ -71,11 +71,12 @@ static void checkdbs(const char *dbpath, alpm_list_t *dbnames) { char syncdbpath[PATH_MAX]; alpm_db_t *db = NULL; alpm_list_t *i; + const alpm_siglevel_t level = ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; for(i = dbnames; i; i = alpm_list_next(i)) { char *dbname = alpm_list_getdata(i); snprintf(syncdbpath, PATH_MAX, "%s/sync/%s", dbpath, dbname); - db = alpm_db_register_sync(handle, dbname, PM_PGP_VERIFY_OPTIONAL); + db = alpm_db_register_sync(handle, dbname, level); if(db == NULL) { fprintf(stderr, "error: could not register sync database (%s)\n", alpm_strerror(alpm_errno(handle))); diff --git a/src/util/pactree.c b/src/util/pactree.c index 7b87ac13..9b678631 100644 --- a/src/util/pactree.c +++ b/src/util/pactree.c @@ -124,6 +124,7 @@ static int register_syncs(void) { FILE *fp; char *ptr, *section = NULL; char line[LINE_MAX]; + const alpm_siglevel_t level = ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; fp = fopen(CONFFILE, "r"); if(!fp) { @@ -147,7 +148,7 @@ static int register_syncs(void) { section = strndup(&line[1], strlen(line) - 2); if(section && strcmp(section, "options") != 0) { - alpm_db_register_sync(handle, section, PM_PGP_VERIFY_OPTIONAL); + alpm_db_register_sync(handle, section, level); } } } diff --git a/src/util/testdb.c b/src/util/testdb.c index 642890b6..ee169df2 100644 --- a/src/util/testdb.c +++ b/src/util/testdb.c @@ -148,10 +148,11 @@ static int check_syncdbs(alpm_list_t *dbnames) { int ret = 0; alpm_db_t *db = NULL; alpm_list_t *i, *pkglist, *syncpkglist = NULL; + const alpm_siglevel_t level = ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; for(i = dbnames; i; i = alpm_list_next(i)) { char *dbname = alpm_list_getdata(i); - db = alpm_db_register_sync(handle, dbname, PM_PGP_VERIFY_OPTIONAL); + db = alpm_db_register_sync(handle, dbname, level); if(db == NULL) { fprintf(stderr, "error: could not register sync database (%s)\n", alpm_strerror(alpm_errno(handle))); diff --git a/src/util/testpkg.c b/src/util/testpkg.c index 03234ed5..ac2dde28 100644 --- a/src/util/testpkg.c +++ b/src/util/testpkg.c @@ -43,6 +43,7 @@ int main(int argc, char *argv[]) alpm_handle_t *handle; enum _alpm_errno_t err; alpm_pkg_t *pkg = NULL; + const alpm_siglevel_t level = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL; if(argc != 2) { fprintf(stderr, "usage: %s \n", BASENAME); @@ -58,7 +59,7 @@ int main(int argc, char *argv[]) /* let us get log messages from libalpm */ alpm_option_set_logcb(handle, output_cb); - if(alpm_pkg_load(handle, argv[1], 1, PM_PGP_VERIFY_OPTIONAL, &pkg) == -1 + if(alpm_pkg_load(handle, argv[1], 1, level, &pkg) == -1 || pkg == NULL) { err = alpm_errno(handle); switch(err) { -- cgit v1.2.3-24-g4f1b