Commit 8af92f28 authored by Trond Myklebust's avatar Trond Myklebust

RPC: Document the format of the gssd downcalls

 - Document the format of the gssd downcalls
 - Separate out "uid" field from rest of GSS context data struct
   since it will not be needed for the keyring-based contexts.
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 41e3d179
...@@ -68,7 +68,7 @@ struct rpc_gss_init_res { ...@@ -68,7 +68,7 @@ struct rpc_gss_init_res {
struct gss_cl_ctx { struct gss_cl_ctx {
atomic_t count; atomic_t count;
u32 gc_proc; enum rpc_gss_proc gc_proc;
u32 gc_seq; u32 gc_seq;
spinlock_t gc_seq_lock; spinlock_t gc_seq_lock;
struct gss_ctx *gc_gss_ctx; struct gss_ctx *gc_gss_ctx;
......
...@@ -175,42 +175,34 @@ gss_cred_is_uptodate_ctx(struct rpc_cred *cred) ...@@ -175,42 +175,34 @@ gss_cred_is_uptodate_ctx(struct rpc_cred *cred)
return res; return res;
} }
static inline int static const void *
simple_get_bytes(char **ptr, const char *end, void *res, int len) simple_get_bytes(const void *p, const void *end, void *res, size_t len)
{ {
char *p, *q; const void *q = (const void *)((const char *)p + len);
p = *ptr; if (unlikely(q > end || q < p))
q = p + len; return ERR_PTR(-EFAULT);
if (q > end || q < p)
return -1;
memcpy(res, p, len); memcpy(res, p, len);
*ptr = q; return q;
return 0;
}
static inline int
simple_get_netobj(char **ptr, const char *end, struct xdr_netobj *res)
{
char *p, *q;
p = *ptr;
if (simple_get_bytes(&p, end, &res->len, sizeof(res->len)))
return -1;
q = p + res->len;
if (q > end || q < p)
return -1;
res->data = p;
*ptr = q;
return 0;
} }
static int static inline const void *
dup_netobj(struct xdr_netobj *source, struct xdr_netobj *dest) simple_get_netobj(const void *p, const void *end, struct xdr_netobj *dest)
{ {
dest->len = source->len; const void *q;
if (!(dest->data = kmalloc(dest->len, GFP_KERNEL))) unsigned int len;
return -1;
memcpy(dest->data, source->data, dest->len); p = simple_get_bytes(p, end, &len, sizeof(len));
return 0; 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 * static struct gss_cl_ctx *
...@@ -226,64 +218,68 @@ gss_cred_get_ctx(struct rpc_cred *cred) ...@@ -226,64 +218,68 @@ gss_cred_get_ctx(struct rpc_cred *cred)
return ctx; return ctx;
} }
static int static struct gss_cl_ctx *
gss_parse_init_downcall(struct gss_api_mech *gm, struct xdr_netobj *buf, gss_alloc_context(void)
struct gss_cl_ctx **gc, uid_t *uid, int *gss_err)
{ {
char *end = buf->data + buf->len;
char *p = buf->data;
struct gss_cl_ctx *ctx; struct gss_cl_ctx *ctx;
struct xdr_netobj tmp_buf;
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;
}
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; unsigned int timeout;
int err = -EIO; u32 window_size;
int ret;
if (!(ctx = kmalloc(sizeof(*ctx), GFP_KERNEL))) { /* First unsigned int gives the lifetime (in seconds) of the cred */
err = -ENOMEM; p = simple_get_bytes(p, end, &timeout, sizeof(timeout));
if (IS_ERR(p))
goto err; goto err;
} /* Sequence number window. Determines the maximum number of simultaneous requests */
ctx->gc_proc = RPC_GSS_PROC_DATA; p = simple_get_bytes(p, end, &window_size, sizeof(window_size));
ctx->gc_seq = 1; /* NetApp 6.4R1 doesn't accept seq. no. 0 */ if (IS_ERR(p))
spin_lock_init(&ctx->gc_seq_lock); goto err;
atomic_set(&ctx->count,1); ctx->gc_win = window_size;
if (simple_get_bytes(&p, end, uid, sizeof(*uid)))
goto err_free_ctx;
/* FIXME: discarded timeout for now */
if (simple_get_bytes(&p, end, &timeout, sizeof(timeout)))
goto err_free_ctx;
*gss_err = 0;
if (simple_get_bytes(&p, end, &ctx->gc_win, sizeof(ctx->gc_win)))
goto err_free_ctx;
/* gssd signals an error by passing ctx->gc_win = 0: */ /* gssd signals an error by passing ctx->gc_win = 0: */
if (!ctx->gc_win) { if (ctx->gc_win == 0) {
/* in which case the next int is an error code: */ /* in which case, p points to an error code which we ignore */
if (simple_get_bytes(&p, end, gss_err, sizeof(*gss_err))) p = ERR_PTR(-EACCES);
goto err_free_ctx; goto err;
err = 0; }
goto err_free_ctx; /* 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;
} }
if (simple_get_netobj(&p, end, &tmp_buf)) ret = gss_import_sec_context(p, seclen, gm, &ctx->gc_gss_ctx);
goto err_free_ctx; if (ret < 0) {
if (dup_netobj(&tmp_buf, &ctx->gc_wire_ctx)) { p = ERR_PTR(ret);
err = -ENOMEM; goto err;
goto err_free_ctx;
} }
if (simple_get_netobj(&p, end, &tmp_buf)) return q;
goto err_free_wire_ctx;
if (p != end)
goto err_free_wire_ctx;
if (gss_import_sec_context(tmp_buf.data, tmp_buf.len, gm, &ctx->gc_gss_ctx))
goto err_free_wire_ctx;
*gc = ctx;
return 0;
err_free_wire_ctx:
kfree(ctx->gc_wire_ctx.data);
err_free_ctx:
kfree(ctx);
err: err:
*gc = NULL; dprintk("RPC: gss_fill_context returning %ld\n", -PTR_ERR(p));
dprintk("RPC: gss_parse_init_downcall returning %d\n", err); return p;
return err;
} }
...@@ -441,65 +437,74 @@ gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg, ...@@ -441,65 +437,74 @@ gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
static ssize_t static ssize_t
gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
{ {
struct xdr_netobj obj = { const void *p, *end;
.len = mlen, void *buf;
};
struct inode *inode = filp->f_dentry->d_inode; struct inode *inode = filp->f_dentry->d_inode;
struct rpc_inode *rpci = RPC_I(inode); struct rpc_inode *rpci = RPC_I(inode);
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct rpc_auth *auth;
struct gss_auth *gss_auth; struct gss_auth *gss_auth;
struct gss_api_mech *mech;
struct auth_cred acred = { 0 }; struct auth_cred acred = { 0 };
struct rpc_cred *cred; struct rpc_cred *cred;
struct gss_upcall_msg *gss_msg; struct gss_upcall_msg *gss_msg;
struct gss_cl_ctx *ctx = NULL; struct gss_cl_ctx *ctx;
ssize_t left; uid_t uid;
int err; int err = -EFBIG;
int gss_err;
if (mlen > MSG_BUF_MAXSIZE) if (mlen > MSG_BUF_MAXSIZE)
return -EFBIG;
obj.data = kmalloc(mlen, GFP_KERNEL);
if (!obj.data)
return -ENOMEM;
left = copy_from_user(obj.data, src, mlen);
if (left) {
err = -EFAULT;
goto out; goto out;
} err = -ENOMEM;
buf = kmalloc(mlen, GFP_KERNEL);
if (!buf)
goto out;
clnt = rpci->private; clnt = rpci->private;
auth = clnt->cl_auth; err = -EFAULT;
gss_auth = container_of(auth, struct gss_auth, rpc_auth); if (copy_from_user(buf, src, mlen))
mech = gss_auth->mech; goto err;
err = gss_parse_init_downcall(mech, &obj, &ctx, &acred.uid, &gss_err);
if (err) end = (const void *)((char *)buf + mlen);
p = simple_get_bytes(buf, end, &uid, sizeof(uid));
if (IS_ERR(p)) {
err = PTR_ERR(p);
goto err; goto err;
cred = rpcauth_lookup_credcache(auth, &acred, 0); }
acred.uid = uid;
err = -ENOENT;
cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, 0);
if (!cred) if (!cred)
goto err; goto err;
if (gss_err)
cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; err = -ENOMEM;
else ctx = gss_alloc_context();
gss_cred_set_ctx(cred, ctx); if (ctx == NULL)
goto err;
err = 0;
gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth);
p = gss_fill_context(p, end, ctx, gss_auth->mech);
if (IS_ERR(p)) {
err = PTR_ERR(p);
if (err != -EACCES)
goto err_put_ctx;
} else
gss_cred_set_ctx(cred, gss_get_ctx(ctx));
spin_lock(&gss_auth->lock); spin_lock(&gss_auth->lock);
gss_msg = __gss_find_upcall(gss_auth, acred.uid); gss_msg = __gss_find_upcall(gss_auth, acred.uid);
if (gss_msg) { if (gss_msg) {
if (gss_err) gss_msg->msg.errno = err;
gss_msg->msg.errno = -EACCES;
__gss_unhash_msg(gss_msg); __gss_unhash_msg(gss_msg);
spin_unlock(&gss_auth->lock); spin_unlock(&gss_auth->lock);
gss_release_msg(gss_msg); gss_release_msg(gss_msg);
} else } else
spin_unlock(&gss_auth->lock); spin_unlock(&gss_auth->lock);
kfree(obj.data); gss_put_ctx(ctx);
kfree(buf);
dprintk("RPC: gss_pipe_downcall returning length %Zu\n", mlen); dprintk("RPC: gss_pipe_downcall returning length %Zu\n", mlen);
return mlen; return mlen;
err_put_ctx:
gss_put_ctx(ctx);
err: err:
if (ctx) kfree(buf);
gss_destroy_ctx(ctx);
out: out:
kfree(obj.data);
dprintk("RPC: gss_pipe_downcall returning %d\n", err); dprintk("RPC: gss_pipe_downcall returning %d\n", err);
return err; return err;
} }
...@@ -633,13 +638,8 @@ gss_destroy_ctx(struct gss_cl_ctx *ctx) ...@@ -633,13 +638,8 @@ gss_destroy_ctx(struct gss_cl_ctx *ctx)
if (ctx->gc_gss_ctx) if (ctx->gc_gss_ctx)
gss_delete_sec_context(&ctx->gc_gss_ctx); gss_delete_sec_context(&ctx->gc_gss_ctx);
if (ctx->gc_wire_ctx.len > 0) { kfree(ctx->gc_wire_ctx.data);
kfree(ctx->gc_wire_ctx.data);
ctx->gc_wire_ctx.len = 0;
}
kfree(ctx); kfree(ctx);
} }
static void static void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment