📄 auth_gss.c
字号:
/* * linux/net/sunrpc/auth_gss.c * * RPCSEC_GSS client authentication. * * Copyright (c) 2000 The Regents of the University of Michigan. * All rights reserved. * * Dug Song <dugsong@monkey.org> * 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. * * $Id$ */#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/pagemap.h>#include <linux/sunrpc/clnt.h>#include <linux/sunrpc/auth.h>#include <linux/sunrpc/auth_gss.h>#include <linux/sunrpc/svcauth_gss.h>#include <linux/sunrpc/gss_err.h>#include <linux/workqueue.h>#include <linux/sunrpc/rpc_pipe_fs.h>#include <linux/sunrpc/gss_api.h>#include <asm/uaccess.h>static struct rpc_authops authgss_ops;static struct rpc_credops gss_credops;#ifdef RPC_DEBUG# define RPCDBG_FACILITY RPCDBG_AUTH#endif#define NFS_NGROUPS 16#define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */#define GSS_CRED_SLACK 1024 /* XXX: unused *//* length of a krb5 verifier (48), plus data added before arguments when * using integrity (two 4-byte integers): */#define GSS_VERF_SLACK 56/* XXX this define must match the gssd define* as it is passed to gssd to signal the use of* machine creds should be part of the shared rpc interface */#define CA_RUN_AS_MACHINE 0x00000200 /* dump the buffer in `emacs-hexl' style */#define isprint(c) ((c > 0x1f) && (c < 0x7f))static DEFINE_RWLOCK(gss_ctx_lock);struct gss_auth { struct rpc_auth rpc_auth; struct gss_api_mech *mech; enum rpc_gss_svc service; struct list_head upcalls; struct rpc_clnt *client; struct dentry *dentry; char path[48]; spinlock_t lock;};static void gss_destroy_ctx(struct gss_cl_ctx *);static struct rpc_pipe_ops gss_upcall_ops;voidprint_hexl(u32 *p, u_int length, u_int offset){ u_int i, j, jm; u8 c, *cp; dprintk("RPC: print_hexl: length %d\n",length); dprintk("\n"); cp = (u8 *) p; for (i = 0; i < length; i += 0x10) { dprintk(" %04x: ", (u_int)(i + offset)); jm = length - i; jm = jm > 16 ? 16 : jm; for (j = 0; j < jm; j++) { if ((j % 2) == 1) dprintk("%02x ", (u_int)cp[i+j]); else dprintk("%02x", (u_int)cp[i+j]); } for (; j < 16; j++) { if ((j % 2) == 1) dprintk(" "); else dprintk(" "); } dprintk(" "); for (j = 0; j < jm; j++) { c = cp[i+j]; c = isprint(c) ? c : '.'; dprintk("%c", c); } dprintk("\n"); }}EXPORT_SYMBOL(print_hexl);static inline struct gss_cl_ctx *gss_get_ctx(struct gss_cl_ctx *ctx){ atomic_inc(&ctx->count); return ctx;}static inline voidgss_put_ctx(struct gss_cl_ctx *ctx){ if (atomic_dec_and_test(&ctx->count)) gss_destroy_ctx(ctx);}static voidgss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx){ struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *old; write_lock(&gss_ctx_lock); old = gss_cred->gc_ctx; gss_cred->gc_ctx = ctx; cred->cr_flags |= RPCAUTH_CRED_UPTODATE; write_unlock(&gss_ctx_lock); if (old) gss_put_ctx(old);}static intgss_cred_is_uptodate_ctx(struct rpc_cred *cred){ struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); int res = 0; read_lock(&gss_ctx_lock); if ((cred->cr_flags & RPCAUTH_CRED_UPTODATE) && gss_cred->gc_ctx) res = 1; read_unlock(&gss_ctx_lock); return res;}static const void *simple_get_bytes(const void *p, const void *end, void *res, size_t len){ const void *q = (const void *)((const char *)p + len); if (unlikely(q > end || q < p)) return ERR_PTR(-EFAULT); memcpy(res, p, len); return q;}static inline const void *simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest){ const void *q; unsigned int len; p = simple_get_bytes(p, end, &len, sizeof(len)); if (IS_ERR(p)) return p; q = (const void *)((const char *)p + len); if (unlikely(q > end || q < p)) return ERR_PTR(-EFAULT); dest->data = kmalloc(len, GFP_KERNEL); if (unlikely(dest->data == NULL)) return ERR_PTR(-ENOMEM); dest->len = len; memcpy(dest->data, p, len); return q;}static struct gss_cl_ctx *gss_cred_get_ctx(struct rpc_cred *cred){ struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base); struct gss_cl_ctx *ctx = NULL; read_lock(&gss_ctx_lock); if (gss_cred->gc_ctx) ctx = gss_get_ctx(gss_cred->gc_ctx); read_unlock(&gss_ctx_lock); return ctx;}static struct gss_cl_ctx *gss_alloc_context(void){ struct gss_cl_ctx *ctx; ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); if (ctx != NULL) { memset(ctx, 0, sizeof(*ctx)); ctx->gc_proc = RPC_GSS_PROC_DATA; ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ spin_lock_init(&ctx->gc_seq_lock); atomic_set(&ctx->count,1); } return ctx;}#define GSSD_MIN_TIMEOUT (60 * 60)static const void *gss_fill_context(const void *p, const void *end, struct gss_cl_ctx *ctx, struct gss_api_mech *gm){ const void *q; unsigned int seclen; unsigned int timeout; u32 window_size; int ret; /* First unsigned int gives the lifetime (in seconds) of the cred */ p = simple_get_bytes(p, end, &timeout, sizeof(timeout)); if (IS_ERR(p)) goto err; if (timeout == 0) timeout = GSSD_MIN_TIMEOUT; ctx->gc_expiry = jiffies + (unsigned long)timeout * HZ * 3 / 4; /* Sequence number window. Determines the maximum number of simultaneous requests */ p = simple_get_bytes(p, end, &window_size, sizeof(window_size)); if (IS_ERR(p)) goto err; ctx->gc_win = window_size; /* gssd signals an error by passing ctx->gc_win = 0: */ if (ctx->gc_win == 0) { /* in which case, p points to an error code which we ignore */ p = ERR_PTR(-EACCES); goto err; } /* copy the opaque wire context */ p = simple_get_netobj(p, end, &ctx->gc_wire_ctx); if (IS_ERR(p)) goto err; /* import the opaque security context */ p = simple_get_bytes(p, end, &seclen, sizeof(seclen)); if (IS_ERR(p)) goto err; q = (const void *)((const char *)p + seclen); if (unlikely(q > end || q < p)) { p = ERR_PTR(-EFAULT); goto err; } ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx); if (ret < 0) { p = ERR_PTR(ret); goto err; } return q;err: dprintk("RPC: gss_fill_context returning %ld\n", -PTR_ERR(p)); return p;}struct gss_upcall_msg { atomic_t count; uid_t uid; struct rpc_pipe_msg msg; struct list_head list; struct gss_auth *auth; struct rpc_wait_queue rpc_waitqueue; wait_queue_head_t waitqueue; struct gss_cl_ctx *ctx;};static voidgss_release_msg(struct gss_upcall_msg *gss_msg){ if (!atomic_dec_and_test(&gss_msg->count)) return; BUG_ON(!list_empty(&gss_msg->list)); if (gss_msg->ctx != NULL) gss_put_ctx(gss_msg->ctx); kfree(gss_msg);}static struct gss_upcall_msg *__gss_find_upcall(struct gss_auth *gss_auth, uid_t uid){ struct gss_upcall_msg *pos; list_for_each_entry(pos, &gss_auth->upcalls, list) { if (pos->uid != uid) continue; atomic_inc(&pos->count); dprintk("RPC: gss_find_upcall found msg %p\n", pos); return pos; } dprintk("RPC: gss_find_upcall found nothing\n"); return NULL;}/* Try to add a upcall to the pipefs queue. * If an upcall owned by our uid already exists, then we return a reference * to that upcall instead of adding the new upcall. */static inline struct gss_upcall_msg *gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg){ struct gss_upcall_msg *old; spin_lock(&gss_auth->lock); old = __gss_find_upcall(gss_auth, gss_msg->uid); if (old == NULL) { atomic_inc(&gss_msg->count); list_add(&gss_msg->list, &gss_auth->upcalls); } else gss_msg = old; spin_unlock(&gss_auth->lock); return gss_msg;}static void__gss_unhash_msg(struct gss_upcall_msg *gss_msg){ if (list_empty(&gss_msg->list)) return; list_del_init(&gss_msg->list); rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); wake_up_all(&gss_msg->waitqueue); atomic_dec(&gss_msg->count);}static voidgss_unhash_msg(struct gss_upcall_msg *gss_msg){ struct gss_auth *gss_auth = gss_msg->auth; spin_lock(&gss_auth->lock); __gss_unhash_msg(gss_msg); spin_unlock(&gss_auth->lock);}static voidgss_upcall_callback(struct rpc_task *task){ struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; BUG_ON(gss_msg == NULL); if (gss_msg->ctx) gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx)); else task->tk_status = gss_msg->msg.errno; spin_lock(&gss_msg->auth->lock); gss_cred->gc_upcall = NULL; rpc_wake_up_status(&gss_msg->rpc_waitqueue, gss_msg->msg.errno); spin_unlock(&gss_msg->auth->lock); gss_release_msg(gss_msg);}static inline struct gss_upcall_msg *gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid){ struct gss_upcall_msg *gss_msg; gss_msg = kmalloc(sizeof(*gss_msg), GFP_KERNEL); if (gss_msg != NULL) { memset(gss_msg, 0, sizeof(*gss_msg)); INIT_LIST_HEAD(&gss_msg->list); rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq"); init_waitqueue_head(&gss_msg->waitqueue); atomic_set(&gss_msg->count, 1); gss_msg->msg.data = &gss_msg->uid; gss_msg->msg.len = sizeof(gss_msg->uid); gss_msg->uid = uid; gss_msg->auth = gss_auth; } return gss_msg;}static struct gss_upcall_msg *gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred){ struct gss_upcall_msg *gss_new, *gss_msg; gss_new = gss_alloc_msg(gss_auth, cred->cr_uid); if (gss_new == NULL) return ERR_PTR(-ENOMEM); gss_msg = gss_add_msg(gss_auth, gss_new); if (gss_msg == gss_new) { int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg); if (res) { gss_unhash_msg(gss_new); gss_msg = ERR_PTR(res); } } else gss_release_msg(gss_new); return gss_msg;}static inline intgss_refresh_upcall(struct rpc_task *task){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -