/*
* 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>
/*
* 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_FLG('S', "login-as-so", "login as Security Officer") \
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 *set_so_pin,
int *set_user_pin,
int *set_wheel_pin,
int *read_from_stdin,
int *login_as_so)
{
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 && set_so_pin && set_user_pin && set_wheel_pin && read_from_stdin && login_as_so);
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':
*set_so_pin = 1;
continue;
case 'u':
*set_user_pin = 1;
continue;
case 'w':
*set_wheel_pin = 1;
continue;
case 'S':
*login_as_so = 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[hal_rpc_max_pin_length + 1], *p;
switch (user) {
case HAL_USER_NORMAL:
prompt = "Enter new user PIN: ";
label = "user";
break;
case HAL_USER_SO:
prompt = "Enter new SO PIN: ";
label = "SO";
break;
case HAL_USER_WHEEL:
prompt = "Enter new 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 < hal_rpc_min_pin_length || len > hal_rpc_max_pin_length) {
fprintf(stderr, "Unacceptable length %lu for %s PIN, allowed range [%lu, %lu]\n",
(unsigned long) len, label,
(unsigned long) hal_rpc_min_pin_length,
(unsigned long) hal_rpc_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;
}
static int login(const int login_as_so, const int read_from_stdin)
{
const hal_user_t user = login_as_so ? HAL_USER_SO : HAL_USER_WHEEL;
const hal_client_handle_t client = {HAL_HANDLE_NONE};
char pin[hal_rpc_max_pin_length + 1], *p;
const char *prompt = NULL;
switch (user) {
case HAL_USER_SO:
prompt = "Enter current SO PIN: ";
break;
case HAL_USER_WHEEL:
prompt = "Enter current wheel PIN: ";
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 hal_error_t err = hal_rpc_login(client, user, pin, strlen(pin));
if (err != HAL_OK)
fprintf(stderr, "Couldn't log in: %s\n", hal_error_string(err));
memset(pin, 0, sizeof(pin));
return err == HAL_OK;
}
int main(int argc, char *argv[])
{
int set_so_pin = 0, set_user_pin = 0, set_wheel_pin = 0, read_from_stdin = 0, login_as_so = 0;
hal_error_t err;
int ok = 0;
parse_args(argc, argv, &set_so_pin, &set_user_pin, &set_wheel_pin, &read_from_stdin, &login_as_so);
if ((err = hal_rpc_client_init()) != HAL_OK) {
fprintf(stderr, "Couldn't initialize RPC: %s\n", hal_error_string(err));
goto fail;
}
if (!login(login_as_so, read_from_stdin)) {
fprintf(stderr, "Couldn't log in\n");
goto fail;
}
if (set_wheel_pin && !set_pin(HAL_USER_WHEEL, read_from_stdin)) {
fprintf(stderr, "Couldn't set wheel PIN\n");
goto fail;
}
if (set_so_pin && !set_pin(HAL_USER_SO, read_from_stdin)) {
fprintf(stderr, "Couldn't set SO PIN\n");
goto fail;
}
if (set_user_pin && !set_pin(HAL_USER_NORMAL, read_from_stdin)) {
fprintf(stderr, "Couldn't set user PIN\n");
goto fail;
}
ok = 1;
fail:
if ((err = hal_rpc_client_close()) != HAL_OK) {
fprintf(stderr, "Couldn't shut down RPC: %s\n", hal_error_string(err));
ok = 0;
}
return !ok;
}
/*
* Local variables:
* indent-tabs-mode: nil
* End:
*/