aboutsummaryrefslogblamecommitdiff
path: root/p11util.c
blob: 09f1e4496550e0c2eded6586733bef569603f5b5 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
  


            

                                                                

                      

                                   
  




                                                                           
  


                                                                         
  


                                                                           
  










                                                                           

   
                     









                    










                                                                      

                                              















































                                                                             



                                                                                 
                                                                                 
                                                                                 
                                                                                 



               
                                                   





                                                      

                                                                                                               






              
                                              




                                            
 













                                                                                      
                                                                                                

             


                      





                                                                          




                           
                      


               
                        

               
             




                         

               










                        
                                                                    
 
                                           
                                           
 
                 
 
                       
                                    

                    
 
                   
                                  

                  
 
                      
                                     

                     
 


             










                                                 





                                              
                                                                     

                                                                                     

                                                    
                                


             
                                                       
 
                                                                  
 



                                                                               

   
                              
           

 












































                                                                        

 
                                
 
                                                                                                
                  







                                                                                                     
 



                                             
 



                                                                   
 








                                                                   
 
         
 
      
 



                                                                           
 
             
 





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