aboutsummaryrefslogblamecommitdiff
path: root/p11util.c
blob: 606e40c47020f44a19cd13c0233ac12fe3f26dd6 (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>

#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;

  parse_args(argc, argv, &do_set_so_pin, &do_set_user_pin, &do_set_wheel_pin, &read_from_stdin);

  if (do_set_wheel_pin && !set_pin(HAL_USER_WHEEL, read_from_stdin)) {
    fprintf(stderr, "Couldn't set wheel PIN\n");
    return 1;
  }

  if (do_set_so_pin && !set_pin(HAL_USER_SO, read_from_stdin)) {
    fprintf(stderr, "Couldn't set SO PIN\n");
    return 2;
  }

  if (do_set_user_pin && !set_pin(HAL_USER_NORMAL, read_from_stdin)) {
    fprintf(stderr, "Couldn't set user PIN\n");
    return 3;
  }

  return 0;
}

/*
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */