Commit 47737de1 authored by Trond Myklebust's avatar Trond Myklebust

RPCSEC_GSS: Fix module reference counting.

    Clean up the interface to the GSSAPI code.
                                                                                
Patch by Bruce Fields
parent 71aaeb7f
......@@ -50,46 +50,36 @@ u32 gss_verify_mic(
u32 gss_delete_sec_context(
struct gss_ctx **ctx_id);
/* We maintain a list of the pseudoflavors (equivalently, mechanism-qop-service
* triples) that we currently support: */
struct sup_sec_triple {
struct list_head triples;
u32 pseudoflavor;
struct gss_api_mech *mech;
u32 qop;
u32 service;
struct gss_api_mech * gss_mech_get_by_name(char *name);
struct gss_api_mech * gss_mech_get_by_pseudoflavor(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);
struct pf_desc {
u32 pseudoflavor;
u32 qop;
u32 service;
char *name;
char *auth_domain_name;
};
int gss_register_triple(u32 pseudoflavor, struct gss_api_mech *mech, u32 qop,
u32 service);
int gss_unregister_triple(u32 pseudoflavor);
int gss_pseudoflavor_supported(u32 pseudoflavor);
u32 gss_cmp_triples(u32 oid_len, char *oid_data, u32 qop, u32 service);
u32 gss_get_pseudoflavor(struct gss_ctx *ctx_id, u32 qop, u32 service);
u32 gss_pseudoflavor_to_service(u32 pseudoflavor);
/* Both return NULL on failure: */
struct gss_api_mech * gss_pseudoflavor_to_mech(u32 pseudoflavor);
int gss_pseudoflavor_to_mechOID(u32 pseudoflavor, struct xdr_netobj *mech);
/* Different mechanisms (e.g., krb5 or spkm3) may implement gss-api, and
* mechanisms may be dynamically registered or unregistered by modules.
* Our only built-in mechanism is a trivial debugging mechanism that provides
* no actual security; the following function registers that mechanism: */
void gss_mech_register_debug(void);
* mechanisms may be dynamically registered or unregistered by modules. */
/* Each mechanism is described by the following struct: */
struct gss_api_mech {
struct xdr_netobj gm_oid;
struct list_head gm_list;
atomic_t gm_count;
struct module *gm_owner;
struct xdr_netobj gm_oid;
char *gm_name;
struct gss_api_ops *gm_ops;
/* pseudoflavors supported by this mechanism: */
int gm_pf_num;
struct pf_desc gm_pfs[];
};
/* and must provide the following operations: */
struct gss_api_ops {
char *name;
u32 (*gss_import_sec_context)(
struct xdr_netobj *input_token,
struct gss_ctx *ctx_id);
......@@ -107,29 +97,25 @@ struct gss_api_ops {
void *internal_ctx_id);
};
/* Returns nonzero on failure. */
int gss_mech_register(struct xdr_netobj *, struct gss_api_ops *);
int gss_mech_register(struct gss_api_mech *);
void gss_mech_unregister(struct gss_api_mech *);
/* Returns nonzero iff someone still has a reference to this mech. */
int gss_mech_unregister(struct gss_api_mech *);
/* Returns nonzer iff someone still has a reference to some mech. */
int gss_mech_unregister_all(void);
/* returns a mechanism descriptor given an OID, an increments the mechanism's
/* returns a mechanism descriptor given an OID, and increments the mechanism's
* reference count. */
struct gss_api_mech * gss_mech_get_by_OID(struct xdr_netobj *);
/* Similar, but get by name like "krb5", "spkm", etc., instead of OID. */
/* Returns a reference to a mechanism, given a name like "krb5" etc. */
struct gss_api_mech *gss_mech_get_by_name(char *);
/* Similar, but get by pseudoflavor. */
struct gss_api_mech *gss_mech_get_by_pseudoflavor(u32);
/* Just increments the mechanism's reference count and returns its input: */
struct gss_api_mech * gss_mech_get(struct gss_api_mech *);
/* Returns nonzero iff you've released the last reference to this mech.
* Note that for every succesful gss_get_mech call there must be exactly
* one corresponding call to gss_mech_put.*/
int gss_mech_put(struct gss_api_mech *);
/* For every succesful gss_mech_get or gss_mech_get_by_* call there must be a
* corresponding call to gss_mech_put. */
void gss_mech_put(struct gss_api_mech *);
#endif /* __KERNEL__ */
#endif /* _LINUX_SUNRPC_GSS_API_H */
......
......@@ -4,7 +4,7 @@
obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
auth_rpcgss-objs := auth_gss.o gss_pseudoflavors.o gss_generic_token.o \
auth_rpcgss-objs := auth_gss.o gss_generic_token.o \
sunrpcgss_syms.o gss_mech_switch.o svcauth_gss.o gss_krb5_crypto.o
obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
......
......@@ -559,7 +559,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
goto out_dec;
gss_auth->mech = gss_pseudoflavor_to_mech(flavor);
gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);
if (!gss_auth->mech) {
printk(KERN_WARNING "%s: Pseudoflavor %d not found!",
__FUNCTION__, flavor);
......@@ -578,7 +578,7 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
snprintf(gss_auth->path, sizeof(gss_auth->path), "%s/%s",
clnt->cl_pathname,
gss_auth->mech->gm_ops->name);
gss_auth->mech->gm_name);
gss_auth->dentry = rpc_mkpipe(gss_auth->path, clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
if (IS_ERR(gss_auth->dentry))
goto err_free;
......@@ -696,7 +696,8 @@ gss_marshal(struct rpc_task *task, u32 *p, int ruid)
*p++ = htonl(RPC_AUTH_GSS);
cred_len = p++;
service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
service = gss_pseudoflavor_to_service(ctx->gc_gss_ctx->mech_type,
gss_cred->gc_flavor);
if (service == 0) {
dprintk("RPC: %4u Bad pseudoflavor %d in gss_marshal\n",
task->tk_pid, gss_cred->gc_flavor);
......@@ -785,7 +786,8 @@ gss_validate(struct rpc_task *task, u32 *p)
if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state))
goto out_bad;
service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
service = gss_pseudoflavor_to_service(ctx->gc_gss_ctx->mech_type,
gss_cred->gc_flavor);
switch (service) {
case RPC_GSS_SVC_NONE:
/* verifier data, flavor, length: */
......@@ -836,7 +838,8 @@ gss_wrap_req(struct rpc_task *task,
status = encode(rqstp, p, obj);
goto out;
}
service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
service = gss_pseudoflavor_to_service(ctx->gc_gss_ctx->mech_type,
gss_cred->gc_flavor);
switch (service) {
case RPC_GSS_SVC_NONE:
status = encode(rqstp, p, obj);
......@@ -908,7 +911,8 @@ gss_unwrap_resp(struct rpc_task *task,
if (ctx->gc_proc != RPC_GSS_PROC_DATA)
goto out_decode;
service = gss_pseudoflavor_to_service(gss_cred->gc_flavor);
service = gss_pseudoflavor_to_service(ctx->gc_gss_ctx->mech_type,
gss_cred->gc_flavor);
switch (service) {
case RPC_GSS_SVC_NONE:
goto out_decode;
......@@ -999,7 +1003,6 @@ static int __init init_rpcsec_gss(void)
static void __exit exit_rpcsec_gss(void)
{
gss_svc_shutdown();
gss_mech_unregister_all();
rpcauth_unregister(&authgss_ops);
}
......
......@@ -40,7 +40,6 @@
#include <linux/slab.h>
#include <linux/sunrpc/auth.h>
#include <linux/in.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_krb5.h>
#include <linux/sunrpc/xdr.h>
#include <linux/crypto.h>
......@@ -217,35 +216,36 @@ gss_get_mic_kerberos(struct gss_ctx *ctx,
}
static struct gss_api_ops gss_kerberos_ops = {
.name = "krb5",
.gss_import_sec_context = gss_import_sec_context_kerberos,
.gss_get_mic = gss_get_mic_kerberos,
.gss_verify_mic = gss_verify_mic_kerberos,
.gss_delete_sec_context = gss_delete_sec_context_kerberos,
};
/* XXX error checking? reference counting? */
static struct gss_api_mech gss_kerberos_mech = {
.gm_name = "krb5",
.gm_owner = THIS_MODULE,
.gm_ops = &gss_kerberos_ops,
.gm_pf_num = 2,
.gm_pfs = {
{RPC_AUTH_GSS_KRB5, 0, RPC_GSS_SVC_NONE, "krb5"},
{RPC_AUTH_GSS_KRB5I, 0, RPC_GSS_SVC_INTEGRITY, "krb5i"},
},
};
static int __init init_kerberos_module(void)
{
struct gss_api_mech *gm;
int status;
if (gss_mech_register(&gss_mech_krb5_oid, &gss_kerberos_ops))
status = gss_mech_register(&gss_kerberos_mech);
if (status)
printk("Failed to register kerberos gss mechanism!\n");
gm = gss_mech_get_by_OID(&gss_mech_krb5_oid);
gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE);
gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY);
if (svcauth_gss_register_pseudoflavor(RPC_AUTH_GSS_KRB5, "krb5"))
printk("Failed to register %s with server!\n", "krb5");
if (svcauth_gss_register_pseudoflavor(RPC_AUTH_GSS_KRB5I, "krb5i"))
printk("Failed to register %s with server!\n", "krb5i");
gss_mech_put(gm);
return 0;
return status;
}
static void __exit cleanup_kerberos_module(void)
{
gss_unregister_triple(RPC_AUTH_GSS_KRB5I);
gss_unregister_triple(RPC_AUTH_GSS_KRB5);
gss_mech_unregister(&gss_kerberos_mech);
}
MODULE_LICENSE("GPL");
......
......@@ -36,9 +36,11 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include <linux/module.h>
#include <linux/sunrpc/msg_prot.h>
#include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/auth_gss.h>
#include <linux/sunrpc/svcauth_gss.h>
#include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/sched.h>
#include <linux/sunrpc/gss_api.h>
......@@ -51,143 +53,168 @@
static LIST_HEAD(registered_mechs);
static spinlock_t registered_mechs_lock = SPIN_LOCK_UNLOCKED;
/* Reference counting: The reference count includes the reference in the
* global registered_mechs list. That reference will never diseappear
* (so the reference count will never go below 1) until after the mech
* is removed from the list. Nothing can be removed from the list without
* first getting the registered_mechs_lock, so a gss_api_mech won't diseappear
* from underneath us while we hold the registered_mech_lock. */
int
gss_mech_register(struct xdr_netobj * mech_type, struct gss_api_ops * ops)
static void
gss_mech_free(struct gss_api_mech *gm)
{
struct gss_api_mech *gm;
struct pf_desc *pf;
int i;
if (!(gm = kmalloc(sizeof(*gm), GFP_KERNEL))) {
printk("Failed to allocate memory in gss_mech_register");
return -1;
for (i = 0; i < gm->gm_pf_num; i++) {
pf = &gm->gm_pfs[i];
if (pf->auth_domain_name)
kfree(pf->auth_domain_name);
pf->auth_domain_name = NULL;
}
gm->gm_oid.len = mech_type->len;
if (!(gm->gm_oid.data = kmalloc(mech_type->len, GFP_KERNEL))) {
kfree(gm);
printk("Failed to allocate memory in gss_mech_register");
return -1;
}
memcpy(gm->gm_oid.data, mech_type->data, mech_type->len);
/* We're counting the reference in the registered_mechs list: */
atomic_set(&gm->gm_count, 1);
gm->gm_ops = ops;
spin_lock(&registered_mechs_lock);
list_add(&gm->gm_list, &registered_mechs);
spin_unlock(&registered_mechs_lock);
dprintk("RPC: gss_mech_register: registered mechanism with oid:\n");
print_hexl((u32 *)mech_type->data, mech_type->len, 0);
return 0;
}
/* The following must be called with spinlock held: */
int
do_gss_mech_unregister(struct gss_api_mech *gm)
static inline char *
make_auth_domain_name(char *name)
{
static char *prefix = "gss/";
char *new;
list_del(&gm->gm_list);
new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL);
if (new) {
strcpy(new, prefix);
strcat(new, name);
}
return new;
}
static int
gss_mech_svc_setup(struct gss_api_mech *gm)
{
struct pf_desc *pf;
int i, status;
dprintk("RPC: unregistered mechanism with oid:\n");
print_hexl((u32 *)gm->gm_oid.data, gm->gm_oid.len, 0);
if (!gss_mech_put(gm)) {
dprintk("RPC: We just unregistered a gss_mechanism which someone is still using.\n");
return -1;
} else {
return 0;
for (i = 0; i < gm->gm_pf_num; i++) {
pf = &gm->gm_pfs[i];
pf->auth_domain_name = make_auth_domain_name(pf->name);
status = -ENOMEM;
if (pf->auth_domain_name == NULL)
goto out;
status = svcauth_gss_register_pseudoflavor(pf->pseudoflavor,
pf->auth_domain_name);
if (status)
goto out;
}
return 0;
out:
gss_mech_free(gm);
return status;
}
int
gss_mech_unregister(struct gss_api_mech *gm)
gss_mech_register(struct gss_api_mech *gm)
{
int status;
status = gss_mech_svc_setup(gm);
if (status)
return status;
spin_lock(&registered_mechs_lock);
status = do_gss_mech_unregister(gm);
list_add(&gm->gm_list, &registered_mechs);
spin_unlock(&registered_mechs_lock);
return status;
dprintk("RPC: registered gss mechanism %s\n", gm->gm_name);
return 0;
}
int
gss_mech_unregister_all(void)
void
gss_mech_unregister(struct gss_api_mech *gm)
{
struct list_head *pos;
struct gss_api_mech *gm;
int status = 0;
spin_lock(&registered_mechs_lock);
while (!list_empty(&registered_mechs)) {
pos = registered_mechs.next;
gm = list_entry(pos, struct gss_api_mech, gm_list);
if (do_gss_mech_unregister(gm))
status = -1;
}
list_del(&gm->gm_list);
spin_unlock(&registered_mechs_lock);
return status;
dprintk("RPC: unregistered gss mechanism %s\n", gm->gm_name);
gss_mech_free(gm);
}
struct gss_api_mech *
gss_mech_get(struct gss_api_mech *gm)
{
atomic_inc(&gm->gm_count);
__module_get(gm->gm_owner);
return gm;
}
struct gss_api_mech *
gss_mech_get_by_OID(struct xdr_netobj *mech_type)
gss_mech_get_by_name(char *name)
{
struct gss_api_mech *pos, *gm = NULL;
struct gss_api_mech *pos, *gm = NULL;
dprintk("RPC: gss_mech_get_by_OID searching for mechanism with OID:\n");
print_hexl((u32 *)mech_type->data, mech_type->len, 0);
spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) {
if ((pos->gm_oid.len == mech_type->len)
&& !memcmp(pos->gm_oid.data, mech_type->data,
mech_type->len)) {
gm = gss_mech_get(pos);
if (0 == strcmp(name, pos->gm_name)) {
if (!try_module_get(pos->gm_owner))
continue;
gm = pos;
break;
}
}
spin_unlock(&registered_mechs_lock);
dprintk("RPC: gss_mech_get_by_OID %s it\n", gm ? "found" : "didn't find");
return gm;
}
static inline int
mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
{
int i;
for (i = 0; i < gm->gm_pf_num; i++) {
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
return 1;
}
return 0;
}
struct gss_api_mech *
gss_mech_get_by_name(char *name)
gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
{
struct gss_api_mech *pos, *gm = NULL;
struct gss_api_mech *pos, *gm = NULL;
spin_lock(&registered_mechs_lock);
list_for_each_entry(pos, &registered_mechs, gm_list) {
if (0 == strcmp(name, pos->gm_ops->name)) {
gm = gss_mech_get(pos);
break;
if (!try_module_get(pos->gm_owner))
continue;
if (!mech_supports_pseudoflavor(pos, pseudoflavor)) {
module_put(pos->gm_owner);
continue;
}
gm = pos;
break;
}
spin_unlock(&registered_mechs_lock);
return gm;
}
u32
gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
{
int i;
for (i = 0; i < gm->gm_pf_num; i++) {
if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
return gm->gm_pfs[i].service;
}
return 0;
}
int
gss_mech_put(struct gss_api_mech * gm)
char *
gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
{
if (atomic_dec_and_test(&gm->gm_count)) {
if (gm->gm_oid.len >0)
kfree(gm->gm_oid.data);
kfree(gm);
return 1;
} else {
return 0;
int i;
for (i = 0; i < gm->gm_pf_num; i++) {
if (gm->gm_pfs[i].service == service)
return gm->gm_pfs[i].auth_domain_name;
}
return NULL;
}
void
gss_mech_put(struct gss_api_mech * gm)
{
module_put(gm->gm_owner);
}
/* The mech could probably be determined from the token instead, but it's just
......
......@@ -12,20 +12,18 @@
#include <linux/sunrpc/gss_asn1.h>
#include <linux/sunrpc/gss_krb5.h>
/* sec_triples: */
EXPORT_SYMBOL(gss_register_triple);
EXPORT_SYMBOL(gss_unregister_triple);
EXPORT_SYMBOL(gss_cmp_triples);
EXPORT_SYMBOL(gss_pseudoflavor_to_mechOID);
EXPORT_SYMBOL(gss_pseudoflavor_supported);
EXPORT_SYMBOL(gss_pseudoflavor_to_service);
/* svcauth_gss.c: */
EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
/* registering gss mechanisms to the mech switching code: */
EXPORT_SYMBOL(gss_mech_register);
EXPORT_SYMBOL(gss_mech_unregister);
EXPORT_SYMBOL(gss_mech_get);
EXPORT_SYMBOL(gss_mech_get_by_OID);
EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
EXPORT_SYMBOL(gss_mech_get_by_name);
EXPORT_SYMBOL(gss_mech_put);
EXPORT_SYMBOL(gss_pseudoflavor_to_service);
EXPORT_SYMBOL(gss_service_to_auth_domain_name);
/* generic functionality in gss code: */
EXPORT_SYMBOL(g_make_token_header);
......
......@@ -617,19 +617,15 @@ struct gss_domain {
u32 pseudoflavor;
};
/* XXX this should be done in gss_pseudoflavors, and shouldn't be hardcoded: */
static struct auth_domain *
find_gss_auth_domain(struct gss_ctx *ctx, u32 svc)
{
switch(gss_get_pseudoflavor(ctx, 0, svc)) {
case RPC_AUTH_GSS_KRB5:
return auth_domain_find("gss/krb5");
case RPC_AUTH_GSS_KRB5I:
return auth_domain_find("gss/krb5i");
case RPC_AUTH_GSS_KRB5P:
return auth_domain_find("gss/krb5p");
}
return NULL;
char *name;
name = gss_service_to_auth_domain_name(ctx->mech_type, svc);
if (!name)
return NULL;
return auth_domain_find(name);
}
int
......@@ -637,19 +633,17 @@ svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
{
struct gss_domain *new;
struct auth_domain *test;
static char *prefix = "gss/";
int stat = -1;
int stat = -ENOMEM;
new = kmalloc(sizeof(*new), GFP_KERNEL);
if (!new)
goto out;
cache_init(&new->h.h);
atomic_inc(&new->h.h.refcnt);
new->h.name = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL);
new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL);
if (!new->h.name)
goto out_free_dom;
strcpy(new->h.name, prefix);
strcat(new->h.name, name);
strcpy(new->h.name, name);
new->h.flavour = RPC_AUTH_GSS;
new->pseudoflavor = pseudoflavor;
new->h.h.expiry_time = NEVER;
......
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