]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
s390/zcrypt: Fix buffer over-read in cca_cipher2protkey
authorHarald Freudenberger <freude@linux.ibm.com>
Wed, 29 Jul 2026 11:40:09 +0000 (13:40 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Fri, 31 Jul 2026 11:31:19 +0000 (13:31 +0200)
Add validation of both the actual key buffer size and token length
fields in all the cca_check_sec*token() functions. Additionally check
in cca_gencipherkey() for possible underflow with returned key size.

The CCA token structures contain user-controlled len fields that
were used in operations without proper validation against both the
actual buffer size and minimum token structure size. An attacker
could set this field larger than the actual buffer size, leading to
reading beyond buffer boundaries. This may result in a kernel crash or
exposure of memory via sending this as part of a request down to the
crypto card. Also an attacker could have used a very small len value
and thus enforce a buffer under-run which may produce similar effects
as a over-read.

So now a key must
- key buf length must be at least sizeof the token struct
- the key len field inside the token must fit into the range of
  sizeof key token struct ... key buf length

Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys")
Cc: stable@vger.kernel.org
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
drivers/s390/crypto/pkey_cca.c
drivers/s390/crypto/zcrypt_ccamisc.c
drivers/s390/crypto/zcrypt_ccamisc.h

index 4bc47952324e0981583bdabb4a6f7341d781789e..e8dc2f77c137b1a63ff920e7134835810b10a6b6 100644 (file)
@@ -236,22 +236,16 @@ static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
        if (hdr->type == TOKTYPE_CCA_INTERNAL &&
            hdr->version == TOKVER_CCA_AES) {
                /* CCA AES data key */
-               if (keylen < sizeof(struct secaeskeytoken))
-                       return -EINVAL;
-               if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0))
+               if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0))
                        return -EINVAL;
        } else if (hdr->type == TOKTYPE_CCA_INTERNAL &&
                   hdr->version == TOKVER_CCA_VLSC) {
                /* CCA AES cipher key */
-               if (keylen < hdr->len)
-                       return -EINVAL;
                if (cca_check_secaescipherkey(pkey_dbf_info,
-                                             3, key, 0, 1))
+                                             3, key, keylen, 0, 1))
                        return -EINVAL;
        } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) {
                /* CCA ECC (private) key */
-               if (keylen < sizeof(struct eccprivkeytoken))
-                       return -EINVAL;
                if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1))
                        return -EINVAL;
        } else {
@@ -484,7 +478,7 @@ static int cca_verifykey(const u8 *key, u32 keylen,
            hdr->version == TOKVER_CCA_AES) {
                struct secaeskeytoken *t = (struct secaeskeytoken *)key;
 
-               rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0);
+               rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0);
                if (rc)
                        goto out;
                *keytype = PKEY_TYPE_CCA_DATA;
@@ -512,7 +506,8 @@ static int cca_verifykey(const u8 *key, u32 keylen,
                   hdr->version == TOKVER_CCA_VLSC) {
                struct cipherkeytoken *t = (struct cipherkeytoken *)key;
 
-               rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1);
+               rc = cca_check_secaescipherkey(pkey_dbf_info, 3,
+                                              key, keylen, 0, 1);
                if (rc)
                        goto out;
                *keytype = PKEY_TYPE_CCA_CIPHER;
index 322677a8d3200955d54022eb1544ee359f6be418..f797786107b928b33a7ab737c1356819a6204df6 100644 (file)
@@ -62,12 +62,18 @@ static DEFINE_MUTEX(dev_status_mem_mutex);
  * also checked. Returns 0 on success or errno value on failure.
  */
 int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
-                            const u8 *token, int keybitsize)
+                            const u8 *token, u32 keysize, int keybitsize)
 {
        struct secaeskeytoken *t = (struct secaeskeytoken *)token;
 
 #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
 
+       if (keysize < sizeof(*t)) {
+               if (dbg)
+                       DBF("%s keysize %u < min token size %zu\n",
+                           __func__, keysize, sizeof(*t));
+               return -EINVAL;
+       }
        if (t->type != TOKTYPE_CCA_INTERNAL) {
                if (dbg)
                        DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -101,14 +107,20 @@ EXPORT_SYMBOL(cca_check_secaeskeytoken);
  * Returns 0 on success or errno value on failure.
  */
 int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
-                             const u8 *token, int keybitsize,
-                             int checkcpacfexport)
+                             const u8 *token, u32 keysize,
+                             int keybitsize, int checkcpacfexport)
 {
        struct cipherkeytoken *t = (struct cipherkeytoken *)token;
        bool keybitsizeok = true;
 
 #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
 
+       if (keysize < sizeof(*t)) {
+               if (dbg)
+                       DBF("%s keysize %u < min token size %zu\n",
+                           __func__, keysize, sizeof(*t));
+               return -EINVAL;
+       }
        if (t->type != TOKTYPE_CCA_INTERNAL) {
                if (dbg)
                        DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -121,6 +133,18 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
                            __func__, (int)t->version, TOKVER_CCA_VLSC);
                return -EINVAL;
        }
+       if (t->len > keysize) {
+               if (dbg)
+                       DBF("%s token check failed, len %d > keysize %u\n",
+                           __func__, (int)t->len, keysize);
+               return -EINVAL;
+       }
+       if (t->len < sizeof(*t)) {
+               if (dbg)
+                       DBF("%s token check failed, len %d < min token size %zu\n",
+                           __func__, (int)t->len, sizeof(*t));
+               return -EINVAL;
+       }
        if (t->algtype != 0x02) {
                if (dbg)
                        DBF("%s token check failed, algtype 0x%02x != 0x02\n",
@@ -195,6 +219,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
 
 #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__)
 
+       if (keysize < sizeof(*t)) {
+               if (dbg)
+                       DBF("%s keysize %u < min token size %zu\n",
+                           __func__, keysize, sizeof(*t));
+               return -EINVAL;
+       }
        if (t->type != TOKTYPE_CCA_INTERNAL_PKA) {
                if (dbg)
                        DBF("%s token check failed, type 0x%02x != 0x%02x\n",
@@ -207,6 +237,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl,
                            __func__, (int)t->len, keysize);
                return -EINVAL;
        }
+       if (t->len < sizeof(*t)) {
+               if (dbg)
+                       DBF("%s token check failed, len %d < min token size %zu\n",
+                           __func__, (int)t->len, sizeof(*t));
+               return -EINVAL;
+       }
        if (t->secid != 0x20) {
                if (dbg)
                        DBF("%s token check failed, secid 0x%02x != 0x20\n",
@@ -443,7 +479,8 @@ int cca_genseckey(u16 cardnr, u16 domain,
 
        /* check secure key token */
        rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
-                                     prepparm->lv3.keyblock.tok, 8 * keysize);
+                                     prepparm->lv3.keyblock.tok,
+                                     seckeysize, 8 * keysize);
        if (rc) {
                rc = -EIO;
                goto out;
@@ -582,7 +619,8 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
 
        /* check secure key token */
        rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR,
-                                     prepparm->lv3.keyblock.tok, 8 * keysize);
+                                     prepparm->lv3.keyblock.tok,
+                                     seckeysize, 8 * keysize);
        if (rc) {
                rc = -EIO;
                goto out;
@@ -842,6 +880,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
                } kb;
        } __packed * prepparm;
        struct cipherkeytoken *t;
+       u32 keybuflen;
 
        /* get already prepared memory for 2 cprbs with param block each */
        rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem,
@@ -936,23 +975,28 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags,
        }
 
        /* and some checks on the generated key */
+       t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key;
+       if (prepparm->kb.tlv1.len < 2 * sizeof(uint16_t) + sizeof(*t)) {
+               rc = -EIO;
+               goto out;
+       }
+       keybuflen = prepparm->kb.tlv1.len - 2 * sizeof(uint16_t);
        rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR,
                                       prepparm->kb.tlv1.gen_key,
-                                      keybitsize, 1);
+                                      keybuflen, keybitsize, 1);
        if (rc) {
                rc = -EIO;
                goto out;
        }
 
        /* copy the generated vlsc key token */
-       t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key;
        if (keybuf) {
-               if (*keybufsize >= t->len)
-                       memcpy(keybuf, t, t->len);
+               if (*keybufsize >= keybuflen)
+                       memcpy(keybuf, t, keybuflen);
                else
                        rc = -EINVAL;
        }
-       *keybufsize = t->len;
+       *keybufsize = keybuflen;
 
 out:
        free_cprbmem(mem, PARMBSIZE, false, xflags);
index 07bbb1c2002273c890c29f115631bec1a4878ec0..a4534f8b2f1d497dd7e2b7be80017553a248c3cb 100644 (file)
@@ -136,7 +136,7 @@ struct eccprivkeytoken {
  * also checked. Returns 0 on success or errno value on failure.
  */
 int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
-                            const u8 *token, int keybitsize);
+                            const u8 *token, u32 keysize, int keybitsize);
 
 /*
  * Simple check if the token is a valid CCA secure AES cipher key
@@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl,
  * Returns 0 on success or errno value on failure.
  */
 int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl,
-                             const u8 *token, int keybitsize,
-                             int checkcpacfexport);
+                             const u8 *token, u32 keysize,
+                             int keybitsize, int checkcpacfexport);
 
 /*
  * Simple check if the token is a valid CCA secure ECC private