1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2 * vim:expandtab:shiftwidth=8:tabstop=8:
4 * Modified from NFSv4 projects for Lustre
5 * Copyright 2004, Cluster File Systems, Inc.
7 * Author: Eric Mei <ericm@clusterfs.com>
9 * This file is part of Lustre, http://www.lustre.org.
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.
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.
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.
26 # define EXPORT_SYMTAB
28 #define DEBUG_SUBSYSTEM S_SEC
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>
36 #include <liblustre.h>
37 #include "../kcrypto/libcrypto.h"
38 #include <netinet/in.h>
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>
51 #include "gss_internal.h"
57 int add_padding(rawobj_buf_t *msgbuf, int blocksize)
61 padding = (blocksize - (msgbuf->datalen & (blocksize - 1))) &
66 if (msgbuf->dataoff + msgbuf->datalen + padding > msgbuf->buflen) {
67 CERROR("bufsize %u too small: off %u, len %u, padding %u\n",
68 msgbuf->buflen, msgbuf->dataoff, msgbuf->datalen,
72 memset(msgbuf->buf + msgbuf->dataoff + msgbuf->datalen,
74 msgbuf->datalen += padding;
79 int generate_confounder(rawobj_buf_t *msgbuf, int blocksize)
83 p = msgbuf->buf + msgbuf->dataoff - blocksize;
84 if (p < msgbuf->buf) {
85 CERROR("buf underflow\n");
89 get_random_bytes(p, blocksize);
94 gss_wrap_kerberos(struct gss_ctx *ctx,
99 struct krb5_ctx *kctx = ctx->internal_ctx_id;
101 rawobj_t data_desc, cipher_out, md5cksum;
103 unsigned char *ptr, *krb5_hdr, *msg_start;
104 int head_len, plain_len;
105 __u32 seq_send, major;
109 CERROR("not support qop %x yet\n", qop);
110 RETURN(GSS_S_FAILURE);
113 switch (kctx->signalg) {
114 case SGN_ALG_DES_MAC_MD5:
115 checksum_type = CKSUMTYPE_RSA_MD5;
118 CERROR("not support signalg %x\n", kctx->signalg);
119 RETURN(GSS_S_FAILURE);
121 if (kctx->sealalg != SEAL_ALG_NONE &&
122 kctx->sealalg != SEAL_ALG_DES) {
123 CERROR("not support sealalg %x\n", kctx->sealalg);
124 RETURN(GSS_S_FAILURE);
127 blocksize = crypto_tfm_alg_blocksize(kctx->enc);
128 LASSERT(blocksize <= 16);
129 LASSERT(blocksize == 8); /* acutally must be 8 for now */
131 if (add_padding(msgbuf, blocksize))
132 RETURN(GSS_S_FAILURE);
134 /* confounder size == blocksize */
135 plain_len = msgbuf->datalen + blocksize;
137 head_len = g_token_size(&kctx->mech_used, 22 + plain_len) -
140 LASSERT(token->len >= head_len);
144 * fill in gss header and krb5 header
146 g_make_token_header(&kctx->mech_used, 22 + plain_len, &ptr);
148 msg_start = krb5_hdr + 24;
149 *ptr++ = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff);
150 *ptr++ = (unsigned char) (KG_TOK_WRAP_MSG & 0xff);
151 *(__u16 *)(krb5_hdr + 2) = cpu_to_be16(kctx->signalg);
152 memset(krb5_hdr + 4, 0xff, 4);
153 *(__u16 *)(krb5_hdr + 4) = cpu_to_be16(kctx->sealalg);
156 * prepend confounder on plain text
158 if (generate_confounder(msgbuf, blocksize))
159 RETURN(GSS_S_FAILURE);
162 * compute checksum including confounder
164 data_desc.data = msgbuf->buf + msgbuf->dataoff - blocksize;
165 data_desc.len = msgbuf->datalen + blocksize;
167 if (make_checksum(checksum_type, (char *)krb5_hdr,
168 8, &data_desc, &md5cksum)) {
169 CERROR("checksum error\n");
170 RETURN(GSS_S_FAILURE);
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);
181 memcpy(krb5_hdr + 16,
182 md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH,
189 rawobj_free(&md5cksum);
192 * fill sequence number in krb5 header
194 spin_lock(&krb5_seq_lock);
195 seq_send = kctx->seq_send++;
196 spin_unlock(&krb5_seq_lock);
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);
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);
210 if (gss_encrypt_rawobj(kctx->enc, &data_desc, &cipher_out, 1))
211 RETURN(GSS_S_FAILURE);
213 token->len = (msg_start - token->data) + cipher_out.len;
218 gss_unwrap_kerberos(struct gss_ctx *ctx,
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;
228 int blocksize, seqnum, direction;
233 ptr = in_token->data;
238 major = g_verify_token_header(&kctx->mech_used, &bodysize, &ptr,
241 CERROR("gss token error %d\n", major);
242 RETURN(GSS_S_FAILURE);
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);
254 CERROR("body size only %d\n", bodysize);
255 RETURN(G_WRONG_SIZE);
261 signalg = ptr[0] | (ptr[1] << 8);
262 sealalg = ptr[2] | (ptr[3] << 8);
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);
269 if (sealalg != kctx->sealalg) {
270 CERROR("sealalg %d not matched my %d\n",
271 sealalg, kctx->sealalg);
272 RETURN(GSS_S_DEFECTIVE_TOKEN);
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);
283 /* make bodysize as the actual cipher text size */
286 CERROR("cipher text size %d?\n", bodysize);
287 RETURN(GSS_S_DEFECTIVE_TOKEN);
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);
296 OBD_ALLOC(tmpbuf, bodysize);
298 CERROR("fail alloc %d\n", bodysize);
299 RETURN(GSS_S_FAILURE);
302 cipher_in.data = krb5_hdr + 24;
303 cipher_in.len = bodysize;
304 plain_out.data = tmpbuf;
305 plain_out.len = bodysize;
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);
312 LASSERT(plain_out.len == bodysize);
318 case SGN_ALG_DES_MAC_MD5:
319 checksum_type = CKSUMTYPE_RSA_MD5;
320 major = make_checksum(checksum_type, (char *)krb5_hdr,
321 8, &plain_out, &md5cksum);
323 CERROR("make checksum err: 0x%x\n", major);
324 GOTO(out_free, major);
327 major = krb5_encrypt(kctx->seq, NULL, md5cksum.data,
328 md5cksum.data, md5cksum.len);
330 CERROR("encrypt checksum err: 0x%x\n", major);
331 rawobj_free(&md5cksum);
332 GOTO(out_free, major);
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);
342 CERROR("not support signalg %d\n", signalg);
343 GOTO(out_free, major);
346 rawobj_free(&md5cksum);
348 /* FIXME add expire checking here */
350 major = krb5_get_seq_num(kctx->seq, krb5_hdr + 16,
351 krb5_hdr + 8, &direction,
354 CERROR("get seq number err: 0x%x\n", major);
355 GOTO(out_free, major);
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);
364 /* FIXME how to remove the padding? */
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);
375 out_token->len = bodysize - blocksize;
376 memcpy(out_token->data, plain_out.data + blocksize, out_token->len);
379 OBD_FREE(tmpbuf, bodysize);