Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for Linux kernel rpcsec_gss implementation
#
obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
auth_rpcgss-y := auth_gss.o \
gss_mech_switch.o svcauth_gss.o \
gss_rpc_upcall.o gss_rpc_xdr.o trace.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_wrap.o gss_krb5_crypto.o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* linux/net/sunrpc/auth_gss/auth_gss_internal.h
*
* Internal definitions for RPCSEC_GSS client authentication
*
* Copyright (c) 2000 The Regents of the University of Michigan.
* All rights reserved.
*
*/
#include <linux/err.h>
#include <linux/string.h>
#include <linux/sunrpc/xdr.h>
static inline const void *
simple_get_bytes(const void *p, const void *end, void *res, size_t 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 inline const void *
simple_get_netobj_noprof(const void *p, const void *end, struct xdr_netobj *dest)
{
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);
if (len) {
dest->data = kmemdup_noprof(p, len, GFP_KERNEL);
if (unlikely(dest->data == NULL))
return ERR_PTR(-ENOMEM);
} else
dest->data = NULL;
dest->len = len;
return q;
}
#define simple_get_netobj(...) alloc_hooks(simple_get_netobj_noprof(__VA_ARGS__))
@@ -0,0 +1,321 @@
/*
* 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/highmem.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* 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_MAX_SLACK_NEEDED) 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;
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;
}
/**
* gss_krb5_aead_encrypt - Encrypt a wrap token using crypto/krb5
* @kctx: Kerberos context
* @offset: byte offset of the GSS token header in @buf
* @buf: OUT: send buffer
* @pages: plaintext payload pages (page cache data)
*
* The xdr_buf setup mirrors the original per-enctype encrypt
* functions, but the CBC-CTS encryption and HMAC are replaced
* by a single AEAD operation through the crypto/krb5 library.
*
* Return values:
* %GSS_S_COMPLETE: Encryption successful
* %GSS_S_FAILURE: Encryption failed
*/
u32
gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, struct page **pages)
{
const struct krb5_enctype *krb5 = kctx->krb5e;
struct crypto_aead *aead = kctx->initiate ?
kctx->initiator_enc_aead : kctx->acceptor_enc_aead;
unsigned int conflen = krb5->conf_len;
unsigned int cksum_len = krb5->cksum_len;
unsigned int sec_offset, sec_len, data_len;
struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
struct scatterlist *sg_overflow = NULL;
ssize_t ret;
int nsg;
/* Insert space for the confounder */
if (xdr_extend_head(buf, offset + GSS_KRB5_TOK_HDR_LEN, conflen))
return GSS_S_FAILURE;
/* Ensure a tail segment exists */
if (buf->tail[0].iov_base == NULL) {
buf->tail[0].iov_base = buf->head[0].iov_base
+ buf->head[0].iov_len;
buf->tail[0].iov_len = 0;
}
/* Append a copy of the plaintext GSS token header (RFC 4121 Sec 4.2.4) */
memcpy(buf->tail[0].iov_base + buf->tail[0].iov_len,
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;
/* Reserve space for the integrity checksum */
buf->tail[0].iov_len += cksum_len;
buf->len += cksum_len;
/*
* The AEAD operates in-place, but on the client send path the
* plaintext payload lives in page cache pages that must not be
* modified. Copy the payload into the scratch output pages
* first. On the server send path @pages and buf->pages are
* the same array, and no copy is needed.
*
* Both arrays share buf->page_base, so the same index and
* intra-page offset address corresponding data in each.
*/
if (pages != buf->pages) {
unsigned int poff = buf->page_base;
unsigned int plen = buf->page_len;
unsigned int i = poff >> PAGE_SHIFT;
unsigned int off = offset_in_page(poff);
while (plen) {
unsigned int n = min_t(unsigned int, plen,
PAGE_SIZE - off);
memcpy_page(buf->pages[i], off, pages[i], off, n);
plen -= n;
i++;
off = 0;
}
}
/* Build scatterlist covering the secured region */
sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
sec_len = buf->len - sec_offset;
data_len = sec_len - conflen - cksum_len;
nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
sg, ARRAY_SIZE(sg),
&sg_overflow, GFP_NOFS);
if (nsg < 0)
return GSS_S_FAILURE;
ret = crypto_krb5_encrypt(krb5, aead, sg, nsg, sec_len,
conflen, data_len, false);
kfree(sg_overflow);
if (ret < 0)
return GSS_S_FAILURE;
return GSS_S_COMPLETE;
}
/**
* gss_krb5_aead_decrypt - Decrypt a wrap token using crypto/krb5
* @kctx: Kerberos context
* @offset: byte offset of the GSS token header in @buf
* @len: total length of the GSS token
* @buf: ciphertext buffer, decrypted in-place
* @headskip: OUT: confounder length, in octets
* @tailskip: OUT: checksum length, in octets
*
* Return values:
* %GSS_S_COMPLETE: Decryption and integrity verification succeeded
* %GSS_S_BAD_SIG: Integrity checksum did not match
* %GSS_S_DEFECTIVE_TOKEN: Token is malformed or truncated
* %GSS_S_FAILURE: Decryption failed
*/
u32
gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
struct xdr_buf *buf, u32 *headskip, u32 *tailskip)
{
const struct krb5_enctype *krb5 = kctx->krb5e;
struct crypto_aead *aead = kctx->initiate ?
kctx->acceptor_enc_aead : kctx->initiator_enc_aead;
unsigned int sec_offset, sec_len;
size_t data_offset, data_len;
struct scatterlist sg[XDR_BUF_TO_SG_NENTS];
struct scatterlist *sg_overflow = NULL;
int nsg, ret;
/* Secured region starts after the GSS token header */
sec_offset = offset + GSS_KRB5_TOK_HDR_LEN;
if (len < sec_offset)
return GSS_S_DEFECTIVE_TOKEN;
sec_len = len - sec_offset;
nsg = xdr_buf_to_sg_alloc(buf, sec_offset, sec_len,
sg, ARRAY_SIZE(sg),
&sg_overflow, GFP_NOFS);
if (nsg < 0)
return GSS_S_FAILURE;
data_offset = 0;
data_len = sec_len;
ret = crypto_krb5_decrypt(krb5, aead, sg, nsg,
&data_offset, &data_len);
kfree(sg_overflow);
if (ret < 0)
return gss_krb5_errno_to_status(ret);
*headskip = data_offset;
*tailskip = sec_len - data_offset - data_len;
return GSS_S_COMPLETE;
}
/**
* gss_krb5_mic_build_sg - Build scatterlist for MIC token operations
* @body: xdr_buf containing the message body
* @cksum: pointer to checksum area in the token buffer
* @cksum_len: length of checksum area
* @hdr: pointer to GSS token header
* @sg_head: caller-provided scatterlist array; if more than
* XDR_BUF_TO_SG_NENTS entries are needed, an overflow
* scatterlist is allocated and chained automatically
* @sg_overflow: OUT: overflow scatterlist, caller must kfree
*
* Per RFC 4121 Section 4.2.4, MIC token checksums cover the
* message body followed by the token header. The checksum
* output or received checksum occupies the first scatterlist
* entry. This layout cannot be constructed by
* xdr_buf_to_sg_alloc() because the checksum area and the GSS
* header lie outside the xdr_buf.
*
* Returns the number of scatterlist entries on success, or a
* negative errno on failure.
*/
int gss_krb5_mic_build_sg(const struct xdr_buf *body,
void *cksum, unsigned int cksum_len,
void *hdr,
struct scatterlist *sg_head,
struct scatterlist **sg_overflow)
{
struct scatterlist *entry;
int body_max, body_nsg, nsg;
*sg_overflow = NULL;
body_max = 2;
if (body->page_len)
body_max += DIV_ROUND_UP(body->page_len +
offset_in_page(body->page_base),
PAGE_SIZE);
nsg = 1 + body_max + 1;
if (nsg <= XDR_BUF_TO_SG_NENTS) {
sg_init_table(sg_head, nsg);
} else {
unsigned int overflow_nents =
nsg - XDR_BUF_TO_SG_NENTS + 1;
*sg_overflow = kmalloc_array(overflow_nents,
sizeof(**sg_overflow),
GFP_NOFS);
if (!*sg_overflow)
return -ENOMEM;
sg_init_table(sg_head, XDR_BUF_TO_SG_NENTS);
sg_init_table(*sg_overflow, overflow_nents);
sg_chain(sg_head, XDR_BUF_TO_SG_NENTS, *sg_overflow);
}
sg_set_buf(&sg_head[0], cksum, cksum_len);
body_nsg = xdr_buf_to_sg(body, 0, body->len,
sg_next(&sg_head[0]), body_max);
if (body_nsg < 0)
goto out_err;
/*
* xdr_buf_to_sg marks the last body entry as end-of-list;
* clear it so the trailing header entry is reachable.
*/
if (body_nsg > 0) {
entry = sg_last(sg_next(&sg_head[0]), body_nsg);
sg_unmark_end(entry);
entry = sg_next(entry);
} else {
entry = sg_next(&sg_head[0]);
}
sg_set_buf(entry, hdr, GSS_KRB5_TOK_HDR_LEN);
sg_mark_end(entry);
return 1 + body_nsg + 1;
out_err:
kfree(*sg_overflow);
*sg_overflow = NULL;
return body_nsg;
}
@@ -0,0 +1,72 @@
/* SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause */
/*
* SunRPC GSS Kerberos 5 mechanism internal definitions
*
* Copyright (c) 2022 Oracle and/or its affiliates.
*/
#ifndef _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H
#define _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H
#include <crypto/krb5.h>
struct krb5_ctx;
/* krb5_ctx flags definitions */
#define KRB5_CTX_FLAG_INITIATOR 0x00000001
#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
struct krb5_ctx {
int initiate; /* 1 = initiating, 0 = accepting */
u32 enctype;
u32 flags;
const struct krb5_enctype *krb5e; /* crypto/krb5 enctype */
struct crypto_aead *initiator_enc_aead;
struct crypto_aead *acceptor_enc_aead;
struct crypto_shash *initiator_sign_shash;
struct crypto_shash *acceptor_sign_shash;
u8 Ksess[GSS_KRB5_MAX_KEYLEN]; /* session key */
atomic64_t seq_send64;
time64_t endtime;
struct xdr_netobj mech_used;
};
/*
* GSS Kerberos 5 mechanism Per-Message calls.
*/
u32 gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
struct xdr_netobj *token);
u32 gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
struct xdr_netobj *read_token);
u32 gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
struct xdr_buf *buf, struct page **pages);
u32 gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
struct xdr_buf *buf, unsigned int *slack,
unsigned int *align);
/*
* Implementation internal functions
*/
int xdr_extend_head(struct xdr_buf *buf, unsigned int base,
unsigned int shiftlen);
u32 gss_krb5_errno_to_status(int err);
int gss_krb5_mic_build_sg(const struct xdr_buf *body,
void *cksum, unsigned int cksum_len,
void *hdr,
struct scatterlist *sg_head,
struct scatterlist **sg_overflow);
u32 gss_krb5_aead_encrypt(struct krb5_ctx *kctx, u32 offset,
struct xdr_buf *buf, struct page **pages);
u32 gss_krb5_aead_decrypt(struct krb5_ctx *kctx, u32 offset, u32 len,
struct xdr_buf *buf, u32 *headskip, u32 *tailskip);
#endif /* _NET_SUNRPC_AUTH_GSS_KRB5_INTERNAL_H */
@@ -0,0 +1,402 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* 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>
*/
#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 "auth_gss_internal.h"
#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static struct gss_api_mech gss_kerberos_mech;
/*
* Candidate enctypes in order of most preferred to least.
* Each is probed against crypto/krb5 at module init; only
* enctypes that crypto/krb5 supports are advertised.
*/
static const u32 gss_krb5_enctypes[] = {
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
KRB5_ENCTYPE_CAMELLIA256_CTS_CMAC,
KRB5_ENCTYPE_CAMELLIA128_CTS_CMAC,
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
};
static char gss_krb5_enctype_priority_list[64];
static void gss_krb5_prepare_enctype_priority_list(void)
{
size_t total, i;
char buf[16];
char *sep;
int n;
sep = "";
gss_krb5_enctype_priority_list[0] = '\0';
for (total = 0, i = 0; i < ARRAY_SIZE(gss_krb5_enctypes); i++) {
if (!crypto_krb5_find_enctype(gss_krb5_enctypes[i]))
continue;
n = sprintf(buf, "%s%u", sep, gss_krb5_enctypes[i]);
if (n < 0)
break;
if (total + n >= sizeof(gss_krb5_enctype_priority_list))
break;
strcat(gss_krb5_enctype_priority_list, buf);
sep = ",";
total += n;
}
}
static int
gss_krb5_import_ctx_v2(struct krb5_ctx *ctx, gfp_t gfp_mask)
{
struct krb5_buffer TK = {
.len = ctx->krb5e->key_len,
.data = ctx->Ksess,
};
int ret;
ctx->initiator_enc_aead =
crypto_krb5_prepare_encryption(ctx->krb5e, &TK,
KG_USAGE_INITIATOR_SEAL,
gfp_mask);
if (IS_ERR(ctx->initiator_enc_aead)) {
ret = PTR_ERR(ctx->initiator_enc_aead);
goto out_free;
}
ctx->acceptor_enc_aead =
crypto_krb5_prepare_encryption(ctx->krb5e, &TK,
KG_USAGE_ACCEPTOR_SEAL,
gfp_mask);
if (IS_ERR(ctx->acceptor_enc_aead)) {
ret = PTR_ERR(ctx->acceptor_enc_aead);
goto out_free;
}
ctx->initiator_sign_shash =
crypto_krb5_prepare_checksum(ctx->krb5e, &TK,
KG_USAGE_INITIATOR_SIGN,
gfp_mask);
if (IS_ERR(ctx->initiator_sign_shash)) {
ret = PTR_ERR(ctx->initiator_sign_shash);
goto out_free;
}
ctx->acceptor_sign_shash =
crypto_krb5_prepare_checksum(ctx->krb5e, &TK,
KG_USAGE_ACCEPTOR_SIGN,
gfp_mask);
if (IS_ERR(ctx->acceptor_sign_shash)) {
ret = PTR_ERR(ctx->acceptor_sign_shash);
goto out_free;
}
return 0;
out_free:
crypto_free_shash(ctx->acceptor_sign_shash);
crypto_free_shash(ctx->initiator_sign_shash);
crypto_free_aead(ctx->acceptor_enc_aead);
crypto_free_aead(ctx->initiator_enc_aead);
return ret;
}
static int
gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx,
gfp_t gfp_mask)
{
u64 seq_send64;
int keylen;
u32 time32;
int ret;
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, &time32, sizeof(time32));
if (IS_ERR(p))
goto out_err;
/* unsigned 32-bit time overflows in year 2106 */
ctx->endtime = (time64_t)time32;
p = simple_get_bytes(p, end, &seq_send64, sizeof(seq_send64));
if (IS_ERR(p))
goto out_err;
atomic64_set(&ctx->seq_send64, seq_send64);
p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype));
if (IS_ERR(p))
goto out_err;
ctx->krb5e = crypto_krb5_find_enctype(ctx->enctype);
if (!ctx->krb5e) {
dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n",
ctx->enctype);
p = ERR_PTR(-EINVAL);
goto out_err;
}
keylen = ctx->krb5e->key_len;
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;
ret = gss_krb5_import_ctx_v2(ctx, gfp_mask);
if (ret) {
p = ERR_PTR(ret);
goto out_free;
}
return 0;
out_free:
kfree(ctx->mech_used.data);
out_err:
return PTR_ERR(p);
}
static int
gss_krb5_import_sec_context(const void *p, size_t len, struct gss_ctx *ctx_id,
time64_t *endtime, gfp_t gfp_mask)
{
const void *end = (const void *)((const char *)p + len);
struct krb5_ctx *ctx;
int ret;
ctx = kzalloc_obj(*ctx, gfp_mask);
if (ctx == NULL)
return -ENOMEM;
ret = gss_import_v2_context(p, end, ctx, gfp_mask);
memzero_explicit(&ctx->Ksess, sizeof(ctx->Ksess));
if (ret) {
kfree(ctx);
return ret;
}
ctx_id->internal_ctx_id = ctx;
if (endtime)
*endtime = ctx->endtime;
return 0;
}
static void
gss_krb5_delete_sec_context(void *internal_ctx)
{
struct krb5_ctx *kctx = internal_ctx;
crypto_free_shash(kctx->acceptor_sign_shash);
crypto_free_shash(kctx->initiator_sign_shash);
crypto_free_aead(kctx->acceptor_enc_aead);
crypto_free_aead(kctx->initiator_enc_aead);
kfree(kctx->mech_used.data);
kfree(kctx);
}
/**
* gss_krb5_errno_to_status - Map a negative errno to a GSS major status
* @err: negative errno value, or zero
*
* Returns:
* %GSS_S_COMPLETE if @err is zero
* %GSS_S_BAD_SIG if @err is -EBADMSG (integrity check failure)
* %GSS_S_DEFECTIVE_TOKEN if @err is -EPROTO (malformed token)
* %GSS_S_FAILURE for all other negative values
*/
u32 gss_krb5_errno_to_status(int err)
{
switch (err) {
case 0:
return GSS_S_COMPLETE;
case -EBADMSG:
return GSS_S_BAD_SIG;
case -EPROTO:
return GSS_S_DEFECTIVE_TOKEN;
default:
return GSS_S_FAILURE;
}
}
/**
* gss_krb5_get_mic - get_mic for the Kerberos GSS mechanism
* @gctx: GSS context
* @text: plaintext to checksum
* @token: buffer into which to write the computed checksum
*
* Return values:
* %GSS_S_COMPLETE - success, and @token is filled in
* %GSS_S_FAILURE - checksum could not be generated
* %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
*/
static u32 gss_krb5_get_mic(struct gss_ctx *gctx, struct xdr_buf *text,
struct xdr_netobj *token)
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
return gss_krb5_get_mic_v2(kctx, text, token);
}
/**
* gss_krb5_verify_mic - verify_mic for the Kerberos GSS mechanism
* @gctx: GSS context
* @message_buffer: plaintext to check
* @read_token: received checksum to check
*
* Return values:
* %GSS_S_COMPLETE - computed and received checksums match
* %GSS_S_DEFECTIVE_TOKEN - received checksum is not valid
* %GSS_S_BAD_SIG - computed and received checksums do not match
* %GSS_S_FAILURE - received checksum could not be checked
* %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
*/
static u32 gss_krb5_verify_mic(struct gss_ctx *gctx,
struct xdr_buf *message_buffer,
struct xdr_netobj *read_token)
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
return gss_krb5_verify_mic_v2(kctx, message_buffer, read_token);
}
/**
* gss_krb5_wrap - gss_wrap for the Kerberos GSS mechanism
* @gctx: initialized GSS context
* @offset: byte offset in @buf to start writing the cipher text
* @buf: OUT: send buffer
* @pages: plaintext to wrap
*
* Return values:
* %GSS_S_COMPLETE - success, @buf has been updated
* %GSS_S_FAILURE - @buf could not be wrapped
* %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
*/
static u32 gss_krb5_wrap(struct gss_ctx *gctx, int offset,
struct xdr_buf *buf, struct page **pages)
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
return gss_krb5_wrap_v2(kctx, offset, buf, pages);
}
/**
* gss_krb5_unwrap - gss_unwrap for the Kerberos GSS mechanism
* @gctx: initialized GSS context
* @offset: starting byte offset into @buf
* @len: size of ciphertext to unwrap
* @buf: ciphertext to unwrap
*
* Return values:
* %GSS_S_COMPLETE - success, @buf has been updated
* %GSS_S_DEFECTIVE_TOKEN - received blob is not valid
* %GSS_S_BAD_SIG - computed and received checksums do not match
* %GSS_S_FAILURE - @buf could not be unwrapped
* %GSS_S_CONTEXT_EXPIRED - Kerberos context is no longer valid
*/
static u32 gss_krb5_unwrap(struct gss_ctx *gctx, int offset,
int len, struct xdr_buf *buf)
{
struct krb5_ctx *kctx = gctx->internal_ctx_id;
return gss_krb5_unwrap_v2(kctx, offset, len, buf,
&gctx->slack, &gctx->align);
}
static const struct gss_api_ops gss_kerberos_ops = {
.gss_import_sec_context = gss_krb5_import_sec_context,
.gss_get_mic = gss_krb5_get_mic,
.gss_verify_mic = gss_krb5_verify_mic,
.gss_wrap = gss_krb5_wrap,
.gss_unwrap = gss_krb5_unwrap,
.gss_delete_sec_context = gss_krb5_delete_sec_context,
};
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",
.datatouch = true,
},
[2] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5P,
.qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_PRIVACY,
.name = "krb5p",
.datatouch = true,
},
};
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 = gss_krb5_enctype_priority_list,
};
static int __init init_kerberos_module(void)
{
int status;
gss_krb5_prepare_enctype_priority_list();
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_DESCRIPTION("Sun RPC Kerberos 5 module");
MODULE_LICENSE("GPL");
module_init(init_kerberos_module);
module_exit(cleanup_kerberos_module);
@@ -0,0 +1,152 @@
/*
* 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/atomic.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static void *
setup_token_v2(struct krb5_ctx *ctx, struct xdr_netobj *token)
{
u16 *ptr;
void *krb5_hdr;
u8 *p, flags = 0x00;
if (!ctx->initiate)
flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
if (ctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* Per rfc 4121, sec 4.2.6.1, there is no header,
* just start the token.
*/
krb5_hdr = (u16 *)token->data;
ptr = krb5_hdr;
*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->krb5e->cksum_len;
return krb5_hdr;
}
u32
gss_krb5_get_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *text,
struct xdr_netobj *token)
{
const struct krb5_enctype *krb5 = ctx->krb5e;
struct crypto_shash *shash = ctx->initiate ?
ctx->initiator_sign_shash : ctx->acceptor_sign_shash;
unsigned int cksum_len = krb5->cksum_len;
struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
struct scatterlist *sg_overflow;
__be64 seq_send_be64;
void *krb5_hdr;
time64_t now;
ssize_t ret;
int nsg;
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 */
seq_send_be64 = cpu_to_be64(atomic64_fetch_inc(&ctx->seq_send64));
memcpy(krb5_hdr + 8, (char *) &seq_send_be64, 8);
/*
* The checksum is written directly into the token buffer.
* This is safe: crypto_krb5_get_mic uses shash (software
* hash), so the scatterlist is never DMA-mapped.
*/
nsg = gss_krb5_mic_build_sg(text,
krb5_hdr + GSS_KRB5_TOK_HDR_LEN,
cksum_len, krb5_hdr,
sg_head, &sg_overflow);
if (nsg < 0)
return GSS_S_FAILURE;
ret = crypto_krb5_get_mic(krb5, shash, NULL, sg_head, nsg,
cksum_len + text->len +
GSS_KRB5_TOK_HDR_LEN,
cksum_len,
text->len + GSS_KRB5_TOK_HDR_LEN);
kfree(sg_overflow);
if (ret < 0)
return GSS_S_FAILURE;
now = ktime_get_real_seconds();
return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}
@@ -0,0 +1,136 @@
/*
* 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/scatterlist.h>
#include <linux/slab.h>
#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
u32
gss_krb5_verify_mic_v2(struct krb5_ctx *ctx, struct xdr_buf *message_buffer,
struct xdr_netobj *read_token)
{
const struct krb5_enctype *krb5 = ctx->krb5e;
struct crypto_shash *shash = ctx->initiate ?
ctx->acceptor_sign_shash : ctx->initiator_sign_shash;
unsigned int cksum_len = krb5->cksum_len;
struct scatterlist sg_head[XDR_BUF_TO_SG_NENTS];
struct scatterlist *sg_overflow;
size_t mic_offset, mic_len;
u8 *ptr = read_token->data;
__be16 be16_ptr;
time64_t now;
u8 flags;
int nsg, i;
int ret;
dprintk("RPC: %s\n", __func__);
memcpy(&be16_ptr, (char *) ptr, 2);
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;
nsg = gss_krb5_mic_build_sg(message_buffer,
ptr + GSS_KRB5_TOK_HDR_LEN,
cksum_len, ptr,
sg_head, &sg_overflow);
if (nsg < 0)
return GSS_S_FAILURE;
mic_offset = 0;
mic_len = cksum_len + message_buffer->len + GSS_KRB5_TOK_HDR_LEN;
ret = crypto_krb5_verify_mic(krb5, shash, NULL, sg_head, nsg,
&mic_offset, &mic_len);
kfree(sg_overflow);
if (ret)
return gss_krb5_errno_to_status(ret);
/* it got through unscathed. Make sure the context is unexpired */
now = ktime_get_real_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;
}
@@ -0,0 +1,236 @@
/*
* 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/pagemap.h>
#include "gss_krb5_internal.h"
#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* 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);
}
u32
gss_krb5_wrap_v2(struct krb5_ctx *kctx, int offset,
struct xdr_buf *buf, struct page **pages)
{
u8 *ptr;
time64_t now;
u8 flags = 0x00;
__be16 *be16ptr;
__be64 *be64ptr;
u32 err;
dprintk("RPC: %s\n", __func__);
/* 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 = buf->head[0].iov_base + offset;
*ptr++ = (unsigned char) ((KG2_TOK_WRAP>>8) & 0xff);
*ptr++ = (unsigned char) (KG2_TOK_WRAP & 0xff);
if (!kctx->initiate)
flags |= KG2_TOKEN_FLAG_SENTBYACCEPTOR;
if (kctx->flags & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
flags |= KG2_TOKEN_FLAG_ACCEPTORSUBKEY;
/* We always do confidentiality in wrap tokens */
flags |= KG2_TOKEN_FLAG_SEALED;
*ptr++ = flags;
*ptr++ = 0xff;
be16ptr = (__be16 *)ptr;
*be16ptr++ = 0;
/* "inner" token header always uses 0 for RRC */
*be16ptr++ = 0;
be64ptr = (__be64 *)be16ptr;
*be64ptr = cpu_to_be64(atomic64_fetch_inc(&kctx->seq_send64));
err = gss_krb5_aead_encrypt(kctx, offset, buf, pages);
if (err)
return err;
now = ktime_get_real_seconds();
return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
}
u32
gss_krb5_unwrap_v2(struct krb5_ctx *kctx, int offset, int len,
struct xdr_buf *buf, unsigned int *slack,
unsigned int *align)
{
time64_t 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__);
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 = gss_krb5_aead_decrypt(kctx, offset, len, buf,
&headskip, &tailskip);
if (err)
return err;
/*
* Retrieve the decrypted gss token header and verify
* it against the original
*/
err = read_bytes_from_xdr_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 = ktime_get_real_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, 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 = 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);
*align = XDR_QUADLEN(GSS_KRB5_TOK_HDR_LEN + headskip);
*slack = *align + XDR_QUADLEN(ec + GSS_KRB5_TOK_HDR_LEN + tailskip);
return GSS_S_COMPLETE;
}
@@ -0,0 +1,447 @@
// SPDX-License-Identifier: BSD-3-Clause
/*
* 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>
*/
#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/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>
#include <trace/events/rpcgss.h>
#if IS_ENABLED(CONFIG_SUNRPC_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];
if (pf->domain)
auth_domain_put(pf->domain);
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 auth_domain *dom;
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;
dom = svcauth_gss_register_pseudoflavor(
pf->pseudoflavor, pf->auth_domain_name);
if (IS_ERR(dom)) {
status = PTR_ERR(dom);
goto out;
}
pf->domain = dom;
}
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(&registered_mechs_lock);
list_add_rcu(&gm->gm_list, &registered_mechs);
spin_unlock(&registered_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(&registered_mechs_lock);
list_del_rcu(&gm->gm_list);
spin_unlock(&registered_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;
rcu_read_lock();
list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
if (0 == strcmp(name, pos->gm_name)) {
if (try_module_get(pos->gm_owner))
gm = pos;
break;
}
}
rcu_read_unlock();
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;
request_module("rpc-auth-gss-%s", buf);
rcu_read_lock();
list_for_each_entry_rcu(pos, &registered_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;
}
}
}
rcu_read_unlock();
if (!gm)
trace_rpcgss_oid_to_mech(buf);
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;
rcu_read_lock();
list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
if (!mech_supports_pseudoflavor(pos, pseudoflavor))
continue;
if (try_module_get(pos->gm_owner))
gm = pos;
break;
}
rcu_read_unlock();
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_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);
bool
gss_pseudoflavor_to_datatouch(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].datatouch;
}
return false;
}
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,
time64_t *endtime,
gfp_t gfp_mask)
{
if (!(*ctx_id = kzalloc_obj(**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,
int len,
struct xdr_buf *buf)
{
return ctx_id->mech_type->gm_ops
->gss_unwrap(ctx_id, offset, len, 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;
}
@@ -0,0 +1,403 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* linux/net/sunrpc/gss_rpc_upcall.c
*
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
*/
#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 = gssx_enc_##name, \
.p_decode = gssx_dec_##name, \
.p_arglen = GSSX_ARG_##name##_sz, \
.p_replen = GSSX_RES_##name##_sz, \
.p_statidx = GSSX_##proc, \
.p_name = #proc, \
}
static const 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_CONNECTED |
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)
refcount_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)
{
unsigned int i;
for (i = 0; i < arg->npages && arg->pages[i]; i++)
__free_page(arg->pages[i]);
kfree(arg->pages);
}
static int gssp_alloc_receive_pages(struct gssx_arg_accept_sec_context *arg)
{
unsigned int i;
arg->npages = DIV_ROUND_UP(NGROUPS_MAX * 4, PAGE_SIZE);
arg->pages = kzalloc_objs(struct page *, arg->npages);
if (!arg->pages)
return -ENOMEM;
for (i = 0; i < arg->npages; i++) {
arg->pages[i] = alloc_page(GFP_KERNEL);
if (!arg->pages[i]) {
gssp_free_receive_pages(arg);
return -ENOMEM;
}
}
return 0;
}
static char *gssp_stringify(struct xdr_netobj *netobj)
{
return kmemdup_nul(netobj->data, netobj->len, GFP_KERNEL);
}
static void gssp_hostbased_service(char **principal)
{
char *c;
if (!*principal)
return;
/* terminate and remove realm part */
c = strchr(*principal, '@');
if (c) {
*c = '\0';
/* change service-hostname delimiter */
c = strchr(*principal, '/');
if (c)
*c = '@';
}
if (!c) {
/* not a service principal */
kfree(*principal);
*principal = NULL;
}
}
/*
* 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,
.targ_name.display_name.len = GSSX_max_princ_sz,
.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 };
struct xdr_netobj target_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;
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);
kfree(rctxh.mech.data);
}
client_name = rctxh.src_name.display_name;
target_name = rctxh.targ_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) {
if (client_name.data) {
data->creds.cr_raw_principal =
gssp_stringify(&client_name);
data->creds.cr_principal =
gssp_stringify(&client_name);
gssp_hostbased_service(&data->creds.cr_principal);
}
if (target_name.data) {
data->creds.cr_targ_princ =
gssp_stringify(&target_name);
gssp_hostbased_service(&data->creds.cr_targ_princ);
}
}
kfree(client_name.data);
kfree(target_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 unsigned int gssp_version1_counts[ARRAY_SIZE(gssp_procedures)];
static const struct rpc_version gssp_version1 = {
.number = GSSPROXY_VERS_1,
.nrprocs = ARRAY_SIZE(gssp_procedures),
.procs = gssp_procedures,
.counts = gssp_version1_counts,
};
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,
};
@@ -0,0 +1,36 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* linux/net/sunrpc/gss_rpc_upcall.h
*
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
*/
#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 */
@@ -0,0 +1,895 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* GSS Proxy upcall module
*
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
*/
#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,
const 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,
const 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,
const 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;
creds->cr_group_info->gid[i] = kgid;
}
groups_sort(creds->cr_group_info);
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_obj(struct gssx_option);
if (!oa->data)
return -ENOMEM;
creds = kzalloc_obj(struct svc_cred);
if (!creds) {
err = -ENOMEM;
goto free_oa;
}
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)) {
err = -ENOSPC;
goto free_creds;
}
length = be32_to_cpup(p);
p = xdr_inline_decode(xdr, length);
if (unlikely(p == NULL)) {
err = -ENOSPC;
goto free_creds;
}
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)
goto free_creds;
oa->data[0].value.len = 1; /* presence */
} else {
/* consume uninteresting buffer */
err = gssx_dec_buffer(xdr, &dummy);
if (err)
goto free_creds;
}
}
return 0;
free_creds:
kfree(creds);
free_oa:
kfree(oa->data);
oa->data = NULL;
return err;
}
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)) {
err = -ENOSPC;
goto out_free_mech;
}
p = xdr_decode_hyper(p, &status->minor_status);
/* status->major_status_string */
err = gssx_dec_buffer(xdr, &status->major_status_string);
if (err)
goto out_free_mech;
/* status->minor_status_string */
err = gssx_dec_buffer(xdr, &status->minor_status_string);
if (err)
goto out_free_major_status_string;
/* status->server_ctx */
err = gssx_dec_buffer(xdr, &status->server_ctx);
if (err)
goto out_free_minor_status_string;
/* we assume we have no options for now, so simply consume them */
/* status->options */
err = dummy_dec_opt_array(xdr, &status->options);
if (err)
goto out_free_server_ctx;
return 0;
out_free_server_ctx:
kfree(status->server_ctx.data);
status->server_ctx.data = NULL;
out_free_minor_status_string:
kfree(status->minor_status_string.data);
status->minor_status_string.data = NULL;
out_free_major_status_string:
kfree(status->major_status_string.data);
status->major_status_string.data = NULL;
out_free_mech:
kfree(status->mech.data);
status->mech.data = NULL;
return err;
}
static int gssx_enc_call_ctx(struct xdr_stream *xdr,
const 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)
goto out_free_display_name;
/* name->exported_name */
err = gssx_dec_buffer(xdr, &dummy_netobj);
if (err)
goto out_free_display_name;
/* name->exported_composite_name */
err = gssx_dec_buffer(xdr, &dummy_netobj);
if (err)
goto out_free_display_name;
/* 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)
goto out_free_display_name;
/* we assume we have no options for now, so simply consume them */
/* name->extensions */
err = dummy_dec_opt_array(xdr, &dummy_option_array);
if (err)
goto out_free_display_name;
return 0;
out_free_display_name:
kfree(name->display_name.data);
name->display_name.data = NULL;
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)
goto out_free_exported_context_token;
/* ctx->need_release */
err = gssx_dec_bool(xdr, &ctx->need_release);
if (err)
goto out_free_state;
/* ctx->mech */
err = gssx_dec_buffer(xdr, &ctx->mech);
if (err)
goto out_free_state;
/* ctx->src_name */
err = gssx_dec_name(xdr, &ctx->src_name);
if (err)
goto out_free_mech;
/* ctx->targ_name */
err = gssx_dec_name(xdr, &ctx->targ_name);
if (err)
goto out_free_src_name;
/* ctx->lifetime */
p = xdr_inline_decode(xdr, 8+8);
if (unlikely(p == NULL)) {
err = -ENOSPC;
goto out_free_targ_name;
}
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)
goto out_free_targ_name;
/* ctx->open */
err = gssx_dec_bool(xdr, &ctx->open);
if (err)
goto out_free_targ_name;
/* we assume we have no options for now, so simply consume them */
/* ctx->options */
err = dummy_dec_opt_array(xdr, &ctx->options);
if (err)
goto out_free_targ_name;
return 0;
out_free_targ_name:
kfree(ctx->targ_name.display_name.data);
ctx->targ_name.display_name.data = NULL;
out_free_src_name:
kfree(ctx->src_name.display_name.data);
ctx->src_name.display_name.data = NULL;
out_free_mech:
kfree(ctx->mech.data);
ctx->mech.data = NULL;
out_free_state:
kfree(ctx->state.data);
ctx->state.data = NULL;
out_free_exported_context_token:
kfree(ctx->exported_context_token.data);
ctx->exported_context_token.data = NULL;
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,
const void *data)
{
const struct gssx_arg_accept_sec_context *arg = data;
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,
void *data)
{
struct gssx_res_accept_sec_context *res = data;
u32 value_follows;
int err;
struct folio *scratch;
scratch = folio_alloc(GFP_KERNEL, 0);
if (!scratch)
return -ENOMEM;
xdr_set_scratch_folio(xdr, scratch);
/* res->status */
err = gssx_dec_status(xdr, &res->status);
if (err)
goto out_free;
/* res->context_handle */
err = gssx_dec_bool(xdr, &value_follows);
if (err)
goto out_free;
if (value_follows) {
err = gssx_dec_ctx(xdr, res->context_handle);
if (err)
goto out_free;
} else {
res->context_handle = NULL;
}
/* res->output_token */
err = gssx_dec_bool(xdr, &value_follows);
if (err)
goto out_free;
if (value_follows) {
err = gssx_dec_buffer(xdr, res->output_token);
if (err)
goto out_free;
} else {
res->output_token = NULL;
}
/* res->delegated_cred_handle */
err = gssx_dec_bool(xdr, &value_follows);
if (err)
goto out_free;
if (value_follows) {
/* we do not support upcall servers sending this data. */
err = -EINVAL;
goto out_free;
}
/* res->options */
err = gssx_dec_option_array(xdr, &res->options);
out_free:
folio_put(scratch);
return err;
}
@@ -0,0 +1,252 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* GSS Proxy upcall module
*
* Copyright (C) 2012 Simo Sorce <simo@redhat.com>
*/
#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>
#if IS_ENABLED(CONFIG_SUNRPC_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,
const void *data);
int gssx_dec_accept_sec_context(struct rpc_rqst *rqstp,
struct xdr_stream *xdr,
void *data);
#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 */
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2018, 2019 Oracle. All rights reserved.
*/
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svc_xprt.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/gss_err.h>
#define CREATE_TRACE_POINTS
#include <trace/events/rpcgss.h>