X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fcifs%2Fconnect.c;h=2caca06b4bae214327c3fccc7a046f6a0269ac37;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=bb6b0f3b2fb205621e217363bdc95e7e51360cfb;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index bb6b0f3b2..2caca06b4 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1,7 +1,7 @@ /* * fs/cifs/connect.c * - * Copyright (C) International Business Machines Corp., 2002,2004 + * Copyright (C) International Business Machines Corp., 2002,2006 * Author(s): Steve French (sfrench@us.ibm.com) * * This library is free software; you can redistribute it and/or modify @@ -23,12 +23,15 @@ #include #include #include -#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include "cifspdu.h" @@ -40,15 +43,15 @@ #include "ntlmssp.h" #include "nterr.h" #include "rfc1002pdu.h" +#include "cn_cifs.h" #define CIFS_PORT 445 #define RFC1001_PORT 139 -extern void SMBencrypt(unsigned char *passwd, unsigned char *c8, - unsigned char *p24); +static DECLARE_COMPLETION(cifsd_complete); + extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24); -extern int cifs_inet_pton(int, const char *, void *dst); extern mempool_t *cifs_req_poolp; @@ -58,26 +61,42 @@ struct smb_vol { 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 */ + char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */ uid_t linux_uid; gid_t linux_gid; mode_t file_mode; mode_t dir_mode; - int rw:1; - int retry:1; - int intr:1; - int setuids:1; - int noperm:1; + unsigned secFlg; + 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 cifs_acl:1; + 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 remap:1; /* set to remap seven reserved chars in filenames */ + unsigned posix_paths:1; /* unset to not ask for posix pathnames. */ + unsigned sfu_emul:1; + unsigned nullauth:1; /* attempt to authenticate with null user */ + unsigned nocase; /* request case insensitive filenames */ + unsigned nobrl; /* disable sending byte range locks to srv */ unsigned int rsize; unsigned int wsize; unsigned int sockopt; unsigned short int port; + char * prepath; }; static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netb_name); + char * netb_name, + char * server_netb_name); static int ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket); @@ -91,7 +110,7 @@ static int ipv6_connect(struct sockaddr_in6 *psin_server, * wake up waiters on reconnection? - (not needed currently) */ -int +static int cifs_reconnect(struct TCP_Server_Info *server) { int rc = 0; @@ -111,7 +130,7 @@ cifs_reconnect(struct TCP_Server_Info *server) spin_unlock(&GlobalMid_Lock); server->maxBuf = 0; - cFYI(1, ("Reconnecting tcp session ")); + 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 */ @@ -152,9 +171,10 @@ cifs_reconnect(struct TCP_Server_Info *server) 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 */ + /* 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; } } @@ -164,22 +184,25 @@ cifs_reconnect(struct TCP_Server_Info *server) while ((server->tcpStatus != CifsExiting) && (server->tcpStatus != CifsGood)) { + try_to_freeze(); 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); + server->workstation_RFC1001_name, + server->server_RFC1001_name); } if(rc) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(3 * HZ); + cFYI(1,("reconnect error %d",rc)); + msleep(3000); } else { atomic_inc(&tcpSesReconnectCount); spin_lock(&GlobalMid_Lock); if(server->tcpStatus != CifsExiting) server->tcpStatus = CifsGood; - spin_unlock(&GlobalMid_Lock); + server->sequence_number = 0; + spin_unlock(&GlobalMid_Lock); /* atomic_set(&server->inFlight,0);*/ wake_up(&server->response_q); } @@ -187,12 +210,129 @@ cifs_reconnect(struct TCP_Server_Info *server) return rc; } +/* + return codes: + 0 not a transact2, or all data present + >0 transact2 with that much data missing + -EINVAL = invalid transact2 + + */ +static int check2ndT2(struct smb_hdr * pSMB, unsigned int maxBufSize) +{ + struct smb_t2_rsp * pSMBt; + int total_data_size; + int data_in_this_rsp; + int remaining; + + if(pSMB->Command != SMB_COM_TRANSACTION2) + return 0; + + /* check for plausible wct, bcc and t2 data and parm sizes */ + /* check for parm and data offset going beyond end of smb */ + if(pSMB->WordCount != 10) { /* coalesce_t2 depends on this */ + cFYI(1,("invalid transact2 word count")); + return -EINVAL; + } + + pSMBt = (struct smb_t2_rsp *)pSMB; + + total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); + data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount); + + remaining = total_data_size - data_in_this_rsp; + + if(remaining == 0) + return 0; + else if(remaining < 0) { + cFYI(1,("total data %d smaller than data in frame %d", + total_data_size, data_in_this_rsp)); + return -EINVAL; + } else { + cFYI(1,("missing %d bytes from transact2, check next response", + remaining)); + if(total_data_size > maxBufSize) { + cERROR(1,("TotalDataSize %d is over maximum buffer %d", + total_data_size,maxBufSize)); + return -EINVAL; + } + return remaining; + } +} + +static int coalesce_t2(struct smb_hdr * psecond, struct smb_hdr *pTargetSMB) +{ + struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond; + struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB; + int total_data_size; + int total_in_buf; + int remaining; + int total_in_buf2; + char * data_area_of_target; + char * data_area_of_buf2; + __u16 byte_count; + + total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount); + + if(total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) { + cFYI(1,("total data sizes of primary and secondary t2 differ")); + } + + total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount); + + remaining = total_data_size - total_in_buf; + + if(remaining < 0) + return -EINVAL; + + if(remaining == 0) /* nothing to do, ignore */ + return 0; + + total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount); + if(remaining < total_in_buf2) { + cFYI(1,("transact2 2nd response contains too much data")); + } + + /* find end of first SMB data area */ + data_area_of_target = (char *)&pSMBt->hdr.Protocol + + le16_to_cpu(pSMBt->t2_rsp.DataOffset); + /* validate target area */ + + data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol + + le16_to_cpu(pSMB2->t2_rsp.DataOffset); + + data_area_of_target += total_in_buf; + + /* copy second buffer into end of first buffer */ + memcpy(data_area_of_target,data_area_of_buf2,total_in_buf2); + total_in_buf += total_in_buf2; + pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf); + byte_count = le16_to_cpu(BCC_LE(pTargetSMB)); + byte_count += total_in_buf2; + BCC_LE(pTargetSMB) = cpu_to_le16(byte_count); + + byte_count = pTargetSMB->smb_buf_length; + byte_count += total_in_buf2; + + /* BB also add check that we are not beyond maximum buffer size */ + + pTargetSMB->smb_buf_length = byte_count; + + if(remaining == total_in_buf2) { + cFYI(1,("found the last secondary response")); + return 0; /* we are done */ + } else /* more responses to go */ + return 1; + +} + 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 smb_hdr *bigbuf = NULL; + struct smb_hdr *smallbuf = NULL; struct msghdr smb_msg; struct kvec iov; struct socket *csocket = server->ssocket; @@ -200,244 +340,346 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) struct cifsSesInfo *ses; struct task_struct *task_to_wake = NULL; struct mid_q_entry *mid_entry; - char *temp; + char temp; + int isLargeBuf = FALSE; + int isMultiRsp; + int reconnect; 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); + write_lock(&GlobalSMBSeslock); atomic_inc(&tcpSesAllocCount); length = tcpSesAllocCount.counter; write_unlock(&GlobalSMBSeslock); + complete(&cifsd_complete); if(length > 1) { mempool_resize(cifs_req_poolp, - length + CIFS_MIN_RCV_POOL, + 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 */ + if (try_to_freeze()) continue; + if (bigbuf == NULL) { + bigbuf = cifs_buf_get(); + if (!bigbuf) { + cERROR(1, ("No memory for large SMB response")); + msleep(3000); + /* retry will check if exiting */ + continue; + } + } else if (isLargeBuf) { + /* we are reusing a dirty large buf, clear its start */ + memset(bigbuf, 0, sizeof (struct smb_hdr)); } + + if (smallbuf == NULL) { + smallbuf = cifs_small_buf_get(); + if (!smallbuf) { + cERROR(1, ("No memory for SMB response")); + msleep(1000); + /* retry will check if exiting */ + continue; + } + /* beginning of smb buffer is cleared in our buf_get */ + } else /* if existing small buf clear beginning */ + memset(smallbuf, 0, sizeof (struct smb_hdr)); + + isLargeBuf = FALSE; + isMultiRsp = FALSE; + smb_buffer = smallbuf; iov.iov_base = smb_buffer; - iov.iov_len = sizeof (struct smb_hdr) - 1; - /* 1 byte less above since wct is not always returned in error cases */ + iov.iov_len = 4; smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; - length = kernel_recvmsg(csocket, &smb_msg, - &iov, 1, - sizeof (struct smb_hdr) - - 1 /* RFC1001 header and SMB header */ , - MSG_PEEK /* flags see socket.h */ ); + &iov, 1, 4, 0 /* BB see socket.h flags */); - if(server->tcpStatus == CifsExiting) { + if (server->tcpStatus == CifsExiting) { break; } else if (server->tcpStatus == CifsNeedReconnect) { - cFYI(1,("Reconnecting after server stopped responding")); + cFYI(1, ("Reconnect after server stopped responding")); cifs_reconnect(server); - cFYI(1,("call to reconnect done")); + 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 + msleep(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 */ + if (server->tcpStatus == CifsNew) { + cFYI(1, ("tcp session abend after SMBnegprot")); + /* some servers kill the TCP session rather than + returning an SMB negprot error, in which + case reconnecting here is not going to help, + and so simply return error to mount */ break; } - if(length == -EINTR) { + if (!try_to_freeze() && (length == -EINTR)) { cFYI(1,("cifsd thread killed")); break; } - cFYI(1,("Reconnecting after unexpected peek error %d",length)); + cFYI(1,("Reconnect after unexpected peek error %d", + length)); + cifs_reconnect(server); + csocket = server->ssocket; + wake_up(&server->response_q); + continue; + } else if (length < 4) { + cFYI(1, + ("Frame under four bytes received (%d bytes long)", + length)); cifs_reconnect(server); csocket = server->ssocket; wake_up(&server->response_q); continue; } - pdu_length = 4 + ntohl(smb_buffer->smb_buf_length); - /* Ony 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, ("Peek length rcvd: 0x%x beginning 0x%x)", length, pdu_length)); - - temp = (char *) smb_buffer; - if (length > 3) { - if (temp[0] == (char) RFC1002_SESSION_KEEP_ALIVE) { - iov.iov_base = smb_buffer; - iov.iov_len = 4; - length = kernel_recvmsg(csocket, &smb_msg, - &iov, 1, 4, 0); - cFYI(0,("Received 4 byte keep alive packet")); - } else if (temp[0] == (char) RFC1002_POSITIVE_SESSION_RESPONSE) { - iov.iov_base = smb_buffer; - iov.iov_len = 4; - length = kernel_recvmsg(csocket, &smb_msg, - &iov, 1, 4, 0); - cFYI(1,("Good RFC 1002 session rsp")); - } else if ((temp[0] == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) - && (length == 5)) { - /* 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 = 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); + /* The right amount was read from socket - 4 bytes */ + /* so we can now interpret the length field */ + + /* the first byte big endian of the length field, + is actually not part of the length but the type + with the most common, zero, as regular data */ + temp = *((char *) smb_buffer); + + /* Note that FC 1001 length is big endian on the wire, + but we convert it here so it is always manipulated + as host byte order */ + pdu_length = ntohl(smb_buffer->smb_buf_length); + smb_buffer->smb_buf_length = pdu_length; + + cFYI(1,("rfc1002 length 0x%x)", pdu_length+4)); + + if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) { + continue; + } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) { + cFYI(1,("Good RFC 1002 session rsp")); + continue; + } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) { + /* we get this from Windows 98 instead of + an error on SMB negprot response */ + cFYI(1,("Negative RFC1002 Session Response Error 0x%x)", + pdu_length)); + 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 */ + msleep(1000); + /* 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 ((length != sizeof (struct smb_hdr) - 1) - || (pdu_length > - CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) - || (pdu_length < - sizeof (struct smb_hdr) - 1) - || - (checkSMBhdr - (smb_buffer, smb_buffer->Mid))) { - cERROR(1, - ("Invalid size or format for SMB found with length %d and pdu_lenght %d", - length, pdu_length)); - cifs_dump_mem("Received Data is: ",temp,sizeof(struct smb_hdr)); - /* could we fix this network corruption by finding next - smb header (instead of killing the session) and - restart reading from next valid SMB found? */ - cifs_reconnect(server); - csocket = server->ssocket; - continue; - } else { /* length ok */ - - length = 0; - iov.iov_base = 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; - continue; - } - } - } + } + } else if (temp != (char) 0) { + cERROR(1,("Unknown RFC 1002 frame")); + cifs_dump_mem(" Received Data: ", (char *)smb_buffer, + length); + cifs_reconnect(server); + csocket = server->ssocket; + continue; + } - dump_smb(smb_buffer, length); - if (checkSMB - (smb_buffer, smb_buffer->Mid, total_read)) { - cERROR(1, ("Bad SMB Received ")); - continue; - } + /* else we have an SMB response */ + if((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) || + (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) { + cERROR(1, ("Invalid size SMB length %d pdu_length %d", + length, pdu_length+4)); + cifs_reconnect(server); + csocket = server->ssocket; + wake_up(&server->response_q); + 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); + /* else length ok */ + reconnect = 0; - 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; + if(pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) { + isLargeBuf = TRUE; + memcpy(bigbuf, smallbuf, 4); + smb_buffer = bigbuf; + } + 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((server->tcpStatus == CifsExiting) || + (length == -EINTR)) { + /* then will exit */ + reconnect = 2; + break; + } else if (server->tcpStatus == CifsNeedReconnect) { + cifs_reconnect(server); + csocket = server->ssocket; + /* Reconnect wakes up rspns q */ + /* Now we will reread sock */ + reconnect = 1; + break; + } else if ((length == -ERESTARTSYS) || + (length == -EAGAIN)) { + msleep(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) { + cERROR(1,("Received no data, expecting %d", + pdu_length - total_read)); + cifs_reconnect(server); + csocket = server->ssocket; + reconnect = 1; + break; + } + } + if(reconnect == 2) + break; + else if(reconnect == 1) + continue; + + length += 4; /* account for rfc1002 hdr */ + + + dump_smb(smb_buffer, length); + if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) { + cifs_dump_mem("Bad SMB: ", smb_buffer, 48); + 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) && + (mid_entry->command == smb_buffer->Command)) { + if(check2ndT2(smb_buffer,server->maxBuf) > 0) { + /* We have a multipart transact2 resp */ + isMultiRsp = TRUE; + if(mid_entry->resp_buf) { + /* merge response - fix up 1st*/ + if(coalesce_t2(smb_buffer, + mid_entry->resp_buf)) { + mid_entry->multiRsp = 1; + break; + } else { + /* all parts received */ + mid_entry->multiEnd = 1; + goto multi_t2_fnd; + } + } else { + if(!isLargeBuf) { + cERROR(1,("1st trans2 resp needs bigbuf")); + /* BB maybe we can fix this up, switch + to already allocated large buffer? */ + } else { + /* Have first buffer */ + mid_entry->resp_buf = + smb_buffer; + mid_entry->largeBuf = 1; + bigbuf = NULL; + } } - } - 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)); - } + break; + } + mid_entry->resp_buf = smb_buffer; + if(isLargeBuf) + mid_entry->largeBuf = 1; + else + mid_entry->largeBuf = 0; +multi_t2_fnd: + task_to_wake = mid_entry->tsk; + mid_entry->midState = MID_RESPONSE_RECEIVED; +#ifdef CONFIG_CIFS_STATS2 + mid_entry->when_received = jiffies; +#endif + /* so we do not time out requests to server + which is still responding (since server could + be busy but not dead) */ + server->lstrp = jiffies; + break; } - } else { - cFYI(0, - ("Frame less than four bytes received %d bytes long.", - length)); - if (length > 0) { - length = kernel_recvmsg(csocket, &smb_msg, - &iov, 1, - length, 0); /* throw away junk frame */ - cFYI(1, - (" with junk 0x%x in it ", - *(__u32 *) smb_buffer)); + } + spin_unlock(&GlobalMid_Lock); + if (task_to_wake) { + /* Was previous buf put in mpx struct for multi-rsp? */ + if(!isMultiRsp) { + /* smb buffer will be freed by user thread */ + if(isLargeBuf) { + bigbuf = NULL; + } else + smallbuf = NULL; } + wake_up_process(task_to_wake); + } else if ((is_valid_oplock_break(smb_buffer, server) == FALSE) + && (isMultiRsp == FALSE)) { + cERROR(1, ("No task to wake, unknown frame rcvd! NumMids %d", midCount.counter)); + cifs_dump_mem("Received Data is: ",(char *)smb_buffer, + sizeof(struct smb_hdr)); +#ifdef CONFIG_CIFS_DEBUG2 + cifs_dump_detail(smb_buffer); + cifs_dump_mids(server); +#endif /* CIFS_DEBUG2 */ + } - } + } /* end while !EXITING */ + spin_lock(&GlobalMid_Lock); server->tcpStatus = CifsExiting; server->tsk = NULL; - atomic_set(&server->inFlight, 0); + /* check if we have blocked requests that need to free */ + /* Note that cifs_max_pending is normally 50, but + can be set at module install time to as little as two */ + if(atomic_read(&server->inFlight) >= cifs_max_pending) + atomic_set(&server->inFlight, cifs_max_pending - 1); + /* We do not want to set the max_pending too low or we + could end up with the counter going negative */ 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 + 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); - + msleep(125); + 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); + /* buffer usuallly freed in free_mid - need to free it here on exit */ + if (bigbuf != NULL) + cifs_buf_release(bigbuf); + if (smallbuf != NULL) + cifs_small_buf_release(smallbuf); read_lock(&GlobalSMBSeslock); if (list_empty(&server->pending_mid_q)) { - /* loop through server session structures attached to this and mark them dead */ + /* loop through server session structures attached to this and + mark them dead */ list_for_each(tmp, &GlobalSMBSessionList) { ses = list_entry(tmp, struct cifsSesInfo, @@ -449,12 +691,23 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } read_unlock(&GlobalSMBSeslock); } else { + /* although we can not zero the server struct pointer yet, + since there are active requests which may depnd on them, + mark the corresponding SMB sessions as exiting too */ + list_for_each(tmp, &GlobalSMBSessionList) { + ses = list_entry(tmp, struct cifsSesInfo, + cifsSessionList); + if (ses->server == server) { + ses->status = CifsExiting; + } + } + 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)); + ("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); @@ -463,49 +716,53 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) } 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); + msleep(125); } - if (list_empty(&server->pending_mid_q)) { + 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 */ + /* due to delays on oplock break requests, we need + to wait at least 45 seconds before giving up + on a request getting a response and going ahead + and killing cifsd */ cFYI(1, ("Wait for exit from demultiplex thread")); - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(46 * HZ); + msleep(46000); /* 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; + + /* last chance to mark ses pointers invalid + if there are any pointing to this (e.g + if a crazy root user tried to kill cifsd + kernel thread explicitly this might happen) */ + list_for_each(tmp, &GlobalSMBSessionList) { + ses = list_entry(tmp, struct cifsSesInfo, + cifsSessionList); + if (ses->server == server) { + ses->server = NULL; + } + } write_unlock(&GlobalSMBSeslock); + + kfree(server); if(length > 0) { mempool_resize(cifs_req_poolp, - length + CIFS_MIN_RCV_POOL, + length + cifs_min_rcv, GFP_KERNEL); } - - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ/4); + + complete_and_exit(&cifsd_complete, 0); return 0; } -static void * -cifs_kcalloc(size_t size, int 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) +cifs_parse_mount_options(char *options, const char *devname,struct smb_vol *vol) { char *value; char *data; @@ -515,15 +772,23 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol 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]); + if (Local_System_Name[0] != 0) + memcpy(vol->source_rfc1001_name, Local_System_Name,15); + else { + char *nodename = utsname()->nodename; + int n = strnlen(nodename,15); + memset(vol->source_rfc1001_name,0x20,15); + for(i=0 ; i < n ; i++) { + /* does not have to be perfect mapping since 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(nodename[i]); + } } vol->source_rfc1001_name[15] = 0; - + /* 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; /* current->euid instead? */ vol->linux_gid = current->gid; vol->dir_mode = S_IRWXUGO; @@ -532,6 +797,8 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */ vol->rw = TRUE; + /* default is always to request posix paths. */ + vol->posix_paths = 1; if (!options) return 1; @@ -550,11 +817,19 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol continue; if ((value = strchr(data, '=')) != NULL) *value++ = '\0'; - if (strnicmp(data, "user", 4) == 0) { - if (!value || !*value) { + + 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) { printk(KERN_WARNING "CIFS: invalid or missing username\n"); return 1; /* needs_arg; */ + } else if(!*value) { + /* null user, ie anonymous, authentication */ + vol->nullauth = 1; } if (strnlen(value, 200) < 200) { vol->username = value; @@ -563,9 +838,18 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol return 1; } } else if (strnicmp(data, "pass", 4) == 0) { - if (!value || !*value) { + 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 @@ -580,15 +864,23 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol /* 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])) { + + 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]) && (value[temp_len+1] != separator[0])) { - /* single comma indicating start of next parm */ - break; + if (value[temp_len] == separator[0]) { + if (value[temp_len+1] == + separator[0]) { + /* skip second comma */ + temp_len++; + } else { + /* single comma indicating start + of next parm */ + break; + } } temp_len++; } @@ -596,22 +888,32 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol options = NULL; } else { value[temp_len] = 0; - /* move options to point to start of next parm */ + /* point option to start of next parm */ options = value + temp_len + 1; } - /* go from value to (value + temp_len) condensing double commas to singles */ - vol->password = cifs_kcalloc(temp_len, GFP_KERNEL); + /* 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 = kzalloc(temp_len, GFP_KERNEL); + if(vol->password == NULL) { + printk("CIFS: no memory for pass\n"); + return 1; + } for(i=0,j=0;ipassword[j] = value[i]; - if(value[i] == separator[0] && value[i+1] == separator[0]) { + if(value[i] == separator[0] + && value[i+1] == separator[0]) { /* skip second comma */ i++; } } - /* value[temp_len] is zeroed above so - vol->password[temp_len] guaranteed to be null */ + vol->password[j] = 0; } else { - vol->password = cifs_kcalloc(temp_len + 1, GFP_KERNEL); + vol->password = kzalloc(temp_len+1, GFP_KERNEL); + if(vol->password == NULL) { + printk("CIFS: no memory for pass\n"); + return 1; + } strcpy(vol->password, value); } } else if (strnicmp(data, "ip", 2) == 0) { @@ -623,6 +925,44 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol printk(KERN_WARNING "CIFS: ip address too long\n"); return 1; } + } else if (strnicmp(data, "sec", 3) == 0) { + if (!value || !*value) { + cERROR(1,("no security value specified")); + continue; + } else if (strnicmp(value, "krb5i", 5) == 0) { + vol->secFlg |= CIFSSEC_MAY_KRB5 | + CIFSSEC_MUST_SIGN; + } else if (strnicmp(value, "krb5p", 5) == 0) { + /* vol->secFlg |= CIFSSEC_MUST_SEAL | + CIFSSEC_MAY_KRB5; */ + cERROR(1,("Krb5 cifs privacy not supported")); + return 1; + } else if (strnicmp(value, "krb5", 4) == 0) { + vol->secFlg |= CIFSSEC_MAY_KRB5; + } else if (strnicmp(value, "ntlmv2i", 7) == 0) { + vol->secFlg |= CIFSSEC_MAY_NTLMV2 | + CIFSSEC_MUST_SIGN; + } else if (strnicmp(value, "ntlmv2", 6) == 0) { + vol->secFlg |= CIFSSEC_MAY_NTLMV2; + } else if (strnicmp(value, "ntlmi", 5) == 0) { + vol->secFlg |= CIFSSEC_MAY_NTLM | + CIFSSEC_MUST_SIGN; + } else if (strnicmp(value, "ntlm", 4) == 0) { + /* ntlm is default so can be turned off too */ + vol->secFlg |= CIFSSEC_MAY_NTLM; + } else if (strnicmp(value, "nontlm", 6) == 0) { + /* BB is there a better way to do this? */ + vol->secFlg |= CIFSSEC_MAY_NTLMV2; +#ifdef CONFIG_CIFS_WEAK_PW_HASH + } else if (strnicmp(value, "lanman", 6) == 0) { + vol->secFlg |= CIFSSEC_MAY_LANMAN; +#endif + } else if (strnicmp(value, "none", 4) == 0) { + vol->nullauth = 1; + } else { + cERROR(1,("bad security option: %s", value)); + return 1; + } } else if ((strnicmp(data, "unc", 3) == 0) || (strnicmp(data, "target", 6) == 0) || (strnicmp(data, "path", 4) == 0)) { @@ -656,13 +996,35 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol } /* BB are there cases in which a comma can be valid in a domain name and need special handling? */ - if (strnlen(value, 65) < 65) { + if (strnlen(value, 256) < 256) { vol->domainname = value; cFYI(1, ("Domain name set")); } else { printk(KERN_WARNING "CIFS: domain name too long\n"); return 1; } + } else if (strnicmp(data, "prefixpath", 10) == 0) { + if (!value || !*value) { + printk(KERN_WARNING + "CIFS: invalid path prefix\n"); + return 1; /* needs_arg; */ + } + if ((temp_len = strnlen(value, 1024)) < 1024) { + if(value[0] != '/') + temp_len++; /* missing leading slash */ + vol->prepath = kmalloc(temp_len+1,GFP_KERNEL); + if(vol->prepath == NULL) + return 1; + if(value[0] != '/') { + vol->prepath[0] = '/'; + strcpy(vol->prepath+1,value); + } else + strcpy(vol->prepath,value); + cFYI(1,("prefix path %s",vol->prepath)); + } else { + printk(KERN_WARNING "CIFS: prefix too long\n"); + return 1; + } } else if (strnicmp(data, "iocharset", 9) == 0) { if (!value || !*value) { printk(KERN_WARNING "CIFS: invalid iocharset specified\n"); @@ -692,7 +1054,12 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol vol->file_mode = simple_strtoul(value, &value, 0); } - } else if (strnicmp(data, "dir_mode", 3) == 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); @@ -736,12 +1103,38 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol /* 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"); + printk(KERN_WARNING "CIFS: netbiosname longer than 15 truncated.\n"); + } + } else if (strnicmp(data, "servern", 7) == 0) { + /* servernetbiosname specified override *SMBSERVER */ + if (!value || !*value || (*value == ' ')) { + cFYI(1,("empty server netbiosname specified")); + } else { + /* last byte, type, is 0x20 for servr type */ + memset(vol->target_rfc1001_name,0x20,16); + + 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)? */ + + /* user or mount helper must uppercase netbiosname */ + if (value[i]==0) + break; + else + vol->target_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: server netbiosname longer than 15 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) || @@ -768,6 +1161,31 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol vol->noperm = 0; } else if (strnicmp(data, "noperm", 6) == 0) { vol->noperm = 1; + } else if (strnicmp(data, "mapchars", 8) == 0) { + vol->remap = 1; + } else if (strnicmp(data, "nomapchars", 10) == 0) { + vol->remap = 0; + } else if (strnicmp(data, "sfu", 3) == 0) { + vol->sfu_emul = 1; + } else if (strnicmp(data, "nosfu", 5) == 0) { + vol->sfu_emul = 0; + } else if (strnicmp(data, "posixpaths", 10) == 0) { + vol->posix_paths = 1; + } else if (strnicmp(data, "noposixpaths", 12) == 0) { + vol->posix_paths = 0; + } else if ((strnicmp(data, "nocase", 6) == 0) || + (strnicmp(data, "ignorecase", 10) == 0)) { + vol->nocase = 1; + } else if (strnicmp(data, "brl", 3) == 0) { + vol->nobrl = 0; + } else if ((strnicmp(data, "nobrl", 5) == 0) || + (strnicmp(data, "nolock", 6) == 0)) { + vol->nobrl = 1; + /* turn off mandatory locking in mode + if remote locking is turned off since the + local vfs will do advisory */ + if(vol->file_mode == (S_IALLUGO & ~(S_ISUID | S_IXGRP))) + vol->file_mode = S_IALLUGO; } else if (strnicmp(data, "setuids", 7) == 0) { vol->setuids = 1; } else if (strnicmp(data, "nosetuids", 9) == 0) { @@ -780,6 +1198,35 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol 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, "cifsacl",7) == 0) { + vol->cifs_acl = 1; + } else if (strnicmp(data, "nocifsacl", 9) == 0) { + vol->cifs_acl = 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, "sign",4) == 0) { + vol->secFlg |= CIFSSEC_MUST_SIGN; +/* } else if (strnicmp(data, "seal",4) == 0) { + vol->secFlg |= CIFSSEC_MUST_SEAL; */ + } 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 @@ -807,26 +1254,30 @@ cifs_parse_mount_options(char *options, const char *devname, struct smb_vol *vol return 1; } } - if(vol->UNCip == 0) + if(vol->UNCip == NULL) vol->UNCip = &vol->UNC[2]; return 0; } static struct cifsSesInfo * -cifs_find_tcp_session(__u32 new_target_ip_addr, +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 (ses->server->addr.sockAddr.sin_addr.s_addr == - new_target_ip_addr) { + 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 */ @@ -845,40 +1296,42 @@ cifs_find_tcp_session(__u32 new_target_ip_addr, } static struct cifsTconInfo * -find_unc(__u32 new_target_ip_addr, char *uncName, char *userName) +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 - ")); + 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 ?", + ("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? */ + /* BB lock tcon, 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 ?", + cFYI(1,("IP match, 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 ?", + ("and old usr: %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)*/ + /* matched smb session + (user name */ + return tcon; } } } @@ -891,21 +1344,21 @@ find_unc(__u32 new_target_ip_addr, char *uncName, char *userName) int connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, - const char *old_path, const struct nls_table *nls_codepage) + const char *old_path, const struct nls_table *nls_codepage, + int remap) { unsigned char *referrals = NULL; unsigned int num_referrals; int rc = 0; rc = get_dfs_path(xid, pSesInfo,old_path, nls_codepage, - &num_referrals, &referrals); + &num_referrals, &referrals, remap); /* 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); + kfree(referrals); return rc; } @@ -913,7 +1366,8 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, 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) + unsigned int *pnum_referrals, + unsigned char ** preferrals, int remap) { char *temp_unc; int rc = 0; @@ -938,7 +1392,7 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, } if (rc == 0) rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals, - pnum_referrals, nls_codepage); + pnum_referrals, nls_codepage, remap); return rc; } @@ -960,11 +1414,11 @@ static void rfc1002mangle(char * target,char * source, unsigned int length) static int ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, - char * netbios_name) + char * netbios_name, char * target_name) { int rc = 0; int connected = 0; - unsigned short int orig_port = 0; + __be16 orig_port = 0; if(*csocket == NULL) { rc = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, csocket); @@ -1025,21 +1479,33 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, /* Eventually check for other socket options to change from the default. sock_setsockopt not used because it expects user space buffer */ + cFYI(1,("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",(*csocket)->sk->sk_sndbuf, + (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo)); (*csocket)->sk->sk_rcvtimeo = 7 * HZ; + /* make the bufsizes depend on wsize/rsize and max requests */ + if((*csocket)->sk->sk_sndbuf < (200 * 1024)) + (*csocket)->sk->sk_sndbuf = 200 * 1024; + if((*csocket)->sk->sk_rcvbuf < (140 * 1024)) + (*csocket)->sk->sk_rcvbuf = 140 * 1024; /* send RFC1001 sessinit */ - - if(psin_server->sin_port == htons(139)) { + 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); + ses_init_buf = kzalloc(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); + if(target_name && (target_name[0] != 0)) { + rfc1002mangle(ses_init_buf->trailer.session_req.called_name, + target_name, 16); + } else { + 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. */ @@ -1058,6 +1524,14 @@ ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket, rc = smb_send(*csocket, smb_buf, 0x44, (struct sockaddr *)psin_server); kfree(ses_init_buf); + msleep(1); /* RFC1001 layer in at least one server + requires very short break before negprot + presumably because not expecting negprot + to follow so fast. This is a simple + solution that works without + complicating the code and causes no + significant slowing down on mount + for everyone else */ } /* else the negprot may still work without this even though malloc failed */ @@ -1072,6 +1546,7 @@ 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); @@ -1097,6 +1572,10 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) } 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); @@ -1119,6 +1598,8 @@ ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket) /* 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; @@ -1138,6 +1619,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, { int rc = 0; int xid; + int address_type = AF_INET; struct socket *csocket = NULL; struct sockaddr_in sin_server; struct sockaddr_in6 sin_server6; @@ -1153,25 +1635,26 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return -EINVAL; } if (volume_info.username) { + /* BB fixme parse for domain name here */ cFYI(1, ("Username: %s ", volume_info.username)); + } else if (volume_info.nullauth) { + cFYI(1,("null user")); } else { - cifserror("No username specified "); + 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); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return -EINVAL; } @@ -1179,17 +1662,20 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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) { + 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 != 1) { + if(rc <= 0) { /* we failed translating address */ - if(volume_info.UNC) - kfree(volume_info.UNC); - if(volume_info.password) - kfree(volume_info.password); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return -EINVAL; } @@ -1200,19 +1686,17 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, } 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); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); 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); + ("CIFS mount error: No UNC path (e.g. -o unc=//192.168.1.100/public) specified")); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return -EINVAL; } @@ -1225,35 +1709,49 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return -ELIBACC; } } - existingCifsSes = - cifs_find_tcp_session(sin_server.sin_addr.s_addr, - volume_info.username, &srvTcp); + 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 { + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); + FreeXid(xid); + return -EINVAL; + } + + if (srvTcp) { - cFYI(1, ("Existing tcp session with server found ")); + 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); + rc = ipv4_connect(&sin_server,&csocket, + volume_info.source_rfc1001_name, + volume_info.target_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); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return rc; } @@ -1262,10 +1760,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (srvTcp == NULL) { rc = -ENOMEM; sock_release(csocket); - if(volume_info.UNC) - kfree(volume_info.UNC); - if(volume_info.password) - kfree(volume_info.password); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); FreeXid(xid); return rc; } else { @@ -1283,20 +1780,32 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, so no need to spinlock this init of tcpStatus */ srvTcp->tcpStatus = CifsNew; init_MUTEX(&srvTcp->tcpSem); - kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp, + rc = (int)kernel_thread((void *)(void *)cifs_demultiplex_thread, srvTcp, CLONE_FS | CLONE_FILES | CLONE_VM); + if(rc < 0) { + rc = -ENOMEM; + sock_release(csocket); + kfree(volume_info.UNC); + kfree(volume_info.password); + kfree(volume_info.prepath); + FreeXid(xid); + return rc; + } + wait_for_completion(&cifsd_complete); + rc = 0; memcpy(srvTcp->workstation_RFC1001_name, volume_info.source_rfc1001_name,16); + memcpy(srvTcp->server_RFC1001_name, volume_info.target_rfc1001_name,16); + srvTcp->sequence_number = 0; } } if (existingCifsSes) { pSesInfo = existingCifsSes; - cFYI(1, ("Existing smb sess found ")); - if(volume_info.password) - kfree(volume_info.password); + cFYI(1, ("Existing smb sess found")); + kfree(volume_info.password); /* volume_info.UNC freed at end of function */ } else if (!rc) { - cFYI(1, ("Existing smb sess not found ")); + cFYI(1, ("Existing smb sess not found")); pSesInfo = sesInfoAlloc(); if (pSesInfo == NULL) rc = -ENOMEM; @@ -1313,55 +1822,107 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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); + if (volume_info.domainname) { + int len = strlen(volume_info.domainname); + pSesInfo->domainName = + kmalloc(len + 1, GFP_KERNEL); + if(pSesInfo->domainName) + strcpy(pSesInfo->domainName, + volume_info.domainname); + } pSesInfo->linux_uid = volume_info.linux_uid; + pSesInfo->overrideSecFlg = volume_info.secFlg; down(&pSesInfo->sesSem); + /* BB FIXME need to pass vol->secFlgs BB */ 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); + kfree(volume_info.password); } /* search for existing tcon to this server share */ if (!rc) { - if((volume_info.rsize) && (volume_info.rsize + MAX_CIFS_HDR_SIZE < srvTcp->maxBuf)) + if(volume_info.rsize > CIFSMaxBufSize) { + cERROR(1,("rsize %d too large, using MaxBufSize", + volume_info.rsize)); + cifs_sb->rsize = CIFSMaxBufSize; + } else 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 + MAX_CIFS_HDR_SIZE < srvTcp->maxBuf)) + else /* default */ + cifs_sb->rsize = CIFSMaxBufSize; + + if(volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) { + cERROR(1,("wsize %d too large using 4096 instead", + volume_info.wsize)); + cifs_sb->wsize = 4096; + } else if(volume_info.wsize) cifs_sb->wsize = volume_info.wsize; else - cifs_sb->wsize = srvTcp->maxBuf - MAX_CIFS_HDR_SIZE; /* 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->wsize = + min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE, + 127*1024); + /* old default of CIFSMaxBufSize was too small now + that SMB Write2 can send multiple pages in kvec. + RFC1001 does not describe what happens when frame + bigger than 128K is sent so use that as max in + conjunction with 52K kvec constraint on arch with 4K + page size */ + + if(cifs_sb->rsize < 2048) { + cifs_sb->rsize = 2048; + /* Windows ME may prefer this */ + cFYI(1,("readsize set to minimum 2048")); } + /* calculate prepath */ + cifs_sb->prepath = volume_info.prepath; + if(cifs_sb->prepath) { + cifs_sb->prepathlen = strlen(cifs_sb->prepath); + cifs_sb->prepath[0] = CIFS_DIR_SEP(cifs_sb); + volume_info.prepath = NULL; + } else + cifs_sb->prepathlen = 0; 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)); + 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.remap) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR; + if(volume_info.no_xattr) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR; + if(volume_info.sfu_emul) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL; + if(volume_info.nobrl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL; + if(volume_info.cifs_acl) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL; + + if(volume_info.direct_io) { + cFYI(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 ")); + 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; + tcon->nocase = volume_info.nocase; } else { tcon = tconInfoAlloc(); if (tcon == NULL) @@ -1373,13 +1934,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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); + rc = connect_to_dfs_path(xid, pSesInfo, + "", cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + kfree(volume_info.UNC); FreeXid(xid); return -ENODEV; } else { @@ -1391,6 +1950,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (!rc) { atomic_inc(&pSesInfo->inUse); tcon->retry = volume_info.retry; + tcon->nocase = volume_info.nocase; } } } @@ -1402,6 +1962,8 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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 @@ -1410,13 +1972,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, spin_lock(&GlobalMid_Lock); srvTcp->tcpStatus = CifsExiting; spin_unlock(&GlobalMid_Lock); - if(srvTcp->tsk) + if(srvTcp->tsk) { send_sig(SIGKILL,srvTcp->tsk,1); + wait_for_completion(&cifsd_complete); + } } /* 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 == 0) { + if (existingCifsSes == NULL) { if (pSesInfo) { if ((pSesInfo->server) && (pSesInfo->status == CifsGood)) { @@ -1424,8 +1988,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, temp_rc = CIFSSMBLogoff(xid, pSesInfo); /* if the socketUseCount is now zero */ if((temp_rc == -ESHUTDOWN) && - (pSesInfo->server->tsk)) + (pSesInfo->server->tsk)) { send_sig(SIGKILL,pSesInfo->server->tsk,1); + wait_for_completion(&cifsd_complete); + } } else cFYI(1, ("No session or bad tcon")); sesInfoFree(pSesInfo); @@ -1437,26 +2003,71 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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) - CIFSSMBQFSUnixInfo(xid, tcon, cifs_sb->local_nls); - } + /* do not care if following two calls succeed - informational */ + CIFSSMBQFSDeviceInfo(xid, tcon); + CIFSSMBQFSAttributeInfo(xid, tcon); + + if (tcon->ses->capabilities & CAP_UNIX) { + if(!CIFSSMBQFSUnixInfo(xid, tcon)) { + __u64 cap = + le64_to_cpu(tcon->fsUnixInfo.Capability); + cap &= CIFS_UNIX_CAP_MASK; + if(volume_info.no_psx_acl) + cap &= ~CIFS_UNIX_POSIX_ACL_CAP; + else if(CIFS_UNIX_POSIX_ACL_CAP & cap) { + cFYI(1,("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 + if(volume_info.posix_paths == 0) + cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP; + else if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + cFYI(1,("negotiate posix pathnames")); + cifs_sb->mnt_cifs_flags |= + CIFS_MOUNT_POSIX_PATHS; + } + + cFYI(1,("Negotiate caps 0x%x",(int)cap)); +#ifdef CONFIG_CIFS_DEBUG2 + if(cap & CIFS_UNIX_FCNTL_CAP) + cFYI(1,("FCNTL cap")); + if(cap & CIFS_UNIX_EXTATTR_CAP) + cFYI(1,("EXTATTR cap")); + if(cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) + cFYI(1,("POSIX path cap")); + if(cap & CIFS_UNIX_XATTR_CAP) + cFYI(1,("XATTR cap")); + if(cap & CIFS_UNIX_POSIX_ACL_CAP) + cFYI(1,("POSIX ACL cap")); +#endif /* CIFS_DEBUG2 */ + if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) { + cFYI(1,("setting capabilities failed")); + } + } + } + if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X)) + cifs_sb->wsize = min(cifs_sb->wsize, + (tcon->ses->server->maxBuf - + MAX_CIFS_HDR_SIZE)); + if (!(tcon->ses->capabilities & CAP_LARGE_READ_X)) + cifs_sb->rsize = min(cifs_sb->rsize, + (tcon->ses->server->maxBuf - + MAX_CIFS_HDR_SIZE)); + } + + /* 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); + kfree(volume_info.UNC); + kfree(volume_info.prepath); FreeXid(xid); return rc; } static int CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, - char session_key[CIFS_SESSION_KEY_SIZE], + char session_key[CIFS_SESS_KEY_SIZE], const struct nls_table *nls_codepage) { struct smb_hdr *smb_buffer; @@ -1464,17 +2075,22 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *user = ses->userName; - char *domain = ses->domainName; + 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 ")); - + cFYI(1, ("In sesssetup")); + if(ses == NULL) + return -EINVAL; + user = ses->userName; + domain = ses->domainName; smb_buffer = cifs_buf_get(); - if (smb_buffer == 0) { + if (smb_buffer == NULL) { return -ENOMEM; } smb_buffer_response = smb_buffer; @@ -1484,6 +2100,7 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 13 /* wct */ ); + smb_buffer->Mid = GetNextMid(ses->server); 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); @@ -1491,67 +2108,68 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - pSMB->req_no_secext.Capabilities = - CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS; + 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; - pSMB->req_no_secext.Capabilities |= CAP_UNICODE; + capabilities |= CAP_UNICODE; } if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - pSMB->req_no_secext.Capabilities |= CAP_STATUS32; + capabilities |= CAP_STATUS32; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; - pSMB->req_no_secext.Capabilities |= CAP_DFS; + capabilities |= CAP_DFS; } - pSMB->req_no_secext.Capabilities = - cpu_to_le32(pSMB->req_no_secext.Capabilities); - /* pSMB->req_no_secext.CaseInsensitivePasswordLength = - CIFS_SESSION_KEY_SIZE; */ - pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; + pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); + + pSMB->req_no_secext.CaseInsensitivePasswordLength = + cpu_to_le16(CIFS_SESS_KEY_SIZE); + pSMB->req_no_secext.CaseSensitivePasswordLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); bcc_ptr = pByteArea(smb_buffer); - /* memcpy(bcc_ptr, (char *) lm_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; + memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; + memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE); + bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { - if ((long) bcc_ptr % 2) { /* must be word aligned for 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 */ + bytes_returned = 0; /* skip null user */ else bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, user, 100, + cifs_strtoUCS((__le16 *) bcc_ptr, user, 100, nls_codepage); - bcc_ptr += 2 * bytes_returned; /* convert num 16 bit words to bytes */ + /* 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_strtoUCS((__le16 *) bcc_ptr, "CIFS_LINUX_DOM", 32, nls_codepage); else bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, domain, 64, + cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ", + cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, UTS_RELEASE, 32, - nls_codepage); + cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, + 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, CIFS_NETWORK_OPSYS, + cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; bcc_ptr += 2; @@ -1573,14 +2191,14 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, } strcpy(bcc_ptr, "Linux version "); bcc_ptr += strlen("Linux version "); - strcpy(bcc_ptr, UTS_RELEASE); - bcc_ptr += strlen(UTS_RELEASE) + 1; + strcpy(bcc_ptr, utsname()->release); + bcc_ptr += strlen(utsname()->release) + 1; strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; } - BCC(smb_buffer) = (long) bcc_ptr - (long) pByteArea(smb_buffer); - smb_buffer->smb_buf_length += BCC(smb_buffer); - BCC(smb_buffer) = cpu_to_le16(BCC(smb_buffer)); + 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); @@ -1588,373 +2206,139 @@ CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* 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)) { - pSMBr->resp.Action = le16_to_cpu(pSMBr->resp.Action); - if (pSMBr->resp.Action & GUEST_LOGIN) + __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 ? */ - if (ses) { - ses->Suid = smb_buffer_response->Uid; /* UID left in wire format (le) */ - cFYI(1, ("UID = %d ", ses->Suid)); + 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) - && (pSMBr->resp.SecurityBlobLength < - pSMBr->resp.ByteCount))) { - if (pSMBr->resp.hdr.WordCount == 4) - bcc_ptr += - pSMBr->resp.SecurityBlobLength; - - 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); + 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); + if(ses->serverOS) + kfree(ses->serverOS); + ses->serverOS = kzalloc(2 * (len + 1), GFP_KERNEL); + if(ses->serverOS == NULL) + goto sesssetup_nomem; + cifs_strfromUCS_le(ses->serverOS, + (__le16 *)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); + kfree(ses->serverNOS); + ses->serverNOS = kzalloc(2 * (len + 1),GFP_KERNEL); + if(ses->serverNOS == NULL) + goto sesssetup_nomem; + cifs_strfromUCS_le(ses->serverNOS, + (__le16 *)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; - 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); + 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 */ + if(ses->serverDomain) + kfree(ses->serverDomain); 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 -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 = ses->userName; - char *domain = ses->domainName; - int rc = 0; - int remaining_words = 0; - int bytes_returned = 0; - int len; - - cFYI(1, ("In spnego sesssetup ")); - - smb_buffer = cifs_buf_get(); - if (smb_buffer == 0) { - 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; - - pSMB->req.Capabilities = - CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | - CAP_EXTENDED_SECURITY; - if (ses->capabilities & CAP_UNICODE) { - smb_buffer->Flags2 |= SMBFLG2_UNICODE; - pSMB->req.Capabilities |= CAP_UNICODE; - } - if (ses->capabilities & CAP_STATUS32) { - smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - pSMB->req.Capabilities |= CAP_STATUS32; - } - if (ses->capabilities & CAP_DFS) { - smb_buffer->Flags2 |= SMBFLG2_DFS; - pSMB->req.Capabilities |= CAP_DFS; - } - pSMB->req.Capabilities = cpu_to_le32(pSMB->req.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, UTS_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, UTS_RELEASE); - bcc_ptr += strlen(UTS_RELEASE) + 1; - strcpy(bcc_ptr, CIFS_NETWORK_OPSYS); - bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1; - } - BCC(smb_buffer) = (long) bcc_ptr - (long) pByteArea(smb_buffer); - smb_buffer->smb_buf_length += BCC(smb_buffer); - BCC(smb_buffer) = cpu_to_le16(BCC(smb_buffer)); - - 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)) { - pSMBr->resp.Action = le16_to_cpu(pSMBr->resp.Action); - pSMBr->resp.SecurityBlobLength = - le16_to_cpu(pSMBr->resp.SecurityBlobLength); - if (pSMBr->resp.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) - && (pSMBr->resp.SecurityBlobLength < - pSMBr->resp.ByteCount))) { - if (pSMBr->resp.hdr.WordCount == 4) { - bcc_ptr += - pSMBr->resp.SecurityBlobLength; - cFYI(1, - ("Security Blob Length %d ", - pSMBr->resp.SecurityBlobLength)); - } - - 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); + kzalloc(2*(len+1),GFP_KERNEL); + if(ses->serverDomain == NULL) + goto sesssetup_nomem; + cifs_strfromUCS_le(ses->serverDomain, + (__le16 *)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); + ses->serverDomain[2*len] = 0; + ses->serverDomain[1+(2*len)] = 0; + } /* else no more room so create dummy domain string */ + else { + if(ses->serverDomain) + kfree(ses->serverDomain); + ses->serverDomain = + kzalloc(2, GFP_KERNEL); } - } else { /* ASCII */ - - len = strnlen(bcc_ptr, 1024); - if (((long) bcc_ptr + len) - (long) - pByteArea(smb_buffer_response) + } else { /* no room so create dummy domain and NOS string */ + /* if these kcallocs fail not much we + can do, but better to not fail the + sesssetup itself */ + kfree(ses->serverDomain); + ses->serverDomain = + kzalloc(2, GFP_KERNEL); + kfree(ses->serverNOS); + ses->serverNOS = + kzalloc(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); + kfree(ses->serverOS); + ses->serverOS = kzalloc(len + 1,GFP_KERNEL); + if(ses->serverOS == NULL) + goto sesssetup_nomem; + strncpy(ses->serverOS,bcc_ptr, len); - bcc_ptr += len; - bcc_ptr[0] = 0; /* null terminate the string */ - bcc_ptr++; + 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); + kfree(ses->serverNOS); + ses->serverNOS = kzalloc(len + 1,GFP_KERNEL); + if(ses->serverNOS == NULL) + goto sesssetup_nomem; + 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")); + len = strnlen(bcc_ptr, 1024); + if(ses->serverDomain) + kfree(ses->serverDomain); + ses->serverDomain = kzalloc(len + 1,GFP_KERNEL); + if(ses->serverDomain == NULL) + goto sesssetup_nomem; + 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, ("No session structure passed in.")); + cERROR(1, + (" Security Blob Length extends beyond end of SMB")); } } else { cERROR(1, @@ -1962,7 +2346,9 @@ CIFSSpnegoSessSetup(unsigned int xid, struct cifsSesInfo *ses, smb_buffer_response->WordCount)); rc = -EIO; } - +sesssetup_nomem: /* do not return an error on nomem for the info strings, + since that could make reconnection harder, and + reconnection might be needed to free memory */ if (smb_buffer) cifs_buf_release(smb_buffer); @@ -1979,7 +2365,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *domain = ses->domainName; + char *domain; int rc = 0; int remaining_words = 0; int bytes_returned = 0; @@ -1987,11 +2373,16 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, int SecurityBlobLength = sizeof (NEGOTIATE_MESSAGE); PNEGOTIATE_MESSAGE SecurityBlob; PCHALLENGE_MESSAGE SecurityBlob2; + __u32 negotiate_flags, capabilities; + __u16 count; - cFYI(1, ("In NTLMSSP sesssetup (negotiate) ")); + 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 == 0) { + if (smb_buffer == NULL) { return -ENOMEM; } smb_buffer_response = smb_buffer; @@ -2001,6 +2392,8 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); @@ -2011,35 +2404,35 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - pSMB->req.Capabilities = - CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_EXTENDED_SECURITY; if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; - pSMB->req.Capabilities |= CAP_UNICODE; + capabilities |= CAP_UNICODE; } if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - pSMB->req.Capabilities |= CAP_STATUS32; + capabilities |= CAP_STATUS32; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; - pSMB->req.Capabilities |= CAP_DFS; + capabilities |= CAP_DFS; } - pSMB->req.Capabilities = cpu_to_le32(pSMB->req.Capabilities); + 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; - SecurityBlob->NegotiateFlags = + negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM | - NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | 0x80000000 | + NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | + NTLMSSP_NEGOTIATE_56 | /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128; if(sign_CIFS_PDUs) - SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN; - if(ntlmv2_support) - SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; + negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN; +/* if(ntlmv2_support) + negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/ /* setup pointers to domain name and workstation name */ bcc_ptr += SecurityBlobLength; @@ -2047,26 +2440,11 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, SecurityBlob->WorkstationName.Length = 0; SecurityBlob->WorkstationName.MaximumLength = 0; - if (domain == NULL) { - SecurityBlob->DomainName.Buffer = 0; - SecurityBlob->DomainName.Length = 0; - SecurityBlob->DomainName.MaximumLength = 0; - } else { - SecurityBlob->NegotiateFlags |= - NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED; - strncpy(bcc_ptr, domain, 63); - SecurityBlob->DomainName.Length = strnlen(domain, 64); - SecurityBlob->DomainName.MaximumLength = - cpu_to_le16(SecurityBlob->DomainName.Length); - SecurityBlob->DomainName.Buffer = - cpu_to_le32((long) &SecurityBlob-> - DomainString - - (long) &SecurityBlob->Signature); - bcc_ptr += SecurityBlob->DomainName.Length; - SecurityBlobLength += SecurityBlob->DomainName.Length; - SecurityBlob->DomainName.Length = - cpu_to_le16(SecurityBlob->DomainName.Length); - } + /* Domain not sent on first Sesssetup in NTLMSSP, instead it is sent + along with username on auth request (ie the response to challenge) */ + SecurityBlob->DomainName.Buffer = 0; + SecurityBlob->DomainName.Length = 0; + SecurityBlob->DomainName.MaximumLength = 0; if (ses->capabilities & CAP_UNICODE) { if ((long) bcc_ptr % 2) { *bcc_ptr = 0; @@ -2074,16 +2452,16 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, } bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ", + cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, UTS_RELEASE, 32, + cifs_strtoUCS((__le16 *) bcc_ptr, 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, + cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; *(bcc_ptr + 1) = 0; @@ -2095,35 +2473,34 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, } else { /* ASCII */ strcpy(bcc_ptr, "Linux version "); bcc_ptr += strlen("Linux version "); - strcpy(bcc_ptr, UTS_RELEASE); - bcc_ptr += strlen(UTS_RELEASE) + 1; + strcpy(bcc_ptr, utsname()->release); + bcc_ptr += strlen(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(SecurityBlob->NegotiateFlags); + SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags); pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength); - BCC(smb_buffer) = (long) bcc_ptr - (long) pByteArea(smb_buffer); - smb_buffer->smb_buf_length += BCC(smb_buffer); - BCC(smb_buffer) = cpu_to_le16(BCC(smb_buffer)); + 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 == - (NT_STATUS_MORE_PROCESSING_REQUIRED)) + 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)) { - pSMBr->resp.Action = le16_to_cpu(pSMBr->resp.Action); - pSMBr->resp.SecurityBlobLength = - le16_to_cpu(pSMBr->resp.SecurityBlobLength); - if (pSMBr->resp.Action & GUEST_LOGIN) + __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? */ @@ -2137,34 +2514,34 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, SecurityBlob2->MessageType)); } else if (ses) { ses->Suid = smb_buffer_response->Uid; /* UID left in le format */ - cFYI(1, ("UID = %d ", ses->Suid)); + cFYI(1, ("UID = %d", ses->Suid)); if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) - && (pSMBr->resp.SecurityBlobLength < + && (blob_len < pSMBr->resp.ByteCount))) { + if (pSMBr->resp.hdr.WordCount == 4) { - bcc_ptr += - pSMBr->resp.SecurityBlobLength; - cFYI(1, - ("Security Blob Length %d ", - pSMBr->resp.SecurityBlobLength)); + bcc_ptr += blob_len; + cFYI(1, ("Security Blob Length %d", + blob_len)); } - cFYI(1, ("NTLMSSP Challenge rcvd ")); + cFYI(1, ("NTLMSSP Challenge rcvd")); memcpy(ses->server->cryptKey, SecurityBlob2->Challenge, CIFS_CRYPTO_KEY_SIZE); - if(SecurityBlob2->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLMV2) + if(SecurityBlob2->NegotiateFlags & + cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2)) *pNTLMv2_flag = TRUE; if((SecurityBlob2->NegotiateFlags & - NTLMSSP_NEGOTIATE_ALWAYS_SIGN) + cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN)) || (sign_CIFS_PDUs > 1)) ses->server->secMode |= SECMODE_SIGN_REQUIRED; if ((SecurityBlob2->NegotiateFlags & - NTLMSSP_NEGOTIATE_SIGN) && (sign_CIFS_PDUs)) + cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs)) ses->server->secMode |= SECMODE_SIGN_ENABLED; @@ -2185,10 +2562,12 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, /* 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 */ + if(ses->serverOS) + kfree(ses->serverOS); ses->serverOS = - cifs_kcalloc(2 * (len + 1), GFP_KERNEL); + kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses->serverOS, - (wchar_t *) + (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); @@ -2200,12 +2579,13 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, bcc_ptr, remaining_words - 1); + kfree(ses->serverNOS); ses->serverNOS = - cifs_kcalloc(2 * (len + 1), + kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses-> serverNOS, - (wchar_t *) + (__le16 *) bcc_ptr, len, nls_codepage); @@ -2217,48 +2597,47 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, 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) */ + kfree(ses->serverDomain); ses->serverDomain = - cifs_kcalloc(2 * + kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le - (ses-> - serverDomain, - (wchar_t *) - bcc_ptr, len, - nls_codepage); + (ses->serverDomain, + (__le16 *)bcc_ptr, + len, nls_codepage); bcc_ptr += 2 * (len + 1); - ses-> - serverDomain[2 - * len] + ses->serverDomain[2*len] = 0; - ses-> - serverDomain[1 - + - (2 - * - len)] + ses->serverDomain + [1 + (2 * len)] = 0; } /* else no more room so create dummy domain string */ - else + else { + kfree(ses->serverDomain); ses->serverDomain = - cifs_kcalloc(2, + kzalloc(2, GFP_KERNEL); + } } else { /* no room so create dummy domain and NOS string */ + kfree(ses->serverDomain); ses->serverDomain = - cifs_kcalloc(2, GFP_KERNEL); + kzalloc(2, GFP_KERNEL); + kfree(ses->serverNOS); ses->serverNOS = - cifs_kcalloc(2, GFP_KERNEL); + kzalloc(2, GFP_KERNEL); } } else { /* ASCII */ len = strnlen(bcc_ptr, 1024); if (((long) bcc_ptr + len) - (long) pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { + if(ses->serverOS) + kfree(ses->serverOS); ses->serverOS = - cifs_kcalloc(len + 1, + kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverOS, bcc_ptr, len); @@ -2268,8 +2647,9 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, bcc_ptr++; len = strnlen(bcc_ptr, 1024); + kfree(ses->serverNOS); ses->serverNOS = - cifs_kcalloc(len + 1, + kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverNOS, bcc_ptr, len); bcc_ptr += len; @@ -2277,8 +2657,9 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, bcc_ptr++; len = strnlen(bcc_ptr, 1024); + kfree(ses->serverDomain); ses->serverDomain = - cifs_kcalloc(len + 1, + kzalloc(len + 1, GFP_KERNEL); strncpy(ses->serverDomain, bcc_ptr, len); bcc_ptr += len; @@ -2286,7 +2667,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, bcc_ptr++; } else cFYI(1, - ("Variable field of length %d extends beyond end of smb ", + ("Variable field of length %d extends beyond end of smb", len)); } } else { @@ -2298,7 +2679,7 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, } } else { cERROR(1, - (" Invalid Word count %d: ", + (" Invalid Word count %d:", smb_buffer_response->WordCount)); rc = -EIO; } @@ -2308,7 +2689,6 @@ CIFSNTLMSSPNegotiateSessSetup(unsigned int xid, return rc; } - static int CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, char *ntlm_session_key, int ntlmv2_flag, @@ -2319,19 +2699,24 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMBr; char *bcc_ptr; - char *user = ses->userName; - char *domain = ses->domainName; + 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 == 0) { + if (smb_buffer == NULL) { return -ENOMEM; } smb_buffer_response = smb_buffer; @@ -2341,6 +2726,8 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* send SMBsessionSetup here */ header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX, NULL /* no tCon exists yet */ , 12 /* wct */ ); + + smb_buffer->Mid = GetNextMid(ses->server); pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT); pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.AndXCommand = 0xFF; @@ -2352,36 +2739,35 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; - pSMB->req.Capabilities = - CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | + capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | CAP_EXTENDED_SECURITY; if (ses->capabilities & CAP_UNICODE) { smb_buffer->Flags2 |= SMBFLG2_UNICODE; - pSMB->req.Capabilities |= CAP_UNICODE; + capabilities |= CAP_UNICODE; } if (ses->capabilities & CAP_STATUS32) { smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS; - pSMB->req.Capabilities |= CAP_STATUS32; + capabilities |= CAP_STATUS32; } if (ses->capabilities & CAP_DFS) { smb_buffer->Flags2 |= SMBFLG2_DFS; - pSMB->req.Capabilities |= CAP_DFS; + capabilities |= CAP_DFS; } - pSMB->req.Capabilities = cpu_to_le32(pSMB->req.Capabilities); + 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; - SecurityBlob->NegotiateFlags = + negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO | 0x80000000 | NTLMSSP_NEGOTIATE_128; if(sign_CIFS_PDUs) - SecurityBlob->NegotiateFlags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN; + negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN; if(ntlmv2_flag) - SecurityBlob->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLMV2; + negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2; /* setup pointers to domain name and workstation name */ @@ -2397,14 +2783,14 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, SecurityBlob->LmChallengeResponse.Buffer = 0; SecurityBlob->NtChallengeResponse.Length = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); SecurityBlob->NtChallengeResponse.MaximumLength = - cpu_to_le16(CIFS_SESSION_KEY_SIZE); - memcpy(bcc_ptr, ntlm_session_key, CIFS_SESSION_KEY_SIZE); + cpu_to_le16(CIFS_SESS_KEY_SIZE); + memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE); SecurityBlob->NtChallengeResponse.Buffer = cpu_to_le32(SecurityBlobLength); - SecurityBlobLength += CIFS_SESSION_KEY_SIZE; - bcc_ptr += CIFS_SESSION_KEY_SIZE; + SecurityBlobLength += CIFS_SESS_KEY_SIZE; + bcc_ptr += CIFS_SESS_KEY_SIZE; if (ses->capabilities & CAP_UNICODE) { if (domain == NULL) { @@ -2412,39 +2798,39 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, SecurityBlob->DomainName.Length = 0; SecurityBlob->DomainName.MaximumLength = 0; } else { - SecurityBlob->DomainName.Length = - cifs_strtoUCS((wchar_t *) bcc_ptr, domain, 64, + __u16 len = + cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64, nls_codepage); - SecurityBlob->DomainName.Length *= 2; + len *= 2; SecurityBlob->DomainName.MaximumLength = - cpu_to_le16(SecurityBlob->DomainName.Length); + cpu_to_le16(len); SecurityBlob->DomainName.Buffer = cpu_to_le32(SecurityBlobLength); - bcc_ptr += SecurityBlob->DomainName.Length; - SecurityBlobLength += SecurityBlob->DomainName.Length; + bcc_ptr += len; + SecurityBlobLength += len; SecurityBlob->DomainName.Length = - cpu_to_le16(SecurityBlob->DomainName.Length); + cpu_to_le16(len); } if (user == NULL) { SecurityBlob->UserName.Buffer = 0; SecurityBlob->UserName.Length = 0; SecurityBlob->UserName.MaximumLength = 0; } else { - SecurityBlob->UserName.Length = - cifs_strtoUCS((wchar_t *) bcc_ptr, user, 64, + __u16 len = + cifs_strtoUCS((__le16 *) bcc_ptr, user, 64, nls_codepage); - SecurityBlob->UserName.Length *= 2; + len *= 2; SecurityBlob->UserName.MaximumLength = - cpu_to_le16(SecurityBlob->UserName.Length); + cpu_to_le16(len); SecurityBlob->UserName.Buffer = cpu_to_le32(SecurityBlobLength); - bcc_ptr += SecurityBlob->UserName.Length; - SecurityBlobLength += SecurityBlob->UserName.Length; + bcc_ptr += len; + SecurityBlobLength += len; SecurityBlob->UserName.Length = - cpu_to_le16(SecurityBlob->UserName.Length); + cpu_to_le16(len); } - /* SecurityBlob->WorkstationName.Length = cifs_strtoUCS((wchar_t *) bcc_ptr, "AMACHINE",64, nls_codepage); + /* SecurityBlob->WorkstationName.Length = cifs_strtoUCS((__le16 *) 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); @@ -2457,16 +2843,16 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr++; } bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, "Linux version ", + cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ", 32, nls_codepage); bcc_ptr += 2 * bytes_returned; bytes_returned = - cifs_strtoUCS((wchar_t *) bcc_ptr, UTS_RELEASE, 32, + cifs_strtoUCS((__le16 *) bcc_ptr, 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, + cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS, 64, nls_codepage); bcc_ptr += 2 * bytes_returned; *(bcc_ptr + 1) = 0; @@ -2481,52 +2867,50 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, SecurityBlob->DomainName.Length = 0; SecurityBlob->DomainName.MaximumLength = 0; } else { - SecurityBlob->NegotiateFlags |= - NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED; + __u16 len; + negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED; strncpy(bcc_ptr, domain, 63); - SecurityBlob->DomainName.Length = strnlen(domain, 64); + len = strnlen(domain, 64); SecurityBlob->DomainName.MaximumLength = - cpu_to_le16(SecurityBlob->DomainName.Length); + cpu_to_le16(len); SecurityBlob->DomainName.Buffer = cpu_to_le32(SecurityBlobLength); - bcc_ptr += SecurityBlob->DomainName.Length; - SecurityBlobLength += SecurityBlob->DomainName.Length; - SecurityBlob->DomainName.Length = - cpu_to_le16(SecurityBlob->DomainName.Length); + 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); - SecurityBlob->UserName.Length = strnlen(user, 64); + len = strnlen(user, 64); SecurityBlob->UserName.MaximumLength = - cpu_to_le16(SecurityBlob->UserName.Length); + cpu_to_le16(len); SecurityBlob->UserName.Buffer = cpu_to_le32(SecurityBlobLength); - bcc_ptr += SecurityBlob->UserName.Length; - SecurityBlobLength += SecurityBlob->UserName.Length; - SecurityBlob->UserName.Length = - cpu_to_le16(SecurityBlob->UserName.Length); + 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, UTS_RELEASE); - bcc_ptr += strlen(UTS_RELEASE) + 1; + strcpy(bcc_ptr, utsname()->release); + bcc_ptr += strlen(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(SecurityBlob->NegotiateFlags); + SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags); pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength); - BCC(smb_buffer) = (long) bcc_ptr - (long) pByteArea(smb_buffer); - smb_buffer->smb_buf_length += BCC(smb_buffer); - BCC(smb_buffer) = cpu_to_le16(BCC(smb_buffer)); + 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); @@ -2534,10 +2918,10 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* 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)) { - pSMBr->resp.Action = le16_to_cpu(pSMBr->resp.Action); - pSMBr->resp.SecurityBlobLength = + __u16 action = le16_to_cpu(pSMBr->resp.Action); + __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength); - if (pSMBr->resp.Action & GUEST_LOGIN) + 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 ")); @@ -2551,14 +2935,14 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* response can have either 3 or 4 word count - Samba sends 3 */ if ((pSMBr->resp.hdr.WordCount == 3) || ((pSMBr->resp.hdr.WordCount == 4) - && (pSMBr->resp.SecurityBlobLength < + && (blob_len < pSMBr->resp.ByteCount))) { if (pSMBr->resp.hdr.WordCount == 4) { bcc_ptr += - pSMBr->resp.SecurityBlobLength; + blob_len; cFYI(1, ("Security Blob Length %d ", - pSMBr->resp.SecurityBlobLength)); + blob_len)); } cFYI(1, @@ -2578,10 +2962,12 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, /* 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 */ + if(ses->serverOS) + kfree(ses->serverOS); ses->serverOS = - cifs_kcalloc(2 * (len + 1), GFP_KERNEL); + kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses->serverOS, - (wchar_t *) + (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += 2 * (len + 1); @@ -2593,12 +2979,13 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr, remaining_words - 1); + kfree(ses->serverNOS); ses->serverNOS = - cifs_kcalloc(2 * (len + 1), + kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le(ses-> serverNOS, - (wchar_t *) + (__le16 *) bcc_ptr, len, nls_codepage); @@ -2609,15 +2996,17 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, if (remaining_words > 0) { len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words); /* last string not always null terminated (e.g. for Windows XP & 2000) */ + if(ses->serverDomain) + kfree(ses->serverDomain); ses->serverDomain = - cifs_kcalloc(2 * + kzalloc(2 * (len + 1), GFP_KERNEL); cifs_strfromUCS_le (ses-> serverDomain, - (wchar_t *) + (__le16 *) bcc_ptr, len, nls_codepage); bcc_ptr += @@ -2634,18 +3023,26 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, len)] = 0; } /* else no more room so create dummy domain string */ - else - ses->serverDomain = cifs_kcalloc(2,GFP_KERNEL); + else { + if(ses->serverDomain) + kfree(ses->serverDomain); + ses->serverDomain = kzalloc(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); + if(ses->serverDomain) + kfree(ses->serverDomain); + ses->serverDomain = kzalloc(2, GFP_KERNEL); + kfree(ses->serverNOS); + ses->serverNOS = kzalloc(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); + if(ses->serverOS) + kfree(ses->serverOS); + ses->serverOS = kzalloc(len + 1,GFP_KERNEL); strncpy(ses->serverOS,bcc_ptr, len); bcc_ptr += len; @@ -2653,14 +3050,17 @@ CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses, bcc_ptr++; len = strnlen(bcc_ptr, 1024); - ses->serverNOS = cifs_kcalloc(len+1,GFP_KERNEL); + kfree(ses->serverNOS); + ses->serverNOS = kzalloc(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); + if(ses->serverDomain) + kfree(ses->serverDomain); + ses->serverDomain = kzalloc(len+1,GFP_KERNEL); strncpy(ses->serverDomain, bcc_ptr, len); bcc_ptr += len; bcc_ptr[0] = 0; @@ -2699,32 +3099,64 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *smb_buffer_response; TCONX_REQ *pSMB; TCONX_RSP *pSMBr; - char *bcc_ptr; + unsigned char *bcc_ptr; int rc = 0; int length; + __u16 count; if (ses == NULL) return -EIO; smb_buffer = cifs_buf_get(); - if (smb_buffer == 0) { + 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->Mid = GetNextMid(ses->server); 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 */ + bcc_ptr = &pSMB->Password[0]; + if((ses->server->secMode) & SECMODE_USER) { + pSMB->PasswordLength = cpu_to_le16(1); /* minimum */ + *bcc_ptr = 0; /* password is null byte */ + bcc_ptr++; /* skip password */ + /* already aligned so no need to do it below */ + } else { + pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); + /* BB FIXME add code to fail this if NTLMv2 or Kerberos + specified as required (when that support is added to + the vfs in the future) as only NTLM or the much + weaker LANMAN (which we do not send by default) is accepted + by Samba (not sure whether other servers allow + NTLMv2 password here) */ +#ifdef CONFIG_CIFS_WEAK_PW_HASH + if((extended_security & CIFSSEC_MAY_LANMAN) && + (ses->server->secType == LANMAN)) + calc_lanman_hash(ses, bcc_ptr); + else +#endif /* CIFS_WEAK_PW_HASH */ + SMBNTencrypt(ses->password, + ses->server->cryptKey, + bcc_ptr); + + bcc_ptr += CIFS_SESS_KEY_SIZE; + if(ses->capabilities & CAP_UNICODE) { + /* must align unicode strings */ + *bcc_ptr = 0; /* null byte password */ + bcc_ptr++; + } + } - if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) + if(ses->server->secMode & + (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE; if (ses->capabilities & CAP_STATUS32) { @@ -2736,27 +3168,28 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, 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 */ + cifs_strtoUCS((__le16 *) bcc_ptr, tree, + 6 /* max utf8 char length in bytes */ * + (/* server len*/ + 256 /* share len */), nls_codepage); + bcc_ptr += 2 * length; /* convert num 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; - BCC(smb_buffer) = (long) bcc_ptr - (long) pByteArea(smb_buffer); - smb_buffer->smb_buf_length += BCC(smb_buffer); - BCC(smb_buffer) = cpu_to_le16(BCC(smb_buffer)); + 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->tidStatus = CifsGood; tcon->tid = smb_buffer_response->Tid; bcc_ptr = pByteArea(smb_buffer_response); length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2); @@ -2765,15 +3198,14 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, strncpy(tcon->treeName, tree, MAX_TREE_SIZE); if (smb_buffer->Flags2 & SMBFLG2_UNICODE) { length = UniStrnlen((wchar_t *) bcc_ptr, 512); - if (((long) bcc_ptr + (2 * length)) - - (long) pByteArea(smb_buffer_response) <= + if ((bcc_ptr + (2 * length)) - + pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { - if(tcon->nativeFileSystem) - kfree(tcon->nativeFileSystem); + kfree(tcon->nativeFileSystem); tcon->nativeFileSystem = - cifs_kcalloc(length + 2, GFP_KERNEL); + kzalloc(length + 2, GFP_KERNEL); cifs_strfromUCS_le(tcon->nativeFileSystem, - (wchar_t *) bcc_ptr, + (__le16 *) bcc_ptr, length, nls_codepage); bcc_ptr += 2 * length; bcc_ptr[0] = 0; /* null terminate the string */ @@ -2783,19 +3215,23 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, /* else do not bother copying these informational fields */ } else { length = strnlen(bcc_ptr, 1024); - if (((long) bcc_ptr + length) - - (long) pByteArea(smb_buffer_response) <= + if ((bcc_ptr + length) - + pByteArea(smb_buffer_response) <= BCC(smb_buffer_response)) { - if(tcon->nativeFileSystem) - kfree(tcon->nativeFileSystem); + kfree(tcon->nativeFileSystem); tcon->nativeFileSystem = - cifs_kcalloc(length + 1, GFP_KERNEL); + kzalloc(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); + if((smb_buffer_response->WordCount == 3) || + (smb_buffer_response->WordCount == 7)) + /* field is in same location */ + tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport); + else + tcon->Flags = 0; cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags)); } else if ((rc == 0) && tcon == NULL) { /* all we need to save for IPC$ connection */ @@ -2814,6 +3250,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) int xid; struct cifsSesInfo *ses = NULL; struct task_struct *cifsd_task; + char * tmp; xid = GetXid(); @@ -2835,7 +3272,10 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) return 0; } else if (rc == -ESHUTDOWN) { cFYI(1,("Waking up socket by sending it signal")); - send_sig(SIGKILL,cifsd_task,1); + if(cifsd_task) { + send_sig(SIGKILL,cifsd_task,1); + wait_for_completion(&cifsd_complete); + } rc = 0; } /* else - we have an smb session left on this socket do not kill cifsd */ @@ -2844,10 +3284,12 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) } cifs_sb->tcon = NULL; - if (ses) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(HZ / 2); - } + tmp = cifs_sb->prepath; + cifs_sb->prepathlen = 0; + cifs_sb->prepath = NULL; + kfree(tmp); + if (ses) + schedule_timeout_interruptible(msecs_to_jiffies(500)); if (ses) sesInfoFree(ses); @@ -2859,8 +3301,9 @@ 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]; + char ntlm_session_key[CIFS_SESS_KEY_SIZE]; int ntlmv2_flag = FALSE; + int first_time = 0; /* what if server changes its buffer size after dropping the session? */ if(pSesInfo->server->maxBuf == 0) /* no need to send on reconnect */ { @@ -2879,28 +3322,30 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, spin_unlock(&GlobalMid_Lock); } + first_time = 1; } if (!rc) { + pSesInfo->flags = 0; 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->sequence_number = 0;*/ + cFYI(1,("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d", pSesInfo->server->secMode, pSesInfo->server->capabilities, - pSesInfo->server->timeZone)); - if (extended_security - && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) + pSesInfo->server->timeAdj)); + if(experimEnabled < 2) + rc = CIFS_SessSetup(xid, pSesInfo, + first_time, nls_info); + else 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); + rc = -EOPNOTSUPP; } else if (extended_security && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY) && (pSesInfo->server->secType == RawNTLMSSP)) { - cFYI(1, ("NTLMSSP sesssetup ")); + cFYI(1, ("NTLMSSP sesssetup")); rc = CIFSNTLMSSPNegotiateSessSetup(xid, pSesInfo, &ntlmv2_flag, @@ -2908,26 +3353,36 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, if (!rc) { if(ntlmv2_flag) { char * v2_response; - cFYI(1,("Can use more secure NTLM version 2 password hash")); - CalcNTLMv2_partial_mac_key(pSesInfo, - nls_info); - v2_response = kmalloc(16 + 64 /* blob */, GFP_KERNEL); + cFYI(1,("more secure NTLM ver2 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, */ + /* if(first_time) + cifs_calculate_ntlmv2_mac_key( + pSesInfo->server->mac_signing_key, + response, ntlm_session_key, */ kfree(v2_response); /* BB Put dummy sig in SessSetup PDU? */ - } else + } 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); + if(first_time) + cifs_calculate_mac_key( + pSesInfo->server->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 */ @@ -2943,8 +3398,11 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, pSesInfo->server->cryptKey, ntlm_session_key); - cifs_calculate_mac_key(pSesInfo->mac_signing_key, - ntlm_session_key, pSesInfo->password); + if(first_time) + cifs_calculate_mac_key( + pSesInfo->server->mac_signing_key, + ntlm_session_key, pSesInfo->password); + rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info); } @@ -2955,6 +3413,7 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo, pSesInfo->status = CifsGood; } } +ss_err_exit: return rc; }