/* * 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 #include #include #include #include #include #include /* * 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: */