blob: c682be8f298413a24d7d123cef7368694d123dbd [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/connect.c
3 *
Steve French366781c2008-01-25 10:12:41 +00004 * Copyright (C) International Business Machines Corp., 2002,2008
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070052extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
53 unsigned char *p24);
54
55extern mempool_t *cifs_req_poolp;
56
57struct smb_vol {
58 char *username;
59 char *password;
60 char *domainname;
61 char *UNC;
62 char *UNCip;
Steve French95b1cb92008-05-15 16:44:38 +000063 char *in6_addr; /* ipv6 address as human readable form of in6_addr */
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 char *iocharset; /* local code page for mapping to and from Unicode */
65 char source_rfc1001_name[16]; /* netbios name of client */
Steve Frencha10faeb22005-08-22 21:38:31 -070066 char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 uid_t linux_uid;
68 gid_t linux_gid;
69 mode_t file_mode;
70 mode_t dir_mode;
Steve French189acaa2006-06-23 02:33:48 +000071 unsigned secFlg;
Steve French4b18f2a2008-04-29 00:06:05 +000072 bool rw:1;
73 bool retry:1;
74 bool intr:1;
75 bool setuids:1;
76 bool override_uid:1;
77 bool override_gid:1;
Jeff Laytond0a9c072008-05-12 22:23:49 +000078 bool dynperm:1;
Steve French4b18f2a2008-04-29 00:06:05 +000079 bool noperm:1;
80 bool no_psx_acl:1; /* set if posix acl support should be disabled */
81 bool cifs_acl:1;
82 bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
83 bool server_ino:1; /* use inode numbers from server ie UniqueId */
84 bool direct_io:1;
Steve French95b1cb92008-05-15 16:44:38 +000085 bool remap:1; /* set to remap seven reserved chars in filenames */
86 bool posix_paths:1; /* unset to not ask for posix pathnames. */
Steve French4b18f2a2008-04-29 00:06:05 +000087 bool no_linux_ext:1;
88 bool sfu_emul:1;
Steve French95b1cb92008-05-15 16:44:38 +000089 bool nullauth:1; /* attempt to authenticate with null user */
90 bool nocase:1; /* request case insensitive filenames */
91 bool nobrl:1; /* disable sending byte range locks to srv */
92 bool seal:1; /* request transport encryption on share */
Steve French84210e92008-10-23 04:42:37 +000093 bool nodfs:1; /* Do not request DFS, even if available */
94 bool local_lease:1; /* check leases only on local system, not remote */
Steve Frenchedf1ae42008-10-29 00:47:57 +000095 bool noblocksnd:1;
96 bool noautotune:1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 unsigned int rsize;
98 unsigned int wsize;
99 unsigned int sockopt;
100 unsigned short int port;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000101 char *prepath;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102};
103
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000104static int ipv4_connect(struct sockaddr_in *psin_server,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 struct socket **csocket,
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000106 char *netb_name,
Steve Frenchedf1ae42008-10-29 00:47:57 +0000107 char *server_netb_name,
108 bool noblocksnd,
109 bool nosndbuf); /* ipv6 never set sndbuf size */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000110static int ipv6_connect(struct sockaddr_in6 *psin_server,
Steve Frenchedf1ae42008-10-29 00:47:57 +0000111 struct socket **csocket, bool noblocksnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112
113
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000114 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 * cifs tcp session reconnection
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000116 *
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 * mark tcp session as reconnecting so temporarily locked
118 * mark all smb sessions as reconnecting for tcp session
119 * reconnect tcp session
120 * wake up waiters on reconnection? - (not needed currently)
121 */
122
Steve French2cd646a2006-09-28 19:43:08 +0000123static int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124cifs_reconnect(struct TCP_Server_Info *server)
125{
126 int rc = 0;
127 struct list_head *tmp;
128 struct cifsSesInfo *ses;
129 struct cifsTconInfo *tcon;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000130 struct mid_q_entry *mid_entry;
Steve French50c2f752007-07-13 00:33:32 +0000131
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 spin_lock(&GlobalMid_Lock);
Jeff Layton469ee612008-10-16 18:46:39 +0000133 if (server->tcpStatus == CifsExiting) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000134 /* the demux thread will exit normally
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 next time through the loop */
136 spin_unlock(&GlobalMid_Lock);
137 return rc;
138 } else
139 server->tcpStatus = CifsNeedReconnect;
140 spin_unlock(&GlobalMid_Lock);
141 server->maxBuf = 0;
142
Steve Frenche4eb2952005-04-28 22:41:09 -0700143 cFYI(1, ("Reconnecting tcp session"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
145 /* before reconnecting the tcp session, mark the smb session (uid)
146 and the tid bad so they are not used until reconnected */
147 read_lock(&GlobalSMBSeslock);
148 list_for_each(tmp, &GlobalSMBSessionList) {
149 ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
150 if (ses->server) {
151 if (ses->server == server) {
152 ses->status = CifsNeedReconnect;
153 ses->ipc_tid = 0;
154 }
155 }
156 /* else tcp and smb sessions need reconnection */
157 }
158 list_for_each(tmp, &GlobalTreeConnectionList) {
159 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
Steve Frenchad8b15f2008-08-08 21:10:16 +0000160 if ((tcon->ses) && (tcon->ses->server == server))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 tcon->tidStatus = CifsNeedReconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 }
163 read_unlock(&GlobalSMBSeslock);
164 /* do not want to be sending data on a socket we are freeing */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000165 down(&server->tcpSem);
166 if (server->ssocket) {
Steve French467a8f82007-06-27 22:41:32 +0000167 cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 server->ssocket->flags));
Trond Myklebust91cf45f2007-11-12 18:10:39 -0800169 kernel_sock_shutdown(server->ssocket, SHUT_WR);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000170 cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx",
Steve French467a8f82007-06-27 22:41:32 +0000171 server->ssocket->state,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 server->ssocket->flags));
173 sock_release(server->ssocket);
174 server->ssocket = NULL;
175 }
176
177 spin_lock(&GlobalMid_Lock);
178 list_for_each(tmp, &server->pending_mid_q) {
179 mid_entry = list_entry(tmp, struct
180 mid_q_entry,
181 qhead);
Steve Frenchad8b15f2008-08-08 21:10:16 +0000182 if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
Steve French09d1db52005-04-28 22:41:08 -0700183 /* Mark other intransit requests as needing
184 retry so we do not immediately mark the
185 session bad again (ie after we reconnect
186 below) as they timeout too */
Steve Frenchad8b15f2008-08-08 21:10:16 +0000187 mid_entry->midState = MID_RETRY_NEEDED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 }
189 }
190 spin_unlock(&GlobalMid_Lock);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000191 up(&server->tcpSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Jeff Layton469ee612008-10-16 18:46:39 +0000193 while ((server->tcpStatus != CifsExiting) &&
194 (server->tcpStatus != CifsGood)) {
Steve French6c3d8902006-07-31 22:46:20 +0000195 try_to_freeze();
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000196 if (server->protocolType == IPV6) {
197 rc = ipv6_connect(&server->addr.sockAddr6,
Steve Frenchedf1ae42008-10-29 00:47:57 +0000198 &server->ssocket, server->noautotune);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 } else {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000200 rc = ipv4_connect(&server->addr.sockAddr,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 &server->ssocket,
Steve Frencha10faeb22005-08-22 21:38:31 -0700202 server->workstation_RFC1001_name,
Steve Frenchedf1ae42008-10-29 00:47:57 +0000203 server->server_RFC1001_name,
204 server->noblocksnd, server->noautotune);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000206 if (rc) {
207 cFYI(1, ("reconnect error %d", rc));
Steve French0cb766a2005-04-28 22:41:11 -0700208 msleep(3000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 } else {
210 atomic_inc(&tcpSesReconnectCount);
211 spin_lock(&GlobalMid_Lock);
Jeff Layton469ee612008-10-16 18:46:39 +0000212 if (server->tcpStatus != CifsExiting)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 server->tcpStatus = CifsGood;
Steve Frenchad009ac2005-04-28 22:41:05 -0700214 server->sequence_number = 0;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000215 spin_unlock(&GlobalMid_Lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216 /* atomic_set(&server->inFlight,0);*/
217 wake_up(&server->response_q);
218 }
219 }
220 return rc;
221}
222
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000223/*
Steve Frenche4eb2952005-04-28 22:41:09 -0700224 return codes:
225 0 not a transact2, or all data present
226 >0 transact2 with that much data missing
227 -EINVAL = invalid transact2
228
229 */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000230static int check2ndT2(struct smb_hdr *pSMB, unsigned int maxBufSize)
Steve Frenche4eb2952005-04-28 22:41:09 -0700231{
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000232 struct smb_t2_rsp *pSMBt;
233 int total_data_size;
Steve Frenche4eb2952005-04-28 22:41:09 -0700234 int data_in_this_rsp;
235 int remaining;
236
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000237 if (pSMB->Command != SMB_COM_TRANSACTION2)
Steve Frenche4eb2952005-04-28 22:41:09 -0700238 return 0;
239
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000240 /* check for plausible wct, bcc and t2 data and parm sizes */
241 /* check for parm and data offset going beyond end of smb */
242 if (pSMB->WordCount != 10) { /* coalesce_t2 depends on this */
Steve French467a8f82007-06-27 22:41:32 +0000243 cFYI(1, ("invalid transact2 word count"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700244 return -EINVAL;
245 }
246
247 pSMBt = (struct smb_t2_rsp *)pSMB;
248
249 total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
250 data_in_this_rsp = le16_to_cpu(pSMBt->t2_rsp.DataCount);
251
252 remaining = total_data_size - data_in_this_rsp;
253
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000254 if (remaining == 0)
Steve Frenche4eb2952005-04-28 22:41:09 -0700255 return 0;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000256 else if (remaining < 0) {
Steve French467a8f82007-06-27 22:41:32 +0000257 cFYI(1, ("total data %d smaller than data in frame %d",
Steve Frenche4eb2952005-04-28 22:41:09 -0700258 total_data_size, data_in_this_rsp));
259 return -EINVAL;
260 } else {
Steve French467a8f82007-06-27 22:41:32 +0000261 cFYI(1, ("missing %d bytes from transact2, check next response",
Steve Frenche4eb2952005-04-28 22:41:09 -0700262 remaining));
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000263 if (total_data_size > maxBufSize) {
264 cERROR(1, ("TotalDataSize %d is over maximum buffer %d",
265 total_data_size, maxBufSize));
266 return -EINVAL;
Steve Frenche4eb2952005-04-28 22:41:09 -0700267 }
268 return remaining;
269 }
270}
271
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000272static int coalesce_t2(struct smb_hdr *psecond, struct smb_hdr *pTargetSMB)
Steve Frenche4eb2952005-04-28 22:41:09 -0700273{
274 struct smb_t2_rsp *pSMB2 = (struct smb_t2_rsp *)psecond;
275 struct smb_t2_rsp *pSMBt = (struct smb_t2_rsp *)pTargetSMB;
276 int total_data_size;
277 int total_in_buf;
278 int remaining;
279 int total_in_buf2;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000280 char *data_area_of_target;
281 char *data_area_of_buf2;
Steve Frenche4eb2952005-04-28 22:41:09 -0700282 __u16 byte_count;
283
284 total_data_size = le16_to_cpu(pSMBt->t2_rsp.TotalDataCount);
285
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000286 if (total_data_size != le16_to_cpu(pSMB2->t2_rsp.TotalDataCount)) {
Steve French63135e02007-07-17 17:34:02 +0000287 cFYI(1, ("total data size of primary and secondary t2 differ"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700288 }
289
290 total_in_buf = le16_to_cpu(pSMBt->t2_rsp.DataCount);
291
292 remaining = total_data_size - total_in_buf;
Steve French50c2f752007-07-13 00:33:32 +0000293
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000294 if (remaining < 0)
Steve Frenche4eb2952005-04-28 22:41:09 -0700295 return -EINVAL;
296
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000297 if (remaining == 0) /* nothing to do, ignore */
Steve Frenche4eb2952005-04-28 22:41:09 -0700298 return 0;
Steve French50c2f752007-07-13 00:33:32 +0000299
Steve Frenche4eb2952005-04-28 22:41:09 -0700300 total_in_buf2 = le16_to_cpu(pSMB2->t2_rsp.DataCount);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000301 if (remaining < total_in_buf2) {
Steve French467a8f82007-06-27 22:41:32 +0000302 cFYI(1, ("transact2 2nd response contains too much data"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700303 }
304
305 /* find end of first SMB data area */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000306 data_area_of_target = (char *)&pSMBt->hdr.Protocol +
Steve Frenche4eb2952005-04-28 22:41:09 -0700307 le16_to_cpu(pSMBt->t2_rsp.DataOffset);
308 /* validate target area */
309
310 data_area_of_buf2 = (char *) &pSMB2->hdr.Protocol +
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000311 le16_to_cpu(pSMB2->t2_rsp.DataOffset);
Steve Frenche4eb2952005-04-28 22:41:09 -0700312
313 data_area_of_target += total_in_buf;
314
315 /* copy second buffer into end of first buffer */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000316 memcpy(data_area_of_target, data_area_of_buf2, total_in_buf2);
Steve Frenche4eb2952005-04-28 22:41:09 -0700317 total_in_buf += total_in_buf2;
318 pSMBt->t2_rsp.DataCount = cpu_to_le16(total_in_buf);
319 byte_count = le16_to_cpu(BCC_LE(pTargetSMB));
320 byte_count += total_in_buf2;
321 BCC_LE(pTargetSMB) = cpu_to_le16(byte_count);
322
Steve French70ca7342005-09-22 16:32:06 -0700323 byte_count = pTargetSMB->smb_buf_length;
Steve Frenche4eb2952005-04-28 22:41:09 -0700324 byte_count += total_in_buf2;
325
326 /* BB also add check that we are not beyond maximum buffer size */
Steve French50c2f752007-07-13 00:33:32 +0000327
Steve French70ca7342005-09-22 16:32:06 -0700328 pTargetSMB->smb_buf_length = byte_count;
Steve Frenche4eb2952005-04-28 22:41:09 -0700329
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000330 if (remaining == total_in_buf2) {
Steve French467a8f82007-06-27 22:41:32 +0000331 cFYI(1, ("found the last secondary response"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700332 return 0; /* we are done */
333 } else /* more responses to go */
334 return 1;
335
336}
337
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338static int
339cifs_demultiplex_thread(struct TCP_Server_Info *server)
340{
341 int length;
342 unsigned int pdu_length, total_read;
343 struct smb_hdr *smb_buffer = NULL;
Steve Frenchb8643e12005-04-28 22:41:07 -0700344 struct smb_hdr *bigbuf = NULL;
345 struct smb_hdr *smallbuf = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 struct msghdr smb_msg;
347 struct kvec iov;
348 struct socket *csocket = server->ssocket;
349 struct list_head *tmp;
350 struct cifsSesInfo *ses;
351 struct task_struct *task_to_wake = NULL;
352 struct mid_q_entry *mid_entry;
Steve French70ca7342005-09-22 16:32:06 -0700353 char temp;
Steve French4b18f2a2008-04-29 00:06:05 +0000354 bool isLargeBuf = false;
355 bool isMultiRsp;
Steve Frenche4eb2952005-04-28 22:41:09 -0700356 int reconnect;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 current->flags |= PF_MEMALLOC;
Pavel Emelyanovba25f9d2007-10-18 23:40:40 -0700359 cFYI(1, ("Demultiplex PID: %d", task_pid_nr(current)));
Jeff Layton93d0ec82008-08-02 08:00:48 -0400360
361 length = atomic_inc_return(&tcpSesAllocCount);
362 if (length > 1)
Steve French26f57362007-08-30 22:09:15 +0000363 mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
364 GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Rafael J. Wysocki83144182007-07-17 04:03:35 -0700366 set_freezable();
Jeff Layton469ee612008-10-16 18:46:39 +0000367 while (server->tcpStatus != CifsExiting) {
Steve Frenchede13272005-08-30 20:10:14 -0700368 if (try_to_freeze())
369 continue;
Steve Frenchb8643e12005-04-28 22:41:07 -0700370 if (bigbuf == NULL) {
371 bigbuf = cifs_buf_get();
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000372 if (!bigbuf) {
373 cERROR(1, ("No memory for large SMB response"));
Steve Frenchb8643e12005-04-28 22:41:07 -0700374 msleep(3000);
375 /* retry will check if exiting */
376 continue;
377 }
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000378 } else if (isLargeBuf) {
379 /* we are reusing a dirty large buf, clear its start */
Steve French26f57362007-08-30 22:09:15 +0000380 memset(bigbuf, 0, sizeof(struct smb_hdr));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 }
Steve Frenchb8643e12005-04-28 22:41:07 -0700382
383 if (smallbuf == NULL) {
384 smallbuf = cifs_small_buf_get();
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000385 if (!smallbuf) {
386 cERROR(1, ("No memory for SMB response"));
Steve Frenchb8643e12005-04-28 22:41:07 -0700387 msleep(1000);
388 /* retry will check if exiting */
389 continue;
390 }
391 /* beginning of smb buffer is cleared in our buf_get */
392 } else /* if existing small buf clear beginning */
Steve French26f57362007-08-30 22:09:15 +0000393 memset(smallbuf, 0, sizeof(struct smb_hdr));
Steve Frenchb8643e12005-04-28 22:41:07 -0700394
Steve French4b18f2a2008-04-29 00:06:05 +0000395 isLargeBuf = false;
396 isMultiRsp = false;
Steve Frenchb8643e12005-04-28 22:41:07 -0700397 smb_buffer = smallbuf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 iov.iov_base = smb_buffer;
399 iov.iov_len = 4;
400 smb_msg.msg_control = NULL;
401 smb_msg.msg_controllen = 0;
Steve Frenchf01d5e12007-08-30 21:13:31 +0000402 pdu_length = 4; /* enough to get RFC1001 header */
403incomplete_rcv:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 length =
405 kernel_recvmsg(csocket, &smb_msg,
Steve Frenchf01d5e12007-08-30 21:13:31 +0000406 &iov, 1, pdu_length, 0 /* BB other flags? */);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407
Jeff Layton469ee612008-10-16 18:46:39 +0000408 if (server->tcpStatus == CifsExiting) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 break;
410 } else if (server->tcpStatus == CifsNeedReconnect) {
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000411 cFYI(1, ("Reconnect after server stopped responding"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 cifs_reconnect(server);
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000413 cFYI(1, ("call to reconnect done"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 csocket = server->ssocket;
415 continue;
416 } else if ((length == -ERESTARTSYS) || (length == -EAGAIN)) {
Steve Frenchb8643e12005-04-28 22:41:07 -0700417 msleep(1); /* minimum sleep to prevent looping
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 allowing socket to clear and app threads to set
419 tcpStatus CifsNeedReconnect if server hung */
Steve Frenchc527c8a2008-11-03 20:46:21 +0000420 if (pdu_length < 4) {
421 iov.iov_base = (4 - pdu_length) +
422 (char *)smb_buffer;
423 iov.iov_len = pdu_length;
424 smb_msg.msg_control = NULL;
425 smb_msg.msg_controllen = 0;
Steve Frenchc18c7322007-10-17 18:01:11 +0000426 goto incomplete_rcv;
Steve Frenchc527c8a2008-11-03 20:46:21 +0000427 } else
Steve Frenchc18c7322007-10-17 18:01:11 +0000428 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429 } else if (length <= 0) {
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000430 if (server->tcpStatus == CifsNew) {
431 cFYI(1, ("tcp session abend after SMBnegprot"));
Steve French09d1db52005-04-28 22:41:08 -0700432 /* some servers kill the TCP session rather than
433 returning an SMB negprot error, in which
434 case reconnecting here is not going to help,
435 and so simply return error to mount */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 break;
437 }
Pavel Machek0fd1ffe2006-06-13 21:31:39 +0000438 if (!try_to_freeze() && (length == -EINTR)) {
Steve French467a8f82007-06-27 22:41:32 +0000439 cFYI(1, ("cifsd thread killed"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440 break;
441 }
Steve French467a8f82007-06-27 22:41:32 +0000442 cFYI(1, ("Reconnect after unexpected peek error %d",
Steve French57337e42005-04-28 22:41:10 -0700443 length));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 cifs_reconnect(server);
445 csocket = server->ssocket;
446 wake_up(&server->response_q);
447 continue;
Petr Tesarik2a974682007-11-20 02:24:08 +0000448 } else if (length < pdu_length) {
449 cFYI(1, ("requested %d bytes but only got %d bytes",
450 pdu_length, length));
Steve Frenchf01d5e12007-08-30 21:13:31 +0000451 pdu_length -= length;
Steve Frenchf01d5e12007-08-30 21:13:31 +0000452 msleep(1);
453 goto incomplete_rcv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 }
Steve French67010fb2005-04-28 22:41:09 -0700455
Steve French70ca7342005-09-22 16:32:06 -0700456 /* The right amount was read from socket - 4 bytes */
457 /* so we can now interpret the length field */
Steve French46810cb2005-04-28 22:41:09 -0700458
Steve French70ca7342005-09-22 16:32:06 -0700459 /* the first byte big endian of the length field,
460 is actually not part of the length but the type
461 with the most common, zero, as regular data */
462 temp = *((char *) smb_buffer);
463
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000464 /* Note that FC 1001 length is big endian on the wire,
Steve French70ca7342005-09-22 16:32:06 -0700465 but we convert it here so it is always manipulated
466 as host byte order */
Harvey Harrison5ca33c62008-07-23 17:45:58 -0700467 pdu_length = be32_to_cpu((__force __be32)smb_buffer->smb_buf_length);
Steve French70ca7342005-09-22 16:32:06 -0700468 smb_buffer->smb_buf_length = pdu_length;
Steve French46810cb2005-04-28 22:41:09 -0700469
Steve French467a8f82007-06-27 22:41:32 +0000470 cFYI(1, ("rfc1002 length 0x%x", pdu_length+4));
Steve French70ca7342005-09-22 16:32:06 -0700471
472 if (temp == (char) RFC1002_SESSION_KEEP_ALIVE) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000473 continue;
Steve French70ca7342005-09-22 16:32:06 -0700474 } else if (temp == (char)RFC1002_POSITIVE_SESSION_RESPONSE) {
Steve French467a8f82007-06-27 22:41:32 +0000475 cFYI(1, ("Good RFC 1002 session rsp"));
Steve Frenche4eb2952005-04-28 22:41:09 -0700476 continue;
Steve French70ca7342005-09-22 16:32:06 -0700477 } else if (temp == (char)RFC1002_NEGATIVE_SESSION_RESPONSE) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000478 /* we get this from Windows 98 instead of
Steve French46810cb2005-04-28 22:41:09 -0700479 an error on SMB negprot response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000480 cFYI(1, ("Negative RFC1002 Session Response Error 0x%x)",
Steve French70ca7342005-09-22 16:32:06 -0700481 pdu_length));
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000482 if (server->tcpStatus == CifsNew) {
483 /* if nack on negprot (rather than
Steve French46810cb2005-04-28 22:41:09 -0700484 ret of smb negprot error) reconnecting
485 not going to help, ret error to mount */
486 break;
487 } else {
488 /* give server a second to
489 clean up before reconnect attempt */
490 msleep(1000);
491 /* always try 445 first on reconnect
492 since we get NACK on some if we ever
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000493 connected to port 139 (the NACK is
Steve French46810cb2005-04-28 22:41:09 -0700494 since we do not begin with RFC1001
495 session initialize frame) */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000496 server->addr.sockAddr.sin_port =
Steve French46810cb2005-04-28 22:41:09 -0700497 htons(CIFS_PORT);
498 cifs_reconnect(server);
499 csocket = server->ssocket;
500 wake_up(&server->response_q);
501 continue;
502 }
Steve French70ca7342005-09-22 16:32:06 -0700503 } else if (temp != (char) 0) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000504 cERROR(1, ("Unknown RFC 1002 frame"));
Steve French70ca7342005-09-22 16:32:06 -0700505 cifs_dump_mem(" Received Data: ", (char *)smb_buffer,
506 length);
Steve French46810cb2005-04-28 22:41:09 -0700507 cifs_reconnect(server);
508 csocket = server->ssocket;
509 continue;
Steve Frenche4eb2952005-04-28 22:41:09 -0700510 }
511
512 /* else we have an SMB response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000513 if ((pdu_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) ||
Steve French26f57362007-08-30 22:09:15 +0000514 (pdu_length < sizeof(struct smb_hdr) - 1 - 4)) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700515 cERROR(1, ("Invalid size SMB length %d pdu_length %d",
Steve French46810cb2005-04-28 22:41:09 -0700516 length, pdu_length+4));
Steve Frenche4eb2952005-04-28 22:41:09 -0700517 cifs_reconnect(server);
518 csocket = server->ssocket;
519 wake_up(&server->response_q);
520 continue;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000521 }
Steve Frenche4eb2952005-04-28 22:41:09 -0700522
523 /* else length ok */
524 reconnect = 0;
525
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000526 if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
Steve French4b18f2a2008-04-29 00:06:05 +0000527 isLargeBuf = true;
Steve Frenche4eb2952005-04-28 22:41:09 -0700528 memcpy(bigbuf, smallbuf, 4);
529 smb_buffer = bigbuf;
530 }
531 length = 0;
532 iov.iov_base = 4 + (char *)smb_buffer;
533 iov.iov_len = pdu_length;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000534 for (total_read = 0; total_read < pdu_length;
Steve Frenche4eb2952005-04-28 22:41:09 -0700535 total_read += length) {
536 length = kernel_recvmsg(csocket, &smb_msg, &iov, 1,
537 pdu_length - total_read, 0);
Jeff Layton469ee612008-10-16 18:46:39 +0000538 if ((server->tcpStatus == CifsExiting) ||
Steve Frenche4eb2952005-04-28 22:41:09 -0700539 (length == -EINTR)) {
540 /* then will exit */
541 reconnect = 2;
542 break;
543 } else if (server->tcpStatus == CifsNeedReconnect) {
Steve French46810cb2005-04-28 22:41:09 -0700544 cifs_reconnect(server);
545 csocket = server->ssocket;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000546 /* Reconnect wakes up rspns q */
Steve Frenche4eb2952005-04-28 22:41:09 -0700547 /* Now we will reread sock */
548 reconnect = 1;
549 break;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000550 } else if ((length == -ERESTARTSYS) ||
Steve Frenche4eb2952005-04-28 22:41:09 -0700551 (length == -EAGAIN)) {
552 msleep(1); /* minimum sleep to prevent looping,
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000553 allowing socket to clear and app
Steve Frenche4eb2952005-04-28 22:41:09 -0700554 threads to set tcpStatus
555 CifsNeedReconnect if server hung*/
Steve Frenchc18c7322007-10-17 18:01:11 +0000556 length = 0;
Steve French46810cb2005-04-28 22:41:09 -0700557 continue;
Steve Frenche4eb2952005-04-28 22:41:09 -0700558 } else if (length <= 0) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000559 cERROR(1, ("Received no data, expecting %d",
Steve Frenche4eb2952005-04-28 22:41:09 -0700560 pdu_length - total_read));
561 cifs_reconnect(server);
562 csocket = server->ssocket;
563 reconnect = 1;
564 break;
Steve French46810cb2005-04-28 22:41:09 -0700565 }
566 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000567 if (reconnect == 2)
Steve Frenche4eb2952005-04-28 22:41:09 -0700568 break;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000569 else if (reconnect == 1)
Steve Frenche4eb2952005-04-28 22:41:09 -0700570 continue;
571
572 length += 4; /* account for rfc1002 hdr */
Steve French50c2f752007-07-13 00:33:32 +0000573
Steve Frenche4eb2952005-04-28 22:41:09 -0700574
575 dump_smb(smb_buffer, length);
Steve French184ed212006-02-24 06:15:11 +0000576 if (checkSMB(smb_buffer, smb_buffer->Mid, total_read+4)) {
Steve Frenchb387eae2005-10-10 14:21:15 -0700577 cifs_dump_mem("Bad SMB: ", smb_buffer, 48);
Steve Frenche4eb2952005-04-28 22:41:09 -0700578 continue;
579 }
580
581
582 task_to_wake = NULL;
583 spin_lock(&GlobalMid_Lock);
584 list_for_each(tmp, &server->pending_mid_q) {
585 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
586
Steve French50c2f752007-07-13 00:33:32 +0000587 if ((mid_entry->mid == smb_buffer->Mid) &&
Steve Frenche4eb2952005-04-28 22:41:09 -0700588 (mid_entry->midState == MID_REQUEST_SUBMITTED) &&
589 (mid_entry->command == smb_buffer->Command)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000590 if (check2ndT2(smb_buffer,server->maxBuf) > 0) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700591 /* We have a multipart transact2 resp */
Steve French4b18f2a2008-04-29 00:06:05 +0000592 isMultiRsp = true;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000593 if (mid_entry->resp_buf) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700594 /* merge response - fix up 1st*/
Steve French50c2f752007-07-13 00:33:32 +0000595 if (coalesce_t2(smb_buffer,
Steve Frenche4eb2952005-04-28 22:41:09 -0700596 mid_entry->resp_buf)) {
Steve French4b18f2a2008-04-29 00:06:05 +0000597 mid_entry->multiRsp =
598 true;
Steve Frenche4eb2952005-04-28 22:41:09 -0700599 break;
600 } else {
601 /* all parts received */
Steve French4b18f2a2008-04-29 00:06:05 +0000602 mid_entry->multiEnd =
603 true;
Steve French50c2f752007-07-13 00:33:32 +0000604 goto multi_t2_fnd;
Steve Frenche4eb2952005-04-28 22:41:09 -0700605 }
606 } else {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000607 if (!isLargeBuf) {
Steve Frenche4eb2952005-04-28 22:41:09 -0700608 cERROR(1,("1st trans2 resp needs bigbuf"));
609 /* BB maybe we can fix this up, switch
Steve French50c2f752007-07-13 00:33:32 +0000610 to already allocated large buffer? */
Steve Frenche4eb2952005-04-28 22:41:09 -0700611 } else {
Steve Frenchcd634992005-04-28 22:41:10 -0700612 /* Have first buffer */
Steve Frenche4eb2952005-04-28 22:41:09 -0700613 mid_entry->resp_buf =
614 smb_buffer;
Steve French4b18f2a2008-04-29 00:06:05 +0000615 mid_entry->largeBuf =
616 true;
Steve Frenche4eb2952005-04-28 22:41:09 -0700617 bigbuf = NULL;
618 }
619 }
620 break;
Steve French50c2f752007-07-13 00:33:32 +0000621 }
Steve Frenche4eb2952005-04-28 22:41:09 -0700622 mid_entry->resp_buf = smb_buffer;
Steve French4b18f2a2008-04-29 00:06:05 +0000623 mid_entry->largeBuf = isLargeBuf;
Steve Frenche4eb2952005-04-28 22:41:09 -0700624multi_t2_fnd:
625 task_to_wake = mid_entry->tsk;
626 mid_entry->midState = MID_RESPONSE_RECEIVED;
Steve French1047abc2005-10-11 19:58:06 -0700627#ifdef CONFIG_CIFS_STATS2
628 mid_entry->when_received = jiffies;
629#endif
Steve French3a5ff612006-07-14 22:37:11 +0000630 /* so we do not time out requests to server
631 which is still responding (since server could
632 be busy but not dead) */
633 server->lstrp = jiffies;
Steve Frenche4eb2952005-04-28 22:41:09 -0700634 break;
635 }
636 }
637 spin_unlock(&GlobalMid_Lock);
638 if (task_to_wake) {
Steve Frenchcd634992005-04-28 22:41:10 -0700639 /* Was previous buf put in mpx struct for multi-rsp? */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000640 if (!isMultiRsp) {
Steve Frenchcd634992005-04-28 22:41:10 -0700641 /* smb buffer will be freed by user thread */
Steve French26f57362007-08-30 22:09:15 +0000642 if (isLargeBuf)
Steve Frenchcd634992005-04-28 22:41:10 -0700643 bigbuf = NULL;
Steve French26f57362007-08-30 22:09:15 +0000644 else
Steve Frenchcd634992005-04-28 22:41:10 -0700645 smallbuf = NULL;
646 }
Steve Frenche4eb2952005-04-28 22:41:09 -0700647 wake_up_process(task_to_wake);
Steve French4b18f2a2008-04-29 00:06:05 +0000648 } else if (!is_valid_oplock_break(smb_buffer, server) &&
649 !isMultiRsp) {
Steve French50c2f752007-07-13 00:33:32 +0000650 cERROR(1, ("No task to wake, unknown frame received! "
651 "NumMids %d", midCount.counter));
652 cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
Steve French70ca7342005-09-22 16:32:06 -0700653 sizeof(struct smb_hdr));
Steve French39798772006-05-31 22:40:51 +0000654#ifdef CONFIG_CIFS_DEBUG2
655 cifs_dump_detail(smb_buffer);
656 cifs_dump_mids(server);
657#endif /* CIFS_DEBUG2 */
Steve French50c2f752007-07-13 00:33:32 +0000658
Steve Frenche4eb2952005-04-28 22:41:09 -0700659 }
660 } /* end while !EXITING */
661
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 spin_lock(&GlobalMid_Lock);
663 server->tcpStatus = CifsExiting;
Steve Frenche691b9d2008-05-11 15:53:33 +0000664 spin_unlock(&GlobalMid_Lock);
Steve Frenchdbdbb872008-06-10 21:21:56 +0000665 wake_up_all(&server->response_q);
Steve Frenche691b9d2008-05-11 15:53:33 +0000666
Steve French31ca3bc2005-04-28 22:41:11 -0700667 /* check if we have blocked requests that need to free */
668 /* Note that cifs_max_pending is normally 50, but
669 can be set at module install time to as little as two */
Steve Frenche691b9d2008-05-11 15:53:33 +0000670 spin_lock(&GlobalMid_Lock);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000671 if (atomic_read(&server->inFlight) >= cifs_max_pending)
Steve French31ca3bc2005-04-28 22:41:11 -0700672 atomic_set(&server->inFlight, cifs_max_pending - 1);
673 /* We do not want to set the max_pending too low or we
674 could end up with the counter going negative */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 spin_unlock(&GlobalMid_Lock);
Steve French50c2f752007-07-13 00:33:32 +0000676 /* Although there should not be any requests blocked on
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 this queue it can not hurt to be paranoid and try to wake up requests
Steve French09d1db52005-04-28 22:41:08 -0700678 that may haven been blocked when more than 50 at time were on the wire
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 to the same server - they now will see the session is in exit state
680 and get out of SendReceive. */
681 wake_up_all(&server->request_q);
682 /* give those requests time to exit */
Steve Frenchb8643e12005-04-28 22:41:07 -0700683 msleep(125);
Steve French50c2f752007-07-13 00:33:32 +0000684
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000685 if (server->ssocket) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 sock_release(csocket);
687 server->ssocket = NULL;
688 }
Steve Frenchb8643e12005-04-28 22:41:07 -0700689 /* buffer usuallly freed in free_mid - need to free it here on exit */
Mariusz Kozlowskia8a11d32007-10-03 16:41:24 +0000690 cifs_buf_release(bigbuf);
691 if (smallbuf) /* no sense logging a debug message if NULL */
Steve Frenchb8643e12005-04-28 22:41:07 -0700692 cifs_small_buf_release(smallbuf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693
694 read_lock(&GlobalSMBSeslock);
695 if (list_empty(&server->pending_mid_q)) {
Steve French09d1db52005-04-28 22:41:08 -0700696 /* loop through server session structures attached to this and
697 mark them dead */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 list_for_each(tmp, &GlobalSMBSessionList) {
699 ses =
700 list_entry(tmp, struct cifsSesInfo,
701 cifsSessionList);
702 if (ses->server == server) {
703 ses->status = CifsExiting;
704 ses->server = NULL;
705 }
706 }
707 read_unlock(&GlobalSMBSeslock);
708 } else {
Steve French31ca3bc2005-04-28 22:41:11 -0700709 /* although we can not zero the server struct pointer yet,
710 since there are active requests which may depnd on them,
711 mark the corresponding SMB sessions as exiting too */
712 list_for_each(tmp, &GlobalSMBSessionList) {
713 ses = list_entry(tmp, struct cifsSesInfo,
714 cifsSessionList);
Steve French26f57362007-08-30 22:09:15 +0000715 if (ses->server == server)
Steve French31ca3bc2005-04-28 22:41:11 -0700716 ses->status = CifsExiting;
Steve French31ca3bc2005-04-28 22:41:11 -0700717 }
718
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 spin_lock(&GlobalMid_Lock);
720 list_for_each(tmp, &server->pending_mid_q) {
721 mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
722 if (mid_entry->midState == MID_REQUEST_SUBMITTED) {
Steve French50c2f752007-07-13 00:33:32 +0000723 cFYI(1, ("Clearing Mid 0x%x - waking up ",
724 mid_entry->mid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725 task_to_wake = mid_entry->tsk;
Steve French26f57362007-08-30 22:09:15 +0000726 if (task_to_wake)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 wake_up_process(task_to_wake);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 }
729 }
730 spin_unlock(&GlobalMid_Lock);
731 read_unlock(&GlobalSMBSeslock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 /* 1/8th of sec is more than enough time for them to exit */
Steve Frenchb8643e12005-04-28 22:41:07 -0700733 msleep(125);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734 }
735
Steve Frenchf1914012005-08-18 09:37:34 -0700736 if (!list_empty(&server->pending_mid_q)) {
Steve French50c2f752007-07-13 00:33:32 +0000737 /* mpx threads have not exited yet give them
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738 at least the smb send timeout time for long ops */
Steve French31ca3bc2005-04-28 22:41:11 -0700739 /* due to delays on oplock break requests, we need
740 to wait at least 45 seconds before giving up
741 on a request getting a response and going ahead
742 and killing cifsd */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 cFYI(1, ("Wait for exit from demultiplex thread"));
Steve French31ca3bc2005-04-28 22:41:11 -0700744 msleep(46000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 /* if threads still have not exited they are probably never
746 coming home not much else we can do but free the memory */
747 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
Steve French31ca3bc2005-04-28 22:41:11 -0700749 /* last chance to mark ses pointers invalid
750 if there are any pointing to this (e.g
Steve French50c2f752007-07-13 00:33:32 +0000751 if a crazy root user tried to kill cifsd
Steve French31ca3bc2005-04-28 22:41:11 -0700752 kernel thread explicitly this might happen) */
Jeff Layton93d0ec82008-08-02 08:00:48 -0400753 write_lock(&GlobalSMBSeslock);
Steve French31ca3bc2005-04-28 22:41:11 -0700754 list_for_each(tmp, &GlobalSMBSessionList) {
755 ses = list_entry(tmp, struct cifsSesInfo,
756 cifsSessionList);
Steve French26f57362007-08-30 22:09:15 +0000757 if (ses->server == server)
Steve French31ca3bc2005-04-28 22:41:11 -0700758 ses->server = NULL;
Steve French31ca3bc2005-04-28 22:41:11 -0700759 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 write_unlock(&GlobalSMBSeslock);
Steve French31ca3bc2005-04-28 22:41:11 -0700761
Jeff Laytonc359cf32007-11-16 22:22:06 +0000762 kfree(server->hostname);
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -0400763 task_to_wake = xchg(&server->tsk, NULL);
Steve French31ca3bc2005-04-28 22:41:11 -0700764 kfree(server);
Jeff Layton93d0ec82008-08-02 08:00:48 -0400765
766 length = atomic_dec_return(&tcpSesAllocCount);
Steve French26f57362007-08-30 22:09:15 +0000767 if (length > 0)
768 mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
769 GFP_KERNEL);
Steve French50c2f752007-07-13 00:33:32 +0000770
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -0400771 /* if server->tsk was NULL then wait for a signal before exiting */
772 if (!task_to_wake) {
773 set_current_state(TASK_INTERRUPTIBLE);
774 while (!signal_pending(current)) {
775 schedule();
776 set_current_state(TASK_INTERRUPTIBLE);
777 }
778 set_current_state(TASK_RUNNING);
779 }
780
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 return 0;
782}
783
Jeff Laytonc359cf32007-11-16 22:22:06 +0000784/* extract the host portion of the UNC string */
785static char *
786extract_hostname(const char *unc)
787{
788 const char *src;
789 char *dst, *delim;
790 unsigned int len;
791
792 /* skip double chars at beginning of string */
793 /* BB: check validity of these bytes? */
794 src = unc + 2;
795
796 /* delimiter between hostname and sharename is always '\\' now */
797 delim = strchr(src, '\\');
798 if (!delim)
799 return ERR_PTR(-EINVAL);
800
801 len = delim - src;
802 dst = kmalloc((len + 1), GFP_KERNEL);
803 if (dst == NULL)
804 return ERR_PTR(-ENOMEM);
805
806 memcpy(dst, src, len);
807 dst[len] = '\0';
808
809 return dst;
810}
811
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812static int
Steve French50c2f752007-07-13 00:33:32 +0000813cifs_parse_mount_options(char *options, const char *devname,
814 struct smb_vol *vol)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815{
816 char *value;
817 char *data;
818 unsigned int temp_len, i, j;
819 char separator[2];
820
821 separator[0] = ',';
Steve French50c2f752007-07-13 00:33:32 +0000822 separator[1] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Linus Torvalds12e36b22006-10-13 08:09:29 -0700824 if (Local_System_Name[0] != 0)
Steve French50c2f752007-07-13 00:33:32 +0000825 memcpy(vol->source_rfc1001_name, Local_System_Name, 15);
Steve French2cd646a2006-09-28 19:43:08 +0000826 else {
Linus Torvalds12e36b22006-10-13 08:09:29 -0700827 char *nodename = utsname()->nodename;
Steve French50c2f752007-07-13 00:33:32 +0000828 int n = strnlen(nodename, 15);
829 memset(vol->source_rfc1001_name, 0x20, 15);
830 for (i = 0; i < n; i++) {
Steve French2cd646a2006-09-28 19:43:08 +0000831 /* does not have to be perfect mapping since field is
832 informational, only used for servers that do not support
833 port 445 and it can be overridden at mount time */
Linus Torvalds12e36b22006-10-13 08:09:29 -0700834 vol->source_rfc1001_name[i] = toupper(nodename[i]);
Steve French2cd646a2006-09-28 19:43:08 +0000835 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 }
837 vol->source_rfc1001_name[15] = 0;
Steve Frencha10faeb22005-08-22 21:38:31 -0700838 /* null target name indicates to use *SMBSERVR default called name
839 if we end up sending RFC1001 session initialize */
840 vol->target_rfc1001_name[0] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 vol->linux_uid = current->uid; /* current->euid instead? */
842 vol->linux_gid = current->gid;
843 vol->dir_mode = S_IRWXUGO;
844 /* 2767 perms indicate mandatory locking support */
Steve French7505e052007-11-01 18:03:01 +0000845 vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
847 /* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
Steve French4b18f2a2008-04-29 00:06:05 +0000848 vol->rw = true;
Jeremy Allisonac670552005-06-22 17:26:35 -0700849 /* default is always to request posix paths. */
850 vol->posix_paths = 1;
851
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 if (!options)
853 return 1;
854
Steve French50c2f752007-07-13 00:33:32 +0000855 if (strncmp(options, "sep=", 4) == 0) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000856 if (options[4] != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857 separator[0] = options[4];
858 options += 5;
859 } else {
Steve French467a8f82007-06-27 22:41:32 +0000860 cFYI(1, ("Null separator not allowed"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861 }
862 }
Steve French50c2f752007-07-13 00:33:32 +0000863
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 while ((data = strsep(&options, separator)) != NULL) {
865 if (!*data)
866 continue;
867 if ((value = strchr(data, '=')) != NULL)
868 *value++ = '\0';
869
Steve French50c2f752007-07-13 00:33:32 +0000870 /* Have to parse this before we parse for "user" */
871 if (strnicmp(data, "user_xattr", 10) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 vol->no_xattr = 0;
Steve French50c2f752007-07-13 00:33:32 +0000873 } else if (strnicmp(data, "nouser_xattr", 12) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 vol->no_xattr = 1;
875 } else if (strnicmp(data, "user", 4) == 0) {
Steve French4b952a92006-10-30 21:46:13 +0000876 if (!value) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 printk(KERN_WARNING
878 "CIFS: invalid or missing username\n");
879 return 1; /* needs_arg; */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000880 } else if (!*value) {
Steve French4b952a92006-10-30 21:46:13 +0000881 /* null user, ie anonymous, authentication */
882 vol->nullauth = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 }
884 if (strnlen(value, 200) < 200) {
885 vol->username = value;
886 } else {
887 printk(KERN_WARNING "CIFS: username too long\n");
888 return 1;
889 }
890 } else if (strnicmp(data, "pass", 4) == 0) {
891 if (!value) {
892 vol->password = NULL;
893 continue;
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000894 } else if (value[0] == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 /* check if string begins with double comma
896 since that would mean the password really
897 does start with a comma, and would not
898 indicate an empty string */
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000899 if (value[1] != separator[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 vol->password = NULL;
901 continue;
902 }
903 }
904 temp_len = strlen(value);
905 /* removed password length check, NTLM passwords
906 can be arbitrarily long */
907
Steve French50c2f752007-07-13 00:33:32 +0000908 /* if comma in password, the string will be
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909 prematurely null terminated. Commas in password are
910 specified across the cifs mount interface by a double
911 comma ie ,, and a comma used as in other cases ie ','
912 as a parameter delimiter/separator is single and due
913 to the strsep above is temporarily zeroed. */
914
915 /* NB: password legally can have multiple commas and
916 the only illegal character in a password is null */
917
Steve French50c2f752007-07-13 00:33:32 +0000918 if ((value[temp_len] == 0) &&
Steve French09d1db52005-04-28 22:41:08 -0700919 (value[temp_len+1] == separator[0])) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 /* reinsert comma */
921 value[temp_len] = separator[0];
Steve French50c2f752007-07-13 00:33:32 +0000922 temp_len += 2; /* move after second comma */
923 while (value[temp_len] != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 if (value[temp_len] == separator[0]) {
Steve French50c2f752007-07-13 00:33:32 +0000925 if (value[temp_len+1] ==
Steve French09d1db52005-04-28 22:41:08 -0700926 separator[0]) {
927 /* skip second comma */
928 temp_len++;
Steve French50c2f752007-07-13 00:33:32 +0000929 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 /* single comma indicating start
931 of next parm */
932 break;
933 }
934 }
935 temp_len++;
936 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000937 if (value[temp_len] == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 options = NULL;
939 } else {
940 value[temp_len] = 0;
941 /* point option to start of next parm */
942 options = value + temp_len + 1;
943 }
Steve French50c2f752007-07-13 00:33:32 +0000944 /* go from value to value + temp_len condensing
Linus Torvalds1da177e2005-04-16 15:20:36 -0700945 double commas to singles. Note that this ends up
946 allocating a few bytes too many, which is ok */
Pekka Enberge915fc42005-09-06 15:18:35 -0700947 vol->password = kzalloc(temp_len, GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000948 if (vol->password == NULL) {
Steve French50c2f752007-07-13 00:33:32 +0000949 printk(KERN_WARNING "CIFS: no memory "
950 "for password\n");
Steve French433dc242005-04-28 22:41:08 -0700951 return 1;
952 }
Steve French50c2f752007-07-13 00:33:32 +0000953 for (i = 0, j = 0; i < temp_len; i++, j++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 vol->password[j] = value[i];
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000955 if (value[i] == separator[0]
Steve French09d1db52005-04-28 22:41:08 -0700956 && value[i+1] == separator[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 /* skip second comma */
958 i++;
959 }
960 }
961 vol->password[j] = 0;
962 } else {
Pekka Enberge915fc42005-09-06 15:18:35 -0700963 vol->password = kzalloc(temp_len+1, GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +0000964 if (vol->password == NULL) {
Steve French50c2f752007-07-13 00:33:32 +0000965 printk(KERN_WARNING "CIFS: no memory "
966 "for password\n");
Steve French433dc242005-04-28 22:41:08 -0700967 return 1;
968 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 strcpy(vol->password, value);
970 }
971 } else if (strnicmp(data, "ip", 2) == 0) {
972 if (!value || !*value) {
973 vol->UNCip = NULL;
974 } else if (strnlen(value, 35) < 35) {
975 vol->UNCip = value;
976 } else {
Steve French50c2f752007-07-13 00:33:32 +0000977 printk(KERN_WARNING "CIFS: ip address "
978 "too long\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 return 1;
980 }
Steve French50c2f752007-07-13 00:33:32 +0000981 } else if (strnicmp(data, "sec", 3) == 0) {
982 if (!value || !*value) {
983 cERROR(1, ("no security value specified"));
984 continue;
985 } else if (strnicmp(value, "krb5i", 5) == 0) {
986 vol->secFlg |= CIFSSEC_MAY_KRB5 |
Steve French189acaa2006-06-23 02:33:48 +0000987 CIFSSEC_MUST_SIGN;
Steve Frenchbf820672005-12-01 22:32:42 -0800988 } else if (strnicmp(value, "krb5p", 5) == 0) {
Steve French50c2f752007-07-13 00:33:32 +0000989 /* vol->secFlg |= CIFSSEC_MUST_SEAL |
990 CIFSSEC_MAY_KRB5; */
991 cERROR(1, ("Krb5 cifs privacy not supported"));
Steve Frenchbf820672005-12-01 22:32:42 -0800992 return 1;
993 } else if (strnicmp(value, "krb5", 4) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000994 vol->secFlg |= CIFSSEC_MAY_KRB5;
Steve Frenchbf820672005-12-01 22:32:42 -0800995 } else if (strnicmp(value, "ntlmv2i", 7) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000996 vol->secFlg |= CIFSSEC_MAY_NTLMV2 |
Steve French189acaa2006-06-23 02:33:48 +0000997 CIFSSEC_MUST_SIGN;
Steve Frenchbf820672005-12-01 22:32:42 -0800998 } else if (strnicmp(value, "ntlmv2", 6) == 0) {
Steve French750d1152006-06-27 06:28:30 +0000999 vol->secFlg |= CIFSSEC_MAY_NTLMV2;
Steve Frenchbf820672005-12-01 22:32:42 -08001000 } else if (strnicmp(value, "ntlmi", 5) == 0) {
Steve French750d1152006-06-27 06:28:30 +00001001 vol->secFlg |= CIFSSEC_MAY_NTLM |
Steve French189acaa2006-06-23 02:33:48 +00001002 CIFSSEC_MUST_SIGN;
Steve Frenchbf820672005-12-01 22:32:42 -08001003 } else if (strnicmp(value, "ntlm", 4) == 0) {
1004 /* ntlm is default so can be turned off too */
Steve French750d1152006-06-27 06:28:30 +00001005 vol->secFlg |= CIFSSEC_MAY_NTLM;
Steve Frenchbf820672005-12-01 22:32:42 -08001006 } else if (strnicmp(value, "nontlm", 6) == 0) {
Steve French189acaa2006-06-23 02:33:48 +00001007 /* BB is there a better way to do this? */
Steve French750d1152006-06-27 06:28:30 +00001008 vol->secFlg |= CIFSSEC_MAY_NTLMV2;
Steve French189acaa2006-06-23 02:33:48 +00001009#ifdef CONFIG_CIFS_WEAK_PW_HASH
1010 } else if (strnicmp(value, "lanman", 6) == 0) {
Steve French50c2f752007-07-13 00:33:32 +00001011 vol->secFlg |= CIFSSEC_MAY_LANMAN;
Steve French189acaa2006-06-23 02:33:48 +00001012#endif
Steve Frenchbf820672005-12-01 22:32:42 -08001013 } else if (strnicmp(value, "none", 4) == 0) {
Steve French189acaa2006-06-23 02:33:48 +00001014 vol->nullauth = 1;
Steve French50c2f752007-07-13 00:33:32 +00001015 } else {
1016 cERROR(1, ("bad security option: %s", value));
1017 return 1;
1018 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 } else if ((strnicmp(data, "unc", 3) == 0)
1020 || (strnicmp(data, "target", 6) == 0)
1021 || (strnicmp(data, "path", 4) == 0)) {
1022 if (!value || !*value) {
Steve French50c2f752007-07-13 00:33:32 +00001023 printk(KERN_WARNING "CIFS: invalid path to "
1024 "network resource\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 return 1; /* needs_arg; */
1026 }
1027 if ((temp_len = strnlen(value, 300)) < 300) {
Steve French50c2f752007-07-13 00:33:32 +00001028 vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
Steve French4523cc32007-04-30 20:13:06 +00001029 if (vol->UNC == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 return 1;
Steve French50c2f752007-07-13 00:33:32 +00001031 strcpy(vol->UNC, value);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 if (strncmp(vol->UNC, "//", 2) == 0) {
1033 vol->UNC[0] = '\\';
1034 vol->UNC[1] = '\\';
Steve French50c2f752007-07-13 00:33:32 +00001035 } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 printk(KERN_WARNING
Steve French50c2f752007-07-13 00:33:32 +00001037 "CIFS: UNC Path does not begin "
1038 "with // or \\\\ \n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 return 1;
1040 }
1041 } else {
1042 printk(KERN_WARNING "CIFS: UNC name too long\n");
1043 return 1;
1044 }
1045 } else if ((strnicmp(data, "domain", 3) == 0)
1046 || (strnicmp(data, "workgroup", 5) == 0)) {
1047 if (!value || !*value) {
1048 printk(KERN_WARNING "CIFS: invalid domain name\n");
1049 return 1; /* needs_arg; */
1050 }
1051 /* BB are there cases in which a comma can be valid in
1052 a domain name and need special handling? */
Steve French39798772006-05-31 22:40:51 +00001053 if (strnlen(value, 256) < 256) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 vol->domainname = value;
1055 cFYI(1, ("Domain name set"));
1056 } else {
Steve French50c2f752007-07-13 00:33:32 +00001057 printk(KERN_WARNING "CIFS: domain name too "
1058 "long\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 return 1;
1060 }
Steve French50c2f752007-07-13 00:33:32 +00001061 } else if (strnicmp(data, "prefixpath", 10) == 0) {
1062 if (!value || !*value) {
1063 printk(KERN_WARNING
1064 "CIFS: invalid path prefix\n");
1065 return 1; /* needs_argument */
1066 }
1067 if ((temp_len = strnlen(value, 1024)) < 1024) {
Steve French4523cc32007-04-30 20:13:06 +00001068 if (value[0] != '/')
Steve French2fe87f02006-09-21 07:02:52 +00001069 temp_len++; /* missing leading slash */
Steve French50c2f752007-07-13 00:33:32 +00001070 vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
1071 if (vol->prepath == NULL)
1072 return 1;
Steve French4523cc32007-04-30 20:13:06 +00001073 if (value[0] != '/') {
Steve French2fe87f02006-09-21 07:02:52 +00001074 vol->prepath[0] = '/';
Steve French50c2f752007-07-13 00:33:32 +00001075 strcpy(vol->prepath+1, value);
Steve French2fe87f02006-09-21 07:02:52 +00001076 } else
Steve French50c2f752007-07-13 00:33:32 +00001077 strcpy(vol->prepath, value);
1078 cFYI(1, ("prefix path %s", vol->prepath));
1079 } else {
1080 printk(KERN_WARNING "CIFS: prefix too long\n");
1081 return 1;
1082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 } else if (strnicmp(data, "iocharset", 9) == 0) {
1084 if (!value || !*value) {
Steve French63135e02007-07-17 17:34:02 +00001085 printk(KERN_WARNING "CIFS: invalid iocharset "
1086 "specified\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 return 1; /* needs_arg; */
1088 }
1089 if (strnlen(value, 65) < 65) {
Steve French50c2f752007-07-13 00:33:32 +00001090 if (strnicmp(value, "default", 7))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 vol->iocharset = value;
Steve French50c2f752007-07-13 00:33:32 +00001092 /* if iocharset not set then load_nls_default
1093 is used by caller */
1094 cFYI(1, ("iocharset set to %s", value));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 } else {
Steve French63135e02007-07-17 17:34:02 +00001096 printk(KERN_WARNING "CIFS: iocharset name "
1097 "too long.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 return 1;
1099 }
1100 } else if (strnicmp(data, "uid", 3) == 0) {
1101 if (value && *value) {
1102 vol->linux_uid =
1103 simple_strtoul(value, &value, 0);
Steve French4523cc32007-04-30 20:13:06 +00001104 vol->override_uid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 }
1106 } else if (strnicmp(data, "gid", 3) == 0) {
1107 if (value && *value) {
1108 vol->linux_gid =
1109 simple_strtoul(value, &value, 0);
Steve French4523cc32007-04-30 20:13:06 +00001110 vol->override_gid = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 }
1112 } else if (strnicmp(data, "file_mode", 4) == 0) {
1113 if (value && *value) {
1114 vol->file_mode =
1115 simple_strtoul(value, &value, 0);
1116 }
1117 } else if (strnicmp(data, "dir_mode", 4) == 0) {
1118 if (value && *value) {
1119 vol->dir_mode =
1120 simple_strtoul(value, &value, 0);
1121 }
1122 } else if (strnicmp(data, "dirmode", 4) == 0) {
1123 if (value && *value) {
1124 vol->dir_mode =
1125 simple_strtoul(value, &value, 0);
1126 }
1127 } else if (strnicmp(data, "port", 4) == 0) {
1128 if (value && *value) {
1129 vol->port =
1130 simple_strtoul(value, &value, 0);
1131 }
1132 } else if (strnicmp(data, "rsize", 5) == 0) {
1133 if (value && *value) {
1134 vol->rsize =
1135 simple_strtoul(value, &value, 0);
1136 }
1137 } else if (strnicmp(data, "wsize", 5) == 0) {
1138 if (value && *value) {
1139 vol->wsize =
1140 simple_strtoul(value, &value, 0);
1141 }
1142 } else if (strnicmp(data, "sockopt", 5) == 0) {
1143 if (value && *value) {
1144 vol->sockopt =
1145 simple_strtoul(value, &value, 0);
1146 }
1147 } else if (strnicmp(data, "netbiosname", 4) == 0) {
1148 if (!value || !*value || (*value == ' ')) {
Steve French63135e02007-07-17 17:34:02 +00001149 cFYI(1, ("invalid (empty) netbiosname"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 } else {
Steve French50c2f752007-07-13 00:33:32 +00001151 memset(vol->source_rfc1001_name, 0x20, 15);
1152 for (i = 0; i < 15; i++) {
1153 /* BB are there cases in which a comma can be
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 valid in this workstation netbios name (and need
1155 special handling)? */
1156
1157 /* We do not uppercase netbiosname for user */
Steve French50c2f752007-07-13 00:33:32 +00001158 if (value[i] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
Steve French50c2f752007-07-13 00:33:32 +00001160 else
1161 vol->source_rfc1001_name[i] =
1162 value[i];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 }
1164 /* The string has 16th byte zero still from
1165 set at top of the function */
Steve French50c2f752007-07-13 00:33:32 +00001166 if ((i == 15) && (value[i] != 0))
1167 printk(KERN_WARNING "CIFS: netbiosname"
1168 " longer than 15 truncated.\n");
Steve Frencha10faeb22005-08-22 21:38:31 -07001169 }
1170 } else if (strnicmp(data, "servern", 7) == 0) {
1171 /* servernetbiosname specified override *SMBSERVER */
1172 if (!value || !*value || (*value == ' ')) {
Steve French467a8f82007-06-27 22:41:32 +00001173 cFYI(1, ("empty server netbiosname specified"));
Steve Frencha10faeb22005-08-22 21:38:31 -07001174 } else {
1175 /* last byte, type, is 0x20 for servr type */
Steve French50c2f752007-07-13 00:33:32 +00001176 memset(vol->target_rfc1001_name, 0x20, 16);
Steve Frencha10faeb22005-08-22 21:38:31 -07001177
Steve French50c2f752007-07-13 00:33:32 +00001178 for (i = 0; i < 15; i++) {
Steve Frencha10faeb22005-08-22 21:38:31 -07001179 /* BB are there cases in which a comma can be
Steve French50c2f752007-07-13 00:33:32 +00001180 valid in this workstation netbios name
1181 (and need special handling)? */
Steve Frencha10faeb22005-08-22 21:38:31 -07001182
Steve French50c2f752007-07-13 00:33:32 +00001183 /* user or mount helper must uppercase
1184 the netbiosname */
1185 if (value[i] == 0)
Steve Frencha10faeb22005-08-22 21:38:31 -07001186 break;
1187 else
Steve French50c2f752007-07-13 00:33:32 +00001188 vol->target_rfc1001_name[i] =
1189 value[i];
Steve Frencha10faeb22005-08-22 21:38:31 -07001190 }
1191 /* The string has 16th byte zero still from
1192 set at top of the function */
Steve French50c2f752007-07-13 00:33:32 +00001193 if ((i == 15) && (value[i] != 0))
1194 printk(KERN_WARNING "CIFS: server net"
1195 "biosname longer than 15 truncated.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196 }
1197 } else if (strnicmp(data, "credentials", 4) == 0) {
1198 /* ignore */
1199 } else if (strnicmp(data, "version", 3) == 0) {
1200 /* ignore */
Steve French50c2f752007-07-13 00:33:32 +00001201 } else if (strnicmp(data, "guest", 5) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 /* ignore */
1203 } else if (strnicmp(data, "rw", 2) == 0) {
Steve French4b18f2a2008-04-29 00:06:05 +00001204 vol->rw = true;
Steve Frenchedf1ae42008-10-29 00:47:57 +00001205 } else if (strnicmp(data, "noblocksend", 11) == 0) {
1206 vol->noblocksnd = 1;
1207 } else if (strnicmp(data, "noautotune", 10) == 0) {
1208 vol->noautotune = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 } else if ((strnicmp(data, "suid", 4) == 0) ||
1210 (strnicmp(data, "nosuid", 6) == 0) ||
1211 (strnicmp(data, "exec", 4) == 0) ||
1212 (strnicmp(data, "noexec", 6) == 0) ||
1213 (strnicmp(data, "nodev", 5) == 0) ||
1214 (strnicmp(data, "noauto", 6) == 0) ||
1215 (strnicmp(data, "dev", 3) == 0)) {
1216 /* The mount tool or mount.cifs helper (if present)
Steve French50c2f752007-07-13 00:33:32 +00001217 uses these opts to set flags, and the flags are read
1218 by the kernel vfs layer before we get here (ie
1219 before read super) so there is no point trying to
1220 parse these options again and set anything and it
1221 is ok to just ignore them */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 continue;
1223 } else if (strnicmp(data, "ro", 2) == 0) {
Steve French4b18f2a2008-04-29 00:06:05 +00001224 vol->rw = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 } else if (strnicmp(data, "hard", 4) == 0) {
1226 vol->retry = 1;
1227 } else if (strnicmp(data, "soft", 4) == 0) {
1228 vol->retry = 0;
1229 } else if (strnicmp(data, "perm", 4) == 0) {
1230 vol->noperm = 0;
1231 } else if (strnicmp(data, "noperm", 6) == 0) {
1232 vol->noperm = 1;
Steve French6a0b4822005-04-28 22:41:05 -07001233 } else if (strnicmp(data, "mapchars", 8) == 0) {
1234 vol->remap = 1;
1235 } else if (strnicmp(data, "nomapchars", 10) == 0) {
1236 vol->remap = 0;
Steve French50c2f752007-07-13 00:33:32 +00001237 } else if (strnicmp(data, "sfu", 3) == 0) {
1238 vol->sfu_emul = 1;
1239 } else if (strnicmp(data, "nosfu", 5) == 0) {
1240 vol->sfu_emul = 0;
Steve French2c1b8612008-10-16 18:35:21 +00001241 } else if (strnicmp(data, "nodfs", 5) == 0) {
1242 vol->nodfs = 1;
Jeremy Allisonac670552005-06-22 17:26:35 -07001243 } else if (strnicmp(data, "posixpaths", 10) == 0) {
1244 vol->posix_paths = 1;
1245 } else if (strnicmp(data, "noposixpaths", 12) == 0) {
1246 vol->posix_paths = 0;
Steve Frenchc18c8422007-07-18 23:21:09 +00001247 } else if (strnicmp(data, "nounix", 6) == 0) {
1248 vol->no_linux_ext = 1;
1249 } else if (strnicmp(data, "nolinux", 7) == 0) {
1250 vol->no_linux_ext = 1;
Steve French50c2f752007-07-13 00:33:32 +00001251 } else if ((strnicmp(data, "nocase", 6) == 0) ||
Steve Frencha10faeb22005-08-22 21:38:31 -07001252 (strnicmp(data, "ignorecase", 10) == 0)) {
Steve French50c2f752007-07-13 00:33:32 +00001253 vol->nocase = 1;
Steve Frenchc46fa8a2005-08-18 20:49:57 -07001254 } else if (strnicmp(data, "brl", 3) == 0) {
1255 vol->nobrl = 0;
Steve French50c2f752007-07-13 00:33:32 +00001256 } else if ((strnicmp(data, "nobrl", 5) == 0) ||
Steve French1c955182005-08-30 20:58:07 -07001257 (strnicmp(data, "nolock", 6) == 0)) {
Steve Frenchc46fa8a2005-08-18 20:49:57 -07001258 vol->nobrl = 1;
Steve Frenchd3485d32005-08-19 11:04:29 -07001259 /* turn off mandatory locking in mode
1260 if remote locking is turned off since the
1261 local vfs will do advisory */
Steve French50c2f752007-07-13 00:33:32 +00001262 if (vol->file_mode ==
1263 (S_IALLUGO & ~(S_ISUID | S_IXGRP)))
Steve Frenchd3485d32005-08-19 11:04:29 -07001264 vol->file_mode = S_IALLUGO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 } else if (strnicmp(data, "setuids", 7) == 0) {
1266 vol->setuids = 1;
1267 } else if (strnicmp(data, "nosetuids", 9) == 0) {
1268 vol->setuids = 0;
Jeff Laytond0a9c072008-05-12 22:23:49 +00001269 } else if (strnicmp(data, "dynperm", 7) == 0) {
1270 vol->dynperm = true;
1271 } else if (strnicmp(data, "nodynperm", 9) == 0) {
1272 vol->dynperm = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 } else if (strnicmp(data, "nohard", 6) == 0) {
1274 vol->retry = 0;
1275 } else if (strnicmp(data, "nosoft", 6) == 0) {
1276 vol->retry = 1;
1277 } else if (strnicmp(data, "nointr", 6) == 0) {
1278 vol->intr = 0;
1279 } else if (strnicmp(data, "intr", 4) == 0) {
1280 vol->intr = 1;
Steve French50c2f752007-07-13 00:33:32 +00001281 } else if (strnicmp(data, "serverino", 7) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282 vol->server_ino = 1;
Steve French50c2f752007-07-13 00:33:32 +00001283 } else if (strnicmp(data, "noserverino", 9) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 vol->server_ino = 0;
Steve French50c2f752007-07-13 00:33:32 +00001285 } else if (strnicmp(data, "cifsacl", 7) == 0) {
Steve French0a4b92c2006-01-12 15:44:21 -08001286 vol->cifs_acl = 1;
1287 } else if (strnicmp(data, "nocifsacl", 9) == 0) {
1288 vol->cifs_acl = 0;
Steve French50c2f752007-07-13 00:33:32 +00001289 } else if (strnicmp(data, "acl", 3) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 vol->no_psx_acl = 0;
Steve French50c2f752007-07-13 00:33:32 +00001291 } else if (strnicmp(data, "noacl", 5) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 vol->no_psx_acl = 1;
Steve French84210e92008-10-23 04:42:37 +00001293#ifdef CONFIG_CIFS_EXPERIMENTAL
1294 } else if (strnicmp(data, "locallease", 6) == 0) {
1295 vol->local_lease = 1;
1296#endif
Steve French50c2f752007-07-13 00:33:32 +00001297 } else if (strnicmp(data, "sign", 4) == 0) {
Steve French750d1152006-06-27 06:28:30 +00001298 vol->secFlg |= CIFSSEC_MUST_SIGN;
Steve French95b1cb92008-05-15 16:44:38 +00001299 } else if (strnicmp(data, "seal", 4) == 0) {
1300 /* we do not do the following in secFlags because seal
1301 is a per tree connection (mount) not a per socket
1302 or per-smb connection option in the protocol */
1303 /* vol->secFlg |= CIFSSEC_MUST_SEAL; */
1304 vol->seal = 1;
Steve French50c2f752007-07-13 00:33:32 +00001305 } else if (strnicmp(data, "direct", 6) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 vol->direct_io = 1;
Steve French50c2f752007-07-13 00:33:32 +00001307 } else if (strnicmp(data, "forcedirectio", 13) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 vol->direct_io = 1;
Steve French50c2f752007-07-13 00:33:32 +00001309 } else if (strnicmp(data, "in6_addr", 8) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 if (!value || !*value) {
1311 vol->in6_addr = NULL;
1312 } else if (strnlen(value, 49) == 48) {
1313 vol->in6_addr = value;
1314 } else {
Steve French50c2f752007-07-13 00:33:32 +00001315 printk(KERN_WARNING "CIFS: ip v6 address not "
1316 "48 characters long\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 return 1;
1318 }
1319 } else if (strnicmp(data, "noac", 4) == 0) {
Steve French50c2f752007-07-13 00:33:32 +00001320 printk(KERN_WARNING "CIFS: Mount option noac not "
1321 "supported. Instead set "
1322 "/proc/fs/cifs/LookupCacheEnabled to 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323 } else
Steve French50c2f752007-07-13 00:33:32 +00001324 printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
1325 data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 }
1327 if (vol->UNC == NULL) {
Steve French4523cc32007-04-30 20:13:06 +00001328 if (devname == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001329 printk(KERN_WARNING "CIFS: Missing UNC name for mount "
1330 "target\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 return 1;
1332 }
1333 if ((temp_len = strnlen(devname, 300)) < 300) {
Steve French50c2f752007-07-13 00:33:32 +00001334 vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
Steve French4523cc32007-04-30 20:13:06 +00001335 if (vol->UNC == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 return 1;
Steve French50c2f752007-07-13 00:33:32 +00001337 strcpy(vol->UNC, devname);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 if (strncmp(vol->UNC, "//", 2) == 0) {
1339 vol->UNC[0] = '\\';
1340 vol->UNC[1] = '\\';
1341 } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
Steve French50c2f752007-07-13 00:33:32 +00001342 printk(KERN_WARNING "CIFS: UNC Path does not "
1343 "begin with // or \\\\ \n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344 return 1;
1345 }
Igor Mammedov7c5e6282008-05-08 20:48:42 +00001346 value = strpbrk(vol->UNC+2, "/\\");
1347 if (value)
1348 *value = '\\';
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 } else {
1350 printk(KERN_WARNING "CIFS: UNC name too long\n");
1351 return 1;
1352 }
1353 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001354 if (vol->UNCip == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355 vol->UNCip = &vol->UNC[2];
1356
1357 return 0;
1358}
1359
1360static struct cifsSesInfo *
Steve French50c2f752007-07-13 00:33:32 +00001361cifs_find_tcp_session(struct in_addr *target_ip_addr,
Cyrill Gorcunov1b20d672008-05-09 18:17:21 +00001362 struct in6_addr *target_ip6_addr,
1363 char *userName, struct TCP_Server_Info **psrvTcp)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364{
1365 struct list_head *tmp;
1366 struct cifsSesInfo *ses;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Cyrill Gorcunov1b20d672008-05-09 18:17:21 +00001368 *psrvTcp = NULL;
1369
1370 read_lock(&GlobalSMBSeslock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371 list_for_each(tmp, &GlobalSMBSessionList) {
1372 ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
Cyrill Gorcunov1b20d672008-05-09 18:17:21 +00001373 if (!ses->server)
1374 continue;
Steve French50c2f752007-07-13 00:33:32 +00001375
Cyrill Gorcunov1b20d672008-05-09 18:17:21 +00001376 if (target_ip_addr &&
1377 ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr)
1378 continue;
1379 else if (target_ip6_addr &&
1380 memcmp(&ses->server->addr.sockAddr6.sin6_addr,
1381 target_ip6_addr, sizeof(*target_ip6_addr)))
1382 continue;
Jeff Layton02eadef2008-05-09 21:26:11 +00001383 /* BB lock server and tcp session; increment use count here?? */
Steve French50c2f752007-07-13 00:33:32 +00001384
Cyrill Gorcunov1b20d672008-05-09 18:17:21 +00001385 /* found a match on the TCP session */
1386 *psrvTcp = ses->server;
1387
1388 /* BB check if reconnection needed */
1389 if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) {
1390 read_unlock(&GlobalSMBSeslock);
1391 /* Found exact match on both TCP and
1392 SMB sessions */
1393 return ses;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 }
1395 /* else tcp and smb sessions need reconnection */
1396 }
1397 read_unlock(&GlobalSMBSeslock);
Cyrill Gorcunov1b20d672008-05-09 18:17:21 +00001398
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399 return NULL;
1400}
1401
1402static struct cifsTconInfo *
1403find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
1404{
1405 struct list_head *tmp;
1406 struct cifsTconInfo *tcon;
Steve Frenchdea570e02008-05-06 22:05:51 +00001407 __be32 old_ip;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
1409 read_lock(&GlobalSMBSeslock);
Steve Frenchdea570e02008-05-06 22:05:51 +00001410
Linus Torvalds1da177e2005-04-16 15:20:36 -07001411 list_for_each(tmp, &GlobalTreeConnectionList) {
Steve Frenche466e482006-08-15 13:07:18 +00001412 cFYI(1, ("Next tcon"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
Steve Frenchdea570e02008-05-06 22:05:51 +00001414 if (!tcon->ses || !tcon->ses->server)
1415 continue;
1416
1417 old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr;
1418 cFYI(1, ("old ip addr: %x == new ip %x ?",
1419 old_ip, new_target_ip_addr));
1420
1421 if (old_ip != new_target_ip_addr)
1422 continue;
1423
1424 /* BB lock tcon, server, tcp session and increment use count? */
1425 /* found a match on the TCP session */
1426 /* BB check if reconnection needed */
1427 cFYI(1, ("IP match, old UNC: %s new: %s",
1428 tcon->treeName, uncName));
1429
1430 if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
1431 continue;
1432
1433 cFYI(1, ("and old usr: %s new: %s",
1434 tcon->treeName, uncName));
1435
1436 if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
1437 continue;
1438
1439 /* matched smb session (user name) */
1440 read_unlock(&GlobalSMBSeslock);
1441 return tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442 }
Steve Frenchdea570e02008-05-06 22:05:51 +00001443
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 read_unlock(&GlobalSMBSeslock);
1445 return NULL;
1446}
1447
1448int
Steve French50c2f752007-07-13 00:33:32 +00001449get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
1450 const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
Steve French366781c2008-01-25 10:12:41 +00001451 struct dfs_info3_param **preferrals, int remap)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452{
1453 char *temp_unc;
1454 int rc = 0;
1455
1456 *pnum_referrals = 0;
Steve French366781c2008-01-25 10:12:41 +00001457 *preferrals = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458
1459 if (pSesInfo->ipc_tid == 0) {
1460 temp_unc = kmalloc(2 /* for slashes */ +
Steve French50c2f752007-07-13 00:33:32 +00001461 strnlen(pSesInfo->serverName,
1462 SERVER_NAME_LEN_WITH_NULL * 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 + 1 + 4 /* slash IPC$ */ + 2,
1464 GFP_KERNEL);
1465 if (temp_unc == NULL)
1466 return -ENOMEM;
1467 temp_unc[0] = '\\';
1468 temp_unc[1] = '\\';
1469 strcpy(temp_unc + 2, pSesInfo->serverName);
1470 strcpy(temp_unc + 2 + strlen(pSesInfo->serverName), "\\IPC$");
1471 rc = CIFSTCon(xid, pSesInfo, temp_unc, NULL, nls_codepage);
1472 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00001473 ("CIFS Tcon rc = %d ipc_tid = %d", rc, pSesInfo->ipc_tid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 kfree(temp_unc);
1475 }
1476 if (rc == 0)
Steve Frenchc2cf07d2008-05-15 06:20:02 +00001477 rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
Steve French737b7582005-04-28 22:41:06 -07001478 pnum_referrals, nls_codepage, remap);
Steve French366781c2008-01-25 10:12:41 +00001479 /* BB map targetUNCs to dfs_info3 structures, here or
1480 in CIFSGetDFSRefer BB */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
1482 return rc;
1483}
1484
Jeff Layton09e50d52008-07-23 10:11:19 -04001485#ifdef CONFIG_DEBUG_LOCK_ALLOC
1486static struct lock_class_key cifs_key[2];
1487static struct lock_class_key cifs_slock_key[2];
1488
1489static inline void
1490cifs_reclassify_socket4(struct socket *sock)
1491{
1492 struct sock *sk = sock->sk;
1493 BUG_ON(sock_owned_by_user(sk));
1494 sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
1495 &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
1496}
1497
1498static inline void
1499cifs_reclassify_socket6(struct socket *sock)
1500{
1501 struct sock *sk = sock->sk;
1502 BUG_ON(sock_owned_by_user(sk));
1503 sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
1504 &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
1505}
1506#else
1507static inline void
1508cifs_reclassify_socket4(struct socket *sock)
1509{
1510}
1511
1512static inline void
1513cifs_reclassify_socket6(struct socket *sock)
1514{
1515}
1516#endif
1517
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518/* See RFC1001 section 14 on representation of Netbios names */
Steve French50c2f752007-07-13 00:33:32 +00001519static void rfc1002mangle(char *target, char *source, unsigned int length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520{
Steve French50c2f752007-07-13 00:33:32 +00001521 unsigned int i, j;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
Steve French50c2f752007-07-13 00:33:32 +00001523 for (i = 0, j = 0; i < (length); i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 /* mask a nibble at a time and encode */
1525 target[j] = 'A' + (0x0F & (source[i] >> 4));
1526 target[j+1] = 'A' + (0x0F & source[i]);
Steve French50c2f752007-07-13 00:33:32 +00001527 j += 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528 }
1529
1530}
1531
1532
1533static int
Steve French50c2f752007-07-13 00:33:32 +00001534ipv4_connect(struct sockaddr_in *psin_server, struct socket **csocket,
Steve Frenchedf1ae42008-10-29 00:47:57 +00001535 char *netbios_name, char *target_name,
1536 bool noblocksnd, bool noautotune)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001537{
1538 int rc = 0;
1539 int connected = 0;
1540 __be16 orig_port = 0;
1541
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001542 if (*csocket == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001543 rc = sock_create_kern(PF_INET, SOCK_STREAM,
1544 IPPROTO_TCP, csocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 if (rc < 0) {
Steve French50c2f752007-07-13 00:33:32 +00001546 cERROR(1, ("Error %d creating socket", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 *csocket = NULL;
1548 return rc;
1549 } else {
1550 /* BB other socket options to set KEEPALIVE, NODELAY? */
Steve French467a8f82007-06-27 22:41:32 +00001551 cFYI(1, ("Socket created"));
Steve French50c2f752007-07-13 00:33:32 +00001552 (*csocket)->sk->sk_allocation = GFP_NOFS;
Jeff Layton09e50d52008-07-23 10:11:19 -04001553 cifs_reclassify_socket4(*csocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 }
1555 }
1556
1557 psin_server->sin_family = AF_INET;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001558 if (psin_server->sin_port) { /* user overrode default port */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559 rc = (*csocket)->ops->connect(*csocket,
1560 (struct sockaddr *) psin_server,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00001561 sizeof(struct sockaddr_in), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562 if (rc >= 0)
1563 connected = 1;
Steve French50c2f752007-07-13 00:33:32 +00001564 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001566 if (!connected) {
Steve French50c2f752007-07-13 00:33:32 +00001567 /* save original port so we can retry user specified port
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 later if fall back ports fail this time */
1569 orig_port = psin_server->sin_port;
1570
1571 /* do not retry on the same port we just failed on */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001572 if (psin_server->sin_port != htons(CIFS_PORT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573 psin_server->sin_port = htons(CIFS_PORT);
1574
1575 rc = (*csocket)->ops->connect(*csocket,
1576 (struct sockaddr *) psin_server,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00001577 sizeof(struct sockaddr_in), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 if (rc >= 0)
1579 connected = 1;
1580 }
1581 }
1582 if (!connected) {
1583 psin_server->sin_port = htons(RFC1001_PORT);
1584 rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
Steve French50c2f752007-07-13 00:33:32 +00001585 psin_server,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00001586 sizeof(struct sockaddr_in), 0);
Steve French50c2f752007-07-13 00:33:32 +00001587 if (rc >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588 connected = 1;
1589 }
1590
1591 /* give up here - unless we want to retry on different
1592 protocol families some day */
1593 if (!connected) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001594 if (orig_port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 psin_server->sin_port = orig_port;
Steve French50c2f752007-07-13 00:33:32 +00001596 cFYI(1, ("Error %d connecting to server via ipv4", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001597 sock_release(*csocket);
1598 *csocket = NULL;
1599 return rc;
1600 }
Steve French50c2f752007-07-13 00:33:32 +00001601 /* Eventually check for other socket options to change from
1602 the default. sock_setsockopt not used because it expects
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 user space buffer */
Steve French50c2f752007-07-13 00:33:32 +00001604 cFYI(1, ("sndbuf %d rcvbuf %d rcvtimeo 0x%lx",
1605 (*csocket)->sk->sk_sndbuf,
Steve Frenchb387eae2005-10-10 14:21:15 -07001606 (*csocket)->sk->sk_rcvbuf, (*csocket)->sk->sk_rcvtimeo));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001607 (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
Steve Frenchedf1ae42008-10-29 00:47:57 +00001608 if (!noblocksnd)
1609 (*csocket)->sk->sk_sndtimeo = 3 * HZ;
1610
Steve Frenchb387eae2005-10-10 14:21:15 -07001611 /* make the bufsizes depend on wsize/rsize and max requests */
Steve Frenchedf1ae42008-10-29 00:47:57 +00001612 if (noautotune) {
1613 if ((*csocket)->sk->sk_sndbuf < (200 * 1024))
1614 (*csocket)->sk->sk_sndbuf = 200 * 1024;
1615 if ((*csocket)->sk->sk_rcvbuf < (140 * 1024))
1616 (*csocket)->sk->sk_rcvbuf = 140 * 1024;
1617 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
1619 /* send RFC1001 sessinit */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001620 if (psin_server->sin_port == htons(RFC1001_PORT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 /* some servers require RFC1001 sessinit before sending
Steve French50c2f752007-07-13 00:33:32 +00001622 negprot - BB check reconnection in case where second
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 sessinit is sent but no second negprot */
Steve French50c2f752007-07-13 00:33:32 +00001624 struct rfc1002_session_packet *ses_init_buf;
1625 struct smb_hdr *smb_buf;
1626 ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
1627 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001628 if (ses_init_buf) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 ses_init_buf->trailer.session_req.called_len = 32;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001630 if (target_name && (target_name[0] != 0)) {
Steve Frencha10faeb22005-08-22 21:38:31 -07001631 rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
1632 target_name, 16);
1633 } else {
1634 rfc1002mangle(ses_init_buf->trailer.session_req.called_name,
Steve French50c2f752007-07-13 00:33:32 +00001635 DEFAULT_CIFS_CALLED_NAME, 16);
Steve Frencha10faeb22005-08-22 21:38:31 -07001636 }
1637
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 ses_init_buf->trailer.session_req.calling_len = 32;
1639 /* calling name ends in null (byte 16) from old smb
1640 convention. */
Steve French50c2f752007-07-13 00:33:32 +00001641 if (netbios_name && (netbios_name[0] != 0)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
Steve French50c2f752007-07-13 00:33:32 +00001643 netbios_name, 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 } else {
1645 rfc1002mangle(ses_init_buf->trailer.session_req.calling_name,
Steve French50c2f752007-07-13 00:33:32 +00001646 "LINUX_CIFS_CLNT", 16);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 }
1648 ses_init_buf->trailer.session_req.scope1 = 0;
1649 ses_init_buf->trailer.session_req.scope2 = 0;
1650 smb_buf = (struct smb_hdr *)ses_init_buf;
1651 /* sizeof RFC1002_SESSION_REQUEST with no scope */
1652 smb_buf->smb_buf_length = 0x81000044;
1653 rc = smb_send(*csocket, smb_buf, 0x44,
Steve Frenchedf1ae42008-10-29 00:47:57 +00001654 (struct sockaddr *)psin_server, noblocksnd);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001655 kfree(ses_init_buf);
Steve French50c2f752007-07-13 00:33:32 +00001656 msleep(1); /* RFC1001 layer in at least one server
Steve French083d3a22006-03-03 09:53:36 +00001657 requires very short break before negprot
1658 presumably because not expecting negprot
1659 to follow so fast. This is a simple
Steve French50c2f752007-07-13 00:33:32 +00001660 solution that works without
Steve French083d3a22006-03-03 09:53:36 +00001661 complicating the code and causes no
1662 significant slowing down on mount
1663 for everyone else */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664 }
Steve French50c2f752007-07-13 00:33:32 +00001665 /* else the negprot may still work without this
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 even though malloc failed */
Steve French50c2f752007-07-13 00:33:32 +00001667
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 }
Steve French50c2f752007-07-13 00:33:32 +00001669
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 return rc;
1671}
1672
1673static int
Steve Frenchedf1ae42008-10-29 00:47:57 +00001674ipv6_connect(struct sockaddr_in6 *psin_server, struct socket **csocket,
1675 bool noblocksnd)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676{
1677 int rc = 0;
1678 int connected = 0;
1679 __be16 orig_port = 0;
1680
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001681 if (*csocket == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001682 rc = sock_create_kern(PF_INET6, SOCK_STREAM,
1683 IPPROTO_TCP, csocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684 if (rc < 0) {
Steve French50c2f752007-07-13 00:33:32 +00001685 cERROR(1, ("Error %d creating ipv6 socket", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686 *csocket = NULL;
1687 return rc;
1688 } else {
1689 /* BB other socket options to set KEEPALIVE, NODELAY? */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001690 cFYI(1, ("ipv6 Socket created"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 (*csocket)->sk->sk_allocation = GFP_NOFS;
Jeff Layton09e50d52008-07-23 10:11:19 -04001692 cifs_reclassify_socket6(*csocket);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 }
1694 }
1695
1696 psin_server->sin6_family = AF_INET6;
1697
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001698 if (psin_server->sin6_port) { /* user overrode default port */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 rc = (*csocket)->ops->connect(*csocket,
1700 (struct sockaddr *) psin_server,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00001701 sizeof(struct sockaddr_in6), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 if (rc >= 0)
1703 connected = 1;
Steve French50c2f752007-07-13 00:33:32 +00001704 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001706 if (!connected) {
Steve French50c2f752007-07-13 00:33:32 +00001707 /* save original port so we can retry user specified port
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 later if fall back ports fail this time */
1709
1710 orig_port = psin_server->sin6_port;
1711 /* do not retry on the same port we just failed on */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001712 if (psin_server->sin6_port != htons(CIFS_PORT)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713 psin_server->sin6_port = htons(CIFS_PORT);
1714
1715 rc = (*csocket)->ops->connect(*csocket,
1716 (struct sockaddr *) psin_server,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00001717 sizeof(struct sockaddr_in6), 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001718 if (rc >= 0)
1719 connected = 1;
1720 }
1721 }
1722 if (!connected) {
1723 psin_server->sin6_port = htons(RFC1001_PORT);
1724 rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00001725 psin_server, sizeof(struct sockaddr_in6), 0);
Steve French50c2f752007-07-13 00:33:32 +00001726 if (rc >= 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001727 connected = 1;
1728 }
1729
1730 /* give up here - unless we want to retry on different
1731 protocol families some day */
1732 if (!connected) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001733 if (orig_port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 psin_server->sin6_port = orig_port;
Steve French50c2f752007-07-13 00:33:32 +00001735 cFYI(1, ("Error %d connecting to server via ipv6", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 sock_release(*csocket);
1737 *csocket = NULL;
1738 return rc;
1739 }
Steve French50c2f752007-07-13 00:33:32 +00001740 /* Eventually check for other socket options to change from
1741 the default. sock_setsockopt not used because it expects
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 user space buffer */
1743 (*csocket)->sk->sk_rcvtimeo = 7 * HZ;
Steve Frenchedf1ae42008-10-29 00:47:57 +00001744 if (!noblocksnd)
1745 (*csocket)->sk->sk_sndtimeo = 3 * HZ;
1746
Steve French50c2f752007-07-13 00:33:32 +00001747
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748 return rc;
1749}
1750
Steve French50c2f752007-07-13 00:33:32 +00001751void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon,
1752 struct super_block *sb, struct smb_vol *vol_info)
Steve French8af18972007-02-14 04:42:51 +00001753{
1754 /* if we are reconnecting then should we check to see if
1755 * any requested capabilities changed locally e.g. via
1756 * remount but we can not do much about it here
1757 * if they have (even if we could detect it by the following)
1758 * Perhaps we could add a backpointer to array of sb from tcon
1759 * or if we change to make all sb to same share the same
1760 * sb as NFS - then we only have one backpointer to sb.
1761 * What if we wanted to mount the server share twice once with
1762 * and once without posixacls or posix paths? */
1763 __u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
Steve French50c2f752007-07-13 00:33:32 +00001764
Steve Frenchc18c8422007-07-18 23:21:09 +00001765 if (vol_info && vol_info->no_linux_ext) {
1766 tcon->fsUnixInfo.Capability = 0;
1767 tcon->unix_ext = 0; /* Unix Extensions disabled */
1768 cFYI(1, ("Linux protocol extensions disabled"));
1769 return;
1770 } else if (vol_info)
1771 tcon->unix_ext = 1; /* Unix Extensions supported */
1772
1773 if (tcon->unix_ext == 0) {
1774 cFYI(1, ("Unix extensions disabled so not set on reconnect"));
1775 return;
1776 }
Steve French50c2f752007-07-13 00:33:32 +00001777
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001778 if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
Steve French8af18972007-02-14 04:42:51 +00001779 __u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
Steve French50c2f752007-07-13 00:33:32 +00001780
Steve French8af18972007-02-14 04:42:51 +00001781 /* check for reconnect case in which we do not
1782 want to change the mount behavior if we can avoid it */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001783 if (vol_info == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001784 /* turn off POSIX ACL and PATHNAMES if not set
Steve French8af18972007-02-14 04:42:51 +00001785 originally at mount time */
1786 if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
1787 cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
Igor Mammedov11b6d642008-02-15 19:06:04 +00001788 if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
1789 if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
1790 cERROR(1, ("POSIXPATH support change"));
Steve French8af18972007-02-14 04:42:51 +00001791 cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
Igor Mammedov11b6d642008-02-15 19:06:04 +00001792 } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
1793 cERROR(1, ("possible reconnect error"));
1794 cERROR(1,
1795 ("server disabled POSIX path support"));
1796 }
Steve French8af18972007-02-14 04:42:51 +00001797 }
Steve French50c2f752007-07-13 00:33:32 +00001798
Steve French8af18972007-02-14 04:42:51 +00001799 cap &= CIFS_UNIX_CAP_MASK;
Steve French75865f8c2007-06-24 18:30:48 +00001800 if (vol_info && vol_info->no_psx_acl)
Steve French8af18972007-02-14 04:42:51 +00001801 cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
Steve French75865f8c2007-06-24 18:30:48 +00001802 else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001803 cFYI(1, ("negotiated posix acl support"));
1804 if (sb)
Steve French8af18972007-02-14 04:42:51 +00001805 sb->s_flags |= MS_POSIXACL;
1806 }
1807
Steve French75865f8c2007-06-24 18:30:48 +00001808 if (vol_info && vol_info->posix_paths == 0)
Steve French8af18972007-02-14 04:42:51 +00001809 cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
Steve French75865f8c2007-06-24 18:30:48 +00001810 else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001811 cFYI(1, ("negotiate posix pathnames"));
Steve French75865f8c2007-06-24 18:30:48 +00001812 if (sb)
Steve French50c2f752007-07-13 00:33:32 +00001813 CIFS_SB(sb)->mnt_cifs_flags |=
Steve French8af18972007-02-14 04:42:51 +00001814 CIFS_MOUNT_POSIX_PATHS;
1815 }
Steve French50c2f752007-07-13 00:33:32 +00001816
Steve French984acfe2007-04-26 16:42:50 +00001817 /* We might be setting the path sep back to a different
1818 form if we are reconnecting and the server switched its
Steve French50c2f752007-07-13 00:33:32 +00001819 posix path capability for this share */
Steve French75865f8c2007-06-24 18:30:48 +00001820 if (sb && (CIFS_SB(sb)->prepathlen > 0))
Steve French984acfe2007-04-26 16:42:50 +00001821 CIFS_SB(sb)->prepath[0] = CIFS_DIR_SEP(CIFS_SB(sb));
Steve French75865f8c2007-06-24 18:30:48 +00001822
1823 if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) {
1824 if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
1825 CIFS_SB(sb)->rsize = 127 * 1024;
Steve French90c81e02008-02-12 20:32:36 +00001826 cFYI(DBG2,
1827 ("larger reads not supported by srv"));
Steve French75865f8c2007-06-24 18:30:48 +00001828 }
1829 }
Steve French50c2f752007-07-13 00:33:32 +00001830
1831
1832 cFYI(1, ("Negotiate caps 0x%x", (int)cap));
Steve French8af18972007-02-14 04:42:51 +00001833#ifdef CONFIG_CIFS_DEBUG2
Steve French75865f8c2007-06-24 18:30:48 +00001834 if (cap & CIFS_UNIX_FCNTL_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001835 cFYI(1, ("FCNTL cap"));
Steve French75865f8c2007-06-24 18:30:48 +00001836 if (cap & CIFS_UNIX_EXTATTR_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001837 cFYI(1, ("EXTATTR cap"));
Steve French75865f8c2007-06-24 18:30:48 +00001838 if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001839 cFYI(1, ("POSIX path cap"));
Steve French75865f8c2007-06-24 18:30:48 +00001840 if (cap & CIFS_UNIX_XATTR_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001841 cFYI(1, ("XATTR cap"));
Steve French75865f8c2007-06-24 18:30:48 +00001842 if (cap & CIFS_UNIX_POSIX_ACL_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001843 cFYI(1, ("POSIX ACL cap"));
Steve French75865f8c2007-06-24 18:30:48 +00001844 if (cap & CIFS_UNIX_LARGE_READ_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001845 cFYI(1, ("very large read cap"));
Steve French75865f8c2007-06-24 18:30:48 +00001846 if (cap & CIFS_UNIX_LARGE_WRITE_CAP)
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001847 cFYI(1, ("very large write cap"));
Steve French8af18972007-02-14 04:42:51 +00001848#endif /* CIFS_DEBUG2 */
1849 if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
Steve French442aa312007-09-24 20:25:46 +00001850 if (vol_info == NULL) {
Steve French5a44b312007-09-20 15:16:24 +00001851 cFYI(1, ("resetting capabilities failed"));
Steve French442aa312007-09-24 20:25:46 +00001852 } else
Steve French5a44b312007-09-20 15:16:24 +00001853 cERROR(1, ("Negotiating Unix capabilities "
1854 "with the server failed. Consider "
1855 "mounting with the Unix Extensions\n"
1856 "disabled, if problems are found, "
1857 "by specifying the nounix mount "
Steve French2224f4e2007-09-20 15:37:29 +00001858 "option."));
Steve French5a44b312007-09-20 15:16:24 +00001859
Steve French8af18972007-02-14 04:42:51 +00001860 }
1861 }
1862}
1863
Steve French03a143c2008-02-14 06:38:30 +00001864static void
1865convert_delimiter(char *path, char delim)
1866{
1867 int i;
Steve Frenchc2d68ea2008-02-15 19:20:18 +00001868 char old_delim;
Steve French03a143c2008-02-14 06:38:30 +00001869
1870 if (path == NULL)
1871 return;
1872
Steve French582d21e2008-05-13 04:54:12 +00001873 if (delim == '/')
Steve Frenchc2d68ea2008-02-15 19:20:18 +00001874 old_delim = '\\';
1875 else
1876 old_delim = '/';
1877
Steve French03a143c2008-02-14 06:38:30 +00001878 for (i = 0; path[i] != '\0'; i++) {
Steve Frenchc2d68ea2008-02-15 19:20:18 +00001879 if (path[i] == old_delim)
Steve French03a143c2008-02-14 06:38:30 +00001880 path[i] = delim;
1881 }
1882}
1883
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -04001884static void
1885kill_cifsd(struct TCP_Server_Info *server)
1886{
1887 struct task_struct *task;
1888
1889 task = xchg(&server->tsk, NULL);
1890 if (task)
1891 force_sig(SIGKILL, task);
1892}
1893
Linus Torvalds1da177e2005-04-16 15:20:36 -07001894int
1895cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
1896 char *mount_data, const char *devname)
1897{
1898 int rc = 0;
1899 int xid;
1900 int address_type = AF_INET;
1901 struct socket *csocket = NULL;
1902 struct sockaddr_in sin_server;
1903 struct sockaddr_in6 sin_server6;
1904 struct smb_vol volume_info;
1905 struct cifsSesInfo *pSesInfo = NULL;
1906 struct cifsSesInfo *existingCifsSes = NULL;
1907 struct cifsTconInfo *tcon = NULL;
1908 struct TCP_Server_Info *srvTcp = NULL;
1909
1910 xid = GetXid();
1911
1912/* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
Steve French50c2f752007-07-13 00:33:32 +00001913
1914 memset(&volume_info, 0, sizeof(struct smb_vol));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915 if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001916 rc = -EINVAL;
1917 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001918 }
1919
Jeff Layton8426c392007-05-05 03:27:49 +00001920 if (volume_info.nullauth) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001921 cFYI(1, ("null user"));
Jeff Layton9b8f5f52007-11-09 23:25:04 +00001922 volume_info.username = "";
Jeff Layton8426c392007-05-05 03:27:49 +00001923 } else if (volume_info.username) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 /* BB fixme parse for domain name here */
Steve French467a8f82007-06-27 22:41:32 +00001925 cFYI(1, ("Username: %s", volume_info.username));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001926 } else {
Steve Frenchbf820672005-12-01 22:32:42 -08001927 cifserror("No username specified");
Steve French50c2f752007-07-13 00:33:32 +00001928 /* In userspace mount helper we can get user name from alternate
1929 locations such as env variables and files on disk */
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001930 rc = -EINVAL;
1931 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932 }
1933
1934 if (volume_info.UNCip && volume_info.UNC) {
Steve French50c2f752007-07-13 00:33:32 +00001935 rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
1936 &sin_server.sin_addr.s_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001938 if (rc <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939 /* not ipv4 address, try ipv6 */
Steve French50c2f752007-07-13 00:33:32 +00001940 rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
1941 &sin_server6.sin6_addr.in6_u);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001942 if (rc > 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943 address_type = AF_INET6;
1944 } else {
1945 address_type = AF_INET;
1946 }
Steve French50c2f752007-07-13 00:33:32 +00001947
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001948 if (rc <= 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949 /* we failed translating address */
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001950 rc = -EINVAL;
1951 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952 }
1953
1954 cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
1955 /* success */
1956 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00001957 } else if (volume_info.UNCip) {
1958 /* BB using ip addr as server name to connect to the
1959 DFS root below */
1960 cERROR(1, ("Connecting to DFS root not implemented yet"));
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001961 rc = -EINVAL;
1962 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001963 } else /* which servers DFS root would we conect to */ {
1964 cERROR(1,
Steve French50c2f752007-07-13 00:33:32 +00001965 ("CIFS mount error: No UNC path (e.g. -o "
1966 "unc=//192.168.1.100/public) specified"));
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001967 rc = -EINVAL;
1968 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001969 }
1970
1971 /* this is needed for ASCII cp to Unicode converts */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001972 if (volume_info.iocharset == NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973 cifs_sb->local_nls = load_nls_default();
1974 /* load_nls_default can not return null */
1975 } else {
1976 cifs_sb->local_nls = load_nls(volume_info.iocharset);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001977 if (cifs_sb->local_nls == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00001978 cERROR(1, ("CIFS mount error: iocharset %s not found",
1979 volume_info.iocharset));
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001980 rc = -ELIBACC;
1981 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982 }
1983 }
1984
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001985 if (address_type == AF_INET)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr,
1987 NULL /* no ipv6 addr */,
1988 volume_info.username, &srvTcp);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00001989 else if (address_type == AF_INET6) {
1990 cFYI(1, ("looking for ipv6 address"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991 existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */,
1992 &sin_server6.sin6_addr,
1993 volume_info.username, &srvTcp);
Steve French5858ae42007-04-25 11:59:10 +00001994 } else {
Jeff Layton70fe7dc2007-11-16 22:21:07 +00001995 rc = -EINVAL;
1996 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 }
1998
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 if (srvTcp) {
Steve French50c2f752007-07-13 00:33:32 +00002000 cFYI(1, ("Existing tcp session with server found"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 } else { /* create socket */
Steve French4523cc32007-04-30 20:13:06 +00002002 if (volume_info.port)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 sin_server.sin_port = htons(volume_info.port);
2004 else
2005 sin_server.sin_port = 0;
Steve French5858ae42007-04-25 11:59:10 +00002006 if (address_type == AF_INET6) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002007 cFYI(1, ("attempting ipv6 connect"));
Steve French5858ae42007-04-25 11:59:10 +00002008 /* BB should we allow ipv6 on port 139? */
2009 /* other OS never observed in Wild doing 139 with v6 */
Steve Frenchedf1ae42008-10-29 00:47:57 +00002010 rc = ipv6_connect(&sin_server6, &csocket,
2011 volume_info.noblocksnd);
Steve French50c2f752007-07-13 00:33:32 +00002012 } else
2013 rc = ipv4_connect(&sin_server, &csocket,
Steve Frencha10faeb22005-08-22 21:38:31 -07002014 volume_info.source_rfc1001_name,
Steve Frenchedf1ae42008-10-29 00:47:57 +00002015 volume_info.target_rfc1001_name,
2016 volume_info.noblocksnd,
2017 volume_info.noautotune);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 if (rc < 0) {
Steve French50c2f752007-07-13 00:33:32 +00002019 cERROR(1, ("Error connecting to IPv4 socket. "
2020 "Aborting operation"));
Steve French4523cc32007-04-30 20:13:06 +00002021 if (csocket != NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 sock_release(csocket);
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002023 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002024 }
2025
Mariusz Kozlowskia8a11d32007-10-03 16:41:24 +00002026 srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
2027 if (!srvTcp) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 rc = -ENOMEM;
2029 sock_release(csocket);
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002030 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031 } else {
Steve Frenchedf1ae42008-10-29 00:47:57 +00002032 srvTcp->noblocksnd = volume_info.noblocksnd;
2033 srvTcp->noautotune = volume_info.noautotune;
Steve French50c2f752007-07-13 00:33:32 +00002034 memcpy(&srvTcp->addr.sockAddr, &sin_server,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00002035 sizeof(struct sockaddr_in));
Steve French50c2f752007-07-13 00:33:32 +00002036 atomic_set(&srvTcp->inFlight, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037 /* BB Add code for ipv6 case too */
2038 srvTcp->ssocket = csocket;
2039 srvTcp->protocolType = IPV4;
Jeff Laytonc359cf32007-11-16 22:22:06 +00002040 srvTcp->hostname = extract_hostname(volume_info.UNC);
2041 if (IS_ERR(srvTcp->hostname)) {
2042 rc = PTR_ERR(srvTcp->hostname);
2043 sock_release(csocket);
2044 goto out;
2045 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046 init_waitqueue_head(&srvTcp->response_q);
2047 init_waitqueue_head(&srvTcp->request_q);
2048 INIT_LIST_HEAD(&srvTcp->pending_mid_q);
2049 /* at this point we are the only ones with the pointer
2050 to the struct since the kernel thread not created yet
2051 so no need to spinlock this init of tcpStatus */
2052 srvTcp->tcpStatus = CifsNew;
2053 init_MUTEX(&srvTcp->tcpSem);
Igor Mammedovaaf737a2007-04-03 19:16:43 +00002054 srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
Steve French8840dee2007-11-16 23:05:52 +00002055 if (IS_ERR(srvTcp->tsk)) {
Igor Mammedovaaf737a2007-04-03 19:16:43 +00002056 rc = PTR_ERR(srvTcp->tsk);
Steve French50c2f752007-07-13 00:33:32 +00002057 cERROR(1, ("error %d create cifsd thread", rc));
Igor Mammedovaaf737a2007-04-03 19:16:43 +00002058 srvTcp->tsk = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 sock_release(csocket);
Jeff Laytonc359cf32007-11-16 22:22:06 +00002060 kfree(srvTcp->hostname);
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002061 goto out;
Steve Frenchf1914012005-08-18 09:37:34 -07002062 }
Steve Frenchf1914012005-08-18 09:37:34 -07002063 rc = 0;
Steve French50c2f752007-07-13 00:33:32 +00002064 memcpy(srvTcp->workstation_RFC1001_name,
2065 volume_info.source_rfc1001_name, 16);
2066 memcpy(srvTcp->server_RFC1001_name,
2067 volume_info.target_rfc1001_name, 16);
Steve Frenchad009ac2005-04-28 22:41:05 -07002068 srvTcp->sequence_number = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 }
2070 }
2071
2072 if (existingCifsSes) {
2073 pSesInfo = existingCifsSes;
Jeff Layton1d9a8852007-12-31 01:37:11 +00002074 cFYI(1, ("Existing smb sess found (status=%d)",
2075 pSesInfo->status));
Steve French88e7d702008-01-03 17:37:09 +00002076 down(&pSesInfo->sesSem);
Jeff Layton1d9a8852007-12-31 01:37:11 +00002077 if (pSesInfo->status == CifsNeedReconnect) {
2078 cFYI(1, ("Session needs reconnect"));
Jeff Layton1d9a8852007-12-31 01:37:11 +00002079 rc = cifs_setup_session(xid, pSesInfo,
2080 cifs_sb->local_nls);
Jeff Layton1d9a8852007-12-31 01:37:11 +00002081 }
Steve French88e7d702008-01-03 17:37:09 +00002082 up(&pSesInfo->sesSem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 } else if (!rc) {
Steve Frenchbf820672005-12-01 22:32:42 -08002084 cFYI(1, ("Existing smb sess not found"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002085 pSesInfo = sesInfoAlloc();
2086 if (pSesInfo == NULL)
2087 rc = -ENOMEM;
2088 else {
2089 pSesInfo->server = srvTcp;
2090 sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
2091 NIPQUAD(sin_server.sin_addr.s_addr));
2092 }
2093
Steve French50c2f752007-07-13 00:33:32 +00002094 if (!rc) {
2095 /* volume_info.password freed at unmount */
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002096 if (volume_info.password) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 pSesInfo->password = volume_info.password;
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002098 /* set to NULL to prevent freeing on exit */
2099 volume_info.password = NULL;
2100 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 if (volume_info.username)
2102 strncpy(pSesInfo->userName,
Steve French50c2f752007-07-13 00:33:32 +00002103 volume_info.username,
2104 MAX_USERNAME_SIZE);
Steve French39798772006-05-31 22:40:51 +00002105 if (volume_info.domainname) {
2106 int len = strlen(volume_info.domainname);
Steve French50c2f752007-07-13 00:33:32 +00002107 pSesInfo->domainName =
Steve French39798772006-05-31 22:40:51 +00002108 kmalloc(len + 1, GFP_KERNEL);
Steve French4523cc32007-04-30 20:13:06 +00002109 if (pSesInfo->domainName)
Steve French39798772006-05-31 22:40:51 +00002110 strcpy(pSesInfo->domainName,
2111 volume_info.domainname);
2112 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 pSesInfo->linux_uid = volume_info.linux_uid;
Steve French750d1152006-06-27 06:28:30 +00002114 pSesInfo->overrideSecFlg = volume_info.secFlg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 down(&pSesInfo->sesSem);
Steve French189acaa2006-06-23 02:33:48 +00002116 /* BB FIXME need to pass vol->secFlgs BB */
Steve French50c2f752007-07-13 00:33:32 +00002117 rc = cifs_setup_session(xid, pSesInfo,
2118 cifs_sb->local_nls);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 up(&pSesInfo->sesSem);
Steve French4523cc32007-04-30 20:13:06 +00002120 if (!rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 atomic_inc(&srvTcp->socketUseCount);
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123 }
Steve French50c2f752007-07-13 00:33:32 +00002124
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 /* search for existing tcon to this server share */
2126 if (!rc) {
Steve French4523cc32007-04-30 20:13:06 +00002127 if (volume_info.rsize > CIFSMaxBufSize) {
Steve French50c2f752007-07-13 00:33:32 +00002128 cERROR(1, ("rsize %d too large, using MaxBufSize",
Steve French0ae0efa2005-10-10 10:57:19 -07002129 volume_info.rsize));
2130 cifs_sb->rsize = CIFSMaxBufSize;
Steve French75865f8c2007-06-24 18:30:48 +00002131 } else if ((volume_info.rsize) &&
2132 (volume_info.rsize <= CIFSMaxBufSize))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 cifs_sb->rsize = volume_info.rsize;
Steve French0ae0efa2005-10-10 10:57:19 -07002134 else /* default */
2135 cifs_sb->rsize = CIFSMaxBufSize;
2136
Steve French4523cc32007-04-30 20:13:06 +00002137 if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
Steve French50c2f752007-07-13 00:33:32 +00002138 cERROR(1, ("wsize %d too large, using 4096 instead",
Steve French0ae0efa2005-10-10 10:57:19 -07002139 volume_info.wsize));
2140 cifs_sb->wsize = 4096;
Steve French4523cc32007-04-30 20:13:06 +00002141 } else if (volume_info.wsize)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 cifs_sb->wsize = volume_info.wsize;
2143 else
Steve French50c2f752007-07-13 00:33:32 +00002144 cifs_sb->wsize =
Steve French1877c9e2006-01-27 18:36:11 -08002145 min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE,
2146 127*1024);
Steve French17cbbaf2006-01-24 20:26:48 -08002147 /* old default of CIFSMaxBufSize was too small now
Steve French50c2f752007-07-13 00:33:32 +00002148 that SMB Write2 can send multiple pages in kvec.
Steve French17cbbaf2006-01-24 20:26:48 -08002149 RFC1001 does not describe what happens when frame
2150 bigger than 128K is sent so use that as max in
2151 conjunction with 52K kvec constraint on arch with 4K
2152 page size */
2153
Steve French4523cc32007-04-30 20:13:06 +00002154 if (cifs_sb->rsize < 2048) {
Steve French50c2f752007-07-13 00:33:32 +00002155 cifs_sb->rsize = 2048;
Steve French6cec2ae2006-02-22 17:31:52 -06002156 /* Windows ME may prefer this */
Steve French467a8f82007-06-27 22:41:32 +00002157 cFYI(1, ("readsize set to minimum: 2048"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 }
Steve French2fe87f02006-09-21 07:02:52 +00002159 /* calculate prepath */
2160 cifs_sb->prepath = volume_info.prepath;
Steve French4523cc32007-04-30 20:13:06 +00002161 if (cifs_sb->prepath) {
Steve French2fe87f02006-09-21 07:02:52 +00002162 cifs_sb->prepathlen = strlen(cifs_sb->prepath);
Steve French03a143c2008-02-14 06:38:30 +00002163 /* we can not convert the / to \ in the path
2164 separators in the prefixpath yet because we do not
2165 know (until reset_cifs_unix_caps is called later)
2166 whether POSIX PATH CAP is available. We normalize
2167 the / to \ after reset_cifs_unix_caps is called */
Steve French2fe87f02006-09-21 07:02:52 +00002168 volume_info.prepath = NULL;
Steve French50c2f752007-07-13 00:33:32 +00002169 } else
Steve French2fe87f02006-09-21 07:02:52 +00002170 cifs_sb->prepathlen = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 cifs_sb->mnt_uid = volume_info.linux_uid;
2172 cifs_sb->mnt_gid = volume_info.linux_gid;
2173 cifs_sb->mnt_file_mode = volume_info.file_mode;
2174 cifs_sb->mnt_dir_mode = volume_info.dir_mode;
Steve French467a8f82007-06-27 22:41:32 +00002175 cFYI(1, ("file mode: 0x%x dir mode: 0x%x",
2176 cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177
Steve French4523cc32007-04-30 20:13:06 +00002178 if (volume_info.noperm)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002179 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
Steve French4523cc32007-04-30 20:13:06 +00002180 if (volume_info.setuids)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
Steve French4523cc32007-04-30 20:13:06 +00002182 if (volume_info.server_ino)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
Steve French4523cc32007-04-30 20:13:06 +00002184 if (volume_info.remap)
Steve French6a0b4822005-04-28 22:41:05 -07002185 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
Steve French4523cc32007-04-30 20:13:06 +00002186 if (volume_info.no_xattr)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
Steve French4523cc32007-04-30 20:13:06 +00002188 if (volume_info.sfu_emul)
Steve Frenchd7245c22005-07-14 18:25:12 -05002189 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
Steve French4523cc32007-04-30 20:13:06 +00002190 if (volume_info.nobrl)
Steve Frenchc46fa8a2005-08-18 20:49:57 -07002191 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
Steve French4523cc32007-04-30 20:13:06 +00002192 if (volume_info.cifs_acl)
Steve French0a4b92c2006-01-12 15:44:21 -08002193 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
Steve French4523cc32007-04-30 20:13:06 +00002194 if (volume_info.override_uid)
2195 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
2196 if (volume_info.override_gid)
2197 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
Jeff Laytond0a9c072008-05-12 22:23:49 +00002198 if (volume_info.dynperm)
2199 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
Steve French4523cc32007-04-30 20:13:06 +00002200 if (volume_info.direct_io) {
Steve French467a8f82007-06-27 22:41:32 +00002201 cFYI(1, ("mounting share using direct i/o"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002202 cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
2203 }
2204
Steve French27adb442008-05-23 19:43:29 +00002205 if ((volume_info.cifs_acl) && (volume_info.dynperm))
2206 cERROR(1, ("mount option dynperm ignored if cifsacl "
2207 "mount option supported"));
2208
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209 tcon =
2210 find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
2211 volume_info.username);
2212 if (tcon) {
Steve Frenchbf820672005-12-01 22:32:42 -08002213 cFYI(1, ("Found match on UNC path"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214 /* we can have only one retry value for a connection
2215 to a share so for resources mounted more than once
Steve French50c2f752007-07-13 00:33:32 +00002216 to the same server share the last value passed in
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217 for the retry flag is used */
2218 tcon->retry = volume_info.retry;
Steve Frenchd3485d32005-08-19 11:04:29 -07002219 tcon->nocase = volume_info.nocase;
Steve French84210e92008-10-23 04:42:37 +00002220 tcon->local_lease = volume_info.local_lease;
Steve French95b1cb92008-05-15 16:44:38 +00002221 if (tcon->seal != volume_info.seal)
2222 cERROR(1, ("transport encryption setting "
2223 "conflicts with existing tid"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 } else {
2225 tcon = tconInfoAlloc();
2226 if (tcon == NULL)
2227 rc = -ENOMEM;
2228 else {
Steve French50c2f752007-07-13 00:33:32 +00002229 /* check for null share name ie connecting to
Steve French8af18972007-02-14 04:42:51 +00002230 * dfs root */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231
Steve French50c2f752007-07-13 00:33:32 +00002232 /* BB check if this works for exactly length
Steve French8af18972007-02-14 04:42:51 +00002233 * three strings */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 if ((strchr(volume_info.UNC + 3, '\\') == NULL)
2235 && (strchr(volume_info.UNC + 3, '/') ==
2236 NULL)) {
Steve French646dd532008-05-15 01:50:56 +00002237/* rc = connect_to_dfs_path(xid, pSesInfo,
Steve French8af18972007-02-14 04:42:51 +00002238 "", cifs_sb->local_nls,
Steve French50c2f752007-07-13 00:33:32 +00002239 cifs_sb->mnt_cifs_flags &
Steve French646dd532008-05-15 01:50:56 +00002240 CIFS_MOUNT_MAP_SPECIAL_CHR);*/
2241 cFYI(1, ("DFS root not supported"));
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002242 rc = -ENODEV;
2243 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 } else {
Steve French8af18972007-02-14 04:42:51 +00002245 /* BB Do we need to wrap sesSem around
2246 * this TCon call and Unix SetFS as
2247 * we do on SessSetup and reconnect? */
Steve French50c2f752007-07-13 00:33:32 +00002248 rc = CIFSTCon(xid, pSesInfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 volume_info.UNC,
2250 tcon, cifs_sb->local_nls);
2251 cFYI(1, ("CIFS Tcon rc = %d", rc));
Steve French2c1b8612008-10-16 18:35:21 +00002252 if (volume_info.nodfs) {
2253 tcon->Flags &=
2254 ~SMB_SHARE_IS_IN_DFS;
2255 cFYI(1, ("DFS disabled (%d)",
2256 tcon->Flags));
2257 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258 }
2259 if (!rc) {
2260 atomic_inc(&pSesInfo->inUse);
2261 tcon->retry = volume_info.retry;
Steve Frenchd3485d32005-08-19 11:04:29 -07002262 tcon->nocase = volume_info.nocase;
Steve French95b1cb92008-05-15 16:44:38 +00002263 tcon->seal = volume_info.seal;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 }
2265 }
2266 }
2267 }
Steve French4523cc32007-04-30 20:13:06 +00002268 if (pSesInfo) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 if (pSesInfo->capabilities & CAP_LARGE_FILES) {
2270 sb->s_maxbytes = (u64) 1 << 63;
2271 } else
2272 sb->s_maxbytes = (u64) 1 << 31; /* 2 GB */
2273 }
2274
Steve French8af18972007-02-14 04:42:51 +00002275 /* BB FIXME fix time_gran to be larger for LANMAN sessions */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 sb->s_time_gran = 100;
2277
2278/* on error free sesinfo and tcon struct if needed */
2279 if (rc) {
2280 /* if session setup failed, use count is zero but
2281 we still need to free cifsd thread */
Steve French4523cc32007-04-30 20:13:06 +00002282 if (atomic_read(&srvTcp->socketUseCount) == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 spin_lock(&GlobalMid_Lock);
2284 srvTcp->tcpStatus = CifsExiting;
2285 spin_unlock(&GlobalMid_Lock);
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -04002286 kill_cifsd(srvTcp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 }
2288 /* If find_unc succeeded then rc == 0 so we can not end */
2289 if (tcon) /* up accidently freeing someone elses tcon struct */
2290 tconInfoFree(tcon);
2291 if (existingCifsSes == NULL) {
2292 if (pSesInfo) {
Steve French50c2f752007-07-13 00:33:32 +00002293 if ((pSesInfo->server) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 (pSesInfo->status == CifsGood)) {
2295 int temp_rc;
2296 temp_rc = CIFSSMBLogoff(xid, pSesInfo);
2297 /* if the socketUseCount is now zero */
Steve French4523cc32007-04-30 20:13:06 +00002298 if ((temp_rc == -ESHUTDOWN) &&
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -04002299 (pSesInfo->server))
2300 kill_cifsd(pSesInfo->server);
Steve Frencha0136892007-10-04 20:05:09 +00002301 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 cFYI(1, ("No session or bad tcon"));
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -04002303 if (pSesInfo->server) {
Jeff Layton469ee612008-10-16 18:46:39 +00002304 spin_lock(&GlobalMid_Lock);
2305 srvTcp->tcpStatus = CifsExiting;
2306 spin_unlock(&GlobalMid_Lock);
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -04002307 kill_cifsd(pSesInfo->server);
Steve Frencha0136892007-10-04 20:05:09 +00002308 }
2309 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 sesInfoFree(pSesInfo);
2311 /* pSesInfo = NULL; */
2312 }
2313 }
2314 } else {
2315 atomic_inc(&tcon->useCount);
2316 cifs_sb->tcon = tcon;
2317 tcon->ses = pSesInfo;
2318
Steve French82940a42006-03-02 03:24:57 +00002319 /* do not care if following two calls succeed - informational */
Steve French7f8ed422007-09-28 22:28:55 +00002320 if (!tcon->ipc) {
2321 CIFSSMBQFSDeviceInfo(xid, tcon);
2322 CIFSSMBQFSAttributeInfo(xid, tcon);
2323 }
Steve French50c2f752007-07-13 00:33:32 +00002324
Steve French8af18972007-02-14 04:42:51 +00002325 /* tell server which Unix caps we support */
2326 if (tcon->ses->capabilities & CAP_UNIX)
Steve Frenchc18c8422007-07-18 23:21:09 +00002327 /* reset of caps checks mount to see if unix extensions
2328 disabled for just this mount */
Steve French8af18972007-02-14 04:42:51 +00002329 reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
Steve Frenchc18c8422007-07-18 23:21:09 +00002330 else
2331 tcon->unix_ext = 0; /* server does not support them */
2332
Steve French03a143c2008-02-14 06:38:30 +00002333 /* convert forward to back slashes in prepath here if needed */
Igor Mammedov11b6d642008-02-15 19:06:04 +00002334 if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
2335 convert_delimiter(cifs_sb->prepath,
2336 CIFS_DIR_SEP(cifs_sb));
Steve French03a143c2008-02-14 06:38:30 +00002337
Steve Frenchc18c8422007-07-18 23:21:09 +00002338 if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
Steve French75865f8c2007-06-24 18:30:48 +00002339 cifs_sb->rsize = 1024 * 127;
Steve French90c81e02008-02-12 20:32:36 +00002340 cFYI(DBG2,
2341 ("no very large read support, rsize now 127K"));
Steve French75865f8c2007-06-24 18:30:48 +00002342 }
Steve French3e844692005-10-03 13:37:24 -07002343 if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
2344 cifs_sb->wsize = min(cifs_sb->wsize,
2345 (tcon->ses->server->maxBuf -
2346 MAX_CIFS_HDR_SIZE));
Steve French0ae0efa2005-10-10 10:57:19 -07002347 if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
Steve French50c2f752007-07-13 00:33:32 +00002348 cifs_sb->rsize = min(cifs_sb->rsize,
2349 (tcon->ses->server->maxBuf -
2350 MAX_CIFS_HDR_SIZE));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 }
2352
2353 /* volume_info.password is freed above when existing session found
2354 (in which case it is not needed anymore) but when new sesion is created
2355 the password ptr is put in the new session structure (in which case the
2356 password will be freed at unmount time) */
Jeff Layton70fe7dc2007-11-16 22:21:07 +00002357out:
2358 /* zero out password before freeing */
2359 if (volume_info.password != NULL) {
2360 memset(volume_info.password, 0, strlen(volume_info.password));
2361 kfree(volume_info.password);
2362 }
Jesper Juhlf99d49a2005-11-07 01:01:34 -08002363 kfree(volume_info.UNC);
Steve French2fe87f02006-09-21 07:02:52 +00002364 kfree(volume_info.prepath);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 FreeXid(xid);
2366 return rc;
2367}
2368
2369static int
2370CIFSSessSetup(unsigned int xid, struct cifsSesInfo *ses,
Steve French7c7b25b2006-06-01 19:20:10 +00002371 char session_key[CIFS_SESS_KEY_SIZE],
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372 const struct nls_table *nls_codepage)
2373{
2374 struct smb_hdr *smb_buffer;
2375 struct smb_hdr *smb_buffer_response;
2376 SESSION_SETUP_ANDX *pSMB;
2377 SESSION_SETUP_ANDX *pSMBr;
2378 char *bcc_ptr;
2379 char *user;
2380 char *domain;
2381 int rc = 0;
2382 int remaining_words = 0;
2383 int bytes_returned = 0;
2384 int len;
2385 __u32 capabilities;
2386 __u16 count;
2387
Steve Frencheeac8042006-01-13 21:34:58 -08002388 cFYI(1, ("In sesssetup"));
Steve French4523cc32007-04-30 20:13:06 +00002389 if (ses == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 return -EINVAL;
2391 user = ses->userName;
2392 domain = ses->domainName;
2393 smb_buffer = cifs_buf_get();
Steve French582d21e2008-05-13 04:54:12 +00002394
2395 if (smb_buffer == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002396 return -ENOMEM;
Steve French582d21e2008-05-13 04:54:12 +00002397
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398 smb_buffer_response = smb_buffer;
2399 pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
2400
2401 /* send SMBsessionSetup here */
2402 header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
2403 NULL /* no tCon exists yet */ , 13 /* wct */ );
2404
Steve French1982c342005-08-17 12:38:22 -07002405 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406 pSMB->req_no_secext.AndXCommand = 0xFF;
2407 pSMB->req_no_secext.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
2408 pSMB->req_no_secext.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
2409
Steve French50c2f752007-07-13 00:33:32 +00002410 if (ses->server->secMode &
2411 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
2413
2414 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
2415 CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
2416 if (ses->capabilities & CAP_UNICODE) {
2417 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
2418 capabilities |= CAP_UNICODE;
2419 }
2420 if (ses->capabilities & CAP_STATUS32) {
2421 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
2422 capabilities |= CAP_STATUS32;
2423 }
2424 if (ses->capabilities & CAP_DFS) {
2425 smb_buffer->Flags2 |= SMBFLG2_DFS;
2426 capabilities |= CAP_DFS;
2427 }
2428 pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
2429
Steve French50c2f752007-07-13 00:33:32 +00002430 pSMB->req_no_secext.CaseInsensitivePasswordLength =
Steve French7c7b25b2006-06-01 19:20:10 +00002431 cpu_to_le16(CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432
2433 pSMB->req_no_secext.CaseSensitivePasswordLength =
Steve French7c7b25b2006-06-01 19:20:10 +00002434 cpu_to_le16(CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 bcc_ptr = pByteArea(smb_buffer);
Steve French7c7b25b2006-06-01 19:20:10 +00002436 memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
2437 bcc_ptr += CIFS_SESS_KEY_SIZE;
2438 memcpy(bcc_ptr, (char *) session_key, CIFS_SESS_KEY_SIZE);
2439 bcc_ptr += CIFS_SESS_KEY_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440
2441 if (ses->capabilities & CAP_UNICODE) {
2442 if ((long) bcc_ptr % 2) { /* must be word aligned for Unicode */
2443 *bcc_ptr = 0;
2444 bcc_ptr++;
2445 }
Steve French4523cc32007-04-30 20:13:06 +00002446 if (user == NULL)
Steve French39798772006-05-31 22:40:51 +00002447 bytes_returned = 0; /* skip null user */
Steve French50c2f752007-07-13 00:33:32 +00002448 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07002449 bytes_returned =
Steve French50c2f752007-07-13 00:33:32 +00002450 cifs_strtoUCS((__le16 *) bcc_ptr, user, 100,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002451 nls_codepage);
2452 /* convert number of 16 bit words to bytes */
2453 bcc_ptr += 2 * bytes_returned;
2454 bcc_ptr += 2; /* trailing null */
2455 if (domain == NULL)
2456 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002457 cifs_strtoUCS((__le16 *) bcc_ptr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 "CIFS_LINUX_DOM", 32, nls_codepage);
2459 else
2460 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002461 cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 nls_codepage);
2463 bcc_ptr += 2 * bytes_returned;
2464 bcc_ptr += 2;
2465 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002466 cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002467 32, nls_codepage);
2468 bcc_ptr += 2 * bytes_returned;
2469 bytes_returned =
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002470 cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471 32, nls_codepage);
2472 bcc_ptr += 2 * bytes_returned;
2473 bcc_ptr += 2;
2474 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002475 cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 64, nls_codepage);
2477 bcc_ptr += 2 * bytes_returned;
2478 bcc_ptr += 2;
2479 } else {
Steve French50c2f752007-07-13 00:33:32 +00002480 if (user != NULL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481 strncpy(bcc_ptr, user, 200);
2482 bcc_ptr += strnlen(user, 200);
2483 }
2484 *bcc_ptr = 0;
2485 bcc_ptr++;
2486 if (domain == NULL) {
2487 strcpy(bcc_ptr, "CIFS_LINUX_DOM");
2488 bcc_ptr += strlen("CIFS_LINUX_DOM") + 1;
2489 } else {
2490 strncpy(bcc_ptr, domain, 64);
2491 bcc_ptr += strnlen(domain, 64);
2492 *bcc_ptr = 0;
2493 bcc_ptr++;
2494 }
2495 strcpy(bcc_ptr, "Linux version ");
2496 bcc_ptr += strlen("Linux version ");
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002497 strcpy(bcc_ptr, utsname()->release);
2498 bcc_ptr += strlen(utsname()->release) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
2500 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
2501 }
2502 count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
2503 smb_buffer->smb_buf_length += count;
2504 pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
2505
2506 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
Steve French133672e2007-11-13 22:41:37 +00002507 &bytes_returned, CIFS_LONG_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002508 if (rc) {
2509/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
2510 } else if ((smb_buffer_response->WordCount == 3)
2511 || (smb_buffer_response->WordCount == 4)) {
2512 __u16 action = le16_to_cpu(pSMBr->resp.Action);
2513 __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
2514 if (action & GUEST_LOGIN)
Steve French50c2f752007-07-13 00:33:32 +00002515 cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
2516 ses->Suid = smb_buffer_response->Uid; /* UID left in wire format
2517 (little endian) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 cFYI(1, ("UID = %d ", ses->Suid));
Steve French50c2f752007-07-13 00:33:32 +00002519 /* response can have either 3 or 4 word count - Samba sends 3 */
2520 bcc_ptr = pByteArea(smb_buffer_response);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521 if ((pSMBr->resp.hdr.WordCount == 3)
2522 || ((pSMBr->resp.hdr.WordCount == 4)
2523 && (blob_len < pSMBr->resp.ByteCount))) {
2524 if (pSMBr->resp.hdr.WordCount == 4)
2525 bcc_ptr += blob_len;
2526
2527 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
2528 if ((long) (bcc_ptr) % 2) {
2529 remaining_words =
Steve French50c2f752007-07-13 00:33:32 +00002530 (BCC(smb_buffer_response) - 1) / 2;
2531 /* Unicode strings must be word
2532 aligned */
2533 bcc_ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 } else {
2535 remaining_words =
2536 BCC(smb_buffer_response) / 2;
2537 }
2538 len =
2539 UniStrnlen((wchar_t *) bcc_ptr,
2540 remaining_words - 1);
2541/* We look for obvious messed up bcc or strings in response so we do not go off
2542 the end since (at least) WIN2K and Windows XP have a major bug in not null
2543 terminating last Unicode string in response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002544 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00002545 kfree(ses->serverOS);
Steve French50c2f752007-07-13 00:33:32 +00002546 ses->serverOS = kzalloc(2 * (len + 1),
2547 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002548 if (ses->serverOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002549 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 cifs_strfromUCS_le(ses->serverOS,
Steve French50c2f752007-07-13 00:33:32 +00002551 (__le16 *)bcc_ptr,
2552 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 bcc_ptr += 2 * (len + 1);
2554 remaining_words -= len + 1;
2555 ses->serverOS[2 * len] = 0;
2556 ses->serverOS[1 + (2 * len)] = 0;
2557 if (remaining_words > 0) {
2558 len = UniStrnlen((wchar_t *)bcc_ptr,
2559 remaining_words-1);
Steve Frenchcd49b492006-06-26 04:22:36 +00002560 kfree(ses->serverNOS);
Steve French50c2f752007-07-13 00:33:32 +00002561 ses->serverNOS = kzalloc(2 * (len + 1),
2562 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002563 if (ses->serverNOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002564 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 cifs_strfromUCS_le(ses->serverNOS,
Steve French50c2f752007-07-13 00:33:32 +00002566 (__le16 *)bcc_ptr,
2567 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 bcc_ptr += 2 * (len + 1);
2569 ses->serverNOS[2 * len] = 0;
2570 ses->serverNOS[1 + (2 * len)] = 0;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002571 if (strncmp(ses->serverNOS,
Steve French50c2f752007-07-13 00:33:32 +00002572 "NT LAN Manager 4", 16) == 0) {
Steve French467a8f82007-06-27 22:41:32 +00002573 cFYI(1, ("NT4 server"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574 ses->flags |= CIFS_SES_NT4;
2575 }
2576 remaining_words -= len + 1;
2577 if (remaining_words > 0) {
Steve French433dc242005-04-28 22:41:08 -07002578 len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
Steve French50c2f752007-07-13 00:33:32 +00002579 /* last string is not always null terminated
2580 (for e.g. for Windows XP & 2000) */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002581 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00002582 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002583 ses->serverDomain =
Steve French50c2f752007-07-13 00:33:32 +00002584 kzalloc(2*(len+1),
2585 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002586 if (ses->serverDomain == NULL)
Steve French433dc242005-04-28 22:41:08 -07002587 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588 cifs_strfromUCS_le(ses->serverDomain,
Steve French50c2f752007-07-13 00:33:32 +00002589 (__le16 *)bcc_ptr,
2590 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591 bcc_ptr += 2 * (len + 1);
2592 ses->serverDomain[2*len] = 0;
2593 ses->serverDomain[1+(2*len)] = 0;
Steve French50c2f752007-07-13 00:33:32 +00002594 } else { /* else no more room so create
2595 dummy domain string */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002596 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00002597 kfree(ses->serverDomain);
Steve French50c2f752007-07-13 00:33:32 +00002598 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002599 kzalloc(2, GFP_KERNEL);
Steve Frencha424f8b2006-05-30 18:06:04 +00002600 }
Steve French50c2f752007-07-13 00:33:32 +00002601 } else { /* no room so create dummy domain
2602 and NOS string */
2603
Steve French433dc242005-04-28 22:41:08 -07002604 /* if these kcallocs fail not much we
2605 can do, but better to not fail the
2606 sesssetup itself */
Steve Frenchcd49b492006-06-26 04:22:36 +00002607 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002609 kzalloc(2, GFP_KERNEL);
Steve Frenchcd49b492006-06-26 04:22:36 +00002610 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002612 kzalloc(2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 }
2614 } else { /* ASCII */
2615 len = strnlen(bcc_ptr, 1024);
2616 if (((long) bcc_ptr + len) - (long)
2617 pByteArea(smb_buffer_response)
2618 <= BCC(smb_buffer_response)) {
Steve Frenchcd49b492006-06-26 04:22:36 +00002619 kfree(ses->serverOS);
Steve French50c2f752007-07-13 00:33:32 +00002620 ses->serverOS = kzalloc(len + 1,
2621 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002622 if (ses->serverOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002623 goto sesssetup_nomem;
Steve French50c2f752007-07-13 00:33:32 +00002624 strncpy(ses->serverOS, bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625
2626 bcc_ptr += len;
Steve French50c2f752007-07-13 00:33:32 +00002627 /* null terminate the string */
2628 bcc_ptr[0] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629 bcc_ptr++;
2630
2631 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00002632 kfree(ses->serverNOS);
Steve French50c2f752007-07-13 00:33:32 +00002633 ses->serverNOS = kzalloc(len + 1,
2634 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002635 if (ses->serverNOS == NULL)
Steve French433dc242005-04-28 22:41:08 -07002636 goto sesssetup_nomem;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 strncpy(ses->serverNOS, bcc_ptr, len);
2638 bcc_ptr += len;
2639 bcc_ptr[0] = 0;
2640 bcc_ptr++;
2641
2642 len = strnlen(bcc_ptr, 1024);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002643 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00002644 kfree(ses->serverDomain);
Steve French50c2f752007-07-13 00:33:32 +00002645 ses->serverDomain = kzalloc(len + 1,
2646 GFP_KERNEL);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002647 if (ses->serverDomain == NULL)
Steve French433dc242005-04-28 22:41:08 -07002648 goto sesssetup_nomem;
Steve French50c2f752007-07-13 00:33:32 +00002649 strncpy(ses->serverDomain, bcc_ptr,
2650 len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 bcc_ptr += len;
2652 bcc_ptr[0] = 0;
2653 bcc_ptr++;
2654 } else
2655 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00002656 ("Variable field of length %d "
2657 "extends beyond end of smb ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658 len));
2659 }
2660 } else {
2661 cERROR(1,
Steve French50c2f752007-07-13 00:33:32 +00002662 (" Security Blob Length extends beyond "
2663 "end of SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 }
2665 } else {
2666 cERROR(1,
2667 (" Invalid Word count %d: ",
2668 smb_buffer_response->WordCount));
2669 rc = -EIO;
2670 }
Steve French433dc242005-04-28 22:41:08 -07002671sesssetup_nomem: /* do not return an error on nomem for the info strings,
2672 since that could make reconnection harder, and
2673 reconnection might be needed to free memory */
Mariusz Kozlowskia8a11d32007-10-03 16:41:24 +00002674 cifs_buf_release(smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002675
2676 return rc;
2677}
2678
2679static int
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
Steve French4b18f2a2008-04-29 00:06:05 +00002681 struct cifsSesInfo *ses, bool *pNTLMv2_flag,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002682 const struct nls_table *nls_codepage)
2683{
2684 struct smb_hdr *smb_buffer;
2685 struct smb_hdr *smb_buffer_response;
2686 SESSION_SETUP_ANDX *pSMB;
2687 SESSION_SETUP_ANDX *pSMBr;
2688 char *bcc_ptr;
2689 char *domain;
2690 int rc = 0;
2691 int remaining_words = 0;
2692 int bytes_returned = 0;
2693 int len;
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00002694 int SecurityBlobLength = sizeof(NEGOTIATE_MESSAGE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695 PNEGOTIATE_MESSAGE SecurityBlob;
2696 PCHALLENGE_MESSAGE SecurityBlob2;
2697 __u32 negotiate_flags, capabilities;
2698 __u16 count;
2699
Steve French12b3b8f2006-02-09 21:12:47 +00002700 cFYI(1, ("In NTLMSSP sesssetup (negotiate)"));
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002701 if (ses == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 return -EINVAL;
2703 domain = ses->domainName;
Steve French4b18f2a2008-04-29 00:06:05 +00002704 *pNTLMv2_flag = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 smb_buffer = cifs_buf_get();
2706 if (smb_buffer == NULL) {
2707 return -ENOMEM;
2708 }
2709 smb_buffer_response = smb_buffer;
2710 pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
2711 pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
2712
2713 /* send SMBsessionSetup here */
2714 header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
2715 NULL /* no tCon exists yet */ , 12 /* wct */ );
Steve French1982c342005-08-17 12:38:22 -07002716
2717 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718 pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
2719 pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
2720
2721 pSMB->req.AndXCommand = 0xFF;
2722 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
2723 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
2724
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002725 if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002726 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
2727
2728 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
2729 CAP_EXTENDED_SECURITY;
2730 if (ses->capabilities & CAP_UNICODE) {
2731 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
2732 capabilities |= CAP_UNICODE;
2733 }
2734 if (ses->capabilities & CAP_STATUS32) {
2735 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
2736 capabilities |= CAP_STATUS32;
2737 }
2738 if (ses->capabilities & CAP_DFS) {
2739 smb_buffer->Flags2 |= SMBFLG2_DFS;
2740 capabilities |= CAP_DFS;
2741 }
2742 pSMB->req.Capabilities = cpu_to_le32(capabilities);
2743
2744 bcc_ptr = (char *) &pSMB->req.SecurityBlob;
2745 SecurityBlob = (PNEGOTIATE_MESSAGE) bcc_ptr;
2746 strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
2747 SecurityBlob->MessageType = NtLmNegotiate;
2748 negotiate_flags =
2749 NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_OEM |
Steve French12b3b8f2006-02-09 21:12:47 +00002750 NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
2751 NTLMSSP_NEGOTIATE_56 |
Linus Torvalds1da177e2005-04-16 15:20:36 -07002752 /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN | */ NTLMSSP_NEGOTIATE_128;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002753 if (sign_CIFS_PDUs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 negotiate_flags |= NTLMSSP_NEGOTIATE_SIGN;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002755/* if (ntlmv2_support)
Steve French39798772006-05-31 22:40:51 +00002756 negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757 /* setup pointers to domain name and workstation name */
2758 bcc_ptr += SecurityBlobLength;
2759
2760 SecurityBlob->WorkstationName.Buffer = 0;
2761 SecurityBlob->WorkstationName.Length = 0;
2762 SecurityBlob->WorkstationName.MaximumLength = 0;
2763
Steve French12b3b8f2006-02-09 21:12:47 +00002764 /* Domain not sent on first Sesssetup in NTLMSSP, instead it is sent
2765 along with username on auth request (ie the response to challenge) */
2766 SecurityBlob->DomainName.Buffer = 0;
2767 SecurityBlob->DomainName.Length = 0;
2768 SecurityBlob->DomainName.MaximumLength = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 if (ses->capabilities & CAP_UNICODE) {
2770 if ((long) bcc_ptr % 2) {
2771 *bcc_ptr = 0;
2772 bcc_ptr++;
2773 }
2774
2775 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002776 cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002777 32, nls_codepage);
2778 bcc_ptr += 2 * bytes_returned;
2779 bytes_returned =
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002780 cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002781 nls_codepage);
2782 bcc_ptr += 2 * bytes_returned;
2783 bcc_ptr += 2; /* null terminate Linux version */
2784 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08002785 cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 64, nls_codepage);
2787 bcc_ptr += 2 * bytes_returned;
2788 *(bcc_ptr + 1) = 0;
2789 *(bcc_ptr + 2) = 0;
2790 bcc_ptr += 2; /* null terminate network opsys string */
2791 *(bcc_ptr + 1) = 0;
2792 *(bcc_ptr + 2) = 0;
2793 bcc_ptr += 2; /* null domain */
2794 } else { /* ASCII */
2795 strcpy(bcc_ptr, "Linux version ");
2796 bcc_ptr += strlen("Linux version ");
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07002797 strcpy(bcc_ptr, utsname()->release);
2798 bcc_ptr += strlen(utsname()->release) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
2800 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
2801 bcc_ptr++; /* empty domain field */
2802 *bcc_ptr = 0;
2803 }
2804 SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
2805 pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
2806 count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
2807 smb_buffer->smb_buf_length += count;
2808 pSMB->req.ByteCount = cpu_to_le16(count);
2809
2810 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
Steve French133672e2007-11-13 22:41:37 +00002811 &bytes_returned, CIFS_LONG_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812
2813 if (smb_buffer_response->Status.CifsError ==
2814 cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
2815 rc = 0;
2816
2817 if (rc) {
2818/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
2819 } else if ((smb_buffer_response->WordCount == 3)
2820 || (smb_buffer_response->WordCount == 4)) {
2821 __u16 action = le16_to_cpu(pSMBr->resp.Action);
2822 __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
2823
2824 if (action & GUEST_LOGIN)
Steve French50c2f752007-07-13 00:33:32 +00002825 cFYI(1, (" Guest login"));
2826 /* Do we want to set anything in SesInfo struct when guest login? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827
Steve French50c2f752007-07-13 00:33:32 +00002828 bcc_ptr = pByteArea(smb_buffer_response);
2829 /* response can have either 3 or 4 word count - Samba sends 3 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830
2831 SecurityBlob2 = (PCHALLENGE_MESSAGE) bcc_ptr;
2832 if (SecurityBlob2->MessageType != NtLmChallenge) {
2833 cFYI(1,
2834 ("Unexpected NTLMSSP message type received %d",
2835 SecurityBlob2->MessageType));
2836 } else if (ses) {
Steve French50c2f752007-07-13 00:33:32 +00002837 ses->Suid = smb_buffer_response->Uid; /* UID left in le format */
Steve French12b3b8f2006-02-09 21:12:47 +00002838 cFYI(1, ("UID = %d", ses->Suid));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 if ((pSMBr->resp.hdr.WordCount == 3)
2840 || ((pSMBr->resp.hdr.WordCount == 4)
2841 && (blob_len <
2842 pSMBr->resp.ByteCount))) {
2843
2844 if (pSMBr->resp.hdr.WordCount == 4) {
2845 bcc_ptr += blob_len;
Steve French12b3b8f2006-02-09 21:12:47 +00002846 cFYI(1, ("Security Blob Length %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 blob_len));
2848 }
2849
Steve French12b3b8f2006-02-09 21:12:47 +00002850 cFYI(1, ("NTLMSSP Challenge rcvd"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851
2852 memcpy(ses->server->cryptKey,
2853 SecurityBlob2->Challenge,
2854 CIFS_CRYPTO_KEY_SIZE);
Steve French50c2f752007-07-13 00:33:32 +00002855 if (SecurityBlob2->NegotiateFlags &
Steve French12b3b8f2006-02-09 21:12:47 +00002856 cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2))
Steve French4b18f2a2008-04-29 00:06:05 +00002857 *pNTLMv2_flag = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858
Steve French50c2f752007-07-13 00:33:32 +00002859 if ((SecurityBlob2->NegotiateFlags &
2860 cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861 || (sign_CIFS_PDUs > 1))
Steve French50c2f752007-07-13 00:33:32 +00002862 ses->server->secMode |=
2863 SECMODE_SIGN_REQUIRED;
2864 if ((SecurityBlob2->NegotiateFlags &
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865 cpu_to_le32(NTLMSSP_NEGOTIATE_SIGN)) && (sign_CIFS_PDUs))
Steve French50c2f752007-07-13 00:33:32 +00002866 ses->server->secMode |=
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867 SECMODE_SIGN_ENABLED;
2868
2869 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
2870 if ((long) (bcc_ptr) % 2) {
2871 remaining_words =
2872 (BCC(smb_buffer_response)
2873 - 1) / 2;
Steve French50c2f752007-07-13 00:33:32 +00002874 /* Must word align unicode strings */
2875 bcc_ptr++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 } else {
2877 remaining_words =
2878 BCC
2879 (smb_buffer_response) / 2;
2880 }
2881 len =
2882 UniStrnlen((wchar_t *) bcc_ptr,
2883 remaining_words - 1);
2884/* We look for obvious messed up bcc or strings in response so we do not go off
2885 the end since (at least) WIN2K and Windows XP have a major bug in not null
2886 terminating last Unicode string in response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002887 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00002888 kfree(ses->serverOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 ses->serverOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002890 kzalloc(2 * (len + 1), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002891 cifs_strfromUCS_le(ses->serverOS,
Steve Frenche89dc922005-11-11 15:18:19 -08002892 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 bcc_ptr, len,
2894 nls_codepage);
2895 bcc_ptr += 2 * (len + 1);
2896 remaining_words -= len + 1;
2897 ses->serverOS[2 * len] = 0;
2898 ses->serverOS[1 + (2 * len)] = 0;
2899 if (remaining_words > 0) {
2900 len = UniStrnlen((wchar_t *)
2901 bcc_ptr,
2902 remaining_words
2903 - 1);
Steve Frenchcd49b492006-06-26 04:22:36 +00002904 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002905 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002906 kzalloc(2 * (len + 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 GFP_KERNEL);
2908 cifs_strfromUCS_le(ses->
2909 serverNOS,
Steve Frenche89dc922005-11-11 15:18:19 -08002910 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911 bcc_ptr,
2912 len,
2913 nls_codepage);
2914 bcc_ptr += 2 * (len + 1);
2915 ses->serverNOS[2 * len] = 0;
2916 ses->serverNOS[1 +
2917 (2 * len)] = 0;
2918 remaining_words -= len + 1;
2919 if (remaining_words > 0) {
Steve French50c2f752007-07-13 00:33:32 +00002920 len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
2921 /* last string not always null terminated
2922 (for e.g. for Windows XP & 2000) */
Steve Frenchcd49b492006-06-26 04:22:36 +00002923 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002925 kzalloc(2 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 (len +
2927 1),
2928 GFP_KERNEL);
2929 cifs_strfromUCS_le
Steve Frenche89dc922005-11-11 15:18:19 -08002930 (ses->serverDomain,
2931 (__le16 *)bcc_ptr,
2932 len, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002933 bcc_ptr +=
2934 2 * (len + 1);
Steve Frenche89dc922005-11-11 15:18:19 -08002935 ses->serverDomain[2*len]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936 = 0;
Steve Frenche89dc922005-11-11 15:18:19 -08002937 ses->serverDomain
2938 [1 + (2 * len)]
Linus Torvalds1da177e2005-04-16 15:20:36 -07002939 = 0;
2940 } /* else no more room so create dummy domain string */
Steve Frencha424f8b2006-05-30 18:06:04 +00002941 else {
Steve Frenchcd49b492006-06-26 04:22:36 +00002942 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002944 kzalloc(2,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 GFP_KERNEL);
Steve Frencha424f8b2006-05-30 18:06:04 +00002946 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947 } else { /* no room so create dummy domain and NOS string */
Steve Frenchcd49b492006-06-26 04:22:36 +00002948 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002950 kzalloc(2, GFP_KERNEL);
Steve Frenchcd49b492006-06-26 04:22:36 +00002951 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002953 kzalloc(2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954 }
2955 } else { /* ASCII */
2956 len = strnlen(bcc_ptr, 1024);
2957 if (((long) bcc_ptr + len) - (long)
2958 pByteArea(smb_buffer_response)
2959 <= BCC(smb_buffer_response)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00002960 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00002961 kfree(ses->serverOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962 ses->serverOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002963 kzalloc(len + 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002964 GFP_KERNEL);
2965 strncpy(ses->serverOS,
2966 bcc_ptr, len);
2967
2968 bcc_ptr += len;
2969 bcc_ptr[0] = 0; /* null terminate string */
2970 bcc_ptr++;
2971
2972 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00002973 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002974 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07002975 kzalloc(len + 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002976 GFP_KERNEL);
2977 strncpy(ses->serverNOS, bcc_ptr, len);
2978 bcc_ptr += len;
2979 bcc_ptr[0] = 0;
2980 bcc_ptr++;
2981
2982 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00002983 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002984 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07002985 kzalloc(len + 1,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002986 GFP_KERNEL);
Steve French50c2f752007-07-13 00:33:32 +00002987 strncpy(ses->serverDomain,
2988 bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002989 bcc_ptr += len;
2990 bcc_ptr[0] = 0;
2991 bcc_ptr++;
2992 } else
2993 cFYI(1,
Steve French63135e02007-07-17 17:34:02 +00002994 ("field of length %d "
2995 "extends beyond end of smb",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002996 len));
2997 }
2998 } else {
Steve French50c2f752007-07-13 00:33:32 +00002999 cERROR(1, ("Security Blob Length extends beyond"
3000 " end of SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003001 }
3002 } else {
3003 cERROR(1, ("No session structure passed in."));
3004 }
3005 } else {
3006 cERROR(1,
Steve French5815449d2006-02-14 01:36:20 +00003007 (" Invalid Word count %d:",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008 smb_buffer_response->WordCount));
3009 rc = -EIO;
3010 }
3011
Mariusz Kozlowskia8a11d32007-10-03 16:41:24 +00003012 cifs_buf_release(smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003013
3014 return rc;
3015}
3016static int
3017CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
Steve French4b18f2a2008-04-29 00:06:05 +00003018 char *ntlm_session_key, bool ntlmv2_flag,
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003019 const struct nls_table *nls_codepage)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020{
3021 struct smb_hdr *smb_buffer;
3022 struct smb_hdr *smb_buffer_response;
3023 SESSION_SETUP_ANDX *pSMB;
3024 SESSION_SETUP_ANDX *pSMBr;
3025 char *bcc_ptr;
3026 char *user;
3027 char *domain;
3028 int rc = 0;
3029 int remaining_words = 0;
3030 int bytes_returned = 0;
3031 int len;
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003032 int SecurityBlobLength = sizeof(AUTHENTICATE_MESSAGE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003033 PAUTHENTICATE_MESSAGE SecurityBlob;
3034 __u32 negotiate_flags, capabilities;
3035 __u16 count;
3036
3037 cFYI(1, ("In NTLMSSPSessSetup (Authenticate)"));
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003038 if (ses == NULL)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003039 return -EINVAL;
3040 user = ses->userName;
3041 domain = ses->domainName;
3042 smb_buffer = cifs_buf_get();
3043 if (smb_buffer == NULL) {
3044 return -ENOMEM;
3045 }
3046 smb_buffer_response = smb_buffer;
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003047 pSMB = (SESSION_SETUP_ANDX *)smb_buffer;
3048 pSMBr = (SESSION_SETUP_ANDX *)smb_buffer_response;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049
3050 /* send SMBsessionSetup here */
3051 header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
3052 NULL /* no tCon exists yet */ , 12 /* wct */ );
Steve French1982c342005-08-17 12:38:22 -07003053
3054 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003055 pSMB->req.hdr.Flags |= (SMBFLG_CASELESS | SMBFLG_CANONICAL_PATH_FORMAT);
3056 pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
3057 pSMB->req.AndXCommand = 0xFF;
3058 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
3059 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
3060
3061 pSMB->req.hdr.Uid = ses->Suid;
3062
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003063 if (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
3065
3066 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003067 CAP_EXTENDED_SECURITY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003068 if (ses->capabilities & CAP_UNICODE) {
3069 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
3070 capabilities |= CAP_UNICODE;
3071 }
3072 if (ses->capabilities & CAP_STATUS32) {
3073 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
3074 capabilities |= CAP_STATUS32;
3075 }
3076 if (ses->capabilities & CAP_DFS) {
3077 smb_buffer->Flags2 |= SMBFLG2_DFS;
3078 capabilities |= CAP_DFS;
3079 }
3080 pSMB->req.Capabilities = cpu_to_le32(capabilities);
3081
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003082 bcc_ptr = (char *)&pSMB->req.SecurityBlob;
3083 SecurityBlob = (PAUTHENTICATE_MESSAGE)bcc_ptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003084 strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
3085 SecurityBlob->MessageType = NtLmAuthenticate;
3086 bcc_ptr += SecurityBlobLength;
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003087 negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
3088 NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
3089 0x80000000 | NTLMSSP_NEGOTIATE_128;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003090 if (sign_CIFS_PDUs)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091 negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003092 if (ntlmv2_flag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093 negotiate_flags |= NTLMSSP_NEGOTIATE_NTLMV2;
3094
3095/* setup pointers to domain name and workstation name */
3096
3097 SecurityBlob->WorkstationName.Buffer = 0;
3098 SecurityBlob->WorkstationName.Length = 0;
3099 SecurityBlob->WorkstationName.MaximumLength = 0;
3100 SecurityBlob->SessionKey.Length = 0;
3101 SecurityBlob->SessionKey.MaximumLength = 0;
3102 SecurityBlob->SessionKey.Buffer = 0;
3103
3104 SecurityBlob->LmChallengeResponse.Length = 0;
3105 SecurityBlob->LmChallengeResponse.MaximumLength = 0;
3106 SecurityBlob->LmChallengeResponse.Buffer = 0;
3107
3108 SecurityBlob->NtChallengeResponse.Length =
Steve French7c7b25b2006-06-01 19:20:10 +00003109 cpu_to_le16(CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003110 SecurityBlob->NtChallengeResponse.MaximumLength =
Steve French7c7b25b2006-06-01 19:20:10 +00003111 cpu_to_le16(CIFS_SESS_KEY_SIZE);
3112 memcpy(bcc_ptr, ntlm_session_key, CIFS_SESS_KEY_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003113 SecurityBlob->NtChallengeResponse.Buffer =
3114 cpu_to_le32(SecurityBlobLength);
Steve French7c7b25b2006-06-01 19:20:10 +00003115 SecurityBlobLength += CIFS_SESS_KEY_SIZE;
3116 bcc_ptr += CIFS_SESS_KEY_SIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003117
3118 if (ses->capabilities & CAP_UNICODE) {
3119 if (domain == NULL) {
3120 SecurityBlob->DomainName.Buffer = 0;
3121 SecurityBlob->DomainName.Length = 0;
3122 SecurityBlob->DomainName.MaximumLength = 0;
3123 } else {
Steve French77159b42007-08-31 01:10:17 +00003124 __u16 ln = cifs_strtoUCS((__le16 *) bcc_ptr, domain, 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003125 nls_codepage);
Steve French77159b42007-08-31 01:10:17 +00003126 ln *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003127 SecurityBlob->DomainName.MaximumLength =
Steve French77159b42007-08-31 01:10:17 +00003128 cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003129 SecurityBlob->DomainName.Buffer =
3130 cpu_to_le32(SecurityBlobLength);
Steve French77159b42007-08-31 01:10:17 +00003131 bcc_ptr += ln;
3132 SecurityBlobLength += ln;
3133 SecurityBlob->DomainName.Length = cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134 }
3135 if (user == NULL) {
3136 SecurityBlob->UserName.Buffer = 0;
3137 SecurityBlob->UserName.Length = 0;
3138 SecurityBlob->UserName.MaximumLength = 0;
3139 } else {
Steve French77159b42007-08-31 01:10:17 +00003140 __u16 ln = cifs_strtoUCS((__le16 *) bcc_ptr, user, 64,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003141 nls_codepage);
Steve French77159b42007-08-31 01:10:17 +00003142 ln *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003143 SecurityBlob->UserName.MaximumLength =
Steve French77159b42007-08-31 01:10:17 +00003144 cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 SecurityBlob->UserName.Buffer =
3146 cpu_to_le32(SecurityBlobLength);
Steve French77159b42007-08-31 01:10:17 +00003147 bcc_ptr += ln;
3148 SecurityBlobLength += ln;
3149 SecurityBlob->UserName.Length = cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150 }
3151
Steve French63135e02007-07-17 17:34:02 +00003152 /* SecurityBlob->WorkstationName.Length =
3153 cifs_strtoUCS((__le16 *) bcc_ptr, "AMACHINE",64, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003154 SecurityBlob->WorkstationName.Length *= 2;
Steve French63135e02007-07-17 17:34:02 +00003155 SecurityBlob->WorkstationName.MaximumLength =
3156 cpu_to_le16(SecurityBlob->WorkstationName.Length);
3157 SecurityBlob->WorkstationName.Buffer =
3158 cpu_to_le32(SecurityBlobLength);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003159 bcc_ptr += SecurityBlob->WorkstationName.Length;
3160 SecurityBlobLength += SecurityBlob->WorkstationName.Length;
Steve French63135e02007-07-17 17:34:02 +00003161 SecurityBlob->WorkstationName.Length =
3162 cpu_to_le16(SecurityBlob->WorkstationName.Length); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003163
3164 if ((long) bcc_ptr % 2) {
3165 *bcc_ptr = 0;
3166 bcc_ptr++;
3167 }
3168 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08003169 cifs_strtoUCS((__le16 *) bcc_ptr, "Linux version ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003170 32, nls_codepage);
3171 bcc_ptr += 2 * bytes_returned;
3172 bytes_returned =
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07003173 cifs_strtoUCS((__le16 *) bcc_ptr, utsname()->release, 32,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003174 nls_codepage);
3175 bcc_ptr += 2 * bytes_returned;
3176 bcc_ptr += 2; /* null term version string */
3177 bytes_returned =
Steve Frenche89dc922005-11-11 15:18:19 -08003178 cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179 64, nls_codepage);
3180 bcc_ptr += 2 * bytes_returned;
3181 *(bcc_ptr + 1) = 0;
3182 *(bcc_ptr + 2) = 0;
3183 bcc_ptr += 2; /* null terminate network opsys string */
3184 *(bcc_ptr + 1) = 0;
3185 *(bcc_ptr + 2) = 0;
3186 bcc_ptr += 2; /* null domain */
3187 } else { /* ASCII */
3188 if (domain == NULL) {
3189 SecurityBlob->DomainName.Buffer = 0;
3190 SecurityBlob->DomainName.Length = 0;
3191 SecurityBlob->DomainName.MaximumLength = 0;
3192 } else {
Steve French77159b42007-08-31 01:10:17 +00003193 __u16 ln;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 negotiate_flags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
3195 strncpy(bcc_ptr, domain, 63);
Steve French77159b42007-08-31 01:10:17 +00003196 ln = strnlen(domain, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003197 SecurityBlob->DomainName.MaximumLength =
Steve French77159b42007-08-31 01:10:17 +00003198 cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003199 SecurityBlob->DomainName.Buffer =
3200 cpu_to_le32(SecurityBlobLength);
Steve French77159b42007-08-31 01:10:17 +00003201 bcc_ptr += ln;
3202 SecurityBlobLength += ln;
3203 SecurityBlob->DomainName.Length = cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204 }
3205 if (user == NULL) {
3206 SecurityBlob->UserName.Buffer = 0;
3207 SecurityBlob->UserName.Length = 0;
3208 SecurityBlob->UserName.MaximumLength = 0;
3209 } else {
Steve French77159b42007-08-31 01:10:17 +00003210 __u16 ln;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003211 strncpy(bcc_ptr, user, 63);
Steve French77159b42007-08-31 01:10:17 +00003212 ln = strnlen(user, 64);
3213 SecurityBlob->UserName.MaximumLength = cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 SecurityBlob->UserName.Buffer =
Steve French77159b42007-08-31 01:10:17 +00003215 cpu_to_le32(SecurityBlobLength);
3216 bcc_ptr += ln;
3217 SecurityBlobLength += ln;
3218 SecurityBlob->UserName.Length = cpu_to_le16(ln);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 }
3220 /* BB fill in our workstation name if known BB */
3221
3222 strcpy(bcc_ptr, "Linux version ");
3223 bcc_ptr += strlen("Linux version ");
Serge E. Hallyne9ff3992006-10-02 02:18:11 -07003224 strcpy(bcc_ptr, utsname()->release);
3225 bcc_ptr += strlen(utsname()->release) + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
3227 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
3228 bcc_ptr++; /* null domain */
3229 *bcc_ptr = 0;
3230 }
3231 SecurityBlob->NegotiateFlags = cpu_to_le32(negotiate_flags);
3232 pSMB->req.SecurityBlobLength = cpu_to_le16(SecurityBlobLength);
3233 count = (long) bcc_ptr - (long) pByteArea(smb_buffer);
3234 smb_buffer->smb_buf_length += count;
3235 pSMB->req.ByteCount = cpu_to_le16(count);
3236
3237 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
Steve French133672e2007-11-13 22:41:37 +00003238 &bytes_returned, CIFS_LONG_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003239 if (rc) {
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003240/* rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */
3241 } else if ((smb_buffer_response->WordCount == 3) ||
3242 (smb_buffer_response->WordCount == 4)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 __u16 action = le16_to_cpu(pSMBr->resp.Action);
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003244 __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003245 if (action & GUEST_LOGIN)
Steve French50c2f752007-07-13 00:33:32 +00003246 cFYI(1, (" Guest login")); /* BB Should we set anything
3247 in SesInfo struct ? */
3248/* if (SecurityBlob2->MessageType != NtLm??) {
3249 cFYI("Unexpected message type on auth response is %d"));
3250 } */
3251
Linus Torvalds1da177e2005-04-16 15:20:36 -07003252 if (ses) {
3253 cFYI(1,
Steve French50c2f752007-07-13 00:33:32 +00003254 ("Check challenge UID %d vs auth response UID %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003255 ses->Suid, smb_buffer_response->Uid));
Steve French50c2f752007-07-13 00:33:32 +00003256 /* UID left in wire format */
3257 ses->Suid = smb_buffer_response->Uid;
3258 bcc_ptr = pByteArea(smb_buffer_response);
3259 /* response can have either 3 or 4 word count - Samba sends 3 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003260 if ((pSMBr->resp.hdr.WordCount == 3)
3261 || ((pSMBr->resp.hdr.WordCount == 4)
3262 && (blob_len <
3263 pSMBr->resp.ByteCount))) {
3264 if (pSMBr->resp.hdr.WordCount == 4) {
3265 bcc_ptr +=
3266 blob_len;
3267 cFYI(1,
3268 ("Security Blob Length %d ",
3269 blob_len));
3270 }
3271
3272 cFYI(1,
3273 ("NTLMSSP response to Authenticate "));
3274
3275 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
3276 if ((long) (bcc_ptr) % 2) {
3277 remaining_words =
3278 (BCC(smb_buffer_response)
3279 - 1) / 2;
3280 bcc_ptr++; /* Unicode strings must be word aligned */
3281 } else {
3282 remaining_words = BCC(smb_buffer_response) / 2;
3283 }
Steve French77159b42007-08-31 01:10:17 +00003284 len = UniStrnlen((wchar_t *) bcc_ptr,
3285 remaining_words - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003286/* We look for obvious messed up bcc or strings in response so we do not go off
3287 the end since (at least) WIN2K and Windows XP have a major bug in not null
3288 terminating last Unicode string in response */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003289 if (ses->serverOS)
Steve French08775832006-05-30 18:08:26 +00003290 kfree(ses->serverOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003291 ses->serverOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07003292 kzalloc(2 * (len + 1), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003293 cifs_strfromUCS_le(ses->serverOS,
Steve Frenche89dc922005-11-11 15:18:19 -08003294 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003295 bcc_ptr, len,
3296 nls_codepage);
3297 bcc_ptr += 2 * (len + 1);
3298 remaining_words -= len + 1;
3299 ses->serverOS[2 * len] = 0;
3300 ses->serverOS[1 + (2 * len)] = 0;
3301 if (remaining_words > 0) {
3302 len = UniStrnlen((wchar_t *)
3303 bcc_ptr,
3304 remaining_words
3305 - 1);
Steve Frenchcd49b492006-06-26 04:22:36 +00003306 kfree(ses->serverNOS);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003307 ses->serverNOS =
Pekka Enberge915fc42005-09-06 15:18:35 -07003308 kzalloc(2 * (len + 1),
Linus Torvalds1da177e2005-04-16 15:20:36 -07003309 GFP_KERNEL);
3310 cifs_strfromUCS_le(ses->
3311 serverNOS,
Steve Frenche89dc922005-11-11 15:18:19 -08003312 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003313 bcc_ptr,
3314 len,
3315 nls_codepage);
3316 bcc_ptr += 2 * (len + 1);
3317 ses->serverNOS[2 * len] = 0;
3318 ses->serverNOS[1+(2*len)] = 0;
3319 remaining_words -= len + 1;
3320 if (remaining_words > 0) {
Steve French50c2f752007-07-13 00:33:32 +00003321 len = UniStrnlen((wchar_t *) bcc_ptr, remaining_words);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003322 /* last string not always null terminated (e.g. for Windows XP & 2000) */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003323 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003324 kfree(ses->serverDomain);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003325 ses->serverDomain =
Pekka Enberge915fc42005-09-06 15:18:35 -07003326 kzalloc(2 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07003327 (len +
3328 1),
3329 GFP_KERNEL);
3330 cifs_strfromUCS_le
3331 (ses->
3332 serverDomain,
Steve Frenche89dc922005-11-11 15:18:19 -08003333 (__le16 *)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003334 bcc_ptr, len,
3335 nls_codepage);
3336 bcc_ptr +=
3337 2 * (len + 1);
3338 ses->
3339 serverDomain[2
3340 * len]
3341 = 0;
3342 ses->
3343 serverDomain[1
3344 +
3345 (2
3346 *
3347 len)]
3348 = 0;
3349 } /* else no more room so create dummy domain string */
Steve Frencha424f8b2006-05-30 18:06:04 +00003350 else {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003351 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003352 kfree(ses->serverDomain);
Pekka Enberge915fc42005-09-06 15:18:35 -07003353 ses->serverDomain = kzalloc(2,GFP_KERNEL);
Steve Frencha424f8b2006-05-30 18:06:04 +00003354 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003355 } else { /* no room so create dummy domain and NOS string */
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003356 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003357 kfree(ses->serverDomain);
Pekka Enberge915fc42005-09-06 15:18:35 -07003358 ses->serverDomain = kzalloc(2, GFP_KERNEL);
Steve Frenchcd49b492006-06-26 04:22:36 +00003359 kfree(ses->serverNOS);
Pekka Enberge915fc42005-09-06 15:18:35 -07003360 ses->serverNOS = kzalloc(2, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361 }
3362 } else { /* ASCII */
3363 len = strnlen(bcc_ptr, 1024);
Steve French50c2f752007-07-13 00:33:32 +00003364 if (((long) bcc_ptr + len) -
3365 (long) pByteArea(smb_buffer_response)
Steve French63135e02007-07-17 17:34:02 +00003366 <= BCC(smb_buffer_response)) {
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003367 if (ses->serverOS)
Steve Frencha424f8b2006-05-30 18:06:04 +00003368 kfree(ses->serverOS);
Steve French77159b42007-08-31 01:10:17 +00003369 ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003370 strncpy(ses->serverOS,bcc_ptr, len);
3371
3372 bcc_ptr += len;
3373 bcc_ptr[0] = 0; /* null terminate the string */
3374 bcc_ptr++;
3375
3376 len = strnlen(bcc_ptr, 1024);
Steve Frenchcd49b492006-06-26 04:22:36 +00003377 kfree(ses->serverNOS);
Steve French50c2f752007-07-13 00:33:32 +00003378 ses->serverNOS = kzalloc(len+1,
3379 GFP_KERNEL);
Steve French63135e02007-07-17 17:34:02 +00003380 strncpy(ses->serverNOS,
3381 bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003382 bcc_ptr += len;
3383 bcc_ptr[0] = 0;
3384 bcc_ptr++;
3385
3386 len = strnlen(bcc_ptr, 1024);
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003387 if (ses->serverDomain)
Steve Frencha424f8b2006-05-30 18:06:04 +00003388 kfree(ses->serverDomain);
Steve French63135e02007-07-17 17:34:02 +00003389 ses->serverDomain =
3390 kzalloc(len+1,
3391 GFP_KERNEL);
3392 strncpy(ses->serverDomain,
3393 bcc_ptr, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003394 bcc_ptr += len;
3395 bcc_ptr[0] = 0;
3396 bcc_ptr++;
3397 } else
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003398 cFYI(1, ("field of length %d "
Steve French63135e02007-07-17 17:34:02 +00003399 "extends beyond end of smb ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 len));
3401 }
3402 } else {
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003403 cERROR(1, ("Security Blob extends beyond end "
Steve French63135e02007-07-17 17:34:02 +00003404 "of SMB"));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003405 }
3406 } else {
3407 cERROR(1, ("No session structure passed in."));
3408 }
3409 } else {
Cyrill Gorcunov6345a3a2007-10-16 17:57:55 +00003410 cERROR(1, ("Invalid Word count %d: ",
Linus Torvalds1da177e2005-04-16 15:20:36 -07003411 smb_buffer_response->WordCount));
3412 rc = -EIO;
3413 }
3414
Mariusz Kozlowskia8a11d32007-10-03 16:41:24 +00003415 cifs_buf_release(smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003416
3417 return rc;
3418}
3419
3420int
3421CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
3422 const char *tree, struct cifsTconInfo *tcon,
3423 const struct nls_table *nls_codepage)
3424{
3425 struct smb_hdr *smb_buffer;
3426 struct smb_hdr *smb_buffer_response;
3427 TCONX_REQ *pSMB;
3428 TCONX_RSP *pSMBr;
3429 unsigned char *bcc_ptr;
3430 int rc = 0;
3431 int length;
3432 __u16 count;
3433
3434 if (ses == NULL)
3435 return -EIO;
3436
3437 smb_buffer = cifs_buf_get();
3438 if (smb_buffer == NULL) {
3439 return -ENOMEM;
3440 }
3441 smb_buffer_response = smb_buffer;
3442
3443 header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
3444 NULL /*no tid */ , 4 /*wct */ );
Steve French1982c342005-08-17 12:38:22 -07003445
3446 smb_buffer->Mid = GetNextMid(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003447 smb_buffer->Uid = ses->Suid;
3448 pSMB = (TCONX_REQ *) smb_buffer;
3449 pSMBr = (TCONX_RSP *) smb_buffer_response;
3450
3451 pSMB->AndXCommand = 0xFF;
3452 pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003453 bcc_ptr = &pSMB->Password[0];
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003454 if ((ses->server->secMode) & SECMODE_USER) {
Steve Frencheeac8042006-01-13 21:34:58 -08003455 pSMB->PasswordLength = cpu_to_le16(1); /* minimum */
Steve French7c7b25b2006-06-01 19:20:10 +00003456 *bcc_ptr = 0; /* password is null byte */
Steve Frencheeac8042006-01-13 21:34:58 -08003457 bcc_ptr++; /* skip password */
Steve French7c7b25b2006-06-01 19:20:10 +00003458 /* already aligned so no need to do it below */
Steve Frencheeac8042006-01-13 21:34:58 -08003459 } else {
Steve French7c7b25b2006-06-01 19:20:10 +00003460 pSMB->PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE);
Steve Frencheeac8042006-01-13 21:34:58 -08003461 /* BB FIXME add code to fail this if NTLMv2 or Kerberos
3462 specified as required (when that support is added to
3463 the vfs in the future) as only NTLM or the much
Steve French7c7b25b2006-06-01 19:20:10 +00003464 weaker LANMAN (which we do not send by default) is accepted
Steve Frencheeac8042006-01-13 21:34:58 -08003465 by Samba (not sure whether other servers allow
3466 NTLMv2 password here) */
Steve French7c7b25b2006-06-01 19:20:10 +00003467#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French50c2f752007-07-13 00:33:32 +00003468 if ((extended_security & CIFSSEC_MAY_LANMAN) &&
Steve French7c7b25b2006-06-01 19:20:10 +00003469 (ses->server->secType == LANMAN))
3470 calc_lanman_hash(ses, bcc_ptr);
3471 else
3472#endif /* CIFS_WEAK_PW_HASH */
Steve Frencheeac8042006-01-13 21:34:58 -08003473 SMBNTencrypt(ses->password,
3474 ses->server->cryptKey,
3475 bcc_ptr);
3476
Steve French7c7b25b2006-06-01 19:20:10 +00003477 bcc_ptr += CIFS_SESS_KEY_SIZE;
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003478 if (ses->capabilities & CAP_UNICODE) {
Steve French7c7b25b2006-06-01 19:20:10 +00003479 /* must align unicode strings */
3480 *bcc_ptr = 0; /* null byte password */
3481 bcc_ptr++;
3482 }
Steve Frencheeac8042006-01-13 21:34:58 -08003483 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003484
Steve French50c2f752007-07-13 00:33:32 +00003485 if (ses->server->secMode &
Steve Frencha878fb22006-05-30 18:04:19 +00003486 (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07003487 smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
3488
3489 if (ses->capabilities & CAP_STATUS32) {
3490 smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
3491 }
3492 if (ses->capabilities & CAP_DFS) {
3493 smb_buffer->Flags2 |= SMBFLG2_DFS;
3494 }
3495 if (ses->capabilities & CAP_UNICODE) {
3496 smb_buffer->Flags2 |= SMBFLG2_UNICODE;
3497 length =
Steve French50c2f752007-07-13 00:33:32 +00003498 cifs_strtoUCS((__le16 *) bcc_ptr, tree,
3499 6 /* max utf8 char length in bytes */ *
Steve Frencha878fb22006-05-30 18:04:19 +00003500 (/* server len*/ + 256 /* share len */), nls_codepage);
3501 bcc_ptr += 2 * length; /* convert num 16 bit words to bytes */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003502 bcc_ptr += 2; /* skip trailing null */
3503 } else { /* ASCII */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003504 strcpy(bcc_ptr, tree);
3505 bcc_ptr += strlen(tree) + 1;
3506 }
3507 strcpy(bcc_ptr, "?????");
3508 bcc_ptr += strlen("?????");
3509 bcc_ptr += 1;
3510 count = bcc_ptr - &pSMB->Password[0];
3511 pSMB->hdr.smb_buf_length += count;
3512 pSMB->ByteCount = cpu_to_le16(count);
3513
Steve French133672e2007-11-13 22:41:37 +00003514 rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
3515 CIFS_STD_OP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516
3517 /* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
3518 /* above now done in SendReceive */
3519 if ((rc == 0) && (tcon != NULL)) {
3520 tcon->tidStatus = CifsGood;
3521 tcon->tid = smb_buffer_response->Tid;
3522 bcc_ptr = pByteArea(smb_buffer_response);
3523 length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
Steve French50c2f752007-07-13 00:33:32 +00003524 /* skip service field (NB: this field is always ASCII) */
Steve French7f8ed422007-09-28 22:28:55 +00003525 if (length == 3) {
3526 if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
3527 (bcc_ptr[2] == 'C')) {
3528 cFYI(1, ("IPC connection"));
3529 tcon->ipc = 1;
3530 }
3531 } else if (length == 2) {
3532 if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
3533 /* the most common case */
3534 cFYI(1, ("disk share connection"));
3535 }
3536 }
Steve French50c2f752007-07-13 00:33:32 +00003537 bcc_ptr += length + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003538 strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
3539 if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
3540 length = UniStrnlen((wchar_t *) bcc_ptr, 512);
3541 if ((bcc_ptr + (2 * length)) -
3542 pByteArea(smb_buffer_response) <=
3543 BCC(smb_buffer_response)) {
Jesper Juhlf99d49a2005-11-07 01:01:34 -08003544 kfree(tcon->nativeFileSystem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003545 tcon->nativeFileSystem =
Pekka Enberge915fc42005-09-06 15:18:35 -07003546 kzalloc(length + 2, GFP_KERNEL);
Steve French88f370a2007-09-15 03:01:17 +00003547 if (tcon->nativeFileSystem)
3548 cifs_strfromUCS_le(
3549 tcon->nativeFileSystem,
3550 (__le16 *) bcc_ptr,
3551 length, nls_codepage);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003552 bcc_ptr += 2 * length;
3553 bcc_ptr[0] = 0; /* null terminate the string */
3554 bcc_ptr[1] = 0;
3555 bcc_ptr += 2;
3556 }
Steve French50c2f752007-07-13 00:33:32 +00003557 /* else do not bother copying these information fields*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003558 } else {
3559 length = strnlen(bcc_ptr, 1024);
3560 if ((bcc_ptr + length) -
3561 pByteArea(smb_buffer_response) <=
3562 BCC(smb_buffer_response)) {
Jesper Juhlf99d49a2005-11-07 01:01:34 -08003563 kfree(tcon->nativeFileSystem);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003564 tcon->nativeFileSystem =
Pekka Enberge915fc42005-09-06 15:18:35 -07003565 kzalloc(length + 1, GFP_KERNEL);
Steve French88f370a2007-09-15 03:01:17 +00003566 if (tcon->nativeFileSystem)
3567 strncpy(tcon->nativeFileSystem, bcc_ptr,
3568 length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003569 }
Steve French50c2f752007-07-13 00:33:32 +00003570 /* else do not bother copying these information fields*/
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003572 if ((smb_buffer_response->WordCount == 3) ||
Steve French1a4e15a2006-10-12 21:33:51 +00003573 (smb_buffer_response->WordCount == 7))
3574 /* field is in same location */
Steve French39798772006-05-31 22:40:51 +00003575 tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
3576 else
3577 tcon->Flags = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003578 cFYI(1, ("Tcon flags: 0x%x ", tcon->Flags));
3579 } else if ((rc == 0) && tcon == NULL) {
Steve French50c2f752007-07-13 00:33:32 +00003580 /* all we need to save for IPC$ connection */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003581 ses->ipc_tid = smb_buffer_response->Tid;
3582 }
3583
Mariusz Kozlowskia8a11d32007-10-03 16:41:24 +00003584 cifs_buf_release(smb_buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003585 return rc;
3586}
3587
3588int
3589cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3590{
3591 int rc = 0;
3592 int xid;
3593 struct cifsSesInfo *ses = NULL;
Steve French50c2f752007-07-13 00:33:32 +00003594 char *tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003595
3596 xid = GetXid();
3597
3598 if (cifs_sb->tcon) {
3599 ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
3600 rc = CIFSSMBTDis(xid, cifs_sb->tcon);
3601 if (rc == -EBUSY) {
3602 FreeXid(xid);
3603 return 0;
3604 }
Steve French5d941ca2008-04-15 18:40:48 +00003605 DeleteTconOplockQEntries(cifs_sb->tcon);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606 tconInfoFree(cifs_sb->tcon);
3607 if ((ses) && (ses->server)) {
3608 /* save off task so we do not refer to ses later */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003609 cFYI(1, ("About to do SMBLogoff "));
3610 rc = CIFSSMBLogoff(xid, ses);
3611 if (rc == -EBUSY) {
3612 FreeXid(xid);
3613 return 0;
3614 } else if (rc == -ESHUTDOWN) {
Steve French467a8f82007-06-27 22:41:32 +00003615 cFYI(1, ("Waking up socket by sending signal"));
Jeff Laytonb1c8d2b2008-10-22 13:57:07 -04003616 if (ses->server)
3617 kill_cifsd(ses->server);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003618 rc = 0;
3619 } /* else - we have an smb session
3620 left on this socket do not kill cifsd */
3621 } else
3622 cFYI(1, ("No session or bad tcon"));
3623 }
Steve French50c2f752007-07-13 00:33:32 +00003624
Linus Torvalds1da177e2005-04-16 15:20:36 -07003625 cifs_sb->tcon = NULL;
Steve French2fe87f02006-09-21 07:02:52 +00003626 tmp = cifs_sb->prepath;
3627 cifs_sb->prepathlen = 0;
3628 cifs_sb->prepath = NULL;
3629 kfree(tmp);
Nishanth Aravamudan041e0e32005-09-10 00:27:23 -07003630 if (ses)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003631 sesInfoFree(ses);
3632
3633 FreeXid(xid);
Steve French88e7d702008-01-03 17:37:09 +00003634 return rc;
Steve French50c2f752007-07-13 00:33:32 +00003635}
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636
3637int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
Steve French50c2f752007-07-13 00:33:32 +00003638 struct nls_table *nls_info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639{
3640 int rc = 0;
Steve French7c7b25b2006-06-01 19:20:10 +00003641 char ntlm_session_key[CIFS_SESS_KEY_SIZE];
Steve French4b18f2a2008-04-29 00:06:05 +00003642 bool ntlmv2_flag = false;
Steve Frenchad009ac2005-04-28 22:41:05 -07003643 int first_time = 0;
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003644 struct TCP_Server_Info *server = pSesInfo->server;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003645
3646 /* what if server changes its buffer size after dropping the session? */
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003647 if (server->maxBuf == 0) /* no need to send on reconnect */ {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003648 rc = CIFSSMBNegotiate(xid, pSesInfo);
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003649 if (rc == -EAGAIN) {
3650 /* retry only once on 1st time connection */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003651 rc = CIFSSMBNegotiate(xid, pSesInfo);
Steve French50c2f752007-07-13 00:33:32 +00003652 if (rc == -EAGAIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003653 rc = -EHOSTDOWN;
3654 }
Steve Frenchfb8c4b12007-07-10 01:16:18 +00003655 if (rc == 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003656 spin_lock(&GlobalMid_Lock);
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003657 if (server->tcpStatus != CifsExiting)
3658 server->tcpStatus = CifsGood;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003659 else
3660 rc = -EHOSTDOWN;
3661 spin_unlock(&GlobalMid_Lock);
3662
3663 }
Steve Frenchad009ac2005-04-28 22:41:05 -07003664 first_time = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003665 }
Steve French26b994f2008-08-06 05:11:33 +00003666
3667 if (rc)
3668 goto ss_err_exit;
3669
3670 pSesInfo->flags = 0;
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003671 pSesInfo->capabilities = server->capabilities;
Steve French26b994f2008-08-06 05:11:33 +00003672 if (linuxExtEnabled == 0)
3673 pSesInfo->capabilities &= (~CAP_UNIX);
Steve Frenchad009ac2005-04-28 22:41:05 -07003674 /* pSesInfo->sequence_number = 0;*/
Steve French26b994f2008-08-06 05:11:33 +00003675 cFYI(1, ("Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d",
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003676 server->secMode, server->capabilities, server->timeAdj));
3677
Steve French26b994f2008-08-06 05:11:33 +00003678 if (experimEnabled < 2)
3679 rc = CIFS_SessSetup(xid, pSesInfo, first_time, nls_info);
3680 else if (extended_security
3681 && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003682 && (server->secType == NTLMSSP)) {
Steve French26b994f2008-08-06 05:11:33 +00003683 rc = -EOPNOTSUPP;
3684 } else if (extended_security
3685 && (pSesInfo->capabilities & CAP_EXTENDED_SECURITY)
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003686 && (server->secType == RawNTLMSSP)) {
Steve French26b994f2008-08-06 05:11:33 +00003687 cFYI(1, ("NTLMSSP sesssetup"));
3688 rc = CIFSNTLMSSPNegotiateSessSetup(xid, pSesInfo, &ntlmv2_flag,
3689 nls_info);
3690 if (!rc) {
3691 if (ntlmv2_flag) {
3692 char *v2_response;
3693 cFYI(1, ("more secure NTLM ver2 hash"));
3694 if (CalcNTLMv2_partial_mac_key(pSesInfo,
3695 nls_info)) {
3696 rc = -ENOMEM;
3697 goto ss_err_exit;
3698 } else
3699 v2_response = kmalloc(16 + 64 /* blob*/,
3700 GFP_KERNEL);
3701 if (v2_response) {
3702 CalcNTLMv2_response(pSesInfo,
3703 v2_response);
3704 /* if (first_time)
3705 cifs_calculate_ntlmv2_mac_key */
3706 kfree(v2_response);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003707 /* BB Put dummy sig in SessSetup PDU? */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003708 } else {
Steve French26b994f2008-08-06 05:11:33 +00003709 rc = -ENOMEM;
3710 goto ss_err_exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711 }
Steve French26b994f2008-08-06 05:11:33 +00003712
3713 } else {
3714 SMBNTencrypt(pSesInfo->password,
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003715 server->cryptKey,
Steve French26b994f2008-08-06 05:11:33 +00003716 ntlm_session_key);
3717
3718 if (first_time)
3719 cifs_calculate_mac_key(
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003720 &server->mac_signing_key,
Steve French26b994f2008-08-06 05:11:33 +00003721 ntlm_session_key,
3722 pSesInfo->password);
3723 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003724 /* for better security the weaker lanman hash not sent
3725 in AuthSessSetup so we no longer calculate it */
3726
Steve French26b994f2008-08-06 05:11:33 +00003727 rc = CIFSNTLMSSPAuthSessSetup(xid, pSesInfo,
3728 ntlm_session_key,
3729 ntlmv2_flag,
3730 nls_info);
3731 }
3732 } else { /* old style NTLM 0.12 session setup */
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003733 SMBNTencrypt(pSesInfo->password, server->cryptKey,
Steve French26b994f2008-08-06 05:11:33 +00003734 ntlm_session_key);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003735
Steve French26b994f2008-08-06 05:11:33 +00003736 if (first_time)
Jeff Laytoncb7691b2008-08-18 15:41:05 -04003737 cifs_calculate_mac_key(&server->mac_signing_key,
3738 ntlm_session_key,
3739 pSesInfo->password);
Steve Frenchad009ac2005-04-28 22:41:05 -07003740
Steve French26b994f2008-08-06 05:11:33 +00003741 rc = CIFSSessSetup(xid, pSesInfo, ntlm_session_key, nls_info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003742 }
Steve French26b994f2008-08-06 05:11:33 +00003743 if (rc) {
3744 cERROR(1, ("Send error in SessSetup = %d", rc));
3745 } else {
3746 cFYI(1, ("CIFS Session Established successfully"));
Jeff Layton469ee612008-10-16 18:46:39 +00003747 spin_lock(&GlobalMid_Lock);
Steve French26b994f2008-08-06 05:11:33 +00003748 pSesInfo->status = CifsGood;
Jeff Layton469ee612008-10-16 18:46:39 +00003749 spin_unlock(&GlobalMid_Lock);
Steve French26b994f2008-08-06 05:11:33 +00003750 }
3751
Linus Torvalds1da177e2005-04-16 15:20:36 -07003752ss_err_exit:
3753 return rc;
3754}
3755