mirror of
https://github.com/gnuton/asuswrt-merlin.ng.git
synced 2025-05-19 16:02:36 +02:00
545 lines
18 KiB
C
545 lines
18 KiB
C
/*
|
|
* fscryptctl.c - Low level tool for managing keys and policies for the
|
|
* fs/crypto kernel interface. Specifically, this tool:
|
|
* - Computes the descriptor for a provided key
|
|
* - Inserts a provided key into the keyring
|
|
* - Queries the key descriptor for an encrypted directory
|
|
* - Applies an encryption policy to an empty directory
|
|
*
|
|
* Copyright 2017 Google Inc.
|
|
* Author: Joe Richey (joerichey@google.com)
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
* use this file except in compliance with the License. You may obtain a copy of
|
|
* the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations under
|
|
* the License.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <getopt.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/syscall.h>
|
|
#include <unistd.h>
|
|
|
|
#include "sha512.h"
|
|
|
|
// Some of the necessary structures, constants, and functions are declared in
|
|
// <linux/fs.h> or <keyutils.h> but are either incomplete (depending on your
|
|
// kernel version) or require an external library. So to simplify things, they
|
|
// are just redeclared here.
|
|
|
|
/* Begin <linux/fs.h> */
|
|
#define FS_MAX_KEY_SIZE 64
|
|
|
|
struct fscrypt_key {
|
|
uint32_t mode;
|
|
uint8_t raw[FS_MAX_KEY_SIZE];
|
|
uint32_t size;
|
|
} __attribute__((packed));
|
|
|
|
#define FS_KEY_DESCRIPTOR_SIZE 8
|
|
#define FS_KEY_DESCRIPTOR_HEX_SIZE ((2 * FS_KEY_DESCRIPTOR_SIZE) + 1)
|
|
|
|
// Amount of padding
|
|
#define FS_POLICY_FLAGS_PAD_4 0x00
|
|
#define FS_POLICY_FLAGS_PAD_8 0x01
|
|
#define FS_POLICY_FLAGS_PAD_16 0x02
|
|
#define FS_POLICY_FLAGS_PAD_32 0x03
|
|
#define FS_POLICY_FLAGS_PAD_MASK 0x03
|
|
|
|
// Encryption algorithms
|
|
#define FS_ENCRYPTION_MODE_INVALID 0
|
|
#define FS_ENCRYPTION_MODE_AES_256_XTS 1
|
|
#define FS_ENCRYPTION_MODE_AES_256_GCM 2
|
|
#define FS_ENCRYPTION_MODE_AES_256_CBC 3
|
|
#define FS_ENCRYPTION_MODE_AES_256_CTS 4
|
|
#define FS_ENCRYPTION_MODE_AES_128_CBC 5
|
|
#define FS_ENCRYPTION_MODE_AES_128_CTS 6
|
|
|
|
// Policy provided via an ioctl on the topmost directory
|
|
struct fscrypt_policy {
|
|
uint8_t version;
|
|
uint8_t contents_encryption_mode;
|
|
uint8_t filenames_encryption_mode;
|
|
uint8_t flags;
|
|
uint8_t master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
|
|
} __attribute__((packed));
|
|
|
|
#define FS_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct fscrypt_policy)
|
|
#define FS_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct fscrypt_policy)
|
|
|
|
// Service prefixes for encryption keys
|
|
#define FS_KEY_DESC_PREFIX "fscrypt:"
|
|
#define EXT4_KEY_DESC_PREFIX "ext4:" // For ext4 before 4.8 kernel
|
|
#define F2FS_KEY_DESC_PREFIX "f2fs:" // For f2fs before 4.6 kernel
|
|
#define MAX_KEY_DESC_PREFIX_SIZE 8
|
|
/* End <linux/fs.h> */
|
|
|
|
/* Begin <keyutils.h> */
|
|
typedef int32_t key_serial_t;
|
|
#define KEYCTL_GET_KEYRING_ID 0 /* ask for a keyring's ID */
|
|
#define KEY_SPEC_SESSION_KEYRING -3 /* current session keyring */
|
|
|
|
key_serial_t add_key(const char *type, const char *description,
|
|
const void *payload, size_t plen, key_serial_t ringid) {
|
|
return syscall(__NR_add_key, type, description, payload, plen, ringid);
|
|
}
|
|
|
|
key_serial_t keyctl_get_keyring_ID(key_serial_t id, int create) {
|
|
return syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, id, create);
|
|
}
|
|
/* End <keyutils.h> */
|
|
|
|
// Human-readable strings for encryption modes, indexed by the encryption mode
|
|
#define NUM_ENCRYPTION_MODES 7
|
|
const char *const mode_strings[NUM_ENCRYPTION_MODES] = {
|
|
"INVALID", "AES-256-XTS", "AES-256-GCM", "AES-256-CBC",
|
|
"AES-256-CTS", "AES-128-CBC", "AES-128-CTS"};
|
|
|
|
// Valid amounts of filename padding, indexed by the padding flag
|
|
#define NUM_PADDING_VALUES 4
|
|
const int padding_values[NUM_PADDING_VALUES] = {4, 8, 16, 32};
|
|
|
|
/* util-linux style usage */
|
|
static void __attribute__((__noreturn__)) usage(FILE *out) {
|
|
fputs(
|
|
"\nUsage:\n"
|
|
" fscryptctl <command> [arguments] [options]\n"
|
|
"\nCommands:\n"
|
|
" fscryptctl get_descriptor\n"
|
|
" Read a key from stdin, and print the hex descriptor of the key.\n"
|
|
" fscryptctl insert_key\n"
|
|
" Read a key from stdin, insert the key into the current session\n"
|
|
" keyring (or the user session keyring if a session keyring does not\n"
|
|
" exist), and print the descriptor of the key.\n"
|
|
" fscryptctl get_policy <file or directory>\n"
|
|
" Print out the encryption policy for the specified path.\n"
|
|
" fscryptctl set_policy <key descriptor> <directory>\n"
|
|
" Set up an encryption policy on the specified directory with the\n"
|
|
" specified key descriptor.\n"
|
|
"\nOptions:\n"
|
|
" -h, --help\n"
|
|
" print this help screen\n"
|
|
" -v, --version\n"
|
|
" print the version of fscrypt\n"
|
|
" insert_key\n"
|
|
" --ext4\n"
|
|
" for use with an ext4 filesystem before kernel v4.8\n"
|
|
" --f2fs\n"
|
|
" for use with an F2FS filesystem before kernel v4.6\n"
|
|
" set_policy\n"
|
|
" --contents=<mode>\n"
|
|
" contents encryption mode (default: AES-256-XTS)\n"
|
|
" --filenames=<mode>\n"
|
|
" filenames encryption mode (default: AES-256-CTS)\n"
|
|
" --padding=<bytes>\n"
|
|
" bytes of zero padding for filenames (default: 32)\n"
|
|
"\nNotes:\n"
|
|
" All input keys are 64 bytes long and formatted as binary.\n"
|
|
" All descriptors are 8 bytes and formatted as hex (16 characters).\n",
|
|
out);
|
|
|
|
exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
|
|
// For getting/setting policies, our error messages might differ from the
|
|
// standard ones for certain errno values.
|
|
const char *policy_error(int errno_val) {
|
|
// Only these errno values actually relate to filesystem encryption
|
|
switch (errno_val) {
|
|
case ENOTTY:
|
|
return "your kernel is too old to support filesystem encryption, or the "
|
|
"filesystem you are using does not support encryption";
|
|
case EOPNOTSUPP:
|
|
return "filesystem encryption has been disabled in the kernel config, or "
|
|
"you need to enable encryption on your filesystem (see the README "
|
|
"for more detailed instructions).";
|
|
case ENODATA:
|
|
return "file or directory not encrypted";
|
|
case EEXIST:
|
|
// EINVAL was returned instead of EEXIST on some filesystems before v4.11.
|
|
// However, we do not attempt to work around this.
|
|
return "file or directory already encrypted";
|
|
case EINVAL:
|
|
return "invalid encryption options provided";
|
|
default:
|
|
return strerror(errno_val);
|
|
}
|
|
}
|
|
|
|
// Converts str to an encryption mode. Returns 0 (FS_ENCRYPTION_MODE_INVALID) if
|
|
// the string does not correspond to an encryption mode.
|
|
static uint8_t string_to_mode(const char *str) {
|
|
uint8_t i;
|
|
for (i = 1; i < NUM_ENCRYPTION_MODES; ++i) {
|
|
if (strcmp(str, mode_strings[i]) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Converts the encryption mode to a human-readable string. Returns "INVALID" if
|
|
// the mode is not a valid encryption mode.
|
|
static const char *mode_to_string(uint8_t mode) {
|
|
if (mode >= NUM_ENCRYPTION_MODES) {
|
|
mode = 0;
|
|
}
|
|
return mode_strings[mode];
|
|
}
|
|
|
|
// Converts an amount of padding (as a string) into the appropriate padding
|
|
// flag. Returns -1 if the flag is invalid.
|
|
static int string_to_padding_flag(const char *str) {
|
|
int i, padding = atoi(str);
|
|
for (i = 0; i < NUM_PADDING_VALUES; ++i) {
|
|
if (padding == padding_values[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Takes an input key descriptor as a byte array and outputs a hex string.
|
|
static void key_descriptor_to_hex(const uint8_t bytes[FS_KEY_DESCRIPTOR_SIZE],
|
|
char hex[FS_KEY_DESCRIPTOR_HEX_SIZE]) {
|
|
int i;
|
|
for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; ++i) {
|
|
sprintf(hex + 2 * i, "%02x", bytes[i]);
|
|
}
|
|
}
|
|
|
|
// Takes an input key descriptor as a hex string and outputs a bytes array.
|
|
// Returns non-zero if the provided hex string is not formatted correctly.
|
|
static int key_descriptor_to_bytes(const char *hex,
|
|
uint8_t bytes[FS_KEY_DESCRIPTOR_SIZE]) {
|
|
if (strlen(hex) != FS_KEY_DESCRIPTOR_HEX_SIZE - 1) {
|
|
return -1;
|
|
}
|
|
|
|
int i, bytes_converted, chars_read;
|
|
for (i = 0; i < FS_KEY_DESCRIPTOR_SIZE; ++i) {
|
|
// We must read two hex characters of input into one byte of buffer.
|
|
bytes_converted = sscanf(hex + 2 * i, "%2hhx%n", bytes + i, &chars_read);
|
|
if (bytes_converted != 1 || chars_read != 2) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Reads key data from stdin into the provided data buffer. Return 0 on success.
|
|
static int read_key(uint8_t key[FS_MAX_KEY_SIZE]) {
|
|
size_t rc = fread(key, 1, FS_MAX_KEY_SIZE, stdin);
|
|
int end = fgetc(stdin);
|
|
// We should read exactly FS_MAX_KEY_SIZE bytes, then hit EOF
|
|
if (rc == FS_MAX_KEY_SIZE && end == EOF && feof(stdin)) {
|
|
return 0;
|
|
}
|
|
|
|
fprintf(stderr, "error: input key must be %d bytes\n", FS_MAX_KEY_SIZE);
|
|
return -1;
|
|
}
|
|
|
|
// The descriptor is just the first 8 bytes of a double application of SHA512
|
|
// formatted as hex (so 16 characters).
|
|
static void compute_descriptor(const uint8_t key[FS_MAX_KEY_SIZE],
|
|
char descriptor[FS_KEY_DESCRIPTOR_HEX_SIZE]) {
|
|
uint8_t digest1[SHA512_DIGEST_LENGTH];
|
|
SHA512(key, FS_MAX_KEY_SIZE, digest1);
|
|
|
|
uint8_t digest2[SHA512_DIGEST_LENGTH];
|
|
SHA512(digest1, SHA512_DIGEST_LENGTH, digest2);
|
|
|
|
key_descriptor_to_hex(digest2, descriptor);
|
|
secure_wipe(digest1, SHA512_DIGEST_LENGTH);
|
|
secure_wipe(digest2, SHA512_DIGEST_LENGTH);
|
|
}
|
|
|
|
// Inserts the key into the current session keyring with type logon and the
|
|
// service specified by service_prefix.
|
|
static int insert_logon_key(const uint8_t key_data[FS_MAX_KEY_SIZE],
|
|
const char descriptor[FS_KEY_DESCRIPTOR_HEX_SIZE],
|
|
const char *service_prefix) {
|
|
// We cannot add directly to KEY_SPEC_SESSION_KEYRING, as that will make a new
|
|
// session keyring if one does not exist, rather than adding it to the user
|
|
// session keyring.
|
|
int keyring_id = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
|
|
if (keyring_id < 0) {
|
|
return -1;
|
|
}
|
|
|
|
char description[MAX_KEY_DESC_PREFIX_SIZE + FS_KEY_DESCRIPTOR_HEX_SIZE];
|
|
sprintf(description, "%s%s", service_prefix, descriptor);
|
|
|
|
struct fscrypt_key key = {.mode = 0, .size = FS_MAX_KEY_SIZE};
|
|
memcpy(key.raw, key_data, FS_MAX_KEY_SIZE);
|
|
|
|
int ret =
|
|
add_key("logon", description, &key, sizeof(key), keyring_id) < 0 ? -1 : 0;
|
|
|
|
secure_wipe(key.raw, FS_MAX_KEY_SIZE);
|
|
return ret;
|
|
}
|
|
|
|
static int get_policy(const char *path, struct fscrypt_policy *policy) {
|
|
// We can query the policy for a directory or a file in that directory.
|
|
int fd = open(path, O_RDONLY);
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
int ret = ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, policy);
|
|
close(fd);
|
|
|
|
if (ret < 0) {
|
|
// Kernels prior to v4.11 returned ENOENT if the file did not have an
|
|
// encryption policy, newer kernels properly return ENODATA. This lets us
|
|
// print the right error in policy_error regardless of kernel version.
|
|
if (errno == ENOENT) {
|
|
errno = ENODATA;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int set_policy(const char *path, const struct fscrypt_policy *policy) {
|
|
// Policies can only be set on directories
|
|
int fd = open(path, O_RDONLY | O_DIRECTORY);
|
|
if (fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
int ret = ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, policy);
|
|
close(fd);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Functions for various actions, return 0 on success, non-zero on failure. */
|
|
|
|
// Get the descriptor for some key data passed via stdin. Provided key data must
|
|
// have length FS_MAX_KEY_SIZE. Output will be formatted as hex.
|
|
static int cmd_get_descriptor(int argc, char *const argv[]) {
|
|
if (argc != 2) {
|
|
fputs("error: unexpected arguments\n", stderr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
int ret = EXIT_SUCCESS;
|
|
uint8_t key[FS_MAX_KEY_SIZE];
|
|
|
|
if (read_key(key)) {
|
|
ret = EXIT_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
char descriptor[FS_KEY_DESCRIPTOR_HEX_SIZE];
|
|
compute_descriptor(key, descriptor);
|
|
puts(descriptor);
|
|
|
|
cleanup:
|
|
secure_wipe(key, FS_MAX_KEY_SIZE);
|
|
return ret;
|
|
}
|
|
|
|
// Insert a key read from stdin into the current session keyring. This has the
|
|
// effect of unlocking files encrypted with that key.
|
|
static int cmd_insert_key(int argc, char *const argv[]) {
|
|
// Which prefix will be used in this program, changed via command line flag.
|
|
const char *service_prefix = FS_KEY_DESC_PREFIX;
|
|
|
|
static const struct option insert_key_options[] = {
|
|
{"ext4", no_argument, NULL, 'e'},
|
|
{"f2fs", no_argument, NULL, 'f'},
|
|
{NULL, 0, NULL, 0}};
|
|
|
|
int ch;
|
|
while ((ch = getopt_long(argc, argv, "", insert_key_options, NULL)) != -1) {
|
|
switch (ch) {
|
|
case 'e':
|
|
service_prefix = EXT4_KEY_DESC_PREFIX;
|
|
break;
|
|
case 'f':
|
|
service_prefix = F2FS_KEY_DESC_PREFIX;
|
|
break;
|
|
default:
|
|
usage(stderr);
|
|
}
|
|
}
|
|
|
|
// This command does not need additional arguments
|
|
if (argc != optind + 1) {
|
|
fputs("error: unexpected arguments\n", stderr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
int ret = EXIT_SUCCESS;
|
|
uint8_t key[FS_MAX_KEY_SIZE];
|
|
if (read_key(key)) {
|
|
ret = EXIT_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
|
|
char descriptor[FS_KEY_DESCRIPTOR_HEX_SIZE];
|
|
compute_descriptor(key, descriptor);
|
|
if (insert_logon_key(key, descriptor, service_prefix)) {
|
|
fprintf(stderr, "error: inserting key: %s\n", strerror(errno));
|
|
ret = EXIT_FAILURE;
|
|
goto cleanup;
|
|
}
|
|
puts(descriptor);
|
|
|
|
cleanup:
|
|
secure_wipe(key, FS_MAX_KEY_SIZE);
|
|
return ret;
|
|
}
|
|
|
|
// For a specified file or directory with encryption enabled, print the
|
|
// corresponding policy to stdout. Key descriptor will be formatted as hex.
|
|
static int cmd_get_policy(int argc, char *const argv[]) {
|
|
if (argc != 3) {
|
|
fputs("error: must specify a single file or directory\n", stderr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
const char *path = argv[2];
|
|
|
|
struct fscrypt_policy policy;
|
|
if (get_policy(path, &policy)) {
|
|
fprintf(stderr, "error: getting policy for %s: %s\n", path,
|
|
policy_error(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
// Pretty print the policy (includes key descriptor and flags)
|
|
char descriptor[FS_KEY_DESCRIPTOR_HEX_SIZE];
|
|
key_descriptor_to_hex(policy.master_key_descriptor, descriptor);
|
|
int padding = padding_values[policy.flags & FS_POLICY_FLAGS_PAD_MASK];
|
|
|
|
printf("Encryption policy for %s:\n", path);
|
|
printf("\tPolicy Version: %d\n", policy.version);
|
|
printf("\tKey Descriptor: %s\n", descriptor);
|
|
printf("\tContents: %s\n", mode_to_string(policy.contents_encryption_mode));
|
|
printf("\tFilenames: %s\n", mode_to_string(policy.filenames_encryption_mode));
|
|
printf("\tPadding: %d\n", padding);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
// Apply a policy (i.e. the specified descriptor) to the specified directory.
|
|
// The policy options defaults can be overridden by command-line options.
|
|
static int cmd_set_policy(int argc, char *const argv[]) {
|
|
// As Kernel version 4.9, the only policy field that has multiple valid
|
|
// options is "flags", which sets the amount of zero padding on filenames.
|
|
struct fscrypt_policy policy = {
|
|
.version = 0,
|
|
.contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS,
|
|
.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS,
|
|
// Use maximum zero-padding to leak less info about filename length
|
|
.flags = FS_POLICY_FLAGS_PAD_32};
|
|
|
|
// Use the command-line options to modify the policy
|
|
static const struct option insert_key_options[] = {
|
|
{"contents", required_argument, NULL, 'c'},
|
|
{"filenames", required_argument, NULL, 'f'},
|
|
{"padding", required_argument, NULL, 'p'},
|
|
{NULL, 0, NULL, 0}};
|
|
|
|
int ch, padding_flag;
|
|
while ((ch = getopt_long(argc, argv, "", insert_key_options, NULL)) != -1) {
|
|
switch (ch) {
|
|
case 'c':
|
|
policy.contents_encryption_mode = string_to_mode(optarg);
|
|
if (policy.contents_encryption_mode == FS_ENCRYPTION_MODE_INVALID) {
|
|
fprintf(stderr, "error: invalid contents mode: %s\n", optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'f':
|
|
policy.filenames_encryption_mode = string_to_mode(optarg);
|
|
if (policy.filenames_encryption_mode == FS_ENCRYPTION_MODE_INVALID) {
|
|
fprintf(stderr, "error: invalid filenames mode: %s\n", optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
break;
|
|
case 'p':
|
|
padding_flag = string_to_padding_flag(optarg);
|
|
if (padding_flag < 0) {
|
|
fprintf(stderr, "error: invalid padding: %s\n", optarg);
|
|
return EXIT_FAILURE;
|
|
}
|
|
policy.flags = padding_flag;
|
|
break;
|
|
default:
|
|
usage(stderr);
|
|
}
|
|
}
|
|
|
|
// We should have exactly 2 more arguments
|
|
if (argc != optind + 3) {
|
|
fputs("error: must specify a key descriptor and directory\n", stderr);
|
|
return EXIT_FAILURE;
|
|
}
|
|
const char *descriptor = argv[optind + 1];
|
|
const char *path = argv[optind + 2];
|
|
|
|
// Copy the descriptor into the policy, requires changing format.
|
|
if (key_descriptor_to_bytes(descriptor, policy.master_key_descriptor)) {
|
|
fprintf(stderr, "error: invalid descriptor: %s\n", descriptor);
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (set_policy(path, &policy)) {
|
|
fprintf(stderr, "error: setting policy for %s: %s\n", path,
|
|
policy_error(errno));
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int main(int argc, char *const argv[]) {
|
|
// Check for the help flag
|
|
int i;
|
|
for (i = 1; i < argc; ++i) {
|
|
if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
|
|
usage(stdout);
|
|
}
|
|
if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
|
|
puts(VERSION);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (argc < 2) {
|
|
fputs("error: no command specified\n", stderr);
|
|
usage(stderr);
|
|
}
|
|
const char *command = argv[1];
|
|
|
|
if (strcmp(command, "get_descriptor") == 0) {
|
|
return cmd_get_descriptor(argc, argv);
|
|
} else if (strcmp(command, "insert_key") == 0) {
|
|
return cmd_insert_key(argc, argv);
|
|
} else if (strcmp(command, "get_policy") == 0) {
|
|
return cmd_get_policy(argc, argv);
|
|
} else if (strcmp(command, "set_policy") == 0) {
|
|
return cmd_set_policy(argc, argv);
|
|
}
|
|
|
|
fprintf(stderr, "error: invalid command: %s\n", command);
|
|
usage(stderr);
|
|
}
|