/*
 * nasd_keymgmt_dr.c
 *
 * Routines to access keys 
 *
 * Author: Howard Gobioff
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */


#define PRINT_ERROR 1
#ifdef DEBUG_ALL
#define DEBUG_INIT_KEY_MGMT 1
#endif

#include <nasd/nasd_options.h>
#include <nasd/nasd_drive_options.h>
#include <nasd/nasd_keymgmt_dr.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_security_dr.h>
#include <nasd/nasd_cache.h>

#ifndef KERNEL
#include <string.h>
#endif /* !KERNEL */

NASD_DECLARE_MUTEX(nasd_sec_keylock);
#define NASD_SEC_LOCK_KEYS()		NASD_LOCK_MUTEX(nasd_sec_keylock)
#define NASD_SEC_UNLOCK_KEYS()		NASD_UNLOCK_MUTEX(nasd_sec_keylock)

/* Do start-of-day initialization for key management subsystem. */
nasd_status_t
nasd_sec_init_keymgmt()
{
  nasd_mutex_init(&nasd_sec_keylock);
  return NASD_SUCCESS;
}

/* retrieve one of the drive's keys. */
nasd_status_t
nasd_sec_getkey(nasd_partnum_t partnum, nasd_uint16 type,
		nasd_key_t out_key)
{
  nasd_byte_t *kp = NULL;

  switch(type) {
  case NASD_RED_KEY:
    kp = PART(partnum).red_key;
    break;
  case NASD_BLACK_KEY:
    kp = PART(partnum).black_key;
    break;
  case NASD_PARTITION_KEY:
    kp = PART(partnum).partition_key;
    break;
  case NASD_DRIVE_KEY:
    kp = DISK.drive_key;
    break;
  case NASD_MASTER_KEY:
    kp = DISK.master_key;
    break;
  default:
    return NASD_BAD_KEYTYPE;
  }

  NASD_ASSERT(kp != NULL);

  NASD_SEC_LOCK_KEYS();
  bcopy(kp, out_key, sizeof(nasd_key_t));
  NASD_SEC_UNLOCK_KEYS();
  return NASD_SUCCESS;
}

/* set one of the drive's keys.  we assume that whoever calls this
   is authorized to make the change. */
nasd_status_t
nasd_sec_setkey(nasd_partnum_t partnum, nasd_uint16 type,
		nasd_key_t in_key)
{
  nasd_byte_t *kp = NULL;

  switch(type) {
  case NASD_RED_KEY:
    kp = PART(partnum).red_key;
    break;
  case NASD_BLACK_KEY:
    kp = PART(partnum).black_key;
    break;
  case NASD_PARTITION_KEY:
    kp = PART(partnum).partition_key;
    break;
  case NASD_DRIVE_KEY:
    kp = DISK.drive_key;
    break;
  case NASD_MASTER_KEY:
    return NASD_IMMUTABLE_KEY;
  default:
    return NASD_BAD_KEYTYPE;
  }

  NASD_ASSERT(kp != NULL);

  NASD_SEC_LOCK_KEYS();
  bcopy(in_key, kp, sizeof(nasd_key_t));
  NASD_SEC_UNLOCK_KEYS();
  nasd_od_update_nvclock();
  nasd_od_write_diskstate(0);
  return NASD_SUCCESS;
}

/* Initialize the master key and drive key.  This should only be
   called once over the lifetime of the drive.  The master key, once
   set, is immutable without reformatting the drive and losing all
   data. */
nasd_status_t
nasd_sec_initialize_drive(nasd_key_t master_key, nasd_key_t drive_key)
{
  NASD_SEC_LOCK_KEYS();
  if (DISK.initialized) {
    NASD_SEC_UNLOCK_KEYS();
    return NASD_DRIVE_INITIALIZED;
  }
  bcopy(drive_key, DISK.drive_key, sizeof(nasd_key_t));
  bcopy(master_key, DISK.master_key, sizeof(nasd_key_t));
  DISK.initialized = 1;
  NASD_SEC_UNLOCK_KEYS();

  nasd_od_update_nvclock();
  nasd_od_write_diskstate(1);
  return NASD_SUCCESS;
}
