Commit 2d2da60c63b67174add32f06e8d54c3a0c5cd9cf
Committed by
Trond Myklebust
1 parent
24b2605bec
Exists in
master
and in
7 other branches
RPCSEC_GSS: client-side privacy support
Add the code to the client side to handle privacy. This is dead code until we actually add privacy support to krb5. Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Showing 1 changed file with 148 additions and 1 deletions Side-by-side Diff
net/sunrpc/auth_gss/auth_gss.c
... | ... | @@ -43,6 +43,7 @@ |
43 | 43 | #include <linux/types.h> |
44 | 44 | #include <linux/slab.h> |
45 | 45 | #include <linux/sched.h> |
46 | +#include <linux/pagemap.h> | |
46 | 47 | #include <linux/sunrpc/clnt.h> |
47 | 48 | #include <linux/sunrpc/auth.h> |
48 | 49 | #include <linux/sunrpc/auth_gss.h> |
49 | 50 | |
... | ... | @@ -975,7 +976,115 @@ |
975 | 976 | return 0; |
976 | 977 | } |
977 | 978 | |
979 | +static void | |
980 | +priv_release_snd_buf(struct rpc_rqst *rqstp) | |
981 | +{ | |
982 | + int i; | |
983 | + | |
984 | + for (i=0; i < rqstp->rq_enc_pages_num; i++) | |
985 | + __free_page(rqstp->rq_enc_pages[i]); | |
986 | + kfree(rqstp->rq_enc_pages); | |
987 | +} | |
988 | + | |
978 | 989 | static int |
990 | +alloc_enc_pages(struct rpc_rqst *rqstp) | |
991 | +{ | |
992 | + struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | |
993 | + int first, last, i; | |
994 | + | |
995 | + if (snd_buf->page_len == 0) { | |
996 | + rqstp->rq_enc_pages_num = 0; | |
997 | + return 0; | |
998 | + } | |
999 | + | |
1000 | + first = snd_buf->page_base >> PAGE_CACHE_SHIFT; | |
1001 | + last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_CACHE_SHIFT; | |
1002 | + rqstp->rq_enc_pages_num = last - first + 1 + 1; | |
1003 | + rqstp->rq_enc_pages | |
1004 | + = kmalloc(rqstp->rq_enc_pages_num * sizeof(struct page *), | |
1005 | + GFP_NOFS); | |
1006 | + if (!rqstp->rq_enc_pages) | |
1007 | + goto out; | |
1008 | + for (i=0; i < rqstp->rq_enc_pages_num; i++) { | |
1009 | + rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS); | |
1010 | + if (rqstp->rq_enc_pages[i] == NULL) | |
1011 | + goto out_free; | |
1012 | + } | |
1013 | + rqstp->rq_release_snd_buf = priv_release_snd_buf; | |
1014 | + return 0; | |
1015 | +out_free: | |
1016 | + for (i--; i >= 0; i--) { | |
1017 | + __free_page(rqstp->rq_enc_pages[i]); | |
1018 | + } | |
1019 | +out: | |
1020 | + return -EAGAIN; | |
1021 | +} | |
1022 | + | |
1023 | +static inline int | |
1024 | +gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |
1025 | + kxdrproc_t encode, struct rpc_rqst *rqstp, u32 *p, void *obj) | |
1026 | +{ | |
1027 | + struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | |
1028 | + u32 offset; | |
1029 | + u32 maj_stat; | |
1030 | + int status; | |
1031 | + u32 *opaque_len; | |
1032 | + struct page **inpages; | |
1033 | + int first; | |
1034 | + int pad; | |
1035 | + struct kvec *iov; | |
1036 | + char *tmp; | |
1037 | + | |
1038 | + opaque_len = p++; | |
1039 | + offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | |
1040 | + *p++ = htonl(rqstp->rq_seqno); | |
1041 | + | |
1042 | + status = encode(rqstp, p, obj); | |
1043 | + if (status) | |
1044 | + return status; | |
1045 | + | |
1046 | + status = alloc_enc_pages(rqstp); | |
1047 | + if (status) | |
1048 | + return status; | |
1049 | + first = snd_buf->page_base >> PAGE_CACHE_SHIFT; | |
1050 | + inpages = snd_buf->pages + first; | |
1051 | + snd_buf->pages = rqstp->rq_enc_pages; | |
1052 | + snd_buf->page_base -= first << PAGE_CACHE_SHIFT; | |
1053 | + /* Give the tail its own page, in case we need extra space in the | |
1054 | + * head when wrapping: */ | |
1055 | + if (snd_buf->page_len || snd_buf->tail[0].iov_len) { | |
1056 | + tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); | |
1057 | + memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); | |
1058 | + snd_buf->tail[0].iov_base = tmp; | |
1059 | + } | |
1060 | + maj_stat = gss_wrap(ctx->gc_gss_ctx, GSS_C_QOP_DEFAULT, offset, | |
1061 | + snd_buf, inpages); | |
1062 | + /* RPC_SLACK_SPACE should prevent this ever happening: */ | |
1063 | + BUG_ON(snd_buf->len > snd_buf->buflen); | |
1064 | + status = -EIO; | |
1065 | + /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was | |
1066 | + * done anyway, so it's safe to put the request on the wire: */ | |
1067 | + if (maj_stat == GSS_S_CONTEXT_EXPIRED) | |
1068 | + cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | |
1069 | + else if (maj_stat) | |
1070 | + return status; | |
1071 | + | |
1072 | + *opaque_len = htonl(snd_buf->len - offset); | |
1073 | + /* guess whether we're in the head or the tail: */ | |
1074 | + if (snd_buf->page_len || snd_buf->tail[0].iov_len) | |
1075 | + iov = snd_buf->tail; | |
1076 | + else | |
1077 | + iov = snd_buf->head; | |
1078 | + p = iov->iov_base + iov->iov_len; | |
1079 | + pad = 3 - ((snd_buf->len - offset - 1) & 3); | |
1080 | + memset(p, 0, pad); | |
1081 | + iov->iov_len += pad; | |
1082 | + snd_buf->len += pad; | |
1083 | + | |
1084 | + return 0; | |
1085 | +} | |
1086 | + | |
1087 | +static int | |
979 | 1088 | gss_wrap_req(struct rpc_task *task, |
980 | 1089 | kxdrproc_t encode, void *rqstp, u32 *p, void *obj) |
981 | 1090 | { |
... | ... | @@ -1002,6 +1111,8 @@ |
1002 | 1111 | rqstp, p, obj); |
1003 | 1112 | break; |
1004 | 1113 | case RPC_GSS_SVC_PRIVACY: |
1114 | + status = gss_wrap_req_priv(cred, ctx, encode, | |
1115 | + rqstp, p, obj); | |
1005 | 1116 | break; |
1006 | 1117 | } |
1007 | 1118 | out: |
... | ... | @@ -1048,6 +1159,36 @@ |
1048 | 1159 | return 0; |
1049 | 1160 | } |
1050 | 1161 | |
1162 | +static inline int | |
1163 | +gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |
1164 | + struct rpc_rqst *rqstp, u32 **p) | |
1165 | +{ | |
1166 | + struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; | |
1167 | + u32 offset; | |
1168 | + u32 opaque_len; | |
1169 | + u32 maj_stat; | |
1170 | + int status = -EIO; | |
1171 | + | |
1172 | + opaque_len = ntohl(*(*p)++); | |
1173 | + offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base; | |
1174 | + if (offset + opaque_len > rcv_buf->len) | |
1175 | + return status; | |
1176 | + /* remove padding: */ | |
1177 | + rcv_buf->len = offset + opaque_len; | |
1178 | + | |
1179 | + maj_stat = gss_unwrap(ctx->gc_gss_ctx, NULL, | |
1180 | + offset, rcv_buf); | |
1181 | + if (maj_stat == GSS_S_CONTEXT_EXPIRED) | |
1182 | + cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | |
1183 | + if (maj_stat != GSS_S_COMPLETE) | |
1184 | + return status; | |
1185 | + if (ntohl(*(*p)++) != rqstp->rq_seqno) | |
1186 | + return status; | |
1187 | + | |
1188 | + return 0; | |
1189 | +} | |
1190 | + | |
1191 | + | |
1051 | 1192 | static int |
1052 | 1193 | gss_unwrap_resp(struct rpc_task *task, |
1053 | 1194 | kxdrproc_t decode, void *rqstp, u32 *p, void *obj) |
... | ... | @@ -1057,6 +1198,8 @@ |
1057 | 1198 | gc_base); |
1058 | 1199 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); |
1059 | 1200 | u32 *savedp = p; |
1201 | + struct kvec *head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head; | |
1202 | + int savedlen = head->iov_len; | |
1060 | 1203 | int status = -EIO; |
1061 | 1204 | |
1062 | 1205 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) |
1063 | 1206 | |
... | ... | @@ -1070,10 +1213,14 @@ |
1070 | 1213 | goto out; |
1071 | 1214 | break; |
1072 | 1215 | case RPC_GSS_SVC_PRIVACY: |
1216 | + status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p); | |
1217 | + if (status) | |
1218 | + goto out; | |
1073 | 1219 | break; |
1074 | 1220 | } |
1075 | 1221 | /* take into account extra slack for integrity and privacy cases: */ |
1076 | - task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp); | |
1222 | + task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp) | |
1223 | + + (savedlen - head->iov_len); | |
1077 | 1224 | out_decode: |
1078 | 1225 | status = decode(rqstp, p, obj); |
1079 | 1226 | out: |