Skip to content

Commit

Permalink
cifs: fetch credentials out of keyring for non-krb5 auth multiuser mo…
Browse files Browse the repository at this point in the history
…unts

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>
  • Loading branch information
Jeff Layton authored and Steve French committed Jan 18, 2012
1 parent 04febab commit 8a8798a
Showing 1 changed file with 165 additions and 10 deletions.
175 changes: 165 additions & 10 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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

if (vol->UNCip == NULL)
vol->UNCip = &vol->UNC[2];
Expand Down Expand Up @@ -2061,6 +2065,132 @@ 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_logon, 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_logon, 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, "%s: username=%s", __func__, 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;
}

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 *
Expand Down Expand Up @@ -3693,25 +3823,45 @@ 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_MUST_NTLMSSP;
break;
case LANMAN:
vol->secFlg = CIFSSEC_MUST_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) {
tcon = ERR_PTR(-ENOMEM);
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;
Expand All @@ -3721,8 +3871,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);
Expand All @@ -3745,6 +3898,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;
Expand Down

0 comments on commit 8a8798a

Please sign in to comment.