From: David Howells For those who think a filesystem is the right interface for everything, despite it being ~18KB to the ~4KB required for a syscall interface, and _much_ less efficient generally in to the bargain. Signed-off-by: Andrew Morton --- 25-akpm/Documentation/keys.txt | 168 +++++++ 25-akpm/fs/Kconfig | 9 25-akpm/fs/Makefile | 1 25-akpm/fs/keyfs/Makefile | 14 25-akpm/fs/keyfs/keydir.c | 547 +++++++++++++++++++++++++ 25-akpm/fs/keyfs/keyfile.c | 874 +++++++++++++++++++++++++++++++++++++++++ 25-akpm/fs/keyfs/keyfs.h | 86 ++++ 25-akpm/fs/keyfs/ringdir.c | 467 +++++++++++++++++++++ 25-akpm/fs/keyfs/root.c | 362 ++++++++++++++++ 25-akpm/fs/keyfs/rootfile.c | 455 +++++++++++++++++++++ 25-akpm/fs/keyfs/rootlink.c | 180 ++++++++ 25-akpm/fs/keyfs/super.c | 117 +++++ 12 files changed, 3280 insertions(+) diff -puN Documentation/keys.txt~keys-keyring-management-keyfs-patch Documentation/keys.txt --- 25/Documentation/keys.txt~keys-keyring-management-keyfs-patch 2004-08-21 01:03:03.000000000 -0700 +++ 25-akpm/Documentation/keys.txt 2004-08-21 01:03:03.000000000 -0700 @@ -794,3 +794,171 @@ by executing: In this case, the program isn't required to actually attach the key to a ring; the rings are provided for reference. + + +===================== +KEY ACCESS FILESYSTEM +===================== + +The keyfs filesystem can be mounted and used to access the key database. It +represents each key as a directory in the root of the filesystem. Only keys +that provide at least one permission to a process attempting to read the +root directory will appear in the list returned. Similarly, any operation (such +as chdir) that tries to access a key directory will return error ENOENT unless +that key supports at least one permission for the offending process. + +All reads and writes to control files must be achieved with a single system +call. The files do not try to stitch together several buffers. + + +Root Directory Features +----------------------- + +The keyfs layout is as follows: + +/keyfs/ + / } + / } + / } Key and keyring directories + / } + ... } + thread } + process } Symbolic links to key directories corresponding to a + session } process's subscribed keyrings and those of its owning + user-session } user + user } + join-session - Session joining file + search - Master search + request-key - Master request key + +Writing a keyring name to /keyfs/join-session will cause a process to attach +that keyring as its session keyring, creating the keyring if necessary. The +keyring name can be blank, in which case an anonymous keyring is used. Any +terminal newline character is stripped. + + /bin/echo dhowells-kde-ring >/keyfs/join-session + +Writing a string consisting of: a type name, a newline character, a +description, a newline character, an optional keyring ID and another newline +character will cause a search to be made of all the process's keyrings for a +matching key. If found, an attempt will be made to link the key to the +keyring specified by the optional ID. + + { /bin/echo -ne user\\ncopper\\n\\n >&5; cat <&5; } 5<>/keyfs/search + +Similarly, writing a string consisting of: a type name, a newline character, a +description and a newline character will cause a search to be made of the +processes keyrings, with a fallback to calling /sbin/request-key for the key. + + { /bin/echo -ne user\\ncopper\\n >&5; cat <&5; } 5<>/keyfs/request-key + + +Keyring Directory Features +-------------------------- + +The / directory for a keyring looks like: + +/keyfs/ + / + add - Key adding file + description - Key description file + expiry - Key expiry time file + flags - Key flags file + perm - Key permissions file + revoke - Key revoke file + search - Keyring search file + type - Key type name file + usage - Key usage count file + keyring/ + } + } + } Symbolic links to subscribed keys + } + } + +The description, expiry, flags, type and usage files are purely for reading +information about the keyring. + +The keyring can be revoked by writing to the revoke file: + + echo >/keyfs/1/revoke + +It can have its permissions altered by writing a number corresponding to the +permissions mask to the perm file (a trailing NL will be stripped): + + echo 0x1f0909 >/keyfs/1/perm + +And the perm file can be read back to see what the current state is. The key's +UID[*] and GID can be changed by using chmod or chgrp on the key directory: + + chmod dhowells.cambridge /keyfs/1 + chgrp cambridge /keyfs/1 + +[*] UID changing is not currently supported. + +Writing a string consisting of: a type name, a newline character, a +description, a newline and optionally a payload blob to this file will cause a +suitable key to be constructed and attached to the keyring if a matching key +doesn't already exist there; or if one does exist, it is updated if possible +instead. + + { /bin/echo -ne user\\nelement:Cu\\ncopper >&5; cat <&5; } 5<>/keyfs/1/add + +Writing a string consisting of: a type name, a newline character, a +description, a newline character, an optional keyring ID and another newline +character will cause a search to be made of this keyring and its children for a +matching key. If found, an attempt will be made to link the key to the keyring +specified by the optional ID. + + { /bin/echo -ne user\\ncopper\\n\\n >&5; cat <&5; } 5<>/keyfs/1/search + +The keyring/ subdirectory contains symbolic links to the key directories +corresponding to those keys to which the keyring holds links. These symbolic +links can be deleted with unlink: + + rm /keyfs/1/keyring/2 + +And made with symlink (note the path entered must contain "../../" and the +keyID in the symlink must match that of the symlink's filename): + + ln -s ../../7 /keyfs/1/keyring/7 + + +Key Directory Features +---------------------- + +The / directory for an ordinary key looks like: + +/keyfs/ + / + description - Key description file + expiry - Key expiry time file + flags - Key flags file + perm - Key permissions file + revoke - Key revoke file + type - Key type name file + usage - Key usage count file + payload - Key payload file + instantiate - Key instantiation file (temporary) + negate - Key negative instantiation file (temporary) + +The description, expiry, flags, perm, revoke, type and usage files all work as +for keyring directories (described above). + +The payload file can be used to read out the payload of a key, as presented by +the key type, and can be written to to update the payload if supported by the +type. + +The instantiate file only exists for an uninstantiated key. Writing to it a +string consisting of an optional keyring ID, a newline character and an +optional payload blob will result in the key being instantiated and linked to +the suggested keyring. + + { /bin/echo -ne 2\\ncopper >&5; cat <&5; } 5<>/keyfs/1/instantiate + +Similarly, the negate file only exists for an uninstantiated key. Writing to +it a string consisting of an optional keyring ID, a newline character, an +optional timeout value and another newline character will result in the key +being negatively instantiated and linked to the suggested keyring. + + { /bin/echo -ne 2\\n\\n >&5; cat <&5; } 5<>/keyfs/1/negate diff -puN fs/Kconfig~keys-keyring-management-keyfs-patch fs/Kconfig --- 25/fs/Kconfig~keys-keyring-management-keyfs-patch 2004-08-21 01:03:03.000000000 -0700 +++ 25-akpm/fs/Kconfig 2004-08-21 13:23:28.750158120 -0700 @@ -937,6 +937,15 @@ config RAMFS To compile this as a module, choose M here: the module will be called ramfs. +config KEYFS + bool "Key managment database interface filesystem" + depends on KEYS + help + Keyfs is a filesystem that allows access to the keys database that + can be maintained by the kernel. It allows all the operations + userspace might require to be performed using read, write, symlink + and unlink. + endmenu menu "Miscellaneous filesystems" diff -puN /dev/null fs/keyfs/keydir.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/keydir.c 2004-08-21 13:24:41.089160920 -0700 @@ -0,0 +1,547 @@ +/* keydir.c: key representation directory operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * key directory operations + */ +static int keyfs_dir_readdir(struct file *, void *, filldir_t); + +static struct file_operations keyfs_dir_file_operations = { + .readdir = keyfs_dir_readdir, +}; + +static struct dentry *keyfs_dir_lookup(struct inode *, struct dentry *, + struct nameidata *); + +static int keyfs_dir_permission(struct inode *, int, struct nameidata *); + +static int keyfs_dir_getattr(struct vfsmount *, struct dentry *, + struct kstat *); + +static int keyfs_dir_setattr(struct dentry *, struct iattr *); + +static struct inode_operations keyfs_dir_inode_operations = { + .lookup = keyfs_dir_lookup, + .permission = keyfs_dir_permission, + .getattr = keyfs_dir_getattr, + .setattr = keyfs_dir_setattr, +}; + +static int keyfs_dir_d_revalidate(struct dentry *, struct nameidata *); + +struct dentry_operations keyfs_dir_dentry_operations = { + .d_revalidate = keyfs_dir_d_revalidate, + .d_delete = keyfs_d_delete, +}; + +/*****************************************************************************/ +/* + * update the attributes on a key management directory + */ +static void keyfs_dir_update_inode(struct inode *inode) +{ + struct key *key = inode->u.generic_ip; + + /* update the inode from the key */ + down_read(&key->sem); + + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode &= S_IFMT; + + /* we have to provide access to a keys control files if any of + * them can be accessed for any reason */ + inode->i_mode |= S_IRUSR | S_IXUSR; + + if (key->perm & KEY_GRP_ALL) + inode->i_mode |= S_IRGRP | S_IXGRP; + + if (key->perm & KEY_OTH_ALL) + inode->i_mode |= S_IROTH | S_IXOTH; + + up_read(&key->sem); + +} /* end keyfs_dir_update_inode() */ + +/*****************************************************************************/ +/* + * get the directory for a key + */ +struct inode *keyfs_get_keydir(struct super_block *super, key_serial_t id) +{ + struct inode *inode; + struct key *key; + + /* the key must exist, and we must have at least one permission on it + * to be able to access it */ + key = key_lookup(id); + if (IS_ERR(key)) { + inode = ERR_PTR(PTR_ERR(key)); + goto error; + } + + if (!key_any_permission(key, KEY_ALL)) { + /* we pretend a key doesn't exist if a process is not allowed + * to access it */ + key_put(key); + inode = ERR_PTR(-ENOENT); + goto error; + } + + /* get the key directory inode */ + inode = iget_locked(super, (id << 16) + KEYFS_INO_K_DIR); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode = S_IFDIR; + inode->i_op = &keyfs_dir_inode_operations; + inode->i_fop = &keyfs_dir_file_operations; + inode->i_nlink = 2; + inode->u.generic_ip = key; + + /* set the ownership and permissions from the key */ + keyfs_dir_update_inode(inode); + + /* success */ + unlock_new_inode(inode); + } + else if (inode) { + /* exists */ + key_put(key); + } + else { + /* error */ + inode = ERR_PTR(-ENOMEM); + key_put(key); + } + + error: + return inode; + +} /* end keyfs_get_keydir() */ + +/*****************************************************************************/ +/* + * update the attributes on a key management directory during pathwalk + */ +static int keyfs_dir_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + keyfs_dir_update_inode(dentry->d_inode); + return 1; + +} /* end keyfs_dir_d_revalidate() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode, updating from the key + */ +static int keyfs_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + /* update the inode */ + keyfs_dir_update_inode(dentry->d_inode); + + /* transfer attributes from the inode structure to the stat + * structure */ + generic_fillattr(dentry->d_inode, stat); + + return 0; + +} /* end keyfs_dir_getattr() */ + +/*****************************************************************************/ +/* + * enumerate the keys in a key directory + */ +static int keyfs_dir_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + struct key *key; + ino_t ino; + int ret; + + key = file->f_dentry->d_inode->u.generic_ip; + ino = file->f_dentry->d_inode->i_ino & ~0xffffULL; + + /* read the usual "." and ".." first followed by the key control + * files */ + switch (file->f_pos) { + case 0: + ret = filldir(cookie, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + + case 1: + ret = filldir(cookie, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + + case 2: + ret = filldir(cookie, "type", 4, file->f_pos, + ino | KEYFS_INO_K_TYPE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 3: + ret = filldir(cookie, "description", 11, file->f_pos, + ino | KEYFS_INO_K_DESC, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 4: + ret = filldir(cookie, "expiry", 6, file->f_pos, + ino | KEYFS_INO_K_EXPIRY, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 5: + ret = filldir(cookie, "perm", 4, file->f_pos, + ino | KEYFS_INO_K_PERM, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 6: + ret = filldir(cookie, "revoke", 6, file->f_pos, + ino | KEYFS_INO_K_REVOKE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 7: + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + ret = filldir(cookie, "instantiate", 11, file->f_pos, + ino | KEYFS_INO_K_INSTANTIATE, DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + case 8: + if (!(key->flags & KEY_FLAG_INSTANTIATED)) { + ret = filldir(cookie, "negate", 6, file->f_pos, + ino | KEYFS_INO_K_NEGATE, DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + case 9: + if (key->flags & KEY_FLAG_INSTANTIATED) { + if (key->type == &key_type_keyring) { + ret = filldir(cookie, "search", 6, file->f_pos, + ino | KEYFS_INO_K_NEGATE, DT_REG); + if (ret < 0) + goto done; + } + } + file->f_pos++; + + case 10: + ret = filldir(cookie, "usage", 5, file->f_pos, + ino | KEYFS_INO_K_USAGE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 11: + ret = filldir(cookie, "flags", 5, file->f_pos, + ino | KEYFS_INO_K_USAGE, DT_REG); + if (ret < 0) + goto done; + file->f_pos++; + + case 12: + if (key->flags & KEY_FLAG_INSTANTIATED) { + ret = filldir(cookie, "add", 3, file->f_pos, + ino | KEYFS_INO_K_ADD, DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + case 13: + if (key->flags & KEY_FLAG_INSTANTIATED) { + if (key->type == &key_type_keyring) + ret = filldir(cookie, "keyring", 7, + file->f_pos, + ino | KEYFS_INO_K_PAYLOAD, + DT_DIR); + else + ret = filldir(cookie, "payload", 7, + file->f_pos, + ino | KEYFS_INO_K_PAYLOAD, + DT_REG); + if (ret < 0) + goto done; + } + file->f_pos++; + + default: + break; + } + + done: + return 0; + +} /* end keyfs_dir_readdir() */ + +/*****************************************************************************/ +/* + * look up an inode in a key directory + */ +static struct dentry *keyfs_dir_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry *ret; + struct inode *target; + struct key *key; + const char *name; + + key = dir->u.generic_ip; + + /* determine which virtual file they want */ + name = dentry->d_name.name; + + switch (dentry->d_name.len) { + case 1: + if (memcmp(name, ".", 1) == 0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 2: + if (memcmp(name, "..", 2)==0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 3: + if (memcmp(name, "add", 3) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_ADD); + goto instantiate; + } + break; + + case 4: + if (memcmp(name, "type", 4) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_TYPE); + goto instantiate; + } + if (memcmp(name, "perm", 4) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_PERM); + goto instantiate; + } + break; + + case 5: + if (memcmp(name, "usage", 5) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_USAGE); + goto instantiate; + } + if (memcmp(name, "flags", 5) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_FLAGS); + goto instantiate; + } + break; + + case 6: + if (memcmp(name, "expiry", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_EXPIRY); + goto instantiate; + } + if (memcmp(name, "revoke", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_REVOKE); + goto instantiate; + } + if (memcmp(name, "negate", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_NEGATE); + goto instantiate; + } + if (key->type != &key_type_keyring) + break; + if (memcmp(name, "search", 6) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_SEARCH); + goto instantiate; + } + break; + + case 7: + if (key->type == &key_type_keyring) { + if (memcmp(name, "keyring", 7) == 0) { + target = keyfs_get_keyfile( + dir, + KEYFS_INO_K_PAYLOAD); + goto instantiate; + } + } + else { + if (memcmp(name, "payload", 7) == 0) { + target = keyfs_get_keyfile( + dir, + KEYFS_INO_K_PAYLOAD); + goto instantiate; + } + } + break; + + case 11: + if (memcmp(name, "description", 11) == 0) { + target = keyfs_get_keyfile(dir, KEYFS_INO_K_DESC); + goto instantiate; + } + if (memcmp(name, "instantiate", 11) == 0) { + target = keyfs_get_keyfile(dir, + KEYFS_INO_K_INSTANTIATE); + goto instantiate; + } + break; + + default: + break; + } + + /* make a negative dentry */ + d_add(dentry, NULL); + ret = NULL; + goto out; + + /* instantiate the dentry */ + instantiate: + if (IS_ERR(target)) { + ret = ERR_PTR(PTR_ERR(target)); + goto out; + } + + dentry->d_op = &keyfs_file_dentry_operations; + d_add(dentry, target); + ret = NULL; + + out: + return ret; + +} /* end keyfs_dir_lookup() */ + +/*****************************************************************************/ +/* + * get permission for an operation on a directory + */ +static int keyfs_dir_permission(struct inode *inode, int mask, + struct nameidata *nd) +{ + struct key *key; + int ret; + + key = inode->u.generic_ip; + + /* we pretend a key doesn't exist if a process is not allowed to access + * it */ + ret = -ENOENT; + if (!key_any_permission(key, KEY_ALL)) + goto error; + + /* check the VFS permissions too */ + ret = vfs_permission(inode, mask); + error: + return ret; + +} /* end keyfs_dir_permission() */ + +/*****************************************************************************/ +/* + * permit the key's ownership to be changed + */ +static int keyfs_dir_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode; + unsigned int ia_valid = iattr->ia_valid; + struct key *key; + int ret; + + inode = dentry->d_inode; + key = inode->u.generic_ip; + + ret = -EINVAL; + if (ia_valid & (ATTR_SIZE | ATTR_MODE)) + goto error; + + /* make the changes with the locks held to prevent chown/chown races */ + ret = -EACCES; + down_write(&key->sem); + write_lock(&key->lock); + + if (!capable(CAP_SYS_ADMIN)) { + /* only the sysadmin can chown a key to some other UID */ + if (ia_valid & ATTR_UID && key->uid != iattr->ia_uid) + goto no_access; + + /* only the sysadmin can set the key's GID to a group other + * than one of those that the current process subscribes to */ + if (ia_valid & ATTR_GID && + iattr->ia_gid != key->gid && + !in_group_p(iattr->ia_gid)) + goto no_access; + } + + /* change the UID (have to update the quotas) */ + if (ia_valid & ATTR_UID && iattr->ia_uid != key->uid) { + /* don't support UID changing yet */ + ret = -EOPNOTSUPP; + goto no_access; + } + + /* change the GID */ + if (ia_valid & ATTR_GID) { + inode->i_gid = iattr->ia_gid; + key->gid = iattr->ia_gid; + } + + ret = 0; + + if (ia_valid & ATTR_ATIME) + inode->i_atime = iattr->ia_atime; + if (ia_valid & ATTR_MTIME) + inode->i_mtime = iattr->ia_mtime; + if (ia_valid & ATTR_CTIME) + inode->i_ctime = iattr->ia_ctime; + + no_access: + write_unlock(&key->lock); + up_write(&key->sem); + + error: + return ret; + +} /* end keyfs_dir_setattr() */ diff -puN /dev/null fs/keyfs/keyfile.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/keyfile.c 2004-08-21 01:03:03.000000000 -0700 @@ -0,0 +1,874 @@ +/* keyfile.c: keyring management files + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * miscellaneous key file operations + */ +static ssize_t keyfs_file_read(struct file *, char __user *, size_t, loff_t *); +static ssize_t keyfs_file_write(struct file *, const char __user *, size_t, + loff_t *); + +static struct file_operations keyfs_file_file_operations = { + .read = keyfs_file_read, + .write = keyfs_file_write, + .llseek = no_llseek, +}; + +static struct inode_operations keyfs_file_inode_operations = { + .getattr = keyfs_file_getattr, +}; + +static int keyfs_file_d_revalidate(struct dentry *, struct nameidata *); + +struct dentry_operations keyfs_file_dentry_operations = { + .d_revalidate = keyfs_file_d_revalidate, + .d_delete = keyfs_d_delete, +}; + +/* + * key search file operation + */ +static ssize_t keyfs_search_file_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_search_file_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_search_file_write, + .llseek = no_llseek, +}; + +/* + * add/update key operation + */ +static ssize_t keyfs_add_file_write(struct file *, const char __user *, size_t, + loff_t *); + +static struct file_operations keyfs_add_file_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_add_file_write, + .llseek = no_llseek, +}; + +/*****************************************************************************/ +/* + * update the attributes on a key management file + */ +static void keyfs_file_update_inode(struct inode *inode) +{ + struct key *key = inode->u.generic_ip; + + /* update the inode from the key */ + down_read(&key->sem); + + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode &= S_IFMT; + + /* we have to provide access to key management files in various ways + * depending on various factors */ + switch (inode->i_ino & 0xffff) { + case KEYFS_INO_K_PAYLOAD: + /* the payload is a file if it's an ordinary key and a + * directory if it's a keyring, so we need to adjust + * appropriately */ + if (key->perm & KEY_USR_READ) + inode->i_mode |= S_IRUSR; + + if (key->perm & KEY_USR_WRITE) + inode->i_mode |= S_IWUSR; + + if (key->perm & KEY_GRP_READ) + inode->i_mode |= S_IRGRP; + + if (key->perm & KEY_GRP_WRITE) + inode->i_mode |= S_IWGRP; + + if (key->perm & KEY_OTH_READ) + inode->i_mode |= S_IROTH; + + if (key->perm & KEY_OTH_WRITE) + inode->i_mode |= S_IWOTH; + + if (key->type == &key_type_keyring) { + /* copy R->X bits on a keyring */ + inode->i_mode |= (inode->i_mode & S_IRUGO) >> 2; + } + else { + /* turn off write on the payload if they can't + * write to it */ + if (key->flags & KEY_FLAG_INSTANTIATED && + !key->type->update) + inode->i_mode &= ~S_IWUGO; + + /* turn off read if they can't read it */ + if (!key->type->read) + inode->i_mode &= ~S_IRUGO; + } + break; + + case KEYFS_INO_K_SEARCH: + if (key->perm & KEY_USR_SEARCH) + inode->i_mode |= S_IRUSR | S_IWUSR; + if (key->perm & KEY_GRP_SEARCH) + inode->i_mode |= S_IRGRP | S_IWGRP; + if (key->perm & KEY_OTH_SEARCH) + inode->i_mode |= S_IROTH | S_IWOTH; + break; + + case KEYFS_INO_K_ADD: + if (key->perm & KEY_USR_WRITE) + inode->i_mode |= S_IRUSR | S_IWUSR; + if (key->perm & KEY_GRP_WRITE) + inode->i_mode |= S_IRGRP | S_IWGRP; + if (key->perm & KEY_OTH_WRITE) + inode->i_mode |= S_IROTH | S_IWOTH; + break; + + case KEYFS_INO_K_REVOKE: + case KEYFS_INO_K_INSTANTIATE: + case KEYFS_INO_K_NEGATE: + /* only the owner can revoke or instantiate the key */ + inode->i_mode |= S_IWUSR; + break; + + case KEYFS_INO_K_PERM: + /* only the owner can change the mode */ + inode->i_mode |= S_IWUSR | S_IRUGO; + break; + + default: + if (key->perm & KEY_USR_VIEW) + inode->i_mode |= S_IRUSR; + if (key->perm & KEY_GRP_VIEW) + inode->i_mode |= S_IRGRP; + if (key->perm & KEY_OTH_VIEW) + inode->i_mode |= S_IROTH; + break; + } + + up_read(&key->sem); + +} /* end keyfs_file_update_inode() */ + +/*****************************************************************************/ +/* + * get a key control/access file + */ +struct inode *keyfs_get_keyfile(struct inode *dir, ino_t ino) +{ + struct inode *inode; + struct key *key; + + key = dir->u.generic_ip; + + /* get the key file inode */ + inode = iget_locked(dir->i_sb, (dir->i_ino & ~(ino_t)0xffffU) | ino); + if (inode && inode->i_state & I_NEW) { + atomic_inc(&key->usage); + inode->u.generic_ip = key; + + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_mode = S_IFREG; + inode->i_nlink = 1; + inode->i_op = &keyfs_file_inode_operations; + inode->i_fop = &keyfs_file_file_operations; + + if (key->type == &key_type_keyring) { + switch (inode->i_ino & 0xffff) { + case KEYFS_INO_K_PAYLOAD: + inode->i_mode = S_IFDIR; + inode->i_nlink = 2; + inode->i_op = &keyfs_ring_inode_operations; + inode->i_fop = &keyfs_ring_file_operations; + break; + + case KEYFS_INO_K_SEARCH: + inode->i_fop = + &keyfs_search_file_file_operations; + break; + + case KEYFS_INO_K_ADD: + inode->i_fop = &keyfs_add_file_file_operations; + break; + } + } + + /* transfer the ownership and permissions */ + keyfs_file_update_inode(inode); + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_keyfile() */ + +/*****************************************************************************/ +/* + * update the attributes on a key management file during pathwalk + */ +static int keyfs_file_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + keyfs_file_update_inode(dentry->d_inode); + return 1; + +} /* end keyfs_file_d_revalidate() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode, updating from the key + */ +int keyfs_file_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + keyfs_file_update_inode(dentry->d_inode); + + /* transfer attributes from the inode structure to the stat + * structure */ + generic_fillattr(dentry->d_inode, stat); + + return 0; + +} /* end keyfs_file_getattr() */ + +/*****************************************************************************/ +/* + * read the data from a key file + */ +static ssize_t keyfs_file_read(struct file *file, char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct key *key; + const char *data; + char buffer[30]; + ssize_t ret; + size_t len; + + ret = 0; + if (*fpos > 0) + goto error; + + key = file->f_dentry->d_inode->u.generic_ip; + + switch (file->f_dentry->d_inode->i_ino & 0xffff) { + case KEYFS_INO_K_TYPE: + data = key->type->name; + goto copyout; + + case KEYFS_INO_K_DESC: + data = key->description; + goto copyout; + + case KEYFS_INO_K_EXPIRY: + sprintf(buffer, "%lu", key->expiry); + data = buffer; + goto copyout; + + case KEYFS_INO_K_PERM: + sprintf(buffer, "0x%06x", key->perm); + data = buffer; + goto copyout; + + case KEYFS_INO_K_REVOKE: + case KEYFS_INO_K_NEGATE: + ret = -EOPNOTSUPP; + goto error; + + case KEYFS_INO_K_USAGE: + sprintf(buffer, "%d", atomic_read(&key->usage)); + data = buffer; + goto copyout; + + case KEYFS_INO_K_FLAGS: + sprintf(buffer, "%08x", key->flags); + data = buffer; + goto copyout; + + case KEYFS_INO_K_PAYLOAD: + ret = -EOPNOTSUPP; + if (!key->type->read) + goto error; + + /* read the data with the semaphore held (since we + * might sleep) */ + down_read(&key->sem); + ret = key->type->read(key, _buffer, buflen); + up_read(&key->sem); + + if (ret > 0) { + if (ret > buflen) + ret = buflen; + *fpos += ret; + } + goto error; + + default: + BUG(); + } + + error: + return ret; + + /* if the return is a simple string constructed on the spot, copy that + * to userspace */ + copyout: + len = strlen(data); + if (len > buflen) + len = buflen; + + ret = -EFAULT; + if (copy_to_user(_buffer, data, len) == 0) { + if (len < buflen && put_user('\n', _buffer + len) == 0) + ret = len + 1; + else + ret = len; + } + + *fpos += ret; + goto error; + +} /* end keyfs_file_read() */ + +/*****************************************************************************/ +/* + * write command to change a key's permissions + * - format: [NL] + */ +static ssize_t keyfs_perm_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + key_perm_t perm; + ssize_t ret; + char buffer[16], *p; + + ret = -EINVAL; + if (buflen > sizeof(buffer) - 1) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error; + buffer[buflen] = 0; + + ret = -EINVAL; + perm = simple_strtoul(buffer, &p, 0); + if (*p == '\n') + p++; + if (*p) + goto error; + + /* check the process's owner owns it */ + ret = -EPERM; + if (current->fsuid != key->uid) + goto error; + + /* make the changes with the locks held to prevent chown/chmod races */ + down_write(&key->sem); + write_lock(&key->lock); + key->perm = perm; + write_unlock(&key->lock); + up_write(&key->sem); + + ret = buflen; + + error: + return ret; + +} /* end keyfs_perm_write() */ + +/*****************************************************************************/ +/* + * instantiate a key + * - format: []NL[] + */ +static ssize_t keyfs_instantiate_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + key_serial_t ringid; + struct key *keyring; + ssize_t ret; + size_t plen; + char *buffer, *bend, *p, *ring, *payload; + + ret = -EINVAL; + if (buflen > 40000) + goto error; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the string */ + ret = -EINVAL; + + ring = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + payload = p; + plen = bend - payload; + if (plen == 0) + payload = NULL; + + /* if supplied, turn the ring ID into a keyring */ + keyring = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* instantiate the key, potentially attaching it to the target + * keyring */ + ret = key_instantiate_and_link(key, payload, plen, keyring); + if (ret < 0) { + ret = PTR_ERR(key); + goto error3; + } + + ret = buflen; + + error3: + key_put(keyring); + error2: + kfree(buffer); + error: + return ret; + +} /* end keyfs_instantiate_write() */ + +/*****************************************************************************/ +/* + * negatively instantiate a key + * - format: []NL[]NL + */ +static ssize_t keyfs_negate_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + key_serial_t ringid; + struct key *keyring; + unsigned timo; + ssize_t ret; + char *buffer, *bend, *p, *ring, *timeout; + + ret = -EINVAL; + if (buflen > 100) + goto error; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the string */ + ret = -EINVAL; + + ring = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + timeout = p; + + p = memchr(p, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* if supplied, turn the ring ID into a keyring */ + keyring = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* turn the timeout into number */ + timo = key_negative_timeout; + + if (*timeout) { + timo = simple_strtoul(ring, &p, 0); + if (*p) + goto error3; + } + + /* negatively instantiate the key, potentially attaching to + * the target keyring */ + ret = key_negate_and_link(key, timo, keyring); + if (ret < 0) { + ret = PTR_ERR(key); + goto error3; + } + + ret = buflen; + + error3: + key_put(keyring); + error2: + kfree(buffer); + error: + return ret; + +} /* end keyfs_negate_write() */ + +/*****************************************************************************/ +/* + * update the payload + */ +static ssize_t keyfs_payload_write(struct key *key, + const char __user *_buffer, + size_t buflen) +{ + char *buffer; + ssize_t ret; + + ret = -EINVAL; + if (buflen == 0 || buflen > 32767) + goto error; + + /* must have write permission on the key */ + ret = -EACCES; + if (!key_permission(key, KEY_WRITE)) + goto error; + + /* the key must be instantiated and updatable */ + ret = -EPERM; + if (!(key->flags & KEY_FLAG_INSTANTIATED) || + !key->type->update) + goto error; + + /* fetch the data into kernel space */ + ret = -ENOMEM; + buffer = kmalloc(buflen, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) == 0) { + /* perform the update */ + ret = key_update(key, buffer, buflen); + if (ret == 0) + ret = buflen; + } + + kfree(buffer); + + error: + return ret; + +} /* end keyfs_payload_write() */ + +/*****************************************************************************/ +/* + * allow userspace to control keys by writing to certain files + */ +static ssize_t keyfs_file_write(struct file *file, const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct key *key; + ssize_t ret; + + key = file->f_dentry->d_inode->u.generic_ip; + + switch (file->f_dentry->d_inode->i_ino & 0xffff) { + case KEYFS_INO_K_PERM: + ret = keyfs_perm_write(key, _buffer, buflen); + break; + + case KEYFS_INO_K_REVOKE: + ret = -EACCES; + if (!key_permission(key, KEY_WRITE)) + goto error; + + key_revoke(key); + ret = 0; + goto error; + + case KEYFS_INO_K_INSTANTIATE: + ret = keyfs_instantiate_write(key, _buffer, buflen); + goto error; + + case KEYFS_INO_K_NEGATE: + ret = keyfs_negate_write(key, _buffer, buflen); + goto error; + + case KEYFS_INO_K_PAYLOAD: + ret = keyfs_payload_write(key, _buffer, buflen); + break; + + default: + ret = -EPERM; + break; + } + + error: + return ret; + +} /* end keyfs_file_write() */ + +/*****************************************************************************/ +/* + * search for a key in a keyring + * - format: NLNL[]NL + */ +static ssize_t keyfs_search_file_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key_type *ktype; + key_serial_t ringid; + struct key *key, *keyring, *dest; + ssize_t ret; + char *buffer, *bend, *p, *type, *desc, *ring; + + keyring = file->f_dentry->d_inode->u.generic_ip; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + ring = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* if supplied, turn the destination ring ID into a keyring */ + dest = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + dest = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(dest)) { + ret = PTR_ERR(dest); + goto error2; + } + } + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error3; + } + + /* search for a key */ + key = keyring_search(keyring, ktype, desc); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error4; + } + + /* link the resulting key to the destination keyring if we can */ + if (dest) { + ret = -EACCES; + if (!key_permission(key, KEY_LINK)) + goto error5; + + ret = key_link(dest, key); + if (ret < 0) + goto error5; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + + ret = buflen; + + error5: + key_put(key); + error4: + key_type_put(ktype); + error3: + key_put(dest); + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_search_file_write() */ + +/*****************************************************************************/ +/* + * create a key + * - format: NLNL[] + */ +static ssize_t keyfs_add_file_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key *keyring, *key; + ssize_t ret; + size_t plen; + char *buffer, *bend, *p, *type, *desc, *payload; + + keyring = file->f_dentry->d_inode->u.generic_ip; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + payload = p; + plen = bend - payload; + if (plen == 0) + payload = NULL; + + /* create or update the requested key and add it to the target + * keyring */ + key = key_create_or_update(keyring, type, desc, payload, plen, 0); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error2; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + key_put(key); + + ret = buflen; + + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_add_file_write() */ diff -puN /dev/null fs/keyfs/keyfs.h --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/keyfs.h 2004-08-21 01:03:03.000000000 -0700 @@ -0,0 +1,86 @@ +/* keyfs.h: keyfs defines + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include + +/* special inodes */ +#define KEYFS_INO_ROOT 1 +#define KEYFS_INO_THREAD 2 +#define KEYFS_INO_PROCESS 3 +#define KEYFS_INO_SESSION 4 +#define KEYFS_INO_USER_SESSION 5 +#define KEYFS_INO_USER 6 +#define KEYFS_INO_REQUEST_KEY 7 +#define KEYFS_INO_SEARCH 8 +#define KEYFS_INO_JOIN_SESSION 9 +#define KEYFS_INO__LAST 9 + +/* key dir inodes added to keyid << 16 */ +#define KEYFS_INO_K_SYMLINK 0 /* symlink to key's directory */ +#define KEYFS_INO_K_DIR 1 /* key's directory */ +#define KEYFS_INO_K_TYPE 2 +#define KEYFS_INO_K_DESC 3 +#define KEYFS_INO_K_EXPIRY 4 +#define KEYFS_INO_K_PERM 5 +#define KEYFS_INO_K_REVOKE 6 +#define KEYFS_INO_K_INSTANTIATE 7 +#define KEYFS_INO_K_NEGATE 8 +#define KEYFS_INO_K_SEARCH 9 +#define KEYFS_INO_K_USAGE 10 +#define KEYFS_INO_K_FLAGS 11 +#define KEYFS_INO_K_ADD 12 +#define KEYFS_INO_K_PAYLOAD 13 +#define KEYFS_INO_K__LAST 13 + +/* root.c */ +extern struct inode *keyfs_get_rootdir(struct super_block *super); +extern int keyfs_d_delete(struct dentry *dentry); + +/* rootfile.c */ +enum keyfs_bifile_state { + KEYFS_BIFILE_WANT_PARAMS, + KEYFS_BIFILE_GOT_RESULT, + KEYFS_BIFILE_DONE +}; + +struct keyfs_bifile_record { + enum keyfs_bifile_state state; + key_serial_t id; + ssize_t error; +}; + +extern ssize_t keyfs_bifile_read(struct file *, char __user *, size_t, loff_t *); +extern int keyfs_bifile_open(struct inode *, struct file *); +extern int keyfs_bifile_release(struct inode *, struct file *); + +extern struct inode *keyfs_get_rootfile(struct super_block *super, ino_t ino); + +/* rootlink.c */ +extern struct inode *keyfs_get_rootlink(struct super_block *super, ino_t ino); + +/* keydir.c */ +extern struct dentry_operations keyfs_dir_dentry_operations; + +extern struct inode *keyfs_get_keydir(struct super_block *super, + key_serial_t id); + +/* keyfile.c */ +extern struct dentry_operations keyfs_file_dentry_operations; + +extern struct inode *keyfs_get_keyfile(struct inode *dir, ino_t ino); + +extern int keyfs_file_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); + +/* ringdir.c */ +extern struct file_operations keyfs_ring_file_operations; +extern struct inode_operations keyfs_ring_inode_operations; diff -puN /dev/null fs/keyfs/Makefile --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/Makefile 2004-08-21 01:03:03.000000000 -0700 @@ -0,0 +1,14 @@ +# +# Makefile for Key view filesystem +# + +keyfs-objs := \ + super.o \ + root.o \ + rootfile.o \ + rootlink.o \ + keydir.o \ + keyfile.o \ + ringdir.o + +obj-$(CONFIG_KEYFS) := keyfs.o diff -puN /dev/null fs/keyfs/ringdir.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/ringdir.c 2004-08-21 13:24:41.089160920 -0700 @@ -0,0 +1,467 @@ +/* ringdir.c: keyring directory + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * keyring dir operations + */ +static int keyfs_ring_readdir(struct file *, void *, filldir_t); +static struct dentry *keyfs_ring_lookup(struct inode *, struct dentry *, + struct nameidata *); + +struct file_operations keyfs_ring_file_operations = { + .readdir = keyfs_ring_readdir, +}; + +static int keyfs_ring_unlink(struct inode *, struct dentry *); +static int keyfs_ring_symlink(struct inode *, struct dentry *, const char *); + +struct inode_operations keyfs_ring_inode_operations = { + .getattr = keyfs_file_getattr, + .lookup = keyfs_ring_lookup, + .unlink = keyfs_ring_unlink, + .symlink = keyfs_ring_symlink, +}; + +/* + * key link operations + */ +static struct file_operations keyfs_keylink_file_operations = { +}; + +static int keyfs_keylink_getattr(struct vfsmount *, struct dentry *, + struct kstat *); + +static int keyfs_keylink_readlink(struct dentry *, char __user *, int); +static int keyfs_keylink_follow_link(struct dentry *, struct nameidata *); + +static struct inode_operations keyfs_keylink_inode_operations = { + .getattr = keyfs_keylink_getattr, + .follow_link = keyfs_keylink_follow_link, + .readlink = keyfs_keylink_readlink, +}; + +static int keyfs_keylink_d_revalidate(struct dentry *, struct nameidata *); + +static struct dentry_operations keyfs_keylink_dentry_operations = { + .d_revalidate = keyfs_keylink_d_revalidate, + .d_delete = keyfs_d_delete, +}; + +/*****************************************************************************/ +/* + * get a key symlink + */ +static struct inode *keyfs_get_keylink(struct super_block *super, + key_serial_t id) +{ + struct inode *inode; + struct key *key; + + key = key_lookup(id); + if (IS_ERR(key)) { + inode = ERR_PTR(PTR_ERR(key)); + goto error; + } + + /* get the appropriate key symlink inode */ + inode = iget_locked(super, (id << 16) + KEYFS_INO_K_SYMLINK); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + down_read(&key->sem); + + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = key->uid; + inode->i_gid = key->gid; + inode->i_mode = S_IFLNK | S_IRWXUGO; + inode->i_op = &keyfs_keylink_inode_operations; + inode->i_fop = &keyfs_keylink_file_operations; + inode->i_nlink = 2; + inode->u.generic_ip = key; + + up_read(&key->sem); + + /* success */ + unlock_new_inode(inode); + } + else if (inode) { + /* reuse one that already exists */ + key_put(key); + } + else { + /* error */ + inode = ERR_PTR(-ENOMEM); + key_put(key); + } + + error: + return inode; + +} /* end keyfs_get_keylink() */ + +/*****************************************************************************/ +/* + * update the attributes on a keyring symlink + */ +static int keyfs_keylink_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + struct inode *inode; + struct key *key; + + inode = dentry->d_inode; + key = inode->u.generic_ip; + + /* update the inode from the key */ + down_read(&key->sem); + + inode->i_uid = key->uid; + inode->i_gid = key->gid; + + up_read(&key->sem); + + return 1; + +} /* end keyfs_keylink_d_revalidate() */ + +/*****************************************************************************/ +/* + * read the attributes of an inode, updating from the key + */ +static int keyfs_keylink_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + /* update the inode */ + keyfs_keylink_d_revalidate(dentry, NULL); + + /* transfer attributes from the inode structure to the stat + * structure */ + generic_fillattr(dentry->d_inode, stat); + + return 0; + +} /* end keyfs_keylink_getattr() */ + +/*****************************************************************************/ +/* + * enumerate the keys in the ring directory + */ +static int keyfs_ring_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + struct keyring_list *klist; + struct key *key; + loff_t pos; + char id[16]; + int ret, loop, n; + + key = file->f_dentry->d_inode->u.generic_ip; + pos = file->f_pos; + + /* read the usual "." and ".." first followed by the key symlinks */ + switch (file->f_pos) { + case 0: + ret = filldir(cookie, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + + case 1: + ret = filldir(cookie, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (ret < 0) + goto done; + file->f_pos++; + pos--; + + default: + break; + } + + pos -= 2; + + /* then come the key links, a symlink for each */ + down_read(&key->sem); + + klist = key->payload.subscriptions; + if (!klist || pos >= klist->nkeys) + goto done_unlock; + + /* enumerate the keys */ + for (loop = pos; loop < klist->nkeys; loop++) { + /* each symlink's name is the corresponding key serial + * number, and the inode number is based on that too */ + n = sprintf(id, "%d", klist->keys[loop]->serial); + + ret = filldir(cookie, id, n, file->f_pos, + (ino_t) klist->keys[loop] << 16 | + KEYFS_INO_K_SYMLINK, + DT_LNK); + if (ret < 0) + goto done_unlock; + + file->f_pos++; + } + + done_unlock: + up_read(&key->sem); + + done: + return 0; + +} /* end keyfs_ring_readdir() */ + +/*****************************************************************************/ +/* + * look up an inode in the ring directory + */ +static struct dentry *keyfs_ring_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + struct keyring_list *klist; + struct dentry *ret; + struct inode *target; + struct key *key; + key_serial_t id; + const char *name; + char *p; + int loop; + + key = dir->u.generic_ip; + + /* determine which virtual file they want */ + name = dentry->d_name.name; + + switch (dentry->d_name.len) { + case 1: + if (memcmp(name, ".", 1) == 0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 2: + if (memcmp(name, "..", 2)==0) { + target = igrab(dir); + goto instantiate; + } + break; + + + default: + break; + } + + /* it's going to be a keyring symlink then */ + id = simple_strtoul(name, &p, 10); + if (*p) + goto noent; /* not a decimal number */ + + /* check the keyring has a link to the specified key */ + ret = NULL; + + read_lock(&key->lock); + + klist = key->payload.subscriptions; + if (!klist) + goto noent_unlock; + + for (loop = 0; loop < klist->nkeys; loop++) + if (klist->keys[loop]->serial == id) + goto found; + + goto noent_unlock; + found: + + read_unlock(&key->lock); + + /* get the symlink inode */ + target = keyfs_get_keylink(dir->i_sb, id); + if (!IS_ERR(target)) + goto instantiate; + + if (ret == ERR_PTR(-ENOENT)) + goto noent; + + ret = ERR_PTR(PTR_ERR(target)); + goto error; + + /* instantiate the dentry */ + instantiate: + dentry->d_op = &keyfs_keylink_dentry_operations; + d_add(dentry, target); + ret = NULL; + + error: + return ret; + + noent_unlock: + read_unlock(&key->lock); + noent: + /* make a negative dentry */ + d_add(dentry, NULL); + ret = NULL; + goto error; + +} /* end keyfs_ring_lookup() */ + +/*****************************************************************************/ +/* + * unlink a key from a keyring + */ +static int keyfs_ring_unlink(struct inode *dir, struct dentry *dentry) +{ + struct key *keyring, *key; + int ret; + + keyring = dir->u.generic_ip; + key = dentry->d_inode->u.generic_ip; + + /* we must have write permission on the keyring */ + ret = -EPERM; + if (key_permission(keyring, KEY_WRITE)) + /* attempt the unlink */ + ret = key_unlink(keyring, key); + + return ret; + +} /* end keyfs_ring_unlink() */ + +/*****************************************************************************/ +/* + * link a key into a keyring + */ +static int keyfs_ring_symlink(struct inode *dir, struct dentry *dentry, + const char *to) +{ + struct inode *inode; + key_serial_t id; + struct key *keyring, *key; + char *p; + int ret; + + keyring = dir->u.generic_ip; + + /* must have write permission on the keyring */ + ret = -EPERM; + if (!key_permission(keyring, KEY_WRITE)) + goto error; + + /* extract the ID from the symlink target */ + ret = -EINVAL; + if (strncmp(to, "../../", 6) != 0) + goto error; + id = simple_strtoul(to + 6, &p, 10); + if (*p) + goto error; + + /* the new filename must specify the same ID */ + if (strlen(to + 6) != dentry->d_name.len) + goto error; + + if (memcmp(dentry->d_name.name, to + 6, dentry->d_name.len) != 0) + goto error; + + /* create an inode for the symlink */ + inode = keyfs_get_keylink(dir->i_sb, id); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + goto error; + } + + key = inode->u.generic_ip; + + /* we don't admit a key exists if we have no permissions on it at + * all */ + ret = -ENOENT; + if (!key_any_permission(key, KEY_ALL)) + goto error2; + + /* check the key has link access */ + ret = -EPERM; + if (!key_permission(key, KEY_LINK)) + goto error2; + + /* attempt to forge a link */ + ret = key_link(keyring, key); + if (ret < 0) + goto error2; + + /* point the dentry at the inode */ + d_instantiate(dentry, inode); + inode = NULL; + ret = 0; + + error2: + iput(inode); + error: + return ret; + +} /* end keyfs_ring_symlink() */ + +/*****************************************************************************/ +/* + * read a symlink that goes from a keyring to a key + */ +static int keyfs_keylink_readlink(struct dentry *dentry, char __user *_buffer, + int buflen) +{ + key_serial_t id; + char buffer[30]; + int n; + + id = dentry->d_inode->i_ino >> 16; + + /* we render the symlink in the form required for pathwalk */ + n = sprintf(buffer, "../../%d", id); + if (buflen > n) + buflen = n; + + /* and pass back to userspace */ + if (copy_to_user(_buffer, buffer, buflen) != 0) + buflen = -EFAULT; + + return buflen; + +} /* end keyfs_keylink_readlink() */ + +/*****************************************************************************/ +/* + * follow a symlink from a keyring to a key + */ +static int keyfs_keylink_follow_link(struct dentry *dentry, + struct nameidata *nd) +{ + key_serial_t id; + char buffer[30]; + + id = dentry->d_inode->i_ino >> 16; + + /* we render the symlink in the form required for pathwalk */ + sprintf(buffer, "../../%d", id); + nd_set_link(nd, buffer); + + return 0; + +} /* end keyfs_keylink_follow_link() */ diff -puN /dev/null fs/keyfs/root.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/root.c 2004-08-21 13:24:41.088161072 -0700 @@ -0,0 +1,362 @@ +/* root.c: keyfs root inode operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +/* + * root directory operations + */ +static int keyfs_root_readdir(struct file *, void *, filldir_t); + +static struct file_operations keyfs_root_file_operations = { + .readdir = keyfs_root_readdir, +}; + +static struct dentry *keyfs_root_lookup(struct inode *, struct dentry *, + struct nameidata *); + +static struct inode_operations keyfs_root_inode_operations = { + .lookup = keyfs_root_lookup, +}; + +/*****************************************************************************/ +/* + * get the root directory + */ +struct inode *keyfs_get_rootdir(struct super_block *super) +{ + struct inode *inode; + + /* get the root inode */ + inode = iget_locked(super, KEYFS_INO_ROOT); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + inode->i_op = &keyfs_root_inode_operations; + inode->i_fop = &keyfs_root_file_operations; + inode->i_nlink = 2; + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_rootdir() */ + +/*****************************************************************************/ +/* + * enumerate the keys in the root directory + */ +static int keyfs_root_readdir(struct file *file, void *cookie, filldir_t filldir) +{ + struct rb_node *_p; + struct key *next, *cursor; + loff_t pos; + ino_t ino; + char buf[16]; + int ret, n; + + pos = file->f_pos; + + /* read the usual "." and ".." first and then the master control files + * and special symlinks to process/user specific keyrings + */ + switch (file->f_pos) { + case 0: + ret = filldir(cookie, ".", 1, file->f_pos, + file->f_dentry->d_inode->i_ino, DT_DIR); + if (ret < 0) + goto out; + file->f_pos++; + + case 1: + ret = filldir(cookie, "..", 2, file->f_pos, + parent_ino(file->f_dentry), DT_DIR); + if (ret < 0) + goto out; + file->f_pos++; + pos--; + + case 2: + ret = filldir(cookie, "thread", 6, file->f_pos, + KEYFS_INO_THREAD, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 3: + ret = filldir(cookie, "process", 7, file->f_pos, + KEYFS_INO_PROCESS, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 4: + ret = filldir(cookie, "session", 7, file->f_pos, + KEYFS_INO_SESSION, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 5: + ret = filldir(cookie, "user-session", 12, file->f_pos, + KEYFS_INO_USER_SESSION, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 6: + ret = filldir(cookie, "user", 4, file->f_pos, + KEYFS_INO_USER, DT_LNK); + if (ret < 0) + goto out; + file->f_pos++; + + case 7: + ret = filldir(cookie, "request-key", 11, file->f_pos, + KEYFS_INO_REQUEST_KEY, DT_REG); + if (ret < 0) + goto out; + file->f_pos++; + + case 8: + ret = filldir(cookie, "search", 6, file->f_pos, + KEYFS_INO_SEARCH, DT_REG); + if (ret < 0) + goto out; + file->f_pos++; + + case 9: + ret = filldir(cookie, "join-session", 12, file->f_pos, + KEYFS_INO_REQUEST_KEY, DT_REG); + if (ret < 0) + goto out; + file->f_pos++; + + default: + break; + } + + /* then come the keys, a directory for each for each one we can see */ + pos = file->f_pos - 10; + + cursor = NULL; + ret = 0; + + spin_lock(&key_serial_lock); + + /* start with the Nth key */ + _p = rb_first(&key_serial_tree); + if (!_p) + goto out_unlock; + + while (pos > 0) { + pos--; + _p = rb_next(_p); + if (!_p) + goto out_unlock; + } + + /* loop around using the key currently being examined as a cursor (we + * keep it pinned whilst we're outside of the lock so that it doesn't + * get removed from the tree) */ + for (;;) { + next = rb_entry(_p, struct key, serial_node); + atomic_inc(&next->usage); + + spin_unlock(&key_serial_lock); + + key_put(cursor); + cursor = next; + + /* a process is only allowed to see a key if it has at least + * one applicable permission */ + if (key_any_permission(cursor, KEY_ALL)) { + /* each directory's name is the corresponding key + * serial number, and the inode number is based on that + * too */ + n = sprintf(buf, "%d", cursor->serial); + + ino = (ino_t) cursor->serial << 16 | KEYFS_INO_K_DIR; + + ret = filldir(cookie, buf, n, file->f_pos, + ino, DT_DIR); + if (ret < 0) + goto out_put; + } + + file->f_pos++; + + /* advance the cursor under lock */ + spin_lock(&key_serial_lock); + _p = rb_next(_p); + if (!_p) + goto out_unlock; + } + + out_unlock: + spin_unlock(&key_serial_lock); + out_put: + key_put(cursor); + out: + return 0; + +} /* end keyfs_root_readdir() */ + +/*****************************************************************************/ +/* + * look up an inode in the root directory + */ +static struct dentry *keyfs_root_lookup(struct inode *dir, + struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry_operations *dops = NULL; + struct dentry *ret; + struct inode *target; + key_serial_t id; + const char *name; + char *p; + + /* determine which virtual file they're asking for */ + name = dentry->d_name.name; + + switch (dentry->d_name.len) { + case 1: + if (memcmp(name, ".", 1) == 0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 2: + if (memcmp(name, "..", 2)==0) { + target = igrab(dir); + goto instantiate; + } + break; + + case 4: + if (memcmp(name, "user", 4) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_USER); + goto instantiate; + } + break; + + case 6: + if (memcmp(name, "thread", 6) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_THREAD); + goto instantiate; + } + if (memcmp(name, "search", 6) == 0) { + target = keyfs_get_rootfile(dir->i_sb, + KEYFS_INO_SEARCH); + goto instantiate; + } + break; + + case 7: + if (memcmp(name, "process", 7) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_PROCESS); + goto instantiate; + } + if (memcmp(name, "session", 7) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_SESSION); + goto instantiate; + } + break; + + case 11: + if (memcmp(name, "request-key", 11) == 0) { + target = keyfs_get_rootfile(dir->i_sb, + KEYFS_INO_REQUEST_KEY); + goto instantiate; + } + break; + + case 12: + if (memcmp(name, "user-session", 12) == 0) { + target = keyfs_get_rootlink(dir->i_sb, + KEYFS_INO_USER_SESSION); + goto instantiate; + } + if (memcmp(name, "join-session", 12) == 0) { + target = keyfs_get_rootfile(dir->i_sb, + KEYFS_INO_JOIN_SESSION); + goto instantiate; + } + break; + + default: + break; + } + + /* see if they asked for a virtual keyring directory */ + id = simple_strtoul(name, &p, 10); + if (*p) + goto noent; /* not a decimal number, so no */ + + target = keyfs_get_keydir(dir->i_sb, id); + if (!IS_ERR(target)) + goto instantiate_keydir; + if (target == ERR_PTR(-ENOENT)) + goto noent; + + ret = ERR_PTR(PTR_ERR(target)); + goto out; + + /* instantiate the dentry */ + instantiate_keydir: + dops = &keyfs_dir_dentry_operations; + instantiate: + dentry->d_op = dops; + d_add(dentry, target); + ret = NULL; + + out: + return ret; + + noent: + /* make a negative dentry */ + d_add(dentry, NULL); + ret = NULL; + goto out; + +} /* end keyfs_root_lookup() */ + +/*****************************************************************************/ +/* + * we don't want key-specifc files cached, lest the dentry-cache pins keys + */ +int keyfs_d_delete(struct dentry *dentry) +{ + return 1; + +} /* end keyfs_d_delete() */ diff -puN /dev/null fs/keyfs/rootfile.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/rootfile.c 2004-08-21 01:03:03.000000000 -0700 @@ -0,0 +1,455 @@ +/* rootfile.c: keyfs root file operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +static struct inode_operations keyfs_rootfile_inode_operations = { +}; + +/* + * request-key operation + */ +static ssize_t keyfs_request_key_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_request_key_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_request_key_write, + .llseek = no_llseek, +}; + +/* + * search operation + */ +static ssize_t keyfs_search_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_search_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_search_write, + .llseek = no_llseek, +}; + +/* + * join session operation + */ +static ssize_t keyfs_join_session_write(struct file *, const char __user *, + size_t, loff_t *); + +static struct file_operations keyfs_join_session_file_operations = { + .open = keyfs_bifile_open, + .release = keyfs_bifile_release, + .read = keyfs_bifile_read, + .write = keyfs_join_session_write, + .llseek = no_llseek, +}; + +/*****************************************************************************/ +/* + * get a root file inode + */ +struct inode *keyfs_get_rootfile(struct super_block *super, ino_t ino) +{ + struct inode *inode; + + /* get the inode */ + inode = iget_locked(super, ino); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_op = &keyfs_rootfile_inode_operations; + inode->i_nlink = 1; + + switch (ino) { + case KEYFS_INO_REQUEST_KEY: + inode->i_fop = &keyfs_request_key_file_operations; + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO; + break; + + case KEYFS_INO_SEARCH: + inode->i_fop = &keyfs_search_file_operations; + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO; + break; + + case KEYFS_INO_JOIN_SESSION: + inode->i_fop = &keyfs_join_session_file_operations; + inode->i_mode = S_IFREG | S_IRUGO | S_IWUGO; + break; + } + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_rootfile() */ + +/*****************************************************************************/ +/* + * open a two-phase command file + */ +int keyfs_bifile_open(struct inode *inode, struct file *file) +{ + int ret; + + /* allocate and initialise a state record */ + ret = -ENOMEM; + file->private_data = + kmalloc(sizeof(struct keyfs_bifile_record), GFP_KERNEL); + if (!file->private_data) + goto error; + + memset(file->private_data, 0, sizeof(struct keyfs_bifile_record)); + ret = 0; + + error: + return ret; + +} /* end keyfs_bifile_open() */ + +/*****************************************************************************/ +/* + * release a two-phase command file + */ +int keyfs_bifile_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; + +} /* end keyfs_bifile_release() */ + +/*****************************************************************************/ +/* + * read the operation result from a two-phase command file (key ID) + */ +ssize_t keyfs_bifile_read(struct file *file, char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + char buffer[16]; + ssize_t ret, n; + + ret = rec->error; + if (rec->error) + goto error; + + ret = -EINVAL; + if (rec->state == KEYFS_BIFILE_WANT_PARAMS) + goto error; + + ret = 0; + if (rec->state != KEYFS_BIFILE_GOT_RESULT) + goto error; + + rec->state = KEYFS_BIFILE_DONE; + + /* allow userspace to read back the new keyID */ + n = sprintf(buffer, "%d\n", rec->id); + if (n > buflen) + n = buflen; + + ret = -EAGAIN; + if (copy_to_user(_buffer, buffer, n) != 0) + goto error; + + ret = n; + + error: + return ret; + +} /* end keyfs_bifile_read() */ + +/*****************************************************************************/ +/* + * request a key + * - format: NLNL + */ +static ssize_t keyfs_request_key_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key_type *ktype; + struct key *key; + ssize_t ret; + char *buffer, *bend, *p, *type, *desc; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error2; + } + + /* request the key */ + key = request_key(ktype, desc, 1); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error3; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + key_put(key); + + ret = buflen; + + error3: + key_type_put(ktype); + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_request_key_write() */ + +/*****************************************************************************/ +/* + * search for a key + * - format: NLNL[]NL + */ +static ssize_t keyfs_search_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + struct key_type *ktype; + key_serial_t ringid; + struct key *key, *keyring; + ssize_t ret; + char *buffer, *bend, *p, *type, *desc, *ring; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > 40000) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + type = buffer; + + p = memchr(buffer, '\n', bend - buffer); + if (!p) + goto error2; + *p++ = 0; + desc = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + ring = p; + + p = memchr(p, '\n', bend - p); + if (!p) + goto error2; + *p++ = 0; + + if (p != bend) + goto error2; + + /* if supplied, turn the ring ID into a keyring */ + keyring = NULL; + + if (*ring) { + ringid = simple_strtoul(ring, &p, 0); + if (*p) + goto error2; + + /* find the target keyring (which must be writable) */ + keyring = lookup_user_key(ringid, 1, 0, KEY_WRITE); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto error2; + } + } + + /* find the key type */ + ktype = key_type_lookup(type); + if (IS_ERR(ktype)) { + ret = PTR_ERR(ktype); + goto error3; + } + + /* search for the key */ + key = search_process_keyrings(ktype, desc); + if (IS_ERR(key)) { + ret = PTR_ERR(key); + goto error4; + } + + /* link the resulting key to the destination keyring if we can */ + if (keyring) { + ret = -EACCES; + if (!key_permission(key, KEY_LINK)) + goto error5; + + ret = key_link(keyring, key); + if (ret < 0) + goto error5; + } + + /* we'll be returning the key ID on the next read */ + rec->id = key->serial; + + ret = buflen; + + error5: + key_put(key); + error4: + key_type_put(ktype); + error3: + key_put(keyring); + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_search_write() */ + +/*****************************************************************************/ +/* + * join a session keyring, creating it if it doesn't exist + * - format: [][NL] + */ +static ssize_t keyfs_join_session_write(struct file *file, + const char __user *_buffer, + size_t buflen, loff_t *fpos) +{ + struct keyfs_bifile_record *rec = file->private_data; + ssize_t ret; + long jret; + char *buffer, *bend, *p, *name; + + ret = -EINVAL; + if (rec->state != KEYFS_BIFILE_WANT_PARAMS || buflen > PAGE_SIZE) + goto error; + + rec->state = KEYFS_BIFILE_GOT_RESULT; + + /* fetch the data into kernel memory */ + ret = -ENOMEM; + buffer = kmalloc(buflen + 1, GFP_KERNEL); + if (!buffer) + goto error; + + ret = -EFAULT; + if (copy_from_user(buffer, _buffer, buflen) != 0) + goto error2; + bend = buffer + buflen; + *bend = 0; + + /* parse the parameter string */ + ret = -EINVAL; + + p = memchr(buffer, '\n', bend - buffer); + if (p) { + *p++ = 0; + if (p != bend) + goto error2; + } + + name = buffer; + if (!*buffer) + name = NULL; /* anonymous session */ + + /* attempt to join the named session */ + jret = join_session_keyring(name); + if (jret < 0) { + ret = jret; + goto error2; + } + + /* we'll be returning the key ID on the next read */ + rec->id = jret; + ret = buflen; + + error2: + kfree(buffer); + error: + if (ret < 0) + rec->error = ret; + return ret; + +} /* end keyfs_join_session_write() */ diff -puN /dev/null fs/keyfs/rootlink.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/rootlink.c 2004-08-21 01:03:03.000000000 -0700 @@ -0,0 +1,180 @@ +/* rootlink.c: special process/user keyring symlink operations + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +static struct file_operations keyfs_rootlink_file_operations = { +}; + +static int keyfs_rootlink_readlink(struct dentry *, char __user *, int); +static int keyfs_rootlink_follow_link(struct dentry *, struct nameidata *); + +static struct inode_operations keyfs_rootlink_inode_operations = { + .follow_link = keyfs_rootlink_follow_link, + .readlink = keyfs_rootlink_readlink, +}; + +/*****************************************************************************/ +/* + * get a special process keyring symlink inode + */ +struct inode *keyfs_get_rootlink(struct super_block *super, ino_t ino) +{ + struct inode *inode; + + /* get the inode */ + inode = iget_locked(super, ino); + if (inode && inode->i_state & I_NEW) { + /* initialise it */ + inode->i_mtime = inode->i_atime = inode->i_ctime = + CURRENT_TIME; + inode->i_blocks = 0; + inode->i_blksize = 1024; + inode->i_uid = 0; + inode->i_gid = 0; + inode->i_mode = S_IFLNK | S_IRUGO | S_IXUGO; + inode->i_op = &keyfs_rootlink_inode_operations; + inode->i_fop = &keyfs_rootlink_file_operations; + inode->i_nlink = 2; + + /* success */ + unlock_new_inode(inode); + } + + return inode; + +} /* end keyfs_get_rootlink() */ + +/*****************************************************************************/ +/* + * read the symlink + */ +static int keyfs_rootlink_readlink(struct dentry *dentry, + char __user *_buffer, + int buflen) +{ + key_serial_t id; + char buffer[12]; + int n; + + id = 0; + + /* the symlink targets the appropriate keyring directory */ + switch (dentry->d_inode->i_ino) { + case KEYFS_INO_THREAD: + if (current->thread_keyring) + id = current->thread_keyring->serial; + break; + + case KEYFS_INO_PROCESS: + if (current->process_keyring) + id = current->process_keyring->serial; + break; + + case KEYFS_INO_SESSION: + if (current->session_keyring) + id = current->session_keyring->serial; + break; + + case KEYFS_INO_USER_SESSION: + if (current->user->uid_keyring) + id = current->user->uid_keyring->serial; + break; + + case KEYFS_INO_USER: + if (current->user->session_keyring) + id = current->user->session_keyring->serial; + break; + + default: + BUG(); + } + + /* render the keyring ID as a string and write to userspace */ + n = sprintf(buffer, "%d", id); + if (buflen > n) + buflen = n; + + if (copy_to_user(_buffer, buffer, buflen) != 0) + buflen = -EFAULT; + + return buflen; + +} /* end keyfs_rootlink_readlink() */ + +/*****************************************************************************/ +/* + * follow the symlink to the appropriate directory + */ +static int keyfs_rootlink_follow_link(struct dentry *dentry, + struct nameidata *nd) +{ + key_serial_t id; + char buffer[13]; + int ret; + + id = 0; + ret = -ENOENT; + + /* target the symlink to the appropriate keyring directory */ + switch (dentry->d_inode->i_ino) { + case KEYFS_INO_THREAD: + if (!current->thread_keyring) + goto no_keyring; + id = current->thread_keyring->serial; + break; + + case KEYFS_INO_PROCESS: + if (!current->process_keyring) + goto no_keyring; + id = current->process_keyring->serial; + break; + + case KEYFS_INO_SESSION: + if (!current->session_keyring) + goto no_keyring; + id = current->session_keyring->serial; + break; + + case KEYFS_INO_USER_SESSION: + if (!current->user->uid_keyring) + goto no_keyring; + id = current->user->uid_keyring->serial; + break; + + case KEYFS_INO_USER: + if (!current->user->session_keyring) + goto no_keyring; + id = current->user->session_keyring->serial; + break; + + default: + BUG(); + } + + /* render the keyring ID as a string and pass back to pathwalk */ + sprintf(buffer, "%d", id); + nd_set_link(nd, buffer); + ret = 0; + + no_keyring: + return ret; + +} /* end keyfs_rootlink_follow_link() */ diff -puN /dev/null fs/keyfs/super.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/keyfs/super.c 2004-08-21 01:03:03.000000000 -0700 @@ -0,0 +1,117 @@ +/* super.c: key management filesystme + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "keyfs.h" + +#define KEYFS_FS_MAGIC 0x4B455953 /* 'KEYS' */ + +static struct super_block *keyfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data); + +static int keyfs_fill_super(struct super_block *s, void *data, int silent); + +static struct file_system_type keyfs_fs_type = { + .owner = THIS_MODULE, + .name = "keyfs", + .get_sb = keyfs_get_sb, + .kill_sb = kill_anon_super, +}; + +static void keyfs_clear_inode(struct inode *vfs_inode); + +static struct super_operations keyfs_super_ops = { + .statfs = simple_statfs, + .clear_inode = keyfs_clear_inode, +}; + + +/*****************************************************************************/ +/* + * initialise the filesystem + */ +static int __init keyfs_fs_init(void) +{ + /* export our filesystem to lesser mortals */ + return register_filesystem(&keyfs_fs_type); + +} /* end keyfs_fs_init() */ + +fs_initcall(keyfs_fs_init); + +/*****************************************************************************/ +/* + * create the keyfs superblock (one time only) + */ +static struct super_block *keyfs_get_sb(struct file_system_type *fs_type, + int flags, const char *dev_name, + void *data) +{ + return get_sb_single(&keyfs_fs_type, flags, data, keyfs_fill_super); + +} /* end keyfs_get_sb() */ + +/*****************************************************************************/ +/* + * fill out the superblock + */ +static int keyfs_fill_super(struct super_block *s, void *data, int silent) +{ + struct inode *inode; + int ret; + + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = KEYFS_FS_MAGIC; + s->s_op = &keyfs_super_ops; + + /* allocate the root inode */ + ret = -ENOMEM; + inode = keyfs_get_rootdir(s); + + if (!IS_ERR(inode)) { + /* allocate a root dentry */ + s->s_root = d_alloc_root(inode); + if (s->s_root) { + ret = 0; + } + else { + printk("keyfs: get root dentry failed\n"); + iput(inode); + } + } + + + return ret; + +} /* end keyfs_fill_super() */ + +/*****************************************************************************/ +/* + * clear an inode + */ +static void keyfs_clear_inode(struct inode *inode) +{ + struct key *key = inode->u.generic_ip; + + inode->u.generic_ip = NULL; + + key_put(key); + +} /* end keyfs_clear_inode() */ diff -puN fs/Makefile~keys-keyring-management-keyfs-patch fs/Makefile --- 25/fs/Makefile~keys-keyring-management-keyfs-patch 2004-08-21 01:03:03.000000000 -0700 +++ 25-akpm/fs/Makefile 2004-08-21 13:23:28.752157816 -0700 @@ -93,3 +93,4 @@ obj-$(CONFIG_AFS_FS) += afs/ obj-$(CONFIG_BEFS_FS) += befs/ obj-$(CONFIG_HOSTFS) += hostfs/ obj-$(CONFIG_HPPFS) += hppfs/ +obj-$(CONFIG_KEYFS) += keyfs/ _