mirror of
https://github.com/systemd/systemd.git
synced 2025-03-11 20:58:27 +03:00
homectl: add signing key management verbs
This commit is contained in:
parent
f1b6417fea
commit
88392a1f60
@ -179,6 +179,18 @@
|
|||||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--key-name=</option></term>
|
||||||
|
|
||||||
|
<listitem><para>When used with the <command>add-signing-key</command> command, specify or override
|
||||||
|
the name under which to store the public key being added. The specified name can be chosen freely,
|
||||||
|
but must be suffixed with <literal>.public</literal>. If this option is not used the name is derived
|
||||||
|
from the specified filename. If a key is read from standard input this option is mandatory in order
|
||||||
|
to provide a suitable name for the key being added.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<xi:include href="user-system-options.xml" xpointer="host" />
|
<xi:include href="user-system-options.xml" xpointer="host" />
|
||||||
<xi:include href="user-system-options.xml" xpointer="machine" />
|
<xi:include href="user-system-options.xml" xpointer="machine" />
|
||||||
|
|
||||||
@ -1311,6 +1323,55 @@
|
|||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>list-signing-keys</command></term>
|
||||||
|
|
||||||
|
<listitem><para>Show a list of public keys that home directories can be signed with to be allowed for
|
||||||
|
local login. One such key (<filename>local.public</filename>) will be generated automatically for
|
||||||
|
signing locally created home directories, but additional public keys may be registered to accept home
|
||||||
|
directories from other origins too (see <command>add-signing-key</command> below).</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>get-signing-key</command> [<replaceable>NAME…</replaceable>]</term>
|
||||||
|
|
||||||
|
<listitem><para>Write the public key identified by the specified name to standard output (in PEM
|
||||||
|
format). If no name is specified defaults to <filename>local.public</filename>, i.e. the
|
||||||
|
automatically generated key for locally created home directories.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>add-signing-key</command> [<replaceable>FILE…</replaceable>]</term>
|
||||||
|
|
||||||
|
<listitem><para>Add public key(s) from the specified PEM key file(s) to the list of keys that home
|
||||||
|
areas have to be signed by to be permitted for local login. If a path of <literal>-</literal> is
|
||||||
|
specified, or if no file is specified at all, the key will be read from standard input. The key file
|
||||||
|
name(s) must carry the <filename>.public</filename> suffix, and the file name(s) will be used to name
|
||||||
|
the key(s) once added, too. If a key is added from standard input the key name must be specified
|
||||||
|
explicitly via <option>--key-name=</option>, see above.</para>
|
||||||
|
|
||||||
|
<para>This command is useful for permitting local home directories to be used on a remote
|
||||||
|
system. Example:</para>
|
||||||
|
|
||||||
|
<programlisting>homectl get-signing-key | ssh myotherhost homectl add-signing-key --key-name="$HOSTNAME".public</programlisting>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>remove-signing-key</command> <replaceable>NAME…</replaceable></term>
|
||||||
|
|
||||||
|
<listitem><para>Remove the public key identified by the specified name from the list of keys that
|
||||||
|
control from which origins to permit home directories for login.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
|
@ -112,7 +112,8 @@ _homectl() {
|
|||||||
--avatar
|
--avatar
|
||||||
--login-background
|
--login-background
|
||||||
--session-launcher
|
--session-launcher
|
||||||
--session-type'
|
--session-type
|
||||||
|
--key-name'
|
||||||
)
|
)
|
||||||
|
|
||||||
if __contains_word "$prev" ${OPTS[ARG]}; then
|
if __contains_word "$prev" ${OPTS[ARG]}; then
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
#include "glyph-util.h"
|
#include "glyph-util.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
|
#include "hexdecoct.h"
|
||||||
#include "home-util.h"
|
#include "home-util.h"
|
||||||
#include "homectl-fido2.h"
|
#include "homectl-fido2.h"
|
||||||
#include "homectl-pkcs11.h"
|
#include "homectl-pkcs11.h"
|
||||||
@ -33,6 +34,7 @@
|
|||||||
#include "locale-util.h"
|
#include "locale-util.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "memory-util.h"
|
#include "memory-util.h"
|
||||||
|
#include "openssl-util.h"
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "parse-argument.h"
|
#include "parse-argument.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
@ -96,6 +98,7 @@ static bool arg_prompt_new_user = false;
|
|||||||
static char *arg_blob_dir = NULL;
|
static char *arg_blob_dir = NULL;
|
||||||
static bool arg_blob_clear = false;
|
static bool arg_blob_clear = false;
|
||||||
static Hashmap *arg_blob_files = NULL;
|
static Hashmap *arg_blob_files = NULL;
|
||||||
|
static char *arg_key_name = NULL;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, sd_json_variant_unrefp);
|
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra, sd_json_variant_unrefp);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, sd_json_variant_unrefp);
|
STATIC_DESTRUCTOR_REGISTER(arg_identity_extra_this_machine, sd_json_variant_unrefp);
|
||||||
@ -107,6 +110,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_pkcs11_token_uri, strv_freep);
|
|||||||
STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_fido2_device, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_blob_dir, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_blob_dir, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_blob_files, hashmap_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_blob_files, hashmap_freep);
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_key_name, freep);
|
||||||
|
|
||||||
static const BusLocator *bus_mgr;
|
static const BusLocator *bus_mgr;
|
||||||
|
|
||||||
@ -2795,6 +2799,11 @@ static int help(int argc, char *argv[], void *userdata) {
|
|||||||
" rebalance Rebalance free space between home areas\n"
|
" rebalance Rebalance free space between home areas\n"
|
||||||
" with USER [COMMAND…] Run shell or command with access to a home area\n"
|
" with USER [COMMAND…] Run shell or command with access to a home area\n"
|
||||||
" firstboot Run first-boot home area creation wizard\n"
|
" firstboot Run first-boot home area creation wizard\n"
|
||||||
|
"\n%4$sSigning Keys Commands:%5$s\n"
|
||||||
|
" list-signing-keys List home signing keys\n"
|
||||||
|
" get-signing-key [NAME…] Get a named home signing key\n"
|
||||||
|
" add-signing-key FILE… Add home signing key\n"
|
||||||
|
" remove-signing-key NAME… Remove home signing key\n"
|
||||||
"\n%4$sOptions:%5$s\n"
|
"\n%4$sOptions:%5$s\n"
|
||||||
" -h --help Show this help\n"
|
" -h --help Show this help\n"
|
||||||
" --version Show package version\n"
|
" --version Show package version\n"
|
||||||
@ -2816,6 +2825,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
|||||||
" -j --export-format=minimal\n"
|
" -j --export-format=minimal\n"
|
||||||
" --prompt-new-user firstboot: Query user interactively for user\n"
|
" --prompt-new-user firstboot: Query user interactively for user\n"
|
||||||
" to create\n"
|
" to create\n"
|
||||||
|
" --key-name=NAME Key name when adding a signing key\n"
|
||||||
"\n%4$sGeneral User Record Properties:%5$s\n"
|
"\n%4$sGeneral User Record Properties:%5$s\n"
|
||||||
" -c --real-name=REALNAME Real name for user\n"
|
" -c --real-name=REALNAME Real name for user\n"
|
||||||
" --realm=REALM Realm to create user in\n"
|
" --realm=REALM Realm to create user in\n"
|
||||||
@ -3049,6 +3059,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_TMP_LIMIT,
|
ARG_TMP_LIMIT,
|
||||||
ARG_DEV_SHM_LIMIT,
|
ARG_DEV_SHM_LIMIT,
|
||||||
ARG_DEFAULT_AREA,
|
ARG_DEFAULT_AREA,
|
||||||
|
ARG_KEY_NAME,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@ -3152,6 +3163,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "tmp-limit", required_argument, NULL, ARG_TMP_LIMIT },
|
{ "tmp-limit", required_argument, NULL, ARG_TMP_LIMIT },
|
||||||
{ "dev-shm-limit", required_argument, NULL, ARG_DEV_SHM_LIMIT },
|
{ "dev-shm-limit", required_argument, NULL, ARG_DEV_SHM_LIMIT },
|
||||||
{ "default-area", required_argument, NULL, ARG_DEFAULT_AREA },
|
{ "default-area", required_argument, NULL, ARG_DEFAULT_AREA },
|
||||||
|
{ "key-name", required_argument, NULL, ARG_KEY_NAME },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4653,6 +4665,21 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ARG_KEY_NAME:
|
||||||
|
if (isempty(optarg)) {
|
||||||
|
arg_key_name = mfree(arg_key_name);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename_is_valid(optarg))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified key name not valid: %s", optarg);
|
||||||
|
|
||||||
|
r = free_and_strdup_warn(&arg_key_name, optarg);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -4915,26 +4942,247 @@ static int fallback_shell(int argc, char *argv[]) {
|
|||||||
return log_error_errno(errno, "Failed to execute shell '%s': %m", shell);
|
return log_error_errno(errno, "Failed to execute shell '%s': %m", shell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int verb_list_signing_keys(int argc, char *argv[], void *userdata) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
r = bus_call_method(bus, bus_mgr, "ListSigningKeys", &error, &reply, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to list signing keys: %s", bus_error_message(&error, r));
|
||||||
|
|
||||||
|
_cleanup_(table_unrefp) Table *table = table_new("name", "key");
|
||||||
|
if (!table)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
r = sd_bus_message_enter_container(reply, 'a', "(sst)");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
const char *name, *pem;
|
||||||
|
|
||||||
|
r = sd_bus_message_read(reply, "(sst)", &name, &pem, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
if (r == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
_cleanup_free_ char *h = NULL;
|
||||||
|
if (!sd_json_format_enabled(arg_json_format_flags)) {
|
||||||
|
/* Let's decode the PEM key to DER (so that we lose prefix/suffix), then truncate it
|
||||||
|
* for display reasons. */
|
||||||
|
|
||||||
|
_cleanup_(EVP_PKEY_freep) EVP_PKEY *key = NULL;
|
||||||
|
r = openssl_pubkey_from_pem(pem, SIZE_MAX, &key);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse PEM: %m");
|
||||||
|
|
||||||
|
_cleanup_free_ void *der = NULL;
|
||||||
|
int n = i2d_PUBKEY(key, (unsigned char**) &der);
|
||||||
|
if (n < 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), "Failed to encode key as DER: %m");
|
||||||
|
|
||||||
|
ssize_t m = base64mem(der, MIN(n, 64), &h);
|
||||||
|
if (m < 0)
|
||||||
|
return log_oom();
|
||||||
|
if (n > 64) /* check if we truncated the original version */
|
||||||
|
if (!strextend(&h, special_glyph(SPECIAL_GLYPH_ELLIPSIS)))
|
||||||
|
return log_oom();
|
||||||
|
}
|
||||||
|
|
||||||
|
r = table_add_many(
|
||||||
|
table,
|
||||||
|
TABLE_STRING, name,
|
||||||
|
TABLE_STRING, h ?: pem);
|
||||||
|
if (r < 0)
|
||||||
|
return table_log_add_error(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_exit_container(reply);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_parse_error(r);
|
||||||
|
|
||||||
|
if (!table_isempty(table) || sd_json_format_enabled(arg_json_format_flags)) {
|
||||||
|
r = table_set_sort(table, (size_t) 0);
|
||||||
|
if (r < 0)
|
||||||
|
return table_log_sort_error(r);
|
||||||
|
|
||||||
|
r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_legend && !sd_json_format_enabled(arg_json_format_flags)) {
|
||||||
|
if (table_isempty(table))
|
||||||
|
printf("No signing keys.\n");
|
||||||
|
else
|
||||||
|
printf("\n%zu signing keys listed.\n", table_get_rows(table) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verb_get_signing_key(int argc, char *argv[], void *userdata) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
char **keys = argc >= 2 ? strv_skip(argv, 1) : STRV_MAKE("local.public");
|
||||||
|
int ret = 0;
|
||||||
|
STRV_FOREACH(k, keys) {
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
|
||||||
|
r = bus_call_method(bus, bus_mgr, "GetSigningKey", &error, &reply, "s", *k);
|
||||||
|
if (r < 0) {
|
||||||
|
RET_GATHER(ret, log_error_errno(r, "Failed to get signing key '%s': %s", *k, bus_error_message(&error, r)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *pem;
|
||||||
|
r = sd_bus_message_read(reply, "st", &pem, NULL);
|
||||||
|
if (r < 0) {
|
||||||
|
RET_GATHER(ret, bus_log_parse_error(r));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
fputs(pem, stdout);
|
||||||
|
if (!endswith(pem, "\n"))
|
||||||
|
fputc('\n', stdout);
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int add_signing_key_one(sd_bus *bus, const char *fn, FILE *key) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_se(bus);
|
||||||
|
assert_se(fn);
|
||||||
|
assert_se(key);
|
||||||
|
|
||||||
|
_cleanup_free_ char *pem = NULL;
|
||||||
|
r = read_full_stream(key, &pem, /* ret_size= */ NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to read key '%s': %m", fn);
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
r = bus_call_method(bus, bus_mgr, "AddSigningKey", &error, /* reply= */ NULL, "sst", fn, pem, UINT64_C(0));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add signing key '%s': %s", fn, bus_error_message(&error, r));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verb_add_signing_key(int argc, char *argv[], void *userdata) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
int ret = EXIT_SUCCESS;
|
||||||
|
if (argc < 2 || streq(argv[1], "-")) {
|
||||||
|
if (!arg_key_name)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Key name must be specified via --key-name= when reading key from standard input, refusing.");
|
||||||
|
|
||||||
|
RET_GATHER(ret, add_signing_key_one(bus, arg_key_name, stdin));
|
||||||
|
} else {
|
||||||
|
/* Refuse if more han one key is specified in combination with --key-name= */
|
||||||
|
if (argc >= 3 && arg_key_name)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--key-name= is not supported if multiple signing keys are specified, refusing.");
|
||||||
|
|
||||||
|
STRV_FOREACH(k, strv_skip(argv, 1)) {
|
||||||
|
|
||||||
|
if (streq(*k, "-"))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing to read from standard input if multiple keys are specified.");
|
||||||
|
|
||||||
|
_cleanup_free_ char *fn = NULL;
|
||||||
|
if (!arg_key_name) {
|
||||||
|
r = path_extract_filename(*k, &fn);
|
||||||
|
if (r < 0) {
|
||||||
|
RET_GATHER(ret, log_error_errno(r, "Failed to extract filename from path '%s': %m", *k));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_fclose_ FILE *f = fopen(*k, "re");
|
||||||
|
if (!f) {
|
||||||
|
RET_GATHER(ret, log_error_errno(errno, "Failed to open '%s': %m", *k));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
RET_GATHER(ret, add_signing_key_one(bus, fn ?: arg_key_name, f));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int remove_signing_key_one(sd_bus *bus, const char *fn) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert_se(bus);
|
||||||
|
assert_se(fn);
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
|
r = bus_call_method(bus, bus_mgr, "RemoveSigningKey", &error, /* reply= */ NULL, "st", fn, UINT64_C(0));
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to remove signing key '%s': %s", fn, bus_error_message(&error, r));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int verb_remove_signing_key(int argc, char *argv[], void *userdata) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
|
||||||
|
r = acquire_bus(&bus);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = EXIT_SUCCESS;
|
||||||
|
STRV_FOREACH(k, strv_skip(argv, 1))
|
||||||
|
RET_GATHER(r, remove_signing_key_one(bus, *k));
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static int run(int argc, char *argv[]) {
|
static int run(int argc, char *argv[]) {
|
||||||
static const Verb verbs[] = {
|
static const Verb verbs[] = {
|
||||||
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||||
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_homes },
|
{ "list", VERB_ANY, 1, VERB_DEFAULT, list_homes },
|
||||||
{ "activate", 2, VERB_ANY, 0, activate_home },
|
{ "activate", 2, VERB_ANY, 0, activate_home },
|
||||||
{ "deactivate", 2, VERB_ANY, 0, deactivate_home },
|
{ "deactivate", 2, VERB_ANY, 0, deactivate_home },
|
||||||
{ "inspect", VERB_ANY, VERB_ANY, 0, inspect_home },
|
{ "inspect", VERB_ANY, VERB_ANY, 0, inspect_home },
|
||||||
{ "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home },
|
{ "authenticate", VERB_ANY, VERB_ANY, 0, authenticate_home },
|
||||||
{ "create", VERB_ANY, 2, 0, create_home },
|
{ "create", VERB_ANY, 2, 0, create_home },
|
||||||
{ "remove", 2, VERB_ANY, 0, remove_home },
|
{ "remove", 2, VERB_ANY, 0, remove_home },
|
||||||
{ "update", VERB_ANY, 2, 0, update_home },
|
{ "update", VERB_ANY, 2, 0, update_home },
|
||||||
{ "passwd", VERB_ANY, 2, 0, passwd_home },
|
{ "passwd", VERB_ANY, 2, 0, passwd_home },
|
||||||
{ "resize", 2, 3, 0, resize_home },
|
{ "resize", 2, 3, 0, resize_home },
|
||||||
{ "lock", 2, VERB_ANY, 0, lock_home },
|
{ "lock", 2, VERB_ANY, 0, lock_home },
|
||||||
{ "unlock", 2, VERB_ANY, 0, unlock_home },
|
{ "unlock", 2, VERB_ANY, 0, unlock_home },
|
||||||
{ "with", 2, VERB_ANY, 0, with_home },
|
{ "with", 2, VERB_ANY, 0, with_home },
|
||||||
{ "lock-all", VERB_ANY, 1, 0, lock_all_homes },
|
{ "lock-all", VERB_ANY, 1, 0, lock_all_homes },
|
||||||
{ "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes },
|
{ "deactivate-all", VERB_ANY, 1, 0, deactivate_all_homes },
|
||||||
{ "rebalance", VERB_ANY, 1, 0, rebalance },
|
{ "rebalance", VERB_ANY, 1, 0, rebalance },
|
||||||
{ "firstboot", VERB_ANY, 1, 0, verb_firstboot },
|
{ "firstboot", VERB_ANY, 1, 0, verb_firstboot },
|
||||||
|
{ "list-signing-keys", VERB_ANY, 1, 0, verb_list_signing_keys },
|
||||||
|
{ "get-signing-key", VERB_ANY, VERB_ANY, 0, verb_get_signing_key },
|
||||||
|
{ "add-signing-key", VERB_ANY, VERB_ANY, 0, verb_add_signing_key },
|
||||||
|
{ "remove-signing-key", 2, VERB_ANY, 0, verb_remove_signing_key },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user