Initial support for asignify signatures

jeremy at merelinux.org jeremy at merelinux.org
Sun Dec 26 22:01:08 UTC 2021


From: Jeremy Huntwork <jeremy at merelinux.org>

This is a proof of concept that shows how asignify can be used instead
of gpgme to validate packages signed with the asignify tool.

Signed-off-by: Jeremy Huntwork <jeremy at merelinux.org>
---
 lib/libalpm/alpm.c       |  2 +-
 lib/libalpm/be_package.c | 10 ++++++
 lib/libalpm/be_sync.c    |  2 +-
 lib/libalpm/handle.c     |  6 ++--
 lib/libalpm/signing.c    | 67 ++++++++++++++++++++++++++++++++++++++++
 lib/libalpm/signing.h    | 11 ++++---
 meson.build              | 11 ++++++-
 meson_options.txt        |  3 ++
 8 files changed, 101 insertions(+), 11 deletions(-)

diff --git a/lib/libalpm/alpm.c b/lib/libalpm/alpm.c
index 5b326ab0..d1261660 100644
--- a/lib/libalpm/alpm.c
+++ b/lib/libalpm/alpm.c
@@ -137,7 +137,7 @@ int SYMEXPORT alpm_capabilities(void)
 #ifdef HAVE_LIBCURL
 		| ALPM_CAPABILITY_DOWNLOADER
 #endif
-#ifdef HAVE_LIBGPGME
+#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY)
 		| ALPM_CAPABILITY_SIGNATURES
 #endif
 		| 0;
diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c
index 5ca2865c..1b6887ce 100644
--- a/lib/libalpm/be_package.c
+++ b/lib/libalpm/be_package.c
@@ -342,12 +342,20 @@ int _alpm_pkg_validate_internal(alpm_handle_t *handle,
 			handle->pm_errno = ALPM_ERR_PKG_MISSING_SIG;
 			return -1;
 		}
+#ifdef HAVE_LIBGPGME
 		if(_alpm_check_pgp_helper(handle, pkgfile, sig,
 					level & ALPM_SIG_PACKAGE_OPTIONAL, level & ALPM_SIG_PACKAGE_MARGINAL_OK,
 					level & ALPM_SIG_PACKAGE_UNKNOWN_OK, sigdata)) {
 			handle->pm_errno = ALPM_ERR_PKG_INVALID_SIG;
 			return -1;
 		}
+#endif
+#ifdef HAVE_LIBASIGNIFY
+		if(_alpm_check_asignify_helper(handle, pkgfile, sigdata)) {
+			handle->pm_errno = ALPM_ERR_PKG_INVALID_SIG;
+			return -1;
+		}
+#endif
 		if(validation && has_sig) {
 			*validation |= ALPM_PKG_VALIDATION_SIGNATURE;
 		}
@@ -743,6 +751,7 @@ int SYMEXPORT alpm_pkg_load(alpm_handle_t *handle, const char *filename, int ful
 				return -1;
 			}
 
+#ifndef HAVE_LIBASIGNIFY
 			if(alpm_extract_keyid(handle, filename, sig, len, &keys) == 0) {
 				alpm_list_t *k;
 				for(k = keys; k; k = k->next) {
@@ -771,6 +780,7 @@ int SYMEXPORT alpm_pkg_load(alpm_handle_t *handle, const char *filename, int ful
 				free(sigpath);
 				return -1;
 			}
+#endif
 		}
 	}
 	free(sigpath);
diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c
index ede25985..81654902 100644
--- a/lib/libalpm/be_sync.c
+++ b/lib/libalpm/be_sync.c
@@ -697,7 +697,7 @@ alpm_db_t *_alpm_db_register_sync(alpm_handle_t *handle, const char *treename,
 
 	_alpm_log(handle, ALPM_LOG_DEBUG, "registering sync database '%s'\n", treename);
 
-#ifndef HAVE_LIBGPGME
+#if !defined(HAVE_LIBGPGME) || !defined(HAVE_LIBASIGNIFY)
 	if(level != 0 && level != ALPM_SIG_USE_DEFAULT) {
 		RET_ERR(handle, ALPM_ERR_MISSING_CAPABILITY_SIGNATURES, NULL);
 	}
diff --git a/lib/libalpm/handle.c b/lib/libalpm/handle.c
index 101d4a78..78c7d7bd 100644
--- a/lib/libalpm/handle.c
+++ b/lib/libalpm/handle.c
@@ -829,7 +829,7 @@ int SYMEXPORT alpm_option_set_default_siglevel(alpm_handle_t *handle,
 	if(level == ALPM_SIG_USE_DEFAULT) {
 		RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1);
 	}
-#ifdef HAVE_LIBGPGME
+#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY)
 	handle->siglevel = level;
 #else
 	if(level != 0) {
@@ -849,7 +849,7 @@ int SYMEXPORT alpm_option_set_local_file_siglevel(alpm_handle_t *handle,
 		int level)
 {
 	CHECK_HANDLE(handle, return -1);
-#ifdef HAVE_LIBGPGME
+#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY)
 	handle->localfilesiglevel = level;
 #else
 	if(level != 0 && level != ALPM_SIG_USE_DEFAULT) {
@@ -873,7 +873,7 @@ int SYMEXPORT alpm_option_set_remote_file_siglevel(alpm_handle_t *handle,
 		int level)
 {
 	CHECK_HANDLE(handle, return -1);
-#ifdef HAVE_LIBGPGME
+#if defined(HAVE_LIBGPGME) || defined(HAVE_LIBASIGNIFY)
 	handle->remotefilesiglevel = level;
 #else
 	if(level != 0 && level != ALPM_SIG_USE_DEFAULT) {
diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c
index 66cc3923..cf778f9d 100644
--- a/lib/libalpm/signing.c
+++ b/lib/libalpm/signing.c
@@ -26,6 +26,12 @@
 #include <gpgme.h>
 #endif
 
+#ifdef HAVE_LIBASIGNIFY
+/* libasignify */
+#include <asignify.h>
+#include <dirent.h>
+#endif
+
 /* libalpm */
 #include "signing.h"
 #include "package.h"
@@ -810,6 +816,67 @@ char *_alpm_sigpath(alpm_handle_t *handle, const char *path)
 	return sigpath;
 }
 
+/**
+ * Helper for checking the asignify signature for the given file path.
+ * @param handle the context handle
+ * @param path the full path to a file
+ * @param sigdata a pointer to storage for signature results
+ * @return 0 on success, -1 on error (consult pm_errno or sigdata)
+ */
+int _alpm_check_asignify_helper(alpm_handle_t *handle, const char *path,
+		alpm_siglist_t **sigdata)
+{
+		int ret = 0;
+		struct dirent *entry;
+		struct stat statbuf;
+		const char *pubkeydir = "/etc/pacman.d/trustedkeys"; /* Add config for this */
+
+		char *sigpath = _alpm_sigpath(handle, path);
+		asignify_verify_t *vrf = asignify_verify_init();
+
+		DIR *pkd = opendir(pubkeydir);
+		if(pkd == NULL) {
+			_alpm_log(handle, ALPM_LOG_DEBUG, "cannot open directory: %s\n", pubkeydir);
+			return -1;
+		}
+
+		while((entry = readdir(pkd)) != NULL) {
+			char *fullpath = malloc(strlen(pubkeydir) + strlen(entry->d_name) + 2);
+			if (fullpath == NULL) {
+				_alpm_log(handle, ALPM_LOG_DEBUG, "malloc error\n");
+				return -1;
+			}
+			sprintf(fullpath, "%s/%s", pubkeydir, entry->d_name);
+			stat(fullpath, &statbuf);
+			if (S_ISREG(statbuf.st_mode)) {
+				if (!asignify_verify_load_pubkey(vrf, fullpath)) {
+					/* Don't return here because there may be multiple public keys to load. */
+					_alpm_log(handle, ALPM_LOG_DEBUG, "cannot load public key file: %s\n", fullpath);
+				}
+			}
+			free(fullpath);
+		}
+
+		if (!asignify_verify_load_signature(vrf, sigpath)) {
+			_alpm_log(handle, ALPM_LOG_DEBUG, "cannot load signature\n");
+			ret = -1;
+			goto asignify_cleanup;
+		}
+
+		if (!asignify_verify_file(vrf, path)) {
+			_alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n");
+			ret = -1;
+			goto asignify_cleanup;
+		}
+
+		_alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n");
+
+asignify_cleanup:
+		free(sigpath);
+		asignify_verify_free(vrf);
+		return ret;
+}
+
 /**
  * Helper for checking the PGP signature for the given file path.
  * This wraps #_alpm_gpgme_checksig in a slightly friendlier manner to simplify
diff --git a/lib/libalpm/signing.h b/lib/libalpm/signing.h
index 112b2a1f..ce859373 100644
--- a/lib/libalpm/signing.h
+++ b/lib/libalpm/signing.h
@@ -23,13 +23,14 @@
 
 char *_alpm_sigpath(alpm_handle_t *handle, const char *path);
 int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path,
-		const char *base64_sig, alpm_siglist_t *result);
-
+						 const char *base64_sig, alpm_siglist_t *result);
+int _alpm_check_asignify_helper(alpm_handle_t *handle, const char *path,
+								alpm_siglist_t **sigdata);
 int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path,
-		const char *base64_sig, int optional, int marginal, int unknown,
-		alpm_siglist_t **sigdata);
+						   const char *base64_sig, int optional, int marginal, int unknown,
+						   alpm_siglist_t **sigdata);
 int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier,
-		alpm_siglist_t *siglist, int optional, int marginal, int unknown);
+						  alpm_siglist_t *siglist, int optional, int marginal, int unknown);
 
 int _alpm_key_in_keychain(alpm_handle_t *handle, const char *fpr);
 int _alpm_key_import(alpm_handle_t *handle, const char *uid, const char *fpr);
diff --git a/meson.build b/meson.build
index 76b9d2aa..fc9a6f1c 100644
--- a/meson.build
+++ b/meson.build
@@ -105,6 +105,14 @@ gpgme = dependency('gpgme',
                    not_found_message : 'gpgme @0@ is needed for GPG signature support'.format(needed_gpgme_version))
 conf.set('HAVE_LIBGPGME', gpgme.found())
 
+needed_libasignify_version = '>=1.0'
+libasignify = dependency('libasignify',
+                   version : needed_libasignify_version,
+                   required : get_option('asignify'),
+                   static : get_option('buildstatic'),
+                   not_found_message : 'libasignify @0@ is needed for asignify signature support'.format(needed_libasignify_version))
+conf.set('HAVE_LIBASIGNIFY', libasignify.found())
+
 want_crypto = get_option('crypto')
 if want_crypto == 'openssl'
   libcrypto = dependency('libcrypto', static : get_option('buildstatic'),
@@ -305,7 +313,7 @@ libcommon = static_library(
   gnu_symbol_visibility : 'hidden',
   install : false)
 
-alpm_deps = [crypto_provider, libarchive, libcurl, libintl, gpgme]
+alpm_deps = [crypto_provider, libarchive, libasignify, libcurl, libintl, gpgme]
 
 libalpm_a = static_library(
   'alpm_objlib',
@@ -453,6 +461,7 @@ message('\n    '.join([
   '  i18n support             : @0@'.format(get_option('i18n')),
   '  Build docs               : @0@'.format(build_doc),
   '  debug build              : @0@'.format(get_option('buildtype') == 'debug'),
+  '  Use libasignify          : @0@'.format(conf.get('HAVE_LIBASIGNIFY')),
   '  Use libcurl              : @0@'.format(conf.get('HAVE_LIBCURL')),
   '  Use GPGME                : @0@'.format(conf.get('HAVE_LIBGPGME')),
   '  Use OpenSSL              : @0@'.format(conf.has('HAVE_LIBSSL') and
diff --git a/meson_options.txt b/meson_options.txt
index 4d8cc300..a254a5d4 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -48,6 +48,9 @@ option('crypto', type : 'combo', choices : ['openssl', 'nettle'],
 option('gpgme', type : 'feature', value : 'auto',
        description : 'use GPGME for PGP signature verification')
 
+option('asignify', type : 'feature', value : 'auto',
+       description : 'use asignify for signature verification')
+
 option('i18n', type : 'boolean', value : true,
        description : 'enable localization of pacman, libalpm and scripts')
 
-- 
2.34.1



More information about the pacman-dev mailing list