Added gpg verification options per repo to the config file.
[pacman-ng.git] / lib / libalpm / signing.c
blob08e9b297162927f0844b10abe2409f601c05e1fd
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>
25 #include <locale.h> /* setlocale() */
26 #include <gpgme.h>
28 /* libalpm */
29 #include "signing.h"
30 #include "package.h"
31 #include "util.h"
32 #include "log.h"
33 #include "alpm.h"
35 #define CHECK_ERR(void) do { \
36 if(err != GPG_ERR_NO_ERROR) { goto error; } \
37 } while(0)
39 static int gpgme_init(void)
41 static int init = 0;
42 const char *version;
43 gpgme_error_t err;
44 gpgme_engine_info_t enginfo;
46 ALPM_LOG_FUNC;
48 if(init) {
49 /* we already successfully initialized the library */
50 return 0;
53 if(!alpm_option_get_signaturedir()) {
54 RET_ERR(PM_ERR_SIG_MISSINGDIR, 1);
57 /* calling gpgme_check_version() returns the current version and runs
58 * some internal library setup code */
59 version = gpgme_check_version(NULL);
60 _alpm_log(PM_LOG_DEBUG, "GPGME version: %s\n", version);
61 gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL));
62 #ifdef LC_MESSAGES
63 gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL));
64 #endif
65 /* NOTE:
66 * The GPGME library installs a SIGPIPE signal handler automatically if
67 * the default signal hander is in use. The only time we set a handler
68 * for SIGPIPE is in dload.c, and we reset it when we are done. Given that
69 * we do this, we can let GPGME do its automagic. However, if we install
70 * a library-wide SIGPIPE handler, we will have to be careful.
73 /* check for OpenPGP support (should be a no-brainer, but be safe) */
74 err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP);
75 CHECK_ERR();
77 /* set and check engine information */
78 err = gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL,
79 alpm_option_get_signaturedir());
80 CHECK_ERR();
81 err = gpgme_get_engine_info(&enginfo);
82 CHECK_ERR();
83 _alpm_log(PM_LOG_DEBUG, "GPGME engine info: file=%s, home=%s\n",
84 enginfo->file_name, enginfo->home_dir);
86 init = 1;
87 return 0;
89 error:
90 _alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
91 RET_ERR(PM_ERR_GPGME, 1);
94 /**
95 * Check the PGP package signature for the given package file.
96 * @param pkgpath the full path to a package file
97 * @param sig PGP signature data in raw form (already decoded)
98 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured)
100 int _alpm_gpgme_checksig(const char *pkgpath, const pmpgpsig_t *sig)
102 int ret = 0;
103 gpgme_error_t err;
104 gpgme_ctx_t ctx;
105 gpgme_data_t pkgdata, sigdata;
106 gpgme_verify_result_t result;
107 gpgme_signature_t gpgsig;
108 FILE *pkgfile = NULL, *sigfile = NULL;
110 ALPM_LOG_FUNC;
112 if(!sig || !sig->rawdata) {
113 RET_ERR(PM_ERR_SIG_UNKNOWN, -1);
115 if(!pkgpath || access(pkgpath, R_OK) != 0) {
116 RET_ERR(PM_ERR_PKG_NOT_FOUND, -1);
118 if(gpgme_init()) {
119 /* pm_errno was set in gpgme_init() */
120 return -1;
123 _alpm_log(PM_LOG_DEBUG, "checking package signature for %s\n", pkgpath);
125 memset(&ctx, 0, sizeof(ctx));
126 memset(&sigdata, 0, sizeof(sigdata));
127 memset(&pkgdata, 0, sizeof(pkgdata));
129 err = gpgme_new(&ctx);
130 CHECK_ERR();
132 /* create our necessary data objects to verify the signature */
133 /* first the package itself */
134 pkgfile = fopen(pkgpath, "rb");
135 if(pkgfile == NULL) {
136 pm_errno = PM_ERR_PKG_OPEN;
137 ret = -1;
138 goto error;
140 err = gpgme_data_new_from_stream(&pkgdata, pkgfile);
141 CHECK_ERR();
143 /* next create data object for the signature */
144 err = gpgme_data_new_from_mem(&sigdata, (char*)sig->rawdata, sig->rawlen, 0);
145 CHECK_ERR();
147 /* here's where the magic happens */
148 err = gpgme_op_verify(ctx, sigdata, pkgdata, NULL);
149 CHECK_ERR();
150 result = gpgme_op_verify_result(ctx);
151 gpgsig = result->signatures;
152 if (!gpgsig || gpgsig->next) {
153 _alpm_log(PM_LOG_ERROR, _("Unexpected number of signatures\n"));
154 ret = -1;
155 goto error;
157 fprintf(stdout, "\nsummary=%x\n", gpgsig->summary);
158 fprintf(stdout, "fpr=%s\n", gpgsig->fpr);
159 fprintf(stdout, "status=%d\n", gpgsig->status);
160 fprintf(stdout, "timestamp=%lu\n", gpgsig->timestamp);
161 fprintf(stdout, "wrong_key_usage=%u\n", gpgsig->wrong_key_usage);
162 fprintf(stdout, "pka_trust=%u\n", gpgsig->pka_trust);
163 fprintf(stdout, "chain_model=%u\n", gpgsig->chain_model);
164 fprintf(stdout, "validity=%d\n", gpgsig->validity);
165 fprintf(stdout, "validity_reason=%d\n", gpgsig->validity_reason);
166 fprintf(stdout, "key=%d\n", gpgsig->pubkey_algo);
167 fprintf(stdout, "hash=%d\n", gpgsig->hash_algo);
169 if(gpgsig->summary & GPGME_SIGSUM_VALID) {
170 /* good signature, continue */
171 _alpm_log(PM_LOG_DEBUG, _("Package %s has a valid signature.\n"),
172 pkgpath);
173 } else if(gpgsig->summary & GPGME_SIGSUM_GREEN) {
174 /* 'green' signature, not sure what to do here */
175 _alpm_log(PM_LOG_WARNING, _("Package %s has a green signature.\n"),
176 pkgpath);
177 } else if(gpgsig->summary & GPGME_SIGSUM_KEY_MISSING) {
178 pm_errno = PM_ERR_SIG_UNKNOWN;
179 _alpm_log(PM_LOG_WARNING, _("Package %s has a signature from an unknown key.\n"),
180 pkgpath);
181 ret = -1;
182 } else {
183 /* we'll capture everything else here */
184 pm_errno = PM_ERR_SIG_INVALID;
185 _alpm_log(PM_LOG_ERROR, _("Package %s has an invalid signature.\n"),
186 pkgpath);
187 ret = 1;
190 error:
191 gpgme_data_release(sigdata);
192 gpgme_data_release(pkgdata);
193 gpgme_release(ctx);
194 if(sigfile) {
195 fclose(sigfile);
197 if(pkgfile) {
198 fclose(pkgfile);
200 if(err != GPG_ERR_NO_ERROR) {
201 _alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
202 RET_ERR(PM_ERR_GPGME, -1);
204 return ret;
208 * Check the PGP package signature for the given package file.
209 * @param pkg the package to check
210 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured)
212 int SYMEXPORT alpm_pkg_check_pgp_signature(pmpkg_t *pkg)
214 ALPM_LOG_FUNC;
215 ASSERT(pkg != NULL, return 0);
217 return _alpm_gpgme_checksig(alpm_pkg_get_filename(pkg),
218 alpm_pkg_get_pgpsig(pkg));
221 /* vim: set ts=2 sw=2 noet: */