diff options
author | Jeff Layton <jlayton@redhat.com> | 2012-01-17 16:09:15 -0500 |
---|---|---|
committer | Steve French <smfrench@gmail.com> | 2012-01-17 23:40:28 -0500 |
commit | 8a8798a5ff90977d6459ce1d657cf8fe13a51e97 (patch) | |
tree | 42708337792bc20295faed2c78f4dca89e009ffa /fs/cifs | |
parent | 04febabcf55beeffb8794a0d8c539e571bd2ae29 (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/cifs')
-rw-r--r-- | fs/cifs/connect.c | 175 |
1 files changed, 165 insertions, 10 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index b952a21e917b..28f23c03da53 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 */ | ||
2074 | static int | ||
2075 | cifs_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 | |||
2177 | out_key_put: | ||
2178 | up_read(&key->sem); | ||
2179 | key_put(key); | ||
2180 | out_err: | ||
2181 | kfree(desc); | ||
2182 | cFYI(1, "%s: returning %d", __func__, rc); | ||
2183 | return rc; | ||
2184 | } | ||
2185 | #else /* ! CONFIG_KEYS */ | ||
2186 | static inline int | ||
2187 | cifs_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 | |||
2064 | static bool warned_on_ntlm; /* globals init to false automatically */ | 2194 | static bool warned_on_ntlm; /* globals init to false automatically */ |
2065 | 2195 | ||
2066 | static struct cifs_ses * | 2196 | static 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 | ||
3826 | static int | ||
3827 | cifs_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 | |||
3696 | static struct cifs_tcon * | 3850 | static struct cifs_tcon * |
3697 | cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) | 3851 | cifs_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); |
3747 | out: | 3900 | out: |
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; |