/** * Initialize the GPGME library. * This can be safely called multiple times; however it is not thread-safe. * @param handle the context handle * @return 0 on success, -1 on error */ static int init_gpgme(alpm_handle_t *handle) { static int init = 0; const char *version, *sigdir; gpgme_error_t gpg_err; gpgme_engine_info_t enginfo; if(init) { /* we already successfully initialized the library */ return 0; } sigdir = handle->gpgdir; if(_alpm_access(handle, sigdir, "pubring.gpg", R_OK) || _alpm_access(handle, sigdir, "trustdb.gpg", R_OK)) { handle->pm_errno = ALPM_ERR_NOT_A_FILE; _alpm_log(handle, ALPM_LOG_DEBUG, "Signature verification will fail!\n"); _alpm_log(handle, ALPM_LOG_WARNING, _("Public keyring not found; have you run '%s'?\n"), "pacman-key --init"); } /* calling gpgme_check_version() returns the current version and runs * some internal library setup code */ version = gpgme_check_version(NULL); _alpm_log(handle, ALPM_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) */ gpg_err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); CHECK_ERR(); /* set and check engine information */ gpg_err = gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL, sigdir); CHECK_ERR(); gpg_err = gpgme_get_engine_info(&enginfo); CHECK_ERR(); _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME engine info: file=%s, home=%s\n", enginfo->file_name, enginfo->home_dir); init = 1; return 0; gpg_error: _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(gpg_err)); RET_ERR(handle, ALPM_ERR_GPGME, -1); }
/** * Import a key defined by a fingerprint into the local keyring. * @param handle the context handle * @param fpr the fingerprint key ID to import * @return 0 on success, -1 on error */ int _alpm_key_import(alpm_handle_t *handle, const char *fpr) { int answer = 0, ret = -1; alpm_pgpkey_t fetch_key; memset(&fetch_key, 0, sizeof(fetch_key)); if(key_search(handle, fpr, &fetch_key) == 1) { _alpm_log(handle, ALPM_LOG_DEBUG, "unknown key, found %s on keyserver\n", fetch_key.uid); if(!_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) { QUESTION(handle, ALPM_QUESTION_IMPORT_KEY, &fetch_key, NULL, NULL, &answer); if(answer) { if(key_import(handle, &fetch_key) == 0) { ret = 0; } else { _alpm_log(handle, ALPM_LOG_ERROR, _("key \"%s\" could not be imported\n"), fetch_key.uid); } } } else { /* keyring directory was not writable, so we don't even try */ _alpm_log(handle, ALPM_LOG_WARNING, _("key %s, \"%s\" found on keyserver, keyring is not writable\n"), fetch_key.fingerprint, fetch_key.uid); } } else { _alpm_log(handle, ALPM_LOG_ERROR, _("key \"%s\" could not be looked up remotely\n"), fpr); } gpgme_key_unref(fetch_key.data); return ret; }
/** * @brief Check if alpm can delete a file. * * @param handle the context handle * @param file file to be removed * @param skip_remove list of files that will not be removed * * @return 1 if the file can be deleted, 0 if it cannot be deleted */ static int can_remove_file(alpm_handle_t *handle, const alpm_file_t *file, alpm_list_t *skip_remove) { char filepath[PATH_MAX]; if(alpm_list_find(skip_remove, file->name, _alpm_fnmatch)) { /* return success because we will never actually remove this file */ return 1; } snprintf(filepath, PATH_MAX, "%s%s", handle->root, file->name); if(file->name[strlen(file->name) - 1] == '/' && dir_is_mountpoint(handle, filepath, NULL)) { /* we do not remove mountpoints */ return 1; } /* If we fail write permissions due to a read-only filesystem, abort. * Assume all other possible failures are covered somewhere else */ if(_alpm_access(handle, NULL, filepath, W_OK) == -1) { if(errno != EACCES && errno != ETXTBSY && access(filepath, F_OK) == 0) { /* only return failure if the file ACTUALLY exists and we can't write to * it - ignore "chmod -w" simple permission failures */ _alpm_log(handle, ALPM_LOG_ERROR, _("cannot remove file '%s': %s\n"), filepath, strerror(errno)); return 0; } } return 1; }
/** Check the alpm cachedirs for existance and find a writable one. * If no valid cache directory can be found, use /tmp. * @param handle the context handle * @return pointer to a writable cache directory. */ const char *_alpm_filecache_setup(alpm_handle_t *handle) { struct stat buf; alpm_list_t *i; char *cachedir; const char *tmpdir; /* Loop through the cache dirs until we find a usable directory */ for(i = handle->cachedirs; i; i = i->next) { cachedir = i->data; if(stat(cachedir, &buf) != 0) { /* cache directory does not exist.... try creating it */ _alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"), cachedir); if(_alpm_makepath(cachedir) == 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir); return cachedir; } } else if(!S_ISDIR(buf.st_mode)) { _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir, not a directory: %s\n", cachedir); } else if(_alpm_access(handle, NULL, cachedir, W_OK) != 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir, not writable: %s\n", cachedir); } else if(!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) { _alpm_log(handle, ALPM_LOG_DEBUG, "skipping cachedir, no write bits set: %s\n", cachedir); } else { _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir); return cachedir; } } /* we didn't find a valid cache directory. use TMPDIR or /tmp. */ if((tmpdir = getenv("TMPDIR")) && stat(tmpdir, &buf) && S_ISDIR(buf.st_mode)) { /* TMPDIR was good, we can use it */ } else { tmpdir = "/tmp"; } alpm_option_add_cachedir(handle, tmpdir); cachedir = handle->cachedirs->prev->data; _alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir); _alpm_log(handle, ALPM_LOG_WARNING, _("couldn't find or create package cache, using %s instead\n"), cachedir); return cachedir; }
/** * Import a key into the local keyring. * @param handle the context handle * @param key the key to import, likely retrieved from #key_search * @return 0 on success, -1 on error */ static int key_import(alpm_handle_t *handle, alpm_pgpkey_t *key) { gpgme_error_t gpg_err; gpgme_ctx_t ctx; gpgme_key_t keys[2]; gpgme_import_result_t result; int ret = -1; if(_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) { /* no chance of import succeeding if pubring isn't writable */ _alpm_log(handle, ALPM_LOG_ERROR, _("keyring is not writable\n")); return -1; } memset(&ctx, 0, sizeof(ctx)); gpg_err = gpgme_new(&ctx); CHECK_ERR(); _alpm_log(handle, ALPM_LOG_DEBUG, "importing key\n"); keys[0] = key->data; keys[1] = NULL; gpg_err = gpgme_op_import_keys(ctx, keys); CHECK_ERR(); result = gpgme_op_import_result(ctx); CHECK_ERR(); /* we know we tried to import exactly one key, so check for this */ if(result->considered != 1 || !result->imports) { _alpm_log(handle, ALPM_LOG_DEBUG, "could not import key, 0 results\n"); ret = -1; } else if(result->imports->result != GPG_ERR_NO_ERROR) { _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(gpg_err)); ret = -1; } else { ret = 0; } gpg_error: gpgme_release(ctx); return ret; }
/** * 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 {
int _alpm_runscriptlet(alpm_handle_t *handle, const char *filepath, const char *script, const char *ver, const char *oldver, int is_archive) { char arg0[64], arg1[3], cmdline[PATH_MAX]; char *argv[] = { arg0, arg1, cmdline, NULL }; char *tmpdir, *scriptfn = NULL, *scriptpath; int retval = 0; size_t len; if(_alpm_access(handle, NULL, filepath, R_OK) != 0) { _alpm_log(handle, ALPM_LOG_DEBUG, "scriptlet '%s' not found\n", filepath); return 0; } if(!is_archive && !grep(filepath, script)) { /* script not found in scriptlet file; we can only short-circuit this early * if it is an actual scriptlet file and not an archive. */ return 0; } strcpy(arg0, SCRIPTLET_SHELL); strcpy(arg1, "-c"); /* create a directory in $root/tmp/ for copying/extracting the scriptlet */ len = strlen(handle->root) + strlen("tmp/alpm_XXXXXX") + 1; MALLOC(tmpdir, len, RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(tmpdir, len, "%stmp/", handle->root); if(access(tmpdir, F_OK) != 0) { _alpm_makepath_mode(tmpdir, 01777); } snprintf(tmpdir, len, "%stmp/alpm_XXXXXX", handle->root); if(mkdtemp(tmpdir) == NULL) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not create temp directory\n")); free(tmpdir); return 1; } /* either extract or copy the scriptlet */ len += strlen("/.INSTALL"); MALLOC(scriptfn, len, free(tmpdir); RET_ERR(handle, ALPM_ERR_MEMORY, -1)); snprintf(scriptfn, len, "%s/.INSTALL", tmpdir); if(is_archive) { if(_alpm_unpack_single(handle, filepath, tmpdir, ".INSTALL")) { retval = 1; } } else { if(_alpm_copyfile(filepath, scriptfn)) { _alpm_log(handle, ALPM_LOG_ERROR, _("could not copy tempfile to %s (%s)\n"), scriptfn, strerror(errno)); retval = 1; } } if(retval == 1) { goto cleanup; } if(is_archive && !grep(scriptfn, script)) { /* script not found in extracted scriptlet file */ goto cleanup; } /* chop off the root so we can find the tmpdir in the chroot */ scriptpath = scriptfn + strlen(handle->root) - 1; if(oldver) { snprintf(cmdline, PATH_MAX, ". %s; %s %s %s", scriptpath, script, ver, oldver); } else { snprintf(cmdline, PATH_MAX, ". %s; %s %s", scriptpath, script, ver); } _alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\"\n", cmdline); retval = _alpm_run_chroot(handle, SCRIPTLET_SHELL, argv, NULL, NULL); cleanup: if(scriptfn && unlink(scriptfn)) { _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove %s\n"), scriptfn); } if(rmdir(tmpdir)) { _alpm_log(handle, ALPM_LOG_WARNING, _("could not remove tmpdir %s\n"), tmpdir); } free(scriptfn); free(tmpdir); return retval; }
static int sync_db_validate(alpm_db_t *db) { alpm_siglevel_t level; const char *dbpath; if(db->status & DB_STATUS_VALID || db->status & DB_STATUS_MISSING) { return 0; } if(db->status & DB_STATUS_INVALID) { db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG; return -1; } dbpath = _alpm_db_path(db); if(!dbpath) { /* pm_errno set in _alpm_db_path() */ return -1; } /* we can skip any validation if the database doesn't exist */ if(_alpm_access(db->handle, NULL, dbpath, R_OK) != 0 && errno == ENOENT) { db->status &= ~DB_STATUS_EXISTS; db->status |= DB_STATUS_MISSING; EVENT(db->handle, ALPM_EVENT_DATABASE_MISSING, db->treename, NULL); goto valid; } db->status |= DB_STATUS_EXISTS; db->status &= ~DB_STATUS_MISSING; /* this takes into account the default verification level if UNKNOWN * was assigned to this db */ level = alpm_db_get_siglevel(db); if(level & ALPM_SIG_DATABASE) { int retry, ret; do { retry = 0; alpm_siglist_t *siglist; ret = _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, &siglist); if(ret) { retry = _alpm_process_siglist(db->handle, db->treename, siglist, level & ALPM_SIG_DATABASE_OPTIONAL, level & ALPM_SIG_DATABASE_MARGINAL_OK, level & ALPM_SIG_DATABASE_UNKNOWN_OK); } alpm_siglist_cleanup(siglist); free(siglist); } while(retry); if(ret) { db->status &= ~DB_STATUS_VALID; db->status |= DB_STATUS_INVALID; db->handle->pm_errno = ALPM_ERR_DB_INVALID_SIG; return 1; } } valid: db->status |= DB_STATUS_VALID; db->status &= ~DB_STATUS_INVALID; return 0; }