fixes, need new nfsutils patches version 8.
* more precise error notify between kernel and lgssd.
* minor gss api param fix. and various minor gss fixes.
rq_timedout:1, rq_resend:1, rq_restart:1, rq_replay:1,
rq_no_resend:1, rq_waiting:1, rq_receiving_reply:1,
rq_no_delay:1, rq_net_err:1, rq_req_wrapped:1,
- rq_ptlrpcs_restart:1;
+ rq_ptlrpcs_restart:1, rq_ptlrpcs_err:1;
int rq_phase;
/* client-side refcount for SENT race */
atomic_t rq_refcount;
};
struct ptlrpc_credops {
+ int (*match) (struct ptlrpc_cred *cred, struct vfs_cred *vcred);
int (*refresh)(struct ptlrpc_cred *cred);
- int (*match) (struct ptlrpc_cred *cred,
- struct ptlrpc_request *req,
- struct vfs_cred *vcred);
- int (*sign) (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
- int (*verify) (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
- int (*seal) (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
- int (*unseal) (struct ptlrpc_cred *cred, struct ptlrpc_request *req);
void (*destroy)(struct ptlrpc_cred *cred);
+ int (*sign) (struct ptlrpc_cred *cred,
+ struct ptlrpc_request *req);
+ int (*verify) (struct ptlrpc_cred *cred,
+ struct ptlrpc_request *req);
+ int (*seal) (struct ptlrpc_cred *cred,
+ struct ptlrpc_request *req);
+ int (*unseal) (struct ptlrpc_cred *cred,
+ struct ptlrpc_request *req);
};
-#define PTLRPC_CRED_UPTODATE 0x00000001
-#define PTLRPC_CRED_DEAD 0x00000002
+#define PTLRPC_CRED_UPTODATE 0x00000001 /* uptodate */
+#define PTLRPC_CRED_DEAD 0x00000002 /* mark expired gracefully */
+#define PTLRPC_CRED_ERROR 0x00000004 /* fatal error (refresh, etc.) */
+#define PTLRPC_CRED_FLAGS_MASK 0x00000007
struct ptlrpc_cred {
struct list_head pc_hash; /* linked into hash table */
atomic_t pc_refcount;
struct ptlrpc_sec *pc_sec;
struct ptlrpc_credops *pc_ops;
- struct ptlrpc_request *pc_req;
unsigned long pc_expire;
int pc_flags;
/* XXX maybe should not be here */
void *pipe_data);
void (*destroy_sec) (struct ptlrpc_sec *sec);
struct ptlrpc_cred * (*create_cred) (struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
struct vfs_cred *vcred);
/* buffer manipulation */
int (*alloc_reqbuf) (struct ptlrpc_sec *sec,
{
LASSERT(cred);
LASSERT(atomic_read(&cred->pc_refcount));
- return (cred->pc_flags & PTLRPC_CRED_UPTODATE);
+ return ((cred->pc_flags & PTLRPC_CRED_FLAGS_MASK) ==
+ PTLRPC_CRED_UPTODATE);
}
static inline int ptlrpcs_cred_refresh(struct ptlrpc_cred *cred)
{
LASSERT(rsd);
LASSERT(rsd->rsd_ngroups <= LUSTRE_MAX_GROUPS);
+ /* XXX We'v no dedicated bits indicating whether GSS is used,
+ * and authenticated/mapped uid is valid. currently we suppose
+ * gss must initialize rq_sec_svcdata.
+ */
+ if (req->rq_sec_svcdata && req->rq_auth_uid == -1) {
+ CWARN("user not authenticated, deny access\n");
+ RETURN(-EPERM);
+ }
+
strong_sec = (req->rq_auth_uid != -1);
LASSERT(!(req->rq_remote_realm && !strong_sec));
GOTO(out_free, rc);
}
- /* just a try on refresh, but we proceed even if it failed */
- rc = ptlrpcs_cred_refresh(request->rq_cred);
- if (!ptlrpcs_cred_is_uptodate(request->rq_cred)) {
- CERROR("req %p: failed to refresh cred %p, rc %d, continue\n",
- request, request->rq_cred, rc);
- }
+ /* try to refresh the cred. we do this here in order to let fewer
+ * refresh be performed in ptlrpcd context (which might block ptlrpcd).
+ * fail out only if a fatal ptlrpcs error occured.
+ */
+ ptlrpcs_req_refresh_cred(request);
+ if (request->rq_ptlrpcs_err)
+ GOTO(out_cred, rc = -EPERM);
rc = lustre_pack_request(request, count, lengths, bufs);
if (rc) {
if (req->rq_net_err && !req->rq_timedout)
ptlrpc_expire_one_request(req);
- if (req->rq_err) {
+ if (req->rq_err || req->rq_ptlrpcs_err) {
ptlrpc_unregister_reply(req);
if (req->rq_status == 0)
- req->rq_status = -EIO;
+ req->rq_status = req->rq_err ? -EIO : -EPERM;
req->rq_phase = RQ_PHASE_INTERPRET;
spin_lock_irqsave(&imp->imp_lock, flags);
if (req->rq_err)
GOTO(out, rc = -EIO);
+ if (req->rq_ptlrpcs_err)
+ GOTO(out, rc = -EPERM);
+
/* Resend if we need to, unless we were interrupted. */
if (req->rq_resend && !req->rq_intr) {
/* ...unless we were specifically told otherwise. */
request->rq_timedout = 0;
request->rq_net_err = 0;
request->rq_resend = 0;
- request->rq_ptlrpcs_restart = 0;
request->rq_restart = 0;
+ request->rq_ptlrpcs_restart = 0;
+ request->rq_ptlrpcs_err = 0;
spin_unlock_irqrestore (&request->rq_lock, flags);
reply_md.start = request->rq_repbuf;
if (request.rq_err || request.rq_resend || request.rq_intr ||
request.rq_timedout || !request.rq_replied) {
CERROR("secinit rpc error: err %d, resend %d, "
- "intr %d, timeout %d, replied %d\n",
+ "intr %d, timedout %d, replied %d\n",
request.rq_err, request.rq_resend, request.rq_intr,
request.rq_timedout, request.rq_replied);
- rc = -EINVAL;
+ if (request.rq_timedout)
+ rc = -ETIMEDOUT;
+ else
+ rc = -EINVAL;
} else {
*replenp = request.rq_nob_received;
rc = 0;
if (!reqbuf || !repbuf) {
CERROR("Can't alloc buffer: %p/%p\n", reqbuf, repbuf);
- GOTO(out_free, rc = -ENOMEM);
+ param.status = -ENOMEM;
+ goto out_copy;
}
/* get token */
param.uid, param.gid,
param.send_token_size,
param.send_token);
- if (reqlen < 0)
- GOTO(out_free, rc = reqlen);
+ if (reqlen < 0) {
+ param.status = reqlen;
+ goto out_copy;
+ }
replen = repbuf_size;
rc = ptlrpc_do_rawrpc(imp, reqbuf, reqlen,
repbuf, &replen, SECINIT_RPC_TIMEOUT);
- if (rc)
- GOTO(out_free, rc);
+ if (rc) {
+ param.status = rc;
+ goto out_copy;
+ }
if (replen > param.reply_buf_size) {
CERROR("output buffer size %ld too small, need %d\n",
param.reply_buf_size, replen);
- GOTO(out_free, rc = -EINVAL);
+ param.status = -EINVAL;
+ goto out_copy;
}
lsize = secinit_parse_reply(repbuf, replen,
param.reply_buf, param.reply_buf_size);
- if (lsize < 0)
- GOTO(out_free, rc = (int)lsize);
+ if (lsize < 0) {
+ param.status = (int) lsize;
+ goto out_copy;
+ }
param.status = 0;
param.reply_length = lsize;
+out_copy:
if (copy_to_user(buffer, ¶m, sizeof(param)))
rc = -EFAULT;
else
rc = 0;
-out_free:
+
class_import_put(imp);
if (repbuf)
OBD_FREE(repbuf, repbuf_size);
struct gss_cl_ctx *ctx;
rawobj_t tmp_buf;
unsigned int timeout;
- int err = -EIO;
+ int err = -EPERM;
ENTRY;
*gc = NULL;
+ *gss_err = 0;
OBD_ALLOC(ctx, sizeof(*ctx));
if (!ctx)
atomic_set(&ctx->gc_refcount,1);
if (simple_get_bytes(&p, &len, &gmd->gum_uid, sizeof(gmd->gum_uid)))
- GOTO(err_free_ctx, err);
+ goto err_free_ctx;
if (simple_get_bytes(&p, &len, &gmd->gum_svc, sizeof(gmd->gum_svc)))
- GOTO(err_free_ctx, err);
+ goto err_free_ctx;
if (simple_get_bytes(&p, &len, &gmd->gum_nal, sizeof(gmd->gum_nal)))
- GOTO(err_free_ctx, err);
+ goto err_free_ctx;
if (simple_get_bytes(&p, &len, &gmd->gum_netid, sizeof(gmd->gum_netid)))
- GOTO(err_free_ctx, err);
+ goto err_free_ctx;
if (simple_get_bytes(&p, &len, &gmd->gum_nid, sizeof(gmd->gum_nid)))
- GOTO(err_free_ctx, err);
+ goto err_free_ctx;
/* FIXME: discarded timeout for now */
if (simple_get_bytes(&p, &len, &timeout, sizeof(timeout)))
- GOTO(err_free_ctx, err);
- *gss_err = 0;
+ goto err_free_ctx;
if (simple_get_bytes(&p, &len, &ctx->gc_win, sizeof(ctx->gc_win)))
- GOTO(err_free_ctx, err);
- /* gssd signals an error by passing ctx->gc_win = 0: */
+ goto err_free_ctx;
+
+ /* lgssd signals an error by passing ctx->gc_win = 0: */
if (!ctx->gc_win) {
- /* in which case the next int is an error code: */
- if (simple_get_bytes(&p, &len, gss_err, sizeof(*gss_err)))
- GOTO(err_free_ctx, err);
- if (*gss_err == 0) {
- CERROR("error downcall pass no gss error\n");
- GOTO(err_free_ctx, err);
+ /* in which case the next 2 int are:
+ * - rpc error
+ * - gss error
+ */
+ if (simple_get_bytes(&p, &len, &err, sizeof(err))) {
+ err = -EPERM;
+ goto err_free_ctx;
}
- GOTO(err_free_ctx, err = 0);
+ if (simple_get_bytes(&p, &len, gss_err, sizeof(*gss_err))) {
+ err = -EPERM;
+ goto err_free_ctx;
+ }
+ if (err == 0 && *gss_err == 0) {
+ CERROR("no error passed from downcall\n");
+ err = -EPERM;
+ }
+ goto err_free_ctx;
}
+
if (rawobj_extract_local(&tmp_buf, (__u32 **) ((void *)&p), &len))
- GOTO(err_free_ctx, err);
+ goto err_free_ctx;
if (rawobj_dup(&ctx->gc_wire_ctx, &tmp_buf)) {
- GOTO(err_free_ctx, err = -ENOMEM);
+ err = -ENOMEM;
+ goto err_free_ctx;
}
if (rawobj_extract_local(&tmp_buf, (__u32 **) ((void *)&p), &len))
- GOTO(err_free_wire_ctx, err);
+ goto err_free_wire_ctx;
if (len) {
CERROR("unexpected trailing %u bytes\n", len);
- GOTO(err_free_wire_ctx, err);
+ goto err_free_wire_ctx;
}
if (kgss_import_sec_context(&tmp_buf, gm, &ctx->gc_gss_ctx))
- GOTO(err_free_wire_ctx, err);
+ goto err_free_wire_ctx;
*gc = ctx;
RETURN(0);
spin_lock(&gsec->gs_lock);
gss_msg = gss_find_upcall(gsec, obdname, &gmd);
if (gss_msg) {
- spin_unlock(&gsec->gs_lock);
+ if (gss_new) {
+ OBD_FREE(gss_new, sizeof(*gss_new));
+ gss_new = NULL;
+ }
GOTO(waiting, res);
}
+
if (!gss_new) {
spin_unlock(&gsec->gs_lock);
OBD_ALLOC(gss_new, sizeof(*gss_new));
- if (!gss_new) {
- CERROR("fail to alloc memory\n");
+ if (!gss_new)
RETURN(-ENOMEM);
- }
goto again;
}
/* so far we'v created gss_new */
CERROR("rpc_queue_upcall failed: %d\n", res);
gss_unhash_msg(gss_new);
gss_release_msg(gss_new);
+ cred->pc_flags |= PTLRPC_CRED_ERROR;
RETURN(res);
}
gss_msg = gss_new;
+ spin_lock(&gsec->gs_lock);
waiting:
+ /* upcall might finish quickly */
+ if (list_empty(&gss_msg->gum_list)) {
+ spin_unlock(&gsec->gs_lock);
+ res = 0;
+ goto out;
+ }
+
init_waitqueue_entry(&wait, current);
- spin_lock(&gsec->gs_lock);
- add_wait_queue(&gss_msg->gum_waitq, &wait);
set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&gss_msg->gum_waitq, &wait);
spin_unlock(&gsec->gs_lock);
res = schedule_timeout(CRED_REFRESH_UPCALL_TIMEOUT * HZ);
-
remove_wait_queue(&gss_msg->gum_waitq, &wait);
+
if (signal_pending(current)) {
- CERROR("interrupted gss upcall: cred %p\n", cred);
+ CERROR("cred %p: interrupted upcall\n", cred);
+ if (gss_new)
+ cred->pc_flags |= PTLRPC_CRED_DEAD | PTLRPC_CRED_ERROR;
res = -EINTR;
} else if (res == 0) {
- CERROR("gss upcall timeout: cred %p\n", cred);
+ CERROR("cred %p: upcall timedout\n", cred);
+ cred->pc_flags |= PTLRPC_CRED_DEAD | PTLRPC_CRED_ERROR;
res = -ETIMEDOUT;
} else
res = 0;
+out:
+ spin_lock(&gsec->gs_lock);
gss_release_msg(gss_msg);
+ spin_unlock(&gsec->gs_lock);
+
RETURN(res);
}
#else /* !__KERNEL__ */
LASSERT(mech);
rc = gss_parse_init_downcall(mech, &obj, &ctx, &vcred, &dest_ip,
&gss_err);
- if (rc) {
- CERROR("parse init downcall error %d\n", rc);
- goto err_out;
- }
-
- if (gss_err) {
- CERROR("cred fresh got gss error %x\n", gss_err);
- rc = -EINVAL;
+ if (rc || gss_err) {
+ CERROR("parse init downcall: rpc %d, gss 0x%x\n", rc, gss_err);
+ if (rc != -ERESTART || gss_err != 0)
+ cred->pc_flags |= PTLRPC_CRED_ERROR;
+ if (rc == 0)
+ rc = -EPERM;
goto err_out;
}
#endif
static int gss_cred_match(struct ptlrpc_cred *cred,
- struct ptlrpc_request *req,
struct vfs_cred *vcred)
{
RETURN(cred->pc_pag == vcred->vc_pag);
cred = ptlrpcs_cred_lookup(sec, &vcred);
if (!cred) {
CWARN("didn't find cred for uid %u\n", vcred.vc_uid);
- GOTO(err, err);
+ GOTO(err, err = -EINVAL);
}
+
if (err || gss_err) {
- CERROR("got err %d, gss err %d, set cred %p dead\n",
- err, gss_err, cred);
cred->pc_flags |= PTLRPC_CRED_DEAD;
+ if (err != -ERESTART || gss_err != 0)
+ cred->pc_flags |= PTLRPC_CRED_ERROR;
+ CERROR("cred %p: rpc err %d, gss err 0x%x, fatal %d\n",
+ cred, err, gss_err,
+ ((cred->pc_flags & PTLRPC_CRED_ERROR) != 0));
} else {
CDEBUG(D_SEC, "get initial ctx:\n");
gss_cred_set_ctx(cred, ctx);
static
struct ptlrpc_cred * gss_create_cred(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
struct vfs_cred *vcred)
{
struct gss_cred *gcred;
atomic_set(&cred->pc_refcount, 0);
cred->pc_sec = sec;
cred->pc_ops = &gss_credops;
- cred->pc_req = req;
cred->pc_expire = get_seconds() + GSS_CRED_EXPIRE;
cred->pc_flags = 0;
cred->pc_pag = vcred->vc_pag;
rsci = gss_svc_searchbyctx(&rsip->out_handle);
if (!rsci) {
CERROR("rsci still not mature yet?\n");
- GOTO(out_rsip, rc = SVC_DROP);
+
+ if (gss_pack_err_notify(req, GSS_S_FAILURE, 0))
+ rc = SVC_DROP;
+ else
+ rc = SVC_COMPLETE;
+
+ GOTO(out_rsip, rc);
}
CWARN("svcsec create gss context %p(%u@%s)\n",
rsci, rsci->cred.vc_uid,
static
struct ptlrpc_cred * cred_cache_lookup(struct ptlrpc_sec *sec,
struct vfs_cred *vcred,
- struct ptlrpc_request *req,
int create)
{
struct ptlrpc_cred *cred, *new = NULL, *n;
continue;
if (ptlrpcs_cred_unlink_expired(cred, &freelist))
continue;
- if (cred->pc_ops->match(cred, req, vcred)) {
+ if (cred->pc_ops->match(cred, vcred)) {
found = 1;
break;
}
cred = new;
} else if (create) {
spin_unlock(&sec->ps_lock);
- new = sec->ps_type->pst_ops->create_cred(sec, req, vcred);
+ new = sec->ps_type->pst_ops->create_cred(sec, vcred);
if (new) {
atomic_inc(&sec->ps_credcount);
goto retry;
struct ptlrpc_cred *cred;
ENTRY;
- cred = cred_cache_lookup(sec, vcred, NULL, 0);
+ cred = cred_cache_lookup(sec, vcred, 0);
RETURN(cred);
}
vcred.vc_pag = (__u64) current->uid;
vcred.vc_uid = current->uid;
- req->rq_cred = cred_cache_lookup(imp->imp_sec, &vcred, req, 1);
+ req->rq_cred = cred_cache_lookup(imp->imp_sec, &vcred, 1);
if (!req->rq_cred) {
CERROR("req %p: fail to get cred from cache\n", req);
RETURN(rc);
}
+/*
+ * since there's no lock on the cred, its status could be changed
+ * by other threads at any time, we allow this race.
+ */
int ptlrpcs_req_refresh_cred(struct ptlrpc_request *req)
{
struct ptlrpc_cred *cred = req->rq_cred;
- int rc;
ENTRY;
LASSERT(cred);
- if ((cred->pc_flags & (PTLRPC_CRED_UPTODATE | PTLRPC_CRED_DEAD)) ==
- PTLRPC_CRED_UPTODATE)
+ if (ptlrpcs_cred_is_uptodate(cred))
RETURN(0);
+ if (cred->pc_flags & PTLRPC_CRED_ERROR) {
+ req->rq_ptlrpcs_err = 1;
+ RETURN(-EPERM);
+ }
+
if (cred->pc_flags & PTLRPC_CRED_DEAD) {
- rc = ptlrpcs_req_replace_dead_cred(req);
- if (!rc) {
+ if (ptlrpcs_req_replace_dead_cred(req) == 0) {
LASSERT(cred != req->rq_cred);
CWARN("req %p: replace cred %p => %p\n",
req, cred, req->rq_cred);
}
}
- rc = ptlrpcs_cred_refresh(cred);
- if (!(cred->pc_flags & PTLRPC_CRED_UPTODATE)) {
- CERROR("req %p: failed to refresh cred %p, rc %d\n",
- req, cred, rc);
- if (!rc)
- rc = -EACCES;
- }
- RETURN(rc);
+ ptlrpcs_cred_refresh(cred);
+ if (!ptlrpcs_cred_is_uptodate(cred)) {
+ if (cred->pc_flags & PTLRPC_CRED_ERROR)
+ req->rq_ptlrpcs_err = 1;
+
+ CERROR("req %p: failed to refresh cred %p, fatal %d\n",
+ req, cred, req->rq_ptlrpcs_err);
+ RETURN(-EPERM);
+ } else
+ RETURN(0);
}
int ptlrpcs_cli_wrap_request(struct ptlrpc_request *req)
}
static int null_cred_match(struct ptlrpc_cred *cred,
- struct ptlrpc_request *req,
struct vfs_cred *vcred)
{
ENTRY;
static
struct ptlrpc_cred* null_create_cred(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
struct vfs_cred *vcred)
{
struct ptlrpc_cred *cred;
atomic_set(&cred->pc_refcount, 0);
cred->pc_sec = sec;
cred->pc_ops = &null_credops;
- cred->pc_req = req;
cred->pc_expire = (-1UL >> 1); /* never expire */
cred->pc_flags = PTLRPC_CRED_UPTODATE;
cred->pc_pag = vcred->vc_pag;