Loading fs/cifs/cifs_debug.c +10 −1 Original line number Diff line number Diff line Loading @@ -676,14 +676,23 @@ static ssize_t cifs_multiuser_mount_proc_write(struct file *file, { char c; int rc; static bool warned; rc = get_user(c, buffer); if (rc) return rc; if (c == '0' || c == 'n' || c == 'N') multiuser_mount = 0; else if (c == '1' || c == 'y' || c == 'Y') else if (c == '1' || c == 'y' || c == 'Y') { multiuser_mount = 1; if (!warned) { warned = true; printk(KERN_WARNING "CIFS VFS: The legacy multiuser " "mount code is scheduled to be deprecated in " "3.5. Please switch to using the multiuser " "mount option."); } } return count; } Loading fs/cifs/cifs_spnego.c +7 −3 Original line number Diff line number Diff line Loading @@ -113,9 +113,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) MAX_MECH_STR_LEN + UID_KEY_LEN + (sizeof(uid_t) * 2) + CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + USER_KEY_LEN + strlen(sesInfo->user_name) + PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; if (sesInfo->user_name) desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); spnego_key = ERR_PTR(-ENOMEM); description = kzalloc(desc_len, GFP_KERNEL); if (description == NULL) Loading Loading @@ -152,8 +154,10 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) dp = description + strlen(description); sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); if (sesInfo->user_name) { dp = description + strlen(description); sprintf(dp, ";user=%s", sesInfo->user_name); } dp = description + strlen(description); sprintf(dp, ";pid=0x%x", current->pid); Loading fs/cifs/cifsencrypt.c +8 −3 Original line number Diff line number Diff line Loading @@ -420,15 +420,20 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, } /* convert ses->user_name to unicode and uppercase */ len = strlen(ses->user_name); len = ses->user_name ? strlen(ses->user_name) : 0; user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) { cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n"); rc = -ENOMEM; return rc; } if (len) { len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp); UniStrupr(user); } else { memset(user, '\0', 2); } rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)user, 2 * len); Loading fs/cifs/connect.c +246 −55 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -225,76 +226,92 @@ static int check2ndT2(struct smb_hdr *pSMB) static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) { struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; char *data_area_of_target; char *data_area_of_buf2; char *data_area_of_tgt; char *data_area_of_src; int remaining; unsigned int byte_count, total_in_buf; __u16 total_data_size, total_in_buf2; unsigned int byte_count, total_in_tgt; __u16 tgt_total_cnt, src_total_cnt, total_in_src; total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); if (total_data_size != get_unaligned_le16(&pSMB2->t2_rsp.TotalDataCount)) cFYI(1, "total data size of primary and secondary t2 differ"); if (tgt_total_cnt != src_total_cnt) cFYI(1, "total data count of primary and secondary t2 differ " "source=%hu target=%hu", src_total_cnt, tgt_total_cnt); total_in_buf = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); remaining = total_data_size - total_in_buf; remaining = tgt_total_cnt - total_in_tgt; if (remaining < 0) if (remaining < 0) { cFYI(1, "Server sent too much data. tgt_total_cnt=%hu " "total_in_tgt=%hu", tgt_total_cnt, total_in_tgt); return -EPROTO; } if (remaining == 0) /* nothing to do, ignore */ if (remaining == 0) { /* nothing to do, ignore */ cFYI(1, "no more data remains"); return 0; } total_in_buf2 = get_unaligned_le16(&pSMB2->t2_rsp.DataCount); if (remaining < total_in_buf2) { total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); if (remaining < total_in_src) cFYI(1, "transact2 2nd response contains too much data"); } /* find end of first SMB data area */ data_area_of_target = (char *)&pSMBt->hdr.Protocol + data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); /* validate target area */ data_area_of_buf2 = (char *)&pSMB2->hdr.Protocol + get_unaligned_le16(&pSMB2->t2_rsp.DataOffset); /* validate target area */ data_area_of_src = (char *)&pSMBs->hdr.Protocol + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); data_area_of_target += total_in_buf; data_area_of_tgt += total_in_tgt; /* copy second buffer into end of first buffer */ total_in_buf += total_in_buf2; total_in_tgt += total_in_src; /* is the result too big for the field? */ if (total_in_buf > USHRT_MAX) if (total_in_tgt > USHRT_MAX) { cFYI(1, "coalesced DataCount too large (%u)", total_in_tgt); return -EPROTO; put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount); } put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); /* fix up the BCC */ byte_count = get_bcc(pTargetSMB); byte_count += total_in_buf2; byte_count += total_in_src; /* is the result too big for the field? */ if (byte_count > USHRT_MAX) if (byte_count > USHRT_MAX) { cFYI(1, "coalesced BCC too large (%u)", byte_count); return -EPROTO; } put_bcc(byte_count, pTargetSMB); byte_count = be32_to_cpu(pTargetSMB->smb_buf_length); byte_count += total_in_buf2; byte_count += total_in_src; /* don't allow buffer to overflow */ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); return -ENOBUFS; } pTargetSMB->smb_buf_length = cpu_to_be32(byte_count); memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); /* copy second buffer into end of first buffer */ memcpy(data_area_of_tgt, data_area_of_src, total_in_src); if (remaining == total_in_buf2) { cFYI(1, "found the last secondary response"); return 0; /* we are done */ } else /* more responses to go */ if (remaining != total_in_src) { /* more responses to go */ cFYI(1, "waiting for more secondary responses"); return 1; } /* we are done */ cFYI(1, "found the last secondary response"); return 0; } static void cifs_echo_request(struct work_struct *work) { Loading Loading @@ -1578,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]; Loading Loading @@ -1981,10 +2001,16 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) return 0; break; default: /* anything else takes username/password */ if (ses->user_name == NULL) /* NULL username means anonymous session */ if (ses->user_name == NULL) { if (!vol->nullauth) return 0; if (strncmp(ses->user_name, vol->username, break; } /* anything else takes username/password */ if (strncmp(ses->user_name, vol->username ? vol->username : "", MAX_USERNAME_SIZE)) return 0; if (strlen(vol->username) != 0 && Loading Loading @@ -2039,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 * Loading Loading @@ -2914,18 +3066,33 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, #define CIFS_DEFAULT_IOSIZE (1024 * 1024) /* * Windows only supports a max of 60k reads. Default to that when posix * extensions aren't in force. * Windows only supports a max of 60kb reads and 65535 byte writes. Default to * those values when posix extensions aren't in force. In actuality here, we * use 65536 to allow for a write that is a multiple of 4k. Most servers seem * to be ok with the extra byte even though Windows doesn't send writes that * are that large. * * Citation: * * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx */ #define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) #define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) static unsigned int cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) { __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); struct TCP_Server_Info *server = tcon->ses->server; unsigned int wsize = pvolume_info->wsize ? pvolume_info->wsize : CIFS_DEFAULT_IOSIZE; unsigned int wsize; /* start with specified wsize, or default */ if (pvolume_info->wsize) wsize = pvolume_info->wsize; else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) wsize = CIFS_DEFAULT_IOSIZE; else wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; /* can server support 24-bit write sizes? (via UNIX extensions) */ if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) Loading Loading @@ -3136,10 +3303,9 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, return -EINVAL; if (volume_info->nullauth) { cFYI(1, "null user"); volume_info->username = kzalloc(1, GFP_KERNEL); if (volume_info->username == NULL) return -ENOMEM; cFYI(1, "Anonymous login"); kfree(volume_info->username); volume_info->username = NULL; } else if (volume_info->username) { /* BB fixme parse for domain name here */ cFYI(1, "Username: %s", volume_info->username); Loading Loading @@ -3657,16 +3823,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_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) { Loading @@ -3674,8 +3862,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; Loading @@ -3685,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); Loading @@ -3709,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; Loading include/keys/user-type.h +2 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ /*****************************************************************************/ /* * the payload for a key of type "user" * the payload for a key of type "user" or "logon" * - once filled in and attached to a key: * - the payload struct is invariant may not be changed, only replaced * - the payload must be read with RCU procedures or with the key semaphore Loading @@ -33,6 +33,7 @@ struct user_key_payload { }; extern struct key_type key_type_user; extern struct key_type key_type_logon; extern int user_instantiate(struct key *key, const void *data, size_t datalen); extern int user_update(struct key *key, const void *data, size_t datalen); Loading Loading
fs/cifs/cifs_debug.c +10 −1 Original line number Diff line number Diff line Loading @@ -676,14 +676,23 @@ static ssize_t cifs_multiuser_mount_proc_write(struct file *file, { char c; int rc; static bool warned; rc = get_user(c, buffer); if (rc) return rc; if (c == '0' || c == 'n' || c == 'N') multiuser_mount = 0; else if (c == '1' || c == 'y' || c == 'Y') else if (c == '1' || c == 'y' || c == 'Y') { multiuser_mount = 1; if (!warned) { warned = true; printk(KERN_WARNING "CIFS VFS: The legacy multiuser " "mount code is scheduled to be deprecated in " "3.5. Please switch to using the multiuser " "mount option."); } } return count; } Loading
fs/cifs/cifs_spnego.c +7 −3 Original line number Diff line number Diff line Loading @@ -113,9 +113,11 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) MAX_MECH_STR_LEN + UID_KEY_LEN + (sizeof(uid_t) * 2) + CREDUID_KEY_LEN + (sizeof(uid_t) * 2) + USER_KEY_LEN + strlen(sesInfo->user_name) + PID_KEY_LEN + (sizeof(pid_t) * 2) + 1; if (sesInfo->user_name) desc_len += USER_KEY_LEN + strlen(sesInfo->user_name); spnego_key = ERR_PTR(-ENOMEM); description = kzalloc(desc_len, GFP_KERNEL); if (description == NULL) Loading Loading @@ -152,8 +154,10 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) dp = description + strlen(description); sprintf(dp, ";creduid=0x%x", sesInfo->cred_uid); if (sesInfo->user_name) { dp = description + strlen(description); sprintf(dp, ";user=%s", sesInfo->user_name); } dp = description + strlen(description); sprintf(dp, ";pid=0x%x", current->pid); Loading
fs/cifs/cifsencrypt.c +8 −3 Original line number Diff line number Diff line Loading @@ -420,15 +420,20 @@ static int calc_ntlmv2_hash(struct cifs_ses *ses, char *ntlmv2_hash, } /* convert ses->user_name to unicode and uppercase */ len = strlen(ses->user_name); len = ses->user_name ? strlen(ses->user_name) : 0; user = kmalloc(2 + (len * 2), GFP_KERNEL); if (user == NULL) { cERROR(1, "calc_ntlmv2_hash: user mem alloc failure\n"); rc = -ENOMEM; return rc; } if (len) { len = cifs_strtoUCS((__le16 *)user, ses->user_name, len, nls_cp); UniStrupr(user); } else { memset(user, '\0', 2); } rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, (char *)user, 2 * len); Loading
fs/cifs/connect.c +246 −55 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -225,76 +226,92 @@ static int check2ndT2(struct smb_hdr *pSMB) static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB) { struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBs = (struct smb_t2_rsp *)psecond; struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; char *data_area_of_target; char *data_area_of_buf2; char *data_area_of_tgt; char *data_area_of_src; int remaining; unsigned int byte_count, total_in_buf; __u16 total_data_size, total_in_buf2; unsigned int byte_count, total_in_tgt; __u16 tgt_total_cnt, src_total_cnt, total_in_src; total_data_size = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); src_total_cnt = get_unaligned_le16(&pSMBs->t2_rsp.TotalDataCount); tgt_total_cnt = get_unaligned_le16(&pSMBt->t2_rsp.TotalDataCount); if (total_data_size != get_unaligned_le16(&pSMB2->t2_rsp.TotalDataCount)) cFYI(1, "total data size of primary and secondary t2 differ"); if (tgt_total_cnt != src_total_cnt) cFYI(1, "total data count of primary and secondary t2 differ " "source=%hu target=%hu", src_total_cnt, tgt_total_cnt); total_in_buf = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); total_in_tgt = get_unaligned_le16(&pSMBt->t2_rsp.DataCount); remaining = total_data_size - total_in_buf; remaining = tgt_total_cnt - total_in_tgt; if (remaining < 0) if (remaining < 0) { cFYI(1, "Server sent too much data. tgt_total_cnt=%hu " "total_in_tgt=%hu", tgt_total_cnt, total_in_tgt); return -EPROTO; } if (remaining == 0) /* nothing to do, ignore */ if (remaining == 0) { /* nothing to do, ignore */ cFYI(1, "no more data remains"); return 0; } total_in_buf2 = get_unaligned_le16(&pSMB2->t2_rsp.DataCount); if (remaining < total_in_buf2) { total_in_src = get_unaligned_le16(&pSMBs->t2_rsp.DataCount); if (remaining < total_in_src) cFYI(1, "transact2 2nd response contains too much data"); } /* find end of first SMB data area */ data_area_of_target = (char *)&pSMBt->hdr.Protocol + data_area_of_tgt = (char *)&pSMBt->hdr.Protocol + get_unaligned_le16(&pSMBt->t2_rsp.DataOffset); /* validate target area */ data_area_of_buf2 = (char *)&pSMB2->hdr.Protocol + get_unaligned_le16(&pSMB2->t2_rsp.DataOffset); /* validate target area */ data_area_of_src = (char *)&pSMBs->hdr.Protocol + get_unaligned_le16(&pSMBs->t2_rsp.DataOffset); data_area_of_target += total_in_buf; data_area_of_tgt += total_in_tgt; /* copy second buffer into end of first buffer */ total_in_buf += total_in_buf2; total_in_tgt += total_in_src; /* is the result too big for the field? */ if (total_in_buf > USHRT_MAX) if (total_in_tgt > USHRT_MAX) { cFYI(1, "coalesced DataCount too large (%u)", total_in_tgt); return -EPROTO; put_unaligned_le16(total_in_buf, &pSMBt->t2_rsp.DataCount); } put_unaligned_le16(total_in_tgt, &pSMBt->t2_rsp.DataCount); /* fix up the BCC */ byte_count = get_bcc(pTargetSMB); byte_count += total_in_buf2; byte_count += total_in_src; /* is the result too big for the field? */ if (byte_count > USHRT_MAX) if (byte_count > USHRT_MAX) { cFYI(1, "coalesced BCC too large (%u)", byte_count); return -EPROTO; } put_bcc(byte_count, pTargetSMB); byte_count = be32_to_cpu(pTargetSMB->smb_buf_length); byte_count += total_in_buf2; byte_count += total_in_src; /* don't allow buffer to overflow */ if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) if (byte_count > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { cFYI(1, "coalesced BCC exceeds buffer size (%u)", byte_count); return -ENOBUFS; } pTargetSMB->smb_buf_length = cpu_to_be32(byte_count); memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2); /* copy second buffer into end of first buffer */ memcpy(data_area_of_tgt, data_area_of_src, total_in_src); if (remaining == total_in_buf2) { cFYI(1, "found the last secondary response"); return 0; /* we are done */ } else /* more responses to go */ if (remaining != total_in_src) { /* more responses to go */ cFYI(1, "waiting for more secondary responses"); return 1; } /* we are done */ cFYI(1, "found the last secondary response"); return 0; } static void cifs_echo_request(struct work_struct *work) { Loading Loading @@ -1578,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]; Loading Loading @@ -1981,10 +2001,16 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol) return 0; break; default: /* anything else takes username/password */ if (ses->user_name == NULL) /* NULL username means anonymous session */ if (ses->user_name == NULL) { if (!vol->nullauth) return 0; if (strncmp(ses->user_name, vol->username, break; } /* anything else takes username/password */ if (strncmp(ses->user_name, vol->username ? vol->username : "", MAX_USERNAME_SIZE)) return 0; if (strlen(vol->username) != 0 && Loading Loading @@ -2039,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 * Loading Loading @@ -2914,18 +3066,33 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, #define CIFS_DEFAULT_IOSIZE (1024 * 1024) /* * Windows only supports a max of 60k reads. Default to that when posix * extensions aren't in force. * Windows only supports a max of 60kb reads and 65535 byte writes. Default to * those values when posix extensions aren't in force. In actuality here, we * use 65536 to allow for a write that is a multiple of 4k. Most servers seem * to be ok with the extra byte even though Windows doesn't send writes that * are that large. * * Citation: * * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx */ #define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024) #define CIFS_DEFAULT_NON_POSIX_WSIZE (65536) static unsigned int cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info) { __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability); struct TCP_Server_Info *server = tcon->ses->server; unsigned int wsize = pvolume_info->wsize ? pvolume_info->wsize : CIFS_DEFAULT_IOSIZE; unsigned int wsize; /* start with specified wsize, or default */ if (pvolume_info->wsize) wsize = pvolume_info->wsize; else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) wsize = CIFS_DEFAULT_IOSIZE; else wsize = CIFS_DEFAULT_NON_POSIX_WSIZE; /* can server support 24-bit write sizes? (via UNIX extensions) */ if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP)) Loading Loading @@ -3136,10 +3303,9 @@ cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, return -EINVAL; if (volume_info->nullauth) { cFYI(1, "null user"); volume_info->username = kzalloc(1, GFP_KERNEL); if (volume_info->username == NULL) return -ENOMEM; cFYI(1, "Anonymous login"); kfree(volume_info->username); volume_info->username = NULL; } else if (volume_info->username) { /* BB fixme parse for domain name here */ cFYI(1, "Username: %s", volume_info->username); Loading Loading @@ -3657,16 +3823,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_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) { Loading @@ -3674,8 +3862,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; Loading @@ -3685,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); Loading @@ -3709,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; Loading
include/keys/user-type.h +2 −1 Original line number Diff line number Diff line Loading @@ -17,7 +17,7 @@ /*****************************************************************************/ /* * the payload for a key of type "user" * the payload for a key of type "user" or "logon" * - once filled in and attached to a key: * - the payload struct is invariant may not be changed, only replaced * - the payload must be read with RCU procedures or with the key semaphore Loading @@ -33,6 +33,7 @@ struct user_key_payload { }; extern struct key_type key_type_user; extern struct key_type key_type_logon; extern int user_instantiate(struct key *key, const void *data, size_t datalen); extern int user_update(struct key *key, const void *data, size_t datalen); Loading