aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/sess.c
diff options
context:
space:
mode:
authorSteve French <sfrench@us.ibm.com>2006-05-31 18:40:51 -0400
committerSteve French <sfrench@us.ibm.com>2006-05-31 18:40:51 -0400
commit3979877e5606ecc58c5a31bd0078c6d80ba9cbe7 (patch)
treed221455b5176ea8b26e750d6498c3ed822998ba3 /fs/cifs/sess.c
parent26a21b980b1897b11fd7f9ba4bf6060c9e15df10 (diff)
[CIFS] Support for setting up SMB sessions to legacy lanman servers
Diffstat (limited to 'fs/cifs/sess.c')
-rw-r--r--fs/cifs/sess.c511
1 files changed, 511 insertions, 0 deletions
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
new file mode 100644
index 000000000000..34998416eb73
--- /dev/null
+++ b/fs/cifs/sess.c
@@ -0,0 +1,511 @@
1/*
2 * fs/cifs/sess.c
3 *
4 * SMB/CIFS session setup handling routines
5 *
6 * Copyright (c) International Business Machines Corp., 2006
7 * Author(s): Steve French (sfrench@us.ibm.com)
8 *
9 * This library is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as published
11 * by the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24#include "cifspdu.h"
25#include "cifsglob.h"
26#include "cifsproto.h"
27#include "cifs_unicode.h"
28#include "cifs_debug.h"
29#include "ntlmssp.h"
30#include "nterr.h"
31#include <linux/ctype.h>
32
33extern void SMBencrypt(unsigned char *passwd, unsigned char *c8,
34 unsigned char *p24);
35
36extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
37 unsigned char *p24);
38
39#ifdef CONFIG_CIFS_EXPERIMENTAL
40
41static __u32 cifs_ssetup_hdr(struct cifsSesInfo *ses, SESSION_SETUP_ANDX *pSMB)
42{
43 __u32 capabilities = 0;
44
45 /* init fields common to all four types of SessSetup */
46 /* note that header is initialized to zero in header_assemble */
47 pSMB->req.AndXCommand = 0xFF;
48 pSMB->req.MaxBufferSize = cpu_to_le16(ses->server->maxBuf);
49 pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq);
50
51 /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
52
53 /* BB verify whether signing required on neg or just on auth frame
54 (and NTLM case) */
55
56 capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
57 CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
58
59 if(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
60 pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
61
62 if (ses->capabilities & CAP_UNICODE) {
63 pSMB->req.hdr.Flags2 |= SMBFLG2_UNICODE;
64 capabilities |= CAP_UNICODE;
65 }
66 if (ses->capabilities & CAP_STATUS32) {
67 pSMB->req.hdr.Flags2 |= SMBFLG2_ERR_STATUS;
68 capabilities |= CAP_STATUS32;
69 }
70 if (ses->capabilities & CAP_DFS) {
71 pSMB->req.hdr.Flags2 |= SMBFLG2_DFS;
72 capabilities |= CAP_DFS;
73 }
74 if (ses->capabilities & CAP_UNIX) {
75 capabilities |= CAP_UNIX;
76 }
77
78 /* BB check whether to init vcnum BB */
79 return capabilities;
80}
81
82void unicode_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
83 const struct nls_table * nls_cp)
84{
85 char * bcc_ptr = *pbcc_area;
86 int bytes_ret = 0;
87
88 /* BB FIXME add check that strings total less
89 than 335 or will need to send them as arrays */
90
91 /* align unicode strings, must be word aligned */
92 if ((long) bcc_ptr % 2) {
93 *bcc_ptr = 0;
94 bcc_ptr++;
95 }
96 /* copy user */
97 if(ses->userName == NULL) {
98 /* BB what about null user mounts - check that we do this BB */
99 } else { /* 300 should be long enough for any conceivable user name */
100 bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->userName,
101 300, nls_cp);
102 }
103 bcc_ptr += 2 * bytes_ret;
104 bcc_ptr += 2; /* account for null termination */
105 /* copy domain */
106 if(ses->domainName == NULL)
107 bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr,
108 "CIFS_LINUX_DOM", 32, nls_cp);
109 else
110 bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, ses->domainName,
111 256, nls_cp);
112 bcc_ptr += 2 * bytes_ret;
113 bcc_ptr += 2; /* account for null terminator */
114
115 /* Copy OS version */
116 bytes_ret = cifs_strtoUCS((__le16 *)bcc_ptr, "Linux version ", 32,
117 nls_cp);
118 bcc_ptr += 2 * bytes_ret;
119 bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, system_utsname.release,
120 32, nls_cp);
121 bcc_ptr += 2 * bytes_ret;
122 bcc_ptr += 2; /* trailing null */
123
124 bytes_ret = cifs_strtoUCS((__le16 *) bcc_ptr, CIFS_NETWORK_OPSYS,
125 32, nls_cp);
126 bcc_ptr += 2 * bytes_ret;
127 bcc_ptr += 2; /* trailing null */
128
129 *pbcc_area = bcc_ptr;
130}
131
132void ascii_ssetup_strings(char ** pbcc_area, struct cifsSesInfo *ses,
133 const struct nls_table * nls_cp)
134{
135 char * bcc_ptr = *pbcc_area;
136
137 /* copy user */
138 /* BB what about null user mounts - check that we do this BB */
139 /* copy user */
140 if(ses->userName == NULL) {
141 /* BB what about null user mounts - check that we do this BB */
142 } else { /* 300 should be long enough for any conceivable user name */
143 strncpy(bcc_ptr, ses->userName, 300);
144 }
145 /* BB improve check for overflow */
146 bcc_ptr += strnlen(ses->userName, 200);
147 *bcc_ptr = 0;
148 bcc_ptr++; /* account for null termination */
149
150 /* copy domain */
151
152 if(ses->domainName == NULL) {
153 strcpy(bcc_ptr, "CIFS_LINUX_DOM");
154 bcc_ptr += 14; /* strlen(CIFS_LINUX_DOM) */
155 } else {
156 strncpy(bcc_ptr, ses->domainName, 256);
157 bcc_ptr += strnlen(ses->domainName, 256);
158 }
159 *bcc_ptr = 0;
160 bcc_ptr++;
161
162 /* BB check for overflow here */
163
164 strcpy(bcc_ptr, "Linux version ");
165 bcc_ptr += strlen("Linux version ");
166 strcpy(bcc_ptr, system_utsname.release);
167 bcc_ptr += strlen(system_utsname.release) + 1;
168
169 strcpy(bcc_ptr, CIFS_NETWORK_OPSYS);
170 bcc_ptr += strlen(CIFS_NETWORK_OPSYS) + 1;
171
172 *pbcc_area = bcc_ptr;
173}
174
175int decode_unicode_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
176 const struct nls_table * nls_cp)
177{
178 int rc = 0;
179 int words_left, len;
180 char * data = *pbcc_area;
181
182
183
184 cFYI(1,("bleft %d",bleft));
185
186
187 /* word align, if bytes remaining is not even */
188 if(bleft % 2) {
189 bleft--;
190 data++;
191 }
192 words_left = bleft / 2;
193
194 /* save off server operating system */
195 len = UniStrnlen((wchar_t *) data, words_left);
196
197/* We look for obvious messed up bcc or strings in response so we do not go off
198 the end since (at least) WIN2K and Windows XP have a major bug in not null
199 terminating last Unicode string in response */
200 if(len >= words_left)
201 return rc;
202
203 if(ses->serverOS)
204 kfree(ses->serverOS);
205 /* UTF-8 string will not grow more than four times as big as UCS-16 */
206 ses->serverOS = kzalloc(4 * len, GFP_KERNEL);
207 if(ses->serverOS != NULL) {
208 cifs_strfromUCS_le(ses->serverOS, (__le16 *)data, len,
209 nls_cp);
210 }
211 data += 2 * (len + 1);
212 words_left -= len + 1;
213
214 /* save off server network operating system */
215 len = UniStrnlen((wchar_t *) data, words_left);
216
217 if(len >= words_left)
218 return rc;
219
220 if(ses->serverNOS)
221 kfree(ses->serverNOS);
222 ses->serverNOS = kzalloc(4 * len, GFP_KERNEL); /* BB this is wrong length FIXME BB */
223 if(ses->serverNOS != NULL) {
224 cifs_strfromUCS_le(ses->serverNOS, (__le16 *)data, len,
225 nls_cp);
226 if(strncmp(ses->serverNOS, "NT LAN Manager 4",16) == 0) {
227 cFYI(1,("NT4 server"));
228 ses->flags |= CIFS_SES_NT4;
229 }
230 }
231 data += 2 * (len + 1);
232 words_left -= len + 1;
233
234 /* save off server domain */
235 len = UniStrnlen((wchar_t *) data, words_left);
236
237 if(len > words_left)
238 return rc;
239
240 if(ses->serverDomain)
241 kfree(ses->serverDomain);
242 ses->serverDomain = kzalloc(2 * (len + 1), GFP_KERNEL); /* BB FIXME wrong length */
243 if(ses->serverDomain != NULL) {
244 cifs_strfromUCS_le(ses->serverDomain, (__le16 *)data, len,
245 nls_cp);
246 ses->serverDomain[2*len] = 0;
247 ses->serverDomain[(2*len) + 1] = 0;
248 }
249 data += 2 * (len + 1);
250 words_left -= len + 1;
251
252 cFYI(1,("words left: %d",words_left));
253
254 return rc;
255}
256
257int decode_ascii_ssetup(char ** pbcc_area, int bleft, struct cifsSesInfo *ses,
258 const struct nls_table * nls_cp)
259{
260 int rc = 0;
261 int len;
262 char * bcc_ptr = *pbcc_area;
263
264 cFYI(1,("decode sessetup ascii. bleft %d", bleft));
265
266 len = strnlen(bcc_ptr, bleft);
267 if(len >= bleft)
268 return rc;
269
270 if(ses->serverOS)
271 kfree(ses->serverOS);
272
273 ses->serverOS = kzalloc(len + 1, GFP_KERNEL);
274 if(ses->serverOS)
275 strncpy(ses->serverOS, bcc_ptr, len);
276
277 bcc_ptr += len + 1;
278 bleft -= len + 1;
279
280 len = strnlen(bcc_ptr, bleft);
281 if(len >= bleft)
282 return rc;
283
284 if(ses->serverNOS)
285 kfree(ses->serverNOS);
286
287 ses->serverNOS = kzalloc(len + 1, GFP_KERNEL);
288 if(ses->serverNOS)
289 strncpy(ses->serverNOS, bcc_ptr, len);
290
291 bcc_ptr += len + 1;
292 bleft -= len + 1;
293
294 len = strnlen(bcc_ptr, bleft);
295 if(len > bleft)
296 return rc;
297
298 if(ses->serverDomain)
299 kfree(ses->serverDomain);
300
301 ses->serverDomain = kzalloc(len + 1, GFP_KERNEL);
302 if(ses->serverOS)
303 strncpy(ses->serverOS, bcc_ptr, len);
304
305 bcc_ptr += len + 1;
306 bleft -= len + 1;
307
308 cFYI(1,("ascii: bytes left %d",bleft));
309
310 return rc;
311}
312
313int
314CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
315 const struct nls_table *nls_cp)
316{
317 int rc = 0;
318 int wct;
319 int i;
320 struct smb_hdr *smb_buf;
321 char *bcc_ptr;
322 SESSION_SETUP_ANDX *pSMB;
323 __u32 capabilities;
324 int count;
325 int resp_buf_type = 0;
326 struct kvec iov[1];
327 enum securityEnum type;
328 __u16 action;
329 int bytes_remaining;
330
331 if(ses == NULL)
332 return -EINVAL;
333
334 type = ses->server->secType;
335 if(type == LANMAN) {
336#ifndef CONFIG_CIFS_WEAK_PW_HASH
337 /* LANMAN and plaintext are less secure and off by default.
338 So we make this explicitly be turned on in kconfig (in the
339 build) and turned on at runtime (changed from the default)
340 in proc/fs/cifs or via mount parm. Unfortunately this is
341 needed for old Win (e.g. Win95), some obscure NAS and OS/2 */
342 return -EOPNOTSUPP;
343#endif
344 wct = 10; /* lanman 2 style sessionsetup */
345 } else if(type == NTLM) /* NTLMv2 may retry NTLM */
346 wct = 13; /* old style NTLM sessionsetup */
347 else /* same size for negotiate or auth, NTLMSSP or extended security */
348 wct = 12;
349
350 rc = small_smb_init_no_tc(SMB_COM_SESSION_SETUP_ANDX, wct, ses,
351 (void **)&smb_buf);
352 if(rc)
353 return rc;
354
355 pSMB = (SESSION_SETUP_ANDX *)smb_buf;
356
357 capabilities = cifs_ssetup_hdr(ses, pSMB);
358 bcc_ptr = pByteArea(smb_buf);
359
360 if(type == LANMAN) {
361#ifdef CONFIG_CIFS_WEAK_PW_HASH
362 char lnm_session_key[CIFS_SESSION_KEY_SIZE];
363 char password_with_pad[CIFS_ENCPWD_SIZE];
364
365 /* no capabilities flags in old lanman negotiation */
366
367 pSMB->old_req.PasswordLength = CIFS_SESSION_KEY_SIZE;
368 /* BB calculate hash with password */
369 /* and copy into bcc */
370
371 memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
372 strncpy(password_with_pad, ses->password, CIFS_ENCPWD_SIZE);
373
374 /* calculate old style session key */
375 /* toupper may be less broken then repeatedly calling
376 nls_toupper would be, but neither handles multibyte code pages
377 but the only alternative would be converting to UCS-16 (Unicode)
378 uppercasing and converting back which is only worth doing if
379 we knew it were utf8. utf8 code page needs its own
380 toupper and tolower and strnicmp functions */
381
382 for(i = 0; i< CIFS_ENCPWD_SIZE; i++) {
383 password_with_pad[i] = toupper(password_with_pad[i]);
384 }
385
386 SMBencrypt(password_with_pad, ses->server->cryptKey,
387 lnm_session_key);
388
389#ifdef CONFIG_CIFS_DEBUG2
390 cifs_dump_mem("cryptkey: ",ses->server->cryptKey,
391 CIFS_SESSION_KEY_SIZE);
392#endif
393 /* clear password before we return/free memory */
394 memset(password_with_pad, 0, CIFS_ENCPWD_SIZE);
395 memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESSION_KEY_SIZE);
396 bcc_ptr += CIFS_SESSION_KEY_SIZE;
397
398 /* can not sign if LANMAN negotiated so no need
399 to calculate signing key? but what if server
400 changed to do higher than lanman dialect and
401 we reconnected would we ever calc signing_key? */
402
403 cERROR(1,("Negotiating LANMAN setting up strings"));
404 /* Unicode not allowed for LANMAN dialects */
405 ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
406#endif
407 } else if (type == NTLM) {
408 char ntlm_session_key[CIFS_SESSION_KEY_SIZE];
409
410 pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
411 pSMB->req_no_secext.CaseInsensitivePasswordLength =
412 cpu_to_le16(CIFS_SESSION_KEY_SIZE);
413 pSMB->req_no_secext.CaseSensitivePasswordLength =
414 cpu_to_le16(CIFS_SESSION_KEY_SIZE);
415
416 /* calculate session key */
417 SMBNTencrypt(ses->password, ses->server->cryptKey,
418 ntlm_session_key);
419
420 if(first_time) /* should this be moved into common code
421 with similar ntlmv2 path? */
422 cifs_calculate_mac_key(
423 ses->server->mac_signing_key,
424 ntlm_session_key, ses->password);
425 /* copy session key */
426
427 memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE);
428 bcc_ptr += CIFS_SESSION_KEY_SIZE;
429 memcpy(bcc_ptr, (char *)ntlm_session_key,CIFS_SESSION_KEY_SIZE);
430 bcc_ptr += CIFS_SESSION_KEY_SIZE;
431 if(ses->capabilities & CAP_UNICODE)
432 unicode_ssetup_strings(&bcc_ptr, ses, nls_cp);
433 else
434 ascii_ssetup_strings(&bcc_ptr, ses, nls_cp);
435 } else /* NTLMSSP or SPNEGO */ {
436 pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
437 capabilities |= CAP_EXTENDED_SECURITY;
438 pSMB->req.Capabilities = cpu_to_le32(capabilities);
439 /* BB set password lengths */
440 }
441
442 count = (long) bcc_ptr - (long) pByteArea(smb_buf);
443 smb_buf->smb_buf_length += count;
444
445 /* if we switch to small buffers, count will need to be fewer
446 than 383 (strings less than 335 bytes) */
447
448 BCC_LE(smb_buf) = cpu_to_le16(count);
449
450
451 /* BB FIXME check for other non ntlm code paths */
452
453 /* BB check is this too big for a small smb? */
454
455 iov[0].iov_base = (char *)pSMB;
456 iov[0].iov_len = smb_buf->smb_buf_length + 4;
457
458 rc = SendReceive2(xid, ses, iov, 1 /* num_iovecs */, &resp_buf_type, 0);
459 /* SMB request buf freed in SendReceive2 */
460
461 cFYI(1,("ssetup rc from sendrecv2 is %d",rc));
462 if(rc)
463 goto ssetup_exit;
464
465 pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
466 smb_buf = (struct smb_hdr *)iov[0].iov_base;
467
468 if((smb_buf->WordCount != 3) && (smb_buf->WordCount != 4)) {
469 rc = -EIO;
470 cERROR(1,("bad word count %d", smb_buf->WordCount));
471 goto ssetup_exit;
472 }
473 action = le16_to_cpu(pSMB->resp.Action);
474 if (action & GUEST_LOGIN)
475 cFYI(1, (" Guest login")); /* BB mark SesInfo struct? */
476 ses->Suid = smb_buf->Uid; /* UID left in wire format (le) */
477 cFYI(1, ("UID = %d ", ses->Suid));
478 /* response can have either 3 or 4 word count - Samba sends 3 */
479 /* and lanman response is 3 */
480 bytes_remaining = BCC(smb_buf);
481 bcc_ptr = pByteArea(smb_buf);
482
483 if(smb_buf->WordCount == 4) {
484 __u16 blob_len;
485 blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
486 bcc_ptr += blob_len;
487 if(blob_len > bytes_remaining) {
488 cERROR(1,("bad security blob length %d", blob_len));
489 rc = -EINVAL;
490 goto ssetup_exit;
491 }
492 bytes_remaining -= blob_len;
493 }
494
495 /* BB check if Unicode and decode strings */
496 if(smb_buf->Flags2 & SMBFLG2_UNICODE)
497 rc = decode_unicode_ssetup(&bcc_ptr, bytes_remaining,
498 ses, nls_cp);
499 else
500 rc = decode_ascii_ssetup(&bcc_ptr, bytes_remaining, ses,nls_cp);
501
502ssetup_exit:
503 if(resp_buf_type == CIFS_SMALL_BUFFER) {
504 cFYI(1,("ssetup freeing small buf %p", iov[0].iov_base));
505 cifs_small_buf_release(iov[0].iov_base);
506 } else if(resp_buf_type == CIFS_LARGE_BUFFER)
507 cifs_buf_release(iov[0].iov_base);
508
509 return rc;
510}
511#endif /* CONFIG_CIFS_EXPERIMENTAL */