mirror of
https://github.com/moparisthebest/pacman
synced 2024-12-21 15:28:50 -05:00
Add per-repo PinnedPubKey option
This sets curl's CURLOPT_PINNEDPUBLICKEY option in the built-in downloader, or replaces %p in XferCommand. This pins public keys to ensure your TLS connection is not man-in-the-middled without relying on CAs etc. Probably most useful currently for very small or single groups of servers. It would obviously be best as a per-mirror option, but such a thing currently does not exist. Signed-off-by: Travis Burtrum <travis.archlinux@burtrum.org>
This commit is contained in:
parent
fa06951d90
commit
abb057844e
@ -126,7 +126,8 @@ Options
|
||||
All instances of `%u` will be replaced with the download URL. If present,
|
||||
instances of `%o` will be replaced with the local filename, plus a
|
||||
``.part'' extension, which allows programs like wget to do file resumes
|
||||
properly.
|
||||
properly. If present, instances of `%p` will be replaced with the value of
|
||||
the repo specific PinnedPubKey directive, or `` if that is not set.
|
||||
+
|
||||
This option is useful for users who experience problems with built-in
|
||||
HTTP/FTP support, or need the more advanced proxy support that comes with
|
||||
@ -276,6 +277,15 @@ even be used for different architectures.
|
||||
Note that an enabled repository can be operated on explicitly, regardless of the Usage
|
||||
level set.
|
||||
|
||||
*PinnedPubKey =* pinnedpubkey::
|
||||
The string can be the file name of your pinned public key. The file format expected
|
||||
is "PEM" or "DER". The string can also be any number of base64 encoded sha256
|
||||
hashes preceded by "sha256//" and separated by ";"
|
||||
+
|
||||
When negotiating a TLS or SSL connection, the server sends a certificate indicating
|
||||
its identity. A public key is extracted from this certificate and if it does not
|
||||
exactly match the public key provided to this option, pacman will abort the
|
||||
connection before sending or receiving any data.
|
||||
|
||||
Package and Database Signature Checking[[SC]]
|
||||
---------------------------------------------
|
||||
|
@ -750,11 +750,12 @@ typedef void (*alpm_cb_totaldl)(off_t total);
|
||||
* @param url the URL of the file to be downloaded
|
||||
* @param localpath the directory to which the file should be downloaded
|
||||
* @param force whether to force an update, even if the file is the same
|
||||
* @param pinnedpubkey a pinned public key string
|
||||
* @return 0 on success, 1 if the file exists and is identical, -1 on
|
||||
* error.
|
||||
*/
|
||||
typedef int (*alpm_cb_fetch)(const char *url, const char *localpath,
|
||||
int force);
|
||||
int force, const char *pinnedpubkey);
|
||||
|
||||
/** Fetch a remote pkg.
|
||||
* @param handle the context handle
|
||||
@ -1037,6 +1038,13 @@ alpm_list_t *alpm_db_get_groupcache(alpm_db_t *db);
|
||||
*/
|
||||
alpm_list_t *alpm_db_search(alpm_db_t *db, const alpm_list_t *needles);
|
||||
|
||||
/** Sets the pinned public key of a database.
|
||||
* @param db pointer to the package database to set the status for
|
||||
* @param pinnedpubkey a pinned public key string
|
||||
* @return 0 on success, or -1 on error
|
||||
*/
|
||||
int alpm_db_set_pinnedpubkey(alpm_db_t *db, char *pinnedpubkey);
|
||||
|
||||
typedef enum _alpm_db_usage_ {
|
||||
ALPM_DB_USAGE_SYNC = 1,
|
||||
ALPM_DB_USAGE_SEARCH = (1 << 1),
|
||||
|
@ -242,6 +242,8 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
|
||||
payload.handle = handle;
|
||||
payload.force = force;
|
||||
payload.unlink_on_fail = 1;
|
||||
|
||||
payload.pinnedpubkey = db->pinnedpubkey;
|
||||
|
||||
ret = _alpm_download(&payload, syncpath, NULL, &final_db_url);
|
||||
_alpm_dload_payload_reset(&payload);
|
||||
@ -297,6 +299,8 @@ int SYMEXPORT alpm_db_update(int force, alpm_db_t *db)
|
||||
/* set hard upper limit of 16KiB */
|
||||
payload.max_size = 16 * 1024;
|
||||
|
||||
payload.pinnedpubkey = db->pinnedpubkey;
|
||||
|
||||
sig_ret = _alpm_download(&payload, syncpath, NULL, NULL);
|
||||
/* errors_ok suppresses error messages, but not the return code */
|
||||
sig_ret = payload.errors_ok ? 0 : sig_ret;
|
||||
|
@ -306,6 +306,15 @@ alpm_list_t SYMEXPORT *alpm_db_search(alpm_db_t *db, const alpm_list_t *needles)
|
||||
return _alpm_db_search(db, needles);
|
||||
}
|
||||
|
||||
/** Sets the pinned public key for a repo */
|
||||
int SYMEXPORT alpm_db_set_pinnedpubkey(alpm_db_t *db, char *pinnedpubkey)
|
||||
{
|
||||
ASSERT(db != NULL, return -1);
|
||||
ASSERT(pinnedpubkey != NULL, return 0);
|
||||
db->pinnedpubkey = strdup(pinnedpubkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Sets the usage bitmask for a repo */
|
||||
int SYMEXPORT alpm_db_set_usage(alpm_db_t *db, int usage)
|
||||
{
|
||||
@ -351,6 +360,9 @@ void _alpm_db_free(alpm_db_t *db)
|
||||
FREELIST(db->servers);
|
||||
FREE(db->_path);
|
||||
FREE(db->treename);
|
||||
if(db->pinnedpubkey != NULL) {
|
||||
FREE(db->pinnedpubkey);
|
||||
}
|
||||
FREE(db);
|
||||
|
||||
return;
|
||||
|
@ -67,6 +67,7 @@ struct __alpm_db_t {
|
||||
char *treename;
|
||||
/* do not access directly, use _alpm_db_path(db) for lazy access */
|
||||
char *_path;
|
||||
char *pinnedpubkey;
|
||||
alpm_pkghash_t *pkgcache;
|
||||
alpm_list_t *grpcache;
|
||||
alpm_list_t *servers;
|
||||
|
@ -327,6 +327,12 @@ static void curl_set_handle_opts(struct dload_payload *payload,
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent);
|
||||
}
|
||||
|
||||
if(payload->pinnedpubkey != NULL) {
|
||||
_alpm_log(handle, ALPM_LOG_DEBUG,
|
||||
"using curl pinnedpubkey: %s\n", payload->pinnedpubkey);
|
||||
curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, payload->pinnedpubkey);
|
||||
}
|
||||
|
||||
if(!payload->allow_resume && !payload->force && payload->destfile_name &&
|
||||
stat(payload->destfile_name, &st) == 0) {
|
||||
/* start from scratch, but only download if our local is out of date. */
|
||||
@ -646,7 +652,7 @@ int _alpm_download(struct dload_payload *payload, const char *localpath,
|
||||
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
#endif
|
||||
} else {
|
||||
int ret = handle->fetchcb(payload->fileurl, localpath, payload->force);
|
||||
int ret = handle->fetchcb(payload->fileurl, localpath, payload->force, payload->pinnedpubkey);
|
||||
if(ret == -1 && !payload->errors_ok) {
|
||||
RET_ERR(handle, ALPM_ERR_EXTERNAL_DOWNLOAD, -1);
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ struct dload_payload {
|
||||
int errors_ok;
|
||||
int unlink_on_fail;
|
||||
int trust_remote_name;
|
||||
char *pinnedpubkey;
|
||||
#ifdef HAVE_LIBCURL
|
||||
CURLcode curlerr; /* last error produced by curl */
|
||||
#endif
|
||||
|
@ -862,7 +862,7 @@ static int validate_deltas(alpm_handle_t *handle, alpm_list_t *deltas)
|
||||
}
|
||||
|
||||
static struct dload_payload *build_payload(alpm_handle_t *handle,
|
||||
const char *filename, size_t size, alpm_list_t *servers)
|
||||
const char *filename, size_t size, alpm_list_t *servers, char *pinnedpubkey)
|
||||
{
|
||||
struct dload_payload *payload;
|
||||
|
||||
@ -870,6 +870,7 @@ static struct dload_payload *build_payload(alpm_handle_t *handle,
|
||||
STRDUP(payload->remote_name, filename, FREE(payload); RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
|
||||
payload->max_size = size;
|
||||
payload->servers = servers;
|
||||
payload->pinnedpubkey = pinnedpubkey;
|
||||
return payload;
|
||||
}
|
||||
|
||||
@ -898,7 +899,7 @@ static int find_dl_candidates(alpm_db_t *repo, alpm_list_t **files, alpm_list_t
|
||||
alpm_delta_t *delta = dlts->data;
|
||||
if(delta->download_size != 0) {
|
||||
struct dload_payload *payload = build_payload(
|
||||
handle, delta->delta, delta->delta_size, repo->servers);
|
||||
handle, delta->delta, delta->delta_size, repo->servers, repo->pinnedpubkey);
|
||||
ASSERT(payload, return -1);
|
||||
*files = alpm_list_add(*files, payload);
|
||||
}
|
||||
@ -909,7 +910,7 @@ static int find_dl_candidates(alpm_db_t *repo, alpm_list_t **files, alpm_list_t
|
||||
} else if(spkg->download_size != 0) {
|
||||
struct dload_payload *payload;
|
||||
ASSERT(spkg->filename != NULL, RET_ERR(handle, ALPM_ERR_PKG_INVALID_NAME, -1));
|
||||
payload = build_payload(handle, spkg->filename, spkg->size, repo->servers);
|
||||
payload = build_payload(handle, spkg->filename, spkg->size, repo->servers, repo->pinnedpubkey);
|
||||
ASSERT(payload, return -1);
|
||||
*files = alpm_list_add(*files, payload);
|
||||
}
|
||||
|
@ -163,6 +163,9 @@ void config_repo_free(config_repo_t *repo)
|
||||
if(repo == NULL) {
|
||||
return;
|
||||
}
|
||||
if(repo->pinnedpubkey != NULL) {
|
||||
free(repo->pinnedpubkey);
|
||||
}
|
||||
free(repo->name);
|
||||
FREELIST(repo->servers);
|
||||
free(repo);
|
||||
@ -204,7 +207,7 @@ static char *get_tempfile(const char *path, const char *filename)
|
||||
|
||||
/** External fetch callback */
|
||||
static int download_with_xfercommand(const char *url, const char *localpath,
|
||||
int force)
|
||||
int force, const char *pinnedpubkey)
|
||||
{
|
||||
int ret = 0, retval;
|
||||
int usepart = 0;
|
||||
@ -232,6 +235,16 @@ static int download_with_xfercommand(const char *url, const char *localpath,
|
||||
}
|
||||
|
||||
tempcmd = strdup(config->xfercommand);
|
||||
/* replace all occurrences of %p with pinnedpubkey */
|
||||
if(strstr(tempcmd, "%p")) {
|
||||
if(pinnedpubkey == NULL) {
|
||||
parsedcmd = strreplace(tempcmd, "%p", "");
|
||||
} else {
|
||||
parsedcmd = strreplace(tempcmd, "%p", pinnedpubkey);
|
||||
}
|
||||
free(tempcmd);
|
||||
tempcmd = parsedcmd;
|
||||
}
|
||||
/* replace all occurrences of %o with fn.part */
|
||||
if(strstr(tempcmd, "%o")) {
|
||||
usepart = 1;
|
||||
@ -668,6 +681,8 @@ static int register_repo(config_repo_t *repo)
|
||||
repo->name);
|
||||
alpm_db_set_usage(db, repo->usage == 0 ? ALPM_DB_USAGE_ALL : repo->usage);
|
||||
|
||||
alpm_db_set_pinnedpubkey(db, repo->pinnedpubkey);
|
||||
|
||||
for(i = repo->servers; i; i = alpm_list_next(i)) {
|
||||
char *value = i->data;
|
||||
if(_add_mirror(db, value) != 0) {
|
||||
@ -915,12 +930,19 @@ static int _parse_repo(const char *key, char *value, const char *file,
|
||||
}
|
||||
FREELIST(values);
|
||||
}
|
||||
} else if(strcmp(key, "PinnedPubKey") == 0) {
|
||||
if(!value) {
|
||||
pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
|
||||
file, line, key);
|
||||
} else {
|
||||
repo->pinnedpubkey = strdup(value);
|
||||
pm_printf(ALPM_LOG_DEBUG, "repo config: pinnedpubkey: %s\n", value);
|
||||
}
|
||||
} else {
|
||||
pm_printf(ALPM_LOG_WARNING,
|
||||
_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
|
||||
file, line, key, repo->name);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ typedef struct __config_repo_t {
|
||||
int usage;
|
||||
int siglevel;
|
||||
int siglevel_mask;
|
||||
char *pinnedpubkey;
|
||||
} config_repo_t;
|
||||
|
||||
typedef struct __config_t {
|
||||
|
Loading…
Reference in New Issue
Block a user