[pacman-dev] [PATCH 5/6] Verify the signatures of databases and packages

Denis A. Altoé Falqueto denisfalqueto at gmail.com
Wed Jul 7 13:43:45 EDT 2010


The signatures of databases will be verified when the database
is being updated. The option VerifySig in the database section
of pacman.conf will dictate how to deal with the signature.
If there is a need to verify (VerifySig is Always or Optional),
the signature will be downloaded and verifyed.

When signing packages, the signature will come from the database
repository where it belongs. So, if a package will be installed from
core, the signature will be read from the desc file, field %PGPSIG%.

Signed-off-by: Denis A. Altoé Falqueto <denisfalqueto at gmail.com>
---
 lib/libalpm/be_files.c |   90 ++++++++++++++++++++++++++++++++++++++++++++----
 lib/libalpm/sync.c     |    2 +-
 2 files changed, 84 insertions(+), 8 deletions(-)

diff --git a/lib/libalpm/be_files.c b/lib/libalpm/be_files.c
index 1235165..ae545e8 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,7 @@ 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;
 	const char *dbpath, *syncdbpath;
 	alpm_list_t *newdirlist = NULL, *olddirlist = NULL;
 	alpm_list_t *onlynew = NULL, *onlyold = NULL;
@@ -238,18 +240,68 @@ 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) {
+		char *dbsigfile = NULL;
+
+		/* 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) {
+			char *fulldbfile = NULL, *fulldbsigfile = NULL;
+
+			/* 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);
+	}
+	/* We don't need the db file name anymore */
+	FREE(dbfile);
+
 	syncdbpath = _alpm_db_path(db);

 	/* form the path to the db location */
@@ -610,11 +662,35 @@ 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 */
+				alpm_list_t *pgpsig_lines = NULL;
+				int len = 0;
+
+				/* Create a list of strings to store each line of the signature */
+				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);
+				}
+
+				/* Allocate a buffer to hold the full signature */
+				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);
 				}
-				STRDUP(info->pgpsig.encdata, _alpm_strtrim(line), goto error);
+				pgpsig_lines = NULL;
 			} else if(strcmp(line, "%REPLACES%") == 0) {
 				while(fgets(line, sline, fp) && strlen(_alpm_strtrim(line))) {
 					char *linedup;
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++;
-- 
1.7.1


More information about the pacman-dev mailing list