📄 svcauth_gss.c
字号:
gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, u32 *rpcstart, struct rpc_gss_wire_cred *gc, u32 *authp){ struct gss_ctx *ctx_id = rsci->mechctx; struct xdr_buf rpchdr; struct xdr_netobj checksum; u32 flavor = 0; struct kvec *argv = &rqstp->rq_arg.head[0]; struct kvec iov; /* data to compute the checksum over: */ iov.iov_base = rpcstart; iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart; xdr_buf_from_iov(&iov, &rpchdr); *authp = rpc_autherr_badverf; if (argv->iov_len < 4) return SVC_DENIED; flavor = ntohl(svc_getu32(argv)); if (flavor != RPC_AUTH_GSS) return SVC_DENIED; if (svc_safe_getnetobj(argv, &checksum)) return SVC_DENIED; if (rqstp->rq_deferred) /* skip verification of revisited request */ return SVC_OK; if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) { *authp = rpcsec_gsserr_credproblem; return SVC_DENIED; } if (gc->gc_seq > MAXSEQ) { dprintk("RPC: svcauth_gss: discarding request with large sequence number %d\n", gc->gc_seq); *authp = rpcsec_gsserr_ctxproblem; return SVC_DENIED; } if (!gss_check_seq_num(rsci, gc->gc_seq)) { dprintk("RPC: svcauth_gss: discarding request with old sequence number %d\n", gc->gc_seq); return SVC_DROP; } return SVC_OK;}static intgss_write_verf(struct svc_rqst *rqstp, struct gss_ctx *ctx_id, u32 seq){ u32 xdr_seq; u32 maj_stat; struct xdr_buf verf_data; struct xdr_netobj mic; u32 *p; struct kvec iov; svc_putu32(rqstp->rq_res.head, htonl(RPC_AUTH_GSS)); xdr_seq = htonl(seq); iov.iov_base = &xdr_seq; iov.iov_len = sizeof(xdr_seq); xdr_buf_from_iov(&iov, &verf_data); p = rqstp->rq_res.head->iov_base + rqstp->rq_res.head->iov_len; mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx_id, &verf_data, &mic); if (maj_stat != GSS_S_COMPLETE) return -1; *p++ = htonl(mic.len); memset((u8 *)p + mic.len, 0, round_up_to_quad(mic.len) - mic.len); p += XDR_QUADLEN(mic.len); if (!xdr_ressize_check(rqstp, p)) return -1; return 0;}struct gss_domain { struct auth_domain h; u32 pseudoflavor;};static struct auth_domain *find_gss_auth_domain(struct gss_ctx *ctx, u32 svc){ char *name; name = gss_service_to_auth_domain_name(ctx->mech_type, svc); if (!name) return NULL; return auth_domain_find(name);}intsvcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name){ struct gss_domain *new; struct auth_domain *test; int stat = -ENOMEM; new = kmalloc(sizeof(*new), GFP_KERNEL); if (!new) goto out; cache_init(&new->h.h); new->h.name = kmalloc(strlen(name) + 1, GFP_KERNEL); if (!new->h.name) goto out_free_dom; strcpy(new->h.name, name); new->h.flavour = RPC_AUTH_GSS; new->pseudoflavor = pseudoflavor; new->h.h.expiry_time = NEVER; test = auth_domain_lookup(&new->h, 1); if (test == &new->h) { BUG_ON(atomic_dec_and_test(&new->h.h.refcnt)); } else { /* XXX Duplicate registration? */ auth_domain_put(&new->h); goto out; } return 0;out_free_dom: kfree(new);out: return stat;}EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);static inline intread_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj){ u32 raw; int status; status = read_bytes_from_xdr_buf(buf, base, &raw, sizeof(*obj)); if (status) return status; *obj = ntohl(raw); return 0;}/* It would be nice if this bit of code could be shared with the client. * Obstacles: * The client shouldn't malloc(), would have to pass in own memory. * The server uses base of head iovec as read pointer, while the * client uses separate pointer. */static intunwrap_integ_data(struct xdr_buf *buf, u32 seq, struct gss_ctx *ctx){ int stat = -EINVAL; u32 integ_len, maj_stat; struct xdr_netobj mic; struct xdr_buf integ_buf; integ_len = ntohl(svc_getu32(&buf->head[0])); if (integ_len & 3) goto out; if (integ_len > buf->len) goto out; if (xdr_buf_subsegment(buf, &integ_buf, 0, integ_len)) BUG(); /* copy out mic... */ if (read_u32_from_xdr_buf(buf, integ_len, &mic.len)) BUG(); if (mic.len > RPC_MAX_AUTH_SIZE) goto out; mic.data = kmalloc(mic.len, GFP_KERNEL); if (!mic.data) goto out; if (read_bytes_from_xdr_buf(buf, integ_len + 4, mic.data, mic.len)) goto out; maj_stat = gss_verify_mic(ctx, &integ_buf, &mic); if (maj_stat != GSS_S_COMPLETE) goto out; if (ntohl(svc_getu32(&buf->head[0])) != seq) goto out; stat = 0;out: return stat;}struct gss_svc_data { /* decoded gss client cred: */ struct rpc_gss_wire_cred clcred; /* pointer to the beginning of the procedure-specific results, * which may be encrypted/checksummed in svcauth_gss_release: */ u32 *body_start; struct rsc *rsci;};static intsvcauth_gss_set_client(struct svc_rqst *rqstp){ struct gss_svc_data *svcdata = rqstp->rq_auth_data; struct rsc *rsci = svcdata->rsci; struct rpc_gss_wire_cred *gc = &svcdata->clcred; rqstp->rq_client = find_gss_auth_domain(rsci->mechctx, gc->gc_svc); if (rqstp->rq_client == NULL) return SVC_DENIED; return SVC_OK;}/* * Accept an rpcsec packet. * If context establishment, punt to user space * If data exchange, verify/decrypt * If context destruction, handle here * In the context establishment and destruction case we encode * response here and return SVC_COMPLETE. */static intsvcauth_gss_accept(struct svc_rqst *rqstp, u32 *authp){ struct kvec *argv = &rqstp->rq_arg.head[0]; struct kvec *resv = &rqstp->rq_res.head[0]; u32 crlen; struct xdr_netobj tmpobj; struct gss_svc_data *svcdata = rqstp->rq_auth_data; struct rpc_gss_wire_cred *gc; struct rsc *rsci = NULL; struct rsi *rsip, rsikey; u32 *rpcstart; u32 *reject_stat = resv->iov_base + resv->iov_len; int ret; dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n",argv->iov_len); *authp = rpc_autherr_badcred; if (!svcdata) svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL); if (!svcdata) goto auth_err; rqstp->rq_auth_data = svcdata; svcdata->body_start = NULL; svcdata->rsci = NULL; gc = &svcdata->clcred; /* start of rpc packet is 7 u32's back from here: * xid direction rpcversion prog vers proc flavour */ rpcstart = argv->iov_base; rpcstart -= 7; /* credential is: * version(==1), proc(0,1,2,3), seq, service (1,2,3), handle * at least 5 u32s, and is preceeded by length, so that makes 6. */ if (argv->iov_len < 5 * 4) goto auth_err; crlen = ntohl(svc_getu32(argv)); if (ntohl(svc_getu32(argv)) != RPC_GSS_VERSION) goto auth_err; gc->gc_proc = ntohl(svc_getu32(argv)); gc->gc_seq = ntohl(svc_getu32(argv)); gc->gc_svc = ntohl(svc_getu32(argv)); if (svc_safe_getnetobj(argv, &gc->gc_ctx)) goto auth_err; if (crlen != round_up_to_quad(gc->gc_ctx.len) + 5 * 4) goto auth_err; if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0)) goto auth_err; /* * We've successfully parsed the credential. Let's check out the * verifier. An AUTH_NULL verifier is allowed (and required) for * INIT and CONTINUE_INIT requests. AUTH_RPCSEC_GSS is required for * PROC_DATA and PROC_DESTROY. * * AUTH_NULL verifier is 0 (AUTH_NULL), 0 (length). * AUTH_RPCSEC_GSS verifier is: * 6 (AUTH_RPCSEC_GSS), length, checksum. * checksum is calculated over rpcheader from xid up to here. */ *authp = rpc_autherr_badverf; switch (gc->gc_proc) { case RPC_GSS_PROC_INIT: case RPC_GSS_PROC_CONTINUE_INIT: if (argv->iov_len < 2 * 4) goto auth_err; if (ntohl(svc_getu32(argv)) != RPC_AUTH_NULL) goto auth_err; if (ntohl(svc_getu32(argv)) != 0) goto auth_err; break; case RPC_GSS_PROC_DATA: case RPC_GSS_PROC_DESTROY: *authp = rpcsec_gsserr_credproblem; rsci = gss_svc_searchbyctx(&gc->gc_ctx); if (!rsci) goto auth_err; switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { case SVC_OK: break; case SVC_DENIED: goto auth_err; case SVC_DROP: goto drop; } break; default: *authp = rpc_autherr_rejectedcred; goto auth_err; } /* now act upon the command: */ switch (gc->gc_proc) { case RPC_GSS_PROC_INIT: case RPC_GSS_PROC_CONTINUE_INIT: *authp = rpc_autherr_badcred; if (gc->gc_proc == RPC_GSS_PROC_INIT && gc->gc_ctx.len != 0) goto auth_err; memset(&rsikey, 0, sizeof(rsikey)); if (dup_netobj(&rsikey.in_handle, &gc->gc_ctx)) goto drop; *authp = rpc_autherr_badverf; if (svc_safe_getnetobj(argv, &tmpobj)) { kfree(rsikey.in_handle.data); goto auth_err; } if (dup_netobj(&rsikey.in_token, &tmpobj)) { kfree(rsikey.in_handle.data); goto drop; } rsip = rsi_lookup(&rsikey, 0); rsi_free(&rsikey); if (!rsip) { goto drop; } switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) { case -EAGAIN: goto drop; case -ENOENT: goto drop; case 0: rsci = gss_svc_searchbyctx(&rsip->out_handle); if (!rsci) { goto drop; } if (gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN)) goto drop; if (resv->iov_len + 4 > PAGE_SIZE) goto drop; svc_putu32(resv, rpc_success); if (svc_safe_putnetobj(resv, &rsip->out_handle)) goto drop; if (resv->iov_len + 3 * 4 > PAGE_SIZE) goto drop; svc_putu32(resv, htonl(rsip->major_status)); svc_putu32(resv, htonl(rsip->minor_status)); svc_putu32(resv, htonl(GSS_SEQ_WIN)); if (svc_safe_putnetobj(resv, &rsip->out_token)) goto drop; rqstp->rq_client = NULL; } goto complete; case RPC_GSS_PROC_DESTROY: set_bit(CACHE_NEGATIVE, &rsci->h.flags); if (resv->iov_len + 4 > PAGE_SIZE) goto drop; svc_putu32(resv, rpc_success); goto complete; case RPC_GSS_PROC_DATA: *authp = rpcsec_gsserr_ctxproblem; if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) goto auth_err; rqstp->rq_cred = rsci->cred; get_group_info(rsci->cred.cr_group_info); *authp = rpc_autherr_badcred; switch (gc->gc_svc) { case RPC_GSS_SVC_NONE: break; case RPC_GSS_SVC_INTEGRITY: if (unwrap_integ_data(&rqstp->rq_arg, gc->gc_seq, rsci->mechctx)) goto auth_err; /* placeholders for length and seq. number: */ svcdata->body_start = resv->iov_base + resv->iov_len; svc_putu32(resv, 0); svc_putu32(resv, 0); break; case RPC_GSS_SVC_PRIVACY: /* currently unsupported */ default: goto auth_err; } svcdata->rsci = rsci; cache_get(&rsci->h); ret = SVC_OK; goto out; }auth_err: /* Restore write pointer to original value: */ xdr_ressize_check(rqstp, reject_stat); ret = SVC_DENIED; goto out;complete: ret = SVC_COMPLETE; goto out;drop: ret = SVC_DROP;out: if (rsci) rsc_put(&rsci->h, &rsc_cache); return ret;}static intsvcauth_gss_release(struct svc_rqst *rqstp){ struct gss_svc_data *gsd = (struct gss_svc_data *)rqstp->rq_auth_data; struct rpc_gss_wire_cred *gc = &gsd->clcred; struct xdr_buf *resbuf = &rqstp->rq_res; struct xdr_buf integ_buf; struct xdr_netobj mic; struct kvec *resv; u32 *p; int integ_offset, integ_len; int stat = -EINVAL; if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; /* Release can be called twice, but we only wrap once. */ if (gsd->body_start == NULL) goto out; /* normally not set till svc_send, but we need it here: */ resbuf->len = resbuf->head[0].iov_len + resbuf->page_len + resbuf->tail[0].iov_len; switch (gc->gc_svc) { case RPC_GSS_SVC_NONE: break; case RPC_GSS_SVC_INTEGRITY: p = gsd->body_start; gsd->body_start = NULL; /* move accept_stat to right place: */ memcpy(p, p + 2, 4); /* don't wrap in failure case: */ /* Note: counting on not getting here if call was not even * accepted! */ if (*p != rpc_success) { resbuf->head[0].iov_len -= 2 * 4; goto out; } p++; integ_offset = (u8 *)(p + 1) - (u8 *)resbuf->head[0].iov_base; integ_len = resbuf->len - integ_offset; BUG_ON(integ_len % 4); *p++ = htonl(integ_len); *p++ = htonl(gc->gc_seq); if (xdr_buf_subsegment(resbuf, &integ_buf, integ_offset, integ_len)) BUG(); if (resbuf->page_len == 0 && resbuf->tail[0].iov_len + RPC_MAX_AUTH_SIZE < PAGE_SIZE) { BUG_ON(resbuf->tail[0].iov_len); /* Use head for everything */ resv = &resbuf->head[0]; } else if (resbuf->tail[0].iov_base == NULL) { /* copied from nfsd4_encode_read */ svc_take_page(rqstp); resbuf->tail[0].iov_base = page_address(rqstp ->rq_respages[rqstp->rq_resused-1]); rqstp->rq_restailpage = rqstp->rq_resused-1; resbuf->tail[0].iov_len = 0; resv = &resbuf->tail[0]; } else { resv = &resbuf->tail[0]; } mic.data = (u8 *)resv->iov_base + resv->iov_len + 4; if (gss_get_mic(gsd->rsci->mechctx, &integ_buf, &mic)) goto out_err; svc_putu32(resv, htonl(mic.len)); memset(mic.data + mic.len, 0, round_up_to_quad(mic.len) - mic.len); resv->iov_len += XDR_QUADLEN(mic.len) << 2; /* not strictly required: */ resbuf->len += XDR_QUADLEN(mic.len) << 2; BUG_ON(resv->iov_len > PAGE_SIZE); break; case RPC_GSS_SVC_PRIVACY: default: goto out_err; }out: stat = 0;out_err: if (rqstp->rq_client) auth_domain_put(rqstp->rq_client); rqstp->rq_client = NULL; if (rqstp->rq_cred.cr_group_info) put_group_info(rqstp->rq_cred.cr_group_info); rqstp->rq_cred.cr_group_info = NULL; if (gsd->rsci) rsc_put(&gsd->rsci->h, &rsc_cache); gsd->rsci = NULL; return stat;}static voidsvcauth_gss_domain_release(struct auth_domain *dom){ struct gss_domain *gd = container_of(dom, struct gss_domain, h); kfree(dom->name); kfree(gd);}static struct auth_ops svcauthops_gss = { .name = "rpcsec_gss", .owner = THIS_MODULE, .flavour = RPC_AUTH_GSS, .accept = svcauth_gss_accept, .release = svcauth_gss_release, .domain_release = svcauth_gss_domain_release, .set_client = svcauth_gss_set_client,};intgss_svc_init(void){ int rv = svc_auth_register(RPC_AUTH_GSS, &svcauthops_gss); if (rv == 0) { cache_register(&rsc_cache); cache_register(&rsi_cache); } return rv;}voidgss_svc_shutdown(void){ if (cache_unregister(&rsc_cache)) printk(KERN_ERR "auth_rpcgss: failed to unregister rsc cache\n"); if (cache_unregister(&rsi_cache)) printk(KERN_ERR "auth_rpcgss: failed to unregister rsi cache\n"); svc_auth_unregister(RPC_AUTH_GSS);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -