return ret;
}
- struct net *net = PDE(file->f_path.dentry->d_inode)->data;
+ static int gss_proxy_save_rsc(struct cache_detail *cd,
+ struct gssp_upcall_data *ud,
+ uint64_t *handle)
+ {
+ struct rsc rsci, *rscp = NULL;
+ static atomic64_t ctxhctr;
+ long long ctxh;
+ struct gss_api_mech *gm = NULL;
+ time_t expiry;
+ int status = -EINVAL;
+
+ memset(&rsci, 0, sizeof(rsci));
+ /* context handle */
+ status = -ENOMEM;
+ /* the handle needs to be just a unique id,
+ * use a static counter */
+ ctxh = atomic64_inc_return(&ctxhctr);
+
+ /* make a copy for the caller */
+ *handle = ctxh;
+
+ /* make a copy for the rsc cache */
+ if (dup_to_netobj(&rsci.handle, (char *)handle, sizeof(uint64_t)))
+ goto out;
+ rscp = rsc_lookup(cd, &rsci);
+ if (!rscp)
+ goto out;
+
+ /* creds */
+ if (!ud->found_creds) {
+ /* userspace seem buggy, we should always get at least a
+ * mapping to nobody */
+ dprintk("RPC: No creds found, marking Negative!\n");
+ set_bit(CACHE_NEGATIVE, &rsci.h.flags);
+ } else {
+
+ /* steal creds */
+ rsci.cred = ud->creds;
+ memset(&ud->creds, 0, sizeof(struct svc_cred));
+
+ status = -EOPNOTSUPP;
+ /* get mech handle from OID */
+ gm = gss_mech_get_by_OID(&ud->mech_oid);
+ if (!gm)
+ goto out;
+
+ status = -EINVAL;
+ /* mech-specific data: */
+ status = gss_import_sec_context(ud->out_handle.data,
+ ud->out_handle.len,
+ gm, &rsci.mechctx,
+ &expiry, GFP_KERNEL);
+ if (status)
+ goto out;
+ }
+
+ rsci.h.expiry_time = expiry;
+ rscp = rsc_update(cd, &rsci, rscp);
+ status = 0;
+ out:
+ gss_mech_put(gm);
+ rsc_free(&rsci);
+ if (rscp)
+ cache_put(&rscp->h, cd);
+ else
+ status = -ENOMEM;
+ return status;
+ }
+
+ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,
+ struct rpc_gss_wire_cred *gc, __be32 *authp)
+ {
+ struct kvec *resv = &rqstp->rq_res.head[0];
+ struct xdr_netobj cli_handle;
+ struct gssp_upcall_data ud;
+ uint64_t handle;
+ int status;
+ int ret;
+ struct net *net = rqstp->rq_xprt->xpt_net;
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ memset(&ud, 0, sizeof(ud));
+ ret = gss_read_proxy_verf(rqstp, gc, authp,
+ &ud.in_handle, &ud.in_token);
+ if (ret)
+ return ret;
+
+ ret = SVC_CLOSE;
+
+ /* Perform synchronous upcall to gss-proxy */
+ status = gssp_accept_sec_context_upcall(net, &ud);
+ if (status)
+ goto out;
+
+ dprintk("RPC: svcauth_gss: gss major status = %d\n",
+ ud.major_status);
+
+ switch (ud.major_status) {
+ case GSS_S_CONTINUE_NEEDED:
+ cli_handle = ud.out_handle;
+ break;
+ case GSS_S_COMPLETE:
+ status = gss_proxy_save_rsc(sn->rsc_cache, &ud, &handle);
+ if (status)
+ goto out;
+ cli_handle.data = (u8 *)&handle;
+ cli_handle.len = sizeof(handle);
+ break;
+ default:
+ ret = SVC_CLOSE;
+ goto out;
+ }
+
+ /* Got an answer to the upcall; use it: */
+ if (gss_write_init_verf(sn->rsc_cache, rqstp,
+ &cli_handle, &ud.major_status))
+ goto out;
+ if (gss_write_resv(resv, PAGE_SIZE,
+ &cli_handle, &ud.out_token,
+ ud.major_status, ud.minor_status))
+ goto out;
+
+ ret = SVC_COMPLETE;
+ out:
+ gssp_free_upcall_data(&ud);
+ return ret;
+ }
+
+ DEFINE_SPINLOCK(use_gssp_lock);
+
+ static bool use_gss_proxy(struct net *net)
+ {
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ if (sn->use_gss_proxy != -1)
+ return sn->use_gss_proxy;
+ spin_lock(&use_gssp_lock);
+ /*
+ * If you wanted gss-proxy, you should have said so before
+ * starting to accept requests:
+ */
+ sn->use_gss_proxy = 0;
+ spin_unlock(&use_gssp_lock);
+ return 0;
+ }
+
+ #ifdef CONFIG_PROC_FS
+
+ static bool set_gss_proxy(struct net *net, int type)
+ {
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+ int ret = 0;
+
+ WARN_ON_ONCE(type != 0 && type != 1);
+ spin_lock(&use_gssp_lock);
+ if (sn->use_gss_proxy == -1 || sn->use_gss_proxy == type)
+ sn->use_gss_proxy = type;
+ else
+ ret = -EBUSY;
+ spin_unlock(&use_gssp_lock);
+ wake_up(&sn->gssp_wq);
+ return ret;
+ }
+
+ static inline bool gssp_ready(struct sunrpc_net *sn)
+ {
+ switch (sn->use_gss_proxy) {
+ case -1:
+ return false;
+ case 0:
+ return true;
+ case 1:
+ return sn->gssp_clnt;
+ }
+ WARN_ON_ONCE(1);
+ return false;
+ }
+
+ static int wait_for_gss_proxy(struct net *net)
+ {
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ return wait_event_interruptible(sn->gssp_wq, gssp_ready(sn));
+ }
+
+
+ static ssize_t write_gssp(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+ {
- struct net *net = PDE(file->f_path.dentry->d_inode)->data;
++ struct net *net = PDE_DATA(file->f_path.dentry->d_inode);
+ char tbuf[20];
+ unsigned long i;
+ int res;
+
+ if (*ppos || count > sizeof(tbuf)-1)
+ return -EINVAL;
+ if (copy_from_user(tbuf, buf, count))
+ return -EFAULT;
+
+ tbuf[count] = 0;
+ res = kstrtoul(tbuf, 0, &i);
+ if (res)
+ return res;
+ if (i != 1)
+ return -EINVAL;
+ res = set_gss_proxy(net, 1);
+ if (res)
+ return res;
+ res = set_gssp_clnt(net);
+ if (res)
+ return res;
+ return count;
+ }
+
+ static ssize_t read_gssp(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+ {
++ struct net *net = PDE_DATA(file->f_path.dentry->d_inode);
+ unsigned long p = *ppos;
+ char tbuf[10];
+ size_t len;
+ int ret;
+
+ ret = wait_for_gss_proxy(net);
+ if (ret)
+ return ret;
+
+ snprintf(tbuf, sizeof(tbuf), "%d\n", use_gss_proxy(net));
+ len = strlen(tbuf);
+ if (p >= len)
+ return 0;
+ len -= p;
+ if (len > count)
+ len = count;
+ if (copy_to_user(buf, (void *)(tbuf+p), len))
+ return -EFAULT;
+ *ppos += len;
+ return len;
+ }
+
+ static const struct file_operations use_gss_proxy_ops = {
+ .open = nonseekable_open,
+ .write = write_gssp,
+ .read = read_gssp,
+ };
+
+ static int create_use_gss_proxy_proc_entry(struct net *net)
+ {
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+ struct proc_dir_entry **p = &sn->use_gssp_proc;
+
+ sn->use_gss_proxy = -1;
+ *p = proc_create_data("use-gss-proxy", S_IFREG|S_IRUSR|S_IWUSR,
+ sn->proc_net_rpc,
+ &use_gss_proxy_ops, net);
+ if (!*p)
+ return -ENOMEM;
+ init_gssp_clnt(sn);
+ return 0;
+ }
+
+ static void destroy_use_gss_proxy_proc_entry(struct net *net)
+ {
+ struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+ if (sn->use_gssp_proc) {
+ remove_proc_entry("use-gss-proxy", sn->proc_net_rpc);
+ clear_gssp_clnt(sn);
+ }
+ }
+ #else /* CONFIG_PROC_FS */
+
+ static int create_use_gss_proxy_proc_entry(struct net *net)
+ {
+ return 0;
+ }
+
+ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}
+
+ #endif /* CONFIG_PROC_FS */
+
/*
* Accept an rpcsec packet.
* If context establishment, punt to user space