Rename gpgsig struct fields for clarity
[pacman-ng.git] / lib / libalpm / signing.c
blob9d4fd0761fbfed83c4292f9ddc464fdf4233459e
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 file.
96 * @param path the full path to a 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 *path, const pmpgpsig_t *sig)
102 int ret = 0;
103 gpgme_error_t err;
104 gpgme_ctx_t ctx;
105 gpgme_data_t filedata, sigdata;
106 gpgme_verify_result_t result;
107 gpgme_signature_t gpgsig;
108 FILE *file = NULL, *sigfile = NULL;
110 ALPM_LOG_FUNC;
112 if(!sig || !sig->data) {
113 RET_ERR(PM_ERR_SIG_UNKNOWN, -1);
115 if(!path || access(path, R_OK) != 0) {
116 RET_ERR(PM_ERR_NOT_A_FILE, -1);
118 if(gpgme_init()) {
119 /* pm_errno was set in gpgme_init() */
120 return -1;
123 _alpm_log(PM_LOG_DEBUG, "checking signature for %s\n", path);
125 memset(&ctx, 0, sizeof(ctx));
126 memset(&sigdata, 0, sizeof(sigdata));
127 memset(&filedata, 0, sizeof(filedata));
129 err = gpgme_new(&ctx);
130 CHECK_ERR();
132 /* create our necessary data objects to verify the signature */
133 file = fopen(path, "rb");
134 if(file == NULL) {
135 pm_errno = PM_ERR_NOT_A_FILE;
136 ret = -1;
137 goto error;
139 err = gpgme_data_new_from_stream(&filedata, file);
140 CHECK_ERR();
142 /* next create data object for the signature */
143 err = gpgme_data_new_from_mem(&sigdata, (char *)sig->data, sig->len, 0);
144 CHECK_ERR();
146 /* here's where the magic happens */
147 err = gpgme_op_verify(ctx, sigdata, filedata, NULL);
148 CHECK_ERR();
149 result = gpgme_op_verify_result(ctx);
150 gpgsig = result->signatures;
151 if(!gpgsig || gpgsig->next) {
152 _alpm_log(PM_LOG_ERROR, _("Unexpected number of signatures\n"));
153 ret = -1;
154 goto error;
156 _alpm_log(PM_LOG_DEBUG, "summary=%x\n", gpgsig->summary);
157 _alpm_log(PM_LOG_DEBUG, "fpr=%s\n", gpgsig->fpr);
158 _alpm_log(PM_LOG_DEBUG, "status=%d\n", gpgsig->status);
159 _alpm_log(PM_LOG_DEBUG, "timestamp=%lu\n", gpgsig->timestamp);
160 _alpm_log(PM_LOG_DEBUG, "wrong_key_usage=%u\n", gpgsig->wrong_key_usage);
161 _alpm_log(PM_LOG_DEBUG, "pka_trust=%u\n", gpgsig->pka_trust);
162 _alpm_log(PM_LOG_DEBUG, "chain_model=%u\n", gpgsig->chain_model);
163 _alpm_log(PM_LOG_DEBUG, "validity=%d\n", gpgsig->validity);
164 _alpm_log(PM_LOG_DEBUG, "validity_reason=%d\n", gpgsig->validity_reason);
165 _alpm_log(PM_LOG_DEBUG, "key=%d\n", gpgsig->pubkey_algo);
166 _alpm_log(PM_LOG_DEBUG, "hash=%d\n", gpgsig->hash_algo);
168 if(gpgsig->summary & GPGME_SIGSUM_VALID) {
169 /* good signature, continue */
170 _alpm_log(PM_LOG_DEBUG, _("File %s has a valid signature.\n"),
171 path);
172 } else if(gpgsig->summary & GPGME_SIGSUM_GREEN) {
173 /* 'green' signature, not sure what to do here */
174 _alpm_log(PM_LOG_WARNING, _("File %s has a green signature.\n"),
175 path);
176 } else if(gpgsig->summary & GPGME_SIGSUM_KEY_MISSING) {
177 pm_errno = PM_ERR_SIG_UNKNOWN;
178 _alpm_log(PM_LOG_WARNING, _("File %s has a signature from an unknown key.\n"),
179 path);
180 ret = -1;
181 } else {
182 /* we'll capture everything else here */
183 pm_errno = PM_ERR_SIG_INVALID;
184 _alpm_log(PM_LOG_ERROR, _("File %s has an invalid signature.\n"),
185 path);
186 ret = 1;
189 error:
190 gpgme_data_release(sigdata);
191 gpgme_data_release(filedata);
192 gpgme_release(ctx);
193 if(sigfile) {
194 fclose(sigfile);
196 if(file) {
197 fclose(file);
199 if(err != GPG_ERR_NO_ERROR) {
200 _alpm_log(PM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err));
201 RET_ERR(PM_ERR_GPGME, -1);
203 return ret;
207 * Load the signature from the given path into the provided struct.
208 * @param sigfile the signature to attempt to load
209 * @param pgpsig the struct to place the data in
211 * @return 0 on success, 1 on file not found, -1 on error
213 int _alpm_load_signature(const char *file, pmpgpsig_t *pgpsig) {
214 struct stat st;
215 char *sigfile;
216 int ret = -1;
218 /* look around for a PGP signature file; load if available */
219 MALLOC(sigfile, strlen(file) + 5, RET_ERR(PM_ERR_MEMORY, -1));
220 sprintf(sigfile, "%s.sig", file);
222 if(access(sigfile, R_OK) == 0 && stat(sigfile, &st) == 0) {
223 FILE *f;
224 size_t bytes_read;
226 if(st.st_size > 4096 || (f = fopen(sigfile, "rb")) == NULL) {
227 free(sigfile);
228 return ret;
230 CALLOC(pgpsig->data, st.st_size, sizeof(unsigned char),
231 RET_ERR(PM_ERR_MEMORY, -1));
232 bytes_read = fread(pgpsig->data, sizeof(char), st.st_size, f);
233 if(bytes_read == (size_t)st.st_size) {
234 pgpsig->len = bytes_read;
235 _alpm_log(PM_LOG_DEBUG, "loaded gpg signature file, location %s\n",
236 sigfile);
237 ret = 0;
238 } else {
239 _alpm_log(PM_LOG_WARNING, _("Failed reading PGP signature file %s"),
240 sigfile);
241 FREE(pgpsig->data);
244 fclose(f);
245 } else {
246 _alpm_log(PM_LOG_DEBUG, "signature file %s not found\n", sigfile);
247 /* not fatal...we return a different error code here */
248 ret = 1;
251 free(sigfile);
252 return ret;
256 * Determines the necessity of checking for a valid PGP signature
257 * @param db the sync database to query
259 * @return signature verification level
261 pgp_verify_t _alpm_db_get_sigverify_level(pmdb_t *db)
263 ALPM_LOG_FUNC;
264 ASSERT(db != NULL, RET_ERR(PM_ERR_DB_NULL, PM_PGP_VERIFY_UNKNOWN));
266 if(db->pgp_verify != PM_PGP_VERIFY_UNKNOWN) {
267 return db->pgp_verify;
268 } else {
269 return alpm_option_get_default_sigverify();
274 * Check the PGP package signature for the given package file.
275 * @param pkg the package to check
276 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
278 int SYMEXPORT alpm_pkg_check_pgp_signature(pmpkg_t *pkg)
280 ALPM_LOG_FUNC;
281 ASSERT(pkg != NULL, return 0);
283 return _alpm_gpgme_checksig(alpm_pkg_get_filename(pkg),
284 alpm_pkg_get_pgpsig(pkg));
288 * Check the PGP package signature for the given database.
289 * @param db the database to check
290 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
292 int SYMEXPORT alpm_db_check_pgp_signature(pmdb_t *db)
294 ALPM_LOG_FUNC;
295 ASSERT(db != NULL, return 0);
297 return _alpm_gpgme_checksig(_alpm_db_path(db),
298 _alpm_db_pgpsig(db));
301 /* vim: set ts=2 sw=2 noet: */