blob: 4af3588c1a9615b39976bac9fadc3487a64025eb [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/connect.c
3 *
Steve French75865f82007-06-24 18:30:48 +00004 * Copyright (C) International Business Machines Corp., 2002,2007
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
Steve Frenchfb8c4b12007-07-10 01:16:18 +000019 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 */
21#include <linux/fs.h>
22#include <linux/net.h>
23#include <linux/string.h>
24#include <linux/list.h>
25#include <linux/wait.h>
26#include <linux/ipv6.h>
27#include <linux/pagemap.h>
28#include <linux/ctype.h>
29#include <linux/utsname.h>
30#include <linux/mempool.h>
Steve Frenchb8643e12005-04-28 22:41:07 -070031#include <linux/delay.h>
Steve Frenchf1914012005-08-18 09:37:34 -070032#include <linux/completion.h>
Igor Mammedovaaf737a2007-04-03 19:16:43 +000033#include <linux/kthread.h>
Steve French0ae0efa2005-10-10 10:57:19 -070034#include <linux/pagevec.h>
Nigel Cunningham7dfb7102006-12-06 20:34:23 -080035#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <asm/uaccess.h>
37#include <asm/processor.h>
38#include "cifspdu.h"
39#include "cifsglob.h"
40#include "cifsproto.h"
41#include "cifs_unicode.h"
42#include "cifs_debug.h"
43#include "cifs_fs_sb.h"
44#include "ntlmssp.h"
45#include "nterr.h"
46#include "rfc1002pdu.h"
Steve Frencha2653eb2005-11-10 15:33:38 -080047#include "cn_cifs.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
49#define CIFS_PORT 445
50#define RFC1001_PORT 139
51
Steve Frenchf1914012005-08-18 09:37:34 -070052static DECLARE_COMPLETION(cifsd_complete);
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
55 unsigned char *p24);
56
57extern mempool_t *cifs_req_poolp;
58
59struct smb_vol {
60 char *username;
61 char *password;
62 char *domainname;
63 char *UNC;
64 char *UNCip;
65 char *in6_addr; /* ipv6 address as human readable form of in6_addr */
66 char *iocharset; /* local code page for mapping to and from Unicode */
67 char source_rfc1001_name[16]; /* netbios name of client */
Steve Frencha10faeb22005-08-22 21:38:31 -070068 char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 uid_t linux_uid;
70 gid_t linux_gid;
71 mode_t file_mode;
72 mode_t dir_mode;
Steve French189acaa2006-06-23 02:33:48 +000073 unsigned secFlg;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 unsigned rw:1;
75 unsigned retry:1;
76 unsigned intr:1;
77 unsigned setuids:1;
Steve French4523cc32007-04-30 20:13:06 +000078 unsigned override_uid:1;
79 unsigned override_gid:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080 unsigned noperm:1;
81 unsigned no_psx_acl:1; /* set if posix acl support should be disabled */
Steve French0a4b92c2006-01-12 15:44:21 -080082 unsigned cifs_acl:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 unsigned no_xattr:1; /* set if xattr (EA) support should be disabled*/
84 unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
85 unsigned direct_io:1;
Steve French6a0b4822005-04-28 22:41:05 -070086 unsigned remap:1; /* set to remap seven reserved chars in filenames */
Jeremy Allisonac670552005-06-22 17:26:35 -070087 unsigned posix_paths:1; /* unset to not ask for posix pathnames. */
Steve Frenchc18c8422007-07-18 23:21:09 +000088 unsigned no_linux_ext:1;
Steve Frenchd7245c22005-07-14 18:25:12 -050089 unsigned sfu_emul:1;
Steve Frenchbf820672005-12-01 22:32:42 -080090 unsigned nullauth:1; /* attempt to authenticate with null user */
Steve Frenchc46fa8a2005-08-18 20:49:57 -070091 unsigned nocase; /* request case insensitive filenames */
92 unsigned nobrl; /* disable sending byte range locks to srv */
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 unsigned int rsize;
94 unsigned int wsize;
95 unsigned int sockopt;
96 unsigned short int port;
Steve Frenchfb8c4b12007-07-10 01:16:18 +000097 char *prepath;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098};
99
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000100static int ipv4_connect(struct sockaddr_in *psin_server,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 struct socket **csocket,
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000102 char *netb_name,
103 char *server_netb_name);
104static int ipv6_connect(struct sockaddr_in6 *psin_server,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 struct socket **csocket);
106
107
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000108 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 * cifs tcp session reconnection
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000110 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 * mark tcp session as reconnecting so temporarily locked
112 * mark all smb sessions as reconnecting for tcp session
113 * reconnect tcp session
114 * wake up waiters on reconnection? - (not needed currently)
115 */
116
Steve French2cd646a2006-09-28 19:43:08 +0000117static int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118cifs_reconnect(struct TCP_Server_Info *server)
119{
120 int rc = 0;
121 struct list_head *tmp;
122 struct cifsSesInfo *ses;
123 struct cifsTconInfo *tcon;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000124 struct mid_q_entry *mid_entry;
Steve French50c2f752007-07-13 00:33:32 +0000125
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 spin_lock(&GlobalMid_Lock);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000127 if ( kthread_should_stop() ) {
128 /* the demux thread will exit normally
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 next time through the loop */
130 spin_unlock(&GlobalMid_Lock);
131 return rc;
132 } else
133 server->tcpStatus = CifsNeedReconnect;
134 spin_unlock(&GlobalMid_Lock);
135 server->maxBuf = 0;
136
Steve Frenche4eb2952005-04-28 22:41:09 -0700137 cFYI(1, ("Reconnecting tcp session"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 /* before reconnecting the tcp session, mark the smb session (uid)
140 and the tid bad so they are not used until reconnected */
141 read_lock(&GlobalSMBSeslock);
142 list_for_each(tmp, &GlobalSMBSessionList) {
143 ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
144 if (ses->server) {
145 if (ses->server == server) {
146 ses->status = CifsNeedReconnect;
147 ses->ipc_tid = 0;
148 }
149 }
150 /* else tcp and smb sessions need reconnection */
151 }
152 list_for_each(tmp, &GlobalTreeConnectionList) {
153 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000154 if ((tcon) && (tcon->ses) && (tcon->ses->server == server)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 tcon->tidStatus = CifsNeedReconnect;
156 }
157 }
158 read_unlock(&GlobalSMBSeslock);
159 /* do not want to be sending data on a socket we are freeing */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000160 down(&server->tcpSem);
161 if (server->ssocket) {
Steve French467a8f82007-06-27 22:41:32 +0000162 cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 server->ssocket->flags));
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000164 server->ssocket->ops->shutdown(server->ssocket, SEND_SHUTDOWN);
165 cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx",
Steve French467a8f82007-06-27 22:41:32 +0000166 server->ssocket->state,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 server->ssocket->flags));
168 sock_release(server->ssocket);
169 server->ssocket = NULL;
170 }
171
172 spin_lock(&GlobalMid_Lock);
173 list_for_each(tmp, &server->pending_mid_q) {
174 mid_entry = list_entry(tmp, struct
175 mid_q_entry,
176 qhead);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000177 if (mid_entry) {
178 if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
Steve French09d1db52005-04-28 22:41:08 -0700179 /* Mark other intransit requests as needing
180 retry so we do not immediately mark the
181 session bad again (ie after we reconnect
182 below) as they timeout too */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 mid_entry->midState = MID_RETRY_NEEDED;
184 }
185 }
186 }
187 spin_unlock(&GlobalMid_Lock);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000188 up(&server->tcpSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000190 while ( (!kthread_should_stop()) && (server->tcpStatus != CifsGood)) {
Steve French6c3d8902006-07-31 22:46:20 +0000191 try_to_freeze();
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000192 if (server->protocolType == IPV6) {
193 rc = ipv6_connect(&server->addr.sockAddr6,
194 &server->ssocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 } else {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000196 rc = ipv4_connect(&server->addr.sockAddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 &server->ssocket,
Steve Frencha10faeb22005-08-22 21:38:31 -0700198 server->workstation_RFC1001_name,
199 server->server_RFC1001_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000201 if (rc) {
202 cFYI(1, ("reconnect error %d", rc));
Steve French0cb766a2005-04-28 22:41:11 -0700203 msleep(3000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 } else {
205 atomic_inc(&tcpSesReconnectCount);
206 spin_lock(&GlobalMid_Lock);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000207 if ( !kthread_should_stop() )
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 server->tcpStatus = CifsGood;
Steve Frenchad009ac2005-04-28 22:41:05 -0700209 server->sequence_number = 0;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000210 spin_unlock(&GlobalMid_Lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 /* atomic_set(&server->inFlight,0);*/
212 wake_up(&server->response_q);
213 }
214 }
215 return rc;
216}
217
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000218/*
Steve Frenche4eb2952005-04-28 22:41:09 -0700219 return codes:
220 0 not a transact2, or all data present
221 >0 transact2 with that much data missing
222 -EINVAL = invalid transact2
223
224 */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000225static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
Steve Frenche4eb2952005-04-28 22:41:09 -0700226{
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000227 struct smb_t2_rsp *pSMBt;
228 int total_data_size;
Steve Frenche4eb2952005-04-28 22:41:09 -0700229 int data_in_this_rsp;
230 int remaining;
231
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000232 if (pSMB->Command != SMB_COM_TRANSACTION2)
Steve Frenche4eb2952005-04-28 22:41:09 -0700233 return 0;
234
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000235 /* check for plausible wct, bcc and t2 data and parm sizes */
236 /* check for parm and data offset going beyond end of smb */
237 if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
Steve French467a8f82007-06-27 22:41:32 +0000238 cFYI(1, ("invalid transact2 word count"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700239 return -EINVAL;
240 }
241
242 pSMBt = (struct smb_t2_rsp *)pSMB;
243
244 total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
245 data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount);
246
247 remaining = total_data_size - data_in_this_rsp;
248
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000249 if (remaining == 0)
Steve Frenche4eb2952005-04-28 22:41:09 -0700250 return 0;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000251 else if (remaining < 0) {
Steve French467a8f82007-06-27 22:41:32 +0000252 cFYI(1, ("total data %d smaller than data in frame %d",
Steve Frenche4eb2952005-04-28 22:41:09 -0700253 total_data_size, data_in_this_rsp));
254 return -EINVAL;
255 } else {
Steve French467a8f82007-06-27 22:41:32 +0000256 cFYI(1, ("missing %d bytes from transact2, check next response",
Steve Frenche4eb2952005-04-28 22:41:09 -0700257 remaining));
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000258 if (total_data_size > maxBufSize) {
259 cERROR(1, ("TotalDataSize %d is over maximum buffer %d",
260 total_data_size, maxBufSize));
261 return -EINVAL;
Steve Frenche4eb2952005-04-28 22:41:09 -0700262 }
263 return remaining;
264 }
265}
266
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000267static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
Steve Frenche4eb2952005-04-28 22:41:09 -0700268{
269 struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond;
270 struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB;
271 int total_data_size;
272 int total_in_buf;
273 int remaining;
274 int total_in_buf2;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000275 char *data_area_of_target;
276 char *data_area_of_buf2;
Steve Frenche4eb2952005-04-28 22:41:09 -0700277 __u16 byte_count;
278
279 total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
280
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000281 if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) {
Steve French63135e02007-07-17 17:34:02 +0000282 cFYI(1, ("total data size of primary and secondary t2 differ"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700283 }
284
285 total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount);
286
287 remaining = total_data_size - total_in_buf;
Steve French50c2f752007-07-13 00:33:32 +0000288
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000289 if (remaining < 0)
Steve Frenche4eb2952005-04-28 22:41:09 -0700290 return -EINVAL;
291
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000292 if (remaining == 0) /* nothing to do, ignore */
Steve Frenche4eb2952005-04-28 22:41:09 -0700293 return 0;
Steve French50c2f752007-07-13 00:33:32 +0000294
Steve Frenche4eb2952005-04-28 22:41:09 -0700295 total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000296 if (remaining < total_in_buf2) {
Steve French467a8f82007-06-27 22:41:32 +0000297 cFYI(1, ("transact2 2nd response contains too much data"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700298 }
299
300 /* find end of first SMB data area */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000301 data_area_of_target = (char *)&pSMBt->hdr.Protocol +
Steve Frenche4eb2952005-04-28 22:41:09 -0700302 le16_to_cpu(pSMBt->t2_rsp.DataOffset);
303 /* validate target area */
304
305 data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol +
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000306 le16_to_cpu(pSMB2->t2_rsp.DataOffset);
Steve Frenche4eb2952005-04-28 22:41:09 -0700307
308 data_area_of_target += total_in_buf;
309
310 /* copy second buffer into end of first buffer */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000311 memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2);
Steve Frenche4eb2952005-04-28 22:41:09 -0700312 total_in_buf += total_in_buf2;
313 pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf);
314 byte_count = le16_to_cpu(BCC_LE(pTargetSMB));
315 byte_count += total_in_buf2;
316 BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
317
Steve French70ca7342005-09-22 16:32:06 -0700318 byte_count = pTargetSMB->smb_buf_length;
Steve Frenche4eb2952005-04-28 22:41:09 -0700319 byte_count += total_in_buf2;
320
321 /* BB also add check that we are not beyond maximum buffer size */
Steve French50c2f752007-07-13 00:33:32 +0000322
Steve French70ca7342005-09-22 16:32:06 -0700323 pTargetSMB->smb_buf_length = byte_count;
Steve Frenche4eb2952005-04-28 22:41:09 -0700324
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000325 if (remaining == total_in_buf2) {
Steve French467a8f82007-06-27 22:41:32 +0000326 cFYI(1, ("found the last secondary response"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700327 return 0; /* we are done */
328 } else /* more responses to go */
329 return 1;
330
331}
332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333static int
334cifs_demultiplex_thread(struct TCP_Server_Info *server)
335{
336 int length;
337 unsigned int pdu_length, total_read;
338 struct smb_hdr *smb_buffer = NULL;
Steve Frenchb8643e12005-04-28 22:41:07 -0700339 struct smb_hdr *bigbuf = NULL;
340 struct smb_hdr *smallbuf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 struct msghdr smb_msg;
342 struct kvec iov;
343 struct socket *csocket = server->ssocket;
344 struct list_head *tmp;
345 struct cifsSesInfo *ses;
346 struct task_struct *task_to_wake = NULL;
347 struct mid_q_entry *mid_entry;
Steve French70ca7342005-09-22 16:32:06 -0700348 char temp;
Steve Frenchb8643e12005-04-28 22:41:07 -0700349 int isLargeBuf = FALSE;
Steve Frenche4eb2952005-04-28 22:41:09 -0700350 int isMultiRsp;
351 int reconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353 current->flags |= PF_MEMALLOC;
354 server->tsk = current; /* save process info to wake at shutdown */
355 cFYI(1, ("Demultiplex PID: %d", current->pid));
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000356 write_lock(&GlobalSMBSeslock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 atomic_inc(&tcpSesAllocCount);
358 length = tcpSesAllocCount.counter;
359 write_unlock(&GlobalSMBSeslock);
Steve Frenchf1914012005-08-18 09:37:34 -0700360 complete(&cifsd_complete);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000361 if (length > 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 mempool_resize(cifs_req_poolp,
363 length + cifs_min_rcv,
364 GFP_KERNEL);
365 }
366
Rafael J. Wysocki83144182007-07-17 04:03:35 -0700367 set_freezable();
Igor Mammedovaaf737a2007-04-03 19:16:43 +0000368 while (!kthread_should_stop()) {
Steve Frenchede13272005-08-30 20:10:14 -0700369 if (try_to_freeze())
370 continue;
Steve Frenchb8643e12005-04-28 22:41:07 -0700371 if (bigbuf == NULL) {
372 bigbuf = cifs_buf_get();
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000373 if (!bigbuf) {
374 cERROR(1, ("No memory for large SMB response"));
Steve Frenchb8643e12005-04-28 22:41:07 -0700375 msleep(3000);
376 /* retry will check if exiting */
377 continue;
378 }
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000379 } else if (isLargeBuf) {
380 /* we are reusing a dirty large buf, clear its start */
Steve Frenchb8643e12005-04-28 22:41:07 -0700381 memset(bigbuf, 0, sizeof (struct smb_hdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
Steve Frenchb8643e12005-04-28 22:41:07 -0700383
384 if (smallbuf == NULL) {
385 smallbuf = cifs_small_buf_get();
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000386 if (!smallbuf) {
387 cERROR(1, ("No memory for SMB response"));
Steve Frenchb8643e12005-04-28 22:41:07 -0700388 msleep(1000);
389 /* retry will check if exiting */
390 continue;
391 }
392 /* beginning of smb buffer is cleared in our buf_get */
393 } else /* if existing small buf clear beginning */
394 memset(smallbuf, 0, sizeof (struct smb_hdr));
395
396 isLargeBuf = FALSE;
Steve Frenche4eb2952005-04-28 22:41:09 -0700397 isMultiRsp = FALSE;
Steve Frenchb8643e12005-04-28 22:41:07 -0700398 smb_buffer = smallbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 iov.iov_base = smb_buffer;
400 iov.iov_len = 4;
401 smb_msg.msg_control = NULL;
402 smb_msg.msg_controllen = 0;
403 length =
404 kernel_recvmsg(csocket, &smb_msg,
405 &iov, 1, 4, 0 /* BB see socket.h flags */);
406
Igor Mammedovaaf737a2007-04-03 19:16:43 +0000407 if ( kthread_should_stop() ) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 break;
409 } else if (server->tcpStatus == CifsNeedReconnect) {
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000410 cFYI(1, ("Reconnect after server stopped responding"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 cifs_reconnect(server);
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000412 cFYI(1, ("call to reconnect done"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 csocket = server->ssocket;
414 continue;
415 } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
Steve Frenchb8643e12005-04-28 22:41:07 -0700416 msleep(1); /* minimum sleep to prevent looping
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 allowing socket to clear and app threads to set
418 tcpStatus CifsNeedReconnect if server hung */
419 continue;
420 } else if (length <= 0) {
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000421 if (server->tcpStatus == CifsNew) {
422 cFYI(1, ("tcp session abend after SMBnegprot"));
Steve French09d1db52005-04-28 22:41:08 -0700423 /* some servers kill the TCP session rather than
424 returning an SMB negprot error, in which
425 case reconnecting here is not going to help,
426 and so simply return error to mount */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427 break;
428 }
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000429 if (!try_to_freeze() && (length == -EINTR)) {
Steve French467a8f82007-06-27 22:41:32 +0000430 cFYI(1, ("cifsd thread killed"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 break;
432 }
Steve French467a8f82007-06-27 22:41:32 +0000433 cFYI(1, ("Reconnect after unexpected peek error %d",
Steve French57337e42005-04-28 22:41:10 -0700434 length));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 cifs_reconnect(server);
436 csocket = server->ssocket;
437 wake_up(&server->response_q);
438 continue;
Steve French46810cb2005-04-28 22:41:09 -0700439 } else if (length < 4) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 cFYI(1,
Steve French57337e42005-04-28 22:41:10 -0700441 ("Frame under four bytes received (%d bytes long)",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 length));
443 cifs_reconnect(server);
444 csocket = server->ssocket;
445 wake_up(&server->response_q);
446 continue;
447 }
Steve French67010fb2005-04-28 22:41:09 -0700448
Steve French70ca7342005-09-22 16:32:06 -0700449 /* The right amount was read from socket - 4 bytes */
450 /* so we can now interpret the length field */
Steve French46810cb2005-04-28 22:41:09 -0700451
Steve French70ca7342005-09-22 16:32:06 -0700452 /* the first byte big endian of the length field,
453 is actually not part of the length but the type
454 with the most common, zero, as regular data */
455 temp = *((char *) smb_buffer);
456
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000457 /* Note that FC 1001 length is big endian on the wire,
Steve French70ca7342005-09-22 16:32:06 -0700458 but we convert it here so it is always manipulated
459 as host byte order */
Steve French46810cb2005-04-28 22:41:09 -0700460 pdu_length = ntohl(smb_buffer->smb_buf_length);
Steve French70ca7342005-09-22 16:32:06 -0700461 smb_buffer->smb_buf_length = pdu_length;
Steve French46810cb2005-04-28 22:41:09 -0700462
Steve French467a8f82007-06-27 22:41:32 +0000463 cFYI(1, ("rfc1002 length 0x%x", pdu_length+4));
Steve French70ca7342005-09-22 16:32:06 -0700464
465 if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000466 continue;
Steve French70ca7342005-09-22 16:32:06 -0700467 } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
Steve French467a8f82007-06-27 22:41:32 +0000468 cFYI(1, ("Good RFC 1002 session rsp"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700469 continue;
Steve French70ca7342005-09-22 16:32:06 -0700470 } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000471 /* we get this from Windows 98 instead of
Steve French46810cb2005-04-28 22:41:09 -0700472 an error on SMB negprot response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000473 cFYI(1, ("Negative RFC1002 Session Response Error 0x%x)",
Steve French70ca7342005-09-22 16:32:06 -0700474 pdu_length));
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000475 if (server->tcpStatus == CifsNew) {
476 /* if nack on negprot (rather than
Steve French46810cb2005-04-28 22:41:09 -0700477 ret of smb negprot error) reconnecting
478 not going to help, ret error to mount */
479 break;
480 } else {
481 /* give server a second to
482 clean up before reconnect attempt */
483 msleep(1000);
484 /* always try 445 first on reconnect
485 since we get NACK on some if we ever
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000486 connected to port 139 (the NACK is
Steve French46810cb2005-04-28 22:41:09 -0700487 since we do not begin with RFC1001
488 session initialize frame) */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000489 server->addr.sockAddr.sin_port =
Steve French46810cb2005-04-28 22:41:09 -0700490 htons(CIFS_PORT);
491 cifs_reconnect(server);
492 csocket = server->ssocket;
493 wake_up(&server->response_q);
494 continue;
495 }
Steve French70ca7342005-09-22 16:32:06 -0700496 } else if (temp != (char) 0) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000497 cERROR(1, ("Unknown RFC 1002 frame"));
Steve French70ca7342005-09-22 16:32:06 -0700498 cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
499 length);
Steve French46810cb2005-04-28 22:41:09 -0700500 cifs_reconnect(server);
501 csocket = server->ssocket;
502 continue;
Steve Frenche4eb2952005-04-28 22:41:09 -0700503 }
504
505 /* else we have an SMB response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000506 if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
Steve French46810cb2005-04-28 22:41:09 -0700507 (pdu_length < sizeof (struct smb_hdr) - 1 - 4)) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700508 cERROR(1, ("Invalid size SMB length %d pdu_length %d",
Steve French46810cb2005-04-28 22:41:09 -0700509 length, pdu_length+4));
Steve Frenche4eb2952005-04-28 22:41:09 -0700510 cifs_reconnect(server);
511 csocket = server->ssocket;
512 wake_up(&server->response_q);
513 continue;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000514 }
Steve Frenche4eb2952005-04-28 22:41:09 -0700515
516 /* else length ok */
517 reconnect = 0;
518
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000519 if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700520 isLargeBuf = TRUE;
521 memcpy(bigbuf, smallbuf, 4);
522 smb_buffer = bigbuf;
523 }
524 length = 0;
525 iov.iov_base = 4 + (char *)smb_buffer;
526 iov.iov_len = pdu_length;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000527 for (total_read = 0; total_read < pdu_length;
Steve Frenche4eb2952005-04-28 22:41:09 -0700528 total_read += length) {
529 length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
530 pdu_length - total_read, 0);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000531 if ( kthread_should_stop() ||
Steve Frenche4eb2952005-04-28 22:41:09 -0700532 (length == -EINTR)) {
533 /* then will exit */
534 reconnect = 2;
535 break;
536 } else if (server->tcpStatus == CifsNeedReconnect) {
Steve French46810cb2005-04-28 22:41:09 -0700537 cifs_reconnect(server);
538 csocket = server->ssocket;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000539 /* Reconnect wakes up rspns q */
Steve Frenche4eb2952005-04-28 22:41:09 -0700540 /* Now we will reread sock */
541 reconnect = 1;
542 break;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000543 } else if ((length == -ERESTARTSYS) ||
Steve Frenche4eb2952005-04-28 22:41:09 -0700544 (length == -EAGAIN)) {
545 msleep(1); /* minimum sleep to prevent looping,
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000546 allowing socket to clear and app
Steve Frenche4eb2952005-04-28 22:41:09 -0700547 threads to set tcpStatus
548 CifsNeedReconnect if server hung*/
Steve French46810cb2005-04-28 22:41:09 -0700549 continue;
Steve Frenche4eb2952005-04-28 22:41:09 -0700550 } else if (length <= 0) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000551 cERROR(1, ("Received no data, expecting %d",
Steve Frenche4eb2952005-04-28 22:41:09 -0700552 pdu_length - total_read));
553 cifs_reconnect(server);
554 csocket = server->ssocket;
555 reconnect = 1;
556 break;
Steve French46810cb2005-04-28 22:41:09 -0700557 }
558 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000559 if (reconnect == 2)
Steve Frenche4eb2952005-04-28 22:41:09 -0700560 break;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000561 else if (reconnect == 1)
Steve Frenche4eb2952005-04-28 22:41:09 -0700562 continue;
563
564 length += 4; /* account for rfc1002 hdr */
Steve French50c2f752007-07-13 00:33:32 +0000565
Steve Frenche4eb2952005-04-28 22:41:09 -0700566
567 dump_smb(smb_buffer, length);
Steve French184ed212006-02-24 06:15:11 +0000568 if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) {
Steve Frenchb387eae2005-10-10 14:21:15 -0700569 cifs_dump_mem("Bad SMB: ", smb_buffer, 48);
Steve Frenche4eb2952005-04-28 22:41:09 -0700570 continue;
571 }
572
573
574 task_to_wake = NULL;
575 spin_lock(&GlobalMid_Lock);
576 list_for_each(tmp, &server->pending_mid_q) {
577 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
578
Steve French50c2f752007-07-13 00:33:32 +0000579 if ((mid_entry->mid == smb_buffer->Mid) &&
Steve Frenche4eb2952005-04-28 22:41:09 -0700580 (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
581 (mid_entry->command == smb_buffer->Command)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000582 if (check2ndT2(smb_buffer,server->maxBuf) > 0) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700583 /* We have a multipart transact2 resp */
Steve Frenchcd634992005-04-28 22:41:10 -0700584 isMultiRsp = TRUE;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000585 if (mid_entry->resp_buf) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700586 /* merge response - fix up 1st*/
Steve French50c2f752007-07-13 00:33:32 +0000587 if (coalesce_t2(smb_buffer,
Steve Frenche4eb2952005-04-28 22:41:09 -0700588 mid_entry->resp_buf)) {
Steve French39798772006-05-31 22:40:51 +0000589 mid_entry->multiRsp = 1;
Steve Frenche4eb2952005-04-28 22:41:09 -0700590 break;
591 } else {
592 /* all parts received */
Steve French39798772006-05-31 22:40:51 +0000593 mid_entry->multiEnd = 1;
Steve French50c2f752007-07-13 00:33:32 +0000594 goto multi_t2_fnd;
Steve Frenche4eb2952005-04-28 22:41:09 -0700595 }
596 } else {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000597 if (!isLargeBuf) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700598 cERROR(1,("1st trans2 resp needs bigbuf"));
599 /* BB maybe we can fix this up, switch
Steve French50c2f752007-07-13 00:33:32 +0000600 to already allocated large buffer? */
Steve Frenche4eb2952005-04-28 22:41:09 -0700601 } else {
Steve Frenchcd634992005-04-28 22:41:10 -0700602 /* Have first buffer */
Steve Frenche4eb2952005-04-28 22:41:09 -0700603 mid_entry->resp_buf =
604 smb_buffer;
605 mid_entry->largeBuf = 1;
Steve Frenche4eb2952005-04-28 22:41:09 -0700606 bigbuf = NULL;
607 }
608 }
609 break;
Steve French50c2f752007-07-13 00:33:32 +0000610 }
Steve Frenche4eb2952005-04-28 22:41:09 -0700611 mid_entry->resp_buf = smb_buffer;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000612 if (isLargeBuf)
Steve Frenche4eb2952005-04-28 22:41:09 -0700613 mid_entry->largeBuf = 1;
614 else
615 mid_entry->largeBuf = 0;
616multi_t2_fnd:
617 task_to_wake = mid_entry->tsk;
618 mid_entry->midState = MID_RESPONSE_RECEIVED;
Steve French1047abc2005-10-11 19:58:06 -0700619#ifdef CONFIG_CIFS_STATS2
620 mid_entry->when_received = jiffies;
621#endif
Steve French3a5ff612006-07-14 22:37:11 +0000622 /* so we do not time out requests to server
623 which is still responding (since server could
624 be busy but not dead) */
625 server->lstrp = jiffies;
Steve Frenche4eb2952005-04-28 22:41:09 -0700626 break;
627 }
628 }
629 spin_unlock(&GlobalMid_Lock);
630 if (task_to_wake) {
Steve Frenchcd634992005-04-28 22:41:10 -0700631 /* Was previous buf put in mpx struct for multi-rsp? */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000632 if (!isMultiRsp) {
Steve Frenchcd634992005-04-28 22:41:10 -0700633 /* smb buffer will be freed by user thread */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000634 if (isLargeBuf) {
Steve Frenchcd634992005-04-28 22:41:10 -0700635 bigbuf = NULL;
636 } else
637 smallbuf = NULL;
638 }
Steve Frenche4eb2952005-04-28 22:41:09 -0700639 wake_up_process(task_to_wake);
Steve Frenchd7c8c942006-03-03 10:43:49 +0000640 } else if ((is_valid_oplock_break(smb_buffer, server) == FALSE)
Steve French50c2f752007-07-13 00:33:32 +0000641 && (isMultiRsp == FALSE)) {
642 cERROR(1, ("No task to wake, unknown frame received! "
643 "NumMids %d", midCount.counter));
644 cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
Steve French70ca7342005-09-22 16:32:06 -0700645 sizeof(struct smb_hdr));
Steve French39798772006-05-31 22:40:51 +0000646#ifdef CONFIG_CIFS_DEBUG2
647 cifs_dump_detail(smb_buffer);
648 cifs_dump_mids(server);
649#endif /* CIFS_DEBUG2 */
Steve French50c2f752007-07-13 00:33:32 +0000650
Steve Frenche4eb2952005-04-28 22:41:09 -0700651 }
652 } /* end while !EXITING */
653
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 spin_lock(&GlobalMid_Lock);
655 server->tcpStatus = CifsExiting;
656 server->tsk = NULL;
Steve French31ca3bc2005-04-28 22:41:11 -0700657 /* check if we have blocked requests that need to free */
658 /* Note that cifs_max_pending is normally 50, but
659 can be set at module install time to as little as two */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000660 if (atomic_read(&server->inFlight) >= cifs_max_pending)
Steve French31ca3bc2005-04-28 22:41:11 -0700661 atomic_set(&server->inFlight, cifs_max_pending - 1);
662 /* We do not want to set the max_pending too low or we
663 could end up with the counter going negative */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +0000665 /* Although there should not be any requests blocked on
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 this queue it can not hurt to be paranoid and try to wake up requests
Steve French09d1db52005-04-28 22:41:08 -0700667 that may haven been blocked when more than 50 at time were on the wire
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 to the same server - they now will see the session is in exit state
669 and get out of SendReceive. */
670 wake_up_all(&server->request_q);
671 /* give those requests time to exit */
Steve Frenchb8643e12005-04-28 22:41:07 -0700672 msleep(125);
Steve French50c2f752007-07-13 00:33:32 +0000673
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000674 if (server->ssocket) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 sock_release(csocket);
676 server->ssocket = NULL;
677 }
Steve Frenchb8643e12005-04-28 22:41:07 -0700678 /* buffer usuallly freed in free_mid - need to free it here on exit */
679 if (bigbuf != NULL)
680 cifs_buf_release(bigbuf);
681 if (smallbuf != NULL)
682 cifs_small_buf_release(smallbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683
684 read_lock(&GlobalSMBSeslock);
685 if (list_empty(&server->pending_mid_q)) {
Steve French09d1db52005-04-28 22:41:08 -0700686 /* loop through server session structures attached to this and
687 mark them dead */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 list_for_each(tmp, &GlobalSMBSessionList) {
689 ses =
690 list_entry(tmp, struct cifsSesInfo,
691 cifsSessionList);
692 if (ses->server == server) {
693 ses->status = CifsExiting;
694 ses->server = NULL;
695 }
696 }
697 read_unlock(&GlobalSMBSeslock);
698 } else {
Steve French31ca3bc2005-04-28 22:41:11 -0700699 /* although we can not zero the server struct pointer yet,
700 since there are active requests which may depnd on them,
701 mark the corresponding SMB sessions as exiting too */
702 list_for_each(tmp, &GlobalSMBSessionList) {
703 ses = list_entry(tmp, struct cifsSesInfo,
704 cifsSessionList);
705 if (ses->server == server) {
706 ses->status = CifsExiting;
707 }
708 }
709
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 spin_lock(&GlobalMid_Lock);
711 list_for_each(tmp, &server->pending_mid_q) {
712 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
713 if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
Steve French50c2f752007-07-13 00:33:32 +0000714 cFYI(1, ("Clearing Mid 0x%x - waking up ",
715 mid_entry->mid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 task_to_wake = mid_entry->tsk;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000717 if (task_to_wake) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 wake_up_process(task_to_wake);
719 }
720 }
721 }
722 spin_unlock(&GlobalMid_Lock);
723 read_unlock(&GlobalSMBSeslock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 /* 1/8th of sec is more than enough time for them to exit */
Steve Frenchb8643e12005-04-28 22:41:07 -0700725 msleep(125);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 }
727
Steve Frenchf1914012005-08-18 09:37:34 -0700728 if (!list_empty(&server->pending_mid_q)) {
Steve French50c2f752007-07-13 00:33:32 +0000729 /* mpx threads have not exited yet give them
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 at least the smb send timeout time for long ops */
Steve French31ca3bc2005-04-28 22:41:11 -0700731 /* due to delays on oplock break requests, we need
732 to wait at least 45 seconds before giving up
733 on a request getting a response and going ahead
734 and killing cifsd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 cFYI(1, ("Wait for exit from demultiplex thread"));
Steve French31ca3bc2005-04-28 22:41:11 -0700736 msleep(46000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737 /* if threads still have not exited they are probably never
738 coming home not much else we can do but free the memory */
739 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741 write_lock(&GlobalSMBSeslock);
742 atomic_dec(&tcpSesAllocCount);
743 length = tcpSesAllocCount.counter;
Steve French31ca3bc2005-04-28 22:41:11 -0700744
745 /* last chance to mark ses pointers invalid
746 if there are any pointing to this (e.g
Steve French50c2f752007-07-13 00:33:32 +0000747 if a crazy root user tried to kill cifsd
Steve French31ca3bc2005-04-28 22:41:11 -0700748 kernel thread explicitly this might happen) */
749 list_for_each(tmp, &GlobalSMBSessionList) {
750 ses = list_entry(tmp, struct cifsSesInfo,
751 cifsSessionList);
752 if (ses->server == server) {
753 ses->server = NULL;
754 }
755 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 write_unlock(&GlobalSMBSeslock);
Steve French31ca3bc2005-04-28 22:41:11 -0700757
758 kfree(server);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000759 if (length > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 mempool_resize(cifs_req_poolp,
761 length + cifs_min_rcv,
762 GFP_KERNEL);
763 }
Steve French50c2f752007-07-13 00:33:32 +0000764
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 return 0;
766}
767
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768static int
Steve French50c2f752007-07-13 00:33:32 +0000769cifs_parse_mount_options(char *options, const char *devname,
770 struct smb_vol *vol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771{
772 char *value;
773 char *data;
774 unsigned int temp_len, i, j;
775 char separator[2];
776
777 separator[0] = ',';
Steve French50c2f752007-07-13 00:33:32 +0000778 separator[1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Linus Torvalds12e36b22006-10-13 08:09:29 -0700780 if (Local_System_Name[0] != 0)
Steve French50c2f752007-07-13 00:33:32 +0000781 memcpy(vol->source_rfc1001_name, Local_System_Name, 15);
Steve French2cd646a2006-09-28 19:43:08 +0000782 else {
Linus Torvalds12e36b22006-10-13 08:09:29 -0700783 char *nodename = utsname()->nodename;
Steve French50c2f752007-07-13 00:33:32 +0000784 int n = strnlen(nodename, 15);
785 memset(vol->source_rfc1001_name, 0x20, 15);
786 for (i = 0; i < n; i++) {
Steve French2cd646a2006-09-28 19:43:08 +0000787 /* does not have to be perfect mapping since field is
788 informational, only used for servers that do not support
789 port 445 and it can be overridden at mount time */
Linus Torvalds12e36b22006-10-13 08:09:29 -0700790 vol->source_rfc1001_name[i] = toupper(nodename[i]);
Steve French2cd646a2006-09-28 19:43:08 +0000791 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 }
793 vol->source_rfc1001_name[15] = 0;
Steve Frencha10faeb22005-08-22 21:38:31 -0700794 /* null target name indicates to use *SMBSERVR default called name
795 if we end up sending RFC1001 session initialize */
796 vol->target_rfc1001_name[0] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 vol->linux_uid = current->uid; /* current->euid instead? */
798 vol->linux_gid = current->gid;
799 vol->dir_mode = S_IRWXUGO;
800 /* 2767 perms indicate mandatory locking support */
801 vol->file_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP);
802
803 /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
804 vol->rw = TRUE;
Jeremy Allisonac670552005-06-22 17:26:35 -0700805 /* default is always to request posix paths. */
806 vol->posix_paths = 1;
807
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 if (!options)
809 return 1;
810
Steve French50c2f752007-07-13 00:33:32 +0000811 if (strncmp(options, "sep=", 4) == 0) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000812 if (options[4] != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 separator[0] = options[4];
814 options += 5;
815 } else {
Steve French467a8f82007-06-27 22:41:32 +0000816 cFYI(1, ("Null separator not allowed"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 }
818 }
Steve French50c2f752007-07-13 00:33:32 +0000819
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 while ((data = strsep(&options, separator)) != NULL) {
821 if (!*data)
822 continue;
823 if ((value = strchr(data, '=')) != NULL)
824 *value++ = '\0';
825
Steve French50c2f752007-07-13 00:33:32 +0000826 /* Have to parse this before we parse for "user" */
827 if (strnicmp(data, "user_xattr", 10) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 vol->no_xattr = 0;
Steve French50c2f752007-07-13 00:33:32 +0000829 } else if (strnicmp(data, "nouser_xattr", 12) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 vol->no_xattr = 1;
831 } else if (strnicmp(data, "user", 4) == 0) {
Steve French4b952a92006-10-30 21:46:13 +0000832 if (!value) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 printk(KERN_WARNING
834 "CIFS: invalid or missing username\n");
835 return 1; /* needs_arg; */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000836 } else if (!*value) {
Steve French4b952a92006-10-30 21:46:13 +0000837 /* null user, ie anonymous, authentication */
838 vol->nullauth = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 }
840 if (strnlen(value, 200) < 200) {
841 vol->username = value;
842 } else {
843 printk(KERN_WARNING "CIFS: username too long\n");
844 return 1;
845 }
846 } else if (strnicmp(data, "pass", 4) == 0) {
847 if (!value) {
848 vol->password = NULL;
849 continue;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000850 } else if (value[0] == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700851 /* check if string begins with double comma
852 since that would mean the password really
853 does start with a comma, and would not
854 indicate an empty string */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000855 if (value[1] != separator[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 vol->password = NULL;
857 continue;
858 }
859 }
860 temp_len = strlen(value);
861 /* removed password length check, NTLM passwords
862 can be arbitrarily long */
863
Steve French50c2f752007-07-13 00:33:32 +0000864 /* if comma in password, the string will be
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 prematurely null terminated. Commas in password are
866 specified across the cifs mount interface by a double
867 comma ie ,, and a comma used as in other cases ie ','
868 as a parameter delimiter/separator is single and due
869 to the strsep above is temporarily zeroed. */
870
871 /* NB: password legally can have multiple commas and
872 the only illegal character in a password is null */
873
Steve French50c2f752007-07-13 00:33:32 +0000874 if ((value[temp_len] == 0) &&
Steve French09d1db52005-04-28 22:41:08 -0700875 (value[temp_len+1] == separator[0])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 /* reinsert comma */
877 value[temp_len] = separator[0];
Steve French50c2f752007-07-13 00:33:32 +0000878 temp_len += 2; /* move after second comma */
879 while (value[temp_len] != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 if (value[temp_len] == separator[0]) {
Steve French50c2f752007-07-13 00:33:32 +0000881 if (value[temp_len+1] ==
Steve French09d1db52005-04-28 22:41:08 -0700882 separator[0]) {
883 /* skip second comma */
884 temp_len++;
Steve French50c2f752007-07-13 00:33:32 +0000885 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 /* single comma indicating start
887 of next parm */
888 break;
889 }
890 }
891 temp_len++;
892 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000893 if (value[temp_len] == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 options = NULL;
895 } else {
896 value[temp_len] = 0;
897 /* point option to start of next parm */
898 options = value + temp_len + 1;
899 }
Steve French50c2f752007-07-13 00:33:32 +0000900 /* go from value to value + temp_len condensing
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901 double commas to singles. Note that this ends up
902 allocating a few bytes too many, which is ok */
Pekka Enberge915fc42005-09-06 15:18:35 -0700903 vol->password = kzalloc(temp_len, GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000904 if (vol->password == NULL) {
Steve French50c2f752007-07-13 00:33:32 +0000905 printk(KERN_WARNING "CIFS: no memory "
906 "for password\n");
Steve French433dc242005-04-28 22:41:08 -0700907 return 1;
908 }
Steve French50c2f752007-07-13 00:33:32 +0000909 for (i = 0, j = 0; i < temp_len; i++, j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 vol->password[j] = value[i];
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000911 if (value[i] == separator[0]
Steve French09d1db52005-04-28 22:41:08 -0700912 && value[i+1] == separator[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 /* skip second comma */
914 i++;
915 }
916 }
917 vol->password[j] = 0;
918 } else {
Pekka Enberge915fc42005-09-06 15:18:35 -0700919 vol->password = kzalloc(temp_len+1, GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000920 if (vol->password == NULL) {
Steve French50c2f752007-07-13 00:33:32 +0000921 printk(KERN_WARNING "CIFS: no memory "
922 "for password\n");
Steve French433dc242005-04-28 22:41:08 -0700923 return 1;
924 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 strcpy(vol->password, value);
926 }
927 } else if (strnicmp(data, "ip", 2) == 0) {
928 if (!value || !*value) {
929 vol->UNCip = NULL;
930 } else if (strnlen(value, 35) < 35) {
931 vol->UNCip = value;
932 } else {
Steve French50c2f752007-07-13 00:33:32 +0000933 printk(KERN_WARNING "CIFS: ip address "
934 "too long\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 return 1;
936 }
Steve French50c2f752007-07-13 00:33:32 +0000937 } else if (strnicmp(data, "sec", 3) == 0) {
938 if (!value || !*value) {
939 cERROR(1, ("no security value specified"));
940 continue;
941 } else if (strnicmp(value, "krb5i", 5) == 0) {
942 vol->secFlg |= CIFSSEC_MAY_KRB5 |
Steve French189acaa2006-06-23 02:33:48 +0000943 CIFSSEC_MUST_SIGN;
Steve Frenchbf820672005-12-01 22:32:42 -0800944 } else if (strnicmp(value, "krb5p", 5) == 0) {
Steve French50c2f752007-07-13 00:33:32 +0000945 /* vol->secFlg |= CIFSSEC_MUST_SEAL |
946 CIFSSEC_MAY_KRB5; */
947 cERROR(1, ("Krb5 cifs privacy not supported"));
Steve Frenchbf820672005-12-01 22:32:42 -0800948 return 1;
949 } else if (strnicmp(value, "krb5", 4) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000950 vol->secFlg |= CIFSSEC_MAY_KRB5;
Steve Frenchbf820672005-12-01 22:32:42 -0800951 } else if (strnicmp(value, "ntlmv2i", 7) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000952 vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
Steve French189acaa2006-06-23 02:33:48 +0000953 CIFSSEC_MUST_SIGN;
Steve Frenchbf820672005-12-01 22:32:42 -0800954 } else if (strnicmp(value, "ntlmv2", 6) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000955 vol->secFlg |= CIFSSEC_MAY_NTLMV2;
Steve Frenchbf820672005-12-01 22:32:42 -0800956 } else if (strnicmp(value, "ntlmi", 5) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000957 vol->secFlg |= CIFSSEC_MAY_NTLM |
Steve French189acaa2006-06-23 02:33:48 +0000958 CIFSSEC_MUST_SIGN;
Steve Frenchbf820672005-12-01 22:32:42 -0800959 } else if (strnicmp(value, "ntlm", 4) == 0) {
960 /* ntlm is default so can be turned off too */
Steve French750d1152006-06-27 06:28:30 +0000961 vol->secFlg |= CIFSSEC_MAY_NTLM;
Steve Frenchbf820672005-12-01 22:32:42 -0800962 } else if (strnicmp(value, "nontlm", 6) == 0) {
Steve French189acaa2006-06-23 02:33:48 +0000963 /* BB is there a better way to do this? */
Steve French750d1152006-06-27 06:28:30 +0000964 vol->secFlg |= CIFSSEC_MAY_NTLMV2;
Steve French189acaa2006-06-23 02:33:48 +0000965#ifdef CONFIG_CIFS_WEAK_PW_HASH
966 } else if (strnicmp(value, "lanman", 6) == 0) {
Steve French50c2f752007-07-13 00:33:32 +0000967 vol->secFlg |= CIFSSEC_MAY_LANMAN;
Steve French189acaa2006-06-23 02:33:48 +0000968#endif
Steve Frenchbf820672005-12-01 22:32:42 -0800969 } else if (strnicmp(value, "none", 4) == 0) {
Steve French189acaa2006-06-23 02:33:48 +0000970 vol->nullauth = 1;
Steve French50c2f752007-07-13 00:33:32 +0000971 } else {
972 cERROR(1, ("bad security option: %s", value));
973 return 1;
974 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 } else if ((strnicmp(data, "unc", 3) == 0)
976 || (strnicmp(data, "target", 6) == 0)
977 || (strnicmp(data, "path", 4) == 0)) {
978 if (!value || !*value) {
Steve French50c2f752007-07-13 00:33:32 +0000979 printk(KERN_WARNING "CIFS: invalid path to "
980 "network resource\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 return 1; /* needs_arg; */
982 }
983 if ((temp_len = strnlen(value, 300)) < 300) {
Steve French50c2f752007-07-13 00:33:32 +0000984 vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
Steve French4523cc32007-04-30 20:13:06 +0000985 if (vol->UNC == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986 return 1;
Steve French50c2f752007-07-13 00:33:32 +0000987 strcpy(vol->UNC, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988 if (strncmp(vol->UNC, "//", 2) == 0) {
989 vol->UNC[0] = '\\';
990 vol->UNC[1] = '\\';
Steve French50c2f752007-07-13 00:33:32 +0000991 } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 printk(KERN_WARNING
Steve French50c2f752007-07-13 00:33:32 +0000993 "CIFS: UNC Path does not begin "
994 "with // or \\\\ \n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995 return 1;
996 }
997 } else {
998 printk(KERN_WARNING "CIFS: UNC name too long\n");
999 return 1;
1000 }
1001 } else if ((strnicmp(data, "domain", 3) == 0)
1002 || (strnicmp(data, "workgroup", 5) == 0)) {
1003 if (!value || !*value) {
1004 printk(KERN_WARNING "CIFS: invalid domain name\n");
1005 return 1; /* needs_arg; */
1006 }
1007 /* BB are there cases in which a comma can be valid in
1008 a domain name and need special handling? */
Steve French39798772006-05-31 22:40:51 +00001009 if (strnlen(value, 256) < 256) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 vol->domainname = value;
1011 cFYI(1, ("Domain name set"));
1012 } else {
Steve French50c2f752007-07-13 00:33:32 +00001013 printk(KERN_WARNING "CIFS: domain name too "
1014 "long\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 return 1;
1016 }
Steve French50c2f752007-07-13 00:33:32 +00001017 } else if (strnicmp(data, "prefixpath", 10) == 0) {
1018 if (!value || !*value) {
1019 printk(KERN_WARNING
1020 "CIFS: invalid path prefix\n");
1021 return 1; /* needs_argument */
1022 }
1023 if ((temp_len = strnlen(value, 1024)) < 1024) {
Steve French4523cc32007-04-30 20:13:06 +00001024 if (value[0] != '/')
Steve French2fe87f02006-09-21 07:02:52 +00001025 temp_len++; /* missing leading slash */
Steve French50c2f752007-07-13 00:33:32 +00001026 vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
1027 if (vol->prepath == NULL)
1028 return 1;
Steve French4523cc32007-04-30 20:13:06 +00001029 if (value[0] != '/') {
Steve French2fe87f02006-09-21 07:02:52 +00001030 vol->prepath[0] = '/';
Steve French50c2f752007-07-13 00:33:32 +00001031 strcpy(vol->prepath+1, value);
Steve French2fe87f02006-09-21 07:02:52 +00001032 } else
Steve French50c2f752007-07-13 00:33:32 +00001033 strcpy(vol->prepath, value);
1034 cFYI(1, ("prefix path %s", vol->prepath));
1035 } else {
1036 printk(KERN_WARNING "CIFS: prefix too long\n");
1037 return 1;
1038 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 } else if (strnicmp(data, "iocharset", 9) == 0) {
1040 if (!value || !*value) {
Steve French63135e02007-07-17 17:34:02 +00001041 printk(KERN_WARNING "CIFS: invalid iocharset "
1042 "specified\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 return 1; /* needs_arg; */
1044 }
1045 if (strnlen(value, 65) < 65) {
Steve French50c2f752007-07-13 00:33:32 +00001046 if (strnicmp(value, "default", 7))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 vol->iocharset = value;
Steve French50c2f752007-07-13 00:33:32 +00001048 /* if iocharset not set then load_nls_default
1049 is used by caller */
1050 cFYI(1, ("iocharset set to %s", value));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 } else {
Steve French63135e02007-07-17 17:34:02 +00001052 printk(KERN_WARNING "CIFS: iocharset name "
1053 "too long.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 return 1;
1055 }
1056 } else if (strnicmp(data, "uid", 3) == 0) {
1057 if (value && *value) {
1058 vol->linux_uid =
1059 simple_strtoul(value, &value, 0);
Steve French4523cc32007-04-30 20:13:06 +00001060 vol->override_uid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 }
1062 } else if (strnicmp(data, "gid", 3) == 0) {
1063 if (value && *value) {
1064 vol->linux_gid =
1065 simple_strtoul(value, &value, 0);
Steve French4523cc32007-04-30 20:13:06 +00001066 vol->override_gid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 }
1068 } else if (strnicmp(data, "file_mode", 4) == 0) {
1069 if (value && *value) {
1070 vol->file_mode =
1071 simple_strtoul(value, &value, 0);
1072 }
1073 } else if (strnicmp(data, "dir_mode", 4) == 0) {
1074 if (value && *value) {
1075 vol->dir_mode =
1076 simple_strtoul(value, &value, 0);
1077 }
1078 } else if (strnicmp(data, "dirmode", 4) == 0) {
1079 if (value && *value) {
1080 vol->dir_mode =
1081 simple_strtoul(value, &value, 0);
1082 }
1083 } else if (strnicmp(data, "port", 4) == 0) {
1084 if (value && *value) {
1085 vol->port =
1086 simple_strtoul(value, &value, 0);
1087 }
1088 } else if (strnicmp(data, "rsize", 5) == 0) {
1089 if (value && *value) {
1090 vol->rsize =
1091 simple_strtoul(value, &value, 0);
1092 }
1093 } else if (strnicmp(data, "wsize", 5) == 0) {
1094 if (value && *value) {
1095 vol->wsize =
1096 simple_strtoul(value, &value, 0);
1097 }
1098 } else if (strnicmp(data, "sockopt", 5) == 0) {
1099 if (value && *value) {
1100 vol->sockopt =
1101 simple_strtoul(value, &value, 0);
1102 }
1103 } else if (strnicmp(data, "netbiosname", 4) == 0) {
1104 if (!value || !*value || (*value == ' ')) {
Steve French63135e02007-07-17 17:34:02 +00001105 cFYI(1, ("invalid (empty) netbiosname"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 } else {
Steve French50c2f752007-07-13 00:33:32 +00001107 memset(vol->source_rfc1001_name, 0x20, 15);
1108 for (i = 0; i < 15; i++) {
1109 /* BB are there cases in which a comma can be
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 valid in this workstation netbios name (and need
1111 special handling)? */
1112
1113 /* We do not uppercase netbiosname for user */
Steve French50c2f752007-07-13 00:33:32 +00001114 if (value[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 break;
Steve French50c2f752007-07-13 00:33:32 +00001116 else
1117 vol->source_rfc1001_name[i] =
1118 value[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 }
1120 /* The string has 16th byte zero still from
1121 set at top of the function */
Steve French50c2f752007-07-13 00:33:32 +00001122 if ((i == 15) && (value[i] != 0))
1123 printk(KERN_WARNING "CIFS: netbiosname"
1124 " longer than 15 truncated.\n");
Steve Frencha10faeb22005-08-22 21:38:31 -07001125 }
1126 } else if (strnicmp(data, "servern", 7) == 0) {
1127 /* servernetbiosname specified override *SMBSERVER */
1128 if (!value || !*value || (*value == ' ')) {
Steve French467a8f82007-06-27 22:41:32 +00001129 cFYI(1, ("empty server netbiosname specified"));
Steve Frencha10faeb22005-08-22 21:38:31 -07001130 } else {
1131 /* last byte, type, is 0x20 for servr type */
Steve French50c2f752007-07-13 00:33:32 +00001132 memset(vol->target_rfc1001_name, 0x20, 16);
Steve Frencha10faeb22005-08-22 21:38:31 -07001133
Steve French50c2f752007-07-13 00:33:32 +00001134 for (i = 0; i < 15; i++) {
Steve Frencha10faeb22005-08-22 21:38:31 -07001135 /* BB are there cases in which a comma can be
Steve French50c2f752007-07-13 00:33:32 +00001136 valid in this workstation netbios name
1137 (and need special handling)? */
Steve Frencha10faeb22005-08-22 21:38:31 -07001138
Steve French50c2f752007-07-13 00:33:32 +00001139 /* user or mount helper must uppercase
1140 the netbiosname */
1141 if (value[i] == 0)
Steve Frencha10faeb22005-08-22 21:38:31 -07001142 break;
1143 else
Steve French50c2f752007-07-13 00:33:32 +00001144 vol->target_rfc1001_name[i] =
1145 value[i];
Steve Frencha10faeb22005-08-22 21:38:31 -07001146 }
1147 /* The string has 16th byte zero still from
1148 set at top of the function */
Steve French50c2f752007-07-13 00:33:32 +00001149 if ((i == 15) && (value[i] != 0))
1150 printk(KERN_WARNING "CIFS: server net"
1151 "biosname longer than 15 truncated.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 }
1153 } else if (strnicmp(data, "credentials", 4) == 0) {
1154 /* ignore */
1155 } else if (strnicmp(data, "version", 3) == 0) {
1156 /* ignore */
Steve French50c2f752007-07-13 00:33:32 +00001157 } else if (strnicmp(data, "guest", 5) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 /* ignore */
1159 } else if (strnicmp(data, "rw", 2) == 0) {
1160 vol->rw = TRUE;
1161 } else if ((strnicmp(data, "suid", 4) == 0) ||
1162 (strnicmp(data, "nosuid", 6) == 0) ||
1163 (strnicmp(data, "exec", 4) == 0) ||
1164 (strnicmp(data, "noexec", 6) == 0) ||
1165 (strnicmp(data, "nodev", 5) == 0) ||
1166 (strnicmp(data, "noauto", 6) == 0) ||
1167 (strnicmp(data, "dev", 3) == 0)) {
1168 /* The mount tool or mount.cifs helper (if present)
Steve French50c2f752007-07-13 00:33:32 +00001169 uses these opts to set flags, and the flags are read
1170 by the kernel vfs layer before we get here (ie
1171 before read super) so there is no point trying to
1172 parse these options again and set anything and it
1173 is ok to just ignore them */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 continue;
1175 } else if (strnicmp(data, "ro", 2) == 0) {
1176 vol->rw = FALSE;
1177 } else if (strnicmp(data, "hard", 4) == 0) {
1178 vol->retry = 1;
1179 } else if (strnicmp(data, "soft", 4) == 0) {
1180 vol->retry = 0;
1181 } else if (strnicmp(data, "perm", 4) == 0) {
1182 vol->noperm = 0;
1183 } else if (strnicmp(data, "noperm", 6) == 0) {
1184 vol->noperm = 1;
Steve French6a0b4822005-04-28 22:41:05 -07001185 } else if (strnicmp(data, "mapchars", 8) == 0) {
1186 vol->remap = 1;
1187 } else if (strnicmp(data, "nomapchars", 10) == 0) {
1188 vol->remap = 0;
Steve French50c2f752007-07-13 00:33:32 +00001189 } else if (strnicmp(data, "sfu", 3) == 0) {
1190 vol->sfu_emul = 1;
1191 } else if (strnicmp(data, "nosfu", 5) == 0) {
1192 vol->sfu_emul = 0;
Jeremy Allisonac670552005-06-22 17:26:35 -07001193 } else if (strnicmp(data, "posixpaths", 10) == 0) {
1194 vol->posix_paths = 1;
1195 } else if (strnicmp(data, "noposixpaths", 12) == 0) {
1196 vol->posix_paths = 0;
Steve Frenchc18c8422007-07-18 23:21:09 +00001197 } else if (strnicmp(data, "nounix", 6) == 0) {
1198 vol->no_linux_ext = 1;
1199 } else if (strnicmp(data, "nolinux", 7) == 0) {
1200 vol->no_linux_ext = 1;
Steve French50c2f752007-07-13 00:33:32 +00001201 } else if ((strnicmp(data, "nocase", 6) == 0) ||
Steve Frencha10faeb22005-08-22 21:38:31 -07001202 (strnicmp(data, "ignorecase", 10) == 0)) {
Steve French50c2f752007-07-13 00:33:32 +00001203 vol->nocase = 1;
Steve Frenchc46fa8a2005-08-18 20:49:57 -07001204 } else if (strnicmp(data, "brl", 3) == 0) {
1205 vol->nobrl = 0;
Steve French50c2f752007-07-13 00:33:32 +00001206 } else if ((strnicmp(data, "nobrl", 5) == 0) ||
Steve French1c955182005-08-30 20:58:07 -07001207 (strnicmp(data, "nolock", 6) == 0)) {
Steve Frenchc46fa8a2005-08-18 20:49:57 -07001208 vol->nobrl = 1;
Steve Frenchd3485d32005-08-19 11:04:29 -07001209 /* turn off mandatory locking in mode
1210 if remote locking is turned off since the
1211 local vfs will do advisory */
Steve French50c2f752007-07-13 00:33:32 +00001212 if (vol->file_mode ==
1213 (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
Steve Frenchd3485d32005-08-19 11:04:29 -07001214 vol->file_mode = S_IALLUGO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 } else if (strnicmp(data, "setuids", 7) == 0) {
1216 vol->setuids = 1;
1217 } else if (strnicmp(data, "nosetuids", 9) == 0) {
1218 vol->setuids = 0;
1219 } else if (strnicmp(data, "nohard", 6) == 0) {
1220 vol->retry = 0;
1221 } else if (strnicmp(data, "nosoft", 6) == 0) {
1222 vol->retry = 1;
1223 } else if (strnicmp(data, "nointr", 6) == 0) {
1224 vol->intr = 0;
1225 } else if (strnicmp(data, "intr", 4) == 0) {
1226 vol->intr = 1;
Steve French50c2f752007-07-13 00:33:32 +00001227 } else if (strnicmp(data, "serverino", 7) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 vol->server_ino = 1;
Steve French50c2f752007-07-13 00:33:32 +00001229 } else if (strnicmp(data, "noserverino", 9) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 vol->server_ino = 0;
Steve French50c2f752007-07-13 00:33:32 +00001231 } else if (strnicmp(data, "cifsacl", 7) == 0) {
Steve French0a4b92c2006-01-12 15:44:21 -08001232 vol->cifs_acl = 1;
1233 } else if (strnicmp(data, "nocifsacl", 9) == 0) {
1234 vol->cifs_acl = 0;
Steve French50c2f752007-07-13 00:33:32 +00001235 } else if (strnicmp(data, "acl", 3) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 vol->no_psx_acl = 0;
Steve French50c2f752007-07-13 00:33:32 +00001237 } else if (strnicmp(data, "noacl", 5) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 vol->no_psx_acl = 1;
Steve French50c2f752007-07-13 00:33:32 +00001239 } else if (strnicmp(data, "sign", 4) == 0) {
Steve French750d1152006-06-27 06:28:30 +00001240 vol->secFlg |= CIFSSEC_MUST_SIGN;
1241/* } else if (strnicmp(data, "seal",4) == 0) {
1242 vol->secFlg |= CIFSSEC_MUST_SEAL; */
Steve French50c2f752007-07-13 00:33:32 +00001243 } else if (strnicmp(data, "direct", 6) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001244 vol->direct_io = 1;
Steve French50c2f752007-07-13 00:33:32 +00001245 } else if (strnicmp(data, "forcedirectio", 13) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001246 vol->direct_io = 1;
Steve French50c2f752007-07-13 00:33:32 +00001247 } else if (strnicmp(data, "in6_addr", 8) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 if (!value || !*value) {
1249 vol->in6_addr = NULL;
1250 } else if (strnlen(value, 49) == 48) {
1251 vol->in6_addr = value;
1252 } else {
Steve French50c2f752007-07-13 00:33:32 +00001253 printk(KERN_WARNING "CIFS: ip v6 address not "
1254 "48 characters long\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 return 1;
1256 }
1257 } else if (strnicmp(data, "noac", 4) == 0) {
Steve French50c2f752007-07-13 00:33:32 +00001258 printk(KERN_WARNING "CIFS: Mount option noac not "
1259 "supported. Instead set "
1260 "/proc/fs/cifs/LookupCacheEnabled to 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 } else
Steve French50c2f752007-07-13 00:33:32 +00001262 printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
1263 data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 }
1265 if (vol->UNC == NULL) {
Steve French4523cc32007-04-30 20:13:06 +00001266 if (devname == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001267 printk(KERN_WARNING "CIFS: Missing UNC name for mount "
1268 "target\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 return 1;
1270 }
1271 if ((temp_len = strnlen(devname, 300)) < 300) {
Steve French50c2f752007-07-13 00:33:32 +00001272 vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
Steve French4523cc32007-04-30 20:13:06 +00001273 if (vol->UNC == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274 return 1;
Steve French50c2f752007-07-13 00:33:32 +00001275 strcpy(vol->UNC, devname);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 if (strncmp(vol->UNC, "//", 2) == 0) {
1277 vol->UNC[0] = '\\';
1278 vol->UNC[1] = '\\';
1279 } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
Steve French50c2f752007-07-13 00:33:32 +00001280 printk(KERN_WARNING "CIFS: UNC Path does not "
1281 "begin with // or \\\\ \n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 return 1;
1283 }
1284 } else {
1285 printk(KERN_WARNING "CIFS: UNC name too long\n");
1286 return 1;
1287 }
1288 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001289 if (vol->UNCip == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 vol->UNCip = &vol->UNC[2];
1291
1292 return 0;
1293}
1294
1295static struct cifsSesInfo *
Steve French50c2f752007-07-13 00:33:32 +00001296cifs_find_tcp_session(struct in_addr *target_ip_addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297 struct in6_addr *target_ip6_addr,
1298 char *userName, struct TCP_Server_Info **psrvTcp)
1299{
1300 struct list_head *tmp;
1301 struct cifsSesInfo *ses;
1302 *psrvTcp = NULL;
1303 read_lock(&GlobalSMBSeslock);
1304
1305 list_for_each(tmp, &GlobalSMBSessionList) {
1306 ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
1307 if (ses->server) {
Steve French50c2f752007-07-13 00:33:32 +00001308 if ((target_ip_addr &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309 (ses->server->addr.sockAddr.sin_addr.s_addr
1310 == target_ip_addr->s_addr)) || (target_ip6_addr
1311 && memcmp(&ses->server->addr.sockAddr6.sin6_addr,
Steve French50c2f752007-07-13 00:33:32 +00001312 target_ip6_addr, sizeof(*target_ip6_addr)))) {
1313 /* BB lock server and tcp session and increment
1314 use count here?? */
1315
1316 /* found a match on the TCP session */
1317 *psrvTcp = ses->server;
1318
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319 /* BB check if reconnection needed */
1320 if (strncmp
1321 (ses->userName, userName,
1322 MAX_USERNAME_SIZE) == 0){
1323 read_unlock(&GlobalSMBSeslock);
Steve French50c2f752007-07-13 00:33:32 +00001324 /* Found exact match on both TCP and
1325 SMB sessions */
1326 return ses;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327 }
1328 }
1329 }
1330 /* else tcp and smb sessions need reconnection */
1331 }
1332 read_unlock(&GlobalSMBSeslock);
1333 return NULL;
1334}
1335
1336static struct cifsTconInfo *
1337find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
1338{
1339 struct list_head *tmp;
1340 struct cifsTconInfo *tcon;
1341
1342 read_lock(&GlobalSMBSeslock);
1343 list_for_each(tmp, &GlobalTreeConnectionList) {
Steve Frenche466e482006-08-15 13:07:18 +00001344 cFYI(1, ("Next tcon"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
1346 if (tcon->ses) {
1347 if (tcon->ses->server) {
1348 cFYI(1,
Steve Frenche466e482006-08-15 13:07:18 +00001349 ("old ip addr: %x == new ip %x ?",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 tcon->ses->server->addr.sockAddr.sin_addr.
1351 s_addr, new_target_ip_addr));
1352 if (tcon->ses->server->addr.sockAddr.sin_addr.
1353 s_addr == new_target_ip_addr) {
Steve Frenche466e482006-08-15 13:07:18 +00001354 /* BB lock tcon, server and tcp session and increment use count here? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 /* found a match on the TCP session */
1356 /* BB check if reconnection needed */
Steve French50c2f752007-07-13 00:33:32 +00001357 cFYI(1,
1358 ("IP match, old UNC: %s new: %s",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359 tcon->treeName, uncName));
1360 if (strncmp
1361 (tcon->treeName, uncName,
1362 MAX_TREE_SIZE) == 0) {
1363 cFYI(1,
Steve Frenche466e482006-08-15 13:07:18 +00001364 ("and old usr: %s new: %s",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001365 tcon->treeName, uncName));
1366 if (strncmp
1367 (tcon->ses->userName,
1368 userName,
1369 MAX_USERNAME_SIZE) == 0) {
1370 read_unlock(&GlobalSMBSeslock);
Steve Frenche466e482006-08-15 13:07:18 +00001371 /* matched smb session
1372 (user name */
1373 return tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 }
1375 }
1376 }
1377 }
1378 }
1379 }
1380 read_unlock(&GlobalSMBSeslock);
1381 return NULL;
1382}
1383
1384int
1385connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
Steve French737b7582005-04-28 22:41:06 -07001386 const char *old_path, const struct nls_table *nls_codepage,
1387 int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388{
1389 unsigned char *referrals = NULL;
1390 unsigned int num_referrals;
1391 int rc = 0;
1392
Steve French50c2f752007-07-13 00:33:32 +00001393 rc = get_dfs_path(xid, pSesInfo, old_path, nls_codepage,
Steve French737b7582005-04-28 22:41:06 -07001394 &num_referrals, &referrals, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
1396 /* BB Add in code to: if valid refrl, if not ip address contact
Steve French50c2f752007-07-13 00:33:32 +00001397 the helper that resolves tcp names, mount to it, try to
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 tcon to it unmount it if fail */
1399
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001400 kfree(referrals);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401
1402 return rc;
1403}
1404
1405int
Steve French50c2f752007-07-13 00:33:32 +00001406get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
1407 const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
1408 unsigned char **preferrals, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409{
1410 char *temp_unc;
1411 int rc = 0;
1412
1413 *pnum_referrals = 0;
1414
1415 if (pSesInfo->ipc_tid == 0) {
1416 temp_unc = kmalloc(2 /* for slashes */ +
Steve French50c2f752007-07-13 00:33:32 +00001417 strnlen(pSesInfo->serverName,
1418 SERVER_NAME_LEN_WITH_NULL * 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 + 1 + 4 /* slash IPC$ */ + 2,
1420 GFP_KERNEL);
1421 if (temp_unc == NULL)
1422 return -ENOMEM;
1423 temp_unc[0] = '\\';
1424 temp_unc[1] = '\\';
1425 strcpy(temp_unc + 2, pSesInfo->serverName);
1426 strcpy(temp_unc + 2 + strlen(pSesInfo->serverName), "\\IPC$");
1427 rc = CIFSTCon(xid, pSesInfo, temp_unc, NULL, nls_codepage);
1428 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00001429 ("CIFS Tcon rc = %d ipc_tid = %d", rc, pSesInfo->ipc_tid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 kfree(temp_unc);
1431 }
1432 if (rc == 0)
1433 rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
Steve French737b7582005-04-28 22:41:06 -07001434 pnum_referrals, nls_codepage, remap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435
1436 return rc;
1437}
1438
1439/* See RFC1001 section 14 on representation of Netbios names */
Steve French50c2f752007-07-13 00:33:32 +00001440static void rfc1002mangle(char *target, char *source, unsigned int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441{
Steve French50c2f752007-07-13 00:33:32 +00001442 unsigned int i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443
Steve French50c2f752007-07-13 00:33:32 +00001444 for (i = 0, j = 0; i < (length); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 /* mask a nibble at a time and encode */
1446 target[j] = 'A' + (0x0F & (source[i] >> 4));
1447 target[j+1] = 'A' + (0x0F & source[i]);
Steve French50c2f752007-07-13 00:33:32 +00001448 j += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 }
1450
1451}
1452
1453
1454static int
Steve French50c2f752007-07-13 00:33:32 +00001455ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
1456 char *netbios_name, char *target_name)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457{
1458 int rc = 0;
1459 int connected = 0;
1460 __be16 orig_port = 0;
1461
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001462 if (*csocket == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001463 rc = sock_create_kern(PF_INET, SOCK_STREAM,
1464 IPPROTO_TCP, csocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 if (rc < 0) {
Steve French50c2f752007-07-13 00:33:32 +00001466 cERROR(1, ("Error %d creating socket", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 *csocket = NULL;
1468 return rc;
1469 } else {
1470 /* BB other socket options to set KEEPALIVE, NODELAY? */
Steve French467a8f82007-06-27 22:41:32 +00001471 cFYI(1, ("Socket created"));
Steve French50c2f752007-07-13 00:33:32 +00001472 (*csocket)->sk->sk_allocation = GFP_NOFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473 }
1474 }
1475
1476 psin_server->sin_family = AF_INET;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001477 if (psin_server->sin_port) { /* user overrode default port */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478 rc = (*csocket)->ops->connect(*csocket,
1479 (struct sockaddr *) psin_server,
Steve French50c2f752007-07-13 00:33:32 +00001480 sizeof (struct sockaddr_in), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481 if (rc >= 0)
1482 connected = 1;
Steve French50c2f752007-07-13 00:33:32 +00001483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001484
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001485 if (!connected) {
Steve French50c2f752007-07-13 00:33:32 +00001486 /* save original port so we can retry user specified port
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 later if fall back ports fail this time */
1488 orig_port = psin_server->sin_port;
1489
1490 /* do not retry on the same port we just failed on */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001491 if (psin_server->sin_port != htons(CIFS_PORT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 psin_server->sin_port = htons(CIFS_PORT);
1493
1494 rc = (*csocket)->ops->connect(*csocket,
1495 (struct sockaddr *) psin_server,
Steve French50c2f752007-07-13 00:33:32 +00001496 sizeof (struct sockaddr_in), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 if (rc >= 0)
1498 connected = 1;
1499 }
1500 }
1501 if (!connected) {
1502 psin_server->sin_port = htons(RFC1001_PORT);
1503 rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
Steve French50c2f752007-07-13 00:33:32 +00001504 psin_server,
1505 sizeof (struct sockaddr_in), 0);
1506 if (rc >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 connected = 1;
1508 }
1509
1510 /* give up here - unless we want to retry on different
1511 protocol families some day */
1512 if (!connected) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001513 if (orig_port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 psin_server->sin_port = orig_port;
Steve French50c2f752007-07-13 00:33:32 +00001515 cFYI(1, ("Error %d connecting to server via ipv4", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 sock_release(*csocket);
1517 *csocket = NULL;
1518 return rc;
1519 }
Steve French50c2f752007-07-13 00:33:32 +00001520 /* Eventually check for other socket options to change from
1521 the default. sock_setsockopt not used because it expects
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 user space buffer */
Steve French50c2f752007-07-13 00:33:32 +00001523 cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
1524 (*csocket)->sk->sk_sndbuf,
Steve Frenchb387eae2005-10-10 14:21:15 -07001525 (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
Steve Frenchb387eae2005-10-10 14:21:15 -07001527 /* make the bufsizes depend on wsize/rsize and max requests */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001528 if ((*csocket)->sk->sk_sndbuf < (200 * 1024))
Steve Frenchb387eae2005-10-10 14:21:15 -07001529 (*csocket)->sk->sk_sndbuf = 200 * 1024;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001530 if ((*csocket)->sk->sk_rcvbuf < (140 * 1024))
Steve Frenchb387eae2005-10-10 14:21:15 -07001531 (*csocket)->sk->sk_rcvbuf = 140 * 1024;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
1533 /* send RFC1001 sessinit */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001534 if (psin_server->sin_port == htons(RFC1001_PORT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 /* some servers require RFC1001 sessinit before sending
Steve French50c2f752007-07-13 00:33:32 +00001536 negprot - BB check reconnection in case where second
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537 sessinit is sent but no second negprot */
Steve French50c2f752007-07-13 00:33:32 +00001538 struct rfc1002_session_packet *ses_init_buf;
1539 struct smb_hdr *smb_buf;
1540 ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
1541 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001542 if (ses_init_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 ses_init_buf->trailer.session_req.called_len = 32;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001544 if (target_name && (target_name[0] != 0)) {
Steve Frencha10faeb22005-08-22 21:38:31 -07001545 rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
1546 target_name, 16);
1547 } else {
1548 rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
Steve French50c2f752007-07-13 00:33:32 +00001549 DEFAULT_CIFS_CALLED_NAME, 16);
Steve Frencha10faeb22005-08-22 21:38:31 -07001550 }
1551
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552 ses_init_buf->trailer.session_req.calling_len = 32;
1553 /* calling name ends in null (byte 16) from old smb
1554 convention. */
Steve French50c2f752007-07-13 00:33:32 +00001555 if (netbios_name && (netbios_name[0] != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
Steve French50c2f752007-07-13 00:33:32 +00001557 netbios_name, 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558 } else {
1559 rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
Steve French50c2f752007-07-13 00:33:32 +00001560 "LINUX_CIFS_CLNT", 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 }
1562 ses_init_buf->trailer.session_req.scope1 = 0;
1563 ses_init_buf->trailer.session_req.scope2 = 0;
1564 smb_buf = (struct smb_hdr *)ses_init_buf;
1565 /* sizeof RFC1002_SESSION_REQUEST with no scope */
1566 smb_buf->smb_buf_length = 0x81000044;
1567 rc = smb_send(*csocket, smb_buf, 0x44,
1568 (struct sockaddr *)psin_server);
1569 kfree(ses_init_buf);
Steve French50c2f752007-07-13 00:33:32 +00001570 msleep(1); /* RFC1001 layer in at least one server
Steve French083d3a22006-03-03 09:53:36 +00001571 requires very short break before negprot
1572 presumably because not expecting negprot
1573 to follow so fast. This is a simple
Steve French50c2f752007-07-13 00:33:32 +00001574 solution that works without
Steve French083d3a22006-03-03 09:53:36 +00001575 complicating the code and causes no
1576 significant slowing down on mount
1577 for everyone else */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 }
Steve French50c2f752007-07-13 00:33:32 +00001579 /* else the negprot may still work without this
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580 even though malloc failed */
Steve French50c2f752007-07-13 00:33:32 +00001581
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 }
Steve French50c2f752007-07-13 00:33:32 +00001583
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584 return rc;
1585}
1586
1587static int
1588ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket)
1589{
1590 int rc = 0;
1591 int connected = 0;
1592 __be16 orig_port = 0;
1593
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001594 if (*csocket == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001595 rc = sock_create_kern(PF_INET6, SOCK_STREAM,
1596 IPPROTO_TCP, csocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 if (rc < 0) {
Steve French50c2f752007-07-13 00:33:32 +00001598 cERROR(1, ("Error %d creating ipv6 socket", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 *csocket = NULL;
1600 return rc;
1601 } else {
1602 /* BB other socket options to set KEEPALIVE, NODELAY? */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001603 cFYI(1, ("ipv6 Socket created"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604 (*csocket)->sk->sk_allocation = GFP_NOFS;
1605 }
1606 }
1607
1608 psin_server->sin6_family = AF_INET6;
1609
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001610 if (psin_server->sin6_port) { /* user overrode default port */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 rc = (*csocket)->ops->connect(*csocket,
1612 (struct sockaddr *) psin_server,
Steve French50c2f752007-07-13 00:33:32 +00001613 sizeof (struct sockaddr_in6), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614 if (rc >= 0)
1615 connected = 1;
Steve French50c2f752007-07-13 00:33:32 +00001616 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001618 if (!connected) {
Steve French50c2f752007-07-13 00:33:32 +00001619 /* save original port so we can retry user specified port
Linus Torvalds1da177e2005-04-16 15:20:36 -07001620 later if fall back ports fail this time */
1621
1622 orig_port = psin_server->sin6_port;
1623 /* do not retry on the same port we just failed on */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001624 if (psin_server->sin6_port != htons(CIFS_PORT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 psin_server->sin6_port = htons(CIFS_PORT);
1626
1627 rc = (*csocket)->ops->connect(*csocket,
1628 (struct sockaddr *) psin_server,
Steve French50c2f752007-07-13 00:33:32 +00001629 sizeof (struct sockaddr_in6), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 if (rc >= 0)
1631 connected = 1;
1632 }
1633 }
1634 if (!connected) {
1635 psin_server->sin6_port = htons(RFC1001_PORT);
1636 rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
Steve French50c2f752007-07-13 00:33:32 +00001637 psin_server, sizeof (struct sockaddr_in6), 0);
1638 if (rc >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639 connected = 1;
1640 }
1641
1642 /* give up here - unless we want to retry on different
1643 protocol families some day */
1644 if (!connected) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001645 if (orig_port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 psin_server->sin6_port = orig_port;
Steve French50c2f752007-07-13 00:33:32 +00001647 cFYI(1, ("Error %d connecting to server via ipv6", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 sock_release(*csocket);
1649 *csocket = NULL;
1650 return rc;
1651 }
Steve French50c2f752007-07-13 00:33:32 +00001652 /* Eventually check for other socket options to change from
1653 the default. sock_setsockopt not used because it expects
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 user space buffer */
1655 (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
Steve French50c2f752007-07-13 00:33:32 +00001656
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 return rc;
1658}
1659
Steve French50c2f752007-07-13 00:33:32 +00001660void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
1661 struct super_block *sb, struct smb_vol *vol_info)
Steve French8af18972007-02-14 04:42:51 +00001662{
1663 /* if we are reconnecting then should we check to see if
1664 * any requested capabilities changed locally e.g. via
1665 * remount but we can not do much about it here
1666 * if they have (even if we could detect it by the following)
1667 * Perhaps we could add a backpointer to array of sb from tcon
1668 * or if we change to make all sb to same share the same
1669 * sb as NFS - then we only have one backpointer to sb.
1670 * What if we wanted to mount the server share twice once with
1671 * and once without posixacls or posix paths? */
1672 __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
Steve French50c2f752007-07-13 00:33:32 +00001673
Steve Frenchc18c8422007-07-18 23:21:09 +00001674 if (vol_info && vol_info->no_linux_ext) {
1675 tcon->fsUnixInfo.Capability = 0;
1676 tcon->unix_ext = 0; /* Unix Extensions disabled */
1677 cFYI(1, ("Linux protocol extensions disabled"));
1678 return;
1679 } else if (vol_info)
1680 tcon->unix_ext = 1; /* Unix Extensions supported */
1681
1682 if (tcon->unix_ext == 0) {
1683 cFYI(1, ("Unix extensions disabled so not set on reconnect"));
1684 return;
1685 }
Steve French50c2f752007-07-13 00:33:32 +00001686
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001687 if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
Steve French8af18972007-02-14 04:42:51 +00001688 __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
Steve French50c2f752007-07-13 00:33:32 +00001689
Steve French8af18972007-02-14 04:42:51 +00001690 /* check for reconnect case in which we do not
1691 want to change the mount behavior if we can avoid it */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001692 if (vol_info == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001693 /* turn off POSIX ACL and PATHNAMES if not set
Steve French8af18972007-02-14 04:42:51 +00001694 originally at mount time */
1695 if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
1696 cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
1697 if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0)
1698 cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
Steve French8af18972007-02-14 04:42:51 +00001699 }
Steve French50c2f752007-07-13 00:33:32 +00001700
Steve French8af18972007-02-14 04:42:51 +00001701 cap &= CIFS_UNIX_CAP_MASK;
Steve French75865f82007-06-24 18:30:48 +00001702 if (vol_info && vol_info->no_psx_acl)
Steve French8af18972007-02-14 04:42:51 +00001703 cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
Steve French75865f82007-06-24 18:30:48 +00001704 else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001705 cFYI(1, ("negotiated posix acl support"));
1706 if (sb)
Steve French8af18972007-02-14 04:42:51 +00001707 sb->s_flags |= MS_POSIXACL;
1708 }
1709
Steve French75865f82007-06-24 18:30:48 +00001710 if (vol_info && vol_info->posix_paths == 0)
Steve French8af18972007-02-14 04:42:51 +00001711 cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
Steve French75865f82007-06-24 18:30:48 +00001712 else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001713 cFYI(1, ("negotiate posix pathnames"));
Steve French75865f82007-06-24 18:30:48 +00001714 if (sb)
Steve French50c2f752007-07-13 00:33:32 +00001715 CIFS_SB(sb)->mnt_cifs_flags |=
Steve French8af18972007-02-14 04:42:51 +00001716 CIFS_MOUNT_POSIX_PATHS;
1717 }
Steve French50c2f752007-07-13 00:33:32 +00001718
Steve French984acfe2007-04-26 16:42:50 +00001719 /* We might be setting the path sep back to a different
1720 form if we are reconnecting and the server switched its
Steve French50c2f752007-07-13 00:33:32 +00001721 posix path capability for this share */
Steve French75865f82007-06-24 18:30:48 +00001722 if (sb && (CIFS_SB(sb)->prepathlen > 0))
Steve French984acfe2007-04-26 16:42:50 +00001723 CIFS_SB(sb)->prepath[0] = CIFS_DIR_SEP(CIFS_SB(sb));
Steve French75865f82007-06-24 18:30:48 +00001724
1725 if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) {
1726 if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
1727 CIFS_SB(sb)->rsize = 127 * 1024;
1728#ifdef CONFIG_CIFS_DEBUG2
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001729 cFYI(1, ("larger reads not supported by srv"));
Steve French75865f82007-06-24 18:30:48 +00001730#endif
1731 }
1732 }
Steve French50c2f752007-07-13 00:33:32 +00001733
1734
1735 cFYI(1, ("Negotiate caps 0x%x", (int)cap));
Steve French8af18972007-02-14 04:42:51 +00001736#ifdef CONFIG_CIFS_DEBUG2
Steve French75865f82007-06-24 18:30:48 +00001737 if (cap & CIFS_UNIX_FCNTL_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001738 cFYI(1, ("FCNTL cap"));
Steve French75865f82007-06-24 18:30:48 +00001739 if (cap & CIFS_UNIX_EXTATTR_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001740 cFYI(1, ("EXTATTR cap"));
Steve French75865f82007-06-24 18:30:48 +00001741 if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001742 cFYI(1, ("POSIX path cap"));
Steve French75865f82007-06-24 18:30:48 +00001743 if (cap & CIFS_UNIX_XATTR_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001744 cFYI(1, ("XATTR cap"));
Steve French75865f82007-06-24 18:30:48 +00001745 if (cap & CIFS_UNIX_POSIX_ACL_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001746 cFYI(1, ("POSIX ACL cap"));
Steve French75865f82007-06-24 18:30:48 +00001747 if (cap & CIFS_UNIX_LARGE_READ_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001748 cFYI(1, ("very large read cap"));
Steve French75865f82007-06-24 18:30:48 +00001749 if (cap & CIFS_UNIX_LARGE_WRITE_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001750 cFYI(1, ("very large write cap"));
Steve French8af18972007-02-14 04:42:51 +00001751#endif /* CIFS_DEBUG2 */
1752 if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001753 cFYI(1, ("setting capabilities failed"));
Steve French8af18972007-02-14 04:42:51 +00001754 }
1755 }
1756}
1757
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758int
1759cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
1760 char *mount_data, const char *devname)
1761{
1762 int rc = 0;
1763 int xid;
1764 int address_type = AF_INET;
1765 struct socket *csocket = NULL;
1766 struct sockaddr_in sin_server;
1767 struct sockaddr_in6 sin_server6;
1768 struct smb_vol volume_info;
1769 struct cifsSesInfo *pSesInfo = NULL;
1770 struct cifsSesInfo *existingCifsSes = NULL;
1771 struct cifsTconInfo *tcon = NULL;
1772 struct TCP_Server_Info *srvTcp = NULL;
1773
1774 xid = GetXid();
1775
1776/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
Steve French50c2f752007-07-13 00:33:32 +00001777
1778 memset(&volume_info, 0, sizeof(struct smb_vol));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001779 if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001780 kfree(volume_info.UNC);
1781 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001782 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 FreeXid(xid);
1784 return -EINVAL;
1785 }
1786
Jeff Layton8426c392007-05-05 03:27:49 +00001787 if (volume_info.nullauth) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001788 cFYI(1, ("null user"));
Jeff Layton8426c392007-05-05 03:27:49 +00001789 volume_info.username = NULL;
1790 } else if (volume_info.username) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791 /* BB fixme parse for domain name here */
Steve French467a8f82007-06-27 22:41:32 +00001792 cFYI(1, ("Username: %s", volume_info.username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793 } else {
Steve Frenchbf820672005-12-01 22:32:42 -08001794 cifserror("No username specified");
Steve French50c2f752007-07-13 00:33:32 +00001795 /* In userspace mount helper we can get user name from alternate
1796 locations such as env variables and files on disk */
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001797 kfree(volume_info.UNC);
1798 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001799 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 FreeXid(xid);
1801 return -EINVAL;
1802 }
1803
1804 if (volume_info.UNCip && volume_info.UNC) {
Steve French50c2f752007-07-13 00:33:32 +00001805 rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
1806 &sin_server.sin_addr.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001808 if (rc <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809 /* not ipv4 address, try ipv6 */
Steve French50c2f752007-07-13 00:33:32 +00001810 rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
1811 &sin_server6.sin6_addr.in6_u);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001812 if (rc > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813 address_type = AF_INET6;
1814 } else {
1815 address_type = AF_INET;
1816 }
Steve French50c2f752007-07-13 00:33:32 +00001817
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001818 if (rc <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 /* we failed translating address */
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001820 kfree(volume_info.UNC);
1821 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001822 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823 FreeXid(xid);
1824 return -EINVAL;
1825 }
1826
1827 cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
1828 /* success */
1829 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00001830 } else if (volume_info.UNCip) {
1831 /* BB using ip addr as server name to connect to the
1832 DFS root below */
1833 cERROR(1, ("Connecting to DFS root not implemented yet"));
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001834 kfree(volume_info.UNC);
1835 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001836 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 FreeXid(xid);
1838 return -EINVAL;
1839 } else /* which servers DFS root would we conect to */ {
1840 cERROR(1,
Steve French50c2f752007-07-13 00:33:32 +00001841 ("CIFS mount error: No UNC path (e.g. -o "
1842 "unc=//192.168.1.100/public) specified"));
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001843 kfree(volume_info.UNC);
1844 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001845 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 FreeXid(xid);
1847 return -EINVAL;
1848 }
1849
1850 /* this is needed for ASCII cp to Unicode converts */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001851 if (volume_info.iocharset == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 cifs_sb->local_nls = load_nls_default();
1853 /* load_nls_default can not return null */
1854 } else {
1855 cifs_sb->local_nls = load_nls(volume_info.iocharset);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001856 if (cifs_sb->local_nls == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001857 cERROR(1, ("CIFS mount error: iocharset %s not found",
1858 volume_info.iocharset));
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001859 kfree(volume_info.UNC);
1860 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001861 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 FreeXid(xid);
1863 return -ELIBACC;
1864 }
1865 }
1866
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001867 if (address_type == AF_INET)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868 existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr,
1869 NULL /* no ipv6 addr */,
1870 volume_info.username, &srvTcp);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001871 else if (address_type == AF_INET6) {
1872 cFYI(1, ("looking for ipv6 address"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873 existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */,
1874 &sin_server6.sin6_addr,
1875 volume_info.username, &srvTcp);
Steve French5858ae42007-04-25 11:59:10 +00001876 } else {
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001877 kfree(volume_info.UNC);
1878 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001879 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880 FreeXid(xid);
1881 return -EINVAL;
1882 }
1883
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884 if (srvTcp) {
Steve French50c2f752007-07-13 00:33:32 +00001885 cFYI(1, ("Existing tcp session with server found"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886 } else { /* create socket */
Steve French4523cc32007-04-30 20:13:06 +00001887 if (volume_info.port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001888 sin_server.sin_port = htons(volume_info.port);
1889 else
1890 sin_server.sin_port = 0;
Steve French5858ae42007-04-25 11:59:10 +00001891 if (address_type == AF_INET6) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001892 cFYI(1, ("attempting ipv6 connect"));
Steve French5858ae42007-04-25 11:59:10 +00001893 /* BB should we allow ipv6 on port 139? */
1894 /* other OS never observed in Wild doing 139 with v6 */
Steve French50c2f752007-07-13 00:33:32 +00001895 rc = ipv6_connect(&sin_server6, &csocket);
1896 } else
1897 rc = ipv4_connect(&sin_server, &csocket,
Steve Frencha10faeb22005-08-22 21:38:31 -07001898 volume_info.source_rfc1001_name,
1899 volume_info.target_rfc1001_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 if (rc < 0) {
Steve French50c2f752007-07-13 00:33:32 +00001901 cERROR(1, ("Error connecting to IPv4 socket. "
1902 "Aborting operation"));
Steve French4523cc32007-04-30 20:13:06 +00001903 if (csocket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 sock_release(csocket);
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001905 kfree(volume_info.UNC);
1906 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001907 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908 FreeXid(xid);
1909 return rc;
1910 }
1911
1912 srvTcp = kmalloc(sizeof (struct TCP_Server_Info), GFP_KERNEL);
1913 if (srvTcp == NULL) {
1914 rc = -ENOMEM;
1915 sock_release(csocket);
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001916 kfree(volume_info.UNC);
1917 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001918 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919 FreeXid(xid);
1920 return rc;
1921 } else {
1922 memset(srvTcp, 0, sizeof (struct TCP_Server_Info));
Steve French50c2f752007-07-13 00:33:32 +00001923 memcpy(&srvTcp->addr.sockAddr, &sin_server,
1924 sizeof (struct sockaddr_in));
1925 atomic_set(&srvTcp->inFlight, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 /* BB Add code for ipv6 case too */
1927 srvTcp->ssocket = csocket;
1928 srvTcp->protocolType = IPV4;
1929 init_waitqueue_head(&srvTcp->response_q);
1930 init_waitqueue_head(&srvTcp->request_q);
1931 INIT_LIST_HEAD(&srvTcp->pending_mid_q);
1932 /* at this point we are the only ones with the pointer
1933 to the struct since the kernel thread not created yet
1934 so no need to spinlock this init of tcpStatus */
1935 srvTcp->tcpStatus = CifsNew;
1936 init_MUTEX(&srvTcp->tcpSem);
Igor Mammedovaaf737a2007-04-03 19:16:43 +00001937 srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
Steve French4523cc32007-04-30 20:13:06 +00001938 if ( IS_ERR(srvTcp->tsk) ) {
Igor Mammedovaaf737a2007-04-03 19:16:43 +00001939 rc = PTR_ERR(srvTcp->tsk);
Steve French50c2f752007-07-13 00:33:32 +00001940 cERROR(1, ("error %d create cifsd thread", rc));
Igor Mammedovaaf737a2007-04-03 19:16:43 +00001941 srvTcp->tsk = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942 sock_release(csocket);
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001943 kfree(volume_info.UNC);
1944 kfree(volume_info.password);
Steve French2fe87f02006-09-21 07:02:52 +00001945 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946 FreeXid(xid);
1947 return rc;
Steve Frenchf1914012005-08-18 09:37:34 -07001948 }
1949 wait_for_completion(&cifsd_complete);
1950 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00001951 memcpy(srvTcp->workstation_RFC1001_name,
1952 volume_info.source_rfc1001_name, 16);
1953 memcpy(srvTcp->server_RFC1001_name,
1954 volume_info.target_rfc1001_name, 16);
Steve Frenchad009ac2005-04-28 22:41:05 -07001955 srvTcp->sequence_number = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956 }
1957 }
1958
1959 if (existingCifsSes) {
1960 pSesInfo = existingCifsSes;
Steve Frenchbf820672005-12-01 22:32:42 -08001961 cFYI(1, ("Existing smb sess found"));
Jesper Juhlf99d49a2005-11-07 01:01:34 -08001962 kfree(volume_info.password);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 /* volume_info.UNC freed at end of function */
1964 } else if (!rc) {
Steve Frenchbf820672005-12-01 22:32:42 -08001965 cFYI(1, ("Existing smb sess not found"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966 pSesInfo = sesInfoAlloc();
1967 if (pSesInfo == NULL)
1968 rc = -ENOMEM;
1969 else {
1970 pSesInfo->server = srvTcp;
1971 sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
1972 NIPQUAD(sin_server.sin_addr.s_addr));
1973 }
1974
Steve French50c2f752007-07-13 00:33:32 +00001975 if (!rc) {
1976 /* volume_info.password freed at unmount */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001977 if (volume_info.password)
1978 pSesInfo->password = volume_info.password;
1979 if (volume_info.username)
1980 strncpy(pSesInfo->userName,
Steve French50c2f752007-07-13 00:33:32 +00001981 volume_info.username,
1982 MAX_USERNAME_SIZE);
Steve French39798772006-05-31 22:40:51 +00001983 if (volume_info.domainname) {
1984 int len = strlen(volume_info.domainname);
Steve French50c2f752007-07-13 00:33:32 +00001985 pSesInfo->domainName =
Steve French39798772006-05-31 22:40:51 +00001986 kmalloc(len + 1, GFP_KERNEL);
Steve French4523cc32007-04-30 20:13:06 +00001987 if (pSesInfo->domainName)
Steve French39798772006-05-31 22:40:51 +00001988 strcpy(pSesInfo->domainName,
1989 volume_info.domainname);
1990 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 pSesInfo->linux_uid = volume_info.linux_uid;
Steve French750d1152006-06-27 06:28:30 +00001992 pSesInfo->overrideSecFlg = volume_info.secFlg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 down(&pSesInfo->sesSem);
Steve French189acaa2006-06-23 02:33:48 +00001994 /* BB FIXME need to pass vol->secFlgs BB */
Steve French50c2f752007-07-13 00:33:32 +00001995 rc = cifs_setup_session(xid, pSesInfo,
1996 cifs_sb->local_nls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 up(&pSesInfo->sesSem);
Steve French4523cc32007-04-30 20:13:06 +00001998 if (!rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 atomic_inc(&srvTcp->socketUseCount);
2000 } else
Jesper Juhlf99d49a2005-11-07 01:01:34 -08002001 kfree(volume_info.password);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 }
Steve French50c2f752007-07-13 00:33:32 +00002003
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 /* search for existing tcon to this server share */
2005 if (!rc) {
Steve French4523cc32007-04-30 20:13:06 +00002006 if (volume_info.rsize > CIFSMaxBufSize) {
Steve French50c2f752007-07-13 00:33:32 +00002007 cERROR(1, ("rsize %d too large, using MaxBufSize",
Steve French0ae0efa2005-10-10 10:57:19 -07002008 volume_info.rsize));
2009 cifs_sb->rsize = CIFSMaxBufSize;
Steve French75865f82007-06-24 18:30:48 +00002010 } else if ((volume_info.rsize) &&
2011 (volume_info.rsize <= CIFSMaxBufSize))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 cifs_sb->rsize = volume_info.rsize;
Steve French0ae0efa2005-10-10 10:57:19 -07002013 else /* default */
2014 cifs_sb->rsize = CIFSMaxBufSize;
2015
Steve French4523cc32007-04-30 20:13:06 +00002016 if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
Steve French50c2f752007-07-13 00:33:32 +00002017 cERROR(1, ("wsize %d too large, using 4096 instead",
Steve French0ae0efa2005-10-10 10:57:19 -07002018 volume_info.wsize));
2019 cifs_sb->wsize = 4096;
Steve French4523cc32007-04-30 20:13:06 +00002020 } else if (volume_info.wsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002021 cifs_sb->wsize = volume_info.wsize;
2022 else
Steve French50c2f752007-07-13 00:33:32 +00002023 cifs_sb->wsize =
Steve French1877c9e2006-01-27 18:36:11 -08002024 min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE,
2025 127*1024);
Steve French17cbbaf2006-01-24 20:26:48 -08002026 /* old default of CIFSMaxBufSize was too small now
Steve French50c2f752007-07-13 00:33:32 +00002027 that SMB Write2 can send multiple pages in kvec.
Steve French17cbbaf2006-01-24 20:26:48 -08002028 RFC1001 does not describe what happens when frame
2029 bigger than 128K is sent so use that as max in
2030 conjunction with 52K kvec constraint on arch with 4K
2031 page size */
2032
Steve French4523cc32007-04-30 20:13:06 +00002033 if (cifs_sb->rsize < 2048) {
Steve French50c2f752007-07-13 00:33:32 +00002034 cifs_sb->rsize = 2048;
Steve French6cec2ae2006-02-22 17:31:52 -06002035 /* Windows ME may prefer this */
Steve French467a8f82007-06-27 22:41:32 +00002036 cFYI(1, ("readsize set to minimum: 2048"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 }
Steve French2fe87f02006-09-21 07:02:52 +00002038 /* calculate prepath */
2039 cifs_sb->prepath = volume_info.prepath;
Steve French4523cc32007-04-30 20:13:06 +00002040 if (cifs_sb->prepath) {
Steve French2fe87f02006-09-21 07:02:52 +00002041 cifs_sb->prepathlen = strlen(cifs_sb->prepath);
2042 cifs_sb->prepath[0] = CIFS_DIR_SEP(cifs_sb);
2043 volume_info.prepath = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002044 } else
Steve French2fe87f02006-09-21 07:02:52 +00002045 cifs_sb->prepathlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 cifs_sb->mnt_uid = volume_info.linux_uid;
2047 cifs_sb->mnt_gid = volume_info.linux_gid;
2048 cifs_sb->mnt_file_mode = volume_info.file_mode;
2049 cifs_sb->mnt_dir_mode = volume_info.dir_mode;
Steve French467a8f82007-06-27 22:41:32 +00002050 cFYI(1, ("file mode: 0x%x dir mode: 0x%x",
2051 cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052
Steve French4523cc32007-04-30 20:13:06 +00002053 if (volume_info.noperm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
Steve French4523cc32007-04-30 20:13:06 +00002055 if (volume_info.setuids)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
Steve French4523cc32007-04-30 20:13:06 +00002057 if (volume_info.server_ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
Steve French4523cc32007-04-30 20:13:06 +00002059 if (volume_info.remap)
Steve French6a0b4822005-04-28 22:41:05 -07002060 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
Steve French4523cc32007-04-30 20:13:06 +00002061 if (volume_info.no_xattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
Steve French4523cc32007-04-30 20:13:06 +00002063 if (volume_info.sfu_emul)
Steve Frenchd7245c22005-07-14 18:25:12 -05002064 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
Steve French4523cc32007-04-30 20:13:06 +00002065 if (volume_info.nobrl)
Steve Frenchc46fa8a2005-08-18 20:49:57 -07002066 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
Steve French4523cc32007-04-30 20:13:06 +00002067 if (volume_info.cifs_acl)
Steve French0a4b92c2006-01-12 15:44:21 -08002068 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
Steve French4523cc32007-04-30 20:13:06 +00002069 if (volume_info.override_uid)
2070 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
2071 if (volume_info.override_gid)
2072 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
2073 if (volume_info.direct_io) {
Steve French467a8f82007-06-27 22:41:32 +00002074 cFYI(1, ("mounting share using direct i/o"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
2076 }
2077
2078 tcon =
2079 find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
2080 volume_info.username);
2081 if (tcon) {
Steve Frenchbf820672005-12-01 22:32:42 -08002082 cFYI(1, ("Found match on UNC path"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 /* we can have only one retry value for a connection
2084 to a share so for resources mounted more than once
Steve French50c2f752007-07-13 00:33:32 +00002085 to the same server share the last value passed in
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 for the retry flag is used */
2087 tcon->retry = volume_info.retry;
Steve Frenchd3485d32005-08-19 11:04:29 -07002088 tcon->nocase = volume_info.nocase;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 } else {
2090 tcon = tconInfoAlloc();
2091 if (tcon == NULL)
2092 rc = -ENOMEM;
2093 else {
Steve French50c2f752007-07-13 00:33:32 +00002094 /* check for null share name ie connecting to
Steve French8af18972007-02-14 04:42:51 +00002095 * dfs root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096
Steve French50c2f752007-07-13 00:33:32 +00002097 /* BB check if this works for exactly length
Steve French8af18972007-02-14 04:42:51 +00002098 * three strings */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 if ((strchr(volume_info.UNC + 3, '\\') == NULL)
2100 && (strchr(volume_info.UNC + 3, '/') ==
2101 NULL)) {
Steve French737b7582005-04-28 22:41:06 -07002102 rc = connect_to_dfs_path(xid, pSesInfo,
Steve French8af18972007-02-14 04:42:51 +00002103 "", cifs_sb->local_nls,
Steve French50c2f752007-07-13 00:33:32 +00002104 cifs_sb->mnt_cifs_flags &
Steve French8af18972007-02-14 04:42:51 +00002105 CIFS_MOUNT_MAP_SPECIAL_CHR);
Jesper Juhlf99d49a2005-11-07 01:01:34 -08002106 kfree(volume_info.UNC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 FreeXid(xid);
2108 return -ENODEV;
2109 } else {
Steve French8af18972007-02-14 04:42:51 +00002110 /* BB Do we need to wrap sesSem around
2111 * this TCon call and Unix SetFS as
2112 * we do on SessSetup and reconnect? */
Steve French50c2f752007-07-13 00:33:32 +00002113 rc = CIFSTCon(xid, pSesInfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 volume_info.UNC,
2115 tcon, cifs_sb->local_nls);
2116 cFYI(1, ("CIFS Tcon rc = %d", rc));
2117 }
2118 if (!rc) {
2119 atomic_inc(&pSesInfo->inUse);
2120 tcon->retry = volume_info.retry;
Steve Frenchd3485d32005-08-19 11:04:29 -07002121 tcon->nocase = volume_info.nocase;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 }
2123 }
2124 }
2125 }
Steve French4523cc32007-04-30 20:13:06 +00002126 if (pSesInfo) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002127 if (pSesInfo->capabilities & CAP_LARGE_FILES) {
2128 sb->s_maxbytes = (u64) 1 << 63;
2129 } else
2130 sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */
2131 }
2132
Steve French8af18972007-02-14 04:42:51 +00002133 /* BB FIXME fix time_gran to be larger for LANMAN sessions */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134 sb->s_time_gran = 100;
2135
2136/* on error free sesinfo and tcon struct if needed */
2137 if (rc) {
2138 /* if session setup failed, use count is zero but
2139 we still need to free cifsd thread */
Steve French4523cc32007-04-30 20:13:06 +00002140 if (atomic_read(&srvTcp->socketUseCount) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 spin_lock(&GlobalMid_Lock);
2142 srvTcp->tcpStatus = CifsExiting;
2143 spin_unlock(&GlobalMid_Lock);
Steve French4523cc32007-04-30 20:13:06 +00002144 if (srvTcp->tsk) {
Steve French28356a12007-05-23 14:45:36 +00002145 struct task_struct *tsk;
2146 /* If we could verify that kthread_stop would
2147 always wake up processes blocked in
2148 tcp in recv_mesg then we could remove the
2149 send_sig call */
Steve French50c2f752007-07-13 00:33:32 +00002150 force_sig(SIGKILL, srvTcp->tsk);
Steve French28356a12007-05-23 14:45:36 +00002151 tsk = srvTcp->tsk;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002152 if (tsk)
Steve Frenchf7f7c312007-05-24 02:29:51 +00002153 kthread_stop(tsk);
Steve Frenchf1914012005-08-18 09:37:34 -07002154 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 }
2156 /* If find_unc succeeded then rc == 0 so we can not end */
2157 if (tcon) /* up accidently freeing someone elses tcon struct */
2158 tconInfoFree(tcon);
2159 if (existingCifsSes == NULL) {
2160 if (pSesInfo) {
Steve French50c2f752007-07-13 00:33:32 +00002161 if ((pSesInfo->server) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 (pSesInfo->status == CifsGood)) {
2163 int temp_rc;
2164 temp_rc = CIFSSMBLogoff(xid, pSesInfo);
2165 /* if the socketUseCount is now zero */
Steve French4523cc32007-04-30 20:13:06 +00002166 if ((temp_rc == -ESHUTDOWN) &&
Steve French50c2f752007-07-13 00:33:32 +00002167 (pSesInfo->server) &&
Jeff5d9c7202007-06-25 22:16:35 +00002168 (pSesInfo->server->tsk)) {
Steve French28356a12007-05-23 14:45:36 +00002169 struct task_struct *tsk;
Jeff5d9c7202007-06-25 22:16:35 +00002170 force_sig(SIGKILL,
2171 pSesInfo->server->tsk);
Steve French28356a12007-05-23 14:45:36 +00002172 tsk = pSesInfo->server->tsk;
Steve Frenchf7f7c312007-05-24 02:29:51 +00002173 if (tsk)
Steve French28356a12007-05-23 14:45:36 +00002174 kthread_stop(tsk);
Steve Frenchf1914012005-08-18 09:37:34 -07002175 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002176 } else
2177 cFYI(1, ("No session or bad tcon"));
2178 sesInfoFree(pSesInfo);
2179 /* pSesInfo = NULL; */
2180 }
2181 }
2182 } else {
2183 atomic_inc(&tcon->useCount);
2184 cifs_sb->tcon = tcon;
2185 tcon->ses = pSesInfo;
2186
Steve French82940a42006-03-02 03:24:57 +00002187 /* do not care if following two calls succeed - informational */
Steve French737b7582005-04-28 22:41:06 -07002188 CIFSSMBQFSDeviceInfo(xid, tcon);
2189 CIFSSMBQFSAttributeInfo(xid, tcon);
Steve French50c2f752007-07-13 00:33:32 +00002190
Steve French8af18972007-02-14 04:42:51 +00002191 /* tell server which Unix caps we support */
2192 if (tcon->ses->capabilities & CAP_UNIX)
Steve Frenchc18c8422007-07-18 23:21:09 +00002193 /* reset of caps checks mount to see if unix extensions
2194 disabled for just this mount */
Steve French8af18972007-02-14 04:42:51 +00002195 reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
Steve Frenchc18c8422007-07-18 23:21:09 +00002196 else
2197 tcon->unix_ext = 0; /* server does not support them */
2198
2199 if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
Steve French75865f82007-06-24 18:30:48 +00002200 cifs_sb->rsize = 1024 * 127;
2201#ifdef CONFIG_CIFS_DEBUG2
Steve Frenchc18c8422007-07-18 23:21:09 +00002202 cFYI(1, ("no very large read support, rsize now 127K"));
Steve French75865f82007-06-24 18:30:48 +00002203#endif
Steve French75865f82007-06-24 18:30:48 +00002204 }
Steve French3e844692005-10-03 13:37:24 -07002205 if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
2206 cifs_sb->wsize = min(cifs_sb->wsize,
2207 (tcon->ses->server->maxBuf -
2208 MAX_CIFS_HDR_SIZE));
Steve French0ae0efa2005-10-10 10:57:19 -07002209 if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
Steve French50c2f752007-07-13 00:33:32 +00002210 cifs_sb->rsize = min(cifs_sb->rsize,
2211 (tcon->ses->server->maxBuf -
2212 MAX_CIFS_HDR_SIZE));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 }
2214
2215 /* volume_info.password is freed above when existing session found
2216 (in which case it is not needed anymore) but when new sesion is created
2217 the password ptr is put in the new session structure (in which case the
2218 password will be freed at unmount time) */
Jesper Juhlf99d49a2005-11-07 01:01:34 -08002219 kfree(volume_info.UNC);
Steve French2fe87f02006-09-21 07:02:52 +00002220 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 FreeXid(xid);
2222 return rc;
2223}
2224
2225static int
2226CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
Steve French7c7b25b2006-06-01 19:20:10 +00002227 char session_key[CIFS_SESS_KEY_SIZE],
Linus Torvalds1da177e2005-04-16 15:20:36 -07002228 const struct nls_table *nls_codepage)
2229{
2230 struct smb_hdr *smb_buffer;
2231 struct smb_hdr *smb_buffer_response;
2232 SESSION_SETUP_ANDX *pSMB;
2233 SESSION_SETUP_ANDX *pSMBr;
2234 char *bcc_ptr;
2235 char *user;
2236 char *domain;
2237 int rc = 0;
2238 int remaining_words = 0;
2239 int bytes_returned = 0;
2240 int len;
2241 __u32 capabilities;
2242 __u16 count;
2243
Steve Frencheeac8042006-01-13 21:34:58 -08002244 cFYI(1, ("In sesssetup"));
Steve French4523cc32007-04-30 20:13:06 +00002245 if (ses == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246 return -EINVAL;
2247 user = ses->userName;
2248 domain = ses->domainName;
2249 smb_buffer = cifs_buf_get();
2250 if (smb_buffer == NULL) {
2251 return -ENOMEM;
2252 }
2253 smb_buffer_response = smb_buffer;
2254 pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
2255
2256 /* send SMBsessionSetup here */
2257 header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
2258 NULL /* no tCon exists yet */ , 13 /* wct */ );
2259
Steve French1982c342005-08-17 12:38:22 -07002260 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 pSMB->req_no_secext.AndXCommand = 0xFF;
2262 pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
2263 pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
2264
Steve French50c2f752007-07-13 00:33:32 +00002265 if (ses->server->secMode &
2266 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002267 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
2268
2269 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
2270 CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
2271 if (ses->capabilities & CAP_UNICODE) {
2272 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
2273 capabilities |= CAP_UNICODE;
2274 }
2275 if (ses->capabilities & CAP_STATUS32) {
2276 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
2277 capabilities |= CAP_STATUS32;
2278 }
2279 if (ses->capabilities & CAP_DFS) {
2280 smb_buffer->Flags2 |= SMBFLG2_DFS;
2281 capabilities |= CAP_DFS;
2282 }
2283 pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
2284
Steve French50c2f752007-07-13 00:33:32 +00002285 pSMB->req_no_secext.CaseInsensitivePasswordLength =
Steve French7c7b25b2006-06-01 19:20:10 +00002286 cpu_to_le16(CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287
2288 pSMB->req_no_secext.CaseSensitivePasswordLength =
Steve French7c7b25b2006-06-01 19:20:10 +00002289 cpu_to_le16(CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002290 bcc_ptr = pByteArea(smb_buffer);
Steve French7c7b25b2006-06-01 19:20:10 +00002291 memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
2292 bcc_ptr += CIFS_SESS_KEY_SIZE;
2293 memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
2294 bcc_ptr += CIFS_SESS_KEY_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
2296 if (ses->capabilities & CAP_UNICODE) {
2297 if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */
2298 *bcc_ptr = 0;
2299 bcc_ptr++;
2300 }
Steve French4523cc32007-04-30 20:13:06 +00002301 if (user == NULL)
Steve French39798772006-05-31 22:40:51 +00002302 bytes_returned = 0; /* skip null user */
Steve French50c2f752007-07-13 00:33:32 +00002303 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304 bytes_returned =
Steve French50c2f752007-07-13 00:33:32 +00002305 cifs_strtoUCS((__le16 *) bcc_ptr, user, 100,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 nls_codepage);
2307 /* convert number of 16 bit words to bytes */
2308 bcc_ptr += 2 * bytes_returned;
2309 bcc_ptr += 2; /* trailing null */
2310 if (domain == NULL)
2311 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002312 cifs_strtoUCS((__le16 *) bcc_ptr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313 "CIFS_LINUX_DOM", 32, nls_codepage);
2314 else
2315 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002316 cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002317 nls_codepage);
2318 bcc_ptr += 2 * bytes_returned;
2319 bcc_ptr += 2;
2320 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002321 cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002322 32, nls_codepage);
2323 bcc_ptr += 2 * bytes_returned;
2324 bytes_returned =
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002325 cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326 32, nls_codepage);
2327 bcc_ptr += 2 * bytes_returned;
2328 bcc_ptr += 2;
2329 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002330 cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331 64, nls_codepage);
2332 bcc_ptr += 2 * bytes_returned;
2333 bcc_ptr += 2;
2334 } else {
Steve French50c2f752007-07-13 00:33:32 +00002335 if (user != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002336 strncpy(bcc_ptr, user, 200);
2337 bcc_ptr += strnlen(user, 200);
2338 }
2339 *bcc_ptr = 0;
2340 bcc_ptr++;
2341 if (domain == NULL) {
2342 strcpy(bcc_ptr, "CIFS_LINUX_DOM");
2343 bcc_ptr += strlen("CIFS_LINUX_DOM") + 1;
2344 } else {
2345 strncpy(bcc_ptr, domain, 64);
2346 bcc_ptr += strnlen(domain, 64);
2347 *bcc_ptr = 0;
2348 bcc_ptr++;
2349 }
2350 strcpy(bcc_ptr, "Linux version ");
2351 bcc_ptr += strlen("Linux version ");
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002352 strcpy(bcc_ptr, utsname()->release);
2353 bcc_ptr += strlen(utsname()->release) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
2355 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
2356 }
2357 count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
2358 smb_buffer->smb_buf_length += count;
2359 pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
2360
2361 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
2362 &bytes_returned, 1);
2363 if (rc) {
2364/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
2365 } else if ((smb_buffer_response->WordCount == 3)
2366 || (smb_buffer_response->WordCount == 4)) {
2367 __u16 action = le16_to_cpu(pSMBr->resp.Action);
2368 __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
2369 if (action & GUEST_LOGIN)
Steve French50c2f752007-07-13 00:33:32 +00002370 cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
2371 ses->Suid = smb_buffer_response->Uid; /* UID left in wire format
2372 (little endian) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002373 cFYI(1, ("UID = %d ", ses->Suid));
Steve French50c2f752007-07-13 00:33:32 +00002374 /* response can have either 3 or 4 word count - Samba sends 3 */
2375 bcc_ptr = pByteArea(smb_buffer_response);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 if ((pSMBr->resp.hdr.WordCount == 3)
2377 || ((pSMBr->resp.hdr.WordCount == 4)
2378 && (blob_len < pSMBr->resp.ByteCount))) {
2379 if (pSMBr->resp.hdr.WordCount == 4)
2380 bcc_ptr += blob_len;
2381
2382 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
2383 if ((long) (bcc_ptr) % 2) {
2384 remaining_words =
Steve French50c2f752007-07-13 00:33:32 +00002385 (BCC(smb_buffer_response) - 1) / 2;
2386 /* Unicode strings must be word
2387 aligned */
2388 bcc_ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 } else {
2390 remaining_words =
2391 BCC(smb_buffer_response) / 2;
2392 }
2393 len =
2394 UniStrnlen((wchar_t *) bcc_ptr,
2395 remaining_words - 1);
2396/* We look for obvious messed up bcc or strings in response so we do not go off
2397 the end since (at least) WIN2K and Windows XP have a major bug in not null
2398 terminating last Unicode string in response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002399 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00002400 kfree(ses->serverOS);
Steve French50c2f752007-07-13 00:33:32 +00002401 ses->serverOS = kzalloc(2 * (len + 1),
2402 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002403 if (ses->serverOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002404 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 cifs_strfromUCS_le(ses->serverOS,
Steve French50c2f752007-07-13 00:33:32 +00002406 (__le16 *)bcc_ptr,
2407 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408 bcc_ptr += 2 * (len + 1);
2409 remaining_words -= len + 1;
2410 ses->serverOS[2 * len] = 0;
2411 ses->serverOS[1 + (2 * len)] = 0;
2412 if (remaining_words > 0) {
2413 len = UniStrnlen((wchar_t *)bcc_ptr,
2414 remaining_words-1);
Steve Frenchcd49b492006-06-26 04:22:36 +00002415 kfree(ses->serverNOS);
Steve French50c2f752007-07-13 00:33:32 +00002416 ses->serverNOS = kzalloc(2 * (len + 1),
2417 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002418 if (ses->serverNOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002419 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420 cifs_strfromUCS_le(ses->serverNOS,
Steve French50c2f752007-07-13 00:33:32 +00002421 (__le16 *)bcc_ptr,
2422 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002423 bcc_ptr += 2 * (len + 1);
2424 ses->serverNOS[2 * len] = 0;
2425 ses->serverNOS[1 + (2 * len)] = 0;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002426 if (strncmp(ses->serverNOS,
Steve French50c2f752007-07-13 00:33:32 +00002427 "NT LAN Manager 4", 16) == 0) {
Steve French467a8f82007-06-27 22:41:32 +00002428 cFYI(1, ("NT4 server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429 ses->flags |= CIFS_SES_NT4;
2430 }
2431 remaining_words -= len + 1;
2432 if (remaining_words > 0) {
Steve French433dc242005-04-28 22:41:08 -07002433 len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
Steve French50c2f752007-07-13 00:33:32 +00002434 /* last string is not always null terminated
2435 (for e.g. for Windows XP & 2000) */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002436 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00002437 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438 ses->serverDomain =
Steve French50c2f752007-07-13 00:33:32 +00002439 kzalloc(2*(len+1),
2440 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002441 if (ses->serverDomain == NULL)
Steve French433dc242005-04-28 22:41:08 -07002442 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 cifs_strfromUCS_le(ses->serverDomain,
Steve French50c2f752007-07-13 00:33:32 +00002444 (__le16 *)bcc_ptr,
2445 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446 bcc_ptr += 2 * (len + 1);
2447 ses->serverDomain[2*len] = 0;
2448 ses->serverDomain[1+(2*len)] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002449 } else { /* else no more room so create
2450 dummy domain string */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002451 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00002452 kfree(ses->serverDomain);
Steve French50c2f752007-07-13 00:33:32 +00002453 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002454 kzalloc(2, GFP_KERNEL);
Steve Frencha424f8b2006-05-30 18:06:04 +00002455 }
Steve French50c2f752007-07-13 00:33:32 +00002456 } else { /* no room so create dummy domain
2457 and NOS string */
2458
Steve French433dc242005-04-28 22:41:08 -07002459 /* if these kcallocs fail not much we
2460 can do, but better to not fail the
2461 sesssetup itself */
Steve Frenchcd49b492006-06-26 04:22:36 +00002462 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002464 kzalloc(2, GFP_KERNEL);
Steve Frenchcd49b492006-06-26 04:22:36 +00002465 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002467 kzalloc(2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468 }
2469 } else { /* ASCII */
2470 len = strnlen(bcc_ptr, 1024);
2471 if (((long) bcc_ptr + len) - (long)
2472 pByteArea(smb_buffer_response)
2473 <= BCC(smb_buffer_response)) {
Steve Frenchcd49b492006-06-26 04:22:36 +00002474 kfree(ses->serverOS);
Steve French50c2f752007-07-13 00:33:32 +00002475 ses->serverOS = kzalloc(len + 1,
2476 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002477 if (ses->serverOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002478 goto sesssetup_nomem;
Steve French50c2f752007-07-13 00:33:32 +00002479 strncpy(ses->serverOS, bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480
2481 bcc_ptr += len;
Steve French50c2f752007-07-13 00:33:32 +00002482 /* null terminate the string */
2483 bcc_ptr[0] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484 bcc_ptr++;
2485
2486 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00002487 kfree(ses->serverNOS);
Steve French50c2f752007-07-13 00:33:32 +00002488 ses->serverNOS = kzalloc(len + 1,
2489 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002490 if (ses->serverNOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002491 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 strncpy(ses->serverNOS, bcc_ptr, len);
2493 bcc_ptr += len;
2494 bcc_ptr[0] = 0;
2495 bcc_ptr++;
2496
2497 len = strnlen(bcc_ptr, 1024);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002498 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00002499 kfree(ses->serverDomain);
Steve French50c2f752007-07-13 00:33:32 +00002500 ses->serverDomain = kzalloc(len + 1,
2501 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002502 if (ses->serverDomain == NULL)
Steve French433dc242005-04-28 22:41:08 -07002503 goto sesssetup_nomem;
Steve French50c2f752007-07-13 00:33:32 +00002504 strncpy(ses->serverDomain, bcc_ptr,
2505 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506 bcc_ptr += len;
2507 bcc_ptr[0] = 0;
2508 bcc_ptr++;
2509 } else
2510 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00002511 ("Variable field of length %d "
2512 "extends beyond end of smb ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002513 len));
2514 }
2515 } else {
2516 cERROR(1,
Steve French50c2f752007-07-13 00:33:32 +00002517 (" Security Blob Length extends beyond "
2518 "end of SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519 }
2520 } else {
2521 cERROR(1,
2522 (" Invalid Word count %d: ",
2523 smb_buffer_response->WordCount));
2524 rc = -EIO;
2525 }
Steve French433dc242005-04-28 22:41:08 -07002526sesssetup_nomem: /* do not return an error on nomem for the info strings,
2527 since that could make reconnection harder, and
2528 reconnection might be needed to free memory */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 if (smb_buffer)
2530 cifs_buf_release(smb_buffer);
2531
2532 return rc;
2533}
2534
2535static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
Steve French50c2f752007-07-13 00:33:32 +00002537 struct cifsSesInfo *ses, int *pNTLMv2_flag,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538 const struct nls_table *nls_codepage)
2539{
2540 struct smb_hdr *smb_buffer;
2541 struct smb_hdr *smb_buffer_response;
2542 SESSION_SETUP_ANDX *pSMB;
2543 SESSION_SETUP_ANDX *pSMBr;
2544 char *bcc_ptr;
2545 char *domain;
2546 int rc = 0;
2547 int remaining_words = 0;
2548 int bytes_returned = 0;
2549 int len;
2550 int SecurityBlobLength = sizeof (NEGOTIATE_MESSAGE);
2551 PNEGOTIATE_MESSAGE SecurityBlob;
2552 PCHALLENGE_MESSAGE SecurityBlob2;
2553 __u32 negotiate_flags, capabilities;
2554 __u16 count;
2555
Steve French12b3b8f2006-02-09 21:12:47 +00002556 cFYI(1, ("In NTLMSSP sesssetup (negotiate)"));
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002557 if (ses == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 return -EINVAL;
2559 domain = ses->domainName;
2560 *pNTLMv2_flag = FALSE;
2561 smb_buffer = cifs_buf_get();
2562 if (smb_buffer == NULL) {
2563 return -ENOMEM;
2564 }
2565 smb_buffer_response = smb_buffer;
2566 pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
2567 pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
2568
2569 /* send SMBsessionSetup here */
2570 header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
2571 NULL /* no tCon exists yet */ , 12 /* wct */ );
Steve French1982c342005-08-17 12:38:22 -07002572
2573 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
2575 pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
2576
2577 pSMB->req.AndXCommand = 0xFF;
2578 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
2579 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
2580
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002581 if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
2583
2584 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
2585 CAP_EXTENDED_SECURITY;
2586 if (ses->capabilities & CAP_UNICODE) {
2587 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
2588 capabilities |= CAP_UNICODE;
2589 }
2590 if (ses->capabilities & CAP_STATUS32) {
2591 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
2592 capabilities |= CAP_STATUS32;
2593 }
2594 if (ses->capabilities & CAP_DFS) {
2595 smb_buffer->Flags2 |= SMBFLG2_DFS;
2596 capabilities |= CAP_DFS;
2597 }
2598 pSMB->req.Capabilities = cpu_to_le32(capabilities);
2599
2600 bcc_ptr = (char *) &pSMB->req.SecurityBlob;
2601 SecurityBlob = (PNEGOTIATE_MESSAGE) bcc_ptr;
2602 strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
2603 SecurityBlob->MessageType = NtLmNegotiate;
2604 negotiate_flags =
2605 NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM |
Steve French12b3b8f2006-02-09 21:12:47 +00002606 NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
2607 NTLMSSP_NEGOTIATE_56 |
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002609 if (sign_CIFS_PDUs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002611/* if (ntlmv2_support)
Steve French39798772006-05-31 22:40:51 +00002612 negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 /* setup pointers to domain name and workstation name */
2614 bcc_ptr += SecurityBlobLength;
2615
2616 SecurityBlob->WorkstationName.Buffer = 0;
2617 SecurityBlob->WorkstationName.Length = 0;
2618 SecurityBlob->WorkstationName.MaximumLength = 0;
2619
Steve French12b3b8f2006-02-09 21:12:47 +00002620 /* Domain not sent on first Sesssetup in NTLMSSP, instead it is sent
2621 along with username on auth request (ie the response to challenge) */
2622 SecurityBlob->DomainName.Buffer = 0;
2623 SecurityBlob->DomainName.Length = 0;
2624 SecurityBlob->DomainName.MaximumLength = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 if (ses->capabilities & CAP_UNICODE) {
2626 if ((long) bcc_ptr % 2) {
2627 *bcc_ptr = 0;
2628 bcc_ptr++;
2629 }
2630
2631 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002632 cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 32, nls_codepage);
2634 bcc_ptr += 2 * bytes_returned;
2635 bytes_returned =
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002636 cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 nls_codepage);
2638 bcc_ptr += 2 * bytes_returned;
2639 bcc_ptr += 2; /* null terminate Linux version */
2640 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002641 cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642 64, nls_codepage);
2643 bcc_ptr += 2 * bytes_returned;
2644 *(bcc_ptr + 1) = 0;
2645 *(bcc_ptr + 2) = 0;
2646 bcc_ptr += 2; /* null terminate network opsys string */
2647 *(bcc_ptr + 1) = 0;
2648 *(bcc_ptr + 2) = 0;
2649 bcc_ptr += 2; /* null domain */
2650 } else { /* ASCII */
2651 strcpy(bcc_ptr, "Linux version ");
2652 bcc_ptr += strlen("Linux version ");
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002653 strcpy(bcc_ptr, utsname()->release);
2654 bcc_ptr += strlen(utsname()->release) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002655 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
2656 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
2657 bcc_ptr++; /* empty domain field */
2658 *bcc_ptr = 0;
2659 }
2660 SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
2661 pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
2662 count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
2663 smb_buffer->smb_buf_length += count;
2664 pSMB->req.ByteCount = cpu_to_le16(count);
2665
2666 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
2667 &bytes_returned, 1);
2668
2669 if (smb_buffer_response->Status.CifsError ==
2670 cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
2671 rc = 0;
2672
2673 if (rc) {
2674/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
2675 } else if ((smb_buffer_response->WordCount == 3)
2676 || (smb_buffer_response->WordCount == 4)) {
2677 __u16 action = le16_to_cpu(pSMBr->resp.Action);
2678 __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
2679
2680 if (action & GUEST_LOGIN)
Steve French50c2f752007-07-13 00:33:32 +00002681 cFYI(1, (" Guest login"));
2682 /* Do we want to set anything in SesInfo struct when guest login? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683
Steve French50c2f752007-07-13 00:33:32 +00002684 bcc_ptr = pByteArea(smb_buffer_response);
2685 /* response can have either 3 or 4 word count - Samba sends 3 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686
2687 SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr;
2688 if (SecurityBlob2->MessageType != NtLmChallenge) {
2689 cFYI(1,
2690 ("Unexpected NTLMSSP message type received %d",
2691 SecurityBlob2->MessageType));
2692 } else if (ses) {
Steve French50c2f752007-07-13 00:33:32 +00002693 ses->Suid = smb_buffer_response->Uid; /* UID left in le format */
Steve French12b3b8f2006-02-09 21:12:47 +00002694 cFYI(1, ("UID = %d", ses->Suid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 if ((pSMBr->resp.hdr.WordCount == 3)
2696 || ((pSMBr->resp.hdr.WordCount == 4)
2697 && (blob_len <
2698 pSMBr->resp.ByteCount))) {
2699
2700 if (pSMBr->resp.hdr.WordCount == 4) {
2701 bcc_ptr += blob_len;
Steve French12b3b8f2006-02-09 21:12:47 +00002702 cFYI(1, ("Security Blob Length %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 blob_len));
2704 }
2705
Steve French12b3b8f2006-02-09 21:12:47 +00002706 cFYI(1, ("NTLMSSP Challenge rcvd"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707
2708 memcpy(ses->server->cryptKey,
2709 SecurityBlob2->Challenge,
2710 CIFS_CRYPTO_KEY_SIZE);
Steve French50c2f752007-07-13 00:33:32 +00002711 if (SecurityBlob2->NegotiateFlags &
Steve French12b3b8f2006-02-09 21:12:47 +00002712 cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 *pNTLMv2_flag = TRUE;
2714
Steve French50c2f752007-07-13 00:33:32 +00002715 if ((SecurityBlob2->NegotiateFlags &
2716 cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717 || (sign_CIFS_PDUs > 1))
Steve French50c2f752007-07-13 00:33:32 +00002718 ses->server->secMode |=
2719 SECMODE_SIGN_REQUIRED;
2720 if ((SecurityBlob2->NegotiateFlags &
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs))
Steve French50c2f752007-07-13 00:33:32 +00002722 ses->server->secMode |=
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 SECMODE_SIGN_ENABLED;
2724
2725 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
2726 if ((long) (bcc_ptr) % 2) {
2727 remaining_words =
2728 (BCC(smb_buffer_response)
2729 - 1) / 2;
Steve French50c2f752007-07-13 00:33:32 +00002730 /* Must word align unicode strings */
2731 bcc_ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 } else {
2733 remaining_words =
2734 BCC
2735 (smb_buffer_response) / 2;
2736 }
2737 len =
2738 UniStrnlen((wchar_t *) bcc_ptr,
2739 remaining_words - 1);
2740/* We look for obvious messed up bcc or strings in response so we do not go off
2741 the end since (at least) WIN2K and Windows XP have a major bug in not null
2742 terminating last Unicode string in response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002743 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00002744 kfree(ses->serverOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 ses->serverOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002746 kzalloc(2 * (len + 1), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002747 cifs_strfromUCS_le(ses->serverOS,
Steve Frenche89dc922005-11-11 15:18:19 -08002748 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 bcc_ptr, len,
2750 nls_codepage);
2751 bcc_ptr += 2 * (len + 1);
2752 remaining_words -= len + 1;
2753 ses->serverOS[2 * len] = 0;
2754 ses->serverOS[1 + (2 * len)] = 0;
2755 if (remaining_words > 0) {
2756 len = UniStrnlen((wchar_t *)
2757 bcc_ptr,
2758 remaining_words
2759 - 1);
Steve Frenchcd49b492006-06-26 04:22:36 +00002760 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002762 kzalloc(2 * (len + 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 GFP_KERNEL);
2764 cifs_strfromUCS_le(ses->
2765 serverNOS,
Steve Frenche89dc922005-11-11 15:18:19 -08002766 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 bcc_ptr,
2768 len,
2769 nls_codepage);
2770 bcc_ptr += 2 * (len + 1);
2771 ses->serverNOS[2 * len] = 0;
2772 ses->serverNOS[1 +
2773 (2 * len)] = 0;
2774 remaining_words -= len + 1;
2775 if (remaining_words > 0) {
Steve French50c2f752007-07-13 00:33:32 +00002776 len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
2777 /* last string not always null terminated
2778 (for e.g. for Windows XP & 2000) */
Steve Frenchcd49b492006-06-26 04:22:36 +00002779 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002781 kzalloc(2 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782 (len +
2783 1),
2784 GFP_KERNEL);
2785 cifs_strfromUCS_le
Steve Frenche89dc922005-11-11 15:18:19 -08002786 (ses->serverDomain,
2787 (__le16 *)bcc_ptr,
2788 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002789 bcc_ptr +=
2790 2 * (len + 1);
Steve Frenche89dc922005-11-11 15:18:19 -08002791 ses->serverDomain[2*len]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 = 0;
Steve Frenche89dc922005-11-11 15:18:19 -08002793 ses->serverDomain
2794 [1 + (2 * len)]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 = 0;
2796 } /* else no more room so create dummy domain string */
Steve Frencha424f8b2006-05-30 18:06:04 +00002797 else {
Steve Frenchcd49b492006-06-26 04:22:36 +00002798 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002800 kzalloc(2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 GFP_KERNEL);
Steve Frencha424f8b2006-05-30 18:06:04 +00002802 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 } else { /* no room so create dummy domain and NOS string */
Steve Frenchcd49b492006-06-26 04:22:36 +00002804 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002806 kzalloc(2, GFP_KERNEL);
Steve Frenchcd49b492006-06-26 04:22:36 +00002807 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002809 kzalloc(2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002810 }
2811 } else { /* ASCII */
2812 len = strnlen(bcc_ptr, 1024);
2813 if (((long) bcc_ptr + len) - (long)
2814 pByteArea(smb_buffer_response)
2815 <= BCC(smb_buffer_response)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002816 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00002817 kfree(ses->serverOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818 ses->serverOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002819 kzalloc(len + 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002820 GFP_KERNEL);
2821 strncpy(ses->serverOS,
2822 bcc_ptr, len);
2823
2824 bcc_ptr += len;
2825 bcc_ptr[0] = 0; /* null terminate string */
2826 bcc_ptr++;
2827
2828 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00002829 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002831 kzalloc(len + 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832 GFP_KERNEL);
2833 strncpy(ses->serverNOS, bcc_ptr, len);
2834 bcc_ptr += len;
2835 bcc_ptr[0] = 0;
2836 bcc_ptr++;
2837
2838 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00002839 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002840 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002841 kzalloc(len + 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 GFP_KERNEL);
Steve French50c2f752007-07-13 00:33:32 +00002843 strncpy(ses->serverDomain,
2844 bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002845 bcc_ptr += len;
2846 bcc_ptr[0] = 0;
2847 bcc_ptr++;
2848 } else
2849 cFYI(1,
Steve French63135e02007-07-17 17:34:02 +00002850 ("field of length %d "
2851 "extends beyond end of smb",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852 len));
2853 }
2854 } else {
Steve French50c2f752007-07-13 00:33:32 +00002855 cERROR(1, ("Security Blob Length extends beyond"
2856 " end of SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857 }
2858 } else {
2859 cERROR(1, ("No session structure passed in."));
2860 }
2861 } else {
2862 cERROR(1,
Steve French5815449d2006-02-14 01:36:20 +00002863 (" Invalid Word count %d:",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 smb_buffer_response->WordCount));
2865 rc = -EIO;
2866 }
2867
2868 if (smb_buffer)
2869 cifs_buf_release(smb_buffer);
2870
2871 return rc;
2872}
2873static int
2874CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
2875 char *ntlm_session_key, int ntlmv2_flag,
2876 const struct nls_table *nls_codepage)
2877{
2878 struct smb_hdr *smb_buffer;
2879 struct smb_hdr *smb_buffer_response;
2880 SESSION_SETUP_ANDX *pSMB;
2881 SESSION_SETUP_ANDX *pSMBr;
2882 char *bcc_ptr;
2883 char *user;
2884 char *domain;
2885 int rc = 0;
2886 int remaining_words = 0;
2887 int bytes_returned = 0;
2888 int len;
2889 int SecurityBlobLength = sizeof (AUTHENTICATE_MESSAGE);
2890 PAUTHENTICATE_MESSAGE SecurityBlob;
2891 __u32 negotiate_flags, capabilities;
2892 __u16 count;
2893
2894 cFYI(1, ("In NTLMSSPSessSetup (Authenticate)"));
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002895 if (ses == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002896 return -EINVAL;
2897 user = ses->userName;
2898 domain = ses->domainName;
2899 smb_buffer = cifs_buf_get();
2900 if (smb_buffer == NULL) {
2901 return -ENOMEM;
2902 }
2903 smb_buffer_response = smb_buffer;
2904 pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
2905 pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
2906
2907 /* send SMBsessionSetup here */
2908 header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
2909 NULL /* no tCon exists yet */ , 12 /* wct */ );
Steve French1982c342005-08-17 12:38:22 -07002910
2911 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912 pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
2913 pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
2914 pSMB->req.AndXCommand = 0xFF;
2915 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
2916 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
2917
2918 pSMB->req.hdr.Uid = ses->Suid;
2919
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002920 if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002921 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
2922
2923 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
2924 CAP_EXTENDED_SECURITY;
2925 if (ses->capabilities & CAP_UNICODE) {
2926 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
2927 capabilities |= CAP_UNICODE;
2928 }
2929 if (ses->capabilities & CAP_STATUS32) {
2930 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
2931 capabilities |= CAP_STATUS32;
2932 }
2933 if (ses->capabilities & CAP_DFS) {
2934 smb_buffer->Flags2 |= SMBFLG2_DFS;
2935 capabilities |= CAP_DFS;
2936 }
2937 pSMB->req.Capabilities = cpu_to_le32(capabilities);
2938
2939 bcc_ptr = (char *) &pSMB->req.SecurityBlob;
2940 SecurityBlob = (PAUTHENTICATE_MESSAGE) bcc_ptr;
2941 strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
2942 SecurityBlob->MessageType = NtLmAuthenticate;
2943 bcc_ptr += SecurityBlobLength;
Steve French50c2f752007-07-13 00:33:32 +00002944 negotiate_flags =
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
2946 NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
2947 0x80000000 | NTLMSSP_NEGOTIATE_128;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002948 if (sign_CIFS_PDUs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002950 if (ntlmv2_flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951 negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
2952
2953/* setup pointers to domain name and workstation name */
2954
2955 SecurityBlob->WorkstationName.Buffer = 0;
2956 SecurityBlob->WorkstationName.Length = 0;
2957 SecurityBlob->WorkstationName.MaximumLength = 0;
2958 SecurityBlob->SessionKey.Length = 0;
2959 SecurityBlob->SessionKey.MaximumLength = 0;
2960 SecurityBlob->SessionKey.Buffer = 0;
2961
2962 SecurityBlob->LmChallengeResponse.Length = 0;
2963 SecurityBlob->LmChallengeResponse.MaximumLength = 0;
2964 SecurityBlob->LmChallengeResponse.Buffer = 0;
2965
2966 SecurityBlob->NtChallengeResponse.Length =
Steve French7c7b25b2006-06-01 19:20:10 +00002967 cpu_to_le16(CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968 SecurityBlob->NtChallengeResponse.MaximumLength =
Steve French7c7b25b2006-06-01 19:20:10 +00002969 cpu_to_le16(CIFS_SESS_KEY_SIZE);
2970 memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 SecurityBlob->NtChallengeResponse.Buffer =
2972 cpu_to_le32(SecurityBlobLength);
Steve French7c7b25b2006-06-01 19:20:10 +00002973 SecurityBlobLength += CIFS_SESS_KEY_SIZE;
2974 bcc_ptr += CIFS_SESS_KEY_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975
2976 if (ses->capabilities & CAP_UNICODE) {
2977 if (domain == NULL) {
2978 SecurityBlob->DomainName.Buffer = 0;
2979 SecurityBlob->DomainName.Length = 0;
2980 SecurityBlob->DomainName.MaximumLength = 0;
2981 } else {
2982 __u16 len =
Steve Frenche89dc922005-11-11 15:18:19 -08002983 cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002984 nls_codepage);
2985 len *= 2;
2986 SecurityBlob->DomainName.MaximumLength =
2987 cpu_to_le16(len);
2988 SecurityBlob->DomainName.Buffer =
2989 cpu_to_le32(SecurityBlobLength);
2990 bcc_ptr += len;
2991 SecurityBlobLength += len;
2992 SecurityBlob->DomainName.Length =
2993 cpu_to_le16(len);
2994 }
2995 if (user == NULL) {
2996 SecurityBlob->UserName.Buffer = 0;
2997 SecurityBlob->UserName.Length = 0;
2998 SecurityBlob->UserName.MaximumLength = 0;
2999 } else {
3000 __u16 len =
Steve Frenche89dc922005-11-11 15:18:19 -08003001 cifs_strtoUCS((__le16 *) bcc_ptr, user, 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003002 nls_codepage);
3003 len *= 2;
3004 SecurityBlob->UserName.MaximumLength =
3005 cpu_to_le16(len);
3006 SecurityBlob->UserName.Buffer =
3007 cpu_to_le32(SecurityBlobLength);
3008 bcc_ptr += len;
3009 SecurityBlobLength += len;
3010 SecurityBlob->UserName.Length =
3011 cpu_to_le16(len);
3012 }
3013
Steve French63135e02007-07-17 17:34:02 +00003014 /* SecurityBlob->WorkstationName.Length =
3015 cifs_strtoUCS((__le16 *) bcc_ptr, "AMACHINE",64, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003016 SecurityBlob->WorkstationName.Length *= 2;
Steve French63135e02007-07-17 17:34:02 +00003017 SecurityBlob->WorkstationName.MaximumLength =
3018 cpu_to_le16(SecurityBlob->WorkstationName.Length);
3019 SecurityBlob->WorkstationName.Buffer =
3020 cpu_to_le32(SecurityBlobLength);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021 bcc_ptr += SecurityBlob->WorkstationName.Length;
3022 SecurityBlobLength += SecurityBlob->WorkstationName.Length;
Steve French63135e02007-07-17 17:34:02 +00003023 SecurityBlob->WorkstationName.Length =
3024 cpu_to_le16(SecurityBlob->WorkstationName.Length); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003025
3026 if ((long) bcc_ptr % 2) {
3027 *bcc_ptr = 0;
3028 bcc_ptr++;
3029 }
3030 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08003031 cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032 32, nls_codepage);
3033 bcc_ptr += 2 * bytes_returned;
3034 bytes_returned =
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07003035 cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036 nls_codepage);
3037 bcc_ptr += 2 * bytes_returned;
3038 bcc_ptr += 2; /* null term version string */
3039 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08003040 cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003041 64, nls_codepage);
3042 bcc_ptr += 2 * bytes_returned;
3043 *(bcc_ptr + 1) = 0;
3044 *(bcc_ptr + 2) = 0;
3045 bcc_ptr += 2; /* null terminate network opsys string */
3046 *(bcc_ptr + 1) = 0;
3047 *(bcc_ptr + 2) = 0;
3048 bcc_ptr += 2; /* null domain */
3049 } else { /* ASCII */
3050 if (domain == NULL) {
3051 SecurityBlob->DomainName.Buffer = 0;
3052 SecurityBlob->DomainName.Length = 0;
3053 SecurityBlob->DomainName.MaximumLength = 0;
3054 } else {
3055 __u16 len;
3056 negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
3057 strncpy(bcc_ptr, domain, 63);
3058 len = strnlen(domain, 64);
3059 SecurityBlob->DomainName.MaximumLength =
3060 cpu_to_le16(len);
3061 SecurityBlob->DomainName.Buffer =
3062 cpu_to_le32(SecurityBlobLength);
3063 bcc_ptr += len;
3064 SecurityBlobLength += len;
3065 SecurityBlob->DomainName.Length = cpu_to_le16(len);
3066 }
3067 if (user == NULL) {
3068 SecurityBlob->UserName.Buffer = 0;
3069 SecurityBlob->UserName.Length = 0;
3070 SecurityBlob->UserName.MaximumLength = 0;
3071 } else {
3072 __u16 len;
3073 strncpy(bcc_ptr, user, 63);
3074 len = strnlen(user, 64);
3075 SecurityBlob->UserName.MaximumLength =
3076 cpu_to_le16(len);
3077 SecurityBlob->UserName.Buffer =
3078 cpu_to_le32(SecurityBlobLength);
3079 bcc_ptr += len;
3080 SecurityBlobLength += len;
3081 SecurityBlob->UserName.Length = cpu_to_le16(len);
3082 }
3083 /* BB fill in our workstation name if known BB */
3084
3085 strcpy(bcc_ptr, "Linux version ");
3086 bcc_ptr += strlen("Linux version ");
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07003087 strcpy(bcc_ptr, utsname()->release);
3088 bcc_ptr += strlen(utsname()->release) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003089 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
3090 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
3091 bcc_ptr++; /* null domain */
3092 *bcc_ptr = 0;
3093 }
3094 SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
3095 pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
3096 count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
3097 smb_buffer->smb_buf_length += count;
3098 pSMB->req.ByteCount = cpu_to_le16(count);
3099
3100 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
3101 &bytes_returned, 1);
3102 if (rc) {
3103/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
3104 } else if ((smb_buffer_response->WordCount == 3)
3105 || (smb_buffer_response->WordCount == 4)) {
3106 __u16 action = le16_to_cpu(pSMBr->resp.Action);
3107 __u16 blob_len =
3108 le16_to_cpu(pSMBr->resp.SecurityBlobLength);
3109 if (action & GUEST_LOGIN)
Steve French50c2f752007-07-13 00:33:32 +00003110 cFYI(1, (" Guest login")); /* BB Should we set anything
3111 in SesInfo struct ? */
3112/* if (SecurityBlob2->MessageType != NtLm??) {
3113 cFYI("Unexpected message type on auth response is %d"));
3114 } */
3115
Linus Torvalds1da177e2005-04-16 15:20:36 -07003116 if (ses) {
3117 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00003118 ("Check challenge UID %d vs auth response UID %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003119 ses->Suid, smb_buffer_response->Uid));
Steve French50c2f752007-07-13 00:33:32 +00003120 /* UID left in wire format */
3121 ses->Suid = smb_buffer_response->Uid;
3122 bcc_ptr = pByteArea(smb_buffer_response);
3123 /* response can have either 3 or 4 word count - Samba sends 3 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 if ((pSMBr->resp.hdr.WordCount == 3)
3125 || ((pSMBr->resp.hdr.WordCount == 4)
3126 && (blob_len <
3127 pSMBr->resp.ByteCount))) {
3128 if (pSMBr->resp.hdr.WordCount == 4) {
3129 bcc_ptr +=
3130 blob_len;
3131 cFYI(1,
3132 ("Security Blob Length %d ",
3133 blob_len));
3134 }
3135
3136 cFYI(1,
3137 ("NTLMSSP response to Authenticate "));
3138
3139 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
3140 if ((long) (bcc_ptr) % 2) {
3141 remaining_words =
3142 (BCC(smb_buffer_response)
3143 - 1) / 2;
3144 bcc_ptr++; /* Unicode strings must be word aligned */
3145 } else {
3146 remaining_words = BCC(smb_buffer_response) / 2;
3147 }
3148 len =
3149 UniStrnlen((wchar_t *) bcc_ptr,remaining_words - 1);
3150/* We look for obvious messed up bcc or strings in response so we do not go off
3151 the end since (at least) WIN2K and Windows XP have a major bug in not null
3152 terminating last Unicode string in response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003153 if (ses->serverOS)
Steve French08775832006-05-30 18:08:26 +00003154 kfree(ses->serverOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003155 ses->serverOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07003156 kzalloc(2 * (len + 1), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003157 cifs_strfromUCS_le(ses->serverOS,
Steve Frenche89dc922005-11-11 15:18:19 -08003158 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003159 bcc_ptr, len,
3160 nls_codepage);
3161 bcc_ptr += 2 * (len + 1);
3162 remaining_words -= len + 1;
3163 ses->serverOS[2 * len] = 0;
3164 ses->serverOS[1 + (2 * len)] = 0;
3165 if (remaining_words > 0) {
3166 len = UniStrnlen((wchar_t *)
3167 bcc_ptr,
3168 remaining_words
3169 - 1);
Steve Frenchcd49b492006-06-26 04:22:36 +00003170 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003171 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07003172 kzalloc(2 * (len + 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173 GFP_KERNEL);
3174 cifs_strfromUCS_le(ses->
3175 serverNOS,
Steve Frenche89dc922005-11-11 15:18:19 -08003176 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177 bcc_ptr,
3178 len,
3179 nls_codepage);
3180 bcc_ptr += 2 * (len + 1);
3181 ses->serverNOS[2 * len] = 0;
3182 ses->serverNOS[1+(2*len)] = 0;
3183 remaining_words -= len + 1;
3184 if (remaining_words > 0) {
Steve French50c2f752007-07-13 00:33:32 +00003185 len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186 /* last string not always null terminated (e.g. for Windows XP & 2000) */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003187 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003188 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003189 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07003190 kzalloc(2 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07003191 (len +
3192 1),
3193 GFP_KERNEL);
3194 cifs_strfromUCS_le
3195 (ses->
3196 serverDomain,
Steve Frenche89dc922005-11-11 15:18:19 -08003197 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198 bcc_ptr, len,
3199 nls_codepage);
3200 bcc_ptr +=
3201 2 * (len + 1);
3202 ses->
3203 serverDomain[2
3204 * len]
3205 = 0;
3206 ses->
3207 serverDomain[1
3208 +
3209 (2
3210 *
3211 len)]
3212 = 0;
3213 } /* else no more room so create dummy domain string */
Steve Frencha424f8b2006-05-30 18:06:04 +00003214 else {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003215 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003216 kfree(ses->serverDomain);
Pekka Enberge915fc42005-09-06 15:18:35 -07003217 ses->serverDomain = kzalloc(2,GFP_KERNEL);
Steve Frencha424f8b2006-05-30 18:06:04 +00003218 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 } else { /* no room so create dummy domain and NOS string */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003220 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003221 kfree(ses->serverDomain);
Pekka Enberge915fc42005-09-06 15:18:35 -07003222 ses->serverDomain = kzalloc(2, GFP_KERNEL);
Steve Frenchcd49b492006-06-26 04:22:36 +00003223 kfree(ses->serverNOS);
Pekka Enberge915fc42005-09-06 15:18:35 -07003224 ses->serverNOS = kzalloc(2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003225 }
3226 } else { /* ASCII */
3227 len = strnlen(bcc_ptr, 1024);
Steve French50c2f752007-07-13 00:33:32 +00003228 if (((long) bcc_ptr + len) -
3229 (long) pByteArea(smb_buffer_response)
Steve French63135e02007-07-17 17:34:02 +00003230 <= BCC(smb_buffer_response)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003231 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00003232 kfree(ses->serverOS);
Pekka Enberge915fc42005-09-06 15:18:35 -07003233 ses->serverOS = kzalloc(len + 1,GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003234 strncpy(ses->serverOS,bcc_ptr, len);
3235
3236 bcc_ptr += len;
3237 bcc_ptr[0] = 0; /* null terminate the string */
3238 bcc_ptr++;
3239
3240 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00003241 kfree(ses->serverNOS);
Steve French50c2f752007-07-13 00:33:32 +00003242 ses->serverNOS = kzalloc(len+1,
3243 GFP_KERNEL);
Steve French63135e02007-07-17 17:34:02 +00003244 strncpy(ses->serverNOS,
3245 bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246 bcc_ptr += len;
3247 bcc_ptr[0] = 0;
3248 bcc_ptr++;
3249
3250 len = strnlen(bcc_ptr, 1024);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003251 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003252 kfree(ses->serverDomain);
Steve French63135e02007-07-17 17:34:02 +00003253 ses->serverDomain =
3254 kzalloc(len+1,
3255 GFP_KERNEL);
3256 strncpy(ses->serverDomain,
3257 bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003258 bcc_ptr += len;
3259 bcc_ptr[0] = 0;
3260 bcc_ptr++;
3261 } else
3262 cFYI(1,
Steve French63135e02007-07-17 17:34:02 +00003263 ("field of length %d "
3264 "extends beyond end of smb ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003265 len));
3266 }
3267 } else {
3268 cERROR(1,
Steve French63135e02007-07-17 17:34:02 +00003269 (" Security Blob extends beyond end "
3270 "of SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003271 }
3272 } else {
3273 cERROR(1, ("No session structure passed in."));
3274 }
3275 } else {
3276 cERROR(1,
3277 (" Invalid Word count %d: ",
3278 smb_buffer_response->WordCount));
3279 rc = -EIO;
3280 }
3281
3282 if (smb_buffer)
3283 cifs_buf_release(smb_buffer);
3284
3285 return rc;
3286}
3287
3288int
3289CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
3290 const char *tree, struct cifsTconInfo *tcon,
3291 const struct nls_table *nls_codepage)
3292{
3293 struct smb_hdr *smb_buffer;
3294 struct smb_hdr *smb_buffer_response;
3295 TCONX_REQ *pSMB;
3296 TCONX_RSP *pSMBr;
3297 unsigned char *bcc_ptr;
3298 int rc = 0;
3299 int length;
3300 __u16 count;
3301
3302 if (ses == NULL)
3303 return -EIO;
3304
3305 smb_buffer = cifs_buf_get();
3306 if (smb_buffer == NULL) {
3307 return -ENOMEM;
3308 }
3309 smb_buffer_response = smb_buffer;
3310
3311 header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
3312 NULL /*no tid */ , 4 /*wct */ );
Steve French1982c342005-08-17 12:38:22 -07003313
3314 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003315 smb_buffer->Uid = ses->Suid;
3316 pSMB = (TCONX_REQ *) smb_buffer;
3317 pSMBr = (TCONX_RSP *) smb_buffer_response;
3318
3319 pSMB->AndXCommand = 0xFF;
3320 pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003321 bcc_ptr = &pSMB->Password[0];
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003322 if ((ses->server->secMode) & SECMODE_USER) {
Steve Frencheeac8042006-01-13 21:34:58 -08003323 pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
Steve French7c7b25b2006-06-01 19:20:10 +00003324 *bcc_ptr = 0; /* password is null byte */
Steve Frencheeac8042006-01-13 21:34:58 -08003325 bcc_ptr++; /* skip password */
Steve French7c7b25b2006-06-01 19:20:10 +00003326 /* already aligned so no need to do it below */
Steve Frencheeac8042006-01-13 21:34:58 -08003327 } else {
Steve French7c7b25b2006-06-01 19:20:10 +00003328 pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
Steve Frencheeac8042006-01-13 21:34:58 -08003329 /* BB FIXME add code to fail this if NTLMv2 or Kerberos
3330 specified as required (when that support is added to
3331 the vfs in the future) as only NTLM or the much
Steve French7c7b25b2006-06-01 19:20:10 +00003332 weaker LANMAN (which we do not send by default) is accepted
Steve Frencheeac8042006-01-13 21:34:58 -08003333 by Samba (not sure whether other servers allow
3334 NTLMv2 password here) */
Steve French7c7b25b2006-06-01 19:20:10 +00003335#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French50c2f752007-07-13 00:33:32 +00003336 if ((extended_security & CIFSSEC_MAY_LANMAN) &&
Steve French7c7b25b2006-06-01 19:20:10 +00003337 (ses->server->secType == LANMAN))
3338 calc_lanman_hash(ses, bcc_ptr);
3339 else
3340#endif /* CIFS_WEAK_PW_HASH */
Steve Frencheeac8042006-01-13 21:34:58 -08003341 SMBNTencrypt(ses->password,
3342 ses->server->cryptKey,
3343 bcc_ptr);
3344
Steve French7c7b25b2006-06-01 19:20:10 +00003345 bcc_ptr += CIFS_SESS_KEY_SIZE;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003346 if (ses->capabilities & CAP_UNICODE) {
Steve French7c7b25b2006-06-01 19:20:10 +00003347 /* must align unicode strings */
3348 *bcc_ptr = 0; /* null byte password */
3349 bcc_ptr++;
3350 }
Steve Frencheeac8042006-01-13 21:34:58 -08003351 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003352
Steve French50c2f752007-07-13 00:33:32 +00003353 if (ses->server->secMode &
Steve Frencha878fb22006-05-30 18:04:19 +00003354 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003355 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
3356
3357 if (ses->capabilities & CAP_STATUS32) {
3358 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
3359 }
3360 if (ses->capabilities & CAP_DFS) {
3361 smb_buffer->Flags2 |= SMBFLG2_DFS;
3362 }
3363 if (ses->capabilities & CAP_UNICODE) {
3364 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
3365 length =
Steve French50c2f752007-07-13 00:33:32 +00003366 cifs_strtoUCS((__le16 *) bcc_ptr, tree,
3367 6 /* max utf8 char length in bytes */ *
Steve Frencha878fb22006-05-30 18:04:19 +00003368 (/* server len*/ + 256 /* share len */), nls_codepage);
3369 bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003370 bcc_ptr += 2; /* skip trailing null */
3371 } else { /* ASCII */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003372 strcpy(bcc_ptr, tree);
3373 bcc_ptr += strlen(tree) + 1;
3374 }
3375 strcpy(bcc_ptr, "?????");
3376 bcc_ptr += strlen("?????");
3377 bcc_ptr += 1;
3378 count = bcc_ptr - &pSMB->Password[0];
3379 pSMB->hdr.smb_buf_length += count;
3380 pSMB->ByteCount = cpu_to_le16(count);
3381
3382 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, 0);
3383
3384 /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
3385 /* above now done in SendReceive */
3386 if ((rc == 0) && (tcon != NULL)) {
3387 tcon->tidStatus = CifsGood;
3388 tcon->tid = smb_buffer_response->Tid;
3389 bcc_ptr = pByteArea(smb_buffer_response);
3390 length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
Steve French50c2f752007-07-13 00:33:32 +00003391 /* skip service field (NB: this field is always ASCII) */
3392 bcc_ptr += length + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003393 strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
3394 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
3395 length = UniStrnlen((wchar_t *) bcc_ptr, 512);
3396 if ((bcc_ptr + (2 * length)) -
3397 pByteArea(smb_buffer_response) <=
3398 BCC(smb_buffer_response)) {
Jesper Juhlf99d49a2005-11-07 01:01:34 -08003399 kfree(tcon->nativeFileSystem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 tcon->nativeFileSystem =
Pekka Enberge915fc42005-09-06 15:18:35 -07003401 kzalloc(length + 2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003402 cifs_strfromUCS_le(tcon->nativeFileSystem,
Steve Frenche89dc922005-11-11 15:18:19 -08003403 (__le16 *) bcc_ptr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003404 length, nls_codepage);
3405 bcc_ptr += 2 * length;
3406 bcc_ptr[0] = 0; /* null terminate the string */
3407 bcc_ptr[1] = 0;
3408 bcc_ptr += 2;
3409 }
Steve French50c2f752007-07-13 00:33:32 +00003410 /* else do not bother copying these information fields*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411 } else {
3412 length = strnlen(bcc_ptr, 1024);
3413 if ((bcc_ptr + length) -
3414 pByteArea(smb_buffer_response) <=
3415 BCC(smb_buffer_response)) {
Jesper Juhlf99d49a2005-11-07 01:01:34 -08003416 kfree(tcon->nativeFileSystem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003417 tcon->nativeFileSystem =
Pekka Enberge915fc42005-09-06 15:18:35 -07003418 kzalloc(length + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003419 strncpy(tcon->nativeFileSystem, bcc_ptr,
3420 length);
3421 }
Steve French50c2f752007-07-13 00:33:32 +00003422 /* else do not bother copying these information fields*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003423 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003424 if ((smb_buffer_response->WordCount == 3) ||
Steve French1a4e15a2006-10-12 21:33:51 +00003425 (smb_buffer_response->WordCount == 7))
3426 /* field is in same location */
Steve French39798772006-05-31 22:40:51 +00003427 tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
3428 else
3429 tcon->Flags = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003430 cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags));
3431 } else if ((rc == 0) && tcon == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00003432 /* all we need to save for IPC$ connection */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003433 ses->ipc_tid = smb_buffer_response->Tid;
3434 }
3435
3436 if (smb_buffer)
3437 cifs_buf_release(smb_buffer);
3438 return rc;
3439}
3440
3441int
3442cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3443{
3444 int rc = 0;
3445 int xid;
3446 struct cifsSesInfo *ses = NULL;
3447 struct task_struct *cifsd_task;
Steve French50c2f752007-07-13 00:33:32 +00003448 char *tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003449
3450 xid = GetXid();
3451
3452 if (cifs_sb->tcon) {
3453 ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
3454 rc = CIFSSMBTDis(xid, cifs_sb->tcon);
3455 if (rc == -EBUSY) {
3456 FreeXid(xid);
3457 return 0;
3458 }
3459 tconInfoFree(cifs_sb->tcon);
3460 if ((ses) && (ses->server)) {
3461 /* save off task so we do not refer to ses later */
3462 cifsd_task = ses->server->tsk;
3463 cFYI(1, ("About to do SMBLogoff "));
3464 rc = CIFSSMBLogoff(xid, ses);
3465 if (rc == -EBUSY) {
3466 FreeXid(xid);
3467 return 0;
3468 } else if (rc == -ESHUTDOWN) {
Steve French467a8f82007-06-27 22:41:32 +00003469 cFYI(1, ("Waking up socket by sending signal"));
Steve Frenchf7f7c312007-05-24 02:29:51 +00003470 if (cifsd_task) {
Steve French50c2f752007-07-13 00:33:32 +00003471 force_sig(SIGKILL, cifsd_task);
Igor Mammedovaaf737a2007-04-03 19:16:43 +00003472 kthread_stop(cifsd_task);
Steve Frenchf1914012005-08-18 09:37:34 -07003473 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003474 rc = 0;
3475 } /* else - we have an smb session
3476 left on this socket do not kill cifsd */
3477 } else
3478 cFYI(1, ("No session or bad tcon"));
3479 }
Steve French50c2f752007-07-13 00:33:32 +00003480
Linus Torvalds1da177e2005-04-16 15:20:36 -07003481 cifs_sb->tcon = NULL;
Steve French2fe87f02006-09-21 07:02:52 +00003482 tmp = cifs_sb->prepath;
3483 cifs_sb->prepathlen = 0;
3484 cifs_sb->prepath = NULL;
3485 kfree(tmp);
Nishanth Aravamudan041e0e32005-09-10 00:27:23 -07003486 if (ses)
3487 schedule_timeout_interruptible(msecs_to_jiffies(500));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003488 if (ses)
3489 sesInfoFree(ses);
3490
3491 FreeXid(xid);
Steve French50c2f752007-07-13 00:33:32 +00003492 return rc; /* BB check if we should always return zero here */
3493}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003494
3495int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
Steve French50c2f752007-07-13 00:33:32 +00003496 struct nls_table *nls_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003497{
3498 int rc = 0;
Steve French7c7b25b2006-06-01 19:20:10 +00003499 char ntlm_session_key[CIFS_SESS_KEY_SIZE];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003500 int ntlmv2_flag = FALSE;
Steve Frenchad009ac2005-04-28 22:41:05 -07003501 int first_time = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502
3503 /* what if server changes its buffer size after dropping the session? */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003504 if (pSesInfo->server->maxBuf == 0) /* no need to send on reconnect */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003505 rc = CIFSSMBNegotiate(xid, pSesInfo);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003506 if (rc == -EAGAIN) /* retry only once on 1st time connection */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003507 rc = CIFSSMBNegotiate(xid, pSesInfo);
Steve French50c2f752007-07-13 00:33:32 +00003508 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003509 rc = -EHOSTDOWN;
3510 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003511 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003512 spin_lock(&GlobalMid_Lock);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003513 if (pSesInfo->server->tcpStatus != CifsExiting)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003514 pSesInfo->server->tcpStatus = CifsGood;
3515 else
3516 rc = -EHOSTDOWN;
3517 spin_unlock(&GlobalMid_Lock);
3518
3519 }
Steve Frenchad009ac2005-04-28 22:41:05 -07003520 first_time = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003521 }
3522 if (!rc) {
Steve French9ac00b72006-09-30 04:13:17 +00003523 pSesInfo->flags = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003524 pSesInfo->capabilities = pSesInfo->server->capabilities;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003525 if (linuxExtEnabled == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003526 pSesInfo->capabilities &= (~CAP_UNIX);
Steve Frenchad009ac2005-04-28 22:41:05 -07003527 /* pSesInfo->sequence_number = 0;*/
Steve French50c2f752007-07-13 00:33:32 +00003528 cFYI(1,
3529 ("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003530 pSesInfo->server->secMode,
3531 pSesInfo->server->capabilities,
Steve French175ec9e2006-09-30 01:07:38 +00003532 pSesInfo->server->timeAdj));
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003533 if (experimEnabled < 2)
Steve French39798772006-05-31 22:40:51 +00003534 rc = CIFS_SessSetup(xid, pSesInfo,
3535 first_time, nls_info);
Steve French189acaa2006-06-23 02:33:48 +00003536 else if (extended_security
Steve French50c2f752007-07-13 00:33:32 +00003537 && (pSesInfo->capabilities
Steve French175ec9e2006-09-30 01:07:38 +00003538 & CAP_EXTENDED_SECURITY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003539 && (pSesInfo->server->secType == NTLMSSP)) {
Steve French189acaa2006-06-23 02:33:48 +00003540 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003541 } else if (extended_security
3542 && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
3543 && (pSesInfo->server->secType == RawNTLMSSP)) {
Steve French5815449d2006-02-14 01:36:20 +00003544 cFYI(1, ("NTLMSSP sesssetup"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003545 rc = CIFSNTLMSSPNegotiateSessSetup(xid,
3546 pSesInfo,
3547 &ntlmv2_flag,
3548 nls_info);
3549 if (!rc) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003550 if (ntlmv2_flag) {
Steve French50c2f752007-07-13 00:33:32 +00003551 char *v2_response;
Steve French467a8f82007-06-27 22:41:32 +00003552 cFYI(1, ("more secure NTLM ver2 hash"));
Steve French50c2f752007-07-13 00:33:32 +00003553 if (CalcNTLMv2_partial_mac_key(pSesInfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003554 nls_info)) {
3555 rc = -ENOMEM;
3556 goto ss_err_exit;
3557 } else
3558 v2_response = kmalloc(16 + 64 /* blob */, GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003559 if (v2_response) {
Steve French50c2f752007-07-13 00:33:32 +00003560 CalcNTLMv2_response(pSesInfo,
3561 v2_response);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003562 /* if (first_time)
Steve French50c2f752007-07-13 00:33:32 +00003563 cifs_calculate_ntlmv2_mac_key(
3564 pSesInfo->server->mac_signing_key,
3565 response, ntlm_session_key,*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003566 kfree(v2_response);
3567 /* BB Put dummy sig in SessSetup PDU? */
3568 } else {
3569 rc = -ENOMEM;
3570 goto ss_err_exit;
3571 }
3572
3573 } else {
3574 SMBNTencrypt(pSesInfo->password,
3575 pSesInfo->server->cryptKey,
3576 ntlm_session_key);
3577
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003578 if (first_time)
Steve Frenchad009ac2005-04-28 22:41:05 -07003579 cifs_calculate_mac_key(
Steve Frenchb609f062007-07-09 07:55:14 +00003580 &pSesInfo->server->mac_signing_key,
Steve Frenchad009ac2005-04-28 22:41:05 -07003581 ntlm_session_key,
3582 pSesInfo->password);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003583 }
3584 /* for better security the weaker lanman hash not sent
3585 in AuthSessSetup so we no longer calculate it */
3586
3587 rc = CIFSNTLMSSPAuthSessSetup(xid,
3588 pSesInfo,
3589 ntlm_session_key,
3590 ntlmv2_flag,
3591 nls_info);
3592 }
3593 } else { /* old style NTLM 0.12 session setup */
3594 SMBNTencrypt(pSesInfo->password,
3595 pSesInfo->server->cryptKey,
3596 ntlm_session_key);
3597
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003598 if (first_time)
Steve Frenchad009ac2005-04-28 22:41:05 -07003599 cifs_calculate_mac_key(
Steve Frenchb609f062007-07-09 07:55:14 +00003600 &pSesInfo->server->mac_signing_key,
Steve Frenchad009ac2005-04-28 22:41:05 -07003601 ntlm_session_key, pSesInfo->password);
3602
Linus Torvalds1da177e2005-04-16 15:20:36 -07003603 rc = CIFSSessSetup(xid, pSesInfo,
3604 ntlm_session_key, nls_info);
3605 }
3606 if (rc) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003607 cERROR(1, ("Send error in SessSetup = %d", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003608 } else {
Steve French467a8f82007-06-27 22:41:32 +00003609 cFYI(1, ("CIFS Session Established successfully"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003610 pSesInfo->status = CifsGood;
3611 }
3612 }
3613ss_err_exit:
3614 return rc;
3615}
3616