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/>.
27 #include <locale.h> /* setlocale() */
40 #define CHECK_ERR(void) do { \
41 if(err != GPG_ERR_NO_ERROR) { goto error; } \
44 static const char *gpgme_string_validity(gpgme_validity_t validity
)
47 case GPGME_VALIDITY_UNKNOWN
:
49 case GPGME_VALIDITY_UNDEFINED
:
51 case GPGME_VALIDITY_NEVER
:
53 case GPGME_VALIDITY_MARGINAL
:
55 case GPGME_VALIDITY_FULL
:
57 case GPGME_VALIDITY_ULTIMATE
:
63 static alpm_list_t
*sigsum_test_bit(gpgme_sigsum_t sigsum
, alpm_list_t
*summary
,
64 gpgme_sigsum_t bit
, const char *value
)
67 summary
= alpm_list_add(summary
, (void *)value
);
72 static alpm_list_t
*gpgme_list_sigsum(gpgme_sigsum_t sigsum
)
74 alpm_list_t
*summary
= NULL
;
75 /* The docs say this can be a bitmask...not sure I believe it, but we'll code
76 * for it anyway and show all possible flags in the returned string. */
78 /* The signature is fully valid. */
79 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_VALID
, "valid");
80 /* The signature is good. */
81 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_GREEN
, "green");
82 /* The signature is bad. */
83 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_RED
, "red");
84 /* One key has been revoked. */
85 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_KEY_REVOKED
, "key revoked");
86 /* One key has expired. */
87 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_KEY_EXPIRED
, "key expired");
88 /* The signature has expired. */
89 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_SIG_EXPIRED
, "sig expired");
90 /* Can't verify: key missing. */
91 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_KEY_MISSING
, "key missing");
92 /* CRL not available. */
93 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_CRL_MISSING
, "crl missing");
94 /* Available CRL is too old. */
95 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_CRL_TOO_OLD
, "crl too old");
96 /* A policy was not met. */
97 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_BAD_POLICY
, "bad policy");
98 /* A system error occured. */
99 summary
= sigsum_test_bit(sigsum
, summary
, GPGME_SIGSUM_SYS_ERROR
, "sys error");
102 summary
= alpm_list_add(summary
, (void *)"(empty)");
107 static int gpgme_init(void)
112 gpgme_engine_info_t enginfo
;
115 /* we already successfully initialized the library */
119 if(!alpm_option_get_signaturedir()) {
120 RET_ERR(PM_ERR_SIG_MISSINGDIR
, 1);
123 /* calling gpgme_check_version() returns the current version and runs
124 * some internal library setup code */
125 version
= gpgme_check_version(NULL
);
126 _alpm_log(PM_LOG_DEBUG
, "GPGME version: %s\n", version
);
127 gpgme_set_locale(NULL
, LC_CTYPE
, setlocale(LC_CTYPE
, NULL
));
129 gpgme_set_locale(NULL
, LC_MESSAGES
, setlocale(LC_MESSAGES
, NULL
));
132 * The GPGME library installs a SIGPIPE signal handler automatically if
133 * the default signal hander is in use. The only time we set a handler
134 * for SIGPIPE is in dload.c, and we reset it when we are done. Given that
135 * we do this, we can let GPGME do its automagic. However, if we install
136 * a library-wide SIGPIPE handler, we will have to be careful.
139 /* check for OpenPGP support (should be a no-brainer, but be safe) */
140 err
= gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP
);
143 /* set and check engine information */
144 err
= gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP
, NULL
,
145 alpm_option_get_signaturedir());
147 err
= gpgme_get_engine_info(&enginfo
);
149 _alpm_log(PM_LOG_DEBUG
, "GPGME engine info: file=%s, home=%s\n",
150 enginfo
->file_name
, enginfo
->home_dir
);
156 _alpm_log(PM_LOG_ERROR
, _("GPGME error: %s\n"), gpgme_strerror(err
));
157 RET_ERR(PM_ERR_GPGME
, 1);
161 * Decode a loaded signature in base64 form.
162 * @param base64_data the signature to attempt to decode
163 * @param data the decoded data; must be freed by the caller
164 * @param data_len the length of the returned data
165 * @return 0 on success, 1 on failure to properly decode
167 static int decode_signature(const char *base64_data
,
168 unsigned char **data
, int *data_len
) {
169 unsigned char *usline
;
172 len
= strlen(base64_data
);
173 usline
= (unsigned char *)base64_data
;
174 int ret
, destlen
= 0;
175 /* get the necessary size for the buffer by passing 0 */
176 ret
= base64_decode(NULL
, &destlen
, usline
, len
);
177 if(ret
!= 0 && ret
!= POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL
) {
180 /* alloc our memory and repeat the call to decode */
181 MALLOC(*data
, (size_t)destlen
, goto error
);
182 ret
= base64_decode(*data
, &destlen
, usline
, len
);
196 * Check the PGP signature for the given file.
197 * @param path the full path to a file
198 * @param base64_sig PGP signature data in base64 encoding; if NULL, expect a
199 * signature file next to 'path'
200 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occured)
202 int _alpm_gpgme_checksig(const char *path
, const char *base64_sig
)
207 gpgme_data_t filedata
, sigdata
;
208 gpgme_verify_result_t result
;
209 gpgme_signature_t gpgsig
;
210 char *sigpath
= NULL
;
211 unsigned char *decoded_sigdata
= NULL
;
212 FILE *file
= NULL
, *sigfile
= NULL
;
214 if(!path
|| access(path
, R_OK
) != 0) {
215 RET_ERR(PM_ERR_NOT_A_FILE
, -1);
219 size_t len
= strlen(path
) + 5;
220 CALLOC(sigpath
, len
, sizeof(char), RET_ERR(PM_ERR_MEMORY
, -1));
221 snprintf(sigpath
, len
, "%s.sig", path
);
223 if(!access(sigpath
, R_OK
) == 0) {
225 RET_ERR(PM_ERR_SIG_UNKNOWN
, -1);
230 /* pm_errno was set in gpgme_init() */
234 _alpm_log(PM_LOG_DEBUG
, "checking signature for %s\n", path
);
236 memset(&ctx
, 0, sizeof(ctx
));
237 memset(&sigdata
, 0, sizeof(sigdata
));
238 memset(&filedata
, 0, sizeof(filedata
));
240 err
= gpgme_new(&ctx
);
243 /* create our necessary data objects to verify the signature */
244 file
= fopen(path
, "rb");
246 pm_errno
= PM_ERR_NOT_A_FILE
;
250 err
= gpgme_data_new_from_stream(&filedata
, file
);
253 /* next create data object for the signature */
255 /* memory-based, we loaded it from a sync DB */
257 int decode_ret
= decode_signature(base64_sig
,
258 &decoded_sigdata
, &data_len
);
263 err
= gpgme_data_new_from_mem(&sigdata
,
264 (char *)decoded_sigdata
, data_len
, 0);
266 /* file-based, it is on disk */
267 sigfile
= fopen(sigpath
, "rb");
268 if(sigfile
== NULL
) {
269 pm_errno
= PM_ERR_NOT_A_FILE
;
273 err
= gpgme_data_new_from_stream(&sigdata
, sigfile
);
277 /* here's where the magic happens */
278 err
= gpgme_op_verify(ctx
, sigdata
, filedata
, NULL
);
280 result
= gpgme_op_verify_result(ctx
);
281 gpgsig
= result
->signatures
;
282 if(!gpgsig
|| gpgsig
->next
) {
286 gpgsig
= gpgsig
->next
;
288 _alpm_log(PM_LOG_ERROR
, _("Unexpected number of signatures (%d)\n"),
295 alpm_list_t
*summary_list
, *summary
;
297 _alpm_log(PM_LOG_DEBUG
, "fingerprint: %s\n", gpgsig
->fpr
);
298 summary_list
= gpgme_list_sigsum(gpgsig
->summary
);
299 for(summary
= summary_list
; summary
; summary
= summary
->next
) {
300 _alpm_log(PM_LOG_DEBUG
, "summary: %s\n", (const char *)summary
->data
);
302 alpm_list_free(summary_list
);
303 _alpm_log(PM_LOG_DEBUG
, "status: %s\n", gpgme_strerror(gpgsig
->status
));
304 _alpm_log(PM_LOG_DEBUG
, "timestamp: %lu\n", gpgsig
->timestamp
);
305 _alpm_log(PM_LOG_DEBUG
, "exp_timestamp: %lu\n", gpgsig
->exp_timestamp
);
306 _alpm_log(PM_LOG_DEBUG
, "validity: %s\n",
307 gpgme_string_validity(gpgsig
->validity
));
308 _alpm_log(PM_LOG_DEBUG
, "validity_reason: %s\n",
309 gpgme_strerror(gpgsig
->validity_reason
));
310 _alpm_log(PM_LOG_DEBUG
, "pubkey algo: %s\n",
311 gpgme_pubkey_algo_name(gpgsig
->pubkey_algo
));
312 _alpm_log(PM_LOG_DEBUG
, "hash algo: %s\n",
313 gpgme_hash_algo_name(gpgsig
->hash_algo
));
316 if(gpgsig
->summary
& GPGME_SIGSUM_VALID
) {
317 /* good signature, continue */
318 _alpm_log(PM_LOG_DEBUG
, _("File %s has a valid signature.\n"),
320 } else if(gpgsig
->summary
& GPGME_SIGSUM_GREEN
) {
321 /* 'green' signature, not sure what to do here */
322 _alpm_log(PM_LOG_WARNING
, _("File %s has a green signature.\n"),
324 } else if(gpgsig
->summary
& GPGME_SIGSUM_KEY_MISSING
) {
325 pm_errno
= PM_ERR_SIG_UNKNOWN
;
326 _alpm_log(PM_LOG_WARNING
, _("File %s has a signature from an unknown key.\n"),
330 /* we'll capture everything else here */
331 pm_errno
= PM_ERR_SIG_INVALID
;
332 _alpm_log(PM_LOG_ERROR
, _("File %s has an invalid signature.\n"),
338 gpgme_data_release(sigdata
);
339 gpgme_data_release(filedata
);
348 FREE(decoded_sigdata
);
349 if(err
!= GPG_ERR_NO_ERROR
) {
350 _alpm_log(PM_LOG_ERROR
, _("GPGME error: %s\n"), gpgme_strerror(err
));
351 RET_ERR(PM_ERR_GPGME
, -1);
356 int _alpm_gpgme_checksig(const char *path
, const char *base64_sig
)
363 * Determines the necessity of checking for a valid PGP signature
364 * @param db the sync database to query
366 * @return signature verification level
368 pgp_verify_t
_alpm_db_get_sigverify_level(pmdb_t
*db
)
370 ASSERT(db
!= NULL
, RET_ERR(PM_ERR_DB_NULL
, PM_PGP_VERIFY_UNKNOWN
));
372 if(db
->pgp_verify
!= PM_PGP_VERIFY_UNKNOWN
) {
373 return db
->pgp_verify
;
375 return alpm_option_get_default_sigverify();
380 * Check the PGP signature for the given package file.
381 * @param pkg the package to check
382 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
384 int SYMEXPORT
alpm_pkg_check_pgp_signature(pmpkg_t
*pkg
)
386 ASSERT(pkg
!= NULL
, return 0);
388 return _alpm_gpgme_checksig(alpm_pkg_get_filename(pkg
), pkg
->base64_sig
);
392 * Check the PGP signature for the given database.
393 * @param db the database to check
394 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred)
396 int SYMEXPORT
alpm_db_check_pgp_signature(pmdb_t
*db
)
398 ASSERT(db
!= NULL
, return 0);
400 return _alpm_gpgme_checksig(_alpm_db_path(db
), NULL
);
403 /* vim: set ts=2 sw=2 noet: */