/arch/arc/

yrefslogblamecommitdiffstats
path: root/drivers/usb/wusbcore/security.c
blob: dd88441c8f7891e0d242e1107d6ff585a30999e4 (plain) (tree)
























                                                                      
                       

                          
                         

                   












































































































































































                                                                                         
                                                        
                                                                      


                              
                                                  




                                 
                                                             

                                                            

                                                                 
                         
         
                                                    

                                                         
                                                                                
                         
         
                        
                                                             
                                                        


                                                                 
                         
         
                  

                                    





























                                                                              
                         

                                       


                                                                          
                   
    
                    
                      






                                               








                                                                             
                                                                             

















                                                                             
                                                         














                                                                       
                                                         

























                                                                             
                                                   





















                                                                                



















                                                                          







































                                                                                








                                                                           


















                                                                           









                                                                          




                                                                         








                                                                        




                                                                   
                                                











                                                   
                       
                                                    



















































                                                                                                 
                                                                                         












                                                                                                 
/*
 * Wireless USB Host Controller
 * Security support: encryption enablement, etc
 *
 * Copyright (C) 2006 Intel Corporation
 * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 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 for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 *
 * FIXME: docs
 */
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/usb/ch9.h>
#include <linux/random.h>
#include <linux/export.h>
#include "wusbhc.h"

static void wusbhc_set_gtk_callback(struct urb *urb);
static void wusbhc_gtk_rekey_done_work(struct work_struct *work);

int wusbhc_sec_create(struct wusbhc *wusbhc)
{
	wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data);
	wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY;
	wusbhc->gtk.descr.bReserved = 0;

	wusbhc->gtk_index = wusb_key_index(0, WUSB_KEY_INDEX_TYPE_GTK,
					   WUSB_KEY_INDEX_ORIGINATOR_HOST);

	INIT_WORK(&wusbhc->gtk_rekey_done_work, wusbhc_gtk_rekey_done_work);

	return 0;
}


/* Called when the HC is destroyed */
void wusbhc_sec_destroy(struct wusbhc *wusbhc)
{
}


/**
 * wusbhc_next_tkid - generate a new, currently unused, TKID
 * @wusbhc:   the WUSB host controller
 * @wusb_dev: the device whose PTK the TKID is for
 *            (or NULL for a TKID for a GTK)
 *
 * The generated TKID consist of two parts: the device's authenicated
 * address (or 0 or a GTK); and an incrementing number.  This ensures
 * that TKIDs cannot be shared between devices and by the time the
 * incrementing number wraps around the older TKIDs will no longer be
 * in use (a maximum of two keys may be active at any one time).
 */
static u32 wusbhc_next_tkid(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
{
	u32 *tkid;
	u32 addr;

	if (wusb_dev == NULL) {
		tkid = &wusbhc->gtk_tkid;
		addr = 0;
	} else {
		tkid = &wusb_port_by_idx(wusbhc, wusb_dev->port_idx)->ptk_tkid;
		addr = wusb_dev->addr & 0x7f;
	}

	*tkid = (addr << 8) | ((*tkid + 1) & 0xff);

	return *tkid;
}

static void wusbhc_generate_gtk(struct wusbhc *wusbhc)
{
	const size_t key_size = sizeof(wusbhc->gtk.data);
	u32 tkid;

	tkid = wusbhc_next_tkid(wusbhc, NULL);

	wusbhc->gtk.descr.tTKID[0] = (tkid >>  0) & 0xff;
	wusbhc->gtk.descr.tTKID[1] = (tkid >>  8) & 0xff;
	wusbhc->gtk.descr.tTKID[2] = (tkid >> 16) & 0xff;

	get_random_bytes(wusbhc->gtk.descr.bKeyData, key_size);
}

/**
 * wusbhc_sec_start - start the security management process
 * @wusbhc: the WUSB host controller
 *
 * Generate and set an initial GTK on the host controller.
 *
 * Called when the HC is started.
 */
int wusbhc_sec_start(struct wusbhc *wusbhc)
{
	const size_t key_size = sizeof(wusbhc->gtk.data);
	int result;

	wusbhc_generate_gtk(wusbhc);

	result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
				 &wusbhc->gtk.descr.bKeyData, key_size);
	if (result < 0)
		dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n",
			result);

	return result;
}

/**
 * wusbhc_sec_stop - stop the security management process
 * @wusbhc: the WUSB host controller
 *
 * Wait for any pending GTK rekeys to stop.
 */
void wusbhc_sec_stop(struct wusbhc *wusbhc)
{
	cancel_work_sync(&wusbhc->gtk_rekey_done_work);
}


/** @returns encryption type name */
const char *wusb_et_name(u8 x)
{
	switch (x) {
	case USB_ENC_TYPE_UNSECURE:	return "unsecure";
	case USB_ENC_TYPE_WIRED:	return "wired";
	case USB_ENC_TYPE_CCM_1:	return "CCM-1";
	case USB_ENC_TYPE_RSA_1:	return "RSA-1";
	default: 			return "unknown";
	}
}
EXPORT_SYMBOL_GPL(wusb_et_name);

/*
 * Set the device encryption method
 *
 * We tell the device which encryption method to use; we do this when
 * setting up the device's security.
 */
static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value)
{
	int result;
	struct device *dev = &usb_dev->dev;
	struct wusb_dev *wusb_dev = usb_dev->wusb_dev;

	if (value) {
		value = wusb_dev->ccm1_etd.bEncryptionValue;
	} else {
		/* FIXME: should be wusb_dev->etd[UNSECURE].bEncryptionValue */
		value = 0;
	}
	/* Set device's */
	result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
			USB_REQ_SET_ENCRYPTION,
			USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
			value, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
	if (result < 0)
		dev_err(dev, "Can't set device's WUSB encryption to "
			"%s (value %d): %d\n",
			wusb_et_name(wusb_dev->ccm1_etd.bEncryptionType),
			wusb_dev->ccm1_etd.bEncryptionValue,  result);
	return result;
}

/*
 * Set the GTK to be used by a device.
 *
 * The device must be authenticated.
 */
static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
{
	struct usb_device *usb_dev = wusb_dev->usb_dev;

	return usb_control_msg(
		usb_dev, usb_sndctrlpipe(usb_dev, 0),
		USB_REQ_SET_DESCRIPTOR,
		USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
		USB_DT_KEY << 8 | wusbhc->gtk_index, 0,
		&wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
		1000);
}


/* FIXME: prototype for adding security */
int wusb_dev_sec_add(struct wusbhc *wusbhc,
		     struct usb_device *usb_dev, struct wusb_dev *wusb_dev)
{
	int result, bytes, secd_size;
	struct device *dev = &usb_dev->dev;
	struct usb_security_descriptor *secd, *new_secd;
	const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL;
	const void *itr, *top;
	char buf[64];

	secd = kmalloc(sizeof(*secd), GFP_KERNEL);
	if (secd == NULL) {
		result = -ENOMEM;
		goto out;
	}

	result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
				    0, secd, sizeof(*secd));
	if (result < sizeof(*secd)) {
		dev_err(dev, "Can't read security descriptor or "
			"not enough data: %d\n", result);
		goto out;
	}
	secd_size = le16_to_cpu(secd->wTotalLength);
	new_secd = krealloc(secd, secd_size, GFP_KERNEL);
	if (new_secd == NULL) {
		dev_err(dev, "Can't allocate space for security descriptors\n");
		goto out;
	}
	secd = new_secd;
	result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
				    0, secd, secd_size);
	if (result < secd_size) {
		dev_err(dev, "Can't read security descriptor or "
			"not enough data: %d\n", result);
		goto out;
	}
	bytes = 0;
	itr = &secd[1];
	top = (void *)secd + result;
	while (itr < top) {
		etd = itr;
		if (top - itr < sizeof(*etd)) {
			dev_err(dev, "BUG: bad device security descriptor; "
				"not enough data (%zu vs %zu bytes left)\n",
				top - itr, sizeof(*etd));
			break;
		}
		if (etd->bLength < sizeof(*etd)) {
			dev_err(dev, "BUG: bad device encryption descriptor; "
				"descriptor is too short "
				"(%u vs %zu needed)\n",
				etd->bLength, sizeof(*etd));
			break;
		}
		itr += etd->bLength;
		bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
				  "%s (0x%02x/%02x) ",
				  wusb_et_name(etd->bEncryptionType),
				  etd->bEncryptionValue, etd->bAuthKeyIndex);
		if (etd->bEncryptionType == USB_ENC_TYPE_CCM_1)
			ccm1_etd = etd;
	}