2 Unix SMB/Netbios implementation.
4 client connect/disconnect routines
5 Copyright (C) Andrew Tridgell 1994-1998
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program 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 the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33 {PROTOCOL_CORE,"PC NETWORK PROGRAM 1.0"},
34 {PROTOCOL_COREPLUS,"MICROSOFT NETWORKS 1.03"},
35 {PROTOCOL_LANMAN1,"MICROSOFT NETWORKS 3.0"},
36 {PROTOCOL_LANMAN1,"LANMAN1.0"},
37 {PROTOCOL_LANMAN2,"LM1.2X002"},
38 {PROTOCOL_LANMAN2,"Samba"},
39 {PROTOCOL_NT1,"NT LANMAN 1.0"},
40 {PROTOCOL_NT1,"NT LM 0.12"},
45 /****************************************************************************
46 Send a session setup. The username and workgroup is in UNIX character
47 format and must be converted to DOS codepage format before sending. If the
48 password is in plaintext, the same should be done.
49 ****************************************************************************/
51 BOOL cli_session_setup(struct cli_state *cli,
53 char *pass, int passlen,
54 char *ntpass, int ntpasslen,
58 fstring pword, ntpword;
61 /* allow for workgroups as part of the username */
63 if ((p=strchr(user2,'\\')) || (p=strchr(user2,'/'))) {
69 if (cli->protocol < PROTOCOL_LANMAN1)
72 if (passlen > sizeof(pword)-1 || ntpasslen > sizeof(ntpword)-1) {
76 if (((passlen == 0) || (passlen == 1)) && (pass[0] == '\0')) {
77 /* Null session connect. */
81 if ((cli->sec_mode & 2) && passlen != 24) {
83 * Encrypted mode needed, and non encrypted password supplied.
88 unix_to_dos(pword,True);
89 fstrcpy(ntpword, ntpass);;
90 SMBencrypt((uchar *)pword,(uchar *)cli->cryptkey,(uchar *)pword);
91 SMBNTencrypt((uchar *)ntpword,(uchar *)cli->cryptkey,(uchar *)ntpword);
92 } else if ((cli->sec_mode & 2) && passlen == 24) {
94 * Encrypted mode needed, and encrypted password supplied.
96 memcpy(pword, pass, passlen);
98 memcpy(ntpword, ntpass, ntpasslen);
100 fstrcpy(ntpword, "");
105 * Plaintext mode needed, assume plaintext supplied.
107 passlen = clistr_push(cli, pword, pass, -1, CLISTR_CONVERT|CLISTR_TERMINATE);
108 fstrcpy(ntpword, "");
113 /* if in share level security then don't send a password now */
114 if (!(cli->sec_mode & 1)) {
117 fstrcpy(ntpword, "");
121 /* send a session setup command */
122 memset(cli->outbuf,'\0',smb_size);
124 if (cli->protocol < PROTOCOL_NT1)
126 set_message(cli->outbuf,10, 0, True);
127 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
128 cli_setup_packet(cli);
130 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
131 SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
132 SSVAL(cli->outbuf,smb_vwv3,2);
133 SSVAL(cli->outbuf,smb_vwv4,1);
134 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
135 SSVAL(cli->outbuf,smb_vwv7,passlen);
136 p = smb_buf(cli->outbuf);
137 memcpy(p,pword,passlen);
139 p += clistr_push(cli, p, user, -1, CLISTR_CONVERT|CLISTR_UPPER|CLISTR_TERMINATE);
140 set_message(cli->outbuf,10,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
146 capabilities = CAP_NT_SMBS;
147 if (cli->use_level_II_oplocks)
148 capabilities |= CAP_LEVEL_II_OPLOCKS;
149 if (getenv("USE_UNICODE") &&
150 (cli->capabilities & CAP_UNICODE)) {
151 capabilities |= CAP_UNICODE;
153 set_message(cli->outbuf,13,0,True);
154 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
155 cli_setup_packet(cli);
157 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
158 SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
159 SSVAL(cli->outbuf,smb_vwv3,2);
160 SSVAL(cli->outbuf,smb_vwv4,cli->pid);
161 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
162 SSVAL(cli->outbuf,smb_vwv7,passlen);
163 SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
164 SIVAL(cli->outbuf,smb_vwv11,capabilities);
165 p = smb_buf(cli->outbuf);
166 memcpy(p,pword,passlen);
167 p += SVAL(cli->outbuf,smb_vwv7);
168 memcpy(p,ntpword,ntpasslen);
169 p += SVAL(cli->outbuf,smb_vwv8);
170 p += clistr_push(cli, p, user, -1, CLISTR_CONVERT|CLISTR_TERMINATE|CLISTR_UPPER);
171 p += clistr_push(cli, p, workgroup, -1, CLISTR_CONVERT|CLISTR_TERMINATE|CLISTR_UPPER);
172 p += clistr_push(cli, p, "Unix", -1, CLISTR_CONVERT|CLISTR_TERMINATE);
173 p += clistr_push(cli, p, "Samba", -1, CLISTR_CONVERT|CLISTR_TERMINATE);
174 set_message(cli->outbuf,13,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
178 if (!cli_receive_smb(cli))
181 show_msg(cli->inbuf);
183 if (CVAL(cli->inbuf,smb_rcls) != 0) {
187 /* use the returned vuid from now on */
188 cli->vuid = SVAL(cli->inbuf,smb_uid);
190 if (cli->protocol >= PROTOCOL_NT1) {
192 * Save off some of the connected server
195 char *p = smb_buf(cli->inbuf);
196 p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
197 p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
198 p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
201 fstrcpy(cli->user_name, user);
206 /****************************************************************************
208 *****************************************************************************/
210 BOOL cli_ulogoff(struct cli_state *cli)
212 memset(cli->outbuf,'\0',smb_size);
213 set_message(cli->outbuf,2,0,True);
214 CVAL(cli->outbuf,smb_com) = SMBulogoffX;
215 cli_setup_packet(cli);
216 SSVAL(cli->outbuf,smb_vwv0,0xFF);
217 SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
220 if (!cli_receive_smb(cli))
223 return CVAL(cli->inbuf,smb_rcls) == 0;
226 /****************************************************************************
228 ****************************************************************************/
229 BOOL cli_send_tconX(struct cli_state *cli,
230 char *share, char *dev, char *pass, int passlen)
232 fstring fullshare, pword, dos_pword;
234 memset(cli->outbuf,'\0',smb_size);
235 memset(cli->inbuf,'\0',smb_size);
237 fstrcpy(cli->share, share);
239 /* in user level security don't send a password now */
240 if (cli->sec_mode & 1) {
245 if ((cli->sec_mode & 2) && *pass && passlen != 24) {
247 * Non-encrypted passwords - convert to DOS codepage before encryption.
250 fstrcpy(dos_pword,pass);
251 unix_to_dos(dos_pword,True);
252 SMBencrypt((uchar *)dos_pword,(uchar *)cli->cryptkey,(uchar *)pword);
254 if((cli->sec_mode & 3) == 0) {
256 * Non-encrypted passwords - convert to DOS codepage before using.
258 passlen = clistr_push(cli, pword, pass, -1, CLISTR_CONVERT|CLISTR_TERMINATE);
260 memcpy(pword, pass, passlen);
264 slprintf(fullshare, sizeof(fullshare)-1,
265 "\\\\%s\\%s", cli->desthost, share);
266 unix_to_dos(fullshare, True);
269 set_message(cli->outbuf,4, 0, True);
270 CVAL(cli->outbuf,smb_com) = SMBtconX;
271 cli_setup_packet(cli);
273 SSVAL(cli->outbuf,smb_vwv0,0xFF);
274 SSVAL(cli->outbuf,smb_vwv3,passlen);
276 p = smb_buf(cli->outbuf);
277 memcpy(p,pword,passlen);
279 p += clistr_push(cli, p, fullshare, -1, CLISTR_CONVERT | CLISTR_TERMINATE);
280 fstrcpy(p, dev); p += strlen(dev)+1;
282 set_message(cli->outbuf,4,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
284 SCVAL(cli->inbuf,smb_rcls, 1);
287 if (!cli_receive_smb(cli))
290 if (CVAL(cli->inbuf,smb_rcls) != 0) {
294 fstrcpy(cli->dev, "A:");
296 if (cli->protocol >= PROTOCOL_NT1) {
297 clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, CLISTR_TERMINATE | CLISTR_CONVERT);
300 if (strcasecmp(share,"IPC$")==0) {
301 fstrcpy(cli->dev, "IPC");
304 /* only grab the device if we have a recent protocol level */
305 if (cli->protocol >= PROTOCOL_NT1 &&
306 smb_buflen(cli->inbuf) == 3) {
307 /* almost certainly win95 - enable bug fixes */
311 cli->cnum = SVAL(cli->inbuf,smb_tid);
316 /****************************************************************************
317 send a tree disconnect
318 ****************************************************************************/
319 BOOL cli_tdis(struct cli_state *cli)
321 memset(cli->outbuf,'\0',smb_size);
322 set_message(cli->outbuf,0,0,True);
323 CVAL(cli->outbuf,smb_com) = SMBtdis;
324 SSVAL(cli->outbuf,smb_tid,cli->cnum);
325 cli_setup_packet(cli);
328 if (!cli_receive_smb(cli))
331 return CVAL(cli->inbuf,smb_rcls) == 0;
335 /****************************************************************************
336 send a negprot command
337 ****************************************************************************/
338 void cli_negprot_send(struct cli_state *cli)
344 memset(cli->outbuf,'\0',smb_size);
346 /* setup the protocol strings */
347 for (plength=0,numprots=0;
348 prots[numprots].name && prots[numprots].prot<=cli->protocol;
350 plength += strlen(prots[numprots].name)+2;
352 set_message(cli->outbuf,0,plength,True);
354 p = smb_buf(cli->outbuf);
356 prots[numprots].name && prots[numprots].prot<=cli->protocol;
359 pstrcpy(p,prots[numprots].name);
364 CVAL(cli->outbuf,smb_com) = SMBnegprot;
365 cli_setup_packet(cli);
367 CVAL(smb_buf(cli->outbuf),0) = 2;
373 /****************************************************************************
374 send a negprot command
375 ****************************************************************************/
376 BOOL cli_negprot(struct cli_state *cli)
382 memset(cli->outbuf,'\0',smb_size);
384 /* setup the protocol strings */
385 for (plength=0,numprots=0;
386 prots[numprots].name && prots[numprots].prot<=cli->protocol;
388 plength += strlen(prots[numprots].name)+2;
390 set_message(cli->outbuf,0,plength,True);
392 p = smb_buf(cli->outbuf);
394 prots[numprots].name && prots[numprots].prot<=cli->protocol;
397 pstrcpy(p,prots[numprots].name);
402 CVAL(cli->outbuf,smb_com) = SMBnegprot;
403 cli_setup_packet(cli);
405 CVAL(smb_buf(cli->outbuf),0) = 2;
408 if (!cli_receive_smb(cli))
411 show_msg(cli->inbuf);
413 if (CVAL(cli->inbuf,smb_rcls) != 0 ||
414 ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
418 cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
421 if (cli->protocol >= PROTOCOL_NT1) {
423 cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
424 cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
425 cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
426 cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
427 cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
428 cli->serverzone *= 60;
429 /* this time arrives in real GMT */
430 cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
431 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
432 cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
433 if (cli->capabilities & CAP_RAW_MODE) {
434 cli->readbraw_supported = True;
435 cli->writebraw_supported = True;
437 } else if (cli->protocol >= PROTOCOL_LANMAN1) {
438 cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
439 cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
440 cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
441 cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
442 cli->serverzone *= 60;
443 /* this time is converted to GMT by make_unix_date */
444 cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
445 cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
446 cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
447 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
449 /* the old core protocol */
451 cli->serverzone = TimeDiff(time(NULL));
454 cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
456 /* this ensures cli_use_unicode is setup - delete this call later (tridge) */
457 cli_setup_packet(cli);
463 /****************************************************************************
464 send a session request. see rfc1002.txt 4.3 and 4.3.2
465 ****************************************************************************/
466 BOOL cli_session_request(struct cli_state *cli,
467 struct nmb_name *calling, struct nmb_name *called)
471 extern pstring user_socket_options;
473 /* send a session request (RFC 1002) */
475 memcpy(&(cli->calling), calling, sizeof(*calling));
476 memcpy(&(cli->called ), called , sizeof(*called ));
478 /* put in the destination name */
480 name_mangle(cli->called .name, p, cli->called .name_type);
485 name_mangle(cli->calling.name, p, cli->calling.name_type);
488 /* setup the packet length */
489 _smb_setlen(cli->outbuf,len);
490 CVAL(cli->outbuf,0) = 0x81;
494 #endif /* WITH_SSL */
497 DEBUG(5,("Sent session request\n"));
499 if (!cli_receive_smb(cli))
502 if (CVAL(cli->inbuf,0) == 0x84) {
503 /* C. Hoch 9/14/95 Start */
504 /* For information, here is the response structure.
505 * We do the byte-twiddling to for portability.
506 struct RetargetResponse{
514 int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
515 /* SESSION RETARGET */
516 putip((char *)&cli->dest_ip,cli->inbuf+4);
518 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
522 DEBUG(3,("Retargeted\n"));
524 set_socket_options(cli->fd,user_socket_options);
531 DEBUG(0,("Retarget recursion - failing\n"));
535 ret = cli_session_request(cli, calling, called);
539 } /* C. Hoch 9/14/95 End */
542 if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */
543 if (!sslutil_fd_is_ssl(cli->fd)){
544 if (sslutil_connect(cli->fd) == 0)
548 #endif /* WITH_SSL */
550 if (CVAL(cli->inbuf,0) != 0x82) {
551 /* This is the wrong place to put the error... JRA. */
552 cli->rap_error = CVAL(cli->inbuf,4);
559 /****************************************************************************
560 open the client sockets
561 ****************************************************************************/
562 BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
564 extern struct in_addr ipzero;
565 extern pstring user_socket_options;
567 fstrcpy(cli->desthost, host);
569 if (!ip || ip_equal(*ip, ipzero)) {
570 if (!resolve_name( cli->desthost, &cli->dest_ip, 0x20)) {
573 if (ip) *ip = cli->dest_ip;
578 if (cli->port == 0) cli->port = 139; /* Set to default */
580 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
581 cli->port, cli->timeout);
585 set_socket_options(cli->fd,user_socket_options);
590 /****************************************************************************
591 re-establishes a connection
592 ****************************************************************************/
593 BOOL cli_reestablish_connection(struct cli_state *cli)
595 struct nmb_name calling;
596 struct nmb_name called;
600 BOOL do_tcon = False;
603 if (!cli->initialised || cli->fd == -1)
605 DEBUG(3,("cli_reestablish_connection: not connected\n"));
609 /* copy the parameters necessary to re-establish the connection */
613 fstrcpy(share, cli->share);
614 fstrcpy(dev , cli->dev);
618 memcpy(&called , &(cli->called ), sizeof(called ));
619 memcpy(&calling, &(cli->calling), sizeof(calling));
620 fstrcpy(dest_host, cli->full_dest_host_name);
622 DEBUG(5,("cli_reestablish_connection: %s connecting to %s (ip %s) - %s [%s]\n",
623 nmb_namestr(&calling), nmb_namestr(&called),
624 inet_ntoa(cli->dest_ip),
625 cli->user_name, cli->domain));
629 if (cli_establish_connection(cli,
630 dest_host, &cli->dest_ip,
632 share, dev, False, do_tcon)) {
633 if ((cli->fd != oldfd) && (oldfd != -1)) {
641 /****************************************************************************
642 establishes a connection right up to doing tconX, reading in a password.
643 ****************************************************************************/
644 BOOL cli_establish_connection(struct cli_state *cli,
645 char *dest_host, struct in_addr *dest_ip,
646 struct nmb_name *calling, struct nmb_name *called,
647 char *service, char *service_type,
648 BOOL do_shutdown, BOOL do_tcon)
650 DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n",
651 nmb_namestr(calling), nmb_namestr(called), inet_ntoa(*dest_ip),
652 cli->user_name, cli->domain));
654 /* establish connection */
656 if ((!cli->initialised))
663 if (!cli_connect(cli, dest_host, dest_ip))
665 DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
666 nmb_namestr(calling), inet_ntoa(*dest_ip)));
671 if (!cli_session_request(cli, calling, called))
673 DEBUG(1,("failed session request\n"));
679 if (!cli_negprot(cli))
681 DEBUG(1,("failed negprot\n"));
687 if (cli->pwd.cleartext || cli->pwd.null_pwd)
692 if (cli->pwd.null_pwd)
694 /* attempt null session */
700 /* attempt clear-text session */
701 pwd_get_cleartext(&(cli->pwd), passwd);
702 pass_len = strlen(passwd);
705 /* attempt clear-text session */
706 if (!cli_session_setup(cli, cli->user_name,
711 DEBUG(1,("failed session setup\n"));
720 if (!cli_send_tconX(cli, service, service_type,
721 (char*)passwd, strlen(passwd)))
723 DEBUG(1,("failed tcon_X\n"));
734 /* attempt encrypted session */
735 unsigned char nt_sess_pwd[24];
736 unsigned char lm_sess_pwd[24];
738 /* creates (storing a copy of) and then obtains a 24 byte password OWF */
739 pwd_make_lm_nt_owf(&(cli->pwd), cli->cryptkey);
740 pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
742 /* attempt encrypted session */
743 if (!cli_session_setup(cli, cli->user_name,
744 (char*)lm_sess_pwd, sizeof(lm_sess_pwd),
745 (char*)nt_sess_pwd, sizeof(nt_sess_pwd),
748 DEBUG(1,("failed session setup\n"));
754 DEBUG(1,("session setup ok\n"));
756 if (*cli->server_domain || *cli->server_os || *cli->server_type)
758 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
766 if (!cli_send_tconX(cli, service, service_type,
767 (char*)nt_sess_pwd, sizeof(nt_sess_pwd)))
769 DEBUG(1,("failed tcon_X\n"));
784 /****************************************************************************
785 Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
786 ****************************************************************************/
788 BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char *desthost,
789 struct in_addr *pdest_ip)
791 struct nmb_name calling, called;
793 make_nmb_name(&calling, srchost, 0x0);
796 * If the called name is an IP address
797 * then use *SMBSERVER immediately.
800 if(is_ipaddress(desthost))
801 make_nmb_name(&called, "*SMBSERVER", 0x20);
803 make_nmb_name(&called, desthost, 0x20);
805 if (!cli_session_request(cli, &calling, &called)) {
806 struct nmb_name smbservername;
808 make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
811 * If the name wasn't *SMBSERVER then
812 * try with *SMBSERVER if the first name fails.
815 if (nmb_name_equal(&called, &smbservername)) {
818 * The name used was *SMBSERVER, don't bother with another name.
821 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
822 with error %s.\n", desthost, cli_errstr(cli) ));
829 if (!cli_initialise(cli) ||
830 !cli_connect(cli, desthost, pdest_ip) ||
831 !cli_session_request(cli, &calling, &smbservername)) {
832 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
833 name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));