📄 svcauth_gss.c
字号:
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;}static inline intgss_write_init_verf(struct svc_rqst *rqstp, struct rsi *rsip){ struct rsc *rsci; int rc; if (rsip->major_status != GSS_S_COMPLETE) return gss_write_null_verf(rqstp); rsci = gss_svc_searchbyctx(&rsip->out_handle); if (rsci == NULL) { rsip->major_status = GSS_S_NO_CONTEXT; return gss_write_null_verf(rqstp); } rc = gss_write_verf(rqstp, rsci->mechctx, GSS_SEQ_WIN); cache_put(&rsci->h, &rsc_cache); return rc;}/* * 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, __be32 *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; __be32 *rpcstart; __be32 *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->verf_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 = svc_getnl(argv); if (svc_getnl(argv) != RPC_GSS_VERSION) goto auth_err; gc->gc_proc = svc_getnl(argv); gc->gc_seq = svc_getnl(argv); gc->gc_svc = svc_getnl(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 (svc_getnl(argv) != RPC_AUTH_NULL) goto auth_err; if (svc_getnl(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); rsi_free(&rsikey); if (!rsip) { goto drop; } switch(cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) { case -EAGAIN: case -ETIMEDOUT: case -ENOENT: goto drop; case 0: if (gss_write_init_verf(rqstp, rsip)) goto drop; if (resv->iov_len + 4 > PAGE_SIZE) goto drop; svc_putnl(resv, RPC_SUCCESS); if (svc_safe_putnetobj(resv, &rsip->out_handle)) goto drop; if (resv->iov_len + 3 * 4 > PAGE_SIZE) goto drop; svc_putnl(resv, rsip->major_status); svc_putnl(resv, rsip->minor_status); svc_putnl(resv, GSS_SEQ_WIN); if (svc_safe_putnetobj(resv, &rsip->out_token)) goto drop; rqstp->rq_client = NULL; } goto complete; case RPC_GSS_PROC_DESTROY: if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq)) goto auth_err; set_bit(CACHE_NEGATIVE, &rsci->h.flags); if (resv->iov_len + 4 > PAGE_SIZE) goto drop; svc_putnl(resv, RPC_SUCCESS); goto complete; case RPC_GSS_PROC_DATA: *authp = rpcsec_gsserr_ctxproblem; svcdata->verf_start = resv->iov_base + resv->iov_len; 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: */ svc_putnl(resv, 0); svc_putnl(resv, 0); break; case RPC_GSS_SVC_PRIVACY: if (unwrap_priv_data(rqstp, &rqstp->rq_arg, gc->gc_seq, rsci->mechctx)) goto auth_err; /* placeholders for length and seq. number: */ svc_putnl(resv, 0); svc_putnl(resv, 0); break; 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) cache_put(&rsci->h, &rsc_cache); return ret;}static __be32 *svcauth_gss_prepare_to_wrap(struct xdr_buf *resbuf, struct gss_svc_data *gsd){ __be32 *p; u32 verf_len; p = gsd->verf_start; gsd->verf_start = NULL; /* If the reply stat is nonzero, don't wrap: */ if (*(p-1) != rpc_success) return NULL; /* Skip the verifier: */ p += 1; verf_len = ntohl(*p++); p += XDR_QUADLEN(verf_len); /* move accept_stat to right place: */ memcpy(p, p + 2, 4); /* Also don't wrap if the accept stat is nonzero: */ if (*p != rpc_success) { resbuf->head[0].iov_len -= 2 * 4; return NULL; } p++; return p;}static inline intsvcauth_gss_wrap_resp_integ(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; __be32 *p; int integ_offset, integ_len; int stat = -EINVAL; p = svcauth_gss_prepare_to_wrap(resbuf, gsd); if (p == NULL) goto out; 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->tail[0].iov_base == NULL) { if (resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE > PAGE_SIZE) goto out_err; resbuf->tail[0].iov_base = resbuf->head[0].iov_base + resbuf->head[0].iov_len; 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_putnl(resv, 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);out: stat = 0;out_err: return stat;}static inline intsvcauth_gss_wrap_resp_priv(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 page **inpages = NULL; __be32 *p, *len; int offset; int pad; p = svcauth_gss_prepare_to_wrap(resbuf, gsd); if (p == NULL) return 0; len = p++; offset = (u8 *)p - (u8 *)resbuf->head[0].iov_base; *p++ = htonl(gc->gc_seq); inpages = resbuf->pages; /* XXX: Would be better to write some xdr helper functions for * nfs{2,3,4}xdr.c that place the data right, instead of copying: */ if (resbuf->tail[0].iov_base) { BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base + PAGE_SIZE); BUG_ON(resbuf->tail[0].iov_base < resbuf->head[0].iov_base); if (resbuf->tail[0].iov_len + resbuf->head[0].iov_len + 2 * RPC_MAX_AUTH_SIZE > PAGE_SIZE) return -ENOMEM; memmove(resbuf->tail[0].iov_base + RPC_MAX_AUTH_SIZE, resbuf->tail[0].iov_base, resbuf->tail[0].iov_len); resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE; } if (resbuf->tail[0].iov_base == NULL) { if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE) return -ENOMEM; resbuf->tail[0].iov_base = resbuf->head[0].iov_base + resbuf->head[0].iov_len + RPC_MAX_AUTH_SIZE; resbuf->tail[0].iov_len = 0; } if (gss_wrap(gsd->rsci->mechctx, offset, resbuf, inpages)) return -ENOMEM; *len = htonl(resbuf->len - offset); pad = 3 - ((resbuf->len - offset - 1)&3); p = (__be32 *)(resbuf->tail[0].iov_base + resbuf->tail[0].iov_len); memset(p, 0, pad); resbuf->tail[0].iov_len += pad; resbuf->len += pad; return 0;}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; 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->verf_start == NULL) goto out; /* normally not set till svc_send, but we need it here: */ /* XXX: what for? Do we mess it up the moment we call svc_putu32 * or whatever? */ resbuf->len = total_buf_len(resbuf); switch (gc->gc_svc) { case RPC_GSS_SVC_NONE: break; case RPC_GSS_SVC_INTEGRITY: stat = svcauth_gss_wrap_resp_integ(rqstp); if (stat) goto out_err; break; case RPC_GSS_SVC_PRIVACY: stat = svcauth_gss_wrap_resp_priv(rqstp); if (stat) goto out_err; break; 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) cache_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 + -