/*
* linux/net/sunrpc/gss_krb5_crypto.c
*
* Copyright (c) 2000-2008 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@umich.edu>
* Bruce Fields <bfields@umich.edu>
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <crypto/hash.h>
#include <crypto/skcipher.h>
#include <crypto/utils.h>
#include <linux/err.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/random.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
#include <kunit/visibility.h>
#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/**
* krb5_make_confounder - Generate a confounder string
* @p: memory location into which to write the string
* @conflen: string length to write, in octets
*
* RFCs 1964 and 3961 mention only "a random confounder" without going
* into detail about its function or cryptographic requirements. The
* assumed purpose is to prevent repeated encryption of a plaintext with
* the same key from generating the same ciphertext. It is also used to
* pad minimum plaintext length to at least a single cipher block.
*
* However, in situations like the GSS Kerberos 5 mechanism, where the
* encryption IV is always all zeroes, the confounder also effectively
* functions like an IV. Thus, not only must it be unique from message
* to message, but it must also be difficult to predict. Otherwise an
* attacker can correlate the confounder to previous or future values,
* making the encryption easier to break.
*
* Given that the primary consumer of this encryption mechanism is a
* network storage protocol, a type of traffic that often carries
* predictable payloads (eg, all zeroes when reading unallocated blocks
* from a file), our confounder generation has to be cryptographically
* strong.
*/
void krb5_make_confounder(u8 *p, int conflen)
{
get_random_bytes(p, conflen);
}
/**
* krb5_encrypt - simple encryption of an RPCSEC GSS payload
* @tfm: initialized cipher transform
* @iv: pointer to an IV
* @in: plaintext to encrypt
* @out: OUT: ciphertext
* @length: length of input and output buffers, in bytes
*
* @iv may be NULL to force the use of an all-zero IV.
* The buffer containing the IV must be as large as the
* cipher's ivsize.
*
* Return values:
* %0: @in successfully encrypted into @out
* negative errno: @in not encrypted
*/
u32
krb5_encrypt(
struct crypto_sync_skcipher *tfm,
void * iv,
void * in,
void * out,
int length)
{
u32 ret = -EINVAL;
struct scatterlist sg[1];
u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
goto out;
if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
dprintk("RPC: gss_k5encrypt: tfm iv size too large %d\n",
crypto_sync_skcipher_ivsize(tfm));
goto out;
}
if (iv)
memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
memcpy(out, in, length);
sg_init_one(sg, out, length);
skcipher_request_set_sync_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg, sg, length, local_iv);
ret = crypto_skcipher_encrypt(req);
skcipher_request_zero(req);
out:
dprintk("RPC: krb5_encrypt returns %d\n", ret);
return ret;
}
/**
* krb5_decrypt - simple decryption of an RPCSEC GSS payload
* @tfm: initialized cipher transform
* @iv: pointer to an IV
* @in: ciphertext to decrypt
* @out: OUT: plaintext
* @length: length of input and output buffers, in bytes
*
* @iv may be NULL to force the use of an all-zero IV.
* The buffer containing the IV must be as large as the
* cipher's ivsize.
*
* Return values:
* %0: @in successfully decrypted into @out
* negative errno: @in not decrypted
*/
u32
krb5_decrypt(
struct crypto_sync_skcipher *tfm,
void * iv,
void * in,
void * out,
int length)
{
u32 ret = -EINVAL;
struct scatterlist sg[1];
u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
SYNC_SKCIPHER_REQUEST_ON_STACK(req, tfm);
if (length % crypto_sync_skcipher_blocksize(tfm) != 0)
goto out;
if (crypto_sync_skcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
dprintk("RPC: gss_k5decrypt: tfm iv size too large %d\n",
crypto_sync_skcipher_ivsize(tfm));
goto out;
}
if (iv)
memcpy(local_iv, iv, crypto_sync_skcipher_ivsize(tfm));
memcpy(out, in, length);
sg_init_one(sg, out, length);
skcipher_request_set_sync_tfm(req, tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sg, sg, length, local_iv);
ret = crypto_skcipher_decrypt(req);
skcipher_request_zero(req);
out:
dprintk("RPC: gss_k5decrypt returns %d