示例#1
0
/**
 * 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 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 siglist 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
 * @param siglist a pointer to storage for signature results
 * @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, alpm_siglist_t *siglist)
{
	int ret = -1, sigcount;
	gpgme_error_t gpg_err = 0;
	gpgme_ctx_t ctx;
	gpgme_data_t filedata, sigdata;
	gpgme_verify_result_t verify_result;
	gpgme_signature_t gpgsig;
	char *sigpath = NULL;
	unsigned char *decoded_sigdata = NULL;
	FILE *file = NULL, *sigfile = NULL;

	if(!path || _alpm_access(handle, NULL, path, R_OK) != 0) {
		RET_ERR(handle, ALPM_ERR_NOT_A_FILE, -1);
	}

	if(!siglist) {
		RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1);
	}
	siglist->count = 0;

	if(!base64_sig) {
		sigpath = _alpm_sigpath(handle, path);
		if(_alpm_access(handle, NULL, sigpath, R_OK) != 0
				|| (sigfile = fopen(sigpath, "rb")) == NULL) {
			_alpm_log(handle, ALPM_LOG_DEBUG, "sig path %s could not be opened\n",
					sigpath);
			handle->pm_errno = ALPM_ERR_SIG_MISSING;
			goto error;
		}
	}

	/* does the file we are verifying exist? */
	file = fopen(path, "rb");
	if(file == NULL) {
		handle->pm_errno = ALPM_ERR_NOT_A_FILE;
		goto error;
	}

	if(init_gpgme(handle)) {
		/* pm_errno was set in gpgme_init() */
		goto error;
	}

	_alpm_log(handle, ALPM_LOG_DEBUG, "checking signature for %s\n", path);

	memset(&ctx, 0, sizeof(ctx));
	memset(&sigdata, 0, sizeof(sigdata));
	memset(&filedata, 0, sizeof(filedata));

	gpg_err = gpgme_new(&ctx);
	CHECK_ERR();

	/* create our necessary data objects to verify the signature */
	gpg_err = gpgme_data_new_from_stream(&filedata, file);
	CHECK_ERR();

	/* next create data object for the signature */
	if(base64_sig) {
		/* memory-based, we loaded it from a sync DB */
		size_t data_len;
		int decode_ret = alpm_decode_signature(base64_sig,
				&decoded_sigdata, &data_len);
		if(decode_ret) {
			handle->pm_errno = ALPM_ERR_SIG_INVALID;
			goto gpg_error;
		}
		gpg_err = gpgme_data_new_from_mem(&sigdata,
				(char *)decoded_sigdata, data_len, 0);
	} else {
		/* file-based, it is on disk */
		gpg_err = gpgme_data_new_from_stream(&sigdata, sigfile);
	}
	CHECK_ERR();

	/* here's where the magic happens */
	gpg_err = gpgme_op_verify(ctx, sigdata, filedata, NULL);
	CHECK_ERR();
	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");
		handle->pm_errno = ALPM_ERR_SIG_MISSING;
		goto gpg_error;
	}
	for(gpgsig = verify_result->signatures, sigcount = 0;
			gpgsig; gpgsig = gpgsig->next, sigcount++);
	_alpm_log(handle, ALPM_LOG_DEBUG, "%d signatures returned\n", sigcount);

	CALLOC(siglist->results, sigcount, sizeof(alpm_sigresult_t),
			handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error);
	siglist->count = sigcount;

	for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig;
			gpgsig = gpgsig->next, sigcount++) {
		alpm_list_t *summary_list, *summary;
		alpm_sigstatus_t status;
		alpm_sigvalidity_t validity;
		gpgme_key_t key;
		alpm_sigresult_t *result;

		_alpm_log(handle, ALPM_LOG_DEBUG, "fingerprint: %s\n", gpgsig->fpr);
		summary_list = list_sigsum(gpgsig->summary);
		for(summary = summary_list; summary; summary = summary->next) {
			_alpm_log(handle, ALPM_LOG_DEBUG, "summary: %s\n", (const char *)summary->data);
		}
		alpm_list_free(summary_list);
		_alpm_log(handle, ALPM_LOG_DEBUG, "status: %s\n", gpgme_strerror(gpgsig->status));
		_alpm_log(handle, ALPM_LOG_DEBUG, "timestamp: %lu\n", gpgsig->timestamp);

		if((time_t)gpgsig->timestamp > time(NULL)) {
			_alpm_log(handle, ALPM_LOG_DEBUG,
					"signature timestamp is greater than system time.\n");
		}

		_alpm_log(handle, ALPM_LOG_DEBUG, "exp_timestamp: %lu\n", gpgsig->exp_timestamp);
		_alpm_log(handle, ALPM_LOG_DEBUG, "validity: %s; reason: %s\n",
				string_validity(gpgsig->validity),
				gpgme_strerror(gpgsig->validity_reason));

		result = siglist->results + sigcount;
		gpg_err = gpgme_get_key(ctx, gpgsig->fpr, &key, 0);
		if(gpg_err_code(gpg_err) == GPG_ERR_EOF) {
			_alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
			gpg_err = GPG_ERR_NO_ERROR;
			/* we dupe the fpr in this case since we have no key to point at */
			STRDUP(result->key.fingerprint, gpgsig->fpr,
					handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error);
		} else {
示例#2
0
/** Update a package database
 *
 * An update of the package database \a db will be attempted. Unless
 * \a force is true, the update will only be performed if the remote
 * database was modified since the last update.
 *
 * This operation requires a database lock, and will return an applicable error
 * if the lock could not be obtained.
 *
 * Example:
 * @code
 * alpm_list_t *syncs = alpm_get_syncdbs();
 * for(i = syncs; i; i = alpm_list_next(i)) {
 *     alpm_db_t *db = alpm_list_getdata(i);
 *     result = alpm_db_update(0, db);
 *
 *     if(result < 0) {
 *	       printf("Unable to update database: %s\n", alpm_strerrorlast());
 *     } else if(result == 1) {
 *         printf("Database already up to date\n");
 *     } else {
 *         printf("Database updated\n");
 *     }
 * }
 * @endcode
 *
 * @ingroup alpm_databases
 * @note After a successful update, the \link alpm_db_get_pkgcache()
 * package cache \endlink will be invalidated
 * @param force if true, then forces the update, otherwise update only in case
 * the database isn't up to date
 * @param db pointer to the package database to update
 * @return 0 on success, -1 on error (pm_errno is set accordingly), 1 if up to
 * to date
 */
int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
{
	char *syncpath;
	alpm_list_t *i;
	int ret = -1;
	mode_t oldmask;
	alpm_handle_t *handle;
	alpm_siglevel_t level;

	/* Sanity checks */
	ASSERT(db != NULL, return -1);
	handle = db->handle;
	handle->pm_errno = 0;
	ASSERT(db != handle->db_local, RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1));
	ASSERT(db->servers != NULL, RET_ERR(handle, ALPM_ERR_SERVER_NONE, -1));

	syncpath = get_sync_dir(handle);
	if(!syncpath) {
		return -1;
	}

	/* make sure we have a sane umask */
	oldmask = umask(0022);

	level = alpm_db_get_siglevel(db);

	/* attempt to grab a lock */
	if(_alpm_handle_lock(handle)) {
		free(syncpath);
		umask(oldmask);
		RET_ERR(handle, ALPM_ERR_HANDLE_LOCK, -1);
	}

	for(i = db->servers; i; i = i->next) {
		const char *server = i->data;
		struct dload_payload payload;
		size_t len;
		int sig_ret = 0;

		memset(&payload, 0, sizeof(struct dload_payload));

		/* set hard upper limit of 25MiB */
		payload.max_size = 25 * 1024 * 1024;

		/* print server + filename into a buffer */
		len = strlen(server) + strlen(db->treename) + 5;
		/* TODO fix leak syncpath and umask unset */
		MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
		snprintf(payload.fileurl, len, "%s/%s.db", server, db->treename);
		payload.handle = handle;
		payload.force = force;
		payload.unlink_on_fail = 1;

		ret = _alpm_download(&payload, syncpath, NULL);
		_alpm_dload_payload_reset(&payload);

		if(ret == 0 && (level & ALPM_SIG_DATABASE)) {
			/* an existing sig file is no good at this point */
			char *sigpath = _alpm_sigpath(handle, _alpm_db_path(db));
			if(!sigpath) {
				ret = -1;
				break;
			}
			unlink(sigpath);
			free(sigpath);

			/* if we downloaded a DB, we want the .sig from the same server */
			/* print server + filename into a buffer (leave space for .sig) */
			len = strlen(server) + strlen(db->treename) + 9;
			/* TODO fix leak syncpath and umask unset */
			MALLOC(payload.fileurl, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
			snprintf(payload.fileurl, len, "%s/%s.db.sig", server, db->treename);
			payload.handle = handle;
			payload.force = 1;
			payload.errors_ok = (level & ALPM_SIG_DATABASE_OPTIONAL);

			/* set hard upper limit of 16KiB */
			payload.max_size = 16 * 1024;

			sig_ret = _alpm_download(&payload, syncpath, NULL);
			/* errors_ok suppresses error messages, but not the return code */
			sig_ret = payload.errors_ok ? 0 : sig_ret;
			_alpm_dload_payload_reset(&payload);
		}

		if(ret != -1 && sig_ret != -1) {
			break;
		}
	}

	if(ret == 1) {
		/* files match, do nothing */
		handle->pm_errno = 0;
		goto cleanup;
	} else if(ret == -1) {
		/* pm_errno was set by the download code */
		_alpm_log(handle, ALPM_LOG_DEBUG, "failed to sync db: %s\n",
				alpm_strerror(handle->pm_errno));
		goto cleanup;
	}

	/* Cache needs to be rebuilt */
	_alpm_db_free_pkgcache(db);

	/* clear all status flags regarding validity/existence */
	db->status &= ~DB_STATUS_VALID;
	db->status &= ~DB_STATUS_INVALID;
	db->status &= ~DB_STATUS_EXISTS;
	db->status &= ~DB_STATUS_MISSING;

	if(sync_db_validate(db)) {
		/* pm_errno should be set */
		ret = -1;
	}

cleanup:

	if(_alpm_handle_unlock(handle)) {
		_alpm_log(handle, ALPM_LOG_WARNING, _("could not remove lock file %s\n"),
				handle->lockfile);
	}
	free(syncpath);
	umask(oldmask);
	return ret;
}