mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 09:53:15 -04:00
Merge tag 's390-7.2-7' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 fixes from Vasily Gorbik: - Fix potential uninitialized memory reads and buffer overflows from malformed zcrypt CCA and EP11 requests by properly validating lengths and payloads - Fix possible out of bounds accesses in zcrypt EP11 domain handling by replacing fixed payload layout assumptions with parsing ASN.1 fields with bounds checks - Fix zcrypt CCA and EP11 request and reply buffer allocations missing required 4-byte padding, and scrub the full allocation on release - Fix zcrypt CCA and EP11 messages leaking up to 3 uninitialized bytes of memory by zeroing trailing alignment padding * tag 's390-7.2-7' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: s390/zcrypt: Pad trailing CCA or EP11 message with zeros s390/zcrypt: Improve EP11 CPRB domain handling with ASN.1 parsing s390/zcrypt: Improve EP11 CPRB length and overflow checks s390/zcrypt: Improve CCA CPRB length and overflow checks s390/zcrypt: Fix CPRB memory allocation in zcrypt misc code
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/align.h>
|
||||
#include <asm/zcrypt.h>
|
||||
#include <asm/pkey.h>
|
||||
|
||||
@@ -267,6 +268,10 @@ EXPORT_SYMBOL(cca_check_sececckeytoken);
|
||||
* block, reply CPRB and reply param block and fill in values
|
||||
* for the common fields. Returns 0 on success or errno value
|
||||
* on failure.
|
||||
* It is guaranteed that request and a possible param block
|
||||
* are aligned to a 4 byte boundary. Furthermore if a param
|
||||
* block is used, the memory allocated for this is rounded up to
|
||||
* the next multiple of 4 bytes.
|
||||
*/
|
||||
static int alloc_and_prep_cprbmem(size_t paramblen,
|
||||
u8 **p_cprb_mem,
|
||||
@@ -275,7 +280,8 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
|
||||
u32 xflags)
|
||||
{
|
||||
u8 *cprbmem = NULL;
|
||||
size_t cprbplusparamblen = sizeof(struct CPRBX) + paramblen;
|
||||
size_t cprbplusparamblen =
|
||||
ALIGN(sizeof(struct CPRBX), 4) + ALIGN(paramblen, 4);
|
||||
size_t len = 2 * cprbplusparamblen;
|
||||
struct CPRBX *preqcblk, *prepcblk;
|
||||
|
||||
@@ -302,10 +308,10 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
|
||||
memcpy(preqcblk->func_id, "T2", 2);
|
||||
preqcblk->rpl_msgbl = cprbplusparamblen;
|
||||
if (paramblen) {
|
||||
preqcblk->req_parmb =
|
||||
((u8 __user *)preqcblk) + sizeof(struct CPRBX);
|
||||
preqcblk->rpl_parmb =
|
||||
((u8 __user *)prepcblk) + sizeof(struct CPRBX);
|
||||
preqcblk->req_parmb = ((u8 __user *)preqcblk) +
|
||||
ALIGN(sizeof(struct CPRBX), 4);
|
||||
preqcblk->rpl_parmb = ((u8 __user *)prepcblk) +
|
||||
ALIGN(sizeof(struct CPRBX), 4);
|
||||
}
|
||||
|
||||
*p_cprb_mem = cprbmem;
|
||||
@@ -323,8 +329,10 @@ static int alloc_and_prep_cprbmem(size_t paramblen,
|
||||
*/
|
||||
static void free_cprbmem(void *mem, size_t paramblen, bool scrub, u32 xflags)
|
||||
{
|
||||
size_t cprblen = ALIGN(sizeof(struct CPRBX), 4) + ALIGN(paramblen, 4);
|
||||
|
||||
if (mem && scrub)
|
||||
memzero_explicit(mem, 2 * (sizeof(struct CPRBX) + paramblen));
|
||||
memzero_explicit(mem, 2 * cprblen);
|
||||
|
||||
if (xflags & ZCRYPT_XFLAG_NOMEMALLOC)
|
||||
mempool_free(mem, cprb_mempool);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/align.h>
|
||||
#include <asm/zcrypt.h>
|
||||
#include <asm/pkey.h>
|
||||
#include <crypto/aes.h>
|
||||
@@ -355,21 +356,24 @@ EXPORT_SYMBOL(ep11_check_aes_key);
|
||||
|
||||
/*
|
||||
* Allocate and prepare ep11 cprb plus additional payload.
|
||||
* It is guaranteed that the memory is aligned to a 4 byte boundary.
|
||||
* Furthermore the memory allocation is rounded up to the next
|
||||
* multiple of 4 bytes (with taking the payload_len into account).
|
||||
*/
|
||||
static void *alloc_cprbmem(size_t payload_len, u32 xflags)
|
||||
{
|
||||
size_t len = sizeof(struct ep11_cprb) + payload_len;
|
||||
size_t memlen = ALIGN(sizeof(struct ep11_cprb) + payload_len, 4);
|
||||
struct ep11_cprb *cprb = NULL;
|
||||
|
||||
if (xflags & ZCRYPT_XFLAG_NOMEMALLOC) {
|
||||
if (len <= CPRB_MEMPOOL_ITEM_SIZE)
|
||||
if (memlen <= CPRB_MEMPOOL_ITEM_SIZE)
|
||||
cprb = mempool_alloc_preallocated(cprb_mempool);
|
||||
} else {
|
||||
cprb = kmalloc(len, GFP_KERNEL);
|
||||
cprb = kmalloc(memlen, GFP_KERNEL);
|
||||
}
|
||||
if (!cprb)
|
||||
return NULL;
|
||||
memset(cprb, 0, len);
|
||||
memset(cprb, 0, memlen);
|
||||
|
||||
cprb->cprb_len = sizeof(struct ep11_cprb);
|
||||
cprb->cprb_ver_id = 0x04;
|
||||
@@ -385,8 +389,10 @@ static void *alloc_cprbmem(size_t payload_len, u32 xflags)
|
||||
*/
|
||||
static void free_cprbmem(void *mem, size_t payload_len, bool scrub, u32 xflags)
|
||||
{
|
||||
size_t memlen = ALIGN(sizeof(struct ep11_cprb) + payload_len, 4);
|
||||
|
||||
if (mem && scrub)
|
||||
memzero_explicit(mem, sizeof(struct ep11_cprb) + payload_len);
|
||||
memzero_explicit(mem, memlen);
|
||||
|
||||
if (xflags & ZCRYPT_XFLAG_NOMEMALLOC)
|
||||
mempool_free(mem, cprb_mempool);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#include "ap_bus.h"
|
||||
#include "zcrypt_api.h"
|
||||
@@ -34,6 +35,9 @@
|
||||
#define CEXXC_RESPONSE_TYPE_XCRB 1
|
||||
#define CEXXC_RESPONSE_TYPE_EP11 2
|
||||
|
||||
/* smallest possible EP11 payload size */
|
||||
#define MIN_EP11_PAYLOAD_SIZE 5
|
||||
|
||||
MODULE_AUTHOR("IBM Corporation");
|
||||
MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
|
||||
"Copyright IBM Corp. 2001, 2023");
|
||||
@@ -342,49 +346,39 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
|
||||
};
|
||||
} __packed * msg = ap_msg->msg;
|
||||
|
||||
int rcblen = CEIL4(xcrb->request_control_blk_length);
|
||||
int req_sumlen, resp_sumlen;
|
||||
char *req_data = ap_msg->msg + sizeof(struct type6_hdr) + rcblen;
|
||||
char *function_code;
|
||||
size_t req_cblen, rep_cblen, req_sumlen, rep_sumlen;
|
||||
char *function_code, *req_data;
|
||||
|
||||
if (CEIL4(xcrb->request_control_blk_length) <
|
||||
xcrb->request_control_blk_length)
|
||||
return -EINVAL; /* overflow after alignment*/
|
||||
|
||||
/* length checks */
|
||||
ap_msg->len = sizeof(struct type6_hdr) +
|
||||
CEIL4(xcrb->request_control_blk_length) +
|
||||
xcrb->request_data_length;
|
||||
/* request length and overflow checks */
|
||||
if (xcrb->request_control_blk_length < sizeof(struct CPRBX))
|
||||
return -EINVAL;
|
||||
req_cblen = CEIL4((size_t)xcrb->request_control_blk_length);
|
||||
if (req_cblen > U32_MAX)
|
||||
return -EINVAL;
|
||||
req_sumlen = req_cblen + xcrb->request_data_length;
|
||||
if (req_sumlen > U32_MAX)
|
||||
return -EINVAL;
|
||||
ap_msg->len = sizeof(struct type6_hdr) + req_sumlen;
|
||||
if (ap_msg->len > ap_msg->bufsize)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Overflow check
|
||||
* sum must be greater (or equal) than the largest operand
|
||||
*/
|
||||
req_sumlen = CEIL4(xcrb->request_control_blk_length) +
|
||||
xcrb->request_data_length;
|
||||
if ((CEIL4(xcrb->request_control_blk_length) <=
|
||||
xcrb->request_data_length) ?
|
||||
if (req_cblen <= xcrb->request_data_length ?
|
||||
req_sumlen < xcrb->request_data_length :
|
||||
req_sumlen < CEIL4(xcrb->request_control_blk_length)) {
|
||||
req_sumlen < req_cblen) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (CEIL4(xcrb->reply_control_blk_length) <
|
||||
xcrb->reply_control_blk_length)
|
||||
return -EINVAL; /* overflow after alignment*/
|
||||
|
||||
/*
|
||||
* Overflow check
|
||||
* sum must be greater (or equal) than the largest operand
|
||||
*/
|
||||
resp_sumlen = CEIL4(xcrb->reply_control_blk_length) +
|
||||
xcrb->reply_data_length;
|
||||
if ((CEIL4(xcrb->reply_control_blk_length) <=
|
||||
xcrb->reply_data_length) ?
|
||||
resp_sumlen < xcrb->reply_data_length :
|
||||
resp_sumlen < CEIL4(xcrb->reply_control_blk_length)) {
|
||||
/* reply length and overflow checks */
|
||||
if (xcrb->reply_control_blk_length < sizeof(struct CPRBX))
|
||||
return -EINVAL;
|
||||
rep_cblen = CEIL4((size_t)xcrb->reply_control_blk_length);
|
||||
if (rep_cblen > U32_MAX)
|
||||
return -EINVAL;
|
||||
rep_sumlen = rep_cblen + xcrb->reply_data_length;
|
||||
if (rep_sumlen > U32_MAX)
|
||||
return -EINVAL;
|
||||
if (rep_cblen <= xcrb->reply_data_length ?
|
||||
rep_sumlen < xcrb->reply_data_length :
|
||||
rep_sumlen < rep_cblen) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -393,7 +387,7 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
|
||||
memcpy(msg->hdr.agent_id, &xcrb->agent_ID, sizeof(xcrb->agent_ID));
|
||||
msg->hdr.tocardlen1 = xcrb->request_control_blk_length;
|
||||
if (xcrb->request_data_length) {
|
||||
msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
|
||||
msg->hdr.offset2 = msg->hdr.offset1 + req_cblen;
|
||||
msg->hdr.tocardlen2 = xcrb->request_data_length;
|
||||
}
|
||||
msg->hdr.fromcardlen1 = xcrb->reply_control_blk_length;
|
||||
@@ -404,8 +398,12 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
|
||||
xcrb->request_control_blk_addr,
|
||||
xcrb->request_control_blk_length))
|
||||
return -EFAULT;
|
||||
if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
|
||||
xcrb->request_control_blk_length)
|
||||
/* pad tail with 0 up to req_cblen */
|
||||
if (xcrb->request_control_blk_length < req_cblen)
|
||||
memset(msg->userdata + xcrb->request_control_blk_length,
|
||||
0, req_cblen - xcrb->request_control_blk_length);
|
||||
/* copy subfunction code into AP msg type 6 function code field */
|
||||
if (msg->cprbx.cprb_len > req_cblen - sizeof(msg->hdr.function_code))
|
||||
return -EINVAL;
|
||||
function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
|
||||
memcpy(msg->hdr.function_code, function_code,
|
||||
@@ -437,20 +435,70 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg,
|
||||
}
|
||||
|
||||
/* copy data block */
|
||||
if (xcrb->request_data_length &&
|
||||
z_copy_from_user(userspace, req_data, xcrb->request_data_address,
|
||||
xcrb->request_data_length))
|
||||
return -EFAULT;
|
||||
if (xcrb->request_data_length) {
|
||||
req_data = ap_msg->msg + sizeof(struct type6_hdr) + req_cblen;
|
||||
if (z_copy_from_user(userspace, req_data,
|
||||
xcrb->request_data_address,
|
||||
xcrb->request_data_length))
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple asn1 int reader/decoder helper function
|
||||
* Returns number of bytes processed or < 0 on failure
|
||||
* Only accepts int length values of 1, 2 or 4.
|
||||
*/
|
||||
static inline int asn1_int_decode(const u8 *buf, size_t intlen, u32 *u)
|
||||
{
|
||||
switch (intlen) {
|
||||
case 1:
|
||||
*u = (u32)(*buf);
|
||||
return 1;
|
||||
case 2:
|
||||
*u = (u32)get_unaligned_be16(buf);
|
||||
return 2;
|
||||
case 4:
|
||||
*u = (u32)get_unaligned_be32(buf);
|
||||
return 4;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple asn1 length parse helper function
|
||||
* Returns number of bytes processed or < 0 on failure
|
||||
* Only accepts length encoded within the length octet
|
||||
* or for long form 1, 2 or 4 octet length bytes.
|
||||
*/
|
||||
static inline int asn1_length_decode(const u8 *buf, size_t buflen, u32 *u)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (buflen < 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (*buf < 128) {
|
||||
*u = (u32)(*buf & 0x7F);
|
||||
return 1;
|
||||
}
|
||||
|
||||
i = *buf & 0x7F;
|
||||
if (--buflen < i)
|
||||
return -EINVAL;
|
||||
i = asn1_int_decode(++buf, i, u);
|
||||
|
||||
return i < 0 ? i : i + 1;
|
||||
}
|
||||
|
||||
static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap_msg,
|
||||
struct ep11_urb *xcrb,
|
||||
unsigned int *fcode,
|
||||
unsigned int *domain)
|
||||
{
|
||||
unsigned int lfmt;
|
||||
static struct type6_hdr static_type6_ep11_hdr = {
|
||||
.type = 0x06,
|
||||
.rqid = {0x00, 0x01},
|
||||
@@ -462,34 +510,32 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap
|
||||
struct {
|
||||
struct type6_hdr hdr;
|
||||
union {
|
||||
struct {
|
||||
struct ep11_cprb cprbx;
|
||||
unsigned char pld_tag; /* fixed value 0x30 */
|
||||
unsigned char pld_lenfmt; /* length format */
|
||||
} __packed;
|
||||
struct ep11_cprb cprbx;
|
||||
DECLARE_FLEX_ARRAY(u8, userdata);
|
||||
};
|
||||
} __packed * msg = ap_msg->msg;
|
||||
|
||||
struct pld_hdr {
|
||||
unsigned char func_tag; /* fixed value 0x4 */
|
||||
unsigned char func_len; /* fixed value 0x4 */
|
||||
unsigned int func_val; /* function ID */
|
||||
unsigned char dom_tag; /* fixed value 0x4 */
|
||||
unsigned char dom_len; /* fixed value 0x4 */
|
||||
unsigned int dom_val; /* domain id */
|
||||
} __packed * payload_hdr = NULL;
|
||||
size_t req_len, rep_len, pld_len;
|
||||
unsigned char *pld;
|
||||
int offs = 0, i;
|
||||
unsigned int u;
|
||||
|
||||
if (CEIL4(xcrb->req_len) < xcrb->req_len)
|
||||
return -EINVAL; /* overflow after alignment*/
|
||||
|
||||
/* length checks */
|
||||
ap_msg->len = sizeof(struct type6_hdr) + CEIL4(xcrb->req_len);
|
||||
/* request length and overflow checks */
|
||||
if (xcrb->req_len < sizeof(struct ep11_cprb) + MIN_EP11_PAYLOAD_SIZE)
|
||||
return -EINVAL;
|
||||
req_len = CEIL4(xcrb->req_len);
|
||||
if (req_len < xcrb->req_len || req_len > U32_MAX)
|
||||
return -EINVAL;
|
||||
ap_msg->len = sizeof(struct type6_hdr) + req_len;
|
||||
if (ap_msg->len > ap_msg->bufsize)
|
||||
return -EINVAL;
|
||||
|
||||
if (CEIL4(xcrb->resp_len) < xcrb->resp_len)
|
||||
return -EINVAL; /* overflow after alignment*/
|
||||
/* reply length and overflow checks */
|
||||
if (xcrb->resp_len < sizeof(struct ep11_cprb))
|
||||
return -EINVAL;
|
||||
rep_len = CEIL4(xcrb->resp_len);
|
||||
if (rep_len < xcrb->resp_len || rep_len > U32_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/* prepare type6 header */
|
||||
msg->hdr = static_type6_ep11_hdr;
|
||||
@@ -498,26 +544,55 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap
|
||||
|
||||
/* Import CPRB data from the ioctl input parameter */
|
||||
if (z_copy_from_user(userspace, msg->userdata,
|
||||
(char __force __user *)xcrb->req, xcrb->req_len)) {
|
||||
(char __force __user *)xcrb->req, xcrb->req_len))
|
||||
return -EFAULT;
|
||||
}
|
||||
/* pad tail with 0 up to req_len */
|
||||
if (xcrb->req_len < req_len)
|
||||
memset(msg->userdata + xcrb->req_len, 0,
|
||||
req_len - xcrb->req_len);
|
||||
|
||||
if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
|
||||
switch (msg->pld_lenfmt & 0x03) {
|
||||
case 1:
|
||||
lfmt = 2;
|
||||
break;
|
||||
case 2:
|
||||
lfmt = 3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
lfmt = 1; /* length format #1 */
|
||||
}
|
||||
payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt);
|
||||
*fcode = payload_hdr->func_val & 0xFFFF;
|
||||
pld = msg->userdata + sizeof(struct ep11_cprb);
|
||||
pld_len = msg->cprbx.payload_len;
|
||||
if (pld_len != xcrb->req_len - sizeof(struct ep11_cprb))
|
||||
return -EINVAL;
|
||||
/*
|
||||
* At this point pld_len is always >= MIN_EP11_PAYLOAD_SIZE
|
||||
* and the smallest supported asn1 payload is:
|
||||
* payload tag (1 octet)
|
||||
* payload length (1-5 octets)
|
||||
* function tag (1 octet)
|
||||
* function length (1-5 octets)
|
||||
* function value (1-4 octets)
|
||||
*/
|
||||
|
||||
/* payload tag */
|
||||
if (pld[offs++] != 0x30)
|
||||
return -EINVAL;
|
||||
/* payload length field */
|
||||
i = asn1_length_decode(pld + offs, pld_len - offs, &u);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
if (offs >= pld_len || u > pld_len - offs)
|
||||
return -EINVAL;
|
||||
/* function tag */
|
||||
if (pld[offs++] != 0x04)
|
||||
return -EINVAL;
|
||||
/* function length */
|
||||
if (offs >= pld_len)
|
||||
return -EINVAL;
|
||||
i = asn1_length_decode(pld + offs, pld_len - offs, &u);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
if (offs >= pld_len || u > pld_len - offs)
|
||||
return -EINVAL;
|
||||
/* function value */
|
||||
i = asn1_int_decode(pld + offs, u, &u);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
*fcode = 0xFFFF & u;
|
||||
|
||||
/* enable special processing based on the cprbs flags special bit */
|
||||
if (msg->cprbx.flags & 0x20)
|
||||
@@ -1159,6 +1234,28 @@ int prep_ep11_ap_msg(bool userspace, struct ep11_urb *xcrb,
|
||||
func_code, domain);
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple asn1 int writer/encoder helper function
|
||||
* Returns number of bytes processed or < 0 on failure
|
||||
* Only accepts int length values of 1, 2 or 4.
|
||||
*/
|
||||
static inline int asn1_int_encode(u8 *buf, size_t intlen, u32 u)
|
||||
{
|
||||
switch (intlen) {
|
||||
case 1:
|
||||
*buf = (u8)u;
|
||||
return 1;
|
||||
case 2:
|
||||
put_unaligned_be16((u16)u, buf);
|
||||
return 2;
|
||||
case 4:
|
||||
put_unaligned_be32((u32)u, buf);
|
||||
return 4;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The request distributor calls this function if it picked the CEX4P
|
||||
* device to handle a send_ep11_cprb request.
|
||||
@@ -1171,51 +1268,94 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue *
|
||||
struct ap_message *ap_msg)
|
||||
{
|
||||
int rc;
|
||||
unsigned int lfmt;
|
||||
struct ap_response_type *resp_type = &ap_msg->response;
|
||||
struct {
|
||||
struct type6_hdr hdr;
|
||||
struct ep11_cprb cprbx;
|
||||
unsigned char pld_tag; /* fixed value 0x30 */
|
||||
unsigned char pld_lenfmt; /* payload length format */
|
||||
} __packed * msg = ap_msg->msg;
|
||||
struct pld_hdr {
|
||||
unsigned char func_tag; /* fixed value 0x4 */
|
||||
unsigned char func_len; /* fixed value 0x4 */
|
||||
unsigned int func_val; /* function ID */
|
||||
unsigned char dom_tag; /* fixed value 0x4 */
|
||||
unsigned char dom_len; /* fixed value 0x4 */
|
||||
unsigned int dom_val; /* domain id */
|
||||
} __packed * payload_hdr = NULL;
|
||||
|
||||
/*
|
||||
* The target domain field within the cprb body/payload block will be
|
||||
* replaced by the usage domain for non-management commands only.
|
||||
* Therefore we check the first bit of the 'flags' parameter for
|
||||
* management command indication.
|
||||
* 0 - non management command
|
||||
* 1 - management command
|
||||
*/
|
||||
if (!((msg->cprbx.flags & 0x80) == 0x80)) {
|
||||
msg->cprbx.target_id = (unsigned int)
|
||||
AP_QID_QUEUE(zq->queue->qid);
|
||||
if (!(msg->cprbx.flags & 0x80)) {
|
||||
int i, offs = 0;
|
||||
size_t pld_len;
|
||||
u8 *pld;
|
||||
u32 u;
|
||||
|
||||
if ((msg->pld_lenfmt & 0x80) == 0x80) { /*ext.len.fmt 2 or 3*/
|
||||
switch (msg->pld_lenfmt & 0x03) {
|
||||
case 1:
|
||||
lfmt = 2;
|
||||
break;
|
||||
case 2:
|
||||
lfmt = 3;
|
||||
break;
|
||||
default:
|
||||
/* update target field in ep11_cprb */
|
||||
msg->cprbx.target_id = (u32)AP_QID_QUEUE(zq->queue->qid);
|
||||
|
||||
/* ptr and length to payload */
|
||||
pld = ap_msg->msg +
|
||||
sizeof(struct type6_hdr) + sizeof(struct ep11_cprb);
|
||||
pld_len = msg->cprbx.payload_len;
|
||||
if (pld_len < MIN_EP11_PAYLOAD_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Parse the asn1 payload, at least we have
|
||||
* pld tag (1 octet)
|
||||
* payload length (1-5 octets)
|
||||
* function tag (1 octet)
|
||||
* function length (1-5 octets)
|
||||
* function value (1-4 octets)
|
||||
* ----- optional fields -----
|
||||
* domain tag (1 octet)
|
||||
* domain length (1-5 octets)
|
||||
* domain value (1-4 octets)
|
||||
* ... maybe much more data ...
|
||||
*/
|
||||
|
||||
/* payload tag */
|
||||
if (pld[offs++] != 0x30)
|
||||
return -EINVAL;
|
||||
/* payload length field */
|
||||
i = asn1_length_decode(pld + offs, pld_len - offs, &u);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
if (offs >= pld_len || u > pld_len - offs)
|
||||
return -EINVAL;
|
||||
/* function tag */
|
||||
if (pld[offs++] != 0x04)
|
||||
return -EINVAL;
|
||||
/* function length */
|
||||
if (offs >= pld_len)
|
||||
return -EINVAL;
|
||||
i = asn1_length_decode(pld + offs, pld_len - offs, &u);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
if (u > pld_len - offs)
|
||||
return -EINVAL;
|
||||
/* skip over the function value */
|
||||
offs += u;
|
||||
/* is there some payload left which could hold a domain value ? */
|
||||
if (offs < pld_len && pld_len - offs >= 3) {
|
||||
/* domain tag */
|
||||
if (pld[offs++] != 0x04)
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
lfmt = 1; /* length format #1 */
|
||||
/* domain length */
|
||||
i = asn1_length_decode(pld + offs, pld_len - offs, &u);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
if (offs >= pld_len || u > pld_len - offs)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* pld[offs] is now at the start of the domain value
|
||||
* with the value sprawled in u octets.
|
||||
*/
|
||||
i = asn1_int_encode(pld + offs, u,
|
||||
AP_QID_QUEUE(zq->queue->qid));
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
offs += i;
|
||||
}
|
||||
payload_hdr = (struct pld_hdr *)((&msg->pld_lenfmt) + lfmt);
|
||||
payload_hdr->dom_val = AP_QID_QUEUE(zq->queue->qid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user