diff -ruN 2.6.22-rc3.o/fs/nfs/Makefile 2.6.22-rc3.c/fs/nfs/Makefile --- 2.6.22-rc3.o/fs/nfs/Makefile 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/Makefile 2007-06-11 02:57:48.000000000 -0400 @@ -13,7 +13,7 @@ nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ delegation.o idmap.o \ callback.o callback_xdr.o callback_proc.o \ - nfs4namespace.o + nfs4namespace.o nfs4_crypt.o nfs-$(CONFIG_NFS_DIRECTIO) += direct.o nfs-$(CONFIG_SYSCTL) += sysctl.o nfs-objs := $(nfs-y) diff -ruN 2.6.22-rc3.o/fs/nfs/client.c 2.6.22-rc3.c/fs/nfs/client.c --- 2.6.22-rc3.o/fs/nfs/client.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/client.c 2007-06-11 18:16:32.000000000 -0400 @@ -1020,6 +1020,10 @@ spin_unlock(&nfs_client_lock); server->mount_time = jiffies; + + memset(server->nfs4_key, 0, NFS4_KEY_SIZE); + memset(server->nfs4_cipher, 0, NFS4_MAX_ALG_NAME); + dprintk("<-- nfs4_create_server() = %p\n", server); return server; diff -ruN 2.6.22-rc3.o/fs/nfs/dir.c 2.6.22-rc3.c/fs/nfs/dir.c --- 2.6.22-rc3.o/fs/nfs/dir.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/dir.c 2007-06-11 18:23:56.000000000 -0400 @@ -63,6 +63,9 @@ .open = nfs_opendir, .release = nfs_release, .fsync = nfs_fsync_dir, +#ifdef CONFIG_NFS_V4 + .ioctl = nfs4_ioctl, +#endif }; const struct inode_operations nfs_dir_inode_operations = { @@ -411,6 +414,12 @@ int loop_count = 0, res; + if (file && file->f_dentry && file->f_dentry->d_inode) { + if(is_nfs4(file->f_dentry->d_inode) && !is_key_set(file->f_dentry->d_inode)) { + return -EACCES; + } + } + dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling starting @ cookie %Lu\n", (unsigned long long)entry->cookie); @@ -1008,6 +1017,10 @@ struct dentry *res = NULL; int error; + if(is_nfs4(dir) && !is_key_set(dir)) { + return ERR_PTR(-EACCES); + } + dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); @@ -1964,6 +1977,12 @@ if (mask == 0) goto out; + + if(nfs4_deny_access(inode)) { + res = -EACCES; + goto out; + } + /* Is this sys_access() ? */ if (nd != NULL && (nd->flags & LOOKUP_ACCESS)) goto force_lookup; diff -ruN 2.6.22-rc3.o/fs/nfs/file.c 2.6.22-rc3.c/fs/nfs/file.c --- 2.6.22-rc3.o/fs/nfs/file.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/file.c 2007-06-11 18:23:29.000000000 -0400 @@ -32,6 +32,7 @@ #include #include +#include "nfs4_fs.h" #include "delegation.h" #include "iostat.h" @@ -67,6 +68,9 @@ .flock = nfs_flock, .sendfile = nfs_file_sendfile, .check_flags = nfs_check_flags, +#ifdef CONFIG_NFS_V4 + .ioctl = nfs4_ioctl, +#endif }; const struct inode_operations nfs_file_inode_operations = { diff -ruN 2.6.22-rc3.o/fs/nfs/inode.c 2.6.22-rc3.c/fs/nfs/inode.c --- 2.6.22-rc3.o/fs/nfs/inode.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/inode.c 2007-06-11 18:24:33.000000000 -0400 @@ -1141,12 +1141,14 @@ #endif #ifdef CONFIG_NFS_V4 nfsi->nfs4_acl = NULL; + nfsi->nfs4_nci = NULL; #endif /* CONFIG_NFS_V4 */ return &nfsi->vfs_inode; } void nfs_destroy_inode(struct inode *inode) { + put_nci(inode); kmem_cache_free(nfs_inode_cachep, NFS_I(inode)); } diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4_crypt.c 2.6.22-rc3.c/fs/nfs/nfs4_crypt.c --- 2.6.22-rc3.o/fs/nfs/nfs4_crypt.c 1969-12-31 19:00:00.000000000 -0500 +++ 2.6.22-rc3.c/fs/nfs/nfs4_crypt.c 2007-06-11 18:26:04.000000000 -0400 @@ -0,0 +1,726 @@ +/* + * Crypto functions for rt-privacy. + * + * Copyright (C) 2007 Avishay Traeger, Kumar Thangavelu, Erez Zadok + * Copyright (C) 2007 Stony Brook University (SUNY) + * + */ +#include +#include +#include +#include +#include +#include /* get_random_bytes */ +#include +#include /* Crypto APIs*/ +#include "nfs4_crypt.h" + +/* This is the global default iv */ +unsigned char global_iv[8] = { + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10 +}; + +/* Read the encryption cipher from the user space. */ +int nfs4_get_ioctl_data_SETCIPHER_ucipher(void *out, int len, void *arg) +{ + int ret; + struct _nfs4_ioctl_SETCIPHER this_ioctl; + struct crypto_blkcipher *tfm; + + ret = copy_from_user((char *)&this_ioctl, arg, sizeof(this_ioctl)); + if (ret != 0) { + ret = -EFAULT; + goto out_SETCIPHER; + } + + tfm = crypto_alloc_blkcipher(this_ioctl.ucipher, 0, CRYPTO_ALG_ASYNC); + if (tfm == NULL) { + printk(KERN_DEBUG "Crypto API unavailable. Failed for %s\n", this_ioctl.ucipher); + ret = -EINVAL; + goto out_SETCIPHER; + } + + memcpy(out, &this_ioctl.ucipher, len); + +out_SETCIPHER: + return ret; +} + +/* A utility function that calculates a hash from the msg passed. + * The hash is duplicated to be of the appropriate size and stored in + * result. This result is supposed to act as the key for user. + * This funtion is used by ioctl to set the user key. + */ +int crypt_set_enc_key(char *result, char *msg, int msg_len) { + int len = 0, i = 0; + + if ((len = get_hash(result, msg, msg_len, NFS4_KEY_HASH_ALGO)) < 0) { + return len; + } + + /* Duplicate the hash generated to fill out the whole of the size of + * result. Just in case. */ + for(i=len ; i= 0) + memcpy(out, &this_ioctl.ukey, len); + return ret; +} + +/* + * An ioctl implementation function to read and set the user password. + */ +int nfs4_ioctl(struct inode *dir, struct file *filp, unsigned int cmd, unsigned long arg) { + int err = 0; /* don't fail by default */ + + /* This function is defined only for nfs v4. For all others should give + * unknown ioctl + */ + if(!is_nfs4(dir)) { + return -ENOTTY; + } + + /* check if asked for local commands */ + switch (cmd) { + /* Added a new ioctl command to set the key. */ + case NFS4_IOCTL_SETKEY: + { + char temp_buf[16]; + + if (nfs4_get_ioctl_data_SETKEY_ukey(temp_buf, sizeof(temp_buf), (void *)arg) < 0) { + err = -EFAULT; + break; + } + + /* If the key is of zero length then the user wants to remove the key */ + if (temp_buf[0] != 0) { + /* calcuate the key from the 16 byte (hardcoded in cryptfs) user password given. */ + err = crypt_set_enc_key(NFS_SERVER(dir)->nfs4_key, temp_buf, 16); + } else { + /* User wants to remove the key */ + memset(NFS_SERVER(dir)->nfs4_key, 0, NFS4_KEY_SIZE); + } + } + break; + /* Added a new ioctl command to set the cipher. */ + case NFS4_IOCTL_SETCIPHER: + if ((err = nfs4_get_ioctl_data_SETCIPHER_ucipher((void *) NFS_SERVER(dir)->nfs4_cipher, NFS4_MAX_ALG_NAME, (void *)arg)) < 0) + printk("NFS4_IOCTL_SETCIPHER: cipher setting failed\n"); + else + printk("NFS4_IOCTL_SETCIPHER: cipher set to \"%s\"\n", NFS_SERVER(dir)->nfs4_cipher); + break; + default: + err = -ENOTTY; /* this is an unknown ioctl */ + } /* end of outer switch statement */ + + return err; +} + +/* Checks if the key is set + * Returns 1 if set. Else returns 0 + */ +int is_key_set(struct inode *this_inode) { + int i = 0; + char *key = NFS_SERVER(this_inode)->nfs4_key; + + /* If the key is not set then key will contain all 0s */ + for (i=0 ; infs4_cipher; + + /* If the cipher is not set then cipher will contain all 0s */ + for (i=0 ; iversion != 4) { + return 0; + } else { + return 1; + } +} + +/* Checks if the access should be denied for the given + * inode. This will happen if the inode is not that of the + * root and when the key is not set. + * Returns 1 for denying access. Else returns 0 + */ +int nfs4_deny_access(struct inode *this_inode) { + int res = 0; + if(is_nfs4(this_inode) && !is_key_set(this_inode)) { + /* Skip check for mount point. Key can be set after mounting the FS */ + /* The error conditions should never happen. But checking just in case */ + if (!this_inode) { + printk("ERROR: inode is null\n"); + } else { + struct nfs_open_context *ctx; + ctx = nfs_find_open_context(this_inode, NULL, FMODE_READ); + if (ctx != NULL) { + if (ctx->vfsmnt != NULL) { + if (ctx->vfsmnt->mnt_root != NULL) { + if (ctx->vfsmnt->mnt_root->d_inode != NULL) { + if (this_inode->i_ino == ctx->vfsmnt->mnt_root->d_inode->i_ino) { + ; /* NO-OP . Mount point is visible even when key is not set. */ + } + else { + res = 1; + } + } + } + } + put_nfs_open_context(ctx); + } + } + } + return res; +} + +/* Convert the given parameters to a instance of crypt_vec */ +/* NOTE: No checks are made on the incoming parameters. They may be null or invalid. + * This function just converts a long list of arguments to a single argument. + * The crypt_vec obtained using this function must be released by a call to + * put_crypt_vec(cv) + */ +struct crypt_vec* get_crypt_vec(struct nfs_page *req) { + struct crypt_vec * cv = NULL; + + cv = kmalloc(sizeof(struct crypt_vec), GFP_KERNEL); + if (!cv) { + return ERR_PTR(-ENOMEM); + } + + cv->req = req; + + /* The rest of the fields will be initialized later + * they need not be supplied by the user + */ + cv->this_vnode = NULL; + cv->this_dentry = NULL; + cv->nci = NULL; + + return cv; +} + +/* Release the given crypt_vec. + * NOTE: Do not release the contents that are pointed to by this crypt_vec. + */ +void put_crypt_vec(struct crypt_vec *cv) { + if (cv && !IS_ERR(cv)) { + kfree(cv); + } +} + +/* Initialize the fields of the cv from the existing fields + * 'flag' indicates if the operation is NFS4_ENCRYPT/NFS4_DECRYPT + */ +int init_cv(struct crypt_vec *cv, int flag) { + struct nfs_page *req = cv->req; + struct page *pg = req->wb_page; + + if (pg == NULL) { + return -EINVAL; + } + + cv->this_vnode = pg->mapping->host; + cv->nci = NULL; /* Will be initialized later */ + + if (flag == NFS4_ENCRYPT) { + /* During encryption we need to do a setattr. For that we need the dentry */ + if (req->wb_context == NULL) { + struct nfs_open_context *ctx; + printk("WARNING : Unable to get the nfs_open_context from nfs_page for ino %lu\n", + cv->this_vnode->i_ino); + + /* Trying another way to get the nfs_open_context */ + ctx = nfs_find_open_context(cv->this_vnode, NULL, FMODE_WRITE); + if (ctx == NULL) { + printk("ERROR : Unable to get the nfs_open_context for ino %lu\n", cv->this_vnode->i_ino); + return -EINVAL; + } + cv->this_dentry = ctx->dentry; + put_nfs_open_context(ctx); + } else { + cv->this_dentry = req->wb_context->dentry; + } + } + + return 0; +} + +/* This function checks if the set of arguments in struct crypt_vec are sufficient + * for ??cryption to be done. 'flag' indicates if the operation is NFS4_ENCRYPT/NFS4_DECRYPT + * Returns NFS4_CRYPT_NOT_SUPPORTED if ??cryption cannot be done. Else returns NFS4_CRYPT_SUPPORTED. + */ +int is_cryptable(struct crypt_vec *cv, int flag) { + + /* Define on seperate lines as it can be easy to modify later. */ + if (cv->req == NULL) { + printk("ERROR : The input nfs_page sent is null\n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + /* From the req populate the other contents of the cv. ie, Initialize the fields. */ + if(init_cv(cv, flag)) { + printk("ERROR : Unable to initialise the input for encryption from the input provided.\n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + + /* Define on seperate lines as it can be easy to modify later. */ + if (cv->this_vnode == NULL) { + printk("ERROR : Unable to get inode from page. \n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + if (flag == NFS4_ENCRYPT && cv->this_dentry == NULL) { + /* Dentry is required only for ENCRYPT */ + printk("ERROR : Unable to get dentry from page. \n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + if (NFS_PROTO(cv->this_vnode) == NULL) { + printk("ERROR : RPC ops not defined for this fs. \n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + if (NFS_PROTO(cv->this_vnode)->getattr == NULL) { + printk("ERROR : RPC ops -> getattr not defined for this fs. \n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + if (NFS_PROTO(cv->this_vnode)->setattr == NULL) { + printk("ERROR : RPC ops -> setattr not defined for this fs. \n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + if (NFS_SERVER(cv->this_vnode) == NULL) { + printk("ERROR : Unable to get the NFS server from the page inode.\n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + if (NFS_FH(cv->this_vnode) == NULL) { + printk("ERROR : Unable to get the NFS FH from the page inode.\n"); + return NFS4_CRYPT_NOT_SUPPORTED; + } + + return NFS4_CRYPT_SUPPORTED; +} + +/* Process a nci that is going to be written to the server. */ +void prepare_write_nci(struct crypt_vec *cv) { + char *key = NFS_SERVER(cv->this_vnode)->nfs4_key; + struct nfs4_crypt_info *nci = cv->nci; + unsigned char iv[8]; + + memcpy(iv, global_iv, 8); + + /* We will always crypt the key in the nci using the global cipher, iv and key */ + if (crypt(nci->key, NFS4_KEY_SIZE, iv, 8, key, NFS4_ENCRYPT, NFS4_ENC_ALGO) < 0) { + printk("ERROR: Unable to encrypt key stored in nci. The keys are now in plain."); + } +} + +/* Process a nci that is going has just been read from the server. + * Can also be used to change back the nci after it was written + */ +void prepare_read_nci(struct crypt_vec *cv) { + char *key = NFS_SERVER(cv->this_vnode)->nfs4_key; + struct nfs4_crypt_info *nci = cv->nci; + unsigned char iv[8]; + + memcpy(iv, global_iv, 8); + + /* We will always crypt the key in the nci using the global cipher, iv and key */ + if (crypt(nci->key, NFS4_KEY_SIZE, iv, 8, key, NFS4_DECRYPT, NFS4_ENC_ALGO) < 0) { + printk("ERROR: Unable to decrypt key stored in lockbox. The key will remain encrypted"); + } +} + +/* Create a new nci. The nci should already have been allocated. */ +struct nfs4_crypt_info* get_new_nci(struct crypt_vec *cv) { + struct nfs4_crypt_info* nci = cv->nci; + + nci->ino = cv->this_vnode->i_ino; + get_random_bytes(nci->key, NFS4_KEY_SIZE); + + return nci; +} + +/* Get the crypt info for this file (cv) by reading it from the server. + * cv->nci must already have been allocated. The result is populated in + * cv->nci and sent back. + */ +int read_nci(struct crypt_vec *cv) { + int err = 0; + struct nfs4_crypt_info *nci = cv->nci; + struct nfs_fattr fattr; + + if (!nci) { + /* Should never happen. */ + printk("ERROR : nci is not initialized during attempt to read from server.\n"); + return -EINVAL; + } + + /* Read from the server */ + nfs_fattr_init(&fattr); + + err = NFS_PROTO(cv->this_vnode)->getattr(NFS_SERVER(cv->this_vnode), NFS_FH(cv->this_vnode), &fattr); + + if (err) { + printk("ERROR : Unable to read nci attr from the server. err = %d\n", err); + return -EINVAL; + } + memcpy(nci, &(fattr.nci), sizeof(struct nfs4_crypt_info)); + + /* Process the read nci. Essentially decrypt the key */ + prepare_read_nci(cv); + + /* TODO: Ignore the fattr. We should ideally update the inode with this info! :-) */ + + return err; +} + +/* Write the crypt info for this file (cv) to the server. + * cv->nci must already have been allocated and initialized. + */ +int write_nci(struct crypt_vec *cv) { + int err = 0; + struct nfs_fattr fattr; + struct iattr attr; + + if (!cv->nci) { + /* Should never happen. */ + printk("ERROR : nci is not initialized during attempt to write to the server.\n"); + return -EINVAL; + } + + /* Initialize the iattr struct that we want to send to the server */ + attr.ia_valid = 0; + attr.ia_valid |= ATTR_CRYPT; /* We want to send only one attribute for crypt_info */ + + /* Encrypt the key. Copy to fattr. Decrypt the key */ + /* TBD: Can get race condition if someone else is trying to read when file is first written */ + prepare_write_nci(cv); + memcpy(&(attr.ia_nci), cv->nci, sizeof(struct nfs4_crypt_info)); + prepare_read_nci(cv); + + /* Write to the server. */ + err = NFS_PROTO(cv->this_vnode)->setattr(cv->this_dentry, &fattr, &attr); + + /* TODO: Ignore the fattr. We should ideally update the inode with this info! :-) */ + + if (err) { + printk("ERROR : Unable to write nci attr to the server. err = %d\n", err); + return -EINVAL; + } + + return err; +} + +void put_nci(struct inode * vnode) { + struct nfs4_crypt_info *nci = NFS_I(vnode)->nfs4_nci; + if (nci && !IS_ERR(nci)) { + kfree(nci); + } +} + +/* Get the crypt info for this file. + * flag can be NFS4_ENCRYPT or NFS4_DECRYPT + * The nci obtained using this function has to be released + * using put_nci. + */ +struct nfs4_crypt_info* get_nci(struct crypt_vec *cv, int flag) { + int err = 0; + struct nfs4_crypt_info *nci = cv->nci; + + /* Check if the nci is cached locally. + * This will be present if we are in the same + * encrypt or decrypt call. + */ + if (nci != NULL) { + return nci; + } + + /* Check if the nci is cached in the inode. + * This will be present across encrypt and decrypt calls. + */ + nci = NFS_I(cv->this_vnode)->nfs4_nci; + if (nci != NULL) { + /* Inititalize the local cache */ + cv->nci = nci; + return nci; + } + + /* nci is not cached */ + + /* Allocate the nci */ + nci = kmalloc(sizeof(struct nfs4_crypt_info), GFP_KERNEL); + if (!nci) { + nci = ERR_PTR(-ENOMEM); + return nci; + } + /* Cache the nci for future use. */ + cv->nci = nci; + NFS_I(cv->this_vnode)->nfs4_nci = nci; + + /* Try to get the crypt info (nci) for this inode (cv) from the server */ + err = read_nci(cv); + if (err) { + printk("ERROR : Error while retrieving the nci from the server. err = %d\n", err); + goto out_get_nci_err; + } + + /* Check if the nci sent by the server is valid */ + if (nci->ino != INVALID_NCI) { + if (nci->ino == cv->this_vnode->i_ino) { + /* The nci is valid */ + goto out_get_nci; + } else { + printk("ERROR : Attrs set in the server side are corrupted! Unable to get nci\n"); + printk("ERROR : Current ino %lu does not match server side attr set for ino %lu\n", + cv->this_vnode->i_ino, + nci->ino); + goto out_get_nci_err; + } + } + + /* nci->ino is set to INVALID_NCI. This means that the nci entry was not found + * on the server. */ + + if(flag == NFS4_DECRYPT) { + /* There is no key set for the file. The file was created directly on + * the server + */ + err = -ENODATA; + goto out_get_nci_err; + } + + /* Operation is encryption. Here we are allowed to populate a new nci */ + get_new_nci(cv); + + /* After creating the nci write it to the server */ + err = write_nci(cv); + if (err) { + printk("ERROR : Error while writing the nci to the server.\n"); + goto out_get_nci_err; + } + +out_get_nci: + /* Do not release the nci. It is cached */ + return nci; + +out_get_nci_err: + /* There has been some error. */ + cv->nci = NULL; + NFS_I(cv->this_vnode)->nfs4_nci = NULL; + if (nci && !IS_ERR(nci)) { + kfree(nci); + } + nci = ERR_PTR(err); + return nci; +} + +/* This function is responsible for getting the cryption key. + * flag can be NFS4_ENCRYPT or NFS4_DECRYPT + */ +char* get_key(struct crypt_vec *cv, int flag) { + struct nfs4_crypt_info *nci = NULL; + + /* Get the crypt info (nci) for this inode (cv) */ + nci = get_nci(cv, flag); + if (!nci || IS_ERR(nci)) { + if (nci && PTR_ERR(nci) == -ENODATA) { + /* There is no key set for the file. The file was created directly on + * the server + */ + return ERR_PTR(-ENODATA); + } + return ERR_PTR(-EINVAL); + } + + return nci->key; +} + +/* Encrypt or Decrypt (flag) result(in) and store the contents in result(out) */ +/* result_length should be of padded length */ +/* result is a in/out parameter */ +int crypt(char* result, int result_length, char *iv, int iv_length, char *key, int flag, char *cipher) { + struct scatterlist *sg_enc=NULL; + struct crypto_blkcipher *tfm_enc=NULL; + struct blkcipher_desc desc; + int err=0; + char *iv_ext = NULL; + int iv_ext_sz = 0; + + /*Initialize the encryption structures */ + sg_enc = kmalloc(sizeof(struct scatterlist), GFP_KERNEL); + if (!sg_enc) { + err = -ENOMEM; + goto out; + } + tfm_enc = crypto_alloc_blkcipher(cipher, 0, CRYPTO_ALG_ASYNC); + if (tfm_enc == NULL || IS_ERR(tfm_enc)) { + printk(KERN_DEBUG "crypt: Crypto API unavailable. Failed for %s\n", cipher); + err = -ENOPKG; + goto out_sg_enc; + } + desc.tfm = tfm_enc; + desc.flags = 0; + crypto_blkcipher_clear_flags(tfm_enc, ~0); + err = crypto_blkcipher_setkey(tfm_enc, key, NFS4_KEY_SIZE); + if (err) { + printk("crypt: ERROR: Key error. crypto_cipher_setkey() failed flags=%x\n", crypto_blkcipher_get_flags(tfm_enc)); + goto out_tfm_enc; + } + iv_ext_sz = crypto_blkcipher_ivsize(tfm_enc); + iv_ext = kmalloc(iv_ext_sz,GFP_KERNEL); + if (!iv_ext) { + err = -ENOMEM; + goto out_tfm_enc; + } + memset(iv_ext, 0, iv_ext_sz); + memcpy(iv_ext, iv, ((iv_length < iv_ext_sz) ? iv_length : iv_ext_sz)); + + sg_set_buf(sg_enc, result, result_length); + crypto_blkcipher_set_iv(tfm_enc, iv_ext, iv_ext_sz); + if(flag == NFS4_ENCRYPT) { + crypto_blkcipher_encrypt(&desc, sg_enc, sg_enc, sg_enc->length); + } else { + crypto_blkcipher_decrypt(&desc, sg_enc, sg_enc, sg_enc->length); + } + + kfree(iv_ext); +out_tfm_enc: + crypto_free_blkcipher(tfm_enc); +out_sg_enc: + kfree(sg_enc); +out: + return err; +} + +/* Calcuate the hash of the given message. */ +int get_hash(char *result, char *msg, int msg_len, char *hash_algo) { + struct scatterlist *sg_hash; + struct crypto_hash *tfm_hash; + struct hash_desc desc; + int hash_size = 0; + + sg_hash = kmalloc(sizeof(struct scatterlist), GFP_KERNEL); + if (!sg_hash) { + return -ENOMEM; + } + tfm_hash = crypto_alloc_hash(hash_algo, 0, CRYPTO_ALG_ASYNC); + if (tfm_hash == NULL) { + printk(KERN_DEBUG "Crypto API unavailable. Failed for %s\n", hash_algo); + kfree(sg_hash); + return -ENOPKG; + } + desc.tfm = tfm_hash; + desc.flags = 0; + crypto_hash_init(&desc); + sg_set_buf(&sg_hash[0], msg, msg_len); + crypto_hash_update(&desc, sg_hash, msg_len); + crypto_hash_final(&desc, result); + hash_size = crypto_hash_digestsize(tfm_hash); + crypto_free_hash(tfm_hash); + kfree(sg_hash); + return hash_size; +} + +/* Best effort to get key. Improves performance */ +char* get_cached_key(struct nfs_page *req, int flag) { + struct nfs4_crypt_info *nci = NULL; + if (!req || !req->wb_page || !req->wb_page->mapping->host) { + return NULL; + } + + nci = NFS_I(req->wb_page->mapping->host)->nfs4_nci; + if (!nci) { + return NULL; + } + return nci->key; +} + +/* Return the crypt key for encryption or decryption */ +char* get_crypt_key(struct nfs_page *req, int flag) { + char *key = NULL; + struct crypt_vec* cv = NULL; + if(!is_nfs4(req->wb_page->mapping->host)) { + return NULL; + } + if (!S_ISREG(req->wb_page->mapping->host->i_mode)) { + return NULL; + } + key = get_cached_key(req, flag); + if (key && !IS_ERR(key)) { + return key; + } + cv = get_crypt_vec(req); + if (!cv || IS_ERR(cv)) { + return ERR_PTR(-EINVAL); + } + if (!is_cryptable(cv, flag)) { + /* Based on the input provided. We cannot get the key. */ + printk("ERROR: Unable to get the encryption key. The arguments passed are insufficient.\n"); + key = ERR_PTR(-EINVAL); + goto out_crypt_vec; + } + key = get_key(cv, flag); + if (flag == NFS4_DECRYPT && IS_ERR(key)) { + if (key && PTR_ERR(key) == -ENODATA) { + /* There is no key set for the file. The file was created directly on + * the server + */ + key = NULL; + goto out_crypt_vec; + } + } + if (!key || IS_ERR(key)) { + key = ERR_PTR(-EINVAL); + printk("ERROR! unable to get key!! "); + goto out_crypt_vec; + } +out_crypt_vec: + put_crypt_vec(cv); + return key; +} diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4_crypt.h 2.6.22-rc3.c/fs/nfs/nfs4_crypt.h --- 2.6.22-rc3.o/fs/nfs/nfs4_crypt.h 1969-12-31 19:00:00.000000000 -0500 +++ 2.6.22-rc3.c/fs/nfs/nfs4_crypt.h 2007-06-11 18:26:35.000000000 -0400 @@ -0,0 +1,52 @@ +/* + * Header for rt-privacy. + * + * Copyright (C) 2007 Avishay Traeger, Kumar Thangavelu, Erez Zadok + * Copyright (C) 2007 Stony Brook University (SUNY) + * + */ +#ifndef __LINUX_NFS4_CRYPT_H_ +#define __LINUX_NFS4_CRYPT_H_ + +#define NFS4_MAX_ALG_NAME 16 /* constant used by both user and kernel code */ + +#ifdef __KERNEL__ + +#define NFS4_KEY_SIZE 16 +#define NFS4_KEY_HASH_ALGO "md5" +#define NFS4_ENC_ALGO "cbc(aes)" +#define NFS4_ENCRYPT 00 +#define NFS4_DECRYPT 01 +#define NFS4_CRYPT_NOT_SUPPORTED 00 +#define NFS4_CRYPT_SUPPORTED 01 + +/* This structure is used as input argument when it needs + * to call encryption or decryption. + */ +struct crypt_vec { + struct inode *this_vnode; + struct dentry *this_dentry; /* TODO: Is this required ? */ + struct nfs4_crypt_info *nci; + + struct nfs_page *req; +}; +extern int nfs4_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +extern int nfs4_get_ioctl_data_SETKEY_ukey(void *out, int len, void *arg); +extern int nfs4_get_ioctl_data_SETCIPHER_ucipher(void *out, int len, void *arg); + +extern int crypt_set_enc_key(char *result, char *msg, int msg_len); +extern int get_hash(char *result, char *msg, int msg_len, char *algo); +extern int is_key_set(struct inode *this_inode); +extern int is_nfs4(struct inode *this_inode); +extern int nfs4_deny_access(struct inode *this_inode); +extern char* get_crypt_key(struct nfs_page *req, int flag); +extern int crypt(char* result, int result_length, char *iv, int iv_length, char *key, int flag, char *cipher); +extern void put_nci(struct inode * vnode); + +#endif /* __KERNEL__ */ + +/* ioctls */ +struct _nfs4_ioctl_SETKEY { + char ukey [16]; +}; +#define NFS4_IOCTL_SETKEY _IOW(0x15, 10, struct _nfs4_ioctl_SETKEY) + +struct _nfs4_ioctl_SETCIPHER { + char ucipher [NFS4_MAX_ALG_NAME]; +}; +#define NFS4_IOCTL_SETCIPHER _IOW(0x15, 11, struct _nfs4_ioctl_SETCIPHER) + +#endif /* __LINUX_NFS4_CRYPT_H_*/ diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4_fs.h 2.6.22-rc3.c/fs/nfs/nfs4_fs.h --- 2.6.22-rc3.o/fs/nfs/nfs4_fs.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/nfs4_fs.h 2007-06-11 18:26:49.000000000 -0400 @@ -11,6 +11,8 @@ #ifdef CONFIG_NFS_V4 +#include "nfs4_crypt.h" + struct idmap; /* diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4proc.c 2.6.22-rc3.c/fs/nfs/nfs4proc.c --- 2.6.22-rc3.o/fs/nfs/nfs4proc.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/nfs4proc.c 2007-06-11 18:27:27.000000000 -0400 @@ -95,6 +95,7 @@ | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY + | FATTR4_WORD1_CRYPT_INFO }; const u32 nfs4_statfs_bitmap[2] = { @@ -2316,6 +2317,10 @@ data->timestamp = jiffies; rpc_call_setup(&data->task, &msg, 0); + + data->task.tk_fsl = 1; /* Flag to indicate this is a read/write request */ + data->task.tk_offset_fsl = data->args.offset; + data->task.tk_key_fsl = get_crypt_key(data->req, NFS4_DECRYPT); } static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data) @@ -2360,6 +2365,10 @@ /* Finalize the task. */ rpc_call_setup(&data->task, &msg, 0); + + data->task.tk_fsl = 1; /* Flag to indicate this is a read/write request */ + data->task.tk_offset_fsl = data->args.offset; + data->task.tk_key_fsl = get_crypt_key(data->req, NFS4_ENCRYPT); } static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data) diff -ruN 2.6.22-rc3.o/fs/nfs/nfs4xdr.c 2.6.22-rc3.c/fs/nfs/nfs4xdr.c --- 2.6.22-rc3.o/fs/nfs/nfs4xdr.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/nfs4xdr.c 2007-06-11 18:28:02.000000000 -0400 @@ -562,6 +562,9 @@ len += 16; else if (iap->ia_valid & ATTR_MTIME) len += 4; + if (iap->ia_valid & ATTR_CRYPT) { + len += 4 + (XDR_QUADLEN(sizeof(struct nfs4_crypt_info)) << 2); + } RESERVE_SPACE(len); /* @@ -612,6 +615,11 @@ bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET; WRITE32(NFS4_SET_TO_SERVER_TIME); } + if (iap->ia_valid & ATTR_CRYPT) { + bmval1 |= FATTR4_WORD1_CRYPT_INFO; + WRITE32(sizeof(struct nfs4_crypt_info)); + WRITEMEM(&(iap->ia_nci), sizeof(struct nfs4_crypt_info)); + } /* * Now we backfill the bitmap and the attribute buffer length. @@ -2355,6 +2363,32 @@ return 0; } +static int decode_attr_nci(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_crypt_info *nci) { + uint32_t len; + __be32 *p; + + if (unlikely(bitmap[1] & (FATTR4_WORD1_CRYPT_INFO - 1U))) + return -EIO; + if (likely(bitmap[1] & FATTR4_WORD1_CRYPT_INFO)) { + READ_BUF(4); + READ32(len); + READ_BUF(len); + if (len < XDR_MAX_NETOBJ) { + if (len != sizeof(struct nfs4_crypt_info)) { + printk("ERROR : Server and client seem to be using different \ + versions of struct nfs4_crypt_info.\n"); + nci->ino = INVALID_NCI; + } else { + memcpy(nci, (char *)p, len); + } + } else + printk(KERN_WARNING "%s: name too long (%u)!\n", + __FUNCTION__, len); + bitmap[1] &= ~FATTR4_WORD1_CRYPT_INFO; + } + return 0; +} + static int decode_attr_files_avail(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *res) { __be32 *p; @@ -3076,6 +3110,8 @@ goto xdr_error; if ((status = decode_attr_mounted_on_fileid(xdr, bitmap, &fileid)) != 0) goto xdr_error; + if ((status = decode_attr_nci(xdr, bitmap, &fattr->nci)) != 0) + goto xdr_error; if (fattr->fileid == 0 && fileid != 0) fattr->fileid = fileid; if ((status = verify_attr_len(xdr, savep, attrlen)) == 0) diff -ruN 2.6.22-rc3.o/fs/nfs/super.c 2.6.22-rc3.c/fs/nfs/super.c --- 2.6.22-rc3.o/fs/nfs/super.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfs/super.c 2007-06-11 02:38:14.000000000 -0400 @@ -257,6 +257,7 @@ { RPC_AUTH_GSS_KRB5, "krb5" }, { RPC_AUTH_GSS_KRB5I, "krb5i" }, { RPC_AUTH_GSS_KRB5P, "krb5p" }, + { RPC_AUTH_GSS_KRB5E, "krb5e" }, { RPC_AUTH_GSS_LKEY, "lkey" }, { RPC_AUTH_GSS_LKEYI, "lkeyi" }, { RPC_AUTH_GSS_LKEYP, "lkeyp" }, diff -ruN 2.6.22-rc3.o/fs/nfsd/nfs4proc.c 2.6.22-rc3.c/fs/nfsd/nfs4proc.c --- 2.6.22-rc3.o/fs/nfsd/nfs4proc.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfsd/nfs4proc.c 2007-06-11 18:29:10.000000000 -0400 @@ -507,6 +507,8 @@ if (read->rd_offset >= OFFSET_MAX) return nfserr_inval; + rqstp->rq_rd_offset = read->rd_offset; + nfs4_lock_state(); /* check stateid */ if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh, @@ -909,6 +911,12 @@ if (op->opnum == OP_READ && op->u.read.rd_filp) fput(op->u.read.rd_filp); + if (op->opnum == OP_READ || op->opnum == OP_WRITE) { + rqstp->rq_is_rd_fsl = 1; + } else { + rqstp->rq_is_rd_fsl = 0; + } + nfsd4_increment_op_stats(op->opnum); } diff -ruN 2.6.22-rc3.o/fs/nfsd/nfs4xdr.c 2.6.22-rc3.c/fs/nfsd/nfs4xdr.c --- 2.6.22-rc3.o/fs/nfsd/nfs4xdr.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfsd/nfs4xdr.c 2007-06-11 18:30:13.000000000 -0400 @@ -406,6 +406,22 @@ goto xdr_error; } } + if (bmval[1] & FATTR4_WORD1_CRYPT_INFO) { + READ_BUF(4); + len += 4; + READ32(dummy32); + READ_BUF(dummy32); + len += (XDR_QUADLEN(dummy32) << 2); + READMEM(buf, dummy32); + if (sizeof(struct nfs4_crypt_info) != dummy32) { + printk ("ERROR : The client and server are using \ + different versions of struct nfs4_crypt_info"); + host_err = -EINVAL; + goto xdr_error; + } + memcpy(&(iattr->ia_nci), buf, dummy32); + iattr->ia_valid |= ATTR_CRYPT; + } if (len != expected_len) goto xdr_error; @@ -1810,6 +1826,39 @@ } else WRITE64((u64) stat.ino); } + if (bmval1 & FATTR4_WORD1_CRYPT_INFO) { + struct inode *inode = dentry->d_inode; + struct iattr attr; + int err = 0; + + if (!inode->i_op || !inode->i_op->setxattr) { + printk("ERROR : The file system does not support xattrs\n."); + status = -EOPNOTSUPP; + goto out; + } + err = inode->i_op->getxattr(dentry, USER_NCI, (char *) &(attr.ia_nci), + sizeof(struct nfs4_crypt_info)); + + if(err == -ENODATA) { + /* The attribute has not been set. Send a dummy nci */ + attr.ia_nci.ino = INVALID_NCI; + } else if (err < 0) { + printk("ERROR : Error no %d while reading xattr for ino %lu\n", err, inode->i_ino); + /* We still send a dummy nci */ + attr.ia_nci.ino = INVALID_NCI; + } else if (err != sizeof(struct nfs4_crypt_info)) { + printk("ERROR : Read error while reading xattr for ino %lu\n", inode->i_ino); + /* We still send a dummy nci */ + attr.ia_nci.ino = INVALID_NCI; + } + + /* Encode the nci */ + buflen -= (XDR_QUADLEN(sizeof(struct nfs4_crypt_info)) << 2) + 4; + if (buflen < 0) + goto out_resource; + WRITE32(sizeof(struct nfs4_crypt_info)); + WRITEMEM(&(attr.ia_nci), sizeof(struct nfs4_crypt_info)); + } *attrlenp = htonl((char *)p - (char *)attrlenp - 4); *countp = p - buffer; status = nfs_ok; diff -ruN 2.6.22-rc3.o/fs/nfsd/vfs.c 2.6.22-rc3.c/fs/nfsd/vfs.c --- 2.6.22-rc3.o/fs/nfsd/vfs.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/fs/nfsd/vfs.c 2007-06-11 18:30:35.000000000 -0400 @@ -270,6 +270,26 @@ dentry = fhp->fh_dentry; inode = dentry->d_inode; + if (iap->ia_valid & ATTR_CRYPT) { + if (inode->i_ino != iap->ia_nci.ino) { + printk("ERROR : Failed to set the xattr. inode numbers dont match.\n"); + err = -EINVAL; + goto out; + } + if (!inode->i_op || !inode->i_op->setxattr) { + printk("ERROR : The file system does not support xattrs\n."); + err = -EOPNOTSUPP; + goto out; + } + err = inode->i_op->setxattr(dentry, USER_NCI, (char *) &iap->ia_nci, + sizeof(struct nfs4_crypt_info), 0); + if(err) { + printk("ERROR : Failed to set the xattr %s for ino %lu\n", + USER_NCI, inode->i_ino); + goto out; + } + } + /* Ignore any mode updates on symlinks */ if (S_ISLNK(inode->i_mode)) iap->ia_valid &= ~ATTR_MODE; diff -ruN 2.6.22-rc3.o/include/linux/fs.h 2.6.22-rc3.c/include/linux/fs.h --- 2.6.22-rc3.o/include/linux/fs.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/fs.h 2007-06-11 18:31:06.000000000 -0400 @@ -327,6 +327,25 @@ #define ATTR_KILL_SUID 2048 #define ATTR_KILL_SGID 4096 #define ATTR_FILE 8192 +#define ATTR_CRYPT 16384 /* Added a flag for nfs4_crypt_info */ + +/* This structure represents cryption information for a inode. + * The sizes are hardcoded. But they should be equal to the + * respective constants in the file fs/nfs/nfs4_crypt.h + * The size of this structure is 128 bytes. + */ +struct nfs4_crypt_info { + unsigned long ino; + char key[16]; /* NFS4_KEY_SIZE */ + char padding[12]; /* To make this a multiple of 16 */ +}; + +/* A constant to indicate if the nfs4_crypt_info is valid or not + * ino in nfs4_crypt_info will be set to this value when the field + * is not valid + */ +#define INVALID_NCI -9999 + /* * This is the Inode Attributes structure, used for notify_change(). It @@ -353,6 +372,8 @@ * check for (ia_valid & ATTR_FILE), and not for (ia_file != NULL). */ struct file *ia_file; + + struct nfs4_crypt_info ia_nci; }; /* diff -ruN 2.6.22-rc3.o/include/linux/nfs4.h 2.6.22-rc3.c/include/linux/nfs4.h --- 2.6.22-rc3.o/include/linux/nfs4.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/nfs4.h 2007-06-11 18:31:34.000000000 -0400 @@ -347,6 +347,9 @@ #define FATTR4_WORD1_TIME_MODIFY (1UL << 21) #define FATTR4_WORD1_TIME_MODIFY_SET (1UL << 22) #define FATTR4_WORD1_MOUNTED_ON_FILEID (1UL << 23) +#define FATTR4_WORD1_CRYPT_INFO (1UL << 24) + +#define USER_NCI "user.nci" #define NFSPROC4_NULL 0 #define NFSPROC4_COMPOUND 1 diff -ruN 2.6.22-rc3.o/include/linux/nfs_fs.h 2.6.22-rc3.c/include/linux/nfs_fs.h --- 2.6.22-rc3.o/include/linux/nfs_fs.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/nfs_fs.h 2007-06-11 18:31:47.000000000 -0400 @@ -174,6 +174,7 @@ struct nfs_delegation *delegation; int delegation_state; struct rw_semaphore rwsem; + struct nfs4_crypt_info *nfs4_nci; #endif /* CONFIG_NFS_V4*/ struct inode vfs_inode; }; diff -ruN 2.6.22-rc3.o/include/linux/nfs_fs_sb.h 2.6.22-rc3.c/include/linux/nfs_fs_sb.h --- 2.6.22-rc3.o/include/linux/nfs_fs_sb.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/nfs_fs_sb.h 2007-06-11 18:32:00.000000000 -0400 @@ -110,6 +110,9 @@ u32 acl_bitmask; /* V4 bitmask representing the ACEs that are supported on this filesystem */ + + char nfs4_key[16]; /* hashed user key. */ + char nfs4_cipher[16]; /* encryption cipher */ #endif void (*destroy)(struct nfs_server *); }; diff -ruN 2.6.22-rc3.o/include/linux/nfs_xdr.h 2.6.22-rc3.c/include/linux/nfs_xdr.h --- 2.6.22-rc3.o/include/linux/nfs_xdr.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/nfs_xdr.h 2007-06-11 18:32:19.000000000 -0400 @@ -56,6 +56,8 @@ __u64 change_attr; /* NFSv4 change attribute */ __u64 pre_change_attr;/* pre-op NFSv4 change attribute */ unsigned long time_start; + + struct nfs4_crypt_info nci; }; #define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */ diff -ruN 2.6.22-rc3.o/include/linux/nfsd/nfsd.h 2.6.22-rc3.c/include/linux/nfsd/nfsd.h --- 2.6.22-rc3.o/include/linux/nfsd/nfsd.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/nfsd/nfsd.h 2007-06-11 02:38:14.000000000 -0400 @@ -299,12 +299,13 @@ | FATTR4_WORD0_MAXFILESIZE | FATTR4_WORD0_MAXLINK | FATTR4_WORD0_MAXNAME \ | FATTR4_WORD0_MAXREAD | FATTR4_WORD0_MAXWRITE | FATTR4_WORD0_ACL) +/* added FATTR4_WORD1_CRYPT_INFO to NFSD_SUPPORTED_ATTRS_WORD1 */ #define NFSD_SUPPORTED_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_NO_TRUNC | FATTR4_WORD1_NUMLINKS \ | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP | FATTR4_WORD1_RAWDEV \ | FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE | FATTR4_WORD1_SPACE_TOTAL \ | FATTR4_WORD1_SPACE_USED | FATTR4_WORD1_TIME_ACCESS | FATTR4_WORD1_TIME_ACCESS_SET \ - | FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA \ + | FATTR4_WORD1_TIME_DELTA | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_CRYPT_INFO \ | FATTR4_WORD1_TIME_MODIFY | FATTR4_WORD1_TIME_MODIFY_SET | FATTR4_WORD1_MOUNTED_ON_FILEID) /* These will return ERR_INVAL if specified in GETATTR or READDIR. */ @@ -316,7 +317,8 @@ (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL ) #define NFSD_WRITEABLE_ATTRS_WORD1 \ (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ - | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET) + | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_METADATA | FATTR4_WORD1_TIME_MODIFY_SET \ + | FATTR4_WORD1_CRYPT_INFO) /* added FATTR4_WORD1_CRYPT_INFO */ #endif /* CONFIG_NFSD_V4 */ diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/auth_gss.h 2.6.22-rc3.c/include/linux/sunrpc/auth_gss.h --- 2.6.22-rc3.o/include/linux/sunrpc/auth_gss.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/sunrpc/auth_gss.h 2007-06-11 18:32:52.000000000 -0400 @@ -33,7 +33,8 @@ enum rpc_gss_svc { RPC_GSS_SVC_NONE = 1, RPC_GSS_SVC_INTEGRITY = 2, - RPC_GSS_SVC_PRIVACY = 3 + RPC_GSS_SVC_PRIVACY = 3, + RPC_GSS_SVC_E2E = 4 }; /* on-the-wire gss cred: */ diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/gss_krb5.h 2.6.22-rc3.c/include/linux/sunrpc/gss_krb5.h --- 2.6.22-rc3.o/include/linux/sunrpc/gss_krb5.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/sunrpc/gss_krb5.h 2007-06-11 18:33:14.000000000 -0400 @@ -144,9 +144,17 @@ int offset, struct page **pages); int +gss_encrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf, + int offset, struct page **pages, unsigned int len); + +int gss_decrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *inbuf, int offset); +int +gss_decrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf, + int offset, int len); + s32 krb5_make_seq_num(struct crypto_blkcipher *key, int direction, @@ -156,3 +164,10 @@ krb5_get_seq_num(struct crypto_blkcipher *key, unsigned char *cksum, unsigned char *buf, int *direction, s32 * seqnum); + +/* Values for how */ +#define FSL_ENCRYPT 1 +#define FSL_DECRYPT 0 +int +gss_crypt_xdr_buf_pages_fsl(struct gss_ctx_fsl *ctx_fsl, struct xdr_buf *buf, + int offset, struct page **pages, unsigned int len, int how); diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/msg_prot.h 2.6.22-rc3.c/include/linux/sunrpc/msg_prot.h --- 2.6.22-rc3.o/include/linux/sunrpc/msg_prot.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/sunrpc/msg_prot.h 2007-06-11 02:38:14.000000000 -0400 @@ -35,6 +35,7 @@ RPC_AUTH_GSS_SPKM = 390009, RPC_AUTH_GSS_SPKMI = 390010, RPC_AUTH_GSS_SPKMP = 390011, + RPC_AUTH_GSS_KRB5E = 390012, }; /* Maximum size (in bytes) of an rpc credential or verifier */ diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/sched.h 2.6.22-rc3.c/include/linux/sunrpc/sched.h --- 2.6.22-rc3.o/include/linux/sunrpc/sched.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/sunrpc/sched.h 2007-06-11 02:38:14.000000000 -0400 @@ -97,6 +97,9 @@ #ifdef RPC_DEBUG unsigned short tk_pid; /* debugging aid */ #endif + unsigned short tk_fsl; /* field to indicate if this is a write/read request */ + __u64 tk_offset_fsl; /* field to hold the write/read offset */ + char* tk_key_fsl; /* field to hold the enc/dec key */ }; #define tk_auth tk_client->cl_auth #define tk_xprt tk_client->cl_xprt diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/svc.h 2.6.22-rc3.c/include/linux/sunrpc/svc.h --- 2.6.22-rc3.o/include/linux/sunrpc/svc.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/sunrpc/svc.h 2007-06-11 02:38:14.000000000 -0400 @@ -256,6 +256,8 @@ int rq_sendfile_ok; /* turned off in gss privacy * to prevent encrypting page * cache pages */ + int rq_is_rd_fsl; /* Added flag to indicate READ call */ + unsigned int rq_rd_offset; /* Added element to indicate read offset */ wait_queue_head_t rq_wait; /* synchronization */ struct task_struct *rq_task; /* service thread */ }; diff -ruN 2.6.22-rc3.o/include/linux/sunrpc/xdr.h 2.6.22-rc3.c/include/linux/sunrpc/xdr.h --- 2.6.22-rc3.o/include/linux/sunrpc/xdr.h 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/include/linux/sunrpc/xdr.h 2007-06-11 18:33:46.000000000 -0400 @@ -59,6 +59,18 @@ unsigned int buflen, /* Total length of storage buffer */ len; /* Length of XDR encoded message */ + struct gss_ctx_fsl *ctx_fsl; +}; + +struct gss_ctx_fsl { + int isE2E; + int isClient; + unsigned int isRead; + __u64 offset; + unsigned int page_len; + size_t org_head_len; + size_t org_tail_len; + char *key; }; /* diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/auth_gss.c 2.6.22-rc3.c/net/sunrpc/auth_gss/auth_gss.c --- 2.6.22-rc3.o/net/sunrpc/auth_gss/auth_gss.c 2007-06-11 02:45:49.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/auth_gss/auth_gss.c 2007-06-11 18:59:27.000000000 -0400 @@ -1055,6 +1055,33 @@ return 0; } +static inline int +gss_wrap_req_e2e(struct rpc_cred *cred, struct gss_cl_ctx *ctx, + kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj, + struct rpc_task *task) +{ + int status = 0; + struct xdr_buf *snd_buf = &rqstp->rq_snd_buf; + struct gss_ctx_fsl *ctx_fsl = snd_buf->ctx_fsl; + + if (!ctx_fsl) { + printk("ERROR: gss_wrap_req_e2e: ctx_fsl is NULL.\n"); + return -EIO; + } + + ctx_fsl->isE2E = 1; + if (task->tk_fsl == 1) { + ctx_fsl->isRead = 1; + } else { + ctx_fsl->isRead = 0; + } + ctx_fsl->offset = (task->tk_offset_fsl & PAGE_MASK) + snd_buf->page_base; + ctx_fsl->key = task->tk_key_fsl; + ctx_fsl->isClient = 1; + status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj); + return status; +} + static int gss_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, __be32 *p, void *obj) @@ -1064,6 +1091,7 @@ gc_base); struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); int status = -EIO; + struct gss_ctx_fsl *ctx_fsl = NULL; dprintk("RPC: %5u gss_wrap_req\n", task->tk_pid); if (ctx->gc_proc != RPC_GSS_PROC_DATA) { @@ -1073,6 +1101,15 @@ status = encode(rqstp, p, obj); goto out; } + + ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL); + if (!ctx_fsl) { + status = -ENOMEM; + goto out; + } + memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl)); + ((struct rpc_rqst *)rqstp)->rq_snd_buf.ctx_fsl = ctx_fsl; + switch (gss_cred->gc_service) { case RPC_GSS_SVC_NONE: status = encode(rqstp, p, obj); @@ -1085,7 +1122,17 @@ status = gss_wrap_req_priv(cred, ctx, encode, rqstp, p, obj); break; + case RPC_GSS_SVC_E2E: + status = gss_wrap_req_e2e(cred, ctx, encode, + rqstp, p, obj, task); + break; } + + if (ctx_fsl != NULL) { + kfree(ctx_fsl); + ((struct rpc_rqst *)rqstp)->rq_snd_buf.ctx_fsl = NULL; + } + out: gss_put_ctx(ctx); dprintk("RPC: %5u gss_wrap_req returning %d\n", task->tk_pid, status); @@ -1157,6 +1204,31 @@ return 0; } +static inline int +gss_unwrap_resp_e2e(struct rpc_cred *cred, struct gss_cl_ctx *ctx, + struct rpc_rqst *rqstp, __be32 **p, kxdrproc_t decode, + struct rpc_task *task) +{ + int status = 0; + struct xdr_buf *rcv_buf = &rqstp->rq_rcv_buf; + struct gss_ctx_fsl *ctx_fsl = rcv_buf->ctx_fsl; + + if (!ctx_fsl) { + return -EIO; + } + + ctx_fsl->isE2E = 1; + if (task->tk_fsl == 1) { + ctx_fsl->isRead = 1; + } else { + ctx_fsl->isRead = 0; + } + ctx_fsl->offset = (task->tk_offset_fsl & PAGE_MASK) + rcv_buf->page_base; + ctx_fsl->key = task->tk_key_fsl; + ctx_fsl->isClient = 1; + status = gss_unwrap_resp_priv(cred, ctx, rqstp, p); + return status; +} static int gss_unwrap_resp(struct rpc_task *task, @@ -1170,9 +1242,19 @@ struct kvec *head = ((struct rpc_rqst *)rqstp)->rq_rcv_buf.head; int savedlen = head->iov_len; int status = -EIO; + struct gss_ctx_fsl *ctx_fsl = NULL; if (ctx->gc_proc != RPC_GSS_PROC_DATA) goto out_decode; + + ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL); + if (!ctx_fsl) { + status = -ENOMEM; + goto out; + } + memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl)); + ((struct rpc_rqst *)rqstp)->rq_rcv_buf.ctx_fsl = ctx_fsl; + switch (gss_cred->gc_service) { case RPC_GSS_SVC_NONE: break; @@ -1186,6 +1268,11 @@ if (status) goto out; break; + case RPC_GSS_SVC_E2E: + status = gss_unwrap_resp_e2e(cred, ctx, rqstp, &p, decode, task); + if (status) + goto out; + break; } /* take into account extra slack for integrity and privacy cases: */ task->tk_auth->au_rslack = task->tk_auth->au_verfsize + (p - savedp) @@ -1193,6 +1280,11 @@ out_decode: status = decode(rqstp, p, obj); out: + if (ctx_fsl != NULL) { + kfree(ctx_fsl); + ((struct rpc_rqst *)rqstp)->rq_rcv_buf.ctx_fsl = NULL; + } + gss_put_ctx(ctx); dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, status); diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_crypto.c 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_crypto.c --- 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_crypto.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_crypto.c 2007-06-11 18:42:07.000000000 -0400 @@ -175,6 +175,105 @@ int fraglen; }; +struct encryptor_desc_fsl { + __u64 offset; + char *key; + int how; + struct blkcipher_desc desc; +}; + +#define KEY_SIZE (16) /* Must be same as NFS4_KEY_SIZE */ +#define NONCE_SIZE (8) + +static char def_key[] = { + 5, 0, 6, 5, 0, 6, 5, 0, 6, 5, 0, 6, 5, 0, 6, 5 +}; + +static char def_nonce[] = { + 2, 6, 19, 2, 1, 19, 2, 6 +}; + + +/* Xor the characters in out with the characters in in, storing the result in + * out. */ +static void xor(char *out, char *in, ssize_t len) +{ + ssize_t i; + for (i = 0 ; i < len ; i++) + out[i] ^= in[i]; +} + +int encrypt_ctr_mode(char *key, char *nonce, char *text, ssize_t len, + ssize_t offset, int how, struct blkcipher_desc *desc) +{ + struct crypto_blkcipher *cipher; + int blocksize; + char keystream[16]; + struct scatterlist sg; + int block_num; + int block_start, block_end; + int pos = 0; + + if (nonce == NULL) { + nonce = def_nonce; + } + + cipher = desc->tfm; + desc->flags = 0; + + blocksize = crypto_blkcipher_blocksize(cipher); + + /* Init the scatterlist to encrypt the keystream buffer. */ + sg_set_buf(&sg, keystream, blocksize); + + block_num = offset / blocksize; + block_start = block_num * blocksize; + block_end = block_start + blocksize; + + /* Loop through each block. */ + while (len > 0) { + int block_len; + + /* Concatenate the nonce and the block number into the + * keystream buffer. */ + memset(keystream, 0, blocksize); + + memcpy(keystream, nonce, NONCE_SIZE); + + keystream[blocksize - 4] = (char)(block_num >> 24); + keystream[blocksize - 3] = (char)(block_num >> 16); + keystream[blocksize - 2] = (char)(block_num >> 8); + keystream[blocksize - 1] = (char)block_num; + + /* Encrypting the keystream buffer gives us key material. */ + crypto_blkcipher_encrypt(desc, &sg, &sg, blocksize); + + /* Calculate how many bytes we should encrypt. + * We want to encrypt to the end of the block. */ + block_len = block_end - offset; + + /* But not if the amount of data left to encryt is less than + * the size of the block. */ + if (len < block_len) + block_len = len; + + + xor (text + pos, keystream + (offset - block_start), + block_len); + + /* Update the position in the buffer, current offset, + * block number and amount of data left to encrypt. */ + pos += block_len; + offset += block_len; + len -= block_len; + block_num++; + block_start += blocksize; + block_end += blocksize; + } + + return 0; +} + static int encryptor(struct scatterlist *sg, void *data) { @@ -229,6 +328,27 @@ return 0; } +static int +encryptor_fsl(struct scatterlist *sg, void *data) +{ + struct encryptor_desc_fsl *desc = data; + char *to; + int err = 0; + + to = kmap(sg->page); + to += sg->offset; + err = encrypt_ctr_mode(desc->key, NULL, to, sg->length, desc->offset, desc->how, &(desc->desc)); + kunmap(sg->page); + + if (!err) { + desc->offset += sg->length; + } else { + printk("ERROR: encryptor_fsl: encrypt_ctr_mode returned %d\n", err); + } + + return err; +} + int gss_encrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf, int offset, struct page **pages) @@ -254,6 +374,74 @@ EXPORT_SYMBOL(gss_encrypt_xdr_buf); +int +gss_encrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf, + int offset, struct page **pages, unsigned int len) +{ + int ret; + struct encryptor_desc desc; + + BUG_ON(len % crypto_blkcipher_blocksize(tfm) != 0); + + memset(desc.iv, 0, sizeof(desc.iv)); + desc.desc.tfm = tfm; + desc.desc.info = desc.iv; + desc.desc.flags = 0; + desc.pos = offset; + desc.outbuf = buf; + desc.pages = pages; + desc.fragno = 0; + desc.fraglen = 0; + + ret = xdr_process_buf(buf, offset, len, encryptor, &desc); + return ret; +} + +EXPORT_SYMBOL(gss_encrypt_xdr_buf_fsl); + +int +gss_crypt_xdr_buf_pages_fsl(struct gss_ctx_fsl *ctx_fsl, struct xdr_buf *buf, + int offset, struct page **pages, unsigned int len, int how) +{ + int ret=0; + struct encryptor_desc_fsl desc; + struct crypto_blkcipher *cipher; + int blocksize; + + cipher = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(cipher)) { + printk("Unsupported cipher for NFS encryption.\n"); + ret = PTR_ERR(cipher); + return ret; + } + if (ctx_fsl->key == NULL || IS_ERR(ctx_fsl->key)) { + ctx_fsl->key = def_key; + } + if (crypto_blkcipher_setkey(cipher, ctx_fsl->key, KEY_SIZE) != 0) { + printk("Set key error.\n"); + return -ENOTSUPP; + } + blocksize = crypto_blkcipher_blocksize(cipher); + /* Oops, we didn't allocate enough stack space to hold a block. */ + BUG_ON(blocksize > 16); + /* Oops, our nonce is too big! */ + BUG_ON(NONCE_SIZE + 4 > blocksize); + + desc.offset = ctx_fsl->offset; + desc.key = ctx_fsl->key; + desc.how = how; + desc.desc.tfm = cipher; + desc.desc.flags = 0; + + ret = xdr_process_buf(buf, offset, len, encryptor_fsl, &desc); + if (cipher != NULL) + crypto_free_blkcipher(cipher); + + return ret; +} + +EXPORT_SYMBOL(gss_crypt_xdr_buf_pages_fsl); + struct decryptor_desc { u8 iv[8]; /* XXX hard-coded blocksize */ struct blkcipher_desc desc; @@ -318,3 +506,22 @@ } EXPORT_SYMBOL(gss_decrypt_xdr_buf); + +int +gss_decrypt_xdr_buf_fsl(struct crypto_blkcipher *tfm, struct xdr_buf *buf, + int offset, int len) +{ + struct decryptor_desc desc; + + BUG_ON(len % crypto_blkcipher_blocksize(tfm) != 0); + + memset(desc.iv, 0, sizeof(desc.iv)); + desc.desc.tfm = tfm; + desc.desc.info = desc.iv; + desc.desc.flags = 0; + desc.fragno = 0; + desc.fraglen = 0; + return xdr_process_buf(buf, offset, len, decryptor, &desc); +} + +EXPORT_SYMBOL(gss_decrypt_xdr_buf_fsl); diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_mech.c 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_mech.c --- 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_mech.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_mech.c 2007-06-11 02:38:14.000000000 -0400 @@ -226,6 +226,11 @@ .service = RPC_GSS_SVC_PRIVACY, .name = "krb5p", }, + [3] = { + .pseudoflavor = RPC_AUTH_GSS_KRB5E, + .service = RPC_GSS_SVC_E2E, + .name = "krb5e", + }, }; static struct gss_api_mech gss_kerberos_mech = { diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_wrap.c 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_wrap.c --- 2.6.22-rc3.o/net/sunrpc/auth_gss/gss_krb5_wrap.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/auth_gss/gss_krb5_wrap.c 2007-06-11 18:53:00.000000000 -0400 @@ -107,6 +107,416 @@ *q = i++; } +struct header_fsl_t { + size_t org_head_len; + size_t org_tail_len; + unsigned int isReadWrite; + unsigned int page_len; +}; + +/* Function that returns the size of the fsl header */ +size_t get_header_len_fsl(int blocksize) { + int header_len_fsl = 0; + + header_len_fsl = sizeof(struct header_fsl_t); + if (header_len_fsl % blocksize) { + header_len_fsl += blocksize - (header_len_fsl % blocksize); + } + BUG_ON(header_len_fsl % blocksize); + + return header_len_fsl; +} + +/* Check the incoming argument */ +int check_ctx_fsl(struct gss_ctx_fsl *ctx_fsl) { + if (!ctx_fsl) { + return GSS_S_FAILURE; + } else { + } + return 0; +} + +/* Determinies if the thread is being run in the client or in the server. + * Return 1 if client. Else returns 0 + */ +static int isClient(struct gss_ctx_fsl *ctx_fsl) { + if (!ctx_fsl) { + return 0; + } + if (ctx_fsl->isClient) { + return 1; + } + return 0; +} + +/* Determine if the mount is using krb5e mode. */ +static int isE2E(struct gss_ctx_fsl *ctx_fsl) { + if (!ctx_fsl) { + dump_stack(); + return 0; + } + if (ctx_fsl->isE2E) { + return 1; + } + return 0; +} + +/* Determine if this this is a read/write call */ +static int isReadWrite(struct gss_ctx_fsl *ctx_fsl) { + if (!ctx_fsl) { + return 0; + } + if (ctx_fsl->isRead) { + return 1; + } + return 0; +} + +static int write_fsl_header(struct xdr_buf *buf, int offset, int blocksize, struct gss_ctx_fsl *ctx_fsl) { + struct header_fsl_t *header_fsl = NULL; + size_t org_head_len=0, org_tail_len=0; + int header_len_fsl = 0; + unsigned char *ptr; + + /* Store the starting location and data for the fsl header */ + header_fsl = buf->head[0].iov_base + offset; + org_head_len = buf->head[0].iov_len - offset; + org_tail_len = buf->tail[0].iov_len; + + /* Calculate the size of the header for fsl related data */ + header_len_fsl = get_header_len_fsl(blocksize); + + /* shift data to make room for header. */ + ptr = buf->head[0].iov_base + offset; + memmove(ptr + header_len_fsl, ptr, buf->head[0].iov_len - offset); + buf->head[0].iov_len += header_len_fsl; + buf->len += header_len_fsl; + + /* Populate the fsl header */ + header_fsl->org_head_len = org_head_len; + header_fsl->org_tail_len = org_tail_len; + header_fsl->isReadWrite = (unsigned int) isReadWrite(ctx_fsl); + header_fsl->page_len = buf->page_len; + + return 0; +} + +/* Add seperate padding for tail and head */ +static int gss_krb5e_add_padding_fsl(struct xdr_buf *buf, int offset, int blocksize, struct gss_ctx_fsl *ctx_fsl) { + int padding = 0; + char *p; + struct kvec *iov; + struct header_fsl_t *header_fsl = NULL; + size_t org_head_len=0, org_tail_len=0; + int header_len_fsl = 0; + + /* Must already be initialized */ + header_len_fsl = get_header_len_fsl(blocksize); + header_fsl = buf->head[0].iov_base + offset; + org_head_len = header_fsl->org_head_len; + org_tail_len = header_fsl->org_tail_len; + + if (!isReadWrite(ctx_fsl)) { + gss_krb5_add_padding(buf, offset + header_len_fsl, blocksize); + return 0; + } + + + /* Perform the padding for the head */ + iov = &buf->head[0]; + if (iov->iov_len - offset - header_len_fsl != org_head_len) { + return GSS_S_FAILURE; + } + padding = gss_krb5_padding(blocksize, iov->iov_len - offset - header_len_fsl); + p = iov->iov_base + iov->iov_len; + iov->iov_len += padding; + buf->len += padding; + memset(p, padding, padding); + + /* Perform the padding for the tail */ + iov = &buf->tail[0]; + if (iov->iov_len) { + padding = gss_krb5_padding(blocksize, iov->iov_len); + p = iov->iov_base + iov->iov_len; + iov->iov_len += padding; + buf->len += padding; + memset(p, padding, padding); + } else { + } + + BUG_ON((buf->head[0].iov_len - offset - header_len_fsl) % blocksize); + BUG_ON(buf->tail[0].iov_len % blocksize); + if((buf->head[0].iov_base + offset + header_len_fsl) != + (char*) header_fsl + sizeof(struct header_fsl_t)) { + return GSS_S_FAILURE; + } + + return 0; +} + +static int gss_krb5e_encypt_fsl(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset_header_fsl, + int blocksize, struct page **pages, struct gss_ctx_fsl *ctx_fsl) { + char *p; + struct header_fsl_t *header_fsl; + unsigned thislen; + size_t org_head_len=0, org_tail_len=0; + int header_len_fsl = 0; + + + header_fsl = buf->head[0].iov_base + offset_header_fsl; + + /* Must already be initialized */ + org_head_len = header_fsl->org_head_len; + org_tail_len = header_fsl->org_tail_len; + header_len_fsl = get_header_len_fsl(blocksize); + + if (!isReadWrite(ctx_fsl)) { + return gss_encrypt_xdr_buf(kctx->enc, buf, offset_header_fsl + header_len_fsl, pages); + } + + /* Encrypt the head */ + p = buf->head[0].iov_base + offset_header_fsl + header_len_fsl; + thislen = buf->head[0].iov_len - offset_header_fsl - header_len_fsl; + if (thislen != org_head_len + gss_krb5_padding(blocksize, org_head_len)) { + return GSS_S_FAILURE; + } + + if (thislen % crypto_blkcipher_blocksize(kctx->enc) != 0) { + return GSS_S_FAILURE; + } + + if (gss_encrypt_xdr_buf_fsl(kctx->enc, buf, offset_header_fsl + header_len_fsl, pages, thislen)) { + return GSS_S_FAILURE; + } + + /* Encrypt the tail */ + p = buf->tail[0].iov_base; + thislen = buf->tail[0].iov_len; + if (thislen && thislen != org_tail_len + gss_krb5_padding(blocksize, org_tail_len)) { + return GSS_S_FAILURE; + } + if (thislen % crypto_blkcipher_blocksize(kctx->enc) != 0) { + return GSS_S_FAILURE; /* Delete this */ + } + + if (gss_encrypt_xdr_buf_fsl(kctx->enc, buf, buf->len - buf->tail[0].iov_len, pages, thislen)) { + return GSS_S_FAILURE; + } + + return 0; +} + +static int encrypt_pages_fsl(struct xdr_buf *buf, int offset, int blocksize, + struct page **pages, struct gss_ctx_fsl *ctx_fsl) { + pgoff_t start_index, end_index, index; + unsigned long nrpages, start, end, thislen; + char *to, *from; + int err = 0; + + if (buf->page_len == 0) { + return 0; + } + + if (!isReadWrite(ctx_fsl) || !isClient(ctx_fsl)) { + return 0; + } + + start_index = buf->page_base >> PAGE_CACHE_SHIFT; + index = start_index; + end_index = (buf->page_base + buf->page_len) >> PAGE_CACHE_SHIFT; + nrpages = end_index - start_index + 1; + + /* Encryption has to happen before writing the header because of line below */ + if (isClient(ctx_fsl)) { + if (ctx_fsl->offset != page_offset(pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK)) { + /* Only in client this can be done */ + ctx_fsl->offset = page_offset(pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK); + } else { + } + } + + while (index <= end_index) { + if (index == start_index) { + start = buf->page_base & ~PAGE_CACHE_MASK; + } else { + start = 0; + } + if (index == end_index) { + end = (buf->page_base + buf->page_len) & ~PAGE_CACHE_MASK; + } else { + end = PAGE_CACHE_SIZE; + } + thislen = end - start; + if (thislen) { + to = kmap(buf->pages[index]) + start; + from = kmap(pages[index]) + start; + memcpy(to, from, thislen); + kunmap(buf->pages[index]); + kunmap(pages[index]); + } + index++; + } + + if (isClient(ctx_fsl)) { + err = gss_crypt_xdr_buf_pages_fsl(ctx_fsl, buf, buf->head[0].iov_len, NULL, buf->page_len, FSL_ENCRYPT); + } + + + return err; +} + +static int decrypt_pages_fsl(struct xdr_buf *buf, int offset, int blocksize, + struct gss_ctx_fsl *ctx_fsl) { + int err = 0; + + + if (isClient(ctx_fsl)) { + pgoff_t start_index = buf->page_base >> PAGE_CACHE_SHIFT; + if (ctx_fsl->offset != page_offset(buf->pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK)) { + ctx_fsl->offset = page_offset(buf->pages[start_index]) + (buf->page_base & ~PAGE_CACHE_MASK); + } + } + + err = gss_crypt_xdr_buf_pages_fsl(ctx_fsl, buf, offset + ctx_fsl->org_head_len, NULL, ctx_fsl->page_len, FSL_DECRYPT); + + + return err; +} + +static int gss_krb5e_decypt_fsl(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset_header_fsl, + int blocksize, struct gss_ctx_fsl *ctx_fsl) { + struct header_fsl_t *header_fsl; + unsigned thislen, offset_tail; + int header_len_fsl = 0; + size_t org_head_len=0, org_tail_len=0; + + /* Calculate the size of the header for fsl related data */ + header_len_fsl = get_header_len_fsl(blocksize); + + if (buf->head[0].iov_len - offset_header_fsl < header_len_fsl) { + return GSS_S_DEFECTIVE_TOKEN; + } + + header_fsl = buf->head[0].iov_base + offset_header_fsl; + + org_head_len = header_fsl->org_head_len; + org_tail_len = header_fsl->org_tail_len; + ctx_fsl->org_head_len = org_head_len; + ctx_fsl->org_tail_len = org_tail_len; + ctx_fsl->isRead = header_fsl->isReadWrite; + ctx_fsl->page_len = header_fsl->page_len; + + if (!isReadWrite(ctx_fsl)) { + if (gss_decrypt_xdr_buf(kctx->enc, buf, offset_header_fsl + header_len_fsl)) { + return GSS_S_DEFECTIVE_TOKEN; + } + return 0; + } + + /* Decrypt the head */ + thislen = org_head_len + gss_krb5_padding(blocksize, org_head_len); + if(thislen > buf->head[0].iov_len - offset_header_fsl - header_len_fsl) { + + return GSS_S_BAD_SIG; + } + if (gss_decrypt_xdr_buf_fsl(kctx->enc, buf, offset_header_fsl + header_len_fsl, thislen)) + return GSS_S_DEFECTIVE_TOKEN; + + /* Decrypt the tail */ + if (org_tail_len) { + thislen = org_tail_len + gss_krb5_padding(blocksize, org_tail_len); + /* Replacing below line with another method for calculating tail offset + offset_tail = buf->len - thislen; + */ + offset_tail = offset_header_fsl + header_len_fsl + org_head_len + + gss_krb5_padding(blocksize, org_head_len) + ctx_fsl->page_len; + if (offset_tail > buf->len) { + return GSS_S_BAD_SIG; + } + if (gss_decrypt_xdr_buf_fsl(kctx->enc, buf, offset_tail, thislen)) { + return GSS_S_DEFECTIVE_TOKEN; + } + } + + return 0; +} + +static int gss_krb5e_remove_padding(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset, + int blocksize, struct gss_ctx_fsl *ctx_fsl) { + struct header_fsl_t *header_fsl; + int header_len_fsl = 0, padding=0; + size_t org_head_len=0, org_tail_len=0; + void *data_start, *orig_start; + int data_len; + + header_fsl = buf->head[0].iov_base + offset; + /* TBD: This info is already present in ctx_fsl */ + org_head_len = header_fsl->org_head_len; + org_tail_len = header_fsl->org_tail_len; + ctx_fsl->isRead = (unsigned int) header_fsl->isReadWrite; + + if (!isReadWrite(ctx_fsl)) { + if (gss_krb5_remove_padding(buf, blocksize)) { + return GSS_S_DEFECTIVE_TOKEN; + } + return 0; + } + + /* Calculate the size of the header for fsl related data */ + header_len_fsl = get_header_len_fsl(blocksize); + + /* Remove the padding from the head */ + padding = gss_krb5_padding(blocksize, org_head_len); + data_start = buf->head[0].iov_base + offset + header_len_fsl + org_head_len + padding; + orig_start = buf->head[0].iov_base + offset + header_len_fsl + org_head_len; + data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start; + if(offset + header_len_fsl + org_head_len + padding > buf->head[0].iov_len) { + return GSS_S_DEFECTIVE_TOKEN; + } + memmove(orig_start, data_start, data_len); + buf->head[0].iov_len -= padding; + buf->len -= padding; + + /* Remove the padding from the tail */ + if (org_tail_len) { + padding = gss_krb5_padding(blocksize, org_tail_len); + if(padding > buf->len) { + return GSS_S_DEFECTIVE_TOKEN; + } + /* buf->tail[0].iov_len -= padding; */ + if (buf->len <= buf->head[0].iov_len) { + buf->head[0].iov_len -= padding; + } + buf->len -= padding; + } + + return 0; +} + +int remove_fsl_header(struct krb5_ctx *kctx, struct xdr_buf *buf, int offset, + int blocksize, struct gss_ctx_fsl *ctx_fsl) { + int header_len_fsl = 0; + void *data_start, *orig_start; + int data_len; + + + header_len_fsl = get_header_len_fsl(blocksize); + + /* Remove the fsl header */ + data_start = buf->head[0].iov_base + offset + header_len_fsl; + orig_start = buf->head[0].iov_base + offset; + data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start; + if(offset + header_len_fsl > buf->head[0].iov_len) { + return GSS_S_DEFECTIVE_TOKEN; + } + memmove(orig_start, data_start, data_len); + buf->head[0].iov_len -= header_len_fsl; + buf->len -= header_len_fsl; + + return 0; +} + /* Assumptions: the head and tail of inbuf are ours to play with. * The pages, however, may be real pages in the page cache and we replace * them with scratch pages from **pages before writing to them. */ @@ -128,14 +538,27 @@ int headlen; struct page **tmp_pages; u32 seq_send; + struct gss_ctx_fsl *ctx_fsl = buf->ctx_fsl; - dprintk("RPC: gss_wrap_kerberos\n"); + + if (check_ctx_fsl(ctx_fsl)) { + return GSS_S_FAILURE; + } now = get_seconds(); - blocksize = crypto_blkcipher_blocksize(kctx->enc); - gss_krb5_add_padding(buf, offset, blocksize); - BUG_ON((buf->len - offset) % blocksize); + blocksize = crypto_blkcipher_blocksize(kctx->enc); + if (isE2E(ctx_fsl)) { /* Using mode krb5e */ + if (encrypt_pages_fsl(buf, offset, blocksize, pages, ctx_fsl)) { + return GSS_S_FAILURE; + } + write_fsl_header(buf, offset, blocksize, ctx_fsl); + gss_krb5e_add_padding_fsl(buf, offset, blocksize, ctx_fsl); + } else { + gss_krb5_add_padding(buf, offset, blocksize); + BUG_ON((buf->len - offset) % blocksize); + } + plainlen = blocksize + buf->len - offset; headlen = g_token_size(&kctx->mech_used, 22 + plainlen) - @@ -148,7 +571,10 @@ memmove(ptr + headlen, ptr, buf->head[0].iov_len - offset); buf->head[0].iov_len += headlen; buf->len += headlen; - BUG_ON((buf->len - offset - headlen) % blocksize); + + if (!isE2E(ctx_fsl)) { /* Original code. */ + BUG_ON((buf->len - offset - headlen) % blocksize); + } g_make_token_header(&kctx->mech_used, 22 + plainlen, &ptr); @@ -167,8 +593,10 @@ make_confounder(msg_start, blocksize); /* XXXJBF: UGH!: */ - tmp_pages = buf->pages; - buf->pages = pages; + tmp_pages = buf->pages; + if (!isE2E(ctx_fsl) || !isReadWrite(ctx_fsl)) { + buf->pages = pages; + } if (make_checksum("md5", krb5_hdr, 8, buf, offset + headlen - blocksize, &md5cksum)) return GSS_S_FAILURE; @@ -191,9 +619,15 @@ seq_send, krb5_hdr + 16, krb5_hdr + 8))) return GSS_S_FAILURE; - if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize, - pages)) - return GSS_S_FAILURE; + if (isE2E(ctx_fsl)) { /* Using mode krb5e */ + if (gss_krb5e_encypt_fsl(kctx, buf, offset + headlen, blocksize, pages, ctx_fsl)) { + return GSS_S_FAILURE; + } + } else { + if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize, + pages)) + return GSS_S_FAILURE; + } return (kctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE; } @@ -214,8 +648,14 @@ void *data_start, *orig_start; int data_len; int blocksize; + struct gss_ctx_fsl *ctx_fsl = buf->ctx_fsl; + int offset_header_fsl=0; + - dprintk("RPC: gss_unwrap_kerberos\n"); + + if (check_ctx_fsl(ctx_fsl)) { + return GSS_S_FAILURE; + } ptr = (u8 *)buf->head[0].iov_base + offset; if (g_verify_token_header(&kctx->mech_used, &bodysize, &ptr, @@ -241,9 +681,20 @@ if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) return GSS_S_DEFECTIVE_TOKEN; - if (gss_decrypt_xdr_buf(kctx->enc, buf, - ptr + 22 - (unsigned char *)buf->head[0].iov_base)) - return GSS_S_DEFECTIVE_TOKEN; + if (isE2E(ctx_fsl)) { + int ret = 0; + blocksize = crypto_blkcipher_blocksize(kctx->enc); + offset_header_fsl = ptr + 22 + blocksize - (unsigned char *)buf->head[0].iov_base; + if ((ret = gss_krb5e_decypt_fsl(kctx, buf, offset_header_fsl, blocksize, ctx_fsl))) { + return ret; + } + } else { + /* The original statements. */ + if (gss_decrypt_xdr_buf(kctx->enc, buf, + ptr + 22 - (unsigned char *)buf->head[0].iov_base)) { + return GSS_S_DEFECTIVE_TOKEN; + } + } if (make_checksum("md5", ptr - 2, 8, buf, ptr + 22 - (unsigned char *)buf->head[0].iov_base, &md5cksum)) @@ -284,8 +735,22 @@ buf->head[0].iov_len -= (data_start - orig_start); buf->len -= (data_start - orig_start); - if (gss_krb5_remove_padding(buf, blocksize)) - return GSS_S_DEFECTIVE_TOKEN; + if (isE2E(ctx_fsl)) { + if (gss_krb5e_remove_padding(kctx, buf, offset, blocksize, ctx_fsl)) { + return GSS_S_DEFECTIVE_TOKEN; + } + if (remove_fsl_header(kctx, buf, offset, blocksize, ctx_fsl)) { + return GSS_S_DEFECTIVE_TOKEN; + } + if (isReadWrite(ctx_fsl) && isClient(ctx_fsl)) { + if (decrypt_pages_fsl(buf, offset, blocksize, ctx_fsl)) { + return GSS_S_DEFECTIVE_TOKEN; + } + } + } else { + if (gss_krb5_remove_padding(buf, blocksize)) + return GSS_S_DEFECTIVE_TOKEN; + } return GSS_S_COMPLETE; } diff -ruN 2.6.22-rc3.o/net/sunrpc/auth_gss/svcauth_gss.c 2.6.22-rc3.c/net/sunrpc/auth_gss/svcauth_gss.c --- 2.6.22-rc3.o/net/sunrpc/auth_gss/svcauth_gss.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/auth_gss/svcauth_gss.c 2007-06-11 18:54:07.000000000 -0400 @@ -960,6 +960,14 @@ __be32 *rpcstart; __be32 *reject_stat = resv->iov_base + resv->iov_len; int ret; + struct gss_ctx_fsl *ctx_fsl = NULL; + + ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL); + if (!ctx_fsl) + goto auth_err; + memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl)); + rqstp->rq_arg.ctx_fsl = ctx_fsl; + dprintk("RPC: svcauth_gss: argv->iov_len = %zd\n", argv->iov_len); @@ -1126,6 +1134,19 @@ svc_putnl(resv, 0); svc_putnl(resv, 0); break; + case RPC_GSS_SVC_E2E: + ctx_fsl->isE2E = 1; + ctx_fsl->isRead = rqstp->rq_is_rd_fsl; + ctx_fsl->isClient = 0; + if (unwrap_priv_data(rqstp, &rqstp->rq_arg, + gc->gc_seq, rsci->mechctx)) { + printk("ERROR: unwrap_priv_data returned error\n"); + goto auth_err; + } + /* placeholders for length and seq. number: */ + svc_putnl(resv, 0); + svc_putnl(resv, 0); + break; default: goto auth_err; } @@ -1145,6 +1166,10 @@ drop: ret = SVC_DROP; out: + if (ctx_fsl != NULL) { + kfree(ctx_fsl); + } + rqstp->rq_arg.ctx_fsl = NULL; if (rsci) cache_put(&rsci->h, &rsc_cache); return ret; @@ -1284,6 +1309,13 @@ struct rpc_gss_wire_cred *gc = &gsd->clcred; struct xdr_buf *resbuf = &rqstp->rq_res; int stat = -EINVAL; + struct gss_ctx_fsl *ctx_fsl = NULL; + + ctx_fsl = kmalloc(sizeof(struct gss_ctx_fsl), GFP_KERNEL); + if (!ctx_fsl) + goto out_err; + memset(ctx_fsl, 0, sizeof(struct gss_ctx_fsl)); + resbuf->ctx_fsl = ctx_fsl; if (gc->gc_proc != RPC_GSS_PROC_DATA) goto out; @@ -1307,6 +1339,17 @@ if (stat) goto out_err; break; + case RPC_GSS_SVC_E2E: + ctx_fsl->isE2E = 1; + ctx_fsl->isClient = 0; + ctx_fsl->isRead = rqstp->rq_is_rd_fsl; + ctx_fsl->offset = rqstp->rq_rd_offset; + stat = svcauth_gss_wrap_resp_priv(rqstp); + if (stat) { + printk("ERROR: svcauth_gss_wrap_resp_priv returned %d\n", stat); + goto out_err; + } + break; default: goto out_err; } @@ -1314,6 +1357,11 @@ out: stat = 0; out_err: + if (ctx_fsl != NULL) { + kfree(ctx_fsl); + } + resbuf->ctx_fsl = NULL; + if (rqstp->rq_client) auth_domain_put(rqstp->rq_client); rqstp->rq_client = NULL; diff -ruN 2.6.22-rc3.o/net/sunrpc/sched.c 2.6.22-rc3.c/net/sunrpc/sched.c --- 2.6.22-rc3.o/net/sunrpc/sched.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/sched.c 2007-06-11 18:54:41.000000000 -0400 @@ -842,6 +842,8 @@ /* starting timestamp */ task->tk_start = jiffies; + task->tk_fsl = 0; + dprintk("RPC: new task initialized, procpid %u\n", current->pid); } diff -ruN 2.6.22-rc3.o/net/sunrpc/sunrpc_syms.c 2.6.22-rc3.c/net/sunrpc/sunrpc_syms.c --- 2.6.22-rc3.o/net/sunrpc/sunrpc_syms.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/sunrpc_syms.c 2007-06-11 02:38:14.000000000 -0400 @@ -178,5 +178,6 @@ #endif } MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KRB5E"); module_init(init_sunrpc); module_exit(cleanup_sunrpc); diff -ruN 2.6.22-rc3.o/net/sunrpc/svc.c 2.6.22-rc3.c/net/sunrpc/svc.c --- 2.6.22-rc3.o/net/sunrpc/svc.c 2007-06-11 02:45:50.000000000 -0400 +++ 2.6.22-rc3.c/net/sunrpc/svc.c 2007-06-11 18:55:06.000000000 -0400 @@ -815,6 +815,7 @@ rqstp->rq_res.tail[0].iov_len = 0; /* Will be turned off only in gss privacy case: */ rqstp->rq_sendfile_ok = 1; + rqstp->rq_is_rd_fsl = 0; /* tcp needs a space for the record length... */ if (rqstp->rq_prot == IPPROTO_TCP) svc_putnl(resv, 0);