1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2017 Pakfire development team #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 #############################################################################*/
27 #include <pakfire/constants.h>
28 #include <pakfire/errno.h>
29 #include <pakfire/i18n.h>
30 #include <pakfire/key.h>
31 #include <pakfire/logging.h>
32 #include <pakfire/pakfire.h>
33 #include <pakfire/util.h>
35 #define DEFAULT_KEY_SIZE "rsa4096"
37 gpgme_ctx_t
pakfire_get_gpgctx(Pakfire pakfire
) {
38 static int gpg_initialized
= 0;
40 const char* error_string
;
42 if (!gpg_initialized
) {
44 const char* version
= gpgme_check_version(NULL
);
45 DEBUG("Loaded gpgme %s\n", version
);
47 // Check if we support GPG
48 error
= gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP
);
49 if (gpg_err_code(error
) != GPG_ERR_NO_ERROR
)
52 // GPG has been initialized
56 // Create a new context
58 error
= gpgme_new(&ctx
);
59 if (gpg_err_code(error
) != GPG_ERR_NO_ERROR
)
62 // Set output to be ASCII armoured
63 gpgme_set_armor(ctx
, 1);
66 const char* path
= pakfire_get_path(pakfire
);
67 char* home
= pakfire_path_join(path
, "etc/pakfire/gnupg");
69 // Check if gpg directories exist
70 if (pakfire_access(home
, NULL
, R_OK
) != 0) {
71 DEBUG("Creating GPG database at %s\n", home
);
73 int r
= pakfire_mkdir(home
, S_IRUSR
|S_IWUSR
|S_IXUSR
);
75 ERROR("Could not initialize the GPG database at %s\n", home
);
81 error
= gpgme_ctx_set_engine_info(ctx
, GPGME_PROTOCOL_OpenPGP
, NULL
, home
);
83 if (gpg_err_code(error
) != GPG_ERR_NO_ERROR
)
86 gpgme_engine_info_t engine_info
= gpgme_ctx_get_engine_info(ctx
);
87 DEBUG("GPGME engine info: %s, home = %s\n",
88 engine_info
->file_name
, engine_info
->home_dir
);
95 error_string
= gpgme_strerror(error
);
96 ERROR("%s\n", error_string
);
101 static size_t pakfire_count_keys(Pakfire pakfire
) {
102 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(pakfire
);
106 gpgme_key_t key
= NULL
;
107 gpgme_error_t error
= gpgme_op_keylist_start(gpgctx
, NULL
, 0);
109 error
= gpgme_op_keylist_next(gpgctx
, &key
);
115 gpgme_key_release(key
);
118 DEBUG("%zu key(s) in keystore\n", count
);
119 gpgme_release(gpgctx
);
124 PakfireKey
* pakfire_key_list(Pakfire pakfire
) {
125 size_t count
= pakfire_count_keys(pakfire
);
129 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(pakfire
);
131 PakfireKey
* first
= pakfire_calloc(count
+ 1, sizeof(PakfireKey
));
132 PakfireKey
* list
= first
;
134 gpgme_key_t gpgkey
= NULL
;
135 gpgme_error_t error
= gpgme_op_keylist_start(gpgctx
, NULL
, 0);
137 error
= gpgme_op_keylist_next(gpgctx
, &gpgkey
);
141 // Add key to the list
142 *list
++ = pakfire_key_create(pakfire
, gpgkey
);
144 gpgme_key_release(gpgkey
);
147 // Last list item must be NULL
150 gpgme_release(gpgctx
);
155 PakfireKey
pakfire_key_create(Pakfire pakfire
, gpgme_key_t gpgkey
) {
156 PakfireKey key
= pakfire_calloc(1, sizeof(*key
));
160 key
->pakfire
= pakfire_ref(pakfire
);
162 key
->gpgkey
= gpgkey
;
163 gpgme_key_ref(key
->gpgkey
);
169 static void pakfire_key_free(PakfireKey key
) {
170 pakfire_unref(key
->pakfire
);
171 gpgme_key_unref(key
->gpgkey
);
176 PakfireKey
pakfire_key_ref(PakfireKey key
) {
182 void pakfire_key_unref(PakfireKey key
) {
183 if (--key
->nrefs
> 0)
186 pakfire_key_free(key
);
189 PakfireKey
pakfire_key_get(Pakfire pakfire
, const char* fingerprint
) {
190 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(pakfire
);
192 gpgme_key_t gpgkey
= NULL
;
193 gpgme_error_t error
= gpgme_get_key(gpgctx
, fingerprint
, &gpgkey
, 1);
194 if (error
!= GPG_ERR_NO_ERROR
)
197 PakfireKey key
= pakfire_key_create(pakfire
, gpgkey
);
198 gpgme_key_unref(gpgkey
);
199 gpgme_release(gpgctx
);
204 int pakfire_key_delete(PakfireKey key
) {
205 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(key
->pakfire
);
208 gpgme_error_t error
= gpgme_op_delete(gpgctx
, key
->gpgkey
, 1);
209 if (error
!= GPG_ERR_NO_ERROR
)
212 gpgme_release(gpgctx
);
217 const char* pakfire_key_get_fingerprint(PakfireKey key
) {
218 return key
->gpgkey
->fpr
;
221 const char* pakfire_key_get_uid(PakfireKey key
) {
222 return key
->gpgkey
->uids
->uid
;
225 const char* pakfire_key_get_name(PakfireKey key
) {
226 return key
->gpgkey
->uids
->name
;
229 const char* pakfire_key_get_email(PakfireKey key
) {
230 return key
->gpgkey
->uids
->email
;
233 const char* pakfire_key_get_pubkey_algo(PakfireKey key
) {
234 switch (key
->gpgkey
->subkeys
->pubkey_algo
) {
263 size_t pakfire_key_get_pubkey_length(PakfireKey key
) {
264 return key
->gpgkey
->subkeys
->length
;
267 time_t pakfire_key_get_created(PakfireKey key
) {
268 return key
->gpgkey
->subkeys
->timestamp
;
271 time_t pakfire_key_get_expires(PakfireKey key
) {
272 return key
->gpgkey
->subkeys
->expires
;
275 int pakfire_key_is_revoked(PakfireKey key
) {
276 return key
->gpgkey
->subkeys
->revoked
;
279 PakfireKey
pakfire_key_generate(Pakfire pakfire
, const char* userid
) {
280 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(pakfire
);
282 unsigned int flags
= 0;
284 // Key should be able to be used to sign
285 flags
|= GPGME_CREATE_SIGN
;
287 // Don't set a password
288 flags
|= GPGME_CREATE_NOPASSWD
;
290 // The key should never expire
291 flags
|= GPGME_CREATE_NOEXPIRE
;
294 gpgme_error_t error
= gpgme_op_createkey(gpgctx
, userid
,
295 DEFAULT_KEY_SIZE
, 0, 0, NULL
, flags
);
297 if (error
!= GPG_ERR_NO_ERROR
) {
298 printf("ERROR: %s\n", gpgme_strerror(error
));
302 // Retrieve the result
303 gpgme_genkey_result_t result
= gpgme_op_genkey_result(gpgctx
);
304 gpgme_release(gpgctx
);
306 // Retrieve the key by its fingerprint
307 return pakfire_key_get(pakfire
, result
->fpr
);
310 char* pakfire_key_export(PakfireKey key
, pakfire_key_export_mode_t mode
) {
311 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(key
->pakfire
);
313 gpgme_export_mode_t gpgmode
= 0;
315 case PAKFIRE_KEY_EXPORT_MODE_SECRET
:
316 gpgmode
|= GPGME_EXPORT_MODE_SECRET
;
323 const char* fingerprint
= pakfire_key_get_fingerprint(key
);
325 // Initialize the buffer
326 gpgme_data_t keydata
;
327 gpgme_error_t error
= gpgme_data_new(&keydata
);
328 if (gpg_err_code(error
) != GPG_ERR_NO_ERROR
)
331 // Encode output as ASCII
332 error
= gpgme_data_set_encoding(keydata
, GPGME_DATA_ENCODING_ARMOR
);
333 if (gpg_err_code(error
) != GPG_ERR_NO_ERROR
)
336 // Copy the key to the buffer
337 error
= gpgme_op_export(gpgctx
, fingerprint
, gpgmode
, keydata
);
338 if (gpg_err_code(error
) != GPG_ERR_NO_ERROR
)
341 // Export key in ASCII format
343 char* mem
= gpgme_data_release_and_get_mem(keydata
, &length
);
344 gpgme_release(gpgctx
);
346 // Copy to our own string buffer
347 char* buffer
= pakfire_strdup(mem
);
349 // Release the exported key
355 gpgme_data_release(keydata
);
356 gpgme_release(gpgctx
);
361 PakfireKey
* pakfire_key_import(Pakfire pakfire
, const char* data
) {
363 gpgme_data_t keydata
;
365 gpgme_ctx_t gpgctx
= pakfire_get_gpgctx(pakfire
);
367 // Form a data object out of the input without copying data
368 error
= gpgme_data_new_from_mem(&keydata
, data
, strlen(data
), 0);
369 if (error
!= GPG_ERR_NO_ERROR
)
372 // Try importing the key(s)
373 error
= gpgme_op_import(gpgctx
, keydata
);
375 gpgme_import_result_t result
;
377 // Everything went fine
378 case GPG_ERR_NO_ERROR
:
379 result
= gpgme_op_import_result(gpgctx
);
381 DEBUG("Keys considered = %d\n", result
->considered
);
382 DEBUG("Keys imported = %d\n", result
->imported
);
383 DEBUG("Keys not imported = %d\n", result
->not_imported
);
385 // Did we import any keys?
386 gpgme_import_status_t status
= result
->imports
;
390 PakfireKey
* head
= pakfire_calloc(result
->imported
+ 1, sizeof(*head
));
391 PakfireKey
* list
= head
;
393 // Retrieve all imported keys
395 PakfireKey key
= pakfire_key_get(pakfire
, status
->fpr
);
397 const char* fingerprint
= pakfire_key_get_fingerprint(key
);
398 INFO("Imported key %s\n", fingerprint
);
400 // Append key to list
404 status
= status
->next
;
410 gpgme_data_release(keydata
);
411 gpgme_release(gpgctx
);
416 case GPG_ERR_INV_VALUE
:
417 pakfire_errno
= PAKFIRE_E_INVALID_INPUT
;
420 // Fall through for any other errors
422 ERROR("Failed with gpgme error: %s\n", gpgme_strerror(error
));
427 gpgme_data_release(keydata
);
428 gpgme_release(gpgctx
);
433 char* pakfire_key_dump(PakfireKey key
) {
436 time_t created
= pakfire_key_get_created(key
);
437 char* date_created
= pakfire_format_date(created
);
439 asprintf(&s
, "pub %s%zu/%s %s",
440 pakfire_key_get_pubkey_algo(key
),
441 pakfire_key_get_pubkey_length(key
),
442 pakfire_key_get_fingerprint(key
),
445 pakfire_free(date_created
);
447 const char* uid
= pakfire_key_get_uid(key
);
449 asprintf(&s
, "%s\n %s", s
, uid
);
452 time_t expires
= pakfire_key_get_expires(key
);
454 char* date_expires
= pakfire_format_date(expires
);
455 asprintf(&s
, "%s\n %s: %s", s
, _("Expires"), date_expires
);
456 pakfire_free(date_expires
);