Commit bd1d421a authored by Trond Myklebust's avatar Trond Myklebust

Merge branch 'rpcsec_gss-from_cel' into linux-next

* rpcsec_gss-from_cel: (21 commits)
  NFS: Retry SETCLIENTID with AUTH_SYS instead of AUTH_NONE
  NFSv4: Don't clear the machine cred when client establish returns EACCES
  NFSv4: Fix issues in nfs4_discover_server_trunking
  NFSv4: Fix the fallback to AUTH_NULL if krb5i is not available
  NFS: Use server-recommended security flavor by default (NFSv3)
  SUNRPC: Don't recognize RPC_AUTH_MAXFLAVOR
  NFS: Use "krb5i" to establish NFSv4 state whenever possible
  NFS: Try AUTH_UNIX when PUTROOTFH gets NFS4ERR_WRONGSEC
  NFS: Use static list of security flavors during root FH lookup recovery
  NFS: Avoid PUTROOTFH when managing leases
  NFS: Clean up nfs4_proc_get_rootfh
  NFS: Handle missing rpc.gssd when looking up root FH
  SUNRPC: Remove EXPORT_SYMBOL_GPL() from GSS mech switch
  SUNRPC: Make gss_mech_get() static
  SUNRPC: Refactor nfsd4_do_encode_secinfo()
  SUNRPC: Consider qop when looking up pseudoflavors
  SUNRPC: Load GSS kernel module by OID
  SUNRPC: Introduce rpcauth_get_pseudoflavor()
  SUNRPC: Define rpcsec_gss_info structure
  NFS: Remove unneeded forward declaration
  ...
parents bdeca1b7 79d852bf
...@@ -201,7 +201,9 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp, ...@@ -201,7 +201,9 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
if (clp->cl_minorversion != 0) if (clp->cl_minorversion != 0)
__set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags); __set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags);
__set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags);
error = nfs_create_rpc_client(clp, timeparms, authflavour); error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_GSS_KRB5I);
if (error == -EINVAL)
error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_NULL);
if (error < 0) if (error < 0)
goto error; goto error;
......
...@@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len, ...@@ -134,33 +134,38 @@ static size_t nfs_parse_server_name(char *string, size_t len,
return ret; return ret;
} }
/**
* nfs_find_best_sec - Find a security mechanism supported locally
* @flavors: List of security tuples returned by SECINFO procedure
*
* Return the pseudoflavor of the first security mechanism in
* "flavors" that is locally supported. Return RPC_AUTH_UNIX if
* no matching flavor is found in the array. The "flavors" array
* is searched in the order returned from the server, per RFC 3530
* recommendation.
*/
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors)
{ {
struct gss_api_mech *mech; rpc_authflavor_t pseudoflavor;
struct xdr_netobj oid; struct nfs4_secinfo4 *secinfo;
int i; unsigned int i;
rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX;
for (i = 0; i < flavors->num_flavors; i++) { for (i = 0; i < flavors->num_flavors; i++) {
struct nfs4_secinfo_flavor *flavor; secinfo = &flavors->flavors[i];
flavor = &flavors->flavors[i];
switch (secinfo->flavor) {
if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) { case RPC_AUTH_NULL:
pseudoflavor = flavor->flavor; case RPC_AUTH_UNIX:
break; case RPC_AUTH_GSS:
} else if (flavor->flavor == RPC_AUTH_GSS) { pseudoflavor = rpcauth_get_pseudoflavor(secinfo->flavor,
oid.len = flavor->gss.sec_oid4.len; &secinfo->flavor_info);
oid.data = flavor->gss.sec_oid4.data; if (pseudoflavor != RPC_AUTH_MAXFLAVOR)
mech = gss_mech_get_by_OID(&oid); return pseudoflavor;
if (!mech)
continue;
pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service);
gss_mech_put(mech);
break; break;
} }
} }
return pseudoflavor; return RPC_AUTH_UNIX;
} }
static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name) static rpc_authflavor_t nfs4_negotiate_security(struct inode *inode, struct qstr *name)
......
...@@ -2547,7 +2547,7 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl ...@@ -2547,7 +2547,7 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl
auth = rpcauth_create(flavor, server->client); auth = rpcauth_create(flavor, server->client);
if (IS_ERR(auth)) { if (IS_ERR(auth)) {
ret = -EIO; ret = -EACCES;
goto out; goto out;
} }
ret = nfs4_lookup_root(server, fhandle, info); ret = nfs4_lookup_root(server, fhandle, info);
...@@ -2555,27 +2555,36 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl ...@@ -2555,27 +2555,36 @@ static int nfs4_lookup_root_sec(struct nfs_server *server, struct nfs_fh *fhandl
return ret; return ret;
} }
/*
* Retry pseudoroot lookup with various security flavors. We do this when:
*
* NFSv4.0: the PUTROOTFH operation returns NFS4ERR_WRONGSEC
* NFSv4.1: the server does not support the SECINFO_NO_NAME operation
*
* Returns zero on success, or a negative NFS4ERR value, or a
* negative errno value.
*/
static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info) struct nfs_fsinfo *info)
{ {
int i, len, status = 0; /* Per 3530bis 15.33.5 */
rpc_authflavor_t flav_array[NFS_MAX_SECFLAVORS]; static const rpc_authflavor_t flav_array[] = {
RPC_AUTH_GSS_KRB5P,
len = rpcauth_list_flavors(flav_array, ARRAY_SIZE(flav_array)); RPC_AUTH_GSS_KRB5I,
if (len < 0) RPC_AUTH_GSS_KRB5,
return len; RPC_AUTH_UNIX, /* courtesy */
RPC_AUTH_NULL,
for (i = 0; i < len; i++) { };
/* AUTH_UNIX is the default flavor if none was specified, int status = -EPERM;
* thus has already been tried. */ size_t i;
if (flav_array[i] == RPC_AUTH_UNIX)
continue;
for (i = 0; i < ARRAY_SIZE(flav_array); i++) {
status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]); status = nfs4_lookup_root_sec(server, fhandle, info, flav_array[i]);
if (status == -NFS4ERR_WRONGSEC || status == -EACCES) if (status == -NFS4ERR_WRONGSEC || status == -EACCES)
continue; continue;
break; break;
} }
/* /*
* -EACCESS could mean that the user doesn't have correct permissions * -EACCESS could mean that the user doesn't have correct permissions
* to access the mount. It could also mean that we tried to mount * to access the mount. It could also mean that we tried to mount
...@@ -2588,24 +2597,36 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -2588,24 +2597,36 @@ static int nfs4_find_root_sec(struct nfs_server *server, struct nfs_fh *fhandle,
return status; return status;
} }
/* static int nfs4_do_find_root_sec(struct nfs_server *server,
* get the file handle for the "/" directory on the server struct nfs_fh *fhandle, struct nfs_fsinfo *info)
{
int mv = server->nfs_client->cl_minorversion;
return nfs_v4_minor_ops[mv]->find_root_sec(server, fhandle, info);
}
/**
* nfs4_proc_get_rootfh - get file handle for server's pseudoroot
* @server: initialized nfs_server handle
* @fhandle: we fill in the pseudo-fs root file handle
* @info: we fill in an FSINFO struct
*
* Returns zero on success, or a negative errno.
*/ */
int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle, int nfs4_proc_get_rootfh(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info) struct nfs_fsinfo *info)
{ {
int minor_version = server->nfs_client->cl_minorversion; int status;
int status = nfs4_lookup_root(server, fhandle, info);
if ((status == -NFS4ERR_WRONGSEC) && !(server->flags & NFS_MOUNT_SECFLAVOUR)) status = nfs4_lookup_root(server, fhandle, info);
/* if ((status == -NFS4ERR_WRONGSEC) &&
* A status of -NFS4ERR_WRONGSEC will be mapped to -EPERM !(server->flags & NFS_MOUNT_SECFLAVOUR))
* by nfs4_map_errors() as this function exits. status = nfs4_do_find_root_sec(server, fhandle, info);
*/
status = nfs_v4_minor_ops[minor_version]->find_root_sec(server, fhandle, info);
if (status == 0) if (status == 0)
status = nfs4_server_capabilities(server, fhandle); status = nfs4_server_capabilities(server, fhandle);
if (status == 0) if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info); status = nfs4_do_fsinfo(server, fhandle, info);
return nfs4_map_errors(status); return nfs4_map_errors(status);
} }
...@@ -3484,12 +3505,21 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, ...@@ -3484,12 +3505,21 @@ static int _nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo) static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fsinfo *fsinfo)
{ {
struct nfs4_exception exception = { }; struct nfs4_exception exception = { };
unsigned long now = jiffies;
int err; int err;
do { do {
err = nfs4_handle_exception(server, err = _nfs4_do_fsinfo(server, fhandle, fsinfo);
_nfs4_do_fsinfo(server, fhandle, fsinfo), if (err == 0) {
&exception); struct nfs_client *clp = server->nfs_client;
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo->lease_time * HZ;
clp->cl_last_renewal = now;
spin_unlock(&clp->cl_lock);
break;
}
err = nfs4_handle_exception(server, err, &exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
} }
...@@ -4330,27 +4360,17 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp, ...@@ -4330,27 +4360,17 @@ int nfs4_proc_setclientid_confirm(struct nfs_client *clp,
struct nfs4_setclientid_res *arg, struct nfs4_setclientid_res *arg,
struct rpc_cred *cred) struct rpc_cred *cred)
{ {
struct nfs_fsinfo fsinfo;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETCLIENTID_CONFIRM],
.rpc_argp = arg, .rpc_argp = arg,
.rpc_resp = &fsinfo,
.rpc_cred = cred, .rpc_cred = cred,
}; };
unsigned long now;
int status; int status;
dprintk("NFS call setclientid_confirm auth=%s, (client ID %llx)\n", dprintk("NFS call setclientid_confirm auth=%s, (client ID %llx)\n",
clp->cl_rpcclient->cl_auth->au_ops->au_name, clp->cl_rpcclient->cl_auth->au_ops->au_name,
clp->cl_clientid); clp->cl_clientid);
now = jiffies;
status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT); status = rpc_call_sync(clp->cl_rpcclient, &msg, RPC_TASK_TIMEOUT);
if (status == 0) {
spin_lock(&clp->cl_lock);
clp->cl_lease_time = fsinfo.lease_time * HZ;
clp->cl_last_renewal = now;
spin_unlock(&clp->cl_lock);
}
dprintk("NFS reply setclientid_confirm: %d\n", status); dprintk("NFS reply setclientid_confirm: %d\n", status);
return status; return status;
} }
......
...@@ -154,18 +154,6 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) ...@@ -154,18 +154,6 @@ struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
return cred; return cred;
} }
static void nfs4_clear_machine_cred(struct nfs_client *clp)
{
struct rpc_cred *cred;
spin_lock(&clp->cl_lock);
cred = clp->cl_machine_cred;
clp->cl_machine_cred = NULL;
spin_unlock(&clp->cl_lock);
if (cred != NULL)
put_rpccred(cred);
}
static struct rpc_cred * static struct rpc_cred *
nfs4_get_renew_cred_server_locked(struct nfs_server *server) nfs4_get_renew_cred_server_locked(struct nfs_server *server)
{ {
...@@ -1776,10 +1764,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) ...@@ -1776,10 +1764,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
return -EPERM; return -EPERM;
case -EACCES: case -EACCES:
if (clp->cl_machine_cred == NULL)
return -EACCES;
/* Handle case where the user hasn't set up machine creds */
nfs4_clear_machine_cred(clp);
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
case -ETIMEDOUT: case -ETIMEDOUT:
case -EAGAIN: case -EAGAIN:
...@@ -1874,31 +1858,18 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, ...@@ -1874,31 +1858,18 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
{ {
const struct nfs4_state_recovery_ops *ops = const struct nfs4_state_recovery_ops *ops =
clp->cl_mvops->reboot_recovery_ops; clp->cl_mvops->reboot_recovery_ops;
rpc_authflavor_t *flavors, flav, save;
struct rpc_clnt *clnt; struct rpc_clnt *clnt;
struct rpc_cred *cred; struct rpc_cred *cred;
int i, len, status; int i, status;
dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname); dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
len = NFS_MAX_SECFLAVORS;
flavors = kcalloc(len, sizeof(*flavors), GFP_KERNEL);
if (flavors == NULL) {
status = -ENOMEM;
goto out;
}
len = rpcauth_list_flavors(flavors, len);
if (len < 0) {
status = len;
goto out_free;
}
clnt = clp->cl_rpcclient; clnt = clp->cl_rpcclient;
save = clnt->cl_auth->au_flavor;
i = 0; i = 0;
mutex_lock(&nfs_clid_init_mutex); mutex_lock(&nfs_clid_init_mutex);
status = -ENOENT;
again: again:
status = -ENOENT;
cred = ops->get_clid_cred(clp); cred = ops->get_clid_cred(clp);
if (cred == NULL) if (cred == NULL)
goto out_unlock; goto out_unlock;
...@@ -1908,12 +1879,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, ...@@ -1908,12 +1879,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
switch (status) { switch (status) {
case 0: case 0:
break; break;
case -EACCES:
if (clp->cl_machine_cred == NULL)
break;
/* Handle case where the user hasn't set up machine creds */
nfs4_clear_machine_cred(clp);
case -NFS4ERR_DELAY: case -NFS4ERR_DELAY:
case -ETIMEDOUT: case -ETIMEDOUT:
case -EAGAIN: case -EAGAIN:
...@@ -1922,17 +1887,12 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, ...@@ -1922,17 +1887,12 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
dprintk("NFS: %s after status %d, retrying\n", dprintk("NFS: %s after status %d, retrying\n",
__func__, status); __func__, status);
goto again; goto again;
case -EACCES:
if (i++)
break;
case -NFS4ERR_CLID_INUSE: case -NFS4ERR_CLID_INUSE:
case -NFS4ERR_WRONGSEC: case -NFS4ERR_WRONGSEC:
status = -EPERM; clnt = rpc_clone_client_set_auth(clnt, RPC_AUTH_UNIX);
if (i >= len)
break;
flav = flavors[i++];
if (flav == save)
flav = flavors[i++];
clnt = rpc_clone_client_set_auth(clnt, flav);
if (IS_ERR(clnt)) { if (IS_ERR(clnt)) {
status = PTR_ERR(clnt); status = PTR_ERR(clnt);
break; break;
...@@ -1948,13 +1908,15 @@ int nfs4_discover_server_trunking(struct nfs_client *clp, ...@@ -1948,13 +1908,15 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
* in nfs4_exchange_id */ * in nfs4_exchange_id */
status = -EKEYEXPIRED; status = -EKEYEXPIRED;
break;
default:
pr_warn("NFS: %s unhandled error %d. Exiting with error EIO\n",
__func__, status);
status = -EIO;
} }
out_unlock: out_unlock:
mutex_unlock(&nfs_clid_init_mutex); mutex_unlock(&nfs_clid_init_mutex);
out_free:
kfree(flavors);
out:
dprintk("NFS: %s: status = %d\n", __func__, status); dprintk("NFS: %s: status = %d\n", __func__, status);
return status; return status;
} }
......
...@@ -252,6 +252,8 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name, ...@@ -252,6 +252,8 @@ struct dentry *nfs4_try_mount(int flags, const char *dev_name,
dfprintk(MOUNT, "--> nfs4_try_mount()\n"); dfprintk(MOUNT, "--> nfs4_try_mount()\n");
if (data->auth_flavors[0] == RPC_AUTH_MAXFLAVOR)
data->auth_flavors[0] = RPC_AUTH_UNIX;
export_path = data->nfs_server.export_path; export_path = data->nfs_server.export_path;
data->nfs_server.export_path = "/"; data->nfs_server.export_path = "/";
root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info, root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
......
...@@ -530,14 +530,10 @@ static int nfs4_stat_to_errno(int); ...@@ -530,14 +530,10 @@ static int nfs4_stat_to_errno(int);
decode_setclientid_maxsz) decode_setclientid_maxsz)
#define NFS4_enc_setclientid_confirm_sz \ #define NFS4_enc_setclientid_confirm_sz \
(compound_encode_hdr_maxsz + \ (compound_encode_hdr_maxsz + \
encode_setclientid_confirm_maxsz + \ encode_setclientid_confirm_maxsz)
encode_putrootfh_maxsz + \
encode_fsinfo_maxsz)
#define NFS4_dec_setclientid_confirm_sz \ #define NFS4_dec_setclientid_confirm_sz \
(compound_decode_hdr_maxsz + \ (compound_decode_hdr_maxsz + \
decode_setclientid_confirm_maxsz + \ decode_setclientid_confirm_maxsz)
decode_putrootfh_maxsz + \
decode_fsinfo_maxsz)
#define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \ #define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \
encode_sequence_maxsz + \ encode_sequence_maxsz + \
encode_putfh_maxsz + \ encode_putfh_maxsz + \
...@@ -2601,12 +2597,9 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, ...@@ -2601,12 +2597,9 @@ static void nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req,
struct compound_hdr hdr = { struct compound_hdr hdr = {
.nops = 0, .nops = 0,
}; };
const u32 lease_bitmap[3] = { FATTR4_WORD0_LEASE_TIME };
encode_compound_hdr(xdr, req, &hdr); encode_compound_hdr(xdr, req, &hdr);
encode_setclientid_confirm(xdr, arg, &hdr); encode_setclientid_confirm(xdr, arg, &hdr);
encode_putrootfh(xdr, &hdr);
encode_fsinfo(xdr, lease_bitmap, &hdr);
encode_nops(&hdr); encode_nops(&hdr);
} }
...@@ -5198,27 +5191,30 @@ static int decode_delegreturn(struct xdr_stream *xdr) ...@@ -5198,27 +5191,30 @@ static int decode_delegreturn(struct xdr_stream *xdr)
return decode_op_hdr(xdr, OP_DELEGRETURN); return decode_op_hdr(xdr, OP_DELEGRETURN);
} }
static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor *flavor) static int decode_secinfo_gss(struct xdr_stream *xdr,
struct nfs4_secinfo4 *flavor)
{ {
u32 oid_len;
__be32 *p; __be32 *p;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
flavor->gss.sec_oid4.len = be32_to_cpup(p); oid_len = be32_to_cpup(p);
if (flavor->gss.sec_oid4.len > GSS_OID_MAX_LEN) if (oid_len > GSS_OID_MAX_LEN)
goto out_err; goto out_err;
p = xdr_inline_decode(xdr, flavor->gss.sec_oid4.len); p = xdr_inline_decode(xdr, oid_len);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
memcpy(flavor->gss.sec_oid4.data, p, flavor->gss.sec_oid4.len); memcpy(flavor->flavor_info.oid.data, p, oid_len);
flavor->flavor_info.oid.len = oid_len;
p = xdr_inline_decode(xdr, 8); p = xdr_inline_decode(xdr, 8);
if (unlikely(!p)) if (unlikely(!p))
goto out_overflow; goto out_overflow;
flavor->gss.qop4 = be32_to_cpup(p++); flavor->flavor_info.qop = be32_to_cpup(p++);
flavor->gss.service = be32_to_cpup(p); flavor->flavor_info.service = be32_to_cpup(p);
return 0; return 0;
...@@ -5231,10 +5227,10 @@ static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor ...@@ -5231,10 +5227,10 @@ static int decode_secinfo_gss(struct xdr_stream *xdr, struct nfs4_secinfo_flavor
static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res) static int decode_secinfo_common(struct xdr_stream *xdr, struct nfs4_secinfo_res *res)
{ {
struct nfs4_secinfo_flavor *sec_flavor; struct nfs4_secinfo4 *sec_flavor;
unsigned int i, num_flavors;
int status; int status;
__be32 *p; __be32 *p;
int i, num_flavors;
p = xdr_inline_decode(xdr, 4); p = xdr_inline_decode(xdr, 4);
if (unlikely(!p)) if (unlikely(!p))
...@@ -6637,8 +6633,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, ...@@ -6637,8 +6633,7 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req,
* Decode SETCLIENTID_CONFIRM response * Decode SETCLIENTID_CONFIRM response
*/ */
static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
struct xdr_stream *xdr, struct xdr_stream *xdr)
struct nfs_fsinfo *fsinfo)
{ {
struct compound_hdr hdr; struct compound_hdr hdr;
int status; int status;
...@@ -6646,10 +6641,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, ...@@ -6646,10 +6641,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req,
status = decode_compound_hdr(xdr, &hdr); status = decode_compound_hdr(xdr, &hdr);
if (!status) if (!status)
status = decode_setclientid_confirm(xdr); status = decode_setclientid_confirm(xdr);
if (!status)
status = decode_putrootfh(xdr);
if (!status)
status = decode_fsinfo(xdr, fsinfo);
return status; return status;
} }
......
...@@ -917,7 +917,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void) ...@@ -917,7 +917,7 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
data->mount_server.port = NFS_UNSPEC_PORT; data->mount_server.port = NFS_UNSPEC_PORT;
data->nfs_server.port = NFS_UNSPEC_PORT; data->nfs_server.port = NFS_UNSPEC_PORT;
data->nfs_server.protocol = XPRT_TRANSPORT_TCP; data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
data->auth_flavors[0] = RPC_AUTH_UNIX; data->auth_flavors[0] = RPC_AUTH_MAXFLAVOR;
data->auth_flavor_len = 1; data->auth_flavor_len = 1;
data->minorversion = 0; data->minorversion = 0;
data->need_mount = true; data->need_mount = true;
...@@ -1605,49 +1605,57 @@ static int nfs_parse_mount_options(char *raw, ...@@ -1605,49 +1605,57 @@ static int nfs_parse_mount_options(char *raw,
} }
/* /*
* Match the requested auth flavors with the list returned by * Select a security flavor for this mount. The selected flavor
* the server. Returns zero and sets the mount's authentication * is planted in args->auth_flavors[0].
* flavor on success; returns -EACCES if server does not support
* the requested flavor.
*/ */
static int nfs_walk_authlist(struct nfs_parsed_mount_data *args, static void nfs_select_flavor(struct nfs_parsed_mount_data *args,
struct nfs_mount_request *request) struct nfs_mount_request *request)
{ {
unsigned int i, j, server_authlist_len = *(request->auth_flav_len); unsigned int i, count = *(request->auth_flav_len);
rpc_authflavor_t flavor;
if (args->auth_flavors[0] != RPC_AUTH_MAXFLAVOR)
goto out;
/*
* The NFSv2 MNT operation does not return a flavor list.
*/
if (args->mount_server.version != NFS_MNT3_VERSION)
goto out_default;
/* /*
* Certain releases of Linux's mountd return an empty * Certain releases of Linux's mountd return an empty
* flavor list. To prevent behavioral regression with * flavor list in some cases.
* these servers (ie. rejecting mounts that used to
* succeed), revert to pre-2.6.32 behavior (no checking)
* if the returned flavor list is empty.
*/ */
if (server_authlist_len == 0) if (count == 0)
return 0; goto out_default;
/* /*
* We avoid sophisticated negotiating here, as there are
* plenty of cases where we can get it wrong, providing
* either too little or too much security.
*
* RFC 2623, section 2.7 suggests we SHOULD prefer the * RFC 2623, section 2.7 suggests we SHOULD prefer the
* flavor listed first. However, some servers list * flavor listed first. However, some servers list
* AUTH_NULL first. Our caller plants AUTH_SYS, the * AUTH_NULL first. Avoid ever choosing AUTH_NULL.
* preferred default, in args->auth_flavors[0] if user
* didn't specify sec= mount option.
*/ */
for (i = 0; i < args->auth_flavor_len; i++) for (i = 0; i < count; i++) {
for (j = 0; j < server_authlist_len; j++) struct rpcsec_gss_info info;
if (args->auth_flavors[i] == request->auth_flavs[j]) {
dfprintk(MOUNT, "NFS: using auth flavor %d\n", flavor = request->auth_flavs[i];
request->auth_flavs[j]); switch (flavor) {
args->auth_flavors[0] = request->auth_flavs[j]; case RPC_AUTH_UNIX:
return 0; goto out_set;
} case RPC_AUTH_NULL:
continue;
default:
if (rpcauth_get_gssinfo(flavor, &info) == 0)
goto out_set;
}
}
dfprintk(MOUNT, "NFS: server does not support requested auth flavor\n"); out_default:
nfs_umount(request); flavor = RPC_AUTH_UNIX;
return -EACCES; out_set:
args->auth_flavors[0] = flavor;
out:
dfprintk(MOUNT, "NFS: using auth flavor %d\n", args->auth_flavors[0]);
} }
/* /*
...@@ -1710,12 +1718,8 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args, ...@@ -1710,12 +1718,8 @@ static int nfs_request_mount(struct nfs_parsed_mount_data *args,
return status; return status;
} }
/* nfs_select_flavor(args, &request);
* MNTv1 (NFSv2) does not support auth flavor negotiation. return 0;
*/
if (args->mount_server.version != NFS_MNT3_VERSION)
return 0;
return nfs_walk_authlist(args, &request);
} }
struct dentry *nfs_try_mount(int flags, const char *dev_name, struct dentry *nfs_try_mount(int flags, const char *dev_name,
......
...@@ -3138,10 +3138,9 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_ ...@@ -3138,10 +3138,9 @@ nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
static __be32 static __be32
nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
__be32 nfserr,struct svc_export *exp) __be32 nfserr, struct svc_export *exp)
{ {
int i = 0; u32 i, nflavs;
u32 nflavs;
struct exp_flavor_info *flavs; struct exp_flavor_info *flavs;
struct exp_flavor_info def_flavs[2]; struct exp_flavor_info def_flavs[2];
__be32 *p; __be32 *p;
...@@ -3172,30 +3171,29 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp, ...@@ -3172,30 +3171,29 @@ nfsd4_do_encode_secinfo(struct nfsd4_compoundres *resp,
WRITE32(nflavs); WRITE32(nflavs);
ADJUST_ARGS(); ADJUST_ARGS();
for (i = 0; i < nflavs; i++) { for (i = 0; i < nflavs; i++) {
u32 flav = flavs[i].pseudoflavor; struct rpcsec_gss_info info;
struct gss_api_mech *gm = gss_mech_get_by_pseudoflavor(flav);
if (gm) { if (rpcauth_get_gssinfo(flavs[i].pseudoflavor, &info) == 0) {
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(RPC_AUTH_GSS); WRITE32(RPC_AUTH_GSS);
ADJUST_ARGS(); ADJUST_ARGS();
RESERVE_SPACE(4 + gm->gm_oid.len); RESERVE_SPACE(4 + info.oid.len);
WRITE32(gm->gm_oid.len); WRITE32(info.oid.len);
WRITEMEM(gm->gm_oid.data, gm->gm_oid.len); WRITEMEM(info.oid.data, info.oid.len);
ADJUST_ARGS(); ADJUST_ARGS();
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(0); /* qop */ WRITE32(info.qop);
ADJUST_ARGS(); ADJUST_ARGS();
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(gss_pseudoflavor_to_service(gm, flav)); WRITE32(info.service);
ADJUST_ARGS(); ADJUST_ARGS();
gss_mech_put(gm);
} else { } else {
RESERVE_SPACE(4); RESERVE_SPACE(4);
WRITE32(flav); WRITE32(flavs[i].pseudoflavor);
ADJUST_ARGS(); ADJUST_ARGS();
} }
} }
out: out:
if (exp) if (exp)
exp_put(exp); exp_put(exp);
......
...@@ -14,9 +14,6 @@ ...@@ -14,9 +14,6 @@
#define NFS_DEF_FILE_IO_SIZE (4096U) #define NFS_DEF_FILE_IO_SIZE (4096U)
#define NFS_MIN_FILE_IO_SIZE (1024U) #define NFS_MIN_FILE_IO_SIZE (1024U)
/* Forward declaration for NFS v3 */
struct nfs4_secinfo_flavors;
struct nfs4_string { struct nfs4_string {
unsigned int len; unsigned int len;
char *data; char *data;
...@@ -1053,25 +1050,14 @@ struct nfs4_fs_locations_res { ...@@ -1053,25 +1050,14 @@ struct nfs4_fs_locations_res {
struct nfs4_fs_locations *fs_locations; struct nfs4_fs_locations *fs_locations;
}; };
struct nfs4_secinfo_oid { struct nfs4_secinfo4 {
unsigned int len; u32 flavor;
char data[GSS_OID_MAX_LEN]; struct rpcsec_gss_info flavor_info;
};
struct nfs4_secinfo_gss {
struct nfs4_secinfo_oid sec_oid4;
unsigned int qop4;
unsigned int service;
};
struct nfs4_secinfo_flavor {
unsigned int flavor;
struct nfs4_secinfo_gss gss;
}; };
struct nfs4_secinfo_flavors { struct nfs4_secinfo_flavors {
unsigned int num_flavors; unsigned int num_flavors;
struct nfs4_secinfo_flavor flavors[0]; struct nfs4_secinfo4 flavors[0];
}; };
struct nfs4_secinfo_arg { struct nfs4_secinfo_arg {
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
/* size of the nodename buffer */ /* size of the nodename buffer */
#define UNX_MAXNODENAME 32 #define UNX_MAXNODENAME 32
struct rpcsec_gss_info;
/* Work around the lack of a VFS credential */ /* Work around the lack of a VFS credential */
struct auth_cred { struct auth_cred {
kuid_t uid; kuid_t uid;
...@@ -103,6 +105,9 @@ struct rpc_authops { ...@@ -103,6 +105,9 @@ struct rpc_authops {
int (*pipes_create)(struct rpc_auth *); int (*pipes_create)(struct rpc_auth *);
void (*pipes_destroy)(struct rpc_auth *); void (*pipes_destroy)(struct rpc_auth *);
int (*list_pseudoflavors)(rpc_authflavor_t *, int); int (*list_pseudoflavors)(rpc_authflavor_t *, int);
rpc_authflavor_t (*info2flavor)(struct rpcsec_gss_info *);
int (*flavor2info)(rpc_authflavor_t,
struct rpcsec_gss_info *);
}; };
struct rpc_credops { struct rpc_credops {
...@@ -137,6 +142,10 @@ int rpcauth_register(const struct rpc_authops *); ...@@ -137,6 +142,10 @@ int rpcauth_register(const struct rpc_authops *);
int rpcauth_unregister(const struct rpc_authops *); int rpcauth_unregister(const struct rpc_authops *);
struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *); struct rpc_auth * rpcauth_create(rpc_authflavor_t, struct rpc_clnt *);
void rpcauth_release(struct rpc_auth *); void rpcauth_release(struct rpc_auth *);
rpc_authflavor_t rpcauth_get_pseudoflavor(rpc_authflavor_t,
struct rpcsec_gss_info *);
int rpcauth_get_gssinfo(rpc_authflavor_t,
struct rpcsec_gss_info *);
int rpcauth_list_flavors(rpc_authflavor_t *, int); int rpcauth_list_flavors(rpc_authflavor_t *, int);
struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int); struct rpc_cred * rpcauth_lookup_credcache(struct rpc_auth *, struct auth_cred *, int);
void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *); void rpcauth_init_cred(struct rpc_cred *, const struct auth_cred *, struct rpc_auth *, const struct rpc_credops *);
......
...@@ -25,10 +25,21 @@ struct gss_ctx { ...@@ -25,10 +25,21 @@ struct gss_ctx {
#define GSS_C_NO_BUFFER ((struct xdr_netobj) 0) #define GSS_C_NO_BUFFER ((struct xdr_netobj) 0)
#define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0) #define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0)
#define GSS_C_NULL_OID ((struct xdr_netobj) 0) #define GSS_C_QOP_DEFAULT (0)
/*XXX arbitrary length - is this set somewhere? */ /*XXX arbitrary length - is this set somewhere? */
#define GSS_OID_MAX_LEN 32 #define GSS_OID_MAX_LEN 32
struct rpcsec_gss_oid {
unsigned int len;
u8 data[GSS_OID_MAX_LEN];
};
/* From RFC 3530 */
struct rpcsec_gss_info {
struct rpcsec_gss_oid oid;
u32 qop;
u32 service;
};
/* gss-api prototypes; note that these are somewhat simplified versions of /* gss-api prototypes; note that these are somewhat simplified versions of
* the prototypes specified in RFC 2744. */ * the prototypes specified in RFC 2744. */
...@@ -58,12 +69,14 @@ u32 gss_unwrap( ...@@ -58,12 +69,14 @@ u32 gss_unwrap(
u32 gss_delete_sec_context( u32 gss_delete_sec_context(
struct gss_ctx **ctx_id); struct gss_ctx **ctx_id);
u32 gss_svc_to_pseudoflavor(struct gss_api_mech *, u32 service); rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *, u32 qop,
u32 service);
u32 gss_pseudoflavor_to_service(struct gss_api_mech *, u32 pseudoflavor); u32 gss_pseudoflavor_to_service(struct gss_api_mech *, u32 pseudoflavor);
char *gss_service_to_auth_domain_name(struct gss_api_mech *, u32 service); char *gss_service_to_auth_domain_name(struct gss_api_mech *, u32 service);
struct pf_desc { struct pf_desc {
u32 pseudoflavor; u32 pseudoflavor;
u32 qop;
u32 service; u32 service;
char *name; char *name;
char *auth_domain_name; char *auth_domain_name;
...@@ -76,7 +89,7 @@ struct pf_desc { ...@@ -76,7 +89,7 @@ struct pf_desc {
struct gss_api_mech { struct gss_api_mech {
struct list_head gm_list; struct list_head gm_list;
struct module *gm_owner; struct module *gm_owner;
struct xdr_netobj gm_oid; struct rpcsec_gss_oid gm_oid;
char *gm_name; char *gm_name;
const struct gss_api_ops *gm_ops; const struct gss_api_ops *gm_ops;
/* pseudoflavors supported by this mechanism: */ /* pseudoflavors supported by this mechanism: */
...@@ -117,9 +130,11 @@ struct gss_api_ops { ...@@ -117,9 +130,11 @@ struct gss_api_ops {
int gss_mech_register(struct gss_api_mech *); int gss_mech_register(struct gss_api_mech *);
void gss_mech_unregister(struct gss_api_mech *); void gss_mech_unregister(struct gss_api_mech *);
/* returns a mechanism descriptor given an OID, and increments the mechanism's /* Given a GSS security tuple, look up a pseudoflavor */
* reference count. */ rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *);
struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
/* Given a pseudoflavor, look up a GSS security tuple */
int gss_mech_flavor2info(rpc_authflavor_t, struct rpcsec_gss_info *);
/* Returns a reference to a mechanism, given a name like "krb5" etc. */ /* Returns a reference to a mechanism, given a name like "krb5" etc. */
struct gss_api_mech *gss_mech_get_by_name(const char *); struct gss_api_mech *gss_mech_get_by_name(const char *);
...@@ -130,9 +145,6 @@ struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32); ...@@ -130,9 +145,6 @@ struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
/* Fill in an array with a list of supported pseudoflavors */ /* Fill in an array with a list of supported pseudoflavors */
int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int); int gss_mech_list_pseudoflavors(rpc_authflavor_t *, int);
/* Just increments the mechanism's reference count and returns its input: */
struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
/* For every successful gss_mech_get or gss_mech_get_by_* call there must be a /* For every successful gss_mech_get or gss_mech_get_by_* call there must be a
* corresponding call to gss_mech_put. */ * corresponding call to gss_mech_put. */
void gss_mech_put(struct gss_api_mech *); void gss_mech_put(struct gss_api_mech *);
......
...@@ -3,6 +3,7 @@ config SUNRPC ...@@ -3,6 +3,7 @@ config SUNRPC
config SUNRPC_GSS config SUNRPC_GSS
tristate tristate
select OID_REGISTRY
config SUNRPC_BACKCHANNEL config SUNRPC_BACKCHANNEL
bool bool
......
...@@ -82,7 +82,7 @@ MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size"); ...@@ -82,7 +82,7 @@ MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
static u32 static u32
pseudoflavor_to_flavor(u32 flavor) { pseudoflavor_to_flavor(u32 flavor) {
if (flavor >= RPC_AUTH_MAXFLAVOR) if (flavor > RPC_AUTH_MAXFLAVOR)
return RPC_AUTH_GSS; return RPC_AUTH_GSS;
return flavor; return flavor;
} }
...@@ -123,6 +123,79 @@ rpcauth_unregister(const struct rpc_authops *ops) ...@@ -123,6 +123,79 @@ rpcauth_unregister(const struct rpc_authops *ops)
} }
EXPORT_SYMBOL_GPL(rpcauth_unregister); EXPORT_SYMBOL_GPL(rpcauth_unregister);
/**
* rpcauth_get_pseudoflavor - check if security flavor is supported
* @flavor: a security flavor
* @info: a GSS mech OID, quality of protection, and service value
*
* Verifies that an appropriate kernel module is available or already loaded.
* Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
* not supported locally.
*/
rpc_authflavor_t
rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
{
const struct rpc_authops *ops;
rpc_authflavor_t pseudoflavor;
ops = auth_flavors[flavor];
if (ops == NULL)
request_module("rpc-auth-%u", flavor);
spin_lock(&rpc_authflavor_lock);
ops = auth_flavors[flavor];
if (ops == NULL || !try_module_get(ops->owner)) {
spin_unlock(&rpc_authflavor_lock);
return RPC_AUTH_MAXFLAVOR;
}
spin_unlock(&rpc_authflavor_lock);
pseudoflavor = flavor;
if (ops->info2flavor != NULL)
pseudoflavor = ops->info2flavor(info);
module_put(ops->owner);
return pseudoflavor;
}
EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
/**
* rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
* @pseudoflavor: GSS pseudoflavor to match
* @info: rpcsec_gss_info structure to fill in
*
* Returns zero and fills in "info" if pseudoflavor matches a
* supported mechanism.
*/
int
rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
{
rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
const struct rpc_authops *ops;
int result;
if (flavor >= RPC_AUTH_MAXFLAVOR)
return -EINVAL;
ops = auth_flavors[flavor];
if (ops == NULL)
request_module("rpc-auth-%u", flavor);
spin_lock(&rpc_authflavor_lock);
ops = auth_flavors[flavor];
if (ops == NULL || !try_module_get(ops->owner)) {
spin_unlock(&rpc_authflavor_lock);
return -ENOENT;
}
spin_unlock(&rpc_authflavor_lock);
result = -ENOENT;
if (ops->flavor2info != NULL)
result = ops->flavor2info(pseudoflavor, info);
module_put(ops->owner);
return result;
}
EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
/** /**
* rpcauth_list_flavors - discover registered flavors and pseudoflavors * rpcauth_list_flavors - discover registered flavors and pseudoflavors
* @array: array to fill in * @array: array to fill in
......
...@@ -1641,6 +1641,8 @@ static const struct rpc_authops authgss_ops = { ...@@ -1641,6 +1641,8 @@ static const struct rpc_authops authgss_ops = {
.pipes_create = gss_pipes_dentries_create, .pipes_create = gss_pipes_dentries_create,
.pipes_destroy = gss_pipes_dentries_destroy, .pipes_destroy = gss_pipes_dentries_destroy,
.list_pseudoflavors = gss_mech_list_pseudoflavors, .list_pseudoflavors = gss_mech_list_pseudoflavors,
.info2flavor = gss_mech_info2flavor,
.flavor2info = gss_mech_flavor2info,
}; };
static const struct rpc_credops gss_credops = { static const struct rpc_credops gss_credops = {
...@@ -1733,6 +1735,7 @@ static void __exit exit_rpcsec_gss(void) ...@@ -1733,6 +1735,7 @@ static void __exit exit_rpcsec_gss(void)
rcu_barrier(); /* Wait for completion of call_rcu()'s */ rcu_barrier(); /* Wait for completion of call_rcu()'s */
} }
MODULE_ALIAS("rpc-auth-6");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_param_named(expired_cred_retry_delay, module_param_named(expired_cred_retry_delay,
gss_expired_cred_retry_delay, gss_expired_cred_retry_delay,
......
...@@ -729,16 +729,19 @@ static const struct gss_api_ops gss_kerberos_ops = { ...@@ -729,16 +729,19 @@ static const struct gss_api_ops gss_kerberos_ops = {
static struct pf_desc gss_kerberos_pfs[] = { static struct pf_desc gss_kerberos_pfs[] = {
[0] = { [0] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5, .pseudoflavor = RPC_AUTH_GSS_KRB5,
.qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_NONE, .service = RPC_GSS_SVC_NONE,
.name = "krb5", .name = "krb5",
}, },
[1] = { [1] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5I, .pseudoflavor = RPC_AUTH_GSS_KRB5I,
.qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_INTEGRITY, .service = RPC_GSS_SVC_INTEGRITY,
.name = "krb5i", .name = "krb5i",
}, },
[2] = { [2] = {
.pseudoflavor = RPC_AUTH_GSS_KRB5P, .pseudoflavor = RPC_AUTH_GSS_KRB5P,
.qop = GSS_C_QOP_DEFAULT,
.service = RPC_GSS_SVC_PRIVACY, .service = RPC_GSS_SVC_PRIVACY,
.name = "krb5p", .name = "krb5p",
}, },
...@@ -750,11 +753,12 @@ MODULE_ALIAS("rpc-auth-gss-krb5p"); ...@@ -750,11 +753,12 @@ MODULE_ALIAS("rpc-auth-gss-krb5p");
MODULE_ALIAS("rpc-auth-gss-390003"); MODULE_ALIAS("rpc-auth-gss-390003");
MODULE_ALIAS("rpc-auth-gss-390004"); MODULE_ALIAS("rpc-auth-gss-390004");
MODULE_ALIAS("rpc-auth-gss-390005"); MODULE_ALIAS("rpc-auth-gss-390005");
MODULE_ALIAS("rpc-auth-gss-1.2.840.113554.1.2.2");
static struct gss_api_mech gss_kerberos_mech = { static struct gss_api_mech gss_kerberos_mech = {
.gm_name = "krb5", .gm_name = "krb5",
.gm_owner = THIS_MODULE, .gm_owner = THIS_MODULE,
.gm_oid = {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}, .gm_oid = { 9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" },
.gm_ops = &gss_kerberos_ops, .gm_ops = &gss_kerberos_ops,
.gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs), .gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs),
.gm_pfs = gss_kerberos_pfs, .gm_pfs = gss_kerberos_pfs,
......
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/oid_registry.h>
#include <linux/sunrpc/msg_prot.h> #include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/gss_asn1.h> #include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/auth_gss.h> #include <linux/sunrpc/auth_gss.h>
...@@ -102,8 +103,13 @@ gss_mech_svc_setup(struct gss_api_mech *gm) ...@@ -102,8 +103,13 @@ gss_mech_svc_setup(struct gss_api_mech *gm)
return status; return status;
} }
int /**
gss_mech_register(struct gss_api_mech *gm) * gss_mech_register - register a GSS mechanism
* @gm: GSS mechanism handle
*
* Returns zero if successful, or a negative errno.
*/
int gss_mech_register(struct gss_api_mech *gm)
{ {
int status; int status;
...@@ -116,11 +122,14 @@ gss_mech_register(struct gss_api_mech *gm) ...@@ -116,11 +122,14 @@ gss_mech_register(struct gss_api_mech *gm)
dprintk("RPC: registered gss mechanism %s\n", gm->gm_name); dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(gss_mech_register); EXPORT_SYMBOL_GPL(gss_mech_register);
void /**
gss_mech_unregister(struct gss_api_mech *gm) * gss_mech_unregister - release a GSS mechanism
* @gm: GSS mechanism handle
*
*/
void gss_mech_unregister(struct gss_api_mech *gm)
{ {
spin_lock(&registered_mechs_lock); spin_lock(&registered_mechs_lock);
list_del(&gm->gm_list); list_del(&gm->gm_list);
...@@ -128,18 +137,14 @@ gss_mech_unregister(struct gss_api_mech *gm) ...@@ -128,18 +137,14 @@ gss_mech_unregister(struct gss_api_mech *gm)
dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name); dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
gss_mech_free(gm); gss_mech_free(gm);
} }
EXPORT_SYMBOL_GPL(gss_mech_unregister); EXPORT_SYMBOL_GPL(gss_mech_unregister);
struct gss_api_mech * static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
gss_mech_get(struct gss_api_mech *gm)
{ {
__module_get(gm->gm_owner); __module_get(gm->gm_owner);
return gm; return gm;
} }
EXPORT_SYMBOL_GPL(gss_mech_get);
static struct gss_api_mech * static struct gss_api_mech *
_gss_mech_get_by_name(const char *name) _gss_mech_get_by_name(const char *name)
{ {
...@@ -169,12 +174,16 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name) ...@@ -169,12 +174,16 @@ struct gss_api_mech * gss_mech_get_by_name(const char *name)
} }
return gm; return gm;
} }
EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
struct gss_api_mech * static struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
gss_mech_get_by_OID(struct xdr_netobj *obj)
{ {
struct gss_api_mech *pos, *gm = NULL; struct gss_api_mech *pos, *gm = NULL;
char buf[32];
if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
return NULL;
dprintk("RPC: %s(%s)\n", __func__, buf);
request_module("rpc-auth-gss-%s", buf);
spin_lock(&registered_mechs_lock); spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) { list_for_each_entry(pos, &registered_mechs, gm_list) {
...@@ -188,11 +197,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj) ...@@ -188,11 +197,8 @@ gss_mech_get_by_OID(struct xdr_netobj *obj)
} }
spin_unlock(&registered_mechs_lock); spin_unlock(&registered_mechs_lock);
return gm; return gm;
} }
EXPORT_SYMBOL_GPL(gss_mech_get_by_OID);
static inline int static inline int
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor) mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
{ {
...@@ -237,8 +243,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor) ...@@ -237,8 +243,6 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
return gm; return gm;
} }
EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
/** /**
* gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
* @array: array to fill in * @array: array to fill in
...@@ -268,19 +272,82 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size) ...@@ -268,19 +272,82 @@ int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
return i; return i;
} }
u32 /**
gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service) * gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
* @gm: GSS mechanism handle
* @qop: GSS quality-of-protection value
* @service: GSS service value
*
* Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found.
*/
rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop,
u32 service)
{ {
int i; int i;
for (i = 0; i < gm->gm_pf_num; i++) { for (i = 0; i < gm->gm_pf_num; i++) {
if (gm->gm_pfs[i].service == service) { if (gm->gm_pfs[i].qop == qop &&
gm->gm_pfs[i].service == service) {
return gm->gm_pfs[i].pseudoflavor; return gm->gm_pfs[i].pseudoflavor;
} }
} }
return RPC_AUTH_MAXFLAVOR; /* illegal value */ return RPC_AUTH_MAXFLAVOR;
}
/**
* gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
* @info: a GSS mech OID, quality of protection, and service value
*
* Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
* not supported.
*/
rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
{
rpc_authflavor_t pseudoflavor;
struct gss_api_mech *gm;
gm = gss_mech_get_by_OID(&info->oid);
if (gm == NULL)
return RPC_AUTH_MAXFLAVOR;
pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service);
gss_mech_put(gm);
return pseudoflavor;
}
/**
* gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
* @pseudoflavor: GSS pseudoflavor to match
* @info: rpcsec_gss_info structure to fill in
*
* Returns zero and fills in "info" if pseudoflavor matches a
* supported mechanism. Otherwise a negative errno is returned.
*/
int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor,
struct rpcsec_gss_info *info)
{
struct gss_api_mech *gm;
int i;
gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
if (gm == NULL)
return -ENOENT;
for (i = 0; i < gm->gm_pf_num; i++) {
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
info->oid.len = gm->gm_oid.len;
info->qop = gm->gm_pfs[i].qop;
info->service = gm->gm_pfs[i].service;
gss_mech_put(gm);
return 0;
}
}
gss_mech_put(gm);
return -ENOENT;
} }
EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
u32 u32
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
...@@ -294,8 +361,6 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor) ...@@ -294,8 +361,6 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service);
char * char *
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
{ {
...@@ -308,8 +373,6 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service) ...@@ -308,8 +373,6 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name);
void void
gss_mech_put(struct gss_api_mech * gm) gss_mech_put(struct gss_api_mech * gm)
{ {
...@@ -317,8 +380,6 @@ gss_mech_put(struct gss_api_mech * gm) ...@@ -317,8 +380,6 @@ gss_mech_put(struct gss_api_mech * gm)
module_put(gm->gm_owner); module_put(gm->gm_owner);
} }
EXPORT_SYMBOL_GPL(gss_mech_put);
/* The mech could probably be determined from the token instead, but it's just /* The mech could probably be determined from the token instead, but it's just
* as easy for now to pass it in. */ * as easy for now to pass it in. */
int int
......
...@@ -1216,7 +1216,9 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) ...@@ -1216,7 +1216,9 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)
svcdata->rsci = rsci; svcdata->rsci = rsci;
cache_get(&rsci->h); cache_get(&rsci->h);
rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor( rqstp->rq_cred.cr_flavor = gss_svc_to_pseudoflavor(
rsci->mechctx->mech_type, gc->gc_svc); rsci->mechctx->mech_type,
GSS_C_QOP_DEFAULT,
gc->gc_svc);
ret = SVC_OK; ret = SVC_OK;
goto out; goto out;
} }
......
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