Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

tape390_crypt.c

/*************************************************************************
 *
 * File...........: s390-tools/tape390/tape390_crypt.c
 * Author(s)......: Frank Munzert <munzert@de.ibm.com>
 *                  Michael Holzheu <holzheu@de.ibm.com>
 *
 * Copyright IBM Corp. 2006.
 *
 *************************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <getopt.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <libgen.h>
#include <sys/mtio.h>
#include "zt_common.h"
#include "tape390_common.h" 

#define KEKL_LENGTH 65 /* 64 bytes plus trailing \0 */
#define TOT_KEKL_LENGTH KEKL_LENGTH+1+5 /* kekl plus colon plus LABEL or HASH */
#define ENCRYPTION_ON 1
#define ENCRYPTION_OFF 0
#define CRYPT_SET_FAILED_MSG "%s: Tape drive does not support encryption.\n"
#define CRYPT_QUERY_FAILED_MSG CRYPT_SET_FAILED_MSG
#define KEKL_QUERY_FAILED_MSG "%s: Query of encryption keys failed.\n"
#define KEKL_SET_FAILED_MSG "%s: Unable to set or modify encryption keys.\n"
#define PROC_DEVICES_FILE "/proc/devices"
#define PROC_DEVICES_FILE_WIDTH 100
#define MAX_BLOCKSIZE 65535 /* max blocksize supported by escon/ficon tapes */

#ifndef ENOKEY
#define ENOKEY          126
#endif
#ifndef EKEYREJECTED
#define EKEYREJECTED    129
#endif

#define CHECK_SPEC_MAX(i,j,numstr,str)                       \
      {if (i>j) ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \
      "can only be specified " numstr ".\n",prog_name);}

#define PRINT_TYPE(byte)                                       \
      {if (byte==TAPE390_KEKL_TYPE_LABEL) printf("label\n");\
      else if (byte == TAPE390_KEKL_TYPE_HASH) printf("hash\n");\
      else printf("Unsupported type\n");}

/*
 * Structure tape390_crypt is used to store the command line parameters
 */
00056 struct tape390_crypt {
      int   e_switch;
      char  delimiter;
      char  devname[PATH_MAX];
      char  kekl_input[2][TOT_KEKL_LENGTH];
      int   encryption_specified;
      int   query_specified;
      int   key_specified;
      int   delimiter_specified;
      int   force_specified;
} __attribute__ ((packed));

/*
 * The following ioctl's belong to linux-2.5/include/asm-s390/tape390.h
 *
 * The TAPE390_CRYPT_SET ioctl is used to switch on/off encryption.
 * The "encryption_capable" field is ignored for this ioctl!
 */
#define TAPE390_CRYPT_SET _IOW('d', 2, struct tape390_crypt_info)

/*
 * The TAPE390_CRYPT_QUERY ioctl is used to query the encryption state.
 */
#define TAPE390_CRYPT_QUERY _IOR('d', 3, struct tape390_crypt_info)

00081 struct tape390_crypt_info {
      char capability;
      char status;
      char medium_status;
} __attribute__ ((packed));

/* Macros for "capable" field */
#define TAPE390_CRYPT_SUPPORTED_MASK 0x01
#define TAPE390_CRYPT_SUPPORTED(x) \
      ((x.capability & TAPE390_CRYPT_SUPPORTED_MASK))

/* Macros for "status" field */
#define TAPE390_CRYPT_ON_MASK 0x01
#define TAPE390_CRYPT_ON(x) (((x.status) & TAPE390_CRYPT_ON_MASK))

/* Macros for "medium status" field */
#define TAPE390_MEDIUM_LOADED_MASK 0x01
#define TAPE390_MEDIUM_ENCRYPTED_MASK 0x02
#define TAPE390_MEDIUM_ENCRYPTED(x) \
      (((x.medium_status) & TAPE390_MEDIUM_ENCRYPTED_MASK))
#define TAPE390_MEDIUM_LOADED(x) \
      (((x.medium_status) & TAPE390_MEDIUM_LOADED_MASK))

/*
 * The TAPE390_KEKL_SET ioctl is used to set Key Encrypting Key labels.
 */
#define TAPE390_KEKL_SET _IOW('d', 4, struct tape390_kekl_pair)

/*
 * The TAPE390_KEKL_QUERY ioctl is used to query Key Encrypting Key labels.
 */
#define TAPE390_KEKL_QUERY _IOR('d', 5, struct tape390_kekl_pair)

00114 struct tape390_kekl {
      unsigned char type;
      unsigned char type_on_tape;
      char label[65];
} __attribute__ ((packed));

00120 struct tape390_kekl_pair {
        struct tape390_kekl kekl[2];
} __attribute__ ((packed));

/* Values for "kekl1/2_type" and "kekl1/2_type_on_tape" fields */
#define TAPE390_KEKL_TYPE_NONE 0
#define TAPE390_KEKL_TYPE_LABEL 1
#define TAPE390_KEKL_TYPE_HASH 2

/* Full tool name */
static const char tool_name[] = "tape390_crpyt: zSeries tape encryption program";

/* Copyright notice */
static const char copyright_notice[] = "Copyright IBM Corp. 2006";

/*
 * Print version information.
 */
static void print_version (void)
{
      printf ("%s version %s\n", tool_name, RELEASE_STRING);
      printf ("%s\n", copyright_notice);
}

/*
 * prints out the usage text
 */
static void tape390_crypt_usage (void)
{
      printf ("Usage: tape390_crypt [OPTIONS] [DEVICE]\n"
            "\n"
            "Provide encryption for "
            "tape devices.\n"
            "DEVICE is the node of the tape device (e.g. '/dev/ntibm0')\n"
            "\n"
            "-h, --help               Print this help, then exit\n"
            "-v, --version            Print version information, "
                                      "then exit\n"
            "-q, --query              Query information on encryption "
                                      "status and keys\n"
            "-k, --key value:type     Set encryption key(s)\n"
            "-d, --delimiter char     Specify character separating "
                                      "key value from key type\n"
            "-f, --force              Do not prompt user\n"
            "-e, --encryption on|off  Switch encryption on or off\n");
}

/*
 * initialize the tape390_crypt info structure
 */
static void init_info(struct tape390_crypt *info)
{
      info->delimiter            = ':';
      info->encryption_specified = 0;
      info->query_specified       = 0;
      info->key_specified        = 0;
      info->delimiter_specified  = 0;
      info->force_specified  = 0;
}

/*
 * parse the commandline options
 */
static void tape390_crypt_parse_opts(struct tape390_crypt *info, int argc,
                             char* argv[])
{
      int opt, index, kekl_too_long=0, deli_too_long=0, e_switch_invalid=0;
      int i;
      static struct option crypt_long_options[] = {
            { "version",     no_argument,       NULL, 'v'},
            { "encryption",  required_argument, NULL, 'e'},
            { "query",       no_argument,       NULL, 'q'},
            { "key",         required_argument, NULL, 'k'},
            { "help",        no_argument,       NULL, 'h'},
            { "delimiter",   required_argument, NULL, 'd'},
            { "force",       no_argument,       NULL, 'f'},
            { 0,             0,                 0,    0  }
      };
      /* Command line option abbreviations */
      static const char crypt_option_string[] = "ve:qk:hd:f";

      while (1) {
            opt = getopt_long(argc, argv, crypt_option_string,
                          crypt_long_options, &index);
            if (opt == -1)
                  break;
            switch (opt) {
            case 'v':
                  print_version();
                  exit(0);
            case 'h':
                  tape390_crypt_usage();
                  exit(0);
            case 'q':
                  ++info->query_specified;
                  break;
            case 'k':
                  ++info->key_specified;
                  if(strlen(optarg) >= TOT_KEKL_LENGTH)
                        kekl_too_long = 1;
                  else if (info->key_specified == 1) 
                        for (i = 0; i < 2; i++)
                              strcpy(info->kekl_input[i], optarg);
                  else 
                        strcpy(info->kekl_input[1], optarg);
                  break;
            case 'e':
                  ++info->encryption_specified;
                  if (strcmp(optarg,"off") == 0)
                        info->e_switch = ENCRYPTION_OFF;
                  else if (strcmp(optarg,"on") == 0)
                        info->e_switch = ENCRYPTION_ON;
                  else
                        e_switch_invalid = 1;
                  break;
            case 'd':
                  ++info->delimiter_specified;
                  if(strlen(optarg) > 1)
                        deli_too_long = 1;
                  else
                        info->delimiter = optarg[0];
                  break;
            case 'f':
                  ++info->force_specified;
                  break;
            default:
                  fprintf(stderr, "Try '%s --help' for more"
                              " information.\n",prog_name);
                  exit(1);
            }
      }
      CHECK_SPEC_MAX(info->query_specified, 1, "once", "query");
      CHECK_SPEC_MAX(info->encryption_specified, 1, "once", "encryption");
      CHECK_SPEC_MAX(info->delimiter_specified, 1, "once", "delimiter");
      CHECK_SPEC_MAX(info->force_specified, 1, "once", "force");
      CHECK_SPEC_MAX(info->key_specified,2, "twice", "key");
      if (kekl_too_long)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: Specified key too long.\n",prog_name);
      if (deli_too_long)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: Delimiter must be one character.\n",prog_name);
      if (e_switch_invalid)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: ERROR: encryption can be set 'on' or 'off'.\n",
                      prog_name);
      if (((info->query_specified) &&
           (info->key_specified + info->encryption_specified > 0)) ||
          ((info->encryption_specified) &&
           (info->key_specified + info->query_specified > 0)) ||
          ((info->force_specified) &&
           (info->encryption_specified)) ||
          ((info->delimiter_specified) &&
           (info->encryption_specified + info->query_specified > 0)))
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: Conflicting options specified.\n",prog_name);
      if (info->query_specified + info->encryption_specified +
          info->key_specified == 0)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: ERROR: Missing options.\n",prog_name);
      /* save device */
      if (optind >= argc)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: No device specified.\n",prog_name);
      if (optind + 1 < argc)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: More than one device speficied.\n",prog_name);
      strcpy(info->devname, argv[optind]);
}

/*
 * check and parse key operand, must be value:type
 * where  value  is an arbitrary string, maximal 64 char long
 *        :      is a 1-char delimiter specified with the -d option (default :)
 *        type   is the type identifier, which can be either 'hash' or 'label'
 *               default is 'label'
 */
static void fill_kekl(char *s, char delimiter,
                        struct tape390_kekl *mykekl)
{
      int i;
      char typestring[TOT_KEKL_LENGTH] = {0};
      for (i = strlen(s); i >= 0; i--)
            if (*(s+i) == delimiter)
                  break;
      if (i == -1)            /* delimiter not found, no type specified */
            mykekl->type_on_tape = TAPE390_KEKL_TYPE_LABEL; /* default type*/
      else if (i == 0)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: ERROR: delimiter '%c' found as first "
                      "character.\n",prog_name,delimiter);
      else if (i == (int) strlen(s) - 1)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: ERROR: Delimiter '%c' found as last "
                      "character.\n",prog_name,delimiter);
      else {
            strcpy(typestring,s+i+1);
            *(s+i) = '\0';
            if (strcmp(typestring,"hash") == 0)
                  mykekl->type_on_tape = TAPE390_KEKL_TYPE_HASH;
            else if (strcmp(typestring,"label") == 0)
                  mykekl->type_on_tape = TAPE390_KEKL_TYPE_LABEL;
            else
                  ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: Invalid key type '%s',"
                      " must be either 'label' or 'hash'.\n",
                      prog_name,typestring);
      }
      if (strlen(s) >= KEKL_LENGTH)
            ERRMSG_EXIT(EXIT_MISUSE,
                      "%s: Specified key value too long, has %d chars.\n",
                      prog_name,(int)strlen(s));
      else {
            strcpy(mykekl->label, s);
            mykekl->type = TAPE390_KEKL_TYPE_LABEL;
      }
}

/*
 * Write two tapemarks on tape to initialize tape after kekl set
 */

static int init_tape(int fd)
{
      struct mtop mtop;
      int rc;

      mtop.mt_op = MTWEOF;
      mtop.mt_count = 2;
      rc = ioctl(fd, MTIOCTOP, &mtop);
      if (rc)
            return rc;
      mtop.mt_op = MTREW;
      return ioctl(fd, MTIOCTOP, &mtop);
}

/*
 * Tell the current block on tape
 */

static int tell_tape(int fd)
{
      struct mtop mtop;

      mtop.mt_op = MTTELL;
      mtop.mt_count = 1;
      return ioctl(fd, MTIOCTOP, &mtop);
}


/*
 * Do read IO to tape in order to enforce update of KEKL information.
 * Leave tape position unchanged.
 */

static int read_tape(int fd)
{
      struct mtop mtop;
      int block_number, n_read;
      char buffer[MAX_BLOCKSIZE];

      /* remember current block on tape */
      block_number = tell_tape(fd);
      n_read = read(fd, buffer, sizeof(buffer));
      if (n_read == -1) {
            /* tape read failed, ignore if caused by end-of-volume */
            if (errno == ENOSPC)
                  return 0;
            else
                  return 1;
      }
      if (block_number != tell_tape(fd)) {
            /* re-position tape */
            mtop.mt_op = MTSEEK;
            mtop.mt_count = block_number;
            if (ioctl(fd, MTIOCTOP, &mtop) != 0)
                  return 1;
      }
      return 0;
}

/*
 * set Key Encrypting Key labels
 */
static int set_encryption_keys(int fd, struct tape390_crypt *info)
{
      struct tape390_kekl_pair kekls;
      char delim;
      char *s, inp_buffer[5];
      int i, rc;

      delim = info->delimiter;
      for (i = 0; i < 2; i++) {
            s = info->kekl_input[i];
            fill_kekl(s, delim, &kekls.kekl[i]);
      }
      if (info->force_specified == 0) {
            printf("--->> ATTENTION! <<---\n");
            printf("All data on tape %s will be lost.\nType "
                   "\"yes\" to continue: ", info->devname);
            if (fgets(inp_buffer, sizeof(inp_buffer), stdin) == NULL) {
                  fprintf(stderr, "Reading from terminal failed.\n");
                  exit(1);
            }
            if (strcasecmp(inp_buffer,"yes\n")) {
                  printf("Tape and its encryption keys " 
                         "remain unchanged.\n");
                  exit(0);
            }
      }
      rc = ioctl(fd, TAPE390_KEKL_SET, &kekls);
      if (rc)
            return rc;
      return init_tape(fd);
}

/*
 * Switch on/off encryption
 */
static int switch_encryption_on_or_off(int fd,struct tape390_crypt *info)
{
      struct tape390_crypt_info myinfo;

      memset(&myinfo, 0, sizeof(myinfo));
      if (info->e_switch == ENCRYPTION_ON)
            myinfo.status |= TAPE390_CRYPT_ON_MASK;
      return ioctl(fd, TAPE390_CRYPT_SET, &myinfo);
}

/*
 * Query information on encryption and keys
 */
static int query_encryption(int fd,struct tape390_crypt *info)
{
      struct tape390_crypt_info myinfo;
      struct tape390_kekl_pair kekls;
      int i, ret;

      ret = ioctl(fd, TAPE390_CRYPT_QUERY, &myinfo);
      if (ret)
            return -1;
      if (!TAPE390_MEDIUM_LOADED(myinfo)) {
            printf("Medium not loaded\n");
            return 0;
      }
      printf("ENCRYPTION: ");
      if (TAPE390_CRYPT_ON(myinfo))
            printf("ON\n");
      else
            printf("OFF\n");
      printf("MEDIUM: ");
      if (TAPE390_MEDIUM_ENCRYPTED(myinfo))
            printf("ENCRYPTED\n");
      else {
            printf("NOT ENCRYPTED\n");
            return 0;
      }
      /* 
       * do a dummy read in order to force the control unit to get 
       * the key information from the EKM
      */
      ret = read_tape(fd);
      if (ret && (info->force_specified == 0))
            return -2;
      ret = ioctl(fd, TAPE390_KEKL_QUERY, &kekls);
      if (ret)
            return -2;
      printf("KEKLs on TAPE:\n");
      if (kekls.kekl[0].type == TAPE390_KEKL_TYPE_NONE) {
            ERRMSG("Unable to retrieve key information.\n");
            return 0;
      }
      for (i = 0; i < 2; i++) {
            printf(" KEY%i:\n",i+1);
            if (kekls.kekl[i].type == TAPE390_KEKL_TYPE_NONE)
                  printf("None\n");
            else {
                  printf("  value:  %s\n",kekls.kekl[i].label);
                  printf("  type:   ");
                  PRINT_TYPE(kekls.kekl[i].type);
                  printf("  ontape: ");
                  PRINT_TYPE(kekls.kekl[i].type_on_tape);
            }
      }
      return 0;
}

int main(int argc, char *argv[])
{
      struct tape390_crypt info;
      char error_msg[80];
      int rc=0, fd=-1;

      /* set name of program */
      set_prog_name(basename(argv[0]));

      /* set default values */
      init_info(&info);

      /* parse command line options and check syntax */
      tape390_crypt_parse_opts(&info, argc, argv);

      /* check whether specified device node is tape */
      if (is_not_tape(info.devname))
            exit(EXIT_MISUSE);

      /* open device                                 */
      fd = open_tape(info.devname);

      /* process -k or -e or -q operand              */
      if (info.key_specified >= 1) {
            rc = set_encryption_keys(fd, &info);
            if (rc == 0)
                  printf("SUCCESS: key information set.\n");
            strcpy(error_msg, KEKL_SET_FAILED_MSG);
      } else if (info.encryption_specified) {
            rc = switch_encryption_on_or_off(fd, &info);
            strcpy(error_msg, CRYPT_SET_FAILED_MSG);
      } else if (info.query_specified) {
            rc = query_encryption(fd, &info);
            if (rc == -1)
                  strcpy(error_msg, CRYPT_QUERY_FAILED_MSG);
            else if (rc == -2)
                  strcpy(error_msg, KEKL_QUERY_FAILED_MSG);
      }
      if (rc) {
            ERRMSG(error_msg, prog_name);
            switch (errno) {
                  case ENOKEY:
                        ERRMSG("EKM error.\n");
                        break;
                  case ENOTCONN:
                        ERRMSG("Could not connect to EKM.\n");
                        break;
                  case EKEYREJECTED:
                        ERRMSG("Key not available on EKM.\n");
                        break;
                  case EUNATCH:
                        ERRMSG("Encryption must be set ON prior to "
                               "setting or querying encryption " 
                               "keys.\n");
                        break;
                  case EBADSLT:
                        ERRMSG("Tape must be at load point in order "
                               "to set encryption keys.\n");
                        break;
                  case ENOSYS:
                        ERRMSG("Tape device does not support "
                               "encryption.\n");
                        break;
                  case EINVAL:
                        ERRMSG("Kernel does not support tape "
                               "encryption.\n"); 
                        break;
                  default:
                        perror("");
            }
      }
      if (fd != -1)
            close(fd);
      return(rc);
}

Generated by  Doxygen 1.6.0   Back to index