Whamcloud - gitweb
land lustre part of b_hd_sec on HEAD.
[fs/lustre-release.git] / lustre / sec / gss / gss_krb5_wrap.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *   Modified from NFSv4 projects for Lustre
5  *   Copyright 2004, Cluster File Systems, Inc.
6  *   All rights reserved
7  *   Author: Eric Mei <ericm@clusterfs.com>
8  *
9  *   This file is part of Lustre, http://www.lustre.org.
10  *
11  *   Lustre is free software; you can redistribute it and/or
12  *   modify it under the terms of version 2 of the GNU General Public
13  *   License as published by the Free Software Foundation.
14  *
15  *   Lustre is distributed in the hope that it will be useful,
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *   GNU General Public License for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with Lustre; if not, write to the Free Software
22  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #ifndef EXPORT_SYMTAB
26 # define EXPORT_SYMTAB
27 #endif
28 #define DEBUG_SUBSYSTEM S_SEC
29 #ifdef __KERNEL__
30 #include <linux/init.h>
31 #include <linux/module.h>
32 #include <linux/slab.h>
33 #include <linux/crypto.h>
34 #include <linux/random.h>
35 #else
36 #include <liblustre.h>
37 #include "../kcrypto/libcrypto.h"
38 #include <netinet/in.h>
39 #endif
40
41 #include <libcfs/kp30.h>
42 #include <linux/obd.h>
43 #include <linux/obd_class.h>
44 #include <linux/obd_support.h>
45 #include <linux/lustre_idl.h>
46 #include <linux/lustre_net.h>
47 #include <linux/lustre_import.h>
48 #include <linux/lustre_sec.h>
49
50 #include "gss_err.h"
51 #include "gss_internal.h"
52 #include "gss_api.h"
53 #include "gss_krb5.h"
54 #include "gss_asn1.h"
55
56 static inline
57 int add_padding(rawobj_buf_t *msgbuf, int blocksize)
58 {
59         int padding;
60
61         padding = (blocksize - (msgbuf->datalen & (blocksize - 1))) &
62                   (blocksize - 1);
63         if (padding == 0)
64                 return 0;
65
66         CWARN("add padding %d\n", padding);
67         if (msgbuf->dataoff + msgbuf->datalen + padding > msgbuf->buflen) {
68                 CERROR("bufsize %u too small: off %u, len %u, padding %u\n",
69                         msgbuf->buflen, msgbuf->dataoff, msgbuf->datalen,
70                         padding);
71                 return -EINVAL;
72         }
73         memset(msgbuf->buf + msgbuf->dataoff + msgbuf->datalen,
74                padding, padding);
75         msgbuf->datalen += padding;
76         return 0;
77 }
78
79 static inline
80 int generate_confounder(rawobj_buf_t *msgbuf, int blocksize)
81 {
82         __u8 *p;
83
84         p = msgbuf->buf + msgbuf->dataoff - blocksize;
85         if (p < msgbuf->buf) {
86                 CERROR("buf underflow\n");
87                 return -EINVAL;
88         }
89
90         get_random_bytes(p, blocksize);
91         return 0;
92 }
93
94 __u32
95 gss_wrap_kerberos(struct gss_ctx    *ctx,
96                   __u32              qop,
97                   rawobj_buf_t      *msgbuf,
98                   rawobj_t          *token)
99 {
100         struct krb5_ctx        *kctx = ctx->internal_ctx_id;
101         __u32                   checksum_type;
102         rawobj_t                data_desc, cipher_out, md5cksum;
103         int                     blocksize;
104         unsigned char          *ptr, *krb5_hdr, *msg_start;
105         int                     head_len, plain_len;
106         __u32                   seq_send, major;
107         ENTRY;
108
109         if (qop) {
110                 CERROR("not support qop %x yet\n", qop);
111                 RETURN(GSS_S_FAILURE);
112         }
113
114         switch (kctx->signalg) {
115         case SGN_ALG_DES_MAC_MD5:
116                 checksum_type = CKSUMTYPE_RSA_MD5;
117                 break;
118         default:
119                 CERROR("not support signalg %x\n", kctx->signalg);
120                 RETURN(GSS_S_FAILURE);
121         }
122         if (kctx->sealalg != SEAL_ALG_NONE &&
123             kctx->sealalg != SEAL_ALG_DES) {
124                 CERROR("not support sealalg %x\n", kctx->sealalg);
125                 RETURN(GSS_S_FAILURE);
126         }
127
128         blocksize = crypto_tfm_alg_blocksize(kctx->enc);
129         LASSERT(blocksize <= 16);
130         LASSERT(blocksize == 8); /* acutally must be 8 for now */
131
132         if (add_padding(msgbuf, blocksize))
133                 RETURN(GSS_S_FAILURE);
134
135         /* confounder size == blocksize */
136         plain_len = msgbuf->datalen + blocksize;
137
138         head_len = g_token_size(&kctx->mech_used, 22 + plain_len) -
139                    msgbuf->datalen;
140
141         LASSERT(token->len >= head_len);
142         ptr = token->data;
143
144         /*
145          * fill in gss header and  krb5 header
146          */
147         g_make_token_header(&kctx->mech_used, 22 + plain_len, &ptr);
148         krb5_hdr = ptr;
149         msg_start = krb5_hdr + 24;
150         *ptr++ = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff);
151         *ptr++ = (unsigned char) (KG_TOK_WRAP_MSG & 0xff);
152         *(__u16 *)(krb5_hdr + 2) = cpu_to_be16(kctx->signalg);
153         memset(krb5_hdr + 4, 0xff, 4);
154         *(__u16 *)(krb5_hdr + 4) = cpu_to_be16(kctx->sealalg);
155
156         /*
157          * prepend confounder on plain text
158          */
159         if (generate_confounder(msgbuf, blocksize))
160                 RETURN(GSS_S_FAILURE);
161
162         /*
163          * compute checksum including confounder
164          */
165         data_desc.data = msgbuf->buf + msgbuf->dataoff - blocksize;
166         data_desc.len = msgbuf->datalen + blocksize;
167
168         if (make_checksum(checksum_type, krb5_hdr, 8, &data_desc, &md5cksum)) {
169                 CERROR("checksum error\n");
170                 RETURN(GSS_S_FAILURE);
171         }
172
173         major = GSS_S_FAILURE;
174         switch (kctx->signalg) {
175         case SGN_ALG_DES_MAC_MD5:
176                 if (krb5_encrypt(kctx->seq, NULL, md5cksum.data,
177                                  md5cksum.data, md5cksum.len)) {
178                         rawobj_free(&md5cksum);
179                         RETURN(GSS_S_FAILURE);
180                 }
181                 memcpy(krb5_hdr + 16,
182                        md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH,
183                        KRB5_CKSUM_LENGTH);
184                 break;
185         default:
186                 LBUG();
187         }
188
189         rawobj_free(&md5cksum);
190
191         /*
192          * fill sequence number in krb5 header
193          */
194         spin_lock(&krb5_seq_lock);
195         seq_send = kctx->seq_send++;
196         spin_unlock(&krb5_seq_lock);
197
198         if (krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff,
199                                seq_send, krb5_hdr + 16, krb5_hdr + 8))
200                 RETURN(GSS_S_FAILURE);
201
202         /* do encryption */
203         data_desc.data = msgbuf->buf + msgbuf->dataoff - blocksize;
204         data_desc.len = msgbuf->datalen + blocksize;
205         cipher_out.data = msg_start;
206         cipher_out.len = token->len - (msg_start - token->data);
207         LASSERT(data_desc.len % blocksize == 0);
208         LASSERT(data_desc.len <= cipher_out.len);
209
210         if (gss_encrypt_rawobj(kctx->enc, &data_desc, &cipher_out, 1))
211                 RETURN(GSS_S_FAILURE);
212
213         token->len = (msg_start - token->data) + cipher_out.len;
214         RETURN(0);
215 }
216
217 __u32
218 gss_unwrap_kerberos(struct gss_ctx  *ctx,
219                     __u32            qop,
220                     rawobj_t        *in_token,
221                     rawobj_t        *out_token)
222 {
223         struct krb5_ctx        *kctx = ctx->internal_ctx_id;
224         int                     signalg, sealalg;
225         rawobj_t                cipher_in, plain_out, md5cksum;
226         unsigned char          *ptr, *krb5_hdr, *tmpbuf;
227         int                     bodysize;
228         int                     blocksize, seqnum, direction;
229         __u32                   checksum_type;
230         __u32                   major;
231         ENTRY;
232
233         ptr = in_token->data;
234
235         /*
236          * verify gss header
237          */
238         major = g_verify_token_header(&kctx->mech_used, &bodysize, &ptr,
239                                       in_token->len);
240         if (major) {
241                 CERROR("gss token error %d\n", major);
242                 RETURN(GSS_S_FAILURE);
243         }
244
245         krb5_hdr = ptr;
246
247         if ((*ptr++ != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) ||
248             (*ptr++ !=  (KG_TOK_WRAP_MSG & 0xff))) {
249                 CERROR("token type not matched\n");
250                 RETURN(G_BAD_TOK_HEADER);
251         }
252
253         if (bodysize < 22) {
254                 CERROR("body size only %d\n", bodysize);
255                 RETURN(G_WRONG_SIZE);
256         }
257
258         /*
259          * extract algorithms
260          */
261         signalg = ptr[0] | (ptr[1] << 8);
262         sealalg = ptr[2] | (ptr[3] << 8);
263
264         if (ptr[4] != 0xFF || ptr[5] != 0xFF) {
265                 CERROR("4/5: %d, %d\n", ptr[4], ptr[5]);
266                 RETURN(GSS_S_DEFECTIVE_TOKEN);
267         }
268
269         if (sealalg != kctx->sealalg) {
270                 CERROR("sealalg %d not matched my %d\n",
271                         sealalg, kctx->sealalg);
272                 RETURN(GSS_S_DEFECTIVE_TOKEN);
273         }
274
275         if ((kctx->sealalg == SEAL_ALG_NONE && signalg > 1) ||
276             (kctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) ||
277             (kctx->sealalg == SEAL_ALG_DES3KD &&
278              signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) {
279                 CERROR("bad sealalg %d\n", sealalg);
280                 RETURN(GSS_S_DEFECTIVE_TOKEN);
281         }
282
283         /* make bodysize as the actual cipher text size */
284         bodysize -= 22;
285         if (bodysize <= 0) {
286                 CERROR("cipher text size %d?\n", bodysize);
287                 RETURN(GSS_S_DEFECTIVE_TOKEN);
288         }
289
290         blocksize = crypto_tfm_alg_blocksize(kctx->enc);
291         if (bodysize % blocksize) {
292                 CERROR("odd bodysize %d\n", bodysize);
293                 RETURN(GSS_S_DEFECTIVE_TOKEN);
294         }
295
296         OBD_ALLOC(tmpbuf, bodysize);
297         if (!tmpbuf) {
298                 CERROR("fail alloc %d\n", bodysize);
299                 RETURN(GSS_S_FAILURE);
300         }
301
302         cipher_in.data = krb5_hdr + 24;
303         cipher_in.len = bodysize;
304         plain_out.data = tmpbuf;
305         plain_out.len = bodysize;
306
307         major = GSS_S_DEFECTIVE_TOKEN;
308         if (gss_encrypt_rawobj(kctx->enc, &cipher_in, &plain_out, 0)) {
309                 CERROR("error decrypt: 0x%x\n", major);
310                 GOTO(out_free, major);
311         }
312         LASSERT(plain_out.len == bodysize);
313
314         /*
315          * verify checksum
316          */
317         switch (signalg) {
318         case SGN_ALG_DES_MAC_MD5:
319                 checksum_type = CKSUMTYPE_RSA_MD5;
320                 major = make_checksum(checksum_type, krb5_hdr, 8,
321                                       &plain_out, &md5cksum);
322                 if (major) {
323                         CERROR("make checksum err: 0x%x\n", major);
324                         GOTO(out_free, major);
325                 }
326
327                 major = krb5_encrypt(kctx->seq, NULL, md5cksum.data,
328                                      md5cksum.data, md5cksum.len);
329                 if (major) {
330                         CERROR("encrypt checksum err: 0x%x\n", major);
331                         rawobj_free(&md5cksum);
332                         GOTO(out_free, major);
333                 }
334
335                 if (memcmp(md5cksum.data + 8, krb5_hdr + 16, 8)) {
336                         CERROR("checksum mismatch\n");
337                         rawobj_free(&md5cksum);
338                         GOTO(out_free, major = GSS_S_BAD_SIG);
339                 }
340                 break;
341         default:
342                 CERROR("not support signalg %d\n", signalg);
343                 GOTO(out_free, major);
344         }
345
346         rawobj_free(&md5cksum);
347
348         /* FIXME add expire checking here */
349
350         major = krb5_get_seq_num(kctx->seq, krb5_hdr + 16,
351                                  krb5_hdr + 8, &direction,
352                                  &seqnum);
353         if (major) {
354                 CERROR("get seq number err: 0x%x\n", major);
355                 GOTO(out_free, major);
356         }
357
358         if ((kctx->initiate && direction != 0xff) ||
359             (!kctx->initiate && direction != 0)) {
360                 CERROR("flag checking error\n");
361                 GOTO(out_free, major = GSS_S_BAD_SIG);
362         }
363
364         /* FIXME how to remove the padding? */
365
366         /*
367          * copy back
368          */
369         if (out_token->len < bodysize - blocksize) {
370                 CERROR("data size %d while buffer only %d\n",
371                         bodysize - blocksize, out_token->len);
372                 GOTO(out_free, major = GSS_S_DEFECTIVE_TOKEN);
373         }
374
375         out_token->len = bodysize - blocksize;
376         memcpy(out_token->data, plain_out.data + blocksize, out_token->len);
377         major = 0;
378 out_free:
379         OBD_FREE(tmpbuf, bodysize);
380         RETURN(major);
381 }