diff options
Diffstat (limited to 'signature_check.c')
-rw-r--r-- | signature_check.c | 924 |
1 files changed, 924 insertions, 0 deletions
diff --git a/signature_check.c b/signature_check.c new file mode 100644 index 0000000..c5e1aa1 --- /dev/null +++ b/signature_check.c @@ -0,0 +1,924 @@ +/* This file is part of "reprepro" + * Copyright (C) 2003,2004,2005,2006,2007,2009,2012 Bernhard R. Link + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02111-1301 USA + */ +#include <config.h> + +#include <errno.h> +#include <assert.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <time.h> +#include <string.h> +#include <fcntl.h> +#include "signature_p.h" +#include "ignore.h" +#include "chunks.h" +#include "readtextfile.h" + + +#ifdef HAVE_LIBGPGME + +static retvalue parse_condition_part(bool *allow_subkeys_p, bool *allow_bad_p, const char *full_condition, const char **condition_p, /*@out@*/ char **next_key_p) { + const char *key = *condition_p, *p; + char *next_key, *q; + size_t kl; + + *allow_bad_p = false; + *allow_subkeys_p = false; + + while (*key != '\0' && xisspace(*key)) + key++; + if (*key == '\0') { + fprintf(stderr, +"Error: unexpected end of VerifyRelease condition '%s'!\n", + full_condition); + return RET_ERROR; + } + + p = key; + while ((*p >= 'A' && *p <= 'F') || (*p >= 'a' && *p <= 'f') + || (*p >= '0' && *p <= '9')) + p++; + if (*p != '\0' && !xisspace(*p) && *p != '|' && *p != '!' && *p != '+') { + fprintf(stderr, +"Error: Unexpected character 0x%02hhx='%c' in VerifyRelease condition '%s'!\n", + *p, *p, full_condition); + return RET_ERROR; + } + kl = p - key; + if (kl < 8) { + fprintf(stderr, +"Error: Too short key id '%.*s' in VerifyRelease condition '%s'!\n", + (int)kl, key, full_condition); + return RET_ERROR; + } + next_key = strndup(key, kl); + if (FAILEDTOALLOC(next_key)) + return RET_ERROR_OOM; + key = p; + for (q = next_key ; *q != '\0' ; q++) { + if (*q >= 'a' && *q <= 'f') + *q -= 'a' - 'A'; + } + while (*key != '\0' && xisspace(*key)) + key++; + if (*key == '!') { + *allow_bad_p = true; + key++; + } + while (*key != '\0' && xisspace(*key)) + key++; + if (*key == '+') { + *allow_subkeys_p = true; + key++; + } + while (*key != '\0' && xisspace(*key)) + key++; + if ((*key >= 'A' && *key <= 'F') + || (*key >= 'a' && *key <= 'f') + || (*key >= '0' && *key <= '9')) { + free(next_key); + fprintf(stderr, +"Error: Space separated key-ids in VerifyRelease condition '%s'!\n" +"(Alternate keys can be separated with '|'. Do not put spaces in key-ids.)\n", + full_condition); + return RET_ERROR; + } + if (*key != '\0' && *key != '|') { + free(next_key); + fprintf(stderr, +"Error: Unexpected character 0x%02hhx='%c' in VerifyRelease condition '%s'!\n", + *key, *key, full_condition); + return RET_ERROR; + } + if (*key == '|') + key++; + *next_key_p = next_key; + *condition_p = key; + return RET_OK; +} + +static struct known_key { + struct known_key *next; + /* subkeys, first is primary key */ + int count; + struct known_subkey { + /* full fingerprint or keyid */ + char *name; + unsigned int name_len; + /* true if revoked */ + bool revoked; + /* true if expired */ + bool expired; + /* false if invalid or cannot sign */ + bool cansign; + } subkeys[]; +} *known_keys = NULL; + +struct requested_key { + /* pointer to the key in question */ + const struct known_key *key; + /* which of those keys are requested, -1 for any (i.e. allow subkeys) */ + int subkey; + /* allow some problems, if requested by the user */ + bool allow_bad; +}; + +static retvalue found_key(struct known_key *k, int i, bool allow_subkeys, bool allow_bad, const char *full_condition, const struct known_key **key_found, int *index_found) { + if (!allow_bad && k->subkeys[i].revoked) { + fprintf(stderr, +"VerifyRelease condition '%s' lists revoked key '%s'.\n" +"(To use it anyway, append it with a '!' to force usage).\n", + full_condition, k->subkeys[i].name); + return RET_ERROR; + } + if (!allow_bad && k->subkeys[i].expired) { + fprintf(stderr, +"VerifyRelease condition '%s' lists expired key '%s'.\n" +"(To use it anyway, append it with a '!' to force usage).\n", + full_condition, k->subkeys[i].name); + return RET_ERROR; + } + if (!allow_bad && !k->subkeys[i].cansign) { + fprintf(stderr, +"VerifyRelease condition '%s' lists non-signing key '%s'.\n" +"(To use it anyway, append it with a '!' to force usage).\n", + full_condition, k->subkeys[i].name); + return RET_ERROR; + } + if (allow_subkeys) { + if (i != 0) { + fprintf(stderr, +"VerifyRelease condition '%s' lists non-primary key '%s' with '+'.\n", + full_condition, k->subkeys[i].name); + return RET_ERROR; + } + *index_found = -1; + } else + *index_found = i; + *key_found = k; + return RET_OK; +} + +/* name must already be upper-case */ +static retvalue load_key(const char *name, bool allow_subkeys, bool allow_bad, const char *full_condition, const struct known_key **key_found, int *index_found) { + gpg_error_t err; + gpgme_key_t gpgme_key = NULL; + gpgme_subkey_t subkey; + int found = -1; + struct known_key *k; + int i; + size_t l = strlen(name); + + /* first look if this key was already retrieved: */ + for (k = known_keys ; k != NULL ; k = k->next) { + for(i = 0 ; i < k->count ; i++) { + struct known_subkey *s = &k->subkeys[i]; + + if (s->name_len < l) + continue; + if (memcmp(name, s->name + (s->name_len - l), l) != 0) + continue; + return found_key(k, i, allow_subkeys, allow_bad, + full_condition, + key_found, index_found); + } + } + /* If not yet found, request it: */ + err = gpgme_get_key(context, name, &gpgme_key, 0); + if ((gpg_err_code(err) == GPG_ERR_EOF) && gpgme_key == NULL) { + fprintf(stderr, "Error: unknown key '%s'!\n", name); + return RET_ERROR_MISSING; + } + if (err != 0) { + fprintf(stderr, "gpgme error %s:%d retrieving key '%s': %s\n", + gpg_strsource(err), (int)gpg_err_code(err), + name, gpg_strerror(err)); + if (gpg_err_code(err) == GPG_ERR_ENOMEM) + return RET_ERROR_OOM; + else + return RET_ERROR_GPGME; + } + i = 0; + subkey = gpgme_key->subkeys; + while (subkey != NULL) { + subkey = subkey->next; + i++; + } + k = calloc(1, sizeof(struct known_key) + + i * sizeof(struct known_subkey)); + if (FAILEDTOALLOC(k)) { + gpgme_key_unref(gpgme_key); + return RET_ERROR_OOM; + } + k->count = i; + k->next = known_keys; + known_keys = k; + + subkey = gpgme_key->subkeys; + for (i = 0 ; i < k->count ; i++ , subkey = subkey->next) { + struct known_subkey *s = &k->subkeys[i]; + + assert (subkey != NULL); + + s->revoked = subkey->revoked; + s->expired = subkey->expired; + s->cansign = subkey->can_sign && !subkey->invalid; + s->name = strdup(subkey->keyid); + if (FAILEDTOALLOC(s->name)) { + gpgme_key_unref(gpgme_key); + return RET_ERROR_OOM; + } + for (char *p = s->name ; *p != '\0' ; p++) { + if (*p >= 'a' && *p <= 'z') + *p -= 'a'-'A'; + } + s->name_len = strlen(s->name); + if (memcmp(name, s->name + (s->name_len - l), l) == 0) + found = i; + } + assert (subkey == NULL); + gpgme_key_unref(gpgme_key); + if (found < 0) { + fprintf(stderr, "Error: not a valid key id '%s'!\n" +"Use hex-igits from the end of the key as identifier\n", name); + return RET_ERROR; + } + return found_key(k, found, allow_subkeys, allow_bad, + full_condition, key_found, index_found); +} + +static void free_known_key(/*@only@*/struct known_key *k) { + int i; + + for (i = 0 ; i < k->count ; i++) { + free(k->subkeys[i].name); + } + free(k); +} + +void free_known_keys(void) { + while (known_keys != NULL) { + struct known_key *k = known_keys; + known_keys = k->next; + free_known_key(k); + } + known_keys = NULL; +} + +/* This checks a Release.gpg/Release file pair. requirements is a list of + * requirements. (as this Release file can be requested by multiple update + * rules, there can be multiple requirements for one file) */ + +struct signature_requirement { + /* next condition */ + struct signature_requirement *next; + /* the original description for error messages */ + char *condition; + /* an array of or-connected conditions */ + size_t num_keys; + struct requested_key keys[]; +}; +#define sizeof_requirement(n) (sizeof(struct signature_requirement) + (n) * sizeof(struct requested_key)) + +void signature_requirements_free(struct signature_requirement *list) { + while (list != NULL) { + struct signature_requirement *p = list; + list = p->next; + + free(p->condition); + free(p); + } +} + +static bool key_good(const struct requested_key *req, const gpgme_signature_t signatures) { + const struct known_key *k = req->key; + gpgme_signature_t sig; + + for (sig = signatures ; sig != NULL ; sig = sig->next) { + const char *fpr = sig->fpr; + size_t l = strlen(sig->fpr); + int i; + /* while gpg reports the subkey of an key that is expired + to be expired to, it does not tell this in the signature, + so we use this here... */ + bool key_expired = false; + + if (req->subkey < 0) { + /* any subkey is allowed */ + for(i = 0 ; i < k->count ; i++) { + const struct known_subkey *s = &k->subkeys[i]; + + if (s->name_len > l) + continue; + if (memcmp(s->name, fpr + (l - s->name_len), + s->name_len) != 0) + continue; + key_expired = k->subkeys[i].expired; + break; + } + if (i >= k->count) + continue; + } else { + const struct known_subkey *s; + + assert (req->subkey < k->count); + s = &k->subkeys[req->subkey]; + if (memcmp(s->name, fpr + (l - s->name_len), + s->name_len) != 0) + continue; + key_expired = k->subkeys[req->subkey].expired; + } + /* only accept perfectly good signatures and silently + ignore everything else. Those are warned about or + even accepted in the run with key_good_enough */ + if (gpg_err_code(sig->status) == GPG_ERR_NO_ERROR + && !key_expired) + return true; + /* we have to continue otherwise, + as another subkey might still follow */ + continue; + } + /* no valid signature with this key found */ + return false; +} + +static bool key_good_enough(const struct requested_key *req, const gpgme_signature_t signatures, const char *releasegpg, const char *release) { + const struct known_key *k = req->key; + gpgme_signature_t sig; + + for (sig = signatures ; sig != NULL ; sig = sig->next) { + const char *fpr = sig->fpr; + size_t l = strlen(sig->fpr); + int i; + bool key_expired = false; /* dito */ + + if (req->subkey < 0) { + /* any subkey is allowed */ + for(i = 0 ; i < k->count ; i++) { + const struct known_subkey *s = &k->subkeys[i]; + + if (s->name_len > l) + continue; + if (memcmp(s->name, fpr + (l - s->name_len), + s->name_len) != 0) + continue; + key_expired = k->subkeys[i].expired; + break; + } + if (i >= k->count) + continue; + } else { + const struct known_subkey *s; + + assert (req->subkey < k->count); + s = &k->subkeys[req->subkey]; + if (memcmp(s->name, fpr + (l - s->name_len), + s->name_len) != 0) + continue; + key_expired = k->subkeys[req->subkey].expired; + } + /* this key we look for. if it is acceptable, we are finished. + if it is not acceptable, we still have to look at the other + signatures, as a signature with another subkey is following + */ + switch (gpg_err_code(sig->status)) { + case GPG_ERR_NO_ERROR: + if (! key_expired) + return true; + if (req->allow_bad && IGNORABLE(expiredkey)) { + if (verbose >= 0) + fprintf(stderr, +"WARNING: valid signature in '%s' with parent-expired '%s' is accepted as requested!\n", + releasegpg, fpr); + return true; + } + fprintf(stderr, +"Not accepting valid signature in '%s' with parent-EXPIRED '%s'\n", releasegpg, fpr); + if (verbose >= 0) + fprintf(stderr, +"(To ignore it append a ! to the key and run reprepro with --ignore=expiredkey)\n"); + /* not accepted */ + continue; + case GPG_ERR_KEY_EXPIRED: + if (req->allow_bad && IGNORABLE(expiredkey)) { + if (verbose >= 0) + fprintf(stderr, +"WARNING: valid signature in '%s' with expired '%s' is accepted as requested!\n", + releasegpg, fpr); + return true; + } + fprintf(stderr, +"Not accepting valid signature in '%s' with EXPIRED '%s'\n", releasegpg, fpr); + if (verbose >= 0) + fprintf(stderr, +"(To ignore it append a ! to the key and run reprepro with --ignore=expiredkey)\n"); + /* not accepted */ + continue; + case GPG_ERR_CERT_REVOKED: + if (req->allow_bad && IGNORABLE(revokedkey)) { + if (verbose >= 0) + fprintf(stderr, +"WARNING: valid signature in '%s' with revoked '%s' is accepted as requested!\n", + releasegpg, fpr); + return RET_OK; + } + fprintf(stderr, +"Not accepting valid signature in '%s' with REVOKED '%s'\n", releasegpg, fpr); + if (verbose >= 0) + fprintf(stderr, +"(To ignore it append a ! to the key and run reprepro with --ignore=revokedkey)\n"); + /* not accepted */ + continue; + case GPG_ERR_SIG_EXPIRED: + if (req->allow_bad && IGNORABLE(expiredsignature)) { + if (verbose >= 0) + fprintf(stderr, +"WARNING: valid but expired signature in '%s' with '%s' is accepted as requested!\n", + releasegpg, fpr); + return RET_OK; + } + fprintf(stderr, +"Not accepting valid but EXPIRED signature in '%s' with '%s'\n", releasegpg, fpr); + if (verbose >= 0) + fprintf(stderr, +"(To ignore it append a ! to the key and run reprepro with --ignore=expiredsignature)\n"); + /* not accepted */ + continue; + case GPG_ERR_BAD_SIGNATURE: + case GPG_ERR_NO_PUBKEY: + /* not accepted */ + continue; + case GPG_ERR_GENERAL: + if (release == NULL) + fprintf(stderr, +"gpgme returned an general error verifing signature with '%s' in '%s'!\n" +"Try running gpg --verify '%s' manually for hints what is happening.\n" +"If this does not print any errors, retry the command causing this message.\n", + fpr, releasegpg, + releasegpg); + else + fprintf(stderr, +"gpgme returned an general error verifing signature with '%s' in '%s'!\n" +"Try running gpg --verify '%s' '%s' manually for hints what is happening.\n" +"If this does not print any errors, retry the command causing this message.\n", + fpr, releasegpg, + releasegpg, release); + continue; + /* there sadly no more is a way to make sure we have + * all possible ones handled */ + default: + break; + } + fprintf(stderr, +"Error checking signature (gpgme returned unexpected value %d)!\n" +"Please file a bug report, so reprepro can handle this in the future.\n", + gpg_err_code(sig->status)); + return false; + } + return false; +} + +retvalue signature_requirement_add(struct signature_requirement **list_p, const char *condition) { + struct signature_requirement *req; + const char *full_condition = condition; + retvalue r; + + r = signature_init(false); + if (RET_WAS_ERROR(r)) + return r; + + if (condition == NULL || strcmp(condition, "blindtrust") == 0) + return RET_NOTHING; + + /* no need to add the same condition multiple times */ + for (req = *list_p ; req != NULL ; req = req->next) { + if (strcmp(req->condition, condition) == 0) + return RET_NOTHING; + } + + req = malloc(sizeof_requirement(1)); + if (FAILEDTOALLOC(req)) + return RET_ERROR_OOM; + req->next = NULL; + req->condition = strdup(condition); + if (FAILEDTOALLOC(req->condition)) { + free(req); + return RET_ERROR_OOM; + } + req->num_keys = 0; + do { + bool allow_subkeys, allow_bad; + char *next_key; + + r = parse_condition_part(&allow_subkeys, &allow_bad, + full_condition, &condition, &next_key); + ASSERT_NOT_NOTHING(r); + if (RET_WAS_ERROR(r)) { + signature_requirements_free(req); + return r; + } + req->keys[req->num_keys].allow_bad = allow_bad; + r = load_key(next_key, allow_subkeys, allow_bad, + full_condition, + &req->keys[req->num_keys].key, + &req->keys[req->num_keys].subkey); + free(next_key); + if (RET_WAS_ERROR(r)) { + signature_requirements_free(req); + return r; + } + req->num_keys++; + + if (*condition != '\0') { + struct signature_requirement *h; + + h = realloc(req, sizeof_requirement(req->num_keys+1)); + if (FAILEDTOALLOC(h)) { + signature_requirements_free(req); + return r; + } + req = h; + } else + break; + } while (true); + req->next = *list_p; + *list_p = req; + return RET_OK; +} + +static void print_signatures(FILE *f, gpgme_signature_t s, const char *releasegpg) { + char timebuffer[20]; + struct tm *tm; + time_t t; + + if (s == NULL) { + fprintf(f, "gpgme reported no signatures in '%s':\n" +"Either there are really none or something else is strange.\n" +"One known reason for this effect is forgeting -b when signing.\n", + releasegpg); + return; + } + + fprintf(f, "Signatures in '%s':\n", releasegpg); + for (; s != NULL ; s = s->next) { + t = s->timestamp; tm = localtime(&t); + strftime(timebuffer, 19, "%Y-%m-%d", tm); + fprintf(f, "'%s' (signed %s): ", s->fpr, timebuffer); + switch (gpg_err_code(s->status)) { + case GPG_ERR_NO_ERROR: + fprintf(f, "valid\n"); + continue; + case GPG_ERR_KEY_EXPIRED: + fprintf(f, "expired key\n"); + continue; + case GPG_ERR_CERT_REVOKED: + fprintf(f, "key revoced\n"); + continue; + case GPG_ERR_SIG_EXPIRED: + t = s->exp_timestamp; tm = localtime(&t); + strftime(timebuffer, 19, "%Y-%m-%d", tm); + fprintf(f, "expired signature (since %s)\n", + timebuffer); + continue; + case GPG_ERR_BAD_SIGNATURE: + fprintf(f, "bad signature\n"); + continue; + case GPG_ERR_NO_PUBKEY: + fprintf(f, "missing pubkey\n"); + continue; + default: + fprintf(f, "unknown\n"); + continue; + } + } +} + +static inline retvalue verify_signature(const struct signature_requirement *requirements, const char *releasegpg, const char *releasename) { + gpgme_verify_result_t result; + int i; + const struct signature_requirement *req; + + result = gpgme_op_verify_result(context); + if (result == NULL) { + fprintf(stderr, +"Internal error communicating with libgpgme: no result record!\n\n"); + return RET_ERROR_GPGME; + } + + for (req = requirements ; req != NULL ; req = req->next) { + bool fulfilled = false; + + /* check first for good signatures, and then for good enough + signatures, to not pester the user with warnings of one + of the alternate keys, if the last one is good enough */ + + for (i = 0 ; (size_t)i < req->num_keys ; i++) { + + if (key_good(&req->keys[i], result->signatures)) { + fulfilled = true; + break; + } + } + for (i = 0 ; !fulfilled && (size_t)i < req->num_keys ; i++) { + + if (key_good_enough(&req->keys[i], result->signatures, + releasegpg, releasename)) { + fulfilled = true; + break; + } + } + if (!fulfilled) { + fprintf(stderr, +"ERROR: Condition '%s' not fulfilled for '%s'.\n", + req->condition, releasegpg); + print_signatures(stderr, result->signatures, + releasegpg); + return RET_ERROR_BADSIG; + } + if (verbose > 10) { + fprintf(stdout, "Condition '%s' fulfilled for '%s'.\n", + req->condition, releasegpg); + } + } + if (verbose > 20) + print_signatures(stdout, result->signatures, releasegpg); + return RET_OK; +} + +retvalue signature_check(const struct signature_requirement *requirements, const char *releasegpg, const char *releasename, const char *releasedata, size_t releaselen) { + gpg_error_t err; + int gpgfd; + gpgme_data_t dh, dh_gpg; + + assert (requirements != NULL); + + if (FAILEDTOALLOC(releasedata) || FAILEDTOALLOC(releasegpg)) + return RET_ERROR_OOM; + + assert (context != NULL); + + /* Read the file and its signature into memory: */ + gpgfd = open(releasegpg, O_RDONLY|O_NOCTTY); + if (gpgfd < 0) { + int e = errno; + fprintf(stderr, "Error opening '%s': %s\n", + releasegpg, strerror(e)); + return RET_ERRNO(e); + } + err = gpgme_data_new_from_fd(&dh_gpg, gpgfd); + if (err != 0) { + (void)close(gpgfd); + fprintf(stderr, "Error reading '%s':\n", releasegpg); + return gpgerror(err); + } + err = gpgme_data_new_from_mem(&dh, releasedata, releaselen, 0); + if (err != 0) { + gpgme_data_release(dh_gpg); + return gpgerror(err); + } + + /* Verify the signature */ + + err = gpgme_op_verify(context, dh_gpg, dh, NULL); + gpgme_data_release(dh_gpg); + gpgme_data_release(dh); + close(gpgfd); + if (err != 0) { + fprintf(stderr, "Error verifying '%s':\n", releasegpg); + return gpgerror(err); + } + + return verify_signature(requirements, releasegpg, releasename); +} + +retvalue signature_check_inline(const struct signature_requirement *requirements, const char *filename, char **chunk_p) { + gpg_error_t err; + gpgme_data_t dh, dh_gpg; + int fd; + + fd = open(filename, O_RDONLY|O_NOCTTY); + if (fd < 0) { + int e = errno; + fprintf(stderr, "Error opening '%s': %s\n", + filename, strerror(e)); + return RET_ERRNO(e); + } + err = gpgme_data_new_from_fd(&dh_gpg, fd); + if (err != 0) { + (void)close(fd); + return gpgerror(err); + } + + err = gpgme_data_new(&dh); + if (err != 0) { + (void)close(fd); + gpgme_data_release(dh_gpg); + return gpgerror(err); + } + err = gpgme_op_verify(context, dh_gpg, NULL, dh); + (void)close(fd); + if (gpg_err_code(err) == GPG_ERR_NO_DATA) { + char *chunk; const char *n; + size_t len; + retvalue r; + + gpgme_data_release(dh); + gpgme_data_release(dh_gpg); + + r = readtextfile(filename, filename, &chunk, &len); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) + return r; + + assert (chunk[len] == '\0'); + len = chunk_extract(chunk, chunk, len, false, &n); + if (chunk[0] == '-' || *n != '\0') { + fprintf(stderr, +"Cannot parse '%s': found no signature but does not looks safe to be assumed unsigned, either.\n", + filename); + free(chunk); + return RET_ERROR; + } + if (requirements != NULL) { + free(chunk); + return RET_ERROR_BADSIG; + } + fprintf(stderr, +"WARNING: No signature found in %s, assuming it is unsigned!\n", + filename); + assert (chunk[len] == '\0'); + *chunk_p = realloc(chunk, len+1); + if (FAILEDTOALLOC(*chunk_p)) + *chunk_p = chunk; + return RET_OK; + } else { + char *plain_data, *chunk; + const char *n; + size_t plain_len, len; + retvalue r; + + if (err != 0) { + gpgme_data_release(dh_gpg); + gpgme_data_release(dh); + return gpgerror(err); + } + gpgme_data_release(dh_gpg); + plain_data = gpgme_data_release_and_get_mem(dh, &plain_len); + if (plain_data == NULL) { + fprintf(stderr, +"Error: libgpgme failed to extract the plain data out of\n" +"'%s'.\n" +"While it did so in a way indicating running out of memory, experience says\n" +"this also happens when gpg returns a error code it does not understand.\n" +"To check this please try running gpg --verify '%s' manually.\n" +"Continuing extracting it ignoring all signatures...", + filename, filename); + return RET_ERROR; + } + chunk = malloc(plain_len+1); + if (FAILEDTOALLOC(chunk)) + return RET_ERROR_OOM; + len = chunk_extract(chunk, plain_data, plain_len, false, &n); +#ifdef HAVE_GPGPME_FREE + gpgme_free(plain_data); +#else + free(plain_data); +#endif + assert (len <= plain_len); + if (plain_len != (size_t)(n - plain_data)) { + fprintf(stderr, +"Cannot parse '%s': extraced signed data looks malformed.\n", + filename); + r = RET_ERROR; + } else + r = verify_signature(requirements, filename, NULL); + if (RET_IS_OK(r)) { + *chunk_p = realloc(chunk, len+1); + if (FAILEDTOALLOC(*chunk_p)) + *chunk_p = chunk; + } else + free(chunk); + return r; + } +} +#else /* HAVE_LIBGPGME */ + +retvalue signature_check(const struct signature_requirement *requirements, const char *releasegpg, const char *releasename, const char *releasedata, size_t releaselen) { + assert (requirements != NULL); + + if (FAILEDTOALLOC(releasedata) || FAILEDTOALLOC(releasegpg)) + return RET_ERROR_OOM; + fprintf(stderr, +"ERROR: Cannot check signatures as this reprepro binary is compiled with support\n" +"for libgpgme.\n"); // TODO: "Only running external programs is supported.\n" + return RET_ERROR_GPGME; +} + +retvalue signature_check_inline(const struct signature_requirement *requirements, const char *filename, char **chunk_p) { + retvalue r; + char *chunk; size_t len; + const char *n; + + if (requirements != NULL) { + fprintf(stderr, +"ERROR: Cannot check signatures as this reprepro binary is compiled with support\n" +"for libgpgme.\n"); + return RET_ERROR_GPGME; + } + r = readtextfile(filename, filename, &chunk, &len); + assert (r != RET_NOTHING); + if (RET_WAS_ERROR(r)) + return r; + assert (chunk[len] == '\0'); + + len = chunk_extract(chunk, chunk, len, false, &n); + if (len == 0) { + fprintf(stderr, "Could not find any data within '%s'!\n", + filename); + free(chunk); + return RET_ERROR; + } + if (chunk[0] == '-') { + const char *endmarker; + + if (len < 10 || memcmp(chunk, "-----BEGIN", 10) != 0) { + fprintf(stderr, +"Strange content of '%s': First non-space character is '-',\n" +"but it does not begin with '-----BEGIN'.\n", filename); + free(chunk); + return RET_ERROR; + } + len = chunk_extract(chunk, n, strlen(n), false, &n); + + endmarker = strstr(chunk, "\n-----"); + if (endmarker != NULL) { + endmarker++; + assert ((size_t)(endmarker-chunk) < len); + len = endmarker-chunk; + chunk[len] = '\0'; + } else if (*n == '\0') { + fprintf(stderr, +"ERROR: Could not find end marker of signed data within '%s'.\n" +"Cannot determine what is data and what is not!\n", + filename); + free(chunk); + return RET_ERROR; + } else if (strncmp(n, "-----", 5) != 0) { + fprintf(stderr, +"ERROR: Spurious empty line within '%s'.\n" +"Cannot determine what is data and what is not!\n", + filename); + free(chunk); + return RET_ERROR; + } + } else { + if (*n != '\0') { + fprintf(stderr, +"Cannot parse '%s': found no signature but does not looks safe to be assumed unsigned, either.\n", + filename); + return RET_ERROR; + } + fprintf(stderr, +"WARNING: No signature found in %s, assuming it is unsigned!\n", + filename); + } + assert (chunk[len] == '\0'); + *chunk_p = realloc(chunk, len+1); + if (FAILEDTOALLOC(*chunk_p)) + *chunk_p = chunk; + return RET_OK; +} + +void signature_requirements_free(/*@only@*/struct signature_requirement *p) { + free(p); +} + +retvalue signature_requirement_add(UNUSED(struct signature_requirement **x), const char *condition) { + if (condition == NULL || strcmp(condition, "blindtrust") == 0) + return RET_NOTHING; + + fprintf(stderr, +"ERROR: Cannot check signatures as this reprepro binary is compiled with support\n" +"for libgpgme.\n"); // TODO: "Only running external programs is supported.\n" + return RET_ERROR_GPGME; +} + +void free_known_keys(void) { +} + +#endif /* HAVE_LIBGPGME */ |