From: Trond Myklebust RPCSEC_GSS: Client-side only support for rpcsec_gss integrity protection. Since this requires checksumming an entire request, instead of just the header, and since the request may include, for example, pages with write data, we modify the gss_api routines to pass xdr_buf's instead of xdr_netobj's where necessary. We add rpcauth_wrap_req and rpcauth_unwrap_resp to rpcauth.c, wrappers for the new rpc cred ops crwrap_req and crunwrap_req, which are called just before encoding, and just after decoding, respectively. --- include/linux/sunrpc/auth.h | 6 include/linux/sunrpc/gss_api.h | 9 - include/linux/sunrpc/gss_krb5.h | 6 include/linux/sunrpc/xdr.h | 4 net/sunrpc/auth.c | 29 ++++ net/sunrpc/auth_gss/auth_gss.c | 231 ++++++++++++++++++++++++++++------ net/sunrpc/auth_gss/gss_krb5_crypto.c | 51 ++++++- net/sunrpc/auth_gss/gss_krb5_mech.c | 8 - net/sunrpc/auth_gss/gss_krb5_seal.c | 23 --- net/sunrpc/auth_gss/gss_krb5_unseal.c | 58 +------- net/sunrpc/auth_gss/gss_mech_switch.c | 4 net/sunrpc/clnt.c | 6 net/sunrpc/sunrpc_syms.c | 3 net/sunrpc/xdr.c | 144 +++++++++++++++++++++ 14 files changed, 450 insertions(+), 132 deletions(-) diff -puN include/linux/sunrpc/auth.h~nfs-13-krb5_integ include/linux/sunrpc/auth.h --- 25/include/linux/sunrpc/auth.h~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/include/linux/sunrpc/auth.h 2004-01-09 22:16:15.000000000 -0800 @@ -102,6 +102,10 @@ struct rpc_credops { u32 * (*crmarshal)(struct rpc_task *, u32 *, int); int (*crrefresh)(struct rpc_task *); u32 * (*crvalidate)(struct rpc_task *, u32 *); + int (*crwrap_req)(struct rpc_task *, kxdrproc_t, + void *, u32 *, void *); + int (*crunwrap_resp)(struct rpc_task *, kxdrproc_t, + void *, u32 *, void *); }; extern struct rpc_authops authunix_ops; @@ -124,6 +128,8 @@ void put_rpccred(struct rpc_cred *); void rpcauth_unbindcred(struct rpc_task *); u32 * rpcauth_marshcred(struct rpc_task *, u32 *); u32 * rpcauth_checkverf(struct rpc_task *, u32 *); +int rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, u32 *data, void *obj); +int rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, u32 *data, void *obj); int rpcauth_refreshcred(struct rpc_task *); void rpcauth_invalcred(struct rpc_task *); int rpcauth_uptodatecred(struct rpc_task *); diff -puN include/linux/sunrpc/gss_api.h~nfs-13-krb5_integ include/linux/sunrpc/gss_api.h --- 25/include/linux/sunrpc/gss_api.h~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/include/linux/sunrpc/gss_api.h 2004-01-09 22:16:15.000000000 -0800 @@ -16,6 +16,7 @@ #ifdef __KERNEL__ #include +#include /* The mechanism-independent gss-api context: */ struct gss_ctx { @@ -39,11 +40,11 @@ u32 gss_import_sec_context( u32 gss_get_mic( struct gss_ctx *ctx_id, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token); u32 gss_verify_mic( struct gss_ctx *ctx_id, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate); u32 gss_delete_sec_context( @@ -95,11 +96,11 @@ struct gss_api_ops { u32 (*gss_get_mic)( struct gss_ctx *ctx_id, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token); u32 (*gss_verify_mic)( struct gss_ctx *ctx_id, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate); void (*gss_delete_sec_context)( diff -puN include/linux/sunrpc/gss_krb5.h~nfs-13-krb5_integ include/linux/sunrpc/gss_krb5.h --- 25/include/linux/sunrpc/gss_krb5.h~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/include/linux/sunrpc/gss_krb5.h 2004-01-09 22:16:15.000000000 -0800 @@ -115,18 +115,18 @@ enum seal_alg { #define ENCTYPE_UNKNOWN 0x01ff s32 -krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, +krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body, struct xdr_netobj *cksum); u32 krb5_make_token(struct krb5_ctx *context_handle, int qop_req, - struct xdr_netobj *input_message_buffer, + struct xdr_buf *input_message_buffer, struct xdr_netobj *output_message_buffer, int toktype); u32 krb5_read_token(struct krb5_ctx *context_handle, struct xdr_netobj *input_token_buffer, - struct xdr_netobj *message_buffer, + struct xdr_buf *message_buffer, int *qop_state, int toktype); u32 diff -puN include/linux/sunrpc/xdr.h~nfs-13-krb5_integ include/linux/sunrpc/xdr.h --- 25/include/linux/sunrpc/xdr.h~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/include/linux/sunrpc/xdr.h 2004-01-09 22:16:15.000000000 -0800 @@ -141,6 +141,10 @@ void xdr_shift_iovec(struct iovec *, int extern int xdr_kmap(struct iovec *, struct xdr_buf *, size_t); extern void xdr_kunmap(struct xdr_buf *, size_t); extern void xdr_shift_buf(struct xdr_buf *, size_t); +extern void _copy_from_pages(char *, struct page **, size_t, size_t); +extern void xdr_buf_from_iov(struct iovec *, struct xdr_buf *); +extern int xdr_buf_subsegment(struct xdr_buf *, struct xdr_buf *, int, int); +extern int xdr_buf_read_netobj(struct xdr_buf *, struct xdr_netobj *, int); /* * Helper structure for copying from an sk_buff. diff -puN net/sunrpc/auth.c~nfs-13-krb5_integ net/sunrpc/auth.c --- 25/net/sunrpc/auth.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth.c 2004-01-09 22:16:15.000000000 -0800 @@ -340,6 +340,35 @@ rpcauth_checkverf(struct rpc_task *task, } int +rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, + u32 *data, void *obj) +{ + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d using %s cred %p to wrap rpc data\n", + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); + if (cred->cr_ops->crwrap_req) + return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); + /* By default, we encode the arguments normally. */ + return encode(rqstp, data, obj); +} + +int +rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, + u32 *data, void *obj) +{ + struct rpc_cred *cred = task->tk_msg.rpc_cred; + + dprintk("RPC: %4d using %s cred %p to unwrap rpc data\n", + task->tk_pid, cred->cr_auth->au_ops->au_name, cred); + if (cred->cr_ops->crunwrap_resp) + return cred->cr_ops->crunwrap_resp(task, decode, rqstp, + data, obj); + /* By default, we decode the arguments normally. */ + return decode(rqstp, data, obj); +} + +int rpcauth_refreshcred(struct rpc_task *task) { struct rpc_auth *auth = task->tk_auth; diff -puN net/sunrpc/auth_gss/auth_gss.c~nfs-13-krb5_integ net/sunrpc/auth_gss/auth_gss.c --- 25/net/sunrpc/auth_gss/auth_gss.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/auth_gss.c 2004-01-09 22:16:15.000000000 -0800 @@ -51,6 +51,7 @@ #include #include #include +#include #include static struct rpc_authops authgss_ops; @@ -65,7 +66,9 @@ static struct rpc_credops gss_credops; #define GSS_CRED_EXPIRE (60 * HZ) /* XXX: reasonable? */ #define GSS_CRED_SLACK 1024 /* XXX: unused */ -#define GSS_VERF_SLACK 48 /* length of a krb5 verifier.*/ +/* length of a krb5 verifier (48), plus data added before arguments when + * using integrity (two 4-byte integers): */ +#define GSS_VERF_SLACK 56 /* XXX this define must match the gssd define * as it is passed to gssd to signal the use of @@ -669,21 +672,14 @@ gss_marshal(struct rpc_task *task, u32 * struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 *cred_len; struct rpc_rqst *req = task->tk_rqstp; - struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; - u32 *verfbase = req->rq_svec[0].iov_base; u32 maj_stat = 0; - struct xdr_netobj bufin,bufout; + struct xdr_netobj mic; + struct iovec iov; + struct xdr_buf verf_buf; u32 service; dprintk("RPC: gss_marshal\n"); - /* We compute the checksum for the verifier over the xdr-encoded bytes - * starting with the xid (which verfbase points to) and ending at - * the end of the credential. */ - if (xprt->stream) - verfbase++; /* See clnt.c:call_header() */ - *p++ = htonl(RPC_AUTH_GSS); cred_len = p++; @@ -704,24 +700,28 @@ gss_marshal(struct rpc_task *task, u32 * p = xdr_encode_netobj(p, &ctx->gc_wire_ctx); *cred_len = htonl((p - (cred_len + 1)) << 2); - /* Marshal verifier. */ - bufin.data = (u8 *)verfbase; - bufin.len = (p - verfbase) << 2; + /* We compute the checksum for the verifier over the xdr-encoded bytes + * starting with the xid and ending at the end of the credential: */ + iov.iov_base = req->rq_snd_buf.head[0].iov_base; + if (task->tk_client->cl_xprt->stream) + /* See clnt.c:call_header() */ + iov.iov_base += 4; + iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; + xdr_buf_from_iov(&iov, &verf_buf); /* set verifier flavor*/ *p++ = htonl(RPC_AUTH_GSS); - bufout.data = (u8 *)(p + 1); + mic.data = (u8 *)(p + 1); maj_stat = gss_get_mic(ctx->gc_gss_ctx, GSS_C_QOP_DEFAULT, - &bufin, &bufout); + &verf_buf, &mic); if(maj_stat != 0){ - printk("gss_marshal: gss_get_mic FAILED (%d)\n", - maj_stat); + printk("gss_marshal: gss_get_mic FAILED (%d)\n", maj_stat); goto out_put_ctx; } - *p++ = htonl(bufout.len); - p += XDR_QUADLEN(bufout.len); + *p++ = htonl(mic.len); + p += XDR_QUADLEN(mic.len); gss_put_ctx(ctx); return p; out_put_ctx: @@ -749,35 +749,45 @@ static u32 * gss_validate(struct rpc_task *task, u32 *p) { struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 seq, qop_state; - struct xdr_netobj bufin; - struct xdr_netobj bufout; + struct iovec iov; + struct xdr_buf verf_buf; + struct xdr_netobj mic; u32 flav,len; + u32 service; dprintk("RPC: gss_validate\n"); flav = ntohl(*p++); - if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) { - printk("RPC: giant verf size: %ld\n", (unsigned long) len); + if ((len = ntohl(*p++)) > RPC_MAX_AUTH_SIZE) goto out_bad; - } - dprintk("RPC: gss_validate: verifier flavor %d, len %d\n", flav, len); - - if (flav != RPC_AUTH_GSS) { - printk("RPC: bad verf flavor: %ld\n", (unsigned long)flav); + if (flav != RPC_AUTH_GSS) goto out_bad; - } seq = htonl(task->tk_gss_seqno); - bufin.data = (u8 *) &seq; - bufin.len = sizeof(seq); - bufout.data = (u8 *) p; - bufout.len = len; - - if (gss_verify_mic(ctx->gc_gss_ctx, &bufin, &bufout, &qop_state) != 0) - goto out_bad; - task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; - dprintk("RPC: GSS gss_validate: gss_verify_mic succeeded.\n"); + iov.iov_base = &seq; + iov.iov_len = sizeof(seq); + xdr_buf_from_iov(&iov, &verf_buf); + mic.data = (u8 *)p; + mic.len = len; + + if (gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state)) + goto out_bad; + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + /* verifier data, flavor, length: */ + task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; + break; + case RPC_GSS_SVC_INTEGRITY: + /* verifier data, flavor, length, length, sequence number: */ + task->tk_auth->au_rslack = XDR_QUADLEN(len) + 4; + break; + default: + goto out_bad; + } gss_put_ctx(ctx); return p + XDR_QUADLEN(len); out_bad: @@ -785,6 +795,147 @@ out_bad: return NULL; } +static int +gss_wrap_req(struct rpc_task *task, + kxdrproc_t encode, void *rqstp, u32 *p, void *obj) +{ + struct rpc_rqst *req = (struct rpc_rqst *)rqstp; + struct xdr_buf *snd_buf = &req->rq_snd_buf; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); + u32 *integ_len = NULL; + int status = -EIO; + u32 maj_stat = 0; + struct xdr_buf integ_buf; + struct xdr_netobj mic; + u32 service; + u32 offset, *q; + struct iovec *iov; + + dprintk("RPC: gss_wrap_body\n"); + BUG_ON(!ctx); + if (ctx->gc_proc != RPC_GSS_PROC_DATA) { + /* The spec seems a little ambiguous here, but I think that not + * wrapping context destruction requests makes the most sense. + */ + status = encode(rqstp, p, obj); + goto out; + } + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + status = encode(rqstp, p, obj); + goto out; + case RPC_GSS_SVC_INTEGRITY: + + integ_len = p++; + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; + *p++ = htonl(task->tk_gss_seqno); + + status = encode(rqstp, p, obj); + if (status) + goto out; + + if (xdr_buf_subsegment(snd_buf, &integ_buf, + offset, snd_buf->len - offset)) + goto out; + *integ_len = htonl(integ_buf.len); + + /* guess whether we're in the head or the tail: */ + if (snd_buf->page_len || snd_buf->tail[0].iov_len) + iov = snd_buf->tail; + else + iov = snd_buf->head; + p = iov->iov_base + iov->iov_len; + mic.data = (u8 *)(p + 1); + + maj_stat = gss_get_mic(ctx->gc_gss_ctx, + GSS_C_QOP_DEFAULT, &integ_buf, &mic); + status = -EIO; /* XXX? */ + if (maj_stat) + goto out; + q = p; + *q++ = htonl(mic.len); + q += XDR_QUADLEN(mic.len); + + offset = (u8 *)q - (u8 *)p; + iov->iov_len += offset; + snd_buf->len += offset; + break; + case RPC_GSS_SVC_PRIVACY: + default: + goto out; + } + status = 0; +out: + gss_put_ctx(ctx); + dprintk("RPC: gss_wrap_req returning %d\n", status); + return status; +} + +static int +gss_unwrap_resp(struct rpc_task *task, + kxdrproc_t decode, void *rqstp, u32 *p, void *obj) +{ + struct rpc_rqst *req = (struct rpc_rqst *)rqstp; + struct xdr_buf *rcv_buf = &req->rq_rcv_buf; + struct rpc_cred *cred = task->tk_msg.rpc_cred; + struct gss_cred *gss_cred = container_of(cred, struct gss_cred, + gc_base); + struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); + struct xdr_buf integ_buf; + struct xdr_netobj mic; + int status = -EIO; + u32 maj_stat = 0; + u32 service; + u32 data_offset, mic_offset; + u32 integ_len; + + BUG_ON(!ctx); + + if (ctx->gc_proc != RPC_GSS_PROC_DATA) + goto out_decode; + service = gss_pseudoflavor_to_service(gss_cred->gc_flavor); + switch (service) { + case RPC_GSS_SVC_NONE: + goto out_decode; + case RPC_GSS_SVC_INTEGRITY: + integ_len = ntohl(*p++); + if (integ_len & 3) + goto out; + data_offset = (u8 *)p - (u8 *)rcv_buf->head[0].iov_base; + mic_offset = integ_len + data_offset; + if (mic_offset > rcv_buf->len) + goto out; + if (ntohl(*p++) != task->tk_gss_seqno) + goto out; + + if (xdr_buf_subsegment(rcv_buf, &integ_buf, data_offset, + mic_offset - data_offset)) + goto out; + + if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) + goto out; + + maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, + &mic, NULL); + if (maj_stat != GSS_S_COMPLETE) + goto out; + break; + case RPC_GSS_SVC_PRIVACY: + default: + goto out; + } +out_decode: + status = decode(rqstp, p, obj); +out: + gss_put_ctx(ctx); + dprintk("RPC: gss_unwrap_resp returning %d\n", status); + return status; +} + static struct rpc_authops authgss_ops = { .owner = THIS_MODULE, .au_flavor = RPC_AUTH_GSS, @@ -802,6 +953,8 @@ static struct rpc_credops gss_credops = .crmarshal = gss_marshal, .crrefresh = gss_refresh, .crvalidate = gss_validate, + .crwrap_req = gss_wrap_req, + .crunwrap_resp = gss_unwrap_resp, }; static struct rpc_pipe_ops gss_upcall_ops = { diff -puN net/sunrpc/auth_gss/gss_krb5_crypto.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_crypto.c --- 25/net/sunrpc/auth_gss/gss_krb5_crypto.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_crypto.c 2004-01-09 22:16:15.000000000 -0800 @@ -39,6 +39,7 @@ #include #include #include +#include #include #ifdef RPC_DEBUG @@ -57,7 +58,7 @@ krb5_encrypt( struct scatterlist sg[1]; u8 local_iv[16] = {0}; - dprintk("RPC: gss_k5encrypt: TOP in %p out %p\nin data:\n", out, in); + dprintk("RPC: krb5_encrypt: input data:\n"); print_hexl((u32 *)in, length, 0); if (length % crypto_tfm_alg_blocksize(tfm) != 0) @@ -79,8 +80,10 @@ krb5_encrypt( ret = crypto_cipher_encrypt_iv(tfm, sg, sg, length, local_iv); + dprintk("RPC: krb5_encrypt: output data:\n"); + print_hexl((u32 *)out, length, 0); out: - dprintk("gss_k5encrypt returns %d\n",ret); + dprintk("krb5_encrypt returns %d\n",ret); return(ret); } @@ -96,8 +99,8 @@ krb5_decrypt( struct scatterlist sg[1]; u8 local_iv[16] = {0}; - dprintk("RPC: gss_k5decrypt: TOP in %p out %p\nin data:\n", in, out); - print_hexl((u32 *)in,length,0); + dprintk("RPC: krb5_decrypt: input data:\n"); + print_hexl((u32 *)in, length, 0); if (length % crypto_tfm_alg_blocksize(tfm) != 0) goto out; @@ -117,6 +120,8 @@ krb5_decrypt( ret = crypto_cipher_decrypt_iv(tfm, sg, sg, length, local_iv); + dprintk("RPC: krb5_decrypt: output_data:\n"); + print_hexl((u32 *)out, length, 0); out: dprintk("gss_k5decrypt returns %d\n",ret); return(ret); @@ -132,13 +137,15 @@ buf_to_sg(struct scatterlist *sg, char * /* checksum the plaintext data and the first 8 bytes of the krb5 token header, * as specified by the rfc: */ s32 -krb5_make_checksum(s32 cksumtype, char *header, char *body, int body_len, +krb5_make_checksum(s32 cksumtype, char *header, struct xdr_buf *body, struct xdr_netobj *cksum) { char *cksumname; struct crypto_tfm *tfm = NULL; /* XXX add to ctx? */ - struct scatterlist sg[2]; + struct scatterlist sg[1]; u32 code = GSS_S_FAILURE; + int len, thislen, offset; + int i; switch (cksumtype) { case CKSUMTYPE_RSA_MD5: @@ -155,10 +162,36 @@ krb5_make_checksum(s32 cksumtype, char * if ((cksum->data = kmalloc(cksum->len, GFP_KERNEL)) == NULL) goto out; - buf_to_sg(&sg[0], header, 8); - buf_to_sg(&sg[1], body, body_len); crypto_digest_init(tfm); - crypto_digest_update(tfm, sg, 2); + buf_to_sg(sg, header, 8); + crypto_digest_update(tfm, sg, 1); + if (body->head[0].iov_len) { + buf_to_sg(sg, body->head[0].iov_base, body->head[0].iov_len); + crypto_digest_update(tfm, sg, 1); + } + + len = body->page_len; + offset = body->page_base; + i = 0; + while (len) { + sg->page = body->pages[i]; + sg->offset = offset; + offset = 0; + if (PAGE_SIZE > len) + thislen = len; + else + thislen = PAGE_SIZE; + sg->length = thislen; + kmap(sg->page); /* XXX kmap_atomic? */ + crypto_digest_update(tfm, sg, 1); + kunmap(sg->page); + len -= thislen; + i++; + } + if (body->tail[0].iov_len) { + buf_to_sg(sg, body->tail[0].iov_base, body->tail[0].iov_len); + crypto_digest_update(tfm, sg, 1); + } crypto_digest_final(tfm, cksum->data); code = 0; out: diff -puN net/sunrpc/auth_gss/gss_krb5_mech.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_mech.c --- 25/net/sunrpc/auth_gss/gss_krb5_mech.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_mech.c 2004-01-09 22:16:15.000000000 -0800 @@ -183,7 +183,7 @@ gss_delete_sec_context_kerberos(void *in static u32 gss_verify_mic_kerberos(struct gss_ctx *ctx, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate) { u32 maj_stat = 0; @@ -202,13 +202,11 @@ gss_verify_mic_kerberos(struct gss_ctx static u32 gss_get_mic_kerberos(struct gss_ctx *ctx, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token) { u32 err = 0; struct krb5_ctx *kctx = ctx->internal_ctx_id; - if (!message->data) return GSS_S_FAILURE; - err = krb5_make_token(kctx, qop, message, mic_token, KG_TOK_MIC_MSG); dprintk("RPC: gss_get_mic_kerberos returning %d\n",err); @@ -233,12 +231,14 @@ static int __init init_kerberos_module(v printk("Failed to register kerberos gss mechanism!\n"); gm = gss_mech_get_by_OID(&gss_mech_krb5_oid); gss_register_triple(RPC_AUTH_GSS_KRB5 , gm, 0, RPC_GSS_SVC_NONE); + gss_register_triple(RPC_AUTH_GSS_KRB5I, gm, 0, RPC_GSS_SVC_INTEGRITY); gss_mech_put(gm); return 0; } static void __exit cleanup_kerberos_module(void) { + gss_unregister_triple(RPC_AUTH_GSS_KRB5I); gss_unregister_triple(RPC_AUTH_GSS_KRB5); } diff -puN net/sunrpc/auth_gss/gss_krb5_seal.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_seal.c --- 25/net/sunrpc/auth_gss/gss_krb5_seal.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_seal.c 2004-01-09 22:16:15.000000000 -0800 @@ -80,7 +80,7 @@ gss_krb5_padding(int blocksize, int leng u32 krb5_make_token(struct krb5_ctx *ctx, int qop_req, - struct xdr_netobj * text, struct xdr_netobj * token, + struct xdr_buf *text, struct xdr_netobj *token, int toktype) { s32 checksum_type; @@ -134,24 +134,11 @@ krb5_make_token(struct krb5_ctx *ctx, in *(u16 *)(krb5_hdr + 4) = htons(ctx->sealalg); if (toktype == KG_TOK_WRAP_MSG) { - unsigned char pad = gss_krb5_padding(blocksize, text->len); - - get_random_bytes(msg_start, blocksize); /* "confounder" */ - memcpy(msg_start + blocksize, text->data, text->len); - - memset(msg_start + blocksize + text->len, pad, pad); - - if (krb5_make_checksum(checksum_type, krb5_hdr, msg_start, - tmsglen, &md5cksum)) - goto out_err; - - if (krb5_encrypt(ctx->enc, NULL, msg_start, msg_start, - tmsglen)) - goto out_err; - + /* XXX removing support for now */ + goto out_err; } else { /* Sign only. */ - if (krb5_make_checksum(checksum_type, krb5_hdr, text->data, - text->len, &md5cksum)) + if (krb5_make_checksum(checksum_type, krb5_hdr, text, + &md5cksum)) goto out_err; } diff -puN net/sunrpc/auth_gss/gss_krb5_unseal.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_krb5_unseal.c --- 25/net/sunrpc/auth_gss/gss_krb5_unseal.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/gss_krb5_unseal.c 2004-01-09 22:16:15.000000000 -0800 @@ -75,20 +75,19 @@ * to return the decrypted data. */ +/* XXX will need to change prototype and/or just split into a separate function + * when we add privacy (because read_token will be in pages too). */ u32 krb5_read_token(struct krb5_ctx *ctx, struct xdr_netobj *read_token, - struct xdr_netobj *message_buffer, + struct xdr_buf *message_buffer, int *qop_state, int toktype) { int signalg; int sealalg; - struct xdr_netobj token = {.len = 0, .data = NULL}; s32 checksum_type; struct xdr_netobj md5cksum = {.len = 0, .data = NULL}; s32 now; - unsigned char *plain = NULL; - int plainlen = 0; int direction; s32 seqnum; unsigned char *ptr = (unsigned char *)read_token->data; @@ -100,10 +99,11 @@ krb5_read_token(struct krb5_ctx *ctx, if (g_verify_token_header(&ctx->mech_used, &bodysize, &ptr, toktype, read_token->len)) goto out; + /* XXX sanity-check bodysize?? */ if (toktype == KG_TOK_WRAP_MSG) { - message_buffer->len = 0; - message_buffer->data = NULL; + /* XXX gone */ + goto out; } /* get the sign and seal algorithms */ @@ -135,43 +135,6 @@ krb5_read_token(struct krb5_ctx *ctx, signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) goto out; - if (toktype == KG_TOK_WRAP_MSG) { - int conflen = crypto_tfm_alg_blocksize(ctx->enc); - int padlen; - - plainlen = bodysize - (14 + KRB5_CKSUM_LENGTH); - plain = ptr + 14 + KRB5_CKSUM_LENGTH; - - ret = krb5_decrypt(ctx->enc, NULL, plain, plain, plainlen); - if (ret) - goto out; - - ret = GSS_S_FAILURE; - padlen = plain[plainlen -1]; - if ((padlen < 1) || (padlen > 8)) - goto out; - token.len = plainlen - conflen - padlen; - - if (token.len) { - token.data = kmalloc(token.len, GFP_KERNEL); - if (token.data == NULL) - goto out; - memcpy(token.data, plain + conflen, token.len); - } - - } else if (toktype == KG_TOK_MIC_MSG) { - token = *message_buffer; - plain = token.data; - plainlen = token.len; - } else { - printk("RPC: bad toktype in krb5_read_token"); - ret = GSS_S_FAILURE; - goto out; - } - - dprintk("RPC krb5_read_token: token.len %d plainlen %d\n", token.len, - plainlen); - /* compute the checksum of the message */ /* initialize the the cksum */ @@ -186,8 +149,8 @@ krb5_read_token(struct krb5_ctx *ctx, switch (signalg) { case SGN_ALG_DES_MAC_MD5: - ret = krb5_make_checksum(checksum_type, ptr - 2, plain, - plainlen, &md5cksum); + ret = krb5_make_checksum(checksum_type, ptr - 2, + message_buffer, &md5cksum); if (ret) goto out; @@ -208,9 +171,6 @@ krb5_read_token(struct krb5_ctx *ctx, /* it got through unscathed. Make sure the context is unexpired */ - if (toktype == KG_TOK_WRAP_MSG) - *message_buffer = token; - if (qop_state) *qop_state = GSS_C_QOP_DEFAULT; @@ -234,7 +194,5 @@ krb5_read_token(struct krb5_ctx *ctx, ret = GSS_S_COMPLETE; out: if (md5cksum.data) kfree(md5cksum.data); - if ((toktype == KG_TOK_WRAP_MSG) && ret && token.data) - kfree(token.data); return ret; } diff -puN net/sunrpc/auth_gss/gss_mech_switch.c~nfs-13-krb5_integ net/sunrpc/auth_gss/gss_mech_switch.c --- 25/net/sunrpc/auth_gss/gss_mech_switch.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/auth_gss/gss_mech_switch.c 2004-01-09 22:16:15.000000000 -0800 @@ -196,7 +196,7 @@ gss_import_sec_context(struct xdr_netobj u32 gss_get_mic(struct gss_ctx *context_handle, u32 qop, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token) { return context_handle->mech_type->gm_ops @@ -210,7 +210,7 @@ gss_get_mic(struct gss_ctx *context_hand u32 gss_verify_mic(struct gss_ctx *context_handle, - struct xdr_netobj *message, + struct xdr_buf *message, struct xdr_netobj *mic_token, u32 *qstate) { diff -puN net/sunrpc/clnt.c~nfs-13-krb5_integ net/sunrpc/clnt.c --- 25/net/sunrpc/clnt.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/clnt.c 2004-01-09 22:16:15.000000000 -0800 @@ -568,7 +568,8 @@ call_encode(struct rpc_task *task) rpc_exit(task, -EIO); return; } - if (encode && (status = encode(req, p, task->tk_msg.rpc_argp)) < 0) { + if (encode && (status = rpcauth_wrap_req(task, encode, req, p, + task->tk_msg.rpc_argp)) < 0) { printk(KERN_WARNING "%s: can't encode arguments: %d\n", clnt->cl_protname, -status); rpc_exit(task, status); @@ -827,7 +828,8 @@ call_decode(struct rpc_task *task) task->tk_action = NULL; if (decode) - task->tk_status = decode(req, p, task->tk_msg.rpc_resp); + task->tk_status = rpcauth_unwrap_resp(task, decode, req, p, + task->tk_msg.rpc_resp); dprintk("RPC: %4d call_decode result %d\n", task->tk_pid, task->tk_status); return; diff -puN net/sunrpc/sunrpc_syms.c~nfs-13-krb5_integ net/sunrpc/sunrpc_syms.c --- 25/net/sunrpc/sunrpc_syms.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/sunrpc_syms.c 2004-01-09 22:16:15.000000000 -0800 @@ -126,6 +126,9 @@ EXPORT_SYMBOL(xdr_inline_pages); EXPORT_SYMBOL(xdr_shift_buf); EXPORT_SYMBOL(xdr_write_pages); EXPORT_SYMBOL(xdr_read_pages); +EXPORT_SYMBOL(xdr_buf_from_iov); +EXPORT_SYMBOL(xdr_buf_subsegment); +EXPORT_SYMBOL(xdr_buf_read_netobj); /* Debugging symbols */ #ifdef RPC_DEBUG diff -puN net/sunrpc/xdr.c~nfs-13-krb5_integ net/sunrpc/xdr.c --- 25/net/sunrpc/xdr.c~nfs-13-krb5_integ 2004-01-09 22:16:15.000000000 -0800 +++ 25-akpm/net/sunrpc/xdr.c 2004-01-09 22:16:15.000000000 -0800 @@ -538,7 +538,7 @@ _copy_to_pages(struct page **pages, size * Copies data into an arbitrary memory location from an array of pages * The copy is assumed to be non-overlapping. */ -static void +void _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len) { struct page **pgfrom; @@ -731,3 +731,145 @@ xdr_read_pages(struct xdr_stream *xdr, u xdr->p = (uint32_t *)((char *)iov->iov_base + padding); xdr->end = (uint32_t *)((char *)iov->iov_base + iov->iov_len); } + +static struct iovec empty_iov = {.iov_base = NULL, .iov_len = 0}; + +void +xdr_buf_from_iov(struct iovec *iov, struct xdr_buf *buf) +{ + buf->head[0] = *iov; + buf->tail[0] = empty_iov; + buf->page_len = 0; + buf->len = iov->iov_len; +} + +/* Sets subiov to the intersection of iov with the buffer of length len + * starting base bytes after iov. Indicates empty intersection by setting + * length of subiov to zero. Decrements len by length of subiov, sets base + * to zero (or decrements it by length of iov if subiov is empty). */ +static void +iov_subsegment(struct iovec *iov, struct iovec *subiov, int *base, int *len) +{ + if (*base > iov->iov_len) { + subiov->iov_base = NULL; + subiov->iov_len = 0; + *base -= iov->iov_len; + } else { + subiov->iov_base = iov->iov_base + *base; + subiov->iov_len = min(*len, (int)iov->iov_len - *base); + *base = 0; + } + *len -= subiov->iov_len; +} + +/* Sets subbuf to the portion of buf of length len beginning base bytes + * from the start of buf. Returns -1 if base of length are out of bounds. */ +int +xdr_buf_subsegment(struct xdr_buf *buf, struct xdr_buf *subbuf, + int base, int len) +{ + int i; + + subbuf->len = len; + iov_subsegment(buf->head, subbuf->head, &base, &len); + + if (base < buf->page_len) { + i = (base + buf->page_base) >> PAGE_CACHE_SHIFT; + subbuf->pages = &buf->pages[i]; + subbuf->page_base = (base + buf->page_base) & ~PAGE_CACHE_MASK; + subbuf->page_len = min((int)buf->page_len - base, len); + len -= subbuf->page_len; + base = 0; + } else { + base -= buf->page_len; + subbuf->page_len = 0; + } + + iov_subsegment(buf->tail, subbuf->tail, &base, &len); + if (base || len) + return -1; + return 0; +} + +/* obj is assumed to point to allocated memory of size at least len: */ +static int +read_bytes_from_xdr_buf(struct xdr_buf *buf, int base, void *obj, int len) +{ + struct xdr_buf subbuf; + int this_len; + int status; + + status = xdr_buf_subsegment(buf, &subbuf, base, len); + if (status) + goto out; + this_len = min(len, (int)subbuf.head[0].iov_len); + memcpy(obj, subbuf.head[0].iov_base, this_len); + len -= this_len; + obj += this_len; + this_len = min(len, (int)subbuf.page_len); + if (this_len) + _copy_from_pages(obj, subbuf.pages, subbuf.page_base, this_len); + len -= this_len; + obj += this_len; + this_len = min(len, (int)subbuf.tail[0].iov_len); + memcpy(obj, subbuf.tail[0].iov_base, this_len); +out: + return status; +} + +static int +read_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; +} + +/* If the netobj starting offset bytes from the start of xdr_buf is contained + * entirely in the head or the tail, set object to point to it; otherwise + * try to find space for it at the end of the tail, copy it there, and + * set obj to point to it. */ +int +xdr_buf_read_netobj(struct xdr_buf *buf, struct xdr_netobj *obj, int offset) +{ + u32 tail_offset = buf->head[0].iov_len + buf->page_len; + u32 obj_end_offset; + + if (read_u32_from_xdr_buf(buf, offset, &obj->len)) + goto out; + obj_end_offset = offset + 4 + obj->len; + + if (obj_end_offset <= buf->head[0].iov_len) { + /* The obj is contained entirely in the head: */ + obj->data = buf->head[0].iov_base + offset + 4; + } else if (offset + 4 >= tail_offset) { + if (obj_end_offset - tail_offset + > buf->tail[0].iov_len) + goto out; + /* The obj is contained entirely in the tail: */ + obj->data = buf->tail[0].iov_base + + offset - tail_offset + 4; + } else { + /* use end of tail as storage for obj: + * (We don't copy to the beginning because then we'd have + * to worry about doing a potentially overlapping copy. + * This assumes the object is at most half the length of the + * tail.) */ + if (obj->len > buf->tail[0].iov_len) + goto out; + obj->data = buf->tail[0].iov_base + buf->tail[0].iov_len - + obj->len; + if (read_bytes_from_xdr_buf(buf, offset + 4, + obj->data, obj->len)) + goto out; + + } + return 0; +out: + return -1; +} _