diff options
Diffstat (limited to 'net/sunrpc/auth_gss/auth_gss.c')
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 187 |
1 files changed, 158 insertions, 29 deletions
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 2f7b867161d2..f44f46f1d8e0 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -42,9 +42,8 @@ | |||
42 | #include <linux/init.h> | 42 | #include <linux/init.h> |
43 | #include <linux/types.h> | 43 | #include <linux/types.h> |
44 | #include <linux/slab.h> | 44 | #include <linux/slab.h> |
45 | #include <linux/socket.h> | ||
46 | #include <linux/in.h> | ||
47 | #include <linux/sched.h> | 45 | #include <linux/sched.h> |
46 | #include <linux/pagemap.h> | ||
48 | #include <linux/sunrpc/clnt.h> | 47 | #include <linux/sunrpc/clnt.h> |
49 | #include <linux/sunrpc/auth.h> | 48 | #include <linux/sunrpc/auth.h> |
50 | #include <linux/sunrpc/auth_gss.h> | 49 | #include <linux/sunrpc/auth_gss.h> |
@@ -846,10 +845,8 @@ gss_marshal(struct rpc_task *task, u32 *p) | |||
846 | 845 | ||
847 | /* We compute the checksum for the verifier over the xdr-encoded bytes | 846 | /* We compute the checksum for the verifier over the xdr-encoded bytes |
848 | * starting with the xid and ending at the end of the credential: */ | 847 | * starting with the xid and ending at the end of the credential: */ |
849 | iov.iov_base = req->rq_snd_buf.head[0].iov_base; | 848 | iov.iov_base = xprt_skip_transport_header(task->tk_xprt, |
850 | if (task->tk_client->cl_xprt->stream) | 849 | req->rq_snd_buf.head[0].iov_base); |
851 | /* See clnt.c:call_header() */ | ||
852 | iov.iov_base += 4; | ||
853 | iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; | 850 | iov.iov_len = (u8 *)p - (u8 *)iov.iov_base; |
854 | xdr_buf_from_iov(&iov, &verf_buf); | 851 | xdr_buf_from_iov(&iov, &verf_buf); |
855 | 852 | ||
@@ -857,9 +854,7 @@ gss_marshal(struct rpc_task *task, u32 *p) | |||
857 | *p++ = htonl(RPC_AUTH_GSS); | 854 | *p++ = htonl(RPC_AUTH_GSS); |
858 | 855 | ||
859 | mic.data = (u8 *)(p + 1); | 856 | mic.data = (u8 *)(p + 1); |
860 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, | 857 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, &verf_buf, &mic); |
861 | GSS_C_QOP_DEFAULT, | ||
862 | &verf_buf, &mic); | ||
863 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) { | 858 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) { |
864 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | 859 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; |
865 | } else if (maj_stat != 0) { | 860 | } else if (maj_stat != 0) { |
@@ -890,10 +885,8 @@ static u32 * | |||
890 | gss_validate(struct rpc_task *task, u32 *p) | 885 | gss_validate(struct rpc_task *task, u32 *p) |
891 | { | 886 | { |
892 | struct rpc_cred *cred = task->tk_msg.rpc_cred; | 887 | struct rpc_cred *cred = task->tk_msg.rpc_cred; |
893 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | ||
894 | gc_base); | ||
895 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | 888 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); |
896 | u32 seq, qop_state; | 889 | u32 seq; |
897 | struct kvec iov; | 890 | struct kvec iov; |
898 | struct xdr_buf verf_buf; | 891 | struct xdr_buf verf_buf; |
899 | struct xdr_netobj mic; | 892 | struct xdr_netobj mic; |
@@ -914,23 +907,14 @@ gss_validate(struct rpc_task *task, u32 *p) | |||
914 | mic.data = (u8 *)p; | 907 | mic.data = (u8 *)p; |
915 | mic.len = len; | 908 | mic.len = len; |
916 | 909 | ||
917 | maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic, &qop_state); | 910 | maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic); |
918 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | 911 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) |
919 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | 912 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; |
920 | if (maj_stat) | 913 | if (maj_stat) |
921 | goto out_bad; | 914 | goto out_bad; |
922 | switch (gss_cred->gc_service) { | 915 | /* We leave it to unwrap to calculate au_rslack. For now we just |
923 | case RPC_GSS_SVC_NONE: | 916 | * calculate the length of the verifier: */ |
924 | /* verifier data, flavor, length: */ | 917 | task->tk_auth->au_verfsize = XDR_QUADLEN(len) + 2; |
925 | task->tk_auth->au_rslack = XDR_QUADLEN(len) + 2; | ||
926 | break; | ||
927 | case RPC_GSS_SVC_INTEGRITY: | ||
928 | /* verifier data, flavor, length, length, sequence number: */ | ||
929 | task->tk_auth->au_rslack = XDR_QUADLEN(len) + 4; | ||
930 | break; | ||
931 | case RPC_GSS_SVC_PRIVACY: | ||
932 | goto out_bad; | ||
933 | } | ||
934 | gss_put_ctx(ctx); | 918 | gss_put_ctx(ctx); |
935 | dprintk("RPC: %4u GSS gss_validate: gss_verify_mic succeeded.\n", | 919 | dprintk("RPC: %4u GSS gss_validate: gss_verify_mic succeeded.\n", |
936 | task->tk_pid); | 920 | task->tk_pid); |
@@ -975,8 +959,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |||
975 | p = iov->iov_base + iov->iov_len; | 959 | p = iov->iov_base + iov->iov_len; |
976 | mic.data = (u8 *)(p + 1); | 960 | mic.data = (u8 *)(p + 1); |
977 | 961 | ||
978 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, | 962 | maj_stat = gss_get_mic(ctx->gc_gss_ctx, &integ_buf, &mic); |
979 | GSS_C_QOP_DEFAULT, &integ_buf, &mic); | ||
980 | status = -EIO; /* XXX? */ | 963 | status = -EIO; /* XXX? */ |
981 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | 964 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) |
982 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | 965 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; |
@@ -990,6 +973,113 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |||
990 | return 0; | 973 | return 0; |
991 | } | 974 | } |
992 | 975 | ||
976 | static void | ||
977 | priv_release_snd_buf(struct rpc_rqst *rqstp) | ||
978 | { | ||
979 | int i; | ||
980 | |||
981 | for (i=0; i < rqstp->rq_enc_pages_num; i++) | ||
982 | __free_page(rqstp->rq_enc_pages[i]); | ||
983 | kfree(rqstp->rq_enc_pages); | ||
984 | } | ||
985 | |||
986 | static int | ||
987 | alloc_enc_pages(struct rpc_rqst *rqstp) | ||
988 | { | ||
989 | struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | ||
990 | int first, last, i; | ||
991 | |||
992 | if (snd_buf->page_len == 0) { | ||
993 | rqstp->rq_enc_pages_num = 0; | ||
994 | return 0; | ||
995 | } | ||
996 | |||
997 | first = snd_buf->page_base >> PAGE_CACHE_SHIFT; | ||
998 | last = (snd_buf->page_base + snd_buf->page_len - 1) >> PAGE_CACHE_SHIFT; | ||
999 | rqstp->rq_enc_pages_num = last - first + 1 + 1; | ||
1000 | rqstp->rq_enc_pages | ||
1001 | = kmalloc(rqstp->rq_enc_pages_num * sizeof(struct page *), | ||
1002 | GFP_NOFS); | ||
1003 | if (!rqstp->rq_enc_pages) | ||
1004 | goto out; | ||
1005 | for (i=0; i < rqstp->rq_enc_pages_num; i++) { | ||
1006 | rqstp->rq_enc_pages[i] = alloc_page(GFP_NOFS); | ||
1007 | if (rqstp->rq_enc_pages[i] == NULL) | ||
1008 | goto out_free; | ||
1009 | } | ||
1010 | rqstp->rq_release_snd_buf = priv_release_snd_buf; | ||
1011 | return 0; | ||
1012 | out_free: | ||
1013 | for (i--; i >= 0; i--) { | ||
1014 | __free_page(rqstp->rq_enc_pages[i]); | ||
1015 | } | ||
1016 | out: | ||
1017 | return -EAGAIN; | ||
1018 | } | ||
1019 | |||
1020 | static inline int | ||
1021 | gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | ||
1022 | kxdrproc_t encode, struct rpc_rqst *rqstp, u32 *p, void *obj) | ||
1023 | { | ||
1024 | struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; | ||
1025 | u32 offset; | ||
1026 | u32 maj_stat; | ||
1027 | int status; | ||
1028 | u32 *opaque_len; | ||
1029 | struct page **inpages; | ||
1030 | int first; | ||
1031 | int pad; | ||
1032 | struct kvec *iov; | ||
1033 | char *tmp; | ||
1034 | |||
1035 | opaque_len = p++; | ||
1036 | offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base; | ||
1037 | *p++ = htonl(rqstp->rq_seqno); | ||
1038 | |||
1039 | status = encode(rqstp, p, obj); | ||
1040 | if (status) | ||
1041 | return status; | ||
1042 | |||
1043 | status = alloc_enc_pages(rqstp); | ||
1044 | if (status) | ||
1045 | return status; | ||
1046 | first = snd_buf->page_base >> PAGE_CACHE_SHIFT; | ||
1047 | inpages = snd_buf->pages + first; | ||
1048 | snd_buf->pages = rqstp->rq_enc_pages; | ||
1049 | snd_buf->page_base -= first << PAGE_CACHE_SHIFT; | ||
1050 | /* Give the tail its own page, in case we need extra space in the | ||
1051 | * head when wrapping: */ | ||
1052 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) { | ||
1053 | tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]); | ||
1054 | memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len); | ||
1055 | snd_buf->tail[0].iov_base = tmp; | ||
1056 | } | ||
1057 | maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages); | ||
1058 | /* RPC_SLACK_SPACE should prevent this ever happening: */ | ||
1059 | BUG_ON(snd_buf->len > snd_buf->buflen); | ||
1060 | status = -EIO; | ||
1061 | /* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was | ||
1062 | * done anyway, so it's safe to put the request on the wire: */ | ||
1063 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | ||
1064 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | ||
1065 | else if (maj_stat) | ||
1066 | return status; | ||
1067 | |||
1068 | *opaque_len = htonl(snd_buf->len - offset); | ||
1069 | /* guess whether we're in the head or the tail: */ | ||
1070 | if (snd_buf->page_len || snd_buf->tail[0].iov_len) | ||
1071 | iov = snd_buf->tail; | ||
1072 | else | ||
1073 | iov = snd_buf->head; | ||
1074 | p = iov->iov_base + iov->iov_len; | ||
1075 | pad = 3 - ((snd_buf->len - offset - 1) & 3); | ||
1076 | memset(p, 0, pad); | ||
1077 | iov->iov_len += pad; | ||
1078 | snd_buf->len += pad; | ||
1079 | |||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
993 | static int | 1083 | static int |
994 | gss_wrap_req(struct rpc_task *task, | 1084 | gss_wrap_req(struct rpc_task *task, |
995 | kxdrproc_t encode, void *rqstp, u32 *p, void *obj) | 1085 | kxdrproc_t encode, void *rqstp, u32 *p, void *obj) |
@@ -1017,6 +1107,8 @@ gss_wrap_req(struct rpc_task *task, | |||
1017 | rqstp, p, obj); | 1107 | rqstp, p, obj); |
1018 | break; | 1108 | break; |
1019 | case RPC_GSS_SVC_PRIVACY: | 1109 | case RPC_GSS_SVC_PRIVACY: |
1110 | status = gss_wrap_req_priv(cred, ctx, encode, | ||
1111 | rqstp, p, obj); | ||
1020 | break; | 1112 | break; |
1021 | } | 1113 | } |
1022 | out: | 1114 | out: |
@@ -1054,8 +1146,7 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |||
1054 | if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) | 1146 | if (xdr_buf_read_netobj(rcv_buf, &mic, mic_offset)) |
1055 | return status; | 1147 | return status; |
1056 | 1148 | ||
1057 | maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, | 1149 | maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &integ_buf, &mic); |
1058 | &mic, NULL); | ||
1059 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | 1150 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) |
1060 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | 1151 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; |
1061 | if (maj_stat != GSS_S_COMPLETE) | 1152 | if (maj_stat != GSS_S_COMPLETE) |
@@ -1063,6 +1154,35 @@ gss_unwrap_resp_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | |||
1063 | return 0; | 1154 | return 0; |
1064 | } | 1155 | } |
1065 | 1156 | ||
1157 | static inline int | ||
1158 | gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, | ||
1159 | struct rpc_rqst *rqstp, u32 **p) | ||
1160 | { | ||
1161 | struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; | ||
1162 | u32 offset; | ||
1163 | u32 opaque_len; | ||
1164 | u32 maj_stat; | ||
1165 | int status = -EIO; | ||
1166 | |||
1167 | opaque_len = ntohl(*(*p)++); | ||
1168 | offset = (u8 *)(*p) - (u8 *)rcv_buf->head[0].iov_base; | ||
1169 | if (offset + opaque_len > rcv_buf->len) | ||
1170 | return status; | ||
1171 | /* remove padding: */ | ||
1172 | rcv_buf->len = offset + opaque_len; | ||
1173 | |||
1174 | maj_stat = gss_unwrap(ctx->gc_gss_ctx, offset, rcv_buf); | ||
1175 | if (maj_stat == GSS_S_CONTEXT_EXPIRED) | ||
1176 | cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE; | ||
1177 | if (maj_stat != GSS_S_COMPLETE) | ||
1178 | return status; | ||
1179 | if (ntohl(*(*p)++) != rqstp->rq_seqno) | ||
1180 | return status; | ||
1181 | |||
1182 | return 0; | ||
1183 | } | ||
1184 | |||
1185 | |||
1066 | static int | 1186 | static int |
1067 | gss_unwrap_resp(struct rpc_task *task, | 1187 | gss_unwrap_resp(struct rpc_task *task, |
1068 | kxdrproc_t decode, void *rqstp, u32 *p, void *obj) | 1188 | kxdrproc_t decode, void *rqstp, u32 *p, void *obj) |
@@ -1071,6 +1191,9 @@ gss_unwrap_resp(struct rpc_task *task, | |||
1071 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, | 1191 | struct gss_cred *gss_cred = container_of(cred, struct gss_cred, |
1072 | gc_base); | 1192 | gc_base); |
1073 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); | 1193 | struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); |
1194 | u32 *savedp = p; | ||
1195 | struct kvec *head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head; | ||
1196 | int savedlen = head->iov_len; | ||
1074 | int status = -EIO; | 1197 | int status = -EIO; |
1075 | 1198 | ||
1076 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) | 1199 | if (ctx->gc_proc != RPC_GSS_PROC_DATA) |
@@ -1084,8 +1207,14 @@ gss_unwrap_resp(struct rpc_task *task, | |||
1084 | goto out; | 1207 | goto out; |
1085 | break; | 1208 | break; |
1086 | case RPC_GSS_SVC_PRIVACY: | 1209 | case RPC_GSS_SVC_PRIVACY: |
1210 | status = gss_unwrap_resp_priv(cred, ctx, rqstp, &p); | ||
1211 | if (status) | ||
1212 | goto out; | ||
1087 | break; | 1213 | break; |
1088 | } | 1214 | } |
1215 | /* take into account extra slack for integrity and privacy cases: */ | ||
1216 | task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp) | ||
1217 | + (savedlen - head->iov_len); | ||
1089 | out_decode: | 1218 | out_decode: |
1090 | status = decode(rqstp, p, obj); | 1219 | status = decode(rqstp, p, obj); |
1091 | out: | 1220 | out: |