greybus: Add Component Authentication Protocol support

This patch adds Component Authentication Protocol support in greybus.
The purpose of the CAP protocol is to authenticate the Module hardware,
and it can only be used when it is present as part of the
firmware-management bundle, on a separate CPort.

Compile tested only.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Jun Li <li_jun@projectara.com>
Tested-by: Jun Li <li_jun@projectara.com>
Signed-off-by: Alex Elder <elder@linaro.org>
This commit is contained in:
Viresh Kumar 2016-06-30 10:54:00 -05:00 committed by Alex Elder
parent 80b3982b8d
commit e3eda54d0b
7 changed files with 650 additions and 4 deletions

View file

@ -33,7 +33,7 @@ gb-audio-manager-y += audio_manager.o
gb-audio-manager-y += audio_manager_module.o
gb-bootrom-y := bootrom.o
gb-camera-y := camera.o
gb-firmware-y := fw-core.o fw-download.o fw-management.o
gb-firmware-y := fw-core.o fw-download.o fw-management.o authentication.o
gb-spilib-y := spilib.o
gb-sdio-y := sdio.o
gb-uart-y := uart.o

View file

@ -0,0 +1,437 @@
/*
* Greybus Component Authentication Protocol (CAP) Driver.
*
* Copyright 2016 Google Inc.
* Copyright 2016 Linaro Ltd.
*
* Released under the GPLv2 only.
*/
#include "greybus.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include "greybus_authentication.h"
#include "firmware.h"
#include "greybus.h"
#define CAP_TIMEOUT_MS 1000
/*
* Number of minor devices this driver supports.
* There will be exactly one required per Interface.
*/
#define NUM_MINORS U8_MAX
struct gb_cap {
struct device *parent;
struct gb_connection *connection;
struct kref kref;
struct list_head node;
bool disabled; /* connection getting disabled */
struct mutex mutex;
struct cdev cdev;
struct device *class_device;
dev_t dev_num;
};
static struct class *cap_class;
static dev_t cap_dev_num;
static DEFINE_IDA(cap_minors_map);
static LIST_HEAD(cap_list);
static DEFINE_MUTEX(list_mutex);
static void cap_kref_release(struct kref *kref)
{
struct gb_cap *cap = container_of(kref, struct gb_cap, kref);
kfree(cap);
}
/*
* All users of cap take a reference (from within list_mutex lock), before
* they get a pointer to play with. And the structure will be freed only after
* the last user has put the reference to it.
*/
static void put_cap(struct gb_cap *cap)
{
kref_put(&cap->kref, cap_kref_release);
}
/* Caller must call put_cap() after using struct gb_cap */
static struct gb_cap *get_cap(struct cdev *cdev)
{
struct gb_cap *cap;
mutex_lock(&list_mutex);
list_for_each_entry(cap, &cap_list, node) {
if (&cap->cdev == cdev) {
kref_get(&cap->kref);
goto unlock;
}
}
cap = NULL;
unlock:
mutex_unlock(&list_mutex);
return cap;
}
static int cap_get_endpoint_uid(struct gb_cap *cap, u8 *euid)
{
struct gb_connection *connection = cap->connection;
struct gb_cap_get_endpoint_uid_response response;
int ret;
ret = gb_operation_sync(connection, GB_CAP_TYPE_GET_ENDPOINT_UID, NULL,
0, &response, sizeof(response));
if (ret) {
dev_err(cap->parent, "failed to get endpoint uid (%d)\n", ret);
return ret;
}
memcpy(euid, response.uid, sizeof(response.uid));
return 0;
}
static int cap_get_ims_certificate(struct gb_cap *cap, u32 class, u32 id,
u8 *certificate, u32 *size, u8 *result)
{
struct gb_connection *connection = cap->connection;
struct gb_cap_get_ims_certificate_request *request;
struct gb_cap_get_ims_certificate_response *response;
size_t max_size = gb_operation_get_payload_size_max(connection);
struct gb_operation *op;
int ret;
op = gb_operation_create_flags(connection,
GB_CAP_TYPE_GET_IMS_CERTIFICATE,
sizeof(*request), max_size,
GB_OPERATION_FLAG_SHORT_RESPONSE,
GFP_KERNEL);
if (!op)
return -ENOMEM;
request = op->request->payload;
request->certificate_class = cpu_to_le32(class);
request->certificate_id = cpu_to_le32(id);
ret = gb_operation_request_send_sync(op);
if (ret) {
dev_err(cap->parent, "failed to get certificate (%d)\n", ret);
goto done;
}
response = op->response->payload;
*result = response->result_code;
*size = op->response->payload_size - sizeof(*response);
memcpy(certificate, response->certificate, *size);
done:
gb_operation_put(op);
return ret;
}
static int cap_authenticate(struct gb_cap *cap, u32 auth_type, u8 *uid,
u8 *challenge, u8 *result, u8 *auth_response,
u32 *signature_size, u8 *signature)
{
struct gb_connection *connection = cap->connection;
struct gb_cap_authenticate_request *request;
struct gb_cap_authenticate_response *response;
size_t max_size = gb_operation_get_payload_size_max(connection);
struct gb_operation *op;
int ret;
op = gb_operation_create_flags(connection, GB_CAP_TYPE_AUTHENTICATE,
sizeof(*request), max_size,
GB_OPERATION_FLAG_SHORT_RESPONSE,
GFP_KERNEL);
if (!op)
return -ENOMEM;
request = op->request->payload;
request->auth_type = cpu_to_le32(auth_type);
memcpy(request->uid, uid, sizeof(request->uid));
memcpy(request->challenge, challenge, sizeof(request->challenge));
ret = gb_operation_request_send_sync(op);
if (ret) {
dev_err(cap->parent, "failed to authenticate (%d)\n", ret);
goto done;
}
response = op->response->payload;
*result = response->result_code;
*signature_size = op->response->payload_size - sizeof(*response);
memcpy(auth_response, response->response, sizeof(response->response));
memcpy(signature, response->signature, *signature_size);
done:
gb_operation_put(op);
return ret;
}
/* Char device fops */
static int cap_open(struct inode *inode, struct file *file)
{
struct gb_cap *cap = get_cap(inode->i_cdev);
/* cap structure can't get freed until file descriptor is closed */
if (cap) {
file->private_data = cap;
return 0;
}
return -ENODEV;
}
static int cap_release(struct inode *inode, struct file *file)
{
struct gb_cap *cap = file->private_data;
put_cap(cap);
return 0;
}
static int cap_ioctl(struct gb_cap *cap, unsigned int cmd,
void __user *buf)
{
struct cap_ioc_get_endpoint_uid endpoint_uid;
struct cap_ioc_get_ims_certificate *ims_cert;
struct cap_ioc_authenticate *authenticate;
int ret;
switch (cmd) {
case CAP_IOC_GET_ENDPOINT_UID:
ret = cap_get_endpoint_uid(cap, endpoint_uid.uid);
if (ret)
return ret;
if (copy_to_user(buf, &endpoint_uid, sizeof(endpoint_uid)))
return -EFAULT;
return 0;
case CAP_IOC_GET_IMS_CERTIFICATE:
ims_cert = kzalloc(sizeof(*ims_cert), GFP_KERNEL);
if (!ims_cert)
return -ENOMEM;
if (copy_from_user(ims_cert, buf, sizeof(*ims_cert))) {
ret = -EFAULT;
goto free_ims_cert;
}
ret = cap_get_ims_certificate(cap, ims_cert->certificate_class,
ims_cert->certificate_id,
ims_cert->certificate,
&ims_cert->cert_size,
&ims_cert->result_code);
if (ret)
goto free_ims_cert;
if (copy_to_user(buf, ims_cert, sizeof(*ims_cert)))
ret = -EFAULT;
free_ims_cert:
kfree(ims_cert);
return ret;
case CAP_IOC_AUTHENTICATE:
authenticate = kzalloc(sizeof(*authenticate), GFP_KERNEL);
if (!authenticate)
return -ENOMEM;
if (copy_from_user(authenticate, buf, sizeof(*authenticate))) {
ret = -EFAULT;
goto free_authenticate;
}
ret = cap_authenticate(cap, authenticate->auth_type,
authenticate->uid,
authenticate->challenge,
&authenticate->result_code,
authenticate->response,
&authenticate->signature_size,
authenticate->signature);
if (ret)
goto free_authenticate;
if (copy_to_user(buf, authenticate, sizeof(*authenticate)))
ret = -EFAULT;
free_authenticate:
kfree(authenticate);
return ret;
default:
return -ENOTTY;
}
}
static long cap_ioctl_unlocked(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct gb_cap *cap = file->private_data;
int ret = -ENODEV;
/*
* Serialize ioctls.
*
* We don't want the user to do multiple authentication operations in
* parallel.
*
* This is also used to protect ->disabled, which is used to check if
* the connection is getting disconnected, so that we don't start any
* new operations.
*/
mutex_lock(&cap->mutex);
if (!cap->disabled)
ret = cap_ioctl(cap, cmd, (void __user *)arg);
mutex_unlock(&cap->mutex);
return ret;
}
static const struct file_operations cap_fops = {
.owner = THIS_MODULE,
.open = cap_open,
.release = cap_release,
.unlocked_ioctl = cap_ioctl_unlocked,
};
int gb_cap_connection_init(struct gb_connection *connection)
{
struct gb_cap *cap;
int ret, minor;
if (!connection)
return 0;
cap = kzalloc(sizeof(*cap), GFP_KERNEL);
if (!cap)
return -ENOMEM;
cap->parent = &connection->bundle->dev;
cap->connection = connection;
mutex_init(&cap->mutex);
gb_connection_set_data(connection, cap);
kref_init(&cap->kref);
mutex_lock(&list_mutex);
list_add(&cap->node, &cap_list);
mutex_unlock(&list_mutex);
ret = gb_connection_enable(connection);
if (ret)
goto err_list_del;
minor = ida_simple_get(&cap_minors_map, 0, NUM_MINORS, GFP_KERNEL);
if (minor < 0) {
ret = minor;
goto err_connection_disable;
}
/* Add a char device to allow userspace to interact with cap */
cap->dev_num = MKDEV(MAJOR(cap_dev_num), minor);
cdev_init(&cap->cdev, &cap_fops);
ret = cdev_add(&cap->cdev, cap->dev_num, 1);
if (ret)
goto err_remove_ida;
/* Add a soft link to the previously added char-dev within the bundle */
cap->class_device = device_create(cap_class, cap->parent, cap->dev_num,
NULL, "gb-authenticate-%d", minor);
if (IS_ERR(cap->class_device)) {
ret = PTR_ERR(cap->class_device);
goto err_del_cdev;
}
return 0;
err_del_cdev:
cdev_del(&cap->cdev);
err_remove_ida:
ida_simple_remove(&cap_minors_map, minor);
err_connection_disable:
gb_connection_disable(connection);
err_list_del:
mutex_lock(&list_mutex);
list_del(&cap->node);
mutex_unlock(&list_mutex);
put_cap(cap);
return ret;
}
void gb_cap_connection_exit(struct gb_connection *connection)
{
struct gb_cap *cap;
if (!connection)
return;
cap = gb_connection_get_data(connection);
device_destroy(cap_class, cap->dev_num);
cdev_del(&cap->cdev);
ida_simple_remove(&cap_minors_map, MINOR(cap->dev_num));
/*
* Disallow any new ioctl operations on the char device and wait for
* existing ones to finish.
*/
mutex_lock(&cap->mutex);
cap->disabled = true;
mutex_unlock(&cap->mutex);
/* All pending greybus operations should have finished by now */
gb_connection_disable(cap->connection);
/* Disallow new users to get access to the cap structure */
mutex_lock(&list_mutex);
list_del(&cap->node);
mutex_unlock(&list_mutex);
/*
* All current users of cap would have taken a reference to it by
* now, we can drop our reference and wait the last user will get
* cap freed.
*/
put_cap(cap);
}
int cap_init(void)
{
int ret;
cap_class = class_create(THIS_MODULE, "gb_authenticate");
if (IS_ERR(cap_class))
return PTR_ERR(cap_class);
ret = alloc_chrdev_region(&cap_dev_num, 0, NUM_MINORS,
"gb_authenticate");
if (ret)
goto err_remove_class;
return 0;
err_remove_class:
class_destroy(cap_class);
return ret;
}
void cap_exit(void)
{
unregister_chrdev_region(cap_dev_num, NUM_MINORS);
class_destroy(cap_class);
ida_destroy(&cap_minors_map);
}

View file

@ -25,4 +25,10 @@ int gb_fw_download_request_handler(struct gb_operation *op);
int gb_fw_download_connection_init(struct gb_connection *connection);
void gb_fw_download_connection_exit(struct gb_connection *connection);
/* CAP Protocol specific functions */
int cap_init(void);
void cap_exit(void);
int gb_cap_connection_init(struct gb_connection *connection);
void gb_cap_connection_exit(struct gb_connection *connection);
#endif /* __FIRMWARE_H */

View file

@ -17,6 +17,7 @@ struct gb_fw_core {
struct gb_connection *download_connection;
struct gb_connection *mgmt_connection;
struct gb_connection *spi_connection;
struct gb_connection *cap_connection;
};
struct gb_connection *to_fw_mgmt_connection(struct device *dev)
@ -134,6 +135,24 @@ static int gb_fw_core_probe(struct gb_bundle *bundle,
fw_core->spi_connection = connection;
}
break;
case GREYBUS_PROTOCOL_AUTHENTICATION:
/* Disallow multiple CAP CPorts */
if (fw_core->cap_connection) {
dev_err(&bundle->dev, "multiple Authentication CPorts found\n");
ret = -EINVAL;
goto err_destroy_connections;
}
connection = gb_connection_create(bundle, cport_id,
NULL);
if (IS_ERR(connection)) {
dev_err(&bundle->dev, "failed to create Authentication connection (%ld)\n",
PTR_ERR(connection));
} else {
fw_core->cap_connection = connection;
}
break;
default:
dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n",
@ -168,6 +187,15 @@ static int gb_fw_core_probe(struct gb_bundle *bundle,
fw_core->spi_connection = NULL;
}
ret = gb_cap_connection_init(fw_core->cap_connection);
if (ret) {
/* We may still be able to work with the Interface */
dev_err(&bundle->dev, "failed to initialize CAP connection, disable it (%d)\n",
ret);
gb_connection_destroy(fw_core->cap_connection);
fw_core->cap_connection = NULL;
}
ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection);
if (ret) {
/* We may still be able to work with the Interface */
@ -181,10 +209,12 @@ static int gb_fw_core_probe(struct gb_bundle *bundle,
return 0;
err_exit_connections:
gb_cap_connection_exit(fw_core->cap_connection);
gb_fw_spi_connection_exit(fw_core->spi_connection);
gb_fw_download_connection_exit(fw_core->download_connection);
err_destroy_connections:
gb_connection_destroy(fw_core->mgmt_connection);
gb_connection_destroy(fw_core->cap_connection);
gb_connection_destroy(fw_core->spi_connection);
gb_connection_destroy(fw_core->download_connection);
kfree(fw_core);
@ -197,10 +227,12 @@ static void gb_fw_core_disconnect(struct gb_bundle *bundle)
struct gb_fw_core *fw_core = greybus_get_drvdata(bundle);
gb_fw_mgmt_connection_exit(fw_core->mgmt_connection);
gb_cap_connection_exit(fw_core->cap_connection);
gb_fw_spi_connection_exit(fw_core->spi_connection);
gb_fw_download_connection_exit(fw_core->download_connection);
gb_connection_destroy(fw_core->mgmt_connection);
gb_connection_destroy(fw_core->cap_connection);
gb_connection_destroy(fw_core->spi_connection);
gb_connection_destroy(fw_core->download_connection);
@ -229,19 +261,32 @@ static int fw_core_init(void)
return ret;
}
ret = greybus_register(&gb_fw_core_driver);
ret = cap_init();
if (ret) {
fw_mgmt_exit();
return ret;
pr_err("Failed to initialize component authentication core (%d)\n",
ret);
goto fw_mgmt_exit;
}
ret = greybus_register(&gb_fw_core_driver);
if (ret)
goto cap_exit;
return 0;
cap_exit:
cap_exit();
fw_mgmt_exit:
fw_mgmt_exit();
return ret;
}
module_init(fw_core_init);
static void __exit fw_core_exit(void)
{
greybus_deregister(&gb_fw_core_driver);
cap_exit();
fw_mgmt_exit();
}
module_exit(fw_core_exit);

View file

@ -0,0 +1,120 @@
/*
* Greybus Component Authentication User Header
*
* This file is provided under a dual BSD/GPLv2 license. When using or
* redistributing this file, you may do so under either license.
*
* GPL LICENSE SUMMARY
*
* Copyright(c) 2016 Google Inc. All rights reserved.
* Copyright(c) 2016 Linaro Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details.
*
* BSD LICENSE
*
* Copyright(c) 2016 Google Inc. All rights reserved.
* Copyright(c) 2016 Linaro Ltd. 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 Google Inc. or Linaro Ltd. 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 GOOGLE INC. OR
* LINARO LTD. 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.
*/
#ifndef __GREYBUS_AUTHENTICATION_USER_H
#define __GREYBUS_AUTHENTICATION_USER_H
#include <linux/ioctl.h>
#include <linux/types.h>
#define CAP_CERTIFICATE_MAX_SIZE 1600
#define CAP_SIGNATURE_MAX_SIZE 320
/* Certificate class types */
#define CAP_CERT_IMS_EAPC 0x00000001
#define CAP_CERT_IMS_EASC 0x00000002
#define CAP_CERT_IMS_EARC 0x00000003
#define CAP_CERT_IMS_IAPC 0x00000004
#define CAP_CERT_IMS_IASC 0x00000005
#define CAP_CERT_IMS_IARC 0x00000006
/* IMS Certificate response result codes */
#define CAP_IMS_RESULT_CERT_FOUND 0x00
#define CAP_IMS_RESULT_CERT_CLASS_INVAL 0x01
#define CAP_IMS_RESULT_CERT_CORRUPT 0x02
#define CAP_IMS_RESULT_CERT_NOT_FOUND 0x03
/* Authentication types */
#define CAP_AUTH_IMS_PRI 0x00000001
#define CAP_AUTH_IMS_SEC 0x00000002
#define CAP_AUTH_IMS_RSA 0x00000003
/* Authenticate response result codes */
#define CAP_AUTH_RESULT_CR_SUCCESS 0x00
#define CAP_AUTH_RESULT_CR_BAD_TYPE 0x01
#define CAP_AUTH_RESULT_CR_WRONG_EP 0x02
#define CAP_AUTH_RESULT_CR_NO_KEY 0x03
#define CAP_AUTH_RESULT_CR_SIG_FAIL 0x04
/* IOCTL support */
struct cap_ioc_get_endpoint_uid {
__u8 uid[8];
} __attribute__ ((__packed__));
struct cap_ioc_get_ims_certificate {
__u32 certificate_class;
__u32 certificate_id;
__u8 result_code;
__u32 cert_size;
__u8 certificate[CAP_CERTIFICATE_MAX_SIZE];
} __attribute__ ((__packed__));
struct cap_ioc_authenticate {
__u32 auth_type;
__u8 uid[8];
__u8 challenge[32];
__u8 result_code;
__u8 response[64];
__u32 signature_size;
__u8 signature[CAP_SIGNATURE_MAX_SIZE];
} __attribute__ ((__packed__));
#define CAP_IOCTL_BASE 'C'
#define CAP_IOC_GET_ENDPOINT_UID _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid)
#define CAP_IOC_GET_IMS_CERTIFICATE _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate)
#define CAP_IOC_AUTHENTICATE _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate)
#endif /* __GREYBUS_AUTHENTICATION_USER_H */

View file

@ -47,6 +47,7 @@ enum greybus_protocol {
GREYBUS_PROTOCOL_CAMERA_DATA = 0x16,
GREYBUS_PROTOCOL_FW_DOWNLOAD = 0x17,
GREYBUS_PROTOCOL_FW_MANAGEMENT = 0x18,
GREYBUS_PROTOCOL_AUTHENTICATION = 0x19,
GREYBUS_PROTOCOL_LOG = 0x1a,
/* ... */
GREYBUS_PROTOCOL_RAW = 0xfe,

View file

@ -345,6 +345,43 @@ struct gb_fw_mgmt_backend_fw_updated_request {
/* firmware management backend firmware updated response has no payload */
/* Component Authentication Protocol (CAP) */
/* Request Types */
#define GB_CAP_TYPE_GET_ENDPOINT_UID 0x01
#define GB_CAP_TYPE_GET_IMS_CERTIFICATE 0x02
#define GB_CAP_TYPE_AUTHENTICATE 0x03
/* CAP get endpoint uid request has no payload */
struct gb_cap_get_endpoint_uid_response {
__u8 uid[8];
} __packed;
/* CAP get endpoint ims certificate request/response */
struct gb_cap_get_ims_certificate_request {
__le32 certificate_class;
__le32 certificate_id;
} __packed;
struct gb_cap_get_ims_certificate_response {
__u8 result_code;
__u8 certificate[0];
} __packed;
/* CAP authenticate request/response */
struct gb_cap_authenticate_request {
__le32 auth_type;
__u8 uid[8];
__u8 challenge[32];
} __packed;
struct gb_cap_authenticate_response {
__u8 result_code;
__u8 response[64];
__u8 signature[0];
} __packed;
/* Bootrom Protocol */
/* Version of the Greybus bootrom protocol we support */