[pacman-dev] [PATCH] Package and database signing

Dan McGee dan at archlinux.org
Tue Jun 29 22:54:12 EDT 2010


From: Denis A. Altoé Falqueto <denisfalqueto at gmail.com>

The packages and database files will have the possibility of being
digitally signed, using gpg2 as the tool to sign and verify the
signatures.

makepkg and repo-add were changed so they can receive a key to be
used in the signing process and use gpg2 instead of gpg. The case for
gpg2 is that it allows user agents to be used for passphrase entry.
For example, in KDE, it will open a dialog to ask for the passphrase
and it will be cached for some time. It is also possible with Gnome.
In simpler environments, the passphrase will be asked everytime
in the command line.

repo-add will store the signature of the packages added, if it exists.
The signature will be stored in the section %PGPSIG% of the desc file
and will have a variable length. The reason is that different key sizes
generate different signature sizes. So we must be prepared for any size.

libalpm was changed to check the signature in the database update and
package installation (not in -U, for now). Each repository in pacman.conf
can be configured to use the signature feature according to three options:
Always, Optional and None. Always indicates that the signature must exists
and be valid for the repository to be updated. Optional doesn't demnds the
signature, but if it is present, it must be valid. None doesn't bother with
signatures at all.

Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto at gmail.com>
---
 configure.ac             |    4 -
 doc/makepkg.8.txt        |    4 +
 doc/makepkg.conf.5.txt   |    6 +-
 doc/repo-add.8.txt       |    7 +-
 lib/libalpm/alpm.h       |    2 +-
 lib/libalpm/be_files.c   |   96 ++++++++++++++--
 lib/libalpm/error.c      |    2 -
 lib/libalpm/signing.c    |  282 ++++++++++++++++++++++------------------------
 lib/libalpm/signing.h    |    3 +-
 lib/libalpm/sync.c       |    2 +-
 scripts/.gitignore       |    1 +
 scripts/Makefile.am      |    3 +
 scripts/makepkg.sh.in    |   32 ++++--
 scripts/pacman-key.sh.in |  278 +++++++++++++++++++++++++++++++++++++++++++++
 scripts/repo-add.sh.in   |   63 ++++++++---
 15 files changed, 588 insertions(+), 197 deletions(-)
 create mode 100644 scripts/pacman-key.sh.in

diff --git a/configure.ac b/configure.ac
index b6ce68b..008975b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -154,10 +154,6 @@ else
 fi
 AM_CONDITIONAL(INTERNAL_DOWNLOAD, test "x$internaldownload" = "xyes")
 
-# Check for gpgme
-AC_CHECK_LIB([gpgme], [gpgme_check_version], ,
-	AC_MSG_ERROR([gpgme is needed to compile pacman!]))
-
 # Checks for header files.
 AC_CHECK_HEADERS([fcntl.h glob.h libintl.h limits.h locale.h string.h strings.h sys/ioctl.h sys/param.h sys/statvfs.h sys/syslimits.h sys/time.h syslog.h wchar.h])
 
diff --git a/doc/makepkg.8.txt b/doc/makepkg.8.txt
index a2fdb3f..4d8f26b 100644
--- a/doc/makepkg.8.txt
+++ b/doc/makepkg.8.txt
@@ -161,6 +161,10 @@ Options
 	(Passed to pacman) Prevent pacman from displaying a progress bar;
 	useful if you are redirecting makepkg output to file.
 
+*\--signwithkey*::
+	Select a specific key to be used to sign the package. If absent,
+	the default from the keyring key will be used.
+
 
 Additional Features
 -------------------
diff --git a/doc/makepkg.conf.5.txt b/doc/makepkg.conf.5.txt
index a565bd6..f82bc19 100644
--- a/doc/makepkg.conf.5.txt
+++ b/doc/makepkg.conf.5.txt
@@ -94,9 +94,9 @@ Options
 		PKGBUILD options array.
 
 	*sign*;;
-		Generate a PGP signature file using GnuPG. This will execute `gpg
-		--detach-sign --use-agent` on the built package to generate a detached
-		signature file, using the GPG agent if it is available. The signature
+		Generate a PGP signature file using GnuPG. This will execute `gpg2
+		--detach-sign` on the built package to generate a detached signature 
+		file, using the GPG agent if it is available. The signature
 		file will be the entire filename of the package with a ``.sig''
 		extension.
 
diff --git a/doc/repo-add.8.txt b/doc/repo-add.8.txt
index e6cc940..6b8c0ef 100644
--- a/doc/repo-add.8.txt
+++ b/doc/repo-add.8.txt
@@ -10,9 +10,9 @@ repo-add - package database maintenance utility
 
 Synopsis
 --------
-repo-add [-q] <path-to-db> <package1> [<package2> ...]
+repo-add [-q] [-s [-k key]] <path-to-db> <package1> [<package2> ...]
 
-repo-remove [-q] <path-to-db> <packagename> [<packagename2> ...]
+repo-remove [-q] [-s [-k key]] <path-to-db> <packagename> [<packagename2> ...]
 
 
 Description
@@ -40,6 +40,9 @@ Options
 	signature file, using the GPG agent if it is available. The signature file
 	will be the entire filename of the database with a ``.sig'' extension.
 
+*-k key*::
+	Select a specific key to be used for the signing of the database file.
+	If absent, the default key from the default keyring will be used.
 
 See Also
 --------
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 2ce049d..7eeb734 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -512,6 +512,7 @@ enum _pmerrno_t {
 	PM_ERR_DB_NOT_FOUND,
 	PM_ERR_DB_WRITE,
 	PM_ERR_DB_REMOVE,
+	PM_ERR_DB_SIG,
 	/* Servers */
 	PM_ERR_SERVER_BAD_URL,
 	PM_ERR_SERVER_NONE,
@@ -550,7 +551,6 @@ enum _pmerrno_t {
 	/* External library errors */
 	PM_ERR_LIBARCHIVE,
 	PM_ERR_LIBFETCH,
-	PM_ERR_GPGME,
 	PM_ERR_EXTERNAL_DOWNLOAD
 };
 
diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_files.c
index 1235165..9751b4e 100644
--- a/lib/libalpm/be_files.c
+++ b/lib/libalpm/be_files.c
@@ -49,7 +49,9 @@
 #include "delta.h"
 #include "deps.h"
 #include "dload.h"
+#include "signing.h"
 
+#define DBSIGEXT ".sig"
 
 static int checkdbdir(pmdb_t *db)
 {
@@ -208,7 +210,8 @@ static int remove_olddir(const char *syncdbpath, alpm_list_t *dirlist)
  */
 int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
 {
-	char *dbfile, *dbfilepath;
+	char *dbfile = NULL, *dbfilepath = NULL, *dbsigfile = NULL,
+		*fulldbfile = NULL, *fulldbsigfile = NULL;
 	const char *dbpath, *syncdbpath;
 	alpm_list_t *newdirlist = NULL, *olddirlist = NULL;
 	alpm_list_t *onlynew = NULL, *onlyold = NULL;
@@ -238,17 +241,62 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
 	dbpath = alpm_option_get_dbpath();
 
 	ret = _alpm_download_single_file(dbfile, db->servers, dbpath, force);
-	free(dbfile);
-
 	if(ret == 1) {
 		/* files match, do nothing */
 		pm_errno = 0;
+		free(dbfile);
 		return(1);
 	} else if(ret == -1) {
 		/* pm_errno was set by the download code */
 		_alpm_log(PM_LOG_DEBUG, "failed to sync db: %s\n", alpm_strerrorlast());
+		free(dbfile);
 		return(-1);
 	}
+	
+	/* Check the signature of the database, if it is marked as signed */
+	if(db->pgp_verify != PM_PGP_VERIFY_NEVER) {
+		/* Assemble the signature file name */
+		len = strlen(dbfile) + strlen(DBSIGEXT) + 1;
+		MALLOC(dbsigfile, len, RET_ERR(PM_ERR_MEMORY, -1));
+		sprintf(dbsigfile, "%s" DBSIGEXT, dbfile);
+
+		/* Try to download the signature file */
+		ret = _alpm_download_single_file(dbsigfile, db->servers, dbpath, force);
+		if (ret == -1 && db->pgp_verify == PM_PGP_VERIFY_ALWAYS) {
+			_alpm_log(PM_LOG_DEBUG, "failed to download signature for db: %s\n", alpm_strerrorlast());
+			free(dbfile);
+			free(dbsigfile);
+			return (-1);
+		}
+		else if (ret != -1) {
+			/* Assemble the full db and signature file names */
+			MALLOC(fulldbfile, strlen(dbpath) + strlen(dbfile) + 2, RET_ERR(PM_ERR_MEMORY, -1));
+			MALLOC(fulldbsigfile, strlen(dbpath) + strlen(dbsigfile) + 2, RET_ERR(PM_ERR_MEMORY, -1));
+			sprintf(fulldbfile, "%s/%s", dbpath, dbfile);
+			sprintf(fulldbsigfile, "%s/%s", dbpath, dbsigfile);
+
+			/* Check the signature */
+			int ret = _alpm_gpg_checksig_file(fulldbfile, fulldbsigfile);
+			_alpm_log(PM_LOG_DEBUG, "return from _alpm_gpg_checksig_file = %d\n", ret);
+
+			FREE(fulldbfile);
+			FREE(fulldbsigfile);
+
+			/* VerifSig = Always -> we will only accept 0 as a correct value
+			 * (missing or invalid signatures are errors)
+			 * VerifSig = Optional -> we will accept 0 or -1 as correct values
+			 * (missing signature is ok, but if it present, it must be valid) */
+			if((db->pgp_verify == PM_PGP_VERIFY_ALWAYS && ret != 0) ||
+					(db->pgp_verify == PM_PGP_VERIFY_OPTIONAL && ret == 1)) {
+				_alpm_log(PM_LOG_ERROR, "the signature doesn't match the repository database.\n");
+				free(dbfile);
+				free(dbsigfile);
+				RET_ERR(PM_ERR_DB_SIG, -1);
+			}
+		}
+		FREE(dbsigfile);
+	}
+	FREE(dbfile);
 
 	syncdbpath = _alpm_db_path(db);
 
@@ -256,7 +304,7 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
 	len = strlen(dbpath) + strlen(db->treename) + strlen(DBEXT) + 1;
 	MALLOC(dbfilepath, len, RET_ERR(PM_ERR_MEMORY, -1));
 	sprintf(dbfilepath, "%s%s" DBEXT, dbpath, db->treename);
-
+	
 	if(force) {
 		/* if forcing update, remove the old dir and extract the db */
 		if(_alpm_rmrf(syncdbpath) != 0) {
@@ -291,6 +339,10 @@ int SYMEXPORT alpm_db_update(int force, pmdb_t *db)
 	ret = _alpm_unpack(dbfilepath, syncdbpath, onlynew, 0);
 
 cleanup:
+	if (dbsigfile != NULL)
+		FREE(dbsigfile);
+	if (dbfile != NULL)
+		FREE(dbfile);
 	FREELIST(newdirlist);
 	FREELIST(olddirlist);
 	alpm_list_free(onlynew);
@@ -437,7 +489,8 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq)
 	char line[513];
 	int	sline = sizeof(line)-1;
 	char *pkgpath = NULL;
-
+	alpm_list_t *pgpsig_lines = NULL;
+	
 	ALPM_LOG_FUNC;
 
 	if(db == NULL) {
@@ -610,11 +663,33 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq)
 				STRDUP(info->md5sum, _alpm_strtrim(line), goto error);
 			} else if(strcmp(line, "%PGPSIG%") == 0) {
 				/* PGPSIG tag only appears in sync repositories,
-				 * not the local one. */
-				if(fgets(line, 512, fp) == NULL) {
-					goto error;
+				 * not the local one. The size must not be fixed,
+				 * because the key used will affect the final size */
+				pgpsig_lines = NULL;
+				int len = 0;
+				/* Create a list of strings to store the signature for now */
+				while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) {
+					char *linedup;
+					STRDUP(linedup, _alpm_strtrim(line), goto error);
+					pgpsig_lines = alpm_list_add(pgpsig_lines, linedup);
+					len += strlen(linedup);
 				}
-				STRDUP(info->pgpsig.encdata, _alpm_strtrim(line), goto error);
+
+				MALLOC(info->pgpsig.encdata, (size_t)(len + 1), goto error);
+				info->pgpsig.encdata[0] = '\0';
+				
+				/* Assemble the signature from the list of strings */
+				while (pgpsig_lines != NULL) {
+					alpm_list_t *pgpsig_line = pgpsig_lines;
+					pgpsig_lines = pgpsig_lines->next;
+					
+					strcat(info->pgpsig.encdata, (char *)pgpsig_line->data);
+					
+					/* Free the current node, as it is not needed anymore */
+					free(pgpsig_line->data);
+					free(pgpsig_line);
+				}
+				pgpsig_lines = NULL;
 			} else if(strcmp(line, "%REPLACES%") == 0) {
 				while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) {
 					char *linedup;
@@ -731,6 +806,9 @@ int _alpm_db_read(pmdb_t *db, pmpkg_t *info, pmdbinfrq_t inforeq)
 	return(0);
 
 error:
+	if (pgpsig_lines) {
+		FREELIST(pgpsig_lines);
+	}
 	free(pkgpath);
 	if(fp) {
 		fclose(fp);
diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c
index 907a9f2..1fccc46 100644
--- a/lib/libalpm/error.c
+++ b/lib/libalpm/error.c
@@ -158,8 +158,6 @@ const char SYMEXPORT *alpm_strerror(int err)
 			/* obviously shouldn't get here... */
 			return _("download library error");
 #endif
-		case PM_ERR_GPGME:
-			return _("gpgme error");
 		case PM_ERR_EXTERNAL_DOWNLOAD:
 			return _("error invoking external downloader");
 		/* Unknown error! */
diff --git a/lib/libalpm/signing.c b/lib/libalpm/signing.c
index 2b15528..80eba15 100644
--- a/lib/libalpm/signing.c
+++ b/lib/libalpm/signing.c
@@ -17,13 +17,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "config.h"
-
-#include <stdlib.h>
 #include <stdio.h>
-#include <string.h>
-#include <locale.h> /* setlocale() */
-#include <gpgme.h>
+#include <errno.h>
 
 /* libalpm */
 #include "signing.h"
@@ -32,169 +27,158 @@
 #include "log.h"
 #include "alpm.h"
 
-#define CHECK_ERR(void) do { \
-		if(err != GPG_ERR_NO_ERROR) { goto error; } \
-	} while(0)
+// Max length of the command line string. I believe that it is enough...
+#define MAX_COMMAND 2048
+#define MAX_LINE 1024
+#define TRUST_MARGINAL "[GNUPG:] TRUST_MARGINAL"
+#define TRUST_FULLY "[GNUPG:] TRUST_FULLY"
+#define TRUST_ULTIMATE "[GNUPG:] TRUST_ULTIMATE"
+
+// Names of files related to the keyring
+#define PACMAN_KEYRING_DIR "/etc/pacman.d/gnupg"
+#define PACMAN_SECRET "secret.gpg"
+#define PACMAN_TRUSTDB "trustdb.gpg"
+#define PACMAN_KEYRING "pacman.gpg"
+#define PACMAN_SHARE_DIR "/usr/share/pacman"
+
+// Commands and common parameters
+#define GPG_PROGRAM "gpg2"
+#define GPG GPG_PROGRAM " --ignore-time-conflict --no-options --no-default-keyring"
+#define GPG_PACMAN GPG " --status-fd 1 --homedir " PACMAN_KEYRING_DIR " --secret-keyring " \
+    PACMAN_KEYRING_DIR "/" PACMAN_SECRET " --trustdb-name " PACMAN_KEYRING_DIR \
+    "/" PACMAN_TRUSTDB " --keyring " PACMAN_KEYRING_DIR "/" PACMAN_KEYRING \
+    " --primary-keyring " PACMAN_KEYRING_DIR "/" PACMAN_KEYRING " 2> /dev/null"
 
-static int gpgme_init(void)
+/**
+ * Check the PGP package signature for the given package file.
+ * The signature is stored on memory, so it needs to be saved to
+ * a temporary file.
+ * @param pkgpath the full path to a package file
+ * @param sig PGP signature data in raw form (already decoded)
+ * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured)
+ */
+int _alpm_gpg_checksig_memory(const char *pkgpath, const pmpgpsig_t *sig)
 {
-	static int init = 0;
-	const char *version;
-	gpgme_error_t err;
-	gpgme_engine_info_t enginfo;
-
-	ALPM_LOG_FUNC;
-
-	if(init) {
-		/* we already successfully initialized the library */
-		return(0);
+	int status = 1, i;
+	char *tmpName = NULL;
+	FILE *tmpSig = NULL;
+	
+	// Get a temporary file, which will store the signature.
+	while (tmpSig == NULL) {
+		tmpName = tempnam(NULL, "pacman");
+		if (tmpName == NULL) {
+			_alpm_log(PM_LOG_ERROR, _("Unable to create a temporary file name.\n"));
+			status = -1;
+			goto cleanup;
+		}
+		_alpm_log(PM_LOG_DEBUG, "tmpName: \'%s\'\n", tmpName);
+		
+		// Write the signature to the temporary file
+		tmpSig = fopen(tmpName, "w+x");
+		if (tmpSig == NULL && errno == EEXIST) {
+			_alpm_log(PM_LOG_DEBUG, "the temporary file already exists\n");
+			FREE(tmpName);
+		} else if (tmpSig == NULL) {
+			_alpm_log(PM_LOG_ERROR, _("Unable to create a temporary file.\n"));
+			status = -1;
+			goto cleanup;
+		}
 	}
 
-	if(!alpm_option_get_signaturedir()) {
-		RET_ERR(PM_ERR_SIG_MISSINGDIR, 1);
+	// Copy the signature into the temp file
+	// Write the bytes at once, to make it more efficient
+	for (i = 0; i < sig->rawlen; i++) {
+		if (fputc(sig->rawdata[i], tmpSig) == EOF) {
+			_alpm_log(PM_LOG_ERROR, _("Error writing signature to temporary file\n"));
+			status = -1;
+			goto cleanup;
+		}
 	}
 
-	/* calling gpgme_check_version() returns the current version and runs
-	 * some internal library setup code */
-	version = gpgme_check_version(NULL);
-	_alpm_log(PM_LOG_DEBUG, "GPGME version: %s\n", version);
-	gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
-#ifdef LC_MESSAGES
-	gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
-#endif
-	/* NOTE:
-	 * The GPGME library installs a SIGPIPE signal handler automatically if
-	 * the default signal hander is in use. The only time we set a handler
-	 * for SIGPIPE is in dload.c, and we reset it when we are done. Given that
-	 * we do this, we can let GPGME do its automagic. However, if we install
-	 * a library-wide SIGPIPE handler, we will have to be careful.
-	 */
-
-	/* check for OpenPGP support (should be a no-brainer, but be safe) */
-	err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
-	CHECK_ERR();
-
-	/* set and check engine information */
-	err = gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL,
-			alpm_option_get_signaturedir());
-	CHECK_ERR();
-	err = gpgme_get_engine_info(&enginfo);
-	CHECK_ERR();
-	_alpm_log(PM_LOG_DEBUG, "GPGME engine info: file=%s, home=%s\n",
-			enginfo->file_name, enginfo->home_dir);
-
-	init = 1;
-	return(0);
-
-error:
-	_alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
-	RET_ERR(PM_ERR_GPGME, 1);
+	// Close the temporary file so that gpg can use it
+	fclose(tmpSig);
+	tmpSig = NULL;
+	
+	// Calls the function that checks the signature in a file
+	status = _alpm_gpg_checksig_file(pkgpath, tmpName);
+cleanup:
+	if (tmpSig != NULL) {
+		fclose(tmpSig);
+	}
+	if (tmpName != NULL) {
+		// Delete the temporary file, if it exists
+		if (access(tmpName, F_OK) != -1) {
+			remove(tmpName);
+		}
+		
+		FREE(tmpName);
+	}
+	return status;
 }
 
 /**
  * Check the PGP package signature for the given package file.
+ * The signature is stored in a file, which is passed to gpg.
  * @param pkgpath the full path to a package file
- * @param sig PGP signature data in raw form (already decoded)
+ * @param sigpath path to the PGP Signature file.
  * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured)
  */
-int _alpm_gpgme_checksig(const char *pkgpath, const pmpgpsig_t *sig)
+int _alpm_gpg_checksig_file(const char *pkgpath, const char *sigpath)
 {
-	int ret = 0;
-	gpgme_error_t err;
-	gpgme_ctx_t ctx;
-	gpgme_data_t pkgdata, sigdata;
-	gpgme_verify_result_t result;
-	gpgme_signature_t gpgsig;
-	FILE *pkgfile = NULL, *sigfile = NULL;
-
-	ALPM_LOG_FUNC;
-
-	if(!sig || !sig->rawdata) {
-		 RET_ERR(PM_ERR_SIG_UNKNOWN, -1);
-	}
-	if(!pkgpath || access(pkgpath, R_OK) != 0) {
-		RET_ERR(PM_ERR_PKG_NOT_FOUND, -1);
+	int status = 1;
+	char *command = NULL, *line = NULL;
+	FILE *output = NULL;
+	int resultCommand = 0, statusCommand = 0, trusted = 0;
+	
+	CALLOC(command, MAX_COMMAND, sizeof(char), RET_ERR(PM_ERR_MEMORY, NULL));
+	
+	if (snprintf(command, MAX_COMMAND, GPG_PACMAN " --verify %s %s", sigpath, pkgpath) >= MAX_COMMAND) {
+		_alpm_log(PM_LOG_ERROR, _("The gpg command string is too long\n"));
+		status = -1;
+		goto cleanup;
 	}
-	if(gpgme_init()) {
-		/* pm_errno was set in gpgme_init() */
-		return(-1);
+	_alpm_log(PM_LOG_DEBUG, "gpg command is: \'%s\'\n", command);
+	
+	output = popen(command, "r");
+	if (output == NULL) {
+		_alpm_log(PM_LOG_ERROR, _("Error calling gpg2 external program\n"));
+		status = -1;
+		goto cleanup;
 	}
-	err = gpgme_new(&ctx);
-	CHECK_ERR();
-
-	/* create our necessary data objects to verify the signature */
-	/* first the package itself */
-	pkgfile = fopen(pkgpath, "rb");
-	if(pkgfile == NULL) {
-		pm_errno = PM_ERR_PKG_OPEN;
-		ret = -1;
-		goto error;
-	}
-	err = gpgme_data_new_from_stream(&pkgdata, pkgfile);
-	CHECK_ERR();
-
-	/* next create data object for the signature */
-	err = gpgme_data_new_from_mem(&sigdata, (char*)sig->rawdata, sig->rawlen, 0);
-	CHECK_ERR();
-
-	/* here's where the magic happens */
-	err = gpgme_op_verify(ctx, sigdata, pkgdata, NULL);
-	CHECK_ERR();
-	result = gpgme_op_verify_result(ctx);
-		gpgsig = result->signatures;
-	if (!gpgsig || gpgsig->next) {
-		_alpm_log(PM_LOG_ERROR, _("Unexpected number of signatures\n"));
-		ret = -1;
-		goto error;
-	}
-	fprintf(stdout, "\nsummary=%x\n", gpgsig->summary);
-	fprintf(stdout, "fpr=%s\n", gpgsig->fpr);
-	fprintf(stdout, "status=%d\n", gpgsig->status);
-	fprintf(stdout, "timestamp=%lu\n", gpgsig->timestamp);
-	fprintf(stdout, "wrong_key_usage=%u\n", gpgsig->wrong_key_usage);
-	fprintf(stdout, "pka_trust=%u\n", gpgsig->pka_trust);
-	fprintf(stdout, "chain_model=%u\n", gpgsig->chain_model);
-	fprintf(stdout, "validity=%d\n", gpgsig->validity);
-	fprintf(stdout, "validity_reason=%d\n", gpgsig->validity_reason);
-	fprintf(stdout, "key=%d\n", gpgsig->pubkey_algo);
-	fprintf(stdout, "hash=%d\n", gpgsig->hash_algo);
-
-	if(gpgsig->summary & GPGME_SIGSUM_VALID) {
-		/* good signature, continue */
-		_alpm_log(PM_LOG_DEBUG, _("Package %s has a valid signature.\n"),
-				pkgpath);
-	} else if(gpgsig->summary & GPGME_SIGSUM_GREEN) {
-		/* 'green' signature, not sure what to do here */
-		_alpm_log(PM_LOG_WARNING, _("Package %s has a green signature.\n"),
-				pkgpath);
-	} else if(gpgsig->summary & GPGME_SIGSUM_KEY_MISSING) {
-		pm_errno = PM_ERR_SIG_UNKNOWN;
-		_alpm_log(PM_LOG_WARNING, _("Package %s has a signature from an unknown key.\n"),
-				pkgpath);
-		ret = -1;
-	} else {
-		/* we'll capture everything else here */
-		pm_errno = PM_ERR_SIG_INVALID;
-		_alpm_log(PM_LOG_ERROR, _("Package %s has an invalid signature.\n"),
-				pkgpath);
-		ret = 1;
+	
+	// Read the output of the command to see if there are lines with the pattern
+	// TRUST_MARGINAL, TRUST_FULLY or TRUST_ULTIMATE. These results indicate that
+	// the level of trust of the key are at leas MARGINAL, which means that the
+	// users trusts the signer or there is a path in the web of trust that
+	// assigns at least the MARGINAL level
+	CALLOC(line, MAX_LINE, sizeof(char), RET_ERR(PM_ERR_MEMORY, NULL));
+	while (fgets(line, MAX_LINE, output)) {
+		// Debug of status messages from gpg
+		if (strncmp(line, "[GNUPG:]", (size_t)strlen("[GNUPG:]")) == 0) {
+			_alpm_log(PM_LOG_DEBUG, "%s\n", line);
+		}
+		if (strncmp(line, TRUST_MARGINAL, (size_t)strlen(TRUST_MARGINAL)) == 0 ||
+		    strncmp(line, TRUST_FULLY, (size_t)strlen(TRUST_FULLY)) == 0 ||
+		    strncmp(line, TRUST_ULTIMATE, (size_t)strlen(TRUST_ULTIMATE)) == 0) {
+			trusted = 1;
+		}
 	}
+	
+	resultCommand = pclose(output);
 
-error:
-	gpgme_data_release(sigdata);
-	gpgme_data_release(pkgdata);
-	gpgme_release(ctx);
-	if(sigfile) {
-		fclose(sigfile);
+	// We don't need to check the return status of the command, because the
+	// trusted variable only is 1 if the signature is valid and trusted.
+	if (trusted == 1) {
+	  status = 0;
 	}
-	if(pkgfile) {
-		fclose(pkgfile);
+cleanup:
+	if (line) {
+		FREE(line);
 	}
-	if(err != GPG_ERR_NO_ERROR) {
-		_alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
-		RET_ERR(PM_ERR_GPGME, -1);
+	if (command) {
+		FREE(command);
 	}
-	return(ret);
+	return status;
 }
 
 /**
@@ -207,7 +191,7 @@ int SYMEXPORT alpm_pkg_check_pgp_signature(pmpkg_t *pkg)
 	ALPM_LOG_FUNC;
 	ASSERT(pkg != NULL, return(0));
 
-	return(_alpm_gpgme_checksig(alpm_pkg_get_filename(pkg),
+	return(_alpm_gpg_checksig_memory(alpm_pkg_get_filename(pkg),
 			alpm_pkg_get_pgpsig(pkg)));
 }
 
diff --git a/lib/libalpm/signing.h b/lib/libalpm/signing.h
index c004697..fe28a22 100644
--- a/lib/libalpm/signing.h
+++ b/lib/libalpm/signing.h
@@ -21,7 +21,8 @@
 
 #include "alpm.h"
 
-int _alpm_gpgme_checksig(const char *pkgpath, const pmpgpsig_t *sig);
+int _alpm_gpg_checksig_memory(const char *pkgpath, const pmpgpsig_t *sig);
+int _alpm_gpg_checksig_file(const char *pkgpath, const char *sigpath);
 
 #endif /* _ALPM_SIGNING_H */
 
diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c
index 3994d5d..aa1fffe 100644
--- a/lib/libalpm/sync.c
+++ b/lib/libalpm/sync.c
@@ -938,7 +938,7 @@ int _alpm_sync_commit(pmtrans_t *trans, pmdb_t *db_local, alpm_list_t **data)
 		pmdb_t *sdb = alpm_pkg_get_db(spkg);
 
 		if(sdb->pgp_verify != PM_PGP_VERIFY_NEVER) {
-			int ret = _alpm_gpgme_checksig(filepath, pgpsig);
+			int ret = _alpm_gpg_checksig_memory(filepath, pgpsig);
 			if((sdb->pgp_verify == PM_PGP_VERIFY_ALWAYS && ret != 0) ||
 					(sdb->pgp_verify == PM_PGP_VERIFY_OPTIONAL && ret == 1)) {
 				errors++;
diff --git a/scripts/.gitignore b/scripts/.gitignore
index eafc493..1c662de 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -4,3 +4,4 @@ rankmirrors
 repo-add
 repo-remove
 pkgdelta
+pacman-key
diff --git a/scripts/Makefile.am b/scripts/Makefile.am
index 330acb9..d4f96de 100644
--- a/scripts/Makefile.am
+++ b/scripts/Makefile.am
@@ -7,6 +7,7 @@ bin_SCRIPTS = \
 
 OURSCRIPTS = \
 	makepkg \
+	pacman-key \
 	pacman-optimize \
 	pkgdelta \
 	rankmirrors \
@@ -14,6 +15,7 @@ OURSCRIPTS = \
 
 EXTRA_DIST = \
 	makepkg.sh.in \
+	pacman-key.sh.in \
 	pacman-optimize.sh.in \
 	pkgdelta.sh.in \
 	rankmirrors.sh.in \
@@ -60,6 +62,7 @@ $(OURSCRIPTS): Makefile
 	@mv $@.tmp $@
 
 makepkg: $(srcdir)/makepkg.sh.in
+pacman-key: ${srcdir}/pacman-key.sh.in
 pacman-optimize: $(srcdir)/pacman-optimize.sh.in
 pkgdelta: $(srcdir)/pkgdelta.sh.in
 rankmirrors: $(srcdir)/rankmirrors.sh.in
diff --git a/scripts/makepkg.sh.in b/scripts/makepkg.sh.in
index 223bf12..cb25312 100644
--- a/scripts/makepkg.sh.in
+++ b/scripts/makepkg.sh.in
@@ -28,7 +28,7 @@
 # makepkg uses quite a few external programs during its execution. You
 # need to have at least the following installed for makepkg to function:
 #   bsdtar (libarchive), bzip2, coreutils, fakeroot, find (findutils),
-#   gettext, grep, gzip, openssl, sed, tput (ncurses), xz
+#   gettext, grep, gzip, openssl, sed, tput (ncurses), xz, gpg
 
 # gettext initialization
 export TEXTDOMAIN='pacman'
@@ -43,6 +43,8 @@ BUILDSCRIPT='@BUILDSCRIPT@'
 startdir="$PWD"
 srcdir="$startdir/src"
 pkgdir="$startdir/pkg"
+GPG="gpg2"
+SIG_EXT=".sig"
 
 packaging_options=('strip' 'docs' 'libtool' 'emptydirs' 'zipman' 'purge')
 other_options=('ccache' 'distcc' 'makeflags' 'force')
@@ -74,6 +76,7 @@ BUILDFUNC=0
 PKGFUNC=0
 SPLITPKG=0
 PKGLIST=""
+SIGNKEY=""
 
 # Forces the pkgver of the current PKGBUILD. Used by the fakeroot call
 # when dealing with svn/cvs/etc PKGBUILDs.
@@ -1028,7 +1031,7 @@ create_package() {
 	local ret=0
 
 	[[ -f $pkg_file ]] && rm -f "$pkg_file"
-	[[ -f $pkg_file.sig ]] && rm -f "$pkg_file.sig"
+	[[ -f ${pkg_file}${SIG_EXT} ]] && rm -f "${pkg_file}${SIG_EXT}"
 
 	# when fileglobbing, we want * in an empty directory to expand to
 	# the null string rather than itself
@@ -1055,7 +1058,7 @@ create_package() {
 	if (( ! ret )) && [[ "$PKGDEST" != "${startdir}" ]]; then
 		ln -sf "${pkg_file}" "${pkg_file/$PKGDEST/$startdir}"
 		ret=$?
-		[[ -f $pkg_file.sig ]] && ln -sf "$pkg_file.sig" "${pkg_file/$PKGDEST/$startdir}.sig"
+		[[ -f ${pkg_file}${SIG_EXT} ]] && ln -sf "${pkg_file}${SIG_EXT}" "${pkg_file/$PKGDEST/$startdir}${SIG_EXT}"
 	fi
 
 	if (( ret )); then
@@ -1070,13 +1073,24 @@ create_signature() {
 	local ret=0
 	local filename="$1"
 	msg "$(gettext "Signing package...")"
-	if [ ! $(type -p "gpg") ]; then
-		error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")"
+	if [[ ! $(type -p "${GPG}") ]]; then
+		error "$(gettext "Cannot find the ${GPG} binary! Is gnupg installed?")"
 		exit 1 # $E_MISSING_PROGRAM
 	fi
-	gpg --detach-sign --use-agent "$filename" || ret=$?
+
+	# Check if SIGNKEY is valid.
+	local SIGNWITHKEY=""
+	if [[ "${SIGNKEY}" ]]; then
+		if ! ${GPG} --list-key "${SIGNKEY}" 1>/dev/null 2>&1; then
+			error "$(gettext "The key ${SIGNKEY} doesn\'t exist.")"
+			exit 1
+		fi
+		SIGNWITHKEY="-u ${SIGNKEY}"
+	fi
+	# The signature will be generated directly in ascii-friendly format
+	${GPG} --detach-sign ${SIGNWITHKEY} "$filename" || ret=$?
 	if (( ! ret )); then
-		msg2 "$(gettext "Created signature file %s.")" "$filename.sig"
+		msg2 "$(gettext "Created signature file %s.")" "${filename}${SIG_EXT}"
 	else
 		warning "$(gettext "Failed to sign package file.")"
 	fi
@@ -1522,6 +1536,7 @@ usage() {
 	echo "$(gettext "  --pkg <list>     Only build listed packages from a split package")"
 	echo "$(gettext "  --skipinteg      Do not fail when integrity checks are missing")"
 	echo "$(gettext "  --source         Generate a source-only tarball without downloaded sources")"
+	echo "$(gettext "  --signwithkey    Selects an specific key to use for signing")"
 	echo
 	echo "$(gettext "These options can be passed to pacman:")"
 	echo
@@ -1557,7 +1572,7 @@ OPT_SHORT="AcCdefFghiLmop:rRsV"
 OPT_LONG="allsource,asroot,ignorearch,clean,cleancache,nodeps"
 OPT_LONG="$OPT_LONG,noextract,force,forcever:,geninteg,help,holdver"
 OPT_LONG="$OPT_LONG,install,log,nocolor,nobuild,pkg:,rmdeps,repackage,skipinteg"
-OPT_LONG="$OPT_LONG,source,syncdeps,version,config:"
+OPT_LONG="$OPT_LONG,source,syncdeps,version,config:,signwithkey"
 # Pacman Options
 OPT_LONG="$OPT_LONG,noconfirm,noprogressbar"
 OPT_TEMP="$(parse_options $OPT_SHORT $OPT_LONG "$@" || echo 'PARSE_OPTIONS FAILED')"
@@ -1600,6 +1615,7 @@ while true; do
 		--skipinteg)      SKIPINTEG=1 ;;
 		--source)         SOURCEONLY=1 ;;
 		-s|--syncdeps)    DEP_BIN=1 ;;
+		--signwithkey)    shift; SIGNKEY=$1 ;;
 
 		-h|--help)        usage; exit 0 ;; # E_OK
 		-V|--version)     version; exit 0 ;; # E_OK
diff --git a/scripts/pacman-key.sh.in b/scripts/pacman-key.sh.in
new file mode 100644
index 0000000..a33c684
--- /dev/null
+++ b/scripts/pacman-key.sh.in
@@ -0,0 +1,278 @@
+#!/bin/bash -e
+#
+#   pacman-key - manages pacman's keyring
+#   @configure_input@
+#
+#   Copyright (c) 2010 - Denis A. Altoé Falqueto <denisfalqueto at gmail.com>
+#
+#   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, see <http://www.gnu.org/licenses/>.
+#
+
+# gettext initialization
+export TEXTDOMAIN='pacman'
+export TEXTDOMAINDIR='@localedir@'
+
+# Based on apt-key, from Debian
+# Author: Denis A. Altoé Falqueto <denisfalqueto at gmail dot com>
+
+PACMAN_KEY_VERSION="0.1"
+
+# According to apt-key, gpg doesn't like to be called without a secret keyring.
+# We will not really need one, because pacman will not sign packages, just verify
+# their integrities.
+PACMAN_KEYRING_DIR="/etc/pacman.d/gnupg"
+PACMAN_SECRET="secret.gpg"
+PACMAN_TRUSTDB="trustdb.gpg"
+PACMAN_KEYRING="pacman.gpg"
+PACMAN_SHARE_DIR="/usr/share/pacman"
+
+# Default parameters for the command gpg. Some more will be added when needed
+GPG_PROGRAM="gpg2"
+GPG="${GPG_PROGRAM} --ignore-time-conflict --no-options --no-default-keyring"
+GPG_PACMAN="${GPG} --homedir ${PACMAN_KEYRING_DIR} --secret-keyring ${PACMAN_KEYRING_DIR}/${PACMAN_SECRET} \
+    --trustdb-name ${PACMAN_KEYRING_DIR}/${PACMAN_TRUSTDB} --keyring ${PACMAN_KEYRING_DIR}/${PACMAN_KEYRING} \
+    --primary-keyring ${PACMAN_KEYRING_DIR}/${PACMAN_KEYRING}"
+SIG_EXT=".sig"
+
+# Read-only keyring with keys to be added to the keyring
+ADDED_KEYS="${PACMAN_SHARE_DIR}/addedkeys.gpg"
+
+# Read-only keyring with keys removed from the keyring. They need to be removed before 
+# the keys from the added keyring be really imported
+REMOVED_KEYS="${PACMAN_SHARE_DIR}/removedkeys.gpg"
+
+usage() {
+    echo "pacman-key - Pacman's keyring management utility"
+    echo "Usage: $(basename $0) command [arguments]"
+    echo
+    echo "Manage pacman's list of trusted keys"
+    echo
+    echo "  pacman-key add <file> ...                  - add the key contained in <file> ('-' for stdin)"
+    echo "  pacman-key del <keyid> ...                 - remove the key <keyid>"
+    echo "  pacman-key export <keyid> ...              - output the key <keyid>"
+    echo "  pacman-key exportall                       - output all trusted keys"
+    echo "  pacman-key receive <keyserver> <keyid> ... - fetch the keyids from the specified keyserver URL"
+    echo "  pacman-key trust <keyid> ...               - set the truslevel of the given key"
+    echo "  pacman-key updatedb                        - update the trustdb of pacman"
+    echo "  pacman-key reload                          - reloads the keys from the keyring package"
+    echo "  pacman-key list                            - list keys"
+    echo "  pacman-key finger <keyid> ...              - list fingerprints"
+    echo "  pacman-key adv <params>                    - pass advanced options to gpg"
+    echo "  pacman-key help                            - displays this message"
+    echo "  pacman-key version                         - displays the current version"
+    echo
+    echo "If no specific keyring file is given the command applies to the default keyring."
+}
+
+prepare_homedir() {
+    if [[ ! -d ${PACMAN_KEYRING_DIR} ]] ; then
+        mkdir -p "${PACMAN_KEYRING_DIR}"
+        [[ ! -f "${PACMAN_KEYRING_DIR}/${PACMAN_SECRET}" ]] && touch "${PACMAN_KEYRING_DIR}/${PACMAN_SECRET}"
+        [[ ! -f "${PACMAN_KEYRING_DIR}/${PACMAN_KEYRING}" ]] && touch "${PACMAN_KEYRING_DIR}/${PACMAN_KEYRING}"
+        chmod 700 "${PACMAN_KEYRING_DIR}"
+        chmod 600 "${PACMAN_KEYRING_DIR}"/*
+    fi
+}
+
+add_key() {
+    ${GPG_PACMAN} --quiet --batch --import "$1"
+}
+
+remove_key() {
+    ${GPG_PACMAN} --quiet --batch --delete-key --yes "$1"
+}
+
+update_trustdb() {
+    ${GPG_PACMAN} --batch --check-trustdb
+}
+
+list_sigs() {
+    ${GPG_PACMAN} --batch --list-sigs
+}
+
+list_fingerprints() {
+    ${GPG_PACMAN} --batch --fingerprint $*
+}
+
+export_key() {
+    ${GPG_PACMAN} --armor --export "$1"
+}
+
+export_all() {
+    ${GPG_PACMAN} --armor --export
+}
+
+trust_key() {
+    # Verify if the key exists in pacman's keyring
+    if ${GPG_PACMAN} --list-key "$1" > /dev/null 2>&1 ; then
+      ${GPG_PACMAN} --fingerprint "$1"
+      ${GPG_PACMAN} --edit-key "$1"
+    else
+      echo "The key identified by $1 doesn't exist"
+      exit 1
+    fi
+}
+
+reload_keyring() {
+    # Verify the signature of removed keys file
+    if [[ -f ${REMOVED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${REMOVED_KEYS}${SIG_EXT} ; then
+        echo "The signature of file ${REMOVED_KEYS} is not valid."
+        exit 1
+    fi
+
+    # Verify the signature of the added keys file
+    if [[ -f ${ADDED_KEYS} ]] && ! ${GPG_PACMAN} --quiet --verify ${ADDED_KEYS}${SIG_EXT} ; then
+        echo "The signature of file ${ADDED_KEYS} is not valid."
+        exit 1
+    fi
+
+    # Remove the keys from REMOVED_KEYS keyring
+    [[ -r ${REMOVED_KEYS} ]] && cat "${REMOVED_KEYS}" | while read key ; do
+        ${GPG_PACMAN} --quiet --batch --yes --delete-keys ${key}
+    done
+
+    # Add keys from the current set of keys from pacman-keyring package. The web of trust will
+    # be updated automatically.
+    if [[ -r ${ADDED_KEYS} ]] ; then
+	add_keys=$(${GPG} --keyring ${ADDED_KEYS} --with-colons --list-keys | grep ^pub | cut -d: -f5)
+	for add_key in $add_keys; do
+            echo "Chave $add_key"
+	    ${GPG} --quiet --batch --keyring $ADDED_KEYS --export $add_key | ${GPG_PACMAN} --import
+	    ADDED=1
+	done
+    fi
+
+    # Update trustdb, just to be sure
+    update_trustdb
+}
+
+receive() {
+    keyserver="$1"
+    shift
+    ${GPG_PACMAN} --keyserver ${keyserver} $*
+}
+
+# PROGRAM START
+
+if ! type gettext &>/dev/null; then
+	gettext() {
+		echo "$@"
+	}
+fi
+
+command="$1"
+if [[ -z "$command" ]]; then
+    usage
+    exit 1
+fi
+shift
+
+if [[ "$command" != "version" && "$command" != "help" ]] && ! which "${GPG_PROGRAM}" >/dev/null 2>&1; then
+    echo >&2 "Warning: gnupg does not seem to be installed."
+    echo >&2 "Warning: pacman-key requires gnupg for most operations."
+    echo >&2
+fi
+
+prepare_homedir
+
+case "$command" in
+    add)
+        if (( $# == 0 )) ; then
+            echo "You need to specify at least one key identifier"
+            usage
+            exit 1
+        fi
+        while (( $# > 0 )) ; do
+          add_key $1
+          shift
+        done
+        ;;
+    del|rm|remove)
+        if (( $# == 0 )) ; then
+            echo "You need to specify at least one key identifier"
+            usage
+            exit 1
+        fi
+        while (( $# > 0 )) ; do
+            remove_key $1
+            shift
+        done
+        ;;
+    updatedb)
+        update_trustdb
+	;;
+    reload)
+        reload_keyring
+        ;;
+    list)
+        list_sigs
+        ;;
+    finger*)
+        if (( $# == 0 )) ; then
+            echo "You need to specify at least one key identifier"
+            usage
+            exit 1
+        fi
+        list_fingerprints $*
+        ;;
+    export)
+        if (( $# == 0 )) ; then
+            echo "You need to specify at least one key identifier"
+            usage
+            exit 1
+        fi
+        while (( $# > 0 )) ; do
+            export_key $1
+            shift
+        done
+        ;;
+    exportall)
+        export_all
+        ;;
+    receive)
+        if (( $# < 2 )) ; then
+            echo "You need to specify the keyserver and at least one key identifier"
+            usage
+            exit 1
+        fi
+        receive $*
+        ;;
+    trust)
+        if (( $# == 0 )) ; then
+            echo "You need to specify at least one key identifier"
+            usage
+            exit 1
+        fi
+        while (( $# > 0 )) ; do
+            trust_key $1
+            shift
+        done
+        ;;
+    adv*)
+        echo "Executing: ${GPG_PACMAN} $*"
+        ${GPG_PACMAN} $* || ret=$?
+	exit $ret
+        ;;
+    --help)
+        usage
+        ;;
+    --version)
+        echo "pacman-key v${PACMAN_KEY_VERSION}"
+        echo " This program can be freely distributed under the GPL v2"
+        ;;
+    *)
+        usage
+        exit 1
+        ;;
+esac
diff --git a/scripts/repo-add.sh.in b/scripts/repo-add.sh.in
index 9b04be9..ad9b524 100644
--- a/scripts/repo-add.sh.in
+++ b/scripts/repo-add.sh.in
@@ -26,6 +26,8 @@ export TEXTDOMAINDIR='@localedir@'
 
 myver='@PACKAGE_VERSION@'
 confdir='@sysconfdir@'
+GPG="gpg2"
+SIG_EXT=".sig"
 
 QUIET=0
 SIGN=0
@@ -62,8 +64,8 @@ error() {
 # print usage instructions
 usage() {
 	printf "repo-add, repo-remove (pacman) %s\n\n" "$myver"
-	printf "$(gettext "Usage: repo-add [-q] [-s] [-v] <path-to-db> <package|delta> ...\n")"
-	printf "$(gettext "Usage: repo-remove [-q] <path-to-db> <packagename|delta> ...\n\n")"
+	printf "$(gettext "Usage: repo-add [-q] [-s [-k key]] [-v] <path-to-db> <package|delta> ...\n")"
+	printf "$(gettext "Usage: repo-remove [-q] [-s [-k key]] <path-to-db> <packagename|delta> ...\n\n")"
 	printf "$(gettext "\
 repo-add will update a package database by reading a package file.\n\
 Multiple packages to add can be specified on the command line.\n\n")"
@@ -185,13 +187,24 @@ create_signature() {
 	local dbfile="$1"
 	local ret=0
 	msg "$(gettext "Signing database...")"
-	if [ ! $(type -p "gpg") ]; then
-		error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")"
+	if [ ! $(type -p "${GPG}") ]; then
+		error "$(gettext "Cannot find the ${GPG} binary! Is gnupg installed?")"
 		exit 1 # $E_MISSING_PROGRAM
 	fi
-	gpg --detach-sign --use-agent "$dbfile" || ret=$?
+
+	# Check if SIGNKEY is valid.
+	local SIGNWITHKEY=""
+	if [[ "${SIGNKEY}" ]]; then
+		if ! "${GPG}" --list-key "${SIGNKEY}" 1>/dev/null 2>&1; then
+			error "$(gettext "The key ${SIGNKEY} doesnn't exist.")"
+			exit 1
+		fi
+		SIGNWITHKEY="-u ${SIGNKEY}"
+	fi
+	echo "${GPG} --detach-sign ${SIGNWITHKEY} $dbfile"
+	${GPG} --detach-sign ${SIGNWITHKEY} "$dbfile" || ret=$?
 	if (( ! ret )); then
-		msg2 "$(gettext "Created signature file %s.")" "$dbfile.sig"
+		msg2 "$(gettext "Created signature file %s.")" "${dbfile}${SIG_EXT}"
 	else
 		warning "$(gettext "Failed to sign package database.")"
 	fi
@@ -203,15 +216,15 @@ verify_signature() {
 	local dbfile="$1"
 	local ret=0
 	msg "$(gettext "Verifying database signature...")"
-	if [ ! $(type -p "gpg") ]; then
-		error "$(gettext "Cannot find the gpg binary! Is gnupg installed?")"
+	if [ ! $(type -p "${GPG}") ]; then
+		error "$(gettext "Cannot find the ${GPG} binary! Is gnupg installed?")"
 		exit 1 # $E_MISSING_PROGRAM
 	fi
-	if [[ ! -f $dbfile.sig ]]; then
+	if [[ ! -f ${dbfile}${SIG_EXT} ]]; then
 		warning "$(gettext "No existing signature found, skipping verification.")"
 		return
 	fi
-	gpg --verify "$dbfile.sig" || ret=$?
+	${GPG} --verify "${dbfile}${SIG_EXT}" || ret=$?
 	if (( ! ret )); then
 		msg2 "$(gettext "Database signature file verified.")"
 	else
@@ -298,9 +311,9 @@ db_write_entry()
 	echo -e "%MD5SUM%\n$md5sum\n" >>desc
 
 	# add base64'd PGP signature
-	if [[ -f $startdir/$pkgfile.sig ]]; then
+	if [[ -f $startdir/$pkgfile${SIG_EXT} ]]; then
 		echo -e "%PGPSIG%" >>desc
-		echo -e "$(openssl base64 -in "$startdir/$pkgfile.sig" | tr -d '\n')\n" >>desc
+		echo -e "$(openssl enc -base64 -in "$startdir/$pkgfile${SIG_EXT}")\n" >>desc
 	fi
 
 	[[ -n $url ]] && echo -e "%URL%\n$url\n" >>desc
@@ -492,10 +505,24 @@ trap 'trap_exit "$(gettext "An unknown error has occured. Exiting...")"' ERR
 
 success=0
 # parse arguments
-for arg in "$@"; do
+while [[ $# > 0 ]] ; do
+	arg="$1"
 	case "$arg" in
 		-q|--quiet) QUIET=1;;
-		-s|--sign) SIGN=1;;
+		-s|--sign) 
+			SIGN=1
+			# The signature will be made, even if there are no operations
+			success=1
+			;;
+		-k) 
+			shift
+			SIGNKEY="$1"
+			# Check if key really exists
+			if ! ${GPG} --list-key ${SIGNKEY} 1> /dev/null 2>&1; then
+				error "$(gettext "Cannot find key $SIGNKEY.")"
+				exit 1
+			fi
+			;;
 		-v|--verify) VERIFY=1;;
 		*)
 			if [[ -z $REPO_DB_FILE ]]; then
@@ -510,6 +537,7 @@ for arg in "$@"; do
 			fi
 			;;
 	esac
+	shift
 done
 
 # if at least one operation was a success, re-zip database
@@ -529,18 +557,19 @@ if (( success )); then
 	cd "$tmpdir"
 	if [[ -n $(ls) ]]; then
 		bsdtar -c${TAR_OPT}f "$filename" *
-		create_signature "$filename"
 	else
 		# we have no packages remaining? zip up some emptyness
 		warning "$(gettext "No packages remain, creating empty database.")"
 		bsdtar -c${TAR_OPT}f "$filename" -T /dev/null
 	fi
+	# The signature must be dealt with in both cases, empty repo or not.
+	create_signature "$filename"
 	cd "$startdir"
 
 	[[ -f $REPO_DB_FILE ]] && mv -f "$REPO_DB_FILE" "${REPO_DB_FILE}.old"
-	[[ -f $REPO_DB_FILE.sig ]] && rm -f "$REPO_DB_FILE.sig"
+	[[ -f $REPO_DB_FILE${SIG_EXT} ]] && rm -f "$REPO_DB_FILE${SIG_EXT}"
 	[[ -f $tmpdir/$filename ]] && mv "$tmpdir/$filename" "$REPO_DB_FILE"
-	[[ -f $tmpdir/$filename.sig ]] && mv "$tmpdir/$filename.sig" "$REPO_DB_FILE.sig"
+	[[ -f $tmpdir/$filename${SIG_EXT} ]] && mv "$tmpdir/$filename${SIG_EXT}" "$REPO_DB_FILE${SIG_EXT}"
 	ln -sf "$REPO_DB_FILE" "${REPO_DB_FILE%.tar.*}"
 else
 	msg "$(gettext "No packages modified, nothing to do.")"
-- 
1.7.1



More information about the pacman-dev mailing list