2297 lines
64 KiB
C
2297 lines
64 KiB
C
/*******************************************************************
|
|
* This file is part of the Emulex Linux Device Driver for *
|
|
* Fibre Channel Host Bus Adapters. *
|
|
* Copyright (C) 2018-2020 Broadcom. All Rights Reserved. The term *
|
|
* “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. *
|
|
* Copyright (C) 2004-2016 Emulex. All rights reserved. *
|
|
* EMULEX and SLI are trademarks of Emulex. *
|
|
* www.broadcom.com *
|
|
* *
|
|
* 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. *
|
|
* ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND *
|
|
* WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, *
|
|
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE *
|
|
* DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD *
|
|
* TO BE LEGALLY INVALID. See the GNU General Public License for *
|
|
* more details, a copy of which can be found in the file COPYING *
|
|
* included with this package. *
|
|
*******************************************************************/
|
|
/* See Fibre Channel protocol T11 FC-SP-2 for details */
|
|
#include <linux/blkdev.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/list.h>
|
|
#include <linux/mpi.h>
|
|
#include <crypto/hash.h>
|
|
|
|
#include <scsi/scsi.h>
|
|
#include <scsi/scsi_host.h>
|
|
#include <scsi/scsi_transport_fc.h>
|
|
|
|
#include "lpfc_hw4.h"
|
|
#include "lpfc_hw.h"
|
|
#include "lpfc_sli.h"
|
|
#include "lpfc_sli4.h"
|
|
#include "lpfc_nl.h"
|
|
#include "lpfc_disc.h"
|
|
#include "lpfc_scsi.h"
|
|
#include "lpfc.h"
|
|
#include "lpfc_logmsg.h"
|
|
#include "lpfc_crtn.h"
|
|
#include "lpfc_vport.h"
|
|
#include "lpfc_debugfs.h"
|
|
#include "lpfc_auth.h"
|
|
|
|
struct dh_group dh_group_array[5] = {
|
|
{
|
|
DH_GROUP_NULL, 0,
|
|
{
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
}
|
|
},
|
|
{
|
|
DH_GROUP_1024, 128,
|
|
{
|
|
0xEE, 0xAF, 0x0A, 0xB9, 0xAD, 0xB3, 0x8D, 0xD6,
|
|
0x9C, 0x33, 0xF8, 0x0A, 0xFA, 0x8F, 0xC5, 0xE8,
|
|
0x60, 0x72, 0x61, 0x87, 0x75, 0xFF, 0x3C, 0x0B,
|
|
0x9E, 0xA2, 0x31, 0x4C, 0x9C, 0x25, 0x65, 0x76,
|
|
0xD6, 0x74, 0xDF, 0x74, 0x96, 0xEA, 0x81, 0xD3,
|
|
0x38, 0x3B, 0x48, 0x13, 0xD6, 0x92, 0xC6, 0xE0,
|
|
0xE0, 0xD5, 0xD8, 0xE2, 0x50, 0xB9, 0x8B, 0xE4,
|
|
0x8E, 0x49, 0x5C, 0x1D, 0x60, 0x89, 0xDA, 0xD1,
|
|
0x5D, 0xC7, 0xD7, 0xB4, 0x61, 0x54, 0xD6, 0xB6,
|
|
0xCE, 0x8E, 0xF4, 0xAD, 0x69, 0xB1, 0x5D, 0x49,
|
|
0x82, 0x55, 0x9B, 0x29, 0x7B, 0xCF, 0x18, 0x85,
|
|
0xC5, 0x29, 0xF5, 0x66, 0x66, 0x0E, 0x57, 0xEC,
|
|
0x68, 0xED, 0xBC, 0x3C, 0x05, 0x72, 0x6C, 0xC0,
|
|
0x2F, 0xD4, 0xCB, 0xF4, 0x97, 0x6E, 0xAA, 0x9A,
|
|
0xFD, 0x51, 0x38, 0xFE, 0x83, 0x76, 0x43, 0x5B,
|
|
0x9F, 0xC6, 0x1D, 0x2F, 0xC0, 0xEB, 0x06, 0xE3,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
}
|
|
},
|
|
{
|
|
DH_GROUP_1280, 160,
|
|
{
|
|
0xD7, 0x79, 0x46, 0x82, 0x6E, 0x81, 0x19, 0x14,
|
|
0xB3, 0x94, 0x01, 0xD5, 0x6A, 0x0A, 0x78, 0x43,
|
|
0xA8, 0xE7, 0x57, 0x5D, 0x73, 0x8C, 0x67, 0x2A,
|
|
0x09, 0x0A, 0xB1, 0x18, 0x7D, 0x69, 0x0D, 0xC4,
|
|
0x38, 0x72, 0xFC, 0x06, 0xA7, 0xB6, 0xA4, 0x3F,
|
|
0x3B, 0x95, 0xBE, 0xAE, 0xC7, 0xDF, 0x04, 0xB9,
|
|
0xD2, 0x42, 0xEB, 0xDC, 0x48, 0x11, 0x11, 0x28,
|
|
0x32, 0x16, 0xCE, 0x81, 0x6E, 0x00, 0x4B, 0x78,
|
|
0x6C, 0x5F, 0xCE, 0x85, 0x67, 0x80, 0xD4, 0x18,
|
|
0x37, 0xD9, 0x5A, 0xD7, 0x87, 0xA5, 0x0B, 0xBE,
|
|
0x90, 0xBD, 0x3A, 0x9C, 0x98, 0xAC, 0x0F, 0x5F,
|
|
0xC0, 0xDE, 0x74, 0x4B, 0x1C, 0xDE, 0x18, 0x91,
|
|
0x69, 0x08, 0x94, 0xBC, 0x1F, 0x65, 0xE0, 0x0D,
|
|
0xE1, 0x5B, 0x4B, 0x2A, 0xA6, 0xD8, 0x71, 0x00,
|
|
0xC9, 0xEC, 0xC2, 0x52, 0x7E, 0x45, 0xEB, 0x84,
|
|
0x9D, 0xEB, 0x14, 0xBB, 0x20, 0x49, 0xB1, 0x63,
|
|
0xEA, 0x04, 0x18, 0x7F, 0xD2, 0x7C, 0x1B, 0xD9,
|
|
0xC7, 0x95, 0x8C, 0xD4, 0x0C, 0xE7, 0x06, 0x7A,
|
|
0x9C, 0x02, 0x4F, 0x9B, 0x7C, 0x5A, 0x0B, 0x4F,
|
|
0x50, 0x03, 0x68, 0x61, 0x61, 0xF0, 0x60, 0x5B,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
}
|
|
},
|
|
{
|
|
DH_GROUP_1536, 192,
|
|
{
|
|
0x9D, 0xEF, 0x3C, 0xAF, 0xB9, 0x39, 0x27, 0x7A,
|
|
0xB1, 0xF1, 0x2A, 0x86, 0x17, 0xA4, 0x7B, 0xBB,
|
|
0xDB, 0xA5, 0x1D, 0xF4, 0x99, 0xAC, 0x4C, 0x80,
|
|
0xBE, 0xEE, 0xA9, 0x61, 0x4B, 0x19, 0xCC, 0x4D,
|
|
0x5F, 0x4F, 0x5F, 0x55, 0x6E, 0x27, 0xCB, 0xDE,
|
|
0x51, 0xC6, 0xA9, 0x4B, 0xE4, 0x60, 0x7A, 0x29,
|
|
0x15, 0x58, 0x90, 0x3B, 0xA0, 0xD0, 0xF8, 0x43,
|
|
0x80, 0xB6, 0x55, 0xBB, 0x9A, 0x22, 0xE8, 0xDC,
|
|
0xDF, 0x02, 0x8A, 0x7C, 0xEC, 0x67, 0xF0, 0xD0,
|
|
0x81, 0x34, 0xB1, 0xC8, 0xB9, 0x79, 0x89, 0x14,
|
|
0x9B, 0x60, 0x9E, 0x0B, 0xE3, 0xBA, 0xB6, 0x3D,
|
|
0x47, 0x54, 0x83, 0x81, 0xDB, 0xC5, 0xB1, 0xFC,
|
|
0x76, 0x4E, 0x3F, 0x4B, 0x53, 0xDD, 0x9D, 0xA1,
|
|
0x15, 0x8B, 0xFD, 0x3E, 0x2B, 0x9C, 0x8C, 0xF5,
|
|
0x6E, 0xDF, 0x01, 0x95, 0x39, 0x34, 0x96, 0x27,
|
|
0xDB, 0x2F, 0xD5, 0x3D, 0x24, 0xB7, 0xC4, 0x86,
|
|
0x65, 0x77, 0x2E, 0x43, 0x7D, 0x6C, 0x7F, 0x8C,
|
|
0xE4, 0x42, 0x73, 0x4A, 0xF7, 0xCC, 0xB7, 0xAE,
|
|
0x83, 0x7C, 0x26, 0x4A, 0xE3, 0xA9, 0xBE, 0xB8,
|
|
0x7F, 0x8A, 0x2F, 0xE9, 0xB8, 0xB5, 0x29, 0x2E,
|
|
0x5A, 0x02, 0x1F, 0xFF, 0x5E, 0x91, 0x47, 0x9E,
|
|
0x8C, 0xE7, 0xA2, 0x8C, 0x24, 0x42, 0xC6, 0xF3,
|
|
0x15, 0x18, 0x0F, 0x93, 0x49, 0x9A, 0x23, 0x4D,
|
|
0xCF, 0x76, 0xE3, 0xFE, 0xD1, 0x35, 0xF9, 0xBB,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
}
|
|
},
|
|
{
|
|
DH_GROUP_2048, 256,
|
|
{
|
|
0xAC, 0x6B, 0xDB, 0x41, 0x32, 0x4A, 0x9A, 0x9B,
|
|
0xF1, 0x66, 0xDE, 0x5E, 0x13, 0x89, 0x58, 0x2F,
|
|
0xAF, 0x72, 0xB6, 0x65, 0x19, 0x87, 0xEE, 0x07,
|
|
0xFC, 0x31, 0x92, 0x94, 0x3D, 0xB5, 0x60, 0x50,
|
|
0xA3, 0x73, 0x29, 0xCB, 0xB4, 0xA0, 0x99, 0xED,
|
|
0x81, 0x93, 0xE0, 0x75, 0x77, 0x67, 0xA1, 0x3D,
|
|
0xD5, 0x23, 0x12, 0xAB, 0x4B, 0x03, 0x31, 0x0D,
|
|
0xCD, 0x7F, 0x48, 0xA9, 0xDA, 0x04, 0xFD, 0x50,
|
|
0xE8, 0x08, 0x39, 0x69, 0xED, 0xB7, 0x67, 0xB0,
|
|
0xCF, 0x60, 0x95, 0x17, 0x9A, 0x16, 0x3A, 0xB3,
|
|
0x66, 0x1A, 0x05, 0xFB, 0xD5, 0xFA, 0xAA, 0xE8,
|
|
0x29, 0x18, 0xA9, 0x96, 0x2F, 0x0B, 0x93, 0xB8,
|
|
0x55, 0xF9, 0x79, 0x93, 0xEC, 0x97, 0x5E, 0xEA,
|
|
0xA8, 0x0D, 0x74, 0x0A, 0xDB, 0xF4, 0xFF, 0x74,
|
|
0x73, 0x59, 0xD0, 0x41, 0xD5, 0xC3, 0x3E, 0xA7,
|
|
0x1D, 0x28, 0x1E, 0x44, 0x6B, 0x14, 0x77, 0x3B,
|
|
0xCA, 0x97, 0xB4, 0x3A, 0x23, 0xFB, 0x80, 0x16,
|
|
0x76, 0xBD, 0x20, 0x7A, 0x43, 0x6C, 0x64, 0x81,
|
|
0xF1, 0xD2, 0xB9, 0x07, 0x87, 0x17, 0x46, 0x1A,
|
|
0x5B, 0x9D, 0x32, 0xE6, 0x88, 0xF8, 0x77, 0x48,
|
|
0x54, 0x45, 0x23, 0xB5, 0x24, 0xB0, 0xD5, 0x7D,
|
|
0x5E, 0xA7, 0x7A, 0x27, 0x75, 0xD2, 0xEC, 0xFA,
|
|
0x03, 0x2C, 0xFB, 0xDB, 0xF5, 0x2F, 0xB3, 0x78,
|
|
0x61, 0x60, 0x27, 0x90, 0x04, 0xE5, 0x7A, 0xE6,
|
|
0xAF, 0x87, 0x4E, 0x73, 0x03, 0xCE, 0x53, 0x29,
|
|
0x9C, 0xCC, 0x04, 0x1C, 0x7B, 0xC3, 0x08, 0xD8,
|
|
0x2A, 0x56, 0x98, 0xF3, 0xA8, 0xD0, 0xC3, 0x82,
|
|
0x71, 0xAE, 0x35, 0xF8, 0xE9, 0xDB, 0xFB, 0xB6,
|
|
0x94, 0xB5, 0xC8, 0x03, 0xD8, 0x9F, 0x7A, 0xE4,
|
|
0x35, 0xDE, 0x23, 0x6D, 0x52, 0x5F, 0x54, 0x75,
|
|
0x9B, 0x65, 0xE3, 0x72, 0xFC, 0xD6, 0x8E, 0xF2,
|
|
0x0F, 0xA7, 0x11, 0x1F, 0x9E, 0x4A, 0xFF, 0x73,
|
|
}
|
|
},
|
|
};
|
|
|
|
/**
|
|
* lpfc_auth_cancel_tmr
|
|
* @ptr: pointer to a node-list data structure.
|
|
*
|
|
* This routine cancels any pending authentication timer.
|
|
*
|
|
**/
|
|
void
|
|
lpfc_auth_cancel_tmr(struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_vport *vport = ndlp->vport;
|
|
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
|
|
|
|
if (!(ndlp->nlp_flag & NLP_AUTH_TMO))
|
|
return;
|
|
|
|
spin_lock_irq(shost->host_lock);
|
|
ndlp->nlp_flag &= ~NLP_AUTH_TMO;
|
|
spin_unlock_irq(shost->host_lock);
|
|
del_timer_sync(&ndlp->dhc_cfg.nlp_reauthfunc);
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_start_tmr
|
|
* @ndlp: pointer to a node-list data structure.
|
|
* @value: timer value (in msecs).
|
|
*
|
|
* This routine cancels any pending reauthentication timer and updates
|
|
* the timer with a new value. This timer may also be used during the
|
|
* authentication process to check for auth_tmo.
|
|
*
|
|
**/
|
|
void
|
|
lpfc_auth_start_tmr(struct lpfc_nodelist *ndlp, uint32_t value)
|
|
{
|
|
struct lpfc_vport *vport = ndlp->vport;
|
|
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
|
|
|
|
if (ndlp->nlp_flag & NLP_AUTH_TMO)
|
|
lpfc_auth_cancel_tmr(ndlp);
|
|
|
|
mod_timer(&ndlp->dhc_cfg.nlp_reauthfunc,
|
|
jiffies + msecs_to_jiffies(value));
|
|
spin_lock_irq(shost->host_lock);
|
|
ndlp->nlp_flag |= NLP_AUTH_TMO;
|
|
spin_unlock_irq(shost->host_lock);
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_start_reauth_tmr.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Set-up timeout value for scheduled re-authentication.
|
|
**/
|
|
void
|
|
lpfc_auth_start_reauth_tmr(struct lpfc_nodelist *ndlp)
|
|
{
|
|
u32 msecs;
|
|
|
|
msecs = ndlp->dhc_cfg.reauth_interval * 60000;
|
|
lpfc_auth_start_tmr(ndlp, msecs);
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_findnode - Search for a nodelist item by wwpn pair.
|
|
* @vport: pointer to a vport structure.
|
|
* @lwwpn: pointer to a local portname.
|
|
* @rwwpn: pointer to a remote portname.
|
|
*
|
|
* This routine returns an node list pointer for authentication. The search
|
|
* uses local and remote portnames provided, with specially accomodation for
|
|
* the wwpn used to identify the fabric. The routine returns pointer to the
|
|
* matching ndlp or NULL if no matching entry is found.
|
|
**/
|
|
struct lpfc_nodelist *
|
|
lpfc_auth_findnode(struct lpfc_vport *vport, struct lpfc_name *lwwpn,
|
|
struct lpfc_name *rwwpn)
|
|
{
|
|
struct lpfc_nodelist *ndlp = NULL;
|
|
struct lpfc_name wwpn;
|
|
|
|
wwpn.u.name = AUTH_FABRIC_WWN;
|
|
if (!memcmp(&vport->fc_portname, lwwpn->u.wwn, 8)) {
|
|
if (!memcmp(wwpn.u.wwn, rwwpn->u.wwn, 8))
|
|
ndlp = lpfc_findnode_did(vport, Fabric_DID);
|
|
else
|
|
ndlp = lpfc_findnode_wwpn(vport, rwwpn);
|
|
}
|
|
return ndlp;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_find_cfg_item - Search for a configuration entry by wwpn pair.
|
|
* @phba: pointer to a host.
|
|
* @lwwpn: pointer to a local portname.
|
|
* @rwwpn: pointer to a remote portname.
|
|
* @valid: address of entry valid flag.
|
|
*
|
|
* This routine returns a pointer to an active configuration entry matching
|
|
* the local and remote portnames provided, or NULL if no matching entry is
|
|
* found.
|
|
**/
|
|
struct lpfc_auth_cfg_entry *
|
|
lpfc_auth_find_cfg_item(struct lpfc_hba *phba, uint8_t *lwwpn, uint8_t *rwwpn,
|
|
uint8_t *valid)
|
|
{
|
|
struct lpfc_auth_cfg *pcfg = NULL;
|
|
struct lpfc_auth_cfg_entry *entry = NULL;
|
|
uint8_t *ptmp = NULL;
|
|
uint16_t entry_cnt, i;
|
|
|
|
*valid = 0;
|
|
pcfg = lpfc_auth_get_active_cfg_ptr(phba);
|
|
if (!pcfg)
|
|
return entry;
|
|
|
|
entry_cnt = pcfg->hdr.entry_cnt;
|
|
if (entry_cnt > MAX_AUTH_CONFIG_ENTRIES)
|
|
entry_cnt = MAX_AUTH_CONFIG_ENTRIES;
|
|
|
|
ptmp = (uint8_t *)&pcfg->entry[0];
|
|
for (i = 0; i < entry_cnt; i++) {
|
|
if (i == AUTH_CONFIG_ENTRIES_PER_PAGE) {
|
|
ptmp = (uint8_t *)lpfc_auth_get_active_cfg_p2(phba);
|
|
if (!ptmp)
|
|
return entry;
|
|
}
|
|
if ((memcmp(ptmp + 8, rwwpn, 8)) ||
|
|
(memcmp(ptmp, lwwpn, 8))) {
|
|
ptmp += sizeof(struct lpfc_auth_cfg_entry);
|
|
} else {
|
|
entry = (struct lpfc_auth_cfg_entry *)ptmp;
|
|
/* match found, check validity */
|
|
*valid = entry->auth_flags.valid;
|
|
break;
|
|
}
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_lookup_cfg - Set-up auth configuration information for an ndlp.
|
|
* @ndlp: pointer to lpfc_nodelist instance.
|
|
*
|
|
* This routine will initiate a search for a configuration entry matching the
|
|
* local wwpn and remote wwpn for the ndlp. Initial inplementation is limited
|
|
* to authentication with the fabric. If a configuration entry is found, the
|
|
* dh-chap configuration in the ndlp structure is populated.
|
|
*
|
|
* Return codes
|
|
* 0 - Success
|
|
* 1 - Failure
|
|
**/
|
|
int
|
|
lpfc_auth_lookup_cfg(struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_vport *vport = ndlp->vport;
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct lpfc_auth_cfg_entry *entry = NULL;
|
|
uint8_t rwwpn[8];
|
|
uint8_t valid;
|
|
int i, rc = 0;
|
|
|
|
/* limited to authentication with the fabric */
|
|
u64_to_wwn(AUTH_FABRIC_WWN, rwwpn);
|
|
entry = lpfc_auth_find_cfg_item(phba, (uint8_t *)&vport->fc_portname,
|
|
rwwpn, &valid);
|
|
if (!entry || !valid)
|
|
return 1;
|
|
|
|
ndlp->dhc_cfg.auth_tmo = entry->auth_tmo;
|
|
ndlp->dhc_cfg.auth_mode = entry->auth_mode;
|
|
ndlp->dhc_cfg.bidirectional = entry->auth_flags.bi_direction;
|
|
ndlp->dhc_cfg.reauth_interval = entry->reauth_interval;
|
|
ndlp->dhc_cfg.local_passwd_len = entry->pwd.local_pw_length;
|
|
ndlp->dhc_cfg.remote_passwd_len = entry->pwd.remote_pw_length;
|
|
ndlp->dhc_cfg.direction = AUTH_DIRECTION_NONE;
|
|
memcpy(ndlp->dhc_cfg.local_passwd, &entry->pwd.local_pw,
|
|
entry->pwd.local_pw_length);
|
|
memcpy(ndlp->dhc_cfg.remote_passwd, &entry->pwd.remote_pw,
|
|
entry->pwd.remote_pw_length);
|
|
for (i = 0; i < 4; i++) {
|
|
switch (entry->hash_priority[i]) {
|
|
case LPFC_HASH_MD5:
|
|
ndlp->dhc_cfg.hash_pri[i] = HASH_MD5;
|
|
break;
|
|
case LPFC_HASH_SHA1:
|
|
ndlp->dhc_cfg.hash_pri[i] = HASH_SHA1;
|
|
break;
|
|
default:
|
|
ndlp->dhc_cfg.hash_pri[i] = 0;
|
|
}
|
|
}
|
|
|
|
ndlp->dhc_cfg.dh_grp_cnt = 0;
|
|
for (i = 0; i < 8; i++) {
|
|
switch (entry->dh_grp_priority[i]) {
|
|
case LPFC_DH_GROUP_2048:
|
|
ndlp->dhc_cfg.dh_grp_pri[i] = DH_GROUP_2048;
|
|
break;
|
|
case LPFC_DH_GROUP_1536:
|
|
ndlp->dhc_cfg.dh_grp_pri[i] = DH_GROUP_1536;
|
|
break;
|
|
case LPFC_DH_GROUP_1280:
|
|
ndlp->dhc_cfg.dh_grp_pri[i] = DH_GROUP_1280;
|
|
break;
|
|
case LPFC_DH_GROUP_1024:
|
|
ndlp->dhc_cfg.dh_grp_pri[i] = DH_GROUP_1024;
|
|
break;
|
|
case LPFC_DH_GROUP_NULL:
|
|
ndlp->dhc_cfg.dh_grp_pri[i] = DH_GROUP_NULL;
|
|
break;
|
|
default:
|
|
return rc;
|
|
}
|
|
ndlp->dhc_cfg.dh_grp_cnt++;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_free_active_cfg_list - Free authentication config resources.
|
|
* @phba: pointer to a host.
|
|
*
|
|
* This routine frees any resources allocated to hold the driver's active
|
|
* copy of the authentication configuration object.
|
|
**/
|
|
void
|
|
lpfc_auth_free_active_cfg_list(struct lpfc_hba *phba)
|
|
{
|
|
struct lpfc_dmabuf *dmabuf, *next;
|
|
|
|
if (!list_empty(&phba->lpfc_auth_active_cfg_list)) {
|
|
list_for_each_entry_safe(dmabuf, next,
|
|
&phba->lpfc_auth_active_cfg_list,
|
|
list) {
|
|
list_del(&dmabuf->list);
|
|
dma_free_coherent(&phba->pcidev->dev, SLI4_PAGE_SIZE,
|
|
dmabuf->virt, dmabuf->phys);
|
|
kfree(dmabuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_alloc_active_cfg_list - Allocate authentication config resources.
|
|
* @phba: pointer to a host.
|
|
*
|
|
* This routine attempts to allocate resources to hold the driver's active
|
|
* copy of the authentication configuration object.
|
|
*
|
|
* Return codes
|
|
* 0 - Success
|
|
* 1 - Failure
|
|
**/
|
|
int
|
|
lpfc_auth_alloc_active_cfg_list(struct lpfc_hba *phba)
|
|
{
|
|
struct lpfc_dmabuf *dmabuf;
|
|
uint8_t i;
|
|
int rc = 0;
|
|
|
|
if (!list_empty(&phba->lpfc_auth_active_cfg_list))
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
dmabuf = kzalloc(sizeof(*dmabuf), GFP_KERNEL);
|
|
if (!dmabuf) {
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
dmabuf->virt = dma_alloc_coherent(&phba->pcidev->dev,
|
|
SLI4_PAGE_SIZE,
|
|
&dmabuf->phys,
|
|
GFP_KERNEL);
|
|
if (!dmabuf->virt) {
|
|
kfree(dmabuf);
|
|
rc = -ENOMEM;
|
|
goto out;
|
|
}
|
|
list_add_tail(&dmabuf->list, &phba->lpfc_auth_active_cfg_list);
|
|
}
|
|
out:
|
|
if (rc)
|
|
lpfc_auth_free_active_cfg_list(phba);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_get_active_cfg_ptr - Returns the base address of the
|
|
* authentication config.
|
|
* @phba: pointer to a host.
|
|
*
|
|
* This routine returns a pointer to the first page of the driver's active copy
|
|
* of the authentication configuration for the port. The routine will return
|
|
* NULL if there is no active configuration.
|
|
*
|
|
**/
|
|
struct lpfc_auth_cfg *
|
|
lpfc_auth_get_active_cfg_ptr(struct lpfc_hba *phba)
|
|
{
|
|
struct lpfc_dmabuf *dmabuf;
|
|
struct lpfc_auth_cfg *p = NULL;
|
|
|
|
if (!list_empty(&phba->lpfc_auth_active_cfg_list)) {
|
|
dmabuf = list_first_entry(&phba->lpfc_auth_active_cfg_list,
|
|
struct lpfc_dmabuf, list);
|
|
|
|
p = (struct lpfc_auth_cfg *)dmabuf->virt;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_get_active_cfg_p2 - Returns the base address for the second page
|
|
* of the active authentication config.
|
|
* @phba: pointer to a host.
|
|
*
|
|
* This routine returns a pointer to the second page of the driver's active copy
|
|
* of the authentication configuration for the port. The routine will return
|
|
* NULL if there is no active configuation.
|
|
*
|
|
**/
|
|
struct lpfc_auth_cfg_entry *
|
|
lpfc_auth_get_active_cfg_p2(struct lpfc_hba *phba)
|
|
{
|
|
struct lpfc_dmabuf *dmabuf;
|
|
struct lpfc_auth_cfg_entry *p = NULL;
|
|
|
|
if (!list_empty(&phba->lpfc_auth_active_cfg_list)) {
|
|
dmabuf = list_entry((&phba->lpfc_auth_active_cfg_list)->prev,
|
|
struct lpfc_dmabuf, list);
|
|
|
|
p = (struct lpfc_auth_cfg_entry *)dmabuf->virt;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_cmpl_rd_object, completion for read object mbx cmd.
|
|
* @phba: pointer to a host.
|
|
* @mboxq: pointer to lpfc mailbox command context.
|
|
*
|
|
* This routine handles the completion of a read object mailbox command
|
|
* issued to populate the driver's active copy of the authentication
|
|
* configuration.
|
|
**/
|
|
void
|
|
lpfc_auth_cmpl_rd_object(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
|
|
{
|
|
struct lpfc_mbx_auth_rd_object *rd_object;
|
|
union lpfc_sli4_cfg_shdr *shdr;
|
|
uint32_t shdr_status, shdr_add_status;
|
|
int mb_sts = mboxq->u.mb.mbxStatus;
|
|
|
|
rd_object = (struct lpfc_mbx_auth_rd_object *)mboxq->sge_array->addr[0];
|
|
shdr = &rd_object->cfg_shdr;
|
|
shdr_status = bf_get(lpfc_mbox_hdr_status, &shdr->response);
|
|
shdr_add_status = bf_get(lpfc_mbox_hdr_add_status, &shdr->response);
|
|
|
|
if (shdr_status || shdr_add_status || mb_sts) {
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_AUTH,
|
|
"3040 Read Object mailbox cmd failed with "
|
|
"status x%x add_status x%x, mbx status x%x\n",
|
|
shdr_status, shdr_add_status, mb_sts);
|
|
|
|
if (mboxq->ctx_buf == &phba->lpfc_auth_active_cfg_list)
|
|
lpfc_auth_free_active_cfg_list(phba);
|
|
}
|
|
|
|
lpfc_sli4_mbox_cmd_free(phba, mboxq);
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_read_cfg_object, to read object.
|
|
* @phba: pointer to a host.
|
|
*
|
|
* This routine issues a read object mailbox command to populate the driver's
|
|
* active copy of the authentication configuration.
|
|
*
|
|
* Return codes
|
|
* 0 - Success
|
|
* 1 - Failure
|
|
**/
|
|
int
|
|
lpfc_auth_read_cfg_object(struct lpfc_hba *phba)
|
|
{
|
|
uint32_t dma_size;
|
|
uint32_t offset;
|
|
char *obj_str = {"/driver/auth.cfg"};
|
|
int rc = 0;
|
|
|
|
if (list_empty(&phba->lpfc_auth_active_cfg_list))
|
|
rc = lpfc_auth_alloc_active_cfg_list(phba);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* 8k per port */
|
|
dma_size = SLI4_PAGE_SIZE * 2;
|
|
offset = phba->sli4_hba.lnk_info.lnk_no * dma_size;
|
|
rc = lpfc_auth_issue_rd_object(phba, &phba->lpfc_auth_active_cfg_list,
|
|
dma_size, &offset, obj_str);
|
|
if (rc) {
|
|
/* MBX_BUSY is acceptable, no reason to alarm caller */
|
|
if (rc == MBX_BUSY)
|
|
rc = MBX_SUCCESS;
|
|
else
|
|
lpfc_printf_log(phba, KERN_INFO, LOG_MBOX | LOG_AUTH,
|
|
"3041 Read Object abnormal exit:%x.\n",
|
|
rc);
|
|
}
|
|
if (rc != MBX_SUCCESS)
|
|
lpfc_auth_free_active_cfg_list(phba);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_compute_hash - Calculate the hash
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
*
|
|
* Return value
|
|
* Pointer to hash
|
|
**/
|
|
static uint8_t *
|
|
lpfc_auth_compute_hash(uint32_t hash_id,
|
|
uint8_t *val1,
|
|
uint32_t val1_len,
|
|
uint8_t *val2,
|
|
uint32_t val2_len,
|
|
uint8_t *val3,
|
|
uint32_t val3_len)
|
|
{
|
|
struct crypto_shash *tfm = NULL;
|
|
struct shash_desc *desc = NULL;
|
|
uint8_t *digest = NULL;
|
|
uint32_t chal_len = 0;
|
|
int rc = 0;
|
|
|
|
switch (hash_id) {
|
|
case HASH_MD5:
|
|
tfm = crypto_alloc_shash("md5", 0, 0);
|
|
chal_len = MD5_CHAL_LEN;
|
|
break;
|
|
case HASH_SHA1:
|
|
tfm = crypto_alloc_shash("sha1", 0, 0);
|
|
chal_len = SHA1_CHAL_LEN;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
if (!tfm)
|
|
return NULL;
|
|
|
|
digest = kzalloc(chal_len, GFP_KERNEL);
|
|
if (!digest)
|
|
goto out;
|
|
|
|
desc = kmalloc(sizeof(*desc) + crypto_shash_descsize(tfm),
|
|
GFP_KERNEL);
|
|
if (!desc) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
desc->tfm = tfm;
|
|
#if (KERNEL_MAJOR < 5)
|
|
desc->flags = 0;
|
|
#endif
|
|
|
|
rc = crypto_shash_init(desc);
|
|
if (rc < 0)
|
|
goto out;
|
|
if (val1 && val1_len) {
|
|
rc = crypto_shash_update(desc, val1, val1_len);
|
|
if (rc < 0)
|
|
goto out;
|
|
}
|
|
if (val2 && val2_len) {
|
|
rc = crypto_shash_update(desc, val2, val2_len);
|
|
if (rc < 0)
|
|
goto out;
|
|
}
|
|
if (val3 && val3_len) {
|
|
rc = crypto_shash_update(desc, val3, val3_len);
|
|
if (rc < 0)
|
|
goto out;
|
|
}
|
|
rc = crypto_shash_final(desc, digest);
|
|
|
|
out:
|
|
if (rc) {
|
|
kfree(digest);
|
|
digest = NULL;
|
|
}
|
|
kfree(desc);
|
|
crypto_free_shash(tfm);
|
|
return digest;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_compute_dhkey - Calculate the dhkey
|
|
* @base: base value
|
|
* @base_len: base value length
|
|
* @expo: exponent value
|
|
* @expo_len: exponent value length
|
|
* @mod: modulo value
|
|
* @mod_len: modulo value length
|
|
* @key: resulting key value
|
|
* @key_len: resulting key value length
|
|
*
|
|
* Return value
|
|
* Success / Failure
|
|
**/
|
|
static int
|
|
lpfc_auth_compute_dhkey(uint8_t *base,
|
|
uint32_t base_len,
|
|
uint8_t *expo,
|
|
uint32_t expo_len,
|
|
uint8_t *mod,
|
|
uint32_t mod_len,
|
|
uint8_t *key,
|
|
uint32_t key_len)
|
|
{
|
|
int i, rc;
|
|
uint8_t *temp = NULL;
|
|
uint32_t temp_len = 0;
|
|
MPI k, b, e, m;
|
|
|
|
/*
|
|
* key = ((base)^expo modulo mod)
|
|
*/
|
|
b = mpi_read_raw_data(base, base_len);
|
|
e = mpi_read_raw_data(expo, expo_len);
|
|
m = mpi_read_raw_data(mod, mod_len);
|
|
|
|
k = mpi_alloc(mpi_get_nlimbs(m) * 2);
|
|
|
|
rc = mpi_powm(k, b, e, m);
|
|
if (!rc) {
|
|
temp = mpi_get_buffer(k, &temp_len, NULL);
|
|
if (!temp) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (key_len < temp_len) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* Pad the key with leading zeros */
|
|
memset(key, 0, key_len);
|
|
i = key_len - temp_len;
|
|
memcpy(key + i, temp, temp_len);
|
|
}
|
|
|
|
out:
|
|
mpi_free(b);
|
|
mpi_free(e);
|
|
mpi_free(m);
|
|
mpi_free(k);
|
|
kfree(temp);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_cmpl_negotiate - Completion callback routine
|
|
* @phba: pointer to lpfc hba data structure.
|
|
* @cmdiocb: pointer to lpfc command iocb data structure.
|
|
* @rspiocb: pointer to lpfc response iocb data structure.
|
|
*
|
|
**/
|
|
static void
|
|
lpfc_auth_cmpl_negotiate(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
|
|
struct lpfc_iocbq *rspiocb)
|
|
{
|
|
struct lpfc_vport *vport = phba->pport;
|
|
IOCB_t *irsp = &rspiocb->iocb;
|
|
struct lpfc_nodelist *ndlp = cmdiocb->context1;
|
|
|
|
if (irsp->ulpStatus)
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3043 ELS_AUTH cmpl, ulpstatus=x%x/%x\n",
|
|
irsp->ulpStatus, irsp->un.ulpWord[4]);
|
|
lpfc_els_free_iocb(phba, cmdiocb);
|
|
|
|
if ((irsp->ulpStatus == IOSTAT_LS_RJT) &&
|
|
ndlp && NLP_CHK_NODE_ACT(ndlp) &&
|
|
(ndlp->dhc_cfg.auth_mode == LPFC_AUTH_MODE_ACTIVE)) {
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_UNKNOWN;
|
|
/* logout should be sent on LS REJECT */
|
|
lpfc_issue_els_logo(vport, ndlp, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_issue_negotiate - Issue AUTH_NEGOTIATE command
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* This routine issues AUTH_NEGOTIATE ELS command to the remote
|
|
* port as the authentication initiator.
|
|
*
|
|
* Return codes
|
|
* 0 - Success
|
|
* 1 - Failure
|
|
**/
|
|
static int
|
|
lpfc_auth_issue_negotiate(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct lpfc_iocbq *elsiocb;
|
|
uint8_t *pcmd;
|
|
struct lpfc_auth_hdr *hdr;
|
|
struct lpfc_auth_negotiate_cmn *negotiate;
|
|
struct lpfc_dhchap_param_cmn *dhchap_param;
|
|
struct lpfc_auth_param_hdr *param_hdr;
|
|
uint32_t param_len;
|
|
uint32_t *hash;
|
|
uint32_t *dh_group;
|
|
uint16_t cmdsize;
|
|
uint8_t i, dh_grp_cnt;
|
|
int rc;
|
|
|
|
/* Support 2 Hash Functions and 5 DH-Groups */
|
|
cmdsize = sizeof(struct lpfc_auth_hdr) +
|
|
sizeof(struct lpfc_auth_negotiate_cmn) +
|
|
sizeof(struct lpfc_dhchap_param_cmn) +
|
|
sizeof(struct lpfc_auth_param_hdr) + 2 * sizeof(uint32_t) +
|
|
sizeof(struct lpfc_auth_param_hdr) + 5 * sizeof(uint32_t);
|
|
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
|
|
ndlp->nlp_DID, ELS_CMD_AUTH);
|
|
|
|
if (!elsiocb)
|
|
return 1;
|
|
|
|
/* Save the auth details for later use */
|
|
if (ndlp->dhc_cfg.state != LPFC_AUTH_SUCCESS)
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_UNKNOWN;
|
|
ndlp->dhc_cfg.msg = LPFC_AUTH_NEGOTIATE;
|
|
|
|
pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
|
|
ndlp->dhc_cfg.tran_id += 1;
|
|
|
|
/* Build the AUTH_NEGOTIATE header */
|
|
hdr = (struct lpfc_auth_hdr *)pcmd;
|
|
hdr->auth_els_code = (uint8_t) ELS_CMD_AUTH;
|
|
hdr->auth_els_flags = 0;
|
|
hdr->auth_msg_code = AUTH_NEGOTIATE;
|
|
hdr->protocol_ver = AUTH_PROTOCOL_VER_1;
|
|
hdr->msg_len = be32_to_cpu(cmdsize - sizeof(struct lpfc_auth_hdr));
|
|
hdr->tran_id = be32_to_cpu(ndlp->dhc_cfg.tran_id);
|
|
pcmd += sizeof(struct lpfc_auth_hdr);
|
|
|
|
/* Set up AUTH_NEGOTIATE common data */
|
|
negotiate = (struct lpfc_auth_negotiate_cmn *)pcmd;
|
|
negotiate->name_tag = be16_to_cpu(AUTH_NAME_TAG);
|
|
negotiate->name_len = be16_to_cpu(sizeof(struct lpfc_name));
|
|
memcpy(&negotiate->port_name, &vport->fc_portname,
|
|
sizeof(struct lpfc_name));
|
|
negotiate->num_protocol = be32_to_cpu(1); /* Only support DHCHAP */
|
|
pcmd += sizeof(struct lpfc_auth_negotiate_cmn);
|
|
|
|
/* Set up DH-CHAP parameters common data */
|
|
dh_grp_cnt = ndlp->dhc_cfg.dh_grp_cnt;
|
|
if (dh_grp_cnt > 5)
|
|
dh_grp_cnt = 5;
|
|
dhchap_param = (struct lpfc_dhchap_param_cmn *)pcmd;
|
|
param_len = sizeof(uint32_t) +
|
|
sizeof(struct lpfc_auth_param_hdr) + 2 * sizeof(uint32_t) +
|
|
sizeof(struct lpfc_auth_param_hdr) +
|
|
dh_grp_cnt * sizeof(uint32_t);
|
|
dhchap_param->param_len = be32_to_cpu(param_len);
|
|
dhchap_param->protocol_id = be32_to_cpu(PROTOCOL_DHCHAP);
|
|
pcmd += sizeof(struct lpfc_dhchap_param_cmn);
|
|
|
|
/* Set up Hash Functions */
|
|
param_hdr = (struct lpfc_auth_param_hdr *)pcmd;
|
|
param_hdr->tag = be16_to_cpu(DHCHAP_TAG_HASHLIST);
|
|
param_hdr->wcnt = be16_to_cpu(2); /* 2 HASH Functions */
|
|
pcmd += sizeof(struct lpfc_auth_param_hdr);
|
|
hash = (uint32_t *)pcmd;
|
|
hash[0] = be32_to_cpu(ndlp->dhc_cfg.hash_pri[0]);
|
|
hash[1] = be32_to_cpu(ndlp->dhc_cfg.hash_pri[1]);
|
|
pcmd += 2 * sizeof(uint32_t);
|
|
|
|
/* Setup DH Groups */
|
|
param_hdr = (struct lpfc_auth_param_hdr *)pcmd;
|
|
param_hdr->tag = be16_to_cpu(DHCHAP_TAG_GRP_IDLIST);
|
|
param_hdr->wcnt = be16_to_cpu(dh_grp_cnt); /* max 5 DH Groups */
|
|
pcmd += sizeof(struct lpfc_auth_param_hdr);
|
|
dh_group = (uint32_t *)pcmd;
|
|
for (i = 0; i < dh_grp_cnt; i++)
|
|
dh_group[i] = be32_to_cpu(ndlp->dhc_cfg.dh_grp_pri[i]);
|
|
|
|
elsiocb->iocb_cmpl = lpfc_auth_cmpl_negotiate;
|
|
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
|
|
|
|
if (rc == IOCB_ERROR) {
|
|
lpfc_els_free_iocb(phba, elsiocb);
|
|
return 1;
|
|
}
|
|
/* Set up authentication timer */
|
|
if (ndlp->dhc_cfg.auth_tmo)
|
|
lpfc_auth_start_tmr(ndlp, ndlp->dhc_cfg.auth_tmo * 1000);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_issue_dhchap_challenge - Issue DHCHAP_CHALLENGE command
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* This routine issues DHCHAP_CHALLENGE ELS command to the remote
|
|
* port as the authentication responder.
|
|
*
|
|
* Return codes
|
|
* 0 - Success
|
|
* 1 - Failure
|
|
**/
|
|
static int
|
|
lpfc_auth_issue_dhchap_challenge(struct lpfc_vport *vport,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct lpfc_iocbq *elsiocb;
|
|
struct lpfc_auth_hdr *hdr;
|
|
struct lpfc_auth_challenge *challenge;
|
|
uint8_t *pcmd;
|
|
uint16_t cmdsize;
|
|
uint32_t chal_len;
|
|
uint32_t hash_id = ndlp->dhc_cfg.hash_id;
|
|
uint32_t dh_grp_id = ndlp->dhc_cfg.dh_grp_id;
|
|
uint32_t dh_val_len = dh_group_array[dh_grp_id].length;
|
|
uint8_t nonce[16], key[256];
|
|
uint8_t *rand_num = NULL;
|
|
uint32_t key_len;
|
|
uint8_t key_base = 2;
|
|
int rc = 0;
|
|
|
|
switch (hash_id) {
|
|
case HASH_MD5:
|
|
chal_len = MD5_CHAL_LEN;
|
|
break;
|
|
case HASH_SHA1:
|
|
chal_len = SHA1_CHAL_LEN;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
rand_num = kzalloc(chal_len, GFP_KERNEL);
|
|
if (!rand_num) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
get_random_bytes(rand_num, chal_len);
|
|
|
|
cmdsize = sizeof(struct lpfc_auth_hdr) +
|
|
sizeof(struct lpfc_auth_challenge) +
|
|
chal_len + sizeof(uint32_t) + dh_val_len;
|
|
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
|
|
ndlp->nlp_DID, ELS_CMD_AUTH);
|
|
|
|
if (!elsiocb) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
|
|
|
|
/* Build the DHCHAP_REPLY payload */
|
|
hdr = (struct lpfc_auth_hdr *)pcmd;
|
|
hdr->auth_els_code = (uint8_t) ELS_CMD_AUTH;
|
|
hdr->auth_els_flags = 0;
|
|
hdr->auth_msg_code = DHCHAP_CHALLENGE;
|
|
hdr->protocol_ver = AUTH_PROTOCOL_VER_1;
|
|
hdr->msg_len = be32_to_cpu(cmdsize - sizeof(struct lpfc_auth_hdr));
|
|
hdr->tran_id = be32_to_cpu(ndlp->dhc_cfg.tran_id);
|
|
pcmd += sizeof(struct lpfc_auth_hdr);
|
|
|
|
/* Set up DHCHAP_CHALLENGE payload */
|
|
challenge = (struct lpfc_auth_challenge *)pcmd;
|
|
challenge->name_tag = be16_to_cpu(AUTH_NAME_TAG);
|
|
challenge->name_len = be16_to_cpu(sizeof(struct lpfc_name));
|
|
memcpy(&challenge->port_name, &vport->fc_portname,
|
|
sizeof(struct lpfc_name));
|
|
challenge->hash_id = be32_to_cpu(ndlp->dhc_cfg.hash_id);
|
|
challenge->dh_grp_id = be32_to_cpu(ndlp->dhc_cfg.dh_grp_id);
|
|
challenge->chal_len = be32_to_cpu(chal_len);
|
|
pcmd += sizeof(struct lpfc_auth_challenge);
|
|
memcpy(pcmd, rand_num, chal_len);
|
|
pcmd += chal_len;
|
|
*((uint32_t *)pcmd) = be32_to_cpu(dh_val_len);
|
|
pcmd += sizeof(uint32_t);
|
|
|
|
/* Save the challenge */
|
|
memcpy(ndlp->dhc_cfg.rand_num, rand_num, chal_len);
|
|
|
|
if (dh_grp_id) {
|
|
key_len = dh_group_array[dh_grp_id].length;
|
|
get_random_bytes(nonce, sizeof(nonce));
|
|
|
|
rc = lpfc_auth_compute_dhkey(&key_base, sizeof(key_base),
|
|
nonce, sizeof(nonce),
|
|
dh_group_array[dh_grp_id].value,
|
|
dh_group_array[dh_grp_id].length,
|
|
key, key_len);
|
|
if (rc)
|
|
goto out;
|
|
|
|
memcpy(pcmd, key, key_len);
|
|
|
|
/* Save the nonce */
|
|
memcpy(ndlp->dhc_cfg.nonce, nonce, sizeof(nonce));
|
|
}
|
|
|
|
elsiocb->iocb_cmpl = lpfc_auth_cmpl_negotiate;
|
|
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
|
|
if (rc == IOCB_ERROR)
|
|
lpfc_els_free_iocb(phba, elsiocb);
|
|
|
|
out:
|
|
kfree(rand_num);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_issue_dhchap_reply - Issue DHCHAP_REPLY cmd
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_issue_dhchap_reply(struct lpfc_vport *vport,
|
|
struct lpfc_nodelist *ndlp,
|
|
uint32_t tran_id, uint32_t dh_grp_id,
|
|
uint32_t hash_id, uint8_t *hash_value,
|
|
uint8_t *dh_value, uint32_t dh_val_len)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct lpfc_iocbq *elsiocb;
|
|
struct lpfc_auth_hdr *hdr;
|
|
uint8_t *pcmd;
|
|
uint16_t cmdsize;
|
|
uint32_t chal_len = 0;
|
|
uint8_t *rand_num = NULL;
|
|
int rc = 0;
|
|
|
|
switch (hash_id) {
|
|
case HASH_MD5:
|
|
/* Check for bi_directional support */
|
|
if (ndlp->dhc_cfg.bidirectional)
|
|
chal_len = MD5_CHAL_LEN;
|
|
cmdsize = sizeof(struct lpfc_auth_hdr) +
|
|
sizeof(uint32_t) + MD5_CHAL_LEN +
|
|
sizeof(uint32_t) + dh_val_len +
|
|
sizeof(uint32_t) + chal_len;
|
|
break;
|
|
case HASH_SHA1:
|
|
/* Check for bi_directional support */
|
|
if (ndlp->dhc_cfg.bidirectional)
|
|
chal_len = SHA1_CHAL_LEN;
|
|
cmdsize = sizeof(struct lpfc_auth_hdr) +
|
|
sizeof(uint32_t) + SHA1_CHAL_LEN +
|
|
sizeof(uint32_t) + dh_val_len +
|
|
sizeof(uint32_t) + chal_len;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
ndlp->dhc_cfg.msg = LPFC_DHCHAP_REPLY;
|
|
|
|
/* Check for bi_directional support before allocating rand_num */
|
|
if (ndlp->dhc_cfg.bidirectional) {
|
|
rand_num = kzalloc(chal_len, GFP_KERNEL);
|
|
if (!rand_num) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
get_random_bytes(rand_num, chal_len);
|
|
}
|
|
|
|
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
|
|
ndlp->nlp_DID, ELS_CMD_AUTH);
|
|
|
|
if (!elsiocb) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
|
|
|
|
/* Build the DHCHAP_REPLY payload */
|
|
hdr = (struct lpfc_auth_hdr *)pcmd;
|
|
hdr->auth_els_code = (uint8_t) ELS_CMD_AUTH;
|
|
hdr->auth_els_flags = 0;
|
|
hdr->auth_msg_code = DHCHAP_REPLY;
|
|
hdr->protocol_ver = AUTH_PROTOCOL_VER_1;
|
|
hdr->msg_len = be32_to_cpu(cmdsize - sizeof(struct lpfc_auth_hdr));
|
|
hdr->tran_id = be32_to_cpu(tran_id);
|
|
|
|
pcmd += sizeof(struct lpfc_auth_hdr);
|
|
if (hash_id == HASH_MD5) {
|
|
*((uint32_t *)pcmd) = be32_to_cpu(MD5_CHAL_LEN);
|
|
pcmd += sizeof(uint32_t);
|
|
memcpy(pcmd, hash_value, MD5_CHAL_LEN);
|
|
pcmd += MD5_CHAL_LEN;
|
|
} else if (hash_id == HASH_SHA1) {
|
|
*((uint32_t *)pcmd) = be32_to_cpu(SHA1_CHAL_LEN);
|
|
pcmd += sizeof(uint32_t);
|
|
memcpy(pcmd, hash_value, SHA1_CHAL_LEN);
|
|
pcmd += SHA1_CHAL_LEN;
|
|
}
|
|
|
|
*((uint32_t *)pcmd) = be32_to_cpu(dh_val_len);
|
|
pcmd += sizeof(uint32_t);
|
|
if (dh_val_len) {
|
|
memcpy(pcmd, dh_value, dh_val_len);
|
|
pcmd += dh_val_len;
|
|
}
|
|
*((uint32_t *)pcmd) = be32_to_cpu(chal_len);
|
|
if (chal_len) {
|
|
pcmd += sizeof(uint32_t);
|
|
memcpy(pcmd, rand_num, chal_len);
|
|
}
|
|
|
|
/* Save the auth details for later use */
|
|
ndlp->dhc_cfg.hash_id = hash_id;
|
|
ndlp->dhc_cfg.tran_id = tran_id;
|
|
ndlp->dhc_cfg.dh_grp_id = dh_grp_id;
|
|
memcpy(ndlp->dhc_cfg.rand_num, rand_num, chal_len);
|
|
|
|
elsiocb->iocb_cmpl = lpfc_auth_cmpl_negotiate;
|
|
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
|
|
if (rc == IOCB_ERROR)
|
|
lpfc_els_free_iocb(phba, elsiocb);
|
|
|
|
/* Set up authentication timer */
|
|
if (ndlp->dhc_cfg.auth_tmo)
|
|
lpfc_auth_start_tmr(ndlp, ndlp->dhc_cfg.auth_tmo * 1000);
|
|
out:
|
|
kfree(rand_num);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_issue_dhchap_success - Issue DHCHAP_SUCCESS cmd
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_issue_dhchap_success(struct lpfc_vport *vport,
|
|
struct lpfc_nodelist *ndlp,
|
|
uint8_t *hash_value)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct lpfc_iocbq *elsiocb;
|
|
struct lpfc_auth_hdr *hdr;
|
|
uint8_t *pcmd;
|
|
uint16_t cmdsize;
|
|
uint32_t chal_len = 0;
|
|
int rc = 0;
|
|
|
|
if (ndlp->dhc_cfg.hash_id == HASH_MD5)
|
|
chal_len = MD5_CHAL_LEN;
|
|
else if (ndlp->dhc_cfg.hash_id == HASH_SHA1)
|
|
chal_len = SHA1_CHAL_LEN;
|
|
|
|
cmdsize = sizeof(struct lpfc_auth_hdr) + sizeof(uint32_t);
|
|
if (hash_value)
|
|
cmdsize += chal_len;
|
|
|
|
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
|
|
ndlp->nlp_DID, ELS_CMD_AUTH);
|
|
|
|
if (!elsiocb) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
|
|
|
|
/* Build the DHCHAP_SUCCESS payload */
|
|
hdr = (struct lpfc_auth_hdr *)pcmd;
|
|
hdr->auth_els_code = (uint8_t) ELS_CMD_AUTH;
|
|
hdr->auth_els_flags = 0;
|
|
hdr->auth_msg_code = DHCHAP_SUCCESS;
|
|
hdr->protocol_ver = AUTH_PROTOCOL_VER_1;
|
|
hdr->msg_len = be32_to_cpu(cmdsize - sizeof(struct lpfc_auth_hdr));
|
|
hdr->tran_id = be32_to_cpu(ndlp->dhc_cfg.tran_id);
|
|
pcmd += sizeof(struct lpfc_auth_hdr);
|
|
|
|
if (hash_value) {
|
|
*((uint32_t *)pcmd) = be32_to_cpu(chal_len);
|
|
pcmd += sizeof(uint32_t);
|
|
memcpy(pcmd, hash_value, chal_len);
|
|
} else {
|
|
*((uint32_t *)pcmd) = 0;
|
|
}
|
|
|
|
elsiocb->iocb_cmpl = lpfc_auth_cmpl_negotiate;
|
|
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
|
|
if (rc == IOCB_ERROR)
|
|
lpfc_els_free_iocb(phba, elsiocb);
|
|
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_SUCCESS;
|
|
ndlp->dhc_cfg.msg = LPFC_DHCHAP_SUCCESS_REPLY;
|
|
ndlp->dhc_cfg.direction = AUTH_DIRECTION_LOCAL;
|
|
ndlp->dhc_cfg.last_auth = jiffies;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_issue_reject - Issue AUTH_REJECT cmd
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
* @tran_id: transaction id
|
|
* @rsn_code: reason code
|
|
* @rsn_expl: reason code explanation
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_issue_reject(struct lpfc_vport *vport,
|
|
struct lpfc_nodelist *ndlp,
|
|
uint32_t tran_id,
|
|
uint8_t rsn_code,
|
|
uint8_t rsn_expl)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct lpfc_iocbq *elsiocb;
|
|
struct lpfc_auth_hdr *hdr;
|
|
struct lpfc_auth_reject *rjt;
|
|
uint8_t *pcmd;
|
|
uint16_t cmdsize;
|
|
int rc = 0;
|
|
|
|
cmdsize = sizeof(struct lpfc_auth_hdr) + sizeof(uint32_t);
|
|
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, 0, ndlp,
|
|
ndlp->nlp_DID, ELS_CMD_AUTH);
|
|
|
|
if (!elsiocb) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
pcmd = (uint8_t *)(((struct lpfc_dmabuf *)elsiocb->context2)->virt);
|
|
|
|
/* Build the AUTH_REJECT payload */
|
|
hdr = (struct lpfc_auth_hdr *)pcmd;
|
|
hdr->auth_els_code = (uint8_t) ELS_CMD_AUTH;
|
|
hdr->auth_els_flags = 0;
|
|
hdr->auth_msg_code = AUTH_REJECT;
|
|
hdr->protocol_ver = AUTH_PROTOCOL_VER_1;
|
|
hdr->msg_len = be32_to_cpu(cmdsize - sizeof(struct lpfc_auth_hdr));
|
|
hdr->tran_id = be32_to_cpu(tran_id);
|
|
|
|
rjt = (struct lpfc_auth_reject *)(pcmd + sizeof(struct lpfc_auth_hdr));
|
|
rjt->rsn_code = rsn_code;
|
|
rjt->rsn_expl = rsn_expl;
|
|
|
|
elsiocb->iocb_cmpl = lpfc_auth_cmpl_negotiate;
|
|
rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, elsiocb, 0);
|
|
if (rc == IOCB_ERROR)
|
|
lpfc_els_free_iocb(phba, elsiocb);
|
|
|
|
ndlp->dhc_cfg.msg = LPFC_AUTH_REJECT;
|
|
ndlp->dhc_cfg.direction = AUTH_DIRECTION_NONE;
|
|
out:
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_handle_reject - Handle AUTH Reject for a vport
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @payload: pointer to AUTH Reject cmd payload.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_handle_reject(struct lpfc_vport *vport, uint8_t *payload,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_auth_reject *rjt;
|
|
uint8_t rsn_code;
|
|
uint8_t rsn_expl;
|
|
|
|
rjt = (struct lpfc_auth_reject *)
|
|
(payload + sizeof(struct lpfc_auth_hdr));
|
|
rsn_code = rjt->rsn_code;
|
|
rsn_expl = rjt->rsn_expl;
|
|
|
|
if (rsn_code == AUTHRJT_LOGICAL_ERR &&
|
|
rsn_expl == AUTHEXPL_RESTART_AUTH) {
|
|
/* Restart authentication */
|
|
lpfc_auth_issue_negotiate(vport, ndlp);
|
|
} else {
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_FAIL;
|
|
ndlp->dhc_cfg.msg = LPFC_AUTH_REJECT;
|
|
}
|
|
ndlp->dhc_cfg.direction = AUTH_DIRECTION_NONE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_handle_negotiate - Handle AUTH Negotiate for a vport
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @payload: pointer to AUTH Negotiate cmd payload.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_handle_negotiate(struct lpfc_vport *vport, uint8_t *payload,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_auth_hdr *hdr;
|
|
struct lpfc_auth_negotiate_cmn *negotiate;
|
|
struct lpfc_dhchap_param_cmn *dhchap_param;
|
|
struct lpfc_auth_param_hdr *param_hdr;
|
|
uint32_t protocol_id, hash = 0, dh_group = 0;
|
|
uint8_t found = 0;
|
|
int i;
|
|
int rc = 0;
|
|
|
|
hdr = (struct lpfc_auth_hdr *)payload;
|
|
|
|
/* Nx_Port with the higher port_name shall remain the initiator */
|
|
if (memcmp(&vport->fc_portname, &ndlp->nlp_portname,
|
|
sizeof(struct lpfc_name)) > 0) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_LOGICAL_ERR,
|
|
AUTHEXPL_AUTH_STARTED);
|
|
return 0;
|
|
}
|
|
|
|
/* Sanity check the AUTH message header */
|
|
if (hdr->protocol_ver != AUTH_PROTOCOL_VER_1) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* Sanity check the Negotiate payload */
|
|
payload += sizeof(struct lpfc_auth_hdr);
|
|
negotiate = (struct lpfc_auth_negotiate_cmn *)payload;
|
|
if (be16_to_cpu(negotiate->name_tag) != AUTH_NAME_TAG ||
|
|
be16_to_cpu(negotiate->name_len) != sizeof(struct lpfc_name) ||
|
|
be32_to_cpu(negotiate->num_protocol) == 0) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
payload += sizeof(struct lpfc_auth_negotiate_cmn);
|
|
|
|
/* Scan the payload for Protocol support */
|
|
for (i = 0; i < be32_to_cpu(negotiate->num_protocol); i++) {
|
|
dhchap_param = (struct lpfc_dhchap_param_cmn *)payload;
|
|
protocol_id = be32_to_cpu(dhchap_param->protocol_id);
|
|
|
|
if (protocol_id == PROTOCOL_DHCHAP) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
payload += be32_to_cpu(dhchap_param->param_len);
|
|
}
|
|
|
|
if (!found) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_LOGICAL_ERR,
|
|
AUTHEXPL_MECH_UNUSABLE);
|
|
return 0;
|
|
}
|
|
|
|
payload += sizeof(struct lpfc_dhchap_param_cmn);
|
|
param_hdr = (struct lpfc_auth_param_hdr *)payload;
|
|
payload += sizeof(struct lpfc_auth_param_hdr);
|
|
|
|
/* Scan the payload for Hash support */
|
|
found = 0;
|
|
for (i = 0; i < be32_to_cpu(param_hdr->wcnt); i++) {
|
|
hash = be32_to_cpu(*((uint32_t *)payload));
|
|
|
|
if (hash == HASH_MD5 || hash == HASH_SHA1) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
payload += sizeof(uint32_t);
|
|
}
|
|
|
|
if (!found) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_LOGICAL_ERR,
|
|
AUTHEXPL_HASH_UNUSABLE);
|
|
return 0;
|
|
}
|
|
|
|
param_hdr = (struct lpfc_auth_param_hdr *)payload;
|
|
payload += sizeof(struct lpfc_auth_param_hdr);
|
|
|
|
/* Scan the payload for DH Group support */
|
|
found = 0;
|
|
for (i = 0; i < be32_to_cpu(param_hdr->wcnt); i++) {
|
|
dh_group = be32_to_cpu(*((uint32_t *)payload));
|
|
|
|
if (dh_group >= DH_GROUP_NULL && dh_group <= DH_GROUP_2048) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
payload += sizeof(uint32_t);
|
|
}
|
|
|
|
if (!found) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_LOGICAL_ERR,
|
|
AUTHEXPL_DHGRP_UNUSABLE);
|
|
return 0;
|
|
}
|
|
|
|
/* Save the Authentication settings for this node */
|
|
ndlp->dhc_cfg.tran_id = be32_to_cpu(hdr->tran_id);
|
|
ndlp->dhc_cfg.hash_id = hash;
|
|
ndlp->dhc_cfg.dh_grp_id = dh_group;
|
|
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"6041 AUTH_NEGOTIATE "
|
|
"tran_id: x%x hash_id: x%x dh_grp: x%x\n",
|
|
be32_to_cpu(hdr->tran_id), hash, dh_group);
|
|
|
|
/* Issue a DH-CHAP Challenge to the initiator */
|
|
lpfc_auth_issue_dhchap_challenge(vport, ndlp);
|
|
return 0;
|
|
|
|
out:
|
|
if (rc)
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_handle_dhchap_chal - Handle DHCHAP Challenge for a vport
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @payload: pointer to DHCHAP Challenge cmd payload.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_handle_dhchap_chal(struct lpfc_vport *vport, uint8_t *payload,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_auth_hdr *hdr;
|
|
struct lpfc_auth_challenge *chal;
|
|
uint8_t *hash_value = NULL;
|
|
uint32_t dh_val_len = 0;
|
|
uint8_t *dh_value = NULL;
|
|
uint8_t *aug_chal = NULL;
|
|
uint8_t challenge[20];
|
|
uint8_t tran_id;
|
|
uint32_t hash_id;
|
|
uint32_t chal_len;
|
|
uint32_t dh_grp_id;
|
|
uint8_t nonce[16], key[256], rsp_key[256];
|
|
uint32_t key_len;
|
|
uint32_t rsp_key_len;
|
|
uint8_t rsp_key_base = 2;
|
|
char *passwd = ndlp->dhc_cfg.local_passwd;
|
|
uint32_t passwd_len = ndlp->dhc_cfg.local_passwd_len;
|
|
int rc;
|
|
|
|
hdr = (struct lpfc_auth_hdr *)payload;
|
|
/* Sanity check the AUTH message header */
|
|
if (hdr->protocol_ver != AUTH_PROTOCOL_VER_1 ||
|
|
be32_to_cpu(hdr->tran_id) != ndlp->dhc_cfg.tran_id) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
payload += sizeof(struct lpfc_auth_hdr);
|
|
chal = (struct lpfc_auth_challenge *)payload;
|
|
|
|
tran_id = be32_to_cpu(hdr->tran_id) & 0xff;
|
|
hash_id = be32_to_cpu(chal->hash_id);
|
|
chal_len = be32_to_cpu(chal->chal_len);
|
|
dh_grp_id = be32_to_cpu(chal->dh_grp_id);
|
|
|
|
payload += sizeof(struct lpfc_auth_challenge);
|
|
|
|
ndlp->dhc_cfg.msg = LPFC_DHCHAP_CHALLENGE;
|
|
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3044 AUTH_CHALLENGE "
|
|
"tran_id:x%x hash_id:x%x chal_len=x%x dh_grp=x%x\n",
|
|
tran_id, hash_id, chal_len, dh_grp_id);
|
|
|
|
if (hash_id == HASH_MD5 && chal_len == MD5_CHAL_LEN) {
|
|
memcpy(challenge, payload, MD5_CHAL_LEN);
|
|
} else if (hash_id == HASH_SHA1 && chal_len == SHA1_CHAL_LEN) {
|
|
memcpy(challenge, payload, SHA1_CHAL_LEN);
|
|
} else {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
payload += chal_len;
|
|
|
|
switch (dh_grp_id) {
|
|
case DH_GROUP_2048:
|
|
case DH_GROUP_1536:
|
|
case DH_GROUP_1280:
|
|
case DH_GROUP_1024:
|
|
key_len = dh_group_array[dh_grp_id].length;
|
|
rsp_key_len = dh_group_array[dh_grp_id].length;
|
|
/* Save the dh value */
|
|
dh_val_len = be32_to_cpu(*((uint32_t *)payload));
|
|
payload += sizeof(uint32_t);
|
|
|
|
dh_value = kzalloc(dh_val_len, GFP_KERNEL);
|
|
if (!dh_value)
|
|
goto out;
|
|
memcpy(dh_value, payload, dh_val_len);
|
|
|
|
/* Compute Ephemeral DH Key */
|
|
get_random_bytes(nonce, sizeof(nonce));
|
|
|
|
rc = lpfc_auth_compute_dhkey(dh_value, dh_val_len, nonce,
|
|
sizeof(nonce),
|
|
dh_group_array[dh_grp_id].value,
|
|
dh_group_array[dh_grp_id].length,
|
|
key, key_len);
|
|
if (rc)
|
|
break;
|
|
|
|
/* Save this key, (((g)^x)^y mod p) */
|
|
memcpy(ndlp->dhc_cfg.dhkey, key, key_len);
|
|
ndlp->dhc_cfg.dhkey_len = key_len;
|
|
|
|
rc = lpfc_auth_compute_dhkey(&rsp_key_base,
|
|
sizeof(rsp_key_base),
|
|
nonce, sizeof(nonce),
|
|
dh_group_array[dh_grp_id].value,
|
|
dh_group_array[dh_grp_id].length,
|
|
rsp_key, rsp_key_len);
|
|
if (rc)
|
|
break;
|
|
|
|
/*
|
|
* Compute the augmented challenge
|
|
* aug_chal = hash(Challenge || Ephemeral DH Key)
|
|
*/
|
|
aug_chal = lpfc_auth_compute_hash(hash_id, challenge, chal_len,
|
|
key, key_len, 0, 0);
|
|
if (!aug_chal) {
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3045 Null aug_chal\n");
|
|
break;
|
|
}
|
|
|
|
/* Calculate the hash response */
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
passwd,
|
|
passwd_len,
|
|
aug_chal,
|
|
chal_len);
|
|
break;
|
|
case DH_GROUP_NULL:
|
|
/* Calculate the hash response */
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
passwd,
|
|
passwd_len,
|
|
challenge,
|
|
chal_len);
|
|
rsp_key_len = 0;
|
|
break;
|
|
default:
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE,
|
|
AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
if (!hash_value) {
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3046 Null hash_value\n");
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_AUTH_FAILED);
|
|
goto out;
|
|
}
|
|
|
|
/* Issue DHCHAP reply */
|
|
lpfc_auth_issue_dhchap_reply(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
dh_grp_id, hash_id, hash_value, rsp_key,
|
|
rsp_key_len);
|
|
|
|
out:
|
|
kfree(dh_value);
|
|
kfree(aug_chal);
|
|
kfree(hash_value);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_handle_dhchap_reply - Handle DHCHAP Reply for a vport
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @payload: pointer to DHCHAP Challenge cmd payload.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_handle_dhchap_reply(struct lpfc_vport *vport, uint8_t *payload,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_auth_hdr *hdr;
|
|
uint8_t tran_id = ndlp->dhc_cfg.tran_id & 0xff;
|
|
uint32_t hash_id = ndlp->dhc_cfg.hash_id;
|
|
uint32_t dh_grp_id = ndlp->dhc_cfg.dh_grp_id;
|
|
uint32_t dh_val_len;
|
|
uint8_t dh_value[256], key[256];
|
|
uint32_t resp_len;
|
|
uint32_t key_len = 0;
|
|
uint8_t resp_value[20];
|
|
uint8_t *aug_chal = NULL;
|
|
uint8_t *hash_value = NULL;
|
|
uint8_t challenge[20];
|
|
uint32_t chal_len;
|
|
char *local_passwd = ndlp->dhc_cfg.local_passwd;
|
|
uint32_t local_passwd_len = ndlp->dhc_cfg.local_passwd_len;
|
|
char *remote_passwd = ndlp->dhc_cfg.remote_passwd;
|
|
uint32_t remote_passwd_len = ndlp->dhc_cfg.remote_passwd_len;
|
|
int rc = 0;
|
|
|
|
hdr = (struct lpfc_auth_hdr *)payload;
|
|
/* Sanity check the AUTH message header */
|
|
if (hdr->protocol_ver != AUTH_PROTOCOL_VER_1 ||
|
|
be32_to_cpu(hdr->tran_id) != ndlp->dhc_cfg.tran_id) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE,
|
|
AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
payload += sizeof(struct lpfc_auth_hdr);
|
|
resp_len = be32_to_cpu(*(uint32_t *)payload);
|
|
payload += sizeof(uint32_t);
|
|
|
|
if (hash_id == HASH_MD5 && resp_len == MD5_CHAL_LEN) {
|
|
memcpy(resp_value, payload, MD5_CHAL_LEN);
|
|
} else if (hash_id == HASH_SHA1 && resp_len == SHA1_CHAL_LEN) {
|
|
memcpy(resp_value, payload, SHA1_CHAL_LEN);
|
|
} else {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
payload += resp_len;
|
|
dh_val_len = be32_to_cpu(*(uint32_t *)payload);
|
|
payload += sizeof(uint32_t);
|
|
|
|
if (dh_val_len != dh_group_array[dh_grp_id].length) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
memcpy(dh_value, payload, dh_val_len);
|
|
payload += dh_val_len;
|
|
|
|
/* Check for bidirectional challenge */
|
|
chal_len = be32_to_cpu(*(uint32_t *)payload);
|
|
payload += sizeof(uint32_t);
|
|
if (chal_len) {
|
|
if (chal_len != resp_len) {
|
|
lpfc_auth_issue_reject(vport, ndlp,
|
|
be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE,
|
|
AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
memcpy(challenge, payload, chal_len);
|
|
/* compare this challenge with what was issued */
|
|
if (!memcmp(challenge, ndlp->dhc_cfg.rand_num, chal_len)) {
|
|
lpfc_auth_issue_reject(vport, ndlp,
|
|
be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE,
|
|
AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"6043 AUTH_REPLY tran_id: x%x hash_id: x%x "
|
|
"resp_len: x%x chal_len: x%x dh_grp: x%x\n",
|
|
tran_id, hash_id, resp_len, chal_len, dh_grp_id);
|
|
|
|
switch (dh_grp_id) {
|
|
case DH_GROUP_2048:
|
|
case DH_GROUP_1536:
|
|
case DH_GROUP_1280:
|
|
case DH_GROUP_1024:
|
|
key_len = dh_group_array[dh_grp_id].length;
|
|
|
|
/* Compute Ephemeral DH Key */
|
|
rc = lpfc_auth_compute_dhkey(dh_value, dh_val_len,
|
|
ndlp->dhc_cfg.nonce,
|
|
sizeof(ndlp->dhc_cfg.nonce),
|
|
dh_group_array[dh_grp_id].value,
|
|
dh_group_array[dh_grp_id].length,
|
|
key, key_len);
|
|
if (rc)
|
|
break;
|
|
|
|
/*
|
|
* Compute the augmented challenge
|
|
* aug_chal = hash(Challenge || Ephemeral DH Key)
|
|
*/
|
|
aug_chal = lpfc_auth_compute_hash(hash_id,
|
|
ndlp->dhc_cfg.rand_num,
|
|
resp_len, key, key_len,
|
|
0, 0);
|
|
if (!aug_chal) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* Calculate the hash response */
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
remote_passwd,
|
|
remote_passwd_len,
|
|
aug_chal,
|
|
resp_len);
|
|
break;
|
|
case DH_GROUP_NULL:
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id, sizeof(tran_id),
|
|
remote_passwd,
|
|
remote_passwd_len,
|
|
ndlp->dhc_cfg.rand_num,
|
|
resp_len);
|
|
break;
|
|
default:
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
if (!hash_value) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* compare the hash with hash received from the remote port */
|
|
if (memcmp(hash_value, resp_value, resp_len)) {
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3047 Hash verification failed\n");
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
kfree(aug_chal);
|
|
aug_chal = NULL;
|
|
kfree(hash_value);
|
|
|
|
/* Compute the response hash */
|
|
if (dh_grp_id) {
|
|
/*
|
|
* Compute the augmented challenge
|
|
* aug_chal = hash(Challenge || Ephemeral DH Key)
|
|
*/
|
|
aug_chal = lpfc_auth_compute_hash(hash_id,
|
|
challenge, chal_len,
|
|
key, key_len, 0, 0);
|
|
|
|
if (!aug_chal) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
local_passwd,
|
|
local_passwd_len,
|
|
aug_chal,
|
|
chal_len);
|
|
} else {
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
local_passwd,
|
|
local_passwd_len,
|
|
challenge,
|
|
chal_len);
|
|
}
|
|
|
|
if (!hash_value) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
lpfc_auth_issue_dhchap_success(vport, ndlp, hash_value);
|
|
|
|
out:
|
|
kfree(aug_chal);
|
|
kfree(hash_value);
|
|
if (rc)
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_AUTH_FAILED);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_handle_dhchap_success - Handle DHCHAP Success for a vport
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @payload: pointer to DHCHAP Challenge cmd payload.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
static int
|
|
lpfc_auth_handle_dhchap_success(struct lpfc_vport *vport, uint8_t *payload,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_auth_hdr *hdr;
|
|
uint32_t resp_len;
|
|
uint8_t *hash_value = NULL;
|
|
uint8_t *resp_value = NULL;
|
|
uint8_t *aug_chal = NULL;
|
|
uint8_t tran_id;
|
|
uint32_t hash_id;
|
|
uint32_t dh_grp_id;
|
|
uint32_t chal_len = 0;
|
|
char *local_passwd = ndlp->dhc_cfg.local_passwd;
|
|
uint32_t local_passwd_len = ndlp->dhc_cfg.local_passwd_len;
|
|
char *remote_passwd = ndlp->dhc_cfg.remote_passwd;
|
|
uint32_t remote_passwd_len = ndlp->dhc_cfg.remote_passwd_len;
|
|
int rc = 0;
|
|
|
|
hdr = (struct lpfc_auth_hdr *)payload;
|
|
/* Sanity check the AUTH message header */
|
|
if (hdr->protocol_ver != AUTH_PROTOCOL_VER_1 ||
|
|
be32_to_cpu(hdr->tran_id) != ndlp->dhc_cfg.tran_id) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
payload += sizeof(struct lpfc_auth_hdr);
|
|
|
|
tran_id = be32_to_cpu(hdr->tran_id) & 0xff;
|
|
resp_len = be32_to_cpu(*((uint32_t *)payload));
|
|
resp_value = payload + sizeof(uint32_t);
|
|
|
|
hash_id = ndlp->dhc_cfg.hash_id;
|
|
if (hash_id == HASH_MD5) {
|
|
chal_len = MD5_CHAL_LEN;
|
|
} else if (hash_id == HASH_SHA1) {
|
|
chal_len = SHA1_CHAL_LEN;
|
|
} else {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
dh_grp_id = ndlp->dhc_cfg.dh_grp_id;
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3048 DHCHAP_SUCCESS resp_len = x%x, tran_id=x%x "
|
|
"hash_id=x%x\n",
|
|
resp_len, tran_id, hash_id);
|
|
if (resp_len) {
|
|
if (resp_len != chal_len) {
|
|
lpfc_auth_issue_reject(vport, ndlp,
|
|
be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE,
|
|
AUTHEXPL_BAD_PAYLOAD);
|
|
return 0;
|
|
}
|
|
|
|
if (dh_grp_id) {
|
|
/*
|
|
* Compute the augmented challenge
|
|
* aug_chal = hash(Challenge || Ephemeral DH Key)
|
|
*/
|
|
aug_chal =
|
|
lpfc_auth_compute_hash(hash_id,
|
|
ndlp->dhc_cfg.rand_num,
|
|
chal_len,
|
|
ndlp->dhc_cfg.dhkey,
|
|
ndlp->dhc_cfg.dhkey_len,
|
|
0, 0);
|
|
if (!aug_chal) {
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* Calculate the hash response */
|
|
hash_value = lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
remote_passwd,
|
|
remote_passwd_len,
|
|
aug_chal,
|
|
chal_len);
|
|
} else {
|
|
/* Calculate the hash response */
|
|
hash_value =
|
|
lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
remote_passwd,
|
|
remote_passwd_len,
|
|
ndlp->dhc_cfg.rand_num,
|
|
chal_len);
|
|
}
|
|
|
|
if (!hash_value) {
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3049 Null hash_value\n");
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* compare the hash with hash received from the remote port */
|
|
if (memcmp(hash_value, resp_value, resp_len)) {
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS | LOG_AUTH,
|
|
"3100 Hash verification failed\n");
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (!dh_grp_id) {
|
|
/* test for identical local and remote secrets */
|
|
hash_value =
|
|
lpfc_auth_compute_hash(hash_id,
|
|
&tran_id,
|
|
sizeof(tran_id),
|
|
local_passwd,
|
|
local_passwd_len,
|
|
ndlp->dhc_cfg.rand_num,
|
|
chal_len);
|
|
if (!hash_value) {
|
|
lpfc_printf_vlog(vport, KERN_INFO,
|
|
LOG_ELS | LOG_AUTH,
|
|
"3014 Null hash_value\n");
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
if (!memcmp(hash_value, resp_value, resp_len)) {
|
|
lpfc_printf_vlog(vport, KERN_INFO,
|
|
LOG_ELS | LOG_AUTH,
|
|
"3101 Local and Remote "
|
|
"secrets must differ.\n");
|
|
rc = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
lpfc_auth_issue_dhchap_success(vport, ndlp, NULL);
|
|
|
|
} else {
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_SUCCESS;
|
|
ndlp->dhc_cfg.last_auth = jiffies;
|
|
}
|
|
ndlp->dhc_cfg.msg = LPFC_DHCHAP_SUCCESS;
|
|
ndlp->dhc_cfg.direction |= AUTH_DIRECTION_REMOTE;
|
|
out:
|
|
kfree(aug_chal);
|
|
kfree(hash_value);
|
|
if (rc) {
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_AUTH_FAILED);
|
|
lpfc_issue_els_logo(vport, ndlp, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_handle_cmd - Handle unsolicited auth cmd for a vport
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @cmdiocb: pointer to lpfc command iocb data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* Return code
|
|
* 0 -
|
|
* 1 -
|
|
**/
|
|
int
|
|
lpfc_auth_handle_cmd(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb,
|
|
struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
uint8_t *pcmd = NULL;
|
|
uint32_t size, size1 = 0, size2 = 0;
|
|
struct lpfc_auth_hdr *hdr;
|
|
struct ulp_bde64 *pbde;
|
|
int rc = 0;
|
|
|
|
size = cmdiocb->iocb.unsli3.rcvsli3.acc_len;
|
|
pcmd = kzalloc(size, GFP_KERNEL);
|
|
if (!pcmd)
|
|
goto out;
|
|
|
|
size1 = cmdiocb->iocb.un.cont64[0].tus.f.bdeSize;
|
|
memcpy(pcmd, ((struct lpfc_dmabuf *)cmdiocb->context2)->virt, size1);
|
|
if (cmdiocb->iocb.ulpBdeCount == 2) {
|
|
pbde = (struct ulp_bde64 *)&cmdiocb->iocb.unsli3.sli3Words[4];
|
|
size2 = pbde->tus.f.bdeSize;
|
|
memcpy(pcmd + size1,
|
|
((struct lpfc_dmabuf *)cmdiocb->context3)->virt, size2);
|
|
}
|
|
|
|
lpfc_auth_cancel_tmr(ndlp);
|
|
hdr = (struct lpfc_auth_hdr *)pcmd;
|
|
|
|
switch (hdr->auth_msg_code) {
|
|
case AUTH_REJECT:
|
|
rc = lpfc_auth_handle_reject(vport, pcmd, ndlp);
|
|
break;
|
|
case AUTH_NEGOTIATE:
|
|
rc = lpfc_auth_handle_negotiate(vport, pcmd, ndlp);
|
|
break;
|
|
case AUTH_DONE:
|
|
/* Nothing to do */
|
|
break;
|
|
case DHCHAP_CHALLENGE:
|
|
rc = lpfc_auth_handle_dhchap_chal(vport, pcmd, ndlp);
|
|
break;
|
|
case DHCHAP_REPLY:
|
|
rc = lpfc_auth_handle_dhchap_reply(vport, pcmd, ndlp);
|
|
break;
|
|
case DHCHAP_SUCCESS:
|
|
rc = lpfc_auth_handle_dhchap_success(vport, pcmd, ndlp);
|
|
if (!rc) {
|
|
/* Authentication complete */
|
|
if (vport->port_state < LPFC_VPORT_READY) {
|
|
if (vport->port_state != LPFC_FDISC)
|
|
lpfc_start_fdiscs(phba);
|
|
lpfc_do_scr_ns_plogi(phba, vport);
|
|
}
|
|
|
|
/* Set up reauthentication timer */
|
|
if (ndlp->dhc_cfg.reauth_interval)
|
|
lpfc_auth_start_reauth_tmr(ndlp);
|
|
}
|
|
break;
|
|
default:
|
|
lpfc_auth_issue_reject(vport, ndlp, be32_to_cpu(hdr->tran_id),
|
|
AUTHRJT_FAILURE, AUTHEXPL_BAD_PAYLOAD);
|
|
break;
|
|
}
|
|
|
|
out:
|
|
kfree(pcmd);
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_start - Start DH-CHAP authentication
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* This routine initiates authentication on the @vport.
|
|
*
|
|
* Return codes
|
|
* 0 - Authentication required before logins continue
|
|
* 1 - No Authentication will be done
|
|
**/
|
|
int
|
|
lpfc_auth_start(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct lpfc_hba *phba = vport->phba;
|
|
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
|
|
uint8_t fcsp_en;
|
|
int rc = 0;
|
|
|
|
if (!phba->cfg_enable_auth)
|
|
return 1;
|
|
|
|
if (phba->sli4_hba.fawwpn_in_use)
|
|
return 1;
|
|
|
|
if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
|
|
return 1;
|
|
|
|
/*
|
|
* If FC_DISC_DELAYED is set, delay the authentication.
|
|
*/
|
|
spin_lock_irq(shost->host_lock);
|
|
if (vport->fc_flag & FC_DISC_DELAYED) {
|
|
spin_unlock_irq(shost->host_lock);
|
|
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
|
|
"3435 Delay authentication for %d seconds\n",
|
|
phba->fc_ratov);
|
|
mod_timer(&vport->delayed_disc_tmo,
|
|
jiffies + msecs_to_jiffies(1000 * phba->fc_ratov));
|
|
return 0;
|
|
}
|
|
spin_unlock_irq(shost->host_lock);
|
|
|
|
fcsp_en = (ndlp->nlp_DID == Fabric_DID) ? phba->fc_fabparam.cmn.fcsp :
|
|
ndlp->fc_sparam.cmn.fcsp;
|
|
|
|
/* Determine the dh-chap configuration for port pair */
|
|
rc = lpfc_auth_lookup_cfg(ndlp);
|
|
if (rc) {
|
|
lpfc_printf_vlog(vport, KERN_ERR, LOG_AUTH,
|
|
"3042 No Authentication configuration found "
|
|
"for request.\n");
|
|
|
|
if (fcsp_en) {
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_FAIL;
|
|
lpfc_issue_els_logo(vport, ndlp, 0);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
switch (ndlp->dhc_cfg.auth_mode) {
|
|
case LPFC_AUTH_MODE_PASSIVE:
|
|
if (!fcsp_en)
|
|
return 1;
|
|
/* Fall Thru */
|
|
case LPFC_AUTH_MODE_ACTIVE:
|
|
rc = lpfc_auth_issue_negotiate(vport, ndlp);
|
|
if (rc)
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_UNKNOWN;
|
|
break;
|
|
case LPFC_AUTH_MODE_DISABLE:
|
|
if (!fcsp_en)
|
|
return 1;
|
|
/* fall through */
|
|
default:
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_UNKNOWN;
|
|
/* logout should be sent */
|
|
lpfc_issue_els_logo(vport, ndlp, 0);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_reauth - Restart DH-CHAP authentication
|
|
* @ptr: pointer to a node-list data structure.
|
|
*
|
|
* This routine initiates authentication on the @vport.
|
|
*
|
|
**/
|
|
void
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
|
|
lpfc_auth_reauth(unsigned long ptr)
|
|
#else
|
|
lpfc_auth_reauth(struct timer_list *t)
|
|
#endif
|
|
{
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
|
|
struct lpfc_nodelist *ndlp = (struct lpfc_nodelist *)ptr;
|
|
#else
|
|
struct lpfc_nodelist *ndlp = from_timer(ndlp, t,
|
|
dhc_cfg.nlp_reauthfunc);
|
|
#endif
|
|
struct lpfc_vport *vport = ndlp->vport;
|
|
struct lpfc_hba *phba = vport->phba;
|
|
unsigned long flags;
|
|
struct lpfc_work_evt *evtp = &ndlp->reauth_evt;
|
|
|
|
spin_lock_irqsave(&phba->hbalock, flags);
|
|
if (!list_empty(&evtp->evt_listp)) {
|
|
spin_unlock_irqrestore(&phba->hbalock, flags);
|
|
return;
|
|
}
|
|
|
|
/* We need to hold the node by incrementing the reference
|
|
* count until the queued work is done
|
|
*/
|
|
evtp->evt_arg1 = lpfc_nlp_get(ndlp);
|
|
if (evtp->evt_arg1) {
|
|
evtp->evt = LPFC_EVT_REAUTH;
|
|
list_add_tail(&evtp->evt_listp, &phba->work_list);
|
|
lpfc_worker_wake_up(phba);
|
|
}
|
|
spin_unlock_irqrestore(&phba->hbalock, flags);
|
|
|
|
lpfc_printf_vlog(vport, KERN_INFO, LOG_AUTH,
|
|
"3102 Start Reauthentication\n");
|
|
}
|
|
|
|
/**
|
|
* lpfc_auth_timeout_handler - process authentication timer expiry
|
|
* @vport: pointer to a host virtual N_Port data structure.
|
|
* @ndlp: pointer to a node-list data structure.
|
|
*
|
|
* This routine authentication will clean up an incomplete authentication
|
|
* or start reauthentication.
|
|
*
|
|
**/
|
|
void
|
|
lpfc_auth_timeout_handler(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp)
|
|
{
|
|
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
|
|
struct lpfc_work_evt *evtp;
|
|
|
|
/* determine whether previous authentication completed */
|
|
if (ndlp->dhc_cfg.msg == LPFC_DHCHAP_SUCCESS) {
|
|
lpfc_auth_start(vport, ndlp);
|
|
} else {
|
|
ndlp->dhc_cfg.state = LPFC_AUTH_FAIL;
|
|
ndlp->dhc_cfg.msg = LPFC_AUTH_REJECT;
|
|
ndlp->dhc_cfg.direction = AUTH_DIRECTION_NONE;
|
|
|
|
if (!(ndlp->nlp_flag & NLP_DELAY_TMO))
|
|
return;
|
|
spin_lock_irq(shost->host_lock);
|
|
ndlp->nlp_flag &= ~NLP_DELAY_TMO;
|
|
spin_unlock_irq(shost->host_lock);
|
|
del_timer_sync(&ndlp->nlp_delayfunc);
|
|
ndlp->nlp_last_elscmd = 0;
|
|
if (!list_empty(&ndlp->els_retry_evt.evt_listp)) {
|
|
list_del_init(&ndlp->els_retry_evt.evt_listp);
|
|
/* Decrement ndlp ref count held for delayed retry */
|
|
evtp = &ndlp->els_retry_evt;
|
|
lpfc_nlp_put((struct lpfc_nodelist *)evtp->evt_arg1);
|
|
}
|
|
}
|
|
}
|
|
|