mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-04 23:47:46 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
14
net/sunrpc/auth_gss/Makefile
Normal file
14
net/sunrpc/auth_gss/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# Makefile for Linux kernel rpcsec_gss implementation
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
|
||||
|
||||
auth_rpcgss-y := auth_gss.o gss_generic_token.o \
|
||||
gss_mech_switch.o svcauth_gss.o \
|
||||
gss_rpc_upcall.o gss_rpc_xdr.o
|
||||
|
||||
obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
|
||||
|
||||
rpcsec_gss_krb5-y := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
|
||||
gss_krb5_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o
|
2097
net/sunrpc/auth_gss/auth_gss.c
Normal file
2097
net/sunrpc/auth_gss/auth_gss.c
Normal file
File diff suppressed because it is too large
Load diff
234
net/sunrpc/auth_gss/gss_generic_token.c
Normal file
234
net/sunrpc/auth_gss/gss_generic_token.c
Normal file
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_generic_token.c
|
||||
*
|
||||
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic/util_token.c
|
||||
*
|
||||
* Copyright (c) 2000 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 1993 by OpenVision Technologies, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appears in all copies and
|
||||
* that both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of OpenVision not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. OpenVision makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/sunrpc/sched.h>
|
||||
#include <linux/sunrpc/gss_asn1.h>
|
||||
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
|
||||
/* TWRITE_STR from gssapiP_generic.h */
|
||||
#define TWRITE_STR(ptr, str, len) \
|
||||
memcpy((ptr), (char *) (str), (len)); \
|
||||
(ptr) += (len);
|
||||
|
||||
/* XXXX this code currently makes the assumption that a mech oid will
|
||||
never be longer than 127 bytes. This assumption is not inherent in
|
||||
the interfaces, so the code can be fixed if the OSI namespace
|
||||
balloons unexpectedly. */
|
||||
|
||||
/* Each token looks like this:
|
||||
|
||||
0x60 tag for APPLICATION 0, SEQUENCE
|
||||
(constructed, definite-length)
|
||||
<length> possible multiple bytes, need to parse/generate
|
||||
0x06 tag for OBJECT IDENTIFIER
|
||||
<moid_length> compile-time constant string (assume 1 byte)
|
||||
<moid_bytes> compile-time constant string
|
||||
<inner_bytes> the ANY containing the application token
|
||||
bytes 0,1 are the token type
|
||||
bytes 2,n are the token data
|
||||
|
||||
For the purposes of this abstraction, the token "header" consists of
|
||||
the sequence tag and length octets, the mech OID DER encoding, and the
|
||||
first two inner bytes, which indicate the token type. The token
|
||||
"body" consists of everything else.
|
||||
|
||||
*/
|
||||
|
||||
static int
|
||||
der_length_size( int length)
|
||||
{
|
||||
if (length < (1<<7))
|
||||
return 1;
|
||||
else if (length < (1<<8))
|
||||
return 2;
|
||||
#if (SIZEOF_INT == 2)
|
||||
else
|
||||
return 3;
|
||||
#else
|
||||
else if (length < (1<<16))
|
||||
return 3;
|
||||
else if (length < (1<<24))
|
||||
return 4;
|
||||
else
|
||||
return 5;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
der_write_length(unsigned char **buf, int length)
|
||||
{
|
||||
if (length < (1<<7)) {
|
||||
*(*buf)++ = (unsigned char) length;
|
||||
} else {
|
||||
*(*buf)++ = (unsigned char) (der_length_size(length)+127);
|
||||
#if (SIZEOF_INT > 2)
|
||||
if (length >= (1<<24))
|
||||
*(*buf)++ = (unsigned char) (length>>24);
|
||||
if (length >= (1<<16))
|
||||
*(*buf)++ = (unsigned char) ((length>>16)&0xff);
|
||||
#endif
|
||||
if (length >= (1<<8))
|
||||
*(*buf)++ = (unsigned char) ((length>>8)&0xff);
|
||||
*(*buf)++ = (unsigned char) (length&0xff);
|
||||
}
|
||||
}
|
||||
|
||||
/* returns decoded length, or < 0 on failure. Advances buf and
|
||||
decrements bufsize */
|
||||
|
||||
static int
|
||||
der_read_length(unsigned char **buf, int *bufsize)
|
||||
{
|
||||
unsigned char sf;
|
||||
int ret;
|
||||
|
||||
if (*bufsize < 1)
|
||||
return -1;
|
||||
sf = *(*buf)++;
|
||||
(*bufsize)--;
|
||||
if (sf & 0x80) {
|
||||
if ((sf &= 0x7f) > ((*bufsize)-1))
|
||||
return -1;
|
||||
if (sf > SIZEOF_INT)
|
||||
return -1;
|
||||
ret = 0;
|
||||
for (; sf; sf--) {
|
||||
ret = (ret<<8) + (*(*buf)++);
|
||||
(*bufsize)--;
|
||||
}
|
||||
} else {
|
||||
ret = sf;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* returns the length of a token, given the mech oid and the body size */
|
||||
|
||||
int
|
||||
g_token_size(struct xdr_netobj *mech, unsigned int body_size)
|
||||
{
|
||||
/* set body_size to sequence contents size */
|
||||
body_size += 2 + (int) mech->len; /* NEED overflow check */
|
||||
return 1 + der_length_size(body_size) + body_size;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(g_token_size);
|
||||
|
||||
/* fills in a buffer with the token header. The buffer is assumed to
|
||||
be the right size. buf is advanced past the token header */
|
||||
|
||||
void
|
||||
g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf)
|
||||
{
|
||||
*(*buf)++ = 0x60;
|
||||
der_write_length(buf, 2 + mech->len + body_size);
|
||||
*(*buf)++ = 0x06;
|
||||
*(*buf)++ = (unsigned char) mech->len;
|
||||
TWRITE_STR(*buf, mech->data, ((int) mech->len));
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(g_make_token_header);
|
||||
|
||||
/*
|
||||
* Given a buffer containing a token, reads and verifies the token,
|
||||
* leaving buf advanced past the token header, and setting body_size
|
||||
* to the number of remaining bytes. Returns 0 on success,
|
||||
* G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
|
||||
* mechanism in the token does not match the mech argument. buf and
|
||||
* *body_size are left unmodified on error.
|
||||
*/
|
||||
u32
|
||||
g_verify_token_header(struct xdr_netobj *mech, int *body_size,
|
||||
unsigned char **buf_in, int toksize)
|
||||
{
|
||||
unsigned char *buf = *buf_in;
|
||||
int seqsize;
|
||||
struct xdr_netobj toid;
|
||||
int ret = 0;
|
||||
|
||||
if ((toksize-=1) < 0)
|
||||
return G_BAD_TOK_HEADER;
|
||||
if (*buf++ != 0x60)
|
||||
return G_BAD_TOK_HEADER;
|
||||
|
||||
if ((seqsize = der_read_length(&buf, &toksize)) < 0)
|
||||
return G_BAD_TOK_HEADER;
|
||||
|
||||
if (seqsize != toksize)
|
||||
return G_BAD_TOK_HEADER;
|
||||
|
||||
if ((toksize-=1) < 0)
|
||||
return G_BAD_TOK_HEADER;
|
||||
if (*buf++ != 0x06)
|
||||
return G_BAD_TOK_HEADER;
|
||||
|
||||
if ((toksize-=1) < 0)
|
||||
return G_BAD_TOK_HEADER;
|
||||
toid.len = *buf++;
|
||||
|
||||
if ((toksize-=toid.len) < 0)
|
||||
return G_BAD_TOK_HEADER;
|
||||
toid.data = buf;
|
||||
buf+=toid.len;
|
||||
|
||||
if (! g_OID_equal(&toid, mech))
|
||||
ret = G_WRONG_MECH;
|
||||
|
||||
/* G_WRONG_MECH is not returned immediately because it's more important
|
||||
to return G_BAD_TOK_HEADER if the token header is in fact bad */
|
||||
|
||||
if ((toksize-=2) < 0)
|
||||
return G_BAD_TOK_HEADER;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!ret) {
|
||||
*buf_in = buf;
|
||||
*body_size = toksize;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(g_verify_token_header);
|
||||
|
988
net/sunrpc/auth_gss/gss_krb5_crypto.c
Normal file
988
net/sunrpc/auth_gss/gss_krb5_crypto.c
Normal file
|
@ -0,0 +1,988 @@
|
|||
/*
|
||||
* 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 <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
u32
|
||||
krb5_encrypt(
|
||||
struct crypto_blkcipher *tfm,
|
||||
void * iv,
|
||||
void * in,
|
||||
void * out,
|
||||
int length)
|
||||
{
|
||||
u32 ret = -EINVAL;
|
||||
struct scatterlist sg[1];
|
||||
u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
|
||||
struct blkcipher_desc desc = { .tfm = tfm, .info = local_iv };
|
||||
|
||||
if (length % crypto_blkcipher_blocksize(tfm) != 0)
|
||||
goto out;
|
||||
|
||||
if (crypto_blkcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
|
||||
dprintk("RPC: gss_k5encrypt: tfm iv size too large %d\n",
|
||||
crypto_blkcipher_ivsize(tfm));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iv)
|
||||
memcpy(local_iv, iv, crypto_blkcipher_ivsize(tfm));
|
||||
|
||||
memcpy(out, in, length);
|
||||
sg_init_one(sg, out, length);
|
||||
|
||||
ret = crypto_blkcipher_encrypt_iv(&desc, sg, sg, length);
|
||||
out:
|
||||
dprintk("RPC: krb5_encrypt returns %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32
|
||||
krb5_decrypt(
|
||||
struct crypto_blkcipher *tfm,
|
||||
void * iv,
|
||||
void * in,
|
||||
void * out,
|
||||
int length)
|
||||
{
|
||||
u32 ret = -EINVAL;
|
||||
struct scatterlist sg[1];
|
||||
u8 local_iv[GSS_KRB5_MAX_BLOCKSIZE] = {0};
|
||||
struct blkcipher_desc desc = { .tfm = tfm, .info = local_iv };
|
||||
|
||||
if (length % crypto_blkcipher_blocksize(tfm) != 0)
|
||||
goto out;
|
||||
|
||||
if (crypto_blkcipher_ivsize(tfm) > GSS_KRB5_MAX_BLOCKSIZE) {
|
||||
dprintk("RPC: gss_k5decrypt: tfm iv size too large %d\n",
|
||||
crypto_blkcipher_ivsize(tfm));
|
||||
goto out;
|
||||
}
|
||||
if (iv)
|
||||
memcpy(local_iv,iv, crypto_blkcipher_ivsize(tfm));
|
||||
|
||||
memcpy(out, in, length);
|
||||
sg_init_one(sg, out, length);
|
||||
|
||||
ret = crypto_blkcipher_decrypt_iv(&desc, sg, sg, length);
|
||||
out:
|
||||
dprintk("RPC: gss_k5decrypt returns %d\n",ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
checksummer(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct hash_desc *desc = data;
|
||||
|
||||
return crypto_hash_update(desc, sg, sg->length);
|
||||
}
|
||||
|
||||
static int
|
||||
arcfour_hmac_md5_usage_to_salt(unsigned int usage, u8 salt[4])
|
||||
{
|
||||
unsigned int ms_usage;
|
||||
|
||||
switch (usage) {
|
||||
case KG_USAGE_SIGN:
|
||||
ms_usage = 15;
|
||||
break;
|
||||
case KG_USAGE_SEAL:
|
||||
ms_usage = 13;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
salt[0] = (ms_usage >> 0) & 0xff;
|
||||
salt[1] = (ms_usage >> 8) & 0xff;
|
||||
salt[2] = (ms_usage >> 16) & 0xff;
|
||||
salt[3] = (ms_usage >> 24) & 0xff;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32
|
||||
make_checksum_hmac_md5(struct krb5_ctx *kctx, char *header, int hdrlen,
|
||||
struct xdr_buf *body, int body_offset, u8 *cksumkey,
|
||||
unsigned int usage, struct xdr_netobj *cksumout)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[1];
|
||||
int err;
|
||||
u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
u8 rc4salt[4];
|
||||
struct crypto_hash *md5;
|
||||
struct crypto_hash *hmac_md5;
|
||||
|
||||
if (cksumkey == NULL)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if (cksumout->len < kctx->gk5e->cksumlength) {
|
||||
dprintk("%s: checksum buffer length, %u, too small for %s\n",
|
||||
__func__, cksumout->len, kctx->gk5e->name);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
if (arcfour_hmac_md5_usage_to_salt(usage, rc4salt)) {
|
||||
dprintk("%s: invalid usage value %u\n", __func__, usage);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
md5 = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(md5))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
hmac_md5 = crypto_alloc_hash(kctx->gk5e->cksum_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(hmac_md5)) {
|
||||
crypto_free_hash(md5);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
desc.tfm = md5;
|
||||
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out;
|
||||
sg_init_one(sg, rc4salt, 4);
|
||||
err = crypto_hash_update(&desc, sg, 4);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sg_init_one(sg, header, hdrlen);
|
||||
err = crypto_hash_update(&desc, sg, hdrlen);
|
||||
if (err)
|
||||
goto out;
|
||||
err = xdr_process_buf(body, body_offset, body->len - body_offset,
|
||||
checksummer, &desc);
|
||||
if (err)
|
||||
goto out;
|
||||
err = crypto_hash_final(&desc, checksumdata);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
desc.tfm = hmac_md5;
|
||||
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out;
|
||||
err = crypto_hash_setkey(hmac_md5, cksumkey, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
sg_init_one(sg, checksumdata, crypto_hash_digestsize(md5));
|
||||
err = crypto_hash_digest(&desc, sg, crypto_hash_digestsize(md5),
|
||||
checksumdata);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
memcpy(cksumout->data, checksumdata, kctx->gk5e->cksumlength);
|
||||
cksumout->len = kctx->gk5e->cksumlength;
|
||||
out:
|
||||
crypto_free_hash(md5);
|
||||
crypto_free_hash(hmac_md5);
|
||||
return err ? GSS_S_FAILURE : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* checksum the plaintext data and hdrlen bytes of the token header
|
||||
* The checksum is performed over the first 8 bytes of the
|
||||
* gss token header and then over the data body
|
||||
*/
|
||||
u32
|
||||
make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen,
|
||||
struct xdr_buf *body, int body_offset, u8 *cksumkey,
|
||||
unsigned int usage, struct xdr_netobj *cksumout)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[1];
|
||||
int err;
|
||||
u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
unsigned int checksumlen;
|
||||
|
||||
if (kctx->gk5e->ctype == CKSUMTYPE_HMAC_MD5_ARCFOUR)
|
||||
return make_checksum_hmac_md5(kctx, header, hdrlen,
|
||||
body, body_offset,
|
||||
cksumkey, usage, cksumout);
|
||||
|
||||
if (cksumout->len < kctx->gk5e->cksumlength) {
|
||||
dprintk("%s: checksum buffer length, %u, too small for %s\n",
|
||||
__func__, cksumout->len, kctx->gk5e->name);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
desc.tfm = crypto_alloc_hash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(desc.tfm))
|
||||
return GSS_S_FAILURE;
|
||||
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
checksumlen = crypto_hash_digestsize(desc.tfm);
|
||||
|
||||
if (cksumkey != NULL) {
|
||||
err = crypto_hash_setkey(desc.tfm, cksumkey,
|
||||
kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out;
|
||||
sg_init_one(sg, header, hdrlen);
|
||||
err = crypto_hash_update(&desc, sg, hdrlen);
|
||||
if (err)
|
||||
goto out;
|
||||
err = xdr_process_buf(body, body_offset, body->len - body_offset,
|
||||
checksummer, &desc);
|
||||
if (err)
|
||||
goto out;
|
||||
err = crypto_hash_final(&desc, checksumdata);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
switch (kctx->gk5e->ctype) {
|
||||
case CKSUMTYPE_RSA_MD5:
|
||||
err = kctx->gk5e->encrypt(kctx->seq, NULL, checksumdata,
|
||||
checksumdata, checksumlen);
|
||||
if (err)
|
||||
goto out;
|
||||
memcpy(cksumout->data,
|
||||
checksumdata + checksumlen - kctx->gk5e->cksumlength,
|
||||
kctx->gk5e->cksumlength);
|
||||
break;
|
||||
case CKSUMTYPE_HMAC_SHA1_DES3:
|
||||
memcpy(cksumout->data, checksumdata, kctx->gk5e->cksumlength);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
cksumout->len = kctx->gk5e->cksumlength;
|
||||
out:
|
||||
crypto_free_hash(desc.tfm);
|
||||
return err ? GSS_S_FAILURE : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* checksum the plaintext data and hdrlen bytes of the token header
|
||||
* Per rfc4121, sec. 4.2.4, the checksum is performed over the data
|
||||
* body then over the first 16 octets of the MIC token
|
||||
* Inclusion of the header data in the calculation of the
|
||||
* checksum is optional.
|
||||
*/
|
||||
u32
|
||||
make_checksum_v2(struct krb5_ctx *kctx, char *header, int hdrlen,
|
||||
struct xdr_buf *body, int body_offset, u8 *cksumkey,
|
||||
unsigned int usage, struct xdr_netobj *cksumout)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[1];
|
||||
int err;
|
||||
u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
unsigned int checksumlen;
|
||||
|
||||
if (kctx->gk5e->keyed_cksum == 0) {
|
||||
dprintk("%s: expected keyed hash for %s\n",
|
||||
__func__, kctx->gk5e->name);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
if (cksumkey == NULL) {
|
||||
dprintk("%s: no key supplied for %s\n",
|
||||
__func__, kctx->gk5e->name);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
desc.tfm = crypto_alloc_hash(kctx->gk5e->cksum_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(desc.tfm))
|
||||
return GSS_S_FAILURE;
|
||||
checksumlen = crypto_hash_digestsize(desc.tfm);
|
||||
desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
||||
|
||||
err = crypto_hash_setkey(desc.tfm, cksumkey, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out;
|
||||
err = xdr_process_buf(body, body_offset, body->len - body_offset,
|
||||
checksummer, &desc);
|
||||
if (err)
|
||||
goto out;
|
||||
if (header != NULL) {
|
||||
sg_init_one(sg, header, hdrlen);
|
||||
err = crypto_hash_update(&desc, sg, hdrlen);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = crypto_hash_final(&desc, checksumdata);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
cksumout->len = kctx->gk5e->cksumlength;
|
||||
|
||||
switch (kctx->gk5e->ctype) {
|
||||
case CKSUMTYPE_HMAC_SHA1_96_AES128:
|
||||
case CKSUMTYPE_HMAC_SHA1_96_AES256:
|
||||
/* note that this truncates the hash */
|
||||
memcpy(cksumout->data, checksumdata, kctx->gk5e->cksumlength);
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
out:
|
||||
crypto_free_hash(desc.tfm);
|
||||
return err ? GSS_S_FAILURE : 0;
|
||||
}
|
||||
|
||||
struct encryptor_desc {
|
||||
u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
|
||||
struct blkcipher_desc desc;
|
||||
int pos;
|
||||
struct xdr_buf *outbuf;
|
||||
struct page **pages;
|
||||
struct scatterlist infrags[4];
|
||||
struct scatterlist outfrags[4];
|
||||
int fragno;
|
||||
int fraglen;
|
||||
};
|
||||
|
||||
static int
|
||||
encryptor(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct encryptor_desc *desc = data;
|
||||
struct xdr_buf *outbuf = desc->outbuf;
|
||||
struct page *in_page;
|
||||
int thislen = desc->fraglen + sg->length;
|
||||
int fraglen, ret;
|
||||
int page_pos;
|
||||
|
||||
/* Worst case is 4 fragments: head, end of page 1, start
|
||||
* of page 2, tail. Anything more is a bug. */
|
||||
BUG_ON(desc->fragno > 3);
|
||||
|
||||
page_pos = desc->pos - outbuf->head[0].iov_len;
|
||||
if (page_pos >= 0 && page_pos < outbuf->page_len) {
|
||||
/* pages are not in place: */
|
||||
int i = (page_pos + outbuf->page_base) >> PAGE_CACHE_SHIFT;
|
||||
in_page = desc->pages[i];
|
||||
} else {
|
||||
in_page = sg_page(sg);
|
||||
}
|
||||
sg_set_page(&desc->infrags[desc->fragno], in_page, sg->length,
|
||||
sg->offset);
|
||||
sg_set_page(&desc->outfrags[desc->fragno], sg_page(sg), sg->length,
|
||||
sg->offset);
|
||||
desc->fragno++;
|
||||
desc->fraglen += sg->length;
|
||||
desc->pos += sg->length;
|
||||
|
||||
fraglen = thislen & (crypto_blkcipher_blocksize(desc->desc.tfm) - 1);
|
||||
thislen -= fraglen;
|
||||
|
||||
if (thislen == 0)
|
||||
return 0;
|
||||
|
||||
sg_mark_end(&desc->infrags[desc->fragno - 1]);
|
||||
sg_mark_end(&desc->outfrags[desc->fragno - 1]);
|
||||
|
||||
ret = crypto_blkcipher_encrypt_iv(&desc->desc, desc->outfrags,
|
||||
desc->infrags, thislen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sg_init_table(desc->infrags, 4);
|
||||
sg_init_table(desc->outfrags, 4);
|
||||
|
||||
if (fraglen) {
|
||||
sg_set_page(&desc->outfrags[0], sg_page(sg), fraglen,
|
||||
sg->offset + sg->length - fraglen);
|
||||
desc->infrags[0] = desc->outfrags[0];
|
||||
sg_assign_page(&desc->infrags[0], in_page);
|
||||
desc->fragno = 1;
|
||||
desc->fraglen = fraglen;
|
||||
} else {
|
||||
desc->fragno = 0;
|
||||
desc->fraglen = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gss_encrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
|
||||
int offset, struct page **pages)
|
||||
{
|
||||
int ret;
|
||||
struct encryptor_desc desc;
|
||||
|
||||
BUG_ON((buf->len - offset) % crypto_blkcipher_blocksize(tfm) != 0);
|
||||
|
||||
memset(desc.iv, 0, sizeof(desc.iv));
|
||||
desc.desc.tfm = tfm;
|
||||
desc.desc.info = desc.iv;
|
||||
desc.desc.flags = 0;
|
||||
desc.pos = offset;
|
||||
desc.outbuf = buf;
|
||||
desc.pages = pages;
|
||||
desc.fragno = 0;
|
||||
desc.fraglen = 0;
|
||||
|
||||
sg_init_table(desc.infrags, 4);
|
||||
sg_init_table(desc.outfrags, 4);
|
||||
|
||||
ret = xdr_process_buf(buf, offset, buf->len - offset, encryptor, &desc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct decryptor_desc {
|
||||
u8 iv[GSS_KRB5_MAX_BLOCKSIZE];
|
||||
struct blkcipher_desc desc;
|
||||
struct scatterlist frags[4];
|
||||
int fragno;
|
||||
int fraglen;
|
||||
};
|
||||
|
||||
static int
|
||||
decryptor(struct scatterlist *sg, void *data)
|
||||
{
|
||||
struct decryptor_desc *desc = data;
|
||||
int thislen = desc->fraglen + sg->length;
|
||||
int fraglen, ret;
|
||||
|
||||
/* Worst case is 4 fragments: head, end of page 1, start
|
||||
* of page 2, tail. Anything more is a bug. */
|
||||
BUG_ON(desc->fragno > 3);
|
||||
sg_set_page(&desc->frags[desc->fragno], sg_page(sg), sg->length,
|
||||
sg->offset);
|
||||
desc->fragno++;
|
||||
desc->fraglen += sg->length;
|
||||
|
||||
fraglen = thislen & (crypto_blkcipher_blocksize(desc->desc.tfm) - 1);
|
||||
thislen -= fraglen;
|
||||
|
||||
if (thislen == 0)
|
||||
return 0;
|
||||
|
||||
sg_mark_end(&desc->frags[desc->fragno - 1]);
|
||||
|
||||
ret = crypto_blkcipher_decrypt_iv(&desc->desc, desc->frags,
|
||||
desc->frags, thislen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sg_init_table(desc->frags, 4);
|
||||
|
||||
if (fraglen) {
|
||||
sg_set_page(&desc->frags[0], sg_page(sg), fraglen,
|
||||
sg->offset + sg->length - fraglen);
|
||||
desc->fragno = 1;
|
||||
desc->fraglen = fraglen;
|
||||
} else {
|
||||
desc->fragno = 0;
|
||||
desc->fraglen = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gss_decrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
|
||||
int offset)
|
||||
{
|
||||
struct decryptor_desc desc;
|
||||
|
||||
/* XXXJBF: */
|
||||
BUG_ON((buf->len - offset) % crypto_blkcipher_blocksize(tfm) != 0);
|
||||
|
||||
memset(desc.iv, 0, sizeof(desc.iv));
|
||||
desc.desc.tfm = tfm;
|
||||
desc.desc.info = desc.iv;
|
||||
desc.desc.flags = 0;
|
||||
desc.fragno = 0;
|
||||
desc.fraglen = 0;
|
||||
|
||||
sg_init_table(desc.frags, 4);
|
||||
|
||||
return xdr_process_buf(buf, offset, buf->len - offset, decryptor, &desc);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function makes the assumption that it was ultimately called
|
||||
* from gss_wrap().
|
||||
*
|
||||
* The client auth_gss code moves any existing tail data into a
|
||||
* separate page before calling gss_wrap.
|
||||
* The server svcauth_gss code ensures that both the head and the
|
||||
* tail have slack space of RPC_MAX_AUTH_SIZE before calling gss_wrap.
|
||||
*
|
||||
* Even with that guarantee, this function may be called more than
|
||||
* once in the processing of gss_wrap(). The best we can do is
|
||||
* verify at compile-time (see GSS_KRB5_SLACK_CHECK) that the
|
||||
* largest expected shift will fit within RPC_MAX_AUTH_SIZE.
|
||||
* At run-time we can verify that a single invocation of this
|
||||
* function doesn't attempt to use more the RPC_MAX_AUTH_SIZE.
|
||||
*/
|
||||
|
||||
int
|
||||
xdr_extend_head(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)
|
||||
{
|
||||
u8 *p;
|
||||
|
||||
if (shiftlen == 0)
|
||||
return 0;
|
||||
|
||||
BUILD_BUG_ON(GSS_KRB5_MAX_SLACK_NEEDED > RPC_MAX_AUTH_SIZE);
|
||||
BUG_ON(shiftlen > RPC_MAX_AUTH_SIZE);
|
||||
|
||||
p = buf->head[0].iov_base + base;
|
||||
|
||||
memmove(p + shiftlen, p, buf->head[0].iov_len - base);
|
||||
|
||||
buf->head[0].iov_len += shiftlen;
|
||||
buf->len += shiftlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_krb5_cts_crypt(struct crypto_blkcipher *cipher, struct xdr_buf *buf,
|
||||
u32 offset, u8 *iv, struct page **pages, int encrypt)
|
||||
{
|
||||
u32 ret;
|
||||
struct scatterlist sg[1];
|
||||
struct blkcipher_desc desc = { .tfm = cipher, .info = iv };
|
||||
u8 data[GSS_KRB5_MAX_BLOCKSIZE * 2];
|
||||
struct page **save_pages;
|
||||
u32 len = buf->len - offset;
|
||||
|
||||
if (len > ARRAY_SIZE(data)) {
|
||||
WARN_ON(0);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* For encryption, we want to read from the cleartext
|
||||
* page cache pages, and write the encrypted data to
|
||||
* the supplied xdr_buf pages.
|
||||
*/
|
||||
save_pages = buf->pages;
|
||||
if (encrypt)
|
||||
buf->pages = pages;
|
||||
|
||||
ret = read_bytes_from_xdr_buf(buf, offset, data, len);
|
||||
buf->pages = save_pages;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
sg_init_one(sg, data, len);
|
||||
|
||||
if (encrypt)
|
||||
ret = crypto_blkcipher_encrypt_iv(&desc, sg, sg, len);
|
||||
else
|
||||
ret = crypto_blkcipher_decrypt_iv(&desc, sg, sg, len);
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = write_bytes_to_xdr_buf(buf, offset, data, len);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_krb5_aes_encrypt(struct krb5_ctx *kctx, u32 offset,
|
||||
struct xdr_buf *buf, struct page **pages)
|
||||
{
|
||||
u32 err;
|
||||
struct xdr_netobj hmac;
|
||||
u8 *cksumkey;
|
||||
u8 *ecptr;
|
||||
struct crypto_blkcipher *cipher, *aux_cipher;
|
||||
int blocksize;
|
||||
struct page **save_pages;
|
||||
int nblocks, nbytes;
|
||||
struct encryptor_desc desc;
|
||||
u32 cbcbytes;
|
||||
unsigned int usage;
|
||||
|
||||
if (kctx->initiate) {
|
||||
cipher = kctx->initiator_enc;
|
||||
aux_cipher = kctx->initiator_enc_aux;
|
||||
cksumkey = kctx->initiator_integ;
|
||||
usage = KG_USAGE_INITIATOR_SEAL;
|
||||
} else {
|
||||
cipher = kctx->acceptor_enc;
|
||||
aux_cipher = kctx->acceptor_enc_aux;
|
||||
cksumkey = kctx->acceptor_integ;
|
||||
usage = KG_USAGE_ACCEPTOR_SEAL;
|
||||
}
|
||||
blocksize = crypto_blkcipher_blocksize(cipher);
|
||||
|
||||
/* hide the gss token header and insert the confounder */
|
||||
offset += GSS_KRB5_TOK_HDR_LEN;
|
||||
if (xdr_extend_head(buf, offset, kctx->gk5e->conflen))
|
||||
return GSS_S_FAILURE;
|
||||
gss_krb5_make_confounder(buf->head[0].iov_base + offset, kctx->gk5e->conflen);
|
||||
offset -= GSS_KRB5_TOK_HDR_LEN;
|
||||
|
||||
if (buf->tail[0].iov_base != NULL) {
|
||||
ecptr = buf->tail[0].iov_base + buf->tail[0].iov_len;
|
||||
} else {
|
||||
buf->tail[0].iov_base = buf->head[0].iov_base
|
||||
+ buf->head[0].iov_len;
|
||||
buf->tail[0].iov_len = 0;
|
||||
ecptr = buf->tail[0].iov_base;
|
||||
}
|
||||
|
||||
/* copy plaintext gss token header after filler (if any) */
|
||||
memcpy(ecptr, buf->head[0].iov_base + offset, GSS_KRB5_TOK_HDR_LEN);
|
||||
buf->tail[0].iov_len += GSS_KRB5_TOK_HDR_LEN;
|
||||
buf->len += GSS_KRB5_TOK_HDR_LEN;
|
||||
|
||||
/* Do the HMAC */
|
||||
hmac.len = GSS_KRB5_MAX_CKSUM_LEN;
|
||||
hmac.data = buf->tail[0].iov_base + buf->tail[0].iov_len;
|
||||
|
||||
/*
|
||||
* When we are called, pages points to the real page cache
|
||||
* data -- which we can't go and encrypt! buf->pages points
|
||||
* to scratch pages which we are going to send off to the
|
||||
* client/server. Swap in the plaintext pages to calculate
|
||||
* the hmac.
|
||||
*/
|
||||
save_pages = buf->pages;
|
||||
buf->pages = pages;
|
||||
|
||||
err = make_checksum_v2(kctx, NULL, 0, buf,
|
||||
offset + GSS_KRB5_TOK_HDR_LEN,
|
||||
cksumkey, usage, &hmac);
|
||||
buf->pages = save_pages;
|
||||
if (err)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
nbytes = buf->len - offset - GSS_KRB5_TOK_HDR_LEN;
|
||||
nblocks = (nbytes + blocksize - 1) / blocksize;
|
||||
cbcbytes = 0;
|
||||
if (nblocks > 2)
|
||||
cbcbytes = (nblocks - 2) * blocksize;
|
||||
|
||||
memset(desc.iv, 0, sizeof(desc.iv));
|
||||
|
||||
if (cbcbytes) {
|
||||
desc.pos = offset + GSS_KRB5_TOK_HDR_LEN;
|
||||
desc.fragno = 0;
|
||||
desc.fraglen = 0;
|
||||
desc.pages = pages;
|
||||
desc.outbuf = buf;
|
||||
desc.desc.info = desc.iv;
|
||||
desc.desc.flags = 0;
|
||||
desc.desc.tfm = aux_cipher;
|
||||
|
||||
sg_init_table(desc.infrags, 4);
|
||||
sg_init_table(desc.outfrags, 4);
|
||||
|
||||
err = xdr_process_buf(buf, offset + GSS_KRB5_TOK_HDR_LEN,
|
||||
cbcbytes, encryptor, &desc);
|
||||
if (err)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Make sure IV carries forward from any CBC results. */
|
||||
err = gss_krb5_cts_crypt(cipher, buf,
|
||||
offset + GSS_KRB5_TOK_HDR_LEN + cbcbytes,
|
||||
desc.iv, pages, 1);
|
||||
if (err) {
|
||||
err = GSS_S_FAILURE;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Now update buf to account for HMAC */
|
||||
buf->tail[0].iov_len += kctx->gk5e->cksumlength;
|
||||
buf->len += kctx->gk5e->cksumlength;
|
||||
|
||||
out_err:
|
||||
if (err)
|
||||
err = GSS_S_FAILURE;
|
||||
return err;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_krb5_aes_decrypt(struct krb5_ctx *kctx, u32 offset, struct xdr_buf *buf,
|
||||
u32 *headskip, u32 *tailskip)
|
||||
{
|
||||
struct xdr_buf subbuf;
|
||||
u32 ret = 0;
|
||||
u8 *cksum_key;
|
||||
struct crypto_blkcipher *cipher, *aux_cipher;
|
||||
struct xdr_netobj our_hmac_obj;
|
||||
u8 our_hmac[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
u8 pkt_hmac[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
int nblocks, blocksize, cbcbytes;
|
||||
struct decryptor_desc desc;
|
||||
unsigned int usage;
|
||||
|
||||
if (kctx->initiate) {
|
||||
cipher = kctx->acceptor_enc;
|
||||
aux_cipher = kctx->acceptor_enc_aux;
|
||||
cksum_key = kctx->acceptor_integ;
|
||||
usage = KG_USAGE_ACCEPTOR_SEAL;
|
||||
} else {
|
||||
cipher = kctx->initiator_enc;
|
||||
aux_cipher = kctx->initiator_enc_aux;
|
||||
cksum_key = kctx->initiator_integ;
|
||||
usage = KG_USAGE_INITIATOR_SEAL;
|
||||
}
|
||||
blocksize = crypto_blkcipher_blocksize(cipher);
|
||||
|
||||
|
||||
/* create a segment skipping the header and leaving out the checksum */
|
||||
xdr_buf_subsegment(buf, &subbuf, offset + GSS_KRB5_TOK_HDR_LEN,
|
||||
(buf->len - offset - GSS_KRB5_TOK_HDR_LEN -
|
||||
kctx->gk5e->cksumlength));
|
||||
|
||||
nblocks = (subbuf.len + blocksize - 1) / blocksize;
|
||||
|
||||
cbcbytes = 0;
|
||||
if (nblocks > 2)
|
||||
cbcbytes = (nblocks - 2) * blocksize;
|
||||
|
||||
memset(desc.iv, 0, sizeof(desc.iv));
|
||||
|
||||
if (cbcbytes) {
|
||||
desc.fragno = 0;
|
||||
desc.fraglen = 0;
|
||||
desc.desc.info = desc.iv;
|
||||
desc.desc.flags = 0;
|
||||
desc.desc.tfm = aux_cipher;
|
||||
|
||||
sg_init_table(desc.frags, 4);
|
||||
|
||||
ret = xdr_process_buf(&subbuf, 0, cbcbytes, decryptor, &desc);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Make sure IV carries forward from any CBC results. */
|
||||
ret = gss_krb5_cts_crypt(cipher, &subbuf, cbcbytes, desc.iv, NULL, 0);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
|
||||
/* Calculate our hmac over the plaintext data */
|
||||
our_hmac_obj.len = sizeof(our_hmac);
|
||||
our_hmac_obj.data = our_hmac;
|
||||
|
||||
ret = make_checksum_v2(kctx, NULL, 0, &subbuf, 0,
|
||||
cksum_key, usage, &our_hmac_obj);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/* Get the packet's hmac value */
|
||||
ret = read_bytes_from_xdr_buf(buf, buf->len - kctx->gk5e->cksumlength,
|
||||
pkt_hmac, kctx->gk5e->cksumlength);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
if (memcmp(pkt_hmac, our_hmac, kctx->gk5e->cksumlength) != 0) {
|
||||
ret = GSS_S_BAD_SIG;
|
||||
goto out_err;
|
||||
}
|
||||
*headskip = kctx->gk5e->conflen;
|
||||
*tailskip = kctx->gk5e->cksumlength;
|
||||
out_err:
|
||||
if (ret && ret != GSS_S_BAD_SIG)
|
||||
ret = GSS_S_FAILURE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute Kseq given the initial session key and the checksum.
|
||||
* Set the key of the given cipher.
|
||||
*/
|
||||
int
|
||||
krb5_rc4_setup_seq_key(struct krb5_ctx *kctx, struct crypto_blkcipher *cipher,
|
||||
unsigned char *cksum)
|
||||
{
|
||||
struct crypto_hash *hmac;
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[1];
|
||||
u8 Kseq[GSS_KRB5_MAX_KEYLEN];
|
||||
u32 zeroconstant = 0;
|
||||
int err;
|
||||
|
||||
dprintk("%s: entered\n", __func__);
|
||||
|
||||
hmac = crypto_alloc_hash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(hmac)) {
|
||||
dprintk("%s: error %ld, allocating hash '%s'\n",
|
||||
__func__, PTR_ERR(hmac), kctx->gk5e->cksum_name);
|
||||
return PTR_ERR(hmac);
|
||||
}
|
||||
|
||||
desc.tfm = hmac;
|
||||
desc.flags = 0;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Compute intermediate Kseq from session key */
|
||||
err = crypto_hash_setkey(hmac, kctx->Ksess, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
sg_init_table(sg, 1);
|
||||
sg_set_buf(sg, &zeroconstant, 4);
|
||||
|
||||
err = crypto_hash_digest(&desc, sg, 4, Kseq);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Compute final Kseq from the checksum and intermediate Kseq */
|
||||
err = crypto_hash_setkey(hmac, Kseq, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
sg_set_buf(sg, cksum, 8);
|
||||
|
||||
err = crypto_hash_digest(&desc, sg, 8, Kseq);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = crypto_blkcipher_setkey(cipher, Kseq, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = 0;
|
||||
|
||||
out_err:
|
||||
crypto_free_hash(hmac);
|
||||
dprintk("%s: returning %d\n", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute Kcrypt given the initial session key and the plaintext seqnum.
|
||||
* Set the key of cipher kctx->enc.
|
||||
*/
|
||||
int
|
||||
krb5_rc4_setup_enc_key(struct krb5_ctx *kctx, struct crypto_blkcipher *cipher,
|
||||
s32 seqnum)
|
||||
{
|
||||
struct crypto_hash *hmac;
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[1];
|
||||
u8 Kcrypt[GSS_KRB5_MAX_KEYLEN];
|
||||
u8 zeroconstant[4] = {0};
|
||||
u8 seqnumarray[4];
|
||||
int err, i;
|
||||
|
||||
dprintk("%s: entered, seqnum %u\n", __func__, seqnum);
|
||||
|
||||
hmac = crypto_alloc_hash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(hmac)) {
|
||||
dprintk("%s: error %ld, allocating hash '%s'\n",
|
||||
__func__, PTR_ERR(hmac), kctx->gk5e->cksum_name);
|
||||
return PTR_ERR(hmac);
|
||||
}
|
||||
|
||||
desc.tfm = hmac;
|
||||
desc.flags = 0;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Compute intermediate Kcrypt from session key */
|
||||
for (i = 0; i < kctx->gk5e->keylength; i++)
|
||||
Kcrypt[i] = kctx->Ksess[i] ^ 0xf0;
|
||||
|
||||
err = crypto_hash_setkey(hmac, Kcrypt, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
sg_init_table(sg, 1);
|
||||
sg_set_buf(sg, zeroconstant, 4);
|
||||
|
||||
err = crypto_hash_digest(&desc, sg, 4, Kcrypt);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
/* Compute final Kcrypt from the seqnum and intermediate Kcrypt */
|
||||
err = crypto_hash_setkey(hmac, Kcrypt, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
seqnumarray[0] = (unsigned char) ((seqnum >> 24) & 0xff);
|
||||
seqnumarray[1] = (unsigned char) ((seqnum >> 16) & 0xff);
|
||||
seqnumarray[2] = (unsigned char) ((seqnum >> 8) & 0xff);
|
||||
seqnumarray[3] = (unsigned char) ((seqnum >> 0) & 0xff);
|
||||
|
||||
sg_set_buf(sg, seqnumarray, 4);
|
||||
|
||||
err = crypto_hash_digest(&desc, sg, 4, Kcrypt);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = crypto_blkcipher_setkey(cipher, Kcrypt, kctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err;
|
||||
|
||||
err = 0;
|
||||
|
||||
out_err:
|
||||
crypto_free_hash(hmac);
|
||||
dprintk("%s: returning %d\n", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
327
net/sunrpc/auth_gss/gss_krb5_keys.c
Normal file
327
net/sunrpc/auth_gss/gss_krb5_keys.c
Normal file
|
@ -0,0 +1,327 @@
|
|||
/*
|
||||
* COPYRIGHT (c) 2008
|
||||
* The Regents of the University of Michigan
|
||||
* ALL RIGHTS RESERVED
|
||||
*
|
||||
* Permission is granted to use, copy, create derivative works
|
||||
* and redistribute this software and such derivative works
|
||||
* for any purpose, so long as the name of The University of
|
||||
* Michigan is not used in any advertising or publicity
|
||||
* pertaining to the use of distribution of this software
|
||||
* without specific, written prior authorization. If the
|
||||
* above copyright notice or any other identification of the
|
||||
* University of Michigan is included in any copy of any
|
||||
* portion of this software, then the disclaimer below must
|
||||
* also be included.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
|
||||
* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
|
||||
* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
|
||||
* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
|
||||
* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
|
||||
* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
|
||||
* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <linux/err.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/lcm.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the n-fold function as described in rfc3961, sec 5.1
|
||||
* Taken from MIT Kerberos and modified.
|
||||
*/
|
||||
|
||||
static void krb5_nfold(u32 inbits, const u8 *in,
|
||||
u32 outbits, u8 *out)
|
||||
{
|
||||
unsigned long ulcm;
|
||||
int byte, i, msbit;
|
||||
|
||||
/* the code below is more readable if I make these bytes
|
||||
instead of bits */
|
||||
|
||||
inbits >>= 3;
|
||||
outbits >>= 3;
|
||||
|
||||
/* first compute lcm(n,k) */
|
||||
ulcm = lcm(inbits, outbits);
|
||||
|
||||
/* now do the real work */
|
||||
|
||||
memset(out, 0, outbits);
|
||||
byte = 0;
|
||||
|
||||
/* this will end up cycling through k lcm(k,n)/k times, which
|
||||
is correct */
|
||||
for (i = ulcm-1; i >= 0; i--) {
|
||||
/* compute the msbit in k which gets added into this byte */
|
||||
msbit = (
|
||||
/* first, start with the msbit in the first,
|
||||
* unrotated byte */
|
||||
((inbits << 3) - 1)
|
||||
/* then, for each byte, shift to the right
|
||||
* for each repetition */
|
||||
+ (((inbits << 3) + 13) * (i/inbits))
|
||||
/* last, pick out the correct byte within
|
||||
* that shifted repetition */
|
||||
+ ((inbits - (i % inbits)) << 3)
|
||||
) % (inbits << 3);
|
||||
|
||||
/* pull out the byte value itself */
|
||||
byte += (((in[((inbits - 1) - (msbit >> 3)) % inbits] << 8)|
|
||||
(in[((inbits) - (msbit >> 3)) % inbits]))
|
||||
>> ((msbit & 7) + 1)) & 0xff;
|
||||
|
||||
/* do the addition */
|
||||
byte += out[i % outbits];
|
||||
out[i % outbits] = byte & 0xff;
|
||||
|
||||
/* keep around the carry bit, if any */
|
||||
byte >>= 8;
|
||||
|
||||
}
|
||||
|
||||
/* if there's a carry bit left over, add it back in */
|
||||
if (byte) {
|
||||
for (i = outbits - 1; i >= 0; i--) {
|
||||
/* do the addition */
|
||||
byte += out[i];
|
||||
out[i] = byte & 0xff;
|
||||
|
||||
/* keep around the carry bit, if any */
|
||||
byte >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the DK (derive_key) function as described in rfc3961, sec 5.1
|
||||
* Taken from MIT Kerberos and modified.
|
||||
*/
|
||||
|
||||
u32 krb5_derive_key(const struct gss_krb5_enctype *gk5e,
|
||||
const struct xdr_netobj *inkey,
|
||||
struct xdr_netobj *outkey,
|
||||
const struct xdr_netobj *in_constant,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
size_t blocksize, keybytes, keylength, n;
|
||||
unsigned char *inblockdata, *outblockdata, *rawkey;
|
||||
struct xdr_netobj inblock, outblock;
|
||||
struct crypto_blkcipher *cipher;
|
||||
u32 ret = EINVAL;
|
||||
|
||||
blocksize = gk5e->blocksize;
|
||||
keybytes = gk5e->keybytes;
|
||||
keylength = gk5e->keylength;
|
||||
|
||||
if ((inkey->len != keylength) || (outkey->len != keylength))
|
||||
goto err_return;
|
||||
|
||||
cipher = crypto_alloc_blkcipher(gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(cipher))
|
||||
goto err_return;
|
||||
if (crypto_blkcipher_setkey(cipher, inkey->data, inkey->len))
|
||||
goto err_return;
|
||||
|
||||
/* allocate and set up buffers */
|
||||
|
||||
ret = ENOMEM;
|
||||
inblockdata = kmalloc(blocksize, gfp_mask);
|
||||
if (inblockdata == NULL)
|
||||
goto err_free_cipher;
|
||||
|
||||
outblockdata = kmalloc(blocksize, gfp_mask);
|
||||
if (outblockdata == NULL)
|
||||
goto err_free_in;
|
||||
|
||||
rawkey = kmalloc(keybytes, gfp_mask);
|
||||
if (rawkey == NULL)
|
||||
goto err_free_out;
|
||||
|
||||
inblock.data = (char *) inblockdata;
|
||||
inblock.len = blocksize;
|
||||
|
||||
outblock.data = (char *) outblockdata;
|
||||
outblock.len = blocksize;
|
||||
|
||||
/* initialize the input block */
|
||||
|
||||
if (in_constant->len == inblock.len) {
|
||||
memcpy(inblock.data, in_constant->data, inblock.len);
|
||||
} else {
|
||||
krb5_nfold(in_constant->len * 8, in_constant->data,
|
||||
inblock.len * 8, inblock.data);
|
||||
}
|
||||
|
||||
/* loop encrypting the blocks until enough key bytes are generated */
|
||||
|
||||
n = 0;
|
||||
while (n < keybytes) {
|
||||
(*(gk5e->encrypt))(cipher, NULL, inblock.data,
|
||||
outblock.data, inblock.len);
|
||||
|
||||
if ((keybytes - n) <= outblock.len) {
|
||||
memcpy(rawkey + n, outblock.data, (keybytes - n));
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(rawkey + n, outblock.data, outblock.len);
|
||||
memcpy(inblock.data, outblock.data, outblock.len);
|
||||
n += outblock.len;
|
||||
}
|
||||
|
||||
/* postprocess the key */
|
||||
|
||||
inblock.data = (char *) rawkey;
|
||||
inblock.len = keybytes;
|
||||
|
||||
BUG_ON(gk5e->mk_key == NULL);
|
||||
ret = (*(gk5e->mk_key))(gk5e, &inblock, outkey);
|
||||
if (ret) {
|
||||
dprintk("%s: got %d from mk_key function for '%s'\n",
|
||||
__func__, ret, gk5e->encrypt_name);
|
||||
goto err_free_raw;
|
||||
}
|
||||
|
||||
/* clean memory, free resources and exit */
|
||||
|
||||
ret = 0;
|
||||
|
||||
err_free_raw:
|
||||
memset(rawkey, 0, keybytes);
|
||||
kfree(rawkey);
|
||||
err_free_out:
|
||||
memset(outblockdata, 0, blocksize);
|
||||
kfree(outblockdata);
|
||||
err_free_in:
|
||||
memset(inblockdata, 0, blocksize);
|
||||
kfree(inblockdata);
|
||||
err_free_cipher:
|
||||
crypto_free_blkcipher(cipher);
|
||||
err_return:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define smask(step) ((1<<step)-1)
|
||||
#define pstep(x, step) (((x)&smask(step))^(((x)>>step)&smask(step)))
|
||||
#define parity_char(x) pstep(pstep(pstep((x), 4), 2), 1)
|
||||
|
||||
static void mit_des_fixup_key_parity(u8 key[8])
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
key[i] &= 0xfe;
|
||||
key[i] |= 1^parity_char(key[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the des3 key derivation postprocess function
|
||||
*/
|
||||
u32 gss_krb5_des3_make_key(const struct gss_krb5_enctype *gk5e,
|
||||
struct xdr_netobj *randombits,
|
||||
struct xdr_netobj *key)
|
||||
{
|
||||
int i;
|
||||
u32 ret = EINVAL;
|
||||
|
||||
if (key->len != 24) {
|
||||
dprintk("%s: key->len is %d\n", __func__, key->len);
|
||||
goto err_out;
|
||||
}
|
||||
if (randombits->len != 21) {
|
||||
dprintk("%s: randombits->len is %d\n",
|
||||
__func__, randombits->len);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/* take the seven bytes, move them around into the top 7 bits of the
|
||||
8 key bytes, then compute the parity bits. Do this three times. */
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
memcpy(key->data + i*8, randombits->data + i*7, 7);
|
||||
key->data[i*8+7] = (((key->data[i*8]&1)<<1) |
|
||||
((key->data[i*8+1]&1)<<2) |
|
||||
((key->data[i*8+2]&1)<<3) |
|
||||
((key->data[i*8+3]&1)<<4) |
|
||||
((key->data[i*8+4]&1)<<5) |
|
||||
((key->data[i*8+5]&1)<<6) |
|
||||
((key->data[i*8+6]&1)<<7));
|
||||
|
||||
mit_des_fixup_key_parity(key->data + i*8);
|
||||
}
|
||||
ret = 0;
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the aes key derivation postprocess function
|
||||
*/
|
||||
u32 gss_krb5_aes_make_key(const struct gss_krb5_enctype *gk5e,
|
||||
struct xdr_netobj *randombits,
|
||||
struct xdr_netobj *key)
|
||||
{
|
||||
u32 ret = EINVAL;
|
||||
|
||||
if (key->len != 16 && key->len != 32) {
|
||||
dprintk("%s: key->len is %d\n", __func__, key->len);
|
||||
goto err_out;
|
||||
}
|
||||
if (randombits->len != 16 && randombits->len != 32) {
|
||||
dprintk("%s: randombits->len is %d\n",
|
||||
__func__, randombits->len);
|
||||
goto err_out;
|
||||
}
|
||||
if (randombits->len != key->len) {
|
||||
dprintk("%s: randombits->len is %d, key->len is %d\n",
|
||||
__func__, randombits->len, key->len);
|
||||
goto err_out;
|
||||
}
|
||||
memcpy(key->data, randombits->data, key->len);
|
||||
ret = 0;
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
|
788
net/sunrpc/auth_gss/gss_krb5_mech.c
Normal file
788
net/sunrpc/auth_gss/gss_krb5_mech.c
Normal file
|
@ -0,0 +1,788 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_krb5_mech.c
|
||||
*
|
||||
* Copyright (c) 2001-2008 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
* J. Bruce Fields <bfields@umich.edu>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sunrpc/auth.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/crypto.h>
|
||||
#include <linux/sunrpc/gss_krb5_enctypes.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
static struct gss_api_mech gss_kerberos_mech; /* forward declaration */
|
||||
|
||||
static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = {
|
||||
/*
|
||||
* DES (All DES enctypes are mapped to the same gss functionality)
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_DES_CBC_RAW,
|
||||
.ctype = CKSUMTYPE_RSA_MD5,
|
||||
.name = "des-cbc-crc",
|
||||
.encrypt_name = "cbc(des)",
|
||||
.cksum_name = "md5",
|
||||
.encrypt = krb5_encrypt,
|
||||
.decrypt = krb5_decrypt,
|
||||
.mk_key = NULL,
|
||||
.signalg = SGN_ALG_DES_MAC_MD5,
|
||||
.sealalg = SEAL_ALG_DES,
|
||||
.keybytes = 7,
|
||||
.keylength = 8,
|
||||
.blocksize = 8,
|
||||
.conflen = 8,
|
||||
.cksumlength = 8,
|
||||
.keyed_cksum = 0,
|
||||
},
|
||||
/*
|
||||
* RC4-HMAC
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_ARCFOUR_HMAC,
|
||||
.ctype = CKSUMTYPE_HMAC_MD5_ARCFOUR,
|
||||
.name = "rc4-hmac",
|
||||
.encrypt_name = "ecb(arc4)",
|
||||
.cksum_name = "hmac(md5)",
|
||||
.encrypt = krb5_encrypt,
|
||||
.decrypt = krb5_decrypt,
|
||||
.mk_key = NULL,
|
||||
.signalg = SGN_ALG_HMAC_MD5,
|
||||
.sealalg = SEAL_ALG_MICROSOFT_RC4,
|
||||
.keybytes = 16,
|
||||
.keylength = 16,
|
||||
.blocksize = 1,
|
||||
.conflen = 8,
|
||||
.cksumlength = 8,
|
||||
.keyed_cksum = 1,
|
||||
},
|
||||
/*
|
||||
* 3DES
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_DES3_CBC_RAW,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA1_DES3,
|
||||
.name = "des3-hmac-sha1",
|
||||
.encrypt_name = "cbc(des3_ede)",
|
||||
.cksum_name = "hmac(sha1)",
|
||||
.encrypt = krb5_encrypt,
|
||||
.decrypt = krb5_decrypt,
|
||||
.mk_key = gss_krb5_des3_make_key,
|
||||
.signalg = SGN_ALG_HMAC_SHA1_DES3_KD,
|
||||
.sealalg = SEAL_ALG_DES3KD,
|
||||
.keybytes = 21,
|
||||
.keylength = 24,
|
||||
.blocksize = 8,
|
||||
.conflen = 8,
|
||||
.cksumlength = 20,
|
||||
.keyed_cksum = 1,
|
||||
},
|
||||
/*
|
||||
* AES128
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_AES128_CTS_HMAC_SHA1_96,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA1_96_AES128,
|
||||
.name = "aes128-cts",
|
||||
.encrypt_name = "cts(cbc(aes))",
|
||||
.cksum_name = "hmac(sha1)",
|
||||
.encrypt = krb5_encrypt,
|
||||
.decrypt = krb5_decrypt,
|
||||
.mk_key = gss_krb5_aes_make_key,
|
||||
.encrypt_v2 = gss_krb5_aes_encrypt,
|
||||
.decrypt_v2 = gss_krb5_aes_decrypt,
|
||||
.signalg = -1,
|
||||
.sealalg = -1,
|
||||
.keybytes = 16,
|
||||
.keylength = 16,
|
||||
.blocksize = 16,
|
||||
.conflen = 16,
|
||||
.cksumlength = 12,
|
||||
.keyed_cksum = 1,
|
||||
},
|
||||
/*
|
||||
* AES256
|
||||
*/
|
||||
{
|
||||
.etype = ENCTYPE_AES256_CTS_HMAC_SHA1_96,
|
||||
.ctype = CKSUMTYPE_HMAC_SHA1_96_AES256,
|
||||
.name = "aes256-cts",
|
||||
.encrypt_name = "cts(cbc(aes))",
|
||||
.cksum_name = "hmac(sha1)",
|
||||
.encrypt = krb5_encrypt,
|
||||
.decrypt = krb5_decrypt,
|
||||
.mk_key = gss_krb5_aes_make_key,
|
||||
.encrypt_v2 = gss_krb5_aes_encrypt,
|
||||
.decrypt_v2 = gss_krb5_aes_decrypt,
|
||||
.signalg = -1,
|
||||
.sealalg = -1,
|
||||
.keybytes = 32,
|
||||
.keylength = 32,
|
||||
.blocksize = 16,
|
||||
.conflen = 16,
|
||||
.cksumlength = 12,
|
||||
.keyed_cksum = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static const int num_supported_enctypes =
|
||||
ARRAY_SIZE(supported_gss_krb5_enctypes);
|
||||
|
||||
static int
|
||||
supported_gss_krb5_enctype(int etype)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_supported_enctypes; i++)
|
||||
if (supported_gss_krb5_enctypes[i].etype == etype)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct gss_krb5_enctype *
|
||||
get_gss_krb5_enctype(int etype)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < num_supported_enctypes; i++)
|
||||
if (supported_gss_krb5_enctypes[i].etype == etype)
|
||||
return &supported_gss_krb5_enctypes[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const void *
|
||||
simple_get_bytes(const void *p, const void *end, void *res, int len)
|
||||
{
|
||||
const void *q = (const void *)((const char *)p + len);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
memcpy(res, p, len);
|
||||
return q;
|
||||
}
|
||||
|
||||
static const void *
|
||||
simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res)
|
||||
{
|
||||
const void *q;
|
||||
unsigned int len;
|
||||
|
||||
p = simple_get_bytes(p, end, &len, sizeof(len));
|
||||
if (IS_ERR(p))
|
||||
return p;
|
||||
q = (const void *)((const char *)p + len);
|
||||
if (unlikely(q > end || q < p))
|
||||
return ERR_PTR(-EFAULT);
|
||||
res->data = kmemdup(p, len, GFP_NOFS);
|
||||
if (unlikely(res->data == NULL))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
res->len = len;
|
||||
return q;
|
||||
}
|
||||
|
||||
static inline const void *
|
||||
get_key(const void *p, const void *end,
|
||||
struct krb5_ctx *ctx, struct crypto_blkcipher **res)
|
||||
{
|
||||
struct xdr_netobj key;
|
||||
int alg;
|
||||
|
||||
p = simple_get_bytes(p, end, &alg, sizeof(alg));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
|
||||
switch (alg) {
|
||||
case ENCTYPE_DES_CBC_CRC:
|
||||
case ENCTYPE_DES_CBC_MD4:
|
||||
case ENCTYPE_DES_CBC_MD5:
|
||||
/* Map all these key types to ENCTYPE_DES_CBC_RAW */
|
||||
alg = ENCTYPE_DES_CBC_RAW;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!supported_gss_krb5_enctype(alg)) {
|
||||
printk(KERN_WARNING "gss_kerberos_mech: unsupported "
|
||||
"encryption key algorithm %d\n", alg);
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
p = simple_get_netobj(p, end, &key);
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
|
||||
*res = crypto_alloc_blkcipher(ctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(*res)) {
|
||||
printk(KERN_WARNING "gss_kerberos_mech: unable to initialize "
|
||||
"crypto algorithm %s\n", ctx->gk5e->encrypt_name);
|
||||
*res = NULL;
|
||||
goto out_err_free_key;
|
||||
}
|
||||
if (crypto_blkcipher_setkey(*res, key.data, key.len)) {
|
||||
printk(KERN_WARNING "gss_kerberos_mech: error setting key for "
|
||||
"crypto algorithm %s\n", ctx->gk5e->encrypt_name);
|
||||
goto out_err_free_tfm;
|
||||
}
|
||||
|
||||
kfree(key.data);
|
||||
return p;
|
||||
|
||||
out_err_free_tfm:
|
||||
crypto_free_blkcipher(*res);
|
||||
out_err_free_key:
|
||||
kfree(key.data);
|
||||
p = ERR_PTR(-EINVAL);
|
||||
out_err:
|
||||
return p;
|
||||
}
|
||||
|
||||
static int
|
||||
gss_import_v1_context(const void *p, const void *end, struct krb5_ctx *ctx)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
|
||||
/* Old format supports only DES! Any other enctype uses new format */
|
||||
ctx->enctype = ENCTYPE_DES_CBC_RAW;
|
||||
|
||||
ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
|
||||
if (ctx->gk5e == NULL) {
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* The downcall format was designed before we completely understood
|
||||
* the uses of the context fields; so it includes some stuff we
|
||||
* just give some minimal sanity-checking, and some we ignore
|
||||
* completely (like the next twenty bytes): */
|
||||
if (unlikely(p + 20 > end || p + 20 < p)) {
|
||||
p = ERR_PTR(-EFAULT);
|
||||
goto out_err;
|
||||
}
|
||||
p += 20;
|
||||
p = simple_get_bytes(p, end, &tmp, sizeof(tmp));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
if (tmp != SGN_ALG_DES_MAC_MD5) {
|
||||
p = ERR_PTR(-ENOSYS);
|
||||
goto out_err;
|
||||
}
|
||||
p = simple_get_bytes(p, end, &tmp, sizeof(tmp));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
if (tmp != SEAL_ALG_DES) {
|
||||
p = ERR_PTR(-ENOSYS);
|
||||
goto out_err;
|
||||
}
|
||||
p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
p = simple_get_bytes(p, end, &ctx->seq_send, sizeof(ctx->seq_send));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
p = simple_get_netobj(p, end, &ctx->mech_used);
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
p = get_key(p, end, ctx, &ctx->enc);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_mech;
|
||||
p = get_key(p, end, ctx, &ctx->seq);
|
||||
if (IS_ERR(p))
|
||||
goto out_err_free_key1;
|
||||
if (p != end) {
|
||||
p = ERR_PTR(-EFAULT);
|
||||
goto out_err_free_key2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_err_free_key2:
|
||||
crypto_free_blkcipher(ctx->seq);
|
||||
out_err_free_key1:
|
||||
crypto_free_blkcipher(ctx->enc);
|
||||
out_err_free_mech:
|
||||
kfree(ctx->mech_used.data);
|
||||
out_err:
|
||||
return PTR_ERR(p);
|
||||
}
|
||||
|
||||
static struct crypto_blkcipher *
|
||||
context_v2_alloc_cipher(struct krb5_ctx *ctx, const char *cname, u8 *key)
|
||||
{
|
||||
struct crypto_blkcipher *cp;
|
||||
|
||||
cp = crypto_alloc_blkcipher(cname, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(cp)) {
|
||||
dprintk("gss_kerberos_mech: unable to initialize "
|
||||
"crypto algorithm %s\n", cname);
|
||||
return NULL;
|
||||
}
|
||||
if (crypto_blkcipher_setkey(cp, key, ctx->gk5e->keylength)) {
|
||||
dprintk("gss_kerberos_mech: error setting key for "
|
||||
"crypto algorithm %s\n", cname);
|
||||
crypto_free_blkcipher(cp);
|
||||
return NULL;
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_cdata(u8 cdata[GSS_KRB5_K5CLENGTH], u32 usage, u8 seed)
|
||||
{
|
||||
cdata[0] = (usage>>24)&0xff;
|
||||
cdata[1] = (usage>>16)&0xff;
|
||||
cdata[2] = (usage>>8)&0xff;
|
||||
cdata[3] = usage&0xff;
|
||||
cdata[4] = seed;
|
||||
}
|
||||
|
||||
static int
|
||||
context_derive_keys_des3(struct krb5_ctx *ctx, gfp_t gfp_mask)
|
||||
{
|
||||
struct xdr_netobj c, keyin, keyout;
|
||||
u8 cdata[GSS_KRB5_K5CLENGTH];
|
||||
u32 err;
|
||||
|
||||
c.len = GSS_KRB5_K5CLENGTH;
|
||||
c.data = cdata;
|
||||
|
||||
keyin.data = ctx->Ksess;
|
||||
keyin.len = ctx->gk5e->keylength;
|
||||
keyout.len = ctx->gk5e->keylength;
|
||||
|
||||
/* seq uses the raw key */
|
||||
ctx->seq = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
|
||||
ctx->Ksess);
|
||||
if (ctx->seq == NULL)
|
||||
goto out_err;
|
||||
|
||||
ctx->enc = context_v2_alloc_cipher(ctx, ctx->gk5e->encrypt_name,
|
||||
ctx->Ksess);
|
||||
if (ctx->enc == NULL)
|
||||
goto out_free_seq;
|
||||
|
||||
/* derive cksum */
|
||||
set_cdata(cdata, KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM);
|
||||
keyout.data = ctx->cksum;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving cksum key\n",
|
||||
__func__, err);
|
||||
goto out_free_enc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_enc:
|
||||
crypto_free_blkcipher(ctx->enc);
|
||||
out_free_seq:
|
||||
crypto_free_blkcipher(ctx->seq);
|
||||
out_err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that RC4 depends on deriving keys using the sequence
|
||||
* number or the checksum of a token. Therefore, the final keys
|
||||
* cannot be calculated until the token is being constructed!
|
||||
*/
|
||||
static int
|
||||
context_derive_keys_rc4(struct krb5_ctx *ctx)
|
||||
{
|
||||
struct crypto_hash *hmac;
|
||||
char sigkeyconstant[] = "signaturekey";
|
||||
int slen = strlen(sigkeyconstant) + 1; /* include null terminator */
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[1];
|
||||
int err;
|
||||
|
||||
dprintk("RPC: %s: entered\n", __func__);
|
||||
/*
|
||||
* derive cksum (aka Ksign) key
|
||||
*/
|
||||
hmac = crypto_alloc_hash(ctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(hmac)) {
|
||||
dprintk("%s: error %ld allocating hash '%s'\n",
|
||||
__func__, PTR_ERR(hmac), ctx->gk5e->cksum_name);
|
||||
err = PTR_ERR(hmac);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = crypto_hash_setkey(hmac, ctx->Ksess, ctx->gk5e->keylength);
|
||||
if (err)
|
||||
goto out_err_free_hmac;
|
||||
|
||||
sg_init_table(sg, 1);
|
||||
sg_set_buf(sg, sigkeyconstant, slen);
|
||||
|
||||
desc.tfm = hmac;
|
||||
desc.flags = 0;
|
||||
|
||||
err = crypto_hash_init(&desc);
|
||||
if (err)
|
||||
goto out_err_free_hmac;
|
||||
|
||||
err = crypto_hash_digest(&desc, sg, slen, ctx->cksum);
|
||||
if (err)
|
||||
goto out_err_free_hmac;
|
||||
/*
|
||||
* allocate hash, and blkciphers for data and seqnum encryption
|
||||
*/
|
||||
ctx->enc = crypto_alloc_blkcipher(ctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(ctx->enc)) {
|
||||
err = PTR_ERR(ctx->enc);
|
||||
goto out_err_free_hmac;
|
||||
}
|
||||
|
||||
ctx->seq = crypto_alloc_blkcipher(ctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(ctx->seq)) {
|
||||
crypto_free_blkcipher(ctx->enc);
|
||||
err = PTR_ERR(ctx->seq);
|
||||
goto out_err_free_hmac;
|
||||
}
|
||||
|
||||
dprintk("RPC: %s: returning success\n", __func__);
|
||||
|
||||
err = 0;
|
||||
|
||||
out_err_free_hmac:
|
||||
crypto_free_hash(hmac);
|
||||
out_err:
|
||||
dprintk("RPC: %s: returning %d\n", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
context_derive_keys_new(struct krb5_ctx *ctx, gfp_t gfp_mask)
|
||||
{
|
||||
struct xdr_netobj c, keyin, keyout;
|
||||
u8 cdata[GSS_KRB5_K5CLENGTH];
|
||||
u32 err;
|
||||
|
||||
c.len = GSS_KRB5_K5CLENGTH;
|
||||
c.data = cdata;
|
||||
|
||||
keyin.data = ctx->Ksess;
|
||||
keyin.len = ctx->gk5e->keylength;
|
||||
keyout.len = ctx->gk5e->keylength;
|
||||
|
||||
/* initiator seal encryption */
|
||||
set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
|
||||
keyout.data = ctx->initiator_seal;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving initiator_seal key\n",
|
||||
__func__, err);
|
||||
goto out_err;
|
||||
}
|
||||
ctx->initiator_enc = context_v2_alloc_cipher(ctx,
|
||||
ctx->gk5e->encrypt_name,
|
||||
ctx->initiator_seal);
|
||||
if (ctx->initiator_enc == NULL)
|
||||
goto out_err;
|
||||
|
||||
/* acceptor seal encryption */
|
||||
set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_ENCRYPTION);
|
||||
keyout.data = ctx->acceptor_seal;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving acceptor_seal key\n",
|
||||
__func__, err);
|
||||
goto out_free_initiator_enc;
|
||||
}
|
||||
ctx->acceptor_enc = context_v2_alloc_cipher(ctx,
|
||||
ctx->gk5e->encrypt_name,
|
||||
ctx->acceptor_seal);
|
||||
if (ctx->acceptor_enc == NULL)
|
||||
goto out_free_initiator_enc;
|
||||
|
||||
/* initiator sign checksum */
|
||||
set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
|
||||
keyout.data = ctx->initiator_sign;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving initiator_sign key\n",
|
||||
__func__, err);
|
||||
goto out_free_acceptor_enc;
|
||||
}
|
||||
|
||||
/* acceptor sign checksum */
|
||||
set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM);
|
||||
keyout.data = ctx->acceptor_sign;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving acceptor_sign key\n",
|
||||
__func__, err);
|
||||
goto out_free_acceptor_enc;
|
||||
}
|
||||
|
||||
/* initiator seal integrity */
|
||||
set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
|
||||
keyout.data = ctx->initiator_integ;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving initiator_integ key\n",
|
||||
__func__, err);
|
||||
goto out_free_acceptor_enc;
|
||||
}
|
||||
|
||||
/* acceptor seal integrity */
|
||||
set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_INTEGRITY);
|
||||
keyout.data = ctx->acceptor_integ;
|
||||
err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c, gfp_mask);
|
||||
if (err) {
|
||||
dprintk("%s: Error %d deriving acceptor_integ key\n",
|
||||
__func__, err);
|
||||
goto out_free_acceptor_enc;
|
||||
}
|
||||
|
||||
switch (ctx->enctype) {
|
||||
case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
|
||||
case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
|
||||
ctx->initiator_enc_aux =
|
||||
context_v2_alloc_cipher(ctx, "cbc(aes)",
|
||||
ctx->initiator_seal);
|
||||
if (ctx->initiator_enc_aux == NULL)
|
||||
goto out_free_acceptor_enc;
|
||||
ctx->acceptor_enc_aux =
|
||||
context_v2_alloc_cipher(ctx, "cbc(aes)",
|
||||
ctx->acceptor_seal);
|
||||
if (ctx->acceptor_enc_aux == NULL) {
|
||||
crypto_free_blkcipher(ctx->initiator_enc_aux);
|
||||
goto out_free_acceptor_enc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_acceptor_enc:
|
||||
crypto_free_blkcipher(ctx->acceptor_enc);
|
||||
out_free_initiator_enc:
|
||||
crypto_free_blkcipher(ctx->initiator_enc);
|
||||
out_err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
int keylen;
|
||||
|
||||
p = simple_get_bytes(p, end, &ctx->flags, sizeof(ctx->flags));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
ctx->initiate = ctx->flags & KRB5_CTX_FLAG_INITIATOR;
|
||||
|
||||
p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
p = simple_get_bytes(p, end, &ctx->seq_send64, sizeof(ctx->seq_send64));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
/* set seq_send for use by "older" enctypes */
|
||||
ctx->seq_send = ctx->seq_send64;
|
||||
if (ctx->seq_send64 != ctx->seq_send) {
|
||||
dprintk("%s: seq_send64 %lx, seq_send %x overflow?\n", __func__,
|
||||
(unsigned long)ctx->seq_send64, ctx->seq_send);
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype));
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
/* Map ENCTYPE_DES3_CBC_SHA1 to ENCTYPE_DES3_CBC_RAW */
|
||||
if (ctx->enctype == ENCTYPE_DES3_CBC_SHA1)
|
||||
ctx->enctype = ENCTYPE_DES3_CBC_RAW;
|
||||
ctx->gk5e = get_gss_krb5_enctype(ctx->enctype);
|
||||
if (ctx->gk5e == NULL) {
|
||||
dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
|
||||
ctx->enctype);
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
keylen = ctx->gk5e->keylength;
|
||||
|
||||
p = simple_get_bytes(p, end, ctx->Ksess, keylen);
|
||||
if (IS_ERR(p))
|
||||
goto out_err;
|
||||
|
||||
if (p != end) {
|
||||
p = ERR_PTR(-EINVAL);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ctx->mech_used.data = kmemdup(gss_kerberos_mech.gm_oid.data,
|
||||
gss_kerberos_mech.gm_oid.len, gfp_mask);
|
||||
if (unlikely(ctx->mech_used.data == NULL)) {
|
||||
p = ERR_PTR(-ENOMEM);
|
||||
goto out_err;
|
||||
}
|
||||
ctx->mech_used.len = gss_kerberos_mech.gm_oid.len;
|
||||
|
||||
switch (ctx->enctype) {
|
||||
case ENCTYPE_DES3_CBC_RAW:
|
||||
return context_derive_keys_des3(ctx, gfp_mask);
|
||||
case ENCTYPE_ARCFOUR_HMAC:
|
||||
return context_derive_keys_rc4(ctx);
|
||||
case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
|
||||
case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
|
||||
return context_derive_keys_new(ctx, gfp_mask);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out_err:
|
||||
return PTR_ERR(p);
|
||||
}
|
||||
|
||||
static int
|
||||
gss_import_sec_context_kerberos(const void *p, size_t len,
|
||||
struct gss_ctx *ctx_id,
|
||||
time_t *endtime,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
const void *end = (const void *)((const char *)p + len);
|
||||
struct krb5_ctx *ctx;
|
||||
int ret;
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), gfp_mask);
|
||||
if (ctx == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (len == 85)
|
||||
ret = gss_import_v1_context(p, end, ctx);
|
||||
else
|
||||
ret = gss_import_v2_context(p, end, ctx, gfp_mask);
|
||||
|
||||
if (ret == 0) {
|
||||
ctx_id->internal_ctx_id = ctx;
|
||||
if (endtime)
|
||||
*endtime = ctx->endtime;
|
||||
} else
|
||||
kfree(ctx);
|
||||
|
||||
dprintk("RPC: %s: returning %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
gss_delete_sec_context_kerberos(void *internal_ctx) {
|
||||
struct krb5_ctx *kctx = internal_ctx;
|
||||
|
||||
crypto_free_blkcipher(kctx->seq);
|
||||
crypto_free_blkcipher(kctx->enc);
|
||||
crypto_free_blkcipher(kctx->acceptor_enc);
|
||||
crypto_free_blkcipher(kctx->initiator_enc);
|
||||
crypto_free_blkcipher(kctx->acceptor_enc_aux);
|
||||
crypto_free_blkcipher(kctx->initiator_enc_aux);
|
||||
kfree(kctx->mech_used.data);
|
||||
kfree(kctx);
|
||||
}
|
||||
|
||||
static const struct gss_api_ops gss_kerberos_ops = {
|
||||
.gss_import_sec_context = gss_import_sec_context_kerberos,
|
||||
.gss_get_mic = gss_get_mic_kerberos,
|
||||
.gss_verify_mic = gss_verify_mic_kerberos,
|
||||
.gss_wrap = gss_wrap_kerberos,
|
||||
.gss_unwrap = gss_unwrap_kerberos,
|
||||
.gss_delete_sec_context = gss_delete_sec_context_kerberos,
|
||||
};
|
||||
|
||||
static struct pf_desc gss_kerberos_pfs[] = {
|
||||
[0] = {
|
||||
.pseudoflavor = RPC_AUTH_GSS_KRB5,
|
||||
.qop = GSS_C_QOP_DEFAULT,
|
||||
.service = RPC_GSS_SVC_NONE,
|
||||
.name = "krb5",
|
||||
},
|
||||
[1] = {
|
||||
.pseudoflavor = RPC_AUTH_GSS_KRB5I,
|
||||
.qop = GSS_C_QOP_DEFAULT,
|
||||
.service = RPC_GSS_SVC_INTEGRITY,
|
||||
.name = "krb5i",
|
||||
},
|
||||
[2] = {
|
||||
.pseudoflavor = RPC_AUTH_GSS_KRB5P,
|
||||
.qop = GSS_C_QOP_DEFAULT,
|
||||
.service = RPC_GSS_SVC_PRIVACY,
|
||||
.name = "krb5p",
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_ALIAS("rpc-auth-gss-krb5");
|
||||
MODULE_ALIAS("rpc-auth-gss-krb5i");
|
||||
MODULE_ALIAS("rpc-auth-gss-krb5p");
|
||||
MODULE_ALIAS("rpc-auth-gss-390003");
|
||||
MODULE_ALIAS("rpc-auth-gss-390004");
|
||||
MODULE_ALIAS("rpc-auth-gss-390005");
|
||||
MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
|
||||
|
||||
static struct gss_api_mech gss_kerberos_mech = {
|
||||
.gm_name = "krb5",
|
||||
.gm_owner = THIS_MODULE,
|
||||
.gm_oid = { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
|
||||
.gm_ops = &gss_kerberos_ops,
|
||||
.gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs),
|
||||
.gm_pfs = gss_kerberos_pfs,
|
||||
.gm_upcall_enctypes = KRB5_SUPPORTED_ENCTYPES,
|
||||
};
|
||||
|
||||
static int __init init_kerberos_module(void)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = gss_mech_register(&gss_kerberos_mech);
|
||||
if (status)
|
||||
printk("Failed to register kerberos gss mechanism!\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
static void __exit cleanup_kerberos_module(void)
|
||||
{
|
||||
gss_mech_unregister(&gss_kerberos_mech);
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
module_init(init_kerberos_module);
|
||||
module_exit(cleanup_kerberos_module);
|
229
net/sunrpc/auth_gss/gss_krb5_seal.c
Normal file
229
net/sunrpc/auth_gss/gss_krb5_seal.c
Normal file
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_krb5_seal.c
|
||||
*
|
||||
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/k5seal.c
|
||||
*
|
||||
* Copyright (c) 2000-2008 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
* J. Bruce Fields <bfields@umich.edu>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 1993 by OpenVision Technologies, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appears in all copies and
|
||||
* that both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of OpenVision not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. OpenVision makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <linux/types.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
DEFINE_SPINLOCK(krb5_seq_lock);
|
||||
|
||||
static void *
|
||||
setup_token(struct krb5_ctx *ctx, struct xdr_netobj *token)
|
||||
{
|
||||
u16 *ptr;
|
||||
void *krb5_hdr;
|
||||
int body_size = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
|
||||
|
||||
token->len = g_token_size(&ctx->mech_used, body_size);
|
||||
|
||||
ptr = (u16 *)token->data;
|
||||
g_make_token_header(&ctx->mech_used, body_size, (unsigned char **)&ptr);
|
||||
|
||||
/* ptr now at start of header described in rfc 1964, section 1.2.1: */
|
||||
krb5_hdr = ptr;
|
||||
*ptr++ = KG_TOK_MIC_MSG;
|
||||
/*
|
||||
* signalg is stored as if it were converted from LE to host endian, even
|
||||
* though it's an opaque pair of bytes according to the RFC.
|
||||
*/
|
||||
*ptr++ = (__force u16)cpu_to_le16(ctx->gk5e->signalg);
|
||||
*ptr++ = SEAL_ALG_NONE;
|
||||
*ptr = 0xffff;
|
||||
|
||||
return krb5_hdr;
|
||||
}
|
||||
|
||||
static void *
|
||||
setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
|
||||
{
|
||||
u16 *ptr;
|
||||
void *krb5_hdr;
|
||||
u8 *p, flags = 0x00;
|
||||
|
||||
if ((ctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
|
||||
flags |= 0x01;
|
||||
if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
|
||||
flags |= 0x04;
|
||||
|
||||
/* Per rfc 4121, sec 4.2.6.1, there is no header,
|
||||
* just start the token */
|
||||
krb5_hdr = ptr = (u16 *)token->data;
|
||||
|
||||
*ptr++ = KG2_TOK_MIC;
|
||||
p = (u8 *)ptr;
|
||||
*p++ = flags;
|
||||
*p++ = 0xff;
|
||||
ptr = (u16 *)p;
|
||||
*ptr++ = 0xffff;
|
||||
*ptr = 0xffff;
|
||||
|
||||
token->len = GSS_KRB5_TOK_HDR_LEN + ctx->gk5e->cksumlength;
|
||||
return krb5_hdr;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text,
|
||||
struct xdr_netobj *token)
|
||||
{
|
||||
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
|
||||
.data = cksumdata};
|
||||
void *ptr;
|
||||
s32 now;
|
||||
u32 seq_send;
|
||||
u8 *cksumkey;
|
||||
|
||||
dprintk("RPC: %s\n", __func__);
|
||||
BUG_ON(ctx == NULL);
|
||||
|
||||
now = get_seconds();
|
||||
|
||||
ptr = setup_token(ctx, token);
|
||||
|
||||
if (ctx->gk5e->keyed_cksum)
|
||||
cksumkey = ctx->cksum;
|
||||
else
|
||||
cksumkey = NULL;
|
||||
|
||||
if (make_checksum(ctx, ptr, 8, text, 0, cksumkey,
|
||||
KG_USAGE_SIGN, &md5cksum))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data, md5cksum.len);
|
||||
|
||||
spin_lock(&krb5_seq_lock);
|
||||
seq_send = ctx->seq_send++;
|
||||
spin_unlock(&krb5_seq_lock);
|
||||
|
||||
if (krb5_make_seq_num(ctx, ctx->seq, ctx->initiate ? 0 : 0xff,
|
||||
seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
|
||||
struct xdr_netobj *token)
|
||||
{
|
||||
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
struct xdr_netobj cksumobj = { .len = sizeof(cksumdata),
|
||||
.data = cksumdata};
|
||||
void *krb5_hdr;
|
||||
s32 now;
|
||||
u64 seq_send;
|
||||
u8 *cksumkey;
|
||||
unsigned int cksum_usage;
|
||||
|
||||
dprintk("RPC: %s\n", __func__);
|
||||
|
||||
krb5_hdr = setup_token_v2(ctx, token);
|
||||
|
||||
/* Set up the sequence number. Now 64-bits in clear
|
||||
* text and w/o direction indicator */
|
||||
spin_lock(&krb5_seq_lock);
|
||||
seq_send = ctx->seq_send64++;
|
||||
spin_unlock(&krb5_seq_lock);
|
||||
*((__be64 *)(krb5_hdr + 8)) = cpu_to_be64(seq_send);
|
||||
|
||||
if (ctx->initiate) {
|
||||
cksumkey = ctx->initiator_sign;
|
||||
cksum_usage = KG_USAGE_INITIATOR_SIGN;
|
||||
} else {
|
||||
cksumkey = ctx->acceptor_sign;
|
||||
cksum_usage = KG_USAGE_ACCEPTOR_SIGN;
|
||||
}
|
||||
|
||||
if (make_checksum_v2(ctx, krb5_hdr, GSS_KRB5_TOK_HDR_LEN,
|
||||
text, 0, cksumkey, cksum_usage, &cksumobj))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
memcpy(krb5_hdr + GSS_KRB5_TOK_HDR_LEN, cksumobj.data, cksumobj.len);
|
||||
|
||||
now = get_seconds();
|
||||
|
||||
return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text,
|
||||
struct xdr_netobj *token)
|
||||
{
|
||||
struct krb5_ctx *ctx = gss_ctx->internal_ctx_id;
|
||||
|
||||
switch (ctx->enctype) {
|
||||
default:
|
||||
BUG();
|
||||
case ENCTYPE_DES_CBC_RAW:
|
||||
case ENCTYPE_DES3_CBC_RAW:
|
||||
case ENCTYPE_ARCFOUR_HMAC:
|
||||
return gss_get_mic_v1(ctx, text, token);
|
||||
case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
|
||||
case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
|
||||
return gss_get_mic_v2(ctx, text, token);
|
||||
}
|
||||
}
|
||||
|
166
net/sunrpc/auth_gss/gss_krb5_seqnum.c
Normal file
166
net/sunrpc/auth_gss/gss_krb5_seqnum.c
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_krb5_seqnum.c
|
||||
*
|
||||
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/util_seqnum.c
|
||||
*
|
||||
* Copyright (c) 2000 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 1993 by OpenVision Technologies, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appears in all copies and
|
||||
* that both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of OpenVision not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. OpenVision makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
static s32
|
||||
krb5_make_rc4_seq_num(struct krb5_ctx *kctx, int direction, s32 seqnum,
|
||||
unsigned char *cksum, unsigned char *buf)
|
||||
{
|
||||
struct crypto_blkcipher *cipher;
|
||||
unsigned char plain[8];
|
||||
s32 code;
|
||||
|
||||
dprintk("RPC: %s:\n", __func__);
|
||||
cipher = crypto_alloc_blkcipher(kctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(cipher))
|
||||
return PTR_ERR(cipher);
|
||||
|
||||
plain[0] = (unsigned char) ((seqnum >> 24) & 0xff);
|
||||
plain[1] = (unsigned char) ((seqnum >> 16) & 0xff);
|
||||
plain[2] = (unsigned char) ((seqnum >> 8) & 0xff);
|
||||
plain[3] = (unsigned char) ((seqnum >> 0) & 0xff);
|
||||
plain[4] = direction;
|
||||
plain[5] = direction;
|
||||
plain[6] = direction;
|
||||
plain[7] = direction;
|
||||
|
||||
code = krb5_rc4_setup_seq_key(kctx, cipher, cksum);
|
||||
if (code)
|
||||
goto out;
|
||||
|
||||
code = krb5_encrypt(cipher, cksum, plain, buf, 8);
|
||||
out:
|
||||
crypto_free_blkcipher(cipher);
|
||||
return code;
|
||||
}
|
||||
s32
|
||||
krb5_make_seq_num(struct krb5_ctx *kctx,
|
||||
struct crypto_blkcipher *key,
|
||||
int direction,
|
||||
u32 seqnum,
|
||||
unsigned char *cksum, unsigned char *buf)
|
||||
{
|
||||
unsigned char plain[8];
|
||||
|
||||
if (kctx->enctype == ENCTYPE_ARCFOUR_HMAC)
|
||||
return krb5_make_rc4_seq_num(kctx, direction, seqnum,
|
||||
cksum, buf);
|
||||
|
||||
plain[0] = (unsigned char) (seqnum & 0xff);
|
||||
plain[1] = (unsigned char) ((seqnum >> 8) & 0xff);
|
||||
plain[2] = (unsigned char) ((seqnum >> 16) & 0xff);
|
||||
plain[3] = (unsigned char) ((seqnum >> 24) & 0xff);
|
||||
|
||||
plain[4] = direction;
|
||||
plain[5] = direction;
|
||||
plain[6] = direction;
|
||||
plain[7] = direction;
|
||||
|
||||
return krb5_encrypt(key, cksum, plain, buf, 8);
|
||||
}
|
||||
|
||||
static s32
|
||||
krb5_get_rc4_seq_num(struct krb5_ctx *kctx, unsigned char *cksum,
|
||||
unsigned char *buf, int *direction, s32 *seqnum)
|
||||
{
|
||||
struct crypto_blkcipher *cipher;
|
||||
unsigned char plain[8];
|
||||
s32 code;
|
||||
|
||||
dprintk("RPC: %s:\n", __func__);
|
||||
cipher = crypto_alloc_blkcipher(kctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(cipher))
|
||||
return PTR_ERR(cipher);
|
||||
|
||||
code = krb5_rc4_setup_seq_key(kctx, cipher, cksum);
|
||||
if (code)
|
||||
goto out;
|
||||
|
||||
code = krb5_decrypt(cipher, cksum, buf, plain, 8);
|
||||
if (code)
|
||||
goto out;
|
||||
|
||||
if ((plain[4] != plain[5]) || (plain[4] != plain[6])
|
||||
|| (plain[4] != plain[7])) {
|
||||
code = (s32)KG_BAD_SEQ;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*direction = plain[4];
|
||||
|
||||
*seqnum = ((plain[0] << 24) | (plain[1] << 16) |
|
||||
(plain[2] << 8) | (plain[3]));
|
||||
out:
|
||||
crypto_free_blkcipher(cipher);
|
||||
return code;
|
||||
}
|
||||
|
||||
s32
|
||||
krb5_get_seq_num(struct krb5_ctx *kctx,
|
||||
unsigned char *cksum,
|
||||
unsigned char *buf,
|
||||
int *direction, u32 *seqnum)
|
||||
{
|
||||
s32 code;
|
||||
unsigned char plain[8];
|
||||
struct crypto_blkcipher *key = kctx->seq;
|
||||
|
||||
dprintk("RPC: krb5_get_seq_num:\n");
|
||||
|
||||
if (kctx->enctype == ENCTYPE_ARCFOUR_HMAC)
|
||||
return krb5_get_rc4_seq_num(kctx, cksum, buf,
|
||||
direction, seqnum);
|
||||
|
||||
if ((code = krb5_decrypt(key, cksum, buf, plain, 8)))
|
||||
return code;
|
||||
|
||||
if ((plain[4] != plain[5]) || (plain[4] != plain[6]) ||
|
||||
(plain[4] != plain[7]))
|
||||
return (s32)KG_BAD_SEQ;
|
||||
|
||||
*direction = plain[4];
|
||||
|
||||
*seqnum = ((plain[0]) |
|
||||
(plain[1] << 8) | (plain[2] << 16) | (plain[3] << 24));
|
||||
|
||||
return 0;
|
||||
}
|
226
net/sunrpc/auth_gss/gss_krb5_unseal.c
Normal file
226
net/sunrpc/auth_gss/gss_krb5_unseal.c
Normal file
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_krb5_unseal.c
|
||||
*
|
||||
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/k5unseal.c
|
||||
*
|
||||
* Copyright (c) 2000-2008 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Andy Adamson <andros@umich.edu>
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 1993 by OpenVision Technologies, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without fee,
|
||||
* provided that the above copyright notice appears in all copies and
|
||||
* that both that copyright notice and this permission notice appear in
|
||||
* supporting documentation, and that the name of OpenVision not be used
|
||||
* in advertising or publicity pertaining to distribution of the software
|
||||
* without specific, written prior permission. OpenVision makes no
|
||||
* representations about the suitability of this software for any
|
||||
* purpose. It is provided "as is" without express or implied warranty.
|
||||
*
|
||||
* OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
|
||||
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
|
||||
* EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
|
||||
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
|
||||
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 <linux/types.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
|
||||
/* read_token is a mic token, and message_buffer is the data that the mic was
|
||||
* supposedly taken over. */
|
||||
|
||||
static u32
|
||||
gss_verify_mic_v1(struct krb5_ctx *ctx,
|
||||
struct xdr_buf *message_buffer, struct xdr_netobj *read_token)
|
||||
{
|
||||
int signalg;
|
||||
int sealalg;
|
||||
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
|
||||
.data = cksumdata};
|
||||
s32 now;
|
||||
int direction;
|
||||
u32 seqnum;
|
||||
unsigned char *ptr = (unsigned char *)read_token->data;
|
||||
int bodysize;
|
||||
u8 *cksumkey;
|
||||
|
||||
dprintk("RPC: krb5_read_token\n");
|
||||
|
||||
if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr,
|
||||
read_token->len))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if ((ptr[0] != ((KG_TOK_MIC_MSG >> 8) & 0xff)) ||
|
||||
(ptr[1] != (KG_TOK_MIC_MSG & 0xff)))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
/* XXX sanity-check bodysize?? */
|
||||
|
||||
signalg = ptr[2] + (ptr[3] << 8);
|
||||
if (signalg != ctx->gk5e->signalg)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
sealalg = ptr[4] + (ptr[5] << 8);
|
||||
if (sealalg != SEAL_ALG_NONE)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if ((ptr[6] != 0xff) || (ptr[7] != 0xff))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if (ctx->gk5e->keyed_cksum)
|
||||
cksumkey = ctx->cksum;
|
||||
else
|
||||
cksumkey = NULL;
|
||||
|
||||
if (make_checksum(ctx, ptr, 8, message_buffer, 0,
|
||||
cksumkey, KG_USAGE_SIGN, &md5cksum))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if (memcmp(md5cksum.data, ptr + GSS_KRB5_TOK_HDR_LEN,
|
||||
ctx->gk5e->cksumlength))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
/* it got through unscathed. Make sure the context is unexpired */
|
||||
|
||||
now = get_seconds();
|
||||
|
||||
if (now > ctx->endtime)
|
||||
return GSS_S_CONTEXT_EXPIRED;
|
||||
|
||||
/* do sequencing checks */
|
||||
|
||||
if (krb5_get_seq_num(ctx, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8,
|
||||
&direction, &seqnum))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if ((ctx->initiate && direction != 0xff) ||
|
||||
(!ctx->initiate && direction != 0))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_verify_mic_v2(struct krb5_ctx *ctx,
|
||||
struct xdr_buf *message_buffer, struct xdr_netobj *read_token)
|
||||
{
|
||||
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
struct xdr_netobj cksumobj = {.len = sizeof(cksumdata),
|
||||
.data = cksumdata};
|
||||
s32 now;
|
||||
u8 *ptr = read_token->data;
|
||||
u8 *cksumkey;
|
||||
u8 flags;
|
||||
int i;
|
||||
unsigned int cksum_usage;
|
||||
|
||||
dprintk("RPC: %s\n", __func__);
|
||||
|
||||
if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_MIC)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
flags = ptr[2];
|
||||
if ((!ctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
|
||||
(ctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
if (flags & KG2_TOKEN_FLAG_SEALED) {
|
||||
dprintk("%s: token has unexpected sealed flag\n", __func__);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
for (i = 3; i < 8; i++)
|
||||
if (ptr[i] != 0xff)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if (ctx->initiate) {
|
||||
cksumkey = ctx->acceptor_sign;
|
||||
cksum_usage = KG_USAGE_ACCEPTOR_SIGN;
|
||||
} else {
|
||||
cksumkey = ctx->initiator_sign;
|
||||
cksum_usage = KG_USAGE_INITIATOR_SIGN;
|
||||
}
|
||||
|
||||
if (make_checksum_v2(ctx, ptr, GSS_KRB5_TOK_HDR_LEN, message_buffer, 0,
|
||||
cksumkey, cksum_usage, &cksumobj))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if (memcmp(cksumobj.data, ptr + GSS_KRB5_TOK_HDR_LEN,
|
||||
ctx->gk5e->cksumlength))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
/* it got through unscathed. Make sure the context is unexpired */
|
||||
now = get_seconds();
|
||||
if (now > ctx->endtime)
|
||||
return GSS_S_CONTEXT_EXPIRED;
|
||||
|
||||
/*
|
||||
* NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
|
||||
* doesn't want it checked; see page 6 of rfc 2203.
|
||||
*/
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_verify_mic_kerberos(struct gss_ctx *gss_ctx,
|
||||
struct xdr_buf *message_buffer,
|
||||
struct xdr_netobj *read_token)
|
||||
{
|
||||
struct krb5_ctx *ctx = gss_ctx->internal_ctx_id;
|
||||
|
||||
switch (ctx->enctype) {
|
||||
default:
|
||||
BUG();
|
||||
case ENCTYPE_DES_CBC_RAW:
|
||||
case ENCTYPE_DES3_CBC_RAW:
|
||||
case ENCTYPE_ARCFOUR_HMAC:
|
||||
return gss_verify_mic_v1(ctx, message_buffer, read_token);
|
||||
case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
|
||||
case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
|
||||
return gss_verify_mic_v2(ctx, message_buffer, read_token);
|
||||
}
|
||||
}
|
||||
|
626
net/sunrpc/auth_gss/gss_krb5_wrap.c
Normal file
626
net/sunrpc/auth_gss/gss_krb5_wrap.c
Normal file
|
@ -0,0 +1,626 @@
|
|||
/*
|
||||
* COPYRIGHT (c) 2008
|
||||
* The Regents of the University of Michigan
|
||||
* ALL RIGHTS RESERVED
|
||||
*
|
||||
* Permission is granted to use, copy, create derivative works
|
||||
* and redistribute this software and such derivative works
|
||||
* for any purpose, so long as the name of The University of
|
||||
* Michigan is not used in any advertising or publicity
|
||||
* pertaining to the use of distribution of this software
|
||||
* without specific, written prior authorization. If the
|
||||
* above copyright notice or any other identification of the
|
||||
* University of Michigan is included in any copy of any
|
||||
* portion of this software, then the disclaimer below must
|
||||
* also be included.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
|
||||
* FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
|
||||
* PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
|
||||
* MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
|
||||
* WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
|
||||
* REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
|
||||
* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
|
||||
* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
|
||||
* IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGES.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sunrpc/gss_krb5.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
gss_krb5_padding(int blocksize, int length)
|
||||
{
|
||||
return blocksize - (length % blocksize);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gss_krb5_add_padding(struct xdr_buf *buf, int offset, int blocksize)
|
||||
{
|
||||
int padding = gss_krb5_padding(blocksize, buf->len - offset);
|
||||
char *p;
|
||||
struct kvec *iov;
|
||||
|
||||
if (buf->page_len || buf->tail[0].iov_len)
|
||||
iov = &buf->tail[0];
|
||||
else
|
||||
iov = &buf->head[0];
|
||||
p = iov->iov_base + iov->iov_len;
|
||||
iov->iov_len += padding;
|
||||
buf->len += padding;
|
||||
memset(p, padding, padding);
|
||||
}
|
||||
|
||||
static inline int
|
||||
gss_krb5_remove_padding(struct xdr_buf *buf, int blocksize)
|
||||
{
|
||||
u8 *ptr;
|
||||
u8 pad;
|
||||
size_t len = buf->len;
|
||||
|
||||
if (len <= buf->head[0].iov_len) {
|
||||
pad = *(u8 *)(buf->head[0].iov_base + len - 1);
|
||||
if (pad > buf->head[0].iov_len)
|
||||
return -EINVAL;
|
||||
buf->head[0].iov_len -= pad;
|
||||
goto out;
|
||||
} else
|
||||
len -= buf->head[0].iov_len;
|
||||
if (len <= buf->page_len) {
|
||||
unsigned int last = (buf->page_base + len - 1)
|
||||
>>PAGE_CACHE_SHIFT;
|
||||
unsigned int offset = (buf->page_base + len - 1)
|
||||
& (PAGE_CACHE_SIZE - 1);
|
||||
ptr = kmap_atomic(buf->pages[last]);
|
||||
pad = *(ptr + offset);
|
||||
kunmap_atomic(ptr);
|
||||
goto out;
|
||||
} else
|
||||
len -= buf->page_len;
|
||||
BUG_ON(len > buf->tail[0].iov_len);
|
||||
pad = *(u8 *)(buf->tail[0].iov_base + len - 1);
|
||||
out:
|
||||
/* XXX: NOTE: we do not adjust the page lengths--they represent
|
||||
* a range of data in the real filesystem page cache, and we need
|
||||
* to know that range so the xdr code can properly place read data.
|
||||
* However adjusting the head length, as we do above, is harmless.
|
||||
* In the case of a request that fits into a single page, the server
|
||||
* also uses length and head length together to determine the original
|
||||
* start of the request to copy the request for deferal; so it's
|
||||
* easier on the server if we adjust head and tail length in tandem.
|
||||
* It's not really a problem that we don't fool with the page and
|
||||
* tail lengths, though--at worst badly formed xdr might lead the
|
||||
* server to attempt to parse the padding.
|
||||
* XXX: Document all these weird requirements for gss mechanism
|
||||
* wrap/unwrap functions. */
|
||||
if (pad > blocksize)
|
||||
return -EINVAL;
|
||||
if (buf->len > pad)
|
||||
buf->len -= pad;
|
||||
else
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gss_krb5_make_confounder(char *p, u32 conflen)
|
||||
{
|
||||
static u64 i = 0;
|
||||
u64 *q = (u64 *)p;
|
||||
|
||||
/* rfc1964 claims this should be "random". But all that's really
|
||||
* necessary is that it be unique. And not even that is necessary in
|
||||
* our case since our "gssapi" implementation exists only to support
|
||||
* rpcsec_gss, so we know that the only buffers we will ever encrypt
|
||||
* already begin with a unique sequence number. Just to hedge my bets
|
||||
* I'll make a half-hearted attempt at something unique, but ensuring
|
||||
* uniqueness would mean worrying about atomicity and rollover, and I
|
||||
* don't care enough. */
|
||||
|
||||
/* initialize to random value */
|
||||
if (i == 0) {
|
||||
i = prandom_u32();
|
||||
i = (i << 32) | prandom_u32();
|
||||
}
|
||||
|
||||
switch (conflen) {
|
||||
case 16:
|
||||
*q++ = i++;
|
||||
/* fall through */
|
||||
case 8:
|
||||
*q++ = i++;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
/* Assumptions: the head and tail of inbuf are ours to play with.
|
||||
* The pages, however, may be real pages in the page cache and we replace
|
||||
* them with scratch pages from **pages before writing to them. */
|
||||
/* XXX: obviously the above should be documentation of wrap interface,
|
||||
* and shouldn't be in this kerberos-specific file. */
|
||||
|
||||
/* XXX factor out common code with seal/unseal. */
|
||||
|
||||
static u32
|
||||
gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset,
|
||||
struct xdr_buf *buf, struct page **pages)
|
||||
{
|
||||
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
|
||||
.data = cksumdata};
|
||||
int blocksize = 0, plainlen;
|
||||
unsigned char *ptr, *msg_start;
|
||||
s32 now;
|
||||
int headlen;
|
||||
struct page **tmp_pages;
|
||||
u32 seq_send;
|
||||
u8 *cksumkey;
|
||||
u32 conflen = kctx->gk5e->conflen;
|
||||
|
||||
dprintk("RPC: %s\n", __func__);
|
||||
|
||||
now = get_seconds();
|
||||
|
||||
blocksize = crypto_blkcipher_blocksize(kctx->enc);
|
||||
gss_krb5_add_padding(buf, offset, blocksize);
|
||||
BUG_ON((buf->len - offset) % blocksize);
|
||||
plainlen = conflen + buf->len - offset;
|
||||
|
||||
headlen = g_token_size(&kctx->mech_used,
|
||||
GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength + plainlen) -
|
||||
(buf->len - offset);
|
||||
|
||||
ptr = buf->head[0].iov_base + offset;
|
||||
/* shift data to make room for header. */
|
||||
xdr_extend_head(buf, offset, headlen);
|
||||
|
||||
/* XXX Would be cleverer to encrypt while copying. */
|
||||
BUG_ON((buf->len - offset - headlen) % blocksize);
|
||||
|
||||
g_make_token_header(&kctx->mech_used,
|
||||
GSS_KRB5_TOK_HDR_LEN +
|
||||
kctx->gk5e->cksumlength + plainlen, &ptr);
|
||||
|
||||
|
||||
/* ptr now at header described in rfc 1964, section 1.2.1: */
|
||||
ptr[0] = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff);
|
||||
ptr[1] = (unsigned char) (KG_TOK_WRAP_MSG & 0xff);
|
||||
|
||||
msg_start = ptr + GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength;
|
||||
|
||||
/*
|
||||
* signalg and sealalg are stored as if they were converted from LE
|
||||
* to host endian, even though they're opaque pairs of bytes according
|
||||
* to the RFC.
|
||||
*/
|
||||
*(__le16 *)(ptr + 2) = cpu_to_le16(kctx->gk5e->signalg);
|
||||
*(__le16 *)(ptr + 4) = cpu_to_le16(kctx->gk5e->sealalg);
|
||||
ptr[6] = 0xff;
|
||||
ptr[7] = 0xff;
|
||||
|
||||
gss_krb5_make_confounder(msg_start, conflen);
|
||||
|
||||
if (kctx->gk5e->keyed_cksum)
|
||||
cksumkey = kctx->cksum;
|
||||
else
|
||||
cksumkey = NULL;
|
||||
|
||||
/* XXXJBF: UGH!: */
|
||||
tmp_pages = buf->pages;
|
||||
buf->pages = pages;
|
||||
if (make_checksum(kctx, ptr, 8, buf, offset + headlen - conflen,
|
||||
cksumkey, KG_USAGE_SEAL, &md5cksum))
|
||||
return GSS_S_FAILURE;
|
||||
buf->pages = tmp_pages;
|
||||
|
||||
memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data, md5cksum.len);
|
||||
|
||||
spin_lock(&krb5_seq_lock);
|
||||
seq_send = kctx->seq_send++;
|
||||
spin_unlock(&krb5_seq_lock);
|
||||
|
||||
/* XXX would probably be more efficient to compute checksum
|
||||
* and encrypt at the same time: */
|
||||
if ((krb5_make_seq_num(kctx, kctx->seq, kctx->initiate ? 0 : 0xff,
|
||||
seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8)))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if (kctx->enctype == ENCTYPE_ARCFOUR_HMAC) {
|
||||
struct crypto_blkcipher *cipher;
|
||||
int err;
|
||||
cipher = crypto_alloc_blkcipher(kctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(cipher))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
krb5_rc4_setup_enc_key(kctx, cipher, seq_send);
|
||||
|
||||
err = gss_encrypt_xdr_buf(cipher, buf,
|
||||
offset + headlen - conflen, pages);
|
||||
crypto_free_blkcipher(cipher);
|
||||
if (err)
|
||||
return GSS_S_FAILURE;
|
||||
} else {
|
||||
if (gss_encrypt_xdr_buf(kctx->enc, buf,
|
||||
offset + headlen - conflen, pages))
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
|
||||
{
|
||||
int signalg;
|
||||
int sealalg;
|
||||
char cksumdata[GSS_KRB5_MAX_CKSUM_LEN];
|
||||
struct xdr_netobj md5cksum = {.len = sizeof(cksumdata),
|
||||
.data = cksumdata};
|
||||
s32 now;
|
||||
int direction;
|
||||
s32 seqnum;
|
||||
unsigned char *ptr;
|
||||
int bodysize;
|
||||
void *data_start, *orig_start;
|
||||
int data_len;
|
||||
int blocksize;
|
||||
u32 conflen = kctx->gk5e->conflen;
|
||||
int crypt_offset;
|
||||
u8 *cksumkey;
|
||||
|
||||
dprintk("RPC: gss_unwrap_kerberos\n");
|
||||
|
||||
ptr = (u8 *)buf->head[0].iov_base + offset;
|
||||
if (g_verify_token_header(&kctx->mech_used, &bodysize, &ptr,
|
||||
buf->len - offset))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if ((ptr[0] != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) ||
|
||||
(ptr[1] != (KG_TOK_WRAP_MSG & 0xff)))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
/* XXX sanity-check bodysize?? */
|
||||
|
||||
/* get the sign and seal algorithms */
|
||||
|
||||
signalg = ptr[2] + (ptr[3] << 8);
|
||||
if (signalg != kctx->gk5e->signalg)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
sealalg = ptr[4] + (ptr[5] << 8);
|
||||
if (sealalg != kctx->gk5e->sealalg)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
if ((ptr[6] != 0xff) || (ptr[7] != 0xff))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
/*
|
||||
* Data starts after token header and checksum. ptr points
|
||||
* to the beginning of the token header
|
||||
*/
|
||||
crypt_offset = ptr + (GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength) -
|
||||
(unsigned char *)buf->head[0].iov_base;
|
||||
|
||||
/*
|
||||
* Need plaintext seqnum to derive encryption key for arcfour-hmac
|
||||
*/
|
||||
if (krb5_get_seq_num(kctx, ptr + GSS_KRB5_TOK_HDR_LEN,
|
||||
ptr + 8, &direction, &seqnum))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
if ((kctx->initiate && direction != 0xff) ||
|
||||
(!kctx->initiate && direction != 0))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
if (kctx->enctype == ENCTYPE_ARCFOUR_HMAC) {
|
||||
struct crypto_blkcipher *cipher;
|
||||
int err;
|
||||
|
||||
cipher = crypto_alloc_blkcipher(kctx->gk5e->encrypt_name, 0,
|
||||
CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(cipher))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
krb5_rc4_setup_enc_key(kctx, cipher, seqnum);
|
||||
|
||||
err = gss_decrypt_xdr_buf(cipher, buf, crypt_offset);
|
||||
crypto_free_blkcipher(cipher);
|
||||
if (err)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
} else {
|
||||
if (gss_decrypt_xdr_buf(kctx->enc, buf, crypt_offset))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
}
|
||||
|
||||
if (kctx->gk5e->keyed_cksum)
|
||||
cksumkey = kctx->cksum;
|
||||
else
|
||||
cksumkey = NULL;
|
||||
|
||||
if (make_checksum(kctx, ptr, 8, buf, crypt_offset,
|
||||
cksumkey, KG_USAGE_SEAL, &md5cksum))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
if (memcmp(md5cksum.data, ptr + GSS_KRB5_TOK_HDR_LEN,
|
||||
kctx->gk5e->cksumlength))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
/* it got through unscathed. Make sure the context is unexpired */
|
||||
|
||||
now = get_seconds();
|
||||
|
||||
if (now > kctx->endtime)
|
||||
return GSS_S_CONTEXT_EXPIRED;
|
||||
|
||||
/* do sequencing checks */
|
||||
|
||||
/* Copy the data back to the right position. XXX: Would probably be
|
||||
* better to copy and encrypt at the same time. */
|
||||
|
||||
blocksize = crypto_blkcipher_blocksize(kctx->enc);
|
||||
data_start = ptr + (GSS_KRB5_TOK_HDR_LEN + kctx->gk5e->cksumlength) +
|
||||
conflen;
|
||||
orig_start = buf->head[0].iov_base + offset;
|
||||
data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start;
|
||||
memmove(orig_start, data_start, data_len);
|
||||
buf->head[0].iov_len -= (data_start - orig_start);
|
||||
buf->len -= (data_start - orig_start);
|
||||
|
||||
if (gss_krb5_remove_padding(buf, blocksize))
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can shift data by up to LOCAL_BUF_LEN bytes in a pass. If we need
|
||||
* to do more than that, we shift repeatedly. Kevin Coffman reports
|
||||
* seeing 28 bytes as the value used by Microsoft clients and servers
|
||||
* with AES, so this constant is chosen to allow handling 28 in one pass
|
||||
* without using too much stack space.
|
||||
*
|
||||
* If that proves to a problem perhaps we could use a more clever
|
||||
* algorithm.
|
||||
*/
|
||||
#define LOCAL_BUF_LEN 32u
|
||||
|
||||
static void rotate_buf_a_little(struct xdr_buf *buf, unsigned int shift)
|
||||
{
|
||||
char head[LOCAL_BUF_LEN];
|
||||
char tmp[LOCAL_BUF_LEN];
|
||||
unsigned int this_len, i;
|
||||
|
||||
BUG_ON(shift > LOCAL_BUF_LEN);
|
||||
|
||||
read_bytes_from_xdr_buf(buf, 0, head, shift);
|
||||
for (i = 0; i + shift < buf->len; i += LOCAL_BUF_LEN) {
|
||||
this_len = min(LOCAL_BUF_LEN, buf->len - (i + shift));
|
||||
read_bytes_from_xdr_buf(buf, i+shift, tmp, this_len);
|
||||
write_bytes_to_xdr_buf(buf, i, tmp, this_len);
|
||||
}
|
||||
write_bytes_to_xdr_buf(buf, buf->len - shift, head, shift);
|
||||
}
|
||||
|
||||
static void _rotate_left(struct xdr_buf *buf, unsigned int shift)
|
||||
{
|
||||
int shifted = 0;
|
||||
int this_shift;
|
||||
|
||||
shift %= buf->len;
|
||||
while (shifted < shift) {
|
||||
this_shift = min(shift - shifted, LOCAL_BUF_LEN);
|
||||
rotate_buf_a_little(buf, this_shift);
|
||||
shifted += this_shift;
|
||||
}
|
||||
}
|
||||
|
||||
static void rotate_left(u32 base, struct xdr_buf *buf, unsigned int shift)
|
||||
{
|
||||
struct xdr_buf subbuf;
|
||||
|
||||
xdr_buf_subsegment(buf, &subbuf, base, buf->len - base);
|
||||
_rotate_left(&subbuf, shift);
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_wrap_kerberos_v2(struct krb5_ctx *kctx, u32 offset,
|
||||
struct xdr_buf *buf, struct page **pages)
|
||||
{
|
||||
int blocksize;
|
||||
u8 *ptr, *plainhdr;
|
||||
s32 now;
|
||||
u8 flags = 0x00;
|
||||
__be16 *be16ptr;
|
||||
__be64 *be64ptr;
|
||||
u32 err;
|
||||
|
||||
dprintk("RPC: %s\n", __func__);
|
||||
|
||||
if (kctx->gk5e->encrypt_v2 == NULL)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
/* make room for gss token header */
|
||||
if (xdr_extend_head(buf, offset, GSS_KRB5_TOK_HDR_LEN))
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
/* construct gss token header */
|
||||
ptr = plainhdr = buf->head[0].iov_base + offset;
|
||||
*ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
|
||||
*ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
|
||||
|
||||
if ((kctx->flags & KRB5_CTX_FLAG_INITIATOR) == 0)
|
||||
flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
|
||||
if ((kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY) != 0)
|
||||
flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
|
||||
/* We always do confidentiality in wrap tokens */
|
||||
flags |= KG2_TOKEN_FLAG_SEALED;
|
||||
|
||||
*ptr++ = flags;
|
||||
*ptr++ = 0xff;
|
||||
be16ptr = (__be16 *)ptr;
|
||||
|
||||
blocksize = crypto_blkcipher_blocksize(kctx->acceptor_enc);
|
||||
*be16ptr++ = 0;
|
||||
/* "inner" token header always uses 0 for RRC */
|
||||
*be16ptr++ = 0;
|
||||
|
||||
be64ptr = (__be64 *)be16ptr;
|
||||
spin_lock(&krb5_seq_lock);
|
||||
*be64ptr = cpu_to_be64(kctx->seq_send64++);
|
||||
spin_unlock(&krb5_seq_lock);
|
||||
|
||||
err = (*kctx->gk5e->encrypt_v2)(kctx, offset, buf, pages);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
now = get_seconds();
|
||||
return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
static u32
|
||||
gss_unwrap_kerberos_v2(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf)
|
||||
{
|
||||
s32 now;
|
||||
u8 *ptr;
|
||||
u8 flags = 0x00;
|
||||
u16 ec, rrc;
|
||||
int err;
|
||||
u32 headskip, tailskip;
|
||||
u8 decrypted_hdr[GSS_KRB5_TOK_HDR_LEN];
|
||||
unsigned int movelen;
|
||||
|
||||
|
||||
dprintk("RPC: %s\n", __func__);
|
||||
|
||||
if (kctx->gk5e->decrypt_v2 == NULL)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
ptr = buf->head[0].iov_base + offset;
|
||||
|
||||
if (be16_to_cpu(*((__be16 *)ptr)) != KG2_TOK_WRAP)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
flags = ptr[2];
|
||||
if ((!kctx->initiate && (flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)) ||
|
||||
(kctx->initiate && !(flags & KG2_TOKEN_FLAG_SENTBYACCEPTOR)))
|
||||
return GSS_S_BAD_SIG;
|
||||
|
||||
if ((flags & KG2_TOKEN_FLAG_SEALED) == 0) {
|
||||
dprintk("%s: token missing expected sealed flag\n", __func__);
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
}
|
||||
|
||||
if (ptr[3] != 0xff)
|
||||
return GSS_S_DEFECTIVE_TOKEN;
|
||||
|
||||
ec = be16_to_cpup((__be16 *)(ptr + 4));
|
||||
rrc = be16_to_cpup((__be16 *)(ptr + 6));
|
||||
|
||||
/*
|
||||
* NOTE: the sequence number at ptr + 8 is skipped, rpcsec_gss
|
||||
* doesn't want it checked; see page 6 of rfc 2203.
|
||||
*/
|
||||
|
||||
if (rrc != 0)
|
||||
rotate_left(offset + 16, buf, rrc);
|
||||
|
||||
err = (*kctx->gk5e->decrypt_v2)(kctx, offset, buf,
|
||||
&headskip, &tailskip);
|
||||
if (err)
|
||||
return GSS_S_FAILURE;
|
||||
|
||||
/*
|
||||
* Retrieve the decrypted gss token header and verify
|
||||
* it against the original
|
||||
*/
|
||||
err = read_bytes_from_xdr_buf(buf,
|
||||
buf->len - GSS_KRB5_TOK_HDR_LEN - tailskip,
|
||||
decrypted_hdr, GSS_KRB5_TOK_HDR_LEN);
|
||||
if (err) {
|
||||
dprintk("%s: error %u getting decrypted_hdr\n", __func__, err);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
if (memcmp(ptr, decrypted_hdr, 6)
|
||||
|| memcmp(ptr + 8, decrypted_hdr + 8, 8)) {
|
||||
dprintk("%s: token hdr, plaintext hdr mismatch!\n", __func__);
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
/* do sequencing checks */
|
||||
|
||||
/* it got through unscathed. Make sure the context is unexpired */
|
||||
now = get_seconds();
|
||||
if (now > kctx->endtime)
|
||||
return GSS_S_CONTEXT_EXPIRED;
|
||||
|
||||
/*
|
||||
* Move the head data back to the right position in xdr_buf.
|
||||
* We ignore any "ec" data since it might be in the head or
|
||||
* the tail, and we really don't need to deal with it.
|
||||
* Note that buf->head[0].iov_len may indicate the available
|
||||
* head buffer space rather than that actually occupied.
|
||||
*/
|
||||
movelen = min_t(unsigned int, buf->head[0].iov_len, buf->len);
|
||||
movelen -= offset + GSS_KRB5_TOK_HDR_LEN + headskip;
|
||||
BUG_ON(offset + GSS_KRB5_TOK_HDR_LEN + headskip + movelen >
|
||||
buf->head[0].iov_len);
|
||||
memmove(ptr, ptr + GSS_KRB5_TOK_HDR_LEN + headskip, movelen);
|
||||
buf->head[0].iov_len -= GSS_KRB5_TOK_HDR_LEN + headskip;
|
||||
buf->len -= GSS_KRB5_TOK_HDR_LEN + headskip;
|
||||
|
||||
/* Trim off the trailing "extra count" and checksum blob */
|
||||
xdr_buf_trim(buf, ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_wrap_kerberos(struct gss_ctx *gctx, int offset,
|
||||
struct xdr_buf *buf, struct page **pages)
|
||||
{
|
||||
struct krb5_ctx *kctx = gctx->internal_ctx_id;
|
||||
|
||||
switch (kctx->enctype) {
|
||||
default:
|
||||
BUG();
|
||||
case ENCTYPE_DES_CBC_RAW:
|
||||
case ENCTYPE_DES3_CBC_RAW:
|
||||
case ENCTYPE_ARCFOUR_HMAC:
|
||||
return gss_wrap_kerberos_v1(kctx, offset, buf, pages);
|
||||
case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
|
||||
case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
|
||||
return gss_wrap_kerberos_v2(kctx, offset, buf, pages);
|
||||
}
|
||||
}
|
||||
|
||||
u32
|
||||
gss_unwrap_kerberos(struct gss_ctx *gctx, int offset, struct xdr_buf *buf)
|
||||
{
|
||||
struct krb5_ctx *kctx = gctx->internal_ctx_id;
|
||||
|
||||
switch (kctx->enctype) {
|
||||
default:
|
||||
BUG();
|
||||
case ENCTYPE_DES_CBC_RAW:
|
||||
case ENCTYPE_DES3_CBC_RAW:
|
||||
case ENCTYPE_ARCFOUR_HMAC:
|
||||
return gss_unwrap_kerberos_v1(kctx, offset, buf);
|
||||
case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
|
||||
case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
|
||||
return gss_unwrap_kerberos_v2(kctx, offset, buf);
|
||||
}
|
||||
}
|
||||
|
481
net/sunrpc/auth_gss/gss_mech_switch.c
Normal file
481
net/sunrpc/auth_gss/gss_mech_switch.c
Normal file
|
@ -0,0 +1,481 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_mech_switch.c
|
||||
*
|
||||
* Copyright (c) 2001 The Regents of the University of Michigan.
|
||||
* All rights reserved.
|
||||
*
|
||||
* J. Bruce Fields <bfields@umich.edu>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/oid_registry.h>
|
||||
#include <linux/sunrpc/msg_prot.h>
|
||||
#include <linux/sunrpc/gss_asn1.h>
|
||||
#include <linux/sunrpc/auth_gss.h>
|
||||
#include <linux/sunrpc/svcauth_gss.h>
|
||||
#include <linux/sunrpc/gss_err.h>
|
||||
#include <linux/sunrpc/sched.h>
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
static LIST_HEAD(registered_mechs);
|
||||
static DEFINE_SPINLOCK(registered_mechs_lock);
|
||||
|
||||
static void
|
||||
gss_mech_free(struct gss_api_mech *gm)
|
||||
{
|
||||
struct pf_desc *pf;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
pf = &gm->gm_pfs[i];
|
||||
kfree(pf->auth_domain_name);
|
||||
pf->auth_domain_name = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline char *
|
||||
make_auth_domain_name(char *name)
|
||||
{
|
||||
static char *prefix = "gss/";
|
||||
char *new;
|
||||
|
||||
new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL);
|
||||
if (new) {
|
||||
strcpy(new, prefix);
|
||||
strcat(new, name);
|
||||
}
|
||||
return new;
|
||||
}
|
||||
|
||||
static int
|
||||
gss_mech_svc_setup(struct gss_api_mech *gm)
|
||||
{
|
||||
struct pf_desc *pf;
|
||||
int i, status;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
pf = &gm->gm_pfs[i];
|
||||
pf->auth_domain_name = make_auth_domain_name(pf->name);
|
||||
status = -ENOMEM;
|
||||
if (pf->auth_domain_name == NULL)
|
||||
goto out;
|
||||
status = svcauth_gss_register_pseudoflavor(pf->pseudoflavor,
|
||||
pf->auth_domain_name);
|
||||
if (status)
|
||||
goto out;
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
gss_mech_free(gm);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_mech_register - register a GSS mechanism
|
||||
* @gm: GSS mechanism handle
|
||||
*
|
||||
* Returns zero if successful, or a negative errno.
|
||||
*/
|
||||
int gss_mech_register(struct gss_api_mech *gm)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = gss_mech_svc_setup(gm);
|
||||
if (status)
|
||||
return status;
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_add(&gm->gm_list, ®istered_mechs);
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gss_mech_register);
|
||||
|
||||
/**
|
||||
* gss_mech_unregister - release a GSS mechanism
|
||||
* @gm: GSS mechanism handle
|
||||
*
|
||||
*/
|
||||
void gss_mech_unregister(struct gss_api_mech *gm)
|
||||
{
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_del(&gm->gm_list);
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
|
||||
gss_mech_free(gm);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gss_mech_unregister);
|
||||
|
||||
struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
|
||||
{
|
||||
__module_get(gm->gm_owner);
|
||||
return gm;
|
||||
}
|
||||
EXPORT_SYMBOL(gss_mech_get);
|
||||
|
||||
static struct gss_api_mech *
|
||||
_gss_mech_get_by_name(const char *name)
|
||||
{
|
||||
struct gss_api_mech *pos, *gm = NULL;
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
if (0 == strcmp(name, pos->gm_name)) {
|
||||
if (try_module_get(pos->gm_owner))
|
||||
gm = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return gm;
|
||||
|
||||
}
|
||||
|
||||
struct gss_api_mech * gss_mech_get_by_name(const char *name)
|
||||
{
|
||||
struct gss_api_mech *gm = NULL;
|
||||
|
||||
gm = _gss_mech_get_by_name(name);
|
||||
if (!gm) {
|
||||
request_module("rpc-auth-gss-%s", name);
|
||||
gm = _gss_mech_get_by_name(name);
|
||||
}
|
||||
return gm;
|
||||
}
|
||||
|
||||
struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
|
||||
{
|
||||
struct gss_api_mech *pos, *gm = NULL;
|
||||
char buf[32];
|
||||
|
||||
if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
|
||||
return NULL;
|
||||
dprintk("RPC: %s(%s)\n", __func__, buf);
|
||||
request_module("rpc-auth-gss-%s", buf);
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
if (obj->len == pos->gm_oid.len) {
|
||||
if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) {
|
||||
if (try_module_get(pos->gm_owner))
|
||||
gm = pos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return gm;
|
||||
}
|
||||
|
||||
static inline int
|
||||
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
|
||||
{
|
||||
struct gss_api_mech *gm = NULL, *pos;
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
if (!mech_supports_pseudoflavor(pos, pseudoflavor))
|
||||
continue;
|
||||
if (try_module_get(pos->gm_owner))
|
||||
gm = pos;
|
||||
break;
|
||||
}
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return gm;
|
||||
}
|
||||
|
||||
struct gss_api_mech *
|
||||
gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
|
||||
{
|
||||
struct gss_api_mech *gm;
|
||||
|
||||
gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
|
||||
|
||||
if (!gm) {
|
||||
request_module("rpc-auth-gss-%u", pseudoflavor);
|
||||
gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
|
||||
}
|
||||
return gm;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
|
||||
* @array: array to fill in
|
||||
* @size: size of "array"
|
||||
*
|
||||
* Returns the number of array items filled in, or a negative errno.
|
||||
*
|
||||
* The returned array is not sorted by any policy. Callers should not
|
||||
* rely on the order of the items in the returned array.
|
||||
*/
|
||||
int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
|
||||
{
|
||||
struct gss_api_mech *pos = NULL;
|
||||
int j, i = 0;
|
||||
|
||||
spin_lock(®istered_mechs_lock);
|
||||
list_for_each_entry(pos, ®istered_mechs, gm_list) {
|
||||
for (j = 0; j < pos->gm_pf_num; j++) {
|
||||
if (i >= size) {
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
|
||||
}
|
||||
}
|
||||
spin_unlock(®istered_mechs_lock);
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
|
||||
* @gm: GSS mechanism handle
|
||||
* @qop: GSS quality-of-protection value
|
||||
* @service: GSS service value
|
||||
*
|
||||
* Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found.
|
||||
*/
|
||||
rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop,
|
||||
u32 service)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].qop == qop &&
|
||||
gm->gm_pfs[i].service == service) {
|
||||
return gm->gm_pfs[i].pseudoflavor;
|
||||
}
|
||||
}
|
||||
return RPC_AUTH_MAXFLAVOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
|
||||
* @info: a GSS mech OID, quality of protection, and service value
|
||||
*
|
||||
* Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
|
||||
* not supported.
|
||||
*/
|
||||
rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
|
||||
{
|
||||
rpc_authflavor_t pseudoflavor;
|
||||
struct gss_api_mech *gm;
|
||||
|
||||
gm = gss_mech_get_by_OID(&info->oid);
|
||||
if (gm == NULL)
|
||||
return RPC_AUTH_MAXFLAVOR;
|
||||
|
||||
pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service);
|
||||
|
||||
gss_mech_put(gm);
|
||||
return pseudoflavor;
|
||||
}
|
||||
|
||||
/**
|
||||
* gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
|
||||
* @pseudoflavor: GSS pseudoflavor to match
|
||||
* @info: rpcsec_gss_info structure to fill in
|
||||
*
|
||||
* Returns zero and fills in "info" if pseudoflavor matches a
|
||||
* supported mechanism. Otherwise a negative errno is returned.
|
||||
*/
|
||||
int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor,
|
||||
struct rpcsec_gss_info *info)
|
||||
{
|
||||
struct gss_api_mech *gm;
|
||||
int i;
|
||||
|
||||
gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
|
||||
if (gm == NULL)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
|
||||
memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
|
||||
info->oid.len = gm->gm_oid.len;
|
||||
info->qop = gm->gm_pfs[i].qop;
|
||||
info->service = gm->gm_pfs[i].service;
|
||||
gss_mech_put(gm);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
gss_mech_put(gm);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
u32
|
||||
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
|
||||
return gm->gm_pfs[i].service;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(gss_pseudoflavor_to_service);
|
||||
|
||||
char *
|
||||
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gm->gm_pf_num; i++) {
|
||||
if (gm->gm_pfs[i].service == service)
|
||||
return gm->gm_pfs[i].auth_domain_name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
gss_mech_put(struct gss_api_mech * gm)
|
||||
{
|
||||
if (gm)
|
||||
module_put(gm->gm_owner);
|
||||
}
|
||||
EXPORT_SYMBOL(gss_mech_put);
|
||||
|
||||
/* The mech could probably be determined from the token instead, but it's just
|
||||
* as easy for now to pass it in. */
|
||||
int
|
||||
gss_import_sec_context(const void *input_token, size_t bufsize,
|
||||
struct gss_api_mech *mech,
|
||||
struct gss_ctx **ctx_id,
|
||||
time_t *endtime,
|
||||
gfp_t gfp_mask)
|
||||
{
|
||||
if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask)))
|
||||
return -ENOMEM;
|
||||
(*ctx_id)->mech_type = gss_mech_get(mech);
|
||||
|
||||
return mech->gm_ops->gss_import_sec_context(input_token, bufsize,
|
||||
*ctx_id, endtime, gfp_mask);
|
||||
}
|
||||
|
||||
/* gss_get_mic: compute a mic over message and return mic_token. */
|
||||
|
||||
u32
|
||||
gss_get_mic(struct gss_ctx *context_handle,
|
||||
struct xdr_buf *message,
|
||||
struct xdr_netobj *mic_token)
|
||||
{
|
||||
return context_handle->mech_type->gm_ops
|
||||
->gss_get_mic(context_handle,
|
||||
message,
|
||||
mic_token);
|
||||
}
|
||||
|
||||
/* gss_verify_mic: check whether the provided mic_token verifies message. */
|
||||
|
||||
u32
|
||||
gss_verify_mic(struct gss_ctx *context_handle,
|
||||
struct xdr_buf *message,
|
||||
struct xdr_netobj *mic_token)
|
||||
{
|
||||
return context_handle->mech_type->gm_ops
|
||||
->gss_verify_mic(context_handle,
|
||||
message,
|
||||
mic_token);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called from both the client and server code.
|
||||
* Each makes guarantees about how much "slack" space is available
|
||||
* for the underlying function in "buf"'s head and tail while
|
||||
* performing the wrap.
|
||||
*
|
||||
* The client and server code allocate RPC_MAX_AUTH_SIZE extra
|
||||
* space in both the head and tail which is available for use by
|
||||
* the wrap function.
|
||||
*
|
||||
* Underlying functions should verify they do not use more than
|
||||
* RPC_MAX_AUTH_SIZE of extra space in either the head or tail
|
||||
* when performing the wrap.
|
||||
*/
|
||||
u32
|
||||
gss_wrap(struct gss_ctx *ctx_id,
|
||||
int offset,
|
||||
struct xdr_buf *buf,
|
||||
struct page **inpages)
|
||||
{
|
||||
return ctx_id->mech_type->gm_ops
|
||||
->gss_wrap(ctx_id, offset, buf, inpages);
|
||||
}
|
||||
|
||||
u32
|
||||
gss_unwrap(struct gss_ctx *ctx_id,
|
||||
int offset,
|
||||
struct xdr_buf *buf)
|
||||
{
|
||||
return ctx_id->mech_type->gm_ops
|
||||
->gss_unwrap(ctx_id, offset, buf);
|
||||
}
|
||||
|
||||
|
||||
/* gss_delete_sec_context: free all resources associated with context_handle.
|
||||
* Note this differs from the RFC 2744-specified prototype in that we don't
|
||||
* bother returning an output token, since it would never be used anyway. */
|
||||
|
||||
u32
|
||||
gss_delete_sec_context(struct gss_ctx **context_handle)
|
||||
{
|
||||
dprintk("RPC: gss_delete_sec_context deleting %p\n",
|
||||
*context_handle);
|
||||
|
||||
if (!*context_handle)
|
||||
return GSS_S_NO_CONTEXT;
|
||||
if ((*context_handle)->internal_ctx_id)
|
||||
(*context_handle)->mech_type->gm_ops
|
||||
->gss_delete_sec_context((*context_handle)
|
||||
->internal_ctx_id);
|
||||
gss_mech_put((*context_handle)->mech_type);
|
||||
kfree(*context_handle);
|
||||
*context_handle=NULL;
|
||||
return GSS_S_COMPLETE;
|
||||
}
|
382
net/sunrpc/auth_gss/gss_rpc_upcall.c
Normal file
382
net/sunrpc/auth_gss/gss_rpc_upcall.c
Normal file
|
@ -0,0 +1,382 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_rpc_upcall.c
|
||||
*
|
||||
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/un.h>
|
||||
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
#include "gss_rpc_upcall.h"
|
||||
|
||||
#define GSSPROXY_SOCK_PATHNAME "/var/run/gssproxy.sock"
|
||||
|
||||
#define GSSPROXY_PROGRAM (400112u)
|
||||
#define GSSPROXY_VERS_1 (1u)
|
||||
|
||||
/*
|
||||
* Encoding/Decoding functions
|
||||
*/
|
||||
|
||||
enum {
|
||||
GSSX_NULL = 0, /* Unused */
|
||||
GSSX_INDICATE_MECHS = 1,
|
||||
GSSX_GET_CALL_CONTEXT = 2,
|
||||
GSSX_IMPORT_AND_CANON_NAME = 3,
|
||||
GSSX_EXPORT_CRED = 4,
|
||||
GSSX_IMPORT_CRED = 5,
|
||||
GSSX_ACQUIRE_CRED = 6,
|
||||
GSSX_STORE_CRED = 7,
|
||||
GSSX_INIT_SEC_CONTEXT = 8,
|
||||
GSSX_ACCEPT_SEC_CONTEXT = 9,
|
||||
GSSX_RELEASE_HANDLE = 10,
|
||||
GSSX_GET_MIC = 11,
|
||||
GSSX_VERIFY = 12,
|
||||
GSSX_WRAP = 13,
|
||||
GSSX_UNWRAP = 14,
|
||||
GSSX_WRAP_SIZE_LIMIT = 15,
|
||||
};
|
||||
|
||||
#define PROC(proc, name) \
|
||||
[GSSX_##proc] = { \
|
||||
.p_proc = GSSX_##proc, \
|
||||
.p_encode = (kxdreproc_t)gssx_enc_##name, \
|
||||
.p_decode = (kxdrdproc_t)gssx_dec_##name, \
|
||||
.p_arglen = GSSX_ARG_##name##_sz, \
|
||||
.p_replen = GSSX_RES_##name##_sz, \
|
||||
.p_statidx = GSSX_##proc, \
|
||||
.p_name = #proc, \
|
||||
}
|
||||
|
||||
static struct rpc_procinfo gssp_procedures[] = {
|
||||
PROC(INDICATE_MECHS, indicate_mechs),
|
||||
PROC(GET_CALL_CONTEXT, get_call_context),
|
||||
PROC(IMPORT_AND_CANON_NAME, import_and_canon_name),
|
||||
PROC(EXPORT_CRED, export_cred),
|
||||
PROC(IMPORT_CRED, import_cred),
|
||||
PROC(ACQUIRE_CRED, acquire_cred),
|
||||
PROC(STORE_CRED, store_cred),
|
||||
PROC(INIT_SEC_CONTEXT, init_sec_context),
|
||||
PROC(ACCEPT_SEC_CONTEXT, accept_sec_context),
|
||||
PROC(RELEASE_HANDLE, release_handle),
|
||||
PROC(GET_MIC, get_mic),
|
||||
PROC(VERIFY, verify),
|
||||
PROC(WRAP, wrap),
|
||||
PROC(UNWRAP, unwrap),
|
||||
PROC(WRAP_SIZE_LIMIT, wrap_size_limit),
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Common transport functions
|
||||
*/
|
||||
|
||||
static const struct rpc_program gssp_program;
|
||||
|
||||
static int gssp_rpc_create(struct net *net, struct rpc_clnt **_clnt)
|
||||
{
|
||||
static const struct sockaddr_un gssp_localaddr = {
|
||||
.sun_family = AF_LOCAL,
|
||||
.sun_path = GSSPROXY_SOCK_PATHNAME,
|
||||
};
|
||||
struct rpc_create_args args = {
|
||||
.net = net,
|
||||
.protocol = XPRT_TRANSPORT_LOCAL,
|
||||
.address = (struct sockaddr *)&gssp_localaddr,
|
||||
.addrsize = sizeof(gssp_localaddr),
|
||||
.servername = "localhost",
|
||||
.program = &gssp_program,
|
||||
.version = GSSPROXY_VERS_1,
|
||||
.authflavor = RPC_AUTH_NULL,
|
||||
/*
|
||||
* Note we want connection to be done in the caller's
|
||||
* filesystem namespace. We therefore turn off the idle
|
||||
* timeout, which would result in reconnections being
|
||||
* done without the correct namespace:
|
||||
*/
|
||||
.flags = RPC_CLNT_CREATE_NOPING |
|
||||
RPC_CLNT_CREATE_NO_IDLE_TIMEOUT
|
||||
};
|
||||
struct rpc_clnt *clnt;
|
||||
int result = 0;
|
||||
|
||||
clnt = rpc_create(&args);
|
||||
if (IS_ERR(clnt)) {
|
||||
dprintk("RPC: failed to create AF_LOCAL gssproxy "
|
||||
"client (errno %ld).\n", PTR_ERR(clnt));
|
||||
result = PTR_ERR(clnt);
|
||||
*_clnt = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dprintk("RPC: created new gssp local client (gssp_local_clnt: "
|
||||
"%p)\n", clnt);
|
||||
*_clnt = clnt;
|
||||
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
void init_gssp_clnt(struct sunrpc_net *sn)
|
||||
{
|
||||
mutex_init(&sn->gssp_lock);
|
||||
sn->gssp_clnt = NULL;
|
||||
}
|
||||
|
||||
int set_gssp_clnt(struct net *net)
|
||||
{
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
struct rpc_clnt *clnt;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&sn->gssp_lock);
|
||||
ret = gssp_rpc_create(net, &clnt);
|
||||
if (!ret) {
|
||||
if (sn->gssp_clnt)
|
||||
rpc_shutdown_client(sn->gssp_clnt);
|
||||
sn->gssp_clnt = clnt;
|
||||
}
|
||||
mutex_unlock(&sn->gssp_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clear_gssp_clnt(struct sunrpc_net *sn)
|
||||
{
|
||||
mutex_lock(&sn->gssp_lock);
|
||||
if (sn->gssp_clnt) {
|
||||
rpc_shutdown_client(sn->gssp_clnt);
|
||||
sn->gssp_clnt = NULL;
|
||||
}
|
||||
mutex_unlock(&sn->gssp_lock);
|
||||
}
|
||||
|
||||
static struct rpc_clnt *get_gssp_clnt(struct sunrpc_net *sn)
|
||||
{
|
||||
struct rpc_clnt *clnt;
|
||||
|
||||
mutex_lock(&sn->gssp_lock);
|
||||
clnt = sn->gssp_clnt;
|
||||
if (clnt)
|
||||
atomic_inc(&clnt->cl_count);
|
||||
mutex_unlock(&sn->gssp_lock);
|
||||
return clnt;
|
||||
}
|
||||
|
||||
static int gssp_call(struct net *net, struct rpc_message *msg)
|
||||
{
|
||||
struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
|
||||
struct rpc_clnt *clnt;
|
||||
int status;
|
||||
|
||||
clnt = get_gssp_clnt(sn);
|
||||
if (!clnt)
|
||||
return -EIO;
|
||||
status = rpc_call_sync(clnt, msg, 0);
|
||||
if (status < 0) {
|
||||
dprintk("gssp: rpc_call returned error %d\n", -status);
|
||||
switch (status) {
|
||||
case -EPROTONOSUPPORT:
|
||||
status = -EINVAL;
|
||||
break;
|
||||
case -ECONNREFUSED:
|
||||
case -ETIMEDOUT:
|
||||
case -ENOTCONN:
|
||||
status = -EAGAIN;
|
||||
break;
|
||||
case -ERESTARTSYS:
|
||||
if (signalled ())
|
||||
status = -EINTR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
rpc_release_client(clnt);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void gssp_free_receive_pages(struct gssx_arg_accept_sec_context *arg)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < arg->npages && arg->pages[i]; i++)
|
||||
__free_page(arg->pages[i]);
|
||||
}
|
||||
|
||||
static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg)
|
||||
{
|
||||
arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE);
|
||||
arg->pages = kzalloc(arg->npages * sizeof(struct page *), GFP_KERNEL);
|
||||
/*
|
||||
* XXX: actual pages are allocated by xdr layer in
|
||||
* xdr_partial_copy_from_skb.
|
||||
*/
|
||||
if (!arg->pages)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public functions
|
||||
*/
|
||||
|
||||
/* numbers somewhat arbitrary but large enough for current needs */
|
||||
#define GSSX_MAX_OUT_HANDLE 128
|
||||
#define GSSX_MAX_SRC_PRINC 256
|
||||
#define GSSX_KMEMBUF (GSSX_max_output_handle_sz + \
|
||||
GSSX_max_oid_sz + \
|
||||
GSSX_max_princ_sz + \
|
||||
sizeof(struct svc_cred))
|
||||
|
||||
int gssp_accept_sec_context_upcall(struct net *net,
|
||||
struct gssp_upcall_data *data)
|
||||
{
|
||||
struct gssx_ctx ctxh = {
|
||||
.state = data->in_handle
|
||||
};
|
||||
struct gssx_arg_accept_sec_context arg = {
|
||||
.input_token = data->in_token,
|
||||
};
|
||||
struct gssx_ctx rctxh = {
|
||||
/*
|
||||
* pass in the max length we expect for each of these
|
||||
* buffers but let the xdr code kmalloc them:
|
||||
*/
|
||||
.exported_context_token.len = GSSX_max_output_handle_sz,
|
||||
.mech.len = GSS_OID_MAX_LEN,
|
||||
.src_name.display_name.len = GSSX_max_princ_sz
|
||||
};
|
||||
struct gssx_res_accept_sec_context res = {
|
||||
.context_handle = &rctxh,
|
||||
.output_token = &data->out_token
|
||||
};
|
||||
struct rpc_message msg = {
|
||||
.rpc_proc = &gssp_procedures[GSSX_ACCEPT_SEC_CONTEXT],
|
||||
.rpc_argp = &arg,
|
||||
.rpc_resp = &res,
|
||||
.rpc_cred = NULL, /* FIXME ? */
|
||||
};
|
||||
struct xdr_netobj client_name = { 0 , NULL };
|
||||
int ret;
|
||||
|
||||
if (data->in_handle.len != 0)
|
||||
arg.context_handle = &ctxh;
|
||||
res.output_token->len = GSSX_max_output_token_sz;
|
||||
|
||||
ret = gssp_alloc_receive_pages(&arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* use nfs/ for targ_name ? */
|
||||
|
||||
ret = gssp_call(net, &msg);
|
||||
|
||||
gssp_free_receive_pages(&arg);
|
||||
|
||||
/* we need to fetch all data even in case of error so
|
||||
* that we can free special strctures is they have been allocated */
|
||||
data->major_status = res.status.major_status;
|
||||
data->minor_status = res.status.minor_status;
|
||||
if (res.context_handle) {
|
||||
data->out_handle = rctxh.exported_context_token;
|
||||
data->mech_oid.len = rctxh.mech.len;
|
||||
if (rctxh.mech.data)
|
||||
memcpy(data->mech_oid.data, rctxh.mech.data,
|
||||
data->mech_oid.len);
|
||||
client_name = rctxh.src_name.display_name;
|
||||
}
|
||||
|
||||
if (res.options.count == 1) {
|
||||
gssx_buffer *value = &res.options.data[0].value;
|
||||
/* Currently we only decode CREDS_VALUE, if we add
|
||||
* anything else we'll have to loop and match on the
|
||||
* option name */
|
||||
if (value->len == 1) {
|
||||
/* steal group info from struct svc_cred */
|
||||
data->creds = *(struct svc_cred *)value->data;
|
||||
data->found_creds = 1;
|
||||
}
|
||||
/* whether we use it or not, free data */
|
||||
kfree(value->data);
|
||||
}
|
||||
|
||||
if (res.options.count != 0) {
|
||||
kfree(res.options.data);
|
||||
}
|
||||
|
||||
/* convert to GSS_NT_HOSTBASED_SERVICE form and set into creds */
|
||||
if (data->found_creds && client_name.data != NULL) {
|
||||
char *c;
|
||||
|
||||
data->creds.cr_principal = kstrndup(client_name.data,
|
||||
client_name.len, GFP_KERNEL);
|
||||
if (data->creds.cr_principal) {
|
||||
/* terminate and remove realm part */
|
||||
c = strchr(data->creds.cr_principal, '@');
|
||||
if (c) {
|
||||
*c = '\0';
|
||||
|
||||
/* change service-hostname delimiter */
|
||||
c = strchr(data->creds.cr_principal, '/');
|
||||
if (c) *c = '@';
|
||||
}
|
||||
if (!c) {
|
||||
/* not a service principal */
|
||||
kfree(data->creds.cr_principal);
|
||||
data->creds.cr_principal = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
kfree(client_name.data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gssp_free_upcall_data(struct gssp_upcall_data *data)
|
||||
{
|
||||
kfree(data->in_handle.data);
|
||||
kfree(data->out_handle.data);
|
||||
kfree(data->out_token.data);
|
||||
free_svc_cred(&data->creds);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialization stuff
|
||||
*/
|
||||
|
||||
static const struct rpc_version gssp_version1 = {
|
||||
.number = GSSPROXY_VERS_1,
|
||||
.nrprocs = ARRAY_SIZE(gssp_procedures),
|
||||
.procs = gssp_procedures,
|
||||
};
|
||||
|
||||
static const struct rpc_version *gssp_version[] = {
|
||||
NULL,
|
||||
&gssp_version1,
|
||||
};
|
||||
|
||||
static struct rpc_stat gssp_stats;
|
||||
|
||||
static const struct rpc_program gssp_program = {
|
||||
.name = "gssproxy",
|
||||
.number = GSSPROXY_PROGRAM,
|
||||
.nrvers = ARRAY_SIZE(gssp_version),
|
||||
.version = gssp_version,
|
||||
.stats = &gssp_stats,
|
||||
};
|
48
net/sunrpc/auth_gss/gss_rpc_upcall.h
Normal file
48
net/sunrpc/auth_gss/gss_rpc_upcall.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* linux/net/sunrpc/gss_rpc_upcall.h
|
||||
*
|
||||
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _GSS_RPC_UPCALL_H
|
||||
#define _GSS_RPC_UPCALL_H
|
||||
|
||||
#include <linux/sunrpc/gss_api.h>
|
||||
#include <linux/sunrpc/auth_gss.h>
|
||||
#include "gss_rpc_xdr.h"
|
||||
#include "../netns.h"
|
||||
|
||||
struct gssp_upcall_data {
|
||||
struct xdr_netobj in_handle;
|
||||
struct gssp_in_token in_token;
|
||||
struct xdr_netobj out_handle;
|
||||
struct xdr_netobj out_token;
|
||||
struct rpcsec_gss_oid mech_oid;
|
||||
struct svc_cred creds;
|
||||
int found_creds;
|
||||
int major_status;
|
||||
int minor_status;
|
||||
};
|
||||
|
||||
int gssp_accept_sec_context_upcall(struct net *net,
|
||||
struct gssp_upcall_data *data);
|
||||
void gssp_free_upcall_data(struct gssp_upcall_data *data);
|
||||
|
||||
void init_gssp_clnt(struct sunrpc_net *);
|
||||
int set_gssp_clnt(struct net *);
|
||||
void clear_gssp_clnt(struct sunrpc_net *);
|
||||
#endif /* _GSS_RPC_UPCALL_H */
|
839
net/sunrpc/auth_gss/gss_rpc_xdr.c
Normal file
839
net/sunrpc/auth_gss/gss_rpc_xdr.c
Normal file
|
@ -0,0 +1,839 @@
|
|||
/*
|
||||
* GSS Proxy upcall module
|
||||
*
|
||||
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/sunrpc/svcauth.h>
|
||||
#include "gss_rpc_xdr.h"
|
||||
|
||||
static int gssx_enc_bool(struct xdr_stream *xdr, int v)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
*p = v ? xdr_one : xdr_zero;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_dec_bool(struct xdr_stream *xdr, u32 *v)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
*v = be32_to_cpu(*p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_enc_buffer(struct xdr_stream *xdr,
|
||||
gssx_buffer *buf)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, sizeof(u32) + buf->len);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
xdr_encode_opaque(p, buf->data, buf->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_enc_in_token(struct xdr_stream *xdr,
|
||||
struct gssp_in_token *in)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
*p = cpu_to_be32(in->page_len);
|
||||
|
||||
/* all we need to do is to write pages */
|
||||
xdr_write_pages(xdr, in->pages, in->page_base, in->page_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int gssx_dec_buffer(struct xdr_stream *xdr,
|
||||
gssx_buffer *buf)
|
||||
{
|
||||
u32 length;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
|
||||
length = be32_to_cpup(p);
|
||||
p = xdr_inline_decode(xdr, length);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
|
||||
if (buf->len == 0) {
|
||||
/* we intentionally are not interested in this buffer */
|
||||
return 0;
|
||||
}
|
||||
if (length > buf->len)
|
||||
return -ENOSPC;
|
||||
|
||||
if (!buf->data) {
|
||||
buf->data = kmemdup(p, length, GFP_KERNEL);
|
||||
if (!buf->data)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
memcpy(buf->data, p, length);
|
||||
}
|
||||
buf->len = length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_enc_option(struct xdr_stream *xdr,
|
||||
struct gssx_option *opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = gssx_enc_buffer(xdr, &opt->option);
|
||||
if (err)
|
||||
return err;
|
||||
err = gssx_enc_buffer(xdr, &opt->value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_dec_option(struct xdr_stream *xdr,
|
||||
struct gssx_option *opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = gssx_dec_buffer(xdr, &opt->option);
|
||||
if (err)
|
||||
return err;
|
||||
err = gssx_dec_buffer(xdr, &opt->value);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dummy_enc_opt_array(struct xdr_stream *xdr,
|
||||
struct gssx_option_array *oa)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
if (oa->count != 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
*p = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_dec_opt_array(struct xdr_stream *xdr,
|
||||
struct gssx_option_array *oa)
|
||||
{
|
||||
struct gssx_option dummy;
|
||||
u32 count, i;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
count = be32_to_cpup(p++);
|
||||
memset(&dummy, 0, sizeof(dummy));
|
||||
for (i = 0; i < count; i++) {
|
||||
gssx_dec_option(xdr, &dummy);
|
||||
}
|
||||
|
||||
oa->count = 0;
|
||||
oa->data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_host_u32(struct xdr_stream *xdr, u32 *res)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (!p)
|
||||
return -EINVAL;
|
||||
/* Contents of linux creds are all host-endian: */
|
||||
memcpy(res, p, sizeof(u32));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_dec_linux_creds(struct xdr_stream *xdr,
|
||||
struct svc_cred *creds)
|
||||
{
|
||||
u32 length;
|
||||
__be32 *p;
|
||||
u32 tmp;
|
||||
u32 N;
|
||||
int i, err;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
|
||||
length = be32_to_cpup(p);
|
||||
|
||||
if (length > (3 + NGROUPS_MAX) * sizeof(u32))
|
||||
return -ENOSPC;
|
||||
|
||||
/* uid */
|
||||
err = get_host_u32(xdr, &tmp);
|
||||
if (err)
|
||||
return err;
|
||||
creds->cr_uid = make_kuid(&init_user_ns, tmp);
|
||||
|
||||
/* gid */
|
||||
err = get_host_u32(xdr, &tmp);
|
||||
if (err)
|
||||
return err;
|
||||
creds->cr_gid = make_kgid(&init_user_ns, tmp);
|
||||
|
||||
/* number of additional gid's */
|
||||
err = get_host_u32(xdr, &tmp);
|
||||
if (err)
|
||||
return err;
|
||||
N = tmp;
|
||||
if ((3 + N) * sizeof(u32) != length)
|
||||
return -EINVAL;
|
||||
creds->cr_group_info = groups_alloc(N);
|
||||
if (creds->cr_group_info == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* gid's */
|
||||
for (i = 0; i < N; i++) {
|
||||
kgid_t kgid;
|
||||
err = get_host_u32(xdr, &tmp);
|
||||
if (err)
|
||||
goto out_free_groups;
|
||||
err = -EINVAL;
|
||||
kgid = make_kgid(&init_user_ns, tmp);
|
||||
if (!gid_valid(kgid))
|
||||
goto out_free_groups;
|
||||
GROUP_AT(creds->cr_group_info, i) = kgid;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_free_groups:
|
||||
groups_free(creds->cr_group_info);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_dec_option_array(struct xdr_stream *xdr,
|
||||
struct gssx_option_array *oa)
|
||||
{
|
||||
struct svc_cred *creds;
|
||||
u32 count, i;
|
||||
__be32 *p;
|
||||
int err;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
count = be32_to_cpup(p++);
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
/* we recognize only 1 currently: CREDS_VALUE */
|
||||
oa->count = 1;
|
||||
|
||||
oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL);
|
||||
if (!oa->data)
|
||||
return -ENOMEM;
|
||||
|
||||
creds = kmalloc(sizeof(struct svc_cred), GFP_KERNEL);
|
||||
if (!creds) {
|
||||
kfree(oa->data);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
oa->data[0].option.data = CREDS_VALUE;
|
||||
oa->data[0].option.len = sizeof(CREDS_VALUE);
|
||||
oa->data[0].value.data = (void *)creds;
|
||||
oa->data[0].value.len = 0;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
gssx_buffer dummy = { 0, NULL };
|
||||
u32 length;
|
||||
|
||||
/* option buffer */
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
|
||||
length = be32_to_cpup(p);
|
||||
p = xdr_inline_decode(xdr, length);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
|
||||
if (length == sizeof(CREDS_VALUE) &&
|
||||
memcmp(p, CREDS_VALUE, sizeof(CREDS_VALUE)) == 0) {
|
||||
/* We have creds here. parse them */
|
||||
err = gssx_dec_linux_creds(xdr, creds);
|
||||
if (err)
|
||||
return err;
|
||||
oa->data[0].value.len = 1; /* presence */
|
||||
} else {
|
||||
/* consume uninteresting buffer */
|
||||
err = gssx_dec_buffer(xdr, &dummy);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_dec_status(struct xdr_stream *xdr,
|
||||
struct gssx_status *status)
|
||||
{
|
||||
__be32 *p;
|
||||
int err;
|
||||
|
||||
/* status->major_status */
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
p = xdr_decode_hyper(p, &status->major_status);
|
||||
|
||||
/* status->mech */
|
||||
err = gssx_dec_buffer(xdr, &status->mech);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* status->minor_status */
|
||||
p = xdr_inline_decode(xdr, 8);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
p = xdr_decode_hyper(p, &status->minor_status);
|
||||
|
||||
/* status->major_status_string */
|
||||
err = gssx_dec_buffer(xdr, &status->major_status_string);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* status->minor_status_string */
|
||||
err = gssx_dec_buffer(xdr, &status->minor_status_string);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* status->server_ctx */
|
||||
err = gssx_dec_buffer(xdr, &status->server_ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* we assume we have no options for now, so simply consume them */
|
||||
/* status->options */
|
||||
err = dummy_dec_opt_array(xdr, &status->options);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_enc_call_ctx(struct xdr_stream *xdr,
|
||||
struct gssx_call_ctx *ctx)
|
||||
{
|
||||
struct gssx_option opt;
|
||||
__be32 *p;
|
||||
int err;
|
||||
|
||||
/* ctx->locale */
|
||||
err = gssx_enc_buffer(xdr, &ctx->locale);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->server_ctx */
|
||||
err = gssx_enc_buffer(xdr, &ctx->server_ctx);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* we always want to ask for lucid contexts */
|
||||
/* ctx->options */
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
*p = cpu_to_be32(2);
|
||||
|
||||
/* we want a lucid_v1 context */
|
||||
opt.option.data = LUCID_OPTION;
|
||||
opt.option.len = sizeof(LUCID_OPTION);
|
||||
opt.value.data = LUCID_VALUE;
|
||||
opt.value.len = sizeof(LUCID_VALUE);
|
||||
err = gssx_enc_option(xdr, &opt);
|
||||
|
||||
/* ..and user creds */
|
||||
opt.option.data = CREDS_OPTION;
|
||||
opt.option.len = sizeof(CREDS_OPTION);
|
||||
opt.value.data = CREDS_VALUE;
|
||||
opt.value.len = sizeof(CREDS_VALUE);
|
||||
err = gssx_enc_option(xdr, &opt);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_dec_name_attr(struct xdr_stream *xdr,
|
||||
struct gssx_name_attr *attr)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* attr->attr */
|
||||
err = gssx_dec_buffer(xdr, &attr->attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* attr->value */
|
||||
err = gssx_dec_buffer(xdr, &attr->value);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* attr->extensions */
|
||||
err = dummy_dec_opt_array(xdr, &attr->extensions);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dummy_enc_nameattr_array(struct xdr_stream *xdr,
|
||||
struct gssx_name_attr_array *naa)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
if (naa->count != 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
*p = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dummy_dec_nameattr_array(struct xdr_stream *xdr,
|
||||
struct gssx_name_attr_array *naa)
|
||||
{
|
||||
struct gssx_name_attr dummy = { .attr = {.len = 0} };
|
||||
u32 count, i;
|
||||
__be32 *p;
|
||||
|
||||
p = xdr_inline_decode(xdr, 4);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
count = be32_to_cpup(p++);
|
||||
for (i = 0; i < count; i++) {
|
||||
gssx_dec_name_attr(xdr, &dummy);
|
||||
}
|
||||
|
||||
naa->count = 0;
|
||||
naa->data = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct xdr_netobj zero_netobj = {};
|
||||
|
||||
static struct gssx_name_attr_array zero_name_attr_array = {};
|
||||
|
||||
static struct gssx_option_array zero_option_array = {};
|
||||
|
||||
static int gssx_enc_name(struct xdr_stream *xdr,
|
||||
struct gssx_name *name)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* name->display_name */
|
||||
err = gssx_enc_buffer(xdr, &name->display_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* name->name_type */
|
||||
err = gssx_enc_buffer(xdr, &zero_netobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* name->exported_name */
|
||||
err = gssx_enc_buffer(xdr, &zero_netobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* name->exported_composite_name */
|
||||
err = gssx_enc_buffer(xdr, &zero_netobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* leave name_attributes empty for now, will add once we have any
|
||||
* to pass up at all */
|
||||
/* name->name_attributes */
|
||||
err = dummy_enc_nameattr_array(xdr, &zero_name_attr_array);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* leave options empty for now, will add once we have any options
|
||||
* to pass up at all */
|
||||
/* name->extensions */
|
||||
err = dummy_enc_opt_array(xdr, &zero_option_array);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int gssx_dec_name(struct xdr_stream *xdr,
|
||||
struct gssx_name *name)
|
||||
{
|
||||
struct xdr_netobj dummy_netobj = { .len = 0 };
|
||||
struct gssx_name_attr_array dummy_name_attr_array = { .count = 0 };
|
||||
struct gssx_option_array dummy_option_array = { .count = 0 };
|
||||
int err;
|
||||
|
||||
/* name->display_name */
|
||||
err = gssx_dec_buffer(xdr, &name->display_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* name->name_type */
|
||||
err = gssx_dec_buffer(xdr, &dummy_netobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* name->exported_name */
|
||||
err = gssx_dec_buffer(xdr, &dummy_netobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* name->exported_composite_name */
|
||||
err = gssx_dec_buffer(xdr, &dummy_netobj);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* we assume we have no attributes for now, so simply consume them */
|
||||
/* name->name_attributes */
|
||||
err = dummy_dec_nameattr_array(xdr, &dummy_name_attr_array);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* we assume we have no options for now, so simply consume them */
|
||||
/* name->extensions */
|
||||
err = dummy_dec_opt_array(xdr, &dummy_option_array);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dummy_enc_credel_array(struct xdr_stream *xdr,
|
||||
struct gssx_cred_element_array *cea)
|
||||
{
|
||||
__be32 *p;
|
||||
|
||||
if (cea->count != 0)
|
||||
return -EINVAL;
|
||||
|
||||
p = xdr_reserve_space(xdr, 4);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
*p = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gssx_enc_cred(struct xdr_stream *xdr,
|
||||
struct gssx_cred *cred)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* cred->desired_name */
|
||||
err = gssx_enc_name(xdr, &cred->desired_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* cred->elements */
|
||||
err = dummy_enc_credel_array(xdr, &cred->elements);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* cred->cred_handle_reference */
|
||||
err = gssx_enc_buffer(xdr, &cred->cred_handle_reference);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* cred->needs_release */
|
||||
err = gssx_enc_bool(xdr, cred->needs_release);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_enc_ctx(struct xdr_stream *xdr,
|
||||
struct gssx_ctx *ctx)
|
||||
{
|
||||
__be32 *p;
|
||||
int err;
|
||||
|
||||
/* ctx->exported_context_token */
|
||||
err = gssx_enc_buffer(xdr, &ctx->exported_context_token);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->state */
|
||||
err = gssx_enc_buffer(xdr, &ctx->state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->need_release */
|
||||
err = gssx_enc_bool(xdr, ctx->need_release);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->mech */
|
||||
err = gssx_enc_buffer(xdr, &ctx->mech);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->src_name */
|
||||
err = gssx_enc_name(xdr, &ctx->src_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->targ_name */
|
||||
err = gssx_enc_name(xdr, &ctx->targ_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->lifetime */
|
||||
p = xdr_reserve_space(xdr, 8+8);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
p = xdr_encode_hyper(p, ctx->lifetime);
|
||||
|
||||
/* ctx->ctx_flags */
|
||||
p = xdr_encode_hyper(p, ctx->ctx_flags);
|
||||
|
||||
/* ctx->locally_initiated */
|
||||
err = gssx_enc_bool(xdr, ctx->locally_initiated);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->open */
|
||||
err = gssx_enc_bool(xdr, ctx->open);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* leave options empty for now, will add once we have any options
|
||||
* to pass up at all */
|
||||
/* ctx->options */
|
||||
err = dummy_enc_opt_array(xdr, &ctx->options);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_dec_ctx(struct xdr_stream *xdr,
|
||||
struct gssx_ctx *ctx)
|
||||
{
|
||||
__be32 *p;
|
||||
int err;
|
||||
|
||||
/* ctx->exported_context_token */
|
||||
err = gssx_dec_buffer(xdr, &ctx->exported_context_token);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->state */
|
||||
err = gssx_dec_buffer(xdr, &ctx->state);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->need_release */
|
||||
err = gssx_dec_bool(xdr, &ctx->need_release);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->mech */
|
||||
err = gssx_dec_buffer(xdr, &ctx->mech);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->src_name */
|
||||
err = gssx_dec_name(xdr, &ctx->src_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->targ_name */
|
||||
err = gssx_dec_name(xdr, &ctx->targ_name);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->lifetime */
|
||||
p = xdr_inline_decode(xdr, 8+8);
|
||||
if (unlikely(p == NULL))
|
||||
return -ENOSPC;
|
||||
p = xdr_decode_hyper(p, &ctx->lifetime);
|
||||
|
||||
/* ctx->ctx_flags */
|
||||
p = xdr_decode_hyper(p, &ctx->ctx_flags);
|
||||
|
||||
/* ctx->locally_initiated */
|
||||
err = gssx_dec_bool(xdr, &ctx->locally_initiated);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* ctx->open */
|
||||
err = gssx_dec_bool(xdr, &ctx->open);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* we assume we have no options for now, so simply consume them */
|
||||
/* ctx->options */
|
||||
err = dummy_dec_opt_array(xdr, &ctx->options);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int gssx_enc_cb(struct xdr_stream *xdr, struct gssx_cb *cb)
|
||||
{
|
||||
__be32 *p;
|
||||
int err;
|
||||
|
||||
/* cb->initiator_addrtype */
|
||||
p = xdr_reserve_space(xdr, 8);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
p = xdr_encode_hyper(p, cb->initiator_addrtype);
|
||||
|
||||
/* cb->initiator_address */
|
||||
err = gssx_enc_buffer(xdr, &cb->initiator_address);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* cb->acceptor_addrtype */
|
||||
p = xdr_reserve_space(xdr, 8);
|
||||
if (!p)
|
||||
return -ENOSPC;
|
||||
p = xdr_encode_hyper(p, cb->acceptor_addrtype);
|
||||
|
||||
/* cb->acceptor_address */
|
||||
err = gssx_enc_buffer(xdr, &cb->acceptor_address);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* cb->application_data */
|
||||
err = gssx_enc_buffer(xdr, &cb->application_data);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void gssx_enc_accept_sec_context(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
struct gssx_arg_accept_sec_context *arg)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = gssx_enc_call_ctx(xdr, &arg->call_ctx);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* arg->context_handle */
|
||||
if (arg->context_handle)
|
||||
err = gssx_enc_ctx(xdr, arg->context_handle);
|
||||
else
|
||||
err = gssx_enc_bool(xdr, 0);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* arg->cred_handle */
|
||||
if (arg->cred_handle)
|
||||
err = gssx_enc_cred(xdr, arg->cred_handle);
|
||||
else
|
||||
err = gssx_enc_bool(xdr, 0);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* arg->input_token */
|
||||
err = gssx_enc_in_token(xdr, &arg->input_token);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* arg->input_cb */
|
||||
if (arg->input_cb)
|
||||
err = gssx_enc_cb(xdr, arg->input_cb);
|
||||
else
|
||||
err = gssx_enc_bool(xdr, 0);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = gssx_enc_bool(xdr, arg->ret_deleg_cred);
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
/* leave options empty for now, will add once we have any options
|
||||
* to pass up at all */
|
||||
/* arg->options */
|
||||
err = dummy_enc_opt_array(xdr, &arg->options);
|
||||
|
||||
xdr_inline_pages(&req->rq_rcv_buf,
|
||||
PAGE_SIZE/2 /* pretty arbitrary */,
|
||||
arg->pages, 0 /* page base */, arg->npages * PAGE_SIZE);
|
||||
done:
|
||||
if (err)
|
||||
dprintk("RPC: gssx_enc_accept_sec_context: %d\n", err);
|
||||
}
|
||||
|
||||
int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
struct gssx_res_accept_sec_context *res)
|
||||
{
|
||||
u32 value_follows;
|
||||
int err;
|
||||
|
||||
/* res->status */
|
||||
err = gssx_dec_status(xdr, &res->status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* res->context_handle */
|
||||
err = gssx_dec_bool(xdr, &value_follows);
|
||||
if (err)
|
||||
return err;
|
||||
if (value_follows) {
|
||||
err = gssx_dec_ctx(xdr, res->context_handle);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
res->context_handle = NULL;
|
||||
}
|
||||
|
||||
/* res->output_token */
|
||||
err = gssx_dec_bool(xdr, &value_follows);
|
||||
if (err)
|
||||
return err;
|
||||
if (value_follows) {
|
||||
err = gssx_dec_buffer(xdr, res->output_token);
|
||||
if (err)
|
||||
return err;
|
||||
} else {
|
||||
res->output_token = NULL;
|
||||
}
|
||||
|
||||
/* res->delegated_cred_handle */
|
||||
err = gssx_dec_bool(xdr, &value_follows);
|
||||
if (err)
|
||||
return err;
|
||||
if (value_follows) {
|
||||
/* we do not support upcall servers sending this data. */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* res->options */
|
||||
err = gssx_dec_option_array(xdr, &res->options);
|
||||
|
||||
return err;
|
||||
}
|
267
net/sunrpc/auth_gss/gss_rpc_xdr.h
Normal file
267
net/sunrpc/auth_gss/gss_rpc_xdr.h
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* GSS Proxy upcall module
|
||||
*
|
||||
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_GSS_RPC_XDR_H
|
||||
#define _LINUX_GSS_RPC_XDR_H
|
||||
|
||||
#include <linux/sunrpc/xdr.h>
|
||||
#include <linux/sunrpc/clnt.h>
|
||||
#include <linux/sunrpc/xprtsock.h>
|
||||
|
||||
#ifdef RPC_DEBUG
|
||||
# define RPCDBG_FACILITY RPCDBG_AUTH
|
||||
#endif
|
||||
|
||||
#define LUCID_OPTION "exported_context_type"
|
||||
#define LUCID_VALUE "linux_lucid_v1"
|
||||
#define CREDS_OPTION "exported_creds_type"
|
||||
#define CREDS_VALUE "linux_creds_v1"
|
||||
|
||||
typedef struct xdr_netobj gssx_buffer;
|
||||
typedef struct xdr_netobj utf8string;
|
||||
typedef struct xdr_netobj gssx_OID;
|
||||
|
||||
enum gssx_cred_usage {
|
||||
GSSX_C_INITIATE = 1,
|
||||
GSSX_C_ACCEPT = 2,
|
||||
GSSX_C_BOTH = 3,
|
||||
};
|
||||
|
||||
struct gssx_option {
|
||||
gssx_buffer option;
|
||||
gssx_buffer value;
|
||||
};
|
||||
|
||||
struct gssx_option_array {
|
||||
u32 count;
|
||||
struct gssx_option *data;
|
||||
};
|
||||
|
||||
struct gssx_status {
|
||||
u64 major_status;
|
||||
gssx_OID mech;
|
||||
u64 minor_status;
|
||||
utf8string major_status_string;
|
||||
utf8string minor_status_string;
|
||||
gssx_buffer server_ctx;
|
||||
struct gssx_option_array options;
|
||||
};
|
||||
|
||||
struct gssx_call_ctx {
|
||||
utf8string locale;
|
||||
gssx_buffer server_ctx;
|
||||
struct gssx_option_array options;
|
||||
};
|
||||
|
||||
struct gssx_name_attr {
|
||||
gssx_buffer attr;
|
||||
gssx_buffer value;
|
||||
struct gssx_option_array extensions;
|
||||
};
|
||||
|
||||
struct gssx_name_attr_array {
|
||||
u32 count;
|
||||
struct gssx_name_attr *data;
|
||||
};
|
||||
|
||||
struct gssx_name {
|
||||
gssx_buffer display_name;
|
||||
};
|
||||
typedef struct gssx_name gssx_name;
|
||||
|
||||
struct gssx_cred_element {
|
||||
gssx_name MN;
|
||||
gssx_OID mech;
|
||||
u32 cred_usage;
|
||||
u64 initiator_time_rec;
|
||||
u64 acceptor_time_rec;
|
||||
struct gssx_option_array options;
|
||||
};
|
||||
|
||||
struct gssx_cred_element_array {
|
||||
u32 count;
|
||||
struct gssx_cred_element *data;
|
||||
};
|
||||
|
||||
struct gssx_cred {
|
||||
gssx_name desired_name;
|
||||
struct gssx_cred_element_array elements;
|
||||
gssx_buffer cred_handle_reference;
|
||||
u32 needs_release;
|
||||
};
|
||||
|
||||
struct gssx_ctx {
|
||||
gssx_buffer exported_context_token;
|
||||
gssx_buffer state;
|
||||
u32 need_release;
|
||||
gssx_OID mech;
|
||||
gssx_name src_name;
|
||||
gssx_name targ_name;
|
||||
u64 lifetime;
|
||||
u64 ctx_flags;
|
||||
u32 locally_initiated;
|
||||
u32 open;
|
||||
struct gssx_option_array options;
|
||||
};
|
||||
|
||||
struct gssx_cb {
|
||||
u64 initiator_addrtype;
|
||||
gssx_buffer initiator_address;
|
||||
u64 acceptor_addrtype;
|
||||
gssx_buffer acceptor_address;
|
||||
gssx_buffer application_data;
|
||||
};
|
||||
|
||||
|
||||
/* This structure is not defined in the protocol.
|
||||
* It is used in the kernel to carry around a big buffer
|
||||
* as a set of pages */
|
||||
struct gssp_in_token {
|
||||
struct page **pages; /* Array of contiguous pages */
|
||||
unsigned int page_base; /* Start of page data */
|
||||
unsigned int page_len; /* Length of page data */
|
||||
};
|
||||
|
||||
struct gssx_arg_accept_sec_context {
|
||||
struct gssx_call_ctx call_ctx;
|
||||
struct gssx_ctx *context_handle;
|
||||
struct gssx_cred *cred_handle;
|
||||
struct gssp_in_token input_token;
|
||||
struct gssx_cb *input_cb;
|
||||
u32 ret_deleg_cred;
|
||||
struct gssx_option_array options;
|
||||
struct page **pages;
|
||||
unsigned int npages;
|
||||
};
|
||||
|
||||
struct gssx_res_accept_sec_context {
|
||||
struct gssx_status status;
|
||||
struct gssx_ctx *context_handle;
|
||||
gssx_buffer *output_token;
|
||||
/* struct gssx_cred *delegated_cred_handle; not used in kernel */
|
||||
struct gssx_option_array options;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define gssx_enc_indicate_mechs NULL
|
||||
#define gssx_dec_indicate_mechs NULL
|
||||
#define gssx_enc_get_call_context NULL
|
||||
#define gssx_dec_get_call_context NULL
|
||||
#define gssx_enc_import_and_canon_name NULL
|
||||
#define gssx_dec_import_and_canon_name NULL
|
||||
#define gssx_enc_export_cred NULL
|
||||
#define gssx_dec_export_cred NULL
|
||||
#define gssx_enc_import_cred NULL
|
||||
#define gssx_dec_import_cred NULL
|
||||
#define gssx_enc_acquire_cred NULL
|
||||
#define gssx_dec_acquire_cred NULL
|
||||
#define gssx_enc_store_cred NULL
|
||||
#define gssx_dec_store_cred NULL
|
||||
#define gssx_enc_init_sec_context NULL
|
||||
#define gssx_dec_init_sec_context NULL
|
||||
void gssx_enc_accept_sec_context(struct rpc_rqst *req,
|
||||
struct xdr_stream *xdr,
|
||||
struct gssx_arg_accept_sec_context *args);
|
||||
int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
|
||||
struct xdr_stream *xdr,
|
||||
struct gssx_res_accept_sec_context *res);
|
||||
#define gssx_enc_release_handle NULL
|
||||
#define gssx_dec_release_handle NULL
|
||||
#define gssx_enc_get_mic NULL
|
||||
#define gssx_dec_get_mic NULL
|
||||
#define gssx_enc_verify NULL
|
||||
#define gssx_dec_verify NULL
|
||||
#define gssx_enc_wrap NULL
|
||||
#define gssx_dec_wrap NULL
|
||||
#define gssx_enc_unwrap NULL
|
||||
#define gssx_dec_unwrap NULL
|
||||
#define gssx_enc_wrap_size_limit NULL
|
||||
#define gssx_dec_wrap_size_limit NULL
|
||||
|
||||
/* non implemented calls are set to 0 size */
|
||||
#define GSSX_ARG_indicate_mechs_sz 0
|
||||
#define GSSX_RES_indicate_mechs_sz 0
|
||||
#define GSSX_ARG_get_call_context_sz 0
|
||||
#define GSSX_RES_get_call_context_sz 0
|
||||
#define GSSX_ARG_import_and_canon_name_sz 0
|
||||
#define GSSX_RES_import_and_canon_name_sz 0
|
||||
#define GSSX_ARG_export_cred_sz 0
|
||||
#define GSSX_RES_export_cred_sz 0
|
||||
#define GSSX_ARG_import_cred_sz 0
|
||||
#define GSSX_RES_import_cred_sz 0
|
||||
#define GSSX_ARG_acquire_cred_sz 0
|
||||
#define GSSX_RES_acquire_cred_sz 0
|
||||
#define GSSX_ARG_store_cred_sz 0
|
||||
#define GSSX_RES_store_cred_sz 0
|
||||
#define GSSX_ARG_init_sec_context_sz 0
|
||||
#define GSSX_RES_init_sec_context_sz 0
|
||||
|
||||
#define GSSX_default_in_call_ctx_sz (4 + 4 + 4 + \
|
||||
8 + sizeof(LUCID_OPTION) + sizeof(LUCID_VALUE) + \
|
||||
8 + sizeof(CREDS_OPTION) + sizeof(CREDS_VALUE))
|
||||
#define GSSX_default_in_ctx_hndl_sz (4 + 4+8 + 4 + 4 + 6*4 + 6*4 + 8 + 8 + \
|
||||
4 + 4 + 4)
|
||||
#define GSSX_default_in_cred_sz 4 /* we send in no cred_handle */
|
||||
#define GSSX_default_in_token_sz 4 /* does *not* include token data */
|
||||
#define GSSX_default_in_cb_sz 4 /* we do not use channel bindings */
|
||||
#define GSSX_ARG_accept_sec_context_sz (GSSX_default_in_call_ctx_sz + \
|
||||
GSSX_default_in_ctx_hndl_sz + \
|
||||
GSSX_default_in_cred_sz + \
|
||||
GSSX_default_in_token_sz + \
|
||||
GSSX_default_in_cb_sz + \
|
||||
4 /* no deleg creds boolean */ + \
|
||||
4) /* empty options */
|
||||
|
||||
/* somewhat arbitrary numbers but large enough (we ignore some of the data
|
||||
* sent down, but it is part of the protocol so we need enough space to take
|
||||
* it in) */
|
||||
#define GSSX_default_status_sz 8 + 24 + 8 + 256 + 256 + 16 + 4
|
||||
#define GSSX_max_output_handle_sz 128
|
||||
#define GSSX_max_oid_sz 16
|
||||
#define GSSX_max_princ_sz 256
|
||||
#define GSSX_default_ctx_sz (GSSX_max_output_handle_sz + \
|
||||
16 + 4 + GSSX_max_oid_sz + \
|
||||
2 * GSSX_max_princ_sz + \
|
||||
8 + 8 + 4 + 4 + 4)
|
||||
#define GSSX_max_output_token_sz 1024
|
||||
/* grouplist not included; we allocate separate pages for that: */
|
||||
#define GSSX_max_creds_sz (4 + 4 + 4 /* + NGROUPS_MAX*4 */)
|
||||
#define GSSX_RES_accept_sec_context_sz (GSSX_default_status_sz + \
|
||||
GSSX_default_ctx_sz + \
|
||||
GSSX_max_output_token_sz + \
|
||||
4 + GSSX_max_creds_sz)
|
||||
|
||||
#define GSSX_ARG_release_handle_sz 0
|
||||
#define GSSX_RES_release_handle_sz 0
|
||||
#define GSSX_ARG_get_mic_sz 0
|
||||
#define GSSX_RES_get_mic_sz 0
|
||||
#define GSSX_ARG_verify_sz 0
|
||||
#define GSSX_RES_verify_sz 0
|
||||
#define GSSX_ARG_wrap_sz 0
|
||||
#define GSSX_RES_wrap_sz 0
|
||||
#define GSSX_ARG_unwrap_sz 0
|
||||
#define GSSX_RES_unwrap_sz 0
|
||||
#define GSSX_ARG_wrap_size_limit_sz 0
|
||||
#define GSSX_RES_wrap_size_limit_sz 0
|
||||
|
||||
|
||||
|
||||
#endif /* _LINUX_GSS_RPC_XDR_H */
|
1860
net/sunrpc/auth_gss/svcauth_gss.c
Normal file
1860
net/sunrpc/auth_gss/svcauth_gss.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue