add key algo to import msg
[pacman-ng.git] / lib / libalpm / signing.c
blob0bef445141414b3f5b0b4c823f519cc1ee4622da
1 /*
2 * signing.c
4 * Copyright (c) 2008-2011 Pacman Development Team <pacman-dev@archlinux.org>
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 2 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/>.
20 #include "config.h"
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
26 #if HAVE_LIBGPGME
27 #include <locale.h> /* setlocale() */
28 #include <gpgme.h>
29 #include "base64.h"
30 #endif
32 /* libalpm */
33 #include "signing.h"
34 #include "package.h"
35 #include "util.h"
36 #include "log.h"
37 #include "alpm.h"
38 #include "handle.h"
40 #if HAVE_LIBGPGME
41 #define CHECK_ERR(void) do { \
42 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) { goto error; } \
43 } while(0)
45 /**
46 * Return a statically allocated validity string based on the GPGME validity
47 * code. This is mainly for debug purposes and is not translated.
48 * @param validity a validity code returned by GPGME
49 * @return a string such as "marginal"
51 static const char *string_validity(gpgme_validity_t validity)
53 switch(validity) {
54 case GPGME_VALIDITY_UNKNOWN:
55 return "unknown";
56 case GPGME_VALIDITY_UNDEFINED:
57 return "undefined";
58 case GPGME_VALIDITY_NEVER:
59 return "never";
60 case GPGME_VALIDITY_MARGINAL:
61 return "marginal";
62 case GPGME_VALIDITY_FULL:
63 return "full";
64 case GPGME_VALIDITY_ULTIMATE:
65 return "ultimate";
67 return "???";
70 static void sigsum_test_bit(gpgme_sigsum_t sigsum, alpm_list_t **summary,
71 gpgme_sigsum_t bit, const char *value)
73 if(sigsum & bit) {
74 *summary = alpm_list_add(*summary, (void *)value);
78 /**
79 * Calculate a set of strings to represent the given GPGME signature summary
80 * value. This is a bitmask so you may get any number of strings back.
81 * @param sigsum a GPGME signature summary bitmask
82 * @return the list of signature summary strings
84 static alpm_list_t *list_sigsum(gpgme_sigsum_t sigsum)
86 alpm_list_t *summary = NULL;
87 /* The docs say this can be a bitmask...not sure I believe it, but we'll code
88 * for it anyway and show all possible flags in the returned string. */
90 /* The signature is fully valid. */
91 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_VALID, "valid");
92 /* The signature is good. */
93 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_GREEN, "green");
94 /* The signature is bad. */
95 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_RED, "red");
96 /* One key has been revoked. */
97 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_REVOKED, "key revoked");
98 /* One key has expired. */
99 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_EXPIRED, "key expired");
100 /* The signature has expired. */
101 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_SIG_EXPIRED, "sig expired");
102 /* Can't verify: key missing. */
103 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_MISSING, "key missing");
104 /* CRL not available. */
105 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_CRL_MISSING, "crl missing");
106 /* Available CRL is too old. */
107 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_CRL_TOO_OLD, "crl too old");
108 /* A policy was not met. */
109 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_BAD_POLICY, "bad policy");
110 /* A system error occured. */
111 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_SYS_ERROR, "sys error");
112 /* Fallback case */
113 if(!sigsum) {
114 summary = alpm_list_add(summary, (void *)"(empty)");
116 return summary;
120 * Initialize the GPGME library.
121 * This can be safely called multiple times; however it is not thread-safe.
122 * @param handle the context handle
123 * @return 0 on success, -1 on error
125 static int init_gpgme(alpm_handle_t *handle)
127 static int init = 0;
128 const char *version, *sigdir;
129 gpgme_error_t err;
130 gpgme_engine_info_t enginfo;
132 if(init) {
133 /* we already successfully initialized the library */
134 return 0;
137 sigdir = handle->gpgdir;
139 if(_alpm_access(handle, sigdir, "pubring.gpg", R_OK)
140 || _alpm_access(handle, sigdir, "trustdb.gpg", R_OK)) {
141 handle->pm_errno = ALPM_ERR_NOT_A_FILE;
142 _alpm_log(handle, ALPM_LOG_DEBUG, "Signature verification will fail!\n");
143 _alpm_log(handle, ALPM_LOG_WARNING,
144 _("Public keyring not found; have you run '%s'?\n"),
145 "pacman-key --init");
148 /* calling gpgme_check_version() returns the current version and runs
149 * some internal library setup code */
150 version = gpgme_check_version(NULL);
151 _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME version: %s\n", version);
152 gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
153 #ifdef LC_MESSAGES
154 gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
155 #endif
156 /* NOTE:
157 * The GPGME library installs a SIGPIPE signal handler automatically if
158 * the default signal hander is in use. The only time we set a handler
159 * for SIGPIPE is in dload.c, and we reset it when we are done. Given that
160 * we do this, we can let GPGME do its automagic. However, if we install
161 * a library-wide SIGPIPE handler, we will have to be careful.
164 /* check for OpenPGP support (should be a no-brainer, but be safe) */
165 err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
166 CHECK_ERR();
168 /* set and check engine information */
169 err = gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL, sigdir);
170 CHECK_ERR();
171 err = gpgme_get_engine_info(&enginfo);
172 CHECK_ERR();
173 _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME engine info: file=%s, home=%s\n",
174 enginfo->file_name, enginfo->home_dir);
176 init = 1;
177 return 0;
179 error:
180 _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
181 RET_ERR(handle, ALPM_ERR_GPGME, -1);
185 * Determine if we have a key is known in our local keyring.
186 * @param handle the context handle
187 * @param fpr the fingerprint key ID to look up
188 * @return 1 if key is known, 0 if key is unknown, -1 on error
190 static int key_in_keychain(alpm_handle_t *handle, const char *fpr)
192 gpgme_error_t err;
193 gpgme_ctx_t ctx;
194 gpgme_key_t key;
195 int ret = -1;
197 memset(&ctx, 0, sizeof(ctx));
198 err = gpgme_new(&ctx);
199 CHECK_ERR();
201 _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s locally\n", fpr);
203 err = gpgme_get_key(ctx, fpr, &key, 0);
204 if(gpg_err_code(err) == GPG_ERR_EOF) {
205 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
206 ret = 0;
207 } else if(gpg_err_code(err) == GPG_ERR_NO_ERROR) {
208 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup success, key exists\n");
209 ret = 1;
210 } else {
211 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err));
214 error:
215 gpgme_key_unref(key);
216 gpgme_release(ctx);
217 return ret;
221 * Search for a GPG key in a remote location.
222 * This requires GPGME to call the gpg binary and have a keyserver previously
223 * defined in a gpg.conf configuration file.
224 * @param handle the context handle
225 * @param fpr the fingerprint key ID to look up
226 * @param pgpkey storage location for the given key if found
227 * @return 1 on success, 0 on key not found, -1 on error
229 static int key_search(alpm_handle_t *handle, const char *fpr,
230 alpm_pgpkey_t *pgpkey)
232 gpgme_error_t err;
233 gpgme_ctx_t ctx;
234 gpgme_keylist_mode_t mode;
235 gpgme_key_t key;
236 int ret = -1;
238 memset(&ctx, 0, sizeof(ctx));
239 err = gpgme_new(&ctx);
240 CHECK_ERR();
242 mode = gpgme_get_keylist_mode(ctx);
243 /* using LOCAL and EXTERN together doesn't work for GPG 1.X. Ugh. */
244 mode &= ~GPGME_KEYLIST_MODE_LOCAL;
245 mode |= GPGME_KEYLIST_MODE_EXTERN;
246 err = gpgme_set_keylist_mode(ctx, mode);
247 CHECK_ERR();
249 _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s remotely\n", fpr);
251 err = gpgme_get_key(ctx, fpr, &key, 0);
252 if(gpg_err_code(err) == GPG_ERR_EOF) {
253 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
254 ret = 0;
255 goto error;
256 } else if(gpg_err_code(err) != GPG_ERR_NO_ERROR) {
257 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err));
258 goto error;
261 /* should only get here if key actually exists */
262 pgpkey->data = key;
263 if(key->subkeys->fpr) {
264 pgpkey->fingerprint = key->subkeys->fpr;
265 } else if(key->subkeys->keyid) {
266 pgpkey->fingerprint = key->subkeys->keyid;
268 pgpkey->uid = key->uids->uid;
269 pgpkey->name = key->uids->name;
270 pgpkey->email = key->uids->email;
271 pgpkey->created = key->subkeys->timestamp;
272 pgpkey->expires = key->subkeys->expires;
273 pgpkey->length = key->subkeys->length;
274 pgpkey->revoked = key->subkeys->revoked;
276 switch (key->subkeys->pubkey_algo) {
277 case GPGME_PK_RSA:
278 case GPGME_PK_RSA_E:
279 case GPGME_PK_RSA_S:
280 pgpkey->pubkey_algo = 'R';
281 break;
283 case GPGME_PK_DSA:
284 pgpkey->pubkey_algo = 'D';
285 break;
287 case GPGME_PK_ELG_E:
288 case GPGME_PK_ELG:
289 case GPGME_PK_ECDSA:
290 case GPGME_PK_ECDH:
291 pgpkey->pubkey_algo = 'E';
292 break;
295 ret = 1;
297 error:
298 gpgme_release(ctx);
299 return ret;
303 * Import a key into the local keyring.
304 * @param handle the context handle
305 * @param key the key to import, likely retrieved from #key_search
306 * @return 0 on success, -1 on error
308 static int key_import(alpm_handle_t *handle, alpm_pgpkey_t *key)
310 gpgme_error_t err;
311 gpgme_ctx_t ctx;
312 gpgme_key_t keys[2];
313 gpgme_import_result_t result;
314 int ret = -1;
316 if(_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) {
317 /* no chance of import succeeding if pubring isn't writable */
318 _alpm_log(handle, ALPM_LOG_ERROR, _("keyring is not writable\n"));
319 return -1;
322 memset(&ctx, 0, sizeof(ctx));
323 err = gpgme_new(&ctx);
324 CHECK_ERR();
326 _alpm_log(handle, ALPM_LOG_DEBUG, "importing key\n");
328 keys[0] = key->data;
329 keys[1] = NULL;
330 err = gpgme_op_import_keys(ctx, keys);
331 CHECK_ERR();
332 result = gpgme_op_import_result(ctx);
333 CHECK_ERR();
334 /* we know we tried to import exactly one key, so check for this */
335 if(result->considered != 1 || !result->imports) {
336 _alpm_log(handle, ALPM_LOG_DEBUG, "could not import key, 0 results\n");
337 ret = -1;
338 } else if(result->imports->result != GPG_ERR_NO_ERROR) {
339 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err));
340 ret = -1;
341 } else {
342 ret = 0;
345 error:
346 gpgme_release(ctx);
347 return ret;
351 * Decode a loaded signature in base64 form.
352 * @param base64_data the signature to attempt to decode
353 * @param data the decoded data; must be freed by the caller
354 * @param data_len the length of the returned data
355 * @return 0 on success, -1 on failure to properly decode
357 static int decode_signature(const char *base64_data,
358 unsigned char **data, size_t *data_len) {
359 size_t len = strlen(base64_data);
360 unsigned char *usline = (unsigned char *)base64_data;
361 /* reasonable allocation of expected length is 3/4 of encoded length */
362 size_t destlen = len * 3 / 4;
363 MALLOC(*data, destlen, goto error);
364 if(base64_decode(*data, &destlen, usline, len)) {
365 free(*data);
366 goto error;
368 *data_len = destlen;
369 return 0;
371 error:
372 *data = NULL;
373 *data_len = 0;
374 return -1;
378 * Check the PGP signature for the given file path.
379 * If base64_sig is provided, it will be used as the signature data after
380 * decoding. If base64_sig is NULL, expect a signature file next to path
381 * (e.g. "%s.sig").
383 * The return value will be 0 if nothing abnormal happened during the signature
384 * check, and -1 if an error occurred while checking signatures or if a
385 * signature could not be found; pm_errno will be set. Note that "abnormal"
386 * does not include a failed signature; the value in siglist should be checked
387 * to determine if the signature(s) are good.
388 * @param handle the context handle
389 * @param path the full path to a file
390 * @param base64_sig optional PGP signature data in base64 encoding
391 * @param siglist a pointer to storage for signature results
392 * @return 0 in normal cases, -1 if the something failed in the check process
394 int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path,
395 const char *base64_sig, alpm_siglist_t *siglist)
397 int ret = -1, sigcount;
398 gpgme_error_t err = 0;
399 gpgme_ctx_t ctx;
400 gpgme_data_t filedata, sigdata;
401 gpgme_verify_result_t verify_result;
402 gpgme_signature_t gpgsig;
403 char *sigpath = NULL;
404 unsigned char *decoded_sigdata = NULL;
405 FILE *file = NULL, *sigfile = NULL;
407 if(!path || _alpm_access(handle, NULL, path, R_OK) != 0) {
408 RET_ERR(handle, ALPM_ERR_NOT_A_FILE, -1);
411 if(!siglist) {
412 RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1);
414 siglist->count = 0;
416 if(!base64_sig) {
417 sigpath = _alpm_sigpath(handle, path);
418 /* this will just help debugging */
419 _alpm_access(handle, NULL, sigpath, R_OK);
422 /* does the file we are verifying exist? */
423 file = fopen(path, "rb");
424 if(file == NULL) {
425 handle->pm_errno = ALPM_ERR_NOT_A_FILE;
426 goto error;
429 /* does the sig file exist (if we didn't get the data directly)? */
430 if(!base64_sig) {
431 sigfile = fopen(sigpath, "rb");
432 if(sigfile == NULL) {
433 _alpm_log(handle, ALPM_LOG_DEBUG, "sig path %s could not be opened\n",
434 sigpath);
435 handle->pm_errno = ALPM_ERR_SIG_MISSING;
436 goto error;
440 if(init_gpgme(handle)) {
441 /* pm_errno was set in gpgme_init() */
442 goto error;
445 _alpm_log(handle, ALPM_LOG_DEBUG, "checking signature for %s\n", path);
447 memset(&ctx, 0, sizeof(ctx));
448 memset(&sigdata, 0, sizeof(sigdata));
449 memset(&filedata, 0, sizeof(filedata));
451 err = gpgme_new(&ctx);
452 CHECK_ERR();
454 /* create our necessary data objects to verify the signature */
455 err = gpgme_data_new_from_stream(&filedata, file);
456 CHECK_ERR();
458 /* next create data object for the signature */
459 if(base64_sig) {
460 /* memory-based, we loaded it from a sync DB */
461 size_t data_len;
462 int decode_ret = decode_signature(base64_sig,
463 &decoded_sigdata, &data_len);
464 if(decode_ret) {
465 handle->pm_errno = ALPM_ERR_SIG_INVALID;
466 goto gpg_error;
468 err = gpgme_data_new_from_mem(&sigdata,
469 (char *)decoded_sigdata, data_len, 0);
470 } else {
471 /* file-based, it is on disk */
472 err = gpgme_data_new_from_stream(&sigdata, sigfile);
474 CHECK_ERR();
476 /* here's where the magic happens */
477 err = gpgme_op_verify(ctx, sigdata, filedata, NULL);
478 CHECK_ERR();
479 verify_result = gpgme_op_verify_result(ctx);
480 CHECK_ERR();
481 if(!verify_result || !verify_result->signatures) {
482 _alpm_log(handle, ALPM_LOG_DEBUG, "no signatures returned\n");
483 handle->pm_errno = ALPM_ERR_SIG_MISSING;
484 goto gpg_error;
486 for(gpgsig = verify_result->signatures, sigcount = 0;
487 gpgsig; gpgsig = gpgsig->next, sigcount++);
488 _alpm_log(handle, ALPM_LOG_DEBUG, "%d signatures returned\n", sigcount);
490 CALLOC(siglist->results, sigcount, sizeof(alpm_sigresult_t),
491 handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error);
492 siglist->count = sigcount;
494 for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig;
495 gpgsig = gpgsig->next, sigcount++) {
496 alpm_list_t *summary_list, *summary;
497 alpm_sigstatus_t status;
498 alpm_sigvalidity_t validity;
499 gpgme_key_t key;
500 alpm_sigresult_t *result;
502 _alpm_log(handle, ALPM_LOG_DEBUG, "fingerprint: %s\n", gpgsig->fpr);
503 summary_list = list_sigsum(gpgsig->summary);
504 for(summary = summary_list; summary; summary = summary->next) {
505 _alpm_log(handle, ALPM_LOG_DEBUG, "summary: %s\n", (const char *)summary->data);
507 alpm_list_free(summary_list);
508 _alpm_log(handle, ALPM_LOG_DEBUG, "status: %s\n", gpgme_strerror(gpgsig->status));
509 _alpm_log(handle, ALPM_LOG_DEBUG, "timestamp: %lu\n", gpgsig->timestamp);
510 _alpm_log(handle, ALPM_LOG_DEBUG, "exp_timestamp: %lu\n", gpgsig->exp_timestamp);
511 _alpm_log(handle, ALPM_LOG_DEBUG, "validity: %s; reason: %s\n",
512 string_validity(gpgsig->validity),
513 gpgme_strerror(gpgsig->validity_reason));
515 result = siglist->results + sigcount;
516 err = gpgme_get_key(ctx, gpgsig->fpr, &key, 0);
517 if(gpg_err_code(err) == GPG_ERR_EOF) {
518 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
519 err = GPG_ERR_NO_ERROR;
520 /* we dupe the fpr in this case since we have no key to point at */
521 STRDUP(result->key.fingerprint, gpgsig->fpr,
522 handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error);
523 } else {
524 CHECK_ERR();
525 if(key->uids) {
526 result->key.data = key;
527 result->key.fingerprint = key->subkeys->fpr;
528 result->key.uid = key->uids->uid;
529 result->key.name = key->uids->name;
530 result->key.email = key->uids->email;
531 result->key.created = key->subkeys->timestamp;
532 result->key.expires = key->subkeys->expires;
533 _alpm_log(handle, ALPM_LOG_DEBUG,
534 "key: %s, %s, owner_trust %s, disabled %d\n",
535 key->subkeys->fpr, key->uids->uid,
536 string_validity(key->owner_trust), key->disabled);
540 switch(gpg_err_code(gpgsig->status)) {
541 /* good cases */
542 case GPG_ERR_NO_ERROR:
543 status = ALPM_SIGSTATUS_VALID;
544 break;
545 case GPG_ERR_KEY_EXPIRED:
546 status = ALPM_SIGSTATUS_KEY_EXPIRED;
547 break;
548 /* bad cases */
549 case GPG_ERR_SIG_EXPIRED:
550 status = ALPM_SIGSTATUS_SIG_EXPIRED;
551 break;
552 case GPG_ERR_NO_PUBKEY:
553 status = ALPM_SIGSTATUS_KEY_UNKNOWN;
554 break;
555 case GPG_ERR_BAD_SIGNATURE:
556 default:
557 status = ALPM_SIGSTATUS_INVALID;
558 break;
560 /* special case: key disabled is not returned in above status code */
561 if(result->key.data && key->disabled) {
562 status = ALPM_SIGSTATUS_KEY_DISABLED;
565 switch(gpgsig->validity) {
566 case GPGME_VALIDITY_ULTIMATE:
567 case GPGME_VALIDITY_FULL:
568 validity = ALPM_SIGVALIDITY_FULL;
569 break;
570 case GPGME_VALIDITY_MARGINAL:
571 validity = ALPM_SIGVALIDITY_MARGINAL;
572 break;
573 case GPGME_VALIDITY_NEVER:
574 validity = ALPM_SIGVALIDITY_NEVER;
575 break;
576 case GPGME_VALIDITY_UNKNOWN:
577 case GPGME_VALIDITY_UNDEFINED:
578 default:
579 validity = ALPM_SIGVALIDITY_UNKNOWN;
580 break;
583 result->status = status;
584 result->validity = validity;
587 ret = 0;
589 gpg_error:
590 gpgme_data_release(sigdata);
591 gpgme_data_release(filedata);
592 gpgme_release(ctx);
594 error:
595 if(sigfile) {
596 fclose(sigfile);
598 if(file) {
599 fclose(file);
601 FREE(sigpath);
602 FREE(decoded_sigdata);
603 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) {
604 _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
605 RET_ERR(handle, ALPM_ERR_GPGME, -1);
607 return ret;
610 #else /* HAVE_LIBGPGME */
611 static int key_in_keychain(alpm_handle_t UNUSED *handle, const char UNUSED *fpr)
613 return -1;
615 int _alpm_gpgme_checksig(alpm_handle_t UNUSED *handle, const char UNUSED *path,
616 const char UNUSED *base64_sig, alpm_siglist_t UNUSED *siglist)
618 return -1;
620 #endif /* HAVE_LIBGPGME */
623 * Form a signature path given a file path.
624 * Caller must free the result.
625 * @param handle the context handle
626 * @param path the full path to a file
627 * @return the path with '.sig' appended, NULL on errors
629 char *_alpm_sigpath(alpm_handle_t *handle, const char *path)
631 char *sigpath;
632 size_t len;
634 if(!path) {
635 return NULL;
637 len = strlen(path) + 5;
638 CALLOC(sigpath, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
639 sprintf(sigpath, "%s.sig", path);
640 return sigpath;
644 * Helper for checking the PGP signature for the given file path.
645 * This wraps #_alpm_gpgme_checksig in a slightly friendlier manner to simplify
646 * handling of optional signatures and marginal/unknown trust levels and
647 * handling the correct error code return values.
648 * @param handle the context handle
649 * @param path the full path to a file
650 * @param base64_sig optional PGP signature data in base64 encoding
651 * @param optional whether signatures are optional (e.g., missing OK)
652 * @param marginal whether signatures with marginal trust are acceptable
653 * @param unknown whether signatures with unknown trust are acceptable
654 * @param sigdata a pointer to storage for signature results
655 * @return 0 on success, -1 on error (consult pm_errno or sigdata)
657 int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path,
658 const char *base64_sig, int optional, int marginal, int unknown,
659 alpm_siglist_t **sigdata)
661 alpm_siglist_t *siglist;
662 int ret;
664 CALLOC(siglist, 1, sizeof(alpm_siglist_t),
665 RET_ERR(handle, ALPM_ERR_MEMORY, -1));
667 ret = _alpm_gpgme_checksig(handle, path, base64_sig, siglist);
668 if(ret && handle->pm_errno == ALPM_ERR_SIG_MISSING) {
669 if(optional) {
670 _alpm_log(handle, ALPM_LOG_DEBUG, "missing optional signature\n");
671 handle->pm_errno = 0;
672 ret = 0;
673 } else {
674 _alpm_log(handle, ALPM_LOG_DEBUG, "missing required signature\n");
675 /* ret will already be -1 */
677 } else if(ret) {
678 _alpm_log(handle, ALPM_LOG_DEBUG, "signature check failed\n");
679 /* ret will already be -1 */
680 } else {
681 size_t num;
682 for(num = 0; !ret && num < siglist->count; num++) {
683 switch(siglist->results[num].status) {
684 case ALPM_SIGSTATUS_VALID:
685 case ALPM_SIGSTATUS_KEY_EXPIRED:
686 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n");
687 switch(siglist->results[num].validity) {
688 case ALPM_SIGVALIDITY_FULL:
689 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is fully trusted\n");
690 break;
691 case ALPM_SIGVALIDITY_MARGINAL:
692 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is marginal trust\n");
693 if(!marginal) {
694 ret = -1;
696 break;
697 case ALPM_SIGVALIDITY_UNKNOWN:
698 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is unknown trust\n");
699 if(!unknown) {
700 ret = -1;
702 break;
703 case ALPM_SIGVALIDITY_NEVER:
704 _alpm_log(handle, ALPM_LOG_DEBUG, "signature should never be trusted\n");
705 ret = -1;
706 break;
708 break;
709 case ALPM_SIGSTATUS_SIG_EXPIRED:
710 case ALPM_SIGSTATUS_KEY_UNKNOWN:
711 case ALPM_SIGSTATUS_KEY_DISABLED:
712 case ALPM_SIGSTATUS_INVALID:
713 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n");
714 ret = -1;
715 break;
720 if(sigdata) {
721 *sigdata = siglist;
722 } else {
723 alpm_siglist_cleanup(siglist);
724 free(siglist);
727 return ret;
731 * Examine a signature result list and take any appropriate or necessary
732 * actions. This may include asking the user to import a key or simply printing
733 * helpful failure messages so the user can take action out of band.
734 * @param handle the context handle
735 * @param identifier a friendly name for the signed resource; usually a
736 * database or package name
737 * @param siglist a pointer to storage for signature results
738 * @param optional whether signatures are optional (e.g., missing OK)
739 * @param marginal whether signatures with marginal trust are acceptable
740 * @param unknown whether signatures with unknown trust are acceptable
741 * @return 0 if all signatures are OK, -1 on errors, 1 if we should retry the
742 * validation process
744 int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier,
745 alpm_siglist_t *siglist, int optional, int marginal, int unknown)
747 size_t i;
748 int retry = 0;
750 if(!optional && siglist->count == 0) {
751 _alpm_log(handle, ALPM_LOG_ERROR,
752 _("%s: missing required signature\n"), identifier);
755 for(i = 0; i < siglist->count; i++) {
756 alpm_sigresult_t *result = siglist->results + i;
757 const char *name = result->key.uid ? result->key.uid : result->key.fingerprint;
758 switch(result->status) {
759 case ALPM_SIGSTATUS_VALID:
760 case ALPM_SIGSTATUS_KEY_EXPIRED:
761 switch(result->validity) {
762 case ALPM_SIGVALIDITY_FULL:
763 break;
764 case ALPM_SIGVALIDITY_MARGINAL:
765 if(!marginal) {
766 _alpm_log(handle, ALPM_LOG_ERROR,
767 _("%s: signature from \"%s\" is marginal trust\n"),
768 identifier, name);
769 /* QUESTION(handle, ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */
771 break;
772 case ALPM_SIGVALIDITY_UNKNOWN:
773 if(!unknown) {
774 _alpm_log(handle, ALPM_LOG_ERROR,
775 _("%s: signature from \"%s\" is unknown trust\n"),
776 identifier, name);
777 /* QUESTION(handle, ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */
779 break;
780 case ALPM_SIGVALIDITY_NEVER:
781 _alpm_log(handle, ALPM_LOG_ERROR,
782 _("%s: signature from \"%s\" should never be trusted\n"),
783 identifier, name);
784 break;
786 break;
787 case ALPM_SIGSTATUS_KEY_UNKNOWN:
788 /* ensure this key is still actually unknown; we may have imported it
789 * on an earlier call to this function. */
790 if(key_in_keychain(handle, result->key.fingerprint) == 1) {
791 break;
793 _alpm_log(handle, ALPM_LOG_ERROR,
794 _("%s: key \"%s\" is unknown\n"), identifier, name);
795 #ifdef HAVE_LIBGPGME
797 int answer;
798 alpm_pgpkey_t fetch_key;
799 memset(&fetch_key, 0, sizeof(fetch_key));
801 if(key_search(handle, result->key.fingerprint, &fetch_key) == 1) {
802 _alpm_log(handle, ALPM_LOG_DEBUG,
803 "unknown key, found %s on keyserver\n", fetch_key.uid);
804 if(!_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) {
805 QUESTION(handle, ALPM_QUESTION_IMPORT_KEY,
806 &fetch_key, NULL, NULL, &answer);
807 if(answer) {
808 if(key_import(handle, &fetch_key) == 0) {
809 retry = 1;
810 } else {
811 _alpm_log(handle, ALPM_LOG_ERROR,
812 _("key \"%s\" could not be imported\n"), fetch_key.uid);
815 } else {
816 /* keyring directory was not writable, so we don't even try */
817 _alpm_log(handle, ALPM_LOG_WARNING,
818 _("key %s, \"%s\" found on keyserver, keyring is not writable\n"),
819 fetch_key.fingerprint, fetch_key.uid);
821 } else {
822 _alpm_log(handle, ALPM_LOG_ERROR,
823 _("key \"%s\" could not be looked up remotely\n"), name);
825 gpgme_key_unref(fetch_key.data);
827 #endif
828 break;
829 case ALPM_SIGSTATUS_KEY_DISABLED:
830 _alpm_log(handle, ALPM_LOG_ERROR,
831 _("%s: key \"%s\" is disabled\n"), identifier, name);
832 break;
833 case ALPM_SIGSTATUS_SIG_EXPIRED:
834 _alpm_log(handle, ALPM_LOG_ERROR,
835 _("%s: signature from \"%s\" is expired\n"), identifier, name);
836 break;
837 case ALPM_SIGSTATUS_INVALID:
838 _alpm_log(handle, ALPM_LOG_ERROR,
839 _("%s: signature from \"%s\" is invalid\n"),
840 identifier, name);
841 break;
845 return retry;
849 * Check the PGP signature for the given package file.
850 * @param pkg the package to check
851 * @param siglist a pointer to storage for signature results
852 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
854 int SYMEXPORT alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg,
855 alpm_siglist_t *siglist)
857 ASSERT(pkg != NULL, return -1);
858 ASSERT(siglist != NULL, RET_ERR(pkg->handle, ALPM_ERR_WRONG_ARGS, -1));
859 pkg->handle->pm_errno = 0;
861 return _alpm_gpgme_checksig(pkg->handle, pkg->filename,
862 pkg->base64_sig, siglist);
866 * Check the PGP signature for the given database.
867 * @param db the database to check
868 * @param siglist a pointer to storage for signature results
869 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
871 int SYMEXPORT alpm_db_check_pgp_signature(alpm_db_t *db,
872 alpm_siglist_t *siglist)
874 ASSERT(db != NULL, return -1);
875 ASSERT(siglist != NULL, RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, -1));
876 db->handle->pm_errno = 0;
878 return _alpm_gpgme_checksig(db->handle, _alpm_db_path(db), NULL, siglist);
882 * Clean up and free a signature result list.
883 * Note that this does not free the siglist object itself in case that
884 * was allocated on the stack; this is the responsibility of the caller.
885 * @param siglist a pointer to storage for signature results
886 * @return 0 on success, -1 on error
888 int SYMEXPORT alpm_siglist_cleanup(alpm_siglist_t *siglist)
890 ASSERT(siglist != NULL, return -1);
891 size_t num;
892 for(num = 0; num < siglist->count; num++) {
893 alpm_sigresult_t *result = siglist->results + num;
894 if(result->key.data) {
895 #if HAVE_LIBGPGME
896 gpgme_key_unref(result->key.data);
897 #endif
898 } else {
899 free(result->key.fingerprint);
902 if(siglist->count) {
903 free(siglist->results);
905 siglist->results = NULL;
906 siglist->count = 0;
907 return 0;
910 /* vim: set ts=2 sw=2 noet: */