From patchwork Thu Aug 19 20:27:13 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Igor Druzhinin X-Patchwork-Id: 120398 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o7JKQPul006102 for ; Thu, 19 Aug 2010 20:28:08 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751445Ab0HSU1d (ORCPT ); Thu, 19 Aug 2010 16:27:33 -0400 Received: from mail-ey0-f174.google.com ([209.85.215.174]:49150 "EHLO mail-ey0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751265Ab0HSU1d (ORCPT ); Thu, 19 Aug 2010 16:27:33 -0400 Received: by eyg5 with SMTP id 5so1640561eyg.19 for ; Thu, 19 Aug 2010 13:27:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:from:to:cc:subject:date :message-id:x-mailer:in-reply-to:references; bh=YxVSIEThDd5gZAoZapyXPox7r92aqOkRzsyVO01mohk=; b=LAeKPreijbDgDcQ4DfkSraX675ko+gv64AIFwi6ayceDA+C1Kuu0mXAVn2P4ibW1qb vzc1jnHSPFSTEQKIanyn48ZQWgNtJqHi00TPd4wUaxuTla5a58Ow5/5gGwcQfSD/01Ys iZuM9vrTGrKc4JAm0aBf8ptk+C8DzM3PmbDeU= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=IdlKdr9YddnY4pc8r9jI2QQKmgR5FdLvx9LmstYKKaE63u4lCi/ECtd4PX5BzTsetS Lv7NlZ36lzz3LF/6n8OpljGKGCHt3a3/0BdZ7/rvNVGCcK6swDOhRHeoN1M1qvlvMA3C CCqtDYh1XzqX6GnB6Ix4FpMSaauzvkuup9OYo= Received: by 10.213.17.195 with SMTP id t3mr1196177eba.63.1282249651575; Thu, 19 Aug 2010 13:27:31 -0700 (PDT) Received: from localhost.localdomain ([95.84.52.65]) by mx.google.com with ESMTPS id v8sm3312416eeh.14.2010.08.19.13.27.29 (version=TLSv1/SSLv3 cipher=RC4-MD5); Thu, 19 Aug 2010 13:27:31 -0700 (PDT) From: Igor Druzhinin To: jlayton@samba.org Cc: linux-cifs@vger.kernel.org, Igor Druzhinin Subject: [PATCH 2/2] cifs: infrastructure for stashing passwords in keyring Date: Fri, 20 Aug 2010 00:27:13 +0400 Message-Id: <1282249633-27756-2-git-send-email-jaxbrigs@gmail.com> X-Mailer: git-send-email 1.7.2.1 In-Reply-To: <1282249633-27756-1-git-send-email-jaxbrigs@gmail.com> References: <1282249633-27756-1-git-send-email-jaxbrigs@gmail.com> Sender: linux-cifs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 19 Aug 2010 20:28:09 +0000 (UTC) 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 (jaxbrigs@gmail.com) + * Jeff Layton (jlayton@samba.org) + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 (jaxbrigs@gmail.com) + * Jeff Layton (jlayton@samba.org) + * + * 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 + +#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)) {