aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2012-01-17 16:09:15 -0500
committerSteve French <smfrench@gmail.com>2012-01-17 23:40:28 -0500
commit8a8798a5ff90977d6459ce1d657cf8fe13a51e97 (patch)
tree42708337792bc20295faed2c78f4dca89e009ffa /fs
parent04febabcf55beeffb8794a0d8c539e571bd2ae29 (diff)
cifs: fetch credentials out of keyring for non-krb5 auth multiuser mounts
Fix up multiuser mounts to set the secType and set the username and password from the key payload in the vol info for non-krb5 auth types. Look for a key of type "secret" with a description of "cifs:a:<server address>" or "cifs:d:<domainname>". If that's found, then scrape the username and password out of the key payload and use that to create a new user session. Finally, don't have the code enforce krb5 auth on multiuser mounts, but do require a kernel with keys support. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/cifs/connect.c175
1 files changed, 165 insertions, 10 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index b952a21e917..28f23c03da5 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -38,6 +38,7 @@
38#include <asm/processor.h> 38#include <asm/processor.h>
39#include <linux/inet.h> 39#include <linux/inet.h>
40#include <linux/module.h> 40#include <linux/module.h>
41#include <keys/user-type.h>
41#include <net/ipv6.h> 42#include <net/ipv6.h>
42#include "cifspdu.h" 43#include "cifspdu.h"
43#include "cifsglob.h" 44#include "cifsglob.h"
@@ -1594,11 +1595,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
1594 } 1595 }
1595 } 1596 }
1596 1597
1597 if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { 1598#ifndef CONFIG_KEYS
1598 cERROR(1, "Multiuser mounts currently require krb5 " 1599 /* Muliuser mounts require CONFIG_KEYS support */
1599 "authentication!"); 1600 if (vol->multiuser) {
1601 cERROR(1, "Multiuser mounts require kernels with "
1602 "CONFIG_KEYS enabled.");
1600 goto cifs_parse_mount_err; 1603 goto cifs_parse_mount_err;
1601 } 1604 }
1605#endif
1602 1606
1603 if (vol->UNCip == NULL) 1607 if (vol->UNCip == NULL)
1604 vol->UNCip = &vol->UNC[2]; 1608 vol->UNCip = &vol->UNC[2];
@@ -2061,6 +2065,132 @@ cifs_put_smb_ses(struct cifs_ses *ses)
2061 cifs_put_tcp_session(server); 2065 cifs_put_tcp_session(server);
2062} 2066}
2063 2067
2068#ifdef CONFIG_KEYS
2069
2070/* strlen("cifs:a:") + INET6_ADDRSTRLEN + 1 */
2071#define CIFSCREDS_DESC_SIZE (7 + INET6_ADDRSTRLEN + 1)
2072
2073/* Populate username and pw fields from keyring if possible */
2074static int
2075cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
2076{
2077 int rc = 0;
2078 char *desc, *delim, *payload;
2079 ssize_t len;
2080 struct key *key;
2081 struct TCP_Server_Info *server = ses->server;
2082 struct sockaddr_in *sa;
2083 struct sockaddr_in6 *sa6;
2084 struct user_key_payload *upayload;
2085
2086 desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL);
2087 if (!desc)
2088 return -ENOMEM;
2089
2090 /* try to find an address key first */
2091 switch (server->dstaddr.ss_family) {
2092 case AF_INET:
2093 sa = (struct sockaddr_in *)&server->dstaddr;
2094 sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr);
2095 break;
2096 case AF_INET6:
2097 sa6 = (struct sockaddr_in6 *)&server->dstaddr;
2098 sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr);
2099 break;
2100 default:
2101 cFYI(1, "Bad ss_family (%hu)", server->dstaddr.ss_family);
2102 rc = -EINVAL;
2103 goto out_err;
2104 }
2105
2106 cFYI(1, "%s: desc=%s", __func__, desc);
2107 key = request_key(&key_type_logon, desc, "");
2108 if (IS_ERR(key)) {
2109 if (!ses->domainName) {
2110 cFYI(1, "domainName is NULL");
2111 rc = PTR_ERR(key);
2112 goto out_err;
2113 }
2114
2115 /* didn't work, try to find a domain key */
2116 sprintf(desc, "cifs:d:%s", ses->domainName);
2117 cFYI(1, "%s: desc=%s", __func__, desc);
2118 key = request_key(&key_type_logon, desc, "");
2119 if (IS_ERR(key)) {
2120 rc = PTR_ERR(key);
2121 goto out_err;
2122 }
2123 }
2124
2125 down_read(&key->sem);
2126 upayload = key->payload.data;
2127 if (IS_ERR_OR_NULL(upayload)) {
2128 rc = PTR_ERR(key);
2129 goto out_key_put;
2130 }
2131
2132 /* find first : in payload */
2133 payload = (char *)upayload->data;
2134 delim = strnchr(payload, upayload->datalen, ':');
2135 cFYI(1, "payload=%s", payload);
2136 if (!delim) {
2137 cFYI(1, "Unable to find ':' in payload (datalen=%d)",
2138 upayload->datalen);
2139 rc = -EINVAL;
2140 goto out_key_put;
2141 }
2142
2143 len = delim - payload;
2144 if (len > MAX_USERNAME_SIZE || len <= 0) {
2145 cFYI(1, "Bad value from username search (len=%ld)", len);
2146 rc = -EINVAL;
2147 goto out_key_put;
2148 }
2149
2150 vol->username = kstrndup(payload, len, GFP_KERNEL);
2151 if (!vol->username) {
2152 cFYI(1, "Unable to allocate %ld bytes for username", len);
2153 rc = -ENOMEM;
2154 goto out_key_put;
2155 }
2156 cFYI(1, "%s: username=%s", __func__, vol->username);
2157
2158 len = key->datalen - (len + 1);
2159 if (len > MAX_PASSWORD_SIZE || len <= 0) {
2160 cFYI(1, "Bad len for password search (len=%ld)", len);
2161 rc = -EINVAL;
2162 kfree(vol->username);
2163 vol->username = NULL;
2164 goto out_key_put;
2165 }
2166
2167 ++delim;
2168 vol->password = kstrndup(delim, len, GFP_KERNEL);
2169 if (!vol->password) {
2170 cFYI(1, "Unable to allocate %ld bytes for password", len);
2171 rc = -ENOMEM;
2172 kfree(vol->username);
2173 vol->username = NULL;
2174 goto out_key_put;
2175 }
2176
2177out_key_put:
2178 up_read(&key->sem);
2179 key_put(key);
2180out_err:
2181 kfree(desc);
2182 cFYI(1, "%s: returning %d", __func__, rc);
2183 return rc;
2184}
2185#else /* ! CONFIG_KEYS */
2186static inline int
2187cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
2188 struct cifs_ses *ses __attribute__((unused)))
2189{
2190 return -ENOSYS;
2191}
2192#endif /* CONFIG_KEYS */
2193
2064static bool warned_on_ntlm; /* globals init to false automatically */ 2194static bool warned_on_ntlm; /* globals init to false automatically */
2065 2195
2066static struct cifs_ses * 2196static struct cifs_ses *
@@ -3693,16 +3823,38 @@ int cifs_setup_session(unsigned int xid, struct cifs_ses *ses,
3693 return rc; 3823 return rc;
3694} 3824}
3695 3825
3826static int
3827cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses)
3828{
3829 switch (ses->server->secType) {
3830 case Kerberos:
3831 vol->secFlg = CIFSSEC_MUST_KRB5;
3832 return 0;
3833 case NTLMv2:
3834 vol->secFlg = CIFSSEC_MUST_NTLMV2;
3835 break;
3836 case NTLM:
3837 vol->secFlg = CIFSSEC_MUST_NTLM;
3838 break;
3839 case RawNTLMSSP:
3840 vol->secFlg = CIFSSEC_MUST_NTLMSSP;
3841 break;
3842 case LANMAN:
3843 vol->secFlg = CIFSSEC_MUST_LANMAN;
3844 break;
3845 }
3846
3847 return cifs_set_cifscreds(vol, ses);
3848}
3849
3696static struct cifs_tcon * 3850static struct cifs_tcon *
3697cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) 3851cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
3698{ 3852{
3853 int rc;
3699 struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); 3854 struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
3700 struct cifs_ses *ses; 3855 struct cifs_ses *ses;
3701 struct cifs_tcon *tcon = NULL; 3856 struct cifs_tcon *tcon = NULL;
3702 struct smb_vol *vol_info; 3857 struct smb_vol *vol_info;
3703 char username[28]; /* big enough for "krb50x" + hex of ULONG_MAX 6+16 */
3704 /* We used to have this as MAX_USERNAME which is */
3705 /* way too big now (256 instead of 32) */
3706 3858
3707 vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); 3859 vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL);
3708 if (vol_info == NULL) { 3860 if (vol_info == NULL) {
@@ -3710,8 +3862,6 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
3710 goto out; 3862 goto out;
3711 } 3863 }
3712 3864
3713 snprintf(username, sizeof(username), "krb50x%x", fsuid);
3714 vol_info->username = username;
3715 vol_info->local_nls = cifs_sb->local_nls; 3865 vol_info->local_nls = cifs_sb->local_nls;
3716 vol_info->linux_uid = fsuid; 3866 vol_info->linux_uid = fsuid;
3717 vol_info->cred_uid = fsuid; 3867 vol_info->cred_uid = fsuid;
@@ -3721,8 +3871,11 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
3721 vol_info->local_lease = master_tcon->local_lease; 3871 vol_info->local_lease = master_tcon->local_lease;
3722 vol_info->no_linux_ext = !master_tcon->unix_ext; 3872 vol_info->no_linux_ext = !master_tcon->unix_ext;
3723 3873
3724 /* FIXME: allow for other secFlg settings */ 3874 rc = cifs_set_vol_auth(vol_info, master_tcon->ses);
3725 vol_info->secFlg = CIFSSEC_MUST_KRB5; 3875 if (rc) {
3876 tcon = ERR_PTR(rc);
3877 goto out;
3878 }
3726 3879
3727 /* get a reference for the same TCP session */ 3880 /* get a reference for the same TCP session */
3728 spin_lock(&cifs_tcp_ses_lock); 3881 spin_lock(&cifs_tcp_ses_lock);
@@ -3745,6 +3898,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
3745 if (ses->capabilities & CAP_UNIX) 3898 if (ses->capabilities & CAP_UNIX)
3746 reset_cifs_unix_caps(0, tcon, NULL, vol_info); 3899 reset_cifs_unix_caps(0, tcon, NULL, vol_info);
3747out: 3900out:
3901 kfree(vol_info->username);
3902 kfree(vol_info->password);
3748 kfree(vol_info); 3903 kfree(vol_info);
3749 3904
3750 return tcon; 3905 return tcon;