removed the condition in alpm_db_set_servers since FREELIST is NULL safe
[pacman-ng.git] / lib / libalpm / signing.c
blob1e4171642a6936e007204d10c7d7fde709a5c9ce
1 /*
2 * signing.c
4 * Copyright (c) 2008-2012 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 <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
24 #if HAVE_LIBGPGME
25 #include <locale.h> /* setlocale() */
26 #include <gpgme.h>
27 #include "base64.h"
28 #endif
30 /* libalpm */
31 #include "signing.h"
32 #include "package.h"
33 #include "util.h"
34 #include "log.h"
35 #include "alpm.h"
36 #include "handle.h"
38 #if HAVE_LIBGPGME
39 #define CHECK_ERR(void) do { \
40 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) { goto error; } \
41 } while(0)
43 /**
44 * Return a statically allocated validity string based on the GPGME validity
45 * code. This is mainly for debug purposes and is not translated.
46 * @param validity a validity code returned by GPGME
47 * @return a string such as "marginal"
49 static const char *string_validity(gpgme_validity_t validity)
51 switch(validity) {
52 case GPGME_VALIDITY_UNKNOWN:
53 return "unknown";
54 case GPGME_VALIDITY_UNDEFINED:
55 return "undefined";
56 case GPGME_VALIDITY_NEVER:
57 return "never";
58 case GPGME_VALIDITY_MARGINAL:
59 return "marginal";
60 case GPGME_VALIDITY_FULL:
61 return "full";
62 case GPGME_VALIDITY_ULTIMATE:
63 return "ultimate";
65 return "???";
68 static void sigsum_test_bit(gpgme_sigsum_t sigsum, alpm_list_t **summary,
69 gpgme_sigsum_t bit, const char *value)
71 if(sigsum & bit) {
72 *summary = alpm_list_add(*summary, (void *)value);
76 /**
77 * Calculate a set of strings to represent the given GPGME signature summary
78 * value. This is a bitmask so you may get any number of strings back.
79 * @param sigsum a GPGME signature summary bitmask
80 * @return the list of signature summary strings
82 static alpm_list_t *list_sigsum(gpgme_sigsum_t sigsum)
84 alpm_list_t *summary = NULL;
85 /* The docs say this can be a bitmask...not sure I believe it, but we'll code
86 * for it anyway and show all possible flags in the returned string. */
88 /* The signature is fully valid. */
89 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_VALID, "valid");
90 /* The signature is good. */
91 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_GREEN, "green");
92 /* The signature is bad. */
93 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_RED, "red");
94 /* One key has been revoked. */
95 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_REVOKED, "key revoked");
96 /* One key has expired. */
97 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_EXPIRED, "key expired");
98 /* The signature has expired. */
99 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_SIG_EXPIRED, "sig expired");
100 /* Can't verify: key missing. */
101 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_MISSING, "key missing");
102 /* CRL not available. */
103 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_CRL_MISSING, "crl missing");
104 /* Available CRL is too old. */
105 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_CRL_TOO_OLD, "crl too old");
106 /* A policy was not met. */
107 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_BAD_POLICY, "bad policy");
108 /* A system error occured. */
109 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_SYS_ERROR, "sys error");
110 /* Fallback case */
111 if(!sigsum) {
112 summary = alpm_list_add(summary, (void *)"(empty)");
114 return summary;
118 * Initialize the GPGME library.
119 * This can be safely called multiple times; however it is not thread-safe.
120 * @param handle the context handle
121 * @return 0 on success, -1 on error
123 static int init_gpgme(alpm_handle_t *handle)
125 static int init = 0;
126 const char *version, *sigdir;
127 gpgme_error_t err;
128 gpgme_engine_info_t enginfo;
130 if(init) {
131 /* we already successfully initialized the library */
132 return 0;
135 sigdir = handle->gpgdir;
137 if(_alpm_access(handle, sigdir, "pubring.gpg", R_OK)
138 || _alpm_access(handle, sigdir, "trustdb.gpg", R_OK)) {
139 handle->pm_errno = ALPM_ERR_NOT_A_FILE;
140 _alpm_log(handle, ALPM_LOG_DEBUG, "Signature verification will fail!\n");
141 _alpm_log(handle, ALPM_LOG_WARNING,
142 _("Public keyring not found; have you run '%s'?\n"),
143 "pacman-key --init");
146 /* calling gpgme_check_version() returns the current version and runs
147 * some internal library setup code */
148 version = gpgme_check_version(NULL);
149 _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME version: %s\n", version);
150 gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
151 #ifdef LC_MESSAGES
152 gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
153 #endif
154 /* NOTE:
155 * The GPGME library installs a SIGPIPE signal handler automatically if
156 * the default signal hander is in use. The only time we set a handler
157 * for SIGPIPE is in dload.c, and we reset it when we are done. Given that
158 * we do this, we can let GPGME do its automagic. However, if we install
159 * a library-wide SIGPIPE handler, we will have to be careful.
162 /* check for OpenPGP support (should be a no-brainer, but be safe) */
163 err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
164 CHECK_ERR();
166 /* set and check engine information */
167 err = gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL, sigdir);
168 CHECK_ERR();
169 err = gpgme_get_engine_info(&enginfo);
170 CHECK_ERR();
171 _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME engine info: file=%s, home=%s\n",
172 enginfo->file_name, enginfo->home_dir);
174 init = 1;
175 return 0;
177 error:
178 _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
179 RET_ERR(handle, ALPM_ERR_GPGME, -1);
183 * Determine if we have a key is known in our local keyring.
184 * @param handle the context handle
185 * @param fpr the fingerprint key ID to look up
186 * @return 1 if key is known, 0 if key is unknown, -1 on error
188 static int key_in_keychain(alpm_handle_t *handle, const char *fpr)
190 gpgme_error_t err;
191 gpgme_ctx_t ctx;
192 gpgme_key_t key;
193 int ret = -1;
195 memset(&ctx, 0, sizeof(ctx));
196 err = gpgme_new(&ctx);
197 CHECK_ERR();
199 _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s locally\n", fpr);
201 err = gpgme_get_key(ctx, fpr, &key, 0);
202 if(gpg_err_code(err) == GPG_ERR_EOF) {
203 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
204 ret = 0;
205 } else if(gpg_err_code(err) == GPG_ERR_NO_ERROR) {
206 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup success, key exists\n");
207 ret = 1;
208 } else {
209 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err));
211 gpgme_key_unref(key);
213 error:
214 gpgme_release(ctx);
215 return ret;
219 * Search for a GPG key in a remote location.
220 * This requires GPGME to call the gpg binary and have a keyserver previously
221 * defined in a gpg.conf configuration file.
222 * @param handle the context handle
223 * @param fpr the fingerprint key ID to look up
224 * @param pgpkey storage location for the given key if found
225 * @return 1 on success, 0 on key not found, -1 on error
227 static int key_search(alpm_handle_t *handle, const char *fpr,
228 alpm_pgpkey_t *pgpkey)
230 gpgme_error_t err;
231 gpgme_ctx_t ctx;
232 gpgme_keylist_mode_t mode;
233 gpgme_key_t key;
234 int ret = -1;
235 size_t fpr_len;
236 char *full_fpr;
238 /* gpg2 goes full retard here. For key searches ONLY, we need to prefix the
239 * key fingerprint with 0x, or the lookup will fail. */
240 fpr_len = strlen(fpr);
241 MALLOC(full_fpr, fpr_len + 3, RET_ERR(handle, ALPM_ERR_MEMORY, -1));
242 sprintf(full_fpr, "0x%s", fpr);
244 memset(&ctx, 0, sizeof(ctx));
245 err = gpgme_new(&ctx);
246 CHECK_ERR();
248 mode = gpgme_get_keylist_mode(ctx);
249 /* using LOCAL and EXTERN together doesn't work for GPG 1.X. Ugh. */
250 mode &= ~GPGME_KEYLIST_MODE_LOCAL;
251 mode |= GPGME_KEYLIST_MODE_EXTERN;
252 err = gpgme_set_keylist_mode(ctx, mode);
253 CHECK_ERR();
255 _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s remotely\n", fpr);
257 err = gpgme_get_key(ctx, full_fpr, &key, 0);
258 if(gpg_err_code(err) == GPG_ERR_EOF) {
259 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
260 /* Try an alternate lookup using the 8 character fingerprint value, since
261 * busted-ass keyservers can't support lookups using subkeys with the full
262 * value as of now. This is why 2012 is not the year of PGP encryption. */
263 if(fpr_len > 8) {
264 const char *short_fpr = memcpy(&full_fpr[fpr_len - 8], "0x", 2);
265 _alpm_log(handle, ALPM_LOG_DEBUG,
266 "looking up key %s remotely\n", short_fpr);
267 err = gpgme_get_key(ctx, short_fpr, &key, 0);
268 if(gpg_err_code(err) == GPG_ERR_EOF) {
269 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
270 ret = 0;
272 } else {
273 ret = 0;
277 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) {
278 goto error;
281 /* should only get here if key actually exists */
282 pgpkey->data = key;
283 if(key->subkeys->fpr) {
284 pgpkey->fingerprint = key->subkeys->fpr;
285 } else if(key->subkeys->keyid) {
286 pgpkey->fingerprint = key->subkeys->keyid;
288 pgpkey->uid = key->uids->uid;
289 pgpkey->name = key->uids->name;
290 pgpkey->email = key->uids->email;
291 pgpkey->created = key->subkeys->timestamp;
292 pgpkey->expires = key->subkeys->expires;
293 pgpkey->length = key->subkeys->length;
294 pgpkey->revoked = key->subkeys->revoked;
296 switch (key->subkeys->pubkey_algo) {
297 case GPGME_PK_RSA:
298 case GPGME_PK_RSA_E:
299 case GPGME_PK_RSA_S:
300 pgpkey->pubkey_algo = 'R';
301 break;
303 case GPGME_PK_DSA:
304 pgpkey->pubkey_algo = 'D';
305 break;
307 case GPGME_PK_ELG_E:
308 case GPGME_PK_ELG:
309 case GPGME_PK_ECDSA:
310 case GPGME_PK_ECDH:
311 pgpkey->pubkey_algo = 'E';
312 break;
315 ret = 1;
317 error:
318 if(ret != 1) {
319 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err));
321 free(full_fpr);
322 gpgme_release(ctx);
323 return ret;
327 * Import a key into the local keyring.
328 * @param handle the context handle
329 * @param key the key to import, likely retrieved from #key_search
330 * @return 0 on success, -1 on error
332 static int key_import(alpm_handle_t *handle, alpm_pgpkey_t *key)
334 gpgme_error_t err;
335 gpgme_ctx_t ctx;
336 gpgme_key_t keys[2];
337 gpgme_import_result_t result;
338 int ret = -1;
340 if(_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) {
341 /* no chance of import succeeding if pubring isn't writable */
342 _alpm_log(handle, ALPM_LOG_ERROR, _("keyring is not writable\n"));
343 return -1;
346 memset(&ctx, 0, sizeof(ctx));
347 err = gpgme_new(&ctx);
348 CHECK_ERR();
350 _alpm_log(handle, ALPM_LOG_DEBUG, "importing key\n");
352 keys[0] = key->data;
353 keys[1] = NULL;
354 err = gpgme_op_import_keys(ctx, keys);
355 CHECK_ERR();
356 result = gpgme_op_import_result(ctx);
357 CHECK_ERR();
358 /* we know we tried to import exactly one key, so check for this */
359 if(result->considered != 1 || !result->imports) {
360 _alpm_log(handle, ALPM_LOG_DEBUG, "could not import key, 0 results\n");
361 ret = -1;
362 } else if(result->imports->result != GPG_ERR_NO_ERROR) {
363 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err));
364 ret = -1;
365 } else {
366 ret = 0;
369 error:
370 gpgme_release(ctx);
371 return ret;
375 * Decode a loaded signature in base64 form.
376 * @param base64_data the signature to attempt to decode
377 * @param data the decoded data; must be freed by the caller
378 * @param data_len the length of the returned data
379 * @return 0 on success, -1 on failure to properly decode
381 static int decode_signature(const char *base64_data,
382 unsigned char **data, size_t *data_len) {
383 size_t len = strlen(base64_data);
384 unsigned char *usline = (unsigned char *)base64_data;
385 /* reasonable allocation of expected length is 3/4 of encoded length */
386 size_t destlen = len * 3 / 4;
387 MALLOC(*data, destlen, goto error);
388 if(base64_decode(*data, &destlen, usline, len)) {
389 free(*data);
390 goto error;
392 *data_len = destlen;
393 return 0;
395 error:
396 *data = NULL;
397 *data_len = 0;
398 return -1;
402 * Check the PGP signature for the given file path.
403 * If base64_sig is provided, it will be used as the signature data after
404 * decoding. If base64_sig is NULL, expect a signature file next to path
405 * (e.g. "%s.sig").
407 * The return value will be 0 if nothing abnormal happened during the signature
408 * check, and -1 if an error occurred while checking signatures or if a
409 * signature could not be found; pm_errno will be set. Note that "abnormal"
410 * does not include a failed signature; the value in siglist should be checked
411 * to determine if the signature(s) are good.
412 * @param handle the context handle
413 * @param path the full path to a file
414 * @param base64_sig optional PGP signature data in base64 encoding
415 * @param siglist a pointer to storage for signature results
416 * @return 0 in normal cases, -1 if the something failed in the check process
418 int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path,
419 const char *base64_sig, alpm_siglist_t *siglist)
421 int ret = -1, sigcount;
422 gpgme_error_t err = 0;
423 gpgme_ctx_t ctx;
424 gpgme_data_t filedata, sigdata;
425 gpgme_verify_result_t verify_result;
426 gpgme_signature_t gpgsig;
427 char *sigpath = NULL;
428 unsigned char *decoded_sigdata = NULL;
429 FILE *file = NULL, *sigfile = NULL;
431 if(!path || _alpm_access(handle, NULL, path, R_OK) != 0) {
432 RET_ERR(handle, ALPM_ERR_NOT_A_FILE, -1);
435 if(!siglist) {
436 RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1);
438 siglist->count = 0;
440 if(!base64_sig) {
441 sigpath = _alpm_sigpath(handle, path);
442 if(_alpm_access(handle, NULL, sigpath, R_OK) != 0
443 || (sigfile = fopen(sigpath, "rb")) == NULL) {
444 _alpm_log(handle, ALPM_LOG_DEBUG, "sig path %s could not be opened\n",
445 sigpath);
446 handle->pm_errno = ALPM_ERR_SIG_MISSING;
447 goto error;
451 /* does the file we are verifying exist? */
452 file = fopen(path, "rb");
453 if(file == NULL) {
454 handle->pm_errno = ALPM_ERR_NOT_A_FILE;
455 goto error;
458 if(init_gpgme(handle)) {
459 /* pm_errno was set in gpgme_init() */
460 goto error;
463 _alpm_log(handle, ALPM_LOG_DEBUG, "checking signature for %s\n", path);
465 memset(&ctx, 0, sizeof(ctx));
466 memset(&sigdata, 0, sizeof(sigdata));
467 memset(&filedata, 0, sizeof(filedata));
469 err = gpgme_new(&ctx);
470 CHECK_ERR();
472 /* create our necessary data objects to verify the signature */
473 err = gpgme_data_new_from_stream(&filedata, file);
474 CHECK_ERR();
476 /* next create data object for the signature */
477 if(base64_sig) {
478 /* memory-based, we loaded it from a sync DB */
479 size_t data_len;
480 int decode_ret = decode_signature(base64_sig,
481 &decoded_sigdata, &data_len);
482 if(decode_ret) {
483 handle->pm_errno = ALPM_ERR_SIG_INVALID;
484 goto gpg_error;
486 err = gpgme_data_new_from_mem(&sigdata,
487 (char *)decoded_sigdata, data_len, 0);
488 } else {
489 /* file-based, it is on disk */
490 err = gpgme_data_new_from_stream(&sigdata, sigfile);
492 CHECK_ERR();
494 /* here's where the magic happens */
495 err = gpgme_op_verify(ctx, sigdata, filedata, NULL);
496 CHECK_ERR();
497 verify_result = gpgme_op_verify_result(ctx);
498 CHECK_ERR();
499 if(!verify_result || !verify_result->signatures) {
500 _alpm_log(handle, ALPM_LOG_DEBUG, "no signatures returned\n");
501 handle->pm_errno = ALPM_ERR_SIG_MISSING;
502 goto gpg_error;
504 for(gpgsig = verify_result->signatures, sigcount = 0;
505 gpgsig; gpgsig = gpgsig->next, sigcount++);
506 _alpm_log(handle, ALPM_LOG_DEBUG, "%d signatures returned\n", sigcount);
508 CALLOC(siglist->results, sigcount, sizeof(alpm_sigresult_t),
509 handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error);
510 siglist->count = sigcount;
512 for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig;
513 gpgsig = gpgsig->next, sigcount++) {
514 alpm_list_t *summary_list, *summary;
515 alpm_sigstatus_t status;
516 alpm_sigvalidity_t validity;
517 gpgme_key_t key;
518 alpm_sigresult_t *result;
520 _alpm_log(handle, ALPM_LOG_DEBUG, "fingerprint: %s\n", gpgsig->fpr);
521 summary_list = list_sigsum(gpgsig->summary);
522 for(summary = summary_list; summary; summary = summary->next) {
523 _alpm_log(handle, ALPM_LOG_DEBUG, "summary: %s\n", (const char *)summary->data);
525 alpm_list_free(summary_list);
526 _alpm_log(handle, ALPM_LOG_DEBUG, "status: %s\n", gpgme_strerror(gpgsig->status));
527 _alpm_log(handle, ALPM_LOG_DEBUG, "timestamp: %lu\n", gpgsig->timestamp);
528 _alpm_log(handle, ALPM_LOG_DEBUG, "exp_timestamp: %lu\n", gpgsig->exp_timestamp);
529 _alpm_log(handle, ALPM_LOG_DEBUG, "validity: %s; reason: %s\n",
530 string_validity(gpgsig->validity),
531 gpgme_strerror(gpgsig->validity_reason));
533 result = siglist->results + sigcount;
534 err = gpgme_get_key(ctx, gpgsig->fpr, &key, 0);
535 if(gpg_err_code(err) == GPG_ERR_EOF) {
536 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n");
537 err = GPG_ERR_NO_ERROR;
538 /* we dupe the fpr in this case since we have no key to point at */
539 STRDUP(result->key.fingerprint, gpgsig->fpr,
540 handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error);
541 } else {
542 CHECK_ERR();
543 if(key->uids) {
544 result->key.data = key;
545 result->key.fingerprint = key->subkeys->fpr;
546 result->key.uid = key->uids->uid;
547 result->key.name = key->uids->name;
548 result->key.email = key->uids->email;
549 result->key.created = key->subkeys->timestamp;
550 result->key.expires = key->subkeys->expires;
551 _alpm_log(handle, ALPM_LOG_DEBUG,
552 "key: %s, %s, owner_trust %s, disabled %d\n",
553 key->subkeys->fpr, key->uids->uid,
554 string_validity(key->owner_trust), key->disabled);
558 switch(gpg_err_code(gpgsig->status)) {
559 /* good cases */
560 case GPG_ERR_NO_ERROR:
561 status = ALPM_SIGSTATUS_VALID;
562 break;
563 case GPG_ERR_KEY_EXPIRED:
564 status = ALPM_SIGSTATUS_KEY_EXPIRED;
565 break;
566 /* bad cases */
567 case GPG_ERR_SIG_EXPIRED:
568 status = ALPM_SIGSTATUS_SIG_EXPIRED;
569 break;
570 case GPG_ERR_NO_PUBKEY:
571 status = ALPM_SIGSTATUS_KEY_UNKNOWN;
572 break;
573 case GPG_ERR_BAD_SIGNATURE:
574 default:
575 status = ALPM_SIGSTATUS_INVALID;
576 break;
578 /* special case: key disabled is not returned in above status code */
579 if(result->key.data && key->disabled) {
580 status = ALPM_SIGSTATUS_KEY_DISABLED;
583 switch(gpgsig->validity) {
584 case GPGME_VALIDITY_ULTIMATE:
585 case GPGME_VALIDITY_FULL:
586 validity = ALPM_SIGVALIDITY_FULL;
587 break;
588 case GPGME_VALIDITY_MARGINAL:
589 validity = ALPM_SIGVALIDITY_MARGINAL;
590 break;
591 case GPGME_VALIDITY_NEVER:
592 validity = ALPM_SIGVALIDITY_NEVER;
593 break;
594 case GPGME_VALIDITY_UNKNOWN:
595 case GPGME_VALIDITY_UNDEFINED:
596 default:
597 validity = ALPM_SIGVALIDITY_UNKNOWN;
598 break;
601 result->status = status;
602 result->validity = validity;
605 ret = 0;
607 gpg_error:
608 gpgme_data_release(sigdata);
609 gpgme_data_release(filedata);
610 gpgme_release(ctx);
612 error:
613 if(sigfile) {
614 fclose(sigfile);
616 if(file) {
617 fclose(file);
619 FREE(sigpath);
620 FREE(decoded_sigdata);
621 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) {
622 _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
623 RET_ERR(handle, ALPM_ERR_GPGME, -1);
625 return ret;
628 #else /* HAVE_LIBGPGME */
629 static int key_in_keychain(alpm_handle_t UNUSED *handle, const char UNUSED *fpr)
631 return -1;
633 int _alpm_gpgme_checksig(alpm_handle_t UNUSED *handle, const char UNUSED *path,
634 const char UNUSED *base64_sig, alpm_siglist_t UNUSED *siglist)
636 return -1;
638 #endif /* HAVE_LIBGPGME */
641 * Form a signature path given a file path.
642 * Caller must free the result.
643 * @param handle the context handle
644 * @param path the full path to a file
645 * @return the path with '.sig' appended, NULL on errors
647 char *_alpm_sigpath(alpm_handle_t *handle, const char *path)
649 char *sigpath;
650 size_t len;
652 if(!path) {
653 return NULL;
655 len = strlen(path) + 5;
656 CALLOC(sigpath, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, NULL));
657 sprintf(sigpath, "%s.sig", path);
658 return sigpath;
662 * Helper for checking the PGP signature for the given file path.
663 * This wraps #_alpm_gpgme_checksig in a slightly friendlier manner to simplify
664 * handling of optional signatures and marginal/unknown trust levels and
665 * handling the correct error code return values.
666 * @param handle the context handle
667 * @param path the full path to a file
668 * @param base64_sig optional PGP signature data in base64 encoding
669 * @param optional whether signatures are optional (e.g., missing OK)
670 * @param marginal whether signatures with marginal trust are acceptable
671 * @param unknown whether signatures with unknown trust are acceptable
672 * @param sigdata a pointer to storage for signature results
673 * @return 0 on success, -1 on error (consult pm_errno or sigdata)
675 int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path,
676 const char *base64_sig, int optional, int marginal, int unknown,
677 alpm_siglist_t **sigdata)
679 alpm_siglist_t *siglist;
680 int ret;
682 CALLOC(siglist, 1, sizeof(alpm_siglist_t),
683 RET_ERR(handle, ALPM_ERR_MEMORY, -1));
685 ret = _alpm_gpgme_checksig(handle, path, base64_sig, siglist);
686 if(ret && handle->pm_errno == ALPM_ERR_SIG_MISSING) {
687 if(optional) {
688 _alpm_log(handle, ALPM_LOG_DEBUG, "missing optional signature\n");
689 handle->pm_errno = 0;
690 ret = 0;
691 } else {
692 _alpm_log(handle, ALPM_LOG_DEBUG, "missing required signature\n");
693 /* ret will already be -1 */
695 } else if(ret) {
696 _alpm_log(handle, ALPM_LOG_DEBUG, "signature check failed\n");
697 /* ret will already be -1 */
698 } else {
699 size_t num;
700 for(num = 0; !ret && num < siglist->count; num++) {
701 switch(siglist->results[num].status) {
702 case ALPM_SIGSTATUS_VALID:
703 case ALPM_SIGSTATUS_KEY_EXPIRED:
704 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n");
705 switch(siglist->results[num].validity) {
706 case ALPM_SIGVALIDITY_FULL:
707 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is fully trusted\n");
708 break;
709 case ALPM_SIGVALIDITY_MARGINAL:
710 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is marginal trust\n");
711 if(!marginal) {
712 ret = -1;
714 break;
715 case ALPM_SIGVALIDITY_UNKNOWN:
716 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is unknown trust\n");
717 if(!unknown) {
718 ret = -1;
720 break;
721 case ALPM_SIGVALIDITY_NEVER:
722 _alpm_log(handle, ALPM_LOG_DEBUG, "signature should never be trusted\n");
723 ret = -1;
724 break;
726 break;
727 case ALPM_SIGSTATUS_SIG_EXPIRED:
728 case ALPM_SIGSTATUS_KEY_UNKNOWN:
729 case ALPM_SIGSTATUS_KEY_DISABLED:
730 case ALPM_SIGSTATUS_INVALID:
731 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n");
732 ret = -1;
733 break;
738 if(sigdata) {
739 *sigdata = siglist;
740 } else {
741 alpm_siglist_cleanup(siglist);
742 free(siglist);
745 return ret;
749 * Examine a signature result list and take any appropriate or necessary
750 * actions. This may include asking the user to import a key or simply printing
751 * helpful failure messages so the user can take action out of band.
752 * @param handle the context handle
753 * @param identifier a friendly name for the signed resource; usually a
754 * database or package name
755 * @param siglist a pointer to storage for signature results
756 * @param optional whether signatures are optional (e.g., missing OK)
757 * @param marginal whether signatures with marginal trust are acceptable
758 * @param unknown whether signatures with unknown trust are acceptable
759 * @return 0 if all signatures are OK, -1 on errors, 1 if we should retry the
760 * validation process
762 int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier,
763 alpm_siglist_t *siglist, int optional, int marginal, int unknown)
765 size_t i;
766 int retry = 0;
768 if(!optional && siglist->count == 0) {
769 _alpm_log(handle, ALPM_LOG_ERROR,
770 _("%s: missing required signature\n"), identifier);
773 for(i = 0; i < siglist->count; i++) {
774 alpm_sigresult_t *result = siglist->results + i;
775 const char *name = result->key.uid ? result->key.uid : result->key.fingerprint;
776 switch(result->status) {
777 case ALPM_SIGSTATUS_VALID:
778 case ALPM_SIGSTATUS_KEY_EXPIRED:
779 switch(result->validity) {
780 case ALPM_SIGVALIDITY_FULL:
781 break;
782 case ALPM_SIGVALIDITY_MARGINAL:
783 if(!marginal) {
784 _alpm_log(handle, ALPM_LOG_ERROR,
785 _("%s: signature from \"%s\" is marginal trust\n"),
786 identifier, name);
787 /* QUESTION(handle, ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */
789 break;
790 case ALPM_SIGVALIDITY_UNKNOWN:
791 if(!unknown) {
792 _alpm_log(handle, ALPM_LOG_ERROR,
793 _("%s: signature from \"%s\" is unknown trust\n"),
794 identifier, name);
795 /* QUESTION(handle, ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */
797 break;
798 case ALPM_SIGVALIDITY_NEVER:
799 _alpm_log(handle, ALPM_LOG_ERROR,
800 _("%s: signature from \"%s\" should never be trusted\n"),
801 identifier, name);
802 break;
804 break;
805 case ALPM_SIGSTATUS_KEY_UNKNOWN:
806 /* ensure this key is still actually unknown; we may have imported it
807 * on an earlier call to this function. */
808 if(key_in_keychain(handle, result->key.fingerprint) == 1) {
809 break;
811 _alpm_log(handle, ALPM_LOG_ERROR,
812 _("%s: key \"%s\" is unknown\n"), identifier, name);
813 #ifdef HAVE_LIBGPGME
815 int answer = 0;
816 alpm_pgpkey_t fetch_key;
817 memset(&fetch_key, 0, sizeof(fetch_key));
819 if(key_search(handle, result->key.fingerprint, &fetch_key) == 1) {
820 _alpm_log(handle, ALPM_LOG_DEBUG,
821 "unknown key, found %s on keyserver\n", fetch_key.uid);
822 if(!_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) {
823 QUESTION(handle, ALPM_QUESTION_IMPORT_KEY,
824 &fetch_key, NULL, NULL, &answer);
825 if(answer) {
826 if(key_import(handle, &fetch_key) == 0) {
827 retry = 1;
828 } else {
829 _alpm_log(handle, ALPM_LOG_ERROR,
830 _("key \"%s\" could not be imported\n"), fetch_key.uid);
833 } else {
834 /* keyring directory was not writable, so we don't even try */
835 _alpm_log(handle, ALPM_LOG_WARNING,
836 _("key %s, \"%s\" found on keyserver, keyring is not writable\n"),
837 fetch_key.fingerprint, fetch_key.uid);
839 } else {
840 _alpm_log(handle, ALPM_LOG_ERROR,
841 _("key \"%s\" could not be looked up remotely\n"), name);
843 gpgme_key_unref(fetch_key.data);
845 #endif
846 break;
847 case ALPM_SIGSTATUS_KEY_DISABLED:
848 _alpm_log(handle, ALPM_LOG_ERROR,
849 _("%s: key \"%s\" is disabled\n"), identifier, name);
850 break;
851 case ALPM_SIGSTATUS_SIG_EXPIRED:
852 _alpm_log(handle, ALPM_LOG_ERROR,
853 _("%s: signature from \"%s\" is expired\n"), identifier, name);
854 break;
855 case ALPM_SIGSTATUS_INVALID:
856 _alpm_log(handle, ALPM_LOG_ERROR,
857 _("%s: signature from \"%s\" is invalid\n"),
858 identifier, name);
859 break;
863 return retry;
867 * Check the PGP signature for the given package file.
868 * @param pkg the package to check
869 * @param siglist a pointer to storage for signature results
870 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
872 int SYMEXPORT alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg,
873 alpm_siglist_t *siglist)
875 ASSERT(pkg != NULL, return -1);
876 ASSERT(siglist != NULL, RET_ERR(pkg->handle, ALPM_ERR_WRONG_ARGS, -1));
877 pkg->handle->pm_errno = 0;
879 return _alpm_gpgme_checksig(pkg->handle, pkg->filename,
880 pkg->base64_sig, siglist);
884 * Check the PGP signature for the given database.
885 * @param db the database to check
886 * @param siglist a pointer to storage for signature results
887 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
889 int SYMEXPORT alpm_db_check_pgp_signature(alpm_db_t *db,
890 alpm_siglist_t *siglist)
892 ASSERT(db != NULL, return -1);
893 ASSERT(siglist != NULL, RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, -1));
894 db->handle->pm_errno = 0;
896 return _alpm_gpgme_checksig(db->handle, _alpm_db_path(db), NULL, siglist);
900 * Clean up and free a signature result list.
901 * Note that this does not free the siglist object itself in case that
902 * was allocated on the stack; this is the responsibility of the caller.
903 * @param siglist a pointer to storage for signature results
904 * @return 0 on success, -1 on error
906 int SYMEXPORT alpm_siglist_cleanup(alpm_siglist_t *siglist)
908 ASSERT(siglist != NULL, return -1);
909 size_t num;
910 for(num = 0; num < siglist->count; num++) {
911 alpm_sigresult_t *result = siglist->results + num;
912 if(result->key.data) {
913 #if HAVE_LIBGPGME
914 gpgme_key_unref(result->key.data);
915 #endif
916 } else {
917 free(result->key.fingerprint);
920 if(siglist->count) {
921 free(siglist->results);
923 siglist->results = NULL;
924 siglist->count = 0;
925 return 0;
928 /* vim: set ts=2 sw=2 noet: */