diff options
Diffstat (limited to 'fs/cifs/connect.c')
| -rw-r--r-- | fs/cifs/connect.c | 180 | 
1 files changed, 130 insertions, 50 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 2208f06e4c4..2a43a0aca96 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -48,6 +48,7 @@  #include "nterr.h"  #include "rfc1002pdu.h"  #include "cn_cifs.h" +#include "fscache.h"  #define CIFS_PORT 445  #define RFC1001_PORT 139 @@ -66,6 +67,7 @@ struct smb_vol {  	char *iocharset;  /* local code page for mapping to and from Unicode */  	char source_rfc1001_name[16]; /* netbios name of client */  	char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */ +	uid_t cred_uid;  	uid_t linux_uid;  	gid_t linux_gid;  	mode_t file_mode; @@ -97,6 +99,7 @@ struct smb_vol {  	bool noblocksnd:1;  	bool noautotune:1;  	bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ +	bool fsc:1;	/* enable fscache */  	unsigned int rsize;  	unsigned int wsize;  	bool sockopt_tcp_nodelay:1; @@ -830,7 +833,8 @@ cifs_parse_mount_options(char *options, const char *devname,  	/* null target name indicates to use *SMBSERVR default called name  	   if we end up sending RFC1001 session initialize */  	vol->target_rfc1001_name[0] = 0; -	vol->linux_uid = current_uid();  /* use current_euid() instead? */ +	vol->cred_uid = current_uid(); +	vol->linux_uid = current_uid();  	vol->linux_gid = current_gid();  	/* default to only allowing write access to owner of the mount */ @@ -1257,6 +1261,12 @@ cifs_parse_mount_options(char *options, const char *devname,  		} else if ((strnicmp(data, "nocase", 6) == 0) ||  			   (strnicmp(data, "ignorecase", 10)  == 0)) {  			vol->nocase = 1; +		} else if (strnicmp(data, "mand", 4) == 0) { +			/* ignore */ +		} else if (strnicmp(data, "nomand", 6) == 0) { +			/* ignore */ +		} else if (strnicmp(data, "_netdev", 7) == 0) { +			/* ignore */  		} else if (strnicmp(data, "brl", 3) == 0) {  			vol->nobrl =  0;  		} else if ((strnicmp(data, "nobrl", 5) == 0) || @@ -1331,6 +1341,8 @@ cifs_parse_mount_options(char *options, const char *devname,  			printk(KERN_WARNING "CIFS: Mount option noac not "  				"supported. Instead set "  				"/proc/fs/cifs/LookupCacheEnabled to 0\n"); +		} else if (strnicmp(data, "fsc", 3) == 0) { +			vol->fsc = true;  		} else  			printk(KERN_WARNING "CIFS: Unknown mount option %s\n",  						data); @@ -1380,18 +1392,92 @@ cifs_parse_mount_options(char *options, const char *devname,  	return 0;  } +static bool +match_address(struct TCP_Server_Info *server, struct sockaddr *addr) +{ +	struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; +	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; + +	switch (addr->sa_family) { +	case AF_INET: +		if (addr4->sin_addr.s_addr != +		    server->addr.sockAddr.sin_addr.s_addr) +			return false; +		if (addr4->sin_port && +		    addr4->sin_port != server->addr.sockAddr.sin_port) +			return false; +		break; +	case AF_INET6: +		if (!ipv6_addr_equal(&addr6->sin6_addr, +				     &server->addr.sockAddr6.sin6_addr)) +			return false; +		if (addr6->sin6_scope_id != +		    server->addr.sockAddr6.sin6_scope_id) +			return false; +		if (addr6->sin6_port && +		    addr6->sin6_port != server->addr.sockAddr6.sin6_port) +			return false; +		break; +	} + +	return true; +} + +static bool +match_security(struct TCP_Server_Info *server, struct smb_vol *vol) +{ +	unsigned int secFlags; + +	if (vol->secFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL))) +		secFlags = vol->secFlg; +	else +		secFlags = global_secflags | vol->secFlg; + +	switch (server->secType) { +	case LANMAN: +		if (!(secFlags & (CIFSSEC_MAY_LANMAN|CIFSSEC_MAY_PLNTXT))) +			return false; +		break; +	case NTLMv2: +		if (!(secFlags & CIFSSEC_MAY_NTLMV2)) +			return false; +		break; +	case NTLM: +		if (!(secFlags & CIFSSEC_MAY_NTLM)) +			return false; +		break; +	case Kerberos: +		if (!(secFlags & CIFSSEC_MAY_KRB5)) +			return false; +		break; +	case RawNTLMSSP: +		if (!(secFlags & CIFSSEC_MAY_NTLMSSP)) +			return false; +		break; +	default: +		/* shouldn't happen */ +		return false; +	} + +	/* now check if signing mode is acceptible */ +	if ((secFlags & CIFSSEC_MAY_SIGN) == 0 && +	    (server->secMode & SECMODE_SIGN_REQUIRED)) +			return false; +	else if (((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) && +		 (server->secMode & +		  (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED)) == 0) +			return false; + +	return true; +} +  static struct TCP_Server_Info * -cifs_find_tcp_session(struct sockaddr_storage *addr, unsigned short int port) +cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)  { -	struct list_head *tmp;  	struct TCP_Server_Info *server; -	struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; -	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;  	write_lock(&cifs_tcp_ses_lock); -	list_for_each(tmp, &cifs_tcp_ses_list) { -		server = list_entry(tmp, struct TCP_Server_Info, -				    tcp_ses_list); +	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {  		/*  		 * the demux thread can exit on its own while still in CifsNew  		 * so don't accept any sockets in that state. Since the @@ -1401,37 +1487,11 @@ cifs_find_tcp_session(struct sockaddr_storage *addr, unsigned short int port)  		if (server->tcpStatus == CifsNew)  			continue; -		switch (addr->ss_family) { -		case AF_INET: -			if (addr4->sin_addr.s_addr == -			    server->addr.sockAddr.sin_addr.s_addr) { -				addr4->sin_port = htons(port); -				/* user overrode default port? */ -				if (addr4->sin_port) { -					if (addr4->sin_port != -					    server->addr.sockAddr.sin_port) -						continue; -				} -				break; -			} else -				continue; +		if (!match_address(server, addr)) +			continue; -		case AF_INET6: -			if (ipv6_addr_equal(&addr6->sin6_addr, -			    &server->addr.sockAddr6.sin6_addr) && -			    (addr6->sin6_scope_id == -			    server->addr.sockAddr6.sin6_scope_id)) { -				addr6->sin6_port = htons(port); -				/* user overrode default port? */ -				if (addr6->sin6_port) { -					if (addr6->sin6_port != -					   server->addr.sockAddr6.sin6_port) -						continue; -				} -				break; -			} else -				continue; -		} +		if (!match_security(server, vol)) +			continue;  		++server->srv_count;  		write_unlock(&cifs_tcp_ses_lock); @@ -1460,6 +1520,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)  	server->tcpStatus = CifsExiting;  	spin_unlock(&GlobalMid_Lock); +	cifs_fscache_release_client_cookie(server); +  	task = xchg(&server->tsk, NULL);  	if (task)  		force_sig(SIGKILL, task); @@ -1479,7 +1541,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	cFYI(1, "UNC: %s ip: %s", volume_info->UNC, volume_info->UNCip);  	if (volume_info->UNCip && volume_info->UNC) { -		rc = cifs_convert_address(volume_info->UNCip, &addr); +		rc = cifs_fill_sockaddr((struct sockaddr *)&addr, +					volume_info->UNCip, +					volume_info->port);  		if (!rc) {  			/* we failed translating address */  			rc = -EINVAL; @@ -1499,7 +1563,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	}  	/* see if we already have a matching tcp_ses */ -	tcp_ses = cifs_find_tcp_session(&addr, volume_info->port); +	tcp_ses = cifs_find_tcp_session((struct sockaddr *)&addr, volume_info);  	if (tcp_ses)  		return tcp_ses; @@ -1543,12 +1607,10 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  		cFYI(1, "attempting ipv6 connect");  		/* BB should we allow ipv6 on port 139? */  		/* other OS never observed in Wild doing 139 with v6 */ -		sin_server6->sin6_port = htons(volume_info->port);  		memcpy(&tcp_ses->addr.sockAddr6, sin_server6,  			sizeof(struct sockaddr_in6));  		rc = ipv6_connect(tcp_ses);  	} else { -		sin_server->sin_port = htons(volume_info->port);  		memcpy(&tcp_ses->addr.sockAddr, sin_server,  			sizeof(struct sockaddr_in));  		rc = ipv4_connect(tcp_ses); @@ -1577,6 +1639,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);  	write_unlock(&cifs_tcp_ses_lock); +	cifs_fscache_get_client_cookie(tcp_ses); +  	return tcp_ses;  out_err: @@ -1591,17 +1655,27 @@ out_err:  }  static struct cifsSesInfo * -cifs_find_smb_ses(struct TCP_Server_Info *server, char *username) +cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)  { -	struct list_head *tmp;  	struct cifsSesInfo *ses;  	write_lock(&cifs_tcp_ses_lock); -	list_for_each(tmp, &server->smb_ses_list) { -		ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); -		if (strncmp(ses->userName, username, MAX_USERNAME_SIZE)) -			continue; - +	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { +		switch (server->secType) { +		case Kerberos: +			if (vol->cred_uid != ses->cred_uid) +				continue; +			break; +		default: +			/* anything else takes username/password */ +			if (strncmp(ses->userName, vol->username, +				    MAX_USERNAME_SIZE)) +				continue; +			if (strlen(vol->username) != 0 && +			    strncmp(ses->password, vol->password, +				    MAX_PASSWORD_SIZE)) +				continue; +		}  		++ses->ses_count;  		write_unlock(&cifs_tcp_ses_lock);  		return ses; @@ -1643,7 +1717,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  	xid = GetXid(); -	ses = cifs_find_smb_ses(server, volume_info->username); +	ses = cifs_find_smb_ses(server, volume_info);  	if (ses) {  		cFYI(1, "Existing smb sess found (status=%d)", ses->status); @@ -1706,6 +1780,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)  		if (ses->domainName)  			strcpy(ses->domainName, volume_info->domainname);  	} +	ses->cred_uid = volume_info->cred_uid;  	ses->linux_uid = volume_info->linux_uid;  	ses->overrideSecFlg = volume_info->secFlg; @@ -1773,6 +1848,7 @@ cifs_put_tcon(struct cifsTconInfo *tcon)  	CIFSSMBTDis(xid, tcon);  	_FreeXid(xid); +	cifs_fscache_release_super_cookie(tcon);  	tconInfoFree(tcon);  	cifs_put_smb_ses(ses);  } @@ -1843,6 +1919,8 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info)  	list_add(&tcon->tcon_list, &ses->tcon_list);  	write_unlock(&cifs_tcp_ses_lock); +	cifs_fscache_get_super_cookie(tcon); +  	return tcon;  out_fail: @@ -2397,6 +2475,8 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;  	if (pvolume_info->dynperm)  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; +	if (pvolume_info->fsc) +		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE;  	if (pvolume_info->direct_io) {  		cFYI(1, "mounting share using direct i/o");  		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;  |