cifssmb.c 171 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
/*
 *   fs/cifs/cifssmb.c
 *
4
 *   Copyright (C) International Business Machines  Corp., 2002,2010
Linus Torvalds's avatar
Linus Torvalds committed
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 *   Author(s): Steve French (sfrench@us.ibm.com)
 *
 *   Contains the routines for constructing the SMB PDUs themselves
 *
 *   This library is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published
 *   by the Free Software Foundation; either version 2.1 of the License, or
 *   (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *   the GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

 /* SMB/CIFS PDU handling routines here - except for leftovers in connect.c   */
 /* These are mostly routines that operate on a pathname, or on a tree id     */
 /* (mounted volume), but there are eight handle based routines which must be */
27
28
 /* treated slightly differently for reconnection purposes since we never     */
 /* want to reuse a stale file handle and only the caller knows the file info */
Linus Torvalds's avatar
Linus Torvalds committed
29
30
31
32

#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/vfs.h>
33
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
34
35
36
37
#include <linux/posix_acl_xattr.h>
#include <asm/uaccess.h>
#include "cifspdu.h"
#include "cifsglob.h"
38
#include "cifsacl.h"
Linus Torvalds's avatar
Linus Torvalds committed
39
40
41
42
43
44
45
46
47
#include "cifsproto.h"
#include "cifs_unicode.h"
#include "cifs_debug.h"

#ifdef CONFIG_CIFS_POSIX
static struct {
	int index;
	char *name;
} protocols[] = {
48
49
#ifdef CONFIG_CIFS_WEAK_PW_HASH
	{LANMAN_PROT, "\2LM1.2X002"},
50
	{LANMAN2_PROT, "\2LANMAN2.1"},
51
#endif /* weak password hashing for legacy clients */
52
	{CIFS_PROT, "\2NT LM 0.12"},
53
	{POSIX_PROT, "\2POSIX 2"},
Linus Torvalds's avatar
Linus Torvalds committed
54
55
56
57
58
59
60
	{BAD_PROT, "\2"}
};
#else
static struct {
	int index;
	char *name;
} protocols[] = {
61
62
#ifdef CONFIG_CIFS_WEAK_PW_HASH
	{LANMAN_PROT, "\2LM1.2X002"},
63
	{LANMAN2_PROT, "\2LANMAN2.1"},
64
#endif /* weak password hashing for legacy clients */
Steve French's avatar
Steve French committed
65
	{CIFS_PROT, "\2NT LM 0.12"},
Linus Torvalds's avatar
Linus Torvalds committed
66
67
68
69
	{BAD_PROT, "\2"}
};
#endif

70
71
72
/* define the number of elements in the cifs dialect array */
#ifdef CONFIG_CIFS_POSIX
#ifdef CONFIG_CIFS_WEAK_PW_HASH
73
#define CIFS_NUM_PROT 4
74
75
76
77
78
#else
#define CIFS_NUM_PROT 2
#endif /* CIFS_WEAK_PW_HASH */
#else /* not posix */
#ifdef CONFIG_CIFS_WEAK_PW_HASH
79
#define CIFS_NUM_PROT 3
80
81
82
83
84
#else
#define CIFS_NUM_PROT 1
#endif /* CONFIG_CIFS_WEAK_PW_HASH */
#endif /* CIFS_POSIX */

Linus Torvalds's avatar
Linus Torvalds committed
85
86
/* Mark as invalid, all open files on tree connections since they
   were closed when session to server was lost */
Steve French's avatar
Steve French committed
87
static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
Linus Torvalds's avatar
Linus Torvalds committed
88
89
{
	struct cifsFileInfo *open_file = NULL;
Steve French's avatar
Steve French committed
90
91
	struct list_head *tmp;
	struct list_head *tmp1;
Linus Torvalds's avatar
Linus Torvalds committed
92
93

/* list all files open on tree connection and mark them invalid */
94
	spin_lock(&cifs_file_list_lock);
Linus Torvalds's avatar
Linus Torvalds committed
95
	list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
Steve French's avatar
Steve French committed
96
		open_file = list_entry(tmp, struct cifsFileInfo, tlist);
97
		open_file->invalidHandle = true;
98
		open_file->oplock_break_cancelled = true;
Linus Torvalds's avatar
Linus Torvalds committed
99
	}
100
	spin_unlock(&cifs_file_list_lock);
101
102
	/* BB Add call to invalidate_inodes(sb) for all superblocks mounted
	   to this tcon */
Linus Torvalds's avatar
Linus Torvalds committed
103
104
}

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/* reconnect the socket, tcon, and smb session if needed */
static int
cifs_reconnect_tcon(struct cifsTconInfo *tcon, int smb_command)
{
	int rc = 0;
	struct cifsSesInfo *ses;
	struct TCP_Server_Info *server;
	struct nls_table *nls_codepage;

	/*
	 * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
	 * tcp and smb session status done differently for those three - in the
	 * calling routine
	 */
	if (!tcon)
		return 0;

	ses = tcon->ses;
	server = ses->server;

	/*
	 * only tree disconnect, open, and write, (and ulogoff which does not
	 * have tcon) are allowed as we start force umount
	 */
	if (tcon->tidStatus == CifsExiting) {
		if (smb_command != SMB_COM_WRITE_ANDX &&
		    smb_command != SMB_COM_OPEN_ANDX &&
		    smb_command != SMB_COM_TREE_DISCONNECT) {
133
134
			cFYI(1, "can not send cmd %d while umounting",
				smb_command);
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
			return -ENODEV;
		}
	}

	/*
	 * Give demultiplex thread up to 10 seconds to reconnect, should be
	 * greater than cifs socket timeout which is 7 seconds
	 */
	while (server->tcpStatus == CifsNeedReconnect) {
		wait_event_interruptible_timeout(server->response_q,
			(server->tcpStatus == CifsGood), 10 * HZ);

		/* is TCP session is reestablished now ?*/
		if (server->tcpStatus != CifsNeedReconnect)
			break;

		/*
		 * on "soft" mounts we wait once. Hard mounts keep
		 * retrying until process is killed or server comes
		 * back on-line
		 */
156
		if (!tcon->retry) {
157
			cFYI(1, "gave up waiting on reconnect in smb_init");
158
159
160
161
162
163
164
165
166
167
168
169
170
			return -EHOSTDOWN;
		}
	}

	if (!ses->need_reconnect && !tcon->need_reconnect)
		return 0;

	nls_codepage = load_nls_default();

	/*
	 * need to prevent multiple threads trying to simultaneously
	 * reconnect the same SMB session
	 */
171
	mutex_lock(&ses->session_mutex);
172
173
	rc = cifs_negotiate_protocol(0, ses);
	if (rc == 0 && ses->need_reconnect)
174
175
176
177
		rc = cifs_setup_session(0, ses, nls_codepage);

	/* do we need to reconnect tcon? */
	if (rc || !tcon->need_reconnect) {
178
		mutex_unlock(&ses->session_mutex);
179
180
181
182
183
		goto out;
	}

	mark_open_files_invalid(tcon);
	rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
184
	mutex_unlock(&ses->session_mutex);
185
	cFYI(1, "reconnect tcon rc = %d", rc);
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

	if (rc)
		goto out;

	/*
	 * FIXME: check if wsize needs updated due to negotiated smb buffer
	 * 	  size shrinking
	 */
	atomic_inc(&tconInfoReconnectCount);

	/* tell server Unix caps we support */
	if (ses->capabilities & CAP_UNIX)
		reset_cifs_unix_caps(0, tcon, NULL, NULL);

	/*
	 * Removed call to reopen open files here. It is safer (and faster) to
	 * reopen files one at a time as needed in read and write.
	 *
	 * FIXME: what about file locks? don't we need to reclaim them ASAP?
	 */

out:
	/*
	 * Check if handle based operation so we know whether we can continue
	 * or not without returning to caller to reset file handle
	 */
	switch (smb_command) {
	case SMB_COM_READ_ANDX:
	case SMB_COM_WRITE_ANDX:
	case SMB_COM_CLOSE:
	case SMB_COM_FIND_CLOSE2:
	case SMB_COM_LOCKING_ANDX:
		rc = -EAGAIN;
	}

	unload_nls(nls_codepage);
	return rc;
}

225
226
227
/* Allocate and return pointer to an SMB request buffer, and set basic
   SMB information in the SMB header.  If the return code is zero, this
   function must have filled in request_buf pointer */
Linus Torvalds's avatar
Linus Torvalds committed
228
229
static int
small_smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
230
		void **request_buf)
Linus Torvalds's avatar
Linus Torvalds committed
231
{
232
	int rc;
Linus Torvalds's avatar
Linus Torvalds committed
233

234
	rc = cifs_reconnect_tcon(tcon, smb_command);
Steve French's avatar
Steve French committed
235
	if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
236
237
238
239
240
241
242
243
		return rc;

	*request_buf = cifs_small_buf_get();
	if (*request_buf == NULL) {
		/* BB should we add a retry in here if not a writepage? */
		return -ENOMEM;
	}

244
	header_assemble((struct smb_hdr *) *request_buf, smb_command,
245
			tcon, wct);
Linus Torvalds's avatar
Linus Torvalds committed
246

Steve French's avatar
Steve French committed
247
248
	if (tcon != NULL)
		cifs_stats_inc(&tcon->num_smbs_sent);
249

250
	return 0;
251
252
}

253
int
254
small_smb_init_no_tc(const int smb_command, const int wct,
255
		     struct cifsSesInfo *ses, void **request_buf)
256
257
{
	int rc;
258
	struct smb_hdr *buffer;
259

260
	rc = small_smb_init(smb_command, wct, NULL, request_buf);
Steve French's avatar
Steve French committed
261
	if (rc)
262
263
		return rc;

264
	buffer = (struct smb_hdr *)*request_buf;
265
266
267
	buffer->Mid = GetNextMid(ses->server);
	if (ses->capabilities & CAP_UNICODE)
		buffer->Flags2 |= SMBFLG2_UNICODE;
268
	if (ses->capabilities & CAP_STATUS32)
269
270
271
272
		buffer->Flags2 |= SMBFLG2_ERR_STATUS;

	/* uid, tid can stay at zero as set in header assemble */

273
	/* BB add support for turning on the signing when
274
275
276
277
	this function is used after 1st of session setup requests */

	return rc;
}
Linus Torvalds's avatar
Linus Torvalds committed
278
279
280

/* If the return code is zero, this function must fill in request_buf pointer */
static int
281
282
__smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
			void **request_buf, void **response_buf)
Linus Torvalds's avatar
Linus Torvalds committed
283
284
285
286
287
288
289
290
291
292
{
	*request_buf = cifs_buf_get();
	if (*request_buf == NULL) {
		/* BB should we add a retry in here if not a writepage? */
		return -ENOMEM;
	}
    /* Although the original thought was we needed the response buf for  */
    /* potential retries of smb operations it turns out we can determine */
    /* from the mid flags when the request buffer can be resent without  */
    /* having to use a second distinct buffer for the response */
Steve French's avatar
Steve French committed
293
	if (response_buf)
294
		*response_buf = *request_buf;
Linus Torvalds's avatar
Linus Torvalds committed
295
296

	header_assemble((struct smb_hdr *) *request_buf, smb_command, tcon,
297
			wct);
Linus Torvalds's avatar
Linus Torvalds committed
298

Steve French's avatar
Steve French committed
299
300
	if (tcon != NULL)
		cifs_stats_inc(&tcon->num_smbs_sent);
301

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
	return 0;
}

/* If the return code is zero, this function must fill in request_buf pointer */
static int
smb_init(int smb_command, int wct, struct cifsTconInfo *tcon,
	 void **request_buf, void **response_buf)
{
	int rc;

	rc = cifs_reconnect_tcon(tcon, smb_command);
	if (rc)
		return rc;

	return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
}

static int
smb_init_no_reconnect(int smb_command, int wct, struct cifsTconInfo *tcon,
			void **request_buf, void **response_buf)
{
	if (tcon->ses->need_reconnect || tcon->need_reconnect)
		return -EHOSTDOWN;

	return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
Linus Torvalds's avatar
Linus Torvalds committed
327
328
}

329
static int validate_t2(struct smb_t2_rsp *pSMB)
Linus Torvalds's avatar
Linus Torvalds committed
330
{
331
332
333
334
335
	unsigned int total_size;

	/* check for plausible wct */
	if (pSMB->hdr.WordCount < 10)
		goto vt2_err;
Linus Torvalds's avatar
Linus Torvalds committed
336
337

	/* check for parm and data offset going beyond end of smb */
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
	if (get_unaligned_le16(&pSMB->t2_rsp.ParameterOffset) > 1024 ||
	    get_unaligned_le16(&pSMB->t2_rsp.DataOffset) > 1024)
		goto vt2_err;

	/* check that bcc is at least as big as parms + data */
	/* check that bcc is less than negotiated smb buffer */
	total_size = get_unaligned_le16(&pSMB->t2_rsp.ParameterCount);
	if (total_size >= 512)
		goto vt2_err;

	total_size += get_unaligned_le16(&pSMB->t2_rsp.DataCount);
	if (total_size > get_bcc(&pSMB->hdr) ||
	    total_size >= CIFSMaxBufSize + MAX_CIFS_HDR_SIZE)
		goto vt2_err;

	return 0;
vt2_err:
355
	cifs_dump_mem("Invalid transact2 SMB: ", (char *)pSMB,
Linus Torvalds's avatar
Linus Torvalds committed
356
		sizeof(struct smb_t2_rsp) + 16);
357
	return -EINVAL;
Linus Torvalds's avatar
Linus Torvalds committed
358
}
359

Linus Torvalds's avatar
Linus Torvalds committed
360
361
362
363
364
365
366
int
CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
{
	NEGOTIATE_REQ *pSMB;
	NEGOTIATE_RSP *pSMBr;
	int rc = 0;
	int bytes_returned;
367
	int i;
368
	struct TCP_Server_Info *server;
Linus Torvalds's avatar
Linus Torvalds committed
369
	u16 count;
370
	unsigned int secFlags;
Linus Torvalds's avatar
Linus Torvalds committed
371

Steve French's avatar
Steve French committed
372
	if (ses->server)
Linus Torvalds's avatar
Linus Torvalds committed
373
374
375
376
377
378
379
380
381
		server = ses->server;
	else {
		rc = -EIO;
		return rc;
	}
	rc = smb_init(SMB_COM_NEGOTIATE, 0, NULL /* no tcon yet */ ,
		      (void **) &pSMB, (void **) &pSMBr);
	if (rc)
		return rc;
382
383

	/* if any of auth flags (ie not sign or seal) are overriden use them */
Steve French's avatar
Steve French committed
384
	if (ses->overrideSecFlg & (~(CIFSSEC_MUST_SIGN | CIFSSEC_MUST_SEAL)))
385
		secFlags = ses->overrideSecFlg;  /* BB FIXME fix sign flags? */
386
	else /* if override flags set only sign/seal OR them with global auth */
387
		secFlags = global_secflags | ses->overrideSecFlg;
388

389
	cFYI(1, "secFlags 0x%x", secFlags);
390

391
	pSMB->hdr.Mid = GetNextMid(server);
392
	pSMB->hdr.Flags2 |= (SMBFLG2_UNICODE | SMBFLG2_ERR_STATUS);
393

394
	if ((secFlags & CIFSSEC_MUST_KRB5) == CIFSSEC_MUST_KRB5)
395
		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
396
	else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_KRB5) {
397
		cFYI(1, "Kerberos only mechanism, enable extended security");
398
		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
399
	} else if ((secFlags & CIFSSEC_MUST_NTLMSSP) == CIFSSEC_MUST_NTLMSSP)
400
401
		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
	else if ((secFlags & CIFSSEC_AUTH_MASK) == CIFSSEC_MAY_NTLMSSP) {
402
		cFYI(1, "NTLMSSP only mechanism, enable extended security");
403
404
		pSMB->hdr.Flags2 |= SMBFLG2_EXT_SEC;
	}
405

406
	count = 0;
407
	for (i = 0; i < CIFS_NUM_PROT; i++) {
408
409
410
411
		strncpy(pSMB->DialectsArray+count, protocols[i].name, 16);
		count += strlen(protocols[i].name) + 1;
		/* null at end of source and target buffers anyway */
	}
Linus Torvalds's avatar
Linus Torvalds committed
412
413
414
415
416
	pSMB->hdr.smb_buf_length += count;
	pSMB->ByteCount = cpu_to_le16(count);

	rc = SendReceive(xid, ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
417
	if (rc != 0)
418
419
		goto neg_err_exit;

420
421
	server->dialect = le16_to_cpu(pSMBr->DialectIndex);
	cFYI(1, "Dialect: %d", server->dialect);
422
	/* Check wct = 1 error case */
423
	if ((pSMBr->hdr.WordCount < 13) || (server->dialect == BAD_PROT)) {
424
		/* core returns wct = 1, but we do not ask for core - otherwise
425
		small wct just comes when dialect index is -1 indicating we
426
427
428
		could not negotiate a common dialect */
		rc = -EOPNOTSUPP;
		goto neg_err_exit;
429
#ifdef CONFIG_CIFS_WEAK_PW_HASH
Steve French's avatar
Steve French committed
430
	} else if ((pSMBr->hdr.WordCount == 13)
431
432
			&& ((server->dialect == LANMAN_PROT)
				|| (server->dialect == LANMAN2_PROT))) {
433
		__s16 tmp;
434
		struct lanman_neg_rsp *rsp = (struct lanman_neg_rsp *)pSMBr;
435

Steve French's avatar
Steve French committed
436
		if ((secFlags & CIFSSEC_MAY_LANMAN) ||
437
			(secFlags & CIFSSEC_MAY_PLNTXT))
438
439
			server->secType = LANMAN;
		else {
440
441
			cERROR(1, "mount failed weak security disabled"
				   " in /proc/fs/cifs/SecurityFlags");
442
443
			rc = -EOPNOTSUPP;
			goto neg_err_exit;
444
		}
445
446
447
		server->secMode = (__u8)le16_to_cpu(rsp->SecurityMode);
		server->maxReq = le16_to_cpu(rsp->MaxMpxCount);
		server->maxBuf = min((__u32)le16_to_cpu(rsp->MaxBufSize),
448
				(__u32)CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
449
		server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);
450
451
		/* even though we do not use raw we might as well set this
		accurately, in case we ever find a need for it */
Steve French's avatar
Steve French committed
452
		if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) {
453
			server->max_rw = 0xFF00;
454
455
			server->capabilities = CAP_MPX_MODE | CAP_RAW_MODE;
		} else {
456
			server->max_rw = 0;/* do not need to use raw anyway */
457
458
			server->capabilities = CAP_MPX_MODE;
		}
459
		tmp = (__s16)le16_to_cpu(rsp->ServerTimeZone);
460
		if (tmp == -1) {
461
462
			/* OS/2 often does not set timezone therefore
			 * we must use server time to calc time zone.
463
464
465
466
			 * Could deviate slightly from the right zone.
			 * Smallest defined timezone difference is 15 minutes
			 * (i.e. Nepal).  Rounding up/down is done to match
			 * this requirement.
467
			 */
468
			int val, seconds, remain, result;
469
470
			struct timespec ts, utc;
			utc = CURRENT_TIME;
471
472
			ts = cnvrtDosUnixTm(rsp->SrvTime.Date,
					    rsp->SrvTime.Time, 0);
473
			cFYI(1, "SrvTime %d sec since 1970 (utc: %d) diff: %d",
474
				(int)ts.tv_sec, (int)utc.tv_sec,
475
				(int)(utc.tv_sec - ts.tv_sec));
476
			val = (int)(utc.tv_sec - ts.tv_sec);
477
			seconds = abs(val);
Steve French's avatar
Steve French committed
478
			result = (seconds / MIN_TZ_ADJ) * MIN_TZ_ADJ;
479
			remain = seconds % MIN_TZ_ADJ;
Steve French's avatar
Steve French committed
480
			if (remain >= (MIN_TZ_ADJ / 2))
481
				result += MIN_TZ_ADJ;
Steve French's avatar
Steve French committed
482
			if (val < 0)
483
				result = -result;
484
			server->timeAdj = result;
485
		} else {
486
487
			server->timeAdj = (int)tmp;
			server->timeAdj *= 60; /* also in seconds */
488
		}
489
		cFYI(1, "server->timeAdj: %d seconds", server->timeAdj);
490

491

492
		/* BB get server time for time conversions and add
493
		code to use it and timezone since this is not UTC */
494

495
		if (rsp->EncryptionKeyLength ==
496
				cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
497
			memcpy(ses->server->cryptkey, rsp->EncryptionKey,
498
499
500
501
502
				CIFS_CRYPTO_KEY_SIZE);
		} else if (server->secMode & SECMODE_PW_ENCRYPT) {
			rc = -EIO; /* need cryptkey unless plain text */
			goto neg_err_exit;
		}
503

504
		cFYI(1, "LANMAN negotiated");
505
506
507
		/* we will not end up setting signing flags - as no signing
		was in LANMAN and server did not return the flags on */
		goto signing_check;
508
#else /* weak security disabled */
Steve French's avatar
Steve French committed
509
	} else if (pSMBr->hdr.WordCount == 13) {
510
511
		cERROR(1, "mount failed, cifs module not built "
			  "with CIFS_WEAK_PW_HASH support");
Dan Carpenter's avatar
Dan Carpenter committed
512
		rc = -EOPNOTSUPP;
513
#endif /* WEAK_PW_HASH */
514
		goto neg_err_exit;
Steve French's avatar
Steve French committed
515
	} else if (pSMBr->hdr.WordCount != 17) {
516
517
518
519
520
521
		/* unknown wct */
		rc = -EOPNOTSUPP;
		goto neg_err_exit;
	}
	/* else wct == 17 NTLM */
	server->secMode = pSMBr->SecurityMode;
Steve French's avatar
Steve French committed
522
	if ((server->secMode & SECMODE_USER) == 0)
523
		cFYI(1, "share mode security");
524

Steve French's avatar
Steve French committed
525
	if ((server->secMode & SECMODE_PW_ENCRYPT) == 0)
526
#ifdef CONFIG_CIFS_WEAK_PW_HASH
527
		if ((secFlags & CIFSSEC_MAY_PLNTXT) == 0)
528
#endif /* CIFS_WEAK_PW_HASH */
529
530
			cERROR(1, "Server requests plain text password"
				  " but client support disabled");
531

Steve French's avatar
Steve French committed
532
	if ((secFlags & CIFSSEC_MUST_NTLMV2) == CIFSSEC_MUST_NTLMV2)
533
		server->secType = NTLMv2;
Steve French's avatar
Steve French committed
534
	else if (secFlags & CIFSSEC_MAY_NTLM)
535
		server->secType = NTLM;
Steve French's avatar
Steve French committed
536
	else if (secFlags & CIFSSEC_MAY_NTLMV2)
537
		server->secType = NTLMv2;
538
539
	else if (secFlags & CIFSSEC_MAY_KRB5)
		server->secType = Kerberos;
540
	else if (secFlags & CIFSSEC_MAY_NTLMSSP)
541
		server->secType = RawNTLMSSP;
542
543
544
545
546
547
548
549
	else if (secFlags & CIFSSEC_MAY_LANMAN)
		server->secType = LANMAN;
/* #ifdef CONFIG_CIFS_EXPERIMENTAL
	else if (secFlags & CIFSSEC_MAY_PLNTXT)
		server->secType = ??
#endif */
	else {
		rc = -EOPNOTSUPP;
550
		cERROR(1, "Invalid security type");
551
552
553
		goto neg_err_exit;
	}
	/* else ... any others ...? */
554
555
556
557
558
559

	/* one byte, so no need to convert this or EncryptionKeyLen from
	   little endian */
	server->maxReq = le16_to_cpu(pSMBr->MaxMpxCount);
	/* probably no need to store and check maxvcs */
	server->maxBuf = min(le32_to_cpu(pSMBr->MaxBufferSize),
Linus Torvalds's avatar
Linus Torvalds committed
560
			(__u32) CIFSMaxBufSize + MAX_CIFS_HDR_SIZE);
561
	server->max_rw = le32_to_cpu(pSMBr->MaxRawSize);
562
	cFYI(DBG2, "Max buf = %d", ses->server->maxBuf);
563
	server->capabilities = le32_to_cpu(pSMBr->Capabilities);
564
565
	server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
	server->timeAdj *= 60;
566
	if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
567
		memcpy(ses->server->cryptkey, pSMBr->u.EncryptionKey,
568
569
570
571
572
573
574
575
		       CIFS_CRYPTO_KEY_SIZE);
	} else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
			&& (pSMBr->EncryptionKeyLength == 0)) {
		/* decode security blob */
	} else if (server->secMode & SECMODE_PW_ENCRYPT) {
		rc = -EIO; /* no crypt key only if plain text pwd */
		goto neg_err_exit;
	}
Linus Torvalds's avatar
Linus Torvalds committed
576

577
578
	/* BB might be helpful to save off the domain of server here */

579
	if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) &&
580
581
		(server->capabilities & CAP_EXTENDED_SECURITY)) {
		count = pSMBr->ByteCount;
582
		if (count < 16) {
583
			rc = -EIO;
584
585
			goto neg_err_exit;
		}
586
		spin_lock(&cifs_tcp_ses_lock);
587
		if (server->srv_count > 1) {
588
			spin_unlock(&cifs_tcp_ses_lock);
589
590
591
			if (memcmp(server->server_GUID,
				   pSMBr->u.extended_response.
				   GUID, 16) != 0) {
592
				cFYI(1, "server UID changed");
593
				memcpy(server->server_GUID,
594
595
596
					pSMBr->u.extended_response.GUID,
					16);
			}
597
		} else {
598
			spin_unlock(&cifs_tcp_ses_lock);
599
600
			memcpy(server->server_GUID,
			       pSMBr->u.extended_response.GUID, 16);
601
		}
602
603
604

		if (count == 16) {
			server->secType = RawNTLMSSP;
605
606
		} else {
			rc = decode_negTokenInit(pSMBr->u.extended_response.
607
608
						 SecurityBlob, count - 16,
						 server);
609
			if (rc == 1)
610
				rc = 0;
611
			else
612
				rc = -EINVAL;
613
614
615
616
617
618
619
620
621
			if (server->secType == Kerberos) {
				if (!server->sec_kerberos &&
						!server->sec_mskerberos)
					rc = -EOPNOTSUPP;
			} else if (server->secType == RawNTLMSSP) {
				if (!server->sec_ntlmssp)
					rc = -EOPNOTSUPP;
			} else
					rc = -EOPNOTSUPP;
Linus Torvalds's avatar
Linus Torvalds committed
622
		}
623
624
625
	} else
		server->capabilities &= ~CAP_EXTENDED_SECURITY;

626
#ifdef CONFIG_CIFS_WEAK_PW_HASH
627
signing_check:
628
#endif
629
630
631
	if ((secFlags & CIFSSEC_MAY_SIGN) == 0) {
		/* MUST_SIGN already includes the MAY_SIGN FLAG
		   so if this is zero it means that signing is disabled */
632
		cFYI(1, "Signing disabled");
633
		if (server->secMode & SECMODE_SIGN_REQUIRED) {
634
			cERROR(1, "Server requires "
Jeff Layton's avatar
[CIFS]    
Jeff Layton committed
635
				   "packet signing to be enabled in "
636
				   "/proc/fs/cifs/SecurityFlags.");
637
638
			rc = -EOPNOTSUPP;
		}
639
		server->secMode &=
640
			~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
641
642
	} else if ((secFlags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN) {
		/* signing required */
643
		cFYI(1, "Must sign - secFlags 0x%x", secFlags);
644
645
		if ((server->secMode &
			(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED)) == 0) {
646
			cERROR(1, "signing required but server lacks support");
647
			rc = -EOPNOTSUPP;
648
649
650
651
		} else
			server->secMode |= SECMODE_SIGN_REQUIRED;
	} else {
		/* signing optional ie CIFSSEC_MAY_SIGN */
Steve French's avatar
Steve French committed
652
		if ((server->secMode & SECMODE_SIGN_REQUIRED) == 0)
653
			server->secMode &=
654
				~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
Linus Torvalds's avatar
Linus Torvalds committed
655
	}
656
657

neg_err_exit:
658
	cifs_buf_release(pSMB);
659

660
	cFYI(1, "negprot rc %d", rc);
Linus Torvalds's avatar
Linus Torvalds committed
661
662
663
664
665
666
667
668
669
	return rc;
}

int
CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
{
	struct smb_hdr *smb_buffer;
	int rc = 0;

670
	cFYI(1, "In tree disconnect");
Linus Torvalds's avatar
Linus Torvalds committed
671

672
673
674
	/* BB: do we need to check this? These should never be NULL. */
	if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
		return -EIO;
Linus Torvalds's avatar
Linus Torvalds committed
675

676
677
678
679
680
681
	/*
	 * No need to return error on this operation if tid invalidated and
	 * closed on server already e.g. due to tcp session crashing. Also,
	 * the tcon is no longer on the list, so no need to take lock before
	 * checking this.
	 */
682
	if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
683
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
684

685
	rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
686
			    (void **)&smb_buffer);
687
	if (rc)
Linus Torvalds's avatar
Linus Torvalds committed
688
		return rc;
689
690

	rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
Linus Torvalds's avatar
Linus Torvalds committed
691
	if (rc)
692
		cFYI(1, "Tree disconnect failed %d", rc);
Linus Torvalds's avatar
Linus Torvalds committed
693

694
	/* No need to return error on this operation if tid invalidated and
695
	   closed on server already e.g. due to tcp session crashing */
Linus Torvalds's avatar
Linus Torvalds committed
696
697
698
699
700
701
	if (rc == -EAGAIN)
		rc = 0;

	return rc;
}

702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
/*
 * This is a no-op for now. We're not really interested in the reply, but
 * rather in the fact that the server sent one and that server->lstrp
 * gets updated.
 *
 * FIXME: maybe we should consider checking that the reply matches request?
 */
static void
cifs_echo_callback(struct mid_q_entry *mid)
{
	struct TCP_Server_Info *server = mid->callback_data;

	DeleteMidQEntry(mid);
	atomic_dec(&server->inFlight);
	wake_up(&server->request_q);
}

int
CIFSSMBEcho(struct TCP_Server_Info *server)
{
	ECHO_REQ *smb;
	int rc = 0;

	cFYI(1, "In echo request");

	rc = small_smb_init(SMB_COM_ECHO, 0, NULL, (void **)&smb);
	if (rc)
		return rc;

	/* set up echo request */
	smb->hdr.Tid = cpu_to_le16(0xffff);
733
734
735
	smb->hdr.WordCount = 1;
	put_unaligned_le16(1, &smb->EchoCount);
	put_bcc_le(1, &smb->hdr);
736
737
738
739
740
741
742
743
744
745
746
747
748
	smb->Data[0] = 'a';
	smb->hdr.smb_buf_length += 3;

	rc = cifs_call_async(server, (struct smb_hdr *)smb,
				cifs_echo_callback, server);
	if (rc)
		cFYI(1, "Echo request failed: %d", rc);

	cifs_small_buf_release(smb);

	return rc;
}

Linus Torvalds's avatar
Linus Torvalds committed
749
750
751
752
753
754
int
CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
{
	LOGOFF_ANDX_REQ *pSMB;
	int rc = 0;

755
	cFYI(1, "In SMBLogoff for session disconnect");
756

757
758
759
760
761
762
	/*
	 * BB: do we need to check validity of ses and server? They should
	 * always be valid since we have an active reference. If not, that
	 * should probably be a BUG()
	 */
	if (!ses || !ses->server)
763
764
		return -EIO;

765
	mutex_lock(&ses->session_mutex);
766
767
768
	if (ses->need_reconnect)
		goto session_already_dead; /* no need to send SMBlogoff if uid
					      already closed due to reconnect */
Linus Torvalds's avatar
Linus Torvalds committed
769
770
	rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
	if (rc) {
771
		mutex_unlock(&ses->session_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
772
773
774
		return rc;
	}

775
	pSMB->hdr.Mid = GetNextMid(ses->server);
776

777
	if (ses->server->secMode &
Linus Torvalds's avatar
Linus Torvalds committed
778
779
780
781
782
783
		   (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
			pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;

	pSMB->hdr.Uid = ses->Suid;

	pSMB->AndXCommand = 0xFF;
784
	rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
785
session_already_dead:
786
	mutex_unlock(&ses->session_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
787
788

	/* if session dead then we do not need to do ulogoff,
789
		since server closed smb session, no sense reporting
Linus Torvalds's avatar
Linus Torvalds committed
790
791
792
793
794
795
		error */
	if (rc == -EAGAIN)
		rc = 0;
	return rc;
}

796
797
798
799
800
801
802
803
804
805
806
807
int
CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
		 __u16 type, const struct nls_table *nls_codepage, int remap)
{
	TRANSACTION2_SPI_REQ *pSMB = NULL;
	TRANSACTION2_SPI_RSP *pSMBr = NULL;
	struct unlink_psx_rq *pRqD;
	int name_len;
	int rc = 0;
	int bytes_returned = 0;
	__u16 params, param_offset, offset, byte_count;

808
	cFYI(1, "In POSIX delete");
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
PsxDelete:
	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
		    cifsConvertToUCS((__le16 *) pSMB->FileName, fileName,
				     PATH_MAX, nls_codepage, remap);
		name_len++;	/* trailing null */
		name_len *= 2;
	} else { /* BB add path length overrun check */
		name_len = strnlen(fileName, PATH_MAX);
		name_len++;	/* trailing null */
		strncpy(pSMB->FileName, fileName, name_len);
	}

	params = 6 + name_len;
	pSMB->MaxParameterCount = cpu_to_le16(2);
	pSMB->MaxDataCount = 0; /* BB double check this with jra */
	pSMB->MaxSetupCount = 0;
	pSMB->Reserved = 0;
	pSMB->Flags = 0;
	pSMB->Timeout = 0;
	pSMB->Reserved2 = 0;
	param_offset = offsetof(struct smb_com_transaction2_spi_req,
				InformationLevel) - 4;
	offset = param_offset + params;

	/* Setup pointer to Request Data (inode type) */
	pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset);
	pRqD->type = cpu_to_le16(type);
	pSMB->ParameterOffset = cpu_to_le16(param_offset);
	pSMB->DataOffset = cpu_to_le16(offset);
	pSMB->SetupCount = 1;
	pSMB->Reserved3 = 0;
	pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION);
	byte_count = 3 /* pad */  + params + sizeof(struct unlink_psx_rq);

	pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
	pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq));
	pSMB->ParameterCount = cpu_to_le16(params);
	pSMB->TotalParameterCount = pSMB->ParameterCount;
	pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK);
	pSMB->Reserved4 = 0;
	pSMB->hdr.smb_buf_length += byte_count;
	pSMB->ByteCount = cpu_to_le16(byte_count);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
859
	if (rc)
860
		cFYI(1, "Posix delete returned %d", rc);
861
862
863
864
865
866
867
868
869
870
	cifs_buf_release(pSMB);

	cifs_stats_inc(&tcon->num_deletes);

	if (rc == -EAGAIN)
		goto PsxDelete;

	return rc;
}

Linus Torvalds's avatar
Linus Torvalds committed
871
int
872
873
CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName,
	       const struct nls_table *nls_codepage, int remap)
Linus Torvalds's avatar
Linus Torvalds committed
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
{
	DELETE_FILE_REQ *pSMB = NULL;
	DELETE_FILE_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;

DelFileRetry:
	rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
		name_len =
889
		    cifsConvertToUCS((__le16 *) pSMB->fileName, fileName,
890
				     PATH_MAX, nls_codepage, remap);
Linus Torvalds's avatar
Linus Torvalds committed
891
892
		name_len++;	/* trailing null */
		name_len *= 2;
893
	} else {		/* BB improve check for buffer overruns BB */
Linus Torvalds's avatar
Linus Torvalds committed
894
895
896
897
898
899
900
901
902
903
904
		name_len = strnlen(fileName, PATH_MAX);
		name_len++;	/* trailing null */
		strncpy(pSMB->fileName, fileName, name_len);
	}
	pSMB->SearchAttributes =
	    cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
	pSMB->BufferFormat = 0x04;
	pSMB->hdr.smb_buf_length += name_len + 1;
	pSMB->ByteCount = cpu_to_le16(name_len + 1);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
905
	cifs_stats_inc(&tcon->num_deletes);
906
	if (rc)
907
		cFYI(1, "Error in RMFile = %d", rc);
Linus Torvalds's avatar
Linus Torvalds committed
908
909
910
911
912
913
914
915
916

	cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto DelFileRetry;

	return rc;
}

int
917
CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *dirName,
918
	     const struct nls_table *nls_codepage, int remap)
Linus Torvalds's avatar
Linus Torvalds committed
919
920
921
922
923
924
925
{
	DELETE_DIRECTORY_REQ *pSMB = NULL;
	DELETE_DIRECTORY_RSP *pSMBr = NULL;
	int rc = 0;
	int bytes_returned;
	int name_len;

926
	cFYI(1, "In CIFSSMBRmDir");
Linus Torvalds's avatar
Linus Torvalds committed
927
928
929
930
931
932
933
RmDirRetry:
	rc = smb_init(SMB_COM_DELETE_DIRECTORY, 0, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
934
935
		name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, dirName,
					 PATH_MAX, nls_codepage, remap);
Linus Torvalds's avatar
Linus Torvalds committed
936
937
		name_len++;	/* trailing null */
		name_len *= 2;
938
	} else {		/* BB improve check for buffer overruns BB */
Linus Torvalds's avatar
Linus Torvalds committed
939
940
941
942
943
944
945
946
947
948
		name_len = strnlen(dirName, PATH_MAX);
		name_len++;	/* trailing null */
		strncpy(pSMB->DirName, dirName, name_len);
	}

	pSMB->BufferFormat = 0x04;
	pSMB->hdr.smb_buf_length += name_len + 1;
	pSMB->ByteCount = cpu_to_le16(name_len + 1);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
949
	cifs_stats_inc(&tcon->num_rmdirs);
950
	if (rc)
951
		cFYI(1, "Error in RMDir = %d", rc);
Linus Torvalds's avatar
Linus Torvalds committed
952
953
954
955
956
957
958
959
960

	cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto RmDirRetry;
	return rc;
}

int
CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon,
961
	     const char *name, const struct nls_table *nls_codepage, int remap)
Linus Torvalds's avatar
Linus Torvalds committed
962
963
964
965
966
967
968
{
	int rc = 0;
	CREATE_DIRECTORY_REQ *pSMB = NULL;
	CREATE_DIRECTORY_RSP *pSMBr = NULL;
	int bytes_returned;
	int name_len;

969
	cFYI(1, "In CIFSSMBMkDir");
Linus Torvalds's avatar
Linus Torvalds committed
970
971
972
973
974
975
976
MkDirRetry:
	rc = smb_init(SMB_COM_CREATE_DIRECTORY, 0, tcon, (void **) &pSMB,
		      (void **) &pSMBr);
	if (rc)
		return rc;

	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
977
		name_len = cifsConvertToUCS((__le16 *) pSMB->DirName, name,
978
					    PATH_MAX, nls_codepage, remap);
Linus Torvalds's avatar
Linus Torvalds committed
979
980
		name_len++;	/* trailing null */
		name_len *= 2;
981
	} else {		/* BB improve check for buffer overruns BB */
Linus Torvalds's avatar
Linus Torvalds committed
982
983
984
985
986
987
988
989
990
991
		name_len = strnlen(name, PATH_MAX);
		name_len++;	/* trailing null */
		strncpy(pSMB->DirName, name, name_len);
	}

	pSMB->BufferFormat = 0x04;
	pSMB->hdr.smb_buf_length += name_len + 1;
	pSMB->ByteCount = cpu_to_le16(name_len + 1);
	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);
992
	cifs_stats_inc(&tcon->num_mkdirs);
993
	if (rc)
994
		cFYI(1, "Error in Mkdir = %d", rc);
995

Linus Torvalds's avatar
Linus Torvalds committed
996
997
998
999
1000
	cifs_buf_release(pSMB);
	if (rc == -EAGAIN)
		goto MkDirRetry;
	return rc;
}
For faster browsing, not all history is shown. View entire blame