(2006-08-06) rescue-bootcd

This commit is contained in:
2006-08-06 00:00:00 +02:00
parent 2f796b816a
commit decb062d20
21091 changed files with 7076462 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
#
# Makefile for Linux kernel SUN RPC
#
obj-$(CONFIG_SUNRPC) += sunrpc.o
obj-$(CONFIG_SUNRPC_GSS) += auth_gss/
sunrpc-y := clnt.o xprt.o sched.o \
auth.o auth_null.o auth_unix.o \
svc.o svcsock.o svcauth.o svcauth_unix.o \
pmap_clnt.o timer.o xdr.o \
sunrpc_syms.o cache.o rpc_pipe.o
sunrpc-$(CONFIG_PROC_FS) += stats.o
sunrpc-$(CONFIG_SYSCTL) += sysctl.o

View File

@@ -0,0 +1,422 @@
/*
* linux/net/sunrpc/auth.c
*
* Generic RPC client authentication API.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/sunrpc/clnt.h>
#include <linux/spinlock.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static struct rpc_authops * auth_flavors[RPC_AUTH_MAXFLAVOR] = {
&authnull_ops, /* AUTH_NULL */
&authunix_ops, /* AUTH_UNIX */
NULL, /* others can be loadable modules */
};
u32
pseudoflavor_to_flavor(u32 flavor) {
if (flavor >= RPC_AUTH_MAXFLAVOR)
return RPC_AUTH_GSS;
return flavor;
}
int
rpcauth_register(struct rpc_authops *ops)
{
rpc_authflavor_t flavor;
if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
return -EINVAL;
if (auth_flavors[flavor] != NULL)
return -EPERM; /* what else? */
auth_flavors[flavor] = ops;
return 0;
}
int
rpcauth_unregister(struct rpc_authops *ops)
{
rpc_authflavor_t flavor;
if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
return -EINVAL;
if (auth_flavors[flavor] != ops)
return -EPERM; /* what else? */
auth_flavors[flavor] = NULL;
return 0;
}
struct rpc_auth *
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
{
struct rpc_auth *auth;
struct rpc_authops *ops;
u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
if (flavor >= RPC_AUTH_MAXFLAVOR || !(ops = auth_flavors[flavor]))
return NULL;
if (!try_module_get(ops->owner))
return NULL;
auth = ops->create(clnt, pseudoflavor);
if (!auth)
return NULL;
atomic_set(&auth->au_count, 1);
if (clnt->cl_auth)
rpcauth_destroy(clnt->cl_auth);
clnt->cl_auth = auth;
return auth;
}
void
rpcauth_destroy(struct rpc_auth *auth)
{
if (!atomic_dec_and_test(&auth->au_count))
return;
auth->au_ops->destroy(auth);
module_put(auth->au_ops->owner);
kfree(auth);
}
static spinlock_t rpc_credcache_lock = SPIN_LOCK_UNLOCKED;
/*
* Initialize RPC credential cache
*/
void
rpcauth_init_credcache(struct rpc_auth *auth)
{
int i;
for (i = 0; i < RPC_CREDCACHE_NR; i++)
INIT_LIST_HEAD(&auth->au_credcache[i]);
auth->au_nextgc = jiffies + (auth->au_expire >> 1);
}
/*
* Destroy an unreferenced credential
*/
static inline void
rpcauth_crdestroy(struct rpc_cred *cred)
{
#ifdef RPC_DEBUG
BUG_ON(cred->cr_magic != RPCAUTH_CRED_MAGIC ||
atomic_read(&cred->cr_count) ||
!list_empty(&cred->cr_hash));
cred->cr_magic = 0;
#endif
cred->cr_ops->crdestroy(cred);
}
/*
* Destroy a list of credentials
*/
static inline
void rpcauth_destroy_credlist(struct list_head *head)
{
struct rpc_cred *cred;
while (!list_empty(head)) {
cred = list_entry(head->next, struct rpc_cred, cr_hash);
list_del_init(&cred->cr_hash);
rpcauth_crdestroy(cred);
}
}
/*
* Clear the RPC credential cache, and delete those credentials
* that are not referenced.
*/
void
rpcauth_free_credcache(struct rpc_auth *auth)
{
LIST_HEAD(free);
struct list_head *pos, *next;
struct rpc_cred *cred;
int i;
spin_lock(&rpc_credcache_lock);
for (i = 0; i < RPC_CREDCACHE_NR; i++) {
list_for_each_safe(pos, next, &auth->au_credcache[i]) {
cred = list_entry(pos, struct rpc_cred, cr_hash);
cred->cr_auth = NULL;
list_del_init(&cred->cr_hash);
if (atomic_read(&cred->cr_count) == 0)
list_add(&cred->cr_hash, &free);
}
}
spin_unlock(&rpc_credcache_lock);
rpcauth_destroy_credlist(&free);
}
static inline int
rpcauth_prune_expired(struct rpc_cred *cred, struct list_head *free)
{
if (atomic_read(&cred->cr_count) != 0)
return 0;
if (time_before(jiffies, cred->cr_expire))
return 0;
cred->cr_auth = NULL;
list_del(&cred->cr_hash);
list_add(&cred->cr_hash, free);
return 1;
}
/*
* Remove stale credentials. Avoid sleeping inside the loop.
*/
static void
rpcauth_gc_credcache(struct rpc_auth *auth, struct list_head *free)
{
struct list_head *pos, *next;
struct rpc_cred *cred;
int i;
dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
for (i = 0; i < RPC_CREDCACHE_NR; i++) {
list_for_each_safe(pos, next, &auth->au_credcache[i]) {
cred = list_entry(pos, struct rpc_cred, cr_hash);
rpcauth_prune_expired(cred, free);
}
}
auth->au_nextgc = jiffies + auth->au_expire;
}
/*
* Look up a process' credentials in the authentication cache
*/
struct rpc_cred *
rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
int taskflags)
{
LIST_HEAD(free);
struct list_head *pos, *next;
struct rpc_cred *new = NULL,
*cred = NULL;
int nr = 0;
if (!(taskflags & RPC_TASK_ROOTCREDS))
nr = acred->uid & RPC_CREDCACHE_MASK;
retry:
spin_lock(&rpc_credcache_lock);
if (time_before(auth->au_nextgc, jiffies))
rpcauth_gc_credcache(auth, &free);
list_for_each_safe(pos, next, &auth->au_credcache[nr]) {
struct rpc_cred *entry;
entry = list_entry(pos, struct rpc_cred, cr_hash);
if (entry->cr_flags & RPCAUTH_CRED_DEAD)
continue;
if (rpcauth_prune_expired(entry, &free))
continue;
if (entry->cr_ops->crmatch(acred, entry, taskflags)) {
list_del(&entry->cr_hash);
cred = entry;
break;
}
}
if (new) {
if (cred)
list_add(&new->cr_hash, &free);
else
cred = new;
}
if (cred) {
list_add(&cred->cr_hash, &auth->au_credcache[nr]);
cred->cr_auth = auth;
get_rpccred(cred);
}
spin_unlock(&rpc_credcache_lock);
rpcauth_destroy_credlist(&free);
if (!cred) {
new = auth->au_ops->crcreate(auth, acred, taskflags);
if (new) {
#ifdef RPC_DEBUG
new->cr_magic = RPCAUTH_CRED_MAGIC;
#endif
goto retry;
}
}
return (struct rpc_cred *) cred;
}
struct rpc_cred *
rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
{
struct auth_cred acred;
struct rpc_cred *ret;
get_group_info(current->group_info);
acred.uid = current->fsuid;
acred.gid = current->fsgid;
acred.group_info = current->group_info;
dprintk("RPC: looking up %s cred\n",
auth->au_ops->au_name);
ret = rpcauth_lookup_credcache(auth, &acred, taskflags);
put_group_info(current->group_info);
return ret;
}
struct rpc_cred *
rpcauth_bindcred(struct rpc_task *task)
{
struct rpc_auth *auth = task->tk_auth;
struct auth_cred acred;
struct rpc_cred *ret;
get_group_info(current->group_info);
acred.uid = current->fsuid;
acred.gid = current->fsgid;
acred.group_info = current->group_info;
dprintk("RPC: %4d looking up %s cred\n",
task->tk_pid, task->tk_auth->au_ops->au_name);
task->tk_msg.rpc_cred = rpcauth_lookup_credcache(auth, &acred, task->tk_flags);
if (task->tk_msg.rpc_cred == 0)
task->tk_status = -ENOMEM;
ret = task->tk_msg.rpc_cred;
put_group_info(current->group_info);
return ret;
}
void
rpcauth_holdcred(struct rpc_task *task)
{
dprintk("RPC: %4d holding %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
if (task->tk_msg.rpc_cred)
get_rpccred(task->tk_msg.rpc_cred);
}
void
put_rpccred(struct rpc_cred *cred)
{
if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
return;
if ((cred->cr_flags & RPCAUTH_CRED_DEAD) && !list_empty(&cred->cr_hash))
list_del_init(&cred->cr_hash);
if (list_empty(&cred->cr_hash)) {
spin_unlock(&rpc_credcache_lock);
rpcauth_crdestroy(cred);
return;
}
cred->cr_expire = jiffies + cred->cr_auth->au_expire;
spin_unlock(&rpc_credcache_lock);
}
void
rpcauth_unbindcred(struct rpc_task *task)
{
struct rpc_auth *auth = task->tk_auth;
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %4d releasing %s cred %p\n",
task->tk_pid, auth->au_ops->au_name, cred);
put_rpccred(cred);
task->tk_msg.rpc_cred = NULL;
}
u32 *
rpcauth_marshcred(struct rpc_task *task, u32 *p)
{
struct rpc_auth *auth = task->tk_auth;
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %4d marshaling %s cred %p\n",
task->tk_pid, auth->au_ops->au_name, cred);
return cred->cr_ops->crmarshal(task, p,
task->tk_flags & RPC_CALL_REALUID);
}
u32 *
rpcauth_checkverf(struct rpc_task *task, u32 *p)
{
struct rpc_auth *auth = task->tk_auth;
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %4d validating %s cred %p\n",
task->tk_pid, auth->au_ops->au_name, cred);
return cred->cr_ops->crvalidate(task, p);
}
int
rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
u32 *data, void *obj)
{
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %4d using %s cred %p to wrap rpc data\n",
task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
if (cred->cr_ops->crwrap_req)
return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
/* By default, we encode the arguments normally. */
return encode(rqstp, data, obj);
}
int
rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
u32 *data, void *obj)
{
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n",
task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
if (cred->cr_ops->crunwrap_resp)
return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
data, obj);
/* By default, we decode the arguments normally. */
return decode(rqstp, data, obj);
}
int
rpcauth_refreshcred(struct rpc_task *task)
{
struct rpc_auth *auth = task->tk_auth;
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %4d refreshing %s cred %p\n",
task->tk_pid, auth->au_ops->au_name, cred);
task->tk_status = cred->cr_ops->crrefresh(task);
return task->tk_status;
}
void
rpcauth_invalcred(struct rpc_task *task)
{
dprintk("RPC: %4d invalidating %s cred %p\n",
task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
spin_lock(&rpc_credcache_lock);
if (task->tk_msg.rpc_cred)
task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
spin_unlock(&rpc_credcache_lock);
}
int
rpcauth_uptodatecred(struct rpc_task *task)
{
return !(task->tk_msg.rpc_cred) ||
(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
}
int
rpcauth_deadcred(struct rpc_task *task)
{
return !(task->tk_msg.rpc_cred) ||
(task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_DEAD);
}

View File

@@ -0,0 +1,18 @@
#
# Makefile for Linux kernel rpcsec_gss implementation
#
obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
auth_rpcgss-objs := auth_gss.o gss_generic_token.o \
gss_mech_switch.o svcauth_gss.o gss_krb5_crypto.o
obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
gss_krb5_seqnum.o
obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
rpcsec_gss_spkm3-objs := gss_spkm3_mech.o gss_spkm3_seal.o gss_spkm3_unseal.o \
gss_spkm3_token.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,270 @@
/*
* 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/slab.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 += 4 + (int) mech->len; /* NEED overflow check */
return(1 + der_length_size(body_size) + body_size);
}
EXPORT_SYMBOL(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, 4 + mech->len + body_size);
*(*buf)++ = 0x06;
*(*buf)++ = (unsigned char) mech->len;
TWRITE_STR(*buf, mech->data, ((int) mech->len));
}
EXPORT_SYMBOL(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(g_verify_token_header);
/* Given a buffer containing a token, returns a copy of the mech oid in
* the parameter mech. */
u32
g_get_mech_oid(struct xdr_netobj *mech, struct xdr_netobj * in_buf)
{
unsigned char *buf = in_buf->data;
int len = in_buf->len;
int ret=0;
int seqsize;
if ((len-=1) < 0)
return(G_BAD_TOK_HEADER);
if (*buf++ != 0x60)
return(G_BAD_TOK_HEADER);
if ((seqsize = der_read_length(&buf, &len)) < 0)
return(G_BAD_TOK_HEADER);
if ((len-=1) < 0)
return(G_BAD_TOK_HEADER);
if (*buf++ != 0x06)
return(G_BAD_TOK_HEADER);
if ((len-=1) < 0)
return(G_BAD_TOK_HEADER);
mech->len = *buf++;
if ((len-=mech->len) < 0)
return(G_BAD_TOK_HEADER);
if (!(mech->data = kmalloc(mech->len, GFP_KERNEL)))
return(G_BUFFER_ALLOC);
memcpy(mech->data, buf, mech->len);
return ret;
}

View File

@@ -0,0 +1,209 @@
/*
* linux/net/sunrpc/gss_krb5_crypto.c
*
* Copyright (c) 2000 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/types.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/scatterlist.h>
#include <linux/crypto.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/sunrpc/gss_krb5.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
u32
krb5_encrypt(
struct crypto_tfm *tfm,
void * iv,
void * in,
void * out,
int length)
{
u32 ret = -EINVAL;
struct scatterlist sg[1];
u8 local_iv[16] = {0};
dprintk("RPC: krb5_encrypt: input data:\n");
print_hexl((u32 *)in, length, 0);
if (length % crypto_tfm_alg_blocksize(tfm) != 0)
goto out;
if (crypto_tfm_alg_ivsize(tfm) > 16) {
dprintk("RPC: gss_k5encrypt: tfm iv size to large %d\n",
crypto_tfm_alg_ivsize(tfm));
goto out;
}
if (iv)
memcpy(local_iv, iv, crypto_tfm_alg_ivsize(tfm));
memcpy(out, in, length);
sg[0].page = virt_to_page(out);
sg[0].offset = offset_in_page(out);
sg[0].length = length;
ret = crypto_cipher_encrypt_iv(tfm, sg, sg, length, local_iv);
dprintk("RPC: krb5_encrypt: output data:\n");
print_hexl((u32 *)out, length, 0);
out:
dprintk("RPC: krb5_encrypt returns %d\n",ret);
return(ret);
}
EXPORT_SYMBOL(krb5_encrypt);
u32
krb5_decrypt(
struct crypto_tfm *tfm,
void * iv,
void * in,
void * out,
int length)
{
u32 ret = -EINVAL;
struct scatterlist sg[1];
u8 local_iv[16] = {0};
dprintk("RPC: krb5_decrypt: input data:\n");
print_hexl((u32 *)in, length, 0);
if (length % crypto_tfm_alg_blocksize(tfm) != 0)
goto out;
if (crypto_tfm_alg_ivsize(tfm) > 16) {
dprintk("RPC: gss_k5decrypt: tfm iv size to large %d\n",
crypto_tfm_alg_ivsize(tfm));
goto out;
}
if (iv)
memcpy(local_iv,iv, crypto_tfm_alg_ivsize(tfm));
memcpy(out, in, length);
sg[0].page = virt_to_page(out);
sg[0].offset = offset_in_page(out);
sg[0].length = length;
ret = crypto_cipher_decrypt_iv(tfm, sg, sg, length, local_iv);
dprintk("RPC: krb5_decrypt: output_data:\n");
print_hexl((u32 *)out, length, 0);
out:
dprintk("RPC: gss_k5decrypt returns %d\n",ret);
return(ret);
}
EXPORT_SYMBOL(krb5_decrypt);
void
buf_to_sg(struct scatterlist *sg, char *ptr, int len) {
sg->page = virt_to_page(ptr);
sg->offset = offset_in_page(ptr);
sg->length = len;
}
/* checksum the plaintext data and hdrlen bytes of the token header */
s32
make_checksum(s32 cksumtype, char *header, int hdrlen, struct xdr_buf *body,
struct xdr_netobj *cksum)
{
char *cksumname;
struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */
struct scatterlist sg[1];
u32 code = GSS_S_FAILURE;
int len, thislen, offset;
int i;
switch (cksumtype) {
case CKSUMTYPE_RSA_MD5:
cksumname = "md5";
break;
default:
dprintk("RPC: krb5_make_checksum:"
" unsupported checksum %d", cksumtype);
goto out;
}
if (!(tfm = crypto_alloc_tfm(cksumname, 0)))
goto out;
cksum->len = crypto_tfm_alg_digestsize(tfm);
if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL)
goto out;
crypto_digest_init(tfm);
buf_to_sg(sg, header, hdrlen);
crypto_digest_update(tfm, sg, 1);
if (body->head[0].iov_len) {
buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len);
crypto_digest_update(tfm, sg, 1);
}
len = body->page_len;
if (len != 0) {
offset = body->page_base & (PAGE_CACHE_SIZE - 1);
i = body->page_base >> PAGE_CACHE_SHIFT;
thislen = PAGE_CACHE_SIZE - offset;
do {
if (thislen > len)
thislen = len;
sg->page = body->pages[i];
sg->offset = offset;
sg->length = thislen;
kmap(sg->page); /* XXX kmap_atomic? */
crypto_digest_update(tfm, sg, 1);
kunmap(sg->page);
len -= thislen;
i++;
offset = 0;
thislen = PAGE_CACHE_SIZE;
} while(len != 0);
}
if (body->tail[0].iov_len) {
buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len);
crypto_digest_update(tfm, sg, 1);
}
crypto_digest_final(tfm, cksum->data);
code = 0;
out:
if (tfm)
crypto_free_tfm(tfm);
return code;
}
EXPORT_SYMBOL(make_checksum);

View File

@@ -0,0 +1,263 @@
/*
* linux/net/sunrpc/gss_krb5_mech.c
*
* Copyright (c) 2001 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/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sunrpc/auth.h>
#include <linux/in.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
struct xdr_netobj gss_mech_krb5_oid =
{9, "\052\206\110\206\367\022\001\002\002"};
static inline int
get_bytes(char **ptr, const char *end, void *res, int len)
{
char *p, *q;
p = *ptr;
q = p + len;
if (q > end || q < p)
return -1;
memcpy(res, p, len);
*ptr = q;
return 0;
}
static inline int
get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
{
char *p, *q;
p = *ptr;
if (get_bytes(&p, end, &res->len, sizeof(res->len)))
return -1;
q = p + res->len;
if (q > end || q < p)
return -1;
if (!(res->data = kmalloc(res->len, GFP_KERNEL)))
return -1;
memcpy(res->data, p, res->len);
*ptr = q;
return 0;
}
static inline int
get_key(char **p, char *end, struct crypto_tfm **res)
{
struct xdr_netobj key;
int alg, alg_mode;
char *alg_name;
if (get_bytes(p, end, &alg, sizeof(alg)))
goto out_err;
if ((get_netobj(p, end, &key)))
goto out_err;
switch (alg) {
case ENCTYPE_DES_CBC_RAW:
alg_name = "des";
alg_mode = CRYPTO_TFM_MODE_CBC;
break;
default:
dprintk("RPC: get_key: unsupported algorithm %d\n", alg);
goto out_err_free_key;
}
if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
goto out_err_free_key;
if (crypto_cipher_setkey(*res, key.data, key.len))
goto out_err_free_tfm;
kfree(key.data);
return 0;
out_err_free_tfm:
crypto_free_tfm(*res);
out_err_free_key:
kfree(key.data);
out_err:
return -1;
}
static u32
gss_import_sec_context_kerberos(struct xdr_netobj *inbuf,
struct gss_ctx *ctx_id)
{
char *p = inbuf->data;
char *end = inbuf->data + inbuf->len;
struct krb5_ctx *ctx;
if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)))
goto out_err;
memset(ctx, 0, sizeof(*ctx));
if (get_bytes(&p, end, &ctx->initiate, sizeof(ctx->initiate)))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->seed_init, sizeof(ctx->seed_init)))
goto out_err_free_ctx;
if (get_bytes(&p, end, ctx->seed, sizeof(ctx->seed)))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->signalg, sizeof(ctx->signalg)))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->sealalg, sizeof(ctx->sealalg)))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->endtime, sizeof(ctx->endtime)))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->seq_send, sizeof(ctx->seq_send)))
goto out_err_free_ctx;
if (get_netobj(&p, end, &ctx->mech_used))
goto out_err_free_ctx;
if (get_key(&p, end, &ctx->enc))
goto out_err_free_mech;
if (get_key(&p, end, &ctx->seq))
goto out_err_free_key1;
if (p != end)
goto out_err_free_key2;
ctx_id->internal_ctx_id = ctx;
dprintk("RPC: Succesfully imported new context.\n");
return 0;
out_err_free_key2:
crypto_free_tfm(ctx->seq);
out_err_free_key1:
crypto_free_tfm(ctx->enc);
out_err_free_mech:
kfree(ctx->mech_used.data);
out_err_free_ctx:
kfree(ctx);
out_err:
return GSS_S_FAILURE;
}
static void
gss_delete_sec_context_kerberos(void *internal_ctx) {
struct krb5_ctx *kctx = internal_ctx;
if (kctx->seq)
crypto_free_tfm(kctx->seq);
if (kctx->enc)
crypto_free_tfm(kctx->enc);
if (kctx->mech_used.data)
kfree(kctx->mech_used.data);
kfree(kctx);
}
static u32
gss_verify_mic_kerberos(struct gss_ctx *ctx,
struct xdr_buf *message,
struct xdr_netobj *mic_token,
u32 *qstate) {
u32 maj_stat = 0;
int qop_state;
struct krb5_ctx *kctx = ctx->internal_ctx_id;
maj_stat = krb5_read_token(kctx, mic_token, message, &qop_state,
KG_TOK_MIC_MSG);
if (!maj_stat && qop_state)
*qstate = qop_state;
dprintk("RPC: gss_verify_mic_kerberos returning %d\n", maj_stat);
return maj_stat;
}
static u32
gss_get_mic_kerberos(struct gss_ctx *ctx,
u32 qop,
struct xdr_buf *message,
struct xdr_netobj *mic_token) {
u32 err = 0;
struct krb5_ctx *kctx = ctx->internal_ctx_id;
err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG);
dprintk("RPC: gss_get_mic_kerberos returning %d\n",err);
return err;
}
static 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_delete_sec_context = gss_delete_sec_context_kerberos,
};
static struct pf_desc gss_kerberos_pfs[] = {
[0] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5,
.service = RPC_GSS_SVC_NONE,
.name = "krb5",
},
[1] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5I,
.service = RPC_GSS_SVC_INTEGRITY,
.name = "krb5i",
},
};
static struct gss_api_mech gss_kerberos_mech = {
.gm_name = "krb5",
.gm_owner = THIS_MODULE,
.gm_ops = &gss_kerberos_ops,
.gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs),
.gm_pfs = gss_kerberos_pfs,
};
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);

View File

@@ -0,0 +1,176 @@
/*
* linux/net/sunrpc/gss_krb5_seal.c
*
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/k5seal.c
*
* Copyright (c) 2000 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/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/random.h>
#include <asm/scatterlist.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static inline int
gss_krb5_padding(int blocksize, int length) {
/* Most of the code is block-size independent but in practice we
* use only 8: */
BUG_ON(blocksize != 8);
return 8 - (length & 7);
}
u32
krb5_make_token(struct krb5_ctx *ctx, int qop_req,
struct xdr_buf *text, struct xdr_netobj *token,
int toktype)
{
s32 checksum_type;
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
int blocksize = 0, tmsglen;
unsigned char *ptr, *krb5_hdr, *msg_start;
s32 now;
dprintk("RPC: gss_krb5_seal\n");
now = get_seconds();
if (qop_req != 0)
goto out_err;
switch (ctx->signalg) {
case SGN_ALG_DES_MAC_MD5:
checksum_type = CKSUMTYPE_RSA_MD5;
break;
default:
dprintk("RPC: gss_krb5_seal: ctx->signalg %d not"
" supported\n", ctx->signalg);
goto out_err;
}
if (ctx->sealalg != SEAL_ALG_NONE && ctx->sealalg != SEAL_ALG_DES) {
dprintk("RPC: gss_krb5_seal: ctx->sealalg %d not supported\n",
ctx->sealalg);
goto out_err;
}
if (toktype == KG_TOK_WRAP_MSG) {
blocksize = crypto_tfm_alg_blocksize(ctx->enc);
tmsglen = blocksize + text->len
+ gss_krb5_padding(blocksize, blocksize + text->len);
} else {
tmsglen = 0;
}
token->len = g_token_size(&ctx->mech_used, 22 + tmsglen);
ptr = token->data;
g_make_token_header(&ctx->mech_used, 22 + tmsglen, &ptr);
*ptr++ = (unsigned char) ((toktype>>8)&0xff);
*ptr++ = (unsigned char) (toktype&0xff);
/* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */
krb5_hdr = ptr - 2;
msg_start = krb5_hdr + 24;
*(u16 *)(krb5_hdr + 2) = htons(ctx->signalg);
memset(krb5_hdr + 4, 0xff, 4);
if (toktype == KG_TOK_WRAP_MSG)
*(u16 *)(krb5_hdr + 4) = htons(ctx->sealalg);
if (toktype == KG_TOK_WRAP_MSG) {
/* XXX removing support for now */
goto out_err;
} else { /* Sign only. */
if (make_checksum(checksum_type, krb5_hdr, 8, text,
&md5cksum))
goto out_err;
}
switch (ctx->signalg) {
case SGN_ALG_DES_MAC_MD5:
if (krb5_encrypt(ctx->seq, NULL, md5cksum.data,
md5cksum.data, md5cksum.len))
goto out_err;
memcpy(krb5_hdr + 16,
md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH,
KRB5_CKSUM_LENGTH);
dprintk("RPC: make_seal_token: cksum data: \n");
print_hexl((u32 *) (krb5_hdr + 16), KRB5_CKSUM_LENGTH, 0);
break;
default:
BUG();
}
kfree(md5cksum.data);
if ((krb5_make_seq_num(ctx->seq, ctx->initiate ? 0 : 0xff,
ctx->seq_send, krb5_hdr + 16, krb5_hdr + 8)))
goto out_err;
ctx->seq_send++;
return ((ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE);
out_err:
if (md5cksum.data) kfree(md5cksum.data);
return GSS_S_FAILURE;
}

View File

@@ -0,0 +1,88 @@
/*
* 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/slab.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
s32
krb5_make_seq_num(struct crypto_tfm *key,
int direction,
s32 seqnum,
unsigned char *cksum, unsigned char *buf)
{
unsigned char plain[8];
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);
}
s32
krb5_get_seq_num(struct crypto_tfm *key,
unsigned char *cksum,
unsigned char *buf,
int *direction, s32 * seqnum)
{
s32 code;
unsigned char plain[8];
dprintk("RPC: krb5_get_seq_num:\n");
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);
}

View File

@@ -0,0 +1,202 @@
/*
* linux/net/sunrpc/gss_krb5_unseal.c
*
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/krb5/k5unseal.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.
*/
/*
* 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/slab.h>
#include <linux/jiffies.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/* message_buffer is an input if toktype is MIC and an output if it is WRAP:
* If toktype is MIC: read_token is a mic token, and message_buffer is the
* data that the mic was supposedly taken over.
* If toktype is WRAP: read_token is a wrap token, and message_buffer is used
* to return the decrypted data.
*/
/* XXX will need to change prototype and/or just split into a separate function
* when we add privacy (because read_token will be in pages too). */
u32
krb5_read_token(struct krb5_ctx *ctx,
struct xdr_netobj *read_token,
struct xdr_buf *message_buffer,
int *qop_state, int toktype)
{
int signalg;
int sealalg;
s32 checksum_type;
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
s32 now;
int direction;
s32 seqnum;
unsigned char *ptr = (unsigned char *)read_token->data;
int bodysize;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
dprintk("RPC: krb5_read_token\n");
if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr,
read_token->len))
goto out;
if ((*ptr++ != ((toktype>>8)&0xff)) || (*ptr++ != (toktype&0xff)))
goto out;
/* XXX sanity-check bodysize?? */
if (toktype == KG_TOK_WRAP_MSG) {
/* XXX gone */
goto out;
}
/* get the sign and seal algorithms */
signalg = ptr[0] + (ptr[1] << 8);
sealalg = ptr[2] + (ptr[3] << 8);
/* Sanity checks */
if ((ptr[4] != 0xff) || (ptr[5] != 0xff))
goto out;
if (((toktype != KG_TOK_WRAP_MSG) && (sealalg != 0xffff)) ||
((toktype == KG_TOK_WRAP_MSG) && (sealalg == 0xffff)))
goto out;
/* in the current spec, there is only one valid seal algorithm per
key type, so a simple comparison is ok */
if ((toktype == KG_TOK_WRAP_MSG) && !(sealalg == ctx->sealalg))
goto out;
/* there are several mappings of seal algorithms to sign algorithms,
but few enough that we can try them all. */
if ((ctx->sealalg == SEAL_ALG_NONE && signalg > 1) ||
(ctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) ||
(ctx->sealalg == SEAL_ALG_DES3KD &&
signalg != SGN_ALG_HMAC_SHA1_DES3_KD))
goto out;
/* compute the checksum of the message */
/* initialize the the cksum */
switch (signalg) {
case SGN_ALG_DES_MAC_MD5:
checksum_type = CKSUMTYPE_RSA_MD5;
break;
default:
ret = GSS_S_DEFECTIVE_TOKEN;
goto out;
}
switch (signalg) {
case SGN_ALG_DES_MAC_MD5:
ret = make_checksum(checksum_type, ptr - 2, 8,
message_buffer, &md5cksum);
if (ret)
goto out;
ret = krb5_encrypt(ctx->seq, NULL, md5cksum.data,
md5cksum.data, 16);
if (ret)
goto out;
if (memcmp(md5cksum.data + 8, ptr + 14, 8)) {
ret = GSS_S_BAD_SIG;
goto out;
}
break;
default:
ret = GSS_S_DEFECTIVE_TOKEN;
goto out;
}
/* it got through unscathed. Make sure the context is unexpired */
if (qop_state)
*qop_state = GSS_C_QOP_DEFAULT;
now = get_seconds();
ret = GSS_S_CONTEXT_EXPIRED;
if (now > ctx->endtime)
goto out;
/* do sequencing checks */
ret = GSS_S_BAD_SIG;
if ((ret = krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction,
&seqnum)))
goto out;
if ((ctx->initiate && direction != 0xff) ||
(!ctx->initiate && direction != 0))
goto out;
ret = GSS_S_COMPLETE;
out:
if (md5cksum.data) kfree(md5cksum.data);
return ret;
}

View File

@@ -0,0 +1,303 @@
/*
* 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/socket.h>
#include <linux/module.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 spinlock_t registered_mechs_lock = SPIN_LOCK_UNLOCKED;
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->auth_domain_name)
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;
}
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(&gm->gm_list, &registered_mechs);
spin_unlock(&registered_mechs_lock);
dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
return 0;
}
EXPORT_SYMBOL(gss_mech_register);
void
gss_mech_unregister(struct gss_api_mech *gm)
{
spin_lock(&registered_mechs_lock);
list_del(&gm->gm_list);
spin_unlock(&registered_mechs_lock);
dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
gss_mech_free(gm);
}
EXPORT_SYMBOL(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);
struct gss_api_mech *
gss_mech_get_by_name(char *name)
{
struct gss_api_mech *pos, *gm = NULL;
spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) {
if (0 == strcmp(name, pos->gm_name)) {
if (!try_module_get(pos->gm_owner))
continue;
gm = pos;
break;
}
}
spin_unlock(&registered_mechs_lock);
return gm;
}
EXPORT_SYMBOL(gss_mech_get_by_name);
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;
}
struct gss_api_mech *
gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
{
struct gss_api_mech *pos, *gm = NULL;
spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) {
if (!try_module_get(pos->gm_owner))
continue;
if (!mech_supports_pseudoflavor(pos, pseudoflavor)) {
module_put(pos->gm_owner);
continue;
}
gm = pos;
break;
}
spin_unlock(&registered_mechs_lock);
return gm;
}
EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
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;
}
EXPORT_SYMBOL(gss_service_to_auth_domain_name);
void
gss_mech_put(struct gss_api_mech * 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. */
u32
gss_import_sec_context(struct xdr_netobj *input_token,
struct gss_api_mech *mech,
struct gss_ctx **ctx_id)
{
if (!(*ctx_id = kmalloc(sizeof(**ctx_id), GFP_KERNEL)))
return GSS_S_FAILURE;
memset(*ctx_id, 0, sizeof(**ctx_id));
(*ctx_id)->mech_type = gss_mech_get(mech);
return mech->gm_ops
->gss_import_sec_context(input_token, *ctx_id);
}
/* gss_get_mic: compute a mic over message and return mic_token. */
u32
gss_get_mic(struct gss_ctx *context_handle,
u32 qop,
struct xdr_buf *message,
struct xdr_netobj *mic_token)
{
return context_handle->mech_type->gm_ops
->gss_get_mic(context_handle,
qop,
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,
u32 *qstate)
{
return context_handle->mech_type->gm_ops
->gss_verify_mic(context_handle,
message,
mic_token,
qstate);
}
/* 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 != 0)
(*context_handle)->mech_type->gm_ops
->gss_delete_sec_context((*context_handle)
->internal_ctx_id);
if ((*context_handle)->mech_type)
gss_mech_put((*context_handle)->mech_type);
kfree(*context_handle);
*context_handle=NULL;
return GSS_S_COMPLETE;
}

View File

@@ -0,0 +1,237 @@
/*
* linux/net/sunrpc/gss_union.c
*
* Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic code
*
* Copyright (c) 2001 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/slab.h>
#include <linux/socket.h>
#include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/auth_gss.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static LIST_HEAD(registered_triples);
static spinlock_t registered_triples_lock = SPIN_LOCK_UNLOCKED;
/* The following must be called with spinlock held: */
static struct sup_sec_triple *
do_lookup_triple_by_pseudoflavor(u32 pseudoflavor)
{
struct sup_sec_triple *pos, *triple = NULL;
list_for_each_entry(pos, &registered_triples, triples) {
if (pos->pseudoflavor == pseudoflavor) {
triple = pos;
break;
}
}
return triple;
}
/* XXX Need to think about reference counting of triples and of mechs.
* Currently we do no reference counting of triples, and I think that's
* probably OK given the reference counting on mechs, but there's probably
* a better way to do all this. */
int
gss_register_triple(u32 pseudoflavor, struct gss_api_mech *mech,
u32 qop, u32 service)
{
struct sup_sec_triple *triple;
if (!(triple = kmalloc(sizeof(*triple), GFP_KERNEL))) {
printk("Alloc failed in gss_register_triple");
goto err;
}
triple->pseudoflavor = pseudoflavor;
triple->mech = gss_mech_get_by_OID(&mech->gm_oid);
triple->qop = qop;
triple->service = service;
spin_lock(&registered_triples_lock);
if (do_lookup_triple_by_pseudoflavor(pseudoflavor)) {
printk(KERN_WARNING "RPC: Registered pseudoflavor %d again\n",
pseudoflavor);
goto err_unlock;
}
list_add(&triple->triples, &registered_triples);
spin_unlock(&registered_triples_lock);
dprintk("RPC: registered pseudoflavor %d\n", pseudoflavor);
return 0;
err_unlock:
kfree(triple);
spin_unlock(&registered_triples_lock);
err:
return -1;
}
int
gss_unregister_triple(u32 pseudoflavor)
{
struct sup_sec_triple *triple;
spin_lock(&registered_triples_lock);
if (!(triple = do_lookup_triple_by_pseudoflavor(pseudoflavor))) {
spin_unlock(&registered_triples_lock);
printk("Can't unregister unregistered pseudoflavor %d\n",
pseudoflavor);
return -1;
}
list_del(&triple->triples);
spin_unlock(&registered_triples_lock);
gss_mech_put(triple->mech);
kfree(triple);
return 0;
}
void
print_sec_triple(struct xdr_netobj *oid,u32 qop,u32 service)
{
dprintk("RPC: print_sec_triple:\n");
dprintk(" oid_len %d\n oid :\n",oid->len);
print_hexl((u32 *)oid->data,oid->len,0);
dprintk(" qop %d\n",qop);
dprintk(" service %d\n",service);
}
/* Function: gss_get_cmp_triples
*
* Description: search sec_triples for a matching security triple
* return pseudoflavor if match, else 0
* (Note that 0 is a valid pseudoflavor, but not for any gss pseudoflavor
* (0 means auth_null), so this shouldn't cause confusion.)
*/
u32
gss_cmp_triples(u32 oid_len, char *oid_data, u32 qop, u32 service)
{
struct sup_sec_triple *triple;
u32 pseudoflavor = 0;
struct xdr_netobj oid;
oid.len = oid_len;
oid.data = oid_data;
dprintk("RPC: gss_cmp_triples\n");
print_sec_triple(&oid,qop,service);
spin_lock(&registered_triples_lock);
list_for_each_entry(triple, &registered_triples, triples) {
if((g_OID_equal(&oid, &triple->mech->gm_oid))
&& (qop == triple->qop)
&& (service == triple->service)) {
pseudoflavor = triple->pseudoflavor;
break;
}
}
spin_unlock(&registered_triples_lock);
dprintk("RPC: gss_cmp_triples return %d\n", pseudoflavor);
return pseudoflavor;
}
u32
gss_get_pseudoflavor(struct gss_ctx *ctx, u32 qop, u32 service)
{
return gss_cmp_triples(ctx->mech_type->gm_oid.len,
ctx->mech_type->gm_oid.data,
qop, service);
}
/* Returns nonzero iff the given pseudoflavor is in the supported list.
* (Note that without incrementing a reference count or anything, this
* doesn't give any guarantees.) */
int
gss_pseudoflavor_supported(u32 pseudoflavor)
{
struct sup_sec_triple *triple;
spin_lock(&registered_triples_lock);
triple = do_lookup_triple_by_pseudoflavor(pseudoflavor);
spin_unlock(&registered_triples_lock);
return (triple ? 1 : 0);
}
u32
gss_pseudoflavor_to_service(u32 pseudoflavor)
{
struct sup_sec_triple *triple;
spin_lock(&registered_triples_lock);
triple = do_lookup_triple_by_pseudoflavor(pseudoflavor);
spin_unlock(&registered_triples_lock);
if (!triple) {
dprintk("RPC: gss_pseudoflavor_to_service called with unsupported pseudoflavor %d\n",
pseudoflavor);
return 0;
}
return triple->service;
}
struct gss_api_mech *
gss_pseudoflavor_to_mech(u32 pseudoflavor) {
struct sup_sec_triple *triple;
struct gss_api_mech *mech = NULL;
spin_lock(&registered_triples_lock);
triple = do_lookup_triple_by_pseudoflavor(pseudoflavor);
spin_unlock(&registered_triples_lock);
if (triple)
mech = gss_mech_get(triple->mech);
else
dprintk("RPC: gss_pseudoflavor_to_mech called with unsupported pseudoflavor %d\n",
pseudoflavor);
return mech;
}
int
gss_pseudoflavor_to_mechOID(u32 pseudoflavor, struct xdr_netobj * oid)
{
struct gss_api_mech *mech;
mech = gss_pseudoflavor_to_mech(pseudoflavor);
if (!mech) {
dprintk("RPC: gss_pseudoflavor_to_mechOID called with unsupported pseudoflavor %d\n",
pseudoflavor);
return -1;
}
oid->len = mech->gm_oid.len;
if (!(oid->data = kmalloc(oid->len, GFP_KERNEL)))
return -1;
memcpy(oid->data, mech->gm_oid.data, oid->len);
gss_mech_put(mech);
return 0;
}

View File

@@ -0,0 +1,296 @@
/*
* linux/net/sunrpc/gss_spkm3_mech.c
*
* Copyright (c) 2003 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/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/sunrpc/auth.h>
#include <linux/in.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/sunrpc/xdr.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
struct xdr_netobj gss_mech_spkm3_oid =
{7, "\053\006\001\005\005\001\003"};
static inline int
get_bytes(char **ptr, const char *end, void *res, int len)
{
char *p, *q;
p = *ptr;
q = p + len;
if (q > end || q < p)
return -1;
memcpy(res, p, len);
*ptr = q;
return 0;
}
static inline int
get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
{
char *p, *q;
p = *ptr;
if (get_bytes(&p, end, &res->len, sizeof(res->len)))
return -1;
q = p + res->len;
if(res->len == 0)
goto out_nocopy;
if (q > end || q < p)
return -1;
if (!(res->data = kmalloc(res->len, GFP_KERNEL)))
return -1;
memcpy(res->data, p, res->len);
out_nocopy:
*ptr = q;
return 0;
}
static inline int
get_key(char **p, char *end, struct crypto_tfm **res, int *resalg)
{
struct xdr_netobj key = {
.len = 0,
.data = NULL,
};
int alg_mode,setkey = 0;
char *alg_name;
if (get_bytes(p, end, resalg, sizeof(int)))
goto out_err;
if ((get_netobj(p, end, &key)))
goto out_err;
switch (*resalg) {
case NID_des_cbc:
alg_name = "des";
alg_mode = CRYPTO_TFM_MODE_CBC;
setkey = 1;
break;
case NID_md5:
if (key.len == 0) {
dprintk("RPC: SPKM3 get_key: NID_md5 zero Key length\n");
}
alg_name = "md5";
alg_mode = 0;
setkey = 0;
break;
case NID_cast5_cbc:
dprintk("RPC: SPKM3 get_key: case cast5_cbc, UNSUPPORTED \n");
goto out_err;
break;
default:
dprintk("RPC: SPKM3 get_key: unsupported algorithm %d", *resalg);
goto out_err_free_key;
}
if (!(*res = crypto_alloc_tfm(alg_name, alg_mode)))
goto out_err_free_key;
if (setkey) {
if (crypto_cipher_setkey(*res, key.data, key.len))
goto out_err_free_tfm;
}
if(key.len > 0)
kfree(key.data);
return 0;
out_err_free_tfm:
crypto_free_tfm(*res);
out_err_free_key:
if(key.len > 0)
kfree(key.data);
out_err:
return -1;
}
static u32
gss_import_sec_context_spkm3(struct xdr_netobj *inbuf,
struct gss_ctx *ctx_id)
{
char *p = inbuf->data;
char *end = inbuf->data + inbuf->len;
struct spkm3_ctx *ctx;
if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)))
goto out_err;
memset(ctx, 0, sizeof(*ctx));
if (get_netobj(&p, end, &ctx->ctx_id))
goto out_err_free_ctx;
if (get_bytes(&p, end, &ctx->qop, sizeof(ctx->qop)))
goto out_err_free_ctx_id;
if (get_netobj(&p, end, &ctx->mech_used))
goto out_err_free_mech;
if (get_bytes(&p, end, &ctx->ret_flags, sizeof(ctx->ret_flags)))
goto out_err_free_mech;
if (get_bytes(&p, end, &ctx->req_flags, sizeof(ctx->req_flags)))
goto out_err_free_mech;
if (get_netobj(&p, end, &ctx->share_key))
goto out_err_free_s_key;
if (get_key(&p, end, &ctx->derived_conf_key, &ctx->conf_alg)) {
dprintk("RPC: SPKM3 confidentiality key will be NULL\n");
}
if (get_key(&p, end, &ctx->derived_integ_key, &ctx->intg_alg)) {
dprintk("RPC: SPKM3 integrity key will be NULL\n");
}
if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
goto out_err_free_s_key;
if (get_bytes(&p, end, &ctx->owf_alg, sizeof(ctx->owf_alg)))
goto out_err_free_s_key;
if (p != end)
goto out_err_free_s_key;
ctx_id->internal_ctx_id = ctx;
dprintk("Succesfully imported new spkm context.\n");
return 0;
out_err_free_s_key:
kfree(ctx->share_key.data);
out_err_free_mech:
kfree(ctx->mech_used.data);
out_err_free_ctx_id:
kfree(ctx->ctx_id.data);
out_err_free_ctx:
kfree(ctx);
out_err:
return GSS_S_FAILURE;
}
void
gss_delete_sec_context_spkm3(void *internal_ctx) {
struct spkm3_ctx *sctx = internal_ctx;
if(sctx->derived_integ_key)
crypto_free_tfm(sctx->derived_integ_key);
if(sctx->derived_conf_key)
crypto_free_tfm(sctx->derived_conf_key);
if(sctx->share_key.data)
kfree(sctx->share_key.data);
if(sctx->mech_used.data)
kfree(sctx->mech_used.data);
kfree(sctx);
}
u32
gss_verify_mic_spkm3(struct gss_ctx *ctx,
struct xdr_buf *signbuf,
struct xdr_netobj *checksum,
u32 *qstate) {
u32 maj_stat = 0;
int qop_state = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
dprintk("RPC: gss_verify_mic_spkm3 calling spkm3_read_token\n");
maj_stat = spkm3_read_token(sctx, checksum, signbuf, &qop_state,
SPKM_MIC_TOK);
if (!maj_stat && qop_state)
*qstate = qop_state;
dprintk("RPC: gss_verify_mic_spkm3 returning %d\n", maj_stat);
return maj_stat;
}
u32
gss_get_mic_spkm3(struct gss_ctx *ctx,
u32 qop,
struct xdr_buf *message_buffer,
struct xdr_netobj *message_token) {
u32 err = 0;
struct spkm3_ctx *sctx = ctx->internal_ctx_id;
dprintk("RPC: gss_get_mic_spkm3\n");
err = spkm3_make_token(sctx, qop, message_buffer,
message_token, SPKM_MIC_TOK);
return err;
}
static struct gss_api_ops gss_spkm3_ops = {
.gss_import_sec_context = gss_import_sec_context_spkm3,
.gss_get_mic = gss_get_mic_spkm3,
.gss_verify_mic = gss_verify_mic_spkm3,
.gss_delete_sec_context = gss_delete_sec_context_spkm3,
};
static struct pf_desc gss_spkm3_pfs[] = {
{RPC_AUTH_GSS_SPKM, 0, RPC_GSS_SVC_NONE, "spkm3"},
{RPC_AUTH_GSS_SPKMI, 0, RPC_GSS_SVC_INTEGRITY, "spkm3i"},
};
static struct gss_api_mech gss_spkm3_mech = {
.gm_name = "spkm3",
.gm_owner = THIS_MODULE,
.gm_ops = &gss_spkm3_ops,
.gm_pf_num = ARRAY_SIZE(gss_spkm3_pfs),
.gm_pfs = gss_spkm3_pfs,
};
static int __init init_spkm3_module(void)
{
int status;
status = gss_mech_register(&gss_spkm3_mech);
if (status)
printk("Failed to register spkm3 gss mechanism!\n");
return 0;
}
static void __exit cleanup_spkm3_module(void)
{
gss_mech_unregister(&gss_spkm3_mech);
}
MODULE_LICENSE("GPL");
module_init(init_spkm3_module);
module_exit(cleanup_spkm3_module);

View File

@@ -0,0 +1,132 @@
/*
* linux/net/sunrpc/gss_spkm3_seal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@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/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_make_token()
*
* Only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_make_token(struct spkm3_ctx *ctx, int qop_req,
struct xdr_buf * text, struct xdr_netobj * token,
int toktype)
{
s32 checksum_type;
char tokhdrbuf[25];
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
struct xdr_netobj mic_hdr = {.len = 0, .data = tokhdrbuf};
int tmsglen, tokenlen = 0;
unsigned char *ptr;
s32 now;
int ctxelen = 0, ctxzbit = 0;
int md5elen = 0, md5zbit = 0;
dprintk("RPC: spkm3_make_token\n");
now = jiffies;
if (qop_req != 0)
goto out_err;
if (ctx->ctx_id.len != 16) {
dprintk("RPC: spkm3_make_token BAD ctx_id.len %d\n",
ctx->ctx_id.len);
goto out_err;
}
switch (ctx->intg_alg) {
case NID_md5:
checksum_type = CKSUMTYPE_RSA_MD5;
break;
default:
dprintk("RPC: gss_spkm3_seal: ctx->signalg %d not"
" supported\n", ctx->intg_alg);
goto out_err;
}
/* XXX since we don't support WRAP, perhaps we don't care... */
if (ctx->conf_alg != NID_cast5_cbc) {
dprintk("RPC: gss_spkm3_seal: ctx->sealalg %d not supported\n",
ctx->conf_alg);
goto out_err;
}
if (toktype == SPKM_MIC_TOK) {
tmsglen = 0;
/* Calculate checksum over the mic-header */
asn1_bitstring_len(&ctx->ctx_id, &ctxelen, &ctxzbit);
spkm3_mic_header(&mic_hdr.data, &mic_hdr.len, ctx->ctx_id.data,
ctxelen, ctxzbit);
if (make_checksum(checksum_type, mic_hdr.data, mic_hdr.len,
text, &md5cksum))
goto out_err;
asn1_bitstring_len(&md5cksum, &md5elen, &md5zbit);
tokenlen = 10 + ctxelen + 1 + 2 + md5elen + 1;
/* Create token header using generic routines */
token->len = g_token_size(&ctx->mech_used, tokenlen + tmsglen);
ptr = token->data;
g_make_token_header(&ctx->mech_used, tokenlen + tmsglen, &ptr);
spkm3_make_mic_token(&ptr, tokenlen, &mic_hdr, &md5cksum, md5elen, md5zbit);
} else if (toktype == SPKM_WRAP_TOK) { /* Not Supported */
dprintk("RPC: gss_spkm3_seal: SPKM_WRAP_TOK not supported\n");
goto out_err;
}
kfree(md5cksum.data);
/* XXX need to implement sequence numbers, and ctx->expired */
return GSS_S_COMPLETE;
out_err:
if (md5cksum.data)
kfree(md5cksum.data);
token->data = NULL;
token->len = 0;
return GSS_S_FAILURE;
}

View File

@@ -0,0 +1,266 @@
/*
* linux/net/sunrpc/gss_spkm3_token.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@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/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/random.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* asn1_bitstring_len()
*
* calculate the asn1 bitstring length of the xdr_netobject
*/
void
asn1_bitstring_len(struct xdr_netobj *in, int *enclen, int *zerobits)
{
int i, zbit = 0,elen = in->len;
char *ptr;
ptr = &in->data[in->len -1];
/* count trailing 0's */
for(i = in->len; i > 0; i--) {
if (*ptr == 0) {
ptr--;
elen--;
} else
break;
}
/* count number of 0 bits in final octet */
ptr = &in->data[elen - 1];
for(i = 0; i < 8; i++) {
short mask = 0x01;
if (!((mask << i) & *ptr))
zbit++;
else
break;
}
*enclen = elen;
*zerobits = zbit;
}
/*
* decode_asn1_bitstring()
*
* decode a bitstring into a buffer of the expected length.
* enclen = bit string length
* explen = expected length (define in rfc)
*/
int
decode_asn1_bitstring(struct xdr_netobj *out, char *in, int enclen, int explen)
{
if (!(out->data = kmalloc(explen,GFP_KERNEL)))
return 0;
out->len = explen;
memset(out->data, 0, explen);
memcpy(out->data, in, enclen);
return 1;
}
/*
* SPKMInnerContextToken choice SPKM_MIC asn1 token layout
*
* contextid is always 16 bytes plain data. max asn1 bitstring len = 17.
*
* tokenlen = pos[0] to end of token (max pos[45] with MD5 cksum)
*
* pos value
* ----------
* [0] a4 SPKM-MIC tag
* [1] ?? innertoken length (max 44)
*
*
* tok_hdr piece of checksum data starts here
*
* the maximum mic-header len = 9 + 17 = 26
* mic-header
* ----------
* [2] 30 SEQUENCE tag
* [3] ?? mic-header length: (max 23) = TokenID + ContextID
*
* TokenID - all fields constant and can be hardcoded
* -------
* [4] 02 Type 2
* [5] 02 Length 2
* [6][7] 01 01 TokenID (SPKM_MIC_TOK)
*
* ContextID - encoded length not constant, calculated
* ---------
* [8] 03 Type 3
* [9] ?? encoded length
* [10] ?? ctxzbit
* [11] contextid
*
* mic_header piece of checksum data ends here.
*
* int-cksum - encoded length not constant, calculated
* ---------
* [??] 03 Type 3
* [??] ?? encoded length
* [??] ?? md5zbit
* [??] int-cksum (NID_md5 = 16)
*
* maximum SPKM-MIC innercontext token length =
* 10 + encoded contextid_size(17 max) + 2 + encoded
* cksum_size (17 maxfor NID_md5) = 46
*/
/*
* spkm3_mic_header()
*
* Prepare the SPKM_MIC_TOK mic-header for check-sum calculation
* elen: 16 byte context id asn1 bitstring encoded length
*/
void
spkm3_mic_header(unsigned char **hdrbuf, unsigned int *hdrlen, unsigned char *ctxdata, int elen, int zbit)
{
char *hptr = *hdrbuf;
char *top = *hdrbuf;
*(u8 *)hptr++ = 0x30;
*(u8 *)hptr++ = elen + 7; /* on the wire header length */
/* tokenid */
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x02;
*(u8 *)hptr++ = 0x01;
*(u8 *)hptr++ = 0x01;
/* coniextid */
*(u8 *)hptr++ = 0x03;
*(u8 *)hptr++ = elen + 1; /* add 1 to include zbit */
*(u8 *)hptr++ = zbit;
memcpy(hptr, ctxdata, elen);
hptr += elen;
*hdrlen = hptr - top;
}
/*
* spkm3_mic_innercontext_token()
*
* *tokp points to the beginning of the SPKM_MIC token described
* in rfc 2025, section 3.2.1:
*
*/
void
spkm3_make_mic_token(unsigned char **tokp, int toklen, struct xdr_netobj *mic_hdr, struct xdr_netobj *md5cksum, int md5elen, int md5zbit)
{
unsigned char *ict = *tokp;
*(u8 *)ict++ = 0xa4;
*(u8 *)ict++ = toklen - 2;
memcpy(ict, mic_hdr->data, mic_hdr->len);
ict += mic_hdr->len;
*(u8 *)ict++ = 0x03;
*(u8 *)ict++ = md5elen + 1; /* add 1 to include zbit */
*(u8 *)ict++ = md5zbit;
memcpy(ict, md5cksum->data, md5elen);
}
u32
spkm3_verify_mic_token(unsigned char **tokp, int *mic_hdrlen, unsigned char **cksum)
{
struct xdr_netobj spkm3_ctx_id = {.len =0, .data = NULL};
unsigned char *ptr = *tokp;
int ctxelen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
/* spkm3 innercontext token preamble */
if ((ptr[0] != 0xa4) || (ptr[2] != 0x30)) {
dprintk("RPC: BAD SPKM ictoken preamble\n");
goto out;
}
*mic_hdrlen = ptr[3];
/* token type */
if ((ptr[4] != 0x02) || (ptr[5] != 0x02)) {
dprintk("RPC: BAD asn1 SPKM3 token type\n");
goto out;
}
/* only support SPKM_MIC_TOK */
if((ptr[6] != 0x01) || (ptr[7] != 0x01)) {
dprintk("RPC: ERROR unsupported SPKM3 token \n");
goto out;
}
/* contextid */
if (ptr[8] != 0x03) {
dprintk("RPC: BAD SPKM3 asn1 context-id type\n");
goto out;
}
ctxelen = ptr[9];
if (ctxelen > 17) { /* length includes asn1 zbit octet */
dprintk("RPC: BAD SPKM3 contextid len %d\n", ctxelen);
goto out;
}
/* ignore ptr[10] */
if(!decode_asn1_bitstring(&spkm3_ctx_id, &ptr[11], ctxelen - 1, 16))
goto out;
/*
* in the current implementation: the optional int-alg is not present
* so the default int-alg (md5) is used the optional snd-seq field is
* also not present
*/
if (*mic_hdrlen != 6 + ctxelen) {
dprintk("RPC: BAD SPKM_ MIC_TOK header len %d: we only support default int-alg (should be absent) and do not support snd-seq\n", *mic_hdrlen);
goto out;
}
/* checksum */
*cksum = (&ptr[10] + ctxelen); /* ctxelen includes ptr[10] */
ret = GSS_S_COMPLETE;
out:
if (spkm3_ctx_id.data)
kfree(spkm3_ctx_id.data);
return ret;
}

View File

@@ -0,0 +1,128 @@
/*
* linux/net/sunrpc/gss_spkm3_unseal.c
*
* Copyright (c) 2003 The Regents of the University of Michigan.
* All rights reserved.
*
* Andy Adamson <andros@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/jiffies.h>
#include <linux/sunrpc/gss_spkm3.h>
#include <linux/crypto.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
/*
* spkm3_read_token()
*
* only SPKM_MIC_TOK with md5 intg-alg is supported
*/
u32
spkm3_read_token(struct spkm3_ctx *ctx,
struct xdr_netobj *read_token, /* checksum */
struct xdr_buf *message_buffer, /* signbuf */
int *qop_state, int toktype)
{
s32 code;
struct xdr_netobj wire_cksum = {.len =0, .data = NULL};
struct xdr_netobj md5cksum = {.len = 0, .data = NULL};
unsigned char *ptr = (unsigned char *)read_token->data;
unsigned char *cksum;
int bodysize, md5elen;
int mic_hdrlen;
u32 ret = GSS_S_DEFECTIVE_TOKEN;
dprintk("RPC: spkm3_read_token read_token->len %d\n", read_token->len);
if (g_verify_token_header((struct xdr_netobj *) &ctx->mech_used,
&bodysize, &ptr, read_token->len))
goto out;
/* decode the token */
if (toktype == SPKM_MIC_TOK) {
if ((ret = spkm3_verify_mic_token(&ptr, &mic_hdrlen, &cksum)))
goto out;
if (*cksum++ != 0x03) {
dprintk("RPC: spkm3_read_token BAD checksum type\n");
goto out;
}
md5elen = *cksum++;
cksum++; /* move past the zbit */
if(!decode_asn1_bitstring(&wire_cksum, cksum, md5elen - 1, 16))
goto out;
/* HARD CODED FOR MD5 */
/* compute the checksum of the message.
* ptr + 2 = start of header piece of checksum
* mic_hdrlen + 2 = length of header piece of checksum
*/
ret = GSS_S_DEFECTIVE_TOKEN;
code = make_checksum(CKSUMTYPE_RSA_MD5, ptr + 2,
mic_hdrlen + 2,
message_buffer, &md5cksum);
if (code)
goto out;
dprintk("RPC: spkm3_read_token: digest wire_cksum.len %d:\n",
wire_cksum.len);
dprintk(" md5cksum.data\n");
print_hexl((u32 *) md5cksum.data, 16, 0);
dprintk(" cksum.data:\n");
print_hexl((u32 *) wire_cksum.data, wire_cksum.len, 0);
ret = GSS_S_BAD_SIG;
code = memcmp(md5cksum.data, wire_cksum.data, wire_cksum.len);
if (code)
goto out;
} else {
dprintk("RPC: BAD or UNSUPPORTED SPKM3 token type: %d\n",toktype);
goto out;
}
/* XXX: need to add expiration and sequencing */
ret = GSS_S_COMPLETE;
out:
if (md5cksum.data)
kfree(md5cksum.data);
if (wire_cksum.data)
kfree(wire_cksum.data);
return ret;
}

View File

@@ -0,0 +1,37 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sched.h>
#include <linux/uio.h>
#include <linux/unistd.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/gss_krb5.h>
/* svcauth_gss.c: */
EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
/* registering gss mechanisms to the mech switching code: */
EXPORT_SYMBOL(gss_mech_register);
EXPORT_SYMBOL(gss_mech_unregister);
EXPORT_SYMBOL(gss_mech_get);
EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
EXPORT_SYMBOL(gss_mech_get_by_name);
EXPORT_SYMBOL(gss_mech_put);
EXPORT_SYMBOL(gss_pseudoflavor_to_service);
EXPORT_SYMBOL(gss_service_to_auth_domain_name);
/* generic functionality in gss code: */
EXPORT_SYMBOL(g_make_token_header);
EXPORT_SYMBOL(g_verify_token_header);
EXPORT_SYMBOL(g_token_size);
EXPORT_SYMBOL(make_checksum);
EXPORT_SYMBOL(krb5_encrypt);
EXPORT_SYMBOL(krb5_decrypt);
/* debug */
EXPORT_SYMBOL(print_hexl);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,145 @@
/*
* linux/net/sunrpc/auth_null.c
*
* AUTH_NULL authentication. Really :-)
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/module.h>
#include <linux/in.h>
#include <linux/utsname.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sched.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static struct rpc_credops null_credops;
static struct rpc_auth *
nul_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
{
struct rpc_auth *auth;
dprintk("RPC: creating NULL authenticator for client %p\n", clnt);
if (!(auth = (struct rpc_auth *) kmalloc(sizeof(*auth),GFP_KERNEL)))
return NULL;
auth->au_cslack = 4;
auth->au_rslack = 2;
auth->au_ops = &authnull_ops;
auth->au_expire = 1800 * HZ;
rpcauth_init_credcache(auth);
return (struct rpc_auth *) auth;
}
static void
nul_destroy(struct rpc_auth *auth)
{
dprintk("RPC: destroying NULL authenticator %p\n", auth);
rpcauth_free_credcache(auth);
}
/*
* Create NULL creds for current process
*/
static struct rpc_cred *
nul_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
{
struct rpc_cred *cred;
if (!(cred = (struct rpc_cred *) kmalloc(sizeof(*cred),GFP_KERNEL)))
return NULL;
atomic_set(&cred->cr_count, 0);
cred->cr_flags = RPCAUTH_CRED_UPTODATE;
cred->cr_uid = acred->uid;
cred->cr_ops = &null_credops;
return cred;
}
/*
* Destroy cred handle.
*/
static void
nul_destroy_cred(struct rpc_cred *cred)
{
kfree(cred);
}
/*
* Match cred handle against current process
*/
static int
nul_match(struct auth_cred *acred, struct rpc_cred *cred, int taskflags)
{
return 1;
}
/*
* Marshal credential.
*/
static u32 *
nul_marshal(struct rpc_task *task, u32 *p, int ruid)
{
*p++ = htonl(RPC_AUTH_NULL);
*p++ = 0;
*p++ = htonl(RPC_AUTH_NULL);
*p++ = 0;
return p;
}
/*
* Refresh credential. This is a no-op for AUTH_NULL
*/
static int
nul_refresh(struct rpc_task *task)
{
return task->tk_status = -EACCES;
}
static u32 *
nul_validate(struct rpc_task *task, u32 *p)
{
rpc_authflavor_t flavor;
u32 size;
flavor = ntohl(*p++);
if (flavor != RPC_AUTH_NULL) {
printk("RPC: bad verf flavor: %u\n", flavor);
return NULL;
}
size = ntohl(*p++);
if (size != 0) {
printk("RPC: bad verf size: %u\n", size);
return NULL;
}
return p;
}
struct rpc_authops authnull_ops = {
.owner = THIS_MODULE,
.au_flavor = RPC_AUTH_NULL,
#ifdef RPC_DEBUG
.au_name = "NULL",
#endif
.create = nul_create,
.destroy = nul_destroy,
.crcreate = nul_create_cred,
};
static
struct rpc_credops null_credops = {
.crdestroy = nul_destroy_cred,
.crmatch = nul_match,
.crmarshal = nul_marshal,
.crrefresh = nul_refresh,
.crvalidate = nul_validate,
};

View File

@@ -0,0 +1,236 @@
/*
* linux/net/sunrpc/auth_unix.c
*
* UNIX-style authentication; no AUTH_SHORT support
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/auth.h>
#define NFS_NGROUPS 16
struct unx_cred {
struct rpc_cred uc_base;
gid_t uc_gid;
uid_t uc_puid; /* process uid */
gid_t uc_pgid; /* process gid */
gid_t uc_gids[NFS_NGROUPS];
};
#define uc_uid uc_base.cr_uid
#define uc_count uc_base.cr_count
#define uc_flags uc_base.cr_flags
#define uc_expire uc_base.cr_expire
#define UNX_CRED_EXPIRE (60 * HZ)
#define UNX_WRITESLACK (21 + (UNX_MAXNODENAME >> 2))
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_AUTH
#endif
static struct rpc_credops unix_credops;
static struct rpc_auth *
unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
{
struct rpc_auth *auth;
dprintk("RPC: creating UNIX authenticator for client %p\n", clnt);
if (!(auth = (struct rpc_auth *) kmalloc(sizeof(*auth), GFP_KERNEL)))
return NULL;
auth->au_cslack = UNX_WRITESLACK;
auth->au_rslack = 2; /* assume AUTH_NULL verf */
auth->au_expire = UNX_CRED_EXPIRE;
auth->au_ops = &authunix_ops;
rpcauth_init_credcache(auth);
return auth;
}
static void
unx_destroy(struct rpc_auth *auth)
{
dprintk("RPC: destroying UNIX authenticator %p\n", auth);
rpcauth_free_credcache(auth);
}
static struct rpc_cred *
unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
{
struct unx_cred *cred;
int i;
dprintk("RPC: allocating UNIX cred for uid %d gid %d\n",
acred->uid, acred->gid);
if (!(cred = (struct unx_cred *) kmalloc(sizeof(*cred), GFP_KERNEL)))
return NULL;
atomic_set(&cred->uc_count, 0);
cred->uc_flags = RPCAUTH_CRED_UPTODATE;
if (flags & RPC_TASK_ROOTCREDS) {
cred->uc_uid = cred->uc_puid = 0;
cred->uc_gid = cred->uc_pgid = 0;
cred->uc_gids[0] = NOGROUP;
} else {
int groups = acred->group_info->ngroups;
if (groups > NFS_NGROUPS)
groups = NFS_NGROUPS;
cred->uc_uid = acred->uid;
cred->uc_gid = acred->gid;
cred->uc_puid = current->uid;
cred->uc_pgid = current->gid;
for (i = 0; i < groups; i++)
cred->uc_gids[i] = GROUP_AT(acred->group_info, i);
if (i < NFS_NGROUPS)
cred->uc_gids[i] = NOGROUP;
}
cred->uc_base.cr_ops = &unix_credops;
return (struct rpc_cred *) cred;
}
static void
unx_destroy_cred(struct rpc_cred *cred)
{
kfree(cred);
}
/*
* Match credentials against current process creds.
* The root_override argument takes care of cases where the caller may
* request root creds (e.g. for NFS swapping).
*/
static int
unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int taskflags)
{
struct unx_cred *cred = (struct unx_cred *) rcred;
int i;
if (!(taskflags & RPC_TASK_ROOTCREDS)) {
int groups;
if (cred->uc_uid != acred->uid
|| cred->uc_gid != acred->gid
|| cred->uc_puid != current->uid
|| cred->uc_pgid != current->gid)
return 0;
groups = acred->group_info->ngroups;
if (groups > NFS_NGROUPS)
groups = NFS_NGROUPS;
for (i = 0; i < groups ; i++)
if (cred->uc_gids[i] != GROUP_AT(acred->group_info, i))
return 0;
return 1;
}
return (cred->uc_uid == 0 && cred->uc_puid == 0
&& cred->uc_gid == 0 && cred->uc_pgid == 0
&& cred->uc_gids[0] == (gid_t) NOGROUP);
}
/*
* Marshal credentials.
* Maybe we should keep a cached credential for performance reasons.
*/
static u32 *
unx_marshal(struct rpc_task *task, u32 *p, int ruid)
{
struct rpc_clnt *clnt = task->tk_client;
struct unx_cred *cred = (struct unx_cred *) task->tk_msg.rpc_cred;
u32 *base, *hold;
int i;
*p++ = htonl(RPC_AUTH_UNIX);
base = p++;
*p++ = htonl(jiffies/HZ);
/*
* Copy the UTS nodename captured when the client was created.
*/
p = xdr_encode_array(p, clnt->cl_nodename, clnt->cl_nodelen);
/* Note: we don't use real uid if it involves raising privilege */
if (ruid && cred->uc_puid != 0 && cred->uc_pgid != 0) {
*p++ = htonl((u32) cred->uc_puid);
*p++ = htonl((u32) cred->uc_pgid);
} else {
*p++ = htonl((u32) cred->uc_uid);
*p++ = htonl((u32) cred->uc_gid);
}
hold = p++;
for (i = 0; i < 16 && cred->uc_gids[i] != (gid_t) NOGROUP; i++)
*p++ = htonl((u32) cred->uc_gids[i]);
*hold = htonl(p - hold - 1); /* gid array length */
*base = htonl((p - base - 1) << 2); /* cred length */
*p++ = htonl(RPC_AUTH_NULL);
*p++ = htonl(0);
return p;
}
/*
* Refresh credentials. This is a no-op for AUTH_UNIX
*/
static int
unx_refresh(struct rpc_task *task)
{
task->tk_msg.rpc_cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
return task->tk_status = -EACCES;
}
static u32 *
unx_validate(struct rpc_task *task, u32 *p)
{
rpc_authflavor_t flavor;
u32 size;
flavor = ntohl(*p++);
if (flavor != RPC_AUTH_NULL &&
flavor != RPC_AUTH_UNIX &&
flavor != RPC_AUTH_SHORT) {
printk("RPC: bad verf flavor: %u\n", flavor);
return NULL;
}
size = ntohl(*p++);
if (size > RPC_MAX_AUTH_SIZE) {
printk("RPC: giant verf size: %u\n", size);
return NULL;
}
task->tk_auth->au_rslack = (size >> 2) + 2;
p += (size >> 2);
return p;
}
struct rpc_authops authunix_ops = {
.owner = THIS_MODULE,
.au_flavor = RPC_AUTH_UNIX,
#ifdef RPC_DEBUG
.au_name = "UNIX",
#endif
.create = unx_create,
.destroy = unx_destroy,
.crcreate = unx_create_cred,
};
static
struct rpc_credops unix_credops = {
.crdestroy = unx_destroy_cred,
.crmatch = unx_match,
.crmarshal = unx_marshal,
.crrefresh = unx_refresh,
.crvalidate = unx_validate,
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,301 @@
/*
* linux/net/sunrpc/pmap.c
*
* Portmapper client.
*
* FIXME: In a secure environment, we may want to use an authentication
* flavor other than AUTH_NULL.
*
* Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/uio.h>
#include <linux/in.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/sched.h>
#ifdef RPC_DEBUG
# define RPCDBG_FACILITY RPCDBG_PMAP
#endif
#define PMAP_SET 1
#define PMAP_UNSET 2
#define PMAP_GETPORT 3
static struct rpc_procinfo pmap_procedures[];
static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int);
static void pmap_getport_done(struct rpc_task *);
extern struct rpc_program pmap_program;
static spinlock_t pmap_lock = SPIN_LOCK_UNLOCKED;
/*
* Obtain the port for a given RPC service on a given host. This one can
* be called for an ongoing RPC request.
*/
void
rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt)
{
struct rpc_portmap *map = clnt->cl_pmap;
struct sockaddr_in *sap = &clnt->cl_xprt->addr;
struct rpc_message msg = {
.rpc_proc = &pmap_procedures[PMAP_GETPORT],
.rpc_argp = map,
.rpc_resp = &clnt->cl_port,
.rpc_cred = NULL
};
struct rpc_clnt *pmap_clnt;
struct rpc_task *child;
dprintk("RPC: %4d rpc_getport(%s, %d, %d, %d)\n",
task->tk_pid, clnt->cl_server,
map->pm_prog, map->pm_vers, map->pm_prot);
spin_lock(&pmap_lock);
if (map->pm_binding) {
rpc_sleep_on(&map->pm_bindwait, task, NULL, NULL);
spin_unlock(&pmap_lock);
return;
}
map->pm_binding = 1;
spin_unlock(&pmap_lock);
pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot);
if (IS_ERR(pmap_clnt)) {
task->tk_status = PTR_ERR(pmap_clnt);
goto bailout;
}
task->tk_status = 0;
/*
* Note: rpc_new_child will release client after a failure.
*/
if (!(child = rpc_new_child(pmap_clnt, task)))
goto bailout;
/* Setup the call info struct */
rpc_call_setup(child, &msg, 0);
/* ... and run the child task */
rpc_run_child(task, child, pmap_getport_done);
return;
bailout:
spin_lock(&pmap_lock);
map->pm_binding = 0;
rpc_wake_up(&map->pm_bindwait);
spin_unlock(&pmap_lock);
task->tk_status = -EIO;
task->tk_action = NULL;
}
#ifdef CONFIG_ROOT_NFS
int
rpc_getport_external(struct sockaddr_in *sin, __u32 prog, __u32 vers, int prot)
{
struct rpc_portmap map = {
.pm_prog = prog,
.pm_vers = vers,
.pm_prot = prot,
.pm_port = 0
};
struct rpc_clnt *pmap_clnt;
char hostname[32];
int status;
dprintk("RPC: rpc_getport_external(%u.%u.%u.%u, %d, %d, %d)\n",
NIPQUAD(sin->sin_addr.s_addr), prog, vers, prot);
sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr));
pmap_clnt = pmap_create(hostname, sin, prot);
if (IS_ERR(pmap_clnt))
return PTR_ERR(pmap_clnt);
/* Setup the call info struct */
status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0);
if (status >= 0) {
if (map.pm_port != 0)
return map.pm_port;
status = -EACCES;
}
return status;
}
#endif
static void
pmap_getport_done(struct rpc_task *task)
{
struct rpc_clnt *clnt = task->tk_client;
struct rpc_portmap *map = clnt->cl_pmap;
dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n",
task->tk_pid, task->tk_status, clnt->cl_port);
if (task->tk_status < 0) {
/* Make the calling task exit with an error */
task->tk_action = NULL;
} else if (clnt->cl_port == 0) {
/* Program not registered */
task->tk_status = -EACCES;
task->tk_action = NULL;
} else {
/* byte-swap port number first */
clnt->cl_port = htons(clnt->cl_port);
clnt->cl_xprt->addr.sin_port = clnt->cl_port;
}
spin_lock(&pmap_lock);
map->pm_binding = 0;
rpc_wake_up(&map->pm_bindwait);
spin_unlock(&pmap_lock);
}
/*
* Set or unset a port registration with the local portmapper.
* port == 0 means unregister, port != 0 means register.
*/
int
rpc_register(u32 prog, u32 vers, int prot, unsigned short port, int *okay)
{
struct sockaddr_in sin;
struct rpc_portmap map;
struct rpc_clnt *pmap_clnt;
int error = 0;
dprintk("RPC: registering (%d, %d, %d, %d) with portmapper.\n",
prog, vers, prot, port);
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
pmap_clnt = pmap_create("localhost", &sin, IPPROTO_UDP);
if (IS_ERR(pmap_clnt)) {
error = PTR_ERR(pmap_clnt);
dprintk("RPC: couldn't create pmap client. Error = %d\n", error);
return error;
}
map.pm_prog = prog;
map.pm_vers = vers;
map.pm_prot = prot;
map.pm_port = port;
error = rpc_call(pmap_clnt, port? PMAP_SET : PMAP_UNSET,
&map, okay, 0);
if (error < 0) {
printk(KERN_WARNING
"RPC: failed to contact portmap (errno %d).\n",
error);
}
dprintk("RPC: registration status %d/%d\n", error, *okay);
/* Client deleted automatically because cl_oneshot == 1 */
return error;
}
static struct rpc_clnt *
pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto)
{
struct rpc_xprt *xprt;
struct rpc_clnt *clnt;
/* printk("pmap: create xprt\n"); */
xprt = xprt_create_proto(proto, srvaddr, NULL);
if (IS_ERR(xprt))
return (struct rpc_clnt *)xprt;
xprt->addr.sin_port = htons(RPC_PMAP_PORT);
/* printk("pmap: create clnt\n"); */
clnt = rpc_create_client(xprt, hostname,
&pmap_program, RPC_PMAP_VERSION,
RPC_AUTH_NULL);
if (IS_ERR(clnt)) {
xprt_destroy(xprt);
} else {
clnt->cl_softrtry = 1;
clnt->cl_chatty = 1;
clnt->cl_oneshot = 1;
}
return clnt;
}
/*
* XDR encode/decode functions for PMAP
*/
static int
xdr_encode_mapping(struct rpc_rqst *req, u32 *p, struct rpc_portmap *map)
{
dprintk("RPC: xdr_encode_mapping(%d, %d, %d, %d)\n",
map->pm_prog, map->pm_vers, map->pm_prot, map->pm_port);
*p++ = htonl(map->pm_prog);
*p++ = htonl(map->pm_vers);
*p++ = htonl(map->pm_prot);
*p++ = htonl(map->pm_port);
req->rq_slen = xdr_adjust_iovec(req->rq_svec, p);
return 0;
}
static int
xdr_decode_port(struct rpc_rqst *req, u32 *p, unsigned short *portp)
{
*portp = (unsigned short) ntohl(*p++);
return 0;
}
static int
xdr_decode_bool(struct rpc_rqst *req, u32 *p, unsigned int *boolp)
{
*boolp = (unsigned int) ntohl(*p++);
return 0;
}
static struct rpc_procinfo pmap_procedures[] = {
[PMAP_SET] = {
.p_proc = PMAP_SET,
.p_encode = (kxdrproc_t) xdr_encode_mapping,
.p_decode = (kxdrproc_t) xdr_decode_bool,
.p_bufsiz = 4,
.p_count = 1,
},
[PMAP_UNSET] = {
.p_proc = PMAP_UNSET,
.p_encode = (kxdrproc_t) xdr_encode_mapping,
.p_decode = (kxdrproc_t) xdr_decode_bool,
.p_bufsiz = 4,
.p_count = 1,
},
[PMAP_GETPORT] = {
.p_proc = PMAP_GETPORT,
.p_encode = (kxdrproc_t) xdr_encode_mapping,
.p_decode = (kxdrproc_t) xdr_decode_port,
.p_bufsiz = 4,
.p_count = 1,
},
};
static struct rpc_version pmap_version2 = {
.number = 2,
.nrprocs = 4,
.procs = pmap_procedures
};
static struct rpc_version * pmap_version[] = {
NULL,
NULL,
&pmap_version2
};
static struct rpc_stat pmap_stats;
struct rpc_program pmap_program = {
.name = "portmap",
.number = RPC_PMAP_PROGRAM,
.nrvers = ARRAY_SIZE(pmap_version),
.version = pmap_version,
.stats = &pmap_stats,
};

View File

@@ -0,0 +1,842 @@
/*
* net/sunrpc/rpc_pipe.c
*
* Userland/kernel interface for rpcauth_gss.
* Code shamelessly plagiarized from fs/nfsd/nfsctl.c
* and fs/driverfs/inode.c
*
* Copyright (c) 2002, Trond Myklebust <trond.myklebust@fys.uio.no>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/dnotify.h>
#include <linux/kernel.h>
#include <asm/ioctls.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/wait.h>
#include <linux/seq_file.h>
#include <linux/sunrpc/clnt.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
static struct vfsmount *rpc_mount;
static int rpc_mount_count;
static struct file_system_type rpc_pipe_fs_type;
static kmem_cache_t *rpc_inode_cachep;
#define RPC_UPCALL_TIMEOUT (30*HZ)
static void
__rpc_purge_upcall(struct inode *inode, int err)
{
struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_msg *msg;
while (!list_empty(&rpci->pipe)) {
msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list);
list_del_init(&msg->list);
msg->errno = err;
rpci->ops->destroy_msg(msg);
}
while (!list_empty(&rpci->in_upcall)) {
msg = list_entry(rpci->pipe.next, struct rpc_pipe_msg, list);
list_del_init(&msg->list);
msg->errno = err;
rpci->ops->destroy_msg(msg);
}
rpci->pipelen = 0;
wake_up(&rpci->waitq);
}
static void
rpc_timeout_upcall_queue(void *data)
{
struct rpc_inode *rpci = (struct rpc_inode *)data;
struct inode *inode = &rpci->vfs_inode;
down(&inode->i_sem);
if (rpci->nreaders == 0 && !list_empty(&rpci->pipe))
__rpc_purge_upcall(inode, -ETIMEDOUT);
up(&inode->i_sem);
}
int
rpc_queue_upcall(struct inode *inode, struct rpc_pipe_msg *msg)
{
struct rpc_inode *rpci = RPC_I(inode);
int res = 0;
down(&inode->i_sem);
if (rpci->nreaders) {
list_add_tail(&msg->list, &rpci->pipe);
rpci->pipelen += msg->len;
} else if (rpci->flags & RPC_PIPE_WAIT_FOR_OPEN) {
if (list_empty(&rpci->pipe))
schedule_delayed_work(&rpci->queue_timeout,
RPC_UPCALL_TIMEOUT);
list_add_tail(&msg->list, &rpci->pipe);
rpci->pipelen += msg->len;
} else
res = -EPIPE;
up(&inode->i_sem);
wake_up(&rpci->waitq);
return res;
}
static void
rpc_close_pipes(struct inode *inode)
{
struct rpc_inode *rpci = RPC_I(inode);
cancel_delayed_work(&rpci->queue_timeout);
flush_scheduled_work();
down(&inode->i_sem);
if (rpci->ops != NULL) {
rpci->nreaders = 0;
__rpc_purge_upcall(inode, -EPIPE);
rpci->nwriters = 0;
if (rpci->ops->release_pipe)
rpci->ops->release_pipe(inode);
rpci->ops = NULL;
}
up(&inode->i_sem);
}
static inline void
rpc_inode_setowner(struct inode *inode, void *private)
{
RPC_I(inode)->private = private;
}
static struct inode *
rpc_alloc_inode(struct super_block *sb)
{
struct rpc_inode *rpci;
rpci = (struct rpc_inode *)kmem_cache_alloc(rpc_inode_cachep, SLAB_KERNEL);
if (!rpci)
return NULL;
return &rpci->vfs_inode;
}
static void
rpc_destroy_inode(struct inode *inode)
{
kmem_cache_free(rpc_inode_cachep, RPC_I(inode));
}
static int
rpc_pipe_open(struct inode *inode, struct file *filp)
{
struct rpc_inode *rpci = RPC_I(inode);
int res = -ENXIO;
down(&inode->i_sem);
if (rpci->ops != NULL) {
if (filp->f_mode & FMODE_READ)
rpci->nreaders ++;
if (filp->f_mode & FMODE_WRITE)
rpci->nwriters ++;
res = 0;
}
up(&inode->i_sem);
return res;
}
static int
rpc_pipe_release(struct inode *inode, struct file *filp)
{
struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
struct rpc_pipe_msg *msg;
down(&inode->i_sem);
if (rpci->ops == NULL)
goto out;
msg = (struct rpc_pipe_msg *)filp->private_data;
if (msg != NULL) {
msg->errno = -EPIPE;
list_del_init(&msg->list);
rpci->ops->destroy_msg(msg);
}
if (filp->f_mode & FMODE_WRITE)
rpci->nwriters --;
if (filp->f_mode & FMODE_READ)
rpci->nreaders --;
if (!rpci->nreaders)
__rpc_purge_upcall(inode, -EPIPE);
if (rpci->ops->release_pipe)
rpci->ops->release_pipe(inode);
out:
up(&inode->i_sem);
return 0;
}
static ssize_t
rpc_pipe_read(struct file *filp, char __user *buf, size_t len, loff_t *offset)
{
struct inode *inode = filp->f_dentry->d_inode;
struct rpc_inode *rpci = RPC_I(inode);
struct rpc_pipe_msg *msg;
int res = 0;
down(&inode->i_sem);
if (rpci->ops == NULL) {
res = -EPIPE;
goto out_unlock;
}
msg = filp->private_data;
if (msg == NULL) {
if (!list_empty(&rpci->pipe)) {
msg = list_entry(rpci->pipe.next,
struct rpc_pipe_msg,
list);
list_move(&msg->list, &rpci->in_upcall);
rpci->pipelen -= msg->len;
filp->private_data = msg;
msg->copied = 0;
}
if (msg == NULL)
goto out_unlock;
}
/* NOTE: it is up to the callback to update msg->copied */
res = rpci->ops->upcall(filp, msg, buf, len);
if (res < 0 || msg->len == msg->copied) {
filp->private_data = NULL;
list_del_init(&msg->list);
rpci->ops->destroy_msg(msg);
}
out_unlock:
up(&inode->i_sem);
return res;
}
static ssize_t
rpc_pipe_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset)
{
struct inode *inode = filp->f_dentry->d_inode;
struct rpc_inode *rpci = RPC_I(inode);
int res;
down(&inode->i_sem);
res = -EPIPE;
if (rpci->ops != NULL)
res = rpci->ops->downcall(filp, buf, len);
up(&inode->i_sem);
return res;
}
static unsigned int
rpc_pipe_poll(struct file *filp, struct poll_table_struct *wait)
{
struct rpc_inode *rpci;
unsigned int mask = 0;
rpci = RPC_I(filp->f_dentry->d_inode);
poll_wait(filp, &rpci->waitq, wait);
mask = POLLOUT | POLLWRNORM;
if (rpci->ops == NULL)
mask |= POLLERR | POLLHUP;
if (!list_empty(&rpci->pipe))
mask |= POLLIN | POLLRDNORM;
return mask;
}
static int
rpc_pipe_ioctl(struct inode *ino, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
int len;
switch (cmd) {
case FIONREAD:
if (rpci->ops == NULL)
return -EPIPE;
len = rpci->pipelen;
if (filp->private_data) {
struct rpc_pipe_msg *msg;
msg = (struct rpc_pipe_msg *)filp->private_data;
len += msg->len - msg->copied;
}
return put_user(len, (int __user *)arg);
default:
return -EINVAL;
}
}
struct inode_operations rpc_pipe_iops = {
.lookup = simple_lookup,
};
struct file_operations rpc_pipe_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rpc_pipe_read,
.write = rpc_pipe_write,
.poll = rpc_pipe_poll,
.ioctl = rpc_pipe_ioctl,
.open = rpc_pipe_open,
.release = rpc_pipe_release,
};
static int
rpc_show_info(struct seq_file *m, void *v)
{
struct rpc_clnt *clnt = m->private;
seq_printf(m, "RPC server: %s\n", clnt->cl_server);
seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname,
clnt->cl_prog, clnt->cl_vers);
seq_printf(m, "address: %u.%u.%u.%u\n",
NIPQUAD(clnt->cl_xprt->addr.sin_addr.s_addr));
seq_printf(m, "protocol: %s\n",
clnt->cl_xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
return 0;
}
static int
rpc_info_open(struct inode *inode, struct file *file)
{
struct rpc_clnt *clnt;
int ret = single_open(file, rpc_show_info, NULL);
if (!ret) {
struct seq_file *m = file->private_data;
down(&inode->i_sem);
clnt = RPC_I(inode)->private;
if (clnt) {
atomic_inc(&clnt->cl_users);
m->private = clnt;
} else {
single_release(inode, file);
ret = -EINVAL;
}
up(&inode->i_sem);
}
return ret;
}
static int
rpc_info_release(struct inode *inode, struct file *file)
{
struct seq_file *m = file->private_data;
struct rpc_clnt *clnt = (struct rpc_clnt *)m->private;
if (clnt)
rpc_release_client(clnt);
return single_release(inode, file);
}
static struct file_operations rpc_info_operations = {
.owner = THIS_MODULE,
.open = rpc_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = rpc_info_release,
};
/*
* We have a single directory with 1 node in it.
*/
enum {
RPCAUTH_Root = 1,
RPCAUTH_lockd,
RPCAUTH_mount,
RPCAUTH_nfs,
RPCAUTH_portmap,
RPCAUTH_statd,
RPCAUTH_RootEOF
};
/*
* Description of fs contents.
*/
struct rpc_filelist {
char *name;
struct file_operations *i_fop;
int mode;
};
static struct rpc_filelist files[] = {
[RPCAUTH_lockd] = {
.name = "lockd",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
[RPCAUTH_mount] = {
.name = "mount",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
[RPCAUTH_nfs] = {
.name = "nfs",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
[RPCAUTH_portmap] = {
.name = "portmap",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
[RPCAUTH_statd] = {
.name = "statd",
.mode = S_IFDIR | S_IRUGO | S_IXUGO,
},
};
enum {
RPCAUTH_info = 2,
RPCAUTH_EOF
};
static struct rpc_filelist authfiles[] = {
[RPCAUTH_info] = {
.name = "info",
.i_fop = &rpc_info_operations,
.mode = S_IFREG | S_IRUSR,
},
};
static int
rpc_get_mount(void)
{
return simple_pin_fs("rpc_pipefs", &rpc_mount, &rpc_mount_count);
}
static void
rpc_put_mount(void)
{
simple_release_fs(&rpc_mount, &rpc_mount_count);
}
static int
rpc_lookup_parent(char *path, struct nameidata *nd)
{
if (path[0] == '\0')
return -ENOENT;
if (rpc_get_mount()) {
printk(KERN_WARNING "%s: %s failed to mount "
"pseudofilesystem \n", __FILE__, __FUNCTION__);
return -ENODEV;
}
nd->mnt = mntget(rpc_mount);
nd->dentry = dget(rpc_mount->mnt_root);
nd->last_type = LAST_ROOT;
nd->flags = LOOKUP_PARENT;
nd->depth = 0;
if (path_walk(path, nd)) {
printk(KERN_WARNING "%s: %s failed to find path %s\n",
__FILE__, __FUNCTION__, path);
rpc_put_mount();
return -ENOENT;
}
return 0;
}
static void
rpc_release_path(struct nameidata *nd)
{
path_release(nd);
rpc_put_mount();
}
static struct inode *
rpc_get_inode(struct super_block *sb, int mode)
{
struct inode *inode = new_inode(sb);
if (!inode)
return NULL;
inode->i_mode = mode;
inode->i_uid = inode->i_gid = 0;
inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch(mode & S_IFMT) {
case S_IFDIR:
inode->i_fop = &simple_dir_operations;
inode->i_op = &simple_dir_inode_operations;
inode->i_nlink++;
default:
break;
}
return inode;
}
/*
* FIXME: This probably has races.
*/
static void
rpc_depopulate(struct dentry *parent)
{
struct inode *dir = parent->d_inode;
struct list_head *pos, *next;
struct dentry *dentry, *dvec[10];
int n = 0;
down(&dir->i_sem);
repeat:
spin_lock(&dcache_lock);
list_for_each_safe(pos, next, &parent->d_subdirs) {
dentry = list_entry(pos, struct dentry, d_child);
spin_lock(&dentry->d_lock);
if (!d_unhashed(dentry)) {
dget_locked(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
dvec[n++] = dentry;
if (n == ARRAY_SIZE(dvec))
break;
} else
spin_unlock(&dentry->d_lock);
}
spin_unlock(&dcache_lock);
if (n) {
do {
dentry = dvec[--n];
if (dentry->d_inode) {
rpc_close_pipes(dentry->d_inode);
rpc_inode_setowner(dentry->d_inode, NULL);
simple_unlink(dir, dentry);
}
dput(dentry);
} while (n);
goto repeat;
}
up(&dir->i_sem);
}
static int
rpc_populate(struct dentry *parent,
struct rpc_filelist *files,
int start, int eof)
{
struct inode *inode, *dir = parent->d_inode;
void *private = RPC_I(dir)->private;
struct dentry *dentry;
int mode, i;
down(&dir->i_sem);
for (i = start; i < eof; i++) {
dentry = d_alloc_name(parent, files[i].name);
if (!dentry)
goto out_bad;
mode = files[i].mode;
inode = rpc_get_inode(dir->i_sb, mode);
if (!inode) {
dput(dentry);
goto out_bad;
}
inode->i_ino = i;
if (files[i].i_fop)
inode->i_fop = files[i].i_fop;
if (private)
rpc_inode_setowner(inode, private);
if (S_ISDIR(mode))
dir->i_nlink++;
d_add(dentry, inode);
}
up(&dir->i_sem);
return 0;
out_bad:
up(&dir->i_sem);
printk(KERN_WARNING "%s: %s failed to populate directory %s\n",
__FILE__, __FUNCTION__, parent->d_name.name);
return -ENOMEM;
}
static int
__rpc_mkdir(struct inode *dir, struct dentry *dentry)
{
struct inode *inode;
inode = rpc_get_inode(dir->i_sb, S_IFDIR | S_IRUSR | S_IXUSR);
if (!inode)
goto out_err;
inode->i_ino = iunique(dir->i_sb, 100);
d_instantiate(dentry, inode);
dir->i_nlink++;
inode_dir_notify(dir, DN_CREATE);
rpc_get_mount();
return 0;
out_err:
printk(KERN_WARNING "%s: %s failed to allocate inode for dentry %s\n",
__FILE__, __FUNCTION__, dentry->d_name.name);
return -ENOMEM;
}
static int
__rpc_rmdir(struct inode *dir, struct dentry *dentry)
{
int error;
shrink_dcache_parent(dentry);
if (dentry->d_inode) {
rpc_close_pipes(dentry->d_inode);
rpc_inode_setowner(dentry->d_inode, NULL);
}
if ((error = simple_rmdir(dir, dentry)) != 0)
return error;
if (!error) {
inode_dir_notify(dir, DN_DELETE);
d_drop(dentry);
rpc_put_mount();
}
return 0;
}
struct dentry *
rpc_lookup_negative(char *path, struct nameidata *nd)
{
struct dentry *dentry;
struct inode *dir;
int error;
if ((error = rpc_lookup_parent(path, nd)) != 0)
return ERR_PTR(error);
dir = nd->dentry->d_inode;
down(&dir->i_sem);
dentry = lookup_hash(&nd->last, nd->dentry);
if (IS_ERR(dentry))
goto out_err;
if (dentry->d_inode) {
dput(dentry);
dentry = ERR_PTR(-EEXIST);
goto out_err;
}
return dentry;
out_err:
up(&dir->i_sem);
rpc_release_path(nd);
return dentry;
}
struct dentry *
rpc_mkdir(char *path, struct rpc_clnt *rpc_client)
{
struct nameidata nd;
struct dentry *dentry;
struct inode *dir;
int error;
dentry = rpc_lookup_negative(path, &nd);
if (IS_ERR(dentry))
return dentry;
dir = nd.dentry->d_inode;
if ((error = __rpc_mkdir(dir, dentry)) != 0)
goto err_dput;
RPC_I(dentry->d_inode)->private = rpc_client;
error = rpc_populate(dentry, authfiles,
RPCAUTH_info, RPCAUTH_EOF);
if (error)
goto err_depopulate;
out:
up(&dir->i_sem);
rpc_release_path(&nd);
return dentry;
err_depopulate:
rpc_depopulate(dentry);
__rpc_rmdir(dir, dentry);
err_dput:
dput(dentry);
printk(KERN_WARNING "%s: %s() failed to create directory %s (errno = %d)\n",
__FILE__, __FUNCTION__, path, error);
dentry = ERR_PTR(error);
goto out;
}
int
rpc_rmdir(char *path)
{
struct nameidata nd;
struct dentry *dentry;
struct inode *dir;
int error;
if ((error = rpc_lookup_parent(path, &nd)) != 0)
return error;
dir = nd.dentry->d_inode;
down(&dir->i_sem);
dentry = lookup_hash(&nd.last, nd.dentry);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out_release;
}
rpc_depopulate(dentry);
error = __rpc_rmdir(dir, dentry);
dput(dentry);
out_release:
up(&dir->i_sem);
rpc_release_path(&nd);
return error;
}
struct dentry *
rpc_mkpipe(char *path, void *private, struct rpc_pipe_ops *ops, int flags)
{
struct nameidata nd;
struct dentry *dentry;
struct inode *dir, *inode;
struct rpc_inode *rpci;
dentry = rpc_lookup_negative(path, &nd);
if (IS_ERR(dentry))
return dentry;
dir = nd.dentry->d_inode;
inode = rpc_get_inode(dir->i_sb, S_IFSOCK | S_IRUSR | S_IWUSR);
if (!inode)
goto err_dput;
inode->i_ino = iunique(dir->i_sb, 100);
inode->i_fop = &rpc_pipe_fops;
d_instantiate(dentry, inode);
rpci = RPC_I(inode);
rpci->private = private;
rpci->flags = flags;
rpci->ops = ops;
inode_dir_notify(dir, DN_CREATE);
out:
up(&dir->i_sem);
rpc_release_path(&nd);
return dentry;
err_dput:
dput(dentry);
dentry = ERR_PTR(-ENOMEM);
printk(KERN_WARNING "%s: %s() failed to create pipe %s (errno = %d)\n",
__FILE__, __FUNCTION__, path, -ENOMEM);
goto out;
}
int
rpc_unlink(char *path)
{
struct nameidata nd;
struct dentry *dentry;
struct inode *dir;
int error;
if ((error = rpc_lookup_parent(path, &nd)) != 0)
return error;
dir = nd.dentry->d_inode;
down(&dir->i_sem);
dentry = lookup_hash(&nd.last, nd.dentry);
if (IS_ERR(dentry)) {
error = PTR_ERR(dentry);
goto out_release;
}
d_drop(dentry);
if (dentry->d_inode) {
rpc_close_pipes(dentry->d_inode);
rpc_inode_setowner(dentry->d_inode, NULL);
error = simple_unlink(dir, dentry);
}
dput(dentry);
inode_dir_notify(dir, DN_DELETE);
out_release:
up(&dir->i_sem);
rpc_release_path(&nd);
return error;
}
/*
* populate the filesystem
*/
static struct super_operations s_ops = {
.alloc_inode = rpc_alloc_inode,
.destroy_inode = rpc_destroy_inode,
.statfs = simple_statfs,
};
#define RPCAUTH_GSSMAGIC 0x67596969
static int
rpc_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = RPCAUTH_GSSMAGIC;
sb->s_op = &s_ops;
inode = rpc_get_inode(sb, S_IFDIR | 0755);
if (!inode)
return -ENOMEM;
root = d_alloc_root(inode);
if (!root) {
iput(inode);
return -ENOMEM;
}
if (rpc_populate(root, files, RPCAUTH_Root + 1, RPCAUTH_RootEOF))
goto out;
sb->s_root = root;
return 0;
out:
d_genocide(root);
dput(root);
return -ENOMEM;
}
static struct super_block *
rpc_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data)
{
return get_sb_single(fs_type, flags, data, rpc_fill_super);
}
static struct file_system_type rpc_pipe_fs_type = {
.owner = THIS_MODULE,
.name = "rpc_pipefs",
.get_sb = rpc_get_sb,
.kill_sb = kill_litter_super,
};
static void
init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
{
struct rpc_inode *rpci = (struct rpc_inode *) foo;
if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
SLAB_CTOR_CONSTRUCTOR) {
inode_init_once(&rpci->vfs_inode);
rpci->private = NULL;
rpci->nreaders = 0;
rpci->nwriters = 0;
INIT_LIST_HEAD(&rpci->in_upcall);
INIT_LIST_HEAD(&rpci->pipe);
rpci->pipelen = 0;
init_waitqueue_head(&rpci->waitq);
INIT_WORK(&rpci->queue_timeout, rpc_timeout_upcall_queue, rpci);
rpci->ops = NULL;
}
}
int register_rpc_pipefs(void)
{
rpc_inode_cachep = kmem_cache_create("rpc_inode_cache",
sizeof(struct rpc_inode),
0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
init_once, NULL);
if (!rpc_inode_cachep)
return -ENOMEM;
register_filesystem(&rpc_pipe_fs_type);
return 0;
}
void unregister_rpc_pipefs(void)
{
if (kmem_cache_destroy(rpc_inode_cachep))
printk(KERN_WARNING "RPC: unable to free inode cache\n");
unregister_filesystem(&rpc_pipe_fs_type);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,175 @@
/*
* linux/net/sunrpc/stats.c
*
* procfs-based user access to generic RPC statistics. The stats files
* reside in /proc/net/rpc.
*
* The read routines assume that the buffer passed in is just big enough.
* If you implement an RPC service that has its own stats routine which
* appends the generic RPC stats, make sure you don't exceed the PAGE_SIZE
* limit.
*
* Copyright (C) 1995, 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svcsock.h>
#define RPCDBG_FACILITY RPCDBG_MISC
struct proc_dir_entry *proc_net_rpc = NULL;
/*
* Get RPC client stats
*/
static int rpc_proc_show(struct seq_file *seq, void *v) {
const struct rpc_stat *statp = seq->private;
const struct rpc_program *prog = statp->program;
int i, j;
seq_printf(seq,
"net %d %d %d %d\n",
statp->netcnt,
statp->netudpcnt,
statp->nettcpcnt,
statp->nettcpconn);
seq_printf(seq,
"rpc %d %d %d\n",
statp->rpccnt,
statp->rpcretrans,
statp->rpcauthrefresh);
for (i = 0; i < prog->nrvers; i++) {
const struct rpc_version *vers = prog->version[i];
if (!vers)
continue;
seq_printf(seq, "proc%d %d",
vers->number, vers->nrprocs);
for (j = 0; j < vers->nrprocs; j++)
seq_printf(seq, " %d",
vers->procs[j].p_count);
seq_putc(seq, '\n');
}
return 0;
}
static int rpc_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, rpc_proc_show, PDE(inode)->data);
}
static struct file_operations rpc_proc_fops = {
.owner = THIS_MODULE,
.open = rpc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*
* Get RPC server stats
*/
void svc_seq_show(struct seq_file *seq, const struct svc_stat *statp) {
const struct svc_program *prog = statp->program;
const struct svc_procedure *proc;
const struct svc_version *vers;
int i, j;
seq_printf(seq,
"net %d %d %d %d\n",
statp->netcnt,
statp->netudpcnt,
statp->nettcpcnt,
statp->nettcpconn);
seq_printf(seq,
"rpc %d %d %d %d %d\n",
statp->rpccnt,
statp->rpcbadfmt+statp->rpcbadauth+statp->rpcbadclnt,
statp->rpcbadfmt,
statp->rpcbadauth,
statp->rpcbadclnt);
for (i = 0; i < prog->pg_nvers; i++) {
if (!(vers = prog->pg_vers[i]) || !(proc = vers->vs_proc))
continue;
seq_printf(seq, "proc%d %d", i, vers->vs_nproc);
for (j = 0; j < vers->vs_nproc; j++, proc++)
seq_printf(seq, " %d", proc->pc_count);
seq_putc(seq, '\n');
}
}
/*
* Register/unregister RPC proc files
*/
static inline struct proc_dir_entry *
do_register(const char *name, void *data, struct file_operations *fops)
{
struct proc_dir_entry *ent;
rpc_proc_init();
dprintk("RPC: registering /proc/net/rpc/%s\n", name);
ent = create_proc_entry(name, 0, proc_net_rpc);
if (ent) {
ent->proc_fops = fops;
ent->data = data;
}
return ent;
}
struct proc_dir_entry *
rpc_proc_register(struct rpc_stat *statp)
{
return do_register(statp->program->name, statp, &rpc_proc_fops);
}
void
rpc_proc_unregister(const char *name)
{
remove_proc_entry(name, proc_net_rpc);
}
struct proc_dir_entry *
svc_proc_register(struct svc_stat *statp, struct file_operations *fops)
{
return do_register(statp->program->pg_name, statp, fops);
}
void
svc_proc_unregister(const char *name)
{
remove_proc_entry(name, proc_net_rpc);
}
void
rpc_proc_init(void)
{
dprintk("RPC: registering /proc/net/rpc\n");
if (!proc_net_rpc) {
struct proc_dir_entry *ent;
ent = proc_mkdir("rpc", proc_net);
if (ent) {
ent->owner = THIS_MODULE;
proc_net_rpc = ent;
}
}
}
void
rpc_proc_exit(void)
{
dprintk("RPC: unregistering /proc/net/rpc\n");
if (proc_net_rpc) {
proc_net_rpc = NULL;
remove_proc_entry("net/rpc", NULL);
}
}

View File

@@ -0,0 +1,185 @@
/*
* linux/net/sunrpc/sunrpc_syms.c
*
* Symbols exported by the sunrpc module.
*
* Copyright (C) 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sched.h>
#include <linux/uio.h>
#include <linux/unistd.h>
#include <linux/init.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/svc.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/auth.h>
#include <linux/workqueue.h>
#include <linux/sunrpc/rpc_pipe_fs.h>
/* RPC scheduler */
EXPORT_SYMBOL(rpc_execute);
EXPORT_SYMBOL(rpc_init_task);
EXPORT_SYMBOL(rpc_sleep_on);
EXPORT_SYMBOL(rpc_wake_up_next);
EXPORT_SYMBOL(rpc_wake_up_task);
EXPORT_SYMBOL(rpc_new_child);
EXPORT_SYMBOL(rpc_run_child);
EXPORT_SYMBOL(rpciod_down);
EXPORT_SYMBOL(rpciod_up);
EXPORT_SYMBOL(rpc_new_task);
EXPORT_SYMBOL(rpc_wake_up_status);
EXPORT_SYMBOL(rpc_release_task);
/* RPC client functions */
EXPORT_SYMBOL(rpc_create_client);
EXPORT_SYMBOL(rpc_clone_client);
EXPORT_SYMBOL(rpc_destroy_client);
EXPORT_SYMBOL(rpc_shutdown_client);
EXPORT_SYMBOL(rpc_release_client);
EXPORT_SYMBOL(rpc_killall_tasks);
EXPORT_SYMBOL(rpc_call_sync);
EXPORT_SYMBOL(rpc_call_async);
EXPORT_SYMBOL(rpc_call_setup);
EXPORT_SYMBOL(rpc_clnt_sigmask);
EXPORT_SYMBOL(rpc_clnt_sigunmask);
EXPORT_SYMBOL(rpc_delay);
EXPORT_SYMBOL(rpc_restart_call);
EXPORT_SYMBOL(rpc_setbufsize);
EXPORT_SYMBOL(rpc_unlink);
EXPORT_SYMBOL(rpc_wake_up);
EXPORT_SYMBOL(rpc_queue_upcall);
EXPORT_SYMBOL(rpc_mkpipe);
/* Client transport */
EXPORT_SYMBOL(xprt_create_proto);
EXPORT_SYMBOL(xprt_destroy);
EXPORT_SYMBOL(xprt_set_timeout);
EXPORT_SYMBOL(xprt_udp_slot_table_entries);
EXPORT_SYMBOL(xprt_tcp_slot_table_entries);
/* Client credential cache */
EXPORT_SYMBOL(rpcauth_register);
EXPORT_SYMBOL(rpcauth_unregister);
EXPORT_SYMBOL(rpcauth_create);
EXPORT_SYMBOL(rpcauth_lookupcred);
EXPORT_SYMBOL(rpcauth_lookup_credcache);
EXPORT_SYMBOL(rpcauth_free_credcache);
EXPORT_SYMBOL(rpcauth_init_credcache);
EXPORT_SYMBOL(put_rpccred);
/* RPC server stuff */
EXPORT_SYMBOL(svc_create);
EXPORT_SYMBOL(svc_create_thread);
EXPORT_SYMBOL(svc_exit_thread);
EXPORT_SYMBOL(svc_destroy);
EXPORT_SYMBOL(svc_drop);
EXPORT_SYMBOL(svc_process);
EXPORT_SYMBOL(svc_recv);
EXPORT_SYMBOL(svc_wake_up);
EXPORT_SYMBOL(svc_makesock);
EXPORT_SYMBOL(svc_reserve);
EXPORT_SYMBOL(svc_auth_register);
EXPORT_SYMBOL(auth_domain_lookup);
EXPORT_SYMBOL(svc_authenticate);
/* RPC statistics */
#ifdef CONFIG_PROC_FS
EXPORT_SYMBOL(rpc_proc_register);
EXPORT_SYMBOL(rpc_proc_unregister);
EXPORT_SYMBOL(svc_proc_register);
EXPORT_SYMBOL(svc_proc_unregister);
EXPORT_SYMBOL(svc_seq_show);
#endif
/* caching... */
EXPORT_SYMBOL(auth_domain_find);
EXPORT_SYMBOL(auth_domain_put);
EXPORT_SYMBOL(auth_unix_add_addr);
EXPORT_SYMBOL(auth_unix_forget_old);
EXPORT_SYMBOL(auth_unix_lookup);
EXPORT_SYMBOL(cache_check);
EXPORT_SYMBOL(cache_clean);
EXPORT_SYMBOL(cache_flush);
EXPORT_SYMBOL(cache_purge);
EXPORT_SYMBOL(cache_fresh);
EXPORT_SYMBOL(cache_init);
EXPORT_SYMBOL(cache_register);
EXPORT_SYMBOL(cache_unregister);
EXPORT_SYMBOL(qword_add);
EXPORT_SYMBOL(qword_addhex);
EXPORT_SYMBOL(qword_get);
EXPORT_SYMBOL(svcauth_unix_purge);
EXPORT_SYMBOL(unix_domain_find);
/* Generic XDR */
EXPORT_SYMBOL(xdr_encode_string);
EXPORT_SYMBOL(xdr_decode_string);
EXPORT_SYMBOL(xdr_decode_string_inplace);
EXPORT_SYMBOL(xdr_decode_netobj);
EXPORT_SYMBOL(xdr_encode_netobj);
EXPORT_SYMBOL(xdr_encode_pages);
EXPORT_SYMBOL(xdr_inline_pages);
EXPORT_SYMBOL(xdr_shift_buf);
EXPORT_SYMBOL(xdr_buf_from_iov);
EXPORT_SYMBOL(xdr_buf_subsegment);
EXPORT_SYMBOL(xdr_buf_read_netobj);
EXPORT_SYMBOL(read_bytes_from_xdr_buf);
/* Debugging symbols */
#ifdef RPC_DEBUG
EXPORT_SYMBOL(rpc_debug);
EXPORT_SYMBOL(nfs_debug);
EXPORT_SYMBOL(nfsd_debug);
EXPORT_SYMBOL(nlm_debug);
#endif
extern int register_rpc_pipefs(void);
extern void unregister_rpc_pipefs(void);
static int __init
init_sunrpc(void)
{
int err = register_rpc_pipefs();
if (err)
goto out;
err = rpc_init_mempool() != 0;
if (err)
goto out;
#ifdef RPC_DEBUG
rpc_register_sysctl();
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_init();
#endif
cache_register(&auth_domain_cache);
cache_register(&ip_map_cache);
out:
return err;
}
static void __exit
cleanup_sunrpc(void)
{
unregister_rpc_pipefs();
rpc_destroy_mempool();
cache_unregister(&auth_domain_cache);
cache_unregister(&ip_map_cache);
#ifdef RPC_DEBUG
rpc_unregister_sysctl();
#endif
#ifdef CONFIG_PROC_FS
rpc_proc_exit();
#endif
}
MODULE_LICENSE("GPL");
module_init(init_sunrpc);
module_exit(cleanup_sunrpc);

View File

@@ -0,0 +1,483 @@
/*
* linux/net/sunrpc/svc.c
*
* High-level RPC service routines
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/linkage.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/net.h>
#include <linux/in.h>
#include <linux/mm.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/clnt.h>
#define RPCDBG_FACILITY RPCDBG_SVCDSP
#define RPC_PARANOIA 1
/*
* Create an RPC service
*/
struct svc_serv *
svc_create(struct svc_program *prog, unsigned int bufsize)
{
struct svc_serv *serv;
int vers;
unsigned int xdrsize;
if (!(serv = (struct svc_serv *) kmalloc(sizeof(*serv), GFP_KERNEL)))
return NULL;
memset(serv, 0, sizeof(*serv));
serv->sv_program = prog;
serv->sv_nrthreads = 1;
serv->sv_stats = prog->pg_stats;
serv->sv_bufsz = bufsize? bufsize : 4096;
prog->pg_lovers = prog->pg_nvers-1;
xdrsize = 0;
for (vers=0; vers<prog->pg_nvers ; vers++)
if (prog->pg_vers[vers]) {
prog->pg_hivers = vers;
if (prog->pg_lovers > vers)
prog->pg_lovers = vers;
if (prog->pg_vers[vers]->vs_xdrsize > xdrsize)
xdrsize = prog->pg_vers[vers]->vs_xdrsize;
}
serv->sv_xdrsize = xdrsize;
INIT_LIST_HEAD(&serv->sv_threads);
INIT_LIST_HEAD(&serv->sv_sockets);
INIT_LIST_HEAD(&serv->sv_tempsocks);
INIT_LIST_HEAD(&serv->sv_permsocks);
spin_lock_init(&serv->sv_lock);
serv->sv_name = prog->pg_name;
/* Remove any stale portmap registrations */
svc_register(serv, 0, 0);
return serv;
}
/*
* Destroy an RPC service
*/
void
svc_destroy(struct svc_serv *serv)
{
struct svc_sock *svsk;
dprintk("RPC: svc_destroy(%s, %d)\n",
serv->sv_program->pg_name,
serv->sv_nrthreads);
if (serv->sv_nrthreads) {
if (--(serv->sv_nrthreads) != 0) {
svc_sock_update_bufs(serv);
return;
}
} else
printk("svc_destroy: no threads for serv=%p!\n", serv);
while (!list_empty(&serv->sv_tempsocks)) {
svsk = list_entry(serv->sv_tempsocks.next,
struct svc_sock,
sk_list);
svc_delete_socket(svsk);
}
while (!list_empty(&serv->sv_permsocks)) {
svsk = list_entry(serv->sv_permsocks.next,
struct svc_sock,
sk_list);
svc_delete_socket(svsk);
}
cache_clean_deferred(serv);
/* Unregister service with the portmapper */
svc_register(serv, 0, 0);
kfree(serv);
}
/*
* Allocate an RPC server's buffer space.
* We allocate pages and place them in rq_argpages.
*/
static int
svc_init_buffer(struct svc_rqst *rqstp, unsigned int size)
{
int pages;
int arghi;
if (size > RPCSVC_MAXPAYLOAD)
size = RPCSVC_MAXPAYLOAD;
pages = 2 + (size+ PAGE_SIZE -1) / PAGE_SIZE;
rqstp->rq_argused = 0;
rqstp->rq_resused = 0;
arghi = 0;
if (pages > RPCSVC_MAXPAGES)
BUG();
while (pages) {
struct page *p = alloc_page(GFP_KERNEL);
if (!p)
break;
rqstp->rq_argpages[arghi++] = p;
pages--;
}
rqstp->rq_arghi = arghi;
return ! pages;
}
/*
* Release an RPC server buffer
*/
static void
svc_release_buffer(struct svc_rqst *rqstp)
{
while (rqstp->rq_arghi)
put_page(rqstp->rq_argpages[--rqstp->rq_arghi]);
while (rqstp->rq_resused) {
if (rqstp->rq_respages[--rqstp->rq_resused] == NULL)
continue;
put_page(rqstp->rq_respages[rqstp->rq_resused]);
}
rqstp->rq_argused = 0;
}
/*
* Create a server thread
*/
int
svc_create_thread(svc_thread_fn func, struct svc_serv *serv)
{
struct svc_rqst *rqstp;
int error = -ENOMEM;
rqstp = kmalloc(sizeof(*rqstp), GFP_KERNEL);
if (!rqstp)
goto out;
memset(rqstp, 0, sizeof(*rqstp));
init_waitqueue_head(&rqstp->rq_wait);
if (!(rqstp->rq_argp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
|| !(rqstp->rq_resp = (u32 *) kmalloc(serv->sv_xdrsize, GFP_KERNEL))
|| !svc_init_buffer(rqstp, serv->sv_bufsz))
goto out_thread;
serv->sv_nrthreads++;
rqstp->rq_server = serv;
error = kernel_thread((int (*)(void *)) func, rqstp, 0);
if (error < 0)
goto out_thread;
svc_sock_update_bufs(serv);
error = 0;
out:
return error;
out_thread:
svc_exit_thread(rqstp);
goto out;
}
/*
* Destroy an RPC server thread
*/
void
svc_exit_thread(struct svc_rqst *rqstp)
{
struct svc_serv *serv = rqstp->rq_server;
svc_release_buffer(rqstp);
if (rqstp->rq_resp)
kfree(rqstp->rq_resp);
if (rqstp->rq_argp)
kfree(rqstp->rq_argp);
if (rqstp->rq_auth_data)
kfree(rqstp->rq_auth_data);
kfree(rqstp);
/* Release the server */
if (serv)
svc_destroy(serv);
}
/*
* Register an RPC service with the local portmapper.
* To unregister a service, call this routine with
* proto and port == 0.
*/
int
svc_register(struct svc_serv *serv, int proto, unsigned short port)
{
struct svc_program *progp;
unsigned long flags;
int i, error = 0, dummy;
progp = serv->sv_program;
dprintk("RPC: svc_register(%s, %s, %d)\n",
progp->pg_name, proto == IPPROTO_UDP? "udp" : "tcp", port);
if (!port)
clear_thread_flag(TIF_SIGPENDING);
for (i = 0; i < progp->pg_nvers; i++) {
if (progp->pg_vers[i] == NULL)
continue;
error = rpc_register(progp->pg_prog, i, proto, port, &dummy);
if (error < 0)
break;
if (port && !dummy) {
error = -EACCES;
break;
}
}
if (!port) {
spin_lock_irqsave(&current->sighand->siglock, flags);
recalc_sigpending();
spin_unlock_irqrestore(&current->sighand->siglock, flags);
}
return error;
}
/*
* Process the RPC request.
*/
int
svc_process(struct svc_serv *serv, struct svc_rqst *rqstp)
{
struct svc_program *progp;
struct svc_version *versp = NULL; /* compiler food */
struct svc_procedure *procp = NULL;
struct kvec * argv = &rqstp->rq_arg.head[0];
struct kvec * resv = &rqstp->rq_res.head[0];
kxdrproc_t xdr;
u32 *statp;
u32 dir, prog, vers, proc,
auth_stat, rpc_stat;
int auth_res;
rpc_stat = rpc_success;
if (argv->iov_len < 6*4)
goto err_short_len;
/* setup response xdr_buf.
* Initially it has just one page
*/
svc_take_page(rqstp); /* must succeed */
resv->iov_base = page_address(rqstp->rq_respages[0]);
resv->iov_len = 0;
rqstp->rq_res.pages = rqstp->rq_respages+1;
rqstp->rq_res.len = 0;
rqstp->rq_res.page_base = 0;
rqstp->rq_res.page_len = 0;
rqstp->rq_res.tail[0].iov_len = 0;
/* tcp needs a space for the record length... */
if (rqstp->rq_prot == IPPROTO_TCP)
svc_putu32(resv, 0);
rqstp->rq_xid = svc_getu32(argv);
svc_putu32(resv, rqstp->rq_xid);
dir = ntohl(svc_getu32(argv));
vers = ntohl(svc_getu32(argv));
/* First words of reply: */
svc_putu32(resv, xdr_one); /* REPLY */
if (dir != 0) /* direction != CALL */
goto err_bad_dir;
if (vers != 2) /* RPC version number */
goto err_bad_rpc;
svc_putu32(resv, xdr_zero); /* ACCEPT */
rqstp->rq_prog = prog = ntohl(svc_getu32(argv)); /* program number */
rqstp->rq_vers = vers = ntohl(svc_getu32(argv)); /* version number */
rqstp->rq_proc = proc = ntohl(svc_getu32(argv)); /* procedure number */
progp = serv->sv_program;
/*
* Decode auth data, and add verifier to reply buffer.
* We do this before anything else in order to get a decent
* auth verifier.
*/
if (progp->pg_authenticate != NULL)
auth_res = progp->pg_authenticate(rqstp, &auth_stat);
else
auth_res = svc_authenticate(rqstp, &auth_stat);
switch (auth_res) {
case SVC_OK:
break;
case SVC_GARBAGE:
rpc_stat = rpc_garbage_args;
goto err_bad;
case SVC_SYSERR:
rpc_stat = rpc_system_err;
goto err_bad;
case SVC_DENIED:
goto err_bad_auth;
case SVC_DROP:
goto dropit;
case SVC_COMPLETE:
goto sendit;
}
if (prog != progp->pg_prog)
goto err_bad_prog;
if (vers >= progp->pg_nvers ||
!(versp = progp->pg_vers[vers]))
goto err_bad_vers;
procp = versp->vs_proc + proc;
if (proc >= versp->vs_nproc || !procp->pc_func)
goto err_bad_proc;
rqstp->rq_server = serv;
rqstp->rq_procinfo = procp;
/* Syntactic check complete */
serv->sv_stats->rpccnt++;
/* Build the reply header. */
statp = resv->iov_base +resv->iov_len;
svc_putu32(resv, rpc_success); /* RPC_SUCCESS */
/* Bump per-procedure stats counter */
procp->pc_count++;
/* Initialize storage for argp and resp */
memset(rqstp->rq_argp, 0, procp->pc_argsize);
memset(rqstp->rq_resp, 0, procp->pc_ressize);
/* un-reserve some of the out-queue now that we have a
* better idea of reply size
*/
if (procp->pc_xdrressize)
svc_reserve(rqstp, procp->pc_xdrressize<<2);
/* Call the function that processes the request. */
if (!versp->vs_dispatch) {
/* Decode arguments */
xdr = procp->pc_decode;
if (xdr && !xdr(rqstp, argv->iov_base, rqstp->rq_argp))
goto err_garbage;
*statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
/* Encode reply */
if (*statp == rpc_success && (xdr = procp->pc_encode)
&& !xdr(rqstp, resv->iov_base+resv->iov_len, rqstp->rq_resp)) {
dprintk("svc: failed to encode reply\n");
/* serv->sv_stats->rpcsystemerr++; */
*statp = rpc_system_err;
}
} else {
dprintk("svc: calling dispatcher\n");
if (!versp->vs_dispatch(rqstp, statp)) {
/* Release reply info */
if (procp->pc_release)
procp->pc_release(rqstp, NULL, rqstp->rq_resp);
goto dropit;
}
}
/* Check RPC status result */
if (*statp != rpc_success)
resv->iov_len = ((void*)statp) - resv->iov_base + 4;
/* Release reply info */
if (procp->pc_release)
procp->pc_release(rqstp, NULL, rqstp->rq_resp);
if (procp->pc_encode == NULL)
goto dropit;
sendit:
if (svc_authorise(rqstp))
goto dropit;
return svc_send(rqstp);
dropit:
svc_authorise(rqstp); /* doesn't hurt to call this twice */
dprintk("svc: svc_process dropit\n");
svc_drop(rqstp);
return 0;
err_short_len:
#ifdef RPC_PARANOIA
printk("svc: short len %Zd, dropping request\n", argv->iov_len);
#endif
goto dropit; /* drop request */
err_bad_dir:
#ifdef RPC_PARANOIA
printk("svc: bad direction %d, dropping request\n", dir);
#endif
serv->sv_stats->rpcbadfmt++;
goto dropit; /* drop request */
err_bad_rpc:
serv->sv_stats->rpcbadfmt++;
svc_putu32(resv, xdr_one); /* REJECT */
svc_putu32(resv, xdr_zero); /* RPC_MISMATCH */
svc_putu32(resv, xdr_two); /* Only RPCv2 supported */
svc_putu32(resv, xdr_two);
goto sendit;
err_bad_auth:
dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat));
serv->sv_stats->rpcbadauth++;
resv->iov_len -= 4;
svc_putu32(resv, xdr_one); /* REJECT */
svc_putu32(resv, xdr_one); /* AUTH_ERROR */
svc_putu32(resv, auth_stat); /* status */
goto sendit;
err_bad_prog:
#ifdef RPC_PARANOIA
if (prog != 100227 || progp->pg_prog != 100003)
printk("svc: unknown program %d (me %d)\n", prog, progp->pg_prog);
/* else it is just a Solaris client seeing if ACLs are supported */
#endif
serv->sv_stats->rpcbadfmt++;
svc_putu32(resv, rpc_prog_unavail);
goto sendit;
err_bad_vers:
#ifdef RPC_PARANOIA
printk("svc: unknown version (%d)\n", vers);
#endif
serv->sv_stats->rpcbadfmt++;
svc_putu32(resv, rpc_prog_mismatch);
svc_putu32(resv, htonl(progp->pg_lovers));
svc_putu32(resv, htonl(progp->pg_hivers));
goto sendit;
err_bad_proc:
#ifdef RPC_PARANOIA
printk("svc: unknown procedure (%d)\n", proc);
#endif
serv->sv_stats->rpcbadfmt++;
svc_putu32(resv, rpc_proc_unavail);
goto sendit;
err_garbage:
#ifdef RPC_PARANOIA
printk("svc: failed to decode args\n");
#endif
rpc_stat = rpc_garbage_args;
err_bad:
serv->sv_stats->rpcbadfmt++;
svc_putu32(resv, rpc_stat);
goto sendit;
}

View File

@@ -0,0 +1,210 @@
/*
* linux/net/sunrpc/svcauth.c
*
* The generic interface for RPC authentication on the server side.
*
* Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
*
* CHANGES
* 19-Apr-2000 Chris Evans - Security fix
*/
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/err.h>
#include <linux/hash.h>
#define RPCDBG_FACILITY RPCDBG_AUTH
/*
* Table of authenticators
*/
extern struct auth_ops svcauth_null;
extern struct auth_ops svcauth_unix;
static spinlock_t authtab_lock = SPIN_LOCK_UNLOCKED;
static struct auth_ops *authtab[RPC_AUTH_MAXFLAVOR] = {
[0] = &svcauth_null,
[1] = &svcauth_unix,
};
int
svc_authenticate(struct svc_rqst *rqstp, u32 *authp)
{
rpc_authflavor_t flavor;
struct auth_ops *aops;
*authp = rpc_auth_ok;
flavor = ntohl(svc_getu32(&rqstp->rq_arg.head[0]));
dprintk("svc: svc_authenticate (%d)\n", flavor);
spin_lock(&authtab_lock);
if (flavor >= RPC_AUTH_MAXFLAVOR || !(aops = authtab[flavor])
|| !try_module_get(aops->owner)) {
spin_unlock(&authtab_lock);
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
spin_unlock(&authtab_lock);
rqstp->rq_authop = aops;
return aops->accept(rqstp, authp);
}
/* A request, which was authenticated, has now executed.
* Time to finalise the the credentials and verifier
* and release and resources
*/
int svc_authorise(struct svc_rqst *rqstp)
{
struct auth_ops *aops = rqstp->rq_authop;
int rv = 0;
rqstp->rq_authop = NULL;
if (aops) {
rv = aops->release(rqstp);
module_put(aops->owner);
}
return rv;
}
int
svc_auth_register(rpc_authflavor_t flavor, struct auth_ops *aops)
{
int rv = -EINVAL;
spin_lock(&authtab_lock);
if (flavor < RPC_AUTH_MAXFLAVOR && authtab[flavor] == NULL) {
authtab[flavor] = aops;
rv = 0;
}
spin_unlock(&authtab_lock);
return rv;
}
void
svc_auth_unregister(rpc_authflavor_t flavor)
{
spin_lock(&authtab_lock);
if (flavor < RPC_AUTH_MAXFLAVOR)
authtab[flavor] = NULL;
spin_unlock(&authtab_lock);
}
EXPORT_SYMBOL(svc_auth_unregister);
/**************************************************
* cache for domain name to auth_domain
* Entries are only added by flavours which will normally
* have a structure that 'inherits' from auth_domain.
* e.g. when an IP -> domainname is given to auth_unix,
* and the domain name doesn't exist, it will create a
* auth_unix_domain and add it to this hash table.
* If it finds the name does exist, but isn't AUTH_UNIX,
* it will complain.
*/
/*
* Auth auth_domain cache is somewhat different to other caches,
* largely because the entries are possibly of different types:
* each auth flavour has it's own type.
* One consequence of this that DefineCacheLookup cannot
* allocate a new structure as it cannot know the size.
* Notice that the "INIT" code fragment is quite different
* from other caches. When auth_domain_lookup might be
* creating a new domain, the new domain is passed in
* complete and it is used as-is rather than being copied into
* another structure.
*/
#define DN_HASHBITS 6
#define DN_HASHMAX (1<<DN_HASHBITS)
#define DN_HASHMASK (DN_HASHMAX-1)
static struct cache_head *auth_domain_table[DN_HASHMAX];
void auth_domain_drop(struct cache_head *item, struct cache_detail *cd)
{
struct auth_domain *dom = container_of(item, struct auth_domain, h);
if (cache_put(item,cd))
authtab[dom->flavour]->domain_release(dom);
}
struct cache_detail auth_domain_cache = {
.hash_size = DN_HASHMAX,
.hash_table = auth_domain_table,
.name = "auth.domain",
.cache_put = auth_domain_drop,
};
void auth_domain_put(struct auth_domain *dom)
{
auth_domain_drop(&dom->h, &auth_domain_cache);
}
static inline int auth_domain_hash(struct auth_domain *item)
{
return hash_str(item->name, DN_HASHBITS);
}
static inline int auth_domain_match(struct auth_domain *tmp, struct auth_domain *item)
{
return strcmp(tmp->name, item->name) == 0;
}
struct auth_domain *
auth_domain_lookup(struct auth_domain *item, int set)
{
struct auth_domain *tmp = NULL;
struct cache_head **hp, **head;
head = &auth_domain_cache.hash_table[auth_domain_hash(item)];
if (set)
write_lock(&auth_domain_cache.hash_lock);
else
read_lock(&auth_domain_cache.hash_lock);
for (hp=head; *hp != NULL; hp = &tmp->h.next) {
tmp = container_of(*hp, struct auth_domain, h);
if (!auth_domain_match(tmp, item))
continue;
cache_get(&tmp->h);
if (!set)
goto out_noset;
*hp = tmp->h.next;
tmp->h.next = NULL;
clear_bit(CACHE_HASHED, &tmp->h.flags);
auth_domain_drop(&tmp->h, &auth_domain_cache);
goto out_set;
}
/* Didn't find anything */
if (!set)
goto out_nada;
auth_domain_cache.entries++;
out_set:
set_bit(CACHE_HASHED, &item->h.flags);
item->h.next = *head;
*head = &item->h;
write_unlock(&auth_domain_cache.hash_lock);
cache_fresh(&auth_domain_cache, &item->h, item->h.expiry_time);
cache_get(&item->h);
return item;
out_nada:
tmp = NULL;
out_noset:
read_unlock(&auth_domain_cache.hash_lock);
return tmp;
}
struct auth_domain *auth_domain_find(char *name)
{
struct auth_domain *rv, ad;
ad.name = name;
rv = auth_domain_lookup(&ad, 0);
return rv;
}

View File

@@ -0,0 +1,215 @@
/*
* linux/net/sunrpc/svcauth_des.c
*
* Server-side AUTH_DES handling.
*
* Copyright (C) 1996, 1997 Olaf Kirch <okir@monad.swb.de>
*/
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/sunrpc/svcsock.h>
#define RPCDBG_FACILITY RPCDBG_AUTH
/*
* DES cedential cache.
* The cache is indexed by fullname/key to allow for multiple sessions
* by the same user from different hosts.
* It would be tempting to use the client's IP address rather than the
* conversation key as an index, but that could become problematic for
* multi-homed hosts that distribute traffic across their interfaces.
*/
struct des_cred {
struct des_cred * dc_next;
char * dc_fullname;
u32 dc_nickname;
des_cblock dc_key; /* conversation key */
des_cblock dc_xkey; /* encrypted conv. key */
des_key_schedule dc_keysched;
};
#define ADN_FULLNAME 0
#define ADN_NICKNAME 1
/*
* The default slack allowed when checking for replayed credentials
* (in milliseconds).
*/
#define DES_REPLAY_SLACK 2000
/*
* Make sure we don't place more than one call to the key server at
* a time.
*/
static int in_keycall;
#define FAIL(err) \
{ if (data) put_cred(data); \
*authp = rpc_autherr_##err; \
return; \
}
void
svcauth_des(struct svc_rqst *rqstp, u32 *statp, u32 *authp)
{
struct svc_buf *argp = &rqstp->rq_argbuf;
struct svc_buf *resp = &rqstp->rq_resbuf;
struct svc_cred *cred = &rqstp->rq_cred;
struct des_cred *data = NULL;
u32 cryptkey[2];
u32 cryptbuf[4];
u32 *p = argp->buf;
int len = argp->len, slen, i;
*authp = rpc_auth_ok;
if ((argp->len -= 3) < 0) {
*statp = rpc_garbage_args;
return;
}
p++; /* skip length field */
namekind = ntohl(*p++); /* fullname/nickname */
/* Get the credentials */
if (namekind == ADN_NICKNAME) {
/* If we can't find the cached session key, initiate a
* new session. */
if (!(data = get_cred_bynick(*p++)))
FAIL(rejectedcred);
} else if (namekind == ADN_FULLNAME) {
p = xdr_decode_string(p, &fullname, &len, RPC_MAXNETNAMELEN);
if (p == NULL)
FAIL(badcred);
cryptkey[0] = *p++; /* get the encrypted key */
cryptkey[1] = *p++;
cryptbuf[2] = *p++; /* get the encrypted window */
} else {
FAIL(badcred);
}
/* If we're just updating the key, silently discard the request. */
if (data && data->dc_locked) {
*authp = rpc_autherr_dropit;
_put_cred(data); /* release but don't unlock */
return;
}
/* Get the verifier flavor and length */
if (ntohl(*p++) != RPC_AUTH_DES && ntohl(*p++) != 12)
FAIL(badverf);
cryptbuf[0] = *p++; /* encrypted time stamp */
cryptbuf[1] = *p++;
cryptbuf[3] = *p++; /* 0 or window - 1 */
if (namekind == ADN_NICKNAME) {
status = des_ecb_encrypt((des_block *) cryptbuf,
(des_block *) cryptbuf,
data->dc_keysched, DES_DECRYPT);
} else {
/* We first have to decrypt the new session key and
* fill in the UNIX creds. */
if (!(data = get_cred_byname(rqstp, authp, fullname, cryptkey)))
return;
status = des_cbc_encrypt((des_cblock *) cryptbuf,
(des_cblock *) cryptbuf, 16,
data->dc_keysched,
(des_cblock *) &ivec,
DES_DECRYPT);
}
if (status) {
printk("svcauth_des: DES decryption failed (status %d)\n",
status);
FAIL(badverf);
}
/* Now check the whole lot */
if (namekind == ADN_FULLNAME) {
unsigned long winverf;
data->dc_window = ntohl(cryptbuf[2]);
winverf = ntohl(cryptbuf[2]);
if (window != winverf - 1) {
printk("svcauth_des: bad window verifier!\n");
FAIL(badverf);
}
}
/* XDR the decrypted timestamp */
cryptbuf[0] = ntohl(cryptbuf[0]);
cryptbuf[1] = ntohl(cryptbuf[1]);
if (cryptbuf[1] > 1000000) {
dprintk("svcauth_des: bad usec value %u\n", cryptbuf[1]);
if (namekind == ADN_NICKNAME)
FAIL(rejectedverf);
FAIL(badverf);
}
/*
* Check for replayed credentials. We must allow for reordering
* of requests by the network, and the OS scheduler, hence we
* cannot expect timestamps to be increasing monotonically.
* This opens a small security hole, therefore the replay_slack
* value shouldn't be too large.
*/
if ((delta = cryptbuf[0] - data->dc_timestamp[0]) <= 0) {
switch (delta) {
case -1:
delta = -1000000;
case 0:
delta += cryptbuf[1] - data->dc_timestamp[1];
break;
default:
delta = -1000000;
}
if (delta < DES_REPLAY_SLACK)
FAIL(rejectedverf);
#ifdef STRICT_REPLAY_CHECKS
/* TODO: compare time stamp to last five timestamps cached
* and reject (drop?) request if a match is found. */
#endif
}
now = xtime;
now.tv_secs -= data->dc_window;
if (now.tv_secs < cryptbuf[0] ||
(now.tv_secs == cryptbuf[0] && now.tv_usec < cryptbuf[1]))
FAIL(rejectedverf);
/* Okay, we're done. Update the lot */
if (namekind == ADN_FULLNAME)
data->dc_valid = 1;
data->dc_timestamp[0] = cryptbuf[0];
data->dc_timestamp[1] = cryptbuf[1];
put_cred(data);
return;
garbage:
*statp = rpc_garbage_args;
return;
}
/*
* Call the keyserver to obtain the decrypted conversation key and
* UNIX creds. We use a Linux-specific keycall extension that does
* both things in one go.
*/
static struct des_cred *
get_cred_byname(struct svc_rqst *rqstp, u32 *authp, char *fullname, u32 *cryptkey)
{
static int in_keycall;
struct des_cred *cred;
if (in_keycall) {
*authp = rpc_autherr_dropit;
return NULL;
}
in_keycall = 1;
in_keycall = 0;
return cred;
}

View File

@@ -0,0 +1,524 @@
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
#include <linux/sunrpc/svcsock.h>
#include <linux/sunrpc/svcauth.h>
#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/hash.h>
#define RPCDBG_FACILITY RPCDBG_AUTH
/*
* AUTHUNIX and AUTHNULL credentials are both handled here.
* AUTHNULL is treated just like AUTHUNIX except that the uid/gid
* are always nobody (-2). i.e. we do the same IP address checks for
* AUTHNULL as for AUTHUNIX, and that is done here.
*/
static char *strdup(char *s)
{
char *rv = kmalloc(strlen(s)+1, GFP_KERNEL);
if (rv)
strcpy(rv, s);
return rv;
}
struct unix_domain {
struct auth_domain h;
int addr_changes;
/* other stuff later */
};
struct auth_domain *unix_domain_find(char *name)
{
struct auth_domain *rv, ud;
struct unix_domain *new;
ud.name = name;
rv = auth_domain_lookup(&ud, 0);
foundit:
if (rv && rv->flavour != RPC_AUTH_UNIX) {
auth_domain_put(rv);
return NULL;
}
if (rv)
return rv;
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (new == NULL)
return NULL;
cache_init(&new->h.h);
new->h.name = strdup(name);
new->h.flavour = RPC_AUTH_UNIX;
new->addr_changes = 0;
new->h.h.expiry_time = NEVER;
rv = auth_domain_lookup(&new->h, 2);
if (rv == &new->h) {
if (atomic_dec_and_test(&new->h.h.refcnt)) BUG();
} else {
auth_domain_put(&new->h);
goto foundit;
}
return rv;
}
static void svcauth_unix_domain_release(struct auth_domain *dom)
{
struct unix_domain *ud = container_of(dom, struct unix_domain, h);
kfree(dom->name);
kfree(ud);
}
/**************************************************
* cache for IP address to unix_domain
* as needed by AUTH_UNIX
*/
#define IP_HASHBITS 8
#define IP_HASHMAX (1<<IP_HASHBITS)
#define IP_HASHMASK (IP_HASHMAX-1)
struct ip_map {
struct cache_head h;
char m_class[8]; /* e.g. "nfsd" */
struct in_addr m_addr;
struct unix_domain *m_client;
int m_add_change;
};
static struct cache_head *ip_table[IP_HASHMAX];
void ip_map_put(struct cache_head *item, struct cache_detail *cd)
{
struct ip_map *im = container_of(item, struct ip_map,h);
if (cache_put(item, cd)) {
if (test_bit(CACHE_VALID, &item->flags) &&
!test_bit(CACHE_NEGATIVE, &item->flags))
auth_domain_put(&im->m_client->h);
kfree(im);
}
}
static inline int ip_map_hash(struct ip_map *item)
{
return hash_str(item->m_class, IP_HASHBITS) ^
hash_long((unsigned long)item->m_addr.s_addr, IP_HASHBITS);
}
static inline int ip_map_match(struct ip_map *item, struct ip_map *tmp)
{
return strcmp(tmp->m_class, item->m_class) == 0
&& tmp->m_addr.s_addr == item->m_addr.s_addr;
}
static inline void ip_map_init(struct ip_map *new, struct ip_map *item)
{
strcpy(new->m_class, item->m_class);
new->m_addr.s_addr = item->m_addr.s_addr;
}
static inline void ip_map_update(struct ip_map *new, struct ip_map *item)
{
cache_get(&item->m_client->h.h);
new->m_client = item->m_client;
new->m_add_change = item->m_add_change;
}
static void ip_map_request(struct cache_detail *cd,
struct cache_head *h,
char **bpp, int *blen)
{
char text_addr[20];
struct ip_map *im = container_of(h, struct ip_map, h);
__u32 addr = im->m_addr.s_addr;
snprintf(text_addr, 20, "%u.%u.%u.%u",
ntohl(addr) >> 24 & 0xff,
ntohl(addr) >> 16 & 0xff,
ntohl(addr) >> 8 & 0xff,
ntohl(addr) >> 0 & 0xff);
qword_add(bpp, blen, im->m_class);
qword_add(bpp, blen, text_addr);
(*bpp)[-1] = '\n';
}
static struct ip_map *ip_map_lookup(struct ip_map *, int);
static int ip_map_parse(struct cache_detail *cd,
char *mesg, int mlen)
{
/* class ipaddress [domainname] */
/* should be safe just to use the start of the input buffer
* for scratch: */
char *buf = mesg;
int len;
int b1,b2,b3,b4;
char c;
struct ip_map ipm, *ipmp;
struct auth_domain *dom;
time_t expiry;
if (mesg[mlen-1] != '\n')
return -EINVAL;
mesg[mlen-1] = 0;
/* class */
len = qword_get(&mesg, ipm.m_class, sizeof(ipm.m_class));
if (len <= 0) return -EINVAL;
/* ip address */
len = qword_get(&mesg, buf, mlen);
if (len <= 0) return -EINVAL;
if (sscanf(buf, "%u.%u.%u.%u%c", &b1, &b2, &b3, &b4, &c) != 4)
return -EINVAL;
expiry = get_expiry(&mesg);
if (expiry ==0)
return -EINVAL;
/* domainname, or empty for NEGATIVE */
len = qword_get(&mesg, buf, mlen);
if (len < 0) return -EINVAL;
if (len) {
dom = unix_domain_find(buf);
if (dom == NULL)
return -ENOENT;
} else
dom = NULL;
ipm.m_addr.s_addr =
htonl((((((b1<<8)|b2)<<8)|b3)<<8)|b4);
ipm.h.flags = 0;
if (dom) {
ipm.m_client = container_of(dom, struct unix_domain, h);
ipm.m_add_change = ipm.m_client->addr_changes;
} else
set_bit(CACHE_NEGATIVE, &ipm.h.flags);
ipm.h.expiry_time = expiry;
ipmp = ip_map_lookup(&ipm, 1);
if (ipmp)
ip_map_put(&ipmp->h, &ip_map_cache);
if (dom)
auth_domain_put(dom);
if (!ipmp)
return -ENOMEM;
cache_flush();
return 0;
}
static int ip_map_show(struct seq_file *m,
struct cache_detail *cd,
struct cache_head *h)
{
struct ip_map *im;
struct in_addr addr;
char *dom = "-no-domain-";
if (h == NULL) {
seq_puts(m, "#class IP domain\n");
return 0;
}
im = container_of(h, struct ip_map, h);
/* class addr domain */
addr = im->m_addr;
if (test_bit(CACHE_VALID, &h->flags) &&
!test_bit(CACHE_NEGATIVE, &h->flags))
dom = im->m_client->h.name;
seq_printf(m, "%s %d.%d.%d.%d %s\n",
im->m_class,
htonl(addr.s_addr) >> 24 & 0xff,
htonl(addr.s_addr) >> 16 & 0xff,
htonl(addr.s_addr) >> 8 & 0xff,
htonl(addr.s_addr) >> 0 & 0xff,
dom
);
return 0;
}
struct cache_detail ip_map_cache = {
.hash_size = IP_HASHMAX,
.hash_table = ip_table,
.name = "auth.unix.ip",
.cache_put = ip_map_put,
.cache_request = ip_map_request,
.cache_parse = ip_map_parse,
.cache_show = ip_map_show,
};
static DefineSimpleCacheLookup(ip_map, 0)
int auth_unix_add_addr(struct in_addr addr, struct auth_domain *dom)
{
struct unix_domain *udom;
struct ip_map ip, *ipmp;
if (dom->flavour != RPC_AUTH_UNIX)
return -EINVAL;
udom = container_of(dom, struct unix_domain, h);
strcpy(ip.m_class, "nfsd");
ip.m_addr = addr;
ip.m_client = udom;
ip.m_add_change = udom->addr_changes+1;
ip.h.flags = 0;
ip.h.expiry_time = NEVER;
ipmp = ip_map_lookup(&ip, 1);
if (ipmp) {
ip_map_put(&ipmp->h, &ip_map_cache);
return 0;
} else
return -ENOMEM;
}
int auth_unix_forget_old(struct auth_domain *dom)
{
struct unix_domain *udom;
if (dom->flavour != RPC_AUTH_UNIX)
return -EINVAL;
udom = container_of(dom, struct unix_domain, h);
udom->addr_changes++;
return 0;
}
struct auth_domain *auth_unix_lookup(struct in_addr addr)
{
struct ip_map key, *ipm;
struct auth_domain *rv;
strcpy(key.m_class, "nfsd");
key.m_addr = addr;
ipm = ip_map_lookup(&key, 0);
if (!ipm)
return NULL;
if (cache_check(&ip_map_cache, &ipm->h, NULL))
return NULL;
if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) {
if (test_and_set_bit(CACHE_NEGATIVE, &ipm->h.flags) == 0)
auth_domain_put(&ipm->m_client->h);
rv = NULL;
} else {
rv = &ipm->m_client->h;
cache_get(&rv->h);
}
ip_map_put(&ipm->h, &ip_map_cache);
return rv;
}
void svcauth_unix_purge(void)
{
cache_purge(&ip_map_cache);
cache_purge(&auth_domain_cache);
}
static int
svcauth_null_accept(struct svc_rqst *rqstp, u32 *authp)
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
int rv=0;
struct ip_map key, *ipm;
if (argv->iov_len < 3*4)
return SVC_GARBAGE;
if (svc_getu32(argv) != 0) {
dprintk("svc: bad null cred\n");
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
dprintk("svc: bad null verf\n");
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
/* Signal that mapping to nobody uid/gid is required */
rqstp->rq_cred.cr_uid = (uid_t) -1;
rqstp->rq_cred.cr_gid = (gid_t) -1;
rqstp->rq_cred.cr_group_info = groups_alloc(0);
if (rqstp->rq_cred.cr_group_info == NULL)
return SVC_DROP; /* kmalloc failure - client must retry */
/* Put NULL verifier */
svc_putu32(resv, RPC_AUTH_NULL);
svc_putu32(resv, 0);
strcpy(key.m_class, rqstp->rq_server->sv_program->pg_class);
key.m_addr = rqstp->rq_addr.sin_addr;
ipm = ip_map_lookup(&key, 0);
rqstp->rq_client = NULL;
if (ipm)
switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
case -EAGAIN:
rv = SVC_DROP;
break;
case -ENOENT:
rv = SVC_OK; /* rq_client is NULL */
break;
case 0:
rqstp->rq_client = &ipm->m_client->h;
cache_get(&rqstp->rq_client->h);
ip_map_put(&ipm->h, &ip_map_cache);
rv = SVC_OK;
break;
default: BUG();
}
else rv = SVC_DROP;
if (rqstp->rq_client == NULL && rqstp->rq_proc != 0)
*authp = rpc_autherr_badcred;
return rv;
}
static int
svcauth_null_release(struct svc_rqst *rqstp)
{
if (rqstp->rq_client)
auth_domain_put(rqstp->rq_client);
rqstp->rq_client = NULL;
if (rqstp->rq_cred.cr_group_info)
put_group_info(rqstp->rq_cred.cr_group_info);
rqstp->rq_cred.cr_group_info = NULL;
return 0; /* don't drop */
}
struct auth_ops svcauth_null = {
.name = "null",
.owner = THIS_MODULE,
.flavour = RPC_AUTH_NULL,
.accept = svcauth_null_accept,
.release = svcauth_null_release,
};
int
svcauth_unix_accept(struct svc_rqst *rqstp, u32 *authp)
{
struct kvec *argv = &rqstp->rq_arg.head[0];
struct kvec *resv = &rqstp->rq_res.head[0];
struct svc_cred *cred = &rqstp->rq_cred;
u32 slen, i;
int len = argv->iov_len;
int rv=0;
struct ip_map key, *ipm;
cred->cr_group_info = NULL;
rqstp->rq_client = NULL;
if ((len -= 3*4) < 0)
return SVC_GARBAGE;
svc_getu32(argv); /* length */
svc_getu32(argv); /* time stamp */
slen = XDR_QUADLEN(ntohl(svc_getu32(argv))); /* machname length */
if (slen > 64 || (len -= (slen + 3)*4) < 0)
goto badcred;
argv->iov_base = (void*)((u32*)argv->iov_base + slen); /* skip machname */
argv->iov_len -= slen*4;
cred->cr_uid = ntohl(svc_getu32(argv)); /* uid */
cred->cr_gid = ntohl(svc_getu32(argv)); /* gid */
slen = ntohl(svc_getu32(argv)); /* gids length */
if (slen > 16 || (len -= (slen + 2)*4) < 0)
goto badcred;
cred->cr_group_info = groups_alloc(slen);
if (cred->cr_group_info == NULL)
return SVC_DROP;
for (i = 0; i < slen; i++)
GROUP_AT(cred->cr_group_info, i) = ntohl(svc_getu32(argv));
if (svc_getu32(argv) != RPC_AUTH_NULL || svc_getu32(argv) != 0) {
*authp = rpc_autherr_badverf;
return SVC_DENIED;
}
strcpy(key.m_class, rqstp->rq_server->sv_program->pg_class);
key.m_addr = rqstp->rq_addr.sin_addr;
ipm = ip_map_lookup(&key, 0);
if (ipm)
switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
case -EAGAIN:
rv = SVC_DROP;
break;
case -ENOENT:
rv = SVC_OK; /* rq_client is NULL */
break;
case 0:
rqstp->rq_client = &ipm->m_client->h;
cache_get(&rqstp->rq_client->h);
ip_map_put(&ipm->h, &ip_map_cache);
rv = SVC_OK;
break;
default: BUG();
}
else rv = SVC_DROP;
if (rv == SVC_OK && rqstp->rq_client == NULL && rqstp->rq_proc != 0)
goto badcred;
/* Put NULL verifier */
svc_putu32(resv, RPC_AUTH_NULL);
svc_putu32(resv, 0);
return rv;
badcred:
*authp = rpc_autherr_badcred;
return SVC_DENIED;
}
int
svcauth_unix_release(struct svc_rqst *rqstp)
{
/* Verifier (such as it is) is already in place.
*/
if (rqstp->rq_client)
auth_domain_put(rqstp->rq_client);
rqstp->rq_client = NULL;
if (rqstp->rq_cred.cr_group_info)
put_group_info(rqstp->rq_cred.cr_group_info);
rqstp->rq_cred.cr_group_info = NULL;
return 0;
}
struct auth_ops svcauth_unix = {
.name = "unix",
.owner = THIS_MODULE,
.flavour = RPC_AUTH_UNIX,
.accept = svcauth_unix_accept,
.release = svcauth_unix_release,
.domain_release = svcauth_unix_domain_release,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,193 @@
/*
* linux/net/sunrpc/sysctl.c
*
* Sysctl interface to sunrpc module.
*
* I would prefer to register the sunrpc table below sys/net, but that's
* impossible at the moment.
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/linkage.h>
#include <linux/ctype.h>
#include <linux/fs.h>
#include <linux/sysctl.h>
#include <linux/module.h>
#include <asm/uaccess.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/stats.h>
#include <linux/sunrpc/xprt.h>
/*
* Declare the debug flags here
*/
unsigned int rpc_debug;
unsigned int nfs_debug;
unsigned int nfsd_debug;
unsigned int nlm_debug;
#ifdef RPC_DEBUG
static struct ctl_table_header *sunrpc_table_header;
static ctl_table sunrpc_table[];
void
rpc_register_sysctl(void)
{
if (!sunrpc_table_header) {
sunrpc_table_header = register_sysctl_table(sunrpc_table, 1);
#ifdef CONFIG_PROC_FS
if (sunrpc_table[0].de)
sunrpc_table[0].de->owner = THIS_MODULE;
#endif
}
}
void
rpc_unregister_sysctl(void)
{
if (sunrpc_table_header) {
unregister_sysctl_table(sunrpc_table_header);
sunrpc_table_header = NULL;
}
}
static int
proc_dodebug(ctl_table *table, int write, struct file *file,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
char tmpbuf[20], c, *s;
char __user *p;
unsigned int value;
size_t left, len;
if ((*ppos && !write) || !*lenp) {
*lenp = 0;
return 0;
}
left = *lenp;
if (write) {
if (!access_ok(VERIFY_READ, buffer, left))
return -EFAULT;
p = buffer;
while (left && __get_user(c, p) >= 0 && isspace(c))
left--, p++;
if (!left)
goto done;
if (left > sizeof(tmpbuf) - 1)
return -EINVAL;
if (copy_from_user(tmpbuf, p, left))
return -EFAULT;
tmpbuf[left] = '\0';
for (s = tmpbuf, value = 0; '0' <= *s && *s <= '9'; s++, left--)
value = 10 * value + (*s - '0');
if (*s && !isspace(*s))
return -EINVAL;
while (left && isspace(*s))
left--, s++;
*(unsigned int *) table->data = value;
/* Display the RPC tasks on writing to rpc_debug */
if (table->ctl_name == CTL_RPCDEBUG) {
rpc_show_tasks();
}
} else {
if (!access_ok(VERIFY_WRITE, buffer, left))
return -EFAULT;
len = sprintf(tmpbuf, "%d", *(unsigned int *) table->data);
if (len > left)
len = left;
if (__copy_to_user(buffer, tmpbuf, len))
return -EFAULT;
if ((left -= len) > 0) {
if (put_user('\n', (char __user *)buffer + len))
return -EFAULT;
left--;
}
}
done:
*lenp -= left;
*ppos += *lenp;
return 0;
}
static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
static ctl_table debug_table[] = {
{
.ctl_name = CTL_RPCDEBUG,
.procname = "rpc_debug",
.data = &rpc_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_NFSDEBUG,
.procname = "nfs_debug",
.data = &nfs_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_NFSDDEBUG,
.procname = "nfsd_debug",
.data = &nfsd_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_NLMDEBUG,
.procname = "nlm_debug",
.data = &nlm_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_SLOTTABLE_UDP,
.procname = "udp_slot_table_entries",
.data = &xprt_udp_slot_table_entries,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_slot_table_size,
.extra2 = &max_slot_table_size
},
{
.ctl_name = CTL_SLOTTABLE_TCP,
.procname = "tcp_slot_table_entries",
.data = &xprt_tcp_slot_table_entries,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_slot_table_size,
.extra2 = &max_slot_table_size
},
{ .ctl_name = 0 }
};
static ctl_table sunrpc_table[] = {
{
.ctl_name = CTL_SUNRPC,
.procname = "sunrpc",
.mode = 0555,
.child = debug_table
},
{ .ctl_name = 0 }
};
#endif

View File

@@ -0,0 +1,107 @@
/*
* linux/net/sunrpc/timer.c
*
* Estimate RPC request round trip time.
*
* Based on packet round-trip and variance estimator algorithms described
* in appendix A of "Congestion Avoidance and Control" by Van Jacobson
* and Michael J. Karels (ACM Computer Communication Review; Proceedings
* of the Sigcomm '88 Symposium in Stanford, CA, August, 1988).
*
* This RTT estimator is used only for RPC over datagram protocols.
*
* Copyright (C) 2002 Trond Myklebust <trond.myklebust@fys.uio.no>
*/
#include <asm/param.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/sunrpc/clnt.h>
#include <linux/sunrpc/xprt.h>
#include <linux/sunrpc/timer.h>
#define RPC_RTO_MAX (60*HZ)
#define RPC_RTO_INIT (HZ/5)
#define RPC_RTO_MIN (HZ/10)
void
rpc_init_rtt(struct rpc_rtt *rt, unsigned long timeo)
{
unsigned long init = 0;
unsigned i;
rt->timeo = timeo;
if (timeo > RPC_RTO_INIT)
init = (timeo - RPC_RTO_INIT) << 3;
for (i = 0; i < 5; i++) {
rt->srtt[i] = init;
rt->sdrtt[i] = RPC_RTO_INIT;
rt->ntimeouts[i] = 0;
}
}
/*
* NB: When computing the smoothed RTT and standard deviation,
* be careful not to produce negative intermediate results.
*/
void
rpc_update_rtt(struct rpc_rtt *rt, unsigned timer, long m)
{
long *srtt, *sdrtt;
if (timer-- == 0)
return;
/* jiffies wrapped; ignore this one */
if (m < 0)
return;
if (m == 0)
m = 1L;
srtt = (long *)&rt->srtt[timer];
m -= *srtt >> 3;
*srtt += m;
if (m < 0)
m = -m;
sdrtt = (long *)&rt->sdrtt[timer];
m -= *sdrtt >> 2;
*sdrtt += m;
/* Set lower bound on the variance */
if (*sdrtt < RPC_RTO_MIN)
*sdrtt = RPC_RTO_MIN;
}
/*
* Estimate rto for an nfs rpc sent via. an unreliable datagram.
* Use the mean and mean deviation of rtt for the appropriate type of rpc
* for the frequent rpcs and a default for the others.
* The justification for doing "other" this way is that these rpcs
* happen so infrequently that timer est. would probably be stale.
* Also, since many of these rpcs are
* non-idempotent, a conservative timeout is desired.
* getattr, lookup,
* read, write, commit - A+4D
* other - timeo
*/
unsigned long
rpc_calc_rto(struct rpc_rtt *rt, unsigned timer)
{
unsigned long res;
if (timer-- == 0)
return rt->timeo;
res = ((rt->srtt[timer] + 7) >> 3) + rt->sdrtt[timer];
if (res > RPC_RTO_MAX)
res = RPC_RTO_MAX;
return res;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff