| /* |
| * 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; |
| voff = vmsg->lm_bufcount - 1; |
| LASSERT(vmsg && vmsg->lm_bufcount >= 3); |
| |
| rmsg = req->rq_reqbuf; |
| roff = rmsg->lm_bufcount - 1; /* last segment */ |
| LASSERT(rmsg && rmsg->lm_bufcount >= 3); |
| break; |
| case SPTLRPC_SVC_AUTH: |
| case SPTLRPC_SVC_INTG: |
| vmsg = req->rq_repdata; |
| voff = vmsg->lm_bufcount - 2; |
| LASSERT(vmsg && vmsg->lm_bufcount >= 4); |
| |
| rmsg = req->rq_reqbuf; |
| roff = rmsg->lm_bufcount - 2; /* second last segment */ |
| LASSERT(rmsg && rmsg->lm_bufcount >= 4); |
| break; |
| case SPTLRPC_SVC_PRIV: |
| vmsg = req->rq_repdata; |
| voff = vmsg->lm_bufcount - 1; |
| LASSERT(vmsg && vmsg->lm_bufcount >= 2); |
| |
| rmsg = req->rq_clrbuf; |
| roff = rmsg->lm_bufcount - 1; /* last segment */ |
| LASSERT(rmsg && rmsg->lm_bufcount >= 2); |
| 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; |
| } |