From b30875c6c74391c5239b394ccdce91b45c1b1d2c Mon Sep 17 00:00:00 2001 From: Anderson Toshiyuki Sasaki Date: Tue, 20 Aug 2019 14:49:27 +0200 Subject: [PATCH] examples: Added keygen2 example The added example is an application which can generate keys of different types using libssh. Signed-off-by: Anderson Toshiyuki Sasaki Reviewed-by: Jakub Jelen --- examples/CMakeLists.txt | 4 + examples/keygen2.c | 457 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 examples/keygen2.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d064fd5d..17a7d149 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -38,6 +38,10 @@ if (UNIX AND NOT WIN32) target_compile_options(ssh-client PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) target_link_libraries(ssh-client ${LIBSSH_SHARED_LIBRARY}) + add_executable(keygen2 keygen2.c ${examples_SRCS}) + target_compile_options(keygen2 PRIVATE ${DEFAULT_C_COMPILE_FLAGS}) + target_link_libraries(keygen2 ${LIBSSH_SHARED_LIBRARY}) + if (WITH_SERVER AND (ARGP_LIBRARY OR HAVE_ARGP_H)) if (HAVE_LIBUTIL) add_executable(ssh_server_fork ssh_server_fork.c) diff --git a/examples/keygen2.c b/examples/keygen2.c new file mode 100644 index 00000000..b9a22211 --- /dev/null +++ b/examples/keygen2.c @@ -0,0 +1,457 @@ +/* + * keygen2.c - Generate SSH keys using libssh + * Author: Anderson Toshiyuki Sasaki + */ + +/* + * Copyright (c) 2019 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct arguments_st { + enum ssh_keytypes_e type; + unsigned long bits; + char *file; + char *passphrase; +}; + +static struct argp_option options[] = { + { + .name = "bits", + .key = 'b', + .arg = "BITS", + .flags = 0, + .doc = "The size of the key to be generated. " + "If omitted, a default value is used depending on the TYPE. " + "Accepted values are: " + "1024, 2048, 3072 (default), 4096, and 8192 for TYPE=\"rsa\"; " + "256 (default), 384, and 521 for TYPE=\"ecdsa\"; " + "1024 (default) and 2048 for TYPE=\"dsa\"; " + "can be omitted for TYPE=\"ed25519\" " + "(it will be ignored if provided).\n", + .group = 0 + }, + { + .name = "file", + .key = 'f', + .arg = "FILE", + .flags = 0, + .doc = "The output file. " + "If not provided, the used file name will be generated " + "according to the key type as \"id_TYPE\" " + "(e.g. \"id_rsa\" for type \"rsa\"). " + "The public key file name is generated from the private key " + "file name by appending \".pub\".\n", + .group = 0 + }, + { + .name = "passphrase", + .key = 'p', + .arg = "PASSPHRASE", + .flags = 0, + .doc = "The passphrase used to encrypt the private key. " + "If omitted the file will not be encrypted.\n", + .group = 0 + }, + { + .name = "type", + .key = 't', + .arg = "TYPE", + .flags = 0, + .doc = "The type of the key to be generated. " + "Accepted values are: " + "\"rsa\", \"ecdsa\", \"ed25519\", and \"dsa\".\n", + .group = 0 + }, + { + /* End of the options */ + 0 + }, +}; + +/* Parse a single option. */ +static error_t parse_opt (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + * know is a pointer to our arguments structure. + */ + struct arguments_st *arguments = NULL; + error_t rc = 0; + + if (state == NULL) { + return EINVAL; + } + + arguments = state->input; + if (arguments == NULL) { + fprintf(stderr, "Error: NULL pointer to arguments structure " + "provided\n"); + rc = EINVAL; + goto end; + } + + switch (key) { + case 'b': + errno = 0; + arguments->bits = strtoul(arg, NULL, 10); + if (errno != 0) { + rc = errno; + goto end; + } + break; + case 'f': + arguments->file = strdup(arg); + if (arguments->file == NULL) { + fprintf(stderr, "Error: Out of memory\n"); + rc = ENOMEM; + goto end; + } + break; + case 'p': + arguments->passphrase = strdup(arg); + if (arguments->passphrase == NULL) { + fprintf(stderr, "Error: Out of memory\n"); + rc = ENOMEM; + goto end; + } + break; + case 't': + if (!strcmp(arg, "rsa")) { + arguments->type = SSH_KEYTYPE_RSA; + } + else if (!strcmp(arg, "dsa")) { + arguments->type = SSH_KEYTYPE_DSS; + } + else if (!strcmp(arg, "ecdsa")) { + arguments->type = SSH_KEYTYPE_ECDSA; + } + else if (!strcmp(arg, "ed25519")) { + arguments->type = SSH_KEYTYPE_ED25519; + } + else { + fprintf(stderr, "Error: Invalid key type\n"); + argp_usage(state); + rc = EINVAL; + goto end; + } + break; + case ARGP_KEY_ARG: + if (state->arg_num > 0) { + /* Too many arguments. */ + printf("Error: Too many arguments\n"); + argp_usage(state); + } + break; + case ARGP_KEY_END: + break; + default: + return ARGP_ERR_UNKNOWN; + } + +end: + return rc; +} + +static int validate_args(struct arguments_st *args) +{ + int rc = 0; + + if (args == NULL) { + return EINVAL; + } + + switch(args->type) { + case SSH_KEYTYPE_RSA: + switch(args->bits) { + case 0: + /* If not provided, use default value */ + args->bits = 3072; + break; + case 1024: + case 2048: + case 3072: + case 4096: + case 8192: + break; + default: + fprintf(stderr, "Error: Invalid bits parameter provided\n"); + rc = EINVAL; + break; + } + + if (args->file == NULL) { + args->file = strdup("id_rsa"); + if (args->file == NULL) { + rc = ENOMEM; + break; + } + } + + break; + case SSH_KEYTYPE_ECDSA: + switch(args->bits) { + case 0: + /* If not provided, use default value */ + args->bits = 256; + break; + case 256: + case 384: + case 521: + break; + default: + fprintf(stderr, "Error: Invalid bits parameter provided\n"); + rc = EINVAL; + break; + } + if (args->file == NULL) { + args->file = strdup("id_ecdsa"); + if (args->file == NULL) { + rc = ENOMEM; + break; + } + } + + break; + case SSH_KEYTYPE_DSS: + switch(args->bits) { + case 0: + /* If not provided, use default value */ + args->bits = 1024; + break; + case 1024: + case 2048: + break; + default: + fprintf(stderr, "Error: Invalid bits parameter provided\n"); + rc = EINVAL; + break; + } + if (args->file == NULL) { + args->file = strdup("id_dsa"); + if (args->file == NULL) { + rc = ENOMEM; + break; + } + } + + break; + case SSH_KEYTYPE_ED25519: + /* Ignore value and overwrite with a zero */ + args->bits = 0; + + if (args->file == NULL) { + args->file = strdup("id_ed25519"); + if (args->file == NULL) { + rc = ENOMEM; + break; + } + } + + break; + default: + fprintf(stderr, "Error: unknown key type\n"); + rc = EINVAL; + break; + } + + return rc; +} + +/* Program documentation. */ +static char doc[] = "Generate an SSH key pair. " + "The \"--type\" (short: \"-t\") option is required."; + +/* Our argp parser */ +static struct argp argp = {options, parse_opt, NULL, doc, NULL, NULL, NULL}; + +int main(int argc, char *argv[]) +{ + ssh_key key = NULL; + int rc = 0; + char overwrite[1024] = ""; + + char *pubkey_file = NULL; + + struct arguments_st arguments = { + .type = SSH_KEYTYPE_UNKNOWN, + .bits = 0, + .file = NULL, + .passphrase = NULL, + }; + + if (argc < 2) { + argp_help(&argp, stdout, ARGP_HELP_DOC | ARGP_HELP_USAGE, argv[0]); + goto end; + } + + rc = argp_parse(&argp, argc, argv, 0, 0, &arguments); + if (rc != 0) { + goto end; + } + + rc = validate_args(&arguments); + if (rc != 0) { + goto end; + } + + errno = 0; + rc = open(arguments.file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); + if (rc < 0) { + if (errno == EEXIST) { + printf("File \"%s\" exists. Overwrite it? (y|n) ", arguments.file); + scanf("%1023s", overwrite); + if (tolower(overwrite[0]) == 'y') { + rc = open(arguments.file, O_WRONLY); + if (rc > 0) { + close(rc); + errno = 0; + rc = chmod(arguments.file, S_IRUSR | S_IWUSR); + if (rc != 0) { + fprintf(stderr, + "Error(%d): Could not set file permissions\n", + errno); + goto end; + } + } else { + fprintf(stderr, + "Error: Could not create private key file\n"); + goto end; + } + } else { + goto end; + } + } else { + fprintf(stderr, "Error opening \"%s\" file\n", arguments.file); + goto end; + } + } else { + close(rc); + } + + /* Generate a new private key */ + rc = ssh_pki_generate(arguments.type, arguments.bits, &key); + if (rc != SSH_OK) { + fprintf(stderr, "Error: Failed to generate keys"); + goto end; + } + + /* Write the private key */ + rc = ssh_pki_export_privkey_file(key, arguments.passphrase, NULL, NULL, + arguments.file); + if (rc != SSH_OK) { + fprintf(stderr, "Error: Failed to write private key file"); + goto end; + } + + /* If a passphrase was provided, overwrite and free it as it is not needed + * anymore */ + if (arguments.passphrase != NULL) { +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(arguments.passphrase, strlen(arguments.passphrase)); +#else + bzero(arguments.passphrase, strlen(arguments.passphrase)); +#endif + free(arguments.passphrase); + arguments.passphrase = NULL; + } + + pubkey_file = (char *)malloc(strlen(arguments.file) + 5); + if (pubkey_file == NULL) { + rc = ENOMEM; + goto end; + } + + sprintf(pubkey_file, "%s.pub", arguments.file); + + errno = 0; + rc = open(pubkey_file, + O_CREAT | O_EXCL | O_WRONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (rc < 0) { + if (errno == EEXIST) { + printf("File \"%s\" exists. Overwrite it? (y|n) ", pubkey_file); + scanf("%1023s", overwrite); + if (tolower(overwrite[0]) == 'y') { + rc = open(pubkey_file, O_WRONLY); + if (rc > 0) { + close(rc); + errno = 0; + rc = chmod(pubkey_file, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (rc != 0) { + fprintf(stderr, + "Error(%d): Could not set file permissions\n", + errno); + goto end; + } + } else { + fprintf(stderr, + "Error: Could not create public key file\n"); + goto end; + } + } else { + goto end; + } + } else { + fprintf(stderr, "Error opening \"%s\" file\n", pubkey_file); + goto end; + } + } else { + close(rc); + } + + /* Write the public key */ + rc = ssh_pki_export_pubkey_file(key, pubkey_file); + if (rc != SSH_OK) { + fprintf(stderr, "Error: Failed to write public key file"); + goto end; + } + +end: + if (key != NULL) { + ssh_key_free(key); + } + + if (arguments.file != NULL) { + free(arguments.file); + } + + if (arguments.passphrase != NULL) { +#ifdef HAVE_EXPLICIT_BZERO + explicit_bzero(arguments.passphrase, strlen(arguments.passphrase)); +#else + bzero(arguments.passphrase, strlen(arguments.passphrase)); +#endif + free(arguments.passphrase); + } + + if (pubkey_file != NULL) { + free(pubkey_file); + } + return rc; +}