Added support for the long awaited feature: RSA signed repositories.
This commit is contained in:
parent
ae2eea8937
commit
8d5c48b861
17
NEWS
17
NEWS
@ -1,3 +1,20 @@
|
||||
xbps-0.27 (???):
|
||||
|
||||
* Support for RSA signed repositories. A repository can be signed with your
|
||||
preferred RSA key (any ssh key works) as follows:
|
||||
|
||||
$ xbps-rindex -s --signedby "foobar <foo@bar>" --privkey /priv/key /path/to/repo
|
||||
|
||||
The first time xbps-install(8) access to a signed repository it will ask you
|
||||
to import its public key to verify the signature. Please double-check the
|
||||
hex fingerprint of the public key is the real one!
|
||||
|
||||
Once the public key has been imported it's not expected to change, hence if the
|
||||
repository index has been modified or signed with another key, it will be ignored.
|
||||
|
||||
Starting from now on all remote repositories must be signed and verified.
|
||||
Local repos do not need to be signed and they will work as before.
|
||||
|
||||
xbps-0.26 (2013-09-25):
|
||||
|
||||
* xbps-query(8): the `-D --defrepo' argument has been superseded by
|
||||
|
5
TODO
5
TODO
@ -7,9 +7,6 @@ xbps-create:
|
||||
- Move all configuration files to <prefix>/share/<pkgname>/conf/<cffile>.
|
||||
- Add -i --installed option to create binpkg from an installed version.
|
||||
|
||||
All:
|
||||
- Add support to sign with PGP or RSA the repository index files.
|
||||
|
||||
Issues listed at http://code.google.com/p/xbps/issues/list
|
||||
Issues listed at https://github.com/xtraeme/xbps/issues
|
||||
|
||||
Surely more stuff...
|
||||
|
@ -48,7 +48,7 @@ bool noyes(const char *, ...);
|
||||
void fetch_file_progress_cb(struct xbps_fetch_cb_data *, void *);
|
||||
|
||||
/* from state_cb.c */
|
||||
void state_cb(struct xbps_state_cb_data *, void *);
|
||||
int state_cb(struct xbps_state_cb_data *, void *);
|
||||
|
||||
/* From util.c */
|
||||
void print_package_line(const char *, int, bool);
|
||||
|
@ -31,13 +31,14 @@
|
||||
#include <xbps.h>
|
||||
#include "defs.h"
|
||||
|
||||
void
|
||||
int
|
||||
state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
{
|
||||
xbps_dictionary_t pkgd;
|
||||
const char *instver, *newver;
|
||||
char *pkgname;
|
||||
bool syslog_enabled = false;
|
||||
int rv = 0;
|
||||
|
||||
if (xscd->xhp->flags & XBPS_FLAG_SYSLOG) {
|
||||
syslog_enabled = true;
|
||||
@ -46,6 +47,19 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
|
||||
switch (xscd->state) {
|
||||
/* notifications */
|
||||
case XBPS_STATE_REPO_KEY_IMPORT:
|
||||
printf("%s\n", xscd->desc);
|
||||
printf("Fingerprint: ");
|
||||
xbps_print_hexfp(xscd->arg);
|
||||
printf("\n");
|
||||
rv = noyes("Do you want to import this public key?");
|
||||
break;
|
||||
case XBPS_STATE_REPO_SIGVERIFIED:
|
||||
printf("[*] RSA signature verified correctly\n");
|
||||
break;
|
||||
case XBPS_STATE_REPO_SIGUNVERIFIED:
|
||||
printf("[*] RSA signature UNVERIFIED! ignoring...\n");
|
||||
break;
|
||||
case XBPS_STATE_TRANS_DOWNLOAD:
|
||||
printf("\n[*] Downloading binary packages\n");
|
||||
break;
|
||||
@ -141,7 +155,7 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
case XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL:
|
||||
/* Ignore errors due to not empty directories */
|
||||
if (xscd->err == ENOTEMPTY)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
xbps_error_printf("%s\n", xscd->desc);
|
||||
if (syslog_enabled)
|
||||
@ -152,4 +166,6 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
"%s: unknown state %d\n", xscd->arg, xscd->state);
|
||||
break;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
@ -156,8 +156,15 @@ list_pkgs_pkgdb(struct xbps_handle *xhp)
|
||||
static int
|
||||
repo_list_uri_cb(struct xbps_repo *repo, void *arg _unused, bool *done _unused)
|
||||
{
|
||||
printf("%5zd %s\n", repo->idx ? (ssize_t)xbps_dictionary_count(repo->idx) : -1, repo->uri);
|
||||
|
||||
printf("%5zd %s",
|
||||
repo->idx ? (ssize_t)xbps_dictionary_count(repo->idx) : -1,
|
||||
repo->uri);
|
||||
if (xbps_repository_is_remote(repo->uri)) {
|
||||
printf(" (RSA %s, %s)",
|
||||
repo->is_signed ? "signed" : "unsigned",
|
||||
repo->is_verified ? "verified" : "unverified");
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -162,7 +162,7 @@ repo_ownedby_cb(struct xbps_repo *repo, void *arg, bool *done _unused)
|
||||
struct ffdata *ffd = arg;
|
||||
int rv;
|
||||
|
||||
filesd = xbps_repo_get_plist(repo, XBPS_PKGINDEX_FILES);
|
||||
filesd = xbps_repo_get_plist(repo, XBPS_REPOIDX_FILES);
|
||||
if (filesd == NULL)
|
||||
return 0;
|
||||
|
||||
|
@ -131,6 +131,9 @@ search_pkgs_cb(struct xbps_repo *repo, void *arg, bool *done _unused)
|
||||
struct search_data *sd = arg;
|
||||
int rv;
|
||||
|
||||
if (repo->idx == NULL)
|
||||
return 0;
|
||||
|
||||
allkeys = xbps_dictionary_all_keys(repo->idx);
|
||||
rv = xbps_array_foreach_cb(repo->xhp, allkeys, repo->idx, search_array_cb, sd);
|
||||
xbps_object_release(allkeys);
|
||||
|
@ -50,7 +50,7 @@ usage(bool fail)
|
||||
exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
state_cb(struct xbps_state_cb_data *xscd, void *cbd _unused)
|
||||
{
|
||||
bool syslog_enabled = false;
|
||||
@ -84,6 +84,8 @@ state_cb(struct xbps_state_cb_data *xscd, void *cbd _unused)
|
||||
"%s: unknown state %d\n", xscd->arg, xscd->state);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -62,7 +62,7 @@ usage(bool fail)
|
||||
exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
state_cb_rm(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
{
|
||||
bool syslog_enabled = false;
|
||||
@ -101,7 +101,7 @@ state_cb_rm(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
case XBPS_STATE_REMOVE_FILE_OBSOLETE_FAIL:
|
||||
/* Ignore errors due to not empty directories */
|
||||
if (xscd->err == ENOTEMPTY)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
xbps_error_printf("%s\n", xscd->desc);
|
||||
if (syslog_enabled)
|
||||
@ -112,6 +112,8 @@ state_cb_rm(struct xbps_state_cb_data *xscd, void *cbdata _unused)
|
||||
"%s: unknown state %d\n", xscd->arg, xscd->state);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -3,5 +3,7 @@ TOPDIR = ../..
|
||||
|
||||
BIN = xbps-rindex
|
||||
OBJS = main.o index-add.o index-clean.o remove-obsoletes.o repoflush.o
|
||||
OBJS += readpassphrase.o sign.o
|
||||
EXTRA_CFLAGS = -Wno-unused-result
|
||||
|
||||
include $(TOPDIR)/mk/prog.mk
|
||||
|
@ -28,23 +28,6 @@
|
||||
|
||||
#include <xbps.h>
|
||||
|
||||
/* From index-add.c */
|
||||
int index_add(struct xbps_handle *, int, char **, bool);
|
||||
|
||||
/* From index-clean.c */
|
||||
int index_clean(struct xbps_handle *, const char *);
|
||||
|
||||
/* From index-files.c */
|
||||
int index_files_add(struct xbps_handle *, int, char **);
|
||||
int index_files_clean(struct xbps_handle *, const char *);
|
||||
|
||||
/* From remove-obsoletes.c */
|
||||
int remove_obsoletes(struct xbps_handle *, const char *);
|
||||
|
||||
/* From repoflush.c */
|
||||
int repodata_flush(struct xbps_handle *, const char *,
|
||||
xbps_dictionary_t, xbps_dictionary_t);
|
||||
|
||||
/* libarchive compat */
|
||||
#if ARCHIVE_VERSION_NUMBER >= 3000000
|
||||
|
||||
@ -77,4 +60,41 @@ int repodata_flush(struct xbps_handle *, const char *,
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __UNCONST
|
||||
#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
|
||||
#endif
|
||||
|
||||
|
||||
struct repodata {
|
||||
struct archive *ar;
|
||||
char *repofile;
|
||||
char *tname;
|
||||
int repofd;
|
||||
};
|
||||
|
||||
/* From index-add.c */
|
||||
int index_add(struct xbps_handle *, int, char **, bool);
|
||||
|
||||
/* From index-clean.c */
|
||||
int index_clean(struct xbps_handle *, const char *);
|
||||
|
||||
/* From index-files.c */
|
||||
int index_files_add(struct xbps_handle *, int, char **);
|
||||
int index_files_clean(struct xbps_handle *, const char *);
|
||||
|
||||
/* From remove-obsoletes.c */
|
||||
int remove_obsoletes(struct xbps_handle *, const char *);
|
||||
|
||||
/* From sign.c */
|
||||
int sign_repo(struct xbps_handle *, const char *, const char *,
|
||||
const char *);
|
||||
|
||||
/* From readpass.c */
|
||||
char *readpassphrase(const char *, char *, size_t, int);
|
||||
|
||||
/* From repoflush.c */
|
||||
struct repodata *repodata_init(struct xbps_handle *xhp, const char *);
|
||||
int repodata_add_buf(struct repodata *, const char *, const char *);
|
||||
void repodata_flush(struct repodata *);
|
||||
|
||||
#endif /* !_XBPS_RINDEX_DEFS_H_ */
|
||||
|
@ -52,7 +52,7 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force)
|
||||
const char *oldpkgver, *arch, *oldarch;
|
||||
char *pkgver, *pkgname, *sha256, *repodir, *buf;
|
||||
char *tmprepodir;
|
||||
int rv, ret = 0;
|
||||
int rv = 0, ret = 0;
|
||||
bool flush = false, found = false;
|
||||
|
||||
idx = idxfiles = newpkgd = newpkgfilesd = curpkgd = NULL;
|
||||
@ -67,8 +67,8 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force)
|
||||
|
||||
repo = xbps_repo_open(xhp, repodir);
|
||||
if (repo != NULL) {
|
||||
idx = xbps_repo_get_plist(repo, XBPS_PKGINDEX);
|
||||
idxfiles = xbps_repo_get_plist(repo, XBPS_PKGINDEX_FILES);
|
||||
idx = xbps_repo_get_plist(repo, XBPS_REPOIDX);
|
||||
idxfiles = xbps_repo_get_plist(repo, XBPS_REPOIDX_FILES);
|
||||
}
|
||||
if (idx == NULL)
|
||||
idx = xbps_dictionary_create();
|
||||
@ -267,9 +267,19 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force)
|
||||
* Generate repository data file.
|
||||
*/
|
||||
if (flush) {
|
||||
rv = repodata_flush(xhp, repodir, idx, idxfiles);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
struct repodata *rd;
|
||||
char *xml;
|
||||
|
||||
rd = repodata_init(xhp, repodir);
|
||||
xml = xbps_dictionary_externalize(idx);
|
||||
assert(idx);
|
||||
rv = repodata_add_buf(rd, xml, XBPS_REPOIDX);
|
||||
free(xml);
|
||||
xml = xbps_dictionary_externalize(idxfiles);
|
||||
assert(idx);
|
||||
rv = repodata_add_buf(rd, xml, XBPS_REPOIDX_FILES);
|
||||
free(xml);
|
||||
repodata_flush(rd);
|
||||
}
|
||||
printf("index: %u packages registered.\n",
|
||||
xbps_dictionary_count(idx));
|
||||
@ -279,5 +289,5 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force)
|
||||
xbps_object_release(idx);
|
||||
xbps_object_release(idxfiles);
|
||||
|
||||
return 0;
|
||||
return rv;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ idx_cleaner_cb(struct xbps_handle *xhp,
|
||||
* File can be read; check its hash.
|
||||
*/
|
||||
xbps_dictionary_get_cstring_nocopy(obj,
|
||||
"filename-sha256", &sha256);
|
||||
"filename-sha256", &sha256);
|
||||
if (xbps_file_hash_check(filen, sha256) != 0)
|
||||
xbps_array_add_cstring_nocopy(cbd->array, pkgver);
|
||||
}
|
||||
@ -80,7 +80,7 @@ idx_cleaner_cb(struct xbps_handle *xhp,
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes stalled pkg entries in repository's index.plist file, if any
|
||||
* Removes stalled pkg entries in repository's XBPS_REPOIDX file, if any
|
||||
* binary package cannot be read (unavailable, not enough perms, etc).
|
||||
*/
|
||||
int
|
||||
@ -102,8 +102,8 @@ index_clean(struct xbps_handle *xhp, const char *repodir)
|
||||
fprintf(stderr, "index: cannot read repository data: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
idx = xbps_repo_get_plist(repo, XBPS_PKGINDEX);
|
||||
idxfiles = xbps_repo_get_plist(repo, XBPS_PKGINDEX_FILES);
|
||||
idx = xbps_repo_get_plist(repo, XBPS_REPOIDX);
|
||||
idxfiles = xbps_repo_get_plist(repo, XBPS_REPOIDX_FILES);
|
||||
xbps_repo_close(repo);
|
||||
if (idx == NULL || idxfiles == NULL) {
|
||||
fprintf(stderr, "incomplete repository data file!");
|
||||
@ -111,7 +111,7 @@ index_clean(struct xbps_handle *xhp, const char *repodir)
|
||||
}
|
||||
if (chdir(repodir) == -1) {
|
||||
fprintf(stderr, "index: cannot chdir to %s: %s\n",
|
||||
repodir, strerror(errno));
|
||||
repodir, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
printf("Cleaning `%s' index, please wait...\n", repodir);
|
||||
@ -134,14 +134,25 @@ index_clean(struct xbps_handle *xhp, const char *repodir)
|
||||
xbps_object_release(cbd.array);
|
||||
|
||||
if (flush) {
|
||||
rv = repodata_flush(xhp, repodir, idx, idxfiles);
|
||||
if (rv != 0)
|
||||
return rv;
|
||||
struct repodata *rd;
|
||||
char *xml;
|
||||
|
||||
rd = repodata_init(xhp, repodir);
|
||||
xml = xbps_dictionary_externalize(idx);
|
||||
assert(idx);
|
||||
rv = repodata_add_buf(rd, xml, XBPS_REPOIDX);
|
||||
free(xml);
|
||||
xml = xbps_dictionary_externalize(idxfiles);
|
||||
assert(idx);
|
||||
rv = repodata_add_buf(rd, xml, XBPS_REPOIDX_FILES);
|
||||
free(xml);
|
||||
repodata_flush(rd);
|
||||
|
||||
}
|
||||
printf("index: %u packages registered.\n",
|
||||
xbps_dictionary_count(idx));
|
||||
xbps_dictionary_count(idx));
|
||||
printf("index-files: %u packages registered.\n",
|
||||
xbps_dictionary_count(idxfiles));
|
||||
xbps_dictionary_count(idxfiles));
|
||||
|
||||
xbps_object_release(idx);
|
||||
xbps_object_release(idxfiles);
|
||||
|
@ -39,11 +39,14 @@ usage(bool fail)
|
||||
"OPTIONS\n"
|
||||
" -f --force Force mode to overwrite entry in add mode\n"
|
||||
" -h --help Show help usage\n"
|
||||
" -V --version Show XBPS version\n\n"
|
||||
" -V --version Show XBPS version\n"
|
||||
" --privkey <key> Path to the private key for signing\n"
|
||||
" --signedby <string> Signature details, i.e \"name <email>\"\n\n"
|
||||
"MODE\n"
|
||||
" -a --add <repodir/pkg> ... Add package(s) to repository index\n"
|
||||
" -c --clean <repodir> Cleans obsolete entries in repository index\n"
|
||||
" -r --remove-obsoletes <repodir> Removes obsolete packages from repository\n\n");
|
||||
" -r --remove-obsoletes <repodir> Removes obsolete packages from repository\n"
|
||||
" -s --sign <repodir> Sign repository index\n\n");
|
||||
exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@ -58,14 +61,26 @@ main(int argc, char **argv)
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "remove-obsoletes", no_argument, NULL, 'r' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ "privkey", required_argument, NULL, 0},
|
||||
{ "signedby", required_argument, NULL, 1},
|
||||
{ "sign", no_argument, NULL, 's'},
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
struct xbps_handle xh;
|
||||
const char *privkey = NULL, *signedby = NULL;
|
||||
int rv, c;
|
||||
bool clean_mode = false, add_mode = false, rm_mode = false, force = false;
|
||||
bool clean_mode, add_mode, rm_mode, sign_mode, force;
|
||||
|
||||
clean_mode = add_mode = rm_mode = sign_mode = force = false;
|
||||
|
||||
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 0:
|
||||
privkey = optarg;
|
||||
break;
|
||||
case 1:
|
||||
signedby = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
add_mode = true;
|
||||
break;
|
||||
@ -81,18 +96,22 @@ main(int argc, char **argv)
|
||||
case 'r':
|
||||
rm_mode = true;
|
||||
break;
|
||||
case 's':
|
||||
sign_mode = true;
|
||||
break;
|
||||
case 'V':
|
||||
printf("%s\n", XBPS_RELVER);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
if ((argc == optind) || (!add_mode && !clean_mode && !rm_mode)) {
|
||||
if ((argc == optind) || (!add_mode && !clean_mode && !rm_mode && !sign_mode)) {
|
||||
usage(true);
|
||||
} else if ((add_mode && (clean_mode || rm_mode)) ||
|
||||
(clean_mode && (add_mode || rm_mode)) ||
|
||||
(rm_mode && (add_mode || clean_mode))) {
|
||||
fprintf(stderr, "Only one mode can be specified: add, clean "
|
||||
"or remove-obsoletes.\n");
|
||||
(rm_mode && (add_mode || clean_mode)) ||
|
||||
(sign_mode && (add_mode || clean_mode || rm_mode))) {
|
||||
fprintf(stderr, "Only one mode can be specified: add, clean, "
|
||||
"remove-obsoletes or sign.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -110,6 +129,8 @@ main(int argc, char **argv)
|
||||
rv = index_clean(&xh, argv[optind]);
|
||||
else if (rm_mode)
|
||||
rv = remove_obsoletes(&xh, argv[optind]);
|
||||
else if (sign_mode)
|
||||
rv = sign_repo(&xh, argv[optind], privkey, signedby);
|
||||
|
||||
exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
}
|
||||
|
123
bin/xbps-rindex/readpassphrase.c
Normal file
123
bin/xbps-rindex/readpassphrase.c
Normal file
@ -0,0 +1,123 @@
|
||||
/* $NetBSD: readpassphrase.c,v 1.1 2009/06/07 22:38:47 christos Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2000 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
#define RPP_ECHO_OFF 0x00 /* Turn off echo (default). */
|
||||
#define RPP_ECHO_ON 0x01 /* Leave echo on. */
|
||||
#define RPP_REQUIRE_TTY 0x02 /* Fail if there is no tty. */
|
||||
#define RPP_FORCELOWER 0x04 /* Force input to lower case. */
|
||||
#define RPP_FORCEUPPER 0x08 /* Force input to upper case. */
|
||||
#define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */
|
||||
|
||||
char *
|
||||
readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
|
||||
{
|
||||
struct termios term, oterm;
|
||||
char ch, *p, *end;
|
||||
int input, output;
|
||||
sigset_t oset, nset;
|
||||
|
||||
/* I suppose we could alloc on demand in this case (XXX). */
|
||||
if (bufsiz == 0) {
|
||||
errno = EINVAL;
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read and write to /dev/tty if available. If not, read from
|
||||
* stdin and write to stderr unless a tty is required.
|
||||
*/
|
||||
if ((input = output = open(_PATH_TTY, O_RDWR)) == -1) {
|
||||
if (flags & RPP_REQUIRE_TTY) {
|
||||
errno = ENOTTY;
|
||||
return(NULL);
|
||||
}
|
||||
input = STDIN_FILENO;
|
||||
output = STDERR_FILENO;
|
||||
}
|
||||
|
||||
/*
|
||||
* We block SIGINT and SIGTSTP so the terminal is not left
|
||||
* in an inconsistent state (ie: no echo). It would probably
|
||||
* be better to simply catch these though.
|
||||
*/
|
||||
sigemptyset(&nset);
|
||||
sigaddset(&nset, SIGINT);
|
||||
sigaddset(&nset, SIGTSTP);
|
||||
(void)sigprocmask(SIG_BLOCK, &nset, &oset);
|
||||
|
||||
/* Turn off echo if possible. */
|
||||
if (tcgetattr(input, &oterm) == 0) {
|
||||
memcpy(&term, &oterm, sizeof(term));
|
||||
if (!(flags & RPP_ECHO_ON) && (term.c_lflag & ECHO))
|
||||
term.c_lflag &= ~ECHO;
|
||||
(void)tcsetattr(input, TCSAFLUSH, &term);
|
||||
} else {
|
||||
memset(&term, 0, sizeof(term));
|
||||
memset(&oterm, 0, sizeof(oterm));
|
||||
}
|
||||
|
||||
(void)write(output, prompt, strlen(prompt));
|
||||
end = buf + bufsiz - 1;
|
||||
for (p = buf; read(input, &ch, 1) == 1 && ch != '\n' && ch != '\r';) {
|
||||
if (p < end) {
|
||||
if ((flags & RPP_SEVENBIT))
|
||||
ch &= 0x7f;
|
||||
if (isalpha((unsigned char)ch)) {
|
||||
if ((flags & RPP_FORCELOWER))
|
||||
ch = tolower((unsigned char)ch);
|
||||
if ((flags & RPP_FORCEUPPER))
|
||||
ch = toupper((unsigned char)ch);
|
||||
}
|
||||
*p++ = ch;
|
||||
}
|
||||
}
|
||||
*p = '\0';
|
||||
if (!(term.c_lflag & ECHO))
|
||||
(void)write(output, "\n", 1);
|
||||
|
||||
/* Restore old terminal settings and signal mask. */
|
||||
if (memcmp(&term, &oterm, sizeof(term)) != 0)
|
||||
(void)tcsetattr(input, TCSAFLUSH, &oterm);
|
||||
(void)sigprocmask(SIG_SETMASK, &oset, NULL);
|
||||
if (input != STDIN_FILENO)
|
||||
(void)close(input);
|
||||
|
||||
return(buf);
|
||||
}
|
@ -119,7 +119,7 @@ remove_obsoletes(struct xbps_handle *xhp, const char *repodir)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if ((repo->idx = xbps_repo_get_plist(repo, XBPS_PKGINDEX)) == NULL) {
|
||||
if ((repo->idx = xbps_repo_get_plist(repo, XBPS_REPOIDX)) == NULL) {
|
||||
xbps_repo_close(repo);
|
||||
return -1;
|
||||
}
|
||||
|
@ -37,58 +37,54 @@
|
||||
#include <xbps.h>
|
||||
#include "defs.h"
|
||||
|
||||
int
|
||||
repodata_flush(struct xbps_handle *xhp, const char *repodir,
|
||||
xbps_dictionary_t idx, xbps_dictionary_t idxfiles)
|
||||
struct repodata *
|
||||
repodata_init(struct xbps_handle *xhp, const char *repodir)
|
||||
{
|
||||
struct archive *ar;
|
||||
mode_t myumask;
|
||||
char *repofile, *tname, *xml;
|
||||
int repofd;
|
||||
struct repodata *rd;
|
||||
|
||||
rd = malloc(sizeof(struct repodata));
|
||||
assert(rd);
|
||||
|
||||
/* Create a tempfile for our repository archive */
|
||||
repofile = xbps_repo_path(xhp, repodir);
|
||||
tname = xbps_xasprintf("%s.XXXXXXXXXX", repofile);
|
||||
if ((repofd = mkstemp(tname)) == -1)
|
||||
return errno;
|
||||
rd->repofile = xbps_repo_path(xhp, repodir);
|
||||
rd->tname = xbps_xasprintf("%s.XXXXXXXXXX", rd->repofile);
|
||||
if ((rd->repofd = mkstemp(rd->tname)) == -1) {
|
||||
free(rd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create and write our repository archive */
|
||||
ar = archive_write_new();
|
||||
assert(ar);
|
||||
archive_write_set_compression_gzip(ar);
|
||||
archive_write_set_format_pax_restricted(ar);
|
||||
archive_write_set_options(ar, "compression-level=9");
|
||||
archive_write_open_fd(ar, repofd);
|
||||
rd->ar = archive_write_new();
|
||||
assert(rd->ar);
|
||||
archive_write_set_compression_gzip(rd->ar);
|
||||
archive_write_set_format_pax_restricted(rd->ar);
|
||||
archive_write_set_options(rd->ar, "compression-level=9");
|
||||
archive_write_open_fd(rd->ar, rd->repofd);
|
||||
|
||||
xml = xbps_dictionary_externalize(idx);
|
||||
assert(xml);
|
||||
if (xbps_archive_append_buf(ar, xml, strlen(xml),
|
||||
XBPS_PKGINDEX, 0644, "root", "root") != 0) {
|
||||
free(xml);
|
||||
return -1;
|
||||
}
|
||||
free(xml);
|
||||
return rd;
|
||||
}
|
||||
|
||||
xml = xbps_dictionary_externalize(idxfiles);
|
||||
assert(xml);
|
||||
if (xbps_archive_append_buf(ar, xml, strlen(xml),
|
||||
XBPS_PKGINDEX_FILES, 0644, "root", "root") != 0) {
|
||||
free(xml);
|
||||
return -1;
|
||||
}
|
||||
free(xml);
|
||||
int
|
||||
repodata_add_buf(struct repodata *rd, const char *buf, const char *filename)
|
||||
{
|
||||
return xbps_archive_append_buf(rd->ar, buf, strlen(buf),
|
||||
filename, 0644, "root", "root");
|
||||
}
|
||||
|
||||
archive_write_finish(ar);
|
||||
void
|
||||
repodata_flush(struct repodata *rd)
|
||||
{
|
||||
mode_t myumask;
|
||||
|
||||
/* Write data to tempfile and rename */
|
||||
fdatasync(repofd);
|
||||
archive_write_finish(rd->ar);
|
||||
fdatasync(rd->repofd);
|
||||
myumask = umask(0);
|
||||
(void)umask(myumask);
|
||||
assert(fchmod(repofd, 0666 & ~myumask) != -1);
|
||||
close(repofd);
|
||||
rename(tname, repofile);
|
||||
free(repofile);
|
||||
free(tname);
|
||||
|
||||
return 0;
|
||||
assert(fchmod(rd->repofd, 0666 & ~myumask) != -1);
|
||||
close(rd->repofd);
|
||||
rename(rd->tname, rd->repofile);
|
||||
free(rd->repofile);
|
||||
free(rd->tname);
|
||||
free(rd);
|
||||
}
|
||||
|
231
bin/xbps-rindex/sign.c
Normal file
231
bin/xbps-rindex/sign.c
Normal file
@ -0,0 +1,231 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Juan Romero Pardines.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include "defs.h"
|
||||
|
||||
static int
|
||||
password_cb(char *buf, int size)
|
||||
{
|
||||
int len = 0;
|
||||
char pass[BUFSIZ];
|
||||
|
||||
if (readpassphrase("Enter passphrase: ", pass, BUFSIZ, 0) == NULL)
|
||||
return 0;
|
||||
|
||||
len = strlen(pass);
|
||||
|
||||
if (len <= 0)
|
||||
return 0;
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
memset(buf, '\0', size);
|
||||
memcpy(buf, pass, len);
|
||||
memset(&pass, 0, BUFSIZ);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static RSA *
|
||||
load_rsa_privkey(const char *path)
|
||||
{
|
||||
FILE *fp;
|
||||
RSA *rsa = NULL;
|
||||
|
||||
if ((fp = fopen(path, "r")) == 0)
|
||||
return NULL;
|
||||
|
||||
if ((rsa = RSA_new()) == NULL) {
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rsa = PEM_read_RSAPrivateKey(fp, 0,
|
||||
(pem_password_cb *)password_cb,
|
||||
__UNCONST(path));
|
||||
fclose(fp);
|
||||
return rsa;
|
||||
}
|
||||
|
||||
static char *
|
||||
pubkey_from_privkey(RSA *rsa)
|
||||
{
|
||||
BIO *bp;
|
||||
char *buf;
|
||||
|
||||
bp = BIO_new(BIO_s_mem());
|
||||
assert(bp);
|
||||
|
||||
if (!PEM_write_bio_RSA_PUBKEY(bp, rsa)) {
|
||||
fprintf(stderr, "error writing public key: %s\n",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
BIO_free(bp);
|
||||
return NULL;
|
||||
}
|
||||
/* XXX (xtraeme) 8192 should be always enough? */
|
||||
buf = malloc(8192);
|
||||
assert(buf);
|
||||
BIO_read(bp, buf, 8192);
|
||||
BIO_free(bp);
|
||||
ERR_free_strings();
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static RSA *
|
||||
rsa_sign_buf(const char *privkey, const char *buf,
|
||||
unsigned char **sigret, unsigned int *siglen)
|
||||
{
|
||||
SHA256_CTX context;
|
||||
RSA *rsa;
|
||||
unsigned char sha256[SHA256_DIGEST_LENGTH];
|
||||
|
||||
ERR_load_crypto_strings();
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OpenSSL_add_all_ciphers();
|
||||
OpenSSL_add_all_digests();
|
||||
|
||||
rsa = load_rsa_privkey(privkey);
|
||||
if (rsa == NULL) {
|
||||
fprintf(stderr, "can't load private key from %s\n", privkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SHA256_Init(&context);
|
||||
SHA256_Update(&context, buf, strlen(buf));
|
||||
SHA256_Final(sha256, &context);
|
||||
|
||||
*sigret = calloc(1, RSA_size(rsa) + 1);
|
||||
if (RSA_sign(NID_sha1, sha256, sizeof(sha256),
|
||||
*sigret, siglen, rsa) == 0) {
|
||||
fprintf(stderr, "%s: %s\n", privkey,
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
return NULL;
|
||||
}
|
||||
return rsa;
|
||||
}
|
||||
|
||||
int
|
||||
sign_repo(struct xbps_handle *xhp, const char *repodir,
|
||||
const char *privkey, const char *signedby)
|
||||
{
|
||||
RSA *rsa = NULL;
|
||||
struct repodata *rd;
|
||||
struct xbps_repo *repo;
|
||||
xbps_dictionary_t idx, meta;
|
||||
xbps_data_t data;
|
||||
unsigned int siglen;
|
||||
unsigned char *sig;
|
||||
char *buf, *xml, *defprivkey = NULL;
|
||||
int rv = 0;
|
||||
|
||||
if (signedby == NULL) {
|
||||
fprintf(stderr, "--signedby unset! cannot sign repository\n");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Check that repository index exists and not empty, otherwise bail out.
|
||||
*/
|
||||
repo = xbps_repo_open(xhp, repodir);
|
||||
if (repo == NULL) {
|
||||
fprintf(stderr, "cannot read repository data: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
idx = xbps_repo_get_plist(repo, XBPS_REPOIDX);
|
||||
xbps_repo_close(repo);
|
||||
if (xbps_dictionary_count(idx) == 0) {
|
||||
fprintf(stderr, "invalid number of objects in repository index!\n");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Externalize the index and then sign it.
|
||||
*/
|
||||
xml = xbps_dictionary_externalize(idx);
|
||||
if (xml == NULL) {
|
||||
fprintf(stderr, "failed to externalize repository index: %s\n", strerror(errno));
|
||||
xbps_object_release(idx);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* If privkey not set, default to ~/.ssh/id_rsa.
|
||||
*/
|
||||
if (privkey == NULL)
|
||||
defprivkey = xbps_xasprintf("%s/.ssh/id_rsa", getenv("HOME"));
|
||||
else
|
||||
defprivkey = strdup(privkey);
|
||||
|
||||
rsa = rsa_sign_buf(defprivkey, xml, &sig, &siglen);
|
||||
if (rsa == NULL) {
|
||||
free(xml);
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
* Prepare the XBPS_REPOMETA for our repository data.
|
||||
*/
|
||||
meta = xbps_dictionary_create();
|
||||
xbps_dictionary_set_cstring_nocopy(meta, "signature-by", signedby);
|
||||
xbps_dictionary_set_cstring_nocopy(meta, "signature-type", "rsa");
|
||||
data = xbps_data_create_data_nocopy(sig, siglen);
|
||||
xbps_dictionary_set(meta, "signature", data);
|
||||
|
||||
buf = pubkey_from_privkey(rsa);
|
||||
assert(buf);
|
||||
data = xbps_data_create_data_nocopy(buf, strlen(buf));
|
||||
xbps_dictionary_set(meta, "public-key", data);
|
||||
xbps_dictionary_set_uint16(meta, "public-key-size", RSA_size(rsa) * 8);
|
||||
|
||||
/*
|
||||
* and finally write our repodata file!
|
||||
*/
|
||||
rd = repodata_init(xhp, repodir);
|
||||
assert(rd);
|
||||
xml = xbps_dictionary_externalize(idx);
|
||||
assert(xml);
|
||||
rv = repodata_add_buf(rd, xml, XBPS_REPOIDX);
|
||||
free(xml);
|
||||
xml = xbps_dictionary_externalize(meta);
|
||||
assert(xml);
|
||||
rv = repodata_add_buf(rd, xml, XBPS_REPOMETA);
|
||||
free(xml);
|
||||
repodata_flush(rd);
|
||||
free(buf);
|
||||
RSA_free(rsa);
|
||||
|
||||
return rv;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
.Dd August 18, 2013
|
||||
.Dd October 5, 2013
|
||||
.Os Void Linux
|
||||
.Dt xbps-rindex 8
|
||||
.Sh NAME
|
||||
@ -38,6 +38,17 @@ Removes obsolete packages from
|
||||
.Ar repository .
|
||||
Packages that are not currently registered in repository's index will
|
||||
be removed (out of date, invalid archives, etc).
|
||||
.It Sy -s, --sign
|
||||
Signs a repository with your specified RSA key. If
|
||||
.Fl --privkey
|
||||
argument not set, it defaults to
|
||||
.Sy ~/.ssh/id_rsa .
|
||||
.It Sy --signedby Ar string
|
||||
This is required to sign a repository, use something like
|
||||
.Ar name <email> .
|
||||
.It Sy --privkey Ar key
|
||||
Path to the private RSA key to sign the repository. If unset, defaults to
|
||||
.Sy ~/.ssh/id_rsa .
|
||||
.Sh ENVIRONMENT
|
||||
.Bl -tag -width XBPS_TARGET_ARCH
|
||||
.It Sy XBPS_TARGET_ARCH
|
||||
|
@ -46,7 +46,7 @@
|
||||
*
|
||||
* This header documents the full API for the XBPS Library.
|
||||
*/
|
||||
#define XBPS_API_VERSION "20130918"
|
||||
#define XBPS_API_VERSION "20131005"
|
||||
|
||||
#ifndef XBPS_VERSION
|
||||
#define XBPS_VERSION "UNSET"
|
||||
@ -80,6 +80,12 @@
|
||||
*/
|
||||
#define XBPS_PKGDB "pkgdb-0.21.plist"
|
||||
|
||||
/**
|
||||
* @def XBPS_REPOKEYS
|
||||
* Filename for the repository keys.
|
||||
*/
|
||||
#define XBPS_REPOKEYS "repokeys.plist"
|
||||
|
||||
/**
|
||||
* @def XBPS_PKGPROPS
|
||||
* Filename for package metadata property list.
|
||||
@ -93,16 +99,22 @@
|
||||
#define XBPS_PKGFILES "files.plist"
|
||||
|
||||
/**
|
||||
* @def XBPS_PKGINDEX
|
||||
* Filename for the repository package index property list.
|
||||
* @def XBPS_REPOIDX
|
||||
* Filename for the repository index property list.
|
||||
*/
|
||||
#define XBPS_PKGINDEX "index.plist"
|
||||
#define XBPS_REPOIDX "index.plist"
|
||||
|
||||
/**
|
||||
* @def XBPS_PKGINDEX_FILES
|
||||
* Filename for the repository package index files property list.
|
||||
* @def XBPS_REPOIDX_FILES
|
||||
* Filename for the repository index files property list.
|
||||
*/
|
||||
#define XBPS_PKGINDEX_FILES "index-files.plist"
|
||||
#define XBPS_REPOIDX_FILES "index-files.plist"
|
||||
|
||||
/**
|
||||
* @def XBPS_REPOMETA
|
||||
* Filename for the repository metadata property list.
|
||||
*/
|
||||
#define XBPS_REPOMETA "meta.plist"
|
||||
|
||||
/**
|
||||
* @def XBPS_SYSCONF_PATH
|
||||
@ -238,6 +250,9 @@ extern "C" {
|
||||
* - XBPS_STATE_UPDATE_FAIL: package update has failed.
|
||||
* - XBPS_STATE_UNPACK_FAIL: package unpack has failed.
|
||||
* - XBPS_STATE_REPOSYNC_FAIL: syncing remote repositories has failed.
|
||||
* - XBPS_STATE_REPO_KEY_IMPORT: repository is signed and needs to import pubkey.
|
||||
* - XBPS_STATE_REPO_SIGVERIFIED: repository is signed and verified.
|
||||
* - XBPS_STATE_REPO_SIGUNVERIFIED: repository is signed and UNVERIFIED.
|
||||
*/
|
||||
typedef enum xbps_state {
|
||||
XBPS_STATE_UNKNOWN = 0,
|
||||
@ -274,7 +289,10 @@ typedef enum xbps_state {
|
||||
XBPS_STATE_UPDATE_FAIL,
|
||||
XBPS_STATE_UNPACK_FAIL,
|
||||
XBPS_STATE_REPOSYNC_FAIL,
|
||||
XBPS_STATE_CONFIGURE_DONE
|
||||
XBPS_STATE_CONFIGURE_DONE,
|
||||
XBPS_STATE_REPO_KEY_IMPORT,
|
||||
XBPS_STATE_REPO_SIGVERIFIED,
|
||||
XBPS_STATE_REPO_SIGUNVERIFIED
|
||||
} xbps_state_t;
|
||||
|
||||
/**
|
||||
@ -485,7 +503,7 @@ struct xbps_handle {
|
||||
* Pointer to the supplifed function callback to be used
|
||||
* in the XBPS possible states.
|
||||
*/
|
||||
void (*state_cb)(struct xbps_state_cb_data *, void *);
|
||||
int (*state_cb)(struct xbps_state_cb_data *, void *);
|
||||
/**
|
||||
* @var state_cb_data
|
||||
*
|
||||
@ -1086,6 +1104,7 @@ xbps_dictionary_t xbps_get_pkg_plist_from_binpkg(const char *fname,
|
||||
/** @addtogroup repopool */
|
||||
/*@{*/
|
||||
|
||||
|
||||
/**
|
||||
* @struct xbps_repo xbps.h "xbps.h"
|
||||
* @brief Repository structure
|
||||
@ -1110,17 +1129,36 @@ struct xbps_repo {
|
||||
* Proplib dictionary associated with the repository index files.
|
||||
*/
|
||||
xbps_dictionary_t idxfiles;
|
||||
/**
|
||||
* @var meta
|
||||
*
|
||||
* Proplib dictionary associated with the repository metadata.
|
||||
*/
|
||||
xbps_dictionary_t meta;
|
||||
/**
|
||||
* @var uri
|
||||
*
|
||||
* URI string associated with repository.
|
||||
*/
|
||||
const char *uri;
|
||||
/**
|
||||
* var is_signed
|
||||
*
|
||||
* True if this repository has been signed, false otherwise.
|
||||
* (read-only).
|
||||
*/
|
||||
bool is_signed;
|
||||
/**
|
||||
* var is_verified
|
||||
*
|
||||
* True if this repository has been signed and verified against its public key.
|
||||
* False if the stored public key did not match its signature.
|
||||
*/
|
||||
bool is_verified;
|
||||
/**
|
||||
* @var xhp
|
||||
*
|
||||
* Pointer to our xbps_handle struct passed to xbps_rpool_foreach.
|
||||
* (read-only).
|
||||
*/
|
||||
struct xbps_handle *xhp;
|
||||
};
|
||||
@ -1154,8 +1192,8 @@ int xbps_rpool_sync(struct xbps_handle *xhp, const char *uri);
|
||||
* @return 0 on success, otherwise an errno value.
|
||||
*/
|
||||
int xbps_rpool_foreach(struct xbps_handle *xhp,
|
||||
int (*fn)(struct xbps_repo *, void *, bool *),
|
||||
void *arg);
|
||||
int (*fn)(struct xbps_repo *, void *, bool *),
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* Finds a package dictionary in the repository pool by specifying a
|
||||
@ -1617,6 +1655,24 @@ int xbps_humanize_number(char *buf, int64_t bytes);
|
||||
*/
|
||||
int xbps_cmpver(const char *pkg1, const char *pkg2);
|
||||
|
||||
/**
|
||||
* Converts a RSA public key in PEM format to a hex fingerprint.
|
||||
*
|
||||
* @param[in] xhp The pointer to an xbps_handle struct.
|
||||
* @param[in] pubkey The public-key in PEM format as xbps_data_t.
|
||||
*
|
||||
* @return The hex fingerprint. The returned buffer must be free(3)d
|
||||
* when necessary.
|
||||
*/
|
||||
unsigned char *xbps_pubkey2fp(struct xbps_handle *xhp, xbps_data_t pubkey);
|
||||
|
||||
/**
|
||||
* Prints to stdout the hex fingerprint of a public key.
|
||||
*
|
||||
* @param[in] fp String returned by xbps_pubkey2fp();
|
||||
*/
|
||||
void xbps_print_hexfp(const char *fp);
|
||||
|
||||
/*@}*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -159,6 +159,14 @@ int HIDDEN xbps_entry_install_conf_file(struct xbps_handle *,
|
||||
const char *,
|
||||
const char *,
|
||||
const char *);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* From lib/repo_keys.c
|
||||
*/
|
||||
int HIDDEN xbps_repo_key_import(struct xbps_repo *);
|
||||
int HIDDEN xbps_repo_key_verify(struct xbps_repo *);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* From lib/repo_pkgdeps.c
|
||||
@ -225,8 +233,8 @@ int HIDDEN xbps_file_exec(struct xbps_handle *, const char *, ...);
|
||||
*/
|
||||
void HIDDEN xbps_set_cb_fetch(struct xbps_handle *, off_t, off_t, off_t,
|
||||
const char *, bool, bool, bool);
|
||||
void HIDDEN xbps_set_cb_state(struct xbps_handle *, xbps_state_t, int,
|
||||
const char *, const char *, ...);
|
||||
int HIDDEN xbps_set_cb_state(struct xbps_handle *, xbps_state_t, int,
|
||||
const char *, const char *, ...);
|
||||
|
||||
/**
|
||||
* @private
|
||||
|
@ -38,11 +38,11 @@ OBJS += package_remove.o package_find_obsoletes.o package_state.o
|
||||
OBJS += package_unpack.o package_register.o package_script.o
|
||||
OBJS += transaction_commit.o transaction_package_replace.o
|
||||
OBJS += transaction_dictionary.o transaction_sortdeps.o transaction_ops.o
|
||||
OBJS += transaction_revdeps.o
|
||||
OBJS += transaction_revdeps.o pubkey2fp.o
|
||||
OBJS += download.o initend.o pkgdb.o package_conflicts.o
|
||||
OBJS += plist.o plist_find.o plist_match.o archive.o
|
||||
OBJS += plist_remove.o plist_fetch.o util.o util_hash.o
|
||||
OBJS += repo.o repo_pkgdeps.o repo_sync.o
|
||||
OBJS += repo.o repo_pkgdeps.o repo_sync.o repo_keys.o
|
||||
OBJS += rpool.o rpool_get.o cb_util.o proplib_wrapper.o
|
||||
OBJS += $(EXTOBJS) $(COMPAT_SRCS)
|
||||
|
||||
|
@ -70,7 +70,7 @@ xbps_set_cb_fetch(struct xbps_handle *xhp,
|
||||
(*xhp->fetch_cb)(&xfcd, xhp->fetch_cb_data);
|
||||
}
|
||||
|
||||
void HIDDEN
|
||||
int HIDDEN
|
||||
xbps_set_cb_state(struct xbps_handle *xhp,
|
||||
xbps_state_t state,
|
||||
int err,
|
||||
@ -84,7 +84,7 @@ xbps_set_cb_state(struct xbps_handle *xhp,
|
||||
int retval;
|
||||
|
||||
if (xhp->state_cb == NULL)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
xscd.xhp = xhp;
|
||||
xscd.state = state;
|
||||
@ -99,7 +99,9 @@ xbps_set_cb_state(struct xbps_handle *xhp,
|
||||
else
|
||||
xscd.desc = buf;
|
||||
}
|
||||
(*xhp->state_cb)(&xscd, xhp->state_cb_data);
|
||||
retval = (*xhp->state_cb)(&xscd, xhp->state_cb_data);
|
||||
if (buf != NULL)
|
||||
free(buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
143
lib/pubkey2fp.c
Normal file
143
lib/pubkey2fp.c
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* An implementation of convertion from OpenSSL to OpenSSH public key format
|
||||
*
|
||||
* Copyright (c) 2008 Mounir IDRASSI <mounir.idrassi@idrix.fr>. All rights reserved.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include "xbps_api_impl.h"
|
||||
|
||||
static unsigned char pSshHeader[11] = {
|
||||
0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2D, 0x72, 0x73, 0x61
|
||||
};
|
||||
|
||||
static
|
||||
int SshEncodeBuffer(unsigned char *pEncoding, int bufferLen,
|
||||
unsigned char *pBuffer)
|
||||
{
|
||||
int adjustedLen = bufferLen, index;
|
||||
|
||||
if (*pBuffer & 0x80) {
|
||||
adjustedLen++;
|
||||
pEncoding[4] = 0;
|
||||
index = 5;
|
||||
} else {
|
||||
index = 4;
|
||||
}
|
||||
pEncoding[0] = (unsigned char) (adjustedLen >> 24);
|
||||
pEncoding[1] = (unsigned char) (adjustedLen >> 16);
|
||||
pEncoding[2] = (unsigned char) (adjustedLen >> 8);
|
||||
pEncoding[3] = (unsigned char) (adjustedLen );
|
||||
memcpy(&pEncoding[index], pBuffer, bufferLen);
|
||||
return index + bufferLen;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
xbps_pubkey2fp(struct xbps_handle *xhp, xbps_data_t pubkey)
|
||||
{
|
||||
EVP_MD_CTX mdctx;
|
||||
EVP_PKEY *pPubKey = NULL;
|
||||
RSA *pRsa = NULL;
|
||||
BIO *bio = NULL;
|
||||
const void *pubkeydata;
|
||||
unsigned char *fpstr = NULL, md_value[EVP_MAX_MD_SIZE];
|
||||
unsigned char *nBytes = NULL, *eBytes = NULL, *pEncoding = NULL;
|
||||
unsigned int md_len = 0;
|
||||
int index = 0, nLen = 0, eLen = 0, encodingLength = 0;
|
||||
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
pubkeydata = xbps_data_data_nocopy(pubkey);
|
||||
bio = BIO_new_mem_buf(__UNCONST(pubkeydata), xbps_data_size(pubkey));
|
||||
assert(bio);
|
||||
|
||||
pPubKey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||
if (!pPubKey) {
|
||||
xbps_dbg_printf(xhp,
|
||||
"unable to decode public key from the given file: %s\n",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (EVP_PKEY_type(pPubKey->type) != EVP_PKEY_RSA) {
|
||||
xbps_dbg_printf(xhp, "only RSA public keys are currently supported\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
pRsa = EVP_PKEY_get1_RSA(pPubKey);
|
||||
if (!pRsa) {
|
||||
xbps_dbg_printf(xhp, "failed to get RSA public key : %s\n",
|
||||
ERR_error_string(ERR_get_error(), NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
// reading the modulus
|
||||
nLen = BN_num_bytes(pRsa->n);
|
||||
nBytes = (unsigned char*) malloc(nLen);
|
||||
BN_bn2bin(pRsa->n, nBytes);
|
||||
|
||||
// reading the public exponent
|
||||
eLen = BN_num_bytes(pRsa->e);
|
||||
eBytes = (unsigned char*) malloc(eLen);
|
||||
BN_bn2bin(pRsa->e, eBytes);
|
||||
|
||||
encodingLength = 11 + 4 + eLen + 4 + nLen;
|
||||
// correct depending on the MSB of e and N
|
||||
if (eBytes[0] & 0x80)
|
||||
encodingLength++;
|
||||
if (nBytes[0] & 0x80)
|
||||
encodingLength++;
|
||||
|
||||
pEncoding = malloc(encodingLength);
|
||||
memcpy(pEncoding, pSshHeader, 11);
|
||||
|
||||
index = SshEncodeBuffer(&pEncoding[11], eLen, eBytes);
|
||||
index = SshEncodeBuffer(&pEncoding[11 + index], nLen, nBytes);
|
||||
|
||||
/*
|
||||
* Compute the RSA fingerprint (MD5).
|
||||
*/
|
||||
EVP_MD_CTX_init(&mdctx);
|
||||
EVP_DigestInit_ex(&mdctx, EVP_md5(), NULL);
|
||||
assert(EVP_DigestUpdate(&mdctx, pEncoding, encodingLength) != -1);
|
||||
assert(EVP_DigestFinal_ex(&mdctx, md_value, &md_len) != -1);
|
||||
EVP_MD_CTX_cleanup(&mdctx);
|
||||
|
||||
fpstr = malloc(md_len+1);
|
||||
for (unsigned int i = 0; i < md_len; i++)
|
||||
fpstr[i] = md_value[i];
|
||||
|
||||
fpstr[md_len] = '\0';
|
||||
|
||||
error:
|
||||
if (bio)
|
||||
BIO_free_all(bio);
|
||||
if (pRsa)
|
||||
RSA_free(pRsa);
|
||||
if (pPubKey)
|
||||
EVP_PKEY_free(pPubKey);
|
||||
if (nBytes)
|
||||
free(nBytes);
|
||||
if (eBytes)
|
||||
free(eBytes);
|
||||
if (pEncoding)
|
||||
free(pEncoding);
|
||||
|
||||
EVP_cleanup();
|
||||
ERR_free_strings();
|
||||
|
||||
return fpstr;
|
||||
}
|
44
lib/repo.c
44
lib/repo.c
@ -88,16 +88,17 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url)
|
||||
archive_read_support_format_tar(repo->ar);
|
||||
|
||||
if (stat(repofile, &st) == -1) {
|
||||
xbps_dbg_printf(xhp, "[repo] cannot stat repository file %s: %s\n",
|
||||
repofile, strerror(errno));
|
||||
xbps_dbg_printf(xhp, "[repo] `%s' missing repodata %s: %s\n",
|
||||
url, repofile, strerror(errno));
|
||||
archive_read_finish(repo->ar);
|
||||
free(repo);
|
||||
repo = NULL;
|
||||
goto out;
|
||||
}
|
||||
if (archive_read_open_filename(repo->ar, repofile, st.st_blksize) == ARCHIVE_FATAL) {
|
||||
xbps_dbg_printf(xhp, "[repo] cannot open repository file %s: %s\n",
|
||||
repofile, strerror(archive_errno(repo->ar)));
|
||||
xbps_dbg_printf(xhp,
|
||||
"[repo] `%s' failed to open repodata archive %s: %s\n",
|
||||
url, repofile, strerror(archive_errno(repo->ar)));
|
||||
archive_read_finish(repo->ar);
|
||||
free(repo);
|
||||
repo = NULL;
|
||||
@ -152,15 +153,21 @@ xbps_repo_close(struct xbps_repo *repo)
|
||||
{
|
||||
assert(repo);
|
||||
|
||||
if (repo->ar == NULL)
|
||||
return;
|
||||
if (repo->ar != NULL)
|
||||
archive_read_finish(repo->ar);
|
||||
|
||||
archive_read_finish(repo->ar);
|
||||
if (xbps_object_type(repo->idx) == XBPS_TYPE_DICTIONARY)
|
||||
if (repo->meta != NULL) {
|
||||
xbps_object_release(repo->meta);
|
||||
repo->meta = NULL;
|
||||
}
|
||||
if (repo->idx != NULL) {
|
||||
xbps_object_release(repo->idx);
|
||||
if (xbps_object_type(repo->idxfiles) == XBPS_TYPE_DICTIONARY)
|
||||
repo->idx = NULL;
|
||||
}
|
||||
if (repo->idxfiles != NULL) {
|
||||
xbps_object_release(repo->idxfiles);
|
||||
free(repo);
|
||||
repo->idxfiles = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
xbps_dictionary_t
|
||||
@ -171,14 +178,9 @@ xbps_repo_get_virtualpkg(struct xbps_repo *repo, const char *pkg)
|
||||
assert(repo);
|
||||
assert(pkg);
|
||||
|
||||
if (repo->ar == NULL)
|
||||
if (repo->ar == NULL || repo->idx == NULL)
|
||||
return NULL;
|
||||
|
||||
if (xbps_object_type(repo->idx) != XBPS_TYPE_DICTIONARY) {
|
||||
repo->idx = xbps_repo_get_plist(repo, XBPS_PKGINDEX);
|
||||
if (repo->idx == NULL)
|
||||
return NULL;
|
||||
}
|
||||
pkgd = xbps_find_virtualpkg_in_dict(repo->xhp, repo->idx, pkg);
|
||||
if (pkgd) {
|
||||
xbps_dictionary_set_cstring_nocopy(pkgd,
|
||||
@ -196,14 +198,9 @@ xbps_repo_get_pkg(struct xbps_repo *repo, const char *pkg)
|
||||
assert(repo);
|
||||
assert(pkg);
|
||||
|
||||
if (repo->ar == NULL)
|
||||
if (repo->ar == NULL || repo->idx == NULL)
|
||||
return NULL;
|
||||
|
||||
if (xbps_object_type(repo->idx) != XBPS_TYPE_DICTIONARY) {
|
||||
repo->idx = xbps_repo_get_plist(repo, XBPS_PKGINDEX);
|
||||
if (repo->idx == NULL)
|
||||
return NULL;
|
||||
}
|
||||
pkgd = xbps_find_pkg_in_dict(repo->idx, pkg);
|
||||
if (pkgd) {
|
||||
xbps_dictionary_set_cstring_nocopy(pkgd,
|
||||
@ -336,6 +333,9 @@ xbps_repo_get_pkg_revdeps(struct xbps_repo *repo, const char *pkg)
|
||||
char *buf = NULL;
|
||||
bool match = false;
|
||||
|
||||
if (repo->idx == NULL)
|
||||
return NULL;
|
||||
|
||||
if (((pkgd = xbps_rpool_get_pkg(repo->xhp, pkg)) == NULL) &&
|
||||
((pkgd = xbps_rpool_get_virtualpkg(repo->xhp, pkg)) == NULL)) {
|
||||
errno = ENOENT;
|
||||
|
233
lib/repo_keys.c
Normal file
233
lib/repo_keys.c
Normal file
@ -0,0 +1,233 @@
|
||||
/*-
|
||||
* Copyright (c) 2013 Juan Romero Pardines.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include "xbps_api_impl.h"
|
||||
|
||||
int HIDDEN
|
||||
xbps_repo_key_import(struct xbps_repo *repo)
|
||||
{
|
||||
xbps_dictionary_t repokeyd, rkeysd = NULL, newmetad = NULL;
|
||||
xbps_data_t rpubkey;
|
||||
const char *signedby;
|
||||
unsigned char *fp;
|
||||
char *rkeypath = NULL;
|
||||
int import, rv = 0;
|
||||
|
||||
assert(repo);
|
||||
/*
|
||||
* If repository does not have required metadata plist, ignore it.
|
||||
*/
|
||||
if (xbps_dictionary_count(repo->meta) == 0) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' missing required metadata, ignoring.\n", repo->uri);
|
||||
repo->is_verified = false;
|
||||
repo->is_signed = false;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Check the repository provides a working public-key data object.
|
||||
*/
|
||||
rpubkey = xbps_dictionary_get(repo->meta, "public-key");
|
||||
if (xbps_object_type(rpubkey) != XBPS_TYPE_DATA) {
|
||||
rv = EINVAL;
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' invalid public-key object!\n", repo->uri);
|
||||
repo->is_verified = false;
|
||||
repo->is_signed = false;
|
||||
goto out;
|
||||
}
|
||||
repo->is_signed = true;
|
||||
/*
|
||||
* Check if the public key has been stored for this repository.
|
||||
*/
|
||||
rkeypath = xbps_xasprintf("%s/%s", repo->xhp->metadir, XBPS_REPOKEYS);
|
||||
rkeysd = xbps_dictionary_internalize_from_file(rkeypath);
|
||||
if (xbps_object_type(rkeysd) != XBPS_TYPE_DICTIONARY)
|
||||
rkeysd = xbps_dictionary_create();
|
||||
|
||||
repokeyd = xbps_dictionary_get(rkeysd, repo->uri);
|
||||
if (xbps_object_type(repokeyd) == XBPS_TYPE_DICTIONARY) {
|
||||
if (xbps_dictionary_get(repokeyd, "public-key")) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' public key already stored.\n",
|
||||
repo->uri);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Notify the client and take appropiate action to import
|
||||
* the repository public key. Pass back the public key openssh fingerprint
|
||||
* to the client.
|
||||
*/
|
||||
fp = xbps_pubkey2fp(repo->xhp, rpubkey);
|
||||
xbps_dictionary_get_cstring_nocopy(repo->meta, "signature-by", &signedby);
|
||||
import = xbps_set_cb_state(repo->xhp, XBPS_STATE_REPO_KEY_IMPORT,
|
||||
0, (const char *)fp,
|
||||
"This repository is RSA signed by \"%s\"",
|
||||
signedby);
|
||||
free(fp);
|
||||
if (import <= 0)
|
||||
goto out;
|
||||
/*
|
||||
* Add the meta dictionary into XBPS_REPOKEYS and externalize it.
|
||||
*/
|
||||
newmetad = xbps_dictionary_copy_mutable(repo->meta);
|
||||
xbps_dictionary_remove(newmetad, "signature");
|
||||
xbps_dictionary_set(rkeysd, repo->uri, newmetad);
|
||||
|
||||
if (access(repo->xhp->metadir, R_OK|W_OK) == -1) {
|
||||
if (errno == ENOENT) {
|
||||
xbps_mkpath(repo->xhp->metadir, 0755);
|
||||
} else {
|
||||
rv = errno;
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' cannot create metadir: %s\n",
|
||||
repo->uri, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (!xbps_dictionary_externalize_to_file(rkeysd, rkeypath)) {
|
||||
rv = errno;
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' failed to externalize %s: %s\n",
|
||||
repo->uri, XBPS_REPOKEYS, strerror(rv));
|
||||
}
|
||||
|
||||
out:
|
||||
if (newmetad)
|
||||
xbps_object_release(newmetad);
|
||||
if (xbps_object_type(rkeysd) == XBPS_TYPE_DICTIONARY)
|
||||
xbps_object_release(rkeysd);
|
||||
free(rkeypath);
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int
|
||||
rsa_verify_buf(struct xbps_repo *repo, xbps_data_t sigdata,
|
||||
xbps_data_t pubkey, const char *buf)
|
||||
{
|
||||
SHA256_CTX context;
|
||||
BIO *bio;
|
||||
RSA *rsa;
|
||||
unsigned char sha256[SHA256_DIGEST_LENGTH];
|
||||
int rv = 0;
|
||||
|
||||
ERR_load_crypto_strings();
|
||||
SSL_load_error_strings();
|
||||
OpenSSL_add_all_algorithms();
|
||||
OpenSSL_add_all_ciphers();
|
||||
|
||||
bio = BIO_new_mem_buf(__UNCONST(xbps_data_data_nocopy(pubkey)),
|
||||
xbps_data_size(pubkey));
|
||||
assert(bio);
|
||||
|
||||
rsa = PEM_read_bio_RSA_PUBKEY(bio, NULL, NULL, NULL);
|
||||
if (rsa == NULL) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' error reading public key: %s\n",
|
||||
repo->uri, ERR_error_string(ERR_get_error(), NULL));
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
SHA256_Init(&context);
|
||||
SHA256_Update(&context, buf, strlen(buf));
|
||||
SHA256_Final(sha256, &context);
|
||||
|
||||
if (RSA_verify(NID_sha1, sha256, sizeof(sha256),
|
||||
xbps_data_data_nocopy(sigdata),
|
||||
xbps_data_size(sigdata), rsa) == 0) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' failed to verify signature: %s\n",
|
||||
repo->uri, ERR_error_string(ERR_get_error(), NULL));
|
||||
rv = EPERM;
|
||||
}
|
||||
RSA_free(rsa);
|
||||
BIO_free(bio);
|
||||
ERR_free_strings();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int HIDDEN
|
||||
xbps_repo_key_verify(struct xbps_repo *repo)
|
||||
{
|
||||
xbps_dictionary_t rkeysd, repokeyd;
|
||||
xbps_data_t sigdata, pubkey;
|
||||
char *rkeyspath, *idx_xml;
|
||||
bool verified = false;
|
||||
|
||||
/* unsigned repo */
|
||||
if (!repo->is_signed) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s' ignoring unsigned repository.\n", repo->uri);
|
||||
return 0;
|
||||
}
|
||||
rkeyspath = xbps_xasprintf("%s/%s", repo->xhp->metadir, XBPS_REPOKEYS);
|
||||
rkeysd = xbps_dictionary_internalize_from_file(rkeyspath);
|
||||
if (xbps_dictionary_count(rkeysd) == 0) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s': failed to internalize %s: %s\n",
|
||||
repo->uri, rkeyspath, strerror(errno));
|
||||
free(rkeyspath);
|
||||
return ENODEV;
|
||||
}
|
||||
free(rkeyspath);
|
||||
|
||||
repokeyd = xbps_dictionary_get(rkeysd, repo->uri);
|
||||
if (xbps_dictionary_count(repokeyd) == 0) {
|
||||
xbps_dbg_printf(repo->xhp,
|
||||
"[repo] `%s': empty %s dictionary\n",
|
||||
repo->uri, XBPS_REPOKEYS);
|
||||
xbps_object_release(rkeysd);
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
idx_xml = xbps_dictionary_externalize(repo->idx);
|
||||
assert(idx_xml);
|
||||
|
||||
sigdata = xbps_dictionary_get(repo->meta, "signature");
|
||||
assert(xbps_object_type(sigdata) == XBPS_TYPE_DATA);
|
||||
pubkey = xbps_dictionary_get(repokeyd, "public-key");
|
||||
assert(xbps_object_type(pubkey) == XBPS_TYPE_DATA);
|
||||
/* XXX ignore 'signature-type' for now */
|
||||
if (rsa_verify_buf(repo, sigdata, pubkey, idx_xml) == 0) {
|
||||
repo->is_verified = true;
|
||||
verified = true;
|
||||
}
|
||||
free(idx_xml);
|
||||
|
||||
return verified ? 0 : EPERM;
|
||||
}
|
68
lib/rpool.c
68
lib/rpool.c
@ -53,7 +53,7 @@ xbps_rpool_init(struct xbps_handle *xhp)
|
||||
struct rpool *rp;
|
||||
const char *repouri;
|
||||
bool foundrepo = false;
|
||||
int rv = 0;
|
||||
int retval, rv = 0;
|
||||
|
||||
assert(xhp);
|
||||
|
||||
@ -65,15 +65,59 @@ xbps_rpool_init(struct xbps_handle *xhp)
|
||||
assert(rp);
|
||||
xbps_array_get_cstring_nocopy(xhp->repositories, i, &repouri);
|
||||
if ((rp->repo = xbps_repo_open(xhp, repouri)) == NULL) {
|
||||
rp->repo = calloc(1, sizeof(struct xbps_repo));
|
||||
rp->repo = malloc(sizeof(struct xbps_repo));
|
||||
assert(rp->repo);
|
||||
rp->repo->ar = NULL;
|
||||
rp->repo->is_verified = false;
|
||||
rp->repo->is_signed = false;
|
||||
}
|
||||
rp->repo->idx = xbps_repo_get_plist(rp->repo, XBPS_PKGINDEX);
|
||||
rp->repo->idx = xbps_repo_get_plist(rp->repo, XBPS_REPOIDX);
|
||||
if (xbps_object_type(rp->repo->idx) == XBPS_TYPE_DICTIONARY)
|
||||
xbps_dictionary_make_immutable(rp->repo->idx);
|
||||
|
||||
rp->repo->meta = xbps_repo_get_plist(rp->repo, XBPS_REPOMETA);
|
||||
if (xbps_object_type(rp->repo->meta) == XBPS_TYPE_DICTIONARY)
|
||||
xbps_dictionary_make_immutable(rp->repo->meta);
|
||||
|
||||
rp->repo->uri = repouri;
|
||||
rp->repo->xhp = xhp;
|
||||
|
||||
if (xbps_repository_is_remote(repouri)) {
|
||||
/*
|
||||
* Import the RSA public key (if it's signed).
|
||||
*/
|
||||
retval = xbps_repo_key_import(rp->repo);
|
||||
if (retval != 0) {
|
||||
/* any error */
|
||||
xbps_dbg_printf(xhp, "[rpool] %s: key_import %s\n",
|
||||
rp->repo->uri, strerror(retval));
|
||||
}
|
||||
/*
|
||||
* Check the repository signature against stored public key.
|
||||
*/
|
||||
retval = xbps_repo_key_verify(rp->repo);
|
||||
if (retval == 0) {
|
||||
/* signed, verified */
|
||||
xbps_set_cb_state(xhp, XBPS_STATE_REPO_SIGVERIFIED, 0, NULL, NULL);
|
||||
} else if (retval == EPERM) {
|
||||
/* signed, unverified */
|
||||
xbps_set_cb_state(xhp, XBPS_STATE_REPO_SIGUNVERIFIED, 0, NULL, NULL);
|
||||
xbps_repo_close(rp->repo);
|
||||
} else {
|
||||
/* any error */
|
||||
xbps_dbg_printf(xhp, "[rpool] %s: key_verify %s\n",
|
||||
rp->repo->uri, strerror(retval));
|
||||
xbps_repo_close(rp->repo);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If repository has passed signature checks, add it to the pool.
|
||||
*/
|
||||
SIMPLEQ_INSERT_TAIL(&rpool_queue, rp, entries);
|
||||
foundrepo = true;
|
||||
xbps_dbg_printf(xhp, "[rpool] `%s' registered.\n", repouri);
|
||||
xbps_dbg_printf(xhp, "[rpool] `%s' registered (%s, %s).\n",
|
||||
repouri, rp->repo->is_signed ? "signed" : "unsigned",
|
||||
rp->repo->is_verified ? "verified" : "unverified");
|
||||
}
|
||||
if (!foundrepo) {
|
||||
/* no repositories available, error out */
|
||||
@ -101,6 +145,7 @@ xbps_rpool_release(struct xbps_handle *xhp)
|
||||
while ((rp = SIMPLEQ_FIRST(&rpool_queue))) {
|
||||
SIMPLEQ_REMOVE(&rpool_queue, rp, rpool, entries);
|
||||
xbps_repo_close(rp->repo);
|
||||
free(rp->repo);
|
||||
free(rp);
|
||||
}
|
||||
xhp->rpool_initialized = false;
|
||||
@ -131,8 +176,8 @@ xbps_rpool_sync(struct xbps_handle *xhp, const char *uri)
|
||||
|
||||
int
|
||||
xbps_rpool_foreach(struct xbps_handle *xhp,
|
||||
int (*fn)(struct xbps_repo *, void *, bool *),
|
||||
void *arg)
|
||||
int (*fn)(struct xbps_repo *, void *, bool *),
|
||||
void *arg)
|
||||
{
|
||||
struct rpool *rp;
|
||||
int rv = 0;
|
||||
@ -142,21 +187,14 @@ xbps_rpool_foreach(struct xbps_handle *xhp,
|
||||
/* Initialize repository pool */
|
||||
if ((rv = xbps_rpool_init(xhp)) != 0) {
|
||||
if (rv == ENOTSUP) {
|
||||
xbps_dbg_printf(xhp,
|
||||
"[rpool] empty repository list.\n");
|
||||
xbps_dbg_printf(xhp, "[rpool] empty repository list.\n");
|
||||
} else if (rv != ENOENT && rv != ENOTSUP) {
|
||||
xbps_dbg_printf(xhp,
|
||||
"[rpool] couldn't initialize: %s\n",
|
||||
strerror(rv));
|
||||
xbps_dbg_printf(xhp, "[rpool] couldn't initialize: %s\n", strerror(rv));
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
/* Iterate over repository pool */
|
||||
SIMPLEQ_FOREACH(rp, &rpool_queue, entries) {
|
||||
/* ignore invalid repos */
|
||||
if (rp->repo->idx == NULL)
|
||||
continue;
|
||||
|
||||
rv = (*fn)(rp->repo, arg, &done);
|
||||
if (rv != 0 || done)
|
||||
break;
|
||||
|
17
lib/util.c
17
lib/util.c
@ -297,3 +297,20 @@ xbps_humanize_number(char *buf, int64_t bytes)
|
||||
return humanize_number(buf, 7, bytes, "B",
|
||||
HN_AUTOSCALE, HN_DECIMAL|HN_NOSPACE);
|
||||
}
|
||||
|
||||
void
|
||||
xbps_print_hexfp(const char *fp)
|
||||
{
|
||||
unsigned char *fpstr;
|
||||
unsigned int i, c, len;
|
||||
|
||||
fpstr = (unsigned char *)(void *)(unsigned long)(const void *)fp;
|
||||
len = strlen(fp);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
printf("%02x", fpstr[i]);
|
||||
c = i + 1;
|
||||
if (c < len)
|
||||
putchar(':');
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ endif
|
||||
|
||||
%.o: %.c
|
||||
@printf " [CC]\t\t$@\n"
|
||||
${SILENT}$(CC) $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
${SILENT}$(CC) $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) -c $<
|
||||
|
||||
$(BIN).static: $(OBJS)
|
||||
@printf " [CCLD]\t\t$@\n"
|
||||
|
Loading…
Reference in New Issue
Block a user