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_m(user2,'\\')) || (p=strchr_m(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.
87 clistr_push(cli, pword, pass, -1, STR_TERMINATE);
88 fstrcpy(ntpword, ntpass);;
89 SMBencrypt((uchar *)pword,(uchar *)cli->cryptkey,(uchar *)pword);
90 SMBNTencrypt((uchar *)ntpword,(uchar *)cli->cryptkey,(uchar *)ntpword);
91 } else if ((cli->sec_mode & 2) && passlen == 24) {
93 * Encrypted mode needed, and encrypted password supplied.
95 memcpy(pword, pass, passlen);
97 memcpy(ntpword, ntpass, ntpasslen);
104 * Plaintext mode needed, assume plaintext supplied.
106 passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
107 fstrcpy(ntpword, "");
112 /* if in share level security then don't send a password now */
113 if (!(cli->sec_mode & 1)) {
116 fstrcpy(ntpword, "");
120 /* send a session setup command */
121 memset(cli->outbuf,'\0',smb_size);
123 if (cli->protocol < PROTOCOL_NT1)
125 set_message(cli->outbuf,10, 0, True);
126 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
127 cli_setup_packet(cli);
129 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
130 SSVAL(cli->outbuf,smb_vwv2,cli->max_xmit);
131 SSVAL(cli->outbuf,smb_vwv3,2);
132 SSVAL(cli->outbuf,smb_vwv4,1);
133 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
134 SSVAL(cli->outbuf,smb_vwv7,passlen);
135 p = smb_buf(cli->outbuf);
136 memcpy(p,pword,passlen);
138 p += clistr_push(cli, p, user, -1, STR_UPPER|STR_TERMINATE);
139 cli_setup_bcc(cli, p);
145 capabilities = CAP_NT_SMBS;
147 /* Set the CLI_FORCE_DOSERR environment variable to test
148 client routines using DOS errors instead of STATUS32
149 ones. This intended only as a temporary hack. */
151 if (!getenv("CLI_FORCE_DOSERR")) {
152 capabilities |= CAP_STATUS32;
155 if (cli->use_level_II_oplocks) {
156 capabilities |= CAP_LEVEL_II_OPLOCKS;
158 if (cli->capabilities & CAP_UNICODE) {
159 capabilities |= CAP_UNICODE;
161 set_message(cli->outbuf,13,0,True);
162 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
163 cli_setup_packet(cli);
165 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
166 SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
167 SSVAL(cli->outbuf,smb_vwv3,2);
168 SSVAL(cli->outbuf,smb_vwv4,cli->pid);
169 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
170 SSVAL(cli->outbuf,smb_vwv7,passlen);
171 SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
172 SIVAL(cli->outbuf,smb_vwv11,capabilities);
173 p = smb_buf(cli->outbuf);
174 memcpy(p,pword,passlen);
175 p += SVAL(cli->outbuf,smb_vwv7);
176 memcpy(p,ntpword,ntpasslen);
177 p += SVAL(cli->outbuf,smb_vwv8);
178 p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
179 p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
180 p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
181 p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
182 cli_setup_bcc(cli, p);
186 if (!cli_receive_smb(cli))
189 show_msg(cli->inbuf);
191 if (CVAL(cli->inbuf,smb_rcls) != 0) {
195 /* use the returned vuid from now on */
196 cli->vuid = SVAL(cli->inbuf,smb_uid);
198 if (cli->protocol >= PROTOCOL_NT1) {
200 * Save off some of the connected server
203 char *q = smb_buf(cli->inbuf);
204 q += clistr_pull(cli, cli->server_os, q, sizeof(fstring), -1, STR_TERMINATE);
205 q += clistr_pull(cli, cli->server_type, q, sizeof(fstring), -1, STR_TERMINATE);
206 q += clistr_pull(cli, cli->server_domain, q, sizeof(fstring), -1, STR_TERMINATE);
209 fstrcpy(cli->user_name, user);
214 /****************************************************************************
216 *****************************************************************************/
218 BOOL cli_ulogoff(struct cli_state *cli)
220 memset(cli->outbuf,'\0',smb_size);
221 set_message(cli->outbuf,2,0,True);
222 CVAL(cli->outbuf,smb_com) = SMBulogoffX;
223 cli_setup_packet(cli);
224 SSVAL(cli->outbuf,smb_vwv0,0xFF);
225 SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
228 if (!cli_receive_smb(cli))
231 return CVAL(cli->inbuf,smb_rcls) == 0;
234 /****************************************************************************
236 ****************************************************************************/
237 BOOL cli_send_tconX(struct cli_state *cli,
238 const char *share, const char *dev, const char *pass, int passlen)
240 fstring fullshare, pword, dos_pword;
242 memset(cli->outbuf,'\0',smb_size);
243 memset(cli->inbuf,'\0',smb_size);
245 fstrcpy(cli->share, share);
247 /* in user level security don't send a password now */
248 if (cli->sec_mode & 1) {
253 if ((cli->sec_mode & 2) && *pass && passlen != 24) {
255 * Non-encrypted passwords - convert to DOS codepage before encryption.
258 clistr_push(cli, dos_pword, pass, -1, STR_TERMINATE);
260 SMBencrypt((uchar *)dos_pword,(uchar *)cli->cryptkey,(uchar *)pword);
262 if((cli->sec_mode & 3) == 0) {
264 * Non-encrypted passwords - convert to DOS codepage before using.
266 passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
268 memcpy(pword, pass, passlen);
272 if (cli->port == 445) {
273 slprintf(fullshare, sizeof(fullshare)-1,
276 slprintf(fullshare, sizeof(fullshare)-1,
277 "\\\\%s\\%s", "foo", share);
280 set_message(cli->outbuf,4, 0, True);
281 CVAL(cli->outbuf,smb_com) = SMBtconX;
282 cli_setup_packet(cli);
284 SSVAL(cli->outbuf,smb_vwv0,0xFF);
285 SSVAL(cli->outbuf,smb_vwv3,passlen);
287 p = smb_buf(cli->outbuf);
288 memcpy(p,pword,passlen);
290 p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
291 fstrcpy(p, dev); p += strlen(dev)+1;
293 cli_setup_bcc(cli, p);
295 SCVAL(cli->inbuf,smb_rcls, 1);
298 if (!cli_receive_smb(cli))
301 if (CVAL(cli->inbuf,smb_rcls) != 0) {
305 fstrcpy(cli->dev, "A:");
307 if (cli->protocol >= PROTOCOL_NT1) {
308 clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE);
311 if (strcasecmp(share,"IPC$")==0) {
312 fstrcpy(cli->dev, "IPC");
315 /* only grab the device if we have a recent protocol level */
316 if (cli->protocol >= PROTOCOL_NT1 &&
317 smb_buflen(cli->inbuf) == 3) {
318 /* almost certainly win95 - enable bug fixes */
322 cli->cnum = SVAL(cli->inbuf,smb_tid);
327 /****************************************************************************
328 send a tree disconnect
329 ****************************************************************************/
330 BOOL cli_tdis(struct cli_state *cli)
332 memset(cli->outbuf,'\0',smb_size);
333 set_message(cli->outbuf,0,0,True);
334 CVAL(cli->outbuf,smb_com) = SMBtdis;
335 SSVAL(cli->outbuf,smb_tid,cli->cnum);
336 cli_setup_packet(cli);
339 if (!cli_receive_smb(cli))
342 return CVAL(cli->inbuf,smb_rcls) == 0;
346 /****************************************************************************
347 send a negprot command
348 ****************************************************************************/
349 void cli_negprot_send(struct cli_state *cli)
354 memset(cli->outbuf,'\0',smb_size);
356 /* setup the protocol strings */
357 set_message(cli->outbuf,0,0,True);
359 p = smb_buf(cli->outbuf);
361 prots[numprots].name && prots[numprots].prot<=cli->protocol;
364 p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
367 CVAL(cli->outbuf,smb_com) = SMBnegprot;
368 cli_setup_bcc(cli, p);
369 cli_setup_packet(cli);
371 CVAL(smb_buf(cli->outbuf),0) = 2;
377 /****************************************************************************
378 send a negprot command
379 ****************************************************************************/
380 BOOL cli_negprot(struct cli_state *cli)
386 memset(cli->outbuf,'\0',smb_size);
388 /* setup the protocol strings */
389 for (plength=0,numprots=0;
390 prots[numprots].name && prots[numprots].prot<=cli->protocol;
392 plength += strlen(prots[numprots].name)+2;
394 set_message(cli->outbuf,0,plength,True);
396 p = smb_buf(cli->outbuf);
398 prots[numprots].name && prots[numprots].prot<=cli->protocol;
401 p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
404 CVAL(cli->outbuf,smb_com) = SMBnegprot;
405 cli_setup_packet(cli);
407 CVAL(smb_buf(cli->outbuf),0) = 2;
410 if (!cli_receive_smb(cli))
413 show_msg(cli->inbuf);
415 if (CVAL(cli->inbuf,smb_rcls) != 0 ||
416 ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
420 cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
422 if (cli->protocol >= PROTOCOL_NT1) {
424 cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
425 cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
426 cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
427 cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
428 cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
429 cli->serverzone *= 60;
430 /* this time arrives in real GMT */
431 cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
432 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
433 cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
434 if (cli->capabilities & CAP_RAW_MODE) {
435 cli->readbraw_supported = True;
436 cli->writebraw_supported = True;
438 /* work out if they sent us a workgroup */
439 if (smb_buflen(cli->inbuf) > 8) {
440 clistr_pull(cli, cli->server_domain,
441 smb_buf(cli->inbuf)+8, sizeof(cli->server_domain),
442 smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
444 } else if (cli->protocol >= PROTOCOL_LANMAN1) {
445 cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
446 cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
447 cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
448 cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
449 cli->serverzone *= 60;
450 /* this time is converted to GMT by make_unix_date */
451 cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
452 cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
453 cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
454 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
456 /* the old core protocol */
458 cli->serverzone = TimeDiff(time(NULL));
461 cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
463 /* a way to force ascii SMB */
464 if (getenv("CLI_FORCE_ASCII")) {
465 cli->capabilities &= ~CAP_UNICODE;
472 /****************************************************************************
473 send a session request. see rfc1002.txt 4.3 and 4.3.2
474 ****************************************************************************/
475 BOOL cli_session_request(struct cli_state *cli,
476 struct nmb_name *calling, struct nmb_name *called)
480 extern pstring user_socket_options;
482 /* 445 doesn't have session request */
483 if (cli->port == 445) return True;
485 /* send a session request (RFC 1002) */
487 memcpy(&(cli->calling), calling, sizeof(*calling));
488 memcpy(&(cli->called ), called , sizeof(*called ));
490 /* put in the destination name */
492 name_mangle(cli->called .name, p, cli->called .name_type);
497 name_mangle(cli->calling.name, p, cli->calling.name_type);
500 /* setup the packet length */
501 _smb_setlen(cli->outbuf,len);
502 CVAL(cli->outbuf,0) = 0x81;
506 #endif /* WITH_SSL */
509 DEBUG(5,("Sent session request\n"));
511 if (!cli_receive_smb(cli))
514 if (CVAL(cli->inbuf,0) == 0x84) {
515 /* C. Hoch 9/14/95 Start */
516 /* For information, here is the response structure.
517 * We do the byte-twiddling to for portability.
518 struct RetargetResponse{
526 int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
527 /* SESSION RETARGET */
528 putip((char *)&cli->dest_ip,cli->inbuf+4);
530 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
534 DEBUG(3,("Retargeted\n"));
536 set_socket_options(cli->fd,user_socket_options);
543 DEBUG(0,("Retarget recursion - failing\n"));
547 ret = cli_session_request(cli, calling, called);
551 } /* C. Hoch 9/14/95 End */
554 if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */
555 if (!sslutil_fd_is_ssl(cli->fd)){
556 if (sslutil_connect(cli->fd) == 0)
560 #endif /* WITH_SSL */
562 if (CVAL(cli->inbuf,0) != 0x82) {
563 /* This is the wrong place to put the error... JRA. */
564 cli->rap_error = CVAL(cli->inbuf,4);
570 /****************************************************************************
571 open the client sockets
572 ****************************************************************************/
573 BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
575 extern struct in_addr ipzero;
576 extern pstring user_socket_options;
578 fstrcpy(cli->desthost, host);
580 if (!ip || ip_equal(*ip, ipzero)) {
581 if (!resolve_name( cli->desthost, &cli->dest_ip, 0x20)) {
584 if (ip) *ip = cli->dest_ip;
589 if (getenv("LIBSMB_PROG")) {
590 cli->fd = sock_exec(getenv("LIBSMB_PROG"));
592 /* try 445 first, then 139 */
593 int port = cli->port?cli->port:445;
594 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
596 if (cli->fd == -1 && cli->port == 0) {
598 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
601 if (cli->fd != -1) cli->port = port;
604 DEBUG(1,("Error connecting to %s (%s)\n",
605 inet_ntoa(*ip),strerror(errno)));
609 set_socket_options(cli->fd,user_socket_options);
614 /****************************************************************************
615 re-establishes a connection
616 ****************************************************************************/
617 BOOL cli_reestablish_connection(struct cli_state *cli)
619 struct nmb_name calling;
620 struct nmb_name called;
624 BOOL do_tcon = False;
627 if (!cli->initialised || cli->fd == -1)
629 DEBUG(3,("cli_reestablish_connection: not connected\n"));
633 /* copy the parameters necessary to re-establish the connection */
637 fstrcpy(share, cli->share);
638 fstrcpy(dev , cli->dev);
642 memcpy(&called , &(cli->called ), sizeof(called ));
643 memcpy(&calling, &(cli->calling), sizeof(calling));
644 fstrcpy(dest_host, cli->full_dest_host_name);
646 DEBUG(5,("cli_reestablish_connection: %s connecting to %s (ip %s) - %s [%s]\n",
647 nmb_namestr(&calling), nmb_namestr(&called),
648 inet_ntoa(cli->dest_ip),
649 cli->user_name, cli->domain));
653 if (cli_establish_connection(cli,
654 dest_host, &cli->dest_ip,
656 share, dev, False, do_tcon)) {
657 if ((cli->fd != oldfd) && (oldfd != -1)) {
665 /****************************************************************************
666 establishes a connection right up to doing tconX, reading in a password.
667 ****************************************************************************/
668 BOOL cli_establish_connection(struct cli_state *cli,
669 char *dest_host, struct in_addr *dest_ip,
670 struct nmb_name *calling, struct nmb_name *called,
671 char *service, char *service_type,
672 BOOL do_shutdown, BOOL do_tcon)
674 DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n",
675 nmb_namestr(calling), nmb_namestr(called), inet_ntoa(*dest_ip),
676 cli->user_name, cli->domain));
678 /* establish connection */
680 if ((!cli->initialised))
687 if (!cli_connect(cli, dest_host, dest_ip))
689 DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
690 nmb_namestr(calling), inet_ntoa(*dest_ip)));
695 if (!cli_session_request(cli, calling, called))
697 DEBUG(1,("failed session request\n"));
703 if (!cli_negprot(cli))
705 DEBUG(1,("failed negprot\n"));
711 if (cli->pwd.cleartext || cli->pwd.null_pwd)
716 if (cli->pwd.null_pwd)
718 /* attempt null session */
724 /* attempt clear-text session */
725 pwd_get_cleartext(&(cli->pwd), passwd);
726 pass_len = strlen(passwd);
729 /* attempt clear-text session */
730 if (!cli_session_setup(cli, cli->user_name,
735 DEBUG(1,("failed session setup\n"));
744 if (!cli_send_tconX(cli, service, service_type,
745 (char*)passwd, strlen(passwd)))
747 DEBUG(1,("failed tcon_X\n"));
758 /* attempt encrypted session */
759 unsigned char nt_sess_pwd[24];
760 unsigned char lm_sess_pwd[24];
762 /* creates (storing a copy of) and then obtains a 24 byte password OWF */
763 pwd_make_lm_nt_owf(&(cli->pwd), cli->cryptkey);
764 pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
766 /* attempt encrypted session */
767 if (!cli_session_setup(cli, cli->user_name,
768 (char*)lm_sess_pwd, sizeof(lm_sess_pwd),
769 (char*)nt_sess_pwd, sizeof(nt_sess_pwd),
772 DEBUG(1,("failed session setup\n"));
778 DEBUG(1,("session setup ok\n"));
780 if (*cli->server_domain || *cli->server_os || *cli->server_type)
782 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
790 if (!cli_send_tconX(cli, service, service_type,
791 (char*)nt_sess_pwd, sizeof(nt_sess_pwd)))
793 DEBUG(1,("failed tcon_X\n"));
808 /****************************************************************************
809 Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
810 ****************************************************************************/
812 BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char *desthost,
813 struct in_addr *pdest_ip)
815 struct nmb_name calling, called;
817 make_nmb_name(&calling, srchost, 0x0);
820 * If the called name is an IP address
821 * then use *SMBSERVER immediately.
824 if(is_ipaddress(desthost))
825 make_nmb_name(&called, "*SMBSERVER", 0x20);
827 make_nmb_name(&called, desthost, 0x20);
829 if (!cli_session_request(cli, &calling, &called)) {
830 struct nmb_name smbservername;
832 make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
835 * If the name wasn't *SMBSERVER then
836 * try with *SMBSERVER if the first name fails.
839 if (nmb_name_equal(&called, &smbservername)) {
842 * The name used was *SMBSERVER, don't bother with another name.
845 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
846 with error %s.\n", desthost, cli_errstr(cli) ));
853 if (!cli_initialise(cli) ||
854 !cli_connect(cli, desthost, pdest_ip) ||
855 !cli_session_request(cli, &calling, &smbservername)) {
856 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
857 name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));