This is a kernel part of a new infrastructure for stashing passwords
in kernel keyring per user basis. Keys of "user" type with passwords
are protected from disclosure and stored in uid-specific keyring.
Search is happens according to the key description in format
cifscreds:ip:username:[domainname]. In a case if user has given
credentials otherwise search doesn't happen.

Signed-off-by: Igor Druzhinin  <[email protected]>
---
 fs/cifs/Makefile    |    2 +-
 fs/cifs/cifscreds.c |  192 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/cifscreds.h |   34 +++++++++
 fs/cifs/connect.c   |   13 ++++
 4 files changed, 240 insertions(+), 1 deletions(-)
 create mode 100644 fs/cifs/cifscreds.c
 create mode 100644 fs/cifs/cifscreds.h

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index adefa60..babab37 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -6,7 +6,7 @@ obj-$(CONFIG_CIFS) += cifs.o
 cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \
          link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \
          md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o \
-         readdir.o ioctl.o sess.o export.o cifsacl.o
+         readdir.o ioctl.o sess.o export.o cifsacl.o cifscreds.o
 
 cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o
 
diff --git a/fs/cifs/cifscreds.c b/fs/cifs/cifscreds.c
new file mode 100644
index 0000000..828631f
--- /dev/null
+++ b/fs/cifs/cifscreds.c
@@ -0,0 +1,192 @@
+/*
+ *   fs/cifs/cifscreds.c - CIFS credentinals stashing rountine
+ *
+ *   Copyright (c) 2010 Igor Druzhinin
+ *   Copyright (c) 2010 Jeff Layton
+ *   Author(s): Igor Druzhinin ([email protected])
+ *             Jeff Layton ([email protected])
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/inet.h>
+#include <linux/key.h>
+#include <linux/keyctl.h>
+#include <linux/key-type.h>
+#include <keys/keyring-type.h>
+#include <keys/user-type.h>
+#include "cifsglob.h"
+#include "cifs_debug.h"
+#include "cifscreds.h"
+
+/* create key description from given credentials */
+static char *
+create_key_description(const char *addr, const char *user,
+                       const char *domain, char *desc)
+{
+       char *str_end;
+       int str_len;
+
+       sprintf(desc, "%s:%s:%s:", CIFSCREDS_PROG_NAME, addr, user);
+
+       if (domain != NULL) {
+               str_end = desc + strlen(desc);
+               str_len = strlen(domain);
+               while (str_len--) {
+                       *str_end = tolower(*domain++);
+                       str_end++;
+               }
+               *str_end = '\0';
+       }
+
+       return desc;
+}
+
+/*
+ * searching for requested key in the uid or session specific keyring
+ * according to DEST_KEYRING macro. After all operations with key caller
+ * must do key_put()
+ */
+static struct key *
+cifs_key_search(const char *addr, const char *user, const char *domain)
+{
+       char *desc;
+       struct key *keyring, *key;
+       struct keyring_list *klist;
+       int loop;
+
+       switch (DEST_KEYRING) {
+       case KEY_SPEC_USER_KEYRING:
+               keyring = current_user()->uid_keyring;
+               break;
+
+       case KEY_SPEC_SESSION_KEYRING:
+               keyring = current_user()->session_keyring;
+               break;
+
+       default:
+               printk(KERN_WARNING "CIFS creds: unsupported keyring\n");
+               return NULL;
+       }
+       if (keyring == NULL) {
+               cFYI(1, "CIFS creds: keyring is not installed");
+               return NULL;
+       }
+
+       desc = kmalloc(INET6_ADDRSTRLEN + 200 + 256 + \
+                       sizeof(CIFSCREDS_PROG_NAME) + 3, GFP_KERNEL);
+       if (desc == NULL) {
+               printk(KERN_WARNING "CIFS creds: no memory for description\n");
+               return NULL;
+       }
+
+       create_key_description(addr, user, domain, desc);
+       cFYI(1, "CIFS creds: key description: %s for uid:%d",
+               desc, current_uid());
+
+       klist = rcu_dereference_protected(keyring->payload.subscriptions,
+               rwsem_is_locked((struct rw_semaphore *)&keyring->sem));
+       if (klist) {
+               for (loop = 0; loop < klist->nkeys; loop++) {
+                       key = klist->keys[loop];
+
+                       if (key->type == &key_type_user &&
+                               (!key->type->match ||
+                               key->type->match(key, desc)) &&
+                               !test_bit(KEY_FLAG_REVOKED, &key->flags)
+                       )
+                               goto key_search_found;
+               }
+       }
+       kfree(desc);
+       return NULL;
+
+key_search_found:
+       atomic_inc(&key->usage);
+       kfree(desc);
+       return key;
+}
+
+/* read and copy key data to kernel buffer */
+static long cifs_key_read(const struct key *key, char *buffer, size_t buflen)
+{
+       struct user_key_payload *upayload;
+       long ret;
+
+       upayload = rcu_dereference_protected(key->payload.data,
+               rwsem_is_locked(&((struct key *)key)->sem));
+       ret = upayload->datalen;
+
+       /* we can return the data as is */
+       if (buffer && buflen > 0) {
+               if (buflen > upayload->datalen)
+                       buflen = upayload->datalen;
+
+               if (memcpy(buffer, upayload->data, buflen) == NULL)
+                       ret = -EFAULT;
+       }
+
+       return ret;
+}
+
+/**
+ * cifs_get_key_password - Reading password from the keyring
+ *             according to provided description.
+ * @addr: IP address of target server.
+ * @user: Name of the intrested user.
+ * @domain: Possible domain name. Can be NULL.
+ *
+ * Returns the pointer to password or NULL, and the caller is
+ * responsible for freeing it.
+ */
+char *
+cifs_get_key_password(const char *addr, const char *user, const char *domain)
+{
+       struct key *pass_key;
+       char *key_password = NULL;
+       long pass_len;
+
+       pass_key = cifs_key_search(addr, user, domain);
+       if (pass_key == NULL) {
+               cFYI(1, "CIFS creds: Appropriate key for %s not found", addr);
+               return NULL;
+       }
+
+       pass_len = cifs_key_read(pass_key, NULL, 0);
+       if (pass_len == 0) {
+               cFYI(1, "CIFS creds: Password key for %s is empty", addr);
+               goto no_key_password;
+       }
+
+       key_password = kzalloc(pass_len, GFP_KERNEL);
+       if (key_password == NULL) {
+               printk(KERN_WARNING "CIFS creds: no memory for password\n");
+               goto no_key_password;
+       }
+
+       pass_len = cifs_key_read(pass_key, key_password, pass_len);
+       if (pass_len < 0) {
+               printk(KERN_WARNING "CIFS creds: unable to read key's "
+                       "payload for %s\n", addr);
+               kzfree(key_password);
+               key_password = NULL;
+       }
+
+no_key_password:
+       key_put(pass_key);
+       return key_password;
+}
diff --git a/fs/cifs/cifscreds.h b/fs/cifs/cifscreds.h
new file mode 100644
index 0000000..466ecf9
--- /dev/null
+++ b/fs/cifs/cifscreds.h
@@ -0,0 +1,34 @@
+/*
+ *   fs/cifs/cifscreds.c - CIFS credentinals stashing rountine definition
+ *
+ *   Copyright (c) 2010 Igor Druzhinin
+ *   Copyright (c) 2010 Jeff Layton
+ *   Author(s): Igor Druzhinin ([email protected])
+ *             Jeff Layton ([email protected])
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _CIFSCREDS_H
+#define _CIFSCREDS_H
+
+#include <linux/keyctl.h>
+
+#define CIFSCREDS_PROG_NAME "cifscreds"
+#define DEST_KEYRING KEY_SPEC_USER_KEYRING
+
+extern char *
+cifs_get_key_password(const char *addr,        const char *user, const char 
*domain);
+
+#endif /* _CIFSCREDS_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 95c2ea6..ef4371e 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -49,6 +49,7 @@
 #include "rfc1002pdu.h"
 #include "cn_cifs.h"
 #include "fscache.h"
+#include "cifscreds.h"
 
 #define CIFS_PORT 445
 #define RFC1001_PORT 139
@@ -2568,6 +2569,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info 
*cifs_sb,
        struct TCP_Server_Info *srvTcp;
        char   *full_path;
        char *mount_data = mount_data_global;
+       char *key_password;
 #ifdef CONFIG_CIFS_DFS_UPCALL
        struct dfs_info3_param *referrals = NULL;
        unsigned int num_referrals = 0;
@@ -2622,6 +2624,17 @@ try_mount_again:
        }
        cifs_sb->local_nls = volume_info->local_nls;
 
+       /* search for key with password and read its payload */
+       if (volume_info->password == NULL &&
+               !(volume_info->secFlg & CIFSSEC_MAY_KRB5)
+       ) {
+               key_password = cifs_get_key_password(volume_info->UNCip,
+                                               volume_info->username,
+                                               volume_info->domainname);
+               if (key_password != NULL)
+                       volume_info->password = key_password;
+       }
+
        /* get a reference to a tcp session */
        srvTcp = cifs_get_tcp_session(volume_info);
        if (IS_ERR(srvTcp)) {
-- 
1.7.2.1

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to