linux/net/sunrpc/auth_gss/gss_mech_switch.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause
   2/*
   3 *  linux/net/sunrpc/gss_mech_switch.c
   4 *
   5 *  Copyright (c) 2001 The Regents of the University of Michigan.
   6 *  All rights reserved.
   7 *
   8 *  J. Bruce Fields   <bfields@umich.edu>
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/slab.h>
  13#include <linux/module.h>
  14#include <linux/oid_registry.h>
  15#include <linux/sunrpc/msg_prot.h>
  16#include <linux/sunrpc/gss_asn1.h>
  17#include <linux/sunrpc/auth_gss.h>
  18#include <linux/sunrpc/svcauth_gss.h>
  19#include <linux/sunrpc/gss_err.h>
  20#include <linux/sunrpc/sched.h>
  21#include <linux/sunrpc/gss_api.h>
  22#include <linux/sunrpc/clnt.h>
  23
  24#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
  25# define RPCDBG_FACILITY        RPCDBG_AUTH
  26#endif
  27
  28static LIST_HEAD(registered_mechs);
  29static DEFINE_SPINLOCK(registered_mechs_lock);
  30
  31static void
  32gss_mech_free(struct gss_api_mech *gm)
  33{
  34        struct pf_desc *pf;
  35        int i;
  36
  37        for (i = 0; i < gm->gm_pf_num; i++) {
  38                pf = &gm->gm_pfs[i];
  39                kfree(pf->auth_domain_name);
  40                pf->auth_domain_name = NULL;
  41        }
  42}
  43
  44static inline char *
  45make_auth_domain_name(char *name)
  46{
  47        static char     *prefix = "gss/";
  48        char            *new;
  49
  50        new = kmalloc(strlen(name) + strlen(prefix) + 1, GFP_KERNEL);
  51        if (new) {
  52                strcpy(new, prefix);
  53                strcat(new, name);
  54        }
  55        return new;
  56}
  57
  58static int
  59gss_mech_svc_setup(struct gss_api_mech *gm)
  60{
  61        struct pf_desc *pf;
  62        int i, status;
  63
  64        for (i = 0; i < gm->gm_pf_num; i++) {
  65                pf = &gm->gm_pfs[i];
  66                pf->auth_domain_name = make_auth_domain_name(pf->name);
  67                status = -ENOMEM;
  68                if (pf->auth_domain_name == NULL)
  69                        goto out;
  70                status = svcauth_gss_register_pseudoflavor(pf->pseudoflavor,
  71                                                        pf->auth_domain_name);
  72                if (status)
  73                        goto out;
  74        }
  75        return 0;
  76out:
  77        gss_mech_free(gm);
  78        return status;
  79}
  80
  81/**
  82 * gss_mech_register - register a GSS mechanism
  83 * @gm: GSS mechanism handle
  84 *
  85 * Returns zero if successful, or a negative errno.
  86 */
  87int gss_mech_register(struct gss_api_mech *gm)
  88{
  89        int status;
  90
  91        status = gss_mech_svc_setup(gm);
  92        if (status)
  93                return status;
  94        spin_lock(&registered_mechs_lock);
  95        list_add_rcu(&gm->gm_list, &registered_mechs);
  96        spin_unlock(&registered_mechs_lock);
  97        dprintk("RPC:       registered gss mechanism %s\n", gm->gm_name);
  98        return 0;
  99}
 100EXPORT_SYMBOL_GPL(gss_mech_register);
 101
 102/**
 103 * gss_mech_unregister - release a GSS mechanism
 104 * @gm: GSS mechanism handle
 105 *
 106 */
 107void gss_mech_unregister(struct gss_api_mech *gm)
 108{
 109        spin_lock(&registered_mechs_lock);
 110        list_del_rcu(&gm->gm_list);
 111        spin_unlock(&registered_mechs_lock);
 112        dprintk("RPC:       unregistered gss mechanism %s\n", gm->gm_name);
 113        gss_mech_free(gm);
 114}
 115EXPORT_SYMBOL_GPL(gss_mech_unregister);
 116
 117struct gss_api_mech *gss_mech_get(struct gss_api_mech *gm)
 118{
 119        __module_get(gm->gm_owner);
 120        return gm;
 121}
 122EXPORT_SYMBOL(gss_mech_get);
 123
 124static struct gss_api_mech *
 125_gss_mech_get_by_name(const char *name)
 126{
 127        struct gss_api_mech     *pos, *gm = NULL;
 128
 129        rcu_read_lock();
 130        list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
 131                if (0 == strcmp(name, pos->gm_name)) {
 132                        if (try_module_get(pos->gm_owner))
 133                                gm = pos;
 134                        break;
 135                }
 136        }
 137        rcu_read_unlock();
 138        return gm;
 139
 140}
 141
 142struct gss_api_mech * gss_mech_get_by_name(const char *name)
 143{
 144        struct gss_api_mech *gm = NULL;
 145
 146        gm = _gss_mech_get_by_name(name);
 147        if (!gm) {
 148                request_module("rpc-auth-gss-%s", name);
 149                gm = _gss_mech_get_by_name(name);
 150        }
 151        return gm;
 152}
 153
 154struct gss_api_mech *gss_mech_get_by_OID(struct rpcsec_gss_oid *obj)
 155{
 156        struct gss_api_mech     *pos, *gm = NULL;
 157        char buf[32];
 158
 159        if (sprint_oid(obj->data, obj->len, buf, sizeof(buf)) < 0)
 160                return NULL;
 161        dprintk("RPC:       %s(%s)\n", __func__, buf);
 162        request_module("rpc-auth-gss-%s", buf);
 163
 164        rcu_read_lock();
 165        list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
 166                if (obj->len == pos->gm_oid.len) {
 167                        if (0 == memcmp(obj->data, pos->gm_oid.data, obj->len)) {
 168                                if (try_module_get(pos->gm_owner))
 169                                        gm = pos;
 170                                break;
 171                        }
 172                }
 173        }
 174        rcu_read_unlock();
 175        return gm;
 176}
 177
 178static inline int
 179mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
 180{
 181        int i;
 182
 183        for (i = 0; i < gm->gm_pf_num; i++) {
 184                if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
 185                        return 1;
 186        }
 187        return 0;
 188}
 189
 190static struct gss_api_mech *_gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
 191{
 192        struct gss_api_mech *gm = NULL, *pos;
 193
 194        rcu_read_lock();
 195        list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
 196                if (!mech_supports_pseudoflavor(pos, pseudoflavor))
 197                        continue;
 198                if (try_module_get(pos->gm_owner))
 199                        gm = pos;
 200                break;
 201        }
 202        rcu_read_unlock();
 203        return gm;
 204}
 205
 206struct gss_api_mech *
 207gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
 208{
 209        struct gss_api_mech *gm;
 210
 211        gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
 212
 213        if (!gm) {
 214                request_module("rpc-auth-gss-%u", pseudoflavor);
 215                gm = _gss_mech_get_by_pseudoflavor(pseudoflavor);
 216        }
 217        return gm;
 218}
 219
 220/**
 221 * gss_mech_list_pseudoflavors - Discover registered GSS pseudoflavors
 222 * @array_ptr: array to fill in
 223 * @size: size of "array"
 224 *
 225 * Returns the number of array items filled in, or a negative errno.
 226 *
 227 * The returned array is not sorted by any policy.  Callers should not
 228 * rely on the order of the items in the returned array.
 229 */
 230int gss_mech_list_pseudoflavors(rpc_authflavor_t *array_ptr, int size)
 231{
 232        struct gss_api_mech *pos = NULL;
 233        int j, i = 0;
 234
 235        rcu_read_lock();
 236        list_for_each_entry_rcu(pos, &registered_mechs, gm_list) {
 237                for (j = 0; j < pos->gm_pf_num; j++) {
 238                        if (i >= size) {
 239                                spin_unlock(&registered_mechs_lock);
 240                                return -ENOMEM;
 241                        }
 242                        array_ptr[i++] = pos->gm_pfs[j].pseudoflavor;
 243                }
 244        }
 245        rcu_read_unlock();
 246        return i;
 247}
 248
 249/**
 250 * gss_svc_to_pseudoflavor - map a GSS service number to a pseudoflavor
 251 * @gm: GSS mechanism handle
 252 * @qop: GSS quality-of-protection value
 253 * @service: GSS service value
 254 *
 255 * Returns a matching security flavor, or RPC_AUTH_MAXFLAVOR if none is found.
 256 */
 257rpc_authflavor_t gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 qop,
 258                                         u32 service)
 259{
 260        int i;
 261
 262        for (i = 0; i < gm->gm_pf_num; i++) {
 263                if (gm->gm_pfs[i].qop == qop &&
 264                    gm->gm_pfs[i].service == service) {
 265                        return gm->gm_pfs[i].pseudoflavor;
 266                }
 267        }
 268        return RPC_AUTH_MAXFLAVOR;
 269}
 270
 271/**
 272 * gss_mech_info2flavor - look up a pseudoflavor given a GSS tuple
 273 * @info: a GSS mech OID, quality of protection, and service value
 274 *
 275 * Returns a matching pseudoflavor, or RPC_AUTH_MAXFLAVOR if the tuple is
 276 * not supported.
 277 */
 278rpc_authflavor_t gss_mech_info2flavor(struct rpcsec_gss_info *info)
 279{
 280        rpc_authflavor_t pseudoflavor;
 281        struct gss_api_mech *gm;
 282
 283        gm = gss_mech_get_by_OID(&info->oid);
 284        if (gm == NULL)
 285                return RPC_AUTH_MAXFLAVOR;
 286
 287        pseudoflavor = gss_svc_to_pseudoflavor(gm, info->qop, info->service);
 288
 289        gss_mech_put(gm);
 290        return pseudoflavor;
 291}
 292
 293/**
 294 * gss_mech_flavor2info - look up a GSS tuple for a given pseudoflavor
 295 * @pseudoflavor: GSS pseudoflavor to match
 296 * @info: rpcsec_gss_info structure to fill in
 297 *
 298 * Returns zero and fills in "info" if pseudoflavor matches a
 299 * supported mechanism.  Otherwise a negative errno is returned.
 300 */
 301int gss_mech_flavor2info(rpc_authflavor_t pseudoflavor,
 302                         struct rpcsec_gss_info *info)
 303{
 304        struct gss_api_mech *gm;
 305        int i;
 306
 307        gm = gss_mech_get_by_pseudoflavor(pseudoflavor);
 308        if (gm == NULL)
 309                return -ENOENT;
 310
 311        for (i = 0; i < gm->gm_pf_num; i++) {
 312                if (gm->gm_pfs[i].pseudoflavor == pseudoflavor) {
 313                        memcpy(info->oid.data, gm->gm_oid.data, gm->gm_oid.len);
 314                        info->oid.len = gm->gm_oid.len;
 315                        info->qop = gm->gm_pfs[i].qop;
 316                        info->service = gm->gm_pfs[i].service;
 317                        gss_mech_put(gm);
 318                        return 0;
 319                }
 320        }
 321
 322        gss_mech_put(gm);
 323        return -ENOENT;
 324}
 325
 326u32
 327gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
 328{
 329        int i;
 330
 331        for (i = 0; i < gm->gm_pf_num; i++) {
 332                if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
 333                        return gm->gm_pfs[i].service;
 334        }
 335        return 0;
 336}
 337EXPORT_SYMBOL(gss_pseudoflavor_to_service);
 338
 339bool
 340gss_pseudoflavor_to_datatouch(struct gss_api_mech *gm, u32 pseudoflavor)
 341{
 342        int i;
 343
 344        for (i = 0; i < gm->gm_pf_num; i++) {
 345                if (gm->gm_pfs[i].pseudoflavor == pseudoflavor)
 346                        return gm->gm_pfs[i].datatouch;
 347        }
 348        return false;
 349}
 350
 351char *
 352gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
 353{
 354        int i;
 355
 356        for (i = 0; i < gm->gm_pf_num; i++) {
 357                if (gm->gm_pfs[i].service == service)
 358                        return gm->gm_pfs[i].auth_domain_name;
 359        }
 360        return NULL;
 361}
 362
 363void
 364gss_mech_put(struct gss_api_mech * gm)
 365{
 366        if (gm)
 367                module_put(gm->gm_owner);
 368}
 369EXPORT_SYMBOL(gss_mech_put);
 370
 371/* The mech could probably be determined from the token instead, but it's just
 372 * as easy for now to pass it in. */
 373int
 374gss_import_sec_context(const void *input_token, size_t bufsize,
 375                       struct gss_api_mech      *mech,
 376                       struct gss_ctx           **ctx_id,
 377                       time_t                   *endtime,
 378                       gfp_t gfp_mask)
 379{
 380        if (!(*ctx_id = kzalloc(sizeof(**ctx_id), gfp_mask)))
 381                return -ENOMEM;
 382        (*ctx_id)->mech_type = gss_mech_get(mech);
 383
 384        return mech->gm_ops->gss_import_sec_context(input_token, bufsize,
 385                                                *ctx_id, endtime, gfp_mask);
 386}
 387
 388/* gss_get_mic: compute a mic over message and return mic_token. */
 389
 390u32
 391gss_get_mic(struct gss_ctx      *context_handle,
 392            struct xdr_buf      *message,
 393            struct xdr_netobj   *mic_token)
 394{
 395         return context_handle->mech_type->gm_ops
 396                ->gss_get_mic(context_handle,
 397                              message,
 398                              mic_token);
 399}
 400
 401/* gss_verify_mic: check whether the provided mic_token verifies message. */
 402
 403u32
 404gss_verify_mic(struct gss_ctx           *context_handle,
 405               struct xdr_buf           *message,
 406               struct xdr_netobj        *mic_token)
 407{
 408        return context_handle->mech_type->gm_ops
 409                ->gss_verify_mic(context_handle,
 410                                 message,
 411                                 mic_token);
 412}
 413
 414/*
 415 * This function is called from both the client and server code.
 416 * Each makes guarantees about how much "slack" space is available
 417 * for the underlying function in "buf"'s head and tail while
 418 * performing the wrap.
 419 *
 420 * The client and server code allocate RPC_MAX_AUTH_SIZE extra
 421 * space in both the head and tail which is available for use by
 422 * the wrap function.
 423 *
 424 * Underlying functions should verify they do not use more than
 425 * RPC_MAX_AUTH_SIZE of extra space in either the head or tail
 426 * when performing the wrap.
 427 */
 428u32
 429gss_wrap(struct gss_ctx *ctx_id,
 430         int            offset,
 431         struct xdr_buf *buf,
 432         struct page    **inpages)
 433{
 434        return ctx_id->mech_type->gm_ops
 435                ->gss_wrap(ctx_id, offset, buf, inpages);
 436}
 437
 438u32
 439gss_unwrap(struct gss_ctx       *ctx_id,
 440           int                  offset,
 441           struct xdr_buf       *buf)
 442{
 443        return ctx_id->mech_type->gm_ops
 444                ->gss_unwrap(ctx_id, offset, buf);
 445}
 446
 447
 448/* gss_delete_sec_context: free all resources associated with context_handle.
 449 * Note this differs from the RFC 2744-specified prototype in that we don't
 450 * bother returning an output token, since it would never be used anyway. */
 451
 452u32
 453gss_delete_sec_context(struct gss_ctx   **context_handle)
 454{
 455        dprintk("RPC:       gss_delete_sec_context deleting %p\n",
 456                        *context_handle);
 457
 458        if (!*context_handle)
 459                return GSS_S_NO_CONTEXT;
 460        if ((*context_handle)->internal_ctx_id)
 461                (*context_handle)->mech_type->gm_ops
 462                        ->gss_delete_sec_context((*context_handle)
 463                                                        ->internal_ctx_id);
 464        gss_mech_put((*context_handle)->mech_type);
 465        kfree(*context_handle);
 466        *context_handle=NULL;
 467        return GSS_S_COMPLETE;
 468}
 469