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 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,
73 memset(msgbuf->buf + msgbuf->dataoff + msgbuf->datalen,
75 msgbuf->datalen += padding;
80 int generate_confounder(rawobj_buf_t *msgbuf, int blocksize)
84 p = msgbuf->buf + msgbuf->dataoff - blocksize;
85 if (p < msgbuf->buf) {
86 CERROR("buf underflow\n");
90 get_random_bytes(p, blocksize);
95 gss_wrap_kerberos(struct gss_ctx *ctx,
100 struct krb5_ctx *kctx = ctx->internal_ctx_id;
102 rawobj_t data_desc, cipher_out, md5cksum;
104 unsigned char *ptr, *krb5_hdr, *msg_start;
105 int head_len, plain_len;
106 __u32 seq_send, major;
110 CERROR("not support qop %x yet\n", qop);
111 RETURN(GSS_S_FAILURE);
114 switch (kctx->signalg) {
115 case SGN_ALG_DES_MAC_MD5:
116 checksum_type = CKSUMTYPE_RSA_MD5;
119 CERROR("not support signalg %x\n", kctx->signalg);
120 RETURN(GSS_S_FAILURE);
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);
128 blocksize = crypto_tfm_alg_blocksize(kctx->enc);
129 LASSERT(blocksize <= 16);
130 LASSERT(blocksize == 8); /* acutally must be 8 for now */
132 if (add_padding(msgbuf, blocksize))
133 RETURN(GSS_S_FAILURE);
135 /* confounder size == blocksize */
136 plain_len = msgbuf->datalen + blocksize;
138 head_len = g_token_size(&kctx->mech_used, 22 + plain_len) -
141 LASSERT(token->len >= head_len);
145 * fill in gss header and krb5 header
147 g_make_token_header(&kctx->mech_used, 22 + plain_len, &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);
157 * prepend confounder on plain text
159 if (generate_confounder(msgbuf, blocksize))
160 RETURN(GSS_S_FAILURE);
163 * compute checksum including confounder
165 data_desc.data = msgbuf->buf + msgbuf->dataoff - blocksize;
166 data_desc.len = msgbuf->datalen + blocksize;
168 if (make_checksum(checksum_type, (char *)krb5_hdr,
169 8, &data_desc, &md5cksum)) {
170 CERROR("checksum error\n");
171 RETURN(GSS_S_FAILURE);
174 major = GSS_S_FAILURE;
175 switch (kctx->signalg) {
176 case SGN_ALG_DES_MAC_MD5:
177 if (krb5_encrypt(kctx->seq, NULL, md5cksum.data,
178 md5cksum.data, md5cksum.len)) {
179 rawobj_free(&md5cksum);
180 RETURN(GSS_S_FAILURE);
182 memcpy(krb5_hdr + 16,
183 md5cksum.data + md5cksum.len - KRB5_CKSUM_LENGTH,
190 rawobj_free(&md5cksum);
193 * fill sequence number in krb5 header
195 spin_lock(&krb5_seq_lock);
196 seq_send = kctx->seq_send++;
197 spin_unlock(&krb5_seq_lock);
199 if (krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff,
200 seq_send, krb5_hdr + 16, krb5_hdr + 8))
201 RETURN(GSS_S_FAILURE);
204 data_desc.data = msgbuf->buf + msgbuf->dataoff - blocksize;
205 data_desc.len = msgbuf->datalen + blocksize;
206 cipher_out.data = msg_start;
207 cipher_out.len = token->len - (msg_start - token->data);
208 LASSERT(data_desc.len % blocksize == 0);
209 LASSERT(data_desc.len <= cipher_out.len);
211 if (gss_encrypt_rawobj(kctx->enc, &data_desc, &cipher_out, 1))
212 RETURN(GSS_S_FAILURE);
214 token->len = (msg_start - token->data) + cipher_out.len;
219 gss_unwrap_kerberos(struct gss_ctx *ctx,
224 struct krb5_ctx *kctx = ctx->internal_ctx_id;
225 int signalg, sealalg;
226 rawobj_t cipher_in, plain_out, md5cksum;
227 unsigned char *ptr, *krb5_hdr, *tmpbuf;
229 int blocksize, seqnum, direction;
234 ptr = in_token->data;
239 major = g_verify_token_header(&kctx->mech_used, &bodysize, &ptr,
242 CERROR("gss token error %d\n", major);
243 RETURN(GSS_S_FAILURE);
248 if ((*ptr++ != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) ||
249 (*ptr++ != (KG_TOK_WRAP_MSG & 0xff))) {
250 CERROR("token type not matched\n");
251 RETURN(G_BAD_TOK_HEADER);
255 CERROR("body size only %d\n", bodysize);
256 RETURN(G_WRONG_SIZE);
262 signalg = ptr[0] | (ptr[1] << 8);
263 sealalg = ptr[2] | (ptr[3] << 8);
265 if (ptr[4] != 0xFF || ptr[5] != 0xFF) {
266 CERROR("4/5: %d, %d\n", ptr[4], ptr[5]);
267 RETURN(GSS_S_DEFECTIVE_TOKEN);
270 if (sealalg != kctx->sealalg) {
271 CERROR("sealalg %d not matched my %d\n",
272 sealalg, kctx->sealalg);
273 RETURN(GSS_S_DEFECTIVE_TOKEN);
276 if ((kctx->sealalg == SEAL_ALG_NONE && signalg > 1) ||
277 (kctx->sealalg == SEAL_ALG_1 && signalg != SGN_ALG_3) ||
278 (kctx->sealalg == SEAL_ALG_DES3KD &&
279 signalg != SGN_ALG_HMAC_SHA1_DES3_KD)) {
280 CERROR("bad sealalg %d\n", sealalg);
281 RETURN(GSS_S_DEFECTIVE_TOKEN);
284 /* make bodysize as the actual cipher text size */
287 CERROR("cipher text size %d?\n", bodysize);
288 RETURN(GSS_S_DEFECTIVE_TOKEN);
291 blocksize = crypto_tfm_alg_blocksize(kctx->enc);
292 if (bodysize % blocksize) {
293 CERROR("odd bodysize %d\n", bodysize);
294 RETURN(GSS_S_DEFECTIVE_TOKEN);
297 OBD_ALLOC(tmpbuf, bodysize);
299 CERROR("fail alloc %d\n", bodysize);
300 RETURN(GSS_S_FAILURE);
303 cipher_in.data = krb5_hdr + 24;
304 cipher_in.len = bodysize;
305 plain_out.data = tmpbuf;
306 plain_out.len = bodysize;
308 major = GSS_S_DEFECTIVE_TOKEN;
309 if (gss_encrypt_rawobj(kctx->enc, &cipher_in, &plain_out, 0)) {
310 CERROR("error decrypt: 0x%x\n", major);
311 GOTO(out_free, major);
313 LASSERT(plain_out.len == bodysize);
319 case SGN_ALG_DES_MAC_MD5:
320 checksum_type = CKSUMTYPE_RSA_MD5;
321 major = make_checksum(checksum_type, (char *)krb5_hdr,
322 8, &plain_out, &md5cksum);
324 CERROR("make checksum err: 0x%x\n", major);
325 GOTO(out_free, major);
328 major = krb5_encrypt(kctx->seq, NULL, md5cksum.data,
329 md5cksum.data, md5cksum.len);
331 CERROR("encrypt checksum err: 0x%x\n", major);
332 rawobj_free(&md5cksum);
333 GOTO(out_free, major);
336 if (memcmp(md5cksum.data + 8, krb5_hdr + 16, 8)) {
337 CERROR("checksum mismatch\n");
338 rawobj_free(&md5cksum);
339 GOTO(out_free, major = GSS_S_BAD_SIG);
343 CERROR("not support signalg %d\n", signalg);
344 GOTO(out_free, major);
347 rawobj_free(&md5cksum);
349 /* FIXME add expire checking here */
351 major = krb5_get_seq_num(kctx->seq, krb5_hdr + 16,
352 krb5_hdr + 8, &direction,
355 CERROR("get seq number err: 0x%x\n", major);
356 GOTO(out_free, major);
359 if ((kctx->initiate && direction != 0xff) ||
360 (!kctx->initiate && direction != 0)) {
361 CERROR("flag checking error\n");
362 GOTO(out_free, major = GSS_S_BAD_SIG);
365 /* FIXME how to remove the padding? */
370 if (out_token->len < bodysize - blocksize) {
371 CERROR("data size %d while buffer only %d\n",
372 bodysize - blocksize, out_token->len);
373 GOTO(out_free, major = GSS_S_DEFECTIVE_TOKEN);
376 out_token->len = bodysize - blocksize;
377 memcpy(out_token->data, plain_out.data + blocksize, out_token->len);
380 OBD_FREE(tmpbuf, bodysize);