summaryrefslogtreecommitdiffstats
path: root/fs/cifs/connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r--fs/cifs/connect.c3064
1 files changed, 3064 insertions, 0 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
new file mode 100644
index 00000000000..40470b9d547
--- /dev/null
+++ b/fs/cifs/connect.c
@@ -0,0 +1,3064 @@
+/*
+ * fs/cifs/connect.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002,2004
+ * Author(s): Steve French (sfrench@us.ibm.com)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/net.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/ipv6.h>
+#include <linux/pagemap.h>
+#include <linux/ctype.h>
+#include <linux/utsname.h>
+#include <linux/mempool.h>
+#include <asm/uaccess.h>
+#include <asm/processor.h>
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_unicode.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "ntlmssp.h"
+#include "nterr.h"
+#include "rfc1002pdu.h"
+
+#define CIFS_PORT 445
+#define RFC1001_PORT 139
+
+extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
+ unsigned char *p24);
+extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
+ unsigned char *p24);
+
+extern mempool_t *cifs_req_poolp;
+
+struct smb_vol {
+ char *username;
+ char *password;
+ char *domainname;
+ char *UNC;
+ char *UNCip;
+ char *in6_addr; /* ipv6 address as human readable form of in6_addr */
+ char *iocharset; /* local code page for mapping to and from Unicode */
+ char source_rfc1001_name[16]; /* netbios name of client */
+ uid_t linux_uid;
+ gid_t linux_gid;
+ mode_t file_mode;
+ mode_t dir_mode;
+ unsigned rw:1;
+ unsigned retry:1;
+ unsigned intr:1;
+ unsigned setuids:1;
+ unsigned noperm:1;
+ unsigned no_psx_acl:1; /* set if posix acl support should be disabled */
+ unsigned no_xattr:1; /* set if xattr (EA) support should be disabled*/
+ unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
+ unsigned direct_io:1;
+ unsigned int rsize;
+ unsigned int wsize;
+ unsigned int sockopt;
+ unsigned short int port;
+};
+
+static int ipv4_connect(struct sockaddr_in *psin_server,
+ struct socket **csocket,
+ char * netb_name);
+static int ipv6_connect(struct sockaddr_in6 *psin_server,
+ struct socket **csocket);
+
+
+ /*
+ * cifs tcp session reconnection
+ *
+ * mark tcp session as reconnecting so temporarily locked
+ * mark all smb sessions as reconnecting for tcp session
+ * reconnect tcp session
+ * wake up waiters on reconnection? - (not needed currently)
+ */
+
+int
+cifs_reconnect(struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ struct list_head *tmp;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+ struct mid_q_entry * mid_entry;
+
+ spin_lock(&GlobalMid_Lock);
+ if(server->tcpStatus == CifsExiting) {
+ /* the demux thread will exit normally
+ next time through the loop */
+ spin_unlock(&GlobalMid_Lock);
+ return rc;
+ } else
+ server->tcpStatus = CifsNeedReconnect;
+ spin_unlock(&GlobalMid_Lock);
+ server->maxBuf = 0;
+
+ cFYI(1, ("Reconnecting tcp session "));
+
+ /* before reconnecting the tcp session, mark the smb session (uid)
+ and the tid bad so they are not used until reconnected */
+ read_lock(&GlobalSMBSeslock);
+ list_for_each(tmp, &GlobalSMBSessionList) {
+ ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
+ if (ses->server) {
+ if (ses->server == server) {
+ ses->status = CifsNeedReconnect;
+ ses->ipc_tid = 0;
+ }
+ }
+ /* else tcp and smb sessions need reconnection */
+ }
+ list_for_each(tmp, &GlobalTreeConnectionList) {
+ tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+ if((tcon) && (tcon->ses) && (tcon->ses->server == server)) {
+ tcon->tidStatus = CifsNeedReconnect;
+ }
+ }
+ read_unlock(&GlobalSMBSeslock);
+ /* do not want to be sending data on a socket we are freeing */
+ down(&server->tcpSem);
+ if(server->ssocket) {
+ cFYI(1,("State: 0x%x Flags: 0x%lx", server->ssocket->state,
+ server->ssocket->flags));
+ server->ssocket->ops->shutdown(server->ssocket,SEND_SHUTDOWN);
+ cFYI(1,("Post shutdown state: 0x%x Flags: 0x%lx", server->ssocket->state,
+ server->ssocket->flags));
+ sock_release(server->ssocket);
+ server->ssocket = NULL;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct
+ mid_q_entry,
+ qhead);
+ if(mid_entry) {
+ if(mid_entry->midState == MID_REQUEST_SUBMITTED) {
+ /* Mark other intransit requests as needing retry so
+ we do not immediately mark the session bad again
+ (ie after we reconnect below) as they timeout too */
+ mid_entry->midState = MID_RETRY_NEEDED;
+ }
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ up(&server->tcpSem);
+
+ while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood))
+ {
+ if(server->protocolType == IPV6) {
+ rc = ipv6_connect(&server->addr.sockAddr6,&server->ssocket);
+ } else {
+ rc = ipv4_connect(&server->addr.sockAddr,
+ &server->ssocket,
+ server->workstation_RFC1001_name);
+ }
+ if(rc) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(3 * HZ);
+ } else {
+ atomic_inc(&tcpSesReconnectCount);
+ spin_lock(&GlobalMid_Lock);
+ if(server->tcpStatus != CifsExiting)
+ server->tcpStatus = CifsGood;
+ spin_unlock(&GlobalMid_Lock);
+ /* atomic_set(&server->inFlight,0);*/
+ wake_up(&server->response_q);
+ }
+ }
+ return rc;
+}
+
+static int
+cifs_demultiplex_thread(struct TCP_Server_Info *server)
+{
+ int length;
+ unsigned int pdu_length, total_read;
+ struct smb_hdr *smb_buffer = NULL;
+ struct msghdr smb_msg;
+ struct kvec iov;
+ struct socket *csocket = server->ssocket;
+ struct list_head *tmp;
+ struct cifsSesInfo *ses;
+ struct task_struct *task_to_wake = NULL;
+ struct mid_q_entry *mid_entry;
+ char *temp;
+
+ daemonize("cifsd");
+ allow_signal(SIGKILL);
+ current->flags |= PF_MEMALLOC;
+ server->tsk = current; /* save process info to wake at shutdown */
+ cFYI(1, ("Demultiplex PID: %d", current->pid));
+ write_lock(&GlobalSMBSeslock);
+ atomic_inc(&tcpSesAllocCount);
+ length = tcpSesAllocCount.counter;
+ write_unlock(&GlobalSMBSeslock);
+ if(length > 1) {
+ mempool_resize(cifs_req_poolp,
+ length + cifs_min_rcv,
+ GFP_KERNEL);
+ }
+
+ while (server->tcpStatus != CifsExiting) {
+ if (smb_buffer == NULL)
+ smb_buffer = cifs_buf_get();
+ else
+ memset(smb_buffer, 0, sizeof (struct smb_hdr));
+
+ if (smb_buffer == NULL) {
+ cERROR(1,("Can not get memory for SMB response"));
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ * 3); /* give system time to free memory */
+ continue;
+ }
+ iov.iov_base = smb_buffer;
+ iov.iov_len = 4;
+ smb_msg.msg_control = NULL;
+ smb_msg.msg_controllen = 0;
+ length =
+ kernel_recvmsg(csocket, &smb_msg,
+ &iov, 1, 4, 0 /* BB see socket.h flags */);
+
+ if(server->tcpStatus == CifsExiting) {
+ break;
+ } else if (server->tcpStatus == CifsNeedReconnect) {
+ cFYI(1,("Reconnecting after server stopped responding"));
+ cifs_reconnect(server);
+ cFYI(1,("call to reconnect done"));
+ csocket = server->ssocket;
+ continue;
+ } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1); /* minimum sleep to prevent looping
+ allowing socket to clear and app threads to set
+ tcpStatus CifsNeedReconnect if server hung */
+ continue;
+ } else if (length <= 0) {
+ if(server->tcpStatus == CifsNew) {
+ cFYI(1,("tcp session abended prematurely (after SMBnegprot)"));
+ /* some servers kill tcp session rather than returning
+ smb negprot error in which case reconnecting here is
+ not going to help - return error to mount */
+ break;
+ }
+ if(length == -EINTR) {
+ cFYI(1,("cifsd thread killed"));
+ break;
+ }
+ cFYI(1,("Reconnecting after unexpected peek error %d",length));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ } else if (length > 3) {
+ pdu_length = ntohl(smb_buffer->smb_buf_length);
+ /* Only read pdu_length after below checks for too short (due
+ to e.g. int overflow) and too long ie beyond end of buf */
+ cFYI(1,("rfc1002 length(big endian)0x%x)", pdu_length+4));
+
+ temp = (char *) smb_buffer;
+ if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) {
+ cFYI(0,("Received 4 byte keep alive packet"));
+ } else if (temp[0] == (char) RFC1002_POSITIVE_SESSION_RESPONSE) {
+ cFYI(1,("Good RFC 1002 session rsp"));
+ } else if (temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
+ /* we get this from Windows 98 instead of error on SMB negprot response */
+ cFYI(1,("Negative RFC 1002 Session Response Error 0x%x)",temp[4]));
+ if(server->tcpStatus == CifsNew) {
+ /* if nack on negprot (rather than
+ ret of smb negprot error) reconnecting
+ not going to help, ret error to mount */
+ break;
+ } else {
+ /* give server a second to
+ clean up before reconnect attempt */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ);
+ /* always try 445 first on reconnect
+ since we get NACK on some if we ever
+ connected to port 139 (the NACK is
+ since we do not begin with RFC1001
+ session initialize frame) */
+ server->addr.sockAddr.sin_port = htons(CIFS_PORT);
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ }
+ } else if (temp[0] != (char) 0) {
+ cERROR(1,("Unknown RFC 1002 frame"));
+ cifs_dump_mem(" Received Data: ", temp, length);
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ continue;
+ } else {
+ if((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4)
+ || (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) {
+ cERROR(1,
+ ("Invalid size SMB length %d and pdu_length %d",
+ length, pdu_length+4));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ } else { /* length ok */
+ length = 0;
+ iov.iov_base = 4 + (char *)smb_buffer;
+ iov.iov_len = pdu_length;
+ for (total_read = 0;
+ total_read < pdu_length;
+ total_read += length) {
+ length = kernel_recvmsg(csocket, &smb_msg,
+ &iov, 1,
+ pdu_length - total_read, 0);
+ if (length == 0) {
+ cERROR(1,
+ ("Zero length receive when expecting %d ",
+ pdu_length - total_read));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ }
+ }
+ length += 4; /* account for rfc1002 hdr */
+ }
+
+ dump_smb(smb_buffer, length);
+ if (checkSMB
+ (smb_buffer, smb_buffer->Mid, total_read+4)) {
+ cERROR(1, ("Bad SMB Received "));
+ continue;
+ }
+
+ task_to_wake = NULL;
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct
+ mid_q_entry,
+ qhead);
+
+ if ((mid_entry->mid == smb_buffer->Mid) && (mid_entry->midState == MID_REQUEST_SUBMITTED)) {
+ cFYI(1,
+ (" Mid 0x%x matched - waking up ",mid_entry->mid));
+ task_to_wake = mid_entry->tsk;
+ mid_entry->resp_buf =
+ smb_buffer;
+ mid_entry->midState =
+ MID_RESPONSE_RECEIVED;
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ if (task_to_wake) {
+ smb_buffer = NULL; /* will be freed by users thread after he is done */
+ wake_up_process(task_to_wake);
+ } else if (is_valid_oplock_break(smb_buffer) == FALSE) {
+ cERROR(1, ("No task to wake, unknown frame rcvd!"));
+ cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr));
+ }
+ }
+ } else {
+ cFYI(1,
+ ("Frame less than four bytes received %d bytes long.",
+ length));
+ cifs_reconnect(server);
+ csocket = server->ssocket;
+ wake_up(&server->response_q);
+ continue;
+ }
+ }
+ spin_lock(&GlobalMid_Lock);
+ server->tcpStatus = CifsExiting;
+ server->tsk = NULL;
+ atomic_set(&server->inFlight, 0);
+ spin_unlock(&GlobalMid_Lock);
+ /* Although there should not be any requests blocked on
+ this queue it can not hurt to be paranoid and try to wake up requests
+ that may haven been blocked when more than 50 at time were on the wire
+ to the same server - they now will see the session is in exit state
+ and get out of SendReceive. */
+ wake_up_all(&server->request_q);
+ /* give those requests time to exit */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/8);
+
+ if(server->ssocket) {
+ sock_release(csocket);
+ server->ssocket = NULL;
+ }
+ if (smb_buffer) /* buffer usually freed in free_mid - need to free it on error or exit */
+ cifs_buf_release(smb_buffer);
+
+ read_lock(&GlobalSMBSeslock);
+ if (list_empty(&server->pending_mid_q)) {
+ /* loop through server session structures attached to this and mark them dead */
+ list_for_each(tmp, &GlobalSMBSessionList) {
+ ses =
+ list_entry(tmp, struct cifsSesInfo,
+ cifsSessionList);
+ if (ses->server == server) {
+ ses->status = CifsExiting;
+ ses->server = NULL;
+ }
+ }
+ read_unlock(&GlobalSMBSeslock);
+ } else {
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp, &server->pending_mid_q) {
+ mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
+ if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
+ cFYI(1,
+ (" Clearing Mid 0x%x - waking up ",mid_entry->mid));
+ task_to_wake = mid_entry->tsk;
+ if(task_to_wake) {
+ wake_up_process(task_to_wake);
+ }
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+ read_unlock(&GlobalSMBSeslock);
+ set_current_state(TASK_INTERRUPTIBLE);
+ /* 1/8th of sec is more than enough time for them to exit */
+ schedule_timeout(HZ/8);
+ }
+
+ if (list_empty(&server->pending_mid_q)) {
+ /* mpx threads have not exited yet give them
+ at least the smb send timeout time for long ops */
+ cFYI(1, ("Wait for exit from demultiplex thread"));
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(46 * HZ);
+ /* if threads still have not exited they are probably never
+ coming home not much else we can do but free the memory */
+ }
+ kfree(server);
+
+ write_lock(&GlobalSMBSeslock);
+ atomic_dec(&tcpSesAllocCount);
+ length = tcpSesAllocCount.counter;
+ write_unlock(&GlobalSMBSeslock);
+ if(length > 0) {
+ mempool_resize(cifs_req_poolp,
+ length + cifs_min_rcv,
+ GFP_KERNEL);
+ }
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ/4);
+ return 0;
+}
+
+static void *
+cifs_kcalloc(size_t size, unsigned int __nocast type)
+{
+ void *addr;
+ addr = kmalloc(size, type);
+ if (addr)
+ memset(addr, 0, size);
+ return addr;
+}
+
+static int
+cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol)
+{
+ char *value;
+ char *data;
+ unsigned int temp_len, i, j;
+ char separator[2];
+
+ separator[0] = ',';
+ separator[1] = 0;
+
+ memset(vol->source_rfc1001_name,0x20,15);
+ for(i=0;i < strnlen(system_utsname.nodename,15);i++) {
+ /* does not have to be a perfect mapping since the field is
+ informational, only used for servers that do not support
+ port 445 and it can be overridden at mount time */
+ vol->source_rfc1001_name[i] = toupper(system_utsname.nodename[i]);
+ }
+ vol->source_rfc1001_name[15] = 0;
+
+ vol->linux_uid = current->uid; /* current->euid instead? */
+ vol->linux_gid = current->gid;
+ vol->dir_mode = S_IRWXUGO;
+ /* 2767 perms indicate mandatory locking support */
+ vol->file_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP);
+
+ /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
+ vol->rw = TRUE;
+
+ if (!options)
+ return 1;
+
+ if(strncmp(options,"sep=",4) == 0) {
+ if(options[4] != 0) {
+ separator[0] = options[4];
+ options += 5;
+ } else {
+ cFYI(1,("Null separator not allowed"));
+ }
+ }
+
+ while ((data = strsep(&options, separator)) != NULL) {
+ if (!*data)
+ continue;
+ if ((value = strchr(data, '=')) != NULL)
+ *value++ = '\0';
+
+ if (strnicmp(data, "user_xattr",10) == 0) {/*parse before user*/
+ vol->no_xattr = 0;
+ } else if (strnicmp(data, "nouser_xattr",12) == 0) {
+ vol->no_xattr = 1;
+ } else if (strnicmp(data, "user", 4) == 0) {
+ if (!value || !*value) {
+ printk(KERN_WARNING
+ "CIFS: invalid or missing username\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 200) < 200) {
+ vol->username = value;
+ } else {
+ printk(KERN_WARNING "CIFS: username too long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "pass", 4) == 0) {
+ if (!value) {
+ vol->password = NULL;
+ continue;
+ } else if(value[0] == 0) {
+ /* check if string begins with double comma
+ since that would mean the password really
+ does start with a comma, and would not
+ indicate an empty string */
+ if(value[1] != separator[0]) {
+ vol->password = NULL;
+ continue;
+ }
+ }
+ temp_len = strlen(value);
+ /* removed password length check, NTLM passwords
+ can be arbitrarily long */
+
+ /* if comma in password, the string will be
+ prematurely null terminated. Commas in password are
+ specified across the cifs mount interface by a double
+ comma ie ,, and a comma used as in other cases ie ','
+ as a parameter delimiter/separator is single and due
+ to the strsep above is temporarily zeroed. */
+
+ /* NB: password legally can have multiple commas and
+ the only illegal character in a password is null */
+
+ if ((value[temp_len] == 0) && (value[temp_len+1] == separator[0])) {
+ /* reinsert comma */
+ value[temp_len] = separator[0];
+ temp_len+=2; /* move after the second comma */
+ while(value[temp_len] != 0) {
+ if (value[temp_len] == separator[0]) {
+ if (value[temp_len+1] == separator[0]) {
+ temp_len++; /* skip second comma */
+ } else {
+ /* single comma indicating start
+ of next parm */
+ break;
+ }
+ }
+ temp_len++;
+ }
+ if(value[temp_len] == 0) {
+ options = NULL;
+ } else {
+ value[temp_len] = 0;
+ /* point option to start of next parm */
+ options = value + temp_len + 1;
+ }
+ /* go from value to value + temp_len condensing
+ double commas to singles. Note that this ends up
+ allocating a few bytes too many, which is ok */
+ vol->password = cifs_kcalloc(temp_len, GFP_KERNEL);
+ for(i=0,j=0;i<temp_len;i++,j++) {
+ vol->password[j] = value[i];
+ if(value[i] == separator[0] && value[i+1] == separator[0]) {
+ /* skip second comma */
+ i++;
+ }
+ }
+ vol->password[j] = 0;
+ } else {
+ vol->password = cifs_kcalloc(temp_len + 1, GFP_KERNEL);
+ strcpy(vol->password, value);
+ }
+ } else if (strnicmp(data, "ip", 2) == 0) {
+ if (!value || !*value) {
+ vol->UNCip = NULL;
+ } else if (strnlen(value, 35) < 35) {
+ vol->UNCip = value;
+ } else {
+ printk(KERN_WARNING "CIFS: ip address too long\n");
+ return 1;
+ }
+ } else if ((strnicmp(data, "unc", 3) == 0)
+ || (strnicmp(data, "target", 6) == 0)
+ || (strnicmp(data, "path", 4) == 0)) {
+ if (!value || !*value) {
+ printk(KERN_WARNING
+ "CIFS: invalid path to network resource\n");
+ return 1; /* needs_arg; */
+ }
+ if ((temp_len = strnlen(value, 300)) < 300) {
+ vol->UNC = kmalloc(temp_len+1,GFP_KERNEL);
+ if(vol->UNC == NULL)
+ return 1;
+ strcpy(vol->UNC,value);
+ if (strncmp(vol->UNC, "//", 2) == 0) {
+ vol->UNC[0] = '\\';
+ vol->UNC[1] = '\\';
+ } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
+ printk(KERN_WARNING
+ "CIFS: UNC Path does not begin with // or \\\\ \n");
+ return 1;
+ }
+ } else {
+ printk(KERN_WARNING "CIFS: UNC name too long\n");
+ return 1;
+ }
+ } else if ((strnicmp(data, "domain", 3) == 0)
+ || (strnicmp(data, "workgroup", 5) == 0)) {
+ if (!value || !*value) {
+ printk(KERN_WARNING "CIFS: invalid domain name\n");
+ return 1; /* needs_arg; */
+ }
+ /* BB are there cases in which a comma can be valid in
+ a domain name and need special handling? */
+ if (strnlen(value, 65) < 65) {
+ vol->domainname = value;
+ cFYI(1, ("Domain name set"));
+ } else {
+ printk(KERN_WARNING "CIFS: domain name too long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "iocharset", 9) == 0) {
+ if (!value || !*value) {
+ printk(KERN_WARNING "CIFS: invalid iocharset specified\n");
+ return 1; /* needs_arg; */
+ }
+ if (strnlen(value, 65) < 65) {
+ if(strnicmp(value,"default",7))
+ vol->iocharset = value;
+ /* if iocharset not set load_nls_default used by caller */
+ cFYI(1, ("iocharset set to %s",value));
+ } else {
+ printk(KERN_WARNING "CIFS: iocharset name too long.\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "uid", 3) == 0) {
+ if (value && *value) {
+ vol->linux_uid =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "gid", 3) == 0) {
+ if (value && *value) {
+ vol->linux_gid =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "file_mode", 4) == 0) {
+ if (value && *value) {
+ vol->file_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "dir_mode", 4) == 0) {
+ if (value && *value) {
+ vol->dir_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "dirmode", 4) == 0) {
+ if (value && *value) {
+ vol->dir_mode =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "port", 4) == 0) {
+ if (value && *value) {
+ vol->port =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "rsize", 5) == 0) {
+ if (value && *value) {
+ vol->rsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "wsize", 5) == 0) {
+ if (value && *value) {
+ vol->wsize =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "sockopt", 5) == 0) {
+ if (value && *value) {
+ vol->sockopt =
+ simple_strtoul(value, &value, 0);
+ }
+ } else if (strnicmp(data, "netbiosname", 4) == 0) {
+ if (!value || !*value || (*value == ' ')) {
+ cFYI(1,("invalid (empty) netbiosname specified"));
+ } else {
+ memset(vol->source_rfc1001_name,0x20,15);
+ for(i=0;i<15;i++) {
+ /* BB are there cases in which a comma can be
+ valid in this workstation netbios name (and need
+ special handling)? */
+
+ /* We do not uppercase netbiosname for user */
+ if (value[i]==0)
+ break;
+ else
+ vol->source_rfc1001_name[i] = value[i];
+ }
+ /* The string has 16th byte zero still from
+ set at top of the function */
+ if((i==15) && (value[i] != 0))
+ printk(KERN_WARNING "CIFS: netbiosname longer than 15 and was truncated.\n");
+ }
+ } else if (strnicmp(data, "credentials", 4) == 0) {
+ /* ignore */
+ } else if (strnicmp(data, "version", 3) == 0) {
+ /* ignore */
+ } else if (strnicmp(data, "guest",5) == 0) {
+ /* ignore */
+ } else if (strnicmp(data, "rw", 2) == 0) {
+ vol->rw = TRUE;
+ } else if ((strnicmp(data, "suid", 4) == 0) ||
+ (strnicmp(data, "nosuid", 6) == 0) ||
+ (strnicmp(data, "exec", 4) == 0) ||
+ (strnicmp(data, "noexec", 6) == 0) ||
+ (strnicmp(data, "nodev", 5) == 0) ||
+ (strnicmp(data, "noauto", 6) == 0) ||
+ (strnicmp(data, "dev", 3) == 0)) {
+ /* The mount tool or mount.cifs helper (if present)
+ uses these opts to set flags, and the flags are read
+ by the kernel vfs layer before we get here (ie
+ before read super) so there is no point trying to
+ parse these options again and set anything and it
+ is ok to just ignore them */
+ continue;
+ } else if (strnicmp(data, "ro", 2) == 0) {
+ vol->rw = FALSE;
+ } else if (strnicmp(data, "hard", 4) == 0) {
+ vol->retry = 1;
+ } else if (strnicmp(data, "soft", 4) == 0) {
+ vol->retry = 0;
+ } else if (strnicmp(data, "perm", 4) == 0) {
+ vol->noperm = 0;
+ } else if (strnicmp(data, "noperm", 6) == 0) {
+ vol->noperm = 1;
+ } else if (strnicmp(data, "setuids", 7) == 0) {
+ vol->setuids = 1;
+ } else if (strnicmp(data, "nosetuids", 9) == 0) {
+ vol->setuids = 0;
+ } else if (strnicmp(data, "nohard", 6) == 0) {
+ vol->retry = 0;
+ } else if (strnicmp(data, "nosoft", 6) == 0) {
+ vol->retry = 1;
+ } else if (strnicmp(data, "nointr", 6) == 0) {
+ vol->intr = 0;
+ } else if (strnicmp(data, "intr", 4) == 0) {
+ vol->intr = 1;
+ } else if (strnicmp(data, "serverino",7) == 0) {
+ vol->server_ino = 1;
+ } else if (strnicmp(data, "noserverino",9) == 0) {
+ vol->server_ino = 0;
+ } else if (strnicmp(data, "acl",3) == 0) {
+ vol->no_psx_acl = 0;
+ } else if (strnicmp(data, "noacl",5) == 0) {
+ vol->no_psx_acl = 1;
+ } else if (strnicmp(data, "direct",6) == 0) {
+ vol->direct_io = 1;
+ } else if (strnicmp(data, "forcedirectio",13) == 0) {
+ vol->direct_io = 1;
+ } else if (strnicmp(data, "in6_addr",8) == 0) {
+ if (!value || !*value) {
+ vol->in6_addr = NULL;
+ } else if (strnlen(value, 49) == 48) {
+ vol->in6_addr = value;
+ } else {
+ printk(KERN_WARNING "CIFS: ip v6 address not 48 characters long\n");
+ return 1;
+ }
+ } else if (strnicmp(data, "noac", 4) == 0) {
+ printk(KERN_WARNING "CIFS: Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n");
+ } else
+ printk(KERN_WARNING "CIFS: Unknown mount option %s\n",data);
+ }
+ if (vol->UNC == NULL) {
+ if(devname == NULL) {
+ printk(KERN_WARNING "CIFS: Missing UNC name for mount target\n");
+ return 1;
+ }
+ if ((temp_len = strnlen(devname, 300)) < 300) {
+ vol->UNC = kmalloc(temp_len+1,GFP_KERNEL);
+ if(vol->UNC == NULL)
+ return 1;
+ strcpy(vol->UNC,devname);
+ if (strncmp(vol->UNC, "//", 2) == 0) {
+ vol->UNC[0] = '\\';
+ vol->UNC[1] = '\\';
+ } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
+ printk(KERN_WARNING "CIFS: UNC Path does not begin with // or \\\\ \n");
+ return 1;
+ }
+ } else {
+ printk(KERN_WARNING "CIFS: UNC name too long\n");
+ return 1;
+ }
+ }
+ if(vol->UNCip == NULL)
+ vol->UNCip = &vol->UNC[2];
+
+ return 0;
+}
+
+static struct cifsSesInfo *
+cifs_find_tcp_session(struct in_addr * target_ip_addr,
+ struct in6_addr *target_ip6_addr,
+ char *userName, struct TCP_Server_Info **psrvTcp)
+{
+ struct list_head *tmp;
+ struct cifsSesInfo *ses;
+ *psrvTcp = NULL;
+ read_lock(&GlobalSMBSeslock);
+
+ list_for_each(tmp, &GlobalSMBSessionList) {
+ ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
+ if (ses->server) {
+ if((target_ip_addr &&
+ (ses->server->addr.sockAddr.sin_addr.s_addr
+ == target_ip_addr->s_addr)) || (target_ip6_addr
+ && memcmp(&ses->server->addr.sockAddr6.sin6_addr,
+ target_ip6_addr,sizeof(*target_ip6_addr)))){
+ /* BB lock server and tcp session and increment use count here?? */
+ *psrvTcp = ses->server; /* found a match on the TCP session */
+ /* BB check if reconnection needed */
+ if (strncmp
+ (ses->userName, userName,
+ MAX_USERNAME_SIZE) == 0){
+ read_unlock(&GlobalSMBSeslock);
+ return ses; /* found exact match on both tcp and SMB sessions */
+ }
+ }
+ }
+ /* else tcp and smb sessions need reconnection */
+ }
+ read_unlock(&GlobalSMBSeslock);
+ return NULL;
+}
+
+static struct cifsTconInfo *
+find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
+{
+ struct list_head *tmp;
+ struct cifsTconInfo *tcon;
+
+ read_lock(&GlobalSMBSeslock);
+ list_for_each(tmp, &GlobalTreeConnectionList) {
+ cFYI(1, ("Next tcon - "));
+ tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+ if (tcon->ses) {
+ if (tcon->ses->server) {
+ cFYI(1,
+ (" old ip addr: %x == new ip %x ?",
+ tcon->ses->server->addr.sockAddr.sin_addr.
+ s_addr, new_target_ip_addr));
+ if (tcon->ses->server->addr.sockAddr.sin_addr.
+ s_addr == new_target_ip_addr) {
+ /* BB lock tcon and server and tcp session and increment use count here? */
+ /* found a match on the TCP session */
+ /* BB check if reconnection needed */
+ cFYI(1,("Matched ip, old UNC: %s == new: %s ?",
+ tcon->treeName, uncName));
+ if (strncmp
+ (tcon->treeName, uncName,
+ MAX_TREE_SIZE) == 0) {
+ cFYI(1,
+ ("Matched UNC, old user: %s == new: %s ?",
+ tcon->treeName, uncName));
+ if (strncmp
+ (tcon->ses->userName,
+ userName,
+ MAX_USERNAME_SIZE) == 0) {
+ read_unlock(&GlobalSMBSeslock);
+ return tcon;/* also matched user (smb session)*/
+ }
+ }
+ }
+ }
+ }
+ }
+ read_unlock(&GlobalSMBSeslock);
+ return NULL;
+}
+
+int
+connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
+ const char *old_path, const struct nls_table *nls_codepage)
+{
+ unsigned char *referrals = NULL;
+ unsigned int num_referrals;
+ int rc = 0;
+
+ rc = get_dfs_path(xid, pSesInfo,old_path, nls_codepage,
+ &num_referrals, &referrals);
+
+ /* BB Add in code to: if valid refrl, if not ip address contact
+ the helper that resolves tcp names, mount to it, try to
+ tcon to it unmount it if fail */
+
+ if(referrals)
+ kfree(referrals);
+
+ return rc;
+}
+
+int
+get_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
+ const char *old_path, const struct nls_table *nls_codepage,
+ unsigned int *pnum_referrals, unsigned char ** preferrals)
+{
+ char *temp_unc;
+ int rc = 0;
+
+ *pnum_referrals = 0;
+
+ if (pSesInfo->ipc_tid == 0) {
+ temp_unc = kmalloc(2 /* for slashes */ +
+ strnlen(pSesInfo->serverName,SERVER_NAME_LEN_WITH_NULL * 2)
+ + 1 + 4 /* slash IPC$ */ + 2,
+ GFP_KERNEL);
+ if (temp_unc == NULL)
+ return -ENOMEM;
+ temp_unc[0] = '\\';
+ temp_unc[1] = '\\';
+ strcpy(temp_unc + 2, pSesInfo->serverName);
+ strcpy(temp_unc + 2 + strlen(pSesInfo->serverName), "\\IPC$");
+ rc = CIFSTCon(xid, pSesInfo, temp_unc, NULL, nls_codepage);
+ cFYI(1,
+ ("CIFS Tcon rc = %d ipc_tid = %d", rc,pSesInfo->ipc_tid));
+ kfree(temp_unc);
+ }
+ if (rc == 0)
+ rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
+ pnum_referrals, nls_codepage);
+
+ return rc;
+}
+
+/* See RFC1001 section 14 on representation of Netbios names */
+static void rfc1002mangle(char * target,char * source, unsigned int length)
+{
+ unsigned int i,j;
+
+ for(i=0,j=0;i<(length);i++) {
+ /* mask a nibble at a time and encode */
+ target[j] = 'A' + (0x0F & (source[i] >> 4));
+ target[j+1] = 'A' + (0x0F & source[i]);
+ j+=2;
+ }
+
+}
+
+
+static int
+ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
+ char * netbios_name)
+{
+ int rc = 0;
+ int connected = 0;
+ __be16 orig_port = 0;
+
+ if(*csocket == NULL) {
+ rc = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, csocket);
+ if (rc < 0) {
+ cERROR(1, ("Error %d creating socket",rc));
+ *csocket = NULL;
+ return rc;
+ } else {
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1,("Socket created"));
+ (*csocket)->sk->sk_allocation = GFP_NOFS;
+ }
+ }
+
+ psin_server->sin_family = AF_INET;
+ if(psin_server->sin_port) { /* user overrode default port */
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof (struct sockaddr_in),0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ if(!connected) {
+ /* save original port so we can retry user specified port
+ later if fall back ports fail this time */
+ orig_port = psin_server->sin_port;
+
+ /* do not retry on the same port we just failed on */
+ if(psin_server->sin_port != htons(CIFS_PORT)) {
+ psin_server->sin_port = htons(CIFS_PORT);
+
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof (struct sockaddr_in),0);
+ if (rc >= 0)
+ connected = 1;
+ }
+ }
+ if (!connected) {
+ psin_server->sin_port = htons(RFC1001_PORT);
+ rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
+ psin_server, sizeof (struct sockaddr_in),0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ /* give up here - unless we want to retry on different
+ protocol families some day */
+ if (!connected) {
+ if(orig_port)
+ psin_server->sin_port = orig_port;
+ cFYI(1,("Error %d connecting to server via ipv4",rc));
+ sock_release(*csocket);
+ *csocket = NULL;
+ return rc;
+ }
+ /* Eventually check for other socket options to change from
+ the default. sock_setsockopt not used because it expects
+ user space buffer */
+ (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
+
+ /* send RFC1001 sessinit */
+
+ if(psin_server->sin_port == htons(RFC1001_PORT)) {
+ /* some servers require RFC1001 sessinit before sending
+ negprot - BB check reconnection in case where second
+ sessinit is sent but no second negprot */
+ struct rfc1002_session_packet * ses_init_buf;
+ struct smb_hdr * smb_buf;
+ ses_init_buf = cifs_kcalloc(sizeof(struct rfc1002_session_packet), GFP_KERNEL);
+ if(ses_init_buf) {
+ ses_init_buf->trailer.session_req.called_len = 32;
+ rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
+ DEFAULT_CIFS_CALLED_NAME,16);
+ ses_init_buf->trailer.session_req.calling_len = 32;
+ /* calling name ends in null (byte 16) from old smb
+ convention. */
+ if(netbios_name && (netbios_name[0] !=0)) {
+ rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
+ netbios_name,16);
+ } else {
+ rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
+ "LINUX_CIFS_CLNT",16);
+ }
+ ses_init_buf->trailer.session_req.scope1 = 0;
+ ses_init_buf->trailer.session_req.scope2 = 0;
+ smb_buf = (struct smb_hdr *)ses_init_buf;
+ /* sizeof RFC1002_SESSION_REQUEST with no scope */
+ smb_buf->smb_buf_length = 0x81000044;
+ rc = smb_send(*csocket, smb_buf, 0x44,
+ (struct sockaddr *)psin_server);
+ kfree(ses_init_buf);
+ }
+ /* else the negprot may still work without this
+ even though malloc failed */
+
+ }
+
+ return rc;
+}
+
+static int
+ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
+{
+ int rc = 0;
+ int connected = 0;
+ __be16 orig_port = 0;
+
+ if(*csocket == NULL) {
+ rc = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, csocket);
+ if (rc < 0) {
+ cERROR(1, ("Error %d creating ipv6 socket",rc));
+ *csocket = NULL;
+ return rc;
+ } else {
+ /* BB other socket options to set KEEPALIVE, NODELAY? */
+ cFYI(1,("ipv6 Socket created"));
+ (*csocket)->sk->sk_allocation = GFP_NOFS;
+ }
+ }
+
+ psin_server->sin6_family = AF_INET6;
+
+ if(psin_server->sin6_port) { /* user overrode default port */
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof (struct sockaddr_in6),0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ if(!connected) {
+ /* save original port so we can retry user specified port
+ later if fall back ports fail this time */
+
+ orig_port = psin_server->sin6_port;
+ /* do not retry on the same port we just failed on */
+ if(psin_server->sin6_port != htons(CIFS_PORT)) {
+ psin_server->sin6_port = htons(CIFS_PORT);
+
+ rc = (*csocket)->ops->connect(*csocket,
+ (struct sockaddr *) psin_server,
+ sizeof (struct sockaddr_in6),0);
+ if (rc >= 0)
+ connected = 1;
+ }
+ }
+ if (!connected) {
+ psin_server->sin6_port = htons(RFC1001_PORT);
+ rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
+ psin_server, sizeof (struct sockaddr_in6),0);
+ if (rc >= 0)
+ connected = 1;
+ }
+
+ /* give up here - unless we want to retry on different
+ protocol families some day */
+ if (!connected) {
+ if(orig_port)
+ psin_server->sin6_port = orig_port;
+ cFYI(1,("Error %d connecting to server via ipv6",rc));
+ sock_release(*csocket);
+ *csocket = NULL;
+ return rc;
+ }
+ /* Eventually check for other socket options to change from
+ the default. sock_setsockopt not used because it expects
+ user space buffer */
+ (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
+
+ return rc;
+}
+
+int
+cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
+ char *mount_data, const char *devname)
+{
+ int rc = 0;
+ int xid;
+ int address_type = AF_INET;
+ struct socket *csocket = NULL;
+ struct sockaddr_in sin_server;
+ struct sockaddr_in6 sin_server6;
+ struct smb_vol volume_info;
+ struct cifsSesInfo *pSesInfo = NULL;
+ struct cifsSesInfo *existingCifsSes = NULL;
+ struct cifsTconInfo *tcon = NULL;
+ struct TCP_Server_Info *srvTcp = NULL;
+
+ xid = GetXid();
+
+/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
+
+ memset(&volume_info,0,sizeof(struct smb_vol));
+ if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -EINVAL;
+ }
+
+ if (volume_info.username) {
+ /* BB fixme parse for domain name here */
+ cFYI(1, ("Username: %s ", volume_info.username));
+
+ } else {
+ cifserror("No username specified ");
+ /* In userspace mount helper we can get user name from alternate
+ locations such as env variables and files on disk */
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -EINVAL;
+ }
+
+ if (volume_info.UNCip && volume_info.UNC) {
+ rc = cifs_inet_pton(AF_INET, volume_info.UNCip,&sin_server.sin_addr.s_addr);
+
+ if(rc <= 0) {
+ /* not ipv4 address, try ipv6 */
+ rc = cifs_inet_pton(AF_INET6,volume_info.UNCip,&sin_server6.sin6_addr.in6_u);
+ if(rc > 0)
+ address_type = AF_INET6;
+ } else {
+ address_type = AF_INET;
+ }
+
+ if(rc <= 0) {
+ /* we failed translating address */
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -EINVAL;
+ }
+
+ cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
+ /* success */
+ rc = 0;
+ } else if (volume_info.UNCip){
+ /* BB using ip addr as server name connect to the DFS root below */
+ cERROR(1,("Connecting to DFS root not implemented yet"));
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -EINVAL;
+ } else /* which servers DFS root would we conect to */ {
+ cERROR(1,
+ ("CIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified "));
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -EINVAL;
+ }
+
+ /* this is needed for ASCII cp to Unicode converts */
+ if(volume_info.iocharset == NULL) {
+ cifs_sb->local_nls = load_nls_default();
+ /* load_nls_default can not return null */
+ } else {
+ cifs_sb->local_nls = load_nls(volume_info.iocharset);
+ if(cifs_sb->local_nls == NULL) {
+ cERROR(1,("CIFS mount error: iocharset %s not found",volume_info.iocharset));
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -ELIBACC;
+ }
+ }
+
+ if(address_type == AF_INET)
+ existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr,
+ NULL /* no ipv6 addr */,
+ volume_info.username, &srvTcp);
+ else if(address_type == AF_INET6)
+ existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */,
+ &sin_server6.sin6_addr,
+ volume_info.username, &srvTcp);
+ else {
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return -EINVAL;
+ }
+
+
+ if (srvTcp) {
+ cFYI(1, ("Existing tcp session with server found "));
+ } else { /* create socket */
+ if(volume_info.port)
+ sin_server.sin_port = htons(volume_info.port);
+ else
+ sin_server.sin_port = 0;
+ rc = ipv4_connect(&sin_server,&csocket,volume_info.source_rfc1001_name);
+ if (rc < 0) {
+ cERROR(1,
+ ("Error connecting to IPv4 socket. Aborting operation"));
+ if(csocket != NULL)
+ sock_release(csocket);
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return rc;
+ }
+
+ srvTcp = kmalloc(sizeof (struct TCP_Server_Info), GFP_KERNEL);
+ if (srvTcp == NULL) {
+ rc = -ENOMEM;
+ sock_release(csocket);
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return rc;
+ } else {
+ memset(srvTcp, 0, sizeof (struct TCP_Server_Info));
+ memcpy(&srvTcp->addr.sockAddr, &sin_server, sizeof (struct sockaddr_in));
+ atomic_set(&srvTcp->inFlight,0);
+ /* BB Add code for ipv6 case too */
+ srvTcp->ssocket = csocket;
+ srvTcp->protocolType = IPV4;
+ init_waitqueue_head(&srvTcp->response_q);
+ init_waitqueue_head(&srvTcp->request_q);
+ INIT_LIST_HEAD(&srvTcp->pending_mid_q);
+ /* at this point we are the only ones with the pointer
+ to the struct since the kernel thread not created yet
+ so no need to spinlock this init of tcpStatus */
+ srvTcp->tcpStatus = CifsNew;
+ init_MUTEX(&srvTcp->tcpSem);
+ rc = (int)kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp,
+ CLONE_FS | CLONE_FILES | CLONE_VM);
+ if(rc < 0) {
+ rc = -ENOMEM;
+ sock_release(csocket);
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ if(volume_info.password)
+ kfree(volume_info.password);
+ FreeXid(xid);
+ return rc;
+ } else
+ rc = 0;
+ memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16);
+ }
+ }
+
+ if (existingCifsSes) {
+ pSesInfo = existingCifsSes;
+ cFYI(1, ("Existing smb sess found "));
+ if(volume_info.password)
+ kfree(volume_info.password);
+ /* volume_info.UNC freed at end of function */
+ } else if (!rc) {
+ cFYI(1, ("Existing smb sess not found "));
+ pSesInfo = sesInfoAlloc();
+ if (pSesInfo == NULL)
+ rc = -ENOMEM;
+ else {
+ pSesInfo->server = srvTcp;
+ sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
+ NIPQUAD(sin_server.sin_addr.s_addr));
+ }
+
+ if (!rc){
+ /* volume_info.password freed at unmount */
+ if (volume_info.password)
+ pSesInfo->password = volume_info.password;
+ if (volume_info.username)
+ strncpy(pSesInfo->userName,
+ volume_info.username,MAX_USERNAME_SIZE);
+ if (volume_info.domainname)
+ strncpy(pSesInfo->domainName,
+ volume_info.domainname,MAX_USERNAME_SIZE);
+ pSesInfo->linux_uid = volume_info.linux_uid;
+ down(&pSesInfo->sesSem);
+ rc = cifs_setup_session(xid,pSesInfo, cifs_sb->local_nls);
+ up(&pSesInfo->sesSem);
+ if(!rc)
+ atomic_inc(&srvTcp->socketUseCount);
+ } else
+ if(volume_info.password)
+ kfree(volume_info.password);
+ }
+
+ /* search for existing tcon to this server share */
+ if (!rc) {
+ if((volume_info.rsize) && (volume_info.rsize <= CIFSMaxBufSize))
+ cifs_sb->rsize = volume_info.rsize;
+ else
+ cifs_sb->rsize = srvTcp->maxBuf - MAX_CIFS_HDR_SIZE; /* default */
+ if((volume_info.wsize) && (volume_info.wsize <= CIFSMaxBufSize))
+ cifs_sb->wsize = volume_info.wsize;
+ else
+ cifs_sb->wsize = CIFSMaxBufSize; /* default */
+ if(cifs_sb->rsize < PAGE_CACHE_SIZE) {
+ cifs_sb->rsize = PAGE_CACHE_SIZE;
+ cERROR(1,("Attempt to set readsize for mount to less than one page (4096)"));
+ }
+ cifs_sb->mnt_uid = volume_info.linux_uid;
+ cifs_sb->mnt_gid = volume_info.linux_gid;
+ cifs_sb->mnt_file_mode = volume_info.file_mode;
+ cifs_sb->mnt_dir_mode = volume_info.dir_mode;
+ cFYI(1,("file mode: 0x%x dir mode: 0x%x",cifs_sb->mnt_file_mode,cifs_sb->mnt_dir_mode));
+
+ if(volume_info.noperm)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
+ if(volume_info.setuids)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+ if(volume_info.server_ino)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
+ if(volume_info.no_xattr)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+ if(volume_info.direct_io) {
+ cERROR(1,("mounting share using direct i/o"));
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
+ }
+
+ tcon =
+ find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
+ volume_info.username);
+ if (tcon) {
+ cFYI(1, ("Found match on UNC path "));
+ /* we can have only one retry value for a connection
+ to a share so for resources mounted more than once
+ to the same server share the last value passed in
+ for the retry flag is used */
+ tcon->retry = volume_info.retry;
+ } else {
+ tcon = tconInfoAlloc();
+ if (tcon == NULL)
+ rc = -ENOMEM;
+ else {
+ /* check for null share name ie connect to dfs root */
+
+ /* BB check if this works for exactly length three strings */
+ if ((strchr(volume_info.UNC + 3, '\\') == NULL)
+ && (strchr(volume_info.UNC + 3, '/') ==
+ NULL)) {
+ rc = connect_to_dfs_path(xid,
+ pSesInfo,
+ "",
+ cifs_sb->
+ local_nls);
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ FreeXid(xid);
+ return -ENODEV;
+ } else {
+ rc = CIFSTCon(xid, pSesInfo,
+ volume_info.UNC,
+ tcon, cifs_sb->local_nls);
+ cFYI(1, ("CIFS Tcon rc = %d", rc));
+ }
+ if (!rc) {
+ atomic_inc(&pSesInfo->inUse);
+ tcon->retry = volume_info.retry;
+ }
+ }
+ }
+ }
+ if(pSesInfo) {
+ if (pSesInfo->capabilities & CAP_LARGE_FILES) {
+ sb->s_maxbytes = (u64) 1 << 63;
+ } else
+ sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */
+ }
+
+ sb->s_time_gran = 100;
+
+/* on error free sesinfo and tcon struct if needed */
+ if (rc) {
+ /* if session setup failed, use count is zero but
+ we still need to free cifsd thread */
+ if(atomic_read(&srvTcp->socketUseCount) == 0) {
+ spin_lock(&GlobalMid_Lock);
+ srvTcp->tcpStatus = CifsExiting;
+ spin_unlock(&GlobalMid_Lock);
+ if(srvTcp->tsk)
+ send_sig(SIGKILL,srvTcp->tsk,1);
+ }
+ /* If find_unc succeeded then rc == 0 so we can not end */
+ if (tcon) /* up accidently freeing someone elses tcon struct */
+ tconInfoFree(tcon);
+ if (existingCifsSes == NULL) {
+ if (pSesInfo) {
+ if ((pSesInfo->server) &&
+ (pSesInfo->status == CifsGood)) {
+ int temp_rc;
+ temp_rc = CIFSSMBLogoff(xid, pSesInfo);
+ /* if the socketUseCount is now zero */
+ if((temp_rc == -ESHUTDOWN) &&
+ (pSesInfo->server->tsk))
+ send_sig(SIGKILL,pSesInfo->server->tsk,1);
+ } else
+ cFYI(1, ("No session or bad tcon"));
+ sesInfoFree(pSesInfo);
+ /* pSesInfo = NULL; */
+ }
+ }
+ } else {
+ atomic_inc(&tcon->useCount);
+ cifs_sb->tcon = tcon;
+ tcon->ses = pSesInfo;
+
+ /* do not care if following two calls succeed - informational only */
+ CIFSSMBQFSDeviceInfo(xid, tcon, cifs_sb->local_nls);
+ CIFSSMBQFSAttributeInfo(xid, tcon, cifs_sb->local_nls);
+ if (tcon->ses->capabilities & CAP_UNIX) {
+ if(!CIFSSMBQFSUnixInfo(xid, tcon, cifs_sb->local_nls)) {
+ if(!volume_info.no_psx_acl) {
+ if(CIFS_UNIX_POSIX_ACL_CAP &
+ le64_to_cpu(tcon->fsUnixInfo.Capability))
+ cFYI(1,("server negotiated posix acl support"));
+ sb->s_flags |= MS_POSIXACL;
+ }
+ }
+ }
+ }
+
+ /* volume_info.password is freed above when existing session found
+ (in which case it is not needed anymore) but when new sesion is created
+ the password ptr is put in the new session structure (in which case the
+ password will be freed at unmount time) */
+ if(volume_info.UNC)
+ kfree(volume_info.UNC);
+ FreeXid(xid);
+ return rc;
+}
+
+static int
+CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
+ char session_key[CIFS_SESSION_KEY_SIZE],
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *user;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ __u32 capabilities;
+ __u16 count;
+
+ cFYI(1, ("In sesssetup "));
+ if(ses == NULL)
+ return -EINVAL;
+ user = ses->userName;
+ domain = ses->domainName;
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+ pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 13 /* wct */ );
+
+ pSMB->req_no_secext.AndXCommand = 0xFF;
+ pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
+
+ pSMB->req_no_secext.CaseInsensitivePasswordLength =
+ cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+
+ pSMB->req_no_secext.CaseSensitivePasswordLength =
+ cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ bcc_ptr = pByteArea(smb_buffer);
+ memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE);
+ bcc_ptr += CIFS_SESSION_KEY_SIZE;
+ memcpy(bcc_ptr, (char *) session_key, CIFS_SESSION_KEY_SIZE);
+ bcc_ptr += CIFS_SESSION_KEY_SIZE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ if(user == NULL)
+ bytes_returned = 0; /* skill null user */
+ else
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, user, 100,
+ nls_codepage);
+ /* convert number of 16 bit words to bytes */
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2; /* trailing null */
+ if (domain == NULL)
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr,
+ "CIFS_LINUX_DOM", 32, nls_codepage);
+ else
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, domain, 64,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, system_utsname.release,
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ } else {
+ if(user != NULL) {
+ strncpy(bcc_ptr, user, 200);
+ bcc_ptr += strnlen(user, 200);
+ }
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ if (domain == NULL) {
+ strcpy(bcc_ptr, "CIFS_LINUX_DOM");
+ bcc_ptr += strlen("CIFS_LINUX_DOM") + 1;
+ } else {
+ strncpy(bcc_ptr, domain, 64);
+ bcc_ptr += strnlen(domain, 64);
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, system_utsname.release);
+ bcc_ptr += strlen(system_utsname.release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ }
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, 1);
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
+ } else if ((smb_buffer_response->WordCount == 3)
+ || (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login")); /* do we want to mark SesInfo struct ? */
+ ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */
+ cFYI(1, ("UID = %d ", ses->Suid));
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+ bcc_ptr = pByteArea(smb_buffer_response);
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len < pSMBr->resp.ByteCount))) {
+ if (pSMBr->resp.hdr.WordCount == 4)
+ bcc_ptr += blob_len;
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response) - 1) /2;
+ bcc_ptr++; /* Unicode strings must be word aligned */
+ } else {
+ remaining_words =
+ BCC(smb_buffer_response) / 2;
+ }
+ len =
+ UniStrnlen((wchar_t *) bcc_ptr,
+ remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ ses->serverOS = cifs_kcalloc(2 * (len + 1), GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverOS,
+ (wchar_t *)bcc_ptr, len,nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)bcc_ptr,
+ remaining_words-1);
+ ses->serverNOS =cifs_kcalloc(2 * (len + 1),GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverNOS,
+ (wchar_t *)bcc_ptr,len,nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1 + (2 * len)] = 0;
+ if(strncmp(ses->serverNOS,
+ "NT LAN Manager 4",16) == 0) {
+ cFYI(1,("NT4 server"));
+ ses->flags |= CIFS_SES_NT4;
+ }
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string is not always null terminated (for e.g. for Windows XP & 2000) */
+ ses->serverDomain =
+ cifs_kcalloc(2*(len+1),GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverDomain,
+ (wchar_t *)bcc_ptr,len,nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverDomain[2*len] = 0;
+ ses->serverDomain[1+(2*len)] = 0;
+ } /* else no more room so create dummy domain string */
+ else
+ ses->serverDomain =
+ cifs_kcalloc(2,
+ GFP_KERNEL);
+ } else { /* no room so create dummy domain and NOS string */
+ ses->serverDomain =
+ cifs_kcalloc(2, GFP_KERNEL);
+ ses->serverNOS =
+ cifs_kcalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) - (long)
+ pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ ses->serverOS = cifs_kcalloc(len + 1,GFP_KERNEL);
+ strncpy(ses->serverOS,bcc_ptr, len);
+
+ bcc_ptr += len;
+ bcc_ptr[0] = 0; /* null terminate the string */
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverNOS = cifs_kcalloc(len + 1,GFP_KERNEL);
+ strncpy(ses->serverNOS, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverDomain = cifs_kcalloc(len + 1,GFP_KERNEL);
+ strncpy(ses->serverDomain, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1,
+ ("Variable field of length %d extends beyond end of smb ",
+ len));
+ }
+ } else {
+ cERROR(1,
+ (" Security Blob Length extends beyond end of SMB"));
+ }
+ } else {
+ cERROR(1,
+ (" Invalid Word count %d: ",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+
+ if (smb_buffer)
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+
+static int
+CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses,
+ char *SecurityBlob,int SecurityBlobLength,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *user;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ __u32 capabilities;
+ __u16 count;
+
+ cFYI(1, ("In spnego sesssetup "));
+ if(ses == NULL)
+ return -EINVAL;
+ user = ses->userName;
+ domain = ses->domainName;
+
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+ pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 12 /* wct */ );
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_EXTENDED_SECURITY;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+
+ pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
+ bcc_ptr = pByteArea(smb_buffer);
+ memcpy(bcc_ptr, SecurityBlob, SecurityBlobLength);
+ bcc_ptr += SecurityBlobLength;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode strings */
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, user, 100, nls_codepage);
+ bcc_ptr += 2 * bytes_returned; /* convert num of 16 bit words to bytes */
+ bcc_ptr += 2; /* trailing null */
+ if (domain == NULL)
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr,
+ "CIFS_LINUX_DOM", 32, nls_codepage);
+ else
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, domain, 64,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, system_utsname.release, 32,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2;
+ } else {
+ strncpy(bcc_ptr, user, 200);
+ bcc_ptr += strnlen(user, 200);
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ if (domain == NULL) {
+ strcpy(bcc_ptr, "CIFS_LINUX_DOM");
+ bcc_ptr += strlen("CIFS_LINUX_DOM") + 1;
+ } else {
+ strncpy(bcc_ptr, domain, 64);
+ bcc_ptr += strnlen(domain, 64);
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, system_utsname.release);
+ bcc_ptr += strlen(system_utsname.release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ }
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, 1);
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
+ } else if ((smb_buffer_response->WordCount == 3)
+ || (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len =
+ le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login")); /* BB do we want to set anything in SesInfo struct ? */
+ if (ses) {
+ ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */
+ cFYI(1, ("UID = %d ", ses->Suid));
+ bcc_ptr = pByteArea(smb_buffer_response); /* response can have either 3 or 4 word count - Samba sends 3 */
+
+ /* BB Fix below to make endian neutral !! */
+
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len <
+ pSMBr->resp.ByteCount))) {
+ if (pSMBr->resp.hdr.WordCount == 4) {
+ bcc_ptr +=
+ blob_len;
+ cFYI(1,
+ ("Security Blob Length %d ",
+ blob_len));
+ }
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response)
+ - 1) / 2;
+ bcc_ptr++; /* Unicode strings must be word aligned */
+ } else {
+ remaining_words =
+ BCC
+ (smb_buffer_response) / 2;
+ }
+ len =
+ UniStrnlen((wchar_t *) bcc_ptr,
+ remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ ses->serverOS =
+ cifs_kcalloc(2 * (len + 1), GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverOS,
+ (wchar_t *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)bcc_ptr,
+ remaining_words
+ - 1);
+ ses->serverNOS =
+ cifs_kcalloc(2 * (len + 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverNOS,
+ (wchar_t *)bcc_ptr,
+ len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1 + (2 * len)] = 0;
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string is not always null terminated (for e.g. for Windows XP & 2000) */
+ ses->serverDomain = cifs_kcalloc(2*(len+1),GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverDomain,
+ (wchar_t *)bcc_ptr,
+ len,
+ nls_codepage);
+ bcc_ptr += 2*(len+1);
+ ses->serverDomain[2*len] = 0;
+ ses->serverDomain[1+(2*len)] = 0;
+ } /* else no more room so create dummy domain string */
+ else
+ ses->serverDomain =
+ cifs_kcalloc(2,GFP_KERNEL);
+ } else { /* no room so create dummy domain and NOS string */
+ ses->serverDomain = cifs_kcalloc(2, GFP_KERNEL);
+ ses->serverNOS = cifs_kcalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) - (long)
+ pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ ses->serverOS = cifs_kcalloc(len + 1, GFP_KERNEL);
+ strncpy(ses->serverOS, bcc_ptr, len);
+
+ bcc_ptr += len;
+ bcc_ptr[0] = 0; /* null terminate the string */
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverNOS = cifs_kcalloc(len + 1,GFP_KERNEL);
+ strncpy(ses->serverNOS, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverDomain = cifs_kcalloc(len + 1, GFP_KERNEL);
+ strncpy(ses->serverDomain, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1,
+ ("Variable field of length %d extends beyond end of smb ",
+ len));
+ }
+ } else {
+ cERROR(1,
+ (" Security Blob Length extends beyond end of SMB"));
+ }
+ } else {
+ cERROR(1, ("No session structure passed in."));
+ }
+ } else {
+ cERROR(1,
+ (" Invalid Word count %d: ",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+
+ if (smb_buffer)
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+
+static int
+CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
+ struct cifsSesInfo *ses, int * pNTLMv2_flag,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ int SecurityBlobLength = sizeof (NEGOTIATE_MESSAGE);
+ PNEGOTIATE_MESSAGE SecurityBlob;
+ PCHALLENGE_MESSAGE SecurityBlob2;
+ __u32 negotiate_flags, capabilities;
+ __u16 count;
+
+ cFYI(1, ("In NTLMSSP sesssetup (negotiate) "));
+ if(ses == NULL)
+ return -EINVAL;
+ domain = ses->domainName;
+ *pNTLMv2_flag = FALSE;
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+ pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
+ pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 12 /* wct */ );
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
+
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_EXTENDED_SECURITY;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+
+ bcc_ptr = (char *) &pSMB->req.SecurityBlob;
+ SecurityBlob = (PNEGOTIATE_MESSAGE) bcc_ptr;
+ strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
+ SecurityBlob->MessageType = NtLmNegotiate;
+ negotiate_flags =
+ NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM |
+ NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | 0x80000000 |
+ /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128;
+ if(sign_CIFS_PDUs)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN;
+ if(ntlmv2_support)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
+ /* setup pointers to domain name and workstation name */
+ bcc_ptr += SecurityBlobLength;
+
+ SecurityBlob->WorkstationName.Buffer = 0;
+ SecurityBlob->WorkstationName.Length = 0;
+ SecurityBlob->WorkstationName.MaximumLength = 0;
+
+ if (domain == NULL) {
+ SecurityBlob->DomainName.Buffer = 0;
+ SecurityBlob->DomainName.Length = 0;
+ SecurityBlob->DomainName.MaximumLength = 0;
+ } else {
+ __u16 len;
+ negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
+ strncpy(bcc_ptr, domain, 63);
+ len = strnlen(domain, 64);
+ SecurityBlob->DomainName.MaximumLength =
+ cpu_to_le16(len);
+ SecurityBlob->DomainName.Buffer =
+ cpu_to_le32((long) &SecurityBlob->
+ DomainString -
+ (long) &SecurityBlob->Signature);
+ bcc_ptr += len;
+ SecurityBlobLength += len;
+ SecurityBlob->DomainName.Length =
+ cpu_to_le16(len);
+ }
+ if (ses->capabilities & CAP_UNICODE) {
+ if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, system_utsname.release, 32,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2; /* null terminate Linux version */
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null terminate network opsys string */
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null domain */
+ } else { /* ASCII */
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, system_utsname.release);
+ bcc_ptr += strlen(system_utsname.release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ bcc_ptr++; /* empty domain field */
+ *bcc_ptr = 0;
+ }
+ SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
+ pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, 1);
+
+ if (smb_buffer_response->Status.CifsError ==
+ cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
+ rc = 0;
+
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
+ } else if ((smb_buffer_response->WordCount == 3)
+ || (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login"));
+ /* Do we want to set anything in SesInfo struct when guest login? */
+
+ bcc_ptr = pByteArea(smb_buffer_response);
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+
+ SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr;
+ if (SecurityBlob2->MessageType != NtLmChallenge) {
+ cFYI(1,
+ ("Unexpected NTLMSSP message type received %d",
+ SecurityBlob2->MessageType));
+ } else if (ses) {
+ ses->Suid = smb_buffer_response->Uid; /* UID left in le format */
+ cFYI(1, ("UID = %d ", ses->Suid));
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len <
+ pSMBr->resp.ByteCount))) {
+
+ if (pSMBr->resp.hdr.WordCount == 4) {
+ bcc_ptr += blob_len;
+ cFYI(1,
+ ("Security Blob Length %d ",
+ blob_len));
+ }
+
+ cFYI(1, ("NTLMSSP Challenge rcvd "));
+
+ memcpy(ses->server->cryptKey,
+ SecurityBlob2->Challenge,
+ CIFS_CRYPTO_KEY_SIZE);
+ if(SecurityBlob2->NegotiateFlags & cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2))
+ *pNTLMv2_flag = TRUE;
+
+ if((SecurityBlob2->NegotiateFlags &
+ cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
+ || (sign_CIFS_PDUs > 1))
+ ses->server->secMode |=
+ SECMODE_SIGN_REQUIRED;
+ if ((SecurityBlob2->NegotiateFlags &
+ cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs))
+ ses->server->secMode |=
+ SECMODE_SIGN_ENABLED;
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response)
+ - 1) / 2;
+ bcc_ptr++; /* Unicode strings must be word aligned */
+ } else {
+ remaining_words =
+ BCC
+ (smb_buffer_response) / 2;
+ }
+ len =
+ UniStrnlen((wchar_t *) bcc_ptr,
+ remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ ses->serverOS =
+ cifs_kcalloc(2 * (len + 1), GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverOS,
+ (wchar_t *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)
+ bcc_ptr,
+ remaining_words
+ - 1);
+ ses->serverNOS =
+ cifs_kcalloc(2 * (len + 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le(ses->
+ serverNOS,
+ (wchar_t *)
+ bcc_ptr,
+ len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1 +
+ (2 * len)] = 0;
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string is not always null terminated (for e.g. for Windows XP & 2000) */
+ ses->serverDomain =
+ cifs_kcalloc(2 *
+ (len +
+ 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le
+ (ses->
+ serverDomain,
+ (wchar_t *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr +=
+ 2 * (len + 1);
+ ses->
+ serverDomain[2
+ * len]
+ = 0;
+ ses->
+ serverDomain[1
+ +
+ (2
+ *
+ len)]
+ = 0;
+ } /* else no more room so create dummy domain string */
+ else
+ ses->serverDomain =
+ cifs_kcalloc(2,
+ GFP_KERNEL);
+ } else { /* no room so create dummy domain and NOS string */
+ ses->serverDomain =
+ cifs_kcalloc(2, GFP_KERNEL);
+ ses->serverNOS =
+ cifs_kcalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) - (long)
+ pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ ses->serverOS =
+ cifs_kcalloc(len + 1,
+ GFP_KERNEL);
+ strncpy(ses->serverOS,
+ bcc_ptr, len);
+
+ bcc_ptr += len;
+ bcc_ptr[0] = 0; /* null terminate string */
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverNOS =
+ cifs_kcalloc(len + 1,
+ GFP_KERNEL);
+ strncpy(ses->serverNOS, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverDomain =
+ cifs_kcalloc(len + 1,
+ GFP_KERNEL);
+ strncpy(ses->serverDomain, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1,
+ ("Variable field of length %d extends beyond end of smb ",
+ len));
+ }
+ } else {
+ cERROR(1,
+ (" Security Blob Length extends beyond end of SMB"));
+ }
+ } else {
+ cERROR(1, ("No session structure passed in."));
+ }
+ } else {
+ cERROR(1,
+ (" Invalid Word count %d: ",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+
+ if (smb_buffer)
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+static int
+CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
+ char *ntlm_session_key, int ntlmv2_flag,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ SESSION_SETUP_ANDX *pSMB;
+ SESSION_SETUP_ANDX *pSMBr;
+ char *bcc_ptr;
+ char *user;
+ char *domain;
+ int rc = 0;
+ int remaining_words = 0;
+ int bytes_returned = 0;
+ int len;
+ int SecurityBlobLength = sizeof (AUTHENTICATE_MESSAGE);
+ PAUTHENTICATE_MESSAGE SecurityBlob;
+ __u32 negotiate_flags, capabilities;
+ __u16 count;
+
+ cFYI(1, ("In NTLMSSPSessSetup (Authenticate)"));
+ if(ses == NULL)
+ return -EINVAL;
+ user = ses->userName;
+ domain = ses->domainName;
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+ pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
+ pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
+
+ /* send SMBsessionSetup here */
+ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
+ NULL /* no tCon exists yet */ , 12 /* wct */ );
+ pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
+ pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
+ pSMB->req.AndXCommand = 0xFF;
+ pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
+ pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
+
+ pSMB->req.hdr.Uid = ses->Suid;
+
+ if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
+ CAP_EXTENDED_SECURITY;
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ capabilities |= CAP_UNICODE;
+ }
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ capabilities |= CAP_STATUS32;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ capabilities |= CAP_DFS;
+ }
+ pSMB->req.Capabilities = cpu_to_le32(capabilities);
+
+ bcc_ptr = (char *) &pSMB->req.SecurityBlob;
+ SecurityBlob = (PAUTHENTICATE_MESSAGE) bcc_ptr;
+ strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
+ SecurityBlob->MessageType = NtLmAuthenticate;
+ bcc_ptr += SecurityBlobLength;
+ negotiate_flags =
+ NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
+ 0x80000000 | NTLMSSP_NEGOTIATE_128;
+ if(sign_CIFS_PDUs)
+ negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN;
+ if(ntlmv2_flag)
+ negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
+
+/* setup pointers to domain name and workstation name */
+
+ SecurityBlob->WorkstationName.Buffer = 0;
+ SecurityBlob->WorkstationName.Length = 0;
+ SecurityBlob->WorkstationName.MaximumLength = 0;
+ SecurityBlob->SessionKey.Length = 0;
+ SecurityBlob->SessionKey.MaximumLength = 0;
+ SecurityBlob->SessionKey.Buffer = 0;
+
+ SecurityBlob->LmChallengeResponse.Length = 0;
+ SecurityBlob->LmChallengeResponse.MaximumLength = 0;
+ SecurityBlob->LmChallengeResponse.Buffer = 0;
+
+ SecurityBlob->NtChallengeResponse.Length =
+ cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ SecurityBlob->NtChallengeResponse.MaximumLength =
+ cpu_to_le16(CIFS_SESSION_KEY_SIZE);
+ memcpy(bcc_ptr, ntlm_session_key, CIFS_SESSION_KEY_SIZE);
+ SecurityBlob->NtChallengeResponse.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ SecurityBlobLength += CIFS_SESSION_KEY_SIZE;
+ bcc_ptr += CIFS_SESSION_KEY_SIZE;
+
+ if (ses->capabilities & CAP_UNICODE) {
+ if (domain == NULL) {
+ SecurityBlob->DomainName.Buffer = 0;
+ SecurityBlob->DomainName.Length = 0;
+ SecurityBlob->DomainName.MaximumLength = 0;
+ } else {
+ __u16 len =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, domain, 64,
+ nls_codepage);
+ len *= 2;
+ SecurityBlob->DomainName.MaximumLength =
+ cpu_to_le16(len);
+ SecurityBlob->DomainName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += len;
+ SecurityBlobLength += len;
+ SecurityBlob->DomainName.Length =
+ cpu_to_le16(len);
+ }
+ if (user == NULL) {
+ SecurityBlob->UserName.Buffer = 0;
+ SecurityBlob->UserName.Length = 0;
+ SecurityBlob->UserName.MaximumLength = 0;
+ } else {
+ __u16 len =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, user, 64,
+ nls_codepage);
+ len *= 2;
+ SecurityBlob->UserName.MaximumLength =
+ cpu_to_le16(len);
+ SecurityBlob->UserName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += len;
+ SecurityBlobLength += len;
+ SecurityBlob->UserName.Length =
+ cpu_to_le16(len);
+ }
+
+ /* SecurityBlob->WorkstationName.Length = cifs_strtoUCS((wchar_t *) bcc_ptr, "AMACHINE",64, nls_codepage);
+ SecurityBlob->WorkstationName.Length *= 2;
+ SecurityBlob->WorkstationName.MaximumLength = cpu_to_le16(SecurityBlob->WorkstationName.Length);
+ SecurityBlob->WorkstationName.Buffer = cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += SecurityBlob->WorkstationName.Length;
+ SecurityBlobLength += SecurityBlob->WorkstationName.Length;
+ SecurityBlob->WorkstationName.Length = cpu_to_le16(SecurityBlob->WorkstationName.Length); */
+
+ if ((long) bcc_ptr % 2) {
+ *bcc_ptr = 0;
+ bcc_ptr++;
+ }
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ",
+ 32, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, system_utsname.release, 32,
+ nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ bcc_ptr += 2; /* null term version string */
+ bytes_returned =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, CIFS_NETWORK_OPSYS,
+ 64, nls_codepage);
+ bcc_ptr += 2 * bytes_returned;
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null terminate network opsys string */
+ *(bcc_ptr + 1) = 0;
+ *(bcc_ptr + 2) = 0;
+ bcc_ptr += 2; /* null domain */
+ } else { /* ASCII */
+ if (domain == NULL) {
+ SecurityBlob->DomainName.Buffer = 0;
+ SecurityBlob->DomainName.Length = 0;
+ SecurityBlob->DomainName.MaximumLength = 0;
+ } else {
+ __u16 len;
+ negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
+ strncpy(bcc_ptr, domain, 63);
+ len = strnlen(domain, 64);
+ SecurityBlob->DomainName.MaximumLength =
+ cpu_to_le16(len);
+ SecurityBlob->DomainName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += len;
+ SecurityBlobLength += len;
+ SecurityBlob->DomainName.Length = cpu_to_le16(len);
+ }
+ if (user == NULL) {
+ SecurityBlob->UserName.Buffer = 0;
+ SecurityBlob->UserName.Length = 0;
+ SecurityBlob->UserName.MaximumLength = 0;
+ } else {
+ __u16 len;
+ strncpy(bcc_ptr, user, 63);
+ len = strnlen(user, 64);
+ SecurityBlob->UserName.MaximumLength =
+ cpu_to_le16(len);
+ SecurityBlob->UserName.Buffer =
+ cpu_to_le32(SecurityBlobLength);
+ bcc_ptr += len;
+ SecurityBlobLength += len;
+ SecurityBlob->UserName.Length = cpu_to_le16(len);
+ }
+ /* BB fill in our workstation name if known BB */
+
+ strcpy(bcc_ptr, "Linux version ");
+ bcc_ptr += strlen("Linux version ");
+ strcpy(bcc_ptr, system_utsname.release);
+ bcc_ptr += strlen(system_utsname.release) + 1;
+ strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
+ bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
+ bcc_ptr++; /* null domain */
+ *bcc_ptr = 0;
+ }
+ SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
+ pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
+ count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
+ smb_buffer->smb_buf_length += count;
+ pSMB->req.ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
+ &bytes_returned, 1);
+ if (rc) {
+/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
+ } else if ((smb_buffer_response->WordCount == 3)
+ || (smb_buffer_response->WordCount == 4)) {
+ __u16 action = le16_to_cpu(pSMBr->resp.Action);
+ __u16 blob_len =
+ le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+ if (action & GUEST_LOGIN)
+ cFYI(1, (" Guest login")); /* BB do we want to set anything in SesInfo struct ? */
+/* if(SecurityBlob2->MessageType != NtLm??){
+ cFYI("Unexpected message type on auth response is %d "));
+ } */
+ if (ses) {
+ cFYI(1,
+ ("Does UID on challenge %d match auth response UID %d ",
+ ses->Suid, smb_buffer_response->Uid));
+ ses->Suid = smb_buffer_response->Uid; /* UID left in wire format */
+ bcc_ptr = pByteArea(smb_buffer_response);
+ /* response can have either 3 or 4 word count - Samba sends 3 */
+ if ((pSMBr->resp.hdr.WordCount == 3)
+ || ((pSMBr->resp.hdr.WordCount == 4)
+ && (blob_len <
+ pSMBr->resp.ByteCount))) {
+ if (pSMBr->resp.hdr.WordCount == 4) {
+ bcc_ptr +=
+ blob_len;
+ cFYI(1,
+ ("Security Blob Length %d ",
+ blob_len));
+ }
+
+ cFYI(1,
+ ("NTLMSSP response to Authenticate "));
+
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ if ((long) (bcc_ptr) % 2) {
+ remaining_words =
+ (BCC(smb_buffer_response)
+ - 1) / 2;
+ bcc_ptr++; /* Unicode strings must be word aligned */
+ } else {
+ remaining_words = BCC(smb_buffer_response) / 2;
+ }
+ len =
+ UniStrnlen((wchar_t *) bcc_ptr,remaining_words - 1);
+/* We look for obvious messed up bcc or strings in response so we do not go off
+ the end since (at least) WIN2K and Windows XP have a major bug in not null
+ terminating last Unicode string in response */
+ ses->serverOS =
+ cifs_kcalloc(2 * (len + 1), GFP_KERNEL);
+ cifs_strfromUCS_le(ses->serverOS,
+ (wchar_t *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ remaining_words -= len + 1;
+ ses->serverOS[2 * len] = 0;
+ ses->serverOS[1 + (2 * len)] = 0;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *)
+ bcc_ptr,
+ remaining_words
+ - 1);
+ ses->serverNOS =
+ cifs_kcalloc(2 * (len + 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le(ses->
+ serverNOS,
+ (wchar_t *)
+ bcc_ptr,
+ len,
+ nls_codepage);
+ bcc_ptr += 2 * (len + 1);
+ ses->serverNOS[2 * len] = 0;
+ ses->serverNOS[1+(2*len)] = 0;
+ remaining_words -= len + 1;
+ if (remaining_words > 0) {
+ len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
+ /* last string not always null terminated (e.g. for Windows XP & 2000) */
+ ses->serverDomain =
+ cifs_kcalloc(2 *
+ (len +
+ 1),
+ GFP_KERNEL);
+ cifs_strfromUCS_le
+ (ses->
+ serverDomain,
+ (wchar_t *)
+ bcc_ptr, len,
+ nls_codepage);
+ bcc_ptr +=
+ 2 * (len + 1);
+ ses->
+ serverDomain[2
+ * len]
+ = 0;
+ ses->
+ serverDomain[1
+ +
+ (2
+ *
+ len)]
+ = 0;
+ } /* else no more room so create dummy domain string */
+ else
+ ses->serverDomain = cifs_kcalloc(2,GFP_KERNEL);
+ } else { /* no room so create dummy domain and NOS string */
+ ses->serverDomain = cifs_kcalloc(2, GFP_KERNEL);
+ ses->serverNOS = cifs_kcalloc(2, GFP_KERNEL);
+ }
+ } else { /* ASCII */
+ len = strnlen(bcc_ptr, 1024);
+ if (((long) bcc_ptr + len) -
+ (long) pByteArea(smb_buffer_response)
+ <= BCC(smb_buffer_response)) {
+ ses->serverOS = cifs_kcalloc(len + 1,GFP_KERNEL);
+ strncpy(ses->serverOS,bcc_ptr, len);
+
+ bcc_ptr += len;
+ bcc_ptr[0] = 0; /* null terminate the string */
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverNOS = cifs_kcalloc(len+1,GFP_KERNEL);
+ strncpy(ses->serverNOS, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+
+ len = strnlen(bcc_ptr, 1024);
+ ses->serverDomain = cifs_kcalloc(len+1,GFP_KERNEL);
+ strncpy(ses->serverDomain, bcc_ptr, len);
+ bcc_ptr += len;
+ bcc_ptr[0] = 0;
+ bcc_ptr++;
+ } else
+ cFYI(1,
+ ("Variable field of length %d extends beyond end of smb ",
+ len));
+ }
+ } else {
+ cERROR(1,
+ (" Security Blob Length extends beyond end of SMB"));
+ }
+ } else {
+ cERROR(1, ("No session structure passed in."));
+ }
+ } else {
+ cERROR(1,
+ (" Invalid Word count %d: ",
+ smb_buffer_response->WordCount));
+ rc = -EIO;
+ }
+
+ if (smb_buffer)
+ cifs_buf_release(smb_buffer);
+
+ return rc;
+}
+
+int
+CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
+ const char *tree, struct cifsTconInfo *tcon,
+ const struct nls_table *nls_codepage)
+{
+ struct smb_hdr *smb_buffer;
+ struct smb_hdr *smb_buffer_response;
+ TCONX_REQ *pSMB;
+ TCONX_RSP *pSMBr;
+ unsigned char *bcc_ptr;
+ int rc = 0;
+ int length;
+ __u16 count;
+
+ if (ses == NULL)
+ return -EIO;
+
+ smb_buffer = cifs_buf_get();
+ if (smb_buffer == NULL) {
+ return -ENOMEM;
+ }
+ smb_buffer_response = smb_buffer;
+
+ header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
+ NULL /*no tid */ , 4 /*wct */ );
+ smb_buffer->Uid = ses->Suid;
+ pSMB = (TCONX_REQ *) smb_buffer;
+ pSMBr = (TCONX_RSP *) smb_buffer_response;
+
+ pSMB->AndXCommand = 0xFF;
+ pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
+ pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
+ bcc_ptr = &pSMB->Password[0];
+ bcc_ptr++; /* skip password */
+
+ if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+
+ if (ses->capabilities & CAP_STATUS32) {
+ smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
+ }
+ if (ses->capabilities & CAP_DFS) {
+ smb_buffer->Flags2 |= SMBFLG2_DFS;
+ }
+ if (ses->capabilities & CAP_UNICODE) {
+ smb_buffer->Flags2 |= SMBFLG2_UNICODE;
+ length =
+ cifs_strtoUCS((wchar_t *) bcc_ptr, tree, 100, nls_codepage);
+ bcc_ptr += 2 * length; /* convert num of 16 bit words to bytes */
+ bcc_ptr += 2; /* skip trailing null */
+ } else { /* ASCII */
+
+ strcpy(bcc_ptr, tree);
+ bcc_ptr += strlen(tree) + 1;
+ }
+ strcpy(bcc_ptr, "?????");
+ bcc_ptr += strlen("?????");
+ bcc_ptr += 1;
+ count = bcc_ptr - &pSMB->Password[0];
+ pSMB->hdr.smb_buf_length += count;
+ pSMB->ByteCount = cpu_to_le16(count);
+
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, 0);
+
+ /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
+ /* above now done in SendReceive */
+ if ((rc == 0) && (tcon != NULL)) {
+ tcon->tidStatus = CifsGood;
+ tcon->tid = smb_buffer_response->Tid;
+ bcc_ptr = pByteArea(smb_buffer_response);
+ length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
+ /* skip service field (NB: this field is always ASCII) */
+ bcc_ptr += length + 1;
+ strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
+ if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
+ length = UniStrnlen((wchar_t *) bcc_ptr, 512);
+ if ((bcc_ptr + (2 * length)) -
+ pByteArea(smb_buffer_response) <=
+ BCC(smb_buffer_response)) {
+ if(tcon->nativeFileSystem)
+ kfree(tcon->nativeFileSystem);
+ tcon->nativeFileSystem =
+ cifs_kcalloc(length + 2, GFP_KERNEL);
+ cifs_strfromUCS_le(tcon->nativeFileSystem,
+ (wchar_t *) bcc_ptr,
+ length, nls_codepage);
+ bcc_ptr += 2 * length;
+ bcc_ptr[0] = 0; /* null terminate the string */
+ bcc_ptr[1] = 0;
+ bcc_ptr += 2;
+ }
+ /* else do not bother copying these informational fields */
+ } else {
+ length = strnlen(bcc_ptr, 1024);
+ if ((bcc_ptr + length) -
+ pByteArea(smb_buffer_response) <=
+ BCC(smb_buffer_response)) {
+ if(tcon->nativeFileSystem)
+ kfree(tcon->nativeFileSystem);
+ tcon->nativeFileSystem =
+ cifs_kcalloc(length + 1, GFP_KERNEL);
+ strncpy(tcon->nativeFileSystem, bcc_ptr,
+ length);
+ }
+ /* else do not bother copying these informational fields */
+ }
+ tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
+ cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags));
+ } else if ((rc == 0) && tcon == NULL) {
+ /* all we need to save for IPC$ connection */
+ ses->ipc_tid = smb_buffer_response->Tid;
+ }
+
+ if (smb_buffer)
+ cifs_buf_release(smb_buffer);
+ return rc;
+}
+
+int
+cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
+{
+ int rc = 0;
+ int xid;
+ struct cifsSesInfo *ses = NULL;
+ struct task_struct *cifsd_task;
+
+ xid = GetXid();
+
+ if (cifs_sb->tcon) {
+ ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
+ rc = CIFSSMBTDis(xid, cifs_sb->tcon);
+ if (rc == -EBUSY) {
+ FreeXid(xid);
+ return 0;
+ }
+ tconInfoFree(cifs_sb->tcon);
+ if ((ses) && (ses->server)) {
+ /* save off task so we do not refer to ses later */
+ cifsd_task = ses->server->tsk;
+ cFYI(1, ("About to do SMBLogoff "));
+ rc = CIFSSMBLogoff(xid, ses);
+ if (rc == -EBUSY) {
+ FreeXid(xid);
+ return 0;
+ } else if (rc == -ESHUTDOWN) {
+ cFYI(1,("Waking up socket by sending it signal"));
+ if(cifsd_task)
+ send_sig(SIGKILL,cifsd_task,1);
+ rc = 0;
+ } /* else - we have an smb session
+ left on this socket do not kill cifsd */
+ } else
+ cFYI(1, ("No session or bad tcon"));
+ }
+
+ cifs_sb->tcon = NULL;
+ if (ses) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(HZ / 2);
+ }
+ if (ses)
+ sesInfoFree(ses);
+
+ FreeXid(xid);
+ return rc; /* BB check if we should always return zero here */
+}
+
+int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
+ struct nls_table * nls_info)
+{
+ int rc = 0;
+ char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
+ int ntlmv2_flag = FALSE;
+
+ /* what if server changes its buffer size after dropping the session? */
+ if(pSesInfo->server->maxBuf == 0) /* no need to send on reconnect */ {
+ rc = CIFSSMBNegotiate(xid, pSesInfo);
+ if(rc == -EAGAIN) /* retry only once on 1st time connection */ {
+ rc = CIFSSMBNegotiate(xid, pSesInfo);
+ if(rc == -EAGAIN)
+ rc = -EHOSTDOWN;
+ }
+ if(rc == 0) {
+ spin_lock(&GlobalMid_Lock);
+ if(pSesInfo->server->tcpStatus != CifsExiting)
+ pSesInfo->server->tcpStatus = CifsGood;
+ else
+ rc = -EHOSTDOWN;
+ spin_unlock(&GlobalMid_Lock);
+
+ }
+ }
+ if (!rc) {
+ pSesInfo->capabilities = pSesInfo->server->capabilities;
+ if(linuxExtEnabled == 0)
+ pSesInfo->capabilities &= (~CAP_UNIX);
+ pSesInfo->sequence_number = 0;
+ cFYI(1,("Security Mode: 0x%x Capabilities: 0x%x Time Zone: %d",
+ pSesInfo->server->secMode,
+ pSesInfo->server->capabilities,
+ pSesInfo->server->timeZone));
+ if (extended_security
+ && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
+ && (pSesInfo->server->secType == NTLMSSP)) {
+ cFYI(1, ("New style sesssetup "));
+ rc = CIFSSpnegoSessSetup(xid, pSesInfo,
+ NULL /* security blob */,
+ 0 /* blob length */,
+ nls_info);
+ } else if (extended_security
+ && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
+ && (pSesInfo->server->secType == RawNTLMSSP)) {
+ cFYI(1, ("NTLMSSP sesssetup "));
+ rc = CIFSNTLMSSPNegotiateSessSetup(xid,
+ pSesInfo,
+ &ntlmv2_flag,
+ nls_info);
+ if (!rc) {
+ if(ntlmv2_flag) {
+ char * v2_response;
+ cFYI(1,("Can use more secure NTLM version 2 password hash"));
+ if(CalcNTLMv2_partial_mac_key(pSesInfo,
+ nls_info)) {
+ rc = -ENOMEM;
+ goto ss_err_exit;
+ } else
+ v2_response = kmalloc(16 + 64 /* blob */, GFP_KERNEL);
+ if(v2_response) {
+ CalcNTLMv2_response(pSesInfo,v2_response);
+/* cifs_calculate_ntlmv2_mac_key(pSesInfo->mac_signing_key, response, ntlm_session_key, */
+ kfree(v2_response);
+ /* BB Put dummy sig in SessSetup PDU? */
+ } else {
+ rc = -ENOMEM;
+ goto ss_err_exit;
+ }
+
+ } else {
+ SMBNTencrypt(pSesInfo->password,
+ pSesInfo->server->cryptKey,
+ ntlm_session_key);
+
+ cifs_calculate_mac_key(pSesInfo->mac_signing_key,
+ ntlm_session_key,
+ pSesInfo->password);
+ }
+ /* for better security the weaker lanman hash not sent
+ in AuthSessSetup so we no longer calculate it */
+
+ rc = CIFSNTLMSSPAuthSessSetup(xid,
+ pSesInfo,
+ ntlm_session_key,
+ ntlmv2_flag,
+ nls_info);
+ }
+ } else { /* old style NTLM 0.12 session setup */
+ SMBNTencrypt(pSesInfo->password,
+ pSesInfo->server->cryptKey,
+ ntlm_session_key);
+
+ cifs_calculate_mac_key(pSesInfo->mac_signing_key,
+ ntlm_session_key, pSesInfo->password);
+ rc = CIFSSessSetup(xid, pSesInfo,
+ ntlm_session_key, nls_info);
+ }
+ if (rc) {
+ cERROR(1,("Send error in SessSetup = %d",rc));
+ } else {
+ cFYI(1,("CIFS Session Established successfully"));
+ pSesInfo->status = CifsGood;
+ }
+ }
+ss_err_exit:
+ return rc;
+}
+