Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Introduce pkg-key(8)
Kyle Evans committed 2 years ago
commit 9132ae05c4f535c34326ba15555f2976ca7839be
parent 4a70c81
9 files changed +388 -33
modified docs/Makefile.autosetup
@@ -23,6 +23,7 @@ MAN8= pkg-add.8 \
	pkg-fetch.8 \
	pkg-info.8 \
	pkg-install.8 \
+
	pkg-key.8 \
	pkg-lock.8 \
	pkg-query.8 \
	pkg-register.8 \
added docs/pkg-key.8
@@ -0,0 +1,55 @@
+
.\"
+
.\" FreeBSD pkg - a next generation package for the installation and maintenance
+
.\" of non-core utilities.
+
.\"
+
.\" 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.
+
.\"
+
.\"
+
.\"     @(#)pkg.8
+
.\"
+
.Dd January 14, 2021
+
.Dt PKG-KEY 8
+
.Os
+
.Sh NAME
+
.Nm "pkg key"
+
.Nd create or extract signing key details
+
.Sh SYNOPSIS
+
.Nm
+
.Op Fl -create | Fl -public
+
.Ar key-path
+
.Sh DESCRIPTION
+
.Nm
+
is used to create or extract signing key details for use with
+
.Xr pkg-repo 8 .
+
Cryptographically signing your package repository catalog is strongly
+
recommended.
+
.Pp
+
The default operation for
+
.Nm
+
is to write some useful information about the key at
+
.Ar key-path
+
to stdout.
+
.Sh OPTIONS
+
The following options are supported by
+
.Nm :
+
.Bl -tag -width all
+
.It Fl -create
+
Create the named key.
+
Note that any file at
+
.Ar key-path
+
will be overwritten.
+
.It Fl -public
+
Write the public key to stdout.
+
.El
+
.Sh FILES
+
See
+
.Xr pkg.conf 5 .
+
.Sh SEE ALSO
+
.Xr pkg-repo 8
modified src/Makefile.autosetup
@@ -15,6 +15,7 @@ SRCS= add.c \
	globals.c \
	info.c \
	install.c \
+
	key.c \
	lock.c \
	main.c \
	plugins.c \
@@ -43,7 +44,8 @@ LOCAL_CFLAGS= -I$(top_srcdir)/compat \
		-I$(top_srcdir)/libpkg \
		-I$(top_builddir)/libpkg \
		-DGITHASH=\"@GITHASH@\" \
-
		-DHAVE_CONFIG_H
+
		-DHAVE_CONFIG_H \
+
		-Werror=switch
LIBPKGFLAT=	-L$(top_builddir)/libpkg -lpkg_flat
OTHER_LIBS=	@EXTRA_LIBS@ -lssl -lcrypto

added src/key.c
@@ -0,0 +1,278 @@
+
/*-
+
 * Copyright (c) 2021 Kyle Evans <kevans@FreeBSD.org>
+
 *
+
 * 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
+
 *    in this position and unchanged.
+
 * 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(S) ``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(S) 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.
+
 */
+

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

+
#include <sys/uio.h>
+

+
#include <bsd_compat.h>
+
#include <assert.h>
+
#include <getopt.h>
+
#include <signal.h>
+
#include <stdio.h>
+
#include <string.h>
+

+
#ifdef HAVE_READPASSPHRASE_H
+
#include <readpassphrase.h>
+
#elif defined(HAVE_BSD_READPASSPHRASE_H)
+
#include <bsd/readpassphrase.h>
+
#else
+
#include "readpassphrase_compat.h"
+
#endif
+

+
#include <unistd.h>
+

+
#include <pkg.h>
+
#include "pkgcli.h"
+

+
enum {
+
	ARG_CREATE = CHAR_MAX + 1,
+
	ARG_PUBLIC,
+
};
+

+
typedef enum {
+
	MODE_UNSPECIFIED = 0,
+
	MODE_CREATE,
+
	MODE_PUBLIC,
+
} key_mode_t;
+

+
void
+
usage_key(void)
+
{
+
	fprintf(stderr, "Usage: pkg key [--create | --public] [-t <type>] "
+
	    "<key-path>\n");
+
	fprintf(stderr, "For more information see 'pkg help key'.\n");
+
}
+

+
static int
+
key_create(struct pkg_key *key, int argc __unused, char *argv[] __unused)
+
{
+
	/* No arguments to setup for now. */
+
	return (pkg_key_create(key, NULL, 0));
+
}
+

+
static int
+
key_pubout(struct pkg_key *key)
+
{
+
	char *keybuf = NULL;
+
	size_t keylen;
+
	int ret;
+

+
	ret = pkg_key_pubkey(key, &keybuf, &keylen);
+
	if (ret != EPKG_OK)
+
		return (ret);
+

+
	fwrite(keybuf, keylen, 1, stdout);
+
	if (keybuf[keylen - 1] != '\n')
+
		fputc('\n', stdout);
+
	free(keybuf);
+
	return (0);
+
}
+

+
static int
+
key_info(struct pkg_key *key, const char *file, const char *type)
+
{
+
	struct iovec *iov;
+
	int niov, rc;
+

+
	iov = NULL;
+
	rc = pkg_key_info(key, &iov, &niov);
+
	if (rc != EPKG_OK)
+
		return (rc);
+

+
	assert((niov % 2) == 0);
+

+
	printf("Key file '%s' (type %s)\n", file, type);
+
	for (int i = 0; i < niov; i += 2) {
+
		const char *kv_name = iov[i].iov_base;
+
		const char *kv_val = iov[i + 1].iov_base;
+
		printf("  - %s: %s\n", kv_name, kv_val);
+

+
		free(iov[i + 1].iov_base);
+
	}
+

+
	free(iov);
+
	return (EPKG_OK);
+
}
+

+
int
+
password_cb(char *buf, int size, int rwflag, void *key)
+
{
+
	int len = 0;
+
	char pass[BUFSIZ];
+
	sigset_t sig, oldsig;
+

+
	(void)rwflag;
+
	(void)key;
+

+
	/* Block sigalarm temporary */
+
	sigemptyset(&sig);
+
	sigaddset(&sig, SIGALRM);
+
	sigprocmask(SIG_BLOCK, &sig, &oldsig);
+

+
	if (readpassphrase("\nEnter passphrase: ", pass, BUFSIZ, RPP_ECHO_OFF) == 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);
+

+
	sigprocmask(SIG_SETMASK, &oldsig, NULL);
+

+
	return (len);
+
}
+

+
int
+
exec_key(int argc, char **argv)
+
{
+
	int	 ret;
+
	int	 ch;
+
	struct pkg_key *key = NULL;
+
	const char *keypath, *keytype = NULL;
+
	key_mode_t keymode;
+

+
	struct option longopts[] = {
+
		{ "create",	no_argument,		NULL,	ARG_CREATE },
+
		{ "public", no_argument,		NULL,	ARG_PUBLIC },
+
		{ NULL,		0,			NULL,	0 },
+
	};
+

+
	keymode = MODE_UNSPECIFIED;
+

+
	/* XXX maybe eventually we can just derive the key type. */
+
	while ((ch = getopt_long(argc, argv, "t:", longopts, NULL)) != -1) {
+
		switch (ch) {
+
		case ARG_CREATE:
+
			if (keymode != MODE_UNSPECIFIED) {
+
				usage_key();
+
				return (EXIT_FAILURE);
+
			}
+
			keymode = MODE_CREATE;
+
			break;
+
		case ARG_PUBLIC:
+
			if (keymode != MODE_UNSPECIFIED) {
+
				usage_key();
+
				return (EXIT_FAILURE);
+
			}
+
			keymode = MODE_PUBLIC;
+
			break;
+
		case 't':
+
			keytype = optarg;
+
			break;
+
		default:
+
			usage_key();
+
			return (EXIT_FAILURE);
+
		}
+
	}
+
	argc -= optind;
+
	argv += optind;
+

+
	if (argc != 1) {
+
		usage_key();
+
		return (EXIT_FAILURE);
+
	}
+

+
	if (keytype == NULL)
+
		keytype = "rsa";
+

+
	keypath = argv[0];
+
	if (*keypath == '\0') {
+
		fprintf(stderr, "keypath must not be empty.\n");
+
		usage_key();
+
		return (EXIT_FAILURE);
+
	}
+

+
	ret = pkg_key_new(&key, keytype, keypath, password_cb);
+
	if (ret != EPKG_OK) {
+
		fprintf(stderr, "Failed to create key context.\n");
+
		return (EXIT_FAILURE);
+
	}
+

+
	switch (keymode) {
+
	case MODE_CREATE:
+
		ret = key_create(key, argc, argv);
+
		if (ret != EPKG_OK) {
+
			switch (ret) {
+
			case EPKG_OPNOTSUPP:
+
				fprintf(stderr, "Type '%s' does not support generation.\n",
+
				    keytype);
+
				break;
+
			default:
+
				fprintf(stderr, "Failed to generate the key.\n");
+
				break;
+
			}
+

+
			goto out;
+
		}
+

+
		fprintf(stderr, "Created '%s' private key at %s\n", keytype, keypath);
+
		/* FALLTHROUGH */
+
	case MODE_PUBLIC:
+
		ret = key_pubout(key);
+
		if (ret != EPKG_OK) {
+
			switch (ret) {
+
			case EPKG_OPNOTSUPP:
+
				fprintf(stderr, "Type '%s' does not support pubout.\n",
+
				    keytype);
+
				break;
+
			default:
+
				fprintf(stderr, "Failed to get keyinfo.\n");
+
				break;
+
			}
+

+
			goto out;
+
		}
+

+
		break;
+
	case MODE_UNSPECIFIED:
+
		ret = key_info(key, keypath, keytype);
+
		if (ret != EPKG_OK) {
+
			switch (ret) {
+
			case EPKG_OPNOTSUPP:
+
				printf("Type '%s' does not support keyinfo.\n",
+
				    keytype);
+
				break;
+
			default:
+
				printf("Failed to get keyinfo.\n");
+
				break;
+
			}
+

+
			goto out;
+
		}
+

+
		break;
+
	}
+

+
out:
+
	pkg_key_free(key);
+
	return (ret == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
+
}
modified src/main.c
@@ -91,6 +91,7 @@ static struct commands {
	{ "delete", "Deletes packages from the database and the system", exec_delete, usage_delete},
	{ "fetch", "Fetches packages from a remote repository", exec_fetch, usage_fetch},
	{ "help", "Displays help information", exec_help, usage_help},
+
	{ "key", "Create or display signing key data", exec_key, usage_key},
	{ "info", "Displays information about installed packages", exec_info, usage_info},
	{ "install", "Installs packages from remote package repositories and local archives", exec_install, usage_install},
	{ "lock", "Locks package against modifications or deletion", exec_lock, usage_lock},
modified src/pkgcli.h
@@ -93,6 +93,10 @@ void usage_install(void);
int exec_plugins(int, char **);
void usage_plugins(void);

+
/* pkg key */
+
int exec_key(int, char **);
+
void usage_key(void);
+

/* pkg lock */
int exec_lock(int, char **);
int exec_unlock(int, char **);
@@ -294,6 +298,7 @@ int format_sql_condition(const char *str, xstring *sqlcond,
int analyse_query_string(char *qstr, struct query_flags *q_flags,
			 const unsigned int q_flags_len, int *flags,
			 char *multiline);
+
int password_cb(char *buf, int size, int rwflag, void *key);

extern int default_yes;
extern int yes;
modified src/repo.c
@@ -54,38 +54,6 @@ usage_repo(void)
	fprintf(stderr, "For more information see 'pkg help repo'.\n");
}

-
static int
-
password_cb(char *buf, int size, int rwflag, void *key)
-
{
-
	int len = 0;
-
	char pass[BUFSIZ];
-
	sigset_t sig, oldsig;
-

-
	(void)rwflag;
-
	(void)key;
-

-
	/* Block sigalarm temporary */
-
	sigemptyset(&sig);
-
	sigaddset(&sig, SIGALRM);
-
	sigprocmask(SIG_BLOCK, &sig, &oldsig);
-

-
	if (readpassphrase("\nEnter passphrase: ", pass, BUFSIZ, RPP_ECHO_OFF) == 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);
-

-
	sigprocmask(SIG_SETMASK, &oldsig, NULL);
-

-
	return (len);
-
}
-

int
exec_repo(int argc, char **argv)
{
modified tests/Makefile.autosetup
@@ -31,6 +31,7 @@ TESTS_SH= \
	frontend/fetch.sh \
	frontend/install.sh \
	frontend/jpeg.sh \
+
	frontend/key.sh \
	frontend/lock.sh \
	frontend/messages.sh \
	frontend/multipleprovider.sh \
added tests/frontend/key.sh
@@ -0,0 +1,44 @@
+
#! /usr/bin/env atf-sh
+

+
. $(atf_get_srcdir)/test_environment.sh
+

+
tests_init \
+
	key_create \
+
	key_pubout
+

+
key_create_head() {
+
	atf_set "require.progs" "openssl"
+
}
+
key_create_body() {
+
	echo "secure msg" > msg
+

+
	atf_check -o ignore -e ignore -x pkg key --create -t rsa repo
+
	# Group permissions are OK, but let's strive for limited to the user.
+
	atf_check -o match:'-{6}$' -x \
+
	    'ls -l repo | cut -c1-10'
+
	# Should have also created the corresponding pub key.
+
	atf_check test -f repo.pub
+

+
	# Make sure it's functional.
+
	atf_check -o save:msg.sign openssl dgst -sign repo -sha256 -binary msg
+
	atf_check -o ignore openssl dgst -sha256 -verify repo.pub -signature msg.sign msg
+
}
+

+
key_pubout_head() {
+
	atf_set "require.progs" "openssl"
+
}
+
key_pubout_body() {
+
	echo "secure msg" > msg
+

+
	atf_check -o ignore -e ignore -x pkg key --create -t rsa repo
+
	# Oops, we lost the public key.
+
	rm repo.pub
+
	atf_check test ! -f repo.pub
+
	atf_check -o save:repo.pub pkg key --public -t rsa repo
+

+
	# Make sure it's functional.
+
	atf_check -o save:msg.sign openssl dgst -sign repo -sha256 -binary msg
+
	atf_check -o ignore openssl dgst -sha256 -verify repo.pub -signature msg.sign msg
+
}
+

+