diff options
author | Kevin Coffman <kwc@citi.umich.edu> | 2010-03-17 13:02:52 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-14 15:09:16 -0400 |
commit | e1f6c07b1160ef28e8754d12e6c03288dd9d5ca8 (patch) | |
tree | 5be1f97a1a51feaed4bed2b4a83a568c94921faf | |
parent | 81d4a4333a1dfd6070f046265d928bb4c79aff88 (diff) |
gss_krb5: add ability to have a keyed checksum (hmac)
Encryption types besides DES may use a keyed checksum (hmac).
Modify the make_checksum() function to allow for a key
and take care of enctype-specific processing such as truncating
the resulting hash.
Signed-off-by: Kevin Coffman <kwc@citi.umich.edu>
Signed-off-by: Steve Dickson <steved@redhat.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | include/linux/sunrpc/gss_krb5.h | 11 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_crypto.c | 54 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_mech.c | 1 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_seal.c | 13 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_unseal.c | 13 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_wrap.c | 30 |
6 files changed, 88 insertions, 34 deletions
diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h index f94935599d13..abf26efd44ac 100644 --- a/include/linux/sunrpc/gss_krb5.h +++ b/include/linux/sunrpc/gss_krb5.h | |||
@@ -41,6 +41,9 @@ | |||
41 | #include <linux/sunrpc/gss_err.h> | 41 | #include <linux/sunrpc/gss_err.h> |
42 | #include <linux/sunrpc/gss_asn1.h> | 42 | #include <linux/sunrpc/gss_asn1.h> |
43 | 43 | ||
44 | /* Maximum key length (in bytes) for the supported crypto algorithms*/ | ||
45 | #define GSS_KRB5_MAX_KEYLEN (32) | ||
46 | |||
44 | /* Maximum checksum function output for the supported crypto algorithms */ | 47 | /* Maximum checksum function output for the supported crypto algorithms */ |
45 | #define GSS_KRB5_MAX_CKSUM_LEN (20) | 48 | #define GSS_KRB5_MAX_CKSUM_LEN (20) |
46 | 49 | ||
@@ -74,6 +77,7 @@ struct krb5_ctx { | |||
74 | const struct gss_krb5_enctype *gk5e; /* enctype-specific info */ | 77 | const struct gss_krb5_enctype *gk5e; /* enctype-specific info */ |
75 | struct crypto_blkcipher *enc; | 78 | struct crypto_blkcipher *enc; |
76 | struct crypto_blkcipher *seq; | 79 | struct crypto_blkcipher *seq; |
80 | u8 cksum[GSS_KRB5_MAX_KEYLEN]; | ||
77 | s32 endtime; | 81 | s32 endtime; |
78 | u32 seq_send; | 82 | u32 seq_send; |
79 | struct xdr_netobj mech_used; | 83 | struct xdr_netobj mech_used; |
@@ -159,9 +163,10 @@ enum seal_alg { | |||
159 | + GSS_KRB5_TOK_HDR_LEN \ | 163 | + GSS_KRB5_TOK_HDR_LEN \ |
160 | + GSS_KRB5_MAX_CKSUM_LEN) | 164 | + GSS_KRB5_MAX_CKSUM_LEN) |
161 | 165 | ||
162 | s32 | 166 | u32 |
163 | make_checksum(char *, char *header, int hdrlen, struct xdr_buf *body, | 167 | make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen, |
164 | int body_offset, struct xdr_netobj *cksum); | 168 | struct xdr_buf *body, int body_offset, u8 *cksumkey, |
169 | struct xdr_netobj *cksumout); | ||
165 | 170 | ||
166 | u32 gss_get_mic_kerberos(struct gss_ctx *, struct xdr_buf *, | 171 | u32 gss_get_mic_kerberos(struct gss_ctx *, struct xdr_buf *, |
167 | struct xdr_netobj *); | 172 | struct xdr_netobj *); |
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index ccd5236953f7..cae04d7a45a5 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c | |||
@@ -123,21 +123,42 @@ checksummer(struct scatterlist *sg, void *data) | |||
123 | return crypto_hash_update(desc, sg, sg->length); | 123 | return crypto_hash_update(desc, sg, sg->length); |
124 | } | 124 | } |
125 | 125 | ||
126 | /* checksum the plaintext data and hdrlen bytes of the token header */ | 126 | /* |
127 | s32 | 127 | * checksum the plaintext data and hdrlen bytes of the token header |
128 | make_checksum(char *cksumname, char *header, int hdrlen, struct xdr_buf *body, | 128 | * The checksum is performed over the first 8 bytes of the |
129 | int body_offset, struct xdr_netobj *cksum) | 129 | * gss token header and then over the data body |
130 | */ | ||
131 | u32 | ||
132 | make_checksum(struct krb5_ctx *kctx, char *header, int hdrlen, | ||
133 | struct xdr_buf *body, int body_offset, u8 *cksumkey, | ||
134 | struct xdr_netobj *cksumout) | ||
130 | { | 135 | { |
131 | struct hash_desc desc; /* XXX add to ctx? */ | 136 | struct hash_desc desc; |
132 | struct scatterlist sg[1]; | 137 | struct scatterlist sg[1]; |
133 | int err; | 138 | int err; |
139 | u8 checksumdata[GSS_KRB5_MAX_CKSUM_LEN]; | ||
140 | unsigned int checksumlen; | ||
141 | |||
142 | if (cksumout->len < kctx->gk5e->cksumlength) { | ||
143 | dprintk("%s: checksum buffer length, %u, too small for %s\n", | ||
144 | __func__, cksumout->len, kctx->gk5e->name); | ||
145 | return GSS_S_FAILURE; | ||
146 | } | ||
134 | 147 | ||
135 | desc.tfm = crypto_alloc_hash(cksumname, 0, CRYPTO_ALG_ASYNC); | 148 | desc.tfm = crypto_alloc_hash(kctx->gk5e->cksum_name, 0, CRYPTO_ALG_ASYNC); |
136 | if (IS_ERR(desc.tfm)) | 149 | if (IS_ERR(desc.tfm)) |
137 | return GSS_S_FAILURE; | 150 | return GSS_S_FAILURE; |
138 | cksum->len = crypto_hash_digestsize(desc.tfm); | ||
139 | desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; | 151 | desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; |
140 | 152 | ||
153 | checksumlen = crypto_hash_digestsize(desc.tfm); | ||
154 | |||
155 | if (cksumkey != NULL) { | ||
156 | err = crypto_hash_setkey(desc.tfm, cksumkey, | ||
157 | kctx->gk5e->keylength); | ||
158 | if (err) | ||
159 | goto out; | ||
160 | } | ||
161 | |||
141 | err = crypto_hash_init(&desc); | 162 | err = crypto_hash_init(&desc); |
142 | if (err) | 163 | if (err) |
143 | goto out; | 164 | goto out; |
@@ -149,8 +170,25 @@ make_checksum(char *cksumname, char *header, int hdrlen, struct xdr_buf *body, | |||
149 | checksummer, &desc); | 170 | checksummer, &desc); |
150 | if (err) | 171 | if (err) |
151 | goto out; | 172 | goto out; |
152 | err = crypto_hash_final(&desc, cksum->data); | 173 | err = crypto_hash_final(&desc, checksumdata); |
174 | if (err) | ||
175 | goto out; | ||
153 | 176 | ||
177 | switch (kctx->gk5e->ctype) { | ||
178 | case CKSUMTYPE_RSA_MD5: | ||
179 | err = kctx->gk5e->encrypt(kctx->seq, NULL, checksumdata, | ||
180 | checksumdata, checksumlen); | ||
181 | if (err) | ||
182 | goto out; | ||
183 | memcpy(cksumout->data, | ||
184 | checksumdata + checksumlen - kctx->gk5e->cksumlength, | ||
185 | kctx->gk5e->cksumlength); | ||
186 | break; | ||
187 | default: | ||
188 | BUG(); | ||
189 | break; | ||
190 | } | ||
191 | cksumout->len = kctx->gk5e->cksumlength; | ||
154 | out: | 192 | out: |
155 | crypto_free_hash(desc.tfm); | 193 | crypto_free_hash(desc.tfm); |
156 | return err ? GSS_S_FAILURE : 0; | 194 | return err ? GSS_S_FAILURE : 0; |
diff --git a/net/sunrpc/auth_gss/gss_krb5_mech.c b/net/sunrpc/auth_gss/gss_krb5_mech.c index a66eb706aeb7..6f93f4752be4 100644 --- a/net/sunrpc/auth_gss/gss_krb5_mech.c +++ b/net/sunrpc/auth_gss/gss_krb5_mech.c | |||
@@ -66,6 +66,7 @@ static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { | |||
66 | .keylength = 8, | 66 | .keylength = 8, |
67 | .blocksize = 8, | 67 | .blocksize = 8, |
68 | .cksumlength = 8, | 68 | .cksumlength = 8, |
69 | .keyed_cksum = 0, | ||
69 | }, | 70 | }, |
70 | }; | 71 | }; |
71 | 72 | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c index 46c6f44e5c3f..cd512719092b 100644 --- a/net/sunrpc/auth_gss/gss_krb5_seal.c +++ b/net/sunrpc/auth_gss/gss_krb5_seal.c | |||
@@ -101,6 +101,7 @@ gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text, | |||
101 | void *ptr; | 101 | void *ptr; |
102 | s32 now; | 102 | s32 now; |
103 | u32 seq_send; | 103 | u32 seq_send; |
104 | u8 *cksumkey; | ||
104 | 105 | ||
105 | dprintk("RPC: %s\n", __func__); | 106 | dprintk("RPC: %s\n", __func__); |
106 | BUG_ON(ctx == NULL); | 107 | BUG_ON(ctx == NULL); |
@@ -109,15 +110,15 @@ gss_get_mic_v1(struct krb5_ctx *ctx, struct xdr_buf *text, | |||
109 | 110 | ||
110 | ptr = setup_token(ctx, token); | 111 | ptr = setup_token(ctx, token); |
111 | 112 | ||
112 | if (make_checksum((char *)ctx->gk5e->cksum_name, ptr, 8, | 113 | if (ctx->gk5e->keyed_cksum) |
113 | text, 0, &md5cksum)) | 114 | cksumkey = ctx->cksum; |
114 | return GSS_S_FAILURE; | 115 | else |
116 | cksumkey = NULL; | ||
115 | 117 | ||
116 | if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, | 118 | if (make_checksum(ctx, ptr, 8, text, 0, cksumkey, &md5cksum)) |
117 | md5cksum.data, md5cksum.len)) | ||
118 | return GSS_S_FAILURE; | 119 | return GSS_S_FAILURE; |
119 | 120 | ||
120 | memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8); | 121 | memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data, md5cksum.len); |
121 | 122 | ||
122 | spin_lock(&krb5_seq_lock); | 123 | spin_lock(&krb5_seq_lock); |
123 | seq_send = ctx->seq_send++; | 124 | seq_send = ctx->seq_send++; |
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c index 10ee641a39d0..7515bffddf15 100644 --- a/net/sunrpc/auth_gss/gss_krb5_unseal.c +++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c | |||
@@ -84,6 +84,7 @@ gss_verify_mic_v1(struct krb5_ctx *ctx, | |||
84 | u32 seqnum; | 84 | u32 seqnum; |
85 | unsigned char *ptr = (unsigned char *)read_token->data; | 85 | unsigned char *ptr = (unsigned char *)read_token->data; |
86 | int bodysize; | 86 | int bodysize; |
87 | u8 *cksumkey; | ||
87 | 88 | ||
88 | dprintk("RPC: krb5_read_token\n"); | 89 | dprintk("RPC: krb5_read_token\n"); |
89 | 90 | ||
@@ -108,14 +109,16 @@ gss_verify_mic_v1(struct krb5_ctx *ctx, | |||
108 | if ((ptr[6] != 0xff) || (ptr[7] != 0xff)) | 109 | if ((ptr[6] != 0xff) || (ptr[7] != 0xff)) |
109 | return GSS_S_DEFECTIVE_TOKEN; | 110 | return GSS_S_DEFECTIVE_TOKEN; |
110 | 111 | ||
111 | if (make_checksum((char *)ctx->gk5e->cksum_name, ptr, 8, | 112 | if (ctx->gk5e->keyed_cksum) |
112 | message_buffer, 0, &md5cksum)) | 113 | cksumkey = ctx->cksum; |
113 | return GSS_S_FAILURE; | 114 | else |
115 | cksumkey = NULL; | ||
114 | 116 | ||
115 | if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, 16)) | 117 | if (make_checksum(ctx, ptr, 8, message_buffer, 0, |
118 | cksumkey, &md5cksum)) | ||
116 | return GSS_S_FAILURE; | 119 | return GSS_S_FAILURE; |
117 | 120 | ||
118 | if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, | 121 | if (memcmp(md5cksum.data, ptr + GSS_KRB5_TOK_HDR_LEN, |
119 | ctx->gk5e->cksumlength)) | 122 | ctx->gk5e->cksumlength)) |
120 | return GSS_S_BAD_SIG; | 123 | return GSS_S_BAD_SIG; |
121 | 124 | ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c index 7188891bcc33..2eb3046a84ea 100644 --- a/net/sunrpc/auth_gss/gss_krb5_wrap.c +++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c | |||
@@ -167,6 +167,7 @@ gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset, | |||
167 | int headlen; | 167 | int headlen; |
168 | struct page **tmp_pages; | 168 | struct page **tmp_pages; |
169 | u32 seq_send; | 169 | u32 seq_send; |
170 | u8 *cksumkey; | ||
170 | 171 | ||
171 | dprintk("RPC: %s\n", __func__); | 172 | dprintk("RPC: %s\n", __func__); |
172 | 173 | ||
@@ -205,18 +206,20 @@ gss_wrap_kerberos_v1(struct krb5_ctx *kctx, int offset, | |||
205 | 206 | ||
206 | make_confounder(msg_start, blocksize); | 207 | make_confounder(msg_start, blocksize); |
207 | 208 | ||
209 | if (kctx->gk5e->keyed_cksum) | ||
210 | cksumkey = kctx->cksum; | ||
211 | else | ||
212 | cksumkey = NULL; | ||
213 | |||
208 | /* XXXJBF: UGH!: */ | 214 | /* XXXJBF: UGH!: */ |
209 | tmp_pages = buf->pages; | 215 | tmp_pages = buf->pages; |
210 | buf->pages = pages; | 216 | buf->pages = pages; |
211 | if (make_checksum((char *)kctx->gk5e->cksum_name, ptr, 8, buf, | 217 | if (make_checksum(kctx, ptr, 8, buf, offset + headlen - blocksize, |
212 | offset + headlen - blocksize, &md5cksum)) | 218 | cksumkey, &md5cksum)) |
213 | return GSS_S_FAILURE; | 219 | return GSS_S_FAILURE; |
214 | buf->pages = tmp_pages; | 220 | buf->pages = tmp_pages; |
215 | 221 | ||
216 | if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, | 222 | memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data, md5cksum.len); |
217 | md5cksum.data, md5cksum.len)) | ||
218 | return GSS_S_FAILURE; | ||
219 | memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8); | ||
220 | 223 | ||
221 | spin_lock(&krb5_seq_lock); | 224 | spin_lock(&krb5_seq_lock); |
222 | seq_send = kctx->seq_send++; | 225 | seq_send = kctx->seq_send++; |
@@ -252,6 +255,7 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf) | |||
252 | int data_len; | 255 | int data_len; |
253 | int blocksize; | 256 | int blocksize; |
254 | int crypt_offset; | 257 | int crypt_offset; |
258 | u8 *cksumkey; | ||
255 | 259 | ||
256 | dprintk("RPC: gss_unwrap_kerberos\n"); | 260 | dprintk("RPC: gss_unwrap_kerberos\n"); |
257 | 261 | ||
@@ -288,15 +292,17 @@ gss_unwrap_kerberos_v1(struct krb5_ctx *kctx, int offset, struct xdr_buf *buf) | |||
288 | if (gss_decrypt_xdr_buf(kctx->enc, buf, crypt_offset)) | 292 | if (gss_decrypt_xdr_buf(kctx->enc, buf, crypt_offset)) |
289 | return GSS_S_DEFECTIVE_TOKEN; | 293 | return GSS_S_DEFECTIVE_TOKEN; |
290 | 294 | ||
291 | if (make_checksum((char *)kctx->gk5e->cksum_name, ptr, 8, buf, | 295 | if (kctx->gk5e->keyed_cksum) |
292 | crypt_offset, &md5cksum)) | 296 | cksumkey = kctx->cksum; |
293 | return GSS_S_FAILURE; | 297 | else |
298 | cksumkey = NULL; | ||
294 | 299 | ||
295 | if (krb5_encrypt(kctx->seq, NULL, md5cksum.data, | 300 | if (make_checksum(kctx, ptr, 8, buf, crypt_offset, |
296 | md5cksum.data, md5cksum.len)) | 301 | cksumkey, &md5cksum)) |
297 | return GSS_S_FAILURE; | 302 | return GSS_S_FAILURE; |
298 | 303 | ||
299 | if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8)) | 304 | if (memcmp(md5cksum.data, ptr + GSS_KRB5_TOK_HDR_LEN, |
305 | kctx->gk5e->cksumlength)) | ||
300 | return GSS_S_BAD_SIG; | 306 | return GSS_S_BAD_SIG; |
301 | 307 | ||
302 | /* it got through unscathed. Make sure the context is unexpired */ | 308 | /* it got through unscathed. Make sure the context is unexpired */ |