xbps-rindex(1): add support to sign specific pkgs, not the whole repo.

See NEWS for more information.
This commit is contained in:
Juan RP 2015-06-04 16:01:43 +02:00
parent 0bad752cbe
commit d86cece411
5 changed files with 201 additions and 147 deletions

7
NEWS
View File

@ -1,4 +1,9 @@
xbps-0.45.1 (???): xbps-0.46 (???):
* xbps-rindex(1): use `-s, --sign` to initialize the repository archive
with the required metadata to allow signed packages. Added `-S, --sign-pkg`
to sign a specific package archive. This allows to sign a specific package,
rather than all packages available in that repository.
* If the repository write lock is already taken, sleep for 1 second, rather * If the repository write lock is already taken, sleep for 1 second, rather
than looping endlessly without any timing; this consumed too much CPU time than looping endlessly without any timing; this consumed too much CPU time

View File

@ -1,5 +1,5 @@
/*- /*-
* Copyright (c) 2012-2014 Juan Romero Pardines. * Copyright (c) 2012-2015 Juan Romero Pardines.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -78,6 +78,7 @@ int remove_obsoletes(struct xbps_handle *, const char *);
/* From sign.c */ /* From sign.c */
int sign_repo(struct xbps_handle *, const char *, const char *, int sign_repo(struct xbps_handle *, const char *, const char *,
const char *); const char *);
int sign_pkgs(struct xbps_handle *, int, int, char **, const char *, bool);
/* From repoflush.c */ /* From repoflush.c */
bool repodata_flush(struct xbps_handle *, const char *, bool repodata_flush(struct xbps_handle *, const char *,

View File

@ -48,14 +48,15 @@ usage(bool fail)
" -a --add <repodir/pkg> ... Add package(s) to repository index\n" " -a --add <repodir/pkg> ... Add package(s) to repository index\n"
" -c --clean <repodir> Clean repository index\n" " -c --clean <repodir> Clean repository index\n"
" -r --remove-obsoletes <repodir> Removes obsolete packages from repository\n" " -r --remove-obsoletes <repodir> Removes obsolete packages from repository\n"
" -s --sign <repodir> Sign repository index\n\n"); " -s --sign <repodir> Initialize repository metadata signature\n"
" -S --sign-pkg archive.xbps ... Sign binary package archive\n\n");
exit(fail ? EXIT_FAILURE : EXIT_SUCCESS); exit(fail ? EXIT_FAILURE : EXIT_SUCCESS);
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
const char *shortopts = "acdfhrsVv"; const char *shortopts = "acdfhrsSVv";
struct option longopts[] = { struct option longopts[] = {
{ "add", no_argument, NULL, 'a' }, { "add", no_argument, NULL, 'a' },
{ "clean", no_argument, NULL, 'c' }, { "clean", no_argument, NULL, 'c' },
@ -68,14 +69,15 @@ main(int argc, char **argv)
{ "privkey", required_argument, NULL, 0}, { "privkey", required_argument, NULL, 0},
{ "signedby", required_argument, NULL, 1}, { "signedby", required_argument, NULL, 1},
{ "sign", no_argument, NULL, 's'}, { "sign", no_argument, NULL, 's'},
{ "sign-pkg", no_argument, NULL, 'S'},
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
struct xbps_handle xh; struct xbps_handle xh;
const char *privkey = NULL, *signedby = NULL; const char *privkey = NULL, *signedby = NULL;
int rv, c, flags = 0; int rv, c, flags = 0;
bool add_mode, clean_mode, rm_mode, sign_mode, force; bool add_mode, clean_mode, rm_mode, sign_mode, sign_pkg_mode, force;
add_mode = clean_mode = rm_mode = sign_mode = force = false; add_mode = clean_mode = rm_mode = sign_mode = sign_pkg_mode = force = false;
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
switch (c) { switch (c) {
@ -106,6 +108,9 @@ main(int argc, char **argv)
case 's': case 's':
sign_mode = true; sign_mode = true;
break; break;
case 'S':
sign_pkg_mode = true;
break;
case 'v': case 'v':
flags |= XBPS_FLAG_VERBOSE; flags |= XBPS_FLAG_VERBOSE;
break; break;
@ -114,14 +119,16 @@ main(int argc, char **argv)
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
} }
if ((argc == optind) || (!add_mode && !clean_mode && !rm_mode && !sign_mode)) { if ((argc == optind) ||
(!add_mode && !clean_mode && !rm_mode && !sign_mode && !sign_pkg_mode)) {
usage(true); usage(true);
} else if ((add_mode && (clean_mode || rm_mode || sign_mode)) || } else if ((add_mode && (clean_mode || rm_mode || sign_mode || sign_pkg_mode)) ||
(clean_mode && (add_mode || rm_mode || sign_mode)) || (clean_mode && (add_mode || rm_mode || sign_mode || sign_pkg_mode)) ||
(rm_mode && (add_mode || clean_mode || sign_mode)) || (rm_mode && (add_mode || clean_mode || sign_mode || sign_pkg_mode)) ||
(sign_mode && (add_mode || clean_mode || rm_mode))) { (sign_mode && (add_mode || clean_mode || rm_mode || sign_pkg_mode)) ||
(sign_pkg_mode && (add_mode || clean_mode || rm_mode || sign_mode))) {
fprintf(stderr, "Only one mode can be specified: add, clean, " fprintf(stderr, "Only one mode can be specified: add, clean, "
"remove-obsoletes or sign.\n"); "remove-obsoletes, sign or sign-pkg.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -142,6 +149,8 @@ main(int argc, char **argv)
rv = remove_obsoletes(&xh, argv[optind]); rv = remove_obsoletes(&xh, argv[optind]);
else if (sign_mode) else if (sign_mode)
rv = sign_repo(&xh, argv[optind], privkey, signedby); rv = sign_repo(&xh, argv[optind], privkey, signedby);
else if (sign_pkg_mode)
rv = sign_pkgs(&xh, optind, argc, argv, privkey, force);
exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); exit(rv ? EXIT_FAILURE : EXIT_SUCCESS);
} }

View File

@ -105,7 +105,9 @@ rsa_sign_buf(RSA *rsa, const char *buf, unsigned int buflen,
SHA256_Update(&context, buf, buflen); SHA256_Update(&context, buf, buflen);
SHA256_Final(sha256, &context); SHA256_Final(sha256, &context);
*sigret = calloc(1, RSA_size(rsa) + 1); if ((*sigret = calloc(1, RSA_size(rsa) + 1)) == NULL)
return false;
if (!RSA_sign(NID_sha1, sha256, sizeof(sha256), if (!RSA_sign(NID_sha1, sha256, sizeof(sha256),
*sigret, siglen, rsa)) { *sigret, siglen, rsa)) {
free(*sigret); free(*sigret);
@ -114,52 +116,12 @@ rsa_sign_buf(RSA *rsa, const char *buf, unsigned int buflen,
return true; return true;
} }
int static RSA *
sign_repo(struct xbps_handle *xhp, const char *repodir, load_rsa_key(const char *privkey)
const char *privkey, const char *signedby)
{ {
struct stat st;
struct xbps_repo *repo = NULL;
xbps_dictionary_t pkgd, meta = NULL;
xbps_data_t data = NULL, rpubkey = NULL;
xbps_object_iterator_t iter = NULL;
xbps_object_t obj;
RSA *rsa = NULL; RSA *rsa = NULL;
unsigned char *sig; char *defprivkey;
unsigned int siglen;
uint16_t rpubkeysize, pubkeysize;
const char *arch, *pkgver, *rsignedby = NULL;
char *binpkg = NULL, *binpkg_sig = NULL, *buf = NULL;
char *rlockfname = NULL, *defprivkey = NULL;
int binpkg_fd, binpkg_sig_fd, rlockfd = -1, rv = 0;
bool flush = false;
if (signedby == NULL) {
fprintf(stderr, "--signedby unset! cannot sign repository\n");
return -1;
}
/*
* Check that repository index exists and not empty, otherwise bail out.
*/
if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) {
rv = errno;
fprintf(stderr, "%s: cannot lock repository: %s\n",
_XBPS_RINDEX, strerror(errno));
goto out;
}
repo = xbps_repo_open(xhp, repodir);
if (repo == NULL) {
rv = errno;
fprintf(stderr, "%s: cannot read repository data: %s\n",
_XBPS_RINDEX, strerror(errno));
goto out;
}
if (xbps_dictionary_count(repo->idx) == 0) {
fprintf(stderr, "%s: invalid repository, existing!\n", _XBPS_RINDEX);
rv = EINVAL;
goto out;
}
/* /*
* If privkey not set, default to ~/.ssh/id_rsa. * If privkey not set, default to ~/.ssh/id_rsa.
*/ */
@ -175,87 +137,48 @@ sign_repo(struct xbps_handle *xhp, const char *repodir,
if ((rsa = load_rsa_privkey(defprivkey)) == NULL) { if ((rsa = load_rsa_privkey(defprivkey)) == NULL) {
fprintf(stderr, "%s: failed to read the RSA privkey\n", _XBPS_RINDEX); fprintf(stderr, "%s: failed to read the RSA privkey\n", _XBPS_RINDEX);
exit(EXIT_FAILURE);
}
return rsa;
}
int
sign_repo(struct xbps_handle *xhp, const char *repodir,
const char *privkey, const char *signedby)
{
struct xbps_repo *repo = NULL;
xbps_dictionary_t meta = NULL;
xbps_data_t data = NULL, rpubkey = NULL;
RSA *rsa = NULL;
uint16_t rpubkeysize, pubkeysize;
const char *rsignedby = NULL;
char *buf = NULL, *rlockfname = NULL;
int rlockfd = -1, rv = 0;
bool flush_failed = false, flush = false;
if (signedby == NULL) {
fprintf(stderr, "--signedby unset! cannot initialize signed repository\n");
return -1;
}
/*
* Check that repository index exists and not empty, otherwise bail out.
*/
repo = xbps_repo_open(xhp, repodir);
if (repo == NULL) {
rv = errno;
fprintf(stderr, "%s: cannot read repository data: %s\n",
_XBPS_RINDEX, strerror(errno));
goto out;
}
if (xbps_dictionary_count(repo->idx) == 0) {
fprintf(stderr, "%s: invalid repository, existing!\n", _XBPS_RINDEX);
rv = EINVAL; rv = EINVAL;
goto out; goto out;
} }
/*
* Iterate over the idx dictionary and then sign all binary
* packages in this repository.
*/
iter = xbps_dictionary_iterator(repo->idx);
assert(iter);
while ((obj = xbps_object_iterator_next(iter))) { rsa = load_rsa_key(privkey);
pkgd = xbps_dictionary_get_keysym(repo->idx, obj);
xbps_dictionary_get_cstring_nocopy(pkgd, "architecture", &arch);
xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver);
binpkg = xbps_xasprintf("%s/%s.%s.xbps", repodir, pkgver, arch);
binpkg_sig = xbps_xasprintf("%s.sig", binpkg);
/*
* Skip pkg if file signature exists
*/
if ((binpkg_sig_fd = access(binpkg_sig, R_OK)) == 0) {
if (xhp->flags & XBPS_FLAG_VERBOSE)
fprintf(stderr, "skipping %s, file signature found.\n", pkgver);
free(binpkg);
free(binpkg_sig);
close(binpkg_sig_fd);
continue;
}
/*
* Generate pkg file signature.
*/
if ((binpkg_fd = open(binpkg, O_RDONLY)) == -1) {
fprintf(stderr, "cannot read %s: %s\n", binpkg, strerror(errno));
free(binpkg);
free(binpkg_sig);
continue;
}
fstat(binpkg_fd, &st);
buf = malloc(st.st_size);
assert(buf);
if (read(binpkg_fd, buf, st.st_size) != st.st_size) {
fprintf(stderr, "failed to read %s: %s\n", binpkg, strerror(errno));
close(binpkg_fd);
free(buf);
free(binpkg);
free(binpkg_sig);
continue;
}
close(binpkg_fd);
if (!rsa_sign_buf(rsa, buf, st.st_size, &sig, &siglen)) {
fprintf(stderr, "failed to sign %s: %s\n", binpkg, strerror(errno));
free(buf);
free(binpkg);
free(binpkg_sig);
continue;
}
free(buf);
free(binpkg);
/*
* Write pkg file signature.
*/
binpkg_sig_fd = creat(binpkg_sig, 0644);
if (binpkg_sig_fd == -1) {
fprintf(stderr, "failed to create %s: %s\n", binpkg_sig, strerror(errno));
free(sig);
free(binpkg_sig);
continue;
}
if (write(binpkg_sig_fd, sig, siglen) != (ssize_t)siglen) {
fprintf(stderr, "failed to write %s: %s\n", binpkg_sig, strerror(errno));
free(sig);
free(binpkg_sig);
close(binpkg_sig_fd);
continue;
}
free(sig);
free(binpkg_sig);
close(binpkg_sig_fd);
printf("signed successfully %s\n", pkgver);
}
xbps_object_iterator_release(iter);
/* /*
* Check if repository index-meta contains changes compared to its * Check if repository index-meta contains changes compared to its
* current state. * current state.
@ -292,26 +215,131 @@ sign_repo(struct xbps_handle *xhp, const char *repodir,
xbps_object_release(data); xbps_object_release(data);
data = NULL; data = NULL;
if (!repodata_flush(xhp, repodir, repo->idx, meta)) { /* lock repository to write repodata file */
if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) {
rv = errno;
fprintf(stderr, "%s: cannot lock repository: %s\n",
_XBPS_RINDEX, strerror(errno));
goto out;
}
flush_failed = repodata_flush(xhp, repodir, repo->idx, meta);
xbps_repo_unlock(rlockfd, rlockfname);
if (!flush_failed) {
fprintf(stderr, "failed to write repodata: %s\n", strerror(errno)); fprintf(stderr, "failed to write repodata: %s\n", strerror(errno));
goto out; goto out;
} }
printf("Signed repository (%u package%s)\n", printf("Initialized signed repository (%u package%s)\n",
xbps_dictionary_count(repo->idx), xbps_dictionary_count(repo->idx),
xbps_dictionary_count(repo->idx) == 1 ? "" : "s"); xbps_dictionary_count(repo->idx) == 1 ? "" : "s");
out: out:
if (defprivkey) {
free(defprivkey);
}
if (rsa) { if (rsa) {
RSA_free(rsa); RSA_free(rsa);
rsa = NULL; rsa = NULL;
} }
if (repo) { if (repo)
xbps_repo_close(repo); xbps_repo_close(repo);
}
xbps_repo_unlock(rlockfd, rlockfname);
return rv ? -1 : 0; return rv ? -1 : 0;
} }
static int
sign_pkg(struct xbps_handle *xhp, const char *binpkg, const char *privkey, bool force)
{
RSA *rsa = NULL;
struct stat st;
unsigned char *sig = NULL;
unsigned int siglen = 0;
char *buf = NULL, *sigfile = NULL;
int rv = 0, sigfile_fd = -1, binpkg_fd = -1;
sigfile = xbps_xasprintf("%s.sig", binpkg);
/*
* Skip pkg if file signature exists
*/
if (!force && ((sigfile_fd = access(sigfile, R_OK)) == 0)) {
if (xhp->flags & XBPS_FLAG_VERBOSE)
fprintf(stderr, "skipping %s, file signature found.\n", binpkg);
sigfile_fd = -1;
goto out;
}
/*
* Generate pkg file signature.
*/
if ((binpkg_fd = open(binpkg, O_RDONLY)) == -1) {
fprintf(stderr, "cannot read %s: %s\n", binpkg, strerror(errno));
rv = EINVAL;
goto out;
}
fstat(binpkg_fd, &st);
buf = malloc(st.st_size);
assert(buf);
if (read(binpkg_fd, buf, st.st_size) != st.st_size) {
fprintf(stderr, "failed to read %s: %s\n", binpkg, strerror(errno));
rv = EINVAL;
goto out;
}
close(binpkg_fd);
rsa = load_rsa_key(privkey);
if (!rsa_sign_buf(rsa, buf, st.st_size, &sig, &siglen)) {
fprintf(stderr, "failed to sign %s: %s\n", binpkg, strerror(errno));
rv = EINVAL;
goto out;
}
free(buf);
buf = NULL;
/*
* Write pkg file signature.
*/
if (force)
sigfile_fd = open(sigfile, O_WRONLY|O_TRUNC, 0644);
else
sigfile_fd = creat(sigfile, 0644);
if (sigfile_fd == -1) {
fprintf(stderr, "failed to create %s: %s\n", sigfile, strerror(errno));
rv = EINVAL;
goto out;
}
if (write(sigfile_fd, sig, siglen) != (ssize_t)siglen) {
fprintf(stderr, "failed to write %s: %s\n", sigfile, strerror(errno));
rv = EINVAL;
goto out;
}
printf("signed successfully %s\n", binpkg);
out:
if (rsa) {
RSA_free(rsa);
rsa = NULL;
}
if (buf)
free(buf);
if (sigfile)
free(sigfile);
if (sigfile_fd != -1)
close(sigfile_fd);
if (binpkg_fd != -1)
close(binpkg_fd);
return rv;
}
int
sign_pkgs(struct xbps_handle *xhp, int args, int argmax, char **argv,
const char *privkey, bool force)
{
/*
* Process all packages specified in argv.
*/
for (int i = args; i < argmax; i++) {
int rv;
const char *binpkg = argv[i];
rv = sign_pkg(xhp, binpkg, privkey, force);
if (rv != 0)
return rv;
}
return 0;
}

View File

@ -1,4 +1,4 @@
.Dd May 16, 2015 .Dd June 4, 2015
.Dt XBPS-RINDEX 1 .Dt XBPS-RINDEX 1
.Sh NAME .Sh NAME
.Nm xbps-rindex .Nm xbps-rindex
@ -19,9 +19,12 @@ in local repositories.
Enables extra debugging shown to stderr. Enables extra debugging shown to stderr.
.It Fl f -force .It Fl f -force
Forcefully register binary package into the local repository, overwriting existing entry. Forcefully register binary package into the local repository, overwriting existing entry.
Or forcefully create a package signature, even if there's an existing one already.
This flag is only useful with the This flag is only useful with the
.Em add .Em add
mode. or
.Em sign-pkg
modes.
.It Fl h -help .It Fl h -help
Show the help usage. Show the help usage.
.It Fl V -version .It Fl V -version
@ -52,12 +55,20 @@ Packages that are not currently registered in repository's index will
be removed (out of date, invalid archives, etc). be removed (out of date, invalid archives, etc).
Absolute path to the local repository is expected. Absolute path to the local repository is expected.
.It Sy -s, --sign Ar /path/to/repository .It Sy -s, --sign Ar /path/to/repository
Signs all binary packages stored in repository with your specified RSA key. If Initializes a signed repository with your specified RSA key.
.Fl --privkey Note this only adds some metadata to the repository archive to be able to sign packages.
If the
.Fl -privkey
argument not set, it defaults to argument not set, it defaults to
.Sy ~/.ssh/id_rsa . .Sy ~/.ssh/id_rsa .
Absolute path to the local repository is expected. Only binary packages that .It Sy -S, --sign-pkg Ar /path/to/repository/binpkg.xbps ...
have not been signed are processed. Signs a binary package archive with your specified RSA key. If
.Fl -privkey
argument not set, it defaults to
.Sy ~/.ssh/id_rsa .
If there's an existing signature, it won't be overwritten; use the
.Fl f
option to force the creation.
.El .El
.Sh ENVIRONMENT .Sh ENVIRONMENT
.Bl -tag -width XBPS_TARGET_ARCH .Bl -tag -width XBPS_TARGET_ARCH