/*
* p11util.c
* ---------
*
* Command line tool for setting up PKCS #11. Mostly this means
* things like setting PINs.
*
* Author: Rob Austein
* Copyright (c) 2015, NORDUnet A/S
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - 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.
*
* - Neither the name of the NORDUnet nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT
* HOLDER OR CONTRIBUTORS 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.
*/
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <termios.h>
#include <getopt.h>
#include <hal.h>
#include "p11_common.h"
/*
* Apparently the cool kids don't use getpassword() anymore, and there
* is no fully portable replacement, so the advice is just to roll
* one's own using termios. Yuck, but gives us the opportunity to
* provide a bit of visual feedback, what the heck.
*
* This version requires /dev/tty. Use something else if you're
* reading from a pipe.
*/
static int getpin_tty(const char *prompt,
char *pinbuf,
const size_t pinbuf_len)
{
struct termios oflags, nflags;
int c = '\0', ok = 0, fixtty = 0;
char *p = pinbuf;
FILE *f = NULL;
assert(prompt != NULL && pinbuf != NULL);
if ((f = fopen("/dev/tty", "r+")) == NULL ||
setvbuf(f, NULL, _IONBF, 0) == EOF)
goto fail;
tcgetattr(fileno(f), &oflags);
nflags = oflags;
nflags.c_lflag &= ~ (ECHO | ICANON);
if (tcsetattr(fileno(f), TCSANOW, &nflags) < 0)
goto fail;
fixtty = 1;
fputs(prompt, f);
while (p < pinbuf + pinbuf_len - 1 && (c = fgetc(f)) != EOF && c != '\n') {
fputc('*', f);
*p++ = (char) c;
}
assert(p <= pinbuf + pinbuf_len - 1);
*p = '\0';
fputc('\n', f);
ok = (c == '\n');
fail:
if (!ok)
perror("getpin_tty() failed");
if (fixtty && tcsetattr(fileno(f), TCSANOW, &oflags) < 0)
perror("getpin_tty() couldn't restore termios settings");
if (f != NULL)
fclose(f);
return ok;
}
#define OPTIONS \
OPT_FLG('h', "help", "show help") \
OPT_FLG('s', "set-so-pin", "set Security Officer PIN") \
OPT_FLG('u', "set-user-pin", "set \"user\" PIN") \
OPT_FLG('w', "set-wheel-pin", "set \"wheel\" PIN") \
OPT_FLG('p', "pin-from-stdin", "read PIN from stdin instead of /dev/tty") \
OPT_END
#define OPT_END
static void usage(const int code, const char *jane)
{
assert(jane != NULL);
FILE *f = code ? stderr : stdout;
fprintf(f, "usage: %s [options]\noptions:\n", jane);
#define OPT_FLG(_short_, _long_, _help_) fprintf(f, " -%c --%-32s%s\n", _short_, _long_, _help_);
#define OPT_ARG(_short_, _long_, _help_) fprintf(f, " -%c ARG --%-32s%s\n", _short_, _long_ " ARG", _help_);
OPTIONS;
#undef OPT_ARG
#undef OPT_FLG
exit(code);
}
static void parse_args(int argc, char *argv[],
int *do_set_so_pin,
int *do_set_user_pin,
int *do_set_wheel_pin,
int *read_from_stdin)
{
int c;
#define OPT_FLG(_short_, _long_, _help_) _short_,
#define OPT_ARG(_short_, _long_, _help_) _short_, ':',
static const char short_opts[] = { OPTIONS '\0' };
#undef OPT_ARG
#undef OPT_FLG
#define OPT_FLG(_short_, _long_, _help_) { _long_, no_argument, NULL, _short_ },
#define OPT_ARG(_short_, _long_, _help_) { _long_, required_argument, NULL, _short_ },
struct option long_opts[] = { OPTIONS { NULL } };
#undef OPT_ARG
#undef OPT_FLG
assert(argv && do_set_so_pin && do_set_user_pin && do_set_wheel_pin && read_from_stdin);
opterr = 0;
if (argc == 1)
usage(0, argv[0]);
while ((c = getopt_long(argc, argv, short_opts, long_opts, NULL)) > 0) {
switch (c) {
case 'h':
usage(0, argv[0]);
case 'p':
*read_from_stdin = 1;
continue;
case 's':
*do_set_so_pin = 1;
continue;
case 'u':
*do_set_user_pin = 1;
continue;
case 'w':
*do_set_wheel_pin = 1;
continue;
default:
usage(1, argv[0]);
}
}
if (optind != argc)
usage(1, argv[0]);
}
static int set_pin(const hal_user_t user, const int read_from_stdin)
{
const char *prompt = NULL, *label = NULL;
char pin[P11_MAX_PIN_LENGTH + 1], *p;
switch (user) {
case HAL_USER_NORMAL:
prompt = "Enter user PIN: ";
label = "user";
break;
case HAL_USER_SO:
prompt = "Enter SO PIN: ";
label = "SO";
break;
case HAL_USER_WHEEL:
prompt = "Enter wheel PIN: ";
label = "wheel";
break;
default:
return 0;
}
if (read_from_stdin) {
if (fgets(pin, sizeof(pin), stdin) == NULL) {
perror("Couldn't read PIN");
return 0;
}
if ((p = strchr(pin, '\n')) != NULL)
*p = '\0';
}
else {
if (!getpin_tty(prompt, pin, sizeof(pin)))
return 0;
}
const size_t len = strlen(pin);
if (len < P11_MIN_PIN_LENGTH || len > P11_MAX_PIN_LENGTH) {
fprintf(stderr, "Unacceptable length %lu for %s PIN, allowed range [%lu, %lu]\n",
(unsigned long) len, label,
(unsigned long) P11_MIN_PIN_LENGTH,
(unsigned long) P11_MAX_PIN_LENGTH);
memset(pin, 0, sizeof(pin));
return 0;
}
const hal_client_handle_t client = {HAL_HANDLE_NONE};
const hal_error_t err = hal_rpc_set_pin(client, user, pin, len);
if (err != HAL_OK) {
fprintf(stderr, "Couldn't set %s PIN: %s\n", label, hal_error_string(err));
memset(pin, 0, sizeof(pin));
return 0;
}
memset(pin, 0, sizeof(pin));
return 1;
}
int main(int argc, char *argv[])
{
int do_set_so_pin = 0, do_set_user_pin = 0, do_set_wheel_pin = 0, read_from_stdin = 0;
hal_error_t err;
parse_args(argc, argv, &do_set_so_pin, &do_set_user_pin, &do_set_wheel_pin, &read_from_stdin);
if ((err = hal_rpc_client_init()) != HAL_OK)
return fprintf(stderr, "Couldn't initialize RPC: %s\n", hal_error_string(err)), 1;
if (do_set_wheel_pin && !set_pin(HAL_USER_WHEEL, read_from_stdin))
return fprintf(stderr, "Couldn't set wheel PIN\n"), 2;
if (do_set_so_pin && !set_pin(HAL_USER_SO, read_from_stdin))
return fprintf(stderr, "Couldn't set SO PIN\n"), 3;
if (do_set_user_pin && !set_pin(HAL_USER_NORMAL, read_from_stdin))
return fprintf(stderr, "Couldn't set user PIN\n"), 4;
if ((err = hal_rpc_client_close()) != HAL_OK)
return fprintf(stderr, "Couldn't shut down RPC: %s\n", hal_error_string(err)), 5;
return 0;
}
/*
* Local variables:
* indent-tabs-mode: nil
* End:
*/