ptlrpc-y := $(ldlm_objs) $(ptlrpc_objs)
ptlrpc-$(CONFIG_PROC_FS) += sec_lproc.o
ptlrpc-$(CONFIG_LUSTRE_TRANSLATE_ERRNOS) += errno.o
-
-obj-$(CONFIG_PTLRPC_GSS) += gss/
+++ /dev/null
-obj-$(CONFIG_LUSTRE_FS) := ptlrpc_gss.o
-
-ptlrpc_gss-y := sec_gss.o gss_bulk.o gss_cli_upcall.o gss_svc_upcall.o \
- gss_rawobj.o lproc_gss.o gss_generic_token.o \
- gss_mech_switch.o gss_krb5_mech.o
-
-
-ccflags-y := -I$(src)/../include
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * Somewhat simplified version of the gss api.
- *
- * Dug Song <dugsong@monkey.org>
- * Andy Adamson <andros@umich.edu>
- * Bruce Fields <bfields@umich.edu>
- * Copyright (c) 2000 The Regents of the University of Michigan
- *
- */
-
-#ifndef __PTLRPC_GSS_GSS_API_H_
-#define __PTLRPC_GSS_GSS_API_H_
-
-struct gss_api_mech;
-
-/* The mechanism-independent gss-api context: */
-struct gss_ctx {
- struct gss_api_mech *mech_type;
- void *internal_ctx_id;
-};
-
-#define GSS_C_NO_BUFFER ((rawobj_t) 0)
-#define GSS_C_NO_CONTEXT ((struct gss_ctx *) 0)
-#define GSS_C_NULL_OID ((rawobj_t) 0)
-
-/*
- * gss-api prototypes; note that these are somewhat simplified versions of
- * the prototypes specified in RFC 2744.
- */
-__u32 lgss_import_sec_context(
- rawobj_t *input_token,
- struct gss_api_mech *mech,
- struct gss_ctx **ctx);
-__u32 lgss_copy_reverse_context(
- struct gss_ctx *ctx,
- struct gss_ctx **ctx_new);
-__u32 lgss_inquire_context(
- struct gss_ctx *ctx,
- unsigned long *endtime);
-__u32 lgss_get_mic(
- struct gss_ctx *ctx,
- int msgcnt,
- rawobj_t *msgs,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *mic_token);
-__u32 lgss_verify_mic(
- struct gss_ctx *ctx,
- int msgcnt,
- rawobj_t *msgs,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *mic_token);
-__u32 lgss_wrap(
- struct gss_ctx *ctx,
- rawobj_t *gsshdr,
- rawobj_t *msg,
- int msg_buflen,
- rawobj_t *out_token);
-__u32 lgss_unwrap(
- struct gss_ctx *ctx,
- rawobj_t *gsshdr,
- rawobj_t *token,
- rawobj_t *out_msg);
-__u32 lgss_prep_bulk(
- struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc);
-__u32 lgss_wrap_bulk(
- struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token,
- int adj_nob);
-__u32 lgss_unwrap_bulk(
- struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token,
- int adj_nob);
-__u32 lgss_delete_sec_context(
- struct gss_ctx **ctx);
-int lgss_display(
- struct gss_ctx *ctx,
- char *buf,
- int bufsize);
-
-struct subflavor_desc {
- __u32 sf_subflavor;
- __u32 sf_qop;
- __u32 sf_service;
- char *sf_name;
-};
-
-/* Each mechanism is described by the following struct: */
-struct gss_api_mech {
- struct list_head gm_list;
- struct module *gm_owner;
- char *gm_name;
- rawobj_t gm_oid;
- atomic_t gm_count;
- struct gss_api_ops *gm_ops;
- int gm_sf_num;
- struct subflavor_desc *gm_sfs;
-};
-
-/* and must provide the following operations: */
-struct gss_api_ops {
- __u32 (*gss_import_sec_context)(
- rawobj_t *input_token,
- struct gss_ctx *ctx);
- __u32 (*gss_copy_reverse_context)(
- struct gss_ctx *ctx,
- struct gss_ctx *ctx_new);
- __u32 (*gss_inquire_context)(
- struct gss_ctx *ctx,
- unsigned long *endtime);
- __u32 (*gss_get_mic)(
- struct gss_ctx *ctx,
- int msgcnt,
- rawobj_t *msgs,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *mic_token);
- __u32 (*gss_verify_mic)(
- struct gss_ctx *ctx,
- int msgcnt,
- rawobj_t *msgs,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *mic_token);
- __u32 (*gss_wrap)(
- struct gss_ctx *ctx,
- rawobj_t *gsshdr,
- rawobj_t *msg,
- int msg_buflen,
- rawobj_t *out_token);
- __u32 (*gss_unwrap)(
- struct gss_ctx *ctx,
- rawobj_t *gsshdr,
- rawobj_t *token,
- rawobj_t *out_msg);
- __u32 (*gss_prep_bulk)(
- struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc);
- __u32 (*gss_wrap_bulk)(
- struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token,
- int adj_nob);
- __u32 (*gss_unwrap_bulk)(
- struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token,
- int adj_nob);
- void (*gss_delete_sec_context)(
- void *ctx);
- int (*gss_display)(
- struct gss_ctx *ctx,
- char *buf,
- int bufsize);
-};
-
-int lgss_mech_register(struct gss_api_mech *mech);
-void lgss_mech_unregister(struct gss_api_mech *mech);
-
-struct gss_api_mech * lgss_OID_to_mech(rawobj_t *oid);
-struct gss_api_mech * lgss_name_to_mech(char *name);
-struct gss_api_mech * lgss_subflavor_to_mech(__u32 subflavor);
-
-struct gss_api_mech * lgss_mech_get(struct gss_api_mech *mech);
-void lgss_mech_put(struct gss_api_mech *mech);
-
-#endif /* __PTLRPC_GSS_GSS_API_H_ */
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * minimal asn1 for generic encoding/decoding of gss tokens
- *
- * Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
- * lib/gssapi/krb5/gssapiP_krb5.h, and others
- *
- * Copyright (c) 2000 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Andy Adamson <andros@umich.edu>
- */
-
-/*
- * Copyright 1995 by the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * Export of this software from the United States of America may
- * require a specific license from the United States Government.
- * It is the responsibility of any person or organization contemplating
- * export to obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission. Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose. It is provided "as is" without express
- * or implied warranty.
- *
- */
-
-#define SIZEOF_INT 4
-
-/* from gssapi_err_generic.h */
-#define G_BAD_SERVICE_NAME (-2045022976L)
-#define G_BAD_STRING_UID (-2045022975L)
-#define G_NOUSER (-2045022974L)
-#define G_VALIDATE_FAILED (-2045022973L)
-#define G_BUFFER_ALLOC (-2045022972L)
-#define G_BAD_MSG_CTX (-2045022971L)
-#define G_WRONG_SIZE (-2045022970L)
-#define G_BAD_USAGE (-2045022969L)
-#define G_UNKNOWN_QOP (-2045022968L)
-#define G_NO_HOSTNAME (-2045022967L)
-#define G_BAD_HOSTNAME (-2045022966L)
-#define G_WRONG_MECH (-2045022965L)
-#define G_BAD_TOK_HEADER (-2045022964L)
-#define G_BAD_DIRECTION (-2045022963L)
-#define G_TOK_TRUNC (-2045022962L)
-#define G_REFLECT (-2045022961L)
-#define G_WRONG_TOKID (-2045022960L)
-
-#define g_OID_equal(o1, o2) \
- (((o1)->len == (o2)->len) && \
- (memcmp((o1)->data, (o2)->data, (int) (o1)->len) == 0))
-
-__u32 g_verify_token_header(rawobj_t *mech,
- int *body_size,
- unsigned char **buf_in,
- int toksize);
-
-__u32 g_get_mech_oid(rawobj_t *mech,
- rawobj_t *in_buf);
-
-int g_token_size(rawobj_t *mech,
- unsigned int body_size);
-
-void g_make_token_header(rawobj_t *mech,
- int body_size,
- unsigned char **buf);
+++ /dev/null
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- *
- * lustre/ptlrpc/gss/gss_bulk.c
- *
- * Author: Eric Mei <eric.mei@sun.com>
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <linux/crypto.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc)
-{
- struct gss_cli_ctx *gctx;
- struct lustre_msg *msg;
- struct ptlrpc_bulk_sec_desc *bsd;
- rawobj_t token;
- __u32 maj;
- int offset;
- int rc;
-
- LASSERT(req->rq_pack_bulk);
- LASSERT(req->rq_bulk_read || req->rq_bulk_write);
-
- gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
- LASSERT(gctx->gc_mechctx);
-
- switch (SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc)) {
- case SPTLRPC_SVC_NULL:
- LASSERT(req->rq_reqbuf->lm_bufcount >= 3);
- msg = req->rq_reqbuf;
- offset = msg->lm_bufcount - 1;
- break;
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- LASSERT(req->rq_reqbuf->lm_bufcount >= 4);
- msg = req->rq_reqbuf;
- offset = msg->lm_bufcount - 2;
- break;
- case SPTLRPC_SVC_PRIV:
- LASSERT(req->rq_clrbuf->lm_bufcount >= 2);
- msg = req->rq_clrbuf;
- offset = msg->lm_bufcount - 1;
- break;
- default:
- LBUG();
- }
-
- bsd = lustre_msg_buf(msg, offset, sizeof(*bsd));
- bsd->bsd_version = 0;
- bsd->bsd_flags = 0;
- bsd->bsd_type = SPTLRPC_BULK_DEFAULT;
- bsd->bsd_svc = SPTLRPC_FLVR_BULK_SVC(req->rq_flvr.sf_rpc);
-
- if (bsd->bsd_svc == SPTLRPC_BULK_SVC_NULL)
- return 0;
-
- LASSERT(bsd->bsd_svc == SPTLRPC_BULK_SVC_INTG ||
- bsd->bsd_svc == SPTLRPC_BULK_SVC_PRIV);
-
- if (req->rq_bulk_read) {
- /*
- * bulk read: prepare receiving pages only for privacy mode.
- */
- if (bsd->bsd_svc == SPTLRPC_BULK_SVC_PRIV)
- return gss_cli_prep_bulk(req, desc);
- } else {
- /*
- * bulk write: sign or encrypt bulk pages.
- */
- bsd->bsd_nob = desc->bd_nob;
-
- if (bsd->bsd_svc == SPTLRPC_BULK_SVC_INTG) {
- /* integrity mode */
- token.data = bsd->bsd_data;
- token.len = lustre_msg_buflen(msg, offset) -
- sizeof(*bsd);
-
- maj = lgss_get_mic(gctx->gc_mechctx, 0, NULL,
- desc->bd_iov_count, desc->bd_iov,
- &token);
- if (maj != GSS_S_COMPLETE) {
- CWARN("failed to sign bulk data: %x\n", maj);
- return -EACCES;
- }
- } else {
- /* privacy mode */
- if (desc->bd_iov_count == 0)
- return 0;
-
- rc = sptlrpc_enc_pool_get_pages(desc);
- if (rc) {
- CERROR("bulk write: failed to allocate "
- "encryption pages: %d\n", rc);
- return rc;
- }
-
- token.data = bsd->bsd_data;
- token.len = lustre_msg_buflen(msg, offset) -
- sizeof(*bsd);
-
- maj = lgss_wrap_bulk(gctx->gc_mechctx, desc, &token, 0);
- if (maj != GSS_S_COMPLETE) {
- CWARN("fail to encrypt bulk data: %x\n", maj);
- return -EACCES;
- }
- }
- }
-
- return 0;
-}
-
-int gss_cli_ctx_unwrap_bulk(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc)
-{
- struct gss_cli_ctx *gctx;
- struct lustre_msg *rmsg, *vmsg;
- struct ptlrpc_bulk_sec_desc *bsdr, *bsdv;
- rawobj_t token;
- __u32 maj;
- int roff, voff;
-
- LASSERT(req->rq_pack_bulk);
- LASSERT(req->rq_bulk_read || req->rq_bulk_write);
-
- switch (SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc)) {
- case SPTLRPC_SVC_NULL:
- vmsg = req->rq_repdata;
- LASSERT(vmsg != NULL && vmsg->lm_bufcount >= 3);
- voff = vmsg->lm_bufcount - 1;
-
- rmsg = req->rq_reqbuf;
- LASSERT(rmsg != NULL && rmsg->lm_bufcount >= 3);
- roff = rmsg->lm_bufcount - 1; /* last segment */
- break;
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- vmsg = req->rq_repdata;
- LASSERT(vmsg != NULL && vmsg->lm_bufcount >= 4);
- voff = vmsg->lm_bufcount - 2;
-
- rmsg = req->rq_reqbuf;
- LASSERT(rmsg != NULL && rmsg->lm_bufcount >= 4);
- roff = rmsg->lm_bufcount - 2; /* second last segment */
- break;
- case SPTLRPC_SVC_PRIV:
- vmsg = req->rq_repdata;
- LASSERT(vmsg != NULL && vmsg->lm_bufcount >= 2);
- voff = vmsg->lm_bufcount - 1;
-
- rmsg = req->rq_clrbuf;
- LASSERT(rmsg != NULL && rmsg->lm_bufcount >= 2);
- roff = rmsg->lm_bufcount - 1; /* last segment */
- break;
- default:
- LBUG();
- }
-
- bsdr = lustre_msg_buf(rmsg, roff, sizeof(*bsdr));
- bsdv = lustre_msg_buf(vmsg, voff, sizeof(*bsdv));
- LASSERT(bsdr && bsdv);
-
- if (bsdr->bsd_version != bsdv->bsd_version ||
- bsdr->bsd_type != bsdv->bsd_type ||
- bsdr->bsd_svc != bsdv->bsd_svc) {
- CERROR("bulk security descriptor mismatch: "
- "(%u,%u,%u) != (%u,%u,%u)\n",
- bsdr->bsd_version, bsdr->bsd_type, bsdr->bsd_svc,
- bsdv->bsd_version, bsdv->bsd_type, bsdv->bsd_svc);
- return -EPROTO;
- }
-
- LASSERT(bsdv->bsd_svc == SPTLRPC_BULK_SVC_NULL ||
- bsdv->bsd_svc == SPTLRPC_BULK_SVC_INTG ||
- bsdv->bsd_svc == SPTLRPC_BULK_SVC_PRIV);
-
- /*
- * in privacy mode if return success, make sure bd_nob_transferred
- * is the actual size of the clear text, otherwise upper layer
- * may be surprised.
- */
- if (req->rq_bulk_write) {
- if (bsdv->bsd_flags & BSD_FL_ERR) {
- CERROR("server reported bulk i/o failure\n");
- return -EIO;
- }
-
- if (bsdv->bsd_svc == SPTLRPC_BULK_SVC_PRIV)
- desc->bd_nob_transferred = desc->bd_nob;
- } else {
- /*
- * bulk read, upon return success, bd_nob_transferred is
- * the size of plain text actually received.
- */
- gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
- LASSERT(gctx->gc_mechctx);
-
- if (bsdv->bsd_svc == SPTLRPC_BULK_SVC_INTG) {
- int i, nob;
-
- /* fix the actual data size */
- for (i = 0, nob = 0; i < desc->bd_iov_count; i++) {
- if (desc->bd_iov[i].kiov_len + nob >
- desc->bd_nob_transferred) {
- desc->bd_iov[i].kiov_len =
- desc->bd_nob_transferred - nob;
- }
- nob += desc->bd_iov[i].kiov_len;
- }
-
- token.data = bsdv->bsd_data;
- token.len = lustre_msg_buflen(vmsg, voff) -
- sizeof(*bsdv);
-
- maj = lgss_verify_mic(gctx->gc_mechctx, 0, NULL,
- desc->bd_iov_count, desc->bd_iov,
- &token);
- if (maj != GSS_S_COMPLETE) {
- CERROR("failed to verify bulk read: %x\n", maj);
- return -EACCES;
- }
- } else if (bsdv->bsd_svc == SPTLRPC_BULK_SVC_PRIV) {
- desc->bd_nob = bsdv->bsd_nob;
- if (desc->bd_nob == 0)
- return 0;
-
- token.data = bsdv->bsd_data;
- token.len = lustre_msg_buflen(vmsg, voff) -
- sizeof(*bsdr);
-
- maj = lgss_unwrap_bulk(gctx->gc_mechctx, desc,
- &token, 1);
- if (maj != GSS_S_COMPLETE) {
- CERROR("failed to decrypt bulk read: %x\n",
- maj);
- return -EACCES;
- }
-
- desc->bd_nob_transferred = desc->bd_nob;
- }
- }
-
- return 0;
-}
-
-static int gss_prep_bulk(struct ptlrpc_bulk_desc *desc,
- struct gss_ctx *mechctx)
-{
- int rc;
-
- if (desc->bd_iov_count == 0)
- return 0;
-
- rc = sptlrpc_enc_pool_get_pages(desc);
- if (rc)
- return rc;
-
- if (lgss_prep_bulk(mechctx, desc) != GSS_S_COMPLETE)
- return -EACCES;
-
- return 0;
-}
-
-int gss_cli_prep_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc)
-{
- int rc;
-
- LASSERT(req->rq_cli_ctx);
- LASSERT(req->rq_pack_bulk);
- LASSERT(req->rq_bulk_read);
-
- if (SPTLRPC_FLVR_BULK_SVC(req->rq_flvr.sf_rpc) != SPTLRPC_BULK_SVC_PRIV)
- return 0;
-
- rc = gss_prep_bulk(desc, ctx2gctx(req->rq_cli_ctx)->gc_mechctx);
- if (rc)
- CERROR("bulk read: failed to prepare encryption "
- "pages: %d\n", rc);
-
- return rc;
-}
-
-int gss_svc_prep_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc)
-{
- struct gss_svc_reqctx *grctx;
- struct ptlrpc_bulk_sec_desc *bsd;
- int rc;
-
- LASSERT(req->rq_svc_ctx);
- LASSERT(req->rq_pack_bulk);
- LASSERT(req->rq_bulk_write);
-
- grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- LASSERT(grctx->src_reqbsd);
- LASSERT(grctx->src_repbsd);
- LASSERT(grctx->src_ctx);
- LASSERT(grctx->src_ctx->gsc_mechctx);
-
- bsd = grctx->src_reqbsd;
- if (bsd->bsd_svc != SPTLRPC_BULK_SVC_PRIV)
- return 0;
-
- rc = gss_prep_bulk(desc, grctx->src_ctx->gsc_mechctx);
- if (rc)
- CERROR("bulk write: failed to prepare encryption "
- "pages: %d\n", rc);
-
- return rc;
-}
-
-int gss_svc_unwrap_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc)
-{
- struct gss_svc_reqctx *grctx;
- struct ptlrpc_bulk_sec_desc *bsdr, *bsdv;
- rawobj_t token;
- __u32 maj;
-
- LASSERT(req->rq_svc_ctx);
- LASSERT(req->rq_pack_bulk);
- LASSERT(req->rq_bulk_write);
-
- grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
-
- LASSERT(grctx->src_reqbsd);
- LASSERT(grctx->src_repbsd);
- LASSERT(grctx->src_ctx);
- LASSERT(grctx->src_ctx->gsc_mechctx);
-
- bsdr = grctx->src_reqbsd;
- bsdv = grctx->src_repbsd;
-
- /* bsdr has been sanity checked during unpacking */
- bsdv->bsd_version = 0;
- bsdv->bsd_type = SPTLRPC_BULK_DEFAULT;
- bsdv->bsd_svc = bsdr->bsd_svc;
- bsdv->bsd_flags = 0;
-
- switch (bsdv->bsd_svc) {
- case SPTLRPC_BULK_SVC_INTG:
- token.data = bsdr->bsd_data;
- token.len = grctx->src_reqbsd_size - sizeof(*bsdr);
-
- maj = lgss_verify_mic(grctx->src_ctx->gsc_mechctx, 0, NULL,
- desc->bd_iov_count, desc->bd_iov, &token);
- if (maj != GSS_S_COMPLETE) {
- bsdv->bsd_flags |= BSD_FL_ERR;
- CERROR("failed to verify bulk signature: %x\n", maj);
- return -EACCES;
- }
- break;
- case SPTLRPC_BULK_SVC_PRIV:
- if (bsdr->bsd_nob != desc->bd_nob) {
- bsdv->bsd_flags |= BSD_FL_ERR;
- CERROR("prepared nob %d doesn't match the actual "
- "nob %d\n", desc->bd_nob, bsdr->bsd_nob);
- return -EPROTO;
- }
-
- if (desc->bd_iov_count == 0) {
- LASSERT(desc->bd_nob == 0);
- break;
- }
-
- token.data = bsdr->bsd_data;
- token.len = grctx->src_reqbsd_size - sizeof(*bsdr);
-
- maj = lgss_unwrap_bulk(grctx->src_ctx->gsc_mechctx,
- desc, &token, 0);
- if (maj != GSS_S_COMPLETE) {
- bsdv->bsd_flags |= BSD_FL_ERR;
- CERROR("failed decrypt bulk data: %x\n", maj);
- return -EACCES;
- }
- break;
- }
-
- return 0;
-}
-
-int gss_svc_wrap_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc)
-{
- struct gss_svc_reqctx *grctx;
- struct ptlrpc_bulk_sec_desc *bsdr, *bsdv;
- rawobj_t token;
- __u32 maj;
- int rc;
-
- LASSERT(req->rq_svc_ctx);
- LASSERT(req->rq_pack_bulk);
- LASSERT(req->rq_bulk_read);
-
- grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
-
- LASSERT(grctx->src_reqbsd);
- LASSERT(grctx->src_repbsd);
- LASSERT(grctx->src_ctx);
- LASSERT(grctx->src_ctx->gsc_mechctx);
-
- bsdr = grctx->src_reqbsd;
- bsdv = grctx->src_repbsd;
-
- /* bsdr has been sanity checked during unpacking */
- bsdv->bsd_version = 0;
- bsdv->bsd_type = SPTLRPC_BULK_DEFAULT;
- bsdv->bsd_svc = bsdr->bsd_svc;
- bsdv->bsd_flags = 0;
-
- switch (bsdv->bsd_svc) {
- case SPTLRPC_BULK_SVC_INTG:
- token.data = bsdv->bsd_data;
- token.len = grctx->src_repbsd_size - sizeof(*bsdv);
-
- maj = lgss_get_mic(grctx->src_ctx->gsc_mechctx, 0, NULL,
- desc->bd_iov_count, desc->bd_iov, &token);
- if (maj != GSS_S_COMPLETE) {
- bsdv->bsd_flags |= BSD_FL_ERR;
- CERROR("failed to sign bulk data: %x\n", maj);
- return -EACCES;
- }
- break;
- case SPTLRPC_BULK_SVC_PRIV:
- bsdv->bsd_nob = desc->bd_nob;
-
- if (desc->bd_iov_count == 0) {
- LASSERT(desc->bd_nob == 0);
- break;
- }
-
- rc = sptlrpc_enc_pool_get_pages(desc);
- if (rc) {
- bsdv->bsd_flags |= BSD_FL_ERR;
- CERROR("bulk read: failed to allocate encryption "
- "pages: %d\n", rc);
- return rc;
- }
-
- token.data = bsdv->bsd_data;
- token.len = grctx->src_repbsd_size - sizeof(*bsdv);
-
- maj = lgss_wrap_bulk(grctx->src_ctx->gsc_mechctx,
- desc, &token, 1);
- if (maj != GSS_S_COMPLETE) {
- bsdv->bsd_flags |= BSD_FL_ERR;
- CERROR("failed to encrypt bulk data: %x\n", maj);
- return -EACCES;
- }
- break;
- }
-
- return 0;
-}
+++ /dev/null
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2011, 2012, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- *
- * lustre/ptlrpc/gss/gss_cli_upcall.c
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-/**********************************************
- * gss context init/fini helper *
- **********************************************/
-
-static
-int ctx_init_pack_request(struct obd_import *imp,
- struct ptlrpc_request *req,
- int lustre_srv,
- uid_t uid, gid_t gid,
- long token_size,
- char __user *token)
-{
- struct lustre_msg *msg = req->rq_reqbuf;
- struct gss_sec *gsec;
- struct gss_header *ghdr;
- struct ptlrpc_user_desc *pud;
- __u32 *p, size, offset = 2;
- rawobj_t obj;
-
- LASSERT(msg->lm_bufcount <= 4);
- LASSERT(req->rq_cli_ctx);
- LASSERT(req->rq_cli_ctx->cc_sec);
-
- /* gss hdr */
- ghdr = lustre_msg_buf(msg, 0, sizeof(*ghdr));
- ghdr->gh_version = PTLRPC_GSS_VERSION;
- ghdr->gh_sp = (__u8) imp->imp_sec->ps_part;
- ghdr->gh_flags = 0;
- ghdr->gh_proc = PTLRPC_GSS_PROC_INIT;
- ghdr->gh_seq = 0;
- ghdr->gh_svc = SPTLRPC_SVC_NULL;
- ghdr->gh_handle.len = 0;
-
- /* fix the user desc */
- if (req->rq_pack_udesc) {
- ghdr->gh_flags |= LUSTRE_GSS_PACK_USER;
-
- pud = lustre_msg_buf(msg, offset, sizeof(*pud));
- LASSERT(pud);
- pud->pud_uid = pud->pud_fsuid = uid;
- pud->pud_gid = pud->pud_fsgid = gid;
- pud->pud_cap = 0;
- pud->pud_ngroups = 0;
- offset++;
- }
-
- /* security payload */
- p = lustre_msg_buf(msg, offset, 0);
- size = msg->lm_buflens[offset];
- LASSERT(p);
-
- /* 1. lustre svc type */
- LASSERT(size > 4);
- *p++ = cpu_to_le32(lustre_srv);
- size -= 4;
-
- /* 2. target uuid */
- obj.len = strlen(imp->imp_obd->u.cli.cl_target_uuid.uuid) + 1;
- obj.data = imp->imp_obd->u.cli.cl_target_uuid.uuid;
- if (rawobj_serialize(&obj, &p, &size))
- LBUG();
-
- /* 3. reverse context handle. actually only needed by root user,
- * but we send it anyway. */
- gsec = sec2gsec(req->rq_cli_ctx->cc_sec);
- obj.len = sizeof(gsec->gs_rvs_hdl);
- obj.data = (__u8 *) &gsec->gs_rvs_hdl;
- if (rawobj_serialize(&obj, &p, &size))
- LBUG();
-
- /* 4. now the token */
- LASSERT(size >= (sizeof(__u32) + token_size));
- *p++ = cpu_to_le32(((__u32) token_size));
- if (copy_from_user(p, token, token_size)) {
- CERROR("can't copy token\n");
- return -EFAULT;
- }
- size -= sizeof(__u32) + cfs_size_round4(token_size);
-
- req->rq_reqdata_len = lustre_shrink_msg(req->rq_reqbuf, offset,
- msg->lm_buflens[offset] - size, 0);
- return 0;
-}
-
-static
-int ctx_init_parse_reply(struct lustre_msg *msg, int swabbed,
- char __user *outbuf, long outlen)
-{
- struct gss_rep_header *ghdr;
- __u32 obj_len, round_len;
- __u32 status, effective = 0;
-
- if (msg->lm_bufcount != 3) {
- CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
- return -EPROTO;
- }
-
- ghdr = (struct gss_rep_header *) gss_swab_header(msg, 0, swabbed);
- if (ghdr == NULL) {
- CERROR("unable to extract gss reply header\n");
- return -EPROTO;
- }
-
- if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
- CERROR("invalid gss version %u\n", ghdr->gh_version);
- return -EPROTO;
- }
-
- if (outlen < (4 + 2) * 4 + cfs_size_round4(ghdr->gh_handle.len) +
- cfs_size_round4(msg->lm_buflens[2])) {
- CERROR("output buffer size %ld too small\n", outlen);
- return -EFAULT;
- }
-
- status = 0;
- effective = 0;
-
- if (copy_to_user(outbuf, &status, 4))
- return -EFAULT;
- outbuf += 4;
- if (copy_to_user(outbuf, &ghdr->gh_major, 4))
- return -EFAULT;
- outbuf += 4;
- if (copy_to_user(outbuf, &ghdr->gh_minor, 4))
- return -EFAULT;
- outbuf += 4;
- if (copy_to_user(outbuf, &ghdr->gh_seqwin, 4))
- return -EFAULT;
- outbuf += 4;
- effective += 4 * 4;
-
- /* handle */
- obj_len = ghdr->gh_handle.len;
- round_len = (obj_len + 3) & ~ 3;
- if (copy_to_user(outbuf, &obj_len, 4))
- return -EFAULT;
- outbuf += 4;
- if (copy_to_user(outbuf, (char *) ghdr->gh_handle.data, round_len))
- return -EFAULT;
- outbuf += round_len;
- effective += 4 + round_len;
-
- /* out token */
- obj_len = msg->lm_buflens[2];
- round_len = (obj_len + 3) & ~ 3;
- if (copy_to_user(outbuf, &obj_len, 4))
- return -EFAULT;
- outbuf += 4;
- if (copy_to_user(outbuf, lustre_msg_buf(msg, 2, 0), round_len))
- return -EFAULT;
- outbuf += round_len;
- effective += 4 + round_len;
-
- return effective;
-}
-
-/* XXX move to where lgssd could see */
-struct lgssd_ioctl_param {
- int version; /* in */
- int secid; /* in */
- char *uuid; /* in */
- int lustre_svc; /* in */
- uid_t uid; /* in */
- gid_t gid; /* in */
- long send_token_size;/* in */
- char *send_token; /* in */
- long reply_buf_size; /* in */
- char *reply_buf; /* in */
- long status; /* out */
- long reply_length; /* out */
-};
-
-int gss_do_ctx_init_rpc(__user char *buffer, unsigned long count)
-{
- struct obd_import *imp;
- struct ptlrpc_request *req;
- struct lgssd_ioctl_param param;
- struct obd_device *obd;
- char obdname[64];
- long lsize;
- int rc;
-
- if (count != sizeof(param)) {
- CERROR("ioctl size %lu, expect %lu, please check lgss_keyring "
- "version\n", count, (unsigned long) sizeof(param));
- return -EINVAL;
- }
- if (copy_from_user(¶m, buffer, sizeof(param))) {
- CERROR("failed copy data from lgssd\n");
- return -EFAULT;
- }
-
- if (param.version != GSSD_INTERFACE_VERSION) {
- CERROR("gssd interface version %d (expect %d)\n",
- param.version, GSSD_INTERFACE_VERSION);
- return -EINVAL;
- }
-
- /* take name */
- if (strncpy_from_user(obdname, param.uuid, sizeof(obdname)) <= 0) {
- CERROR("Invalid obdname pointer\n");
- return -EFAULT;
- }
-
- obd = class_name2obd(obdname);
- if (!obd) {
- CERROR("no such obd %s\n", obdname);
- return -EINVAL;
- }
-
- if (unlikely(!obd->obd_set_up)) {
- CERROR("obd %s not setup\n", obdname);
- return -EINVAL;
- }
-
- spin_lock(&obd->obd_dev_lock);
- if (obd->obd_stopping) {
- CERROR("obd %s has stopped\n", obdname);
- spin_unlock(&obd->obd_dev_lock);
- return -EINVAL;
- }
-
- if (strcmp(obd->obd_type->typ_name, LUSTRE_MDC_NAME) &&
- strcmp(obd->obd_type->typ_name, LUSTRE_OSC_NAME) &&
- strcmp(obd->obd_type->typ_name, LUSTRE_MGC_NAME)) {
- CERROR("obd %s is not a client device\n", obdname);
- spin_unlock(&obd->obd_dev_lock);
- return -EINVAL;
- }
- spin_unlock(&obd->obd_dev_lock);
-
- down_read(&obd->u.cli.cl_sem);
- if (obd->u.cli.cl_import == NULL) {
- CERROR("obd %s: import has gone\n", obd->obd_name);
- up_read(&obd->u.cli.cl_sem);
- return -EINVAL;
- }
- imp = class_import_get(obd->u.cli.cl_import);
- up_read(&obd->u.cli.cl_sem);
-
- if (imp->imp_deactive) {
- CERROR("import has been deactivated\n");
- class_import_put(imp);
- return -EINVAL;
- }
-
- req = ptlrpc_request_alloc_pack(imp, &RQF_SEC_CTX, LUSTRE_OBD_VERSION,
- SEC_CTX_INIT);
- if (req == NULL) {
- param.status = -ENOMEM;
- goto out_copy;
- }
-
- if (req->rq_cli_ctx->cc_sec->ps_id != param.secid) {
- CWARN("original secid %d, now has changed to %d, "
- "cancel this negotiation\n", param.secid,
- req->rq_cli_ctx->cc_sec->ps_id);
- param.status = -EINVAL;
- goto out_copy;
- }
-
- /* get token */
- rc = ctx_init_pack_request(imp, req,
- param.lustre_svc,
- param.uid, param.gid,
- param.send_token_size,
- param.send_token);
- if (rc) {
- param.status = rc;
- goto out_copy;
- }
-
- ptlrpc_request_set_replen(req);
-
- rc = ptlrpc_queue_wait(req);
- if (rc) {
- /* If any _real_ denial be made, we expect server return
- * -EACCES reply or return success but indicate gss error
- * inside reply message. All other errors are treated as
- * timeout, caller might try the negotiation repeatedly,
- * leave recovery decisions to general ptlrpc layer.
- *
- * FIXME maybe some other error code shouldn't be treated
- * as timeout. */
- param.status = rc;
- if (rc != -EACCES)
- param.status = -ETIMEDOUT;
- goto out_copy;
- }
-
- LASSERT(req->rq_repdata);
- lsize = ctx_init_parse_reply(req->rq_repdata,
- ptlrpc_rep_need_swab(req),
- param.reply_buf, param.reply_buf_size);
- if (lsize < 0) {
- param.status = (int) lsize;
- goto out_copy;
- }
-
- param.status = 0;
- param.reply_length = lsize;
-
-out_copy:
- if (copy_to_user(buffer, ¶m, sizeof(param)))
- rc = -EFAULT;
- else
- rc = 0;
-
- class_import_put(imp);
- ptlrpc_req_finished(req);
- return rc;
-}
-
-int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx)
-{
- struct ptlrpc_cli_ctx *ctx = &gctx->gc_base;
- struct obd_import *imp = ctx->cc_sec->ps_import;
- struct ptlrpc_request *req;
- struct ptlrpc_user_desc *pud;
- int rc;
-
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- if (cli_ctx_is_error(ctx) || !cli_ctx_is_uptodate(ctx)) {
- CDEBUG(D_SEC, "ctx %p(%u->%s) not uptodate, "
- "don't send destroy rpc\n", ctx,
- ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
- return 0;
- }
-
- might_sleep();
-
- CWARN("%s ctx %p idx "LPX64" (%u->%s)\n",
- sec_is_reverse(ctx->cc_sec) ?
- "server finishing reverse" : "client finishing forward",
- ctx, gss_handle_to_u64(&gctx->gc_handle),
- ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
-
- gctx->gc_proc = PTLRPC_GSS_PROC_DESTROY;
-
- req = ptlrpc_request_alloc(imp, &RQF_SEC_CTX);
- if (req == NULL) {
- CWARN("ctx %p(%u): fail to prepare rpc, destroy locally\n",
- ctx, ctx->cc_vcred.vc_uid);
- GOTO(out, rc = -ENOMEM);
- }
-
- rc = ptlrpc_request_bufs_pack(req, LUSTRE_OBD_VERSION, SEC_CTX_FINI,
- NULL, ctx);
- if (rc) {
- ptlrpc_request_free(req);
- GOTO(out_ref, rc);
- }
-
- /* fix the user desc */
- if (req->rq_pack_udesc) {
- /* we rely the fact that this request is in AUTH mode,
- * and user_desc at offset 2. */
- pud = lustre_msg_buf(req->rq_reqbuf, 2, sizeof(*pud));
- LASSERT(pud);
- pud->pud_uid = pud->pud_fsuid = ctx->cc_vcred.vc_uid;
- pud->pud_gid = pud->pud_fsgid = ctx->cc_vcred.vc_gid;
- pud->pud_cap = 0;
- pud->pud_ngroups = 0;
- }
-
- req->rq_phase = RQ_PHASE_RPC;
- rc = ptl_send_rpc(req, 1);
- if (rc)
- CWARN("ctx %p(%u->%s): rpc error %d, destroy locally\n", ctx,
- ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec), rc);
-
-out_ref:
- ptlrpc_req_finished(req);
-out:
- return rc;
-}
-
-int __init gss_init_cli_upcall(void)
-{
- return 0;
-}
-
-void __exit gss_exit_cli_upcall(void)
-{
-}
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * Adapted from MIT Kerberos 5-1.2.1 include/gssapi/gssapi.h
- *
- * Copyright (c) 2002 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Andy Adamson <andros@umich.edu>
- */
-
-/*
- * Copyright 1993 by OpenVision Technologies, Inc.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without fee,
- * provided that the above copyright notice appears in all copies and
- * that both that copyright notice and this permission notice appear in
- * supporting documentation, and that the name of OpenVision not be used
- * in advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. OpenVision makes no
- * representations about the suitability of this software for any
- * purpose. It is provided "as is" without express or implied warranty.
- *
- * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
- * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef __PTLRPC_GSS_GSS_ERR_H_
-#define __PTLRPC_GSS_GSS_ERR_H_
-
-typedef unsigned int OM_uint32;
-
-/*
- * Flag bits for context-level services.
- */
-#define GSS_C_DELEG_FLAG (1)
-#define GSS_C_MUTUAL_FLAG (2)
-#define GSS_C_REPLAY_FLAG (4)
-#define GSS_C_SEQUENCE_FLAG (8)
-#define GSS_C_CONF_FLAG (16)
-#define GSS_C_INTEG_FLAG (32)
-#define GSS_C_ANON_FLAG (64)
-#define GSS_C_PROT_READY_FLAG (128)
-#define GSS_C_TRANS_FLAG (256)
-
-/*
- * Credential usage options
- */
-#define GSS_C_BOTH (0)
-#define GSS_C_INITIATE (1)
-#define GSS_C_ACCEPT (2)
-
-/*
- * Status code types for gss_display_status
- */
-#define GSS_C_GSS_CODE (1)
-#define GSS_C_MECH_CODE (2)
-
-
-/*
- * Define the default Quality of Protection for per-message services. Note
- * that an implementation that offers multiple levels of QOP may either reserve
- * a value (for example zero, as assumed here) to mean "default protection", or
- * alternatively may simply equate GSS_C_QOP_DEFAULT to a specific explicit
- * QOP value. However a value of 0 should always be interpreted by a GSSAPI
- * implementation as a request for the default protection level.
- */
-#define GSS_C_QOP_DEFAULT (0)
-
-/*
- * Expiration time of 2^32-1 seconds means infinite lifetime for a
- * credential or security context
- */
-#define GSS_C_INDEFINITE ((OM_uint32) 0xfffffffful)
-
-
-/* Major status codes */
-
-#define GSS_S_COMPLETE (0)
-
-/*
- * Some "helper" definitions to make the status code macros obvious.
- */
-#define GSS_C_CALLING_ERROR_OFFSET (24)
-#define GSS_C_ROUTINE_ERROR_OFFSET (16)
-#define GSS_C_SUPPLEMENTARY_OFFSET (0)
-#define GSS_C_CALLING_ERROR_MASK ((OM_uint32) 0377ul)
-#define GSS_C_ROUTINE_ERROR_MASK ((OM_uint32) 0377ul)
-#define GSS_C_SUPPLEMENTARY_MASK ((OM_uint32) 0177777ul)
-
-/*
- * The macros that test status codes for error conditions. Note that the
- * GSS_ERROR() macro has changed slightly from the V1 GSSAPI so that it now
- * evaluates its argument only once.
- */
-#define GSS_CALLING_ERROR(x) \
- ((x) & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET))
-#define GSS_ROUTINE_ERROR(x) \
- ((x) & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))
-#define GSS_SUPPLEMENTARY_INFO(x) \
- ((x) & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET))
-#define GSS_ERROR(x) \
- ((x) & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \
- (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)))
-
-/*
- * Now the actual status code definitions
- */
-
-/*
- * Calling errors:
- */
-#define GSS_S_CALL_INACCESSIBLE_READ \
- (((OM_uint32) 1ul) << GSS_C_CALLING_ERROR_OFFSET)
-#define GSS_S_CALL_INACCESSIBLE_WRITE \
- (((OM_uint32) 2ul) << GSS_C_CALLING_ERROR_OFFSET)
-#define GSS_S_CALL_BAD_STRUCTURE \
- (((OM_uint32) 3ul) << GSS_C_CALLING_ERROR_OFFSET)
-
-/*
- * Routine errors:
- */
-#define GSS_S_BAD_MECH \
- (((OM_uint32) 1ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_NAME \
- (((OM_uint32) 2ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_NAMETYPE \
- (((OM_uint32) 3ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_BINDINGS \
- (((OM_uint32) 4ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_STATUS \
- (((OM_uint32) 5ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_SIG \
- (((OM_uint32) 6ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_NO_CRED \
- (((OM_uint32) 7ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_NO_CONTEXT \
- (((OM_uint32) 8ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_DEFECTIVE_TOKEN \
- (((OM_uint32) 9ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_DEFECTIVE_CREDENTIAL \
- (((OM_uint32) 10ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_CREDENTIALS_EXPIRED \
- (((OM_uint32) 11ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_CONTEXT_EXPIRED \
- (((OM_uint32) 12ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_FAILURE \
- (((OM_uint32) 13ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_BAD_QOP \
- (((OM_uint32) 14ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_UNAUTHORIZED \
- (((OM_uint32) 15ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_UNAVAILABLE \
- (((OM_uint32) 16ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_DUPLICATE_ELEMENT \
- (((OM_uint32) 17ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-#define GSS_S_NAME_NOT_MN \
- (((OM_uint32) 18ul) << GSS_C_ROUTINE_ERROR_OFFSET)
-
-/*
- * Supplementary info bits:
- */
-#define GSS_S_CONTINUE_NEEDED (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 0))
-#define GSS_S_DUPLICATE_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 1))
-#define GSS_S_OLD_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 2))
-#define GSS_S_UNSEQ_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 3))
-#define GSS_S_GAP_TOKEN (1 << (GSS_C_SUPPLEMENTARY_OFFSET + 4))
-
-/* XXXX these are not part of the GSSAPI C bindings! (but should be) */
-
-#define GSS_CALLING_ERROR_FIELD(x) \
- (((x) >> GSS_C_CALLING_ERROR_OFFSET) & GSS_C_CALLING_ERROR_MASK)
-#define GSS_ROUTINE_ERROR_FIELD(x) \
- (((x) >> GSS_C_ROUTINE_ERROR_OFFSET) & GSS_C_ROUTINE_ERROR_MASK)
-#define GSS_SUPPLEMENTARY_INFO_FIELD(x) \
- (((x) >> GSS_C_SUPPLEMENTARY_OFFSET) & GSS_C_SUPPLEMENTARY_MASK)
-
-/* XXXX This is a necessary evil until the spec is fixed */
-#define GSS_S_CRED_UNAVAIL GSS_S_FAILURE
-
-#endif /* __PTLRPC_GSS_GSS_ERR_H_ */
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2011, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * linux/net/sunrpc/gss_generic_token.c
- *
- * Adapted from MIT Kerberos 5-1.2.1 lib/gssapi/generic/util_token.c
- *
- * Copyright (c) 2000 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Andy Adamson <andros@umich.edu>
- */
-
-/*
- * Copyright 1993 by OpenVision Technologies, Inc.
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose is hereby granted without fee,
- * provided that the above copyright notice appears in all copies and
- * that both that copyright notice and this permission notice appear in
- * supporting documentation, and that the name of OpenVision not be used
- * in advertising or publicity pertaining to distribution of the software
- * without specific, written prior permission. OpenVision makes no
- * representations about the suitability of this software for any
- * purpose. It is provided "as is" without express or implied warranty.
- *
- * OPENVISION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
- * EVENT SHALL OPENVISION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
- * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
- * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
- * PERFORMANCE OF THIS SOFTWARE.
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-#include "gss_krb5.h"
-#include "gss_asn1.h"
-
-
-/* TWRITE_STR from gssapiP_generic.h */
-#define TWRITE_STR(ptr, str, len) \
- memcpy((ptr), (char *) (str), (len)); \
- (ptr) += (len);
-
-/* XXXX this code currently makes the assumption that a mech oid will
- never be longer than 127 bytes. This assumption is not inherent in
- the interfaces, so the code can be fixed if the OSI namespace
- balloons unexpectedly. */
-
-/* Each token looks like this:
-
-0x60 tag for APPLICATION 0, SEQUENCE
- (constructed, definite-length)
- <length> possible multiple bytes, need to parse/generate
- 0x06 tag for OBJECT IDENTIFIER
- <moid_length> compile-time constant string (assume 1 byte)
- <moid_bytes> compile-time constant string
- <inner_bytes> the ANY containing the application token
- bytes 0,1 are the token type
- bytes 2,n are the token data
-
-For the purposes of this abstraction, the token "header" consists of
-the sequence tag and length octets, the mech OID DER encoding, and the
-first two inner bytes, which indicate the token type. The token
-"body" consists of everything else.
-
-*/
-
-static
-int der_length_size(int length)
-{
- if (length < (1 << 7))
- return 1;
- else if (length < (1 << 8))
- return 2;
-#if (SIZEOF_INT == 2)
- else
- return 3;
-#else
- else if (length < (1 << 16))
- return 3;
- else if (length < (1 << 24))
- return 4;
- else
- return 5;
-#endif
-}
-
-static
-void der_write_length(unsigned char **buf, int length)
-{
- if (length < (1 << 7)) {
- *(*buf)++ = (unsigned char) length;
- } else {
- *(*buf)++ = (unsigned char) (der_length_size(length) + 127);
-#if (SIZEOF_INT > 2)
- if (length >= (1 << 24))
- *(*buf)++ = (unsigned char) (length >> 24);
- if (length >= (1 << 16))
- *(*buf)++ = (unsigned char) ((length >> 16) & 0xff);
-#endif
- if (length >= (1 << 8))
- *(*buf)++ = (unsigned char) ((length >> 8) & 0xff);
- *(*buf)++ = (unsigned char) (length & 0xff);
- }
-}
-
-/*
- * returns decoded length, or < 0 on failure. Advances buf and
- * decrements bufsize
- */
-static
-int der_read_length(unsigned char **buf, int *bufsize)
-{
- unsigned char sf;
- int ret;
-
- if (*bufsize < 1)
- return -1;
- sf = *(*buf)++;
- (*bufsize)--;
- if (sf & 0x80) {
- sf &= 0x7f;
- if (((*bufsize) - 1) < sf)
- return -1;
- if (sf > SIZEOF_INT)
- return -1;
- ret = 0;
- for (; sf; sf--) {
- ret = (ret << 8) + (*(*buf)++);
- (*bufsize)--;
- }
- } else {
- ret = sf;
- }
-
- return ret;
-}
-
-/*
- * returns the length of a token, given the mech oid and the body size
- */
-int g_token_size(rawobj_t *mech, unsigned int body_size)
-{
- /* set body_size to sequence contents size */
- body_size += 4 + (int) mech->len; /* NEED overflow check */
- return (1 + der_length_size(body_size) + body_size);
-}
-
-/*
- * fills in a buffer with the token header. The buffer is assumed to
- * be the right size. buf is advanced past the token header
- */
-void g_make_token_header(rawobj_t *mech, int body_size, unsigned char **buf)
-{
- *(*buf)++ = 0x60;
- der_write_length(buf, 4 + mech->len + body_size);
- *(*buf)++ = 0x06;
- *(*buf)++ = (unsigned char) mech->len;
- TWRITE_STR(*buf, mech->data, ((int) mech->len));
-}
-
-/*
- * Given a buffer containing a token, reads and verifies the token,
- * leaving buf advanced past the token header, and setting body_size
- * to the number of remaining bytes. Returns 0 on success,
- * G_BAD_TOK_HEADER for a variety of errors, and G_WRONG_MECH if the
- * mechanism in the token does not match the mech argument. buf and
- * *body_size are left unmodified on error.
- */
-__u32 g_verify_token_header(rawobj_t *mech, int *body_size,
- unsigned char **buf_in, int toksize)
-{
- unsigned char *buf = *buf_in;
- int seqsize;
- rawobj_t toid;
- int ret = 0;
-
- toksize -= 1;
- if (0 > toksize)
- return (G_BAD_TOK_HEADER);
- if (*buf++ != 0x60)
- return (G_BAD_TOK_HEADER);
-
- seqsize = der_read_length(&buf, &toksize);
- if (seqsize < 0)
- return(G_BAD_TOK_HEADER);
-
- if (seqsize != toksize)
- return (G_BAD_TOK_HEADER);
-
- toksize -= 1;
- if (0 > toksize)
- return (G_BAD_TOK_HEADER);
- if (*buf++ != 0x06)
- return (G_BAD_TOK_HEADER);
-
- toksize -= 1;
- if (0 > toksize)
- return (G_BAD_TOK_HEADER);
- toid.len = *buf++;
-
- toksize -= toid.len;
- if (0 > toksize)
- return (G_BAD_TOK_HEADER);
- toid.data = buf;
- buf += toid.len;
-
- if (!g_OID_equal(&toid, mech))
- ret = G_WRONG_MECH;
-
- /* G_WRONG_MECH is not returned immediately because it's more
- * important to return G_BAD_TOK_HEADER if the token header is
- * in fact bad
- */
- toksize -= 2;
- if (0 > toksize)
- return (G_BAD_TOK_HEADER);
-
- if (ret)
- return (ret);
-
- if (!ret) {
- *buf_in = buf;
- *body_size = toksize;
- }
-
- return (ret);
-}
-
-/*
- * Given a buffer containing a token, returns a copy of the mech oid in
- * the parameter mech.
- */
-__u32 g_get_mech_oid(rawobj_t *mech, rawobj_t *in_buf)
-{
- unsigned char *buf = in_buf->data;
- int len = in_buf->len;
- int ret = 0;
- int seqsize;
-
- len -= 1;
- if (0 > len)
- return (G_BAD_TOK_HEADER);
- if (*buf++ != 0x60)
- return (G_BAD_TOK_HEADER);
-
- seqsize = der_read_length(&buf, &len);
- if (seqsize < 0)
- return (G_BAD_TOK_HEADER);
-
- len -= 1;
- if (0 > len)
- return (G_BAD_TOK_HEADER);
- if (*buf++ != 0x06)
- return (G_BAD_TOK_HEADER);
-
- len -= 1;
- if (0 > len)
- return (G_BAD_TOK_HEADER);
- mech->len = *buf++;
-
- len -= mech->len;
- if (0 > len)
- return (G_BAD_TOK_HEADER);
- OBD_ALLOC_LARGE(mech->data, mech->len);
- if (!mech->data)
- return (G_BUFFER_ALLOC);
- memcpy(mech->data, buf, mech->len);
-
- return ret;
-}
+++ /dev/null
-/*
- * Modified from NFSv4 project for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2012, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-#ifndef __PTLRPC_GSS_GSS_INTERNAL_H_
-#define __PTLRPC_GSS_GSS_INTERNAL_H_
-
-#include <lustre_sec.h>
-
-/*
- * rawobj stuff
- */
-typedef struct netobj_s {
- __u32 len;
- __u8 data[0];
-} netobj_t;
-
-#define NETOBJ_EMPTY ((netobj_t) { 0 })
-
-typedef struct rawobj_s {
- __u32 len;
- __u8 *data;
-} rawobj_t;
-
-#define RAWOBJ_EMPTY ((rawobj_t) { 0, NULL })
-
-typedef struct rawobj_buf_s {
- __u32 dataoff;
- __u32 datalen;
- __u32 buflen;
- __u8 *buf;
-} rawobj_buf_t;
-
-int rawobj_empty(rawobj_t *obj);
-int rawobj_alloc(rawobj_t *obj, char *buf, int len);
-void rawobj_free(rawobj_t *obj);
-int rawobj_equal(rawobj_t *a, rawobj_t *b);
-int rawobj_dup(rawobj_t *dest, rawobj_t *src);
-int rawobj_serialize(rawobj_t *obj, __u32 **buf, __u32 *buflen);
-int rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen);
-int rawobj_extract_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen);
-int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen);
-int rawobj_extract_local_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen);
-int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj);
-int rawobj_from_netobj_alloc(rawobj_t *obj, netobj_t *netobj);
-
-int buffer_extract_bytes(const void **buf, __u32 *buflen,
- void *res, __u32 reslen);
-
-/*
- * several timeout values. client refresh upcall timeout we using
- * default in pipefs implemnetation.
- */
-#define __TIMEOUT_DELTA (10)
-
-#define GSS_SECINIT_RPC_TIMEOUT \
- (obd_timeout < __TIMEOUT_DELTA ? \
- __TIMEOUT_DELTA : obd_timeout - __TIMEOUT_DELTA)
-
-#define GSS_SECFINI_RPC_TIMEOUT (__TIMEOUT_DELTA)
-#define GSS_SECSVC_UPCALL_TIMEOUT (GSS_SECINIT_RPC_TIMEOUT)
-
-/*
- * default gc interval
- */
-#define GSS_GC_INTERVAL (60 * 60) /* 60 minutes */
-
-static inline
-unsigned long gss_round_ctx_expiry(unsigned long expiry,
- unsigned long sec_flags)
-{
- if (sec_flags & PTLRPC_SEC_FL_REVERSE)
- return expiry;
-
- if (get_seconds() + __TIMEOUT_DELTA <= expiry)
- return expiry - __TIMEOUT_DELTA;
-
- return expiry;
-}
-
-/*
- * Max encryption element in block cipher algorithms.
- */
-#define GSS_MAX_CIPHER_BLOCK (16)
-
-/*
- * XXX make it visible of kernel and lgssd/lsvcgssd
- */
-#define GSSD_INTERFACE_VERSION (1)
-
-#define PTLRPC_GSS_VERSION (1)
-
-
-enum ptlrpc_gss_proc {
- PTLRPC_GSS_PROC_DATA = 0,
- PTLRPC_GSS_PROC_INIT = 1,
- PTLRPC_GSS_PROC_CONTINUE_INIT = 2,
- PTLRPC_GSS_PROC_DESTROY = 3,
- PTLRPC_GSS_PROC_ERR = 4,
-};
-
-enum ptlrpc_gss_tgt {
- LUSTRE_GSS_TGT_MGS = 0,
- LUSTRE_GSS_TGT_MDS = 1,
- LUSTRE_GSS_TGT_OSS = 2,
-};
-
-enum ptlrpc_gss_header_flags {
- LUSTRE_GSS_PACK_BULK = 1,
- LUSTRE_GSS_PACK_USER = 2,
-};
-
-static inline
-__u32 import_to_gss_svc(struct obd_import *imp)
-{
- const char *name = imp->imp_obd->obd_type->typ_name;
-
- if (!strcmp(name, LUSTRE_MGC_NAME))
- return LUSTRE_GSS_TGT_MGS;
- if (!strcmp(name, LUSTRE_MDC_NAME))
- return LUSTRE_GSS_TGT_MDS;
- if (!strcmp(name, LUSTRE_OSC_NAME))
- return LUSTRE_GSS_TGT_OSS;
- LBUG();
- return 0;
-}
-
-/*
- * following 3 header must have the same size and offset
- */
-struct gss_header {
- __u8 gh_version; /* gss version */
- __u8 gh_sp; /* sec part */
- __u16 gh_pad0;
- __u32 gh_flags; /* wrap flags */
- __u32 gh_proc; /* proc */
- __u32 gh_seq; /* sequence */
- __u32 gh_svc; /* service */
- __u32 gh_pad1;
- __u32 gh_pad2;
- __u32 gh_pad3;
- netobj_t gh_handle; /* context handle */
-};
-
-struct gss_rep_header {
- __u8 gh_version;
- __u8 gh_sp;
- __u16 gh_pad0;
- __u32 gh_flags;
- __u32 gh_proc;
- __u32 gh_major;
- __u32 gh_minor;
- __u32 gh_seqwin;
- __u32 gh_pad2;
- __u32 gh_pad3;
- netobj_t gh_handle;
-};
-
-struct gss_err_header {
- __u8 gh_version;
- __u8 gh_sp;
- __u16 gh_pad0;
- __u32 gh_flags;
- __u32 gh_proc;
- __u32 gh_major;
- __u32 gh_minor;
- __u32 gh_pad1;
- __u32 gh_pad2;
- __u32 gh_pad3;
- netobj_t gh_handle;
-};
-
-/*
- * part of wire context information send from client which be saved and
- * used later by server.
- */
-struct gss_wire_ctx {
- __u32 gw_flags;
- __u32 gw_proc;
- __u32 gw_seq;
- __u32 gw_svc;
- rawobj_t gw_handle;
-};
-
-#define PTLRPC_GSS_MAX_HANDLE_SIZE (8)
-#define PTLRPC_GSS_HEADER_SIZE (sizeof(struct gss_header) + \
- PTLRPC_GSS_MAX_HANDLE_SIZE)
-
-
-static inline __u64 gss_handle_to_u64(rawobj_t *handle)
-{
- if (handle->len != PTLRPC_GSS_MAX_HANDLE_SIZE)
- return -1;
- return *((__u64 *) handle->data);
-}
-
-#define GSS_SEQ_WIN (2048)
-#define GSS_SEQ_WIN_MAIN GSS_SEQ_WIN
-#define GSS_SEQ_WIN_BACK (128)
-#define GSS_SEQ_REPACK_THRESHOLD (GSS_SEQ_WIN_MAIN / 2 + \
- GSS_SEQ_WIN_MAIN / 4)
-
-struct gss_svc_seq_data {
- spinlock_t ssd_lock;
- /*
- * highest sequence number seen so far, for main and back window
- */
- __u32 ssd_max_main;
- __u32 ssd_max_back;
- /*
- * main and back window
- * for i such that ssd_max - GSS_SEQ_WIN < i <= ssd_max, the i-th bit
- * of ssd_win is nonzero iff sequence number i has been seen already.
- */
- unsigned long ssd_win_main[GSS_SEQ_WIN_MAIN/BITS_PER_LONG];
- unsigned long ssd_win_back[GSS_SEQ_WIN_BACK/BITS_PER_LONG];
-};
-
-struct gss_svc_ctx {
- struct gss_ctx *gsc_mechctx;
- struct gss_svc_seq_data gsc_seqdata;
- rawobj_t gsc_rvs_hdl;
- __u32 gsc_rvs_seq;
- uid_t gsc_uid;
- gid_t gsc_gid;
- uid_t gsc_mapped_uid;
- unsigned int gsc_usr_root:1,
- gsc_usr_mds:1,
- gsc_usr_oss:1,
- gsc_remote:1,
- gsc_reverse:1;
-};
-
-struct gss_svc_reqctx {
- struct ptlrpc_svc_ctx src_base;
- /*
- * context
- */
- struct gss_wire_ctx src_wirectx;
- struct gss_svc_ctx *src_ctx;
- /*
- * record place of bulk_sec_desc in request/reply buffer
- */
- struct ptlrpc_bulk_sec_desc *src_reqbsd;
- int src_reqbsd_size;
- struct ptlrpc_bulk_sec_desc *src_repbsd;
- int src_repbsd_size;
- /*
- * flags
- */
- unsigned int src_init:1,
- src_init_continue:1,
- src_err_notify:1;
- int src_reserve_len;
-};
-
-struct gss_cli_ctx {
- struct ptlrpc_cli_ctx gc_base;
- __u32 gc_flavor;
- __u32 gc_proc;
- __u32 gc_win;
- atomic_t gc_seq;
- rawobj_t gc_handle;
- struct gss_ctx *gc_mechctx;
- /* handle for the buddy svc ctx */
- rawobj_t gc_svc_handle;
-};
-
-struct gss_cli_ctx_keyring {
- struct gss_cli_ctx gck_base;
- struct key *gck_key;
- struct timer_list *gck_timer;
-};
-
-struct gss_sec {
- struct ptlrpc_sec gs_base;
- struct gss_api_mech *gs_mech;
- spinlock_t gs_lock;
- __u64 gs_rvs_hdl;
-};
-
-struct gss_sec_pipefs {
- struct gss_sec gsp_base;
- int gsp_chash_size; /* must be 2^n */
- struct hlist_head gsp_chash[0];
-};
-
-/*
- * FIXME cleanup the keyring upcall mutexes
- */
-#define HAVE_KEYRING_UPCALL_SERIALIZED 1
-
-struct gss_sec_keyring {
- struct gss_sec gsk_base;
- /*
- * all contexts listed here. access is protected by sec spinlock.
- */
- struct hlist_head gsk_clist;
- /*
- * specially point to root ctx (only one at a time). access is
- * protected by sec spinlock.
- */
- struct ptlrpc_cli_ctx *gsk_root_ctx;
- /*
- * specially serialize upcalls for root context.
- */
- struct mutex gsk_root_uc_lock;
-
-#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
- struct mutex gsk_uc_lock; /* serialize upcalls */
-#endif
-};
-
-static inline struct gss_cli_ctx *ctx2gctx(struct ptlrpc_cli_ctx *ctx)
-{
- return container_of(ctx, struct gss_cli_ctx, gc_base);
-}
-
-static inline
-struct gss_cli_ctx_keyring *ctx2gctx_keyring(struct ptlrpc_cli_ctx *ctx)
-{
- return container_of(ctx2gctx(ctx),
- struct gss_cli_ctx_keyring, gck_base);
-}
-
-static inline struct gss_sec *sec2gsec(struct ptlrpc_sec *sec)
-{
- return container_of(sec, struct gss_sec, gs_base);
-}
-
-static inline struct gss_sec_pipefs *sec2gsec_pipefs(struct ptlrpc_sec *sec)
-{
- return container_of(sec2gsec(sec), struct gss_sec_pipefs, gsp_base);
-}
-
-static inline struct gss_sec_keyring *sec2gsec_keyring(struct ptlrpc_sec *sec)
-{
- return container_of(sec2gsec(sec), struct gss_sec_keyring, gsk_base);
-}
-
-
-#define GSS_CTX_INIT_MAX_LEN (1024)
-
-/*
- * This only guaranteed be enough for current krb5 des-cbc-crc . We might
- * adjust this when new enc type or mech added in.
- */
-#define GSS_PRIVBUF_PREFIX_LEN (32)
-#define GSS_PRIVBUF_SUFFIX_LEN (32)
-
-static inline
-struct gss_svc_reqctx *gss_svc_ctx2reqctx(struct ptlrpc_svc_ctx *ctx)
-{
- LASSERT(ctx);
- return container_of(ctx, struct gss_svc_reqctx, src_base);
-}
-
-static inline
-struct gss_svc_ctx *gss_svc_ctx2gssctx(struct ptlrpc_svc_ctx *ctx)
-{
- LASSERT(ctx);
- return gss_svc_ctx2reqctx(ctx)->src_ctx;
-}
-
-/* sec_gss.c */
-int gss_cli_ctx_match(struct ptlrpc_cli_ctx *ctx, struct vfs_cred *vcred);
-int gss_cli_ctx_display(struct ptlrpc_cli_ctx *ctx, char *buf, int bufsize);
-int gss_cli_ctx_sign(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
-int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
-int gss_cli_ctx_seal(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
-int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx, struct ptlrpc_request *req);
-
-int gss_sec_install_rctx(struct obd_import *imp, struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx);
-int gss_alloc_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
- int msgsize);
-void gss_free_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req);
-int gss_alloc_repbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
- int msgsize);
-void gss_free_repbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req);
-int gss_enlarge_reqbuf(struct ptlrpc_sec *sec, struct ptlrpc_request *req,
- int segment, int newsize);
-
-int gss_svc_accept(struct ptlrpc_sec_policy *policy,
- struct ptlrpc_request *req);
-void gss_svc_invalidate_ctx(struct ptlrpc_svc_ctx *svc_ctx);
-int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen);
-int gss_svc_authorize(struct ptlrpc_request *req);
-void gss_svc_free_rs(struct ptlrpc_reply_state *rs);
-void gss_svc_free_ctx(struct ptlrpc_svc_ctx *ctx);
-
-int cli_ctx_expire(struct ptlrpc_cli_ctx *ctx);
-int cli_ctx_check_death(struct ptlrpc_cli_ctx *ctx);
-
-int gss_copy_rvc_cli_ctx(struct ptlrpc_cli_ctx *cli_ctx,
- struct ptlrpc_svc_ctx *svc_ctx);
-
-struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment,
- int swabbed);
-netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment);
-
-void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx);
-int gss_pack_err_notify(struct ptlrpc_request *req, __u32 major, __u32 minor);
-int gss_check_seq_num(struct gss_svc_seq_data *sd, __u32 seq_num, int set);
-
-int gss_sec_create_common(struct gss_sec *gsec,
- struct ptlrpc_sec_policy *policy,
- struct obd_import *imp,
- struct ptlrpc_svc_ctx *ctx,
- struct sptlrpc_flavor *sf);
-void gss_sec_destroy_common(struct gss_sec *gsec);
-void gss_sec_kill(struct ptlrpc_sec *sec);
-
-int gss_cli_ctx_init_common(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_ctx_ops *ctxops,
- struct vfs_cred *vcred);
-int gss_cli_ctx_fini_common(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx);
-
-void gss_cli_ctx_flags2str(unsigned long flags, char *buf, int bufsize);
-
-/* gss_keyring.c */
-int __init gss_init_keyring(void);
-void __exit gss_exit_keyring(void);
-
-/* gss_pipefs.c */
-int __init gss_init_pipefs(void);
-void __exit gss_exit_pipefs(void);
-
-/* gss_bulk.c */
-int gss_cli_prep_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc);
-int gss_cli_ctx_wrap_bulk(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc);
-int gss_cli_ctx_unwrap_bulk(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc);
-int gss_svc_prep_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc);
-int gss_svc_unwrap_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc);
-int gss_svc_wrap_bulk(struct ptlrpc_request *req,
- struct ptlrpc_bulk_desc *desc);
-
-/* gss_mech_switch.c */
-int init_kerberos_module(void);
-void cleanup_kerberos_module(void);
-
-/* gss_generic_token.c */
-int g_token_size(rawobj_t *mech, unsigned int body_size);
-void g_make_token_header(rawobj_t *mech, int body_size, unsigned char **buf);
-__u32 g_verify_token_header(rawobj_t *mech, int *body_size,
- unsigned char **buf_in, int toksize);
-
-
-/* gss_cli_upcall.c */
-int gss_do_ctx_init_rpc(char *buffer, unsigned long count);
-int gss_do_ctx_fini_rpc(struct gss_cli_ctx *gctx);
-
-int __init gss_init_cli_upcall(void);
-void __exit gss_exit_cli_upcall(void);
-
-/* gss_svc_upcall.c */
-__u64 gss_get_next_ctx_index(void);
-int gss_svc_upcall_install_rvs_ctx(struct obd_import *imp,
- struct gss_sec *gsec,
- struct gss_cli_ctx *gctx);
-int gss_svc_upcall_expire_rvs_ctx(rawobj_t *handle);
-int gss_svc_upcall_dup_handle(rawobj_t *handle, struct gss_svc_ctx *ctx);
-int gss_svc_upcall_update_sequence(rawobj_t *handle, __u32 seq);
-int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
- struct gss_svc_reqctx *grctx,
- struct gss_wire_ctx *gw,
- struct obd_device *target,
- __u32 lustre_svc,
- rawobj_t *rvs_hdl,
- rawobj_t *in_token);
-struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
- struct gss_wire_ctx *gw);
-void gss_svc_upcall_put_ctx(struct gss_svc_ctx *ctx);
-void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx);
-
-int __init gss_init_svc_upcall(void);
-void __exit gss_exit_svc_upcall(void);
-
-/* lproc_gss.c */
-void gss_stat_oos_record_cli(int behind);
-void gss_stat_oos_record_svc(int phase, int replay);
-
-int __init gss_init_lproc(void);
-void __exit gss_exit_lproc(void);
-
-/* gss_krb5_mech.c */
-int __init init_kerberos_module(void);
-void __exit cleanup_kerberos_module(void);
-
-
-/* debug */
-static inline
-void __dbg_memdump(char *name, void *ptr, int size)
-{
- char *buf, *p = (char *) ptr;
- int bufsize = size * 2 + 1, i;
-
- OBD_ALLOC(buf, bufsize);
- if (!buf) {
- CDEBUG(D_ERROR, "DUMP ERROR: can't alloc %d bytes\n", bufsize);
- return;
- }
-
- for (i = 0; i < size; i++)
- sprintf(&buf[i+i], "%02x", (__u8) p[i]);
- buf[size + size] = '\0';
- LCONSOLE_INFO("DUMP %s@%p(%d): %s\n", name, ptr, size, buf);
- OBD_FREE(buf, bufsize);
-}
-
-#endif /* __PTLRPC_GSS_GSS_INTERNAL_H_ */
+++ /dev/null
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2012, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- *
- * lustre/ptlrpc/gss/gss_keyring.c
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/crypto.h>
-#include <linux/key.h>
-#include <linux/keyctl.h>
-#include <linux/key-type.h>
-#include <linux/mutex.h>
-#include <asm/atomic.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_sec.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-static struct ptlrpc_sec_policy gss_policy_keyring;
-static struct ptlrpc_ctx_ops gss_keyring_ctxops;
-static struct key_type gss_key_type;
-
-static int sec_install_rctx_kr(struct ptlrpc_sec *sec,
- struct ptlrpc_svc_ctx *svc_ctx);
-
-/*
- * the timeout is only for the case that upcall child process die abnormally.
- * in any other cases it should finally update kernel key.
- *
- * FIXME we'd better to incorporate the client & server side upcall timeouts
- * into the framework of Adaptive Timeouts, but we need to figure out how to
- * make sure that kernel knows the upcall processes is in-progress or died
- * unexpectedly.
- */
-#define KEYRING_UPCALL_TIMEOUT (obd_timeout + obd_timeout)
-
-/****************************************
- * internal helpers *
- ****************************************/
-
-#define DUMP_PROCESS_KEYRINGS(tsk) \
-{ \
- CWARN("DUMP PK: %s[%u,%u/%u](<-%s[%u,%u/%u]): " \
- "a %d, t %d, p %d, s %d, u %d, us %d, df %d\n", \
- tsk->comm, tsk->pid, tsk->uid, tsk->fsuid, \
- tsk->parent->comm, tsk->parent->pid, \
- tsk->parent->uid, tsk->parent->fsuid, \
- tsk->request_key_auth ? \
- tsk->request_key_auth->serial : 0, \
- key_cred(tsk)->thread_keyring ? \
- key_cred(tsk)->thread_keyring->serial : 0, \
- key_tgcred(tsk)->process_keyring ? \
- key_tgcred(tsk)->process_keyring->serial : 0, \
- key_tgcred(tsk)->session_keyring ? \
- key_tgcred(tsk)->session_keyring->serial : 0, \
- key_cred(tsk)->user->uid_keyring ? \
- key_cred(tsk)->user->uid_keyring->serial : 0, \
- key_cred(tsk)->user->session_keyring ? \
- key_cred(tsk)->user->session_keyring->serial : 0, \
- key_cred(tsk)->jit_keyring \
- ); \
-}
-
-#define DUMP_KEY(key) \
-{ \
- CWARN("DUMP KEY: %p(%d) ref %d u%u/g%u desc %s\n", \
- key, key->serial, atomic_read(&key->usage), \
- key->uid, key->gid, \
- key->description ? key->description : "n/a" \
- ); \
-}
-
-#define key_cred(tsk) ((tsk)->cred)
-#define key_tgcred(tsk) ((tsk)->cred->tgcred)
-
-static inline void keyring_upcall_lock(struct gss_sec_keyring *gsec_kr)
-{
-#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
- mutex_lock(&gsec_kr->gsk_uc_lock);
-#endif
-}
-
-static inline void keyring_upcall_unlock(struct gss_sec_keyring *gsec_kr)
-{
-#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
- mutex_unlock(&gsec_kr->gsk_uc_lock);
-#endif
-}
-
-static inline void key_revoke_locked(struct key *key)
-{
- set_bit(KEY_FLAG_REVOKED, &key->flags);
-}
-
-static void ctx_upcall_timeout_kr(unsigned long data)
-{
- struct ptlrpc_cli_ctx *ctx = (struct ptlrpc_cli_ctx *) data;
- struct key *key = ctx2gctx_keyring(ctx)->gck_key;
-
- CWARN("ctx %p, key %p\n", ctx, key);
-
- LASSERT(key);
-
- cli_ctx_expire(ctx);
- key_revoke_locked(key);
-}
-
-static
-void ctx_start_timer_kr(struct ptlrpc_cli_ctx *ctx, long timeout)
-{
- struct gss_cli_ctx_keyring *gctx_kr = ctx2gctx_keyring(ctx);
- struct timer_list *timer = gctx_kr->gck_timer;
-
- LASSERT(timer);
-
- CDEBUG(D_SEC, "ctx %p: start timer %lds\n", ctx, timeout);
- timeout = timeout * HZ + cfs_time_current();
-
- init_timer(timer);
- timer->expires = timeout;
- timer->data = (unsigned long) ctx;
- timer->function = ctx_upcall_timeout_kr;
-
- add_timer(timer);
-}
-
-/*
- * caller should make sure no race with other threads
- */
-static
-void ctx_clear_timer_kr(struct ptlrpc_cli_ctx *ctx)
-{
- struct gss_cli_ctx_keyring *gctx_kr = ctx2gctx_keyring(ctx);
- struct timer_list *timer = gctx_kr->gck_timer;
-
- if (timer == NULL)
- return;
-
- CDEBUG(D_SEC, "ctx %p, key %p\n", ctx, gctx_kr->gck_key);
-
- gctx_kr->gck_timer = NULL;
-
- del_singleshot_timer_sync(timer);
-
- OBD_FREE_PTR(timer);
-}
-
-static
-struct ptlrpc_cli_ctx *ctx_create_kr(struct ptlrpc_sec *sec,
- struct vfs_cred *vcred)
-{
- struct ptlrpc_cli_ctx *ctx;
- struct gss_cli_ctx_keyring *gctx_kr;
-
- OBD_ALLOC_PTR(gctx_kr);
- if (gctx_kr == NULL)
- return NULL;
-
- OBD_ALLOC_PTR(gctx_kr->gck_timer);
- if (gctx_kr->gck_timer == NULL) {
- OBD_FREE_PTR(gctx_kr);
- return NULL;
- }
- init_timer(gctx_kr->gck_timer);
-
- ctx = &gctx_kr->gck_base.gc_base;
-
- if (gss_cli_ctx_init_common(sec, ctx, &gss_keyring_ctxops, vcred)) {
- OBD_FREE_PTR(gctx_kr->gck_timer);
- OBD_FREE_PTR(gctx_kr);
- return NULL;
- }
-
- ctx->cc_expire = cfs_time_current_sec() + KEYRING_UPCALL_TIMEOUT;
- clear_bit(PTLRPC_CTX_NEW_BIT, &ctx->cc_flags);
- atomic_inc(&ctx->cc_refcount); /* for the caller */
-
- return ctx;
-}
-
-static void ctx_destroy_kr(struct ptlrpc_cli_ctx *ctx)
-{
- struct ptlrpc_sec *sec = ctx->cc_sec;
- struct gss_cli_ctx_keyring *gctx_kr = ctx2gctx_keyring(ctx);
-
- CDEBUG(D_SEC, "destroying ctx %p\n", ctx);
-
- /* at this time the association with key has been broken. */
- LASSERT(sec);
- LASSERT(atomic_read(&sec->ps_refcount) > 0);
- LASSERT(atomic_read(&sec->ps_nctx) > 0);
- LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
- LASSERT(gctx_kr->gck_key == NULL);
-
- ctx_clear_timer_kr(ctx);
- LASSERT(gctx_kr->gck_timer == NULL);
-
- if (gss_cli_ctx_fini_common(sec, ctx))
- return;
-
- OBD_FREE_PTR(gctx_kr);
-
- atomic_dec(&sec->ps_nctx);
- sptlrpc_sec_put(sec);
-}
-
-static void ctx_release_kr(struct ptlrpc_cli_ctx *ctx, int sync)
-{
- if (sync) {
- ctx_destroy_kr(ctx);
- } else {
- atomic_inc(&ctx->cc_refcount);
- sptlrpc_gc_add_ctx(ctx);
- }
-}
-
-static void ctx_put_kr(struct ptlrpc_cli_ctx *ctx, int sync)
-{
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- if (atomic_dec_and_test(&ctx->cc_refcount))
- ctx_release_kr(ctx, sync);
-}
-
-/*
- * key <-> ctx association and rules:
- * - ctx might not bind with any key
- * - key/ctx binding is protected by key semaphore (if the key present)
- * - key and ctx each take a reference of the other
- * - ctx enlist/unlist is protected by ctx spinlock
- * - never enlist a ctx after it's been unlisted
- * - whoever do enlist should also do bind, lock key before enlist:
- * - lock key -> lock ctx -> enlist -> unlock ctx -> bind -> unlock key
- * - whoever do unlist should also do unbind:
- * - lock key -> lock ctx -> unlist -> unlock ctx -> unbind -> unlock key
- * - lock ctx -> unlist -> unlock ctx -> lock key -> unbind -> unlock key
- */
-
-static inline void spin_lock_if(spinlock_t *lock, int condition)
-{
- if (condition)
- spin_lock(lock);
-}
-
-static inline void spin_unlock_if(spinlock_t *lock, int condition)
-{
- if (condition)
- spin_unlock(lock);
-}
-
-static void ctx_enlist_kr(struct ptlrpc_cli_ctx *ctx, int is_root, int locked)
-{
- struct ptlrpc_sec *sec = ctx->cc_sec;
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
-
- LASSERT(!test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags));
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- spin_lock_if(&sec->ps_lock, !locked);
-
- atomic_inc(&ctx->cc_refcount);
- set_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags);
- hlist_add_head(&ctx->cc_cache, &gsec_kr->gsk_clist);
- if (is_root)
- gsec_kr->gsk_root_ctx = ctx;
-
- spin_unlock_if(&sec->ps_lock, !locked);
-}
-
-/*
- * Note after this get called, caller should not access ctx again because
- * it might have been freed, unless caller hold at least one refcount of
- * the ctx.
- *
- * return non-zero if we indeed unlist this ctx.
- */
-static int ctx_unlist_kr(struct ptlrpc_cli_ctx *ctx, int locked)
-{
- struct ptlrpc_sec *sec = ctx->cc_sec;
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
-
- /* if hashed bit has gone, leave the job to somebody who is doing it */
- if (test_and_clear_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0)
- return 0;
-
- /* drop ref inside spin lock to prevent race with other operations */
- spin_lock_if(&sec->ps_lock, !locked);
-
- if (gsec_kr->gsk_root_ctx == ctx)
- gsec_kr->gsk_root_ctx = NULL;
- hlist_del_init(&ctx->cc_cache);
- atomic_dec(&ctx->cc_refcount);
-
- spin_unlock_if(&sec->ps_lock, !locked);
-
- return 1;
-}
-
-/*
- * bind a key with a ctx together.
- * caller must hold write lock of the key, as well as ref on key & ctx.
- */
-static void bind_key_ctx(struct key *key, struct ptlrpc_cli_ctx *ctx)
-{
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(atomic_read(&key->usage) > 0);
- LASSERT(ctx2gctx_keyring(ctx)->gck_key == NULL);
- LASSERT(key->payload.data == NULL);
-
- /* at this time context may or may not in list. */
- key_get(key);
- atomic_inc(&ctx->cc_refcount);
- ctx2gctx_keyring(ctx)->gck_key = key;
- key->payload.data = ctx;
-}
-
-/*
- * unbind a key and a ctx.
- * caller must hold write lock, as well as a ref of the key.
- */
-static void unbind_key_ctx(struct key *key, struct ptlrpc_cli_ctx *ctx)
-{
- LASSERT(key->payload.data == ctx);
- LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
-
- /* must revoke the key, or others may treat it as newly created */
- key_revoke_locked(key);
-
- key->payload.data = NULL;
- ctx2gctx_keyring(ctx)->gck_key = NULL;
-
- /* once ctx get split from key, the timer is meaningless */
- ctx_clear_timer_kr(ctx);
-
- ctx_put_kr(ctx, 1);
- key_put(key);
-}
-
-/*
- * given a ctx, unbind with its coupled key, if any.
- * unbind could only be called once, so we don't worry the key be released
- * by someone else.
- */
-static void unbind_ctx_kr(struct ptlrpc_cli_ctx *ctx)
-{
- struct key *key = ctx2gctx_keyring(ctx)->gck_key;
-
- if (key) {
- LASSERT(key->payload.data == ctx);
-
- key_get(key);
- down_write(&key->sem);
- unbind_key_ctx(key, ctx);
- up_write(&key->sem);
- key_put(key);
- }
-}
-
-/*
- * given a key, unbind with its coupled ctx, if any.
- * caller must hold write lock, as well as a ref of the key.
- */
-static void unbind_key_locked(struct key *key)
-{
- struct ptlrpc_cli_ctx *ctx = key->payload.data;
-
- if (ctx)
- unbind_key_ctx(key, ctx);
-}
-
-/*
- * unlist a ctx, and unbind from coupled key
- */
-static void kill_ctx_kr(struct ptlrpc_cli_ctx *ctx)
-{
- if (ctx_unlist_kr(ctx, 0))
- unbind_ctx_kr(ctx);
-}
-
-/*
- * given a key, unlist and unbind with the coupled ctx (if any).
- * caller must hold write lock, as well as a ref of the key.
- */
-static void kill_key_locked(struct key *key)
-{
- struct ptlrpc_cli_ctx *ctx = key->payload.data;
-
- if (ctx && ctx_unlist_kr(ctx, 0))
- unbind_key_locked(key);
-}
-
-/*
- * caller should hold one ref on contexts in freelist.
- */
-static void dispose_ctx_list_kr(struct hlist_head *freelist)
-{
- struct hlist_node *next;
- struct ptlrpc_cli_ctx *ctx;
- struct gss_cli_ctx *gctx;
-
- hlist_for_each_entry_safe(ctx, next, freelist, cc_cache) {
- hlist_del_init(&ctx->cc_cache);
-
- /* reverse ctx: update current seq to buddy svcctx if exist.
- * ideally this should be done at gss_cli_ctx_finalize(), but
- * the ctx destroy could be delayed by:
- * 1) ctx still has reference;
- * 2) ctx destroy is asynchronous;
- * and reverse import call inval_all_ctx() require this be done
- *_immediately_ otherwise newly created reverse ctx might copy
- * the very old sequence number from svcctx. */
- gctx = ctx2gctx(ctx);
- if (!rawobj_empty(&gctx->gc_svc_handle) &&
- sec_is_reverse(gctx->gc_base.cc_sec)) {
- gss_svc_upcall_update_sequence(&gctx->gc_svc_handle,
- (__u32) atomic_read(&gctx->gc_seq));
- }
-
- /* we need to wakeup waiting reqs here. the context might
- * be forced released before upcall finished, then the
- * late-arrived downcall can't find the ctx even. */
- sptlrpc_cli_ctx_wakeup(ctx);
-
- unbind_ctx_kr(ctx);
- ctx_put_kr(ctx, 0);
- }
-}
-
-/*
- * lookup a root context directly in a sec, return root ctx with a
- * reference taken or NULL.
- */
-static
-struct ptlrpc_cli_ctx * sec_lookup_root_ctx_kr(struct ptlrpc_sec *sec)
-{
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
- struct ptlrpc_cli_ctx *ctx = NULL;
-
- spin_lock(&sec->ps_lock);
-
- ctx = gsec_kr->gsk_root_ctx;
-
- if (ctx == NULL && unlikely(sec_is_reverse(sec))) {
- struct ptlrpc_cli_ctx *tmp;
-
- /* reverse ctx, search root ctx in list, choose the one
- * with shortest expire time, which is most possibly have
- * an established peer ctx at client side. */
- hlist_for_each_entry(tmp, &gsec_kr->gsk_clist, cc_cache) {
- if (ctx == NULL || ctx->cc_expire == 0 ||
- ctx->cc_expire > tmp->cc_expire) {
- ctx = tmp;
- /* promote to be root_ctx */
- gsec_kr->gsk_root_ctx = ctx;
- }
- }
- }
-
- if (ctx) {
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(!hlist_empty(&gsec_kr->gsk_clist));
- atomic_inc(&ctx->cc_refcount);
- }
-
- spin_unlock(&sec->ps_lock);
-
- return ctx;
-}
-
-#define RVS_CTX_EXPIRE_NICE (10)
-
-static
-void rvs_sec_install_root_ctx_kr(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *new_ctx,
- struct key *key)
-{
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
- struct ptlrpc_cli_ctx *ctx;
- cfs_time_t now;
-
- LASSERT(sec_is_reverse(sec));
-
- spin_lock(&sec->ps_lock);
-
- now = cfs_time_current_sec();
-
- /* set all existing ctxs short expiry */
- hlist_for_each_entry(ctx, &gsec_kr->gsk_clist, cc_cache) {
- if (ctx->cc_expire > now + RVS_CTX_EXPIRE_NICE) {
- ctx->cc_early_expire = 1;
- ctx->cc_expire = now + RVS_CTX_EXPIRE_NICE;
- }
- }
-
- /* if there's root_ctx there, instead obsolete the current
- * immediately, we leave it continue operating for a little while.
- * hopefully when the first backward rpc with newest ctx send out,
- * the client side already have the peer ctx well established. */
- ctx_enlist_kr(new_ctx, gsec_kr->gsk_root_ctx ? 0 : 1, 1);
-
- if (key)
- bind_key_ctx(key, new_ctx);
-
- spin_unlock(&sec->ps_lock);
-}
-
-static void construct_key_desc(void *buf, int bufsize,
- struct ptlrpc_sec *sec, uid_t uid)
-{
- snprintf(buf, bufsize, "%d@%x", uid, sec->ps_id);
- ((char *)buf)[bufsize - 1] = '\0';
-}
-
-/****************************************
- * sec apis *
- ****************************************/
-
-static
-struct ptlrpc_sec * gss_sec_create_kr(struct obd_import *imp,
- struct ptlrpc_svc_ctx *svcctx,
- struct sptlrpc_flavor *sf)
-{
- struct gss_sec_keyring *gsec_kr;
-
- OBD_ALLOC(gsec_kr, sizeof(*gsec_kr));
- if (gsec_kr == NULL)
- return NULL;
-
- INIT_HLIST_HEAD(&gsec_kr->gsk_clist);
- gsec_kr->gsk_root_ctx = NULL;
- mutex_init(&gsec_kr->gsk_root_uc_lock);
-#ifdef HAVE_KEYRING_UPCALL_SERIALIZED
- mutex_init(&gsec_kr->gsk_uc_lock);
-#endif
-
- if (gss_sec_create_common(&gsec_kr->gsk_base, &gss_policy_keyring,
- imp, svcctx, sf))
- goto err_free;
-
- if (svcctx != NULL &&
- sec_install_rctx_kr(&gsec_kr->gsk_base.gs_base, svcctx)) {
- gss_sec_destroy_common(&gsec_kr->gsk_base);
- goto err_free;
- }
-
- return &gsec_kr->gsk_base.gs_base;
-
-err_free:
- OBD_FREE(gsec_kr, sizeof(*gsec_kr));
- return NULL;
-}
-
-static
-void gss_sec_destroy_kr(struct ptlrpc_sec *sec)
-{
- struct gss_sec *gsec = sec2gsec(sec);
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
-
- CDEBUG(D_SEC, "destroy %s@%p\n", sec->ps_policy->sp_name, sec);
-
- LASSERT(hlist_empty(&gsec_kr->gsk_clist));
- LASSERT(gsec_kr->gsk_root_ctx == NULL);
-
- gss_sec_destroy_common(gsec);
-
- OBD_FREE(gsec_kr, sizeof(*gsec_kr));
-}
-
-static inline int user_is_root(struct ptlrpc_sec *sec, struct vfs_cred *vcred)
-{
- /* except the ROOTONLY flag, treat it as root user only if real uid
- * is 0, euid/fsuid being 0 are handled as setuid scenarios */
- if (sec_is_rootonly(sec) || (vcred->vc_uid == 0))
- return 1;
- else
- return 0;
-}
-
-/*
- * unlink request key from it's ring, which is linked during request_key().
- * sadly, we have to 'guess' which keyring it's linked to.
- *
- * FIXME this code is fragile, depend on how request_key_link() is implemented.
- */
-static void request_key_unlink(struct key *key)
-{
- struct task_struct *tsk = current;
- struct key *ring;
-
- switch (key_cred(tsk)->jit_keyring) {
- case KEY_REQKEY_DEFL_DEFAULT:
- case KEY_REQKEY_DEFL_THREAD_KEYRING:
- ring = key_get(key_cred(tsk)->thread_keyring);
- if (ring)
- break;
- case KEY_REQKEY_DEFL_PROCESS_KEYRING:
- ring = key_get(key_tgcred(tsk)->process_keyring);
- if (ring)
- break;
- case KEY_REQKEY_DEFL_SESSION_KEYRING:
- rcu_read_lock();
- ring = key_get(rcu_dereference(key_tgcred(tsk)
- ->session_keyring));
- rcu_read_unlock();
- if (ring)
- break;
- case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
- ring = key_get(key_cred(tsk)->user->session_keyring);
- break;
- case KEY_REQKEY_DEFL_USER_KEYRING:
- ring = key_get(key_cred(tsk)->user->uid_keyring);
- break;
- case KEY_REQKEY_DEFL_GROUP_KEYRING:
- default:
- LBUG();
- }
-
- LASSERT(ring);
- key_unlink(ring, key);
- key_put(ring);
-}
-
-static
-struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_kr(struct ptlrpc_sec *sec,
- struct vfs_cred *vcred,
- int create, int remove_dead)
-{
- struct obd_import *imp = sec->ps_import;
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
- struct ptlrpc_cli_ctx *ctx = NULL;
- unsigned int is_root = 0, create_new = 0;
- struct key *key;
- char desc[24];
- char *coinfo;
- int coinfo_size;
- char *co_flags = "";
-
- LASSERT(imp != NULL);
-
- is_root = user_is_root(sec, vcred);
-
- /* a little bit optimization for root context */
- if (is_root) {
- ctx = sec_lookup_root_ctx_kr(sec);
- /*
- * Only lookup directly for REVERSE sec, which should
- * always succeed.
- */
- if (ctx || sec_is_reverse(sec))
- return ctx;
- }
-
- LASSERT(create != 0);
-
- /* for root context, obtain lock and check again, this time hold
- * the root upcall lock, make sure nobody else populated new root
- * context after last check. */
- if (is_root) {
- mutex_lock(&gsec_kr->gsk_root_uc_lock);
-
- ctx = sec_lookup_root_ctx_kr(sec);
- if (ctx)
- goto out;
-
- /* update reverse handle for root user */
- sec2gsec(sec)->gs_rvs_hdl = gss_get_next_ctx_index();
-
- switch (sec->ps_part) {
- case LUSTRE_SP_MDT:
- co_flags = "m";
- break;
- case LUSTRE_SP_OST:
- co_flags = "o";
- break;
- case LUSTRE_SP_MGC:
- co_flags = "rmo";
- break;
- case LUSTRE_SP_CLI:
- co_flags = "r";
- break;
- case LUSTRE_SP_MGS:
- default:
- LBUG();
- }
- }
-
- /* in case of setuid, key will be constructed as owner of fsuid/fsgid,
- * but we do authentication based on real uid/gid. the key permission
- * bits will be exactly as POS_ALL, so only processes who subscribed
- * this key could have the access, although the quota might be counted
- * on others (fsuid/fsgid).
- *
- * keyring will use fsuid/fsgid as upcall parameters, so we have to
- * encode real uid/gid into callout info.
- */
-
- construct_key_desc(desc, sizeof(desc), sec, vcred->vc_uid);
-
- /* callout info format:
- * secid:mech:uid:gid:flags:svc_type:peer_nid:target_uuid
- */
- coinfo_size = sizeof(struct obd_uuid) + MAX_OBD_NAME + 64;
- OBD_ALLOC(coinfo, coinfo_size);
- if (coinfo == NULL)
- goto out;
-
- snprintf(coinfo, coinfo_size, "%d:%s:%u:%u:%s:%d:"LPX64":%s",
- sec->ps_id, sec2gsec(sec)->gs_mech->gm_name,
- vcred->vc_uid, vcred->vc_gid,
- co_flags, import_to_gss_svc(imp),
- imp->imp_connection->c_peer.nid, imp->imp_obd->obd_name);
-
- CDEBUG(D_SEC, "requesting key for %s\n", desc);
-
- keyring_upcall_lock(gsec_kr);
- key = request_key(&gss_key_type, desc, coinfo);
- keyring_upcall_unlock(gsec_kr);
-
- OBD_FREE(coinfo, coinfo_size);
-
- if (IS_ERR(key)) {
- CERROR("failed request key: %ld\n", PTR_ERR(key));
- goto out;
- }
- CDEBUG(D_SEC, "obtained key %08x for %s\n", key->serial, desc);
-
- /* once payload.data was pointed to a ctx, it never changes until
- * we de-associate them; but parallel request_key() may return
- * a key with payload.data == NULL at the same time. so we still
- * need wirtelock of key->sem to serialize them. */
- down_write(&key->sem);
-
- if (likely(key->payload.data != NULL)) {
- ctx = key->payload.data;
-
- LASSERT(atomic_read(&ctx->cc_refcount) >= 1);
- LASSERT(ctx2gctx_keyring(ctx)->gck_key == key);
- LASSERT(atomic_read(&key->usage) >= 2);
-
- /* simply take a ref and return. it's upper layer's
- * responsibility to detect & replace dead ctx. */
- atomic_inc(&ctx->cc_refcount);
- } else {
- /* pre initialization with a cli_ctx. this can't be done in
- * key_instantiate() because we'v no enough information
- * there. */
- ctx = ctx_create_kr(sec, vcred);
- if (ctx != NULL) {
- ctx_enlist_kr(ctx, is_root, 0);
- bind_key_ctx(key, ctx);
-
- ctx_start_timer_kr(ctx, KEYRING_UPCALL_TIMEOUT);
-
- CDEBUG(D_SEC, "installed key %p <-> ctx %p (sec %p)\n",
- key, ctx, sec);
- } else {
- /* we'd prefer to call key_revoke(), but we more like
- * to revoke it within this key->sem locked period. */
- key_revoke_locked(key);
- }
-
- create_new = 1;
- }
-
- up_write(&key->sem);
-
- if (is_root && create_new)
- request_key_unlink(key);
-
- key_put(key);
-out:
- if (is_root)
- mutex_unlock(&gsec_kr->gsk_root_uc_lock);
- return ctx;
-}
-
-static
-void gss_sec_release_ctx_kr(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx,
- int sync)
-{
- LASSERT(atomic_read(&sec->ps_refcount) > 0);
- LASSERT(atomic_read(&ctx->cc_refcount) == 0);
- ctx_release_kr(ctx, sync);
-}
-
-/*
- * flush context of normal user, we must resort to keyring itself to find out
- * contexts which belong to me.
- *
- * Note here we suppose only to flush _my_ context, the "uid" will
- * be ignored in the search.
- */
-static
-void flush_user_ctx_cache_kr(struct ptlrpc_sec *sec,
- uid_t uid,
- int grace, int force)
-{
- struct key *key;
- char desc[24];
-
- /* nothing to do for reverse or rootonly sec */
- if (sec_is_reverse(sec) || sec_is_rootonly(sec))
- return;
-
- construct_key_desc(desc, sizeof(desc), sec, uid);
-
- /* there should be only one valid key, but we put it in the
- * loop in case of any weird cases */
- for (;;) {
- key = request_key(&gss_key_type, desc, NULL);
- if (IS_ERR(key)) {
- CDEBUG(D_SEC, "No more key found for current user\n");
- break;
- }
-
- down_write(&key->sem);
-
- kill_key_locked(key);
-
- /* kill_key_locked() should usually revoke the key, but we
- * revoke it again to make sure, e.g. some case the key may
- * not well coupled with a context. */
- key_revoke_locked(key);
-
- up_write(&key->sem);
-
- key_put(key);
- }
-}
-
-/*
- * flush context of root or all, we iterate through the list.
- */
-static
-void flush_spec_ctx_cache_kr(struct ptlrpc_sec *sec,
- uid_t uid,
- int grace, int force)
-{
- struct gss_sec_keyring *gsec_kr;
- struct hlist_head freelist = HLIST_HEAD_INIT;
- struct hlist_node *next;
- struct ptlrpc_cli_ctx *ctx;
-
- gsec_kr = sec2gsec_keyring(sec);
-
- spin_lock(&sec->ps_lock);
- hlist_for_each_entry_safe(ctx, next,
- &gsec_kr->gsk_clist, cc_cache) {
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- if (uid != -1 && uid != ctx->cc_vcred.vc_uid)
- continue;
-
- /* at this moment there's at least 2 base reference:
- * key association and in-list. */
- if (atomic_read(&ctx->cc_refcount) > 2) {
- if (!force)
- continue;
- CWARN("flush busy ctx %p(%u->%s, extra ref %d)\n",
- ctx, ctx->cc_vcred.vc_uid,
- sec2target_str(ctx->cc_sec),
- atomic_read(&ctx->cc_refcount) - 2);
- }
-
- set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags);
- if (!grace)
- clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
-
- atomic_inc(&ctx->cc_refcount);
-
- if (ctx_unlist_kr(ctx, 1)) {
- hlist_add_head(&ctx->cc_cache, &freelist);
- } else {
- LASSERT(atomic_read(&ctx->cc_refcount) >= 2);
- atomic_dec(&ctx->cc_refcount);
- }
- }
- spin_unlock(&sec->ps_lock);
-
- dispose_ctx_list_kr(&freelist);
-}
-
-static
-int gss_sec_flush_ctx_cache_kr(struct ptlrpc_sec *sec,
- uid_t uid, int grace, int force)
-{
- CDEBUG(D_SEC, "sec %p(%d, nctx %d), uid %d, grace %d, force %d\n",
- sec, atomic_read(&sec->ps_refcount),
- atomic_read(&sec->ps_nctx),
- uid, grace, force);
-
- if (uid != -1 && uid != 0)
- flush_user_ctx_cache_kr(sec, uid, grace, force);
- else
- flush_spec_ctx_cache_kr(sec, uid, grace, force);
-
- return 0;
-}
-
-static
-void gss_sec_gc_ctx_kr(struct ptlrpc_sec *sec)
-{
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
- struct hlist_head freelist = HLIST_HEAD_INIT;
- struct hlist_node *next;
- struct ptlrpc_cli_ctx *ctx;
-
- CWARN("running gc\n");
-
- spin_lock(&sec->ps_lock);
- hlist_for_each_entry_safe(ctx, next,
- &gsec_kr->gsk_clist, cc_cache) {
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- atomic_inc(&ctx->cc_refcount);
-
- if (cli_ctx_check_death(ctx) && ctx_unlist_kr(ctx, 1)) {
- hlist_add_head(&ctx->cc_cache, &freelist);
- CWARN("unhashed ctx %p\n", ctx);
- } else {
- LASSERT(atomic_read(&ctx->cc_refcount) >= 2);
- atomic_dec(&ctx->cc_refcount);
- }
- }
- spin_unlock(&sec->ps_lock);
-
- dispose_ctx_list_kr(&freelist);
-}
-
-static
-int gss_sec_display_kr(struct ptlrpc_sec *sec, struct seq_file *seq)
-{
- struct gss_sec_keyring *gsec_kr = sec2gsec_keyring(sec);
- struct hlist_node *next;
- struct ptlrpc_cli_ctx *ctx;
- struct gss_cli_ctx *gctx;
- time_t now = cfs_time_current_sec();
-
- spin_lock(&sec->ps_lock);
- hlist_for_each_entry_safe(ctx, next,
- &gsec_kr->gsk_clist, cc_cache) {
- struct key *key;
- char flags_str[40];
- char mech[40];
-
- gctx = ctx2gctx(ctx);
- key = ctx2gctx_keyring(ctx)->gck_key;
-
- gss_cli_ctx_flags2str(ctx->cc_flags,
- flags_str, sizeof(flags_str));
-
- if (gctx->gc_mechctx)
- lgss_display(gctx->gc_mechctx, mech, sizeof(mech));
- else
- snprintf(mech, sizeof(mech), "N/A");
- mech[sizeof(mech) - 1] = '\0';
-
- seq_printf(seq, "%p: uid %u, ref %d, expire %ld(%+ld), fl %s, "
- "seq %d, win %u, key %08x(ref %d), "
- "hdl "LPX64":"LPX64", mech: %s\n",
- ctx, ctx->cc_vcred.vc_uid,
- atomic_read(&ctx->cc_refcount),
- ctx->cc_expire,
- ctx->cc_expire ? ctx->cc_expire - now : 0,
- flags_str,
- atomic_read(&gctx->gc_seq),
- gctx->gc_win,
- key ? key->serial : 0,
- key ? atomic_read(&key->usage) : 0,
- gss_handle_to_u64(&gctx->gc_handle),
- gss_handle_to_u64(&gctx->gc_svc_handle),
- mech);
- }
- spin_unlock(&sec->ps_lock);
-
- return 0;
-}
-
-/****************************************
- * cli_ctx apis *
- ****************************************/
-
-static
-int gss_cli_ctx_refresh_kr(struct ptlrpc_cli_ctx *ctx)
-{
- /* upcall is already on the way */
- return 0;
-}
-
-static
-int gss_cli_ctx_validate_kr(struct ptlrpc_cli_ctx *ctx)
-{
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(ctx->cc_sec);
-
- if (cli_ctx_check_death(ctx)) {
- kill_ctx_kr(ctx);
- return 1;
- }
-
- if (cli_ctx_is_ready(ctx))
- return 0;
- return 1;
-}
-
-static
-void gss_cli_ctx_die_kr(struct ptlrpc_cli_ctx *ctx, int grace)
-{
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(ctx->cc_sec);
-
- cli_ctx_expire(ctx);
- kill_ctx_kr(ctx);
-}
-
-/****************************************
- * (reverse) service *
- ****************************************/
-
-/*
- * reverse context could have nothing to do with keyrings. here we still keep
- * the version which bind to a key, for future reference.
- */
-#define HAVE_REVERSE_CTX_NOKEY
-
-
-static
-int sec_install_rctx_kr(struct ptlrpc_sec *sec,
- struct ptlrpc_svc_ctx *svc_ctx)
-{
- struct ptlrpc_cli_ctx *cli_ctx;
- struct vfs_cred vcred = { 0, 0 };
- int rc;
-
- LASSERT(sec);
- LASSERT(svc_ctx);
-
- cli_ctx = ctx_create_kr(sec, &vcred);
- if (cli_ctx == NULL)
- return -ENOMEM;
-
- rc = gss_copy_rvc_cli_ctx(cli_ctx, svc_ctx);
- if (rc) {
- CERROR("failed copy reverse cli ctx: %d\n", rc);
-
- ctx_put_kr(cli_ctx, 1);
- return rc;
- }
-
- rvs_sec_install_root_ctx_kr(sec, cli_ctx, NULL);
-
- ctx_put_kr(cli_ctx, 1);
-
- return 0;
-}
-
-
-/****************************************
- * service apis *
- ****************************************/
-
-static
-int gss_svc_accept_kr(struct ptlrpc_request *req)
-{
- return gss_svc_accept(&gss_policy_keyring, req);
-}
-
-static
-int gss_svc_install_rctx_kr(struct obd_import *imp,
- struct ptlrpc_svc_ctx *svc_ctx)
-{
- struct ptlrpc_sec *sec;
- int rc;
-
- sec = sptlrpc_import_sec_ref(imp);
- LASSERT(sec);
-
- rc = sec_install_rctx_kr(sec, svc_ctx);
- sptlrpc_sec_put(sec);
-
- return rc;
-}
-
-/****************************************
- * key apis *
- ****************************************/
-
-static
-int gss_kt_instantiate(struct key *key, const void *data, size_t datalen)
-{
- int rc;
-
- if (data != NULL || datalen != 0) {
- CERROR("invalid: data %p, len %lu\n", data, (long)datalen);
- return -EINVAL;
- }
-
- if (key->payload.data != 0) {
- CERROR("key already have payload\n");
- return -EINVAL;
- }
-
- /* link the key to session keyring, so following context negotiation
- * rpc fired from user space could find this key. This will be unlinked
- * automatically when upcall processes die.
- *
- * we can't do this through keyctl from userspace, because the upcall
- * might be neither possessor nor owner of the key (setuid).
- *
- * the session keyring is created upon upcall, and don't change all
- * the way until upcall finished, so rcu lock is not needed here.
- */
- LASSERT(key_tgcred(current)->session_keyring);
-
- lockdep_off();
- rc = key_link(key_tgcred(current)->session_keyring, key);
- lockdep_on();
- if (unlikely(rc)) {
- CERROR("failed to link key %08x to keyring %08x: %d\n",
- key->serial,
- key_tgcred(current)->session_keyring->serial, rc);
- return rc;
- }
-
- CDEBUG(D_SEC, "key %p instantiated, ctx %p\n", key, key->payload.data);
- return 0;
-}
-
-/*
- * called with key semaphore write locked. it means we can operate
- * on the context without fear of losing refcount.
- */
-static
-int gss_kt_update(struct key *key, const void *data, size_t datalen)
-{
- struct ptlrpc_cli_ctx *ctx = key->payload.data;
- struct gss_cli_ctx *gctx;
- rawobj_t tmpobj = RAWOBJ_EMPTY;
- __u32 datalen32 = (__u32) datalen;
- int rc;
-
- if (data == NULL || datalen == 0) {
- CWARN("invalid: data %p, len %lu\n", data, (long)datalen);
- return -EINVAL;
- }
-
- /* if upcall finished negotiation too fast (mostly likely because
- * of local error happened) and call kt_update(), the ctx
- * might be still NULL. but the key will finally be associate
- * with a context, or be revoked. if key status is fine, return
- * -EAGAIN to allow userspace sleep a while and call again. */
- if (ctx == NULL) {
- CDEBUG(D_SEC, "update too soon: key %p(%x) flags %lx\n",
- key, key->serial, key->flags);
-
- rc = key_validate(key);
- if (rc == 0)
- return -EAGAIN;
- else
- return rc;
- }
-
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(ctx->cc_sec);
-
- ctx_clear_timer_kr(ctx);
-
- /* don't proceed if already refreshed */
- if (cli_ctx_is_refreshed(ctx)) {
- CWARN("ctx already done refresh\n");
- return 0;
- }
-
- sptlrpc_cli_ctx_get(ctx);
- gctx = ctx2gctx(ctx);
-
- rc = buffer_extract_bytes(&data, &datalen32, &gctx->gc_win,
- sizeof(gctx->gc_win));
- if (rc) {
- CERROR("failed extract seq_win\n");
- goto out;
- }
-
- if (gctx->gc_win == 0) {
- __u32 nego_rpc_err, nego_gss_err;
-
- rc = buffer_extract_bytes(&data, &datalen32, &nego_rpc_err,
- sizeof(nego_rpc_err));
- if (rc) {
- CERROR("failed to extrace rpc rc\n");
- goto out;
- }
-
- rc = buffer_extract_bytes(&data, &datalen32, &nego_gss_err,
- sizeof(nego_gss_err));
- if (rc) {
- CERROR("failed to extrace gss rc\n");
- goto out;
- }
-
- CERROR("negotiation: rpc err %d, gss err %x\n",
- nego_rpc_err, nego_gss_err);
-
- rc = nego_rpc_err ? nego_rpc_err : -EACCES;
- } else {
- rc = rawobj_extract_local_alloc(&gctx->gc_handle,
- (__u32 **) &data, &datalen32);
- if (rc) {
- CERROR("failed extract handle\n");
- goto out;
- }
-
- rc = rawobj_extract_local(&tmpobj, (__u32 **) &data,&datalen32);
- if (rc) {
- CERROR("failed extract mech\n");
- goto out;
- }
-
- rc = lgss_import_sec_context(&tmpobj,
- sec2gsec(ctx->cc_sec)->gs_mech,
- &gctx->gc_mechctx);
- if (rc != GSS_S_COMPLETE)
- CERROR("failed import context\n");
- else
- rc = 0;
- }
-out:
- /* we don't care what current status of this ctx, even someone else
- * is operating on the ctx at the same time. we just add up our own
- * opinions here. */
- if (rc == 0) {
- gss_cli_ctx_uptodate(gctx);
- } else {
- /* this will also revoke the key. has to be done before
- * wakeup waiters otherwise they can find the stale key */
- kill_key_locked(key);
-
- cli_ctx_expire(ctx);
-
- if (rc != -ERESTART)
- set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
- }
-
- /* let user space think it's a success */
- sptlrpc_cli_ctx_put(ctx, 1);
- return 0;
-}
-
-static
-int gss_kt_match(const struct key *key, const void *desc)
-{
- return (strcmp(key->description, (const char *) desc) == 0);
-}
-
-static
-void gss_kt_destroy(struct key *key)
-{
- LASSERT(key->payload.data == NULL);
- CDEBUG(D_SEC, "destroy key %p\n", key);
-}
-
-static
-void gss_kt_describe(const struct key *key, struct seq_file *s)
-{
- if (key->description == NULL)
- seq_puts(s, "[null]");
- else
- seq_puts(s, key->description);
-}
-
-static struct key_type gss_key_type =
-{
- .name = "lgssc",
- .def_datalen = 0,
- .instantiate = gss_kt_instantiate,
- .update = gss_kt_update,
- .match = gss_kt_match,
- .destroy = gss_kt_destroy,
- .describe = gss_kt_describe,
-};
-
-/****************************************
- * lustre gss keyring policy *
- ****************************************/
-
-static struct ptlrpc_ctx_ops gss_keyring_ctxops = {
- .match = gss_cli_ctx_match,
- .refresh = gss_cli_ctx_refresh_kr,
- .validate = gss_cli_ctx_validate_kr,
- .die = gss_cli_ctx_die_kr,
- .sign = gss_cli_ctx_sign,
- .verify = gss_cli_ctx_verify,
- .seal = gss_cli_ctx_seal,
- .unseal = gss_cli_ctx_unseal,
- .wrap_bulk = gss_cli_ctx_wrap_bulk,
- .unwrap_bulk = gss_cli_ctx_unwrap_bulk,
-};
-
-static struct ptlrpc_sec_cops gss_sec_keyring_cops = {
- .create_sec = gss_sec_create_kr,
- .destroy_sec = gss_sec_destroy_kr,
- .kill_sec = gss_sec_kill,
- .lookup_ctx = gss_sec_lookup_ctx_kr,
- .release_ctx = gss_sec_release_ctx_kr,
- .flush_ctx_cache = gss_sec_flush_ctx_cache_kr,
- .gc_ctx = gss_sec_gc_ctx_kr,
- .install_rctx = gss_sec_install_rctx,
- .alloc_reqbuf = gss_alloc_reqbuf,
- .free_reqbuf = gss_free_reqbuf,
- .alloc_repbuf = gss_alloc_repbuf,
- .free_repbuf = gss_free_repbuf,
- .enlarge_reqbuf = gss_enlarge_reqbuf,
- .display = gss_sec_display_kr,
-};
-
-static struct ptlrpc_sec_sops gss_sec_keyring_sops = {
- .accept = gss_svc_accept_kr,
- .invalidate_ctx = gss_svc_invalidate_ctx,
- .alloc_rs = gss_svc_alloc_rs,
- .authorize = gss_svc_authorize,
- .free_rs = gss_svc_free_rs,
- .free_ctx = gss_svc_free_ctx,
- .prep_bulk = gss_svc_prep_bulk,
- .unwrap_bulk = gss_svc_unwrap_bulk,
- .wrap_bulk = gss_svc_wrap_bulk,
- .install_rctx = gss_svc_install_rctx_kr,
-};
-
-static struct ptlrpc_sec_policy gss_policy_keyring = {
- .sp_owner = THIS_MODULE,
- .sp_name = "gss.keyring",
- .sp_policy = SPTLRPC_POLICY_GSS,
- .sp_cops = &gss_sec_keyring_cops,
- .sp_sops = &gss_sec_keyring_sops,
-};
-
-
-int __init gss_init_keyring(void)
-{
- int rc;
-
- rc = register_key_type(&gss_key_type);
- if (rc) {
- CERROR("failed to register keyring type: %d\n", rc);
- return rc;
- }
-
- rc = sptlrpc_register_policy(&gss_policy_keyring);
- if (rc) {
- unregister_key_type(&gss_key_type);
- return rc;
- }
-
- return 0;
-}
-
-void __exit gss_exit_keyring(void)
-{
- unregister_key_type(&gss_key_type);
- sptlrpc_unregister_policy(&gss_policy_keyring);
-}
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * linux/include/linux/sunrpc/gss_krb5_types.h
- *
- * Adapted from MIT Kerberos 5-1.2.1 lib/include/krb5.h,
- * lib/gssapi/krb5/gssapiP_krb5.h, and others
- *
- * Copyright (c) 2000 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Andy Adamson <andros@umich.edu>
- * Bruce Fields <bfields@umich.edu>
- */
-
-/*
- * Copyright 1995 by the Massachusetts Institute of Technology.
- * All Rights Reserved.
- *
- * Export of this software from the United States of America may
- * require a specific license from the United States Government.
- * It is the responsibility of any person or organization contemplating
- * export to obtain such a license before exporting.
- *
- * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
- * distribute this software and its documentation for any purpose and
- * without fee is hereby granted, provided that the above copyright
- * notice appear in all copies and that both that copyright notice and
- * this permission notice appear in supporting documentation, and that
- * the name of M.I.T. not be used in advertising or publicity pertaining
- * to distribution of the software without specific, written prior
- * permission. Furthermore if you modify this software you must label
- * your software as modified software and not distribute it in such a
- * fashion that it might be confused with the original M.I.T. software.
- * M.I.T. makes no representations about the suitability of
- * this software for any purpose. It is provided "as is" without express
- * or implied warranty.
- *
- */
-
-#ifndef PTLRPC_GSS_KRB5_H
-#define PTLRPC_GSS_KRB5_H
-
-/*
- * RFC 4142
- */
-
-#define KG_USAGE_ACCEPTOR_SEAL 22
-#define KG_USAGE_ACCEPTOR_SIGN 23
-#define KG_USAGE_INITIATOR_SEAL 24
-#define KG_USAGE_INITIATOR_SIGN 25
-
-#define KG_TOK_MIC_MSG 0x0404
-#define KG_TOK_WRAP_MSG 0x0504
-
-#define FLAG_SENDER_IS_ACCEPTOR 0x01
-#define FLAG_WRAP_CONFIDENTIAL 0x02
-#define FLAG_ACCEPTOR_SUBKEY 0x04
-
-struct krb5_header {
- __u16 kh_tok_id; /* token id */
- __u8 kh_flags; /* acceptor flags */
- __u8 kh_filler; /* 0xff */
- __u16 kh_ec; /* extra count */
- __u16 kh_rrc; /* right rotation count */
- __u64 kh_seq; /* sequence number */
- __u8 kh_cksum[0]; /* checksum */
-};
-
-struct krb5_keyblock {
- rawobj_t kb_key;
- struct ll_crypto_cipher *kb_tfm;
-};
-
-struct krb5_ctx {
- unsigned int kc_initiate:1,
- kc_cfx:1,
- kc_seed_init:1,
- kc_have_acceptor_subkey:1;
- __s32 kc_endtime;
- __u8 kc_seed[16];
- __u64 kc_seq_send;
- __u64 kc_seq_recv;
- __u32 kc_enctype;
- struct krb5_keyblock kc_keye; /* encryption */
- struct krb5_keyblock kc_keyi; /* integrity */
- struct krb5_keyblock kc_keyc; /* checksum */
- rawobj_t kc_mech_used;
-};
-
-enum sgn_alg {
- SGN_ALG_DES_MAC_MD5 = 0x0000,
- SGN_ALG_MD2_5 = 0x0001,
- SGN_ALG_DES_MAC = 0x0002,
- SGN_ALG_3 = 0x0003, /* not published */
- SGN_ALG_HMAC_MD5 = 0x0011, /* microsoft w2k; no support */
- SGN_ALG_HMAC_SHA1_DES3_KD = 0x0004
-};
-
-enum seal_alg {
- SEAL_ALG_NONE = 0xffff,
- SEAL_ALG_DES = 0x0000,
- SEAL_ALG_1 = 0x0001, /* not published */
- SEAL_ALG_MICROSOFT_RC4 = 0x0010, /* microsoft w2k; no support */
- SEAL_ALG_DES3KD = 0x0002
-};
-
-#define CKSUMTYPE_CRC32 0x0001
-#define CKSUMTYPE_RSA_MD4 0x0002
-#define CKSUMTYPE_RSA_MD4_DES 0x0003
-#define CKSUMTYPE_DESCBC 0x0004
-/* des-mac-k */
-/* rsa-md4-des-k */
-#define CKSUMTYPE_RSA_MD5 0x0007
-#define CKSUMTYPE_RSA_MD5_DES 0x0008
-#define CKSUMTYPE_NIST_SHA 0x0009
-#define CKSUMTYPE_HMAC_SHA1_DES3 0x000c
-#define CKSUMTYPE_HMAC_SHA1_96_AES128 0x000f
-#define CKSUMTYPE_HMAC_SHA1_96_AES256 0x0010
-#define CKSUMTYPE_HMAC_MD5_ARCFOUR -138
-
-/* from gssapi_err_krb5.h */
-#define KG_CCACHE_NOMATCH (39756032L)
-#define KG_KEYTAB_NOMATCH (39756033L)
-#define KG_TGT_MISSING (39756034L)
-#define KG_NO_SUBKEY (39756035L)
-#define KG_CONTEXT_ESTABLISHED (39756036L)
-#define KG_BAD_SIGN_TYPE (39756037L)
-#define KG_BAD_LENGTH (39756038L)
-#define KG_CTX_INCOMPLETE (39756039L)
-#define KG_CONTEXT (39756040L)
-#define KG_CRED (39756041L)
-#define KG_ENC_DESC (39756042L)
-#define KG_BAD_SEQ (39756043L)
-#define KG_EMPTY_CCACHE (39756044L)
-#define KG_NO_CTYPES (39756045L)
-
-/* per Kerberos v5 protocol spec crypto types from the wire.
- * these get mapped to linux kernel crypto routines.
- */
-#define ENCTYPE_NULL 0x0000
-#define ENCTYPE_DES_CBC_CRC 0x0001 /* DES cbc mode with CRC-32 */
-#define ENCTYPE_DES_CBC_MD4 0x0002 /* DES cbc mode with RSA-MD4 */
-#define ENCTYPE_DES_CBC_MD5 0x0003 /* DES cbc mode with RSA-MD5 */
-#define ENCTYPE_DES_CBC_RAW 0x0004 /* DES cbc mode raw */
-/* XXX deprecated? */
-#define ENCTYPE_DES3_CBC_SHA 0x0005 /* DES-3 cbc mode with NIST-SHA */
-#define ENCTYPE_DES3_CBC_RAW 0x0006 /* DES-3 cbc mode raw */
-#define ENCTYPE_DES_HMAC_SHA1 0x0008
-#define ENCTYPE_DES3_CBC_SHA1 0x0010
-#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 0x0011
-#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 0x0012
-#define ENCTYPE_ARCFOUR_HMAC 0x0017
-#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018
-#define ENCTYPE_UNKNOWN 0x01ff
-
-#endif /* PTLRPC_GSS_KRB5_H */
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2011, 2012, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * linux/net/sunrpc/gss_krb5_mech.c
- * linux/net/sunrpc/gss_krb5_crypto.c
- * linux/net/sunrpc/gss_krb5_seal.c
- * linux/net/sunrpc/gss_krb5_seqnum.c
- * linux/net/sunrpc/gss_krb5_unseal.c
- *
- * Copyright (c) 2001 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Andy Adamson <andros@umich.edu>
- * J. Bruce Fields <bfields@umich.edu>
- *
- * 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
- * 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
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/crypto.h>
-#include <linux/mutex.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-#include "gss_asn1.h"
-#include "gss_krb5.h"
-
-static spinlock_t krb5_seq_lock;
-
-struct krb5_enctype {
- char *ke_dispname;
- char *ke_enc_name; /* linux tfm name */
- char *ke_hash_name; /* linux tfm name */
- int ke_enc_mode; /* linux tfm mode */
- int ke_hash_size; /* checksum size */
- int ke_conf_size; /* confounder size */
- unsigned int ke_hash_hmac:1; /* is hmac? */
-};
-
-/*
- * NOTE: for aes128-cts and aes256-cts, MIT implementation use CTS encryption.
- * but currently we simply CBC with padding, because linux doesn't support CTS
- * yet. this need to be fixed in the future.
- */
-static struct krb5_enctype enctypes[] = {
- [ENCTYPE_DES_CBC_RAW] = { /* des-cbc-md5 */
- "des-cbc-md5",
- "cbc(des)",
- "md5",
- 0,
- 16,
- 8,
- 0,
- },
- [ENCTYPE_DES3_CBC_RAW] = { /* des3-hmac-sha1 */
- "des3-hmac-sha1",
- "cbc(des3_ede)",
- "hmac(sha1)",
- 0,
- 20,
- 8,
- 1,
- },
- [ENCTYPE_AES128_CTS_HMAC_SHA1_96] = { /* aes128-cts */
- "aes128-cts-hmac-sha1-96",
- "cbc(aes)",
- "hmac(sha1)",
- 0,
- 12,
- 16,
- 1,
- },
- [ENCTYPE_AES256_CTS_HMAC_SHA1_96] = { /* aes256-cts */
- "aes256-cts-hmac-sha1-96",
- "cbc(aes)",
- "hmac(sha1)",
- 0,
- 12,
- 16,
- 1,
- },
- [ENCTYPE_ARCFOUR_HMAC] = { /* arcfour-hmac-md5 */
- "arcfour-hmac-md5",
- "ecb(arc4)",
- "hmac(md5)",
- 0,
- 16,
- 8,
- 1,
- },
-};
-
-#define MAX_ENCTYPES sizeof(enctypes)/sizeof(struct krb5_enctype)
-
-static const char * enctype2str(__u32 enctype)
-{
- if (enctype < MAX_ENCTYPES && enctypes[enctype].ke_dispname)
- return enctypes[enctype].ke_dispname;
-
- return "unknown";
-}
-
-static
-int keyblock_init(struct krb5_keyblock *kb, char *alg_name, int alg_mode)
-{
- kb->kb_tfm = crypto_alloc_blkcipher(alg_name, alg_mode, 0);
- if (IS_ERR(kb->kb_tfm)) {
- CERROR("failed to alloc tfm: %s, mode %d\n",
- alg_name, alg_mode);
- return -1;
- }
-
- if (crypto_blkcipher_setkey(kb->kb_tfm, kb->kb_key.data, kb->kb_key.len)) {
- CERROR("failed to set %s key, len %d\n",
- alg_name, kb->kb_key.len);
- return -1;
- }
-
- return 0;
-}
-
-static
-int krb5_init_keys(struct krb5_ctx *kctx)
-{
- struct krb5_enctype *ke;
-
- if (kctx->kc_enctype >= MAX_ENCTYPES ||
- enctypes[kctx->kc_enctype].ke_hash_size == 0) {
- CERROR("unsupported enctype %x\n", kctx->kc_enctype);
- return -1;
- }
-
- ke = &enctypes[kctx->kc_enctype];
-
- /* tfm arc4 is stateful, user should alloc-use-free by his own */
- if (kctx->kc_enctype != ENCTYPE_ARCFOUR_HMAC &&
- keyblock_init(&kctx->kc_keye, ke->ke_enc_name, ke->ke_enc_mode))
- return -1;
-
- /* tfm hmac is stateful, user should alloc-use-free by his own */
- if (ke->ke_hash_hmac == 0 &&
- keyblock_init(&kctx->kc_keyi, ke->ke_enc_name, ke->ke_enc_mode))
- return -1;
- if (ke->ke_hash_hmac == 0 &&
- keyblock_init(&kctx->kc_keyc, ke->ke_enc_name, ke->ke_enc_mode))
- return -1;
-
- return 0;
-}
-
-static
-void keyblock_free(struct krb5_keyblock *kb)
-{
- rawobj_free(&kb->kb_key);
- if (kb->kb_tfm)
- crypto_free_blkcipher(kb->kb_tfm);
-}
-
-static
-int keyblock_dup(struct krb5_keyblock *new, struct krb5_keyblock *kb)
-{
- return rawobj_dup(&new->kb_key, &kb->kb_key);
-}
-
-static
-int get_bytes(char **ptr, const char *end, void *res, int len)
-{
- char *p, *q;
- p = *ptr;
- q = p + len;
- if (q > end || q < p)
- return -1;
- memcpy(res, p, len);
- *ptr = q;
- return 0;
-}
-
-static
-int get_rawobj(char **ptr, const char *end, rawobj_t *res)
-{
- char *p, *q;
- __u32 len;
-
- p = *ptr;
- if (get_bytes(&p, end, &len, sizeof(len)))
- return -1;
-
- q = p + len;
- if (q > end || q < p)
- return -1;
-
- OBD_ALLOC_LARGE(res->data, len);
- if (!res->data)
- return -1;
-
- res->len = len;
- memcpy(res->data, p, len);
- *ptr = q;
- return 0;
-}
-
-static
-int get_keyblock(char **ptr, const char *end,
- struct krb5_keyblock *kb, __u32 keysize)
-{
- char *buf;
-
- OBD_ALLOC_LARGE(buf, keysize);
- if (buf == NULL)
- return -1;
-
- if (get_bytes(ptr, end, buf, keysize)) {
- OBD_FREE_LARGE(buf, keysize);
- return -1;
- }
-
- kb->kb_key.len = keysize;
- kb->kb_key.data = buf;
- return 0;
-}
-
-static
-void delete_context_kerberos(struct krb5_ctx *kctx)
-{
- rawobj_free(&kctx->kc_mech_used);
-
- keyblock_free(&kctx->kc_keye);
- keyblock_free(&kctx->kc_keyi);
- keyblock_free(&kctx->kc_keyc);
-}
-
-static
-__u32 import_context_rfc1964(struct krb5_ctx *kctx, char *p, char *end)
-{
- unsigned int tmp_uint, keysize;
-
- /* seed_init flag */
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
- goto out_err;
- kctx->kc_seed_init = (tmp_uint != 0);
-
- /* seed */
- if (get_bytes(&p, end, kctx->kc_seed, sizeof(kctx->kc_seed)))
- goto out_err;
-
- /* sign/seal algorithm, not really used now */
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
- get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
- goto out_err;
-
- /* end time */
- if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime)))
- goto out_err;
-
- /* seq send */
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
- goto out_err;
- kctx->kc_seq_send = tmp_uint;
-
- /* mech oid */
- if (get_rawobj(&p, end, &kctx->kc_mech_used))
- goto out_err;
-
- /* old style enc/seq keys in format:
- * - enctype (u32)
- * - keysize (u32)
- * - keydata
- * we decompose them to fit into the new context
- */
-
- /* enc key */
- if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype)))
- goto out_err;
-
- if (get_bytes(&p, end, &keysize, sizeof(keysize)))
- goto out_err;
-
- if (get_keyblock(&p, end, &kctx->kc_keye, keysize))
- goto out_err;
-
- /* seq key */
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
- tmp_uint != kctx->kc_enctype)
- goto out_err;
-
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)) ||
- tmp_uint != keysize)
- goto out_err;
-
- if (get_keyblock(&p, end, &kctx->kc_keyc, keysize))
- goto out_err;
-
- /* old style fallback */
- if (keyblock_dup(&kctx->kc_keyi, &kctx->kc_keyc))
- goto out_err;
-
- if (p != end)
- goto out_err;
-
- CDEBUG(D_SEC, "successfully imported rfc1964 context\n");
- return 0;
-out_err:
- return GSS_S_FAILURE;
-}
-
-/* Flags for version 2 context flags */
-#define KRB5_CTX_FLAG_INITIATOR 0x00000001
-#define KRB5_CTX_FLAG_CFX 0x00000002
-#define KRB5_CTX_FLAG_ACCEPTOR_SUBKEY 0x00000004
-
-static
-__u32 import_context_rfc4121(struct krb5_ctx *kctx, char *p, char *end)
-{
- unsigned int tmp_uint, keysize;
-
- /* end time */
- if (get_bytes(&p, end, &kctx->kc_endtime, sizeof(kctx->kc_endtime)))
- goto out_err;
-
- /* flags */
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
- goto out_err;
-
- if (tmp_uint & KRB5_CTX_FLAG_INITIATOR)
- kctx->kc_initiate = 1;
- if (tmp_uint & KRB5_CTX_FLAG_CFX)
- kctx->kc_cfx = 1;
- if (tmp_uint & KRB5_CTX_FLAG_ACCEPTOR_SUBKEY)
- kctx->kc_have_acceptor_subkey = 1;
-
- /* seq send */
- if (get_bytes(&p, end, &kctx->kc_seq_send, sizeof(kctx->kc_seq_send)))
- goto out_err;
-
- /* enctype */
- if (get_bytes(&p, end, &kctx->kc_enctype, sizeof(kctx->kc_enctype)))
- goto out_err;
-
- /* size of each key */
- if (get_bytes(&p, end, &keysize, sizeof(keysize)))
- goto out_err;
-
- /* number of keys - should always be 3 */
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint)))
- goto out_err;
-
- if (tmp_uint != 3) {
- CERROR("Invalid number of keys: %u\n", tmp_uint);
- goto out_err;
- }
-
- /* ke */
- if (get_keyblock(&p, end, &kctx->kc_keye, keysize))
- goto out_err;
- /* ki */
- if (get_keyblock(&p, end, &kctx->kc_keyi, keysize))
- goto out_err;
- /* ki */
- if (get_keyblock(&p, end, &kctx->kc_keyc, keysize))
- goto out_err;
-
- CDEBUG(D_SEC, "successfully imported v2 context\n");
- return 0;
-out_err:
- return GSS_S_FAILURE;
-}
-
-/*
- * The whole purpose here is trying to keep user level gss context parsing
- * from nfs-utils unchanged as possible as we can, they are not quite mature
- * yet, and many stuff still not clear, like heimdal etc.
- */
-static
-__u32 gss_import_sec_context_kerberos(rawobj_t *inbuf,
- struct gss_ctx *gctx)
-{
- struct krb5_ctx *kctx;
- char *p = (char *) inbuf->data;
- char *end = (char *) (inbuf->data + inbuf->len);
- unsigned int tmp_uint, rc;
-
- if (get_bytes(&p, end, &tmp_uint, sizeof(tmp_uint))) {
- CERROR("Fail to read version\n");
- return GSS_S_FAILURE;
- }
-
- /* only support 0, 1 for the moment */
- if (tmp_uint > 2) {
- CERROR("Invalid version %u\n", tmp_uint);
- return GSS_S_FAILURE;
- }
-
- OBD_ALLOC_PTR(kctx);
- if (!kctx)
- return GSS_S_FAILURE;
-
- if (tmp_uint == 0 || tmp_uint == 1) {
- kctx->kc_initiate = tmp_uint;
- rc = import_context_rfc1964(kctx, p, end);
- } else {
- rc = import_context_rfc4121(kctx, p, end);
- }
-
- if (rc == 0)
- rc = krb5_init_keys(kctx);
-
- if (rc) {
- delete_context_kerberos(kctx);
- OBD_FREE_PTR(kctx);
-
- return GSS_S_FAILURE;
- }
-
- gctx->internal_ctx_id = kctx;
- return GSS_S_COMPLETE;
-}
-
-static
-__u32 gss_copy_reverse_context_kerberos(struct gss_ctx *gctx,
- struct gss_ctx *gctx_new)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_ctx *knew;
-
- OBD_ALLOC_PTR(knew);
- if (!knew)
- return GSS_S_FAILURE;
-
- knew->kc_initiate = kctx->kc_initiate ? 0 : 1;
- knew->kc_cfx = kctx->kc_cfx;
- knew->kc_seed_init = kctx->kc_seed_init;
- knew->kc_have_acceptor_subkey = kctx->kc_have_acceptor_subkey;
- knew->kc_endtime = kctx->kc_endtime;
-
- memcpy(knew->kc_seed, kctx->kc_seed, sizeof(kctx->kc_seed));
- knew->kc_seq_send = kctx->kc_seq_recv;
- knew->kc_seq_recv = kctx->kc_seq_send;
- knew->kc_enctype = kctx->kc_enctype;
-
- if (rawobj_dup(&knew->kc_mech_used, &kctx->kc_mech_used))
- goto out_err;
-
- if (keyblock_dup(&knew->kc_keye, &kctx->kc_keye))
- goto out_err;
- if (keyblock_dup(&knew->kc_keyi, &kctx->kc_keyi))
- goto out_err;
- if (keyblock_dup(&knew->kc_keyc, &kctx->kc_keyc))
- goto out_err;
- if (krb5_init_keys(knew))
- goto out_err;
-
- gctx_new->internal_ctx_id = knew;
- CDEBUG(D_SEC, "successfully copied reverse context\n");
- return GSS_S_COMPLETE;
-
-out_err:
- delete_context_kerberos(knew);
- OBD_FREE_PTR(knew);
- return GSS_S_FAILURE;
-}
-
-static
-__u32 gss_inquire_context_kerberos(struct gss_ctx *gctx,
- unsigned long *endtime)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
-
- *endtime = (unsigned long) ((__u32) kctx->kc_endtime);
- return GSS_S_COMPLETE;
-}
-
-static
-void gss_delete_sec_context_kerberos(void *internal_ctx)
-{
- struct krb5_ctx *kctx = internal_ctx;
-
- delete_context_kerberos(kctx);
- OBD_FREE_PTR(kctx);
-}
-
-static
-void buf_to_sg(struct scatterlist *sg, void *ptr, int len)
-{
- sg_set_buf(sg, ptr, len);
-}
-
-static
-__u32 krb5_encrypt(struct crypto_blkcipher *tfm,
- int decrypt,
- void * iv,
- void * in,
- void * out,
- int length)
-{
- struct blkcipher_desc desc;
- struct scatterlist sg;
- __u8 local_iv[16] = {0};
- __u32 ret = -EINVAL;
-
- LASSERT(tfm);
- desc.tfm = tfm;
- desc.info = local_iv;
- desc.flags= 0;
-
- if (length % crypto_blkcipher_blocksize(tfm) != 0) {
- CERROR("output length %d mismatch blocksize %d\n",
- length, crypto_blkcipher_blocksize(tfm));
- goto out;
- }
-
- if (crypto_blkcipher_ivsize(tfm) > 16) {
- CERROR("iv size too large %d\n", crypto_blkcipher_ivsize(tfm));
- goto out;
- }
-
- if (iv)
- memcpy(local_iv, iv, crypto_blkcipher_ivsize(tfm));
-
- memcpy(out, in, length);
- buf_to_sg(&sg, out, length);
-
- if (decrypt)
- ret = crypto_blkcipher_decrypt_iv(&desc, &sg, &sg, length);
- else
- ret = crypto_blkcipher_encrypt_iv(&desc, &sg, &sg, length);
-
-out:
- return(ret);
-}
-
-
-static inline
-int krb5_digest_hmac(struct crypto_hash *tfm,
- rawobj_t *key,
- struct krb5_header *khdr,
- int msgcnt, rawobj_t *msgs,
- int iovcnt, lnet_kiov_t *iovs,
- rawobj_t *cksum)
-{
- struct hash_desc desc;
- struct scatterlist sg[1];
- int i;
-
- crypto_hash_setkey(tfm, key->data, key->len);
- desc.tfm = tfm;
- desc.flags= 0;
-
- crypto_hash_init(&desc);
-
- for (i = 0; i < msgcnt; i++) {
- if (msgs[i].len == 0)
- continue;
- buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len);
- crypto_hash_update(&desc, sg, msgs[i].len);
- }
-
- for (i = 0; i < iovcnt; i++) {
- if (iovs[i].kiov_len == 0)
- continue;
-
- sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
- iovs[i].kiov_offset);
- crypto_hash_update(&desc, sg, iovs[i].kiov_len);
- }
-
- if (khdr) {
- buf_to_sg(sg, (char *) khdr, sizeof(*khdr));
- crypto_hash_update(&desc, sg, sizeof(*khdr));
- }
-
- return crypto_hash_final(&desc, cksum->data);
-}
-
-
-static inline
-int krb5_digest_norm(struct crypto_hash *tfm,
- struct krb5_keyblock *kb,
- struct krb5_header *khdr,
- int msgcnt, rawobj_t *msgs,
- int iovcnt, lnet_kiov_t *iovs,
- rawobj_t *cksum)
-{
- struct hash_desc desc;
- struct scatterlist sg[1];
- int i;
-
- LASSERT(kb->kb_tfm);
- desc.tfm = tfm;
- desc.flags= 0;
-
- crypto_hash_init(&desc);
-
- for (i = 0; i < msgcnt; i++) {
- if (msgs[i].len == 0)
- continue;
- buf_to_sg(sg, (char *) msgs[i].data, msgs[i].len);
- crypto_hash_update(&desc, sg, msgs[i].len);
- }
-
- for (i = 0; i < iovcnt; i++) {
- if (iovs[i].kiov_len == 0)
- continue;
-
- sg_set_page(&sg[0], iovs[i].kiov_page, iovs[i].kiov_len,
- iovs[i].kiov_offset);
- crypto_hash_update(&desc, sg, iovs[i].kiov_len);
- }
-
- if (khdr) {
- buf_to_sg(sg, (char *) khdr, sizeof(*khdr));
- crypto_hash_update(&desc, sg, sizeof(*khdr));
- }
-
- crypto_hash_final(&desc, cksum->data);
-
- return krb5_encrypt(kb->kb_tfm, 0, NULL, cksum->data,
- cksum->data, cksum->len);
-}
-
-/*
- * compute (keyed/keyless) checksum against the plain text which appended
- * with krb5 wire token header.
- */
-static
-__s32 krb5_make_checksum(__u32 enctype,
- struct krb5_keyblock *kb,
- struct krb5_header *khdr,
- int msgcnt, rawobj_t *msgs,
- int iovcnt, lnet_kiov_t *iovs,
- rawobj_t *cksum)
-{
- struct krb5_enctype *ke = &enctypes[enctype];
- struct crypto_hash *tfm;
- __u32 code = GSS_S_FAILURE;
- int rc;
-
- tfm = ll_crypto_alloc_hash(ke->ke_hash_name, 0, 0);
- if (!tfm) {
- CERROR("failed to alloc TFM: %s\n", ke->ke_hash_name);
- return GSS_S_FAILURE;
- }
-
- cksum->len = crypto_hash_digestsize(tfm);
- OBD_ALLOC_LARGE(cksum->data, cksum->len);
- if (!cksum->data) {
- cksum->len = 0;
- goto out_tfm;
- }
-
- if (ke->ke_hash_hmac)
- rc = krb5_digest_hmac(tfm, &kb->kb_key,
- khdr, msgcnt, msgs, iovcnt, iovs, cksum);
- else
- rc = krb5_digest_norm(tfm, kb,
- khdr, msgcnt, msgs, iovcnt, iovs, cksum);
-
- if (rc == 0)
- code = GSS_S_COMPLETE;
-out_tfm:
- crypto_free_hash(tfm);
- return code;
-}
-
-static void fill_krb5_header(struct krb5_ctx *kctx,
- struct krb5_header *khdr,
- int privacy)
-{
- unsigned char acceptor_flag;
-
- acceptor_flag = kctx->kc_initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
-
- if (privacy) {
- khdr->kh_tok_id = cpu_to_be16(KG_TOK_WRAP_MSG);
- khdr->kh_flags = acceptor_flag | FLAG_WRAP_CONFIDENTIAL;
- khdr->kh_ec = cpu_to_be16(0);
- khdr->kh_rrc = cpu_to_be16(0);
- } else {
- khdr->kh_tok_id = cpu_to_be16(KG_TOK_MIC_MSG);
- khdr->kh_flags = acceptor_flag;
- khdr->kh_ec = cpu_to_be16(0xffff);
- khdr->kh_rrc = cpu_to_be16(0xffff);
- }
-
- khdr->kh_filler = 0xff;
- spin_lock(&krb5_seq_lock);
- khdr->kh_seq = cpu_to_be64(kctx->kc_seq_send++);
- spin_unlock(&krb5_seq_lock);
-}
-
-static __u32 verify_krb5_header(struct krb5_ctx *kctx,
- struct krb5_header *khdr,
- int privacy)
-{
- unsigned char acceptor_flag;
- __u16 tok_id, ec_rrc;
-
- acceptor_flag = kctx->kc_initiate ? FLAG_SENDER_IS_ACCEPTOR : 0;
-
- if (privacy) {
- tok_id = KG_TOK_WRAP_MSG;
- ec_rrc = 0x0;
- } else {
- tok_id = KG_TOK_MIC_MSG;
- ec_rrc = 0xffff;
- }
-
- /* sanity checks */
- if (be16_to_cpu(khdr->kh_tok_id) != tok_id) {
- CERROR("bad token id\n");
- return GSS_S_DEFECTIVE_TOKEN;
- }
- if ((khdr->kh_flags & FLAG_SENDER_IS_ACCEPTOR) != acceptor_flag) {
- CERROR("bad direction flag\n");
- return GSS_S_BAD_SIG;
- }
- if (privacy && (khdr->kh_flags & FLAG_WRAP_CONFIDENTIAL) == 0) {
- CERROR("missing confidential flag\n");
- return GSS_S_BAD_SIG;
- }
- if (khdr->kh_filler != 0xff) {
- CERROR("bad filler\n");
- return GSS_S_DEFECTIVE_TOKEN;
- }
- if (be16_to_cpu(khdr->kh_ec) != ec_rrc ||
- be16_to_cpu(khdr->kh_rrc) != ec_rrc) {
- CERROR("bad EC or RRC\n");
- return GSS_S_DEFECTIVE_TOKEN;
- }
- return GSS_S_COMPLETE;
-}
-
-static
-__u32 gss_get_mic_kerberos(struct gss_ctx *gctx,
- int msgcnt,
- rawobj_t *msgs,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *token)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
- struct krb5_header *khdr;
- rawobj_t cksum = RAWOBJ_EMPTY;
-
- /* fill krb5 header */
- LASSERT(token->len >= sizeof(*khdr));
- khdr = (struct krb5_header *) token->data;
- fill_krb5_header(kctx, khdr, 0);
-
- /* checksum */
- if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc,
- khdr, msgcnt, msgs, iovcnt, iovs, &cksum))
- return GSS_S_FAILURE;
-
- LASSERT(cksum.len >= ke->ke_hash_size);
- LASSERT(token->len >= sizeof(*khdr) + ke->ke_hash_size);
- memcpy(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size,
- ke->ke_hash_size);
-
- token->len = sizeof(*khdr) + ke->ke_hash_size;
- rawobj_free(&cksum);
- return GSS_S_COMPLETE;
-}
-
-static
-__u32 gss_verify_mic_kerberos(struct gss_ctx *gctx,
- int msgcnt,
- rawobj_t *msgs,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *token)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
- struct krb5_header *khdr;
- rawobj_t cksum = RAWOBJ_EMPTY;
- __u32 major;
-
- if (token->len < sizeof(*khdr)) {
- CERROR("short signature: %u\n", token->len);
- return GSS_S_DEFECTIVE_TOKEN;
- }
-
- khdr = (struct krb5_header *) token->data;
-
- major = verify_krb5_header(kctx, khdr, 0);
- if (major != GSS_S_COMPLETE) {
- CERROR("bad krb5 header\n");
- return major;
- }
-
- if (token->len < sizeof(*khdr) + ke->ke_hash_size) {
- CERROR("short signature: %u, require %d\n",
- token->len, (int) sizeof(*khdr) + ke->ke_hash_size);
- return GSS_S_FAILURE;
- }
-
- if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyc,
- khdr, msgcnt, msgs, iovcnt, iovs, &cksum)) {
- CERROR("failed to make checksum\n");
- return GSS_S_FAILURE;
- }
-
- LASSERT(cksum.len >= ke->ke_hash_size);
- if (memcmp(khdr + 1, cksum.data + cksum.len - ke->ke_hash_size,
- ke->ke_hash_size)) {
- CERROR("checksum mismatch\n");
- rawobj_free(&cksum);
- return GSS_S_BAD_SIG;
- }
-
- rawobj_free(&cksum);
- return GSS_S_COMPLETE;
-}
-
-static
-int add_padding(rawobj_t *msg, int msg_buflen, int blocksize)
-{
- int padding;
-
- padding = (blocksize - (msg->len & (blocksize - 1))) &
- (blocksize - 1);
- if (!padding)
- return 0;
-
- if (msg->len + padding > msg_buflen) {
- CERROR("bufsize %u too small: datalen %u, padding %u\n",
- msg_buflen, msg->len, padding);
- return -EINVAL;
- }
-
- memset(msg->data + msg->len, padding, padding);
- msg->len += padding;
- return 0;
-}
-
-static
-int krb5_encrypt_rawobjs(struct crypto_blkcipher *tfm,
- int mode_ecb,
- int inobj_cnt,
- rawobj_t *inobjs,
- rawobj_t *outobj,
- int enc)
-{
- struct blkcipher_desc desc;
- struct scatterlist src, dst;
- __u8 local_iv[16] = {0}, *buf;
- __u32 datalen = 0;
- int i, rc;
-
- buf = outobj->data;
- desc.tfm = tfm;
- desc.info = local_iv;
- desc.flags = 0;
-
- for (i = 0; i < inobj_cnt; i++) {
- LASSERT(buf + inobjs[i].len <= outobj->data + outobj->len);
-
- buf_to_sg(&src, inobjs[i].data, inobjs[i].len);
- buf_to_sg(&dst, buf, outobj->len - datalen);
-
- if (mode_ecb) {
- if (enc)
- rc = crypto_blkcipher_encrypt(
- &desc, &dst, &src, src.length);
- else
- rc = crypto_blkcipher_decrypt(
- &desc, &dst, &src, src.length);
- } else {
- if (enc)
- rc = crypto_blkcipher_encrypt_iv(
- &desc, &dst, &src, src.length);
- else
- rc = crypto_blkcipher_decrypt_iv(
- &desc, &dst, &src, src.length);
- }
-
- if (rc) {
- CERROR("encrypt error %d\n", rc);
- return rc;
- }
-
- datalen += inobjs[i].len;
- buf += inobjs[i].len;
- }
-
- outobj->len = datalen;
- return 0;
-}
-
-/*
- * if adj_nob != 0, we adjust desc->bd_nob to the actual cipher text size.
- */
-static
-int krb5_encrypt_bulk(struct crypto_blkcipher *tfm,
- struct krb5_header *khdr,
- char *confounder,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *cipher,
- int adj_nob)
-{
- struct blkcipher_desc ciph_desc;
- __u8 local_iv[16] = {0};
- struct scatterlist src, dst;
- int blocksize, i, rc, nob = 0;
-
- LASSERT(desc->bd_iov_count);
- LASSERT(desc->bd_enc_iov);
-
- blocksize = crypto_blkcipher_blocksize(tfm);
- LASSERT(blocksize > 1);
- LASSERT(cipher->len == blocksize + sizeof(*khdr));
-
- ciph_desc.tfm = tfm;
- ciph_desc.info = local_iv;
- ciph_desc.flags = 0;
-
- /* encrypt confounder */
- buf_to_sg(&src, confounder, blocksize);
- buf_to_sg(&dst, cipher->data, blocksize);
-
- rc = crypto_blkcipher_encrypt_iv(&ciph_desc, &dst, &src, blocksize);
- if (rc) {
- CERROR("error to encrypt confounder: %d\n", rc);
- return rc;
- }
-
- /* encrypt clear pages */
- for (i = 0; i < desc->bd_iov_count; i++) {
- sg_set_page(&src, desc->bd_iov[i].kiov_page,
- (desc->bd_iov[i].kiov_len + blocksize - 1) &
- (~(blocksize - 1)),
- desc->bd_iov[i].kiov_offset);
- if (adj_nob)
- nob += src.length;
- sg_set_page(&dst, desc->bd_enc_iov[i].kiov_page, src.length,
- src.offset);
-
- desc->bd_enc_iov[i].kiov_offset = dst.offset;
- desc->bd_enc_iov[i].kiov_len = dst.length;
-
- rc = crypto_blkcipher_encrypt_iv(&ciph_desc, &dst, &src,
- src.length);
- if (rc) {
- CERROR("error to encrypt page: %d\n", rc);
- return rc;
- }
- }
-
- /* encrypt krb5 header */
- buf_to_sg(&src, khdr, sizeof(*khdr));
- buf_to_sg(&dst, cipher->data + blocksize, sizeof(*khdr));
-
- rc = crypto_blkcipher_encrypt_iv(&ciph_desc,
- &dst, &src, sizeof(*khdr));
- if (rc) {
- CERROR("error to encrypt krb5 header: %d\n", rc);
- return rc;
- }
-
- if (adj_nob)
- desc->bd_nob = nob;
-
- return 0;
-}
-
-/*
- * desc->bd_nob_transferred is the size of cipher text received.
- * desc->bd_nob is the target size of plain text supposed to be.
- *
- * if adj_nob != 0, we adjust each page's kiov_len to the actual
- * plain text size.
- * - for client read: we don't know data size for each page, so
- * bd_iov[]->kiov_len is set to PAGE_SIZE, but actual data received might
- * be smaller, so we need to adjust it according to bd_enc_iov[]->kiov_len.
- * this means we DO NOT support the situation that server send an odd size
- * data in a page which is not the last one.
- * - for server write: we knows exactly data size for each page being expected,
- * thus kiov_len is accurate already, so we should not adjust it at all.
- * and bd_enc_iov[]->kiov_len should be round_up(bd_iov[]->kiov_len) which
- * should have been done by prep_bulk().
- */
-static
-int krb5_decrypt_bulk(struct crypto_blkcipher *tfm,
- struct krb5_header *khdr,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *cipher,
- rawobj_t *plain,
- int adj_nob)
-{
- struct blkcipher_desc ciph_desc;
- __u8 local_iv[16] = {0};
- struct scatterlist src, dst;
- int ct_nob = 0, pt_nob = 0;
- int blocksize, i, rc;
-
- LASSERT(desc->bd_iov_count);
- LASSERT(desc->bd_enc_iov);
- LASSERT(desc->bd_nob_transferred);
-
- blocksize = crypto_blkcipher_blocksize(tfm);
- LASSERT(blocksize > 1);
- LASSERT(cipher->len == blocksize + sizeof(*khdr));
-
- ciph_desc.tfm = tfm;
- ciph_desc.info = local_iv;
- ciph_desc.flags = 0;
-
- if (desc->bd_nob_transferred % blocksize) {
- CERROR("odd transferred nob: %d\n", desc->bd_nob_transferred);
- return -EPROTO;
- }
-
- /* decrypt head (confounder) */
- buf_to_sg(&src, cipher->data, blocksize);
- buf_to_sg(&dst, plain->data, blocksize);
-
- rc = crypto_blkcipher_decrypt_iv(&ciph_desc, &dst, &src, blocksize);
- if (rc) {
- CERROR("error to decrypt confounder: %d\n", rc);
- return rc;
- }
-
- for (i = 0; i < desc->bd_iov_count && ct_nob < desc->bd_nob_transferred;
- i++) {
- if (desc->bd_enc_iov[i].kiov_offset % blocksize != 0 ||
- desc->bd_enc_iov[i].kiov_len % blocksize != 0) {
- CERROR("page %d: odd offset %u len %u, blocksize %d\n",
- i, desc->bd_enc_iov[i].kiov_offset,
- desc->bd_enc_iov[i].kiov_len, blocksize);
- return -EFAULT;
- }
-
- if (adj_nob) {
- if (ct_nob + desc->bd_enc_iov[i].kiov_len >
- desc->bd_nob_transferred)
- desc->bd_enc_iov[i].kiov_len =
- desc->bd_nob_transferred - ct_nob;
-
- desc->bd_iov[i].kiov_len = desc->bd_enc_iov[i].kiov_len;
- if (pt_nob + desc->bd_enc_iov[i].kiov_len >desc->bd_nob)
- desc->bd_iov[i].kiov_len = desc->bd_nob -pt_nob;
- } else {
- /* this should be guaranteed by LNET */
- LASSERT(ct_nob + desc->bd_enc_iov[i].kiov_len <=
- desc->bd_nob_transferred);
- LASSERT(desc->bd_iov[i].kiov_len <=
- desc->bd_enc_iov[i].kiov_len);
- }
-
- if (desc->bd_enc_iov[i].kiov_len == 0)
- continue;
-
- sg_set_page(&src, desc->bd_enc_iov[i].kiov_page,
- desc->bd_enc_iov[i].kiov_len,
- desc->bd_enc_iov[i].kiov_offset);
- dst = src;
- if (desc->bd_iov[i].kiov_len % blocksize == 0)
- sg_assign_page(&dst, desc->bd_iov[i].kiov_page);
-
- rc = crypto_blkcipher_decrypt_iv(&ciph_desc, &dst, &src,
- src.length);
- if (rc) {
- CERROR("error to decrypt page: %d\n", rc);
- return rc;
- }
-
- if (desc->bd_iov[i].kiov_len % blocksize != 0) {
- memcpy(page_address(desc->bd_iov[i].kiov_page) +
- desc->bd_iov[i].kiov_offset,
- page_address(desc->bd_enc_iov[i].kiov_page) +
- desc->bd_iov[i].kiov_offset,
- desc->bd_iov[i].kiov_len);
- }
-
- ct_nob += desc->bd_enc_iov[i].kiov_len;
- pt_nob += desc->bd_iov[i].kiov_len;
- }
-
- if (unlikely(ct_nob != desc->bd_nob_transferred)) {
- CERROR("%d cipher text transferred but only %d decrypted\n",
- desc->bd_nob_transferred, ct_nob);
- return -EFAULT;
- }
-
- if (unlikely(!adj_nob && pt_nob != desc->bd_nob)) {
- CERROR("%d plain text expected but only %d received\n",
- desc->bd_nob, pt_nob);
- return -EFAULT;
- }
-
- /* if needed, clear up the rest unused iovs */
- if (adj_nob)
- while (i < desc->bd_iov_count)
- desc->bd_iov[i++].kiov_len = 0;
-
- /* decrypt tail (krb5 header) */
- buf_to_sg(&src, cipher->data + blocksize, sizeof(*khdr));
- buf_to_sg(&dst, cipher->data + blocksize, sizeof(*khdr));
-
- rc = crypto_blkcipher_decrypt_iv(&ciph_desc,
- &dst, &src, sizeof(*khdr));
- if (rc) {
- CERROR("error to decrypt tail: %d\n", rc);
- return rc;
- }
-
- if (memcmp(cipher->data + blocksize, khdr, sizeof(*khdr))) {
- CERROR("krb5 header doesn't match\n");
- return -EACCES;
- }
-
- return 0;
-}
-
-static
-__u32 gss_wrap_kerberos(struct gss_ctx *gctx,
- rawobj_t *gsshdr,
- rawobj_t *msg,
- int msg_buflen,
- rawobj_t *token)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
- struct krb5_header *khdr;
- int blocksize;
- rawobj_t cksum = RAWOBJ_EMPTY;
- rawobj_t data_desc[3], cipher;
- __u8 conf[GSS_MAX_CIPHER_BLOCK];
- int rc = 0;
-
- LASSERT(ke);
- LASSERT(ke->ke_conf_size <= GSS_MAX_CIPHER_BLOCK);
- LASSERT(kctx->kc_keye.kb_tfm == NULL ||
- ke->ke_conf_size >=
- crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm));
-
- /*
- * final token format:
- * ---------------------------------------------------
- * | krb5 header | cipher text | checksum (16 bytes) |
- * ---------------------------------------------------
- */
-
- /* fill krb5 header */
- LASSERT(token->len >= sizeof(*khdr));
- khdr = (struct krb5_header *) token->data;
- fill_krb5_header(kctx, khdr, 1);
-
- /* generate confounder */
- cfs_get_random_bytes(conf, ke->ke_conf_size);
-
- /* get encryption blocksize. note kc_keye might not associated with
- * a tfm, currently only for arcfour-hmac */
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- LASSERT(kctx->kc_keye.kb_tfm == NULL);
- blocksize = 1;
- } else {
- LASSERT(kctx->kc_keye.kb_tfm);
- blocksize = crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm);
- }
- LASSERT(blocksize <= ke->ke_conf_size);
-
- /* padding the message */
- if (add_padding(msg, msg_buflen, blocksize))
- return GSS_S_FAILURE;
-
- /*
- * clear text layout for checksum:
- * ------------------------------------------------------
- * | confounder | gss header | clear msgs | krb5 header |
- * ------------------------------------------------------
- */
- data_desc[0].data = conf;
- data_desc[0].len = ke->ke_conf_size;
- data_desc[1].data = gsshdr->data;
- data_desc[1].len = gsshdr->len;
- data_desc[2].data = msg->data;
- data_desc[2].len = msg->len;
-
- /* compute checksum */
- if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
- khdr, 3, data_desc, 0, NULL, &cksum))
- return GSS_S_FAILURE;
- LASSERT(cksum.len >= ke->ke_hash_size);
-
- /*
- * clear text layout for encryption:
- * -----------------------------------------
- * | confounder | clear msgs | krb5 header |
- * -----------------------------------------
- */
- data_desc[0].data = conf;
- data_desc[0].len = ke->ke_conf_size;
- data_desc[1].data = msg->data;
- data_desc[1].len = msg->len;
- data_desc[2].data = (__u8 *) khdr;
- data_desc[2].len = sizeof(*khdr);
-
- /* cipher text will be directly inplace */
- cipher.data = (__u8 *) (khdr + 1);
- cipher.len = token->len - sizeof(*khdr);
- LASSERT(cipher.len >= ke->ke_conf_size + msg->len + sizeof(*khdr));
-
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- rawobj_t arc4_keye;
- struct crypto_blkcipher *arc4_tfm;
-
- if (krb5_make_checksum(ENCTYPE_ARCFOUR_HMAC, &kctx->kc_keyi,
- NULL, 1, &cksum, 0, NULL, &arc4_keye)) {
- CERROR("failed to obtain arc4 enc key\n");
- GOTO(arc4_out, rc = -EACCES);
- }
-
- arc4_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, 0);
- if (IS_ERR(arc4_tfm)) {
- CERROR("failed to alloc tfm arc4 in ECB mode\n");
- GOTO(arc4_out_key, rc = -EACCES);
- }
-
- if (crypto_blkcipher_setkey(arc4_tfm, arc4_keye.data,
- arc4_keye.len)) {
- CERROR("failed to set arc4 key, len %d\n",
- arc4_keye.len);
- GOTO(arc4_out_tfm, rc = -EACCES);
- }
-
- rc = krb5_encrypt_rawobjs(arc4_tfm, 1,
- 3, data_desc, &cipher, 1);
-arc4_out_tfm:
- crypto_free_blkcipher(arc4_tfm);
-arc4_out_key:
- rawobj_free(&arc4_keye);
-arc4_out:
- do {} while (0); /* just to avoid compile warning */
- } else {
- rc = krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 0,
- 3, data_desc, &cipher, 1);
- }
-
- if (rc != 0) {
- rawobj_free(&cksum);
- return GSS_S_FAILURE;
- }
-
- /* fill in checksum */
- LASSERT(token->len >= sizeof(*khdr) + cipher.len + ke->ke_hash_size);
- memcpy((char *)(khdr + 1) + cipher.len,
- cksum.data + cksum.len - ke->ke_hash_size,
- ke->ke_hash_size);
- rawobj_free(&cksum);
-
- /* final token length */
- token->len = sizeof(*khdr) + cipher.len + ke->ke_hash_size;
- return GSS_S_COMPLETE;
-}
-
-static
-__u32 gss_prep_bulk_kerberos(struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- int blocksize, i;
-
- LASSERT(desc->bd_iov_count);
- LASSERT(desc->bd_enc_iov);
- LASSERT(kctx->kc_keye.kb_tfm);
-
- blocksize = crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm);
-
- for (i = 0; i < desc->bd_iov_count; i++) {
- LASSERT(desc->bd_enc_iov[i].kiov_page);
- /*
- * offset should always start at page boundary of either
- * client or server side.
- */
- if (desc->bd_iov[i].kiov_offset & blocksize) {
- CERROR("odd offset %d in page %d\n",
- desc->bd_iov[i].kiov_offset, i);
- return GSS_S_FAILURE;
- }
-
- desc->bd_enc_iov[i].kiov_offset = desc->bd_iov[i].kiov_offset;
- desc->bd_enc_iov[i].kiov_len = (desc->bd_iov[i].kiov_len +
- blocksize - 1) & (~(blocksize - 1));
- }
-
- return GSS_S_COMPLETE;
-}
-
-static
-__u32 gss_wrap_bulk_kerberos(struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token, int adj_nob)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
- struct krb5_header *khdr;
- int blocksize;
- rawobj_t cksum = RAWOBJ_EMPTY;
- rawobj_t data_desc[1], cipher;
- __u8 conf[GSS_MAX_CIPHER_BLOCK];
- int rc = 0;
-
- LASSERT(ke);
- LASSERT(ke->ke_conf_size <= GSS_MAX_CIPHER_BLOCK);
-
- /*
- * final token format:
- * --------------------------------------------------
- * | krb5 header | head/tail cipher text | checksum |
- * --------------------------------------------------
- */
-
- /* fill krb5 header */
- LASSERT(token->len >= sizeof(*khdr));
- khdr = (struct krb5_header *) token->data;
- fill_krb5_header(kctx, khdr, 1);
-
- /* generate confounder */
- cfs_get_random_bytes(conf, ke->ke_conf_size);
-
- /* get encryption blocksize. note kc_keye might not associated with
- * a tfm, currently only for arcfour-hmac */
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- LASSERT(kctx->kc_keye.kb_tfm == NULL);
- blocksize = 1;
- } else {
- LASSERT(kctx->kc_keye.kb_tfm);
- blocksize = crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm);
- }
-
- /*
- * we assume the size of krb5_header (16 bytes) must be n * blocksize.
- * the bulk token size would be exactly (sizeof(krb5_header) +
- * blocksize + sizeof(krb5_header) + hashsize)
- */
- LASSERT(blocksize <= ke->ke_conf_size);
- LASSERT(sizeof(*khdr) >= blocksize && sizeof(*khdr) % blocksize == 0);
- LASSERT(token->len >= sizeof(*khdr) + blocksize + sizeof(*khdr) + 16);
-
- /*
- * clear text layout for checksum:
- * ------------------------------------------
- * | confounder | clear pages | krb5 header |
- * ------------------------------------------
- */
- data_desc[0].data = conf;
- data_desc[0].len = ke->ke_conf_size;
-
- /* compute checksum */
- if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
- khdr, 1, data_desc,
- desc->bd_iov_count, desc->bd_iov,
- &cksum))
- return GSS_S_FAILURE;
- LASSERT(cksum.len >= ke->ke_hash_size);
-
- /*
- * clear text layout for encryption:
- * ------------------------------------------
- * | confounder | clear pages | krb5 header |
- * ------------------------------------------
- * | | |
- * ---------- (cipher pages) |
- * result token: | |
- * -------------------------------------------
- * | krb5 header | cipher text | cipher text |
- * -------------------------------------------
- */
- data_desc[0].data = conf;
- data_desc[0].len = ke->ke_conf_size;
-
- cipher.data = (__u8 *) (khdr + 1);
- cipher.len = blocksize + sizeof(*khdr);
-
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- LBUG();
- rc = 0;
- } else {
- rc = krb5_encrypt_bulk(kctx->kc_keye.kb_tfm, khdr,
- conf, desc, &cipher, adj_nob);
- }
-
- if (rc != 0) {
- rawobj_free(&cksum);
- return GSS_S_FAILURE;
- }
-
- /* fill in checksum */
- LASSERT(token->len >= sizeof(*khdr) + cipher.len + ke->ke_hash_size);
- memcpy((char *)(khdr + 1) + cipher.len,
- cksum.data + cksum.len - ke->ke_hash_size,
- ke->ke_hash_size);
- rawobj_free(&cksum);
-
- /* final token length */
- token->len = sizeof(*khdr) + cipher.len + ke->ke_hash_size;
- return GSS_S_COMPLETE;
-}
-
-static
-__u32 gss_unwrap_kerberos(struct gss_ctx *gctx,
- rawobj_t *gsshdr,
- rawobj_t *token,
- rawobj_t *msg)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
- struct krb5_header *khdr;
- unsigned char *tmpbuf;
- int blocksize, bodysize;
- rawobj_t cksum = RAWOBJ_EMPTY;
- rawobj_t cipher_in, plain_out;
- rawobj_t hash_objs[3];
- int rc = 0;
- __u32 major;
-
- LASSERT(ke);
-
- if (token->len < sizeof(*khdr)) {
- CERROR("short signature: %u\n", token->len);
- return GSS_S_DEFECTIVE_TOKEN;
- }
-
- khdr = (struct krb5_header *) token->data;
-
- major = verify_krb5_header(kctx, khdr, 1);
- if (major != GSS_S_COMPLETE) {
- CERROR("bad krb5 header\n");
- return major;
- }
-
- /* block size */
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- LASSERT(kctx->kc_keye.kb_tfm == NULL);
- blocksize = 1;
- } else {
- LASSERT(kctx->kc_keye.kb_tfm);
- blocksize = crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm);
- }
-
- /* expected token layout:
- * ----------------------------------------
- * | krb5 header | cipher text | checksum |
- * ----------------------------------------
- */
- bodysize = token->len - sizeof(*khdr) - ke->ke_hash_size;
-
- if (bodysize % blocksize) {
- CERROR("odd bodysize %d\n", bodysize);
- return GSS_S_DEFECTIVE_TOKEN;
- }
-
- if (bodysize <= ke->ke_conf_size + sizeof(*khdr)) {
- CERROR("incomplete token: bodysize %d\n", bodysize);
- return GSS_S_DEFECTIVE_TOKEN;
- }
-
- if (msg->len < bodysize - ke->ke_conf_size - sizeof(*khdr)) {
- CERROR("buffer too small: %u, require %d\n",
- msg->len, bodysize - ke->ke_conf_size);
- return GSS_S_FAILURE;
- }
-
- /* decrypting */
- OBD_ALLOC_LARGE(tmpbuf, bodysize);
- if (!tmpbuf)
- return GSS_S_FAILURE;
-
- major = GSS_S_FAILURE;
-
- cipher_in.data = (__u8 *) (khdr + 1);
- cipher_in.len = bodysize;
- plain_out.data = tmpbuf;
- plain_out.len = bodysize;
-
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- rawobj_t arc4_keye;
- struct crypto_blkcipher *arc4_tfm;
-
- cksum.data = token->data + token->len - ke->ke_hash_size;
- cksum.len = ke->ke_hash_size;
-
- if (krb5_make_checksum(ENCTYPE_ARCFOUR_HMAC, &kctx->kc_keyi,
- NULL, 1, &cksum, 0, NULL, &arc4_keye)) {
- CERROR("failed to obtain arc4 enc key\n");
- GOTO(arc4_out, rc = -EACCES);
- }
-
- arc4_tfm = crypto_alloc_blkcipher("ecb(arc4)", 0, 0);
- if (IS_ERR(arc4_tfm)) {
- CERROR("failed to alloc tfm arc4 in ECB mode\n");
- GOTO(arc4_out_key, rc = -EACCES);
- }
-
- if (crypto_blkcipher_setkey(arc4_tfm,
- arc4_keye.data, arc4_keye.len)) {
- CERROR("failed to set arc4 key, len %d\n",
- arc4_keye.len);
- GOTO(arc4_out_tfm, rc = -EACCES);
- }
-
- rc = krb5_encrypt_rawobjs(arc4_tfm, 1,
- 1, &cipher_in, &plain_out, 0);
-arc4_out_tfm:
- crypto_free_blkcipher(arc4_tfm);
-arc4_out_key:
- rawobj_free(&arc4_keye);
-arc4_out:
- cksum = RAWOBJ_EMPTY;
- } else {
- rc = krb5_encrypt_rawobjs(kctx->kc_keye.kb_tfm, 0,
- 1, &cipher_in, &plain_out, 0);
- }
-
- if (rc != 0) {
- CERROR("error decrypt\n");
- goto out_free;
- }
- LASSERT(plain_out.len == bodysize);
-
- /* expected clear text layout:
- * -----------------------------------------
- * | confounder | clear msgs | krb5 header |
- * -----------------------------------------
- */
-
- /* verify krb5 header in token is not modified */
- if (memcmp(khdr, plain_out.data + plain_out.len - sizeof(*khdr),
- sizeof(*khdr))) {
- CERROR("decrypted krb5 header mismatch\n");
- goto out_free;
- }
-
- /* verify checksum, compose clear text as layout:
- * ------------------------------------------------------
- * | confounder | gss header | clear msgs | krb5 header |
- * ------------------------------------------------------
- */
- hash_objs[0].len = ke->ke_conf_size;
- hash_objs[0].data = plain_out.data;
- hash_objs[1].len = gsshdr->len;
- hash_objs[1].data = gsshdr->data;
- hash_objs[2].len = plain_out.len - ke->ke_conf_size - sizeof(*khdr);
- hash_objs[2].data = plain_out.data + ke->ke_conf_size;
- if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
- khdr, 3, hash_objs, 0, NULL, &cksum))
- goto out_free;
-
- LASSERT(cksum.len >= ke->ke_hash_size);
- if (memcmp((char *)(khdr + 1) + bodysize,
- cksum.data + cksum.len - ke->ke_hash_size,
- ke->ke_hash_size)) {
- CERROR("checksum mismatch\n");
- goto out_free;
- }
-
- msg->len = bodysize - ke->ke_conf_size - sizeof(*khdr);
- memcpy(msg->data, tmpbuf + ke->ke_conf_size, msg->len);
-
- major = GSS_S_COMPLETE;
-out_free:
- OBD_FREE_LARGE(tmpbuf, bodysize);
- rawobj_free(&cksum);
- return major;
-}
-
-static
-__u32 gss_unwrap_bulk_kerberos(struct gss_ctx *gctx,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token, int adj_nob)
-{
- struct krb5_ctx *kctx = gctx->internal_ctx_id;
- struct krb5_enctype *ke = &enctypes[kctx->kc_enctype];
- struct krb5_header *khdr;
- int blocksize;
- rawobj_t cksum = RAWOBJ_EMPTY;
- rawobj_t cipher, plain;
- rawobj_t data_desc[1];
- int rc;
- __u32 major;
-
- LASSERT(ke);
-
- if (token->len < sizeof(*khdr)) {
- CERROR("short signature: %u\n", token->len);
- return GSS_S_DEFECTIVE_TOKEN;
- }
-
- khdr = (struct krb5_header *) token->data;
-
- major = verify_krb5_header(kctx, khdr, 1);
- if (major != GSS_S_COMPLETE) {
- CERROR("bad krb5 header\n");
- return major;
- }
-
- /* block size */
- if (kctx->kc_enctype == ENCTYPE_ARCFOUR_HMAC) {
- LASSERT(kctx->kc_keye.kb_tfm == NULL);
- blocksize = 1;
- LBUG();
- } else {
- LASSERT(kctx->kc_keye.kb_tfm);
- blocksize = crypto_blkcipher_blocksize(kctx->kc_keye.kb_tfm);
- }
- LASSERT(sizeof(*khdr) >= blocksize && sizeof(*khdr) % blocksize == 0);
-
- /*
- * token format is expected as:
- * -----------------------------------------------
- * | krb5 header | head/tail cipher text | cksum |
- * -----------------------------------------------
- */
- if (token->len < sizeof(*khdr) + blocksize + sizeof(*khdr) +
- ke->ke_hash_size) {
- CERROR("short token size: %u\n", token->len);
- return GSS_S_DEFECTIVE_TOKEN;
- }
-
- cipher.data = (__u8 *) (khdr + 1);
- cipher.len = blocksize + sizeof(*khdr);
- plain.data = cipher.data;
- plain.len = cipher.len;
-
- rc = krb5_decrypt_bulk(kctx->kc_keye.kb_tfm, khdr,
- desc, &cipher, &plain, adj_nob);
- if (rc)
- return GSS_S_DEFECTIVE_TOKEN;
-
- /*
- * verify checksum, compose clear text as layout:
- * ------------------------------------------
- * | confounder | clear pages | krb5 header |
- * ------------------------------------------
- */
- data_desc[0].data = plain.data;
- data_desc[0].len = blocksize;
-
- if (krb5_make_checksum(kctx->kc_enctype, &kctx->kc_keyi,
- khdr, 1, data_desc,
- desc->bd_iov_count, desc->bd_iov,
- &cksum))
- return GSS_S_FAILURE;
- LASSERT(cksum.len >= ke->ke_hash_size);
-
- if (memcmp(plain.data + blocksize + sizeof(*khdr),
- cksum.data + cksum.len - ke->ke_hash_size,
- ke->ke_hash_size)) {
- CERROR("checksum mismatch\n");
- rawobj_free(&cksum);
- return GSS_S_BAD_SIG;
- }
-
- rawobj_free(&cksum);
- return GSS_S_COMPLETE;
-}
-
-int gss_display_kerberos(struct gss_ctx *ctx,
- char *buf,
- int bufsize)
-{
- struct krb5_ctx *kctx = ctx->internal_ctx_id;
- int written;
-
- written = snprintf(buf, bufsize, "krb5 (%s)",
- enctype2str(kctx->kc_enctype));
- return written;
-}
-
-static struct gss_api_ops gss_kerberos_ops = {
- .gss_import_sec_context = gss_import_sec_context_kerberos,
- .gss_copy_reverse_context = gss_copy_reverse_context_kerberos,
- .gss_inquire_context = gss_inquire_context_kerberos,
- .gss_get_mic = gss_get_mic_kerberos,
- .gss_verify_mic = gss_verify_mic_kerberos,
- .gss_wrap = gss_wrap_kerberos,
- .gss_unwrap = gss_unwrap_kerberos,
- .gss_prep_bulk = gss_prep_bulk_kerberos,
- .gss_wrap_bulk = gss_wrap_bulk_kerberos,
- .gss_unwrap_bulk = gss_unwrap_bulk_kerberos,
- .gss_delete_sec_context = gss_delete_sec_context_kerberos,
- .gss_display = gss_display_kerberos,
-};
-
-static struct subflavor_desc gss_kerberos_sfs[] = {
- {
- .sf_subflavor = SPTLRPC_SUBFLVR_KRB5N,
- .sf_qop = 0,
- .sf_service = SPTLRPC_SVC_NULL,
- .sf_name = "krb5n"
- },
- {
- .sf_subflavor = SPTLRPC_SUBFLVR_KRB5A,
- .sf_qop = 0,
- .sf_service = SPTLRPC_SVC_AUTH,
- .sf_name = "krb5a"
- },
- {
- .sf_subflavor = SPTLRPC_SUBFLVR_KRB5I,
- .sf_qop = 0,
- .sf_service = SPTLRPC_SVC_INTG,
- .sf_name = "krb5i"
- },
- {
- .sf_subflavor = SPTLRPC_SUBFLVR_KRB5P,
- .sf_qop = 0,
- .sf_service = SPTLRPC_SVC_PRIV,
- .sf_name = "krb5p"
- },
-};
-
-/*
- * currently we leave module owner NULL
- */
-static struct gss_api_mech gss_kerberos_mech = {
- .gm_owner = NULL, /*THIS_MODULE, */
- .gm_name = "krb5",
- .gm_oid = (rawobj_t)
- {9, "\052\206\110\206\367\022\001\002\002"},
- .gm_ops = &gss_kerberos_ops,
- .gm_sf_num = 4,
- .gm_sfs = gss_kerberos_sfs,
-};
-
-int __init init_kerberos_module(void)
-{
- int status;
-
- spin_lock_init(&krb5_seq_lock);
-
- status = lgss_mech_register(&gss_kerberos_mech);
- if (status)
- CERROR("Failed to register kerberos gss mechanism!\n");
- return status;
-}
-
-void __exit cleanup_kerberos_module(void)
-{
- lgss_mech_unregister(&gss_kerberos_mech);
-}
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2012, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * linux/net/sunrpc/gss_mech_switch.c
- *
- * Copyright (c) 2001 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * J. Bruce Fields <bfields@umich.edu>
- *
- * 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
- * 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
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-static LIST_HEAD(registered_mechs);
-static DEFINE_SPINLOCK(registered_mechs_lock);
-
-int lgss_mech_register(struct gss_api_mech *gm)
-{
- spin_lock(®istered_mechs_lock);
- list_add(&gm->gm_list, ®istered_mechs);
- spin_unlock(®istered_mechs_lock);
- CWARN("Register %s mechanism\n", gm->gm_name);
- return 0;
-}
-
-void lgss_mech_unregister(struct gss_api_mech *gm)
-{
- spin_lock(®istered_mechs_lock);
- list_del(&gm->gm_list);
- spin_unlock(®istered_mechs_lock);
- CWARN("Unregister %s mechanism\n", gm->gm_name);
-}
-
-
-struct gss_api_mech *lgss_mech_get(struct gss_api_mech *gm)
-{
- __module_get(gm->gm_owner);
- return gm;
-}
-
-struct gss_api_mech *lgss_name_to_mech(char *name)
-{
- struct gss_api_mech *pos, *gm = NULL;
-
- spin_lock(®istered_mechs_lock);
- list_for_each_entry(pos, ®istered_mechs, gm_list) {
- if (0 == strcmp(name, pos->gm_name)) {
- if (!try_module_get(pos->gm_owner))
- continue;
- gm = pos;
- break;
- }
- }
- spin_unlock(®istered_mechs_lock);
- return gm;
-
-}
-
-static inline
-int mech_supports_subflavor(struct gss_api_mech *gm, __u32 subflavor)
-{
- int i;
-
- for (i = 0; i < gm->gm_sf_num; i++) {
- if (gm->gm_sfs[i].sf_subflavor == subflavor)
- return 1;
- }
- return 0;
-}
-
-struct gss_api_mech *lgss_subflavor_to_mech(__u32 subflavor)
-{
- struct gss_api_mech *pos, *gm = NULL;
-
- spin_lock(®istered_mechs_lock);
- list_for_each_entry(pos, ®istered_mechs, gm_list) {
- if (!try_module_get(pos->gm_owner))
- continue;
- if (!mech_supports_subflavor(pos, subflavor)) {
- module_put(pos->gm_owner);
- continue;
- }
- gm = pos;
- break;
- }
- spin_unlock(®istered_mechs_lock);
- return gm;
-}
-
-void lgss_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
- * as easy for now to pass it in. */
-__u32 lgss_import_sec_context(rawobj_t *input_token,
- struct gss_api_mech *mech,
- struct gss_ctx **ctx_id)
-{
- OBD_ALLOC_PTR(*ctx_id);
- if (*ctx_id == NULL)
- return GSS_S_FAILURE;
-
- (*ctx_id)->mech_type = lgss_mech_get(mech);
-
- LASSERT(mech);
- LASSERT(mech->gm_ops);
- LASSERT(mech->gm_ops->gss_import_sec_context);
- return mech->gm_ops->gss_import_sec_context(input_token, *ctx_id);
-}
-
-__u32 lgss_copy_reverse_context(struct gss_ctx *ctx_id,
- struct gss_ctx **ctx_id_new)
-{
- struct gss_api_mech *mech = ctx_id->mech_type;
- __u32 major;
-
- LASSERT(mech);
-
- OBD_ALLOC_PTR(*ctx_id_new);
- if (*ctx_id_new == NULL)
- return GSS_S_FAILURE;
-
- (*ctx_id_new)->mech_type = lgss_mech_get(mech);
-
- LASSERT(mech);
- LASSERT(mech->gm_ops);
- LASSERT(mech->gm_ops->gss_copy_reverse_context);
-
- major = mech->gm_ops->gss_copy_reverse_context(ctx_id, *ctx_id_new);
- if (major != GSS_S_COMPLETE) {
- lgss_mech_put(mech);
- OBD_FREE_PTR(*ctx_id_new);
- *ctx_id_new = NULL;
- }
- return major;
-}
-
-/*
- * this interface is much simplified, currently we only need endtime.
- */
-__u32 lgss_inquire_context(struct gss_ctx *context_handle,
- unsigned long *endtime)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_inquire_context);
-
- return context_handle->mech_type->gm_ops
- ->gss_inquire_context(context_handle,
- endtime);
-}
-
-/* gss_get_mic: compute a mic over message and return mic_token. */
-__u32 lgss_get_mic(struct gss_ctx *context_handle,
- int msgcnt,
- rawobj_t *msg,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *mic_token)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_get_mic);
-
- return context_handle->mech_type->gm_ops
- ->gss_get_mic(context_handle,
- msgcnt,
- msg,
- iovcnt,
- iovs,
- mic_token);
-}
-
-/* gss_verify_mic: check whether the provided mic_token verifies message. */
-__u32 lgss_verify_mic(struct gss_ctx *context_handle,
- int msgcnt,
- rawobj_t *msg,
- int iovcnt,
- lnet_kiov_t *iovs,
- rawobj_t *mic_token)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_verify_mic);
-
- return context_handle->mech_type->gm_ops
- ->gss_verify_mic(context_handle,
- msgcnt,
- msg,
- iovcnt,
- iovs,
- mic_token);
-}
-
-__u32 lgss_wrap(struct gss_ctx *context_handle,
- rawobj_t *gsshdr,
- rawobj_t *msg,
- int msg_buflen,
- rawobj_t *out_token)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_wrap);
-
- return context_handle->mech_type->gm_ops
- ->gss_wrap(context_handle, gsshdr, msg, msg_buflen, out_token);
-}
-
-__u32 lgss_unwrap(struct gss_ctx *context_handle,
- rawobj_t *gsshdr,
- rawobj_t *token,
- rawobj_t *out_msg)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_unwrap);
-
- return context_handle->mech_type->gm_ops
- ->gss_unwrap(context_handle, gsshdr, token, out_msg);
-}
-
-
-__u32 lgss_prep_bulk(struct gss_ctx *context_handle,
- struct ptlrpc_bulk_desc *desc)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_prep_bulk);
-
- return context_handle->mech_type->gm_ops
- ->gss_prep_bulk(context_handle, desc);
-}
-
-__u32 lgss_wrap_bulk(struct gss_ctx *context_handle,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token,
- int adj_nob)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_wrap_bulk);
-
- return context_handle->mech_type->gm_ops
- ->gss_wrap_bulk(context_handle, desc, token, adj_nob);
-}
-
-__u32 lgss_unwrap_bulk(struct gss_ctx *context_handle,
- struct ptlrpc_bulk_desc *desc,
- rawobj_t *token,
- int adj_nob)
-{
- LASSERT(context_handle);
- LASSERT(context_handle->mech_type);
- LASSERT(context_handle->mech_type->gm_ops);
- LASSERT(context_handle->mech_type->gm_ops->gss_unwrap_bulk);
-
- return context_handle->mech_type->gm_ops
- ->gss_unwrap_bulk(context_handle, desc, token, adj_nob);
-}
-
-/* gss_delete_sec_context: free all resources associated with context_handle.
- * Note this differs from the RFC 2744-specified prototype in that we don't
- * bother returning an output token, since it would never be used anyway. */
-
-__u32 lgss_delete_sec_context(struct gss_ctx **context_handle)
-{
- struct gss_api_mech *mech;
-
- CDEBUG(D_SEC, "deleting %p\n", *context_handle);
-
- if (!*context_handle)
- return(GSS_S_NO_CONTEXT);
-
- mech = (*context_handle)->mech_type;
- if ((*context_handle)->internal_ctx_id != 0) {
- LASSERT(mech);
- LASSERT(mech->gm_ops);
- LASSERT(mech->gm_ops->gss_delete_sec_context);
- mech->gm_ops->gss_delete_sec_context(
- (*context_handle)->internal_ctx_id);
- }
- if (mech)
- lgss_mech_put(mech);
-
- OBD_FREE_PTR(*context_handle);
- *context_handle=NULL;
- return GSS_S_COMPLETE;
-}
-
-int lgss_display(struct gss_ctx *ctx,
- char *buf,
- int bufsize)
-{
- LASSERT(ctx);
- LASSERT(ctx->mech_type);
- LASSERT(ctx->mech_type->gm_ops);
- LASSERT(ctx->mech_type->gm_ops->gss_display);
-
- return ctx->mech_type->gm_ops->gss_display(ctx, buf, bufsize);
-}
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2012, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * linux/net/sunrpc/auth_gss.c
- *
- * RPCSEC_GSS client authentication.
- *
- * Copyright (c) 2000 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Dug Song <dugsong@monkey.org>
- * Andy Adamson <andros@umich.edu>
- *
- * 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
- * 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
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <linux/crypto.h>
-#include <asm/atomic.h>
-struct rpc_clnt; /* for rpc_pipefs */
-#include <linux/sunrpc/rpc_pipe_fs.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_sec.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-static struct ptlrpc_sec_policy gss_policy_pipefs;
-static struct ptlrpc_ctx_ops gss_pipefs_ctxops;
-
-static int gss_cli_ctx_refresh_pf(struct ptlrpc_cli_ctx *ctx);
-
-static int gss_sec_pipe_upcall_init(struct gss_sec *gsec)
-{
- return 0;
-}
-
-static void gss_sec_pipe_upcall_fini(struct gss_sec *gsec)
-{
-}
-
-/****************************************
- * internal context helpers *
- ****************************************/
-
-static
-struct ptlrpc_cli_ctx *ctx_create_pf(struct ptlrpc_sec *sec,
- struct vfs_cred *vcred)
-{
- struct gss_cli_ctx *gctx;
- int rc;
-
- OBD_ALLOC_PTR(gctx);
- if (gctx == NULL)
- return NULL;
-
- rc = gss_cli_ctx_init_common(sec, &gctx->gc_base,
- &gss_pipefs_ctxops, vcred);
- if (rc) {
- OBD_FREE_PTR(gctx);
- return NULL;
- }
-
- return &gctx->gc_base;
-}
-
-static
-void ctx_destroy_pf(struct ptlrpc_sec *sec, struct ptlrpc_cli_ctx *ctx)
-{
- struct gss_cli_ctx *gctx = ctx2gctx(ctx);
-
- if (gss_cli_ctx_fini_common(sec, ctx))
- return;
-
- OBD_FREE_PTR(gctx);
-
- atomic_dec(&sec->ps_nctx);
- sptlrpc_sec_put(sec);
-}
-
-static
-void ctx_enhash_pf(struct ptlrpc_cli_ctx *ctx, struct hlist_head *hash)
-{
- set_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags);
- atomic_inc(&ctx->cc_refcount);
- hlist_add_head(&ctx->cc_cache, hash);
-}
-
-/*
- * caller must hold spinlock
- */
-static
-void ctx_unhash_pf(struct ptlrpc_cli_ctx *ctx, struct hlist_head *freelist)
-{
- assert_spin_locked(&ctx->cc_sec->ps_lock);
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags));
- LASSERT(!hlist_unhashed(&ctx->cc_cache));
-
- clear_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags);
-
- if (atomic_dec_and_test(&ctx->cc_refcount)) {
- __hlist_del(&ctx->cc_cache);
- hlist_add_head(&ctx->cc_cache, freelist);
- } else {
- hlist_del_init(&ctx->cc_cache);
- }
-}
-
-/*
- * return 1 if the context is dead.
- */
-static
-int ctx_check_death_pf(struct ptlrpc_cli_ctx *ctx,
- struct hlist_head *freelist)
-{
- if (cli_ctx_check_death(ctx)) {
- if (freelist)
- ctx_unhash_pf(ctx, freelist);
- return 1;
- }
-
- return 0;
-}
-
-static inline
-int ctx_check_death_locked_pf(struct ptlrpc_cli_ctx *ctx,
- struct hlist_head *freelist)
-{
- LASSERT(ctx->cc_sec);
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags));
-
- return ctx_check_death_pf(ctx, freelist);
-}
-
-static inline
-int ctx_match_pf(struct ptlrpc_cli_ctx *ctx, struct vfs_cred *vcred)
-{
- /* a little bit optimization for null policy */
- if (!ctx->cc_ops->match)
- return 1;
-
- return ctx->cc_ops->match(ctx, vcred);
-}
-
-static
-void ctx_list_destroy_pf(struct hlist_head *head)
-{
- struct ptlrpc_cli_ctx *ctx;
-
- while (!hlist_empty(head)) {
- ctx = hlist_entry(head->first, struct ptlrpc_cli_ctx,
- cc_cache);
-
- LASSERT(atomic_read(&ctx->cc_refcount) == 0);
- LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT,
- &ctx->cc_flags) == 0);
-
- hlist_del_init(&ctx->cc_cache);
- ctx_destroy_pf(ctx->cc_sec, ctx);
- }
-}
-
-/****************************************
- * context apis *
- ****************************************/
-
-static
-int gss_cli_ctx_validate_pf(struct ptlrpc_cli_ctx *ctx)
-{
- if (ctx_check_death_pf(ctx, NULL))
- return 1;
- if (cli_ctx_is_ready(ctx))
- return 0;
- return 1;
-}
-
-static
-void gss_cli_ctx_die_pf(struct ptlrpc_cli_ctx *ctx, int grace)
-{
- LASSERT(ctx->cc_sec);
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- cli_ctx_expire(ctx);
-
- spin_lock(&ctx->cc_sec->ps_lock);
-
- if (test_and_clear_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags)) {
- LASSERT(!hlist_unhashed(&ctx->cc_cache));
- LASSERT(atomic_read(&ctx->cc_refcount) > 1);
-
- hlist_del_init(&ctx->cc_cache);
- if (atomic_dec_and_test(&ctx->cc_refcount))
- LBUG();
- }
-
- spin_unlock(&ctx->cc_sec->ps_lock);
-}
-
-/****************************************
- * reverse context installation *
- ****************************************/
-
-static inline
-unsigned int ctx_hash_index(int hashsize, __u64 key)
-{
- return (unsigned int) (key & ((__u64) hashsize - 1));
-}
-
-static
-void gss_sec_ctx_replace_pf(struct gss_sec *gsec,
- struct ptlrpc_cli_ctx *new)
-{
- struct gss_sec_pipefs *gsec_pf;
- struct ptlrpc_cli_ctx *ctx;
- struct hlist_node *next;
- HLIST_HEAD(freelist);
- unsigned int hash;
-
- gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
-
- hash = ctx_hash_index(gsec_pf->gsp_chash_size,
- (__u64) new->cc_vcred.vc_uid);
- LASSERT(hash < gsec_pf->gsp_chash_size);
-
- spin_lock(&gsec->gs_base.ps_lock);
-
- hlist_for_each_entry_safe(ctx, next,
- &gsec_pf->gsp_chash[hash], cc_cache) {
- if (!ctx_match_pf(ctx, &new->cc_vcred))
- continue;
-
- cli_ctx_expire(ctx);
- ctx_unhash_pf(ctx, &freelist);
- break;
- }
-
- ctx_enhash_pf(new, &gsec_pf->gsp_chash[hash]);
-
- spin_unlock(&gsec->gs_base.ps_lock);
-
- ctx_list_destroy_pf(&freelist);
-}
-
-static
-int gss_install_rvs_cli_ctx_pf(struct gss_sec *gsec,
- struct ptlrpc_svc_ctx *svc_ctx)
-{
- struct vfs_cred vcred;
- struct ptlrpc_cli_ctx *cli_ctx;
- int rc;
-
- vcred.vc_uid = 0;
- vcred.vc_gid = 0;
-
- cli_ctx = ctx_create_pf(&gsec->gs_base, &vcred);
- if (!cli_ctx)
- return -ENOMEM;
-
- rc = gss_copy_rvc_cli_ctx(cli_ctx, svc_ctx);
- if (rc) {
- ctx_destroy_pf(cli_ctx->cc_sec, cli_ctx);
- return rc;
- }
-
- gss_sec_ctx_replace_pf(gsec, cli_ctx);
- return 0;
-}
-
-static
-void gss_ctx_cache_gc_pf(struct gss_sec_pipefs *gsec_pf,
- struct hlist_head *freelist)
-{
- struct ptlrpc_sec *sec;
- struct ptlrpc_cli_ctx *ctx;
- struct hlist_node *next;
- int i;
-
- sec = &gsec_pf->gsp_base.gs_base;
-
- CDEBUG(D_SEC, "do gc on sec %s@%p\n", sec->ps_policy->sp_name, sec);
-
- for (i = 0; i < gsec_pf->gsp_chash_size; i++) {
- hlist_for_each_entry_safe(ctx, next,
- &gsec_pf->gsp_chash[i], cc_cache)
- ctx_check_death_locked_pf(ctx, freelist);
- }
-
- sec->ps_gc_next = cfs_time_current_sec() + sec->ps_gc_interval;
-}
-
-static
-struct ptlrpc_sec* gss_sec_create_pf(struct obd_import *imp,
- struct ptlrpc_svc_ctx *ctx,
- struct sptlrpc_flavor *sf)
-{
- struct gss_sec_pipefs *gsec_pf;
- int alloc_size, hash_size, i;
-
-#define GSS_SEC_PIPEFS_CTX_HASH_SIZE (32)
-
- if (ctx ||
- sf->sf_flags & (PTLRPC_SEC_FL_ROOTONLY | PTLRPC_SEC_FL_REVERSE))
- hash_size = 1;
- else
- hash_size = GSS_SEC_PIPEFS_CTX_HASH_SIZE;
-
- alloc_size = sizeof(*gsec_pf) +
- sizeof(struct hlist_head) * hash_size;
-
- OBD_ALLOC(gsec_pf, alloc_size);
- if (!gsec_pf)
- return NULL;
-
- gsec_pf->gsp_chash_size = hash_size;
- for (i = 0; i < hash_size; i++)
- INIT_HLIST_HEAD(&gsec_pf->gsp_chash[i]);
-
- if (gss_sec_create_common(&gsec_pf->gsp_base, &gss_policy_pipefs,
- imp, ctx, sf))
- goto err_free;
-
- if (ctx == NULL) {
- if (gss_sec_pipe_upcall_init(&gsec_pf->gsp_base))
- goto err_destroy;
- } else {
- if (gss_install_rvs_cli_ctx_pf(&gsec_pf->gsp_base, ctx))
- goto err_destroy;
- }
-
- return &gsec_pf->gsp_base.gs_base;
-
-err_destroy:
- gss_sec_destroy_common(&gsec_pf->gsp_base);
-err_free:
- OBD_FREE(gsec_pf, alloc_size);
- return NULL;
-}
-
-static
-void gss_sec_destroy_pf(struct ptlrpc_sec *sec)
-{
- struct gss_sec_pipefs *gsec_pf;
- struct gss_sec *gsec;
-
- CWARN("destroy %s@%p\n", sec->ps_policy->sp_name, sec);
-
- gsec = container_of(sec, struct gss_sec, gs_base);
- gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
-
- LASSERT(gsec_pf->gsp_chash);
- LASSERT(gsec_pf->gsp_chash_size);
-
- gss_sec_pipe_upcall_fini(gsec);
-
- gss_sec_destroy_common(gsec);
-
- OBD_FREE(gsec, sizeof(*gsec_pf) +
- sizeof(struct hlist_head) * gsec_pf->gsp_chash_size);
-}
-
-static
-struct ptlrpc_cli_ctx * gss_sec_lookup_ctx_pf(struct ptlrpc_sec *sec,
- struct vfs_cred *vcred,
- int create, int remove_dead)
-{
- struct gss_sec *gsec;
- struct gss_sec_pipefs *gsec_pf;
- struct ptlrpc_cli_ctx *ctx = NULL, *new = NULL;
- struct hlist_head *hash_head;
- struct hlist_node *next;
- HLIST_HEAD(freelist);
- unsigned int hash, gc = 0, found = 0;
-
- might_sleep();
-
- gsec = container_of(sec, struct gss_sec, gs_base);
- gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
-
- hash = ctx_hash_index(gsec_pf->gsp_chash_size,
- (__u64) vcred->vc_uid);
- hash_head = &gsec_pf->gsp_chash[hash];
- LASSERT(hash < gsec_pf->gsp_chash_size);
-
-retry:
- spin_lock(&sec->ps_lock);
-
- /* gc_next == 0 means never do gc */
- if (remove_dead && sec->ps_gc_next &&
- cfs_time_after(cfs_time_current_sec(), sec->ps_gc_next)) {
- gss_ctx_cache_gc_pf(gsec_pf, &freelist);
- gc = 1;
- }
-
- hlist_for_each_entry_safe(ctx, next, hash_head, cc_cache) {
- if (gc == 0 &&
- ctx_check_death_locked_pf(ctx,
- remove_dead ? &freelist : NULL))
- continue;
-
- if (ctx_match_pf(ctx, vcred)) {
- found = 1;
- break;
- }
- }
-
- if (found) {
- if (new && new != ctx) {
- /* lost the race, just free it */
- hlist_add_head(&new->cc_cache, &freelist);
- new = NULL;
- }
-
- /* hot node, move to head */
- if (hash_head->first != &ctx->cc_cache) {
- __hlist_del(&ctx->cc_cache);
- hlist_add_head(&ctx->cc_cache, hash_head);
- }
- } else {
- /* don't allocate for reverse sec */
- if (sec_is_reverse(sec)) {
- spin_unlock(&sec->ps_lock);
- return NULL;
- }
-
- if (new) {
- ctx_enhash_pf(new, hash_head);
- ctx = new;
- } else if (create) {
- spin_unlock(&sec->ps_lock);
- new = ctx_create_pf(sec, vcred);
- if (new) {
- clear_bit(PTLRPC_CTX_NEW_BIT, &new->cc_flags);
- goto retry;
- }
- } else {
- ctx = NULL;
- }
- }
-
- /* hold a ref */
- if (ctx)
- atomic_inc(&ctx->cc_refcount);
-
- spin_unlock(&sec->ps_lock);
-
- /* the allocator of the context must give the first push to refresh */
- if (new) {
- LASSERT(new == ctx);
- gss_cli_ctx_refresh_pf(new);
- }
-
- ctx_list_destroy_pf(&freelist);
- return ctx;
-}
-
-static
-void gss_sec_release_ctx_pf(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx,
- int sync)
-{
- LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT, &ctx->cc_flags) == 0);
- LASSERT(hlist_unhashed(&ctx->cc_cache));
-
- /* if required async, we must clear the UPTODATE bit to prevent extra
- * rpcs during destroy procedure. */
- if (!sync)
- clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
-
- /* destroy this context */
- ctx_destroy_pf(sec, ctx);
-}
-
-/*
- * @uid: which user. "-1" means flush all.
- * @grace: mark context DEAD, allow graceful destroy like notify
- * server side, etc.
- * @force: also flush busy entries.
- *
- * return the number of busy context encountered.
- *
- * In any cases, never touch "eternal" contexts.
- */
-static
-int gss_sec_flush_ctx_cache_pf(struct ptlrpc_sec *sec,
- uid_t uid,
- int grace, int force)
-{
- struct gss_sec *gsec;
- struct gss_sec_pipefs *gsec_pf;
- struct ptlrpc_cli_ctx *ctx;
- struct hlist_node *next;
- HLIST_HEAD(freelist);
- int i, busy = 0;
-
- might_sleep_if(grace);
-
- gsec = container_of(sec, struct gss_sec, gs_base);
- gsec_pf = container_of(gsec, struct gss_sec_pipefs, gsp_base);
-
- spin_lock(&sec->ps_lock);
- for (i = 0; i < gsec_pf->gsp_chash_size; i++) {
- hlist_for_each_entry_safe(ctx, next,
- &gsec_pf->gsp_chash[i],
- cc_cache) {
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
-
- if (uid != -1 && uid != ctx->cc_vcred.vc_uid)
- continue;
-
- if (atomic_read(&ctx->cc_refcount) > 1) {
- busy++;
- if (!force)
- continue;
-
- CWARN("flush busy(%d) ctx %p(%u->%s) by force, "
- "grace %d\n",
- atomic_read(&ctx->cc_refcount),
- ctx, ctx->cc_vcred.vc_uid,
- sec2target_str(ctx->cc_sec), grace);
- }
- ctx_unhash_pf(ctx, &freelist);
-
- set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags);
- if (!grace)
- clear_bit(PTLRPC_CTX_UPTODATE_BIT,
- &ctx->cc_flags);
- }
- }
- spin_unlock(&sec->ps_lock);
-
- ctx_list_destroy_pf(&freelist);
- return busy;
-}
-
-/****************************************
- * service apis *
- ****************************************/
-
-static
-int gss_svc_accept_pf(struct ptlrpc_request *req)
-{
- return gss_svc_accept(&gss_policy_pipefs, req);
-}
-
-static
-int gss_svc_install_rctx_pf(struct obd_import *imp,
- struct ptlrpc_svc_ctx *ctx)
-{
- struct ptlrpc_sec *sec;
- int rc;
-
- sec = sptlrpc_import_sec_ref(imp);
- LASSERT(sec);
- rc = gss_install_rvs_cli_ctx_pf(sec2gsec(sec), ctx);
-
- sptlrpc_sec_put(sec);
- return rc;
-}
-
-/****************************************
- * rpc_pipefs definitions *
- ****************************************/
-
-#define LUSTRE_PIPE_ROOT "/lustre"
-#define LUSTRE_PIPE_KRB5 LUSTRE_PIPE_ROOT"/krb5"
-
-struct gss_upcall_msg_data {
- __u32 gum_seq;
- __u32 gum_uid;
- __u32 gum_gid;
- __u32 gum_svc; /* MDS/OSS... */
- __u64 gum_nid; /* peer NID */
- __u8 gum_obd[64]; /* client obd name */
-};
-
-struct gss_upcall_msg {
- struct rpc_pipe_msg gum_base;
- atomic_t gum_refcount;
- struct list_head gum_list;
- __u32 gum_mechidx;
- struct gss_sec *gum_gsec;
- struct gss_cli_ctx *gum_gctx;
- struct gss_upcall_msg_data gum_data;
-};
-
-static atomic_t upcall_seq = ATOMIC_INIT(0);
-
-static inline
-__u32 upcall_get_sequence(void)
-{
- return (__u32) atomic_inc_return(&upcall_seq);
-}
-
-enum mech_idx_t {
- MECH_KRB5 = 0,
- MECH_MAX
-};
-
-static inline
-__u32 mech_name2idx(const char *name)
-{
- LASSERT(!strcmp(name, "krb5"));
- return MECH_KRB5;
-}
-
-/* pipefs dentries for each mechanisms */
-static struct dentry *de_pipes[MECH_MAX] = { NULL, };
-/* all upcall messages linked here */
-static struct list_head upcall_lists[MECH_MAX];
-/* and protected by this */
-static spinlock_t upcall_locks[MECH_MAX];
-
-static inline
-void upcall_list_lock(int idx)
-{
- spin_lock(&upcall_locks[idx]);
-}
-
-static inline
-void upcall_list_unlock(int idx)
-{
- spin_unlock(&upcall_locks[idx]);
-}
-
-static
-void upcall_msg_enlist(struct gss_upcall_msg *msg)
-{
- __u32 idx = msg->gum_mechidx;
-
- upcall_list_lock(idx);
- list_add(&msg->gum_list, &upcall_lists[idx]);
- upcall_list_unlock(idx);
-}
-
-static
-void upcall_msg_delist(struct gss_upcall_msg *msg)
-{
- __u32 idx = msg->gum_mechidx;
-
- upcall_list_lock(idx);
- list_del_init(&msg->gum_list);
- upcall_list_unlock(idx);
-}
-
-/****************************************
- * rpc_pipefs upcall helpers *
- ****************************************/
-
-static
-void gss_release_msg(struct gss_upcall_msg *gmsg)
-{
- LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
-
- if (!atomic_dec_and_test(&gmsg->gum_refcount)) {
- return;
- }
-
- if (gmsg->gum_gctx) {
- sptlrpc_cli_ctx_wakeup(&gmsg->gum_gctx->gc_base);
- sptlrpc_cli_ctx_put(&gmsg->gum_gctx->gc_base, 1);
- gmsg->gum_gctx = NULL;
- }
-
- LASSERT(list_empty(&gmsg->gum_list));
- LASSERT(list_empty(&gmsg->gum_base.list));
- OBD_FREE_PTR(gmsg);
-}
-
-static
-void gss_unhash_msg_nolock(struct gss_upcall_msg *gmsg)
-{
- __u32 idx = gmsg->gum_mechidx;
-
- LASSERT(idx < MECH_MAX);
- assert_spin_locked(&upcall_locks[idx]);
-
- if (list_empty(&gmsg->gum_list))
- return;
-
- list_del_init(&gmsg->gum_list);
- LASSERT(atomic_read(&gmsg->gum_refcount) > 1);
- atomic_dec(&gmsg->gum_refcount);
-}
-
-static
-void gss_unhash_msg(struct gss_upcall_msg *gmsg)
-{
- __u32 idx = gmsg->gum_mechidx;
-
- LASSERT(idx < MECH_MAX);
- upcall_list_lock(idx);
- gss_unhash_msg_nolock(gmsg);
- upcall_list_unlock(idx);
-}
-
-static
-void gss_msg_fail_ctx(struct gss_upcall_msg *gmsg)
-{
- if (gmsg->gum_gctx) {
- struct ptlrpc_cli_ctx *ctx = &gmsg->gum_gctx->gc_base;
-
- LASSERT(atomic_read(&ctx->cc_refcount) > 0);
- sptlrpc_cli_ctx_expire(ctx);
- set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
- }
-}
-
-static
-struct gss_upcall_msg * gss_find_upcall(__u32 mechidx, __u32 seq)
-{
- struct gss_upcall_msg *gmsg;
-
- upcall_list_lock(mechidx);
- list_for_each_entry(gmsg, &upcall_lists[mechidx], gum_list) {
- if (gmsg->gum_data.gum_seq != seq)
- continue;
-
- LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
- LASSERT(gmsg->gum_mechidx == mechidx);
-
- atomic_inc(&gmsg->gum_refcount);
- upcall_list_unlock(mechidx);
- return gmsg;
- }
- upcall_list_unlock(mechidx);
- return NULL;
-}
-
-static
-int simple_get_bytes(char **buf, __u32 *buflen, void *res, __u32 reslen)
-{
- if (*buflen < reslen) {
- CERROR("buflen %u < %u\n", *buflen, reslen);
- return -EINVAL;
- }
-
- memcpy(res, *buf, reslen);
- *buf += reslen;
- *buflen -= reslen;
- return 0;
-}
-
-/****************************************
- * rpc_pipefs apis *
- ****************************************/
-
-static
-ssize_t gss_pipe_upcall(struct file *filp, struct rpc_pipe_msg *msg,
- char *dst, size_t buflen)
-{
- char *data = (char *)msg->data + msg->copied;
- ssize_t mlen = msg->len;
- ssize_t left;
-
- if (mlen > buflen)
- mlen = buflen;
- left = copy_to_user(dst, data, mlen);
- if (left < 0) {
- msg->errno = left;
- return left;
- }
- mlen -= left;
- msg->copied += mlen;
- msg->errno = 0;
- return mlen;
-}
-
-static
-ssize_t gss_pipe_downcall(struct file *filp, const char *src, size_t mlen)
-{
- struct rpc_inode *rpci = RPC_I(filp->f_dentry->d_inode);
- struct gss_upcall_msg *gss_msg;
- struct ptlrpc_cli_ctx *ctx;
- struct gss_cli_ctx *gctx = NULL;
- char *buf, *data;
- int datalen;
- int timeout, rc;
- __u32 mechidx, seq, gss_err;
-
- mechidx = (__u32) (long) rpci->private;
- LASSERT(mechidx < MECH_MAX);
-
- OBD_ALLOC(buf, mlen);
- if (!buf)
- return -ENOMEM;
-
- if (copy_from_user(buf, src, mlen)) {
- CERROR("failed copy user space data\n");
- GOTO(out_free, rc = -EFAULT);
- }
- data = buf;
- datalen = mlen;
-
- /* data passed down format:
- * - seq
- * - timeout
- * - gc_win / error
- * - wire_ctx (rawobj)
- * - mech_ctx (rawobj)
- */
- if (simple_get_bytes(&data, &datalen, &seq, sizeof(seq))) {
- CERROR("fail to get seq\n");
- GOTO(out_free, rc = -EFAULT);
- }
-
- gss_msg = gss_find_upcall(mechidx, seq);
- if (!gss_msg) {
- CERROR("upcall %u has aborted earlier\n", seq);
- GOTO(out_free, rc = -EINVAL);
- }
-
- gss_unhash_msg(gss_msg);
- gctx = gss_msg->gum_gctx;
- LASSERT(gctx);
- LASSERT(atomic_read(&gctx->gc_base.cc_refcount) > 0);
-
- /* timeout is not in use for now */
- if (simple_get_bytes(&data, &datalen, &timeout, sizeof(timeout)))
- GOTO(out_msg, rc = -EFAULT);
-
- /* lgssd signal an error by gc_win == 0 */
- if (simple_get_bytes(&data, &datalen, &gctx->gc_win,
- sizeof(gctx->gc_win)))
- GOTO(out_msg, rc = -EFAULT);
-
- if (gctx->gc_win == 0) {
- /* followed by:
- * - rpc error
- * - gss error
- */
- if (simple_get_bytes(&data, &datalen, &rc, sizeof(rc)))
- GOTO(out_msg, rc = -EFAULT);
- if (simple_get_bytes(&data, &datalen, &gss_err,sizeof(gss_err)))
- GOTO(out_msg, rc = -EFAULT);
-
- if (rc == 0 && gss_err == GSS_S_COMPLETE) {
- CWARN("both rpc & gss error code not set\n");
- rc = -EPERM;
- }
- } else {
- rawobj_t tmpobj;
-
- /* handle */
- if (rawobj_extract_local(&tmpobj, (__u32 **) &data, &datalen))
- GOTO(out_msg, rc = -EFAULT);
- if (rawobj_dup(&gctx->gc_handle, &tmpobj))
- GOTO(out_msg, rc = -ENOMEM);
-
- /* mechctx */
- if (rawobj_extract_local(&tmpobj, (__u32 **) &data, &datalen))
- GOTO(out_msg, rc = -EFAULT);
- gss_err = lgss_import_sec_context(&tmpobj,
- gss_msg->gum_gsec->gs_mech,
- &gctx->gc_mechctx);
- rc = 0;
- }
-
- if (likely(rc == 0 && gss_err == GSS_S_COMPLETE)) {
- gss_cli_ctx_uptodate(gctx);
- } else {
- ctx = &gctx->gc_base;
- sptlrpc_cli_ctx_expire(ctx);
- if (rc != -ERESTART || gss_err != GSS_S_COMPLETE)
- set_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags);
-
- CERROR("refresh ctx %p(uid %d) failed: %d/0x%08x: %s\n",
- ctx, ctx->cc_vcred.vc_uid, rc, gss_err,
- test_bit(PTLRPC_CTX_ERROR_BIT, &ctx->cc_flags) ?
- "fatal error" : "non-fatal");
- }
-
- rc = mlen;
-
-out_msg:
- gss_release_msg(gss_msg);
-
-out_free:
- OBD_FREE(buf, mlen);
- /* FIXME
- * hack pipefs: always return asked length unless all following
- * downcalls might be messed up. */
- rc = mlen;
- return rc;
-}
-
-static
-void gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
-{
- struct gss_upcall_msg *gmsg;
- struct gss_upcall_msg_data *gumd;
- static cfs_time_t ratelimit = 0;
-
- LASSERT(list_empty(&msg->list));
-
- /* normally errno is >= 0 */
- if (msg->errno >= 0) {
- return;
- }
-
- gmsg = container_of(msg, struct gss_upcall_msg, gum_base);
- gumd = &gmsg->gum_data;
- LASSERT(atomic_read(&gmsg->gum_refcount) > 0);
-
- CERROR("failed msg %p (seq %u, uid %u, svc %u, nid "LPX64", obd %.*s): "
- "errno %d\n", msg, gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
- gumd->gum_nid, (int) sizeof(gumd->gum_obd),
- gumd->gum_obd, msg->errno);
-
- atomic_inc(&gmsg->gum_refcount);
- gss_unhash_msg(gmsg);
- if (msg->errno == -ETIMEDOUT || msg->errno == -EPIPE) {
- cfs_time_t now = cfs_time_current_sec();
-
- if (cfs_time_after(now, ratelimit)) {
- CWARN("upcall timed out, is lgssd running?\n");
- ratelimit = now + 15;
- }
- }
- gss_msg_fail_ctx(gmsg);
- gss_release_msg(gmsg);
-}
-
-static
-void gss_pipe_release(struct inode *inode)
-{
- struct rpc_inode *rpci = RPC_I(inode);
- __u32 idx;
-
- idx = (__u32) (long) rpci->private;
- LASSERT(idx < MECH_MAX);
-
- upcall_list_lock(idx);
- while (!list_empty(&upcall_lists[idx])) {
- struct gss_upcall_msg *gmsg;
- struct gss_upcall_msg_data *gumd;
-
- gmsg = list_entry(upcall_lists[idx].next,
- struct gss_upcall_msg, gum_list);
- gumd = &gmsg->gum_data;
- LASSERT(list_empty(&gmsg->gum_base.list));
-
- CERROR("failing remaining msg %p:seq %u, uid %u, svc %u, "
- "nid "LPX64", obd %.*s\n", gmsg,
- gumd->gum_seq, gumd->gum_uid, gumd->gum_svc,
- gumd->gum_nid, (int) sizeof(gumd->gum_obd),
- gumd->gum_obd);
-
- gmsg->gum_base.errno = -EPIPE;
- atomic_inc(&gmsg->gum_refcount);
- gss_unhash_msg_nolock(gmsg);
-
- gss_msg_fail_ctx(gmsg);
-
- upcall_list_unlock(idx);
- gss_release_msg(gmsg);
- upcall_list_lock(idx);
- }
- upcall_list_unlock(idx);
-}
-
-static struct rpc_pipe_ops gss_upcall_ops = {
- .upcall = gss_pipe_upcall,
- .downcall = gss_pipe_downcall,
- .destroy_msg = gss_pipe_destroy_msg,
- .release_pipe = gss_pipe_release,
-};
-
-/****************************************
- * upcall helper functions *
- ****************************************/
-
-static
-int gss_ctx_refresh_pf(struct ptlrpc_cli_ctx *ctx)
-{
- struct obd_import *imp;
- struct gss_sec *gsec;
- struct gss_upcall_msg *gmsg;
- int rc = 0;
-
- might_sleep();
-
- LASSERT(ctx->cc_sec);
- LASSERT(ctx->cc_sec->ps_import);
- LASSERT(ctx->cc_sec->ps_import->imp_obd);
-
- imp = ctx->cc_sec->ps_import;
- if (!imp->imp_connection) {
- CERROR("import has no connection set\n");
- return -EINVAL;
- }
-
- gsec = container_of(ctx->cc_sec, struct gss_sec, gs_base);
-
- OBD_ALLOC_PTR(gmsg);
- if (!gmsg)
- return -ENOMEM;
-
- /* initialize pipefs base msg */
- INIT_LIST_HEAD(&gmsg->gum_base.list);
- gmsg->gum_base.data = &gmsg->gum_data;
- gmsg->gum_base.len = sizeof(gmsg->gum_data);
- gmsg->gum_base.copied = 0;
- gmsg->gum_base.errno = 0;
-
- /* init upcall msg */
- atomic_set(&gmsg->gum_refcount, 1);
- gmsg->gum_mechidx = mech_name2idx(gsec->gs_mech->gm_name);
- gmsg->gum_gsec = gsec;
- gmsg->gum_gctx = container_of(sptlrpc_cli_ctx_get(ctx),
- struct gss_cli_ctx, gc_base);
- gmsg->gum_data.gum_seq = upcall_get_sequence();
- gmsg->gum_data.gum_uid = ctx->cc_vcred.vc_uid;
- gmsg->gum_data.gum_gid = 0; /* not used for now */
- gmsg->gum_data.gum_svc = import_to_gss_svc(imp);
- gmsg->gum_data.gum_nid = imp->imp_connection->c_peer.nid;
- strncpy(gmsg->gum_data.gum_obd, imp->imp_obd->obd_name,
- sizeof(gmsg->gum_data.gum_obd));
-
- /* This only could happen when sysadmin set it dead/expired
- * using lctl by force. */
- if (ctx->cc_flags & PTLRPC_CTX_STATUS_MASK) {
- CWARN("ctx %p(%u->%s) was set flags %lx unexpectedly\n",
- ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
- ctx->cc_flags);
-
- LASSERT(!(ctx->cc_flags & PTLRPC_CTX_UPTODATE));
- ctx->cc_flags |= PTLRPC_CTX_DEAD | PTLRPC_CTX_ERROR;
-
- rc = -EIO;
- goto err_free;
- }
-
- upcall_msg_enlist(gmsg);
-
- rc = rpc_queue_upcall(de_pipes[gmsg->gum_mechidx]->d_inode,
- &gmsg->gum_base);
- if (rc) {
- CERROR("rpc_queue_upcall failed: %d\n", rc);
-
- upcall_msg_delist(gmsg);
- goto err_free;
- }
-
- return 0;
-err_free:
- OBD_FREE_PTR(gmsg);
- return rc;
-}
-
-static
-int gss_cli_ctx_refresh_pf(struct ptlrpc_cli_ctx *ctx)
-{
- /* if we are refreshing for root, also update the reverse
- * handle index, do not confuse reverse contexts. */
- if (ctx->cc_vcred.vc_uid == 0) {
- struct gss_sec *gsec;
-
- gsec = container_of(ctx->cc_sec, struct gss_sec, gs_base);
- gsec->gs_rvs_hdl = gss_get_next_ctx_index();
- }
-
- return gss_ctx_refresh_pf(ctx);
-}
-
-/****************************************
- * lustre gss pipefs policy *
- ****************************************/
-
-static struct ptlrpc_ctx_ops gss_pipefs_ctxops = {
- .match = gss_cli_ctx_match,
- .refresh = gss_cli_ctx_refresh_pf,
- .validate = gss_cli_ctx_validate_pf,
- .die = gss_cli_ctx_die_pf,
- .sign = gss_cli_ctx_sign,
- .verify = gss_cli_ctx_verify,
- .seal = gss_cli_ctx_seal,
- .unseal = gss_cli_ctx_unseal,
- .wrap_bulk = gss_cli_ctx_wrap_bulk,
- .unwrap_bulk = gss_cli_ctx_unwrap_bulk,
-};
-
-static struct ptlrpc_sec_cops gss_sec_pipefs_cops = {
- .create_sec = gss_sec_create_pf,
- .destroy_sec = gss_sec_destroy_pf,
- .kill_sec = gss_sec_kill,
- .lookup_ctx = gss_sec_lookup_ctx_pf,
- .release_ctx = gss_sec_release_ctx_pf,
- .flush_ctx_cache = gss_sec_flush_ctx_cache_pf,
- .install_rctx = gss_sec_install_rctx,
- .alloc_reqbuf = gss_alloc_reqbuf,
- .free_reqbuf = gss_free_reqbuf,
- .alloc_repbuf = gss_alloc_repbuf,
- .free_repbuf = gss_free_repbuf,
- .enlarge_reqbuf = gss_enlarge_reqbuf,
-};
-
-static struct ptlrpc_sec_sops gss_sec_pipefs_sops = {
- .accept = gss_svc_accept_pf,
- .invalidate_ctx = gss_svc_invalidate_ctx,
- .alloc_rs = gss_svc_alloc_rs,
- .authorize = gss_svc_authorize,
- .free_rs = gss_svc_free_rs,
- .free_ctx = gss_svc_free_ctx,
- .unwrap_bulk = gss_svc_unwrap_bulk,
- .wrap_bulk = gss_svc_wrap_bulk,
- .install_rctx = gss_svc_install_rctx_pf,
-};
-
-static struct ptlrpc_sec_policy gss_policy_pipefs = {
- .sp_owner = THIS_MODULE,
- .sp_name = "gss.pipefs",
- .sp_policy = SPTLRPC_POLICY_GSS_PIPEFS,
- .sp_cops = &gss_sec_pipefs_cops,
- .sp_sops = &gss_sec_pipefs_sops,
-};
-
-static
-int __init gss_init_pipefs_upcall(void)
-{
- struct dentry *de;
-
- /* pipe dir */
- de = rpc_mkdir(LUSTRE_PIPE_ROOT, NULL);
- if (IS_ERR(de) && PTR_ERR(de) != -EEXIST) {
- CERROR("Failed to create gss pipe dir: %ld\n", PTR_ERR(de));
- return PTR_ERR(de);
- }
-
- /* FIXME hack pipefs: dput will sometimes cause oops during module
- * unload and lgssd close the pipe fds. */
-
- /* krb5 mechanism */
- de = rpc_mkpipe(LUSTRE_PIPE_KRB5, (void *) MECH_KRB5, &gss_upcall_ops,
- RPC_PIPE_WAIT_FOR_OPEN);
- if (!de || IS_ERR(de)) {
- CERROR("failed to make rpc_pipe %s: %ld\n",
- LUSTRE_PIPE_KRB5, PTR_ERR(de));
- rpc_rmdir(LUSTRE_PIPE_ROOT);
- return PTR_ERR(de);
- }
-
- de_pipes[MECH_KRB5] = de;
- INIT_LIST_HEAD(&upcall_lists[MECH_KRB5]);
- spin_lock_init(&upcall_locks[MECH_KRB5]);
-
- return 0;
-}
-
-static
-void __exit gss_exit_pipefs_upcall(void)
-{
- __u32 i;
-
- for (i = 0; i < MECH_MAX; i++) {
- LASSERT(list_empty(&upcall_lists[i]));
-
- /* dput pipe dentry here might cause lgssd oops. */
- de_pipes[i] = NULL;
- }
-
- rpc_unlink(LUSTRE_PIPE_KRB5);
- rpc_rmdir(LUSTRE_PIPE_ROOT);
-}
-
-int __init gss_init_pipefs(void)
-{
- int rc;
-
- rc = gss_init_pipefs_upcall();
- if (rc)
- return rc;
-
- rc = sptlrpc_register_policy(&gss_policy_pipefs);
- if (rc) {
- gss_exit_pipefs_upcall();
- return rc;
- }
-
- return 0;
-}
-
-void __exit gss_exit_pipefs(void)
-{
- gss_exit_pipefs_upcall();
- sptlrpc_unregister_policy(&gss_policy_pipefs);
-}
+++ /dev/null
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2011, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- *
- * lustre/ptlrpc/gss/gss_rawobj.c
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-
-#include <linux/mutex.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre_sec.h>
-
-#include "gss_internal.h"
-
-int rawobj_empty(rawobj_t *obj)
-{
- LASSERT(equi(obj->len, obj->data));
- return (obj->len == 0);
-}
-
-int rawobj_alloc(rawobj_t *obj, char *buf, int len)
-{
- LASSERT(obj);
- LASSERT(len >= 0);
-
- obj->len = len;
- if (len) {
- OBD_ALLOC_LARGE(obj->data, len);
- if (!obj->data) {
- obj->len = 0;
- return -ENOMEM;
- }
- memcpy(obj->data, buf, len);
- } else
- obj->data = NULL;
- return 0;
-}
-
-void rawobj_free(rawobj_t *obj)
-{
- LASSERT(obj);
-
- if (obj->len) {
- LASSERT(obj->data);
- OBD_FREE_LARGE(obj->data, obj->len);
- obj->len = 0;
- obj->data = NULL;
- } else
- LASSERT(!obj->data);
-}
-
-int rawobj_equal(rawobj_t *a, rawobj_t *b)
-{
- LASSERT(a && b);
-
- return (a->len == b->len &&
- (!a->len || !memcmp(a->data, b->data, a->len)));
-}
-
-int rawobj_dup(rawobj_t *dest, rawobj_t *src)
-{
- LASSERT(src && dest);
-
- dest->len = src->len;
- if (dest->len) {
- OBD_ALLOC_LARGE(dest->data, dest->len);
- if (!dest->data) {
- dest->len = 0;
- return -ENOMEM;
- }
- memcpy(dest->data, src->data, dest->len);
- } else
- dest->data = NULL;
- return 0;
-}
-
-int rawobj_serialize(rawobj_t *obj, __u32 **buf, __u32 *buflen)
-{
- __u32 len;
-
- LASSERT(obj);
- LASSERT(buf);
- LASSERT(buflen);
-
- len = cfs_size_round4(obj->len);
-
- if (*buflen < 4 + len) {
- CERROR("buflen %u < %u\n", *buflen, 4 + len);
- return -EINVAL;
- }
-
- *(*buf)++ = cpu_to_le32(obj->len);
- memcpy(*buf, obj->data, obj->len);
- *buf += (len >> 2);
- *buflen -= (4 + len);
-
- return 0;
-}
-
-static int __rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen,
- int alloc, int local)
-{
- __u32 len;
-
- if (*buflen < sizeof(__u32)) {
- CERROR("buflen %u\n", *buflen);
- return -EINVAL;
- }
-
- obj->len = *(*buf)++;
- if (!local)
- obj->len = le32_to_cpu(obj->len);
- *buflen -= sizeof(__u32);
-
- if (!obj->len) {
- obj->data = NULL;
- return 0;
- }
-
- len = local ? obj->len : cfs_size_round4(obj->len);
- if (*buflen < len) {
- CERROR("buflen %u < %u\n", *buflen, len);
- obj->len = 0;
- return -EINVAL;
- }
-
- if (!alloc)
- obj->data = (__u8 *) *buf;
- else {
- OBD_ALLOC_LARGE(obj->data, obj->len);
- if (!obj->data) {
- CERROR("fail to alloc %u bytes\n", obj->len);
- obj->len = 0;
- return -ENOMEM;
- }
- memcpy(obj->data, *buf, obj->len);
- }
-
- *((char **)buf) += len;
- *buflen -= len;
-
- return 0;
-}
-
-int rawobj_extract(rawobj_t *obj, __u32 **buf, __u32 *buflen)
-{
- return __rawobj_extract(obj, buf, buflen, 0, 0);
-}
-
-int rawobj_extract_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen)
-{
- return __rawobj_extract(obj, buf, buflen, 1, 0);
-}
-
-int rawobj_extract_local(rawobj_t *obj, __u32 **buf, __u32 *buflen)
-{
- return __rawobj_extract(obj, buf, buflen, 0, 1);
-}
-
-int rawobj_extract_local_alloc(rawobj_t *obj, __u32 **buf, __u32 *buflen)
-{
- return __rawobj_extract(obj, buf, buflen, 1, 1);
-}
-
-int rawobj_from_netobj(rawobj_t *rawobj, netobj_t *netobj)
-{
- rawobj->len = netobj->len;
- rawobj->data = netobj->data;
- return 0;
-}
-
-int rawobj_from_netobj_alloc(rawobj_t *rawobj, netobj_t *netobj)
-{
- rawobj->len = 0;
- rawobj->data = NULL;
-
- if (netobj->len == 0)
- return 0;
-
- OBD_ALLOC_LARGE(rawobj->data, netobj->len);
- if (rawobj->data == NULL)
- return -ENOMEM;
-
- rawobj->len = netobj->len;
- memcpy(rawobj->data, netobj->data, netobj->len);
- return 0;
-}
-
-/****************************************
- * misc more *
- ****************************************/
-
-int buffer_extract_bytes(const void **buf, __u32 *buflen,
- void *res, __u32 reslen)
-{
- if (*buflen < reslen) {
- CERROR("buflen %u < %u\n", *buflen, reslen);
- return -EINVAL;
- }
-
- memcpy(res, *buf, reslen);
- *buf += reslen;
- *buflen -= reslen;
- return 0;
-}
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2011, 2012, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * Neil Brown <neilb@cse.unsw.edu.au>
- * J. Bruce Fields <bfields@umich.edu>
- * Andy Adamson <andros@umich.edu>
- * Dug Song <dugsong@monkey.org>
- *
- * RPCSEC_GSS server authentication.
- * This implements RPCSEC_GSS as defined in rfc2203 (rpcsec_gss) and rfc2078
- * (gssapi)
- *
- * The RPCSEC_GSS involves three stages:
- * 1/ context creation
- * 2/ data exchange
- * 3/ context destruction
- *
- * Context creation is handled largely by upcalls to user-space.
- * In particular, GSS_Accept_sec_context is handled by an upcall
- * Data exchange is handled entirely within the kernel
- * In particular, GSS_GetMIC, GSS_VerifyMIC, GSS_Seal, GSS_Unseal are in-kernel.
- * Context destruction is handled in-kernel
- * GSS_Delete_sec_context is in-kernel
- *
- * Context creation is initiated by a RPCSEC_GSS_INIT request arriving.
- * The context handle and gss_token are used as a key into the rpcsec_init cache.
- * The content of this cache includes some of the outputs of GSS_Accept_sec_context,
- * being major_status, minor_status, context_handle, reply_token.
- * These are sent back to the client.
- * Sequence window management is handled by the kernel. The window size if currently
- * a compile time constant.
- *
- * When user-space is happy that a context is established, it places an entry
- * in the rpcsec_context cache. The key for this cache is the context_handle.
- * The content includes:
- * uid/gidlist - for determining access rights
- * mechanism type
- * mechanism specific information, such as a key
- *
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/types.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/hash.h>
-#include <linux/mutex.h>
-#include <linux/sunrpc/cache.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-#define GSS_SVC_UPCALL_TIMEOUT (20)
-
-static spinlock_t __ctx_index_lock;
-static __u64 __ctx_index;
-
-__u64 gss_get_next_ctx_index(void)
-{
- __u64 idx;
-
- spin_lock(&__ctx_index_lock);
- idx = __ctx_index++;
- spin_unlock(&__ctx_index_lock);
-
- return idx;
-}
-
-static inline unsigned long hash_mem(char *buf, int length, int bits)
-{
- unsigned long hash = 0;
- unsigned long l = 0;
- int len = 0;
- unsigned char c;
-
- do {
- if (len == length) {
- c = (char) len;
- len = -1;
- } else
- c = *buf++;
-
- l = (l << 8) | c;
- len++;
-
- if ((len & (BITS_PER_LONG/8-1)) == 0)
- hash = hash_long(hash^l, BITS_PER_LONG);
- } while (len);
-
- return hash >> (BITS_PER_LONG - bits);
-}
-
-/****************************************
- * rsi cache *
- ****************************************/
-
-#define RSI_HASHBITS (6)
-#define RSI_HASHMAX (1 << RSI_HASHBITS)
-#define RSI_HASHMASK (RSI_HASHMAX - 1)
-
-struct rsi {
- struct cache_head h;
- __u32 lustre_svc;
- __u64 nid;
- wait_queue_head_t waitq;
- rawobj_t in_handle, in_token;
- rawobj_t out_handle, out_token;
- int major_status, minor_status;
-};
-
-static struct cache_head *rsi_table[RSI_HASHMAX];
-static struct cache_detail rsi_cache;
-static struct rsi *rsi_update(struct rsi *new, struct rsi *old);
-static struct rsi *rsi_lookup(struct rsi *item);
-
-static inline int rsi_hash(struct rsi *item)
-{
- return hash_mem((char *)item->in_handle.data, item->in_handle.len,
- RSI_HASHBITS) ^
- hash_mem((char *)item->in_token.data, item->in_token.len,
- RSI_HASHBITS);
-}
-
-static inline int __rsi_match(struct rsi *item, struct rsi *tmp)
-{
- return (rawobj_equal(&item->in_handle, &tmp->in_handle) &&
- rawobj_equal(&item->in_token, &tmp->in_token));
-}
-
-static void rsi_free(struct rsi *rsi)
-{
- rawobj_free(&rsi->in_handle);
- rawobj_free(&rsi->in_token);
- rawobj_free(&rsi->out_handle);
- rawobj_free(&rsi->out_token);
-}
-
-static void rsi_request(struct cache_detail *cd,
- struct cache_head *h,
- char **bpp, int *blen)
-{
- struct rsi *rsi = container_of(h, struct rsi, h);
- __u64 index = 0;
-
- /* if in_handle is null, provide kernel suggestion */
- if (rsi->in_handle.len == 0)
- index = gss_get_next_ctx_index();
-
- qword_addhex(bpp, blen, (char *) &rsi->lustre_svc,
- sizeof(rsi->lustre_svc));
- qword_addhex(bpp, blen, (char *) &rsi->nid, sizeof(rsi->nid));
- qword_addhex(bpp, blen, (char *) &index, sizeof(index));
- qword_addhex(bpp, blen, rsi->in_handle.data, rsi->in_handle.len);
- qword_addhex(bpp, blen, rsi->in_token.data, rsi->in_token.len);
- (*bpp)[-1] = '\n';
-}
-
-static int rsi_upcall(struct cache_detail *cd, struct cache_head *h)
-{
- return sunrpc_cache_pipe_upcall(cd, h, rsi_request);
-}
-
-static inline void __rsi_init(struct rsi *new, struct rsi *item)
-{
- new->out_handle = RAWOBJ_EMPTY;
- new->out_token = RAWOBJ_EMPTY;
-
- new->in_handle = item->in_handle;
- item->in_handle = RAWOBJ_EMPTY;
- new->in_token = item->in_token;
- item->in_token = RAWOBJ_EMPTY;
-
- new->lustre_svc = item->lustre_svc;
- new->nid = item->nid;
- init_waitqueue_head(&new->waitq);
-}
-
-static inline void __rsi_update(struct rsi *new, struct rsi *item)
-{
- LASSERT(new->out_handle.len == 0);
- LASSERT(new->out_token.len == 0);
-
- new->out_handle = item->out_handle;
- item->out_handle = RAWOBJ_EMPTY;
- new->out_token = item->out_token;
- item->out_token = RAWOBJ_EMPTY;
-
- new->major_status = item->major_status;
- new->minor_status = item->minor_status;
-}
-
-static void rsi_put(struct kref *ref)
-{
- struct rsi *rsi = container_of(ref, struct rsi, h.ref);
-
- LASSERT(rsi->h.next == NULL);
- rsi_free(rsi);
- OBD_FREE_PTR(rsi);
-}
-
-static int rsi_match(struct cache_head *a, struct cache_head *b)
-{
- struct rsi *item = container_of(a, struct rsi, h);
- struct rsi *tmp = container_of(b, struct rsi, h);
-
- return __rsi_match(item, tmp);
-}
-
-static void rsi_init(struct cache_head *cnew, struct cache_head *citem)
-{
- struct rsi *new = container_of(cnew, struct rsi, h);
- struct rsi *item = container_of(citem, struct rsi, h);
-
- __rsi_init(new, item);
-}
-
-static void update_rsi(struct cache_head *cnew, struct cache_head *citem)
-{
- struct rsi *new = container_of(cnew, struct rsi, h);
- struct rsi *item = container_of(citem, struct rsi, h);
-
- __rsi_update(new, item);
-}
-
-static struct cache_head *rsi_alloc(void)
-{
- struct rsi *rsi;
-
- OBD_ALLOC_PTR(rsi);
- if (rsi)
- return &rsi->h;
- else
- return NULL;
-}
-
-static int rsi_parse(struct cache_detail *cd, char *mesg, int mlen)
-{
- char *buf = mesg;
- char *ep;
- int len;
- struct rsi rsii, *rsip = NULL;
- time_t expiry;
- int status = -EINVAL;
-
- memset(&rsii, 0, sizeof(rsii));
-
- /* handle */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0)
- goto out;
- if (rawobj_alloc(&rsii.in_handle, buf, len)) {
- status = -ENOMEM;
- goto out;
- }
-
- /* token */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0)
- goto out;
- if (rawobj_alloc(&rsii.in_token, buf, len)) {
- status = -ENOMEM;
- goto out;
- }
-
- rsip = rsi_lookup(&rsii);
- if (!rsip)
- goto out;
-
- rsii.h.flags = 0;
- /* expiry */
- expiry = get_expiry(&mesg);
- if (expiry == 0)
- goto out;
-
- len = qword_get(&mesg, buf, mlen);
- if (len <= 0)
- goto out;
-
- /* major */
- rsii.major_status = simple_strtol(buf, &ep, 10);
- if (*ep)
- goto out;
-
- /* minor */
- len = qword_get(&mesg, buf, mlen);
- if (len <= 0)
- goto out;
- rsii.minor_status = simple_strtol(buf, &ep, 10);
- if (*ep)
- goto out;
-
- /* out_handle */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0)
- goto out;
- if (rawobj_alloc(&rsii.out_handle, buf, len)) {
- status = -ENOMEM;
- goto out;
- }
-
- /* out_token */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0)
- goto out;
- if (rawobj_alloc(&rsii.out_token, buf, len)) {
- status = -ENOMEM;
- goto out;
- }
-
- rsii.h.expiry_time = expiry;
- rsip = rsi_update(&rsii, rsip);
- status = 0;
-out:
- rsi_free(&rsii);
- if (rsip) {
- wake_up_all(&rsip->waitq);
- cache_put(&rsip->h, &rsi_cache);
- } else {
- status = -ENOMEM;
- }
-
- if (status)
- CERROR("rsi parse error %d\n", status);
- return status;
-}
-
-static struct cache_detail rsi_cache = {
- .hash_size = RSI_HASHMAX,
- .hash_table = rsi_table,
- .name = "auth.sptlrpc.init",
- .cache_put = rsi_put,
- .cache_upcall = rsi_upcall,
- .cache_parse = rsi_parse,
- .match = rsi_match,
- .init = rsi_init,
- .update = update_rsi,
- .alloc = rsi_alloc,
-};
-
-static struct rsi *rsi_lookup(struct rsi *item)
-{
- struct cache_head *ch;
- int hash = rsi_hash(item);
-
- ch = sunrpc_cache_lookup(&rsi_cache, &item->h, hash);
- if (ch)
- return container_of(ch, struct rsi, h);
- else
- return NULL;
-}
-
-static struct rsi *rsi_update(struct rsi *new, struct rsi *old)
-{
- struct cache_head *ch;
- int hash = rsi_hash(new);
-
- ch = sunrpc_cache_update(&rsi_cache, &new->h, &old->h, hash);
- if (ch)
- return container_of(ch, struct rsi, h);
- else
- return NULL;
-}
-
-/****************************************
- * rsc cache *
- ****************************************/
-
-#define RSC_HASHBITS (10)
-#define RSC_HASHMAX (1 << RSC_HASHBITS)
-#define RSC_HASHMASK (RSC_HASHMAX - 1)
-
-struct rsc {
- struct cache_head h;
- struct obd_device *target;
- rawobj_t handle;
- struct gss_svc_ctx ctx;
-};
-
-static struct cache_head *rsc_table[RSC_HASHMAX];
-static struct cache_detail rsc_cache;
-static struct rsc *rsc_update(struct rsc *new, struct rsc *old);
-static struct rsc *rsc_lookup(struct rsc *item);
-
-static void rsc_free(struct rsc *rsci)
-{
- rawobj_free(&rsci->handle);
- rawobj_free(&rsci->ctx.gsc_rvs_hdl);
- lgss_delete_sec_context(&rsci->ctx.gsc_mechctx);
-}
-
-static inline int rsc_hash(struct rsc *rsci)
-{
- return hash_mem((char *)rsci->handle.data,
- rsci->handle.len, RSC_HASHBITS);
-}
-
-static inline int __rsc_match(struct rsc *new, struct rsc *tmp)
-{
- return rawobj_equal(&new->handle, &tmp->handle);
-}
-
-static inline void __rsc_init(struct rsc *new, struct rsc *tmp)
-{
- new->handle = tmp->handle;
- tmp->handle = RAWOBJ_EMPTY;
-
- new->target = NULL;
- memset(&new->ctx, 0, sizeof(new->ctx));
- new->ctx.gsc_rvs_hdl = RAWOBJ_EMPTY;
-}
-
-static inline void __rsc_update(struct rsc *new, struct rsc *tmp)
-{
- new->ctx = tmp->ctx;
- tmp->ctx.gsc_rvs_hdl = RAWOBJ_EMPTY;
- tmp->ctx.gsc_mechctx = NULL;
-
- memset(&new->ctx.gsc_seqdata, 0, sizeof(new->ctx.gsc_seqdata));
- spin_lock_init(&new->ctx.gsc_seqdata.ssd_lock);
-}
-
-static void rsc_put(struct kref *ref)
-{
- struct rsc *rsci = container_of(ref, struct rsc, h.ref);
-
- LASSERT(rsci->h.next == NULL);
- rsc_free(rsci);
- OBD_FREE_PTR(rsci);
-}
-
-static int rsc_match(struct cache_head *a, struct cache_head *b)
-{
- struct rsc *new = container_of(a, struct rsc, h);
- struct rsc *tmp = container_of(b, struct rsc, h);
-
- return __rsc_match(new, tmp);
-}
-
-static void rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
-{
- struct rsc *new = container_of(cnew, struct rsc, h);
- struct rsc *tmp = container_of(ctmp, struct rsc, h);
-
- __rsc_init(new, tmp);
-}
-
-static void update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
-{
- struct rsc *new = container_of(cnew, struct rsc, h);
- struct rsc *tmp = container_of(ctmp, struct rsc, h);
-
- __rsc_update(new, tmp);
-}
-
-static struct cache_head * rsc_alloc(void)
-{
- struct rsc *rsc;
-
- OBD_ALLOC_PTR(rsc);
- if (rsc)
- return &rsc->h;
- else
- return NULL;
-}
-
-static int rsc_parse(struct cache_detail *cd, char *mesg, int mlen)
-{
- char *buf = mesg;
- int len, rv, tmp_int;
- struct rsc rsci, *rscp = NULL;
- time_t expiry;
- int status = -EINVAL;
- struct gss_api_mech *gm = NULL;
-
- memset(&rsci, 0, sizeof(rsci));
-
- /* context handle */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0) goto out;
- status = -ENOMEM;
- if (rawobj_alloc(&rsci.handle, buf, len))
- goto out;
-
- rsci.h.flags = 0;
- /* expiry */
- expiry = get_expiry(&mesg);
- status = -EINVAL;
- if (expiry == 0)
- goto out;
-
- /* remote flag */
- rv = get_int(&mesg, &tmp_int);
- if (rv) {
- CERROR("fail to get remote flag\n");
- goto out;
- }
- rsci.ctx.gsc_remote = (tmp_int != 0);
-
- /* root user flag */
- rv = get_int(&mesg, &tmp_int);
- if (rv) {
- CERROR("fail to get oss user flag\n");
- goto out;
- }
- rsci.ctx.gsc_usr_root = (tmp_int != 0);
-
- /* mds user flag */
- rv = get_int(&mesg, &tmp_int);
- if (rv) {
- CERROR("fail to get mds user flag\n");
- goto out;
- }
- rsci.ctx.gsc_usr_mds = (tmp_int != 0);
-
- /* oss user flag */
- rv = get_int(&mesg, &tmp_int);
- if (rv) {
- CERROR("fail to get oss user flag\n");
- goto out;
- }
- rsci.ctx.gsc_usr_oss = (tmp_int != 0);
-
- /* mapped uid */
- rv = get_int(&mesg, (int *) &rsci.ctx.gsc_mapped_uid);
- if (rv) {
- CERROR("fail to get mapped uid\n");
- goto out;
- }
-
- rscp = rsc_lookup(&rsci);
- if (!rscp)
- goto out;
-
- /* uid, or NEGATIVE */
- rv = get_int(&mesg, (int *) &rsci.ctx.gsc_uid);
- if (rv == -EINVAL)
- goto out;
- if (rv == -ENOENT) {
- CERROR("NOENT? set rsc entry negative\n");
- set_bit(CACHE_NEGATIVE, &rsci.h.flags);
- } else {
- rawobj_t tmp_buf;
- unsigned long ctx_expiry;
-
- /* gid */
- if (get_int(&mesg, (int *) &rsci.ctx.gsc_gid))
- goto out;
-
- /* mech name */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0)
- goto out;
- gm = lgss_name_to_mech(buf);
- status = -EOPNOTSUPP;
- if (!gm)
- goto out;
-
- status = -EINVAL;
- /* mech-specific data: */
- len = qword_get(&mesg, buf, mlen);
- if (len < 0)
- goto out;
-
- tmp_buf.len = len;
- tmp_buf.data = (unsigned char *)buf;
- if (lgss_import_sec_context(&tmp_buf, gm,
- &rsci.ctx.gsc_mechctx))
- goto out;
-
- /* currently the expiry time passed down from user-space
- * is invalid, here we retrieve it from mech. */
- if (lgss_inquire_context(rsci.ctx.gsc_mechctx, &ctx_expiry)) {
- CERROR("unable to get expire time, drop it\n");
- goto out;
- }
- expiry = (time_t) ctx_expiry;
- }
-
- rsci.h.expiry_time = expiry;
- rscp = rsc_update(&rsci, rscp);
- status = 0;
-out:
- if (gm)
- lgss_mech_put(gm);
- rsc_free(&rsci);
- if (rscp)
- cache_put(&rscp->h, &rsc_cache);
- else
- status = -ENOMEM;
-
- if (status)
- CERROR("parse rsc error %d\n", status);
- return status;
-}
-
-static struct cache_detail rsc_cache = {
- .hash_size = RSC_HASHMAX,
- .hash_table = rsc_table,
- .name = "auth.sptlrpc.context",
- .cache_put = rsc_put,
- .cache_parse = rsc_parse,
- .match = rsc_match,
- .init = rsc_init,
- .update = update_rsc,
- .alloc = rsc_alloc,
-};
-
-static struct rsc *rsc_lookup(struct rsc *item)
-{
- struct cache_head *ch;
- int hash = rsc_hash(item);
-
- ch = sunrpc_cache_lookup(&rsc_cache, &item->h, hash);
- if (ch)
- return container_of(ch, struct rsc, h);
- else
- return NULL;
-}
-
-static struct rsc *rsc_update(struct rsc *new, struct rsc *old)
-{
- struct cache_head *ch;
- int hash = rsc_hash(new);
-
- ch = sunrpc_cache_update(&rsc_cache, &new->h, &old->h, hash);
- if (ch)
- return container_of(ch, struct rsc, h);
- else
- return NULL;
-}
-
-#define COMPAT_RSC_PUT(item, cd) cache_put((item), (cd))
-
-/****************************************
- * rsc cache flush *
- ****************************************/
-
-typedef int rsc_entry_match(struct rsc *rscp, long data);
-
-static void rsc_flush(rsc_entry_match *match, long data)
-{
- struct cache_head **ch;
- struct rsc *rscp;
- int n;
-
- write_lock(&rsc_cache.hash_lock);
- for (n = 0; n < RSC_HASHMAX; n++) {
- for (ch = &rsc_cache.hash_table[n]; *ch;) {
- rscp = container_of(*ch, struct rsc, h);
-
- if (!match(rscp, data)) {
- ch = &((*ch)->next);
- continue;
- }
-
- /* it seems simply set NEGATIVE doesn't work */
- *ch = (*ch)->next;
- rscp->h.next = NULL;
- cache_get(&rscp->h);
- set_bit(CACHE_NEGATIVE, &rscp->h.flags);
- COMPAT_RSC_PUT(&rscp->h, &rsc_cache);
- rsc_cache.entries--;
- }
- }
- write_unlock(&rsc_cache.hash_lock);
-}
-
-static int match_uid(struct rsc *rscp, long uid)
-{
- if ((int) uid == -1)
- return 1;
- return ((int) rscp->ctx.gsc_uid == (int) uid);
-}
-
-static int match_target(struct rsc *rscp, long target)
-{
- return (rscp->target == (struct obd_device *) target);
-}
-
-static inline void rsc_flush_uid(int uid)
-{
- if (uid == -1)
- CWARN("flush all gss contexts...\n");
-
- rsc_flush(match_uid, (long) uid);
-}
-
-static inline void rsc_flush_target(struct obd_device *target)
-{
- rsc_flush(match_target, (long) target);
-}
-
-void gss_secsvc_flush(struct obd_device *target)
-{
- rsc_flush_target(target);
-}
-EXPORT_SYMBOL(gss_secsvc_flush);
-
-static struct rsc *gss_svc_searchbyctx(rawobj_t *handle)
-{
- struct rsc rsci;
- struct rsc *found;
-
- memset(&rsci, 0, sizeof(rsci));
- if (rawobj_dup(&rsci.handle, handle))
- return NULL;
-
- found = rsc_lookup(&rsci);
- rsc_free(&rsci);
- if (!found)
- return NULL;
- if (cache_check(&rsc_cache, &found->h, NULL))
- return NULL;
- return found;
-}
-
-int gss_svc_upcall_install_rvs_ctx(struct obd_import *imp,
- struct gss_sec *gsec,
- struct gss_cli_ctx *gctx)
-{
- struct rsc rsci, *rscp = NULL;
- unsigned long ctx_expiry;
- __u32 major;
- int rc;
-
- memset(&rsci, 0, sizeof(rsci));
-
- if (rawobj_alloc(&rsci.handle, (char *) &gsec->gs_rvs_hdl,
- sizeof(gsec->gs_rvs_hdl)))
- GOTO(out, rc = -ENOMEM);
-
- rscp = rsc_lookup(&rsci);
- if (rscp == NULL)
- GOTO(out, rc = -ENOMEM);
-
- major = lgss_copy_reverse_context(gctx->gc_mechctx,
- &rsci.ctx.gsc_mechctx);
- if (major != GSS_S_COMPLETE)
- GOTO(out, rc = -ENOMEM);
-
- if (lgss_inquire_context(rsci.ctx.gsc_mechctx, &ctx_expiry)) {
- CERROR("unable to get expire time, drop it\n");
- GOTO(out, rc = -EINVAL);
- }
- rsci.h.expiry_time = (time_t) ctx_expiry;
-
- if (strcmp(imp->imp_obd->obd_type->typ_name, LUSTRE_MDC_NAME) == 0)
- rsci.ctx.gsc_usr_mds = 1;
- else if (strcmp(imp->imp_obd->obd_type->typ_name, LUSTRE_OSC_NAME) == 0)
- rsci.ctx.gsc_usr_oss = 1;
- else
- rsci.ctx.gsc_usr_root = 1;
-
- rscp = rsc_update(&rsci, rscp);
- if (rscp == NULL)
- GOTO(out, rc = -ENOMEM);
-
- rscp->target = imp->imp_obd;
- rawobj_dup(&gctx->gc_svc_handle, &rscp->handle);
-
- CWARN("create reverse svc ctx %p to %s: idx "LPX64"\n",
- &rscp->ctx, obd2cli_tgt(imp->imp_obd), gsec->gs_rvs_hdl);
- rc = 0;
-out:
- if (rscp)
- cache_put(&rscp->h, &rsc_cache);
- rsc_free(&rsci);
-
- if (rc)
- CERROR("create reverse svc ctx: idx "LPX64", rc %d\n",
- gsec->gs_rvs_hdl, rc);
- return rc;
-}
-
-int gss_svc_upcall_expire_rvs_ctx(rawobj_t *handle)
-{
- const cfs_time_t expire = 20;
- struct rsc *rscp;
-
- rscp = gss_svc_searchbyctx(handle);
- if (rscp) {
- CDEBUG(D_SEC, "reverse svcctx %p (rsc %p) expire soon\n",
- &rscp->ctx, rscp);
-
- rscp->h.expiry_time = cfs_time_current_sec() + expire;
- COMPAT_RSC_PUT(&rscp->h, &rsc_cache);
- }
- return 0;
-}
-
-int gss_svc_upcall_dup_handle(rawobj_t *handle, struct gss_svc_ctx *ctx)
-{
- struct rsc *rscp = container_of(ctx, struct rsc, ctx);
-
- return rawobj_dup(handle, &rscp->handle);
-}
-
-int gss_svc_upcall_update_sequence(rawobj_t *handle, __u32 seq)
-{
- struct rsc *rscp;
-
- rscp = gss_svc_searchbyctx(handle);
- if (rscp) {
- CDEBUG(D_SEC, "reverse svcctx %p (rsc %p) update seq to %u\n",
- &rscp->ctx, rscp, seq + 1);
-
- rscp->ctx.gsc_rvs_seq = seq + 1;
- COMPAT_RSC_PUT(&rscp->h, &rsc_cache);
- }
- return 0;
-}
-
-static struct cache_deferred_req* cache_upcall_defer(struct cache_req *req)
-{
- return NULL;
-}
-static struct cache_req cache_upcall_chandle = { cache_upcall_defer };
-
-int gss_svc_upcall_handle_init(struct ptlrpc_request *req,
- struct gss_svc_reqctx *grctx,
- struct gss_wire_ctx *gw,
- struct obd_device *target,
- __u32 lustre_svc,
- rawobj_t *rvs_hdl,
- rawobj_t *in_token)
-{
- struct ptlrpc_reply_state *rs;
- struct rsc *rsci = NULL;
- struct rsi *rsip = NULL, rsikey;
- wait_queue_t wait;
- int replen = sizeof(struct ptlrpc_body);
- struct gss_rep_header *rephdr;
- int first_check = 1;
- int rc = SECSVC_DROP;
-
- memset(&rsikey, 0, sizeof(rsikey));
- rsikey.lustre_svc = lustre_svc;
- rsikey.nid = (__u64) req->rq_peer.nid;
-
- /* duplicate context handle. for INIT it always 0 */
- if (rawobj_dup(&rsikey.in_handle, &gw->gw_handle)) {
- CERROR("fail to dup context handle\n");
- GOTO(out, rc);
- }
-
- if (rawobj_dup(&rsikey.in_token, in_token)) {
- CERROR("can't duplicate token\n");
- rawobj_free(&rsikey.in_handle);
- GOTO(out, rc);
- }
-
- rsip = rsi_lookup(&rsikey);
- rsi_free(&rsikey);
- if (!rsip) {
- CERROR("error in rsi_lookup.\n");
-
- if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
- rc = SECSVC_COMPLETE;
-
- GOTO(out, rc);
- }
-
- cache_get(&rsip->h); /* take an extra ref */
- init_waitqueue_head(&rsip->waitq);
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&rsip->waitq, &wait);
-
-cache_check:
- /* Note each time cache_check() will drop a reference if return
- * non-zero. We hold an extra reference on initial rsip, but must
- * take care of following calls. */
- rc = cache_check(&rsi_cache, &rsip->h, &cache_upcall_chandle);
- switch (rc) {
- case -EAGAIN: {
- int valid;
-
- if (first_check) {
- first_check = 0;
-
- read_lock(&rsi_cache.hash_lock);
- valid = test_bit(CACHE_VALID, &rsip->h.flags);
- if (valid == 0)
- set_current_state(TASK_INTERRUPTIBLE);
- read_unlock(&rsi_cache.hash_lock);
-
- if (valid == 0)
- schedule_timeout(GSS_SVC_UPCALL_TIMEOUT *
- HZ);
-
- cache_get(&rsip->h);
- goto cache_check;
- }
- CWARN("waited %ds timeout, drop\n", GSS_SVC_UPCALL_TIMEOUT);
- break;
- }
- case -ENOENT:
- CWARN("cache_check return ENOENT, drop\n");
- break;
- case 0:
- /* if not the first check, we have to release the extra
- * reference we just added on it. */
- if (!first_check)
- cache_put(&rsip->h, &rsi_cache);
- CDEBUG(D_SEC, "cache_check is good\n");
- break;
- }
-
- remove_wait_queue(&rsip->waitq, &wait);
- cache_put(&rsip->h, &rsi_cache);
-
- if (rc)
- GOTO(out, rc = SECSVC_DROP);
-
- rc = SECSVC_DROP;
- rsci = gss_svc_searchbyctx(&rsip->out_handle);
- if (!rsci) {
- CERROR("authentication failed\n");
-
- if (!gss_pack_err_notify(req, GSS_S_FAILURE, 0))
- rc = SECSVC_COMPLETE;
-
- GOTO(out, rc);
- } else {
- cache_get(&rsci->h);
- grctx->src_ctx = &rsci->ctx;
- }
-
- if (rawobj_dup(&rsci->ctx.gsc_rvs_hdl, rvs_hdl)) {
- CERROR("failed duplicate reverse handle\n");
- GOTO(out, rc);
- }
-
- rsci->target = target;
-
- CDEBUG(D_SEC, "server create rsc %p(%u->%s)\n",
- rsci, rsci->ctx.gsc_uid, libcfs_nid2str(req->rq_peer.nid));
-
- if (rsip->out_handle.len > PTLRPC_GSS_MAX_HANDLE_SIZE) {
- CERROR("handle size %u too large\n", rsip->out_handle.len);
- GOTO(out, rc = SECSVC_DROP);
- }
-
- grctx->src_init = 1;
- grctx->src_reserve_len = cfs_size_round4(rsip->out_token.len);
-
- rc = lustre_pack_reply_v2(req, 1, &replen, NULL, 0);
- if (rc) {
- CERROR("failed to pack reply: %d\n", rc);
- GOTO(out, rc = SECSVC_DROP);
- }
-
- rs = req->rq_reply_state;
- LASSERT(rs->rs_repbuf->lm_bufcount == 3);
- LASSERT(rs->rs_repbuf->lm_buflens[0] >=
- sizeof(*rephdr) + rsip->out_handle.len);
- LASSERT(rs->rs_repbuf->lm_buflens[2] >= rsip->out_token.len);
-
- rephdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
- rephdr->gh_version = PTLRPC_GSS_VERSION;
- rephdr->gh_flags = 0;
- rephdr->gh_proc = PTLRPC_GSS_PROC_ERR;
- rephdr->gh_major = rsip->major_status;
- rephdr->gh_minor = rsip->minor_status;
- rephdr->gh_seqwin = GSS_SEQ_WIN;
- rephdr->gh_handle.len = rsip->out_handle.len;
- memcpy(rephdr->gh_handle.data, rsip->out_handle.data,
- rsip->out_handle.len);
-
- memcpy(lustre_msg_buf(rs->rs_repbuf, 2, 0), rsip->out_token.data,
- rsip->out_token.len);
-
- rs->rs_repdata_len = lustre_shrink_msg(rs->rs_repbuf, 2,
- rsip->out_token.len, 0);
-
- rc = SECSVC_OK;
-
-out:
- /* it looks like here we should put rsip also, but this mess up
- * with NFS cache mgmt code... FIXME */
-#if 0
- if (rsip)
- rsi_put(&rsip->h, &rsi_cache);
-#endif
-
- if (rsci) {
- /* if anything went wrong, we don't keep the context too */
- if (rc != SECSVC_OK)
- set_bit(CACHE_NEGATIVE, &rsci->h.flags);
- else
- CDEBUG(D_SEC, "create rsc with idx "LPX64"\n",
- gss_handle_to_u64(&rsci->handle));
-
- COMPAT_RSC_PUT(&rsci->h, &rsc_cache);
- }
- return rc;
-}
-
-struct gss_svc_ctx *gss_svc_upcall_get_ctx(struct ptlrpc_request *req,
- struct gss_wire_ctx *gw)
-{
- struct rsc *rsc;
-
- rsc = gss_svc_searchbyctx(&gw->gw_handle);
- if (!rsc) {
- CWARN("Invalid gss ctx idx "LPX64" from %s\n",
- gss_handle_to_u64(&gw->gw_handle),
- libcfs_nid2str(req->rq_peer.nid));
- return NULL;
- }
-
- return &rsc->ctx;
-}
-
-void gss_svc_upcall_put_ctx(struct gss_svc_ctx *ctx)
-{
- struct rsc *rsc = container_of(ctx, struct rsc, ctx);
-
- COMPAT_RSC_PUT(&rsc->h, &rsc_cache);
-}
-
-void gss_svc_upcall_destroy_ctx(struct gss_svc_ctx *ctx)
-{
- struct rsc *rsc = container_of(ctx, struct rsc, ctx);
-
- /* can't be found */
- set_bit(CACHE_NEGATIVE, &rsc->h.flags);
- /* to be removed at next scan */
- rsc->h.expiry_time = 1;
-}
-
-int __init gss_init_svc_upcall(void)
-{
- int i;
-
- spin_lock_init(&__ctx_index_lock);
- /*
- * this helps reducing context index confliction. after server reboot,
- * conflicting request from clients might be filtered out by initial
- * sequence number checking, thus no chance to sent error notification
- * back to clients.
- */
- cfs_get_random_bytes(&__ctx_index, sizeof(__ctx_index));
-
-
- cache_register(&rsi_cache);
- cache_register(&rsc_cache);
-
- /* FIXME this looks stupid. we intend to give lsvcgssd a chance to open
- * the init upcall channel, otherwise there's big chance that the first
- * upcall issued before the channel be opened thus nfsv4 cache code will
- * drop the request direclty, thus lead to unnecessary recovery time.
- * here we wait at maximum 1.5 seconds. */
- for (i = 0; i < 6; i++) {
- if (atomic_read(&rsi_cache.readers) > 0)
- break;
- set_current_state(TASK_UNINTERRUPTIBLE);
- LASSERT(HZ >= 4);
- schedule_timeout(HZ / 4);
- }
-
- if (atomic_read(&rsi_cache.readers) == 0)
- CWARN("Init channel is not opened by lsvcgssd, following "
- "request might be dropped until lsvcgssd is active\n");
-
- return 0;
-}
-
-void __exit gss_exit_svc_upcall(void)
-{
- cache_purge(&rsi_cache);
- cache_unregister(&rsi_cache);
-
- cache_purge(&rsc_cache);
- cache_unregister(&rsc_cache);
-}
+++ /dev/null
-/*
- * GPL HEADER START
- *
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 only,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License version 2 for more details (a copy is included
- * in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU General Public License
- * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- *
- * GPL HEADER END
- */
-/*
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- * Use is subject to license terms.
- *
- * Copyright (c) 2012, Intel Corporation.
- */
-/*
- * This file is part of Lustre, http://www.lustre.org/
- * Lustre is a trademark of Sun Microsystems, Inc.
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lprocfs_status.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-static struct proc_dir_entry *gss_proc_root = NULL;
-static struct proc_dir_entry *gss_proc_lk = NULL;
-
-/*
- * statistic of "out-of-sequence-window"
- */
-static struct {
- spinlock_t oos_lock;
- atomic_t oos_cli_count; /* client occurrence */
- int oos_cli_behind; /* client max seqs behind */
- atomic_t oos_svc_replay[3]; /* server replay detected */
- atomic_t oos_svc_pass[3]; /* server verified ok */
-} gss_stat_oos = {
- .oos_cli_count = ATOMIC_INIT(0),
- .oos_cli_behind = 0,
- .oos_svc_replay = { ATOMIC_INIT(0), },
- .oos_svc_pass = { ATOMIC_INIT(0), },
-};
-
-void gss_stat_oos_record_cli(int behind)
-{
- atomic_inc(&gss_stat_oos.oos_cli_count);
-
- spin_lock(&gss_stat_oos.oos_lock);
- if (behind > gss_stat_oos.oos_cli_behind)
- gss_stat_oos.oos_cli_behind = behind;
- spin_unlock(&gss_stat_oos.oos_lock);
-}
-
-void gss_stat_oos_record_svc(int phase, int replay)
-{
- LASSERT(phase >= 0 && phase <= 2);
-
- if (replay)
- atomic_inc(&gss_stat_oos.oos_svc_replay[phase]);
- else
- atomic_inc(&gss_stat_oos.oos_svc_pass[phase]);
-}
-
-static int gss_proc_oos_seq_show(struct seq_file *m, void *v)
-{
- return seq_printf(m,
- "seqwin: %u\n"
- "backwin: %u\n"
- "client fall behind seqwin\n"
- " occurrence: %d\n"
- " max seq behind: %d\n"
- "server replay detected:\n"
- " phase 0: %d\n"
- " phase 1: %d\n"
- " phase 2: %d\n"
- "server verify ok:\n"
- " phase 2: %d\n",
- GSS_SEQ_WIN_MAIN,
- GSS_SEQ_WIN_BACK,
- atomic_read(&gss_stat_oos.oos_cli_count),
- gss_stat_oos.oos_cli_behind,
- atomic_read(&gss_stat_oos.oos_svc_replay[0]),
- atomic_read(&gss_stat_oos.oos_svc_replay[1]),
- atomic_read(&gss_stat_oos.oos_svc_replay[2]),
- atomic_read(&gss_stat_oos.oos_svc_pass[2]));
-}
-LPROC_SEQ_FOPS_RO(gss_proc_oos);
-
-static int gss_proc_write_secinit(struct file *file, const char *buffer,
- size_t count, off_t *off)
-{
- int rc;
-
- rc = gss_do_ctx_init_rpc((char *) buffer, count);
- if (rc) {
- LASSERT(rc < 0);
- return rc;
- }
-
- return count;
-}
-
-static const struct file_operations gss_proc_secinit = {
- .write = gss_proc_write_secinit,
-};
-
-static struct lprocfs_vars gss_lprocfs_vars[] = {
- { "replays", &gss_proc_oos_fops },
- { "init_channel", &gss_proc_secinit, NULL, 0222 },
- { NULL }
-};
-
-/*
- * for userspace helper lgss_keyring.
- *
- * debug_level: [0, 4], defined in utils/gss/lgss_utils.h
- */
-static int gss_lk_debug_level = 1;
-
-static int gss_lk_proc_dl_seq_show(struct seq_file *m, void *v)
-{
- return seq_printf(m, "%u\n", gss_lk_debug_level);
-}
-
-static int gss_lk_proc_dl_seq_write(struct file *file, const char *buffer,
- size_t count, off_t *off)
-{
- int val, rc;
-
- rc = lprocfs_write_helper(buffer, count, &val);
- if (rc < 0)
- return rc;
-
- if (val < 0 || val > 4)
- return -ERANGE;
-
- gss_lk_debug_level = val;
- return count;
-}
-LPROC_SEQ_FOPS(gss_lk_proc_dl);
-
-static struct lprocfs_vars gss_lk_lprocfs_vars[] = {
- { "debug_level", &gss_lk_proc_dl_fops },
- { NULL }
-};
-
-void gss_exit_lproc(void)
-{
- if (gss_proc_lk) {
- lprocfs_remove(&gss_proc_lk);
- gss_proc_lk = NULL;
- }
-
- if (gss_proc_root) {
- lprocfs_remove(&gss_proc_root);
- gss_proc_root = NULL;
- }
-}
-
-int gss_init_lproc(void)
-{
- int rc;
-
- spin_lock_init(&gss_stat_oos.oos_lock);
-
- gss_proc_root = lprocfs_register("gss", sptlrpc_proc_root,
- gss_lprocfs_vars, NULL);
- if (IS_ERR(gss_proc_root)) {
- rc = PTR_ERR(gss_proc_root);
- gss_proc_root = NULL;
- GOTO(err_out, rc);
- }
-
- gss_proc_lk = lprocfs_register("lgss_keyring", gss_proc_root,
- gss_lk_lprocfs_vars, NULL);
- if (IS_ERR(gss_proc_lk)) {
- rc = PTR_ERR(gss_proc_lk);
- gss_proc_lk = NULL;
- GOTO(err_out, rc);
- }
-
- return 0;
-
-err_out:
- CERROR("failed to initialize gss lproc entries: %d\n", rc);
- gss_exit_lproc();
- return rc;
-}
+++ /dev/null
-/*
- * Modifications for Lustre
- *
- * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
- *
- * Copyright (c) 2011, 2012, Intel Corporation.
- *
- * Author: Eric Mei <ericm@clusterfs.com>
- */
-
-/*
- * linux/net/sunrpc/auth_gss.c
- *
- * RPCSEC_GSS client authentication.
- *
- * Copyright (c) 2000 The Regents of the University of Michigan.
- * All rights reserved.
- *
- * Dug Song <dugsong@monkey.org>
- * Andy Adamson <andros@umich.edu>
- *
- * 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
- * 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
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
-
-#define DEBUG_SUBSYSTEM S_SEC
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/dcache.h>
-#include <linux/fs.h>
-#include <linux/mutex.h>
-#include <asm/atomic.h>
-
-#include <obd.h>
-#include <obd_class.h>
-#include <obd_support.h>
-#include <obd_cksum.h>
-#include <lustre/lustre_idl.h>
-#include <lustre_net.h>
-#include <lustre_import.h>
-#include <lustre_sec.h>
-
-#include "gss_err.h"
-#include "gss_internal.h"
-#include "gss_api.h"
-
-#include <linux/crypto.h>
-#include <linux/crc32.h>
-
-/*
- * early reply have fixed size, respectively in privacy and integrity mode.
- * so we calculate them only once.
- */
-static int gss_at_reply_off_integ;
-static int gss_at_reply_off_priv;
-
-
-static inline int msg_last_segidx(struct lustre_msg *msg)
-{
- LASSERT(msg->lm_bufcount > 0);
- return msg->lm_bufcount - 1;
-}
-static inline int msg_last_seglen(struct lustre_msg *msg)
-{
- return msg->lm_buflens[msg_last_segidx(msg)];
-}
-
-/********************************************
- * wire data swabber *
- ********************************************/
-
-static
-void gss_header_swabber(struct gss_header *ghdr)
-{
- __swab32s(&ghdr->gh_flags);
- __swab32s(&ghdr->gh_proc);
- __swab32s(&ghdr->gh_seq);
- __swab32s(&ghdr->gh_svc);
- __swab32s(&ghdr->gh_pad1);
- __swab32s(&ghdr->gh_handle.len);
-}
-
-struct gss_header *gss_swab_header(struct lustre_msg *msg, int segment,
- int swabbed)
-{
- struct gss_header *ghdr;
-
- ghdr = lustre_msg_buf(msg, segment, sizeof(*ghdr));
- if (ghdr == NULL)
- return NULL;
-
- if (swabbed)
- gss_header_swabber(ghdr);
-
- if (sizeof(*ghdr) + ghdr->gh_handle.len > msg->lm_buflens[segment]) {
- CERROR("gss header has length %d, now %u received\n",
- (int) sizeof(*ghdr) + ghdr->gh_handle.len,
- msg->lm_buflens[segment]);
- return NULL;
- }
-
- return ghdr;
-}
-
-#if 0
-static
-void gss_netobj_swabber(netobj_t *obj)
-{
- __swab32s(&obj->len);
-}
-
-netobj_t *gss_swab_netobj(struct lustre_msg *msg, int segment)
-{
- netobj_t *obj;
-
- obj = lustre_swab_buf(msg, segment, sizeof(*obj), gss_netobj_swabber);
- if (obj && sizeof(*obj) + obj->len > msg->lm_buflens[segment]) {
- CERROR("netobj require length %u but only %u received\n",
- (unsigned int) sizeof(*obj) + obj->len,
- msg->lm_buflens[segment]);
- return NULL;
- }
-
- return obj;
-}
-#endif
-
-/*
- * payload should be obtained from mechanism. but currently since we
- * only support kerberos, we could simply use fixed value.
- * krb5 "meta" data:
- * - krb5 header: 16
- * - krb5 checksum: 20
- *
- * for privacy mode, payload also include the cipher text which has the same
- * size as plain text, plus possible confounder, padding both at maximum cipher
- * block size.
- */
-#define GSS_KRB5_INTEG_MAX_PAYLOAD (40)
-
-static inline
-int gss_mech_payload(struct gss_ctx *mechctx, int msgsize, int privacy)
-{
- if (privacy)
- return GSS_KRB5_INTEG_MAX_PAYLOAD + 16 + 16 + 16 + msgsize;
- else
- return GSS_KRB5_INTEG_MAX_PAYLOAD;
-}
-
-/*
- * return signature size, otherwise < 0 to indicate error
- */
-static int gss_sign_msg(struct lustre_msg *msg,
- struct gss_ctx *mechctx,
- enum lustre_sec_part sp,
- __u32 flags, __u32 proc, __u32 seq, __u32 svc,
- rawobj_t *handle)
-{
- struct gss_header *ghdr;
- rawobj_t text[4], mic;
- int textcnt, max_textcnt, mic_idx;
- __u32 major;
-
- LASSERT(msg->lm_bufcount >= 2);
-
- /* gss hdr */
- LASSERT(msg->lm_buflens[0] >=
- sizeof(*ghdr) + (handle ? handle->len : 0));
- ghdr = lustre_msg_buf(msg, 0, 0);
-
- ghdr->gh_version = PTLRPC_GSS_VERSION;
- ghdr->gh_sp = (__u8) sp;
- ghdr->gh_flags = flags;
- ghdr->gh_proc = proc;
- ghdr->gh_seq = seq;
- ghdr->gh_svc = svc;
- if (!handle) {
- /* fill in a fake one */
- ghdr->gh_handle.len = 0;
- } else {
- ghdr->gh_handle.len = handle->len;
- memcpy(ghdr->gh_handle.data, handle->data, handle->len);
- }
-
- /* no actual signature for null mode */
- if (svc == SPTLRPC_SVC_NULL)
- return lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
-
- /* MIC */
- mic_idx = msg_last_segidx(msg);
- max_textcnt = (svc == SPTLRPC_SVC_AUTH) ? 1 : mic_idx;
-
- for (textcnt = 0; textcnt < max_textcnt; textcnt++) {
- text[textcnt].len = msg->lm_buflens[textcnt];
- text[textcnt].data = lustre_msg_buf(msg, textcnt, 0);
- }
-
- mic.len = msg->lm_buflens[mic_idx];
- mic.data = lustre_msg_buf(msg, mic_idx, 0);
-
- major = lgss_get_mic(mechctx, textcnt, text, 0, NULL, &mic);
- if (major != GSS_S_COMPLETE) {
- CERROR("fail to generate MIC: %08x\n", major);
- return -EPERM;
- }
- LASSERT(mic.len <= msg->lm_buflens[mic_idx]);
-
- return lustre_shrink_msg(msg, mic_idx, mic.len, 0);
-}
-
-/*
- * return gss error
- */
-static
-__u32 gss_verify_msg(struct lustre_msg *msg,
- struct gss_ctx *mechctx,
- __u32 svc)
-{
- rawobj_t text[4], mic;
- int textcnt, max_textcnt;
- int mic_idx;
- __u32 major;
-
- LASSERT(msg->lm_bufcount >= 2);
-
- if (svc == SPTLRPC_SVC_NULL)
- return GSS_S_COMPLETE;
-
- mic_idx = msg_last_segidx(msg);
- max_textcnt = (svc == SPTLRPC_SVC_AUTH) ? 1 : mic_idx;
-
- for (textcnt = 0; textcnt < max_textcnt; textcnt++) {
- text[textcnt].len = msg->lm_buflens[textcnt];
- text[textcnt].data = lustre_msg_buf(msg, textcnt, 0);
- }
-
- mic.len = msg->lm_buflens[mic_idx];
- mic.data = lustre_msg_buf(msg, mic_idx, 0);
-
- major = lgss_verify_mic(mechctx, textcnt, text, 0, NULL, &mic);
- if (major != GSS_S_COMPLETE)
- CERROR("mic verify error: %08x\n", major);
-
- return major;
-}
-
-/*
- * return gss error code
- */
-static
-__u32 gss_unseal_msg(struct gss_ctx *mechctx,
- struct lustre_msg *msgbuf,
- int *msg_len, int msgbuf_len)
-{
- rawobj_t clear_obj, hdrobj, token;
- __u8 *clear_buf;
- int clear_buflen;
- __u32 major;
-
- if (msgbuf->lm_bufcount != 2) {
- CERROR("invalid bufcount %d\n", msgbuf->lm_bufcount);
- return GSS_S_FAILURE;
- }
-
- /* allocate a temporary clear text buffer, same sized as token,
- * we assume the final clear text size <= token size */
- clear_buflen = lustre_msg_buflen(msgbuf, 1);
- OBD_ALLOC_LARGE(clear_buf, clear_buflen);
- if (!clear_buf)
- return GSS_S_FAILURE;
-
- /* buffer objects */
- hdrobj.len = lustre_msg_buflen(msgbuf, 0);
- hdrobj.data = lustre_msg_buf(msgbuf, 0, 0);
- token.len = lustre_msg_buflen(msgbuf, 1);
- token.data = lustre_msg_buf(msgbuf, 1, 0);
- clear_obj.len = clear_buflen;
- clear_obj.data = clear_buf;
-
- major = lgss_unwrap(mechctx, &hdrobj, &token, &clear_obj);
- if (major != GSS_S_COMPLETE) {
- CERROR("unwrap message error: %08x\n", major);
- GOTO(out_free, major = GSS_S_FAILURE);
- }
- LASSERT(clear_obj.len <= clear_buflen);
- LASSERT(clear_obj.len <= msgbuf_len);
-
- /* now the decrypted message */
- memcpy(msgbuf, clear_obj.data, clear_obj.len);
- *msg_len = clear_obj.len;
-
- major = GSS_S_COMPLETE;
-out_free:
- OBD_FREE_LARGE(clear_buf, clear_buflen);
- return major;
-}
-
-/********************************************
- * gss client context manipulation helpers *
- ********************************************/
-
-int cli_ctx_expire(struct ptlrpc_cli_ctx *ctx)
-{
- LASSERT(atomic_read(&ctx->cc_refcount));
-
- if (!test_and_set_bit(PTLRPC_CTX_DEAD_BIT, &ctx->cc_flags)) {
- if (!ctx->cc_early_expire)
- clear_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
-
- CWARN("ctx %p(%u->%s) get expired: %lu(%+lds)\n",
- ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
- ctx->cc_expire,
- ctx->cc_expire == 0 ? 0 :
- cfs_time_sub(ctx->cc_expire, cfs_time_current_sec()));
-
- sptlrpc_cli_ctx_wakeup(ctx);
- return 1;
- }
-
- return 0;
-}
-
-/*
- * return 1 if the context is dead.
- */
-int cli_ctx_check_death(struct ptlrpc_cli_ctx *ctx)
-{
- if (unlikely(cli_ctx_is_dead(ctx)))
- return 1;
-
- /* expire is 0 means never expire. a newly created gss context
- * which during upcall may has 0 expiration */
- if (ctx->cc_expire == 0)
- return 0;
-
- /* check real expiration */
- if (cfs_time_after(ctx->cc_expire, cfs_time_current_sec()))
- return 0;
-
- cli_ctx_expire(ctx);
- return 1;
-}
-
-void gss_cli_ctx_uptodate(struct gss_cli_ctx *gctx)
-{
- struct ptlrpc_cli_ctx *ctx = &gctx->gc_base;
- unsigned long ctx_expiry;
-
- if (lgss_inquire_context(gctx->gc_mechctx, &ctx_expiry)) {
- CERROR("ctx %p(%u): unable to inquire, expire it now\n",
- gctx, ctx->cc_vcred.vc_uid);
- ctx_expiry = 1; /* make it expired now */
- }
-
- ctx->cc_expire = gss_round_ctx_expiry(ctx_expiry,
- ctx->cc_sec->ps_flvr.sf_flags);
-
- /* At this point this ctx might have been marked as dead by
- * someone else, in which case nobody will make further use
- * of it. we don't care, and mark it UPTODATE will help
- * destroying server side context when it be destroyed. */
- set_bit(PTLRPC_CTX_UPTODATE_BIT, &ctx->cc_flags);
-
- if (sec_is_reverse(ctx->cc_sec)) {
- CWARN("server installed reverse ctx %p idx "LPX64", "
- "expiry %lu(%+lds)\n", ctx,
- gss_handle_to_u64(&gctx->gc_handle),
- ctx->cc_expire, ctx->cc_expire - cfs_time_current_sec());
- } else {
- CWARN("client refreshed ctx %p idx "LPX64" (%u->%s), "
- "expiry %lu(%+lds)\n", ctx,
- gss_handle_to_u64(&gctx->gc_handle),
- ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
- ctx->cc_expire, ctx->cc_expire - cfs_time_current_sec());
-
- /* install reverse svc ctx for root context */
- if (ctx->cc_vcred.vc_uid == 0)
- gss_sec_install_rctx(ctx->cc_sec->ps_import,
- ctx->cc_sec, ctx);
- }
-
- sptlrpc_cli_ctx_wakeup(ctx);
-}
-
-static void gss_cli_ctx_finalize(struct gss_cli_ctx *gctx)
-{
- LASSERT(gctx->gc_base.cc_sec);
-
- if (gctx->gc_mechctx) {
- lgss_delete_sec_context(&gctx->gc_mechctx);
- gctx->gc_mechctx = NULL;
- }
-
- if (!rawobj_empty(&gctx->gc_svc_handle)) {
- /* forward ctx: mark buddy reverse svcctx soon-expire. */
- if (!sec_is_reverse(gctx->gc_base.cc_sec) &&
- !rawobj_empty(&gctx->gc_svc_handle))
- gss_svc_upcall_expire_rvs_ctx(&gctx->gc_svc_handle);
-
- rawobj_free(&gctx->gc_svc_handle);
- }
-
- rawobj_free(&gctx->gc_handle);
-}
-
-/*
- * Based on sequence number algorithm as specified in RFC 2203.
- *
- * modified for our own problem: arriving request has valid sequence number,
- * but unwrapping request might cost a long time, after that its sequence
- * are not valid anymore (fall behind the window). It rarely happen, mostly
- * under extreme load.
- *
- * note we should not check sequence before verify the integrity of incoming
- * request, because just one attacking request with high sequence number might
- * cause all following request be dropped.
- *
- * so here we use a multi-phase approach: prepare 2 sequence windows,
- * "main window" for normal sequence and "back window" for fall behind sequence.
- * and 3-phase checking mechanism:
- * 0 - before integrity verification, perform a initial sequence checking in
- * main window, which only try and don't actually set any bits. if the
- * sequence is high above the window or fit in the window and the bit
- * is 0, then accept and proceed to integrity verification. otherwise
- * reject this sequence.
- * 1 - after integrity verification, check in main window again. if this
- * sequence is high above the window or fit in the window and the bit
- * is 0, then set the bit and accept; if it fit in the window but bit
- * already set, then reject; if it fall behind the window, then proceed
- * to phase 2.
- * 2 - check in back window. if it is high above the window or fit in the
- * window and the bit is 0, then set the bit and accept. otherwise reject.
- *
- * return value:
- * 1: looks like a replay
- * 0: is ok
- * -1: is a replay
- *
- * note phase 0 is necessary, because otherwise replay attacking request of
- * sequence which between the 2 windows can't be detected.
- *
- * this mechanism can't totally solve the problem, but could help much less
- * number of valid requests be dropped.
- */
-static
-int gss_do_check_seq(unsigned long *window, __u32 win_size, __u32 *max_seq,
- __u32 seq_num, int phase)
-{
- LASSERT(phase >= 0 && phase <= 2);
-
- if (seq_num > *max_seq) {
- /*
- * 1. high above the window
- */
- if (phase == 0)
- return 0;
-
- if (seq_num >= *max_seq + win_size) {
- memset(window, 0, win_size / 8);
- *max_seq = seq_num;
- } else {
- while (*max_seq < seq_num) {
- (*max_seq)++;
- __clear_bit((*max_seq) % win_size, window);
- }
- }
- __set_bit(seq_num % win_size, window);
- } else if (seq_num + win_size <= *max_seq) {
- /*
- * 2. low behind the window
- */
- if (phase == 0 || phase == 2)
- goto replay;
-
- CWARN("seq %u is %u behind (size %d), check backup window\n",
- seq_num, *max_seq - win_size - seq_num, win_size);
- return 1;
- } else {
- /*
- * 3. fit into the window
- */
- switch (phase) {
- case 0:
- if (test_bit(seq_num % win_size, window))
- goto replay;
- break;
- case 1:
- case 2:
- if (__test_and_set_bit(seq_num % win_size, window))
- goto replay;
- break;
- }
- }
-
- return 0;
-
-replay:
- CERROR("seq %u (%s %s window) is a replay: max %u, winsize %d\n",
- seq_num,
- seq_num + win_size > *max_seq ? "in" : "behind",
- phase == 2 ? "backup " : "main",
- *max_seq, win_size);
- return -1;
-}
-
-/*
- * Based on sequence number algorithm as specified in RFC 2203.
- *
- * if @set == 0: initial check, don't set any bit in window
- * if @sec == 1: final check, set bit in window
- */
-int gss_check_seq_num(struct gss_svc_seq_data *ssd, __u32 seq_num, int set)
-{
- int rc = 0;
-
- spin_lock(&ssd->ssd_lock);
-
- if (set == 0) {
- /*
- * phase 0 testing
- */
- rc = gss_do_check_seq(ssd->ssd_win_main, GSS_SEQ_WIN_MAIN,
- &ssd->ssd_max_main, seq_num, 0);
- if (unlikely(rc))
- gss_stat_oos_record_svc(0, 1);
- } else {
- /*
- * phase 1 checking main window
- */
- rc = gss_do_check_seq(ssd->ssd_win_main, GSS_SEQ_WIN_MAIN,
- &ssd->ssd_max_main, seq_num, 1);
- switch (rc) {
- case -1:
- gss_stat_oos_record_svc(1, 1);
- /* fall through */
- case 0:
- goto exit;
- }
- /*
- * phase 2 checking back window
- */
- rc = gss_do_check_seq(ssd->ssd_win_back, GSS_SEQ_WIN_BACK,
- &ssd->ssd_max_back, seq_num, 2);
- if (rc)
- gss_stat_oos_record_svc(2, 1);
- else
- gss_stat_oos_record_svc(2, 0);
- }
-exit:
- spin_unlock(&ssd->ssd_lock);
- return rc;
-}
-
-/***************************************
- * cred APIs *
- ***************************************/
-
-static inline int gss_cli_payload(struct ptlrpc_cli_ctx *ctx,
- int msgsize, int privacy)
-{
- return gss_mech_payload(NULL, msgsize, privacy);
-}
-
-static int gss_cli_bulk_payload(struct ptlrpc_cli_ctx *ctx,
- struct sptlrpc_flavor *flvr,
- int reply, int read)
-{
- int payload = sizeof(struct ptlrpc_bulk_sec_desc);
-
- LASSERT(SPTLRPC_FLVR_BULK_TYPE(flvr->sf_rpc) == SPTLRPC_BULK_DEFAULT);
-
- if ((!reply && !read) || (reply && read)) {
- switch (SPTLRPC_FLVR_BULK_SVC(flvr->sf_rpc)) {
- case SPTLRPC_BULK_SVC_NULL:
- break;
- case SPTLRPC_BULK_SVC_INTG:
- payload += gss_cli_payload(ctx, 0, 0);
- break;
- case SPTLRPC_BULK_SVC_PRIV:
- payload += gss_cli_payload(ctx, 0, 1);
- break;
- case SPTLRPC_BULK_SVC_AUTH:
- default:
- LBUG();
- }
- }
-
- return payload;
-}
-
-int gss_cli_ctx_match(struct ptlrpc_cli_ctx *ctx, struct vfs_cred *vcred)
-{
- return (ctx->cc_vcred.vc_uid == vcred->vc_uid);
-}
-
-void gss_cli_ctx_flags2str(unsigned long flags, char *buf, int bufsize)
-{
- buf[0] = '\0';
-
- if (flags & PTLRPC_CTX_NEW)
- strncat(buf, "new,", bufsize);
- if (flags & PTLRPC_CTX_UPTODATE)
- strncat(buf, "uptodate,", bufsize);
- if (flags & PTLRPC_CTX_DEAD)
- strncat(buf, "dead,", bufsize);
- if (flags & PTLRPC_CTX_ERROR)
- strncat(buf, "error,", bufsize);
- if (flags & PTLRPC_CTX_CACHED)
- strncat(buf, "cached,", bufsize);
- if (flags & PTLRPC_CTX_ETERNAL)
- strncat(buf, "eternal,", bufsize);
- if (buf[0] == '\0')
- strncat(buf, "-,", bufsize);
-
- buf[strlen(buf) - 1] = '\0';
-}
-
-int gss_cli_ctx_sign(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req)
-{
- struct gss_cli_ctx *gctx = ctx2gctx(ctx);
- __u32 flags = 0, seq, svc;
- int rc;
-
- LASSERT(req->rq_reqbuf);
- LASSERT(req->rq_reqbuf->lm_bufcount >= 2);
- LASSERT(req->rq_cli_ctx == ctx);
-
- /* nothing to do for context negotiation RPCs */
- if (req->rq_ctx_init)
- return 0;
-
- svc = SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc);
- if (req->rq_pack_bulk)
- flags |= LUSTRE_GSS_PACK_BULK;
- if (req->rq_pack_udesc)
- flags |= LUSTRE_GSS_PACK_USER;
-
-redo:
- seq = atomic_inc_return(&gctx->gc_seq);
-
- rc = gss_sign_msg(req->rq_reqbuf, gctx->gc_mechctx,
- ctx->cc_sec->ps_part,
- flags, gctx->gc_proc, seq, svc,
- &gctx->gc_handle);
- if (rc < 0)
- return rc;
-
- /* gss_sign_msg() msg might take long time to finish, in which period
- * more rpcs could be wrapped up and sent out. if we found too many
- * of them we should repack this rpc, because sent it too late might
- * lead to the sequence number fall behind the window on server and
- * be dropped. also applies to gss_cli_ctx_seal().
- *
- * Note: null mode doesn't check sequence number. */
- if (svc != SPTLRPC_SVC_NULL &&
- atomic_read(&gctx->gc_seq) - seq > GSS_SEQ_REPACK_THRESHOLD) {
- int behind = atomic_read(&gctx->gc_seq) - seq;
-
- gss_stat_oos_record_cli(behind);
- CWARN("req %p: %u behind, retry signing\n", req, behind);
- goto redo;
- }
-
- req->rq_reqdata_len = rc;
- return 0;
-}
-
-static
-int gss_cli_ctx_handle_err_notify(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req,
- struct gss_header *ghdr)
-{
- struct gss_err_header *errhdr;
- int rc;
-
- LASSERT(ghdr->gh_proc == PTLRPC_GSS_PROC_ERR);
-
- errhdr = (struct gss_err_header *) ghdr;
-
- CWARN("req x"LPU64"/t"LPU64", ctx %p idx "LPX64"(%u->%s): "
- "%sserver respond (%08x/%08x)\n",
- req->rq_xid, req->rq_transno, ctx,
- gss_handle_to_u64(&ctx2gctx(ctx)->gc_handle),
- ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec),
- sec_is_reverse(ctx->cc_sec) ? "reverse" : "",
- errhdr->gh_major, errhdr->gh_minor);
-
- /* context fini rpc, let it failed */
- if (req->rq_ctx_fini) {
- CWARN("context fini rpc failed\n");
- return -EINVAL;
- }
-
- /* reverse sec, just return error, don't expire this ctx because it's
- * crucial to callback rpcs. note if the callback rpc failed because
- * of bit flip during network transfer, the client will be evicted
- * directly. so more gracefully we probably want let it retry for
- * number of times. */
- if (sec_is_reverse(ctx->cc_sec))
- return -EINVAL;
-
- if (errhdr->gh_major != GSS_S_NO_CONTEXT &&
- errhdr->gh_major != GSS_S_BAD_SIG)
- return -EACCES;
-
- /* server return NO_CONTEXT might be caused by context expire
- * or server reboot/failover. we try to refresh a new ctx which
- * be transparent to upper layer.
- *
- * In some cases, our gss handle is possible to be incidentally
- * identical to another handle since the handle itself is not
- * fully random. In krb5 case, the GSS_S_BAD_SIG will be
- * returned, maybe other gss error for other mechanism.
- *
- * if we add new mechanism, make sure the correct error are
- * returned in this case. */
- CWARN("%s: server might lost the context, retrying\n",
- errhdr->gh_major == GSS_S_NO_CONTEXT ? "NO_CONTEXT" : "BAD_SIG");
-
- sptlrpc_cli_ctx_expire(ctx);
-
- /* we need replace the ctx right here, otherwise during
- * resent we'll hit the logic in sptlrpc_req_refresh_ctx()
- * which keep the ctx with RESEND flag, thus we'll never
- * get rid of this ctx. */
- rc = sptlrpc_req_replace_dead_ctx(req);
- if (rc == 0)
- req->rq_resend = 1;
-
- return rc;
-}
-
-int gss_cli_ctx_verify(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req)
-{
- struct gss_cli_ctx *gctx;
- struct gss_header *ghdr, *reqhdr;
- struct lustre_msg *msg = req->rq_repdata;
- __u32 major;
- int pack_bulk, swabbed, rc = 0;
-
- LASSERT(req->rq_cli_ctx == ctx);
- LASSERT(msg);
-
- gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
-
- /* special case for context negotiation, rq_repmsg/rq_replen actually
- * are not used currently. but early reply always be treated normally */
- if (req->rq_ctx_init && !req->rq_early) {
- req->rq_repmsg = lustre_msg_buf(msg, 1, 0);
- req->rq_replen = msg->lm_buflens[1];
- return 0;
- }
-
- if (msg->lm_bufcount < 2 || msg->lm_bufcount > 4) {
- CERROR("unexpected bufcount %u\n", msg->lm_bufcount);
- return -EPROTO;
- }
-
- swabbed = ptlrpc_rep_need_swab(req);
-
- ghdr = gss_swab_header(msg, 0, swabbed);
- if (ghdr == NULL) {
- CERROR("can't decode gss header\n");
- return -EPROTO;
- }
-
- /* sanity checks */
- reqhdr = lustre_msg_buf(msg, 0, sizeof(*reqhdr));
- LASSERT(reqhdr);
-
- if (ghdr->gh_version != reqhdr->gh_version) {
- CERROR("gss version %u mismatch, expect %u\n",
- ghdr->gh_version, reqhdr->gh_version);
- return -EPROTO;
- }
-
- switch (ghdr->gh_proc) {
- case PTLRPC_GSS_PROC_DATA:
- pack_bulk = ghdr->gh_flags & LUSTRE_GSS_PACK_BULK;
-
- if (!req->rq_early &&
- !equi(req->rq_pack_bulk == 1, pack_bulk)) {
- CERROR("%s bulk flag in reply\n",
- req->rq_pack_bulk ? "missing" : "unexpected");
- return -EPROTO;
- }
-
- if (ghdr->gh_seq != reqhdr->gh_seq) {
- CERROR("seqnum %u mismatch, expect %u\n",
- ghdr->gh_seq, reqhdr->gh_seq);
- return -EPROTO;
- }
-
- if (ghdr->gh_svc != reqhdr->gh_svc) {
- CERROR("svc %u mismatch, expect %u\n",
- ghdr->gh_svc, reqhdr->gh_svc);
- return -EPROTO;
- }
-
- if (swabbed)
- gss_header_swabber(ghdr);
-
- major = gss_verify_msg(msg, gctx->gc_mechctx, reqhdr->gh_svc);
- if (major != GSS_S_COMPLETE) {
- CERROR("failed to verify reply: %x\n", major);
- return -EPERM;
- }
-
- if (req->rq_early && reqhdr->gh_svc == SPTLRPC_SVC_NULL) {
- __u32 cksum;
-
- cksum = crc32_le(!(__u32) 0,
- lustre_msg_buf(msg, 1, 0),
- lustre_msg_buflen(msg, 1));
- if (cksum != msg->lm_cksum) {
- CWARN("early reply checksum mismatch: "
- "%08x != %08x\n", cksum, msg->lm_cksum);
- return -EPROTO;
- }
- }
-
- if (pack_bulk) {
- /* bulk checksum is right after the lustre msg */
- if (msg->lm_bufcount < 3) {
- CERROR("Invalid reply bufcount %u\n",
- msg->lm_bufcount);
- return -EPROTO;
- }
-
- rc = bulk_sec_desc_unpack(msg, 2, swabbed);
- if (rc) {
- CERROR("unpack bulk desc: %d\n", rc);
- return rc;
- }
- }
-
- req->rq_repmsg = lustre_msg_buf(msg, 1, 0);
- req->rq_replen = msg->lm_buflens[1];
- break;
- case PTLRPC_GSS_PROC_ERR:
- if (req->rq_early) {
- CERROR("server return error with early reply\n");
- rc = -EPROTO;
- } else {
- rc = gss_cli_ctx_handle_err_notify(ctx, req, ghdr);
- }
- break;
- default:
- CERROR("unknown gss proc %d\n", ghdr->gh_proc);
- rc = -EPROTO;
- }
-
- return rc;
-}
-
-int gss_cli_ctx_seal(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req)
-{
- struct gss_cli_ctx *gctx;
- rawobj_t hdrobj, msgobj, token;
- struct gss_header *ghdr;
- __u32 buflens[2], major;
- int wiresize, rc;
-
- LASSERT(req->rq_clrbuf);
- LASSERT(req->rq_cli_ctx == ctx);
- LASSERT(req->rq_reqlen);
-
- gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
-
- /* final clear data length */
- req->rq_clrdata_len = lustre_msg_size_v2(req->rq_clrbuf->lm_bufcount,
- req->rq_clrbuf->lm_buflens);
-
- /* calculate wire data length */
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = gss_cli_payload(&gctx->gc_base, req->rq_clrdata_len, 1);
- wiresize = lustre_msg_size_v2(2, buflens);
-
- /* allocate wire buffer */
- if (req->rq_pool) {
- /* pre-allocated */
- LASSERT(req->rq_reqbuf);
- LASSERT(req->rq_reqbuf != req->rq_clrbuf);
- LASSERT(req->rq_reqbuf_len >= wiresize);
- } else {
- OBD_ALLOC_LARGE(req->rq_reqbuf, wiresize);
- if (!req->rq_reqbuf)
- return -ENOMEM;
- req->rq_reqbuf_len = wiresize;
- }
-
- lustre_init_msg_v2(req->rq_reqbuf, 2, buflens, NULL);
- req->rq_reqbuf->lm_secflvr = req->rq_flvr.sf_rpc;
-
- /* gss header */
- ghdr = lustre_msg_buf(req->rq_reqbuf, 0, 0);
- ghdr->gh_version = PTLRPC_GSS_VERSION;
- ghdr->gh_sp = (__u8) ctx->cc_sec->ps_part;
- ghdr->gh_flags = 0;
- ghdr->gh_proc = gctx->gc_proc;
- ghdr->gh_svc = SPTLRPC_SVC_PRIV;
- ghdr->gh_handle.len = gctx->gc_handle.len;
- memcpy(ghdr->gh_handle.data, gctx->gc_handle.data, gctx->gc_handle.len);
- if (req->rq_pack_bulk)
- ghdr->gh_flags |= LUSTRE_GSS_PACK_BULK;
- if (req->rq_pack_udesc)
- ghdr->gh_flags |= LUSTRE_GSS_PACK_USER;
-
-redo:
- ghdr->gh_seq = atomic_inc_return(&gctx->gc_seq);
-
- /* buffer objects */
- hdrobj.len = PTLRPC_GSS_HEADER_SIZE;
- hdrobj.data = (__u8 *) ghdr;
- msgobj.len = req->rq_clrdata_len;
- msgobj.data = (__u8 *) req->rq_clrbuf;
- token.len = lustre_msg_buflen(req->rq_reqbuf, 1);
- token.data = lustre_msg_buf(req->rq_reqbuf, 1, 0);
-
- major = lgss_wrap(gctx->gc_mechctx, &hdrobj, &msgobj,
- req->rq_clrbuf_len, &token);
- if (major != GSS_S_COMPLETE) {
- CERROR("priv: wrap message error: %08x\n", major);
- GOTO(err_free, rc = -EPERM);
- }
- LASSERT(token.len <= buflens[1]);
-
- /* see explain in gss_cli_ctx_sign() */
- if (unlikely(atomic_read(&gctx->gc_seq) - ghdr->gh_seq >
- GSS_SEQ_REPACK_THRESHOLD)) {
- int behind = atomic_read(&gctx->gc_seq) - ghdr->gh_seq;
-
- gss_stat_oos_record_cli(behind);
- CWARN("req %p: %u behind, retry sealing\n", req, behind);
-
- ghdr->gh_seq = atomic_inc_return(&gctx->gc_seq);
- goto redo;
- }
-
- /* now set the final wire data length */
- req->rq_reqdata_len = lustre_shrink_msg(req->rq_reqbuf, 1, token.len,0);
- return 0;
-
-err_free:
- if (!req->rq_pool) {
- OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
- req->rq_reqbuf = NULL;
- req->rq_reqbuf_len = 0;
- }
- return rc;
-}
-
-int gss_cli_ctx_unseal(struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_request *req)
-{
- struct gss_cli_ctx *gctx;
- struct gss_header *ghdr;
- struct lustre_msg *msg = req->rq_repdata;
- int msglen, pack_bulk, swabbed, rc;
- __u32 major;
-
- LASSERT(req->rq_cli_ctx == ctx);
- LASSERT(req->rq_ctx_init == 0);
- LASSERT(msg);
-
- gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
- swabbed = ptlrpc_rep_need_swab(req);
-
- ghdr = gss_swab_header(msg, 0, swabbed);
- if (ghdr == NULL) {
- CERROR("can't decode gss header\n");
- return -EPROTO;
- }
-
- /* sanity checks */
- if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
- CERROR("gss version %u mismatch, expect %u\n",
- ghdr->gh_version, PTLRPC_GSS_VERSION);
- return -EPROTO;
- }
-
- switch (ghdr->gh_proc) {
- case PTLRPC_GSS_PROC_DATA:
- pack_bulk = ghdr->gh_flags & LUSTRE_GSS_PACK_BULK;
-
- if (!req->rq_early &&
- !equi(req->rq_pack_bulk == 1, pack_bulk)) {
- CERROR("%s bulk flag in reply\n",
- req->rq_pack_bulk ? "missing" : "unexpected");
- return -EPROTO;
- }
-
- if (swabbed)
- gss_header_swabber(ghdr);
-
- /* use rq_repdata_len as buffer size, which assume unseal
- * doesn't need extra memory space. for precise control, we'd
- * better calculate out actual buffer size as
- * (repbuf_len - offset - repdata_len) */
- major = gss_unseal_msg(gctx->gc_mechctx, msg,
- &msglen, req->rq_repdata_len);
- if (major != GSS_S_COMPLETE) {
- CERROR("failed to unwrap reply: %x\n", major);
- rc = -EPERM;
- break;
- }
-
- swabbed = __lustre_unpack_msg(msg, msglen);
- if (swabbed < 0) {
- CERROR("Failed to unpack after decryption\n");
- return -EPROTO;
- }
-
- if (msg->lm_bufcount < 1) {
- CERROR("Invalid reply buffer: empty\n");
- return -EPROTO;
- }
-
- if (pack_bulk) {
- if (msg->lm_bufcount < 2) {
- CERROR("bufcount %u: missing bulk sec desc\n",
- msg->lm_bufcount);
- return -EPROTO;
- }
-
- /* bulk checksum is the last segment */
- if (bulk_sec_desc_unpack(msg, msg->lm_bufcount - 1,
- swabbed))
- return -EPROTO;
- }
-
- req->rq_repmsg = lustre_msg_buf(msg, 0, 0);
- req->rq_replen = msg->lm_buflens[0];
-
- rc = 0;
- break;
- case PTLRPC_GSS_PROC_ERR:
- if (req->rq_early) {
- CERROR("server return error with early reply\n");
- rc = -EPROTO;
- } else {
- rc = gss_cli_ctx_handle_err_notify(ctx, req, ghdr);
- }
- break;
- default:
- CERROR("unexpected proc %d\n", ghdr->gh_proc);
- rc = -EPERM;
- }
-
- return rc;
-}
-
-/*********************************************
- * reverse context installation *
- *********************************************/
-
-static inline
-int gss_install_rvs_svc_ctx(struct obd_import *imp,
- struct gss_sec *gsec,
- struct gss_cli_ctx *gctx)
-{
- return gss_svc_upcall_install_rvs_ctx(imp, gsec, gctx);
-}
-
-/*********************************************
- * GSS security APIs *
- *********************************************/
-int gss_sec_create_common(struct gss_sec *gsec,
- struct ptlrpc_sec_policy *policy,
- struct obd_import *imp,
- struct ptlrpc_svc_ctx *svcctx,
- struct sptlrpc_flavor *sf)
-{
- struct ptlrpc_sec *sec;
-
- LASSERT(imp);
- LASSERT(SPTLRPC_FLVR_POLICY(sf->sf_rpc) == SPTLRPC_POLICY_GSS);
-
- gsec->gs_mech = lgss_subflavor_to_mech(
- SPTLRPC_FLVR_BASE_SUB(sf->sf_rpc));
- if (!gsec->gs_mech) {
- CERROR("gss backend 0x%x not found\n",
- SPTLRPC_FLVR_BASE_SUB(sf->sf_rpc));
- return -EOPNOTSUPP;
- }
-
- spin_lock_init(&gsec->gs_lock);
- gsec->gs_rvs_hdl = 0ULL;
-
- /* initialize upper ptlrpc_sec */
- sec = &gsec->gs_base;
- sec->ps_policy = policy;
- atomic_set(&sec->ps_refcount, 0);
- atomic_set(&sec->ps_nctx, 0);
- sec->ps_id = sptlrpc_get_next_secid();
- sec->ps_flvr = *sf;
- sec->ps_import = class_import_get(imp);
- spin_lock_init(&sec->ps_lock);
- INIT_LIST_HEAD(&sec->ps_gc_list);
-
- if (!svcctx) {
- sec->ps_gc_interval = GSS_GC_INTERVAL;
- } else {
- LASSERT(sec_is_reverse(sec));
-
- /* never do gc on reverse sec */
- sec->ps_gc_interval = 0;
- }
-
- if (SPTLRPC_FLVR_BULK_SVC(sec->ps_flvr.sf_rpc) == SPTLRPC_BULK_SVC_PRIV)
- sptlrpc_enc_pool_add_user();
-
- CDEBUG(D_SEC, "create %s%s@%p\n", (svcctx ? "reverse " : ""),
- policy->sp_name, gsec);
- return 0;
-}
-
-void gss_sec_destroy_common(struct gss_sec *gsec)
-{
- struct ptlrpc_sec *sec = &gsec->gs_base;
-
- LASSERT(sec->ps_import);
- LASSERT(atomic_read(&sec->ps_refcount) == 0);
- LASSERT(atomic_read(&sec->ps_nctx) == 0);
-
- if (gsec->gs_mech) {
- lgss_mech_put(gsec->gs_mech);
- gsec->gs_mech = NULL;
- }
-
- class_import_put(sec->ps_import);
-
- if (SPTLRPC_FLVR_BULK_SVC(sec->ps_flvr.sf_rpc) == SPTLRPC_BULK_SVC_PRIV)
- sptlrpc_enc_pool_del_user();
-}
-
-void gss_sec_kill(struct ptlrpc_sec *sec)
-{
- sec->ps_dying = 1;
-}
-
-int gss_cli_ctx_init_common(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx,
- struct ptlrpc_ctx_ops *ctxops,
- struct vfs_cred *vcred)
-{
- struct gss_cli_ctx *gctx = ctx2gctx(ctx);
-
- gctx->gc_win = 0;
- atomic_set(&gctx->gc_seq, 0);
-
- INIT_HLIST_NODE(&ctx->cc_cache);
- atomic_set(&ctx->cc_refcount, 0);
- ctx->cc_sec = sec;
- ctx->cc_ops = ctxops;
- ctx->cc_expire = 0;
- ctx->cc_flags = PTLRPC_CTX_NEW;
- ctx->cc_vcred = *vcred;
- spin_lock_init(&ctx->cc_lock);
- INIT_LIST_HEAD(&ctx->cc_req_list);
- INIT_LIST_HEAD(&ctx->cc_gc_chain);
-
- /* take a ref on belonging sec, balanced in ctx destroying */
- atomic_inc(&sec->ps_refcount);
- /* statistic only */
- atomic_inc(&sec->ps_nctx);
-
- CDEBUG(D_SEC, "%s@%p: create ctx %p(%u->%s)\n",
- sec->ps_policy->sp_name, ctx->cc_sec,
- ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
- return 0;
-}
-
-/*
- * return value:
- * 1: the context has been taken care of by someone else
- * 0: proceed to really destroy the context locally
- */
-int gss_cli_ctx_fini_common(struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx)
-{
- struct gss_cli_ctx *gctx = ctx2gctx(ctx);
-
- LASSERT(atomic_read(&sec->ps_nctx) > 0);
- LASSERT(atomic_read(&ctx->cc_refcount) == 0);
- LASSERT(ctx->cc_sec == sec);
-
- /*
- * remove UPTODATE flag of reverse ctx thus we won't send fini rpc,
- * this is to avoid potential problems of client side reverse svc ctx
- * be mis-destroyed in various recovery scenarios. anyway client can
- * manage its reverse ctx well by associating it with its buddy ctx.
- */
- if (sec_is_reverse(sec))
- ctx->cc_flags &= ~PTLRPC_CTX_UPTODATE;
-
- if (gctx->gc_mechctx) {
- /* the final context fini rpc will use this ctx too, and it's
- * asynchronous which finished by request_out_callback(). so
- * we add refcount, whoever drop finally drop the refcount to
- * 0 should responsible for the rest of destroy. */
- atomic_inc(&ctx->cc_refcount);
-
- gss_do_ctx_fini_rpc(gctx);
- gss_cli_ctx_finalize(gctx);
-
- if (!atomic_dec_and_test(&ctx->cc_refcount))
- return 1;
- }
-
- if (sec_is_reverse(sec))
- CWARN("reverse sec %p: destroy ctx %p\n",
- ctx->cc_sec, ctx);
- else
- CWARN("%s@%p: destroy ctx %p(%u->%s)\n",
- sec->ps_policy->sp_name, ctx->cc_sec,
- ctx, ctx->cc_vcred.vc_uid, sec2target_str(ctx->cc_sec));
-
- return 0;
-}
-
-static
-int gss_alloc_reqbuf_intg(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int svc, int msgsize)
-{
- int bufsize, txtsize;
- int bufcnt = 2;
- __u32 buflens[5];
-
- /*
- * on-wire data layout:
- * - gss header
- * - lustre message
- * - user descriptor (optional)
- * - bulk sec descriptor (optional)
- * - signature (optional)
- * - svc == NULL: NULL
- * - svc == AUTH: signature of gss header
- * - svc == INTG: signature of all above
- *
- * if this is context negotiation, reserver fixed space
- * at the last (signature) segment regardless of svc mode.
- */
-
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- txtsize = buflens[0];
-
- buflens[1] = msgsize;
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[1];
-
- if (req->rq_pack_udesc) {
- buflens[bufcnt] = sptlrpc_current_user_desc_size();
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[bufcnt];
- bufcnt++;
- }
-
- if (req->rq_pack_bulk) {
- buflens[bufcnt] = gss_cli_bulk_payload(req->rq_cli_ctx,
- &req->rq_flvr,
- 0, req->rq_bulk_read);
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[bufcnt];
- bufcnt++;
- }
-
- if (req->rq_ctx_init)
- buflens[bufcnt++] = GSS_CTX_INIT_MAX_LEN;
- else if (svc != SPTLRPC_SVC_NULL)
- buflens[bufcnt++] = gss_cli_payload(req->rq_cli_ctx, txtsize,0);
-
- bufsize = lustre_msg_size_v2(bufcnt, buflens);
-
- if (!req->rq_reqbuf) {
- bufsize = size_roundup_power2(bufsize);
-
- OBD_ALLOC_LARGE(req->rq_reqbuf, bufsize);
- if (!req->rq_reqbuf)
- return -ENOMEM;
-
- req->rq_reqbuf_len = bufsize;
- } else {
- LASSERT(req->rq_pool);
- LASSERT(req->rq_reqbuf_len >= bufsize);
- memset(req->rq_reqbuf, 0, bufsize);
- }
-
- lustre_init_msg_v2(req->rq_reqbuf, bufcnt, buflens, NULL);
- req->rq_reqbuf->lm_secflvr = req->rq_flvr.sf_rpc;
-
- req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 1, msgsize);
- LASSERT(req->rq_reqmsg);
-
- /* pack user desc here, later we might leave current user's process */
- if (req->rq_pack_udesc)
- sptlrpc_pack_user_desc(req->rq_reqbuf, 2);
-
- return 0;
-}
-
-static
-int gss_alloc_reqbuf_priv(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int msgsize)
-{
- __u32 ibuflens[3], wbuflens[2];
- int ibufcnt;
- int clearsize, wiresize;
-
- LASSERT(req->rq_clrbuf == NULL);
- LASSERT(req->rq_clrbuf_len == 0);
-
- /* Inner (clear) buffers
- * - lustre message
- * - user descriptor (optional)
- * - bulk checksum (optional)
- */
- ibufcnt = 1;
- ibuflens[0] = msgsize;
-
- if (req->rq_pack_udesc)
- ibuflens[ibufcnt++] = sptlrpc_current_user_desc_size();
- if (req->rq_pack_bulk)
- ibuflens[ibufcnt++] = gss_cli_bulk_payload(req->rq_cli_ctx,
- &req->rq_flvr, 0,
- req->rq_bulk_read);
-
- clearsize = lustre_msg_size_v2(ibufcnt, ibuflens);
- /* to allow append padding during encryption */
- clearsize += GSS_MAX_CIPHER_BLOCK;
-
- /* Wrapper (wire) buffers
- * - gss header
- * - cipher text
- */
- wbuflens[0] = PTLRPC_GSS_HEADER_SIZE;
- wbuflens[1] = gss_cli_payload(req->rq_cli_ctx, clearsize, 1);
- wiresize = lustre_msg_size_v2(2, wbuflens);
-
- if (req->rq_pool) {
- /* rq_reqbuf is preallocated */
- LASSERT(req->rq_reqbuf);
- LASSERT(req->rq_reqbuf_len >= wiresize);
-
- memset(req->rq_reqbuf, 0, req->rq_reqbuf_len);
-
- /* if the pre-allocated buffer is big enough, we just pack
- * both clear buf & request buf in it, to avoid more alloc. */
- if (clearsize + wiresize <= req->rq_reqbuf_len) {
- req->rq_clrbuf =
- (void *) (((char *) req->rq_reqbuf) + wiresize);
- } else {
- CWARN("pre-allocated buf size %d is not enough for "
- "both clear (%d) and cipher (%d) text, proceed "
- "with extra allocation\n", req->rq_reqbuf_len,
- clearsize, wiresize);
- }
- }
-
- if (!req->rq_clrbuf) {
- clearsize = size_roundup_power2(clearsize);
-
- OBD_ALLOC_LARGE(req->rq_clrbuf, clearsize);
- if (!req->rq_clrbuf)
- return -ENOMEM;
- }
- req->rq_clrbuf_len = clearsize;
-
- lustre_init_msg_v2(req->rq_clrbuf, ibufcnt, ibuflens, NULL);
- req->rq_reqmsg = lustre_msg_buf(req->rq_clrbuf, 0, msgsize);
-
- if (req->rq_pack_udesc)
- sptlrpc_pack_user_desc(req->rq_clrbuf, 1);
-
- return 0;
-}
-
-/*
- * NOTE: any change of request buffer allocation should also consider
- * changing enlarge_reqbuf() series functions.
- */
-int gss_alloc_reqbuf(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int msgsize)
-{
- int svc = SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc);
-
- LASSERT(!req->rq_pack_bulk ||
- (req->rq_bulk_read || req->rq_bulk_write));
-
- switch (svc) {
- case SPTLRPC_SVC_NULL:
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- return gss_alloc_reqbuf_intg(sec, req, svc, msgsize);
- case SPTLRPC_SVC_PRIV:
- return gss_alloc_reqbuf_priv(sec, req, msgsize);
- default:
- LASSERTF(0, "bad rpc flavor %x\n", req->rq_flvr.sf_rpc);
- return 0;
- }
-}
-
-void gss_free_reqbuf(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req)
-{
- int privacy;
-
- LASSERT(!req->rq_pool || req->rq_reqbuf);
- privacy = SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc) == SPTLRPC_SVC_PRIV;
-
- if (!req->rq_clrbuf)
- goto release_reqbuf;
-
- /* release clear buffer */
- LASSERT(privacy);
- LASSERT(req->rq_clrbuf_len);
-
- if (req->rq_pool == NULL ||
- req->rq_clrbuf < req->rq_reqbuf ||
- (char *) req->rq_clrbuf >=
- (char *) req->rq_reqbuf + req->rq_reqbuf_len)
- OBD_FREE_LARGE(req->rq_clrbuf, req->rq_clrbuf_len);
-
- req->rq_clrbuf = NULL;
- req->rq_clrbuf_len = 0;
-
-release_reqbuf:
- if (!req->rq_pool && req->rq_reqbuf) {
- LASSERT(req->rq_reqbuf_len);
-
- OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
- req->rq_reqbuf = NULL;
- req->rq_reqbuf_len = 0;
- }
-}
-
-static int do_alloc_repbuf(struct ptlrpc_request *req, int bufsize)
-{
- bufsize = size_roundup_power2(bufsize);
-
- OBD_ALLOC_LARGE(req->rq_repbuf, bufsize);
- if (!req->rq_repbuf)
- return -ENOMEM;
-
- req->rq_repbuf_len = bufsize;
- return 0;
-}
-
-static
-int gss_alloc_repbuf_intg(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int svc, int msgsize)
-{
- int txtsize;
- __u32 buflens[4];
- int bufcnt = 2;
- int alloc_size;
-
- /*
- * on-wire data layout:
- * - gss header
- * - lustre message
- * - bulk sec descriptor (optional)
- * - signature (optional)
- * - svc == NULL: NULL
- * - svc == AUTH: signature of gss header
- * - svc == INTG: signature of all above
- *
- * if this is context negotiation, reserver fixed space
- * at the last (signature) segment regardless of svc mode.
- */
-
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- txtsize = buflens[0];
-
- buflens[1] = msgsize;
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[1];
-
- if (req->rq_pack_bulk) {
- buflens[bufcnt] = gss_cli_bulk_payload(req->rq_cli_ctx,
- &req->rq_flvr,
- 1, req->rq_bulk_read);
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[bufcnt];
- bufcnt++;
- }
-
- if (req->rq_ctx_init)
- buflens[bufcnt++] = GSS_CTX_INIT_MAX_LEN;
- else if (svc != SPTLRPC_SVC_NULL)
- buflens[bufcnt++] = gss_cli_payload(req->rq_cli_ctx, txtsize,0);
-
- alloc_size = lustre_msg_size_v2(bufcnt, buflens);
-
- /* add space for early reply */
- alloc_size += gss_at_reply_off_integ;
-
- return do_alloc_repbuf(req, alloc_size);
-}
-
-static
-int gss_alloc_repbuf_priv(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int msgsize)
-{
- int txtsize;
- __u32 buflens[2];
- int bufcnt;
- int alloc_size;
-
- /* inner buffers */
- bufcnt = 1;
- buflens[0] = msgsize;
-
- if (req->rq_pack_bulk)
- buflens[bufcnt++] = gss_cli_bulk_payload(req->rq_cli_ctx,
- &req->rq_flvr,
- 1, req->rq_bulk_read);
- txtsize = lustre_msg_size_v2(bufcnt, buflens);
- txtsize += GSS_MAX_CIPHER_BLOCK;
-
- /* wrapper buffers */
- bufcnt = 2;
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = gss_cli_payload(req->rq_cli_ctx, txtsize, 1);
-
- alloc_size = lustre_msg_size_v2(bufcnt, buflens);
- /* add space for early reply */
- alloc_size += gss_at_reply_off_priv;
-
- return do_alloc_repbuf(req, alloc_size);
-}
-
-int gss_alloc_repbuf(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int msgsize)
-{
- int svc = SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc);
-
- LASSERT(!req->rq_pack_bulk ||
- (req->rq_bulk_read || req->rq_bulk_write));
-
- switch (svc) {
- case SPTLRPC_SVC_NULL:
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- return gss_alloc_repbuf_intg(sec, req, svc, msgsize);
- case SPTLRPC_SVC_PRIV:
- return gss_alloc_repbuf_priv(sec, req, msgsize);
- default:
- LASSERTF(0, "bad rpc flavor %x\n", req->rq_flvr.sf_rpc);
- return 0;
- }
-}
-
-void gss_free_repbuf(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req)
-{
- OBD_FREE_LARGE(req->rq_repbuf, req->rq_repbuf_len);
- req->rq_repbuf = NULL;
- req->rq_repbuf_len = 0;
- req->rq_repdata = NULL;
- req->rq_repdata_len = 0;
-}
-
-static int get_enlarged_msgsize(struct lustre_msg *msg,
- int segment, int newsize)
-{
- int save, newmsg_size;
-
- LASSERT(newsize >= msg->lm_buflens[segment]);
-
- save = msg->lm_buflens[segment];
- msg->lm_buflens[segment] = newsize;
- newmsg_size = lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
- msg->lm_buflens[segment] = save;
-
- return newmsg_size;
-}
-
-static int get_enlarged_msgsize2(struct lustre_msg *msg,
- int segment1, int newsize1,
- int segment2, int newsize2)
-{
- int save1, save2, newmsg_size;
-
- LASSERT(newsize1 >= msg->lm_buflens[segment1]);
- LASSERT(newsize2 >= msg->lm_buflens[segment2]);
-
- save1 = msg->lm_buflens[segment1];
- save2 = msg->lm_buflens[segment2];
- msg->lm_buflens[segment1] = newsize1;
- msg->lm_buflens[segment2] = newsize2;
- newmsg_size = lustre_msg_size_v2(msg->lm_bufcount, msg->lm_buflens);
- msg->lm_buflens[segment1] = save1;
- msg->lm_buflens[segment2] = save2;
-
- return newmsg_size;
-}
-
-static
-int gss_enlarge_reqbuf_intg(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int svc,
- int segment, int newsize)
-{
- struct lustre_msg *newbuf;
- int txtsize, sigsize = 0, i;
- int newmsg_size, newbuf_size;
-
- /*
- * gss header is at seg 0;
- * embedded msg is at seg 1;
- * signature (if any) is at the last seg
- */
- LASSERT(req->rq_reqbuf);
- LASSERT(req->rq_reqbuf_len > req->rq_reqlen);
- LASSERT(req->rq_reqbuf->lm_bufcount >= 2);
- LASSERT(lustre_msg_buf(req->rq_reqbuf, 1, 0) == req->rq_reqmsg);
-
- /* 1. compute new embedded msg size */
- newmsg_size = get_enlarged_msgsize(req->rq_reqmsg, segment, newsize);
- LASSERT(newmsg_size >= req->rq_reqbuf->lm_buflens[1]);
-
- /* 2. compute new wrapper msg size */
- if (svc == SPTLRPC_SVC_NULL) {
- /* no signature, get size directly */
- newbuf_size = get_enlarged_msgsize(req->rq_reqbuf,
- 1, newmsg_size);
- } else {
- txtsize = req->rq_reqbuf->lm_buflens[0];
-
- if (svc == SPTLRPC_SVC_INTG) {
- for (i = 1; i < req->rq_reqbuf->lm_bufcount; i++)
- txtsize += req->rq_reqbuf->lm_buflens[i];
- txtsize += newmsg_size - req->rq_reqbuf->lm_buflens[1];
- }
-
- sigsize = gss_cli_payload(req->rq_cli_ctx, txtsize, 0);
- LASSERT(sigsize >= msg_last_seglen(req->rq_reqbuf));
-
- newbuf_size = get_enlarged_msgsize2(
- req->rq_reqbuf,
- 1, newmsg_size,
- msg_last_segidx(req->rq_reqbuf),
- sigsize);
- }
-
- /* request from pool should always have enough buffer */
- LASSERT(!req->rq_pool || req->rq_reqbuf_len >= newbuf_size);
-
- if (req->rq_reqbuf_len < newbuf_size) {
- newbuf_size = size_roundup_power2(newbuf_size);
-
- OBD_ALLOC_LARGE(newbuf, newbuf_size);
- if (newbuf == NULL)
- return -ENOMEM;
-
- /* Must lock this, so that otherwise unprotected change of
- * rq_reqmsg is not racing with parallel processing of
- * imp_replay_list traversing threads. See LU-3333
- * This is a bandaid at best, we really need to deal with this
- * in request enlarging code before unpacking that's already
- * there */
- if (req->rq_import)
- spin_lock(&req->rq_import->imp_lock);
-
- memcpy(newbuf, req->rq_reqbuf, req->rq_reqbuf_len);
-
- OBD_FREE_LARGE(req->rq_reqbuf, req->rq_reqbuf_len);
- req->rq_reqbuf = newbuf;
- req->rq_reqbuf_len = newbuf_size;
- req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 1, 0);
-
- if (req->rq_import)
- spin_unlock(&req->rq_import->imp_lock);
- }
-
- /* do enlargement, from wrapper to embedded, from end to begin */
- if (svc != SPTLRPC_SVC_NULL)
- _sptlrpc_enlarge_msg_inplace(req->rq_reqbuf,
- msg_last_segidx(req->rq_reqbuf),
- sigsize);
-
- _sptlrpc_enlarge_msg_inplace(req->rq_reqbuf, 1, newmsg_size);
- _sptlrpc_enlarge_msg_inplace(req->rq_reqmsg, segment, newsize);
-
- req->rq_reqlen = newmsg_size;
- return 0;
-}
-
-static
-int gss_enlarge_reqbuf_priv(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int segment, int newsize)
-{
- struct lustre_msg *newclrbuf;
- int newmsg_size, newclrbuf_size, newcipbuf_size;
- __u32 buflens[3];
-
- /*
- * embedded msg is at seg 0 of clear buffer;
- * cipher text is at seg 2 of cipher buffer;
- */
- LASSERT(req->rq_pool ||
- (req->rq_reqbuf == NULL && req->rq_reqbuf_len == 0));
- LASSERT(req->rq_reqbuf == NULL ||
- (req->rq_pool && req->rq_reqbuf->lm_bufcount == 3));
- LASSERT(req->rq_clrbuf);
- LASSERT(req->rq_clrbuf_len > req->rq_reqlen);
- LASSERT(lustre_msg_buf(req->rq_clrbuf, 0, 0) == req->rq_reqmsg);
-
- /* compute new embedded msg size */
- newmsg_size = get_enlarged_msgsize(req->rq_reqmsg, segment, newsize);
-
- /* compute new clear buffer size */
- newclrbuf_size = get_enlarged_msgsize(req->rq_clrbuf, 0, newmsg_size);
- newclrbuf_size += GSS_MAX_CIPHER_BLOCK;
-
- /* compute new cipher buffer size */
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = gss_cli_payload(req->rq_cli_ctx, buflens[0], 0);
- buflens[2] = gss_cli_payload(req->rq_cli_ctx, newclrbuf_size, 1);
- newcipbuf_size = lustre_msg_size_v2(3, buflens);
-
- /* handle the case that we put both clear buf and cipher buf into
- * pre-allocated single buffer. */
- if (unlikely(req->rq_pool) &&
- req->rq_clrbuf >= req->rq_reqbuf &&
- (char *) req->rq_clrbuf <
- (char *) req->rq_reqbuf + req->rq_reqbuf_len) {
- /* it couldn't be better we still fit into the
- * pre-allocated buffer. */
- if (newclrbuf_size + newcipbuf_size <= req->rq_reqbuf_len) {
- void *src, *dst;
-
- if (req->rq_import)
- spin_lock(&req->rq_import->imp_lock);
- /* move clear text backward. */
- src = req->rq_clrbuf;
- dst = (char *) req->rq_reqbuf + newcipbuf_size;
-
- memmove(dst, src, req->rq_clrbuf_len);
-
- req->rq_clrbuf = (struct lustre_msg *) dst;
- req->rq_clrbuf_len = newclrbuf_size;
- req->rq_reqmsg = lustre_msg_buf(req->rq_clrbuf, 0, 0);
-
- if (req->rq_import)
- spin_unlock(&req->rq_import->imp_lock);
- } else {
- /* sadly we have to split out the clear buffer */
- LASSERT(req->rq_reqbuf_len >= newcipbuf_size);
- LASSERT(req->rq_clrbuf_len < newclrbuf_size);
- }
- }
-
- if (req->rq_clrbuf_len < newclrbuf_size) {
- newclrbuf_size = size_roundup_power2(newclrbuf_size);
-
- OBD_ALLOC_LARGE(newclrbuf, newclrbuf_size);
- if (newclrbuf == NULL)
- return -ENOMEM;
-
- /* Must lock this, so that otherwise unprotected change of
- * rq_reqmsg is not racing with parallel processing of
- * imp_replay_list traversing threads. See LU-3333
- * This is a bandaid at best, we really need to deal with this
- * in request enlarging code before unpacking that's already
- * there */
- if (req->rq_import)
- spin_lock(&req->rq_import->imp_lock);
-
- memcpy(newclrbuf, req->rq_clrbuf, req->rq_clrbuf_len);
-
- if (req->rq_reqbuf == NULL ||
- req->rq_clrbuf < req->rq_reqbuf ||
- (char *) req->rq_clrbuf >=
- (char *) req->rq_reqbuf + req->rq_reqbuf_len) {
- OBD_FREE_LARGE(req->rq_clrbuf, req->rq_clrbuf_len);
- }
-
- req->rq_clrbuf = newclrbuf;
- req->rq_clrbuf_len = newclrbuf_size;
- req->rq_reqmsg = lustre_msg_buf(req->rq_clrbuf, 0, 0);
-
- if (req->rq_import)
- spin_unlock(&req->rq_import->imp_lock);
- }
-
- _sptlrpc_enlarge_msg_inplace(req->rq_clrbuf, 0, newmsg_size);
- _sptlrpc_enlarge_msg_inplace(req->rq_reqmsg, segment, newsize);
- req->rq_reqlen = newmsg_size;
-
- return 0;
-}
-
-int gss_enlarge_reqbuf(struct ptlrpc_sec *sec,
- struct ptlrpc_request *req,
- int segment, int newsize)
-{
- int svc = SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc);
-
- LASSERT(!req->rq_ctx_init && !req->rq_ctx_fini);
-
- switch (svc) {
- case SPTLRPC_SVC_NULL:
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- return gss_enlarge_reqbuf_intg(sec, req, svc, segment, newsize);
- case SPTLRPC_SVC_PRIV:
- return gss_enlarge_reqbuf_priv(sec, req, segment, newsize);
- default:
- LASSERTF(0, "bad rpc flavor %x\n", req->rq_flvr.sf_rpc);
- return 0;
- }
-}
-
-int gss_sec_install_rctx(struct obd_import *imp,
- struct ptlrpc_sec *sec,
- struct ptlrpc_cli_ctx *ctx)
-{
- struct gss_sec *gsec;
- struct gss_cli_ctx *gctx;
- int rc;
-
- gsec = container_of(sec, struct gss_sec, gs_base);
- gctx = container_of(ctx, struct gss_cli_ctx, gc_base);
-
- rc = gss_install_rvs_svc_ctx(imp, gsec, gctx);
- return rc;
-}
-
-/********************************************
- * server side API *
- ********************************************/
-
-static inline
-int gss_svc_reqctx_is_special(struct gss_svc_reqctx *grctx)
-{
- LASSERT(grctx);
- return (grctx->src_init || grctx->src_init_continue ||
- grctx->src_err_notify);
-}
-
-static
-void gss_svc_reqctx_free(struct gss_svc_reqctx *grctx)
-{
- if (grctx->src_ctx)
- gss_svc_upcall_put_ctx(grctx->src_ctx);
-
- sptlrpc_policy_put(grctx->src_base.sc_policy);
- OBD_FREE_PTR(grctx);
-}
-
-static inline
-void gss_svc_reqctx_addref(struct gss_svc_reqctx *grctx)
-{
- LASSERT(atomic_read(&grctx->src_base.sc_refcount) > 0);
- atomic_inc(&grctx->src_base.sc_refcount);
-}
-
-static inline
-void gss_svc_reqctx_decref(struct gss_svc_reqctx *grctx)
-{
- LASSERT(atomic_read(&grctx->src_base.sc_refcount) > 0);
-
- if (atomic_dec_and_test(&grctx->src_base.sc_refcount))
- gss_svc_reqctx_free(grctx);
-}
-
-static
-int gss_svc_sign(struct ptlrpc_request *req,
- struct ptlrpc_reply_state *rs,
- struct gss_svc_reqctx *grctx,
- __u32 svc)
-{
- __u32 flags = 0;
- int rc;
-
- LASSERT(rs->rs_msg == lustre_msg_buf(rs->rs_repbuf, 1, 0));
-
- /* embedded lustre_msg might have been shrunk */
- if (req->rq_replen != rs->rs_repbuf->lm_buflens[1])
- lustre_shrink_msg(rs->rs_repbuf, 1, req->rq_replen, 1);
-
- if (req->rq_pack_bulk)
- flags |= LUSTRE_GSS_PACK_BULK;
-
- rc = gss_sign_msg(rs->rs_repbuf, grctx->src_ctx->gsc_mechctx,
- LUSTRE_SP_ANY, flags, PTLRPC_GSS_PROC_DATA,
- grctx->src_wirectx.gw_seq, svc, NULL);
- if (rc < 0)
- return rc;
-
- rs->rs_repdata_len = rc;
-
- if (likely(req->rq_packed_final)) {
- if (lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT)
- req->rq_reply_off = gss_at_reply_off_integ;
- else
- req->rq_reply_off = 0;
- } else {
- if (svc == SPTLRPC_SVC_NULL)
- rs->rs_repbuf->lm_cksum = crc32_le(!(__u32) 0,
- lustre_msg_buf(rs->rs_repbuf, 1, 0),
- lustre_msg_buflen(rs->rs_repbuf, 1));
- req->rq_reply_off = 0;
- }
-
- return 0;
-}
-
-int gss_pack_err_notify(struct ptlrpc_request *req, __u32 major, __u32 minor)
-{
- struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- struct ptlrpc_reply_state *rs;
- struct gss_err_header *ghdr;
- int replen = sizeof(struct ptlrpc_body);
- int rc;
-
- //if (OBD_FAIL_CHECK_ORSET(OBD_FAIL_SVCGSS_ERR_NOTIFY, OBD_FAIL_ONCE))
- // return -EINVAL;
-
- grctx->src_err_notify = 1;
- grctx->src_reserve_len = 0;
-
- rc = lustre_pack_reply_v2(req, 1, &replen, NULL, 0);
- if (rc) {
- CERROR("could not pack reply, err %d\n", rc);
- return rc;
- }
-
- /* gss hdr */
- rs = req->rq_reply_state;
- LASSERT(rs->rs_repbuf->lm_buflens[1] >= sizeof(*ghdr));
- ghdr = lustre_msg_buf(rs->rs_repbuf, 0, 0);
- ghdr->gh_version = PTLRPC_GSS_VERSION;
- ghdr->gh_flags = 0;
- ghdr->gh_proc = PTLRPC_GSS_PROC_ERR;
- ghdr->gh_major = major;
- ghdr->gh_minor = minor;
- ghdr->gh_handle.len = 0; /* fake context handle */
-
- rs->rs_repdata_len = lustre_msg_size_v2(rs->rs_repbuf->lm_bufcount,
- rs->rs_repbuf->lm_buflens);
-
- CDEBUG(D_SEC, "prepare gss error notify(0x%x/0x%x) to %s\n",
- major, minor, libcfs_nid2str(req->rq_peer.nid));
- return 0;
-}
-
-static
-int gss_svc_handle_init(struct ptlrpc_request *req,
- struct gss_wire_ctx *gw)
-{
- struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- struct lustre_msg *reqbuf = req->rq_reqbuf;
- struct obd_uuid *uuid;
- struct obd_device *target;
- rawobj_t uuid_obj, rvs_hdl, in_token;
- __u32 lustre_svc;
- __u32 *secdata, seclen;
- int swabbed, rc;
-
- CDEBUG(D_SEC, "processing gss init(%d) request from %s\n", gw->gw_proc,
- libcfs_nid2str(req->rq_peer.nid));
-
- req->rq_ctx_init = 1;
-
- if (gw->gw_flags & LUSTRE_GSS_PACK_BULK) {
- CERROR("unexpected bulk flag\n");
- return SECSVC_DROP;
- }
-
- if (gw->gw_proc == PTLRPC_GSS_PROC_INIT && gw->gw_handle.len != 0) {
- CERROR("proc %u: invalid handle length %u\n",
- gw->gw_proc, gw->gw_handle.len);
- return SECSVC_DROP;
- }
-
- if (reqbuf->lm_bufcount < 3 || reqbuf->lm_bufcount > 4) {
- CERROR("Invalid bufcount %d\n", reqbuf->lm_bufcount);
- return SECSVC_DROP;
- }
-
- swabbed = ptlrpc_req_need_swab(req);
-
- /* ctx initiate payload is in last segment */
- secdata = lustre_msg_buf(reqbuf, reqbuf->lm_bufcount - 1, 0);
- seclen = reqbuf->lm_buflens[reqbuf->lm_bufcount - 1];
-
- if (seclen < 4 + 4) {
- CERROR("sec size %d too small\n", seclen);
- return SECSVC_DROP;
- }
-
- /* lustre svc type */
- lustre_svc = le32_to_cpu(*secdata++);
- seclen -= 4;
-
- /* extract target uuid, note this code is somewhat fragile
- * because touched internal structure of obd_uuid */
- if (rawobj_extract(&uuid_obj, &secdata, &seclen)) {
- CERROR("failed to extract target uuid\n");
- return SECSVC_DROP;
- }
- uuid_obj.data[uuid_obj.len - 1] = '\0';
-
- uuid = (struct obd_uuid *) uuid_obj.data;
- target = class_uuid2obd(uuid);
- if (!target || target->obd_stopping || !target->obd_set_up) {
- CERROR("target '%s' is not available for context init (%s)\n",
- uuid->uuid, target == NULL ? "no target" :
- (target->obd_stopping ? "stopping" : "not set up"));
- return SECSVC_DROP;
- }
-
- /* extract reverse handle */
- if (rawobj_extract(&rvs_hdl, &secdata, &seclen)) {
- CERROR("failed extract reverse handle\n");
- return SECSVC_DROP;
- }
-
- /* extract token */
- if (rawobj_extract(&in_token, &secdata, &seclen)) {
- CERROR("can't extract token\n");
- return SECSVC_DROP;
- }
-
- rc = gss_svc_upcall_handle_init(req, grctx, gw, target, lustre_svc,
- &rvs_hdl, &in_token);
- if (rc != SECSVC_OK)
- return rc;
-
- if (grctx->src_ctx->gsc_usr_mds || grctx->src_ctx->gsc_usr_oss ||
- grctx->src_ctx->gsc_usr_root)
- CWARN("create svc ctx %p: user from %s authenticated as %s\n",
- grctx->src_ctx, libcfs_nid2str(req->rq_peer.nid),
- grctx->src_ctx->gsc_usr_mds ? "mds" :
- (grctx->src_ctx->gsc_usr_oss ? "oss" : "root"));
- else
- CWARN("create svc ctx %p: accept user %u from %s\n",
- grctx->src_ctx, grctx->src_ctx->gsc_uid,
- libcfs_nid2str(req->rq_peer.nid));
-
- if (gw->gw_flags & LUSTRE_GSS_PACK_USER) {
- if (reqbuf->lm_bufcount < 4) {
- CERROR("missing user descriptor\n");
- return SECSVC_DROP;
- }
- if (sptlrpc_unpack_user_desc(reqbuf, 2, swabbed)) {
- CERROR("Mal-formed user descriptor\n");
- return SECSVC_DROP;
- }
-
- req->rq_pack_udesc = 1;
- req->rq_user_desc = lustre_msg_buf(reqbuf, 2, 0);
- }
-
- req->rq_reqmsg = lustre_msg_buf(reqbuf, 1, 0);
- req->rq_reqlen = lustre_msg_buflen(reqbuf, 1);
-
- return rc;
-}
-
-/*
- * last segment must be the gss signature.
- */
-static
-int gss_svc_verify_request(struct ptlrpc_request *req,
- struct gss_svc_reqctx *grctx,
- struct gss_wire_ctx *gw,
- __u32 *major)
-{
- struct gss_svc_ctx *gctx = grctx->src_ctx;
- struct lustre_msg *msg = req->rq_reqbuf;
- int offset = 2;
- int swabbed;
-
- *major = GSS_S_COMPLETE;
-
- if (msg->lm_bufcount < 2) {
- CERROR("Too few segments (%u) in request\n", msg->lm_bufcount);
- return -EINVAL;
- }
-
- if (gw->gw_svc == SPTLRPC_SVC_NULL)
- goto verified;
-
- if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 0)) {
- CERROR("phase 0: discard replayed req: seq %u\n", gw->gw_seq);
- *major = GSS_S_DUPLICATE_TOKEN;
- return -EACCES;
- }
-
- *major = gss_verify_msg(msg, gctx->gsc_mechctx, gw->gw_svc);
- if (*major != GSS_S_COMPLETE) {
- CERROR("failed to verify request: %x\n", *major);
- return -EACCES;
- }
-
- if (gctx->gsc_reverse == 0 &&
- gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 1)) {
- CERROR("phase 1+: discard replayed req: seq %u\n", gw->gw_seq);
- *major = GSS_S_DUPLICATE_TOKEN;
- return -EACCES;
- }
-
-verified:
- swabbed = ptlrpc_req_need_swab(req);
-
- /* user descriptor */
- if (gw->gw_flags & LUSTRE_GSS_PACK_USER) {
- if (msg->lm_bufcount < (offset + 1)) {
- CERROR("no user desc included\n");
- return -EINVAL;
- }
-
- if (sptlrpc_unpack_user_desc(msg, offset, swabbed)) {
- CERROR("Mal-formed user descriptor\n");
- return -EINVAL;
- }
-
- req->rq_pack_udesc = 1;
- req->rq_user_desc = lustre_msg_buf(msg, offset, 0);
- offset++;
- }
-
- /* check bulk_sec_desc data */
- if (gw->gw_flags & LUSTRE_GSS_PACK_BULK) {
- if (msg->lm_bufcount < (offset + 1)) {
- CERROR("missing bulk sec descriptor\n");
- return -EINVAL;
- }
-
- if (bulk_sec_desc_unpack(msg, offset, swabbed))
- return -EINVAL;
-
- req->rq_pack_bulk = 1;
- grctx->src_reqbsd = lustre_msg_buf(msg, offset, 0);
- grctx->src_reqbsd_size = lustre_msg_buflen(msg, offset);
- }
-
- req->rq_reqmsg = lustre_msg_buf(msg, 1, 0);
- req->rq_reqlen = msg->lm_buflens[1];
- return 0;
-}
-
-static
-int gss_svc_unseal_request(struct ptlrpc_request *req,
- struct gss_svc_reqctx *grctx,
- struct gss_wire_ctx *gw,
- __u32 *major)
-{
- struct gss_svc_ctx *gctx = grctx->src_ctx;
- struct lustre_msg *msg = req->rq_reqbuf;
- int swabbed, msglen, offset = 1;
-
- if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 0)) {
- CERROR("phase 0: discard replayed req: seq %u\n", gw->gw_seq);
- *major = GSS_S_DUPLICATE_TOKEN;
- return -EACCES;
- }
-
- *major = gss_unseal_msg(gctx->gsc_mechctx, msg,
- &msglen, req->rq_reqdata_len);
- if (*major != GSS_S_COMPLETE) {
- CERROR("failed to unwrap request: %x\n", *major);
- return -EACCES;
- }
-
- if (gss_check_seq_num(&gctx->gsc_seqdata, gw->gw_seq, 1)) {
- CERROR("phase 1+: discard replayed req: seq %u\n", gw->gw_seq);
- *major = GSS_S_DUPLICATE_TOKEN;
- return -EACCES;
- }
-
- swabbed = __lustre_unpack_msg(msg, msglen);
- if (swabbed < 0) {
- CERROR("Failed to unpack after decryption\n");
- return -EINVAL;
- }
- req->rq_reqdata_len = msglen;
-
- if (msg->lm_bufcount < 1) {
- CERROR("Invalid buffer: is empty\n");
- return -EINVAL;
- }
-
- if (gw->gw_flags & LUSTRE_GSS_PACK_USER) {
- if (msg->lm_bufcount < offset + 1) {
- CERROR("no user descriptor included\n");
- return -EINVAL;
- }
-
- if (sptlrpc_unpack_user_desc(msg, offset, swabbed)) {
- CERROR("Mal-formed user descriptor\n");
- return -EINVAL;
- }
-
- req->rq_pack_udesc = 1;
- req->rq_user_desc = lustre_msg_buf(msg, offset, 0);
- offset++;
- }
-
- if (gw->gw_flags & LUSTRE_GSS_PACK_BULK) {
- if (msg->lm_bufcount < offset + 1) {
- CERROR("no bulk checksum included\n");
- return -EINVAL;
- }
-
- if (bulk_sec_desc_unpack(msg, offset, swabbed))
- return -EINVAL;
-
- req->rq_pack_bulk = 1;
- grctx->src_reqbsd = lustre_msg_buf(msg, offset, 0);
- grctx->src_reqbsd_size = lustre_msg_buflen(msg, offset);
- }
-
- req->rq_reqmsg = lustre_msg_buf(req->rq_reqbuf, 0, 0);
- req->rq_reqlen = req->rq_reqbuf->lm_buflens[0];
- return 0;
-}
-
-static
-int gss_svc_handle_data(struct ptlrpc_request *req,
- struct gss_wire_ctx *gw)
-{
- struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- __u32 major = 0;
- int rc = 0;
-
- grctx->src_ctx = gss_svc_upcall_get_ctx(req, gw);
- if (!grctx->src_ctx) {
- major = GSS_S_NO_CONTEXT;
- goto error;
- }
-
- switch (gw->gw_svc) {
- case SPTLRPC_SVC_NULL:
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- rc = gss_svc_verify_request(req, grctx, gw, &major);
- break;
- case SPTLRPC_SVC_PRIV:
- rc = gss_svc_unseal_request(req, grctx, gw, &major);
- break;
- default:
- CERROR("unsupported gss service %d\n", gw->gw_svc);
- rc = -EINVAL;
- }
-
- if (rc == 0)
- return SECSVC_OK;
-
- CERROR("svc %u failed: major 0x%08x: req xid "LPU64" ctx %p idx "
- LPX64"(%u->%s)\n", gw->gw_svc, major, req->rq_xid,
- grctx->src_ctx, gss_handle_to_u64(&gw->gw_handle),
- grctx->src_ctx->gsc_uid, libcfs_nid2str(req->rq_peer.nid));
-error:
- /* we only notify client in case of NO_CONTEXT/BAD_SIG, which
- * might happen after server reboot, to allow recovery. */
- if ((major == GSS_S_NO_CONTEXT || major == GSS_S_BAD_SIG) &&
- gss_pack_err_notify(req, major, 0) == 0)
- return SECSVC_COMPLETE;
-
- return SECSVC_DROP;
-}
-
-static
-int gss_svc_handle_destroy(struct ptlrpc_request *req,
- struct gss_wire_ctx *gw)
-{
- struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- __u32 major;
-
- req->rq_ctx_fini = 1;
- req->rq_no_reply = 1;
-
- grctx->src_ctx = gss_svc_upcall_get_ctx(req, gw);
- if (!grctx->src_ctx) {
- CDEBUG(D_SEC, "invalid gss context handle for destroy.\n");
- return SECSVC_DROP;
- }
-
- if (gw->gw_svc != SPTLRPC_SVC_INTG) {
- CERROR("svc %u is not supported in destroy.\n", gw->gw_svc);
- return SECSVC_DROP;
- }
-
- if (gss_svc_verify_request(req, grctx, gw, &major))
- return SECSVC_DROP;
-
- CWARN("destroy svc ctx %p idx "LPX64" (%u->%s)\n",
- grctx->src_ctx, gss_handle_to_u64(&gw->gw_handle),
- grctx->src_ctx->gsc_uid, libcfs_nid2str(req->rq_peer.nid));
-
- gss_svc_upcall_destroy_ctx(grctx->src_ctx);
-
- if (gw->gw_flags & LUSTRE_GSS_PACK_USER) {
- if (req->rq_reqbuf->lm_bufcount < 4) {
- CERROR("missing user descriptor, ignore it\n");
- return SECSVC_OK;
- }
- if (sptlrpc_unpack_user_desc(req->rq_reqbuf, 2,
- ptlrpc_req_need_swab(req))) {
- CERROR("Mal-formed user descriptor, ignore it\n");
- return SECSVC_OK;
- }
-
- req->rq_pack_udesc = 1;
- req->rq_user_desc = lustre_msg_buf(req->rq_reqbuf, 2, 0);
- }
-
- return SECSVC_OK;
-}
-
-int gss_svc_accept(struct ptlrpc_sec_policy *policy, struct ptlrpc_request *req)
-{
- struct gss_header *ghdr;
- struct gss_svc_reqctx *grctx;
- struct gss_wire_ctx *gw;
- int swabbed, rc;
-
- LASSERT(req->rq_reqbuf);
- LASSERT(req->rq_svc_ctx == NULL);
-
- if (req->rq_reqbuf->lm_bufcount < 2) {
- CERROR("buf count only %d\n", req->rq_reqbuf->lm_bufcount);
- return SECSVC_DROP;
- }
-
- swabbed = ptlrpc_req_need_swab(req);
-
- ghdr = gss_swab_header(req->rq_reqbuf, 0, swabbed);
- if (ghdr == NULL) {
- CERROR("can't decode gss header\n");
- return SECSVC_DROP;
- }
-
- /* sanity checks */
- if (ghdr->gh_version != PTLRPC_GSS_VERSION) {
- CERROR("gss version %u, expect %u\n", ghdr->gh_version,
- PTLRPC_GSS_VERSION);
- return SECSVC_DROP;
- }
-
- req->rq_sp_from = ghdr->gh_sp;
-
- /* alloc grctx data */
- OBD_ALLOC_PTR(grctx);
- if (!grctx)
- return SECSVC_DROP;
-
- grctx->src_base.sc_policy = sptlrpc_policy_get(policy);
- atomic_set(&grctx->src_base.sc_refcount, 1);
- req->rq_svc_ctx = &grctx->src_base;
- gw = &grctx->src_wirectx;
-
- /* save wire context */
- gw->gw_flags = ghdr->gh_flags;
- gw->gw_proc = ghdr->gh_proc;
- gw->gw_seq = ghdr->gh_seq;
- gw->gw_svc = ghdr->gh_svc;
- rawobj_from_netobj(&gw->gw_handle, &ghdr->gh_handle);
-
- /* keep original wire header which subject to checksum verification */
- if (swabbed)
- gss_header_swabber(ghdr);
-
- switch (ghdr->gh_proc) {
- case PTLRPC_GSS_PROC_INIT:
- case PTLRPC_GSS_PROC_CONTINUE_INIT:
- rc = gss_svc_handle_init(req, gw);
- break;
- case PTLRPC_GSS_PROC_DATA:
- rc = gss_svc_handle_data(req, gw);
- break;
- case PTLRPC_GSS_PROC_DESTROY:
- rc = gss_svc_handle_destroy(req, gw);
- break;
- default:
- CERROR("unknown proc %u\n", gw->gw_proc);
- rc = SECSVC_DROP;
- break;
- }
-
- switch (rc) {
- case SECSVC_OK:
- LASSERT(grctx->src_ctx);
-
- req->rq_auth_gss = 1;
- req->rq_auth_remote = grctx->src_ctx->gsc_remote;
- req->rq_auth_usr_mdt = grctx->src_ctx->gsc_usr_mds;
- req->rq_auth_usr_ost = grctx->src_ctx->gsc_usr_oss;
- req->rq_auth_usr_root = grctx->src_ctx->gsc_usr_root;
- req->rq_auth_uid = grctx->src_ctx->gsc_uid;
- req->rq_auth_mapped_uid = grctx->src_ctx->gsc_mapped_uid;
- break;
- case SECSVC_COMPLETE:
- break;
- case SECSVC_DROP:
- gss_svc_reqctx_free(grctx);
- req->rq_svc_ctx = NULL;
- break;
- }
-
- return rc;
-}
-
-void gss_svc_invalidate_ctx(struct ptlrpc_svc_ctx *svc_ctx)
-{
- struct gss_svc_reqctx *grctx;
-
- if (svc_ctx == NULL) {
- return;
- }
-
- grctx = gss_svc_ctx2reqctx(svc_ctx);
-
- CWARN("gss svc invalidate ctx %p(%u)\n",
- grctx->src_ctx, grctx->src_ctx->gsc_uid);
- gss_svc_upcall_destroy_ctx(grctx->src_ctx);
-}
-
-static inline
-int gss_svc_payload(struct gss_svc_reqctx *grctx, int early,
- int msgsize, int privacy)
-{
- /* we should treat early reply normally, but which is actually sharing
- * the same ctx with original request, so in this case we should
- * ignore the special ctx's special flags */
- if (early == 0 && gss_svc_reqctx_is_special(grctx))
- return grctx->src_reserve_len;
-
- return gss_mech_payload(NULL, msgsize, privacy);
-}
-
-static int gss_svc_bulk_payload(struct gss_svc_ctx *gctx,
- struct sptlrpc_flavor *flvr,
- int read)
-{
- int payload = sizeof(struct ptlrpc_bulk_sec_desc);
-
- if (read) {
- switch (SPTLRPC_FLVR_BULK_SVC(flvr->sf_rpc)) {
- case SPTLRPC_BULK_SVC_NULL:
- break;
- case SPTLRPC_BULK_SVC_INTG:
- payload += gss_mech_payload(NULL, 0, 0);
- break;
- case SPTLRPC_BULK_SVC_PRIV:
- payload += gss_mech_payload(NULL, 0, 1);
- break;
- case SPTLRPC_BULK_SVC_AUTH:
- default:
- LBUG();
- }
- }
-
- return payload;
-}
-
-int gss_svc_alloc_rs(struct ptlrpc_request *req, int msglen)
-{
- struct gss_svc_reqctx *grctx;
- struct ptlrpc_reply_state *rs;
- int early, privacy, svc, bsd_off = 0;
- __u32 ibuflens[2], buflens[4];
- int ibufcnt = 0, bufcnt;
- int txtsize, wmsg_size, rs_size;
-
- LASSERT(msglen % 8 == 0);
-
- if (req->rq_pack_bulk && !req->rq_bulk_read && !req->rq_bulk_write) {
- CERROR("client request bulk sec on non-bulk rpc\n");
- return -EPROTO;
- }
-
- svc = SPTLRPC_FLVR_SVC(req->rq_flvr.sf_rpc);
- early = (req->rq_packed_final == 0);
-
- grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- if (!early && gss_svc_reqctx_is_special(grctx))
- privacy = 0;
- else
- privacy = (svc == SPTLRPC_SVC_PRIV);
-
- if (privacy) {
- /* inner clear buffers */
- ibufcnt = 1;
- ibuflens[0] = msglen;
-
- if (req->rq_pack_bulk) {
- LASSERT(grctx->src_reqbsd);
-
- bsd_off = ibufcnt;
- ibuflens[ibufcnt++] = gss_svc_bulk_payload(
- grctx->src_ctx,
- &req->rq_flvr,
- req->rq_bulk_read);
- }
-
- txtsize = lustre_msg_size_v2(ibufcnt, ibuflens);
- txtsize += GSS_MAX_CIPHER_BLOCK;
-
- /* wrapper buffer */
- bufcnt = 2;
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = gss_svc_payload(grctx, early, txtsize, 1);
- } else {
- bufcnt = 2;
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = msglen;
-
- txtsize = buflens[0];
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[1];
-
- if (req->rq_pack_bulk) {
- LASSERT(grctx->src_reqbsd);
-
- bsd_off = bufcnt;
- buflens[bufcnt] = gss_svc_bulk_payload(
- grctx->src_ctx,
- &req->rq_flvr,
- req->rq_bulk_read);
- if (svc == SPTLRPC_SVC_INTG)
- txtsize += buflens[bufcnt];
- bufcnt++;
- }
-
- if ((!early && gss_svc_reqctx_is_special(grctx)) ||
- svc != SPTLRPC_SVC_NULL)
- buflens[bufcnt++] = gss_svc_payload(grctx, early,
- txtsize, 0);
- }
-
- wmsg_size = lustre_msg_size_v2(bufcnt, buflens);
-
- rs_size = sizeof(*rs) + wmsg_size;
- rs = req->rq_reply_state;
-
- if (rs) {
- /* pre-allocated */
- LASSERT(rs->rs_size >= rs_size);
- } else {
- OBD_ALLOC_LARGE(rs, rs_size);
- if (rs == NULL)
- return -ENOMEM;
-
- rs->rs_size = rs_size;
- }
-
- rs->rs_repbuf = (struct lustre_msg *) (rs + 1);
- rs->rs_repbuf_len = wmsg_size;
-
- /* initialize the buffer */
- if (privacy) {
- lustre_init_msg_v2(rs->rs_repbuf, ibufcnt, ibuflens, NULL);
- rs->rs_msg = lustre_msg_buf(rs->rs_repbuf, 0, msglen);
- } else {
- lustre_init_msg_v2(rs->rs_repbuf, bufcnt, buflens, NULL);
- rs->rs_repbuf->lm_secflvr = req->rq_flvr.sf_rpc;
-
- rs->rs_msg = lustre_msg_buf(rs->rs_repbuf, 1, 0);
- }
-
- if (bsd_off) {
- grctx->src_repbsd = lustre_msg_buf(rs->rs_repbuf, bsd_off, 0);
- grctx->src_repbsd_size = lustre_msg_buflen(rs->rs_repbuf,
- bsd_off);
- }
-
- gss_svc_reqctx_addref(grctx);
- rs->rs_svc_ctx = req->rq_svc_ctx;
-
- LASSERT(rs->rs_msg);
- req->rq_reply_state = rs;
- return 0;
-}
-
-static int gss_svc_seal(struct ptlrpc_request *req,
- struct ptlrpc_reply_state *rs,
- struct gss_svc_reqctx *grctx)
-{
- struct gss_svc_ctx *gctx = grctx->src_ctx;
- rawobj_t hdrobj, msgobj, token;
- struct gss_header *ghdr;
- __u8 *token_buf;
- int token_buflen;
- __u32 buflens[2], major;
- int msglen, rc;
-
- /* get clear data length. note embedded lustre_msg might
- * have been shrunk */
- if (req->rq_replen != lustre_msg_buflen(rs->rs_repbuf, 0))
- msglen = lustre_shrink_msg(rs->rs_repbuf, 0, req->rq_replen, 1);
- else
- msglen = lustre_msg_size_v2(rs->rs_repbuf->lm_bufcount,
- rs->rs_repbuf->lm_buflens);
-
- /* temporarily use tail of buffer to hold gss header data */
- LASSERT(msglen + PTLRPC_GSS_HEADER_SIZE <= rs->rs_repbuf_len);
- ghdr = (struct gss_header *) ((char *) rs->rs_repbuf +
- rs->rs_repbuf_len - PTLRPC_GSS_HEADER_SIZE);
- ghdr->gh_version = PTLRPC_GSS_VERSION;
- ghdr->gh_sp = LUSTRE_SP_ANY;
- ghdr->gh_flags = 0;
- ghdr->gh_proc = PTLRPC_GSS_PROC_DATA;
- ghdr->gh_seq = grctx->src_wirectx.gw_seq;
- ghdr->gh_svc = SPTLRPC_SVC_PRIV;
- ghdr->gh_handle.len = 0;
- if (req->rq_pack_bulk)
- ghdr->gh_flags |= LUSTRE_GSS_PACK_BULK;
-
- /* allocate temporary cipher buffer */
- token_buflen = gss_mech_payload(gctx->gsc_mechctx, msglen, 1);
- OBD_ALLOC_LARGE(token_buf, token_buflen);
- if (token_buf == NULL)
- return -ENOMEM;
-
- hdrobj.len = PTLRPC_GSS_HEADER_SIZE;
- hdrobj.data = (__u8 *) ghdr;
- msgobj.len = msglen;
- msgobj.data = (__u8 *) rs->rs_repbuf;
- token.len = token_buflen;
- token.data = token_buf;
-
- major = lgss_wrap(gctx->gsc_mechctx, &hdrobj, &msgobj,
- rs->rs_repbuf_len - PTLRPC_GSS_HEADER_SIZE, &token);
- if (major != GSS_S_COMPLETE) {
- CERROR("wrap message error: %08x\n", major);
- GOTO(out_free, rc = -EPERM);
- }
- LASSERT(token.len <= token_buflen);
-
- /* we are about to override data at rs->rs_repbuf, nullify pointers
- * to which to catch further illegal usage. */
- if (req->rq_pack_bulk) {
- grctx->src_repbsd = NULL;
- grctx->src_repbsd_size = 0;
- }
-
- /* now fill the actual wire data
- * - gss header
- * - gss token
- */
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = token.len;
-
- rs->rs_repdata_len = lustre_msg_size_v2(2, buflens);
- LASSERT(rs->rs_repdata_len <= rs->rs_repbuf_len);
-
- lustre_init_msg_v2(rs->rs_repbuf, 2, buflens, NULL);
- rs->rs_repbuf->lm_secflvr = req->rq_flvr.sf_rpc;
-
- memcpy(lustre_msg_buf(rs->rs_repbuf, 0, 0), ghdr,
- PTLRPC_GSS_HEADER_SIZE);
- memcpy(lustre_msg_buf(rs->rs_repbuf, 1, 0), token.data, token.len);
-
- /* reply offset */
- if (req->rq_packed_final &&
- (lustre_msghdr_get_flags(req->rq_reqmsg) & MSGHDR_AT_SUPPORT))
- req->rq_reply_off = gss_at_reply_off_priv;
- else
- req->rq_reply_off = 0;
-
- /* to catch upper layer's further access */
- rs->rs_msg = NULL;
- req->rq_repmsg = NULL;
- req->rq_replen = 0;
-
- rc = 0;
-out_free:
- OBD_FREE_LARGE(token_buf, token_buflen);
- return rc;
-}
-
-int gss_svc_authorize(struct ptlrpc_request *req)
-{
- struct ptlrpc_reply_state *rs = req->rq_reply_state;
- struct gss_svc_reqctx *grctx = gss_svc_ctx2reqctx(req->rq_svc_ctx);
- struct gss_wire_ctx *gw = &grctx->src_wirectx;
- int early, rc;
-
- early = (req->rq_packed_final == 0);
-
- if (!early && gss_svc_reqctx_is_special(grctx)) {
- LASSERT(rs->rs_repdata_len != 0);
-
- req->rq_reply_off = gss_at_reply_off_integ;
- return 0;
- }
-
- /* early reply could happen in many cases */
- if (!early &&
- gw->gw_proc != PTLRPC_GSS_PROC_DATA &&
- gw->gw_proc != PTLRPC_GSS_PROC_DESTROY) {
- CERROR("proc %d not support\n", gw->gw_proc);
- return -EINVAL;
- }
-
- LASSERT(grctx->src_ctx);
-
- switch (gw->gw_svc) {
- case SPTLRPC_SVC_NULL:
- case SPTLRPC_SVC_AUTH:
- case SPTLRPC_SVC_INTG:
- rc = gss_svc_sign(req, rs, grctx, gw->gw_svc);
- break;
- case SPTLRPC_SVC_PRIV:
- rc = gss_svc_seal(req, rs, grctx);
- break;
- default:
- CERROR("Unknown service %d\n", gw->gw_svc);
- GOTO(out, rc = -EINVAL);
- }
- rc = 0;
-
-out:
- return rc;
-}
-
-void gss_svc_free_rs(struct ptlrpc_reply_state *rs)
-{
- struct gss_svc_reqctx *grctx;
-
- LASSERT(rs->rs_svc_ctx);
- grctx = container_of(rs->rs_svc_ctx, struct gss_svc_reqctx, src_base);
-
- gss_svc_reqctx_decref(grctx);
- rs->rs_svc_ctx = NULL;
-
- if (!rs->rs_prealloc)
- OBD_FREE_LARGE(rs, rs->rs_size);
-}
-
-void gss_svc_free_ctx(struct ptlrpc_svc_ctx *ctx)
-{
- LASSERT(atomic_read(&ctx->sc_refcount) == 0);
- gss_svc_reqctx_free(gss_svc_ctx2reqctx(ctx));
-}
-
-int gss_copy_rvc_cli_ctx(struct ptlrpc_cli_ctx *cli_ctx,
- struct ptlrpc_svc_ctx *svc_ctx)
-{
- struct gss_cli_ctx *cli_gctx = ctx2gctx(cli_ctx);
- struct gss_svc_ctx *svc_gctx = gss_svc_ctx2gssctx(svc_ctx);
- struct gss_ctx *mechctx = NULL;
-
- LASSERT(cli_gctx);
- LASSERT(svc_gctx && svc_gctx->gsc_mechctx);
-
- cli_gctx->gc_proc = PTLRPC_GSS_PROC_DATA;
- cli_gctx->gc_win = GSS_SEQ_WIN;
-
- /* The problem is the reverse ctx might get lost in some recovery
- * situations, and the same svc_ctx will be used to re-create it.
- * if there's callback be sentout before that, new reverse ctx start
- * with sequence 0 will lead to future callback rpc be treated as
- * replay.
- *
- * each reverse root ctx will record its latest sequence number on its
- * buddy svcctx before be destroyed, so here we continue use it.
- */
- atomic_set(&cli_gctx->gc_seq, svc_gctx->gsc_rvs_seq);
-
- if (gss_svc_upcall_dup_handle(&cli_gctx->gc_svc_handle, svc_gctx)) {
- CERROR("failed to dup svc handle\n");
- goto err_out;
- }
-
- if (lgss_copy_reverse_context(svc_gctx->gsc_mechctx, &mechctx) !=
- GSS_S_COMPLETE) {
- CERROR("failed to copy mech context\n");
- goto err_svc_handle;
- }
-
- if (rawobj_dup(&cli_gctx->gc_handle, &svc_gctx->gsc_rvs_hdl)) {
- CERROR("failed to dup reverse handle\n");
- goto err_ctx;
- }
-
- cli_gctx->gc_mechctx = mechctx;
- gss_cli_ctx_uptodate(cli_gctx);
-
- return 0;
-
-err_ctx:
- lgss_delete_sec_context(&mechctx);
-err_svc_handle:
- rawobj_free(&cli_gctx->gc_svc_handle);
-err_out:
- return -ENOMEM;
-}
-
-static void gss_init_at_reply_offset(void)
-{
- __u32 buflens[3];
- int clearsize;
-
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = lustre_msg_early_size();
- buflens[2] = gss_cli_payload(NULL, buflens[1], 0);
- gss_at_reply_off_integ = lustre_msg_size_v2(3, buflens);
-
- buflens[0] = lustre_msg_early_size();
- clearsize = lustre_msg_size_v2(1, buflens);
- buflens[0] = PTLRPC_GSS_HEADER_SIZE;
- buflens[1] = gss_cli_payload(NULL, clearsize, 0);
- buflens[2] = gss_cli_payload(NULL, clearsize, 1);
- gss_at_reply_off_priv = lustre_msg_size_v2(3, buflens);
-}
-
-int __init sptlrpc_gss_init(void)
-{
- int rc;
-
- rc = gss_init_lproc();
- if (rc)
- return rc;
-
- rc = gss_init_cli_upcall();
- if (rc)
- goto out_lproc;
-
- rc = gss_init_svc_upcall();
- if (rc)
- goto out_cli_upcall;
-
- rc = init_kerberos_module();
- if (rc)
- goto out_svc_upcall;
-
- /* register policy after all other stuff be initialized, because it
- * might be in used immediately after the registration. */
-
- rc = gss_init_keyring();
- if (rc)
- goto out_kerberos;
-
-#ifdef HAVE_GSS_PIPEFS
- rc = gss_init_pipefs();
- if (rc)
- goto out_keyring;
-#endif
-
- gss_init_at_reply_offset();
-
- return 0;
-
-#ifdef HAVE_GSS_PIPEFS
-out_keyring:
- gss_exit_keyring();
-#endif
-
-out_kerberos:
- cleanup_kerberos_module();
-out_svc_upcall:
- gss_exit_svc_upcall();
-out_cli_upcall:
- gss_exit_cli_upcall();
-out_lproc:
- gss_exit_lproc();
- return rc;
-}
-
-static void __exit sptlrpc_gss_exit(void)
-{
- gss_exit_keyring();
-#ifdef HAVE_GSS_PIPEFS
- gss_exit_pipefs();
-#endif
- cleanup_kerberos_module();
- gss_exit_svc_upcall();
- gss_exit_cli_upcall();
- gss_exit_lproc();
-}
-
-MODULE_AUTHOR("Sun Microsystems, Inc. <http://www.lustre.org/>");
-MODULE_DESCRIPTION("GSS security policy for Lustre");
-MODULE_LICENSE("GPL");
-
-module_init(sptlrpc_gss_init);
-module_exit(sptlrpc_gss_exit);