2018-12-06 23:40:01 +03:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2018 Intel Corporation. All rights reserved. */
# include <linux/module.h>
# include <linux/device.h>
# include <linux/ndctl.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/mm.h>
# include <linux/cred.h>
# include <linux/key.h>
# include <linux/key-type.h>
# include <keys/user-type.h>
# include <keys/encrypted-type.h>
# include "nd-core.h"
# include "nd.h"
2018-12-07 23:29:09 +03:00
# define NVDIMM_BASE_KEY 0
# define NVDIMM_NEW_KEY 1
2018-12-06 23:40:01 +03:00
static bool key_revalidate = true ;
module_param ( key_revalidate , bool , 0444 ) ;
MODULE_PARM_DESC ( key_revalidate , " Require key validation at init. " ) ;
2019-03-27 21:10:44 +03:00
static const char zero_key [ NVDIMM_PASSPHRASE_LEN ] ;
2018-12-06 23:40:01 +03:00
static void * key_data ( struct key * key )
{
struct encrypted_key_payload * epayload = dereference_key_locked ( key ) ;
lockdep_assert_held_read ( & key - > sem ) ;
return epayload - > decrypted_data ;
}
static void nvdimm_put_key ( struct key * key )
{
2018-12-08 00:02:12 +03:00
if ( ! key )
return ;
2018-12-06 23:40:01 +03:00
up_read ( & key - > sem ) ;
key_put ( key ) ;
}
/*
* Retrieve kernel key for DIMM and request from user space if
* necessary . Returns a key held for read and must be put by
* nvdimm_put_key ( ) before the usage goes out of scope .
*/
static struct key * nvdimm_request_key ( struct nvdimm * nvdimm )
{
struct key * key = NULL ;
static const char NVDIMM_PREFIX [ ] = " nvdimm: " ;
char desc [ NVDIMM_KEY_DESC_LEN + sizeof ( NVDIMM_PREFIX ) ] ;
struct device * dev = & nvdimm - > dev ;
sprintf ( desc , " %s%s " , NVDIMM_PREFIX , nvdimm - > dimm_id ) ;
2019-07-11 04:43:43 +03:00
key = request_key ( & key_type_encrypted , desc , " " ) ;
2018-12-06 23:40:01 +03:00
if ( IS_ERR ( key ) ) {
if ( PTR_ERR ( key ) = = - ENOKEY )
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " request_key() found no key \n " ) ;
2018-12-06 23:40:01 +03:00
else
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " request_key() upcall failed \n " ) ;
2018-12-06 23:40:01 +03:00
key = NULL ;
} else {
struct encrypted_key_payload * epayload ;
down_read ( & key - > sem ) ;
epayload = dereference_key_locked ( key ) ;
if ( epayload - > decrypted_datalen ! = NVDIMM_PASSPHRASE_LEN ) {
up_read ( & key - > sem ) ;
key_put ( key ) ;
key = NULL ;
}
}
return key ;
}
2019-03-27 21:12:45 +03:00
static const void * nvdimm_get_key_payload ( struct nvdimm * nvdimm ,
struct key * * key )
{
* key = nvdimm_request_key ( nvdimm ) ;
if ( ! * key )
return zero_key ;
return key_data ( * key ) ;
}
2018-12-07 20:33:30 +03:00
static struct key * nvdimm_lookup_user_key ( struct nvdimm * nvdimm ,
2018-12-07 23:29:09 +03:00
key_serial_t id , int subclass )
2018-12-07 20:33:30 +03:00
{
key_ref_t keyref ;
struct key * key ;
struct encrypted_key_payload * epayload ;
struct device * dev = & nvdimm - > dev ;
keyref = lookup_user_key ( id , 0 , 0 ) ;
if ( IS_ERR ( keyref ) )
return NULL ;
key = key_ref_to_ptr ( keyref ) ;
if ( key - > type ! = & key_type_encrypted ) {
key_put ( key ) ;
return NULL ;
}
2018-12-07 23:29:09 +03:00
dev_dbg ( dev , " %s: key found: %#x \n " , __func__ , key_serial ( key ) ) ;
2018-12-07 20:33:30 +03:00
2018-12-07 23:29:09 +03:00
down_read_nested ( & key - > sem , subclass ) ;
2018-12-07 20:33:30 +03:00
epayload = dereference_key_locked ( key ) ;
if ( epayload - > decrypted_datalen ! = NVDIMM_PASSPHRASE_LEN ) {
up_read ( & key - > sem ) ;
key_put ( key ) ;
key = NULL ;
}
return key ;
}
2019-03-27 21:12:45 +03:00
static const void * nvdimm_get_user_key_payload ( struct nvdimm * nvdimm ,
key_serial_t id , int subclass , struct key * * key )
{
* key = NULL ;
if ( id = = 0 ) {
if ( subclass = = NVDIMM_BASE_KEY )
return zero_key ;
else
return NULL ;
}
* key = nvdimm_lookup_user_key ( nvdimm , id , subclass ) ;
if ( ! * key )
return NULL ;
return key_data ( * key ) ;
}
static int nvdimm_key_revalidate ( struct nvdimm * nvdimm )
2018-12-06 23:40:01 +03:00
{
struct key * key ;
int rc ;
2019-03-27 21:12:45 +03:00
const void * data ;
2018-12-06 23:40:01 +03:00
if ( ! nvdimm - > sec . ops - > change_key )
2019-03-27 21:12:45 +03:00
return - EOPNOTSUPP ;
2018-12-06 23:40:01 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_key_payload ( nvdimm , & key ) ;
2018-12-06 23:40:01 +03:00
/*
* Send the same key to the hardware as new and old key to
* verify that the key is good .
*/
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > change_key ( nvdimm , data , data , NVDIMM_USER ) ;
2018-12-06 23:40:01 +03:00
if ( rc < 0 ) {
nvdimm_put_key ( key ) ;
2019-03-27 21:12:45 +03:00
return rc ;
2018-12-06 23:40:01 +03:00
}
2019-03-27 21:12:45 +03:00
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2019-03-27 21:12:45 +03:00
return 0 ;
2018-12-06 23:40:01 +03:00
}
static int __nvdimm_security_unlock ( struct nvdimm * nvdimm )
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
2019-03-27 21:12:45 +03:00
struct key * key ;
const void * data ;
2018-12-06 23:40:01 +03:00
int rc ;
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > unlock
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-06 23:40:01 +03:00
return - EIO ;
2018-12-14 01:36:18 +03:00
if ( test_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ) {
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " Security operation in progress. \n " ) ;
2018-12-14 01:36:18 +03:00
return - EBUSY ;
}
2018-12-06 23:40:01 +03:00
/*
* If the pre - OS has unlocked the DIMM , attempt to send the key
* from request_key ( ) to the hardware for verification . Failure
* to revalidate the key against the hardware results in a
* freeze of the security configuration . I . e . if the OS does not
* have the key , security is being managed pre - OS .
*/
2019-08-27 03:54:54 +03:00
if ( test_bit ( NVDIMM_SECURITY_UNLOCKED , & nvdimm - > sec . flags ) ) {
2018-12-06 23:40:01 +03:00
if ( ! key_revalidate )
return 0 ;
2019-03-27 21:12:45 +03:00
return nvdimm_key_revalidate ( nvdimm ) ;
2018-12-06 23:40:01 +03:00
} else
2019-03-27 21:12:45 +03:00
data = nvdimm_get_key_payload ( nvdimm , & key ) ;
2018-12-06 23:40:01 +03:00
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > unlock ( nvdimm , data ) ;
2018-12-06 23:40:01 +03:00
dev_dbg ( dev , " key: %d unlock: %s \n " , key_serial ( key ) ,
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2018-12-06 23:40:01 +03:00
return rc ;
}
int nvdimm_security_unlock ( struct device * dev )
{
struct nvdimm * nvdimm = to_nvdimm ( dev ) ;
int rc ;
nvdimm_bus_lock ( dev ) ;
rc = __nvdimm_security_unlock ( nvdimm ) ;
nvdimm_bus_unlock ( dev ) ;
return rc ;
}
2018-12-07 20:33:30 +03:00
2019-08-27 03:54:54 +03:00
static int check_security_state ( struct nvdimm * nvdimm )
{
struct device * dev = & nvdimm - > dev ;
if ( test_bit ( NVDIMM_SECURITY_FROZEN , & nvdimm - > sec . flags ) ) {
dev_dbg ( dev , " Incorrect security state: %#lx \n " ,
nvdimm - > sec . flags ) ;
return - EIO ;
}
if ( test_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ) {
dev_dbg ( dev , " Security operation in progress. \n " ) ;
return - EBUSY ;
}
return 0 ;
}
2018-12-07 20:33:30 +03:00
int nvdimm_security_disable ( struct nvdimm * nvdimm , unsigned int keyid )
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
struct key * key ;
int rc ;
2019-03-27 21:12:45 +03:00
const void * data ;
2018-12-07 20:33:30 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > disable
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-07 20:33:30 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-14 01:36:18 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
2018-12-07 20:33:30 +03:00
return - ENOKEY ;
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > disable ( nvdimm , data ) ;
2018-12-07 20:33:30 +03:00
dev_dbg ( dev , " key: %d disable: %s \n " , key_serial ( key ) ,
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2018-12-07 20:33:30 +03:00
return rc ;
}
2018-12-07 23:29:09 +03:00
int nvdimm_security_update ( struct nvdimm * nvdimm , unsigned int keyid ,
2018-12-10 20:53:22 +03:00
unsigned int new_keyid ,
enum nvdimm_passphrase_type pass_type )
2018-12-07 23:29:09 +03:00
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
struct key * key , * newkey ;
int rc ;
2019-03-27 21:12:45 +03:00
const void * data , * newdata ;
2018-12-07 23:29:09 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > change_key
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-07 23:29:09 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-07 23:29:09 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
return - ENOKEY ;
2018-12-07 23:29:09 +03:00
2019-03-27 21:12:45 +03:00
newdata = nvdimm_get_user_key_payload ( nvdimm , new_keyid ,
NVDIMM_NEW_KEY , & newkey ) ;
if ( ! newdata ) {
2018-12-07 23:29:09 +03:00
nvdimm_put_key ( key ) ;
return - ENOKEY ;
}
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > change_key ( nvdimm , data , newdata , pass_type ) ;
2018-12-10 20:53:22 +03:00
dev_dbg ( dev , " key: %d %d update%s: %s \n " ,
2018-12-07 23:29:09 +03:00
key_serial ( key ) , key_serial ( newkey ) ,
2018-12-10 20:53:22 +03:00
pass_type = = NVDIMM_MASTER ? " (master) " : " (user) " ,
2018-12-07 23:29:09 +03:00
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( newkey ) ;
nvdimm_put_key ( key ) ;
2018-12-10 20:53:22 +03:00
if ( pass_type = = NVDIMM_MASTER )
2019-08-27 03:54:54 +03:00
nvdimm - > sec . ext_flags = nvdimm_security_flags ( nvdimm ,
2018-12-10 20:53:22 +03:00
NVDIMM_MASTER ) ;
else
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm ,
2018-12-10 20:53:22 +03:00
NVDIMM_USER ) ;
2018-12-07 23:29:09 +03:00
return rc ;
}
2018-12-08 00:02:12 +03:00
2018-12-10 20:53:22 +03:00
int nvdimm_security_erase ( struct nvdimm * nvdimm , unsigned int keyid ,
enum nvdimm_passphrase_type pass_type )
2018-12-08 00:02:12 +03:00
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
2019-03-27 21:10:44 +03:00
struct key * key = NULL ;
2018-12-08 00:02:12 +03:00
int rc ;
2019-03-27 21:10:44 +03:00
const void * data ;
2018-12-08 00:02:12 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > erase
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-08 00:02:12 +03:00
return - EOPNOTSUPP ;
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-14 01:36:18 +03:00
2019-08-27 03:54:54 +03:00
if ( ! test_bit ( NVDIMM_SECURITY_UNLOCKED , & nvdimm - > sec . ext_flags )
2018-12-10 20:53:22 +03:00
& & pass_type = = NVDIMM_MASTER ) {
2018-12-22 22:35:41 +03:00
dev_dbg ( dev ,
2018-12-10 20:53:22 +03:00
" Attempt to secure erase in wrong master state. \n " ) ;
return - EOPNOTSUPP ;
}
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
return - ENOKEY ;
2018-12-08 00:02:12 +03:00
2019-03-27 21:10:44 +03:00
rc = nvdimm - > sec . ops - > erase ( nvdimm , data , pass_type ) ;
2018-12-10 20:53:22 +03:00
dev_dbg ( dev , " key: %d erase%s: %s \n " , key_serial ( key ) ,
pass_type = = NVDIMM_MASTER ? " (master) " : " (user) " ,
2018-12-08 00:02:12 +03:00
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
2018-12-08 00:02:12 +03:00
return rc ;
}
2018-12-14 01:36:18 +03:00
int nvdimm_security_overwrite ( struct nvdimm * nvdimm , unsigned int keyid )
{
struct device * dev = & nvdimm - > dev ;
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( dev ) ;
2019-03-27 21:12:45 +03:00
struct key * key = NULL ;
2018-12-14 01:36:18 +03:00
int rc ;
2019-03-27 21:12:45 +03:00
const void * data ;
2018-12-14 01:36:18 +03:00
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > overwrite
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-14 01:36:18 +03:00
return - EOPNOTSUPP ;
if ( dev - > driver = = NULL ) {
2018-12-22 22:35:41 +03:00
dev_dbg ( dev , " Unable to overwrite while DIMM active. \n " ) ;
2018-12-14 01:36:18 +03:00
return - EINVAL ;
}
2019-08-27 03:54:54 +03:00
rc = check_security_state ( nvdimm ) ;
if ( rc )
return rc ;
2018-12-14 01:36:18 +03:00
2019-03-27 21:12:45 +03:00
data = nvdimm_get_user_key_payload ( nvdimm , keyid ,
NVDIMM_BASE_KEY , & key ) ;
if ( ! data )
return - ENOKEY ;
2018-12-14 01:36:18 +03:00
2019-03-27 21:12:45 +03:00
rc = nvdimm - > sec . ops - > overwrite ( nvdimm , data ) ;
2018-12-14 01:36:18 +03:00
dev_dbg ( dev , " key: %d overwrite submission: %s \n " , key_serial ( key ) ,
rc = = 0 ? " success " : " fail " ) ;
nvdimm_put_key ( key ) ;
if ( rc = = 0 ) {
set_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ;
set_bit ( NDD_WORK_PENDING , & nvdimm - > flags ) ;
2019-08-27 03:54:54 +03:00
set_bit ( NVDIMM_SECURITY_OVERWRITE , & nvdimm - > sec . flags ) ;
2018-12-14 01:36:18 +03:00
/*
* Make sure we don ' t lose device while doing overwrite
* query .
*/
get_device ( dev ) ;
queue_delayed_work ( system_wq , & nvdimm - > dwork , 0 ) ;
}
2018-12-10 20:53:22 +03:00
2018-12-14 01:36:18 +03:00
return rc ;
}
void __nvdimm_security_overwrite_query ( struct nvdimm * nvdimm )
{
struct nvdimm_bus * nvdimm_bus = walk_to_nvdimm_bus ( & nvdimm - > dev ) ;
int rc ;
unsigned int tmo ;
/* The bus lock should be held at the top level of the call stack */
lockdep_assert_held ( & nvdimm_bus - > reconfig_mutex ) ;
/*
* Abort and release device if we no longer have the overwrite
* flag set . It means the work has been canceled .
*/
if ( ! test_bit ( NDD_WORK_PENDING , & nvdimm - > flags ) )
return ;
tmo = nvdimm - > sec . overwrite_tmo ;
if ( ! nvdimm - > sec . ops | | ! nvdimm - > sec . ops - > query_overwrite
2019-08-27 03:54:54 +03:00
| | ! nvdimm - > sec . flags )
2018-12-14 01:36:18 +03:00
return ;
rc = nvdimm - > sec . ops - > query_overwrite ( nvdimm ) ;
if ( rc = = - EBUSY ) {
/* setup delayed work again */
tmo + = 10 ;
queue_delayed_work ( system_wq , & nvdimm - > dwork , tmo * HZ ) ;
nvdimm - > sec . overwrite_tmo = min ( 15U * 60U , tmo ) ;
return ;
}
if ( rc < 0 )
2018-12-22 22:35:41 +03:00
dev_dbg ( & nvdimm - > dev , " overwrite failed \n " ) ;
2018-12-14 01:36:18 +03:00
else
dev_dbg ( & nvdimm - > dev , " overwrite completed \n " ) ;
if ( nvdimm - > sec . overwrite_state )
sysfs_notify_dirent ( nvdimm - > sec . overwrite_state ) ;
nvdimm - > sec . overwrite_tmo = 0 ;
clear_bit ( NDD_SECURITY_OVERWRITE , & nvdimm - > flags ) ;
clear_bit ( NDD_WORK_PENDING , & nvdimm - > flags ) ;
put_device ( & nvdimm - > dev ) ;
2019-08-27 03:54:54 +03:00
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_USER ) ;
nvdimm - > sec . flags = nvdimm_security_flags ( nvdimm , NVDIMM_MASTER ) ;
2018-12-14 01:36:18 +03:00
}
void nvdimm_security_overwrite_query ( struct work_struct * work )
{
struct nvdimm * nvdimm =
container_of ( work , typeof ( * nvdimm ) , dwork . work ) ;
nvdimm_bus_lock ( & nvdimm - > dev ) ;
__nvdimm_security_overwrite_query ( nvdimm ) ;
nvdimm_bus_unlock ( & nvdimm - > dev ) ;
}