From: Michael Tremer Date: Thu, 8 Jul 2021 12:57:23 +0000 (+0000) Subject: key: Implement fetching keys from the internet X-Git-Tag: 0.9.28~1074 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b238027bebfe986b7e8d2c5263f46a68fc35b831;p=pakfire.git key: Implement fetching keys from the internet Signed-off-by: Michael Tremer --- diff --git a/src/_pakfire/pakfire.c b/src/_pakfire/pakfire.c index 56facb85e..4c83e1700 100644 --- a/src/_pakfire/pakfire.c +++ b/src/_pakfire/pakfire.c @@ -482,6 +482,29 @@ static PyObject* Pakfire_import_key(PakfireObject* self, PyObject* args) { return _import_keylist(self, keys); } +static PyObject* Pakfire_fetch_key(PakfireObject* self, PyObject* args, PyObject* kwds) { + char* kwlist[] = { "userid", "fingerprint", NULL }; + struct pakfire_key* key = NULL; + const char* userid = NULL; + const char* fingerprint = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$zz", kwlist, &userid, &fingerprint)) + return NULL; + + // Fetch the key + int r = pakfire_key_fetch(&key, self->pakfire, userid, fingerprint); + if (r) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + // Return the result + if (key) + return new_key(&KeyType, key); + + Py_RETURN_NONE; +} + static PyObject* Pakfire_whatprovides(PakfireObject* self, PyObject* args) { const char* provides = NULL; struct pakfire_packagelist* list = NULL; @@ -1007,6 +1030,12 @@ static struct PyMethodDef Pakfire_methods[] = { METH_VARARGS|METH_KEYWORDS, NULL }, + { + "fetch_key", + (PyCFunction)Pakfire_fetch_key, + METH_VARARGS|METH_KEYWORDS, + NULL + }, { "generate_key", (PyCFunction)Pakfire_generate_key, diff --git a/src/libpakfire/include/pakfire/key.h b/src/libpakfire/include/pakfire/key.h index 493299521..ccfa151db 100644 --- a/src/libpakfire/include/pakfire/key.h +++ b/src/libpakfire/include/pakfire/key.h @@ -35,6 +35,9 @@ typedef enum pakfire_key_export_mode { struct pakfire_key* pakfire_key_ref(struct pakfire_key* key); void pakfire_key_unref(struct pakfire_key* key); +int pakfire_key_fetch(struct pakfire_key** key, Pakfire pakfire, + const char* uid, const char* fingerprint); + struct pakfire_key* pakfire_key_get(Pakfire pakfire, const char* fingerprint); int pakfire_key_delete(struct pakfire_key* key); diff --git a/src/libpakfire/key.c b/src/libpakfire/key.c index 67683dbc4..3d4cb639a 100644 --- a/src/libpakfire/key.c +++ b/src/libpakfire/key.c @@ -64,6 +64,131 @@ int pakfire_key_create(struct pakfire_key** key, Pakfire pakfire, gpgme_key_t gp return 0; } +static int pakfire_key_extract_email(const char* uid, char** email) { + if (!uid) + return 1; + + // Find a start + char* start = strrchr(uid, '<'); + if (!start) + return 1; + + // Find the end + char* end = strchr(start, '>'); + if (!end) + return 1; + + // Copy email address to new memory + int r = asprintf(email, "%.*s", (int)(end - start - 1), start + 1); + if (r < 0) + return 1; + + return 0; +} + +static int __pakfire_key_fetch(gpgme_key_t* key, Pakfire pakfire, + const char* what, gpgme_keylist_mode_t flags) { + // Fetch GPGME context + gpgme_ctx_t gpgctx = pakfire_get_gpgctx(pakfire); + if (!gpgctx) + return 1; + + int r = 1; + + // Fetch current keylist mode + gpgme_keylist_mode_t mode = gpgme_get_keylist_mode(gpgctx); + + // Set keylist mode + gpgme_error_t error = gpgme_set_keylist_mode(gpgctx, (mode|flags) & ~GPGME_KEYLIST_MODE_LOCAL); + if (error != GPG_ERR_NO_ERROR) { + ERROR(pakfire, "Could not set GPG keylist mode: %s\n", + gpgme_strerror(error)); + goto ERROR; + } + + // Fetch the key + error = gpgme_get_key(gpgctx, what, key, 0); + switch (gpg_err_code(error)) { + case GPG_ERR_NO_ERROR: + case GPG_ERR_EOF: + break; + + default: + ERROR(pakfire, "Could not fetch key %s: %s\n", what, gpgme_strerror(error)); + r = 1; + goto ERROR; + } + + // Success + r = 0; + +ERROR: + if (r && *key) + gpgme_key_unref(*key); + + // Reset keylist mode + gpgme_set_keylist_mode(gpgctx, mode); + + return r; +} + +static int pakfire_key_fetch_from_wkd(gpgme_key_t* key, Pakfire pakfire, const char* email) { + return __pakfire_key_fetch(key, pakfire, email, GPGME_KEYLIST_MODE_LOCATE); +} + +static int pakfire_key_fetch_from_keyserver(gpgme_key_t* key, Pakfire pakfire, const char* fpr) { + return __pakfire_key_fetch(key, pakfire, fpr, GPGME_KEYLIST_MODE_EXTERN); +} + +PAKFIRE_EXPORT int pakfire_key_fetch(struct pakfire_key** key, Pakfire pakfire, + const char* uid, const char* fingerprint) { + // At least one (uid or fingerprint) must be set + if (!uid && !fingerprint) { + errno = EINVAL; + return 1; + } + + gpgme_key_t gpgkey = NULL; + char* email = NULL; + int r; + + // Extract email address from uid + if (uid) { + r = pakfire_key_extract_email(uid, &email); + if (r) + goto ERROR; + } + + // Try importing the key using Web Key Directory + if (email) { + r = pakfire_key_fetch_from_wkd(&gpgkey, pakfire, email); + if (r) + goto ERROR; + } + + // If nothing was found and we have a fingerprint, let's try a keyserver + if (!gpgkey && fingerprint) { + r = pakfire_key_fetch_from_keyserver(&gpgkey, pakfire, fingerprint); + if (r) + goto ERROR; + } + + // Create a pakfire_key out of the gpg key object + if (gpgkey) { + r = pakfire_key_create(key, pakfire, gpgkey); + if (r) + goto ERROR; + } + +ERROR: + if (gpgkey) + gpgme_key_unref(gpgkey); + if (email) + free(email); + + return r; +} + static void pakfire_key_free(struct pakfire_key* key) { gpgme_key_unref(key->gpgkey); pakfire_unref(key->pakfire); diff --git a/src/libpakfire/libpakfire.sym b/src/libpakfire/libpakfire.sym index dd8d6aa2e..8b920918a 100644 --- a/src/libpakfire/libpakfire.sym +++ b/src/libpakfire/libpakfire.sym @@ -107,6 +107,7 @@ global: pakfire_key_delete; pakfire_key_dump; pakfire_key_export; + pakfire_key_fetch; pakfire_key_generate; pakfire_key_get; pakfire_key_get_email; diff --git a/src/scripts/pakfire.in b/src/scripts/pakfire.in index d6aa8ac8c..112c26250 100644 --- a/src/scripts/pakfire.in +++ b/src/scripts/pakfire.in @@ -88,6 +88,17 @@ class Cli(object): help=_("Include the secret key")) export_key.set_defaults(func=self._export_key) + # fetch-key + fetch_key = subparsers.add_parser("fetch-key", + help=_("Download a key")) + fetch_key.add_argument("--userid", + help=_("The name/email address") + ) + fetch_key.add_argument("--fingerprint", + help=_("The fingerprint of the key") + ) + fetch_key.set_defaults(func=self._fetch_key) + # generate-key generate_key = subparsers.add_parser("generate-key", help=_("Generate a new key")) @@ -261,6 +272,13 @@ class Cli(object): else: print(data) + def _fetch_key(self, p, args): + key = p.fetch_key(userid=args.userid, fingerprint=args.fingerprint) + + # Print the key + if key: + print(key) + def _generate_key(self, p, args): # Generate a new key key = p.generate_key(