11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * linux/net/sunrpc/gss_krb5_mech.c 31da177e4SLinus Torvalds * 481d4a433SKevin Coffman * Copyright (c) 2001-2008 The Regents of the University of Michigan. 51da177e4SLinus Torvalds * All rights reserved. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Andy Adamson <andros@umich.edu> 81da177e4SLinus Torvalds * J. Bruce Fields <bfields@umich.edu> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Redistribution and use in source and binary forms, with or without 111da177e4SLinus Torvalds * modification, are permitted provided that the following conditions 121da177e4SLinus Torvalds * are met: 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * 1. Redistributions of source code must retain the above copyright 151da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer. 161da177e4SLinus Torvalds * 2. Redistributions in binary form must reproduce the above copyright 171da177e4SLinus Torvalds * notice, this list of conditions and the following disclaimer in the 181da177e4SLinus Torvalds * documentation and/or other materials provided with the distribution. 191da177e4SLinus Torvalds * 3. Neither the name of the University nor the names of its 201da177e4SLinus Torvalds * contributors may be used to endorse or promote products derived 211da177e4SLinus Torvalds * from this software without specific prior written permission. 221da177e4SLinus Torvalds * 231da177e4SLinus Torvalds * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED 241da177e4SLinus Torvalds * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 251da177e4SLinus Torvalds * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 261da177e4SLinus Torvalds * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 271da177e4SLinus Torvalds * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 281da177e4SLinus Torvalds * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 291da177e4SLinus Torvalds * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 301da177e4SLinus Torvalds * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 311da177e4SLinus Torvalds * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 321da177e4SLinus Torvalds * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 331da177e4SLinus Torvalds * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 341da177e4SLinus Torvalds * 351da177e4SLinus Torvalds */ 361da177e4SLinus Torvalds 37378c6697SHerbert Xu #include <linux/err.h> 381da177e4SLinus Torvalds #include <linux/module.h> 391da177e4SLinus Torvalds #include <linux/init.h> 401da177e4SLinus Torvalds #include <linux/types.h> 411da177e4SLinus Torvalds #include <linux/slab.h> 421da177e4SLinus Torvalds #include <linux/sunrpc/auth.h> 431da177e4SLinus Torvalds #include <linux/sunrpc/gss_krb5.h> 441da177e4SLinus Torvalds #include <linux/sunrpc/xdr.h> 451da177e4SLinus Torvalds #include <linux/crypto.h> 461da177e4SLinus Torvalds 471da177e4SLinus Torvalds #ifdef RPC_DEBUG 481da177e4SLinus Torvalds # define RPCDBG_FACILITY RPCDBG_AUTH 491da177e4SLinus Torvalds #endif 501da177e4SLinus Torvalds 5147d84807SKevin Coffman static struct gss_api_mech gss_kerberos_mech; /* forward declaration */ 5247d84807SKevin Coffman 5381d4a433SKevin Coffman static const struct gss_krb5_enctype supported_gss_krb5_enctypes[] = { 5481d4a433SKevin Coffman /* 5581d4a433SKevin Coffman * DES (All DES enctypes are mapped to the same gss functionality) 5681d4a433SKevin Coffman */ 5781d4a433SKevin Coffman { 5881d4a433SKevin Coffman .etype = ENCTYPE_DES_CBC_RAW, 5981d4a433SKevin Coffman .ctype = CKSUMTYPE_RSA_MD5, 6081d4a433SKevin Coffman .name = "des-cbc-crc", 6181d4a433SKevin Coffman .encrypt_name = "cbc(des)", 6281d4a433SKevin Coffman .cksum_name = "md5", 6381d4a433SKevin Coffman .encrypt = krb5_encrypt, 6481d4a433SKevin Coffman .decrypt = krb5_decrypt, 654891f2d0SKevin Coffman .mk_key = NULL, 6681d4a433SKevin Coffman .signalg = SGN_ALG_DES_MAC_MD5, 6781d4a433SKevin Coffman .sealalg = SEAL_ALG_DES, 6881d4a433SKevin Coffman .keybytes = 7, 6981d4a433SKevin Coffman .keylength = 8, 7081d4a433SKevin Coffman .blocksize = 8, 7181d4a433SKevin Coffman .cksumlength = 8, 72e1f6c07bSKevin Coffman .keyed_cksum = 0, 7381d4a433SKevin Coffman }, 74958142e9SKevin Coffman /* 75958142e9SKevin Coffman * 3DES 76958142e9SKevin Coffman */ 77958142e9SKevin Coffman { 78958142e9SKevin Coffman .etype = ENCTYPE_DES3_CBC_RAW, 79958142e9SKevin Coffman .ctype = CKSUMTYPE_HMAC_SHA1_DES3, 80958142e9SKevin Coffman .name = "des3-hmac-sha1", 81958142e9SKevin Coffman .encrypt_name = "cbc(des3_ede)", 82958142e9SKevin Coffman .cksum_name = "hmac(sha1)", 83958142e9SKevin Coffman .encrypt = krb5_encrypt, 84958142e9SKevin Coffman .decrypt = krb5_decrypt, 85958142e9SKevin Coffman .mk_key = gss_krb5_des3_make_key, 86958142e9SKevin Coffman .signalg = SGN_ALG_HMAC_SHA1_DES3_KD, 87958142e9SKevin Coffman .sealalg = SEAL_ALG_DES3KD, 88958142e9SKevin Coffman .keybytes = 21, 89958142e9SKevin Coffman .keylength = 24, 90958142e9SKevin Coffman .blocksize = 8, 91958142e9SKevin Coffman .cksumlength = 20, 92958142e9SKevin Coffman .keyed_cksum = 1, 93958142e9SKevin Coffman }, 9481d4a433SKevin Coffman }; 9581d4a433SKevin Coffman 9681d4a433SKevin Coffman static const int num_supported_enctypes = 9781d4a433SKevin Coffman ARRAY_SIZE(supported_gss_krb5_enctypes); 9881d4a433SKevin Coffman 9981d4a433SKevin Coffman static int 10081d4a433SKevin Coffman supported_gss_krb5_enctype(int etype) 10181d4a433SKevin Coffman { 10281d4a433SKevin Coffman int i; 10381d4a433SKevin Coffman for (i = 0; i < num_supported_enctypes; i++) 10481d4a433SKevin Coffman if (supported_gss_krb5_enctypes[i].etype == etype) 10581d4a433SKevin Coffman return 1; 10681d4a433SKevin Coffman return 0; 10781d4a433SKevin Coffman } 10881d4a433SKevin Coffman 10981d4a433SKevin Coffman static const struct gss_krb5_enctype * 11081d4a433SKevin Coffman get_gss_krb5_enctype(int etype) 11181d4a433SKevin Coffman { 11281d4a433SKevin Coffman int i; 11381d4a433SKevin Coffman for (i = 0; i < num_supported_enctypes; i++) 11481d4a433SKevin Coffman if (supported_gss_krb5_enctypes[i].etype == etype) 11581d4a433SKevin Coffman return &supported_gss_krb5_enctypes[i]; 11681d4a433SKevin Coffman return NULL; 11781d4a433SKevin Coffman } 11881d4a433SKevin Coffman 1191da177e4SLinus Torvalds static const void * 1201da177e4SLinus Torvalds simple_get_bytes(const void *p, const void *end, void *res, int len) 1211da177e4SLinus Torvalds { 1221da177e4SLinus Torvalds const void *q = (const void *)((const char *)p + len); 1231da177e4SLinus Torvalds if (unlikely(q > end || q < p)) 1241da177e4SLinus Torvalds return ERR_PTR(-EFAULT); 1251da177e4SLinus Torvalds memcpy(res, p, len); 1261da177e4SLinus Torvalds return q; 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds static const void * 1301da177e4SLinus Torvalds simple_get_netobj(const void *p, const void *end, struct xdr_netobj *res) 1311da177e4SLinus Torvalds { 1321da177e4SLinus Torvalds const void *q; 1331da177e4SLinus Torvalds unsigned int len; 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds p = simple_get_bytes(p, end, &len, sizeof(len)); 1361da177e4SLinus Torvalds if (IS_ERR(p)) 1371da177e4SLinus Torvalds return p; 1381da177e4SLinus Torvalds q = (const void *)((const char *)p + len); 1391da177e4SLinus Torvalds if (unlikely(q > end || q < p)) 1401da177e4SLinus Torvalds return ERR_PTR(-EFAULT); 1410f38b873STrond Myklebust res->data = kmemdup(p, len, GFP_NOFS); 1421da177e4SLinus Torvalds if (unlikely(res->data == NULL)) 1431da177e4SLinus Torvalds return ERR_PTR(-ENOMEM); 1441da177e4SLinus Torvalds res->len = len; 1451da177e4SLinus Torvalds return q; 1461da177e4SLinus Torvalds } 1471da177e4SLinus Torvalds 1481da177e4SLinus Torvalds static inline const void * 14981d4a433SKevin Coffman get_key(const void *p, const void *end, 15081d4a433SKevin Coffman struct krb5_ctx *ctx, struct crypto_blkcipher **res) 1511da177e4SLinus Torvalds { 1521da177e4SLinus Torvalds struct xdr_netobj key; 153378c6697SHerbert Xu int alg; 1541da177e4SLinus Torvalds 1551da177e4SLinus Torvalds p = simple_get_bytes(p, end, &alg, sizeof(alg)); 1561da177e4SLinus Torvalds if (IS_ERR(p)) 1571da177e4SLinus Torvalds goto out_err; 15881d4a433SKevin Coffman 15981d4a433SKevin Coffman switch (alg) { 16081d4a433SKevin Coffman case ENCTYPE_DES_CBC_CRC: 16181d4a433SKevin Coffman case ENCTYPE_DES_CBC_MD4: 16281d4a433SKevin Coffman case ENCTYPE_DES_CBC_MD5: 16381d4a433SKevin Coffman /* Map all these key types to ENCTYPE_DES_CBC_RAW */ 16481d4a433SKevin Coffman alg = ENCTYPE_DES_CBC_RAW; 16581d4a433SKevin Coffman break; 16681d4a433SKevin Coffman } 16781d4a433SKevin Coffman 16881d4a433SKevin Coffman if (!supported_gss_krb5_enctype(alg)) { 16981d4a433SKevin Coffman printk(KERN_WARNING "gss_kerberos_mech: unsupported " 17081d4a433SKevin Coffman "encryption key algorithm %d\n", alg); 17181d4a433SKevin Coffman goto out_err; 17281d4a433SKevin Coffman } 1731da177e4SLinus Torvalds p = simple_get_netobj(p, end, &key); 1741da177e4SLinus Torvalds if (IS_ERR(p)) 1751da177e4SLinus Torvalds goto out_err; 1761da177e4SLinus Torvalds 17781d4a433SKevin Coffman *res = crypto_alloc_blkcipher(ctx->gk5e->encrypt_name, 0, 17881d4a433SKevin Coffman CRYPTO_ALG_ASYNC); 179378c6697SHerbert Xu if (IS_ERR(*res)) { 18081d4a433SKevin Coffman printk(KERN_WARNING "gss_kerberos_mech: unable to initialize " 18181d4a433SKevin Coffman "crypto algorithm %s\n", ctx->gk5e->encrypt_name); 182378c6697SHerbert Xu *res = NULL; 1831da177e4SLinus Torvalds goto out_err_free_key; 1849e56904eSJ. Bruce Fields } 185378c6697SHerbert Xu if (crypto_blkcipher_setkey(*res, key.data, key.len)) { 18681d4a433SKevin Coffman printk(KERN_WARNING "gss_kerberos_mech: error setting key for " 18781d4a433SKevin Coffman "crypto algorithm %s\n", ctx->gk5e->encrypt_name); 1881da177e4SLinus Torvalds goto out_err_free_tfm; 1899e56904eSJ. Bruce Fields } 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds kfree(key.data); 1921da177e4SLinus Torvalds return p; 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds out_err_free_tfm: 195378c6697SHerbert Xu crypto_free_blkcipher(*res); 1961da177e4SLinus Torvalds out_err_free_key: 1971da177e4SLinus Torvalds kfree(key.data); 1981da177e4SLinus Torvalds p = ERR_PTR(-EINVAL); 1991da177e4SLinus Torvalds out_err: 2001da177e4SLinus Torvalds return p; 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds static int 204a8cc1cb7SKevin Coffman gss_import_v1_context(const void *p, const void *end, struct krb5_ctx *ctx) 2051da177e4SLinus Torvalds { 206e678e06bSJ. Bruce Fields int tmp; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds p = simple_get_bytes(p, end, &ctx->initiate, sizeof(ctx->initiate)); 2091da177e4SLinus Torvalds if (IS_ERR(p)) 210a8cc1cb7SKevin Coffman goto out_err; 211a8cc1cb7SKevin Coffman 212a8cc1cb7SKevin Coffman /* Old format supports only DES! Any other enctype uses new format */ 2131ac3719aSKevin Coffman ctx->enctype = ENCTYPE_DES_CBC_RAW; 214a8cc1cb7SKevin Coffman 21581d4a433SKevin Coffman ctx->gk5e = get_gss_krb5_enctype(ctx->enctype); 21681d4a433SKevin Coffman if (ctx->gk5e == NULL) 21781d4a433SKevin Coffman goto out_err; 21881d4a433SKevin Coffman 219717757adSJ. Bruce Fields /* The downcall format was designed before we completely understood 220717757adSJ. Bruce Fields * the uses of the context fields; so it includes some stuff we 221717757adSJ. Bruce Fields * just give some minimal sanity-checking, and some we ignore 222717757adSJ. Bruce Fields * completely (like the next twenty bytes): */ 223717757adSJ. Bruce Fields if (unlikely(p + 20 > end || p + 20 < p)) 224a8cc1cb7SKevin Coffman goto out_err; 225717757adSJ. Bruce Fields p += 20; 226e678e06bSJ. Bruce Fields p = simple_get_bytes(p, end, &tmp, sizeof(tmp)); 2271da177e4SLinus Torvalds if (IS_ERR(p)) 228a8cc1cb7SKevin Coffman goto out_err; 229ef338beeSKevin Coffman if (tmp != SGN_ALG_DES_MAC_MD5) { 230ef338beeSKevin Coffman p = ERR_PTR(-ENOSYS); 231a8cc1cb7SKevin Coffman goto out_err; 232ef338beeSKevin Coffman } 233d922a84aSJ. Bruce Fields p = simple_get_bytes(p, end, &tmp, sizeof(tmp)); 2341da177e4SLinus Torvalds if (IS_ERR(p)) 235a8cc1cb7SKevin Coffman goto out_err; 236ef338beeSKevin Coffman if (tmp != SEAL_ALG_DES) { 237ef338beeSKevin Coffman p = ERR_PTR(-ENOSYS); 238a8cc1cb7SKevin Coffman goto out_err; 239ef338beeSKevin Coffman } 2401da177e4SLinus Torvalds p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime)); 2411da177e4SLinus Torvalds if (IS_ERR(p)) 242a8cc1cb7SKevin Coffman goto out_err; 2431da177e4SLinus Torvalds p = simple_get_bytes(p, end, &ctx->seq_send, sizeof(ctx->seq_send)); 2441da177e4SLinus Torvalds if (IS_ERR(p)) 245a8cc1cb7SKevin Coffman goto out_err; 2461da177e4SLinus Torvalds p = simple_get_netobj(p, end, &ctx->mech_used); 2471da177e4SLinus Torvalds if (IS_ERR(p)) 248a8cc1cb7SKevin Coffman goto out_err; 24981d4a433SKevin Coffman p = get_key(p, end, ctx, &ctx->enc); 2501da177e4SLinus Torvalds if (IS_ERR(p)) 2511da177e4SLinus Torvalds goto out_err_free_mech; 25281d4a433SKevin Coffman p = get_key(p, end, ctx, &ctx->seq); 2531da177e4SLinus Torvalds if (IS_ERR(p)) 2541da177e4SLinus Torvalds goto out_err_free_key1; 2551da177e4SLinus Torvalds if (p != end) { 2561da177e4SLinus Torvalds p = ERR_PTR(-EFAULT); 2571da177e4SLinus Torvalds goto out_err_free_key2; 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds return 0; 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds out_err_free_key2: 263378c6697SHerbert Xu crypto_free_blkcipher(ctx->seq); 2641da177e4SLinus Torvalds out_err_free_key1: 265378c6697SHerbert Xu crypto_free_blkcipher(ctx->enc); 2661da177e4SLinus Torvalds out_err_free_mech: 2671da177e4SLinus Torvalds kfree(ctx->mech_used.data); 2681da177e4SLinus Torvalds out_err: 2691da177e4SLinus Torvalds return PTR_ERR(p); 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 27247d84807SKevin Coffman struct crypto_blkcipher * 27347d84807SKevin Coffman context_v2_alloc_cipher(struct krb5_ctx *ctx, u8 *key) 27447d84807SKevin Coffman { 27547d84807SKevin Coffman struct crypto_blkcipher *cp; 27647d84807SKevin Coffman 27747d84807SKevin Coffman cp = crypto_alloc_blkcipher(ctx->gk5e->encrypt_name, 27847d84807SKevin Coffman 0, CRYPTO_ALG_ASYNC); 27947d84807SKevin Coffman if (IS_ERR(cp)) { 28047d84807SKevin Coffman dprintk("gss_kerberos_mech: unable to initialize " 28147d84807SKevin Coffman "crypto algorithm %s\n", ctx->gk5e->encrypt_name); 28247d84807SKevin Coffman return NULL; 28347d84807SKevin Coffman } 28447d84807SKevin Coffman if (crypto_blkcipher_setkey(cp, key, ctx->gk5e->keylength)) { 28547d84807SKevin Coffman dprintk("gss_kerberos_mech: error setting key for " 28647d84807SKevin Coffman "crypto algorithm %s\n", ctx->gk5e->encrypt_name); 28747d84807SKevin Coffman crypto_free_blkcipher(cp); 28847d84807SKevin Coffman return NULL; 28947d84807SKevin Coffman } 29047d84807SKevin Coffman return cp; 29147d84807SKevin Coffman } 29247d84807SKevin Coffman 29347d84807SKevin Coffman static inline void 29447d84807SKevin Coffman set_cdata(u8 cdata[GSS_KRB5_K5CLENGTH], u32 usage, u8 seed) 29547d84807SKevin Coffman { 29647d84807SKevin Coffman cdata[0] = (usage>>24)&0xff; 29747d84807SKevin Coffman cdata[1] = (usage>>16)&0xff; 29847d84807SKevin Coffman cdata[2] = (usage>>8)&0xff; 29947d84807SKevin Coffman cdata[3] = usage&0xff; 30047d84807SKevin Coffman cdata[4] = seed; 30147d84807SKevin Coffman } 30247d84807SKevin Coffman 30347d84807SKevin Coffman static int 30447d84807SKevin Coffman context_derive_keys_des3(struct krb5_ctx *ctx, u8 *rawkey, u32 keylen) 30547d84807SKevin Coffman { 30647d84807SKevin Coffman struct xdr_netobj c, keyin, keyout; 30747d84807SKevin Coffman u8 cdata[GSS_KRB5_K5CLENGTH]; 30847d84807SKevin Coffman u32 err; 30947d84807SKevin Coffman 31047d84807SKevin Coffman c.len = GSS_KRB5_K5CLENGTH; 31147d84807SKevin Coffman c.data = cdata; 31247d84807SKevin Coffman 31347d84807SKevin Coffman keyin.data = rawkey; 31447d84807SKevin Coffman keyin.len = keylen; 31547d84807SKevin Coffman keyout.len = keylen; 31647d84807SKevin Coffman 31747d84807SKevin Coffman /* seq uses the raw key */ 31847d84807SKevin Coffman ctx->seq = context_v2_alloc_cipher(ctx, rawkey); 31947d84807SKevin Coffman if (ctx->seq == NULL) 32047d84807SKevin Coffman goto out_err; 32147d84807SKevin Coffman 32247d84807SKevin Coffman ctx->enc = context_v2_alloc_cipher(ctx, rawkey); 32347d84807SKevin Coffman if (ctx->enc == NULL) 32447d84807SKevin Coffman goto out_free_seq; 32547d84807SKevin Coffman 32647d84807SKevin Coffman /* derive cksum */ 32747d84807SKevin Coffman set_cdata(cdata, KG_USAGE_SIGN, KEY_USAGE_SEED_CHECKSUM); 32847d84807SKevin Coffman keyout.data = ctx->cksum; 32947d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 33047d84807SKevin Coffman if (err) { 33147d84807SKevin Coffman dprintk("%s: Error %d deriving cksum key\n", 33247d84807SKevin Coffman __func__, err); 33347d84807SKevin Coffman goto out_free_enc; 33447d84807SKevin Coffman } 33547d84807SKevin Coffman 33647d84807SKevin Coffman return 0; 33747d84807SKevin Coffman 33847d84807SKevin Coffman out_free_enc: 33947d84807SKevin Coffman crypto_free_blkcipher(ctx->enc); 34047d84807SKevin Coffman out_free_seq: 34147d84807SKevin Coffman crypto_free_blkcipher(ctx->seq); 34247d84807SKevin Coffman out_err: 34347d84807SKevin Coffman return -EINVAL; 34447d84807SKevin Coffman } 34547d84807SKevin Coffman 34647d84807SKevin Coffman static int 34747d84807SKevin Coffman context_derive_keys_new(struct krb5_ctx *ctx, u8 *rawkey, u32 keylen) 34847d84807SKevin Coffman { 34947d84807SKevin Coffman struct xdr_netobj c, keyin, keyout; 35047d84807SKevin Coffman u8 cdata[GSS_KRB5_K5CLENGTH]; 35147d84807SKevin Coffman u32 err; 35247d84807SKevin Coffman 35347d84807SKevin Coffman c.len = GSS_KRB5_K5CLENGTH; 35447d84807SKevin Coffman c.data = cdata; 35547d84807SKevin Coffman 35647d84807SKevin Coffman keyin.data = rawkey; 35747d84807SKevin Coffman keyin.len = keylen; 35847d84807SKevin Coffman keyout.len = keylen; 35947d84807SKevin Coffman 36047d84807SKevin Coffman /* initiator seal encryption */ 36147d84807SKevin Coffman set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_ENCRYPTION); 36247d84807SKevin Coffman keyout.data = ctx->initiator_seal; 36347d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 36447d84807SKevin Coffman if (err) { 36547d84807SKevin Coffman dprintk("%s: Error %d deriving initiator_seal key\n", 36647d84807SKevin Coffman __func__, err); 36747d84807SKevin Coffman goto out_err; 36847d84807SKevin Coffman } 36947d84807SKevin Coffman ctx->initiator_enc = context_v2_alloc_cipher(ctx, ctx->initiator_seal); 37047d84807SKevin Coffman if (ctx->initiator_enc == NULL) 37147d84807SKevin Coffman goto out_err; 37247d84807SKevin Coffman 37347d84807SKevin Coffman /* acceptor seal encryption */ 37447d84807SKevin Coffman set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_ENCRYPTION); 37547d84807SKevin Coffman keyout.data = ctx->acceptor_seal; 37647d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 37747d84807SKevin Coffman if (err) { 37847d84807SKevin Coffman dprintk("%s: Error %d deriving acceptor_seal key\n", 37947d84807SKevin Coffman __func__, err); 38047d84807SKevin Coffman goto out_free_initiator_enc; 38147d84807SKevin Coffman } 38247d84807SKevin Coffman ctx->acceptor_enc = context_v2_alloc_cipher(ctx, ctx->acceptor_seal); 38347d84807SKevin Coffman if (ctx->acceptor_enc == NULL) 38447d84807SKevin Coffman goto out_free_initiator_enc; 38547d84807SKevin Coffman 38647d84807SKevin Coffman /* initiator sign checksum */ 38747d84807SKevin Coffman set_cdata(cdata, KG_USAGE_INITIATOR_SIGN, KEY_USAGE_SEED_CHECKSUM); 38847d84807SKevin Coffman keyout.data = ctx->initiator_sign; 38947d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 39047d84807SKevin Coffman if (err) { 39147d84807SKevin Coffman dprintk("%s: Error %d deriving initiator_sign key\n", 39247d84807SKevin Coffman __func__, err); 39347d84807SKevin Coffman goto out_free_acceptor_enc; 39447d84807SKevin Coffman } 39547d84807SKevin Coffman 39647d84807SKevin Coffman /* acceptor sign checksum */ 39747d84807SKevin Coffman set_cdata(cdata, KG_USAGE_ACCEPTOR_SIGN, KEY_USAGE_SEED_CHECKSUM); 39847d84807SKevin Coffman keyout.data = ctx->acceptor_sign; 39947d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 40047d84807SKevin Coffman if (err) { 40147d84807SKevin Coffman dprintk("%s: Error %d deriving acceptor_sign key\n", 40247d84807SKevin Coffman __func__, err); 40347d84807SKevin Coffman goto out_free_acceptor_enc; 40447d84807SKevin Coffman } 40547d84807SKevin Coffman 40647d84807SKevin Coffman /* initiator seal integrity */ 40747d84807SKevin Coffman set_cdata(cdata, KG_USAGE_INITIATOR_SEAL, KEY_USAGE_SEED_INTEGRITY); 40847d84807SKevin Coffman keyout.data = ctx->initiator_integ; 40947d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 41047d84807SKevin Coffman if (err) { 41147d84807SKevin Coffman dprintk("%s: Error %d deriving initiator_integ key\n", 41247d84807SKevin Coffman __func__, err); 41347d84807SKevin Coffman goto out_free_acceptor_enc; 41447d84807SKevin Coffman } 41547d84807SKevin Coffman 41647d84807SKevin Coffman /* acceptor seal integrity */ 41747d84807SKevin Coffman set_cdata(cdata, KG_USAGE_ACCEPTOR_SEAL, KEY_USAGE_SEED_INTEGRITY); 41847d84807SKevin Coffman keyout.data = ctx->acceptor_integ; 41947d84807SKevin Coffman err = krb5_derive_key(ctx->gk5e, &keyin, &keyout, &c); 42047d84807SKevin Coffman if (err) { 42147d84807SKevin Coffman dprintk("%s: Error %d deriving acceptor_integ key\n", 42247d84807SKevin Coffman __func__, err); 42347d84807SKevin Coffman goto out_free_acceptor_enc; 42447d84807SKevin Coffman } 42547d84807SKevin Coffman 42647d84807SKevin Coffman return 0; 42747d84807SKevin Coffman 42847d84807SKevin Coffman out_free_acceptor_enc: 42947d84807SKevin Coffman crypto_free_blkcipher(ctx->acceptor_enc); 43047d84807SKevin Coffman out_free_initiator_enc: 43147d84807SKevin Coffman crypto_free_blkcipher(ctx->initiator_enc); 43247d84807SKevin Coffman out_err: 43347d84807SKevin Coffman return -EINVAL; 43447d84807SKevin Coffman } 43547d84807SKevin Coffman 43647d84807SKevin Coffman static int 43747d84807SKevin Coffman gss_import_v2_context(const void *p, const void *end, struct krb5_ctx *ctx) 43847d84807SKevin Coffman { 43947d84807SKevin Coffman u8 rawkey[GSS_KRB5_MAX_KEYLEN]; 44047d84807SKevin Coffman int keylen; 44147d84807SKevin Coffman 44247d84807SKevin Coffman p = simple_get_bytes(p, end, &ctx->flags, sizeof(ctx->flags)); 44347d84807SKevin Coffman if (IS_ERR(p)) 44447d84807SKevin Coffman goto out_err; 44547d84807SKevin Coffman ctx->initiate = ctx->flags & KRB5_CTX_FLAG_INITIATOR; 44647d84807SKevin Coffman 44747d84807SKevin Coffman p = simple_get_bytes(p, end, &ctx->endtime, sizeof(ctx->endtime)); 44847d84807SKevin Coffman if (IS_ERR(p)) 44947d84807SKevin Coffman goto out_err; 45047d84807SKevin Coffman p = simple_get_bytes(p, end, &ctx->seq_send64, sizeof(ctx->seq_send64)); 45147d84807SKevin Coffman if (IS_ERR(p)) 45247d84807SKevin Coffman goto out_err; 45347d84807SKevin Coffman /* set seq_send for use by "older" enctypes */ 45447d84807SKevin Coffman ctx->seq_send = ctx->seq_send64; 45547d84807SKevin Coffman if (ctx->seq_send64 != ctx->seq_send) { 45647d84807SKevin Coffman dprintk("%s: seq_send64 %lx, seq_send %x overflow?\n", __func__, 45747d84807SKevin Coffman (long unsigned)ctx->seq_send64, ctx->seq_send); 45847d84807SKevin Coffman goto out_err; 45947d84807SKevin Coffman } 46047d84807SKevin Coffman p = simple_get_bytes(p, end, &ctx->enctype, sizeof(ctx->enctype)); 46147d84807SKevin Coffman if (IS_ERR(p)) 46247d84807SKevin Coffman goto out_err; 463958142e9SKevin Coffman /* Map ENCTYPE_DES3_CBC_SHA1 to ENCTYPE_DES3_CBC_RAW */ 464958142e9SKevin Coffman if (ctx->enctype == ENCTYPE_DES3_CBC_SHA1) 465958142e9SKevin Coffman ctx->enctype = ENCTYPE_DES3_CBC_RAW; 46647d84807SKevin Coffman ctx->gk5e = get_gss_krb5_enctype(ctx->enctype); 46747d84807SKevin Coffman if (ctx->gk5e == NULL) { 46847d84807SKevin Coffman dprintk("gss_kerberos_mech: unsupported krb5 enctype %u\n", 46947d84807SKevin Coffman ctx->enctype); 47047d84807SKevin Coffman p = ERR_PTR(-EINVAL); 47147d84807SKevin Coffman goto out_err; 47247d84807SKevin Coffman } 47347d84807SKevin Coffman keylen = ctx->gk5e->keylength; 47447d84807SKevin Coffman 47547d84807SKevin Coffman p = simple_get_bytes(p, end, rawkey, keylen); 47647d84807SKevin Coffman if (IS_ERR(p)) 47747d84807SKevin Coffman goto out_err; 47847d84807SKevin Coffman 47947d84807SKevin Coffman if (p != end) { 48047d84807SKevin Coffman p = ERR_PTR(-EINVAL); 48147d84807SKevin Coffman goto out_err; 48247d84807SKevin Coffman } 48347d84807SKevin Coffman 48447d84807SKevin Coffman ctx->mech_used.data = kmemdup(gss_kerberos_mech.gm_oid.data, 48547d84807SKevin Coffman gss_kerberos_mech.gm_oid.len, GFP_KERNEL); 48647d84807SKevin Coffman if (unlikely(ctx->mech_used.data == NULL)) { 48747d84807SKevin Coffman p = ERR_PTR(-ENOMEM); 48847d84807SKevin Coffman goto out_err; 48947d84807SKevin Coffman } 49047d84807SKevin Coffman ctx->mech_used.len = gss_kerberos_mech.gm_oid.len; 49147d84807SKevin Coffman 49247d84807SKevin Coffman switch (ctx->enctype) { 49347d84807SKevin Coffman case ENCTYPE_DES3_CBC_RAW: 49447d84807SKevin Coffman return context_derive_keys_des3(ctx, rawkey, keylen); 49547d84807SKevin Coffman case ENCTYPE_AES128_CTS_HMAC_SHA1_96: 49647d84807SKevin Coffman case ENCTYPE_AES256_CTS_HMAC_SHA1_96: 49747d84807SKevin Coffman return context_derive_keys_new(ctx, rawkey, keylen); 49847d84807SKevin Coffman default: 49947d84807SKevin Coffman return -EINVAL; 50047d84807SKevin Coffman } 50147d84807SKevin Coffman 50247d84807SKevin Coffman out_err: 50347d84807SKevin Coffman return PTR_ERR(p); 50447d84807SKevin Coffman } 50547d84807SKevin Coffman 506a8cc1cb7SKevin Coffman static int 507a8cc1cb7SKevin Coffman gss_import_sec_context_kerberos(const void *p, size_t len, 508a8cc1cb7SKevin Coffman struct gss_ctx *ctx_id) 509a8cc1cb7SKevin Coffman { 510a8cc1cb7SKevin Coffman const void *end = (const void *)((const char *)p + len); 511a8cc1cb7SKevin Coffman struct krb5_ctx *ctx; 512a8cc1cb7SKevin Coffman int ret; 513a8cc1cb7SKevin Coffman 514a8cc1cb7SKevin Coffman ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 515a8cc1cb7SKevin Coffman if (ctx == NULL) 516a8cc1cb7SKevin Coffman return -ENOMEM; 517a8cc1cb7SKevin Coffman 518a8cc1cb7SKevin Coffman if (len == 85) 519a8cc1cb7SKevin Coffman ret = gss_import_v1_context(p, end, ctx); 520a8cc1cb7SKevin Coffman else 52147d84807SKevin Coffman ret = gss_import_v2_context(p, end, ctx); 522a8cc1cb7SKevin Coffman 523a8cc1cb7SKevin Coffman if (ret == 0) 524a8cc1cb7SKevin Coffman ctx_id->internal_ctx_id = ctx; 525a8cc1cb7SKevin Coffman else 526a8cc1cb7SKevin Coffman kfree(ctx); 527a8cc1cb7SKevin Coffman 528a8cc1cb7SKevin Coffman dprintk("RPC: %s: returning %d\n", __func__, ret); 529a8cc1cb7SKevin Coffman return ret; 530a8cc1cb7SKevin Coffman } 531a8cc1cb7SKevin Coffman 5321da177e4SLinus Torvalds static void 5331da177e4SLinus Torvalds gss_delete_sec_context_kerberos(void *internal_ctx) { 5341da177e4SLinus Torvalds struct krb5_ctx *kctx = internal_ctx; 5351da177e4SLinus Torvalds 536378c6697SHerbert Xu crypto_free_blkcipher(kctx->seq); 537378c6697SHerbert Xu crypto_free_blkcipher(kctx->enc); 53847d84807SKevin Coffman crypto_free_blkcipher(kctx->acceptor_enc); 53947d84807SKevin Coffman crypto_free_blkcipher(kctx->initiator_enc); 5401da177e4SLinus Torvalds kfree(kctx->mech_used.data); 5411da177e4SLinus Torvalds kfree(kctx); 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds 544f1c0a861STrond Myklebust static const struct gss_api_ops gss_kerberos_ops = { 5451da177e4SLinus Torvalds .gss_import_sec_context = gss_import_sec_context_kerberos, 5461da177e4SLinus Torvalds .gss_get_mic = gss_get_mic_kerberos, 5471da177e4SLinus Torvalds .gss_verify_mic = gss_verify_mic_kerberos, 54814ae162cSJ. Bruce Fields .gss_wrap = gss_wrap_kerberos, 54914ae162cSJ. Bruce Fields .gss_unwrap = gss_unwrap_kerberos, 5501da177e4SLinus Torvalds .gss_delete_sec_context = gss_delete_sec_context_kerberos, 5511da177e4SLinus Torvalds }; 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds static struct pf_desc gss_kerberos_pfs[] = { 5541da177e4SLinus Torvalds [0] = { 5551da177e4SLinus Torvalds .pseudoflavor = RPC_AUTH_GSS_KRB5, 5561da177e4SLinus Torvalds .service = RPC_GSS_SVC_NONE, 5571da177e4SLinus Torvalds .name = "krb5", 5581da177e4SLinus Torvalds }, 5591da177e4SLinus Torvalds [1] = { 5601da177e4SLinus Torvalds .pseudoflavor = RPC_AUTH_GSS_KRB5I, 5611da177e4SLinus Torvalds .service = RPC_GSS_SVC_INTEGRITY, 5621da177e4SLinus Torvalds .name = "krb5i", 5631da177e4SLinus Torvalds }, 56414ae162cSJ. Bruce Fields [2] = { 56514ae162cSJ. Bruce Fields .pseudoflavor = RPC_AUTH_GSS_KRB5P, 56614ae162cSJ. Bruce Fields .service = RPC_GSS_SVC_PRIVACY, 56714ae162cSJ. Bruce Fields .name = "krb5p", 56814ae162cSJ. Bruce Fields }, 5691da177e4SLinus Torvalds }; 5701da177e4SLinus Torvalds 5711da177e4SLinus Torvalds static struct gss_api_mech gss_kerberos_mech = { 5721da177e4SLinus Torvalds .gm_name = "krb5", 5731da177e4SLinus Torvalds .gm_owner = THIS_MODULE, 574ae4c40b1SUsha Ketineni .gm_oid = {9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"}, 5751da177e4SLinus Torvalds .gm_ops = &gss_kerberos_ops, 5761da177e4SLinus Torvalds .gm_pf_num = ARRAY_SIZE(gss_kerberos_pfs), 5771da177e4SLinus Torvalds .gm_pfs = gss_kerberos_pfs, 578683ac665STrond Myklebust .gm_upcall_enctypes = "enctypes=3,1,2 ", 5791da177e4SLinus Torvalds }; 5801da177e4SLinus Torvalds 5811da177e4SLinus Torvalds static int __init init_kerberos_module(void) 5821da177e4SLinus Torvalds { 5831da177e4SLinus Torvalds int status; 5841da177e4SLinus Torvalds 5851da177e4SLinus Torvalds status = gss_mech_register(&gss_kerberos_mech); 5861da177e4SLinus Torvalds if (status) 5871da177e4SLinus Torvalds printk("Failed to register kerberos gss mechanism!\n"); 5881da177e4SLinus Torvalds return status; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds static void __exit cleanup_kerberos_module(void) 5921da177e4SLinus Torvalds { 5931da177e4SLinus Torvalds gss_mech_unregister(&gss_kerberos_mech); 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds 5961da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 5971da177e4SLinus Torvalds module_init(init_kerberos_module); 5981da177e4SLinus Torvalds module_exit(cleanup_kerberos_module); 599