On Fri, Jan 6, 2012 at 7:11 AM, Jeff Layton <jlayton@xxxxxxxxxx> wrote: > 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@xxxxxxxxxx> > --- > fs/cifs/connect.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++--- > 1 files changed, 166 insertions(+), 10 deletions(-) > > diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c > index 5e96e60..48f2d15 100644 > --- a/fs/cifs/connect.c > +++ b/fs/cifs/connect.c > @@ -38,6 +38,7 @@ > #include <asm/processor.h> > #include <linux/inet.h> > #include <linux/module.h> > +#include <keys/user-type.h> > #include <net/ipv6.h> > #include "cifspdu.h" > #include "cifsglob.h" > @@ -1594,11 +1595,14 @@ cifs_parse_mount_options(const char *mountdata, const char *devname, > } > } > > - if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { > - cERROR(1, "Multiuser mounts currently require krb5 " > - "authentication!"); > +#ifndef CONFIG_KEYS > + /* Muliuser mounts require CONFIG_KEYS support */ > + if (vol->multiuser) { > + cERROR(1, "Multiuser mounts require kernels with " > + "CONFIG_KEYS enabled."); > goto cifs_parse_mount_err; > } > +#endif Will this break existing setups when those users update kernel and/or update cifs module? > > if (vol->UNCip == NULL) > vol->UNCip = &vol->UNC[2]; > @@ -2061,6 +2065,133 @@ cifs_put_smb_ses(struct cifs_ses *ses) > cifs_put_tcp_session(server); > } > > +#ifdef CONFIG_KEYS > + > +/* strlen("cifs:a:") + INET6_ADDRSTRLEN + 1 */ > +#define CIFSCREDS_DESC_SIZE (7 + INET6_ADDRSTRLEN + 1) > + > +/* Populate username and pw fields from keyring if possible */ > +static int > +cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) > +{ > + int rc = 0; > + char *desc, *delim, *payload; > + ssize_t len; > + struct key *key; > + struct TCP_Server_Info *server = ses->server; > + struct sockaddr_in *sa; > + struct sockaddr_in6 *sa6; > + struct user_key_payload *upayload; > + > + desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); > + if (!desc) > + return -ENOMEM; > + > + /* try to find an address key first */ > + switch (server->dstaddr.ss_family) { > + case AF_INET: > + sa = (struct sockaddr_in *)&server->dstaddr; > + sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr); > + break; > + case AF_INET6: > + sa6 = (struct sockaddr_in6 *)&server->dstaddr; > + sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr); > + break; > + default: > + cFYI(1, "Bad ss_family (%hu)", server->dstaddr.ss_family); > + rc = -EINVAL; > + goto out_err; > + } > + > + cFYI(1, "%s: desc=%s", __func__, desc); > + key = request_key(&key_type_secret, desc, ""); > + if (IS_ERR(key)) { > + if (!ses->domainName) { > + cFYI(1, "domainName is NULL"); > + rc = PTR_ERR(key); > + goto out_err; > + } > + > + /* didn't work, try to find a domain key */ > + sprintf(desc, "cifs:d:%s", ses->domainName); > + cFYI(1, "%s: desc=%s", __func__, desc); > + key = request_key(&key_type_secret, desc, ""); > + if (IS_ERR(key)) { > + rc = PTR_ERR(key); > + goto out_err; > + } > + } > + > + down_read(&key->sem); > + upayload = key->payload.data; > + if (IS_ERR_OR_NULL(upayload)) { > + rc = PTR_ERR(key); > + goto out_key_put; > + } > + > + /* find first : in payload */ > + payload = (char *)upayload->data; > + delim = strnchr(payload, upayload->datalen, ':'); > + cFYI(1, "payload=%s", payload); > + if (!delim) { > + cFYI(1, "Unable to find ':' in payload (datalen=%d)", > + upayload->datalen); > + rc = -EINVAL; > + goto out_key_put; > + } > + > + len = delim - payload; > + if (len > MAX_USERNAME_SIZE || len <= 0) { > + cFYI(1, "Bad value from username search (len=%ld)", len); > + rc = -EINVAL; > + goto out_key_put; > + } > + > + vol->username = kstrndup(payload, len, GFP_KERNEL); > + if (!vol->username) { > + cFYI(1, "Unable to allocate %ld bytes for username", len); > + rc = -ENOMEM; > + goto out_key_put; > + } > + cFYI(1, "username=%s", vol->username); > + > + len = key->datalen - (len + 1); > + if (len > MAX_PASSWORD_SIZE || len <= 0) { > + cFYI(1, "Bad len for password search (len=%ld)", len); > + rc = -EINVAL; > + kfree(vol->username); > + vol->username = NULL; > + goto out_key_put; > + } > + > + ++delim; > + vol->password = kstrndup(delim, len, GFP_KERNEL); > + if (!vol->password) { > + cFYI(1, "Unable to allocate %ld bytes for password", len); > + rc = -ENOMEM; > + kfree(vol->username); > + vol->username = NULL; > + goto out_key_put; > + } > + cFYI(1, "password=%s", vol->password); I would be wary of printing the password. > + > +out_key_put: > + up_read(&key->sem); > + key_put(key); > +out_err: > + kfree(desc); > + cFYI(1, "%s: returning %d", __func__, rc); > + return rc; > +} > +#else /* ! CONFIG_KEYS */ > +static inline int > +cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)), > + struct cifs_ses *ses __attribute__((unused))) > +{ > + return -ENOSYS; > +} > +#endif /* CONFIG_KEYS */ > + > static bool warned_on_ntlm; /* globals init to false automatically */ > > static struct cifs_ses * > @@ -3678,16 +3809,38 @@ int cifs_setup_session(unsigned int xid, struct cifs_ses *ses, > return rc; > } > > +static int > +cifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses) > +{ > + switch(ses->server->secType) { > + case Kerberos: > + vol->secFlg = CIFSSEC_MUST_KRB5; > + return 0; > + case NTLMv2: > + vol->secFlg = CIFSSEC_MUST_NTLMV2; > + break; > + case NTLM: > + vol->secFlg = CIFSSEC_MUST_NTLM; > + break; > + case RawNTLMSSP: > + vol->secFlg = CIFSSEC_MAY_NTLMSSP; > + break; > + case LANMAN: > + vol->secFlg = CIFSSEC_MAY_LANMAN; > + break; > + } > + > + return cifs_set_cifscreds(vol, ses); > +} > + > static struct cifs_tcon * > cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) > { > + int rc; > struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb); > struct cifs_ses *ses; > struct cifs_tcon *tcon = NULL; > struct smb_vol *vol_info; > - char username[28]; /* big enough for "krb50x" + hex of ULONG_MAX 6+16 */ > - /* We used to have this as MAX_USERNAME which is */ > - /* way too big now (256 instead of 32) */ > > vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL); > if (vol_info == NULL) { > @@ -3695,8 +3848,6 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) > goto out; > } > > - snprintf(username, sizeof(username), "krb50x%x", fsuid); > - vol_info->username = username; > vol_info->local_nls = cifs_sb->local_nls; > vol_info->linux_uid = fsuid; > vol_info->cred_uid = fsuid; > @@ -3706,8 +3857,11 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) > vol_info->local_lease = master_tcon->local_lease; > vol_info->no_linux_ext = !master_tcon->unix_ext; > > - /* FIXME: allow for other secFlg settings */ > - vol_info->secFlg = CIFSSEC_MUST_KRB5; > + rc = cifs_set_vol_auth(vol_info, master_tcon->ses); > + if (rc) { > + tcon = ERR_PTR(rc); > + goto out; > + } > > /* get a reference for the same TCP session */ > spin_lock(&cifs_tcp_ses_lock); > @@ -3730,6 +3884,8 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid) > if (ses->capabilities & CAP_UNIX) > reset_cifs_unix_caps(0, tcon, NULL, vol_info); > out: > + kfree(vol_info->username); > + kfree(vol_info->password); > kfree(vol_info); > > return tcon; > -- > 1.7.7.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-cifs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html