Merge branch 'nfs-for-next' of git://linux-nfs.org/~trondmy/nfs-2.6 into for-3.10
[linux-3.10.git] / net / sunrpc / auth_gss / gss_mech_switch.c
index f8bac6c..defa9d3 100644 (file)
@@ -6,14 +6,14 @@
  *
  *  J. Bruce Fields   <bfields@umich.edu>
  *
- *  Redistribution and use in source and binary forms, with or without 
+ *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that the following conditions
  *  are met:
  *
  *  1. Redistributions of source code must retain the above copyright
  *     notice, this list of conditions and the following disclaimer.
  *  2. Redistributions in binary form must reproduce the above copyright
- *     notice, this list of conditions and the following disclaimer in the 
+ *     notice, this list of conditions and the following disclaimer in the
  *     documentation and/or other materials provided with the distribution.
  *  3. Neither the name of the University nor the names of its
  *     contributors may be used to endorse or promote products derived
@@ -36,6 +36,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/oid_registry.h>
 #include <linux/sunrpc/msg_prot.h>
 #include <linux/sunrpc/gss_asn1.h>
 #include <linux/sunrpc/auth_gss.h>
@@ -102,8 +103,13 @@ out:
        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;
 
@@ -113,35 +119,34 @@ gss_mech_register(struct gss_api_mech *gm)
        spin_lock(&registered_mechs_lock);
        list_add(&gm->gm_list, &registered_mechs);
        spin_unlock(&registered_mechs_lock);
-       dprintk("RPC:      registered gss mechanism %s\n", gm->gm_name);
+       dprintk("RPC:       registered gss mechanism %s\n", gm->gm_name);
        return 0;
 }
+EXPORT_SYMBOL_GPL(gss_mech_register);
 
-EXPORT_SYMBOL(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);
        list_del(&gm->gm_list);
        spin_unlock(&registered_mechs_lock);
-       dprintk("RPC:      unregistered gss mechanism %s\n", gm->gm_name);
+       dprintk("RPC:       unregistered gss mechanism %s\n", gm->gm_name);
        gss_mech_free(gm);
 }
+EXPORT_SYMBOL_GPL(gss_mech_unregister);
 
-EXPORT_SYMBOL(gss_mech_unregister);
-
-struct gss_api_mech *
-gss_mech_get(struct gss_api_mech *gm)
+static struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
 {
        __module_get(gm->gm_owner);
        return gm;
 }
 
-EXPORT_SYMBOL(gss_mech_get);
-
-struct gss_api_mech *
-gss_mech_get_by_name(const char *name)
+static struct gss_api_mech *
+_gss_mech_get_by_name(const char *name)
 {
        struct gss_api_mech     *pos, *gm = NULL;
 
@@ -158,7 +163,41 @@ gss_mech_get_by_name(const char *name)
 
 }
 
-EXPORT_SYMBOL(gss_mech_get_by_name);
+struct gss_api_mech * gss_mech_get_by_name(const char *name)
+{
+       struct gss_api_mech *gm = NULL;
+
+       gm = _gss_mech_get_by_name(name);
+       if (!gm) {
+               request_module("rpc-auth-gss-%s", name);
+               gm = _gss_mech_get_by_name(name);
+       }
+       return gm;
+}
+
+struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
+{
+       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);
+       list_for_each_entry(pos, &registered_mechs, gm_list) {
+               if (obj->len == pos->gm_oid.len) {
+                       if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) {
+                               if (try_module_get(pos->gm_owner))
+                                       gm = pos;
+                               break;
+                       }
+               }
+       }
+       spin_unlock(&registered_mechs_lock);
+       return gm;
+}
 
 static inline int
 mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -172,10 +211,9 @@ mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
        return 0;
 }
 
-struct gss_api_mech *
-gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
+static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
 {
-       struct gss_api_mech *pos, *gm = NULL;
+       struct gss_api_mech *gm = NULL, *pos;
 
        spin_lock(&registered_mechs_lock);
        list_for_each_entry(pos, &registered_mechs, gm_list) {
@@ -191,7 +229,125 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
        return gm;
 }
 
-EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
+struct gss_api_mech *
+gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
+{
+       struct gss_api_mech *gm;
+
+       gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
+
+       if (!gm) {
+               request_module("rpc-auth-gss-%u", pseudoflavor);
+               gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
+       }
+       return gm;
+}
+
+/**
+ * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
+ * @array: array to fill in
+ * @size: size of "array"
+ *
+ * Returns the number of array items filled in, or a negative errno.
+ *
+ * The returned array is not sorted by any policy.  Callers should not
+ * rely on the order of the items in the returned array.
+ */
+int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
+{
+       struct gss_api_mech *pos = NULL;
+       int j, i = 0;
+
+       spin_lock(&registered_mechs_lock);
+       list_for_each_entry(pos, &registered_mechs, gm_list) {
+               for (j = 0; j < pos->gm_pf_num; j++) {
+                       if (i >= size) {
+                               spin_unlock(&registered_mechs_lock);
+                               return -ENOMEM;
+                       }
+                       array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
+               }
+       }
+       spin_unlock(&registered_mechs_lock);
+       return i;
+}
+
+/**
+ * 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;
+
+       for (i = 0; i < gm->gm_pf_num; i++) {
+               if (gm->gm_pfs[i].qop == qop &&
+                   gm->gm_pfs[i].service == service) {
+                       return gm->gm_pfs[i].pseudoflavor;
+               }
+       }
+       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;
+}
 
 u32
 gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -205,8 +361,6 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
        return 0;
 }
 
-EXPORT_SYMBOL(gss_pseudoflavor_to_service);
-
 char *
 gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
 {
@@ -219,30 +373,28 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
        return NULL;
 }
 
-EXPORT_SYMBOL(gss_service_to_auth_domain_name);
-
 void
 gss_mech_put(struct gss_api_mech * gm)
 {
-       module_put(gm->gm_owner);
+       if (gm)
+               module_put(gm->gm_owner);
 }
 
-EXPORT_SYMBOL(gss_mech_put);
-
 /* The mech could probably be determined from the token instead, but it's just
  * as easy for now to pass it in. */
 int
 gss_import_sec_context(const void *input_token, size_t bufsize,
                       struct gss_api_mech      *mech,
-                      struct gss_ctx           **ctx_id)
+                      struct gss_ctx           **ctx_id,
+                      time_t                   *endtime,
+                      gfp_t gfp_mask)
 {
-       if (!(*ctx_id = kmalloc(sizeof(**ctx_id), GFP_KERNEL)))
-               return GSS_S_FAILURE;
-       memset(*ctx_id, 0, sizeof(**ctx_id));
+       if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask)))
+               return -ENOMEM;
        (*ctx_id)->mech_type = gss_mech_get(mech);
 
-       return mech->gm_ops
-               ->gss_import_sec_context(input_token, bufsize, *ctx_id);
+       return mech->gm_ops->gss_import_sec_context(input_token, bufsize,
+                                               *ctx_id, endtime, gfp_mask);
 }
 
 /* gss_get_mic: compute a mic over message and return mic_token. */
@@ -271,6 +423,20 @@ gss_verify_mic(struct gss_ctx              *context_handle,
                                 mic_token);
 }
 
+/*
+ * This function is called from both the client and server code.
+ * Each makes guarantees about how much "slack" space is available
+ * for the underlying function in "buf"'s head and tail while
+ * performing the wrap.
+ *
+ * The client and server code allocate RPC_MAX_AUTH_SIZE extra
+ * space in both the head and tail which is available for use by
+ * the wrap function.
+ *
+ * Underlying functions should verify they do not use more than
+ * RPC_MAX_AUTH_SIZE of extra space in either the head or tail
+ * when performing the wrap.
+ */
 u32
 gss_wrap(struct gss_ctx        *ctx_id,
         int            offset,
@@ -298,17 +464,16 @@ gss_unwrap(struct gss_ctx *ctx_id,
 u32
 gss_delete_sec_context(struct gss_ctx  **context_handle)
 {
-       dprintk("RPC:      gss_delete_sec_context deleting %p\n",
+       dprintk("RPC:       gss_delete_sec_context deleting %p\n",
                        *context_handle);
 
        if (!*context_handle)
-               return(GSS_S_NO_CONTEXT);
-       if ((*context_handle)->internal_ctx_id != 0)
+               return GSS_S_NO_CONTEXT;
+       if ((*context_handle)->internal_ctx_id)
                (*context_handle)->mech_type->gm_ops
                        ->gss_delete_sec_context((*context_handle)
                                                        ->internal_ctx_id);
-       if ((*context_handle)->mech_type)
-               gss_mech_put((*context_handle)->mech_type);
+       gss_mech_put((*context_handle)->mech_type);
        kfree(*context_handle);
        *context_handle=NULL;
        return GSS_S_COMPLETE;