libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * signing.c 00003 * 00004 * Copyright (c) 2008-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 #include <stdlib.h> 00021 #include <stdio.h> 00022 #include <string.h> 00023 00024 #if HAVE_LIBGPGME 00025 #include <locale.h> /* setlocale() */ 00026 #include <gpgme.h> 00027 #include "base64.h" 00028 #endif 00029 00030 /* libalpm */ 00031 #include "signing.h" 00032 #include "package.h" 00033 #include "util.h" 00034 #include "log.h" 00035 #include "alpm.h" 00036 #include "handle.h" 00037 00038 #if HAVE_LIBGPGME 00039 #define CHECK_ERR(void) do { \ 00040 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) { goto error; } \ 00041 } while(0) 00042 00043 /** 00044 * Return a statically allocated validity string based on the GPGME validity 00045 * code. This is mainly for debug purposes and is not translated. 00046 * @param validity a validity code returned by GPGME 00047 * @return a string such as "marginal" 00048 */ 00049 static const char *string_validity(gpgme_validity_t validity) 00050 { 00051 switch(validity) { 00052 case GPGME_VALIDITY_UNKNOWN: 00053 return "unknown"; 00054 case GPGME_VALIDITY_UNDEFINED: 00055 return "undefined"; 00056 case GPGME_VALIDITY_NEVER: 00057 return "never"; 00058 case GPGME_VALIDITY_MARGINAL: 00059 return "marginal"; 00060 case GPGME_VALIDITY_FULL: 00061 return "full"; 00062 case GPGME_VALIDITY_ULTIMATE: 00063 return "ultimate"; 00064 } 00065 return "???"; 00066 } 00067 00068 static void sigsum_test_bit(gpgme_sigsum_t sigsum, alpm_list_t **summary, 00069 gpgme_sigsum_t bit, const char *value) 00070 { 00071 if(sigsum & bit) { 00072 *summary = alpm_list_add(*summary, (void *)value); 00073 } 00074 } 00075 00076 /** 00077 * Calculate a set of strings to represent the given GPGME signature summary 00078 * value. This is a bitmask so you may get any number of strings back. 00079 * @param sigsum a GPGME signature summary bitmask 00080 * @return the list of signature summary strings 00081 */ 00082 static alpm_list_t *list_sigsum(gpgme_sigsum_t sigsum) 00083 { 00084 alpm_list_t *summary = NULL; 00085 /* The docs say this can be a bitmask...not sure I believe it, but we'll code 00086 * for it anyway and show all possible flags in the returned string. */ 00087 00088 /* The signature is fully valid. */ 00089 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_VALID, "valid"); 00090 /* The signature is good. */ 00091 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_GREEN, "green"); 00092 /* The signature is bad. */ 00093 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_RED, "red"); 00094 /* One key has been revoked. */ 00095 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_REVOKED, "key revoked"); 00096 /* One key has expired. */ 00097 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_EXPIRED, "key expired"); 00098 /* The signature has expired. */ 00099 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_SIG_EXPIRED, "sig expired"); 00100 /* Can't verify: key missing. */ 00101 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_KEY_MISSING, "key missing"); 00102 /* CRL not available. */ 00103 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_CRL_MISSING, "crl missing"); 00104 /* Available CRL is too old. */ 00105 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_CRL_TOO_OLD, "crl too old"); 00106 /* A policy was not met. */ 00107 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_BAD_POLICY, "bad policy"); 00108 /* A system error occured. */ 00109 sigsum_test_bit(sigsum, &summary, GPGME_SIGSUM_SYS_ERROR, "sys error"); 00110 /* Fallback case */ 00111 if(!sigsum) { 00112 summary = alpm_list_add(summary, (void *)"(empty)"); 00113 } 00114 return summary; 00115 } 00116 00117 /** 00118 * Initialize the GPGME library. 00119 * This can be safely called multiple times; however it is not thread-safe. 00120 * @param handle the context handle 00121 * @return 0 on success, -1 on error 00122 */ 00123 static int init_gpgme(alpm_handle_t *handle) 00124 { 00125 static int init = 0; 00126 const char *version, *sigdir; 00127 gpgme_error_t err; 00128 gpgme_engine_info_t enginfo; 00129 00130 if(init) { 00131 /* we already successfully initialized the library */ 00132 return 0; 00133 } 00134 00135 sigdir = handle->gpgdir; 00136 00137 if(_alpm_access(handle, sigdir, "pubring.gpg", R_OK) 00138 || _alpm_access(handle, sigdir, "trustdb.gpg", R_OK)) { 00139 handle->pm_errno = ALPM_ERR_NOT_A_FILE; 00140 _alpm_log(handle, ALPM_LOG_DEBUG, "Signature verification will fail!\n"); 00141 _alpm_log(handle, ALPM_LOG_WARNING, 00142 _("Public keyring not found; have you run '%s'?\n"), 00143 "pacman-key --init"); 00144 } 00145 00146 /* calling gpgme_check_version() returns the current version and runs 00147 * some internal library setup code */ 00148 version = gpgme_check_version(NULL); 00149 _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME version: %s\n", version); 00150 gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL)); 00151 #ifdef LC_MESSAGES 00152 gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL)); 00153 #endif 00154 /* NOTE: 00155 * The GPGME library installs a SIGPIPE signal handler automatically if 00156 * the default signal hander is in use. The only time we set a handler 00157 * for SIGPIPE is in dload.c, and we reset it when we are done. Given that 00158 * we do this, we can let GPGME do its automagic. However, if we install 00159 * a library-wide SIGPIPE handler, we will have to be careful. 00160 */ 00161 00162 /* check for OpenPGP support (should be a no-brainer, but be safe) */ 00163 err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP); 00164 CHECK_ERR(); 00165 00166 /* set and check engine information */ 00167 err = gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, NULL, sigdir); 00168 CHECK_ERR(); 00169 err = gpgme_get_engine_info(&enginfo); 00170 CHECK_ERR(); 00171 _alpm_log(handle, ALPM_LOG_DEBUG, "GPGME engine info: file=%s, home=%s\n", 00172 enginfo->file_name, enginfo->home_dir); 00173 00174 init = 1; 00175 return 0; 00176 00177 error: 00178 _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err)); 00179 RET_ERR(handle, ALPM_ERR_GPGME, -1); 00180 } 00181 00182 /** 00183 * Determine if we have a key is known in our local keyring. 00184 * @param handle the context handle 00185 * @param fpr the fingerprint key ID to look up 00186 * @return 1 if key is known, 0 if key is unknown, -1 on error 00187 */ 00188 static int key_in_keychain(alpm_handle_t *handle, const char *fpr) 00189 { 00190 gpgme_error_t err; 00191 gpgme_ctx_t ctx; 00192 gpgme_key_t key; 00193 int ret = -1; 00194 00195 memset(&ctx, 0, sizeof(ctx)); 00196 err = gpgme_new(&ctx); 00197 CHECK_ERR(); 00198 00199 _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s locally\n", fpr); 00200 00201 err = gpgme_get_key(ctx, fpr, &key, 0); 00202 if(gpg_err_code(err) == GPG_ERR_EOF) { 00203 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); 00204 ret = 0; 00205 } else if(gpg_err_code(err) == GPG_ERR_NO_ERROR) { 00206 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup success, key exists\n"); 00207 ret = 1; 00208 } else { 00209 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err)); 00210 } 00211 00212 error: 00213 gpgme_key_unref(key); 00214 gpgme_release(ctx); 00215 return ret; 00216 } 00217 00218 /** 00219 * Search for a GPG key in a remote location. 00220 * This requires GPGME to call the gpg binary and have a keyserver previously 00221 * defined in a gpg.conf configuration file. 00222 * @param handle the context handle 00223 * @param fpr the fingerprint key ID to look up 00224 * @param pgpkey storage location for the given key if found 00225 * @return 1 on success, 0 on key not found, -1 on error 00226 */ 00227 static int key_search(alpm_handle_t *handle, const char *fpr, 00228 alpm_pgpkey_t *pgpkey) 00229 { 00230 gpgme_error_t err; 00231 gpgme_ctx_t ctx; 00232 gpgme_keylist_mode_t mode; 00233 gpgme_key_t key; 00234 int ret = -1; 00235 00236 memset(&ctx, 0, sizeof(ctx)); 00237 err = gpgme_new(&ctx); 00238 CHECK_ERR(); 00239 00240 mode = gpgme_get_keylist_mode(ctx); 00241 /* using LOCAL and EXTERN together doesn't work for GPG 1.X. Ugh. */ 00242 mode &= ~GPGME_KEYLIST_MODE_LOCAL; 00243 mode |= GPGME_KEYLIST_MODE_EXTERN; 00244 err = gpgme_set_keylist_mode(ctx, mode); 00245 CHECK_ERR(); 00246 00247 _alpm_log(handle, ALPM_LOG_DEBUG, "looking up key %s remotely\n", fpr); 00248 00249 err = gpgme_get_key(ctx, fpr, &key, 0); 00250 if(gpg_err_code(err) == GPG_ERR_EOF) { 00251 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); 00252 /* Try an alternate lookup using the 8 character fingerprint value, since 00253 * busted-ass keyservers can't support lookups using subkeys with the full 00254 * value as of now. This is why 2012 is not the year of PGP encryption. */ 00255 if(strlen(fpr) > 8) { 00256 const char *short_fpr = fpr + strlen(fpr) - 8; 00257 _alpm_log(handle, ALPM_LOG_DEBUG, 00258 "looking up key %s remotely\n", short_fpr); 00259 err = gpgme_get_key(ctx, short_fpr, &key, 0); 00260 if(gpg_err_code(err) == GPG_ERR_EOF) { 00261 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); 00262 ret = 0; 00263 } 00264 } else { 00265 ret = 0; 00266 } 00267 } 00268 00269 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) { 00270 goto error; 00271 } 00272 00273 /* should only get here if key actually exists */ 00274 pgpkey->data = key; 00275 if(key->subkeys->fpr) { 00276 pgpkey->fingerprint = key->subkeys->fpr; 00277 } else if(key->subkeys->keyid) { 00278 pgpkey->fingerprint = key->subkeys->keyid; 00279 } 00280 pgpkey->uid = key->uids->uid; 00281 pgpkey->name = key->uids->name; 00282 pgpkey->email = key->uids->email; 00283 pgpkey->created = key->subkeys->timestamp; 00284 pgpkey->expires = key->subkeys->expires; 00285 pgpkey->length = key->subkeys->length; 00286 pgpkey->revoked = key->subkeys->revoked; 00287 00288 switch (key->subkeys->pubkey_algo) { 00289 case GPGME_PK_RSA: 00290 case GPGME_PK_RSA_E: 00291 case GPGME_PK_RSA_S: 00292 pgpkey->pubkey_algo = 'R'; 00293 break; 00294 00295 case GPGME_PK_DSA: 00296 pgpkey->pubkey_algo = 'D'; 00297 break; 00298 00299 case GPGME_PK_ELG_E: 00300 case GPGME_PK_ELG: 00301 case GPGME_PK_ECDSA: 00302 case GPGME_PK_ECDH: 00303 pgpkey->pubkey_algo = 'E'; 00304 break; 00305 } 00306 00307 ret = 1; 00308 00309 error: 00310 if(ret != 1) { 00311 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err)); 00312 } 00313 gpgme_release(ctx); 00314 return ret; 00315 } 00316 00317 /** 00318 * Import a key into the local keyring. 00319 * @param handle the context handle 00320 * @param key the key to import, likely retrieved from #key_search 00321 * @return 0 on success, -1 on error 00322 */ 00323 static int key_import(alpm_handle_t *handle, alpm_pgpkey_t *key) 00324 { 00325 gpgme_error_t err; 00326 gpgme_ctx_t ctx; 00327 gpgme_key_t keys[2]; 00328 gpgme_import_result_t result; 00329 int ret = -1; 00330 00331 if(_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) { 00332 /* no chance of import succeeding if pubring isn't writable */ 00333 _alpm_log(handle, ALPM_LOG_ERROR, _("keyring is not writable\n")); 00334 return -1; 00335 } 00336 00337 memset(&ctx, 0, sizeof(ctx)); 00338 err = gpgme_new(&ctx); 00339 CHECK_ERR(); 00340 00341 _alpm_log(handle, ALPM_LOG_DEBUG, "importing key\n"); 00342 00343 keys[0] = key->data; 00344 keys[1] = NULL; 00345 err = gpgme_op_import_keys(ctx, keys); 00346 CHECK_ERR(); 00347 result = gpgme_op_import_result(ctx); 00348 CHECK_ERR(); 00349 /* we know we tried to import exactly one key, so check for this */ 00350 if(result->considered != 1 || !result->imports) { 00351 _alpm_log(handle, ALPM_LOG_DEBUG, "could not import key, 0 results\n"); 00352 ret = -1; 00353 } else if(result->imports->result != GPG_ERR_NO_ERROR) { 00354 _alpm_log(handle, ALPM_LOG_DEBUG, "gpg error: %s\n", gpgme_strerror(err)); 00355 ret = -1; 00356 } else { 00357 ret = 0; 00358 } 00359 00360 error: 00361 gpgme_release(ctx); 00362 return ret; 00363 } 00364 00365 /** 00366 * Decode a loaded signature in base64 form. 00367 * @param base64_data the signature to attempt to decode 00368 * @param data the decoded data; must be freed by the caller 00369 * @param data_len the length of the returned data 00370 * @return 0 on success, -1 on failure to properly decode 00371 */ 00372 static int decode_signature(const char *base64_data, 00373 unsigned char **data, size_t *data_len) { 00374 size_t len = strlen(base64_data); 00375 unsigned char *usline = (unsigned char *)base64_data; 00376 /* reasonable allocation of expected length is 3/4 of encoded length */ 00377 size_t destlen = len * 3 / 4; 00378 MALLOC(*data, destlen, goto error); 00379 if(base64_decode(*data, &destlen, usline, len)) { 00380 free(*data); 00381 goto error; 00382 } 00383 *data_len = destlen; 00384 return 0; 00385 00386 error: 00387 *data = NULL; 00388 *data_len = 0; 00389 return -1; 00390 } 00391 00392 /** 00393 * Check the PGP signature for the given file path. 00394 * If base64_sig is provided, it will be used as the signature data after 00395 * decoding. If base64_sig is NULL, expect a signature file next to path 00396 * (e.g. "%s.sig"). 00397 * 00398 * The return value will be 0 if nothing abnormal happened during the signature 00399 * check, and -1 if an error occurred while checking signatures or if a 00400 * signature could not be found; pm_errno will be set. Note that "abnormal" 00401 * does not include a failed signature; the value in siglist should be checked 00402 * to determine if the signature(s) are good. 00403 * @param handle the context handle 00404 * @param path the full path to a file 00405 * @param base64_sig optional PGP signature data in base64 encoding 00406 * @param siglist a pointer to storage for signature results 00407 * @return 0 in normal cases, -1 if the something failed in the check process 00408 */ 00409 int _alpm_gpgme_checksig(alpm_handle_t *handle, const char *path, 00410 const char *base64_sig, alpm_siglist_t *siglist) 00411 { 00412 int ret = -1, sigcount; 00413 gpgme_error_t err = 0; 00414 gpgme_ctx_t ctx; 00415 gpgme_data_t filedata, sigdata; 00416 gpgme_verify_result_t verify_result; 00417 gpgme_signature_t gpgsig; 00418 char *sigpath = NULL; 00419 unsigned char *decoded_sigdata = NULL; 00420 FILE *file = NULL, *sigfile = NULL; 00421 00422 if(!path || _alpm_access(handle, NULL, path, R_OK) != 0) { 00423 RET_ERR(handle, ALPM_ERR_NOT_A_FILE, -1); 00424 } 00425 00426 if(!siglist) { 00427 RET_ERR(handle, ALPM_ERR_WRONG_ARGS, -1); 00428 } 00429 siglist->count = 0; 00430 00431 if(!base64_sig) { 00432 sigpath = _alpm_sigpath(handle, path); 00433 /* this will just help debugging */ 00434 _alpm_access(handle, NULL, sigpath, R_OK); 00435 } 00436 00437 /* does the file we are verifying exist? */ 00438 file = fopen(path, "rb"); 00439 if(file == NULL) { 00440 handle->pm_errno = ALPM_ERR_NOT_A_FILE; 00441 goto error; 00442 } 00443 00444 /* does the sig file exist (if we didn't get the data directly)? */ 00445 if(!base64_sig) { 00446 sigfile = fopen(sigpath, "rb"); 00447 if(sigfile == NULL) { 00448 _alpm_log(handle, ALPM_LOG_DEBUG, "sig path %s could not be opened\n", 00449 sigpath); 00450 handle->pm_errno = ALPM_ERR_SIG_MISSING; 00451 goto error; 00452 } 00453 } 00454 00455 if(init_gpgme(handle)) { 00456 /* pm_errno was set in gpgme_init() */ 00457 goto error; 00458 } 00459 00460 _alpm_log(handle, ALPM_LOG_DEBUG, "checking signature for %s\n", path); 00461 00462 memset(&ctx, 0, sizeof(ctx)); 00463 memset(&sigdata, 0, sizeof(sigdata)); 00464 memset(&filedata, 0, sizeof(filedata)); 00465 00466 err = gpgme_new(&ctx); 00467 CHECK_ERR(); 00468 00469 /* create our necessary data objects to verify the signature */ 00470 err = gpgme_data_new_from_stream(&filedata, file); 00471 CHECK_ERR(); 00472 00473 /* next create data object for the signature */ 00474 if(base64_sig) { 00475 /* memory-based, we loaded it from a sync DB */ 00476 size_t data_len; 00477 int decode_ret = decode_signature(base64_sig, 00478 &decoded_sigdata, &data_len); 00479 if(decode_ret) { 00480 handle->pm_errno = ALPM_ERR_SIG_INVALID; 00481 goto gpg_error; 00482 } 00483 err = gpgme_data_new_from_mem(&sigdata, 00484 (char *)decoded_sigdata, data_len, 0); 00485 } else { 00486 /* file-based, it is on disk */ 00487 err = gpgme_data_new_from_stream(&sigdata, sigfile); 00488 } 00489 CHECK_ERR(); 00490 00491 /* here's where the magic happens */ 00492 err = gpgme_op_verify(ctx, sigdata, filedata, NULL); 00493 CHECK_ERR(); 00494 verify_result = gpgme_op_verify_result(ctx); 00495 CHECK_ERR(); 00496 if(!verify_result || !verify_result->signatures) { 00497 _alpm_log(handle, ALPM_LOG_DEBUG, "no signatures returned\n"); 00498 handle->pm_errno = ALPM_ERR_SIG_MISSING; 00499 goto gpg_error; 00500 } 00501 for(gpgsig = verify_result->signatures, sigcount = 0; 00502 gpgsig; gpgsig = gpgsig->next, sigcount++); 00503 _alpm_log(handle, ALPM_LOG_DEBUG, "%d signatures returned\n", sigcount); 00504 00505 CALLOC(siglist->results, sigcount, sizeof(alpm_sigresult_t), 00506 handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error); 00507 siglist->count = sigcount; 00508 00509 for(gpgsig = verify_result->signatures, sigcount = 0; gpgsig; 00510 gpgsig = gpgsig->next, sigcount++) { 00511 alpm_list_t *summary_list, *summary; 00512 alpm_sigstatus_t status; 00513 alpm_sigvalidity_t validity; 00514 gpgme_key_t key; 00515 alpm_sigresult_t *result; 00516 00517 _alpm_log(handle, ALPM_LOG_DEBUG, "fingerprint: %s\n", gpgsig->fpr); 00518 summary_list = list_sigsum(gpgsig->summary); 00519 for(summary = summary_list; summary; summary = summary->next) { 00520 _alpm_log(handle, ALPM_LOG_DEBUG, "summary: %s\n", (const char *)summary->data); 00521 } 00522 alpm_list_free(summary_list); 00523 _alpm_log(handle, ALPM_LOG_DEBUG, "status: %s\n", gpgme_strerror(gpgsig->status)); 00524 _alpm_log(handle, ALPM_LOG_DEBUG, "timestamp: %lu\n", gpgsig->timestamp); 00525 _alpm_log(handle, ALPM_LOG_DEBUG, "exp_timestamp: %lu\n", gpgsig->exp_timestamp); 00526 _alpm_log(handle, ALPM_LOG_DEBUG, "validity: %s; reason: %s\n", 00527 string_validity(gpgsig->validity), 00528 gpgme_strerror(gpgsig->validity_reason)); 00529 00530 result = siglist->results + sigcount; 00531 err = gpgme_get_key(ctx, gpgsig->fpr, &key, 0); 00532 if(gpg_err_code(err) == GPG_ERR_EOF) { 00533 _alpm_log(handle, ALPM_LOG_DEBUG, "key lookup failed, unknown key\n"); 00534 err = GPG_ERR_NO_ERROR; 00535 /* we dupe the fpr in this case since we have no key to point at */ 00536 STRDUP(result->key.fingerprint, gpgsig->fpr, 00537 handle->pm_errno = ALPM_ERR_MEMORY; goto gpg_error); 00538 } else { 00539 CHECK_ERR(); 00540 if(key->uids) { 00541 result->key.data = key; 00542 result->key.fingerprint = key->subkeys->fpr; 00543 result->key.uid = key->uids->uid; 00544 result->key.name = key->uids->name; 00545 result->key.email = key->uids->email; 00546 result->key.created = key->subkeys->timestamp; 00547 result->key.expires = key->subkeys->expires; 00548 _alpm_log(handle, ALPM_LOG_DEBUG, 00549 "key: %s, %s, owner_trust %s, disabled %d\n", 00550 key->subkeys->fpr, key->uids->uid, 00551 string_validity(key->owner_trust), key->disabled); 00552 } 00553 } 00554 00555 switch(gpg_err_code(gpgsig->status)) { 00556 /* good cases */ 00557 case GPG_ERR_NO_ERROR: 00558 status = ALPM_SIGSTATUS_VALID; 00559 break; 00560 case GPG_ERR_KEY_EXPIRED: 00561 status = ALPM_SIGSTATUS_KEY_EXPIRED; 00562 break; 00563 /* bad cases */ 00564 case GPG_ERR_SIG_EXPIRED: 00565 status = ALPM_SIGSTATUS_SIG_EXPIRED; 00566 break; 00567 case GPG_ERR_NO_PUBKEY: 00568 status = ALPM_SIGSTATUS_KEY_UNKNOWN; 00569 break; 00570 case GPG_ERR_BAD_SIGNATURE: 00571 default: 00572 status = ALPM_SIGSTATUS_INVALID; 00573 break; 00574 } 00575 /* special case: key disabled is not returned in above status code */ 00576 if(result->key.data && key->disabled) { 00577 status = ALPM_SIGSTATUS_KEY_DISABLED; 00578 } 00579 00580 switch(gpgsig->validity) { 00581 case GPGME_VALIDITY_ULTIMATE: 00582 case GPGME_VALIDITY_FULL: 00583 validity = ALPM_SIGVALIDITY_FULL; 00584 break; 00585 case GPGME_VALIDITY_MARGINAL: 00586 validity = ALPM_SIGVALIDITY_MARGINAL; 00587 break; 00588 case GPGME_VALIDITY_NEVER: 00589 validity = ALPM_SIGVALIDITY_NEVER; 00590 break; 00591 case GPGME_VALIDITY_UNKNOWN: 00592 case GPGME_VALIDITY_UNDEFINED: 00593 default: 00594 validity = ALPM_SIGVALIDITY_UNKNOWN; 00595 break; 00596 } 00597 00598 result->status = status; 00599 result->validity = validity; 00600 } 00601 00602 ret = 0; 00603 00604 gpg_error: 00605 gpgme_data_release(sigdata); 00606 gpgme_data_release(filedata); 00607 gpgme_release(ctx); 00608 00609 error: 00610 if(sigfile) { 00611 fclose(sigfile); 00612 } 00613 if(file) { 00614 fclose(file); 00615 } 00616 FREE(sigpath); 00617 FREE(decoded_sigdata); 00618 if(gpg_err_code(err) != GPG_ERR_NO_ERROR) { 00619 _alpm_log(handle, ALPM_LOG_ERROR, _("GPGME error: %s\n"), gpgme_strerror(err)); 00620 RET_ERR(handle, ALPM_ERR_GPGME, -1); 00621 } 00622 return ret; 00623 } 00624 00625 #else /* HAVE_LIBGPGME */ 00626 static int key_in_keychain(alpm_handle_t UNUSED *handle, const char UNUSED *fpr) 00627 { 00628 return -1; 00629 } 00630 int _alpm_gpgme_checksig(alpm_handle_t UNUSED *handle, const char UNUSED *path, 00631 const char UNUSED *base64_sig, alpm_siglist_t UNUSED *siglist) 00632 { 00633 return -1; 00634 } 00635 #endif /* HAVE_LIBGPGME */ 00636 00637 /** 00638 * Form a signature path given a file path. 00639 * Caller must free the result. 00640 * @param handle the context handle 00641 * @param path the full path to a file 00642 * @return the path with '.sig' appended, NULL on errors 00643 */ 00644 char *_alpm_sigpath(alpm_handle_t *handle, const char *path) 00645 { 00646 char *sigpath; 00647 size_t len; 00648 00649 if(!path) { 00650 return NULL; 00651 } 00652 len = strlen(path) + 5; 00653 CALLOC(sigpath, len, sizeof(char), RET_ERR(handle, ALPM_ERR_MEMORY, NULL)); 00654 sprintf(sigpath, "%s.sig", path); 00655 return sigpath; 00656 } 00657 00658 /** 00659 * Helper for checking the PGP signature for the given file path. 00660 * This wraps #_alpm_gpgme_checksig in a slightly friendlier manner to simplify 00661 * handling of optional signatures and marginal/unknown trust levels and 00662 * handling the correct error code return values. 00663 * @param handle the context handle 00664 * @param path the full path to a file 00665 * @param base64_sig optional PGP signature data in base64 encoding 00666 * @param optional whether signatures are optional (e.g., missing OK) 00667 * @param marginal whether signatures with marginal trust are acceptable 00668 * @param unknown whether signatures with unknown trust are acceptable 00669 * @param sigdata a pointer to storage for signature results 00670 * @return 0 on success, -1 on error (consult pm_errno or sigdata) 00671 */ 00672 int _alpm_check_pgp_helper(alpm_handle_t *handle, const char *path, 00673 const char *base64_sig, int optional, int marginal, int unknown, 00674 alpm_siglist_t **sigdata) 00675 { 00676 alpm_siglist_t *siglist; 00677 int ret; 00678 00679 CALLOC(siglist, 1, sizeof(alpm_siglist_t), 00680 RET_ERR(handle, ALPM_ERR_MEMORY, -1)); 00681 00682 ret = _alpm_gpgme_checksig(handle, path, base64_sig, siglist); 00683 if(ret && handle->pm_errno == ALPM_ERR_SIG_MISSING) { 00684 if(optional) { 00685 _alpm_log(handle, ALPM_LOG_DEBUG, "missing optional signature\n"); 00686 handle->pm_errno = 0; 00687 ret = 0; 00688 } else { 00689 _alpm_log(handle, ALPM_LOG_DEBUG, "missing required signature\n"); 00690 /* ret will already be -1 */ 00691 } 00692 } else if(ret) { 00693 _alpm_log(handle, ALPM_LOG_DEBUG, "signature check failed\n"); 00694 /* ret will already be -1 */ 00695 } else { 00696 size_t num; 00697 for(num = 0; !ret && num < siglist->count; num++) { 00698 switch(siglist->results[num].status) { 00699 case ALPM_SIGSTATUS_VALID: 00700 case ALPM_SIGSTATUS_KEY_EXPIRED: 00701 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is valid\n"); 00702 switch(siglist->results[num].validity) { 00703 case ALPM_SIGVALIDITY_FULL: 00704 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is fully trusted\n"); 00705 break; 00706 case ALPM_SIGVALIDITY_MARGINAL: 00707 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is marginal trust\n"); 00708 if(!marginal) { 00709 ret = -1; 00710 } 00711 break; 00712 case ALPM_SIGVALIDITY_UNKNOWN: 00713 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is unknown trust\n"); 00714 if(!unknown) { 00715 ret = -1; 00716 } 00717 break; 00718 case ALPM_SIGVALIDITY_NEVER: 00719 _alpm_log(handle, ALPM_LOG_DEBUG, "signature should never be trusted\n"); 00720 ret = -1; 00721 break; 00722 } 00723 break; 00724 case ALPM_SIGSTATUS_SIG_EXPIRED: 00725 case ALPM_SIGSTATUS_KEY_UNKNOWN: 00726 case ALPM_SIGSTATUS_KEY_DISABLED: 00727 case ALPM_SIGSTATUS_INVALID: 00728 _alpm_log(handle, ALPM_LOG_DEBUG, "signature is not valid\n"); 00729 ret = -1; 00730 break; 00731 } 00732 } 00733 } 00734 00735 if(sigdata) { 00736 *sigdata = siglist; 00737 } else { 00738 alpm_siglist_cleanup(siglist); 00739 free(siglist); 00740 } 00741 00742 return ret; 00743 } 00744 00745 /** 00746 * Examine a signature result list and take any appropriate or necessary 00747 * actions. This may include asking the user to import a key or simply printing 00748 * helpful failure messages so the user can take action out of band. 00749 * @param handle the context handle 00750 * @param identifier a friendly name for the signed resource; usually a 00751 * database or package name 00752 * @param siglist a pointer to storage for signature results 00753 * @param optional whether signatures are optional (e.g., missing OK) 00754 * @param marginal whether signatures with marginal trust are acceptable 00755 * @param unknown whether signatures with unknown trust are acceptable 00756 * @return 0 if all signatures are OK, -1 on errors, 1 if we should retry the 00757 * validation process 00758 */ 00759 int _alpm_process_siglist(alpm_handle_t *handle, const char *identifier, 00760 alpm_siglist_t *siglist, int optional, int marginal, int unknown) 00761 { 00762 size_t i; 00763 int retry = 0; 00764 00765 if(!optional && siglist->count == 0) { 00766 _alpm_log(handle, ALPM_LOG_ERROR, 00767 _("%s: missing required signature\n"), identifier); 00768 } 00769 00770 for(i = 0; i < siglist->count; i++) { 00771 alpm_sigresult_t *result = siglist->results + i; 00772 const char *name = result->key.uid ? result->key.uid : result->key.fingerprint; 00773 switch(result->status) { 00774 case ALPM_SIGSTATUS_VALID: 00775 case ALPM_SIGSTATUS_KEY_EXPIRED: 00776 switch(result->validity) { 00777 case ALPM_SIGVALIDITY_FULL: 00778 break; 00779 case ALPM_SIGVALIDITY_MARGINAL: 00780 if(!marginal) { 00781 _alpm_log(handle, ALPM_LOG_ERROR, 00782 _("%s: signature from \"%s\" is marginal trust\n"), 00783 identifier, name); 00784 /* QUESTION(handle, ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */ 00785 } 00786 break; 00787 case ALPM_SIGVALIDITY_UNKNOWN: 00788 if(!unknown) { 00789 _alpm_log(handle, ALPM_LOG_ERROR, 00790 _("%s: signature from \"%s\" is unknown trust\n"), 00791 identifier, name); 00792 /* QUESTION(handle, ALPM_QUESTION_EDIT_KEY_TRUST, &result->key, NULL, NULL, &answer); */ 00793 } 00794 break; 00795 case ALPM_SIGVALIDITY_NEVER: 00796 _alpm_log(handle, ALPM_LOG_ERROR, 00797 _("%s: signature from \"%s\" should never be trusted\n"), 00798 identifier, name); 00799 break; 00800 } 00801 break; 00802 case ALPM_SIGSTATUS_KEY_UNKNOWN: 00803 /* ensure this key is still actually unknown; we may have imported it 00804 * on an earlier call to this function. */ 00805 if(key_in_keychain(handle, result->key.fingerprint) == 1) { 00806 break; 00807 } 00808 _alpm_log(handle, ALPM_LOG_ERROR, 00809 _("%s: key \"%s\" is unknown\n"), identifier, name); 00810 #ifdef HAVE_LIBGPGME 00811 { 00812 int answer; 00813 alpm_pgpkey_t fetch_key; 00814 memset(&fetch_key, 0, sizeof(fetch_key)); 00815 00816 if(key_search(handle, result->key.fingerprint, &fetch_key) == 1) { 00817 _alpm_log(handle, ALPM_LOG_DEBUG, 00818 "unknown key, found %s on keyserver\n", fetch_key.uid); 00819 if(!_alpm_access(handle, handle->gpgdir, "pubring.gpg", W_OK)) { 00820 QUESTION(handle, ALPM_QUESTION_IMPORT_KEY, 00821 &fetch_key, NULL, NULL, &answer); 00822 if(answer) { 00823 if(key_import(handle, &fetch_key) == 0) { 00824 retry = 1; 00825 } else { 00826 _alpm_log(handle, ALPM_LOG_ERROR, 00827 _("key \"%s\" could not be imported\n"), fetch_key.uid); 00828 } 00829 } 00830 } else { 00831 /* keyring directory was not writable, so we don't even try */ 00832 _alpm_log(handle, ALPM_LOG_WARNING, 00833 _("key %s, \"%s\" found on keyserver, keyring is not writable\n"), 00834 fetch_key.fingerprint, fetch_key.uid); 00835 } 00836 } else { 00837 _alpm_log(handle, ALPM_LOG_ERROR, 00838 _("key \"%s\" could not be looked up remotely\n"), name); 00839 } 00840 gpgme_key_unref(fetch_key.data); 00841 } 00842 #endif 00843 break; 00844 case ALPM_SIGSTATUS_KEY_DISABLED: 00845 _alpm_log(handle, ALPM_LOG_ERROR, 00846 _("%s: key \"%s\" is disabled\n"), identifier, name); 00847 break; 00848 case ALPM_SIGSTATUS_SIG_EXPIRED: 00849 _alpm_log(handle, ALPM_LOG_ERROR, 00850 _("%s: signature from \"%s\" is expired\n"), identifier, name); 00851 break; 00852 case ALPM_SIGSTATUS_INVALID: 00853 _alpm_log(handle, ALPM_LOG_ERROR, 00854 _("%s: signature from \"%s\" is invalid\n"), 00855 identifier, name); 00856 break; 00857 } 00858 } 00859 00860 return retry; 00861 } 00862 00863 /** 00864 * Check the PGP signature for the given package file. 00865 * @param pkg the package to check 00866 * @param siglist a pointer to storage for signature results 00867 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred) 00868 */ 00869 int SYMEXPORT alpm_pkg_check_pgp_signature(alpm_pkg_t *pkg, 00870 alpm_siglist_t *siglist) 00871 { 00872 ASSERT(pkg != NULL, return -1); 00873 ASSERT(siglist != NULL, RET_ERR(pkg->handle, ALPM_ERR_WRONG_ARGS, -1)); 00874 pkg->handle->pm_errno = 0; 00875 00876 return _alpm_gpgme_checksig(pkg->handle, pkg->filename, 00877 pkg->base64_sig, siglist); 00878 } 00879 00880 /** 00881 * Check the PGP signature for the given database. 00882 * @param db the database to check 00883 * @param siglist a pointer to storage for signature results 00884 * @return a int value : 0 (valid), 1 (invalid), -1 (an error occurred) 00885 */ 00886 int SYMEXPORT alpm_db_check_pgp_signature(alpm_db_t *db, 00887 alpm_siglist_t *siglist) 00888 { 00889 ASSERT(db != NULL, return -1); 00890 ASSERT(siglist != NULL, RET_ERR(db->handle, ALPM_ERR_WRONG_ARGS, -1)); 00891 db->handle->pm_errno = 0; 00892 00893 return _alpm_gpgme_checksig(db->handle, _alpm_db_path(db), NULL, siglist); 00894 } 00895 00896 /** 00897 * Clean up and free a signature result list. 00898 * Note that this does not free the siglist object itself in case that 00899 * was allocated on the stack; this is the responsibility of the caller. 00900 * @param siglist a pointer to storage for signature results 00901 * @return 0 on success, -1 on error 00902 */ 00903 int SYMEXPORT alpm_siglist_cleanup(alpm_siglist_t *siglist) 00904 { 00905 ASSERT(siglist != NULL, return -1); 00906 size_t num; 00907 for(num = 0; num < siglist->count; num++) { 00908 alpm_sigresult_t *result = siglist->results + num; 00909 if(result->key.data) { 00910 #if HAVE_LIBGPGME 00911 gpgme_key_unref(result->key.data); 00912 #endif 00913 } else { 00914 free(result->key.fingerprint); 00915 } 00916 } 00917 if(siglist->count) { 00918 free(siglist->results); 00919 } 00920 siglist->results = NULL; 00921 siglist->count = 0; 00922 return 0; 00923 } 00924 00925 /* vim: set ts=2 sw=2 noet: */