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;
146 if (cli->use_level_II_oplocks) {
147 capabilities |= CAP_LEVEL_II_OPLOCKS;
149 if (cli->capabilities & CAP_UNICODE) {
150 capabilities |= CAP_UNICODE;
152 set_message(cli->outbuf,13,0,True);
153 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
154 cli_setup_packet(cli);
156 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
157 SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
158 SSVAL(cli->outbuf,smb_vwv3,2);
159 SSVAL(cli->outbuf,smb_vwv4,cli->pid);
160 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
161 SSVAL(cli->outbuf,smb_vwv7,passlen);
162 SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
163 SIVAL(cli->outbuf,smb_vwv11,capabilities);
164 p = smb_buf(cli->outbuf);
165 memcpy(p,pword,passlen);
166 p += SVAL(cli->outbuf,smb_vwv7);
167 memcpy(p,ntpword,ntpasslen);
168 p += SVAL(cli->outbuf,smb_vwv8);
169 p += clistr_push(cli, p, user, -1, STR_TERMINATE|STR_UPPER);
170 p += clistr_push(cli, p, workgroup, -1, STR_TERMINATE|STR_UPPER);
171 p += clistr_push(cli, p, "Unix", -1, STR_TERMINATE);
172 p += clistr_push(cli, p, "Samba", -1, STR_TERMINATE);
173 cli_setup_bcc(cli, p);
177 if (!cli_receive_smb(cli))
180 show_msg(cli->inbuf);
182 if (CVAL(cli->inbuf,smb_rcls) != 0) {
186 /* use the returned vuid from now on */
187 cli->vuid = SVAL(cli->inbuf,smb_uid);
189 if (cli->protocol >= PROTOCOL_NT1) {
191 * Save off some of the connected server
194 char *q = smb_buf(cli->inbuf);
195 q += clistr_pull(cli, cli->server_os, q, sizeof(fstring), -1, STR_TERMINATE);
196 q += clistr_pull(cli, cli->server_type, q, sizeof(fstring), -1, STR_TERMINATE);
197 q += clistr_pull(cli, cli->server_domain, q, sizeof(fstring), -1, STR_TERMINATE);
200 fstrcpy(cli->user_name, user);
205 /****************************************************************************
207 *****************************************************************************/
209 BOOL cli_ulogoff(struct cli_state *cli)
211 memset(cli->outbuf,'\0',smb_size);
212 set_message(cli->outbuf,2,0,True);
213 CVAL(cli->outbuf,smb_com) = SMBulogoffX;
214 cli_setup_packet(cli);
215 SSVAL(cli->outbuf,smb_vwv0,0xFF);
216 SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
219 if (!cli_receive_smb(cli))
222 return CVAL(cli->inbuf,smb_rcls) == 0;
225 /****************************************************************************
227 ****************************************************************************/
228 BOOL cli_send_tconX(struct cli_state *cli,
229 const char *share, const char *dev, const char *pass, int passlen)
231 fstring fullshare, pword, dos_pword;
233 memset(cli->outbuf,'\0',smb_size);
234 memset(cli->inbuf,'\0',smb_size);
236 fstrcpy(cli->share, share);
238 /* in user level security don't send a password now */
239 if (cli->sec_mode & 1) {
244 if ((cli->sec_mode & 2) && *pass && passlen != 24) {
246 * Non-encrypted passwords - convert to DOS codepage before encryption.
249 clistr_push(cli, dos_pword, pass, -1, STR_TERMINATE);
251 SMBencrypt((uchar *)dos_pword,(uchar *)cli->cryptkey,(uchar *)pword);
253 if((cli->sec_mode & 3) == 0) {
255 * Non-encrypted passwords - convert to DOS codepage before using.
257 passlen = clistr_push(cli, pword, pass, -1, STR_TERMINATE);
259 memcpy(pword, pass, passlen);
263 slprintf(fullshare, sizeof(fullshare)-1,
264 "\\\\%s\\%s", cli->desthost, share);
266 set_message(cli->outbuf,4, 0, True);
267 CVAL(cli->outbuf,smb_com) = SMBtconX;
268 cli_setup_packet(cli);
270 SSVAL(cli->outbuf,smb_vwv0,0xFF);
271 SSVAL(cli->outbuf,smb_vwv3,passlen);
273 p = smb_buf(cli->outbuf);
274 memcpy(p,pword,passlen);
276 p += clistr_push(cli, p, fullshare, -1, STR_TERMINATE |STR_UPPER);
277 fstrcpy(p, dev); p += strlen(dev)+1;
279 cli_setup_bcc(cli, p);
281 SCVAL(cli->inbuf,smb_rcls, 1);
284 if (!cli_receive_smb(cli))
287 if (CVAL(cli->inbuf,smb_rcls) != 0) {
291 fstrcpy(cli->dev, "A:");
293 if (cli->protocol >= PROTOCOL_NT1) {
294 clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, STR_TERMINATE);
297 if (strcasecmp(share,"IPC$")==0) {
298 fstrcpy(cli->dev, "IPC");
301 /* only grab the device if we have a recent protocol level */
302 if (cli->protocol >= PROTOCOL_NT1 &&
303 smb_buflen(cli->inbuf) == 3) {
304 /* almost certainly win95 - enable bug fixes */
308 cli->cnum = SVAL(cli->inbuf,smb_tid);
313 /****************************************************************************
314 send a tree disconnect
315 ****************************************************************************/
316 BOOL cli_tdis(struct cli_state *cli)
318 memset(cli->outbuf,'\0',smb_size);
319 set_message(cli->outbuf,0,0,True);
320 CVAL(cli->outbuf,smb_com) = SMBtdis;
321 SSVAL(cli->outbuf,smb_tid,cli->cnum);
322 cli_setup_packet(cli);
325 if (!cli_receive_smb(cli))
328 return CVAL(cli->inbuf,smb_rcls) == 0;
332 /****************************************************************************
333 send a negprot command
334 ****************************************************************************/
335 void cli_negprot_send(struct cli_state *cli)
340 memset(cli->outbuf,'\0',smb_size);
342 /* setup the protocol strings */
343 set_message(cli->outbuf,0,0,True);
345 p = smb_buf(cli->outbuf);
347 prots[numprots].name && prots[numprots].prot<=cli->protocol;
350 p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
353 CVAL(cli->outbuf,smb_com) = SMBnegprot;
354 cli_setup_bcc(cli, p);
355 cli_setup_packet(cli);
357 CVAL(smb_buf(cli->outbuf),0) = 2;
363 /****************************************************************************
364 send a negprot command
365 ****************************************************************************/
366 BOOL cli_negprot(struct cli_state *cli)
372 memset(cli->outbuf,'\0',smb_size);
374 /* setup the protocol strings */
375 for (plength=0,numprots=0;
376 prots[numprots].name && prots[numprots].prot<=cli->protocol;
378 plength += strlen(prots[numprots].name)+2;
380 set_message(cli->outbuf,0,plength,True);
382 p = smb_buf(cli->outbuf);
384 prots[numprots].name && prots[numprots].prot<=cli->protocol;
387 p += clistr_push(cli, p, prots[numprots].name, -1, STR_TERMINATE);
390 CVAL(cli->outbuf,smb_com) = SMBnegprot;
391 cli_setup_packet(cli);
393 CVAL(smb_buf(cli->outbuf),0) = 2;
396 if (!cli_receive_smb(cli))
399 show_msg(cli->inbuf);
401 if (CVAL(cli->inbuf,smb_rcls) != 0 ||
402 ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
406 cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
409 if (cli->protocol >= PROTOCOL_NT1) {
411 cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
412 cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
413 cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
414 cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
415 cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
416 cli->serverzone *= 60;
417 /* this time arrives in real GMT */
418 cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
419 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
420 cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
421 if (cli->capabilities & CAP_RAW_MODE) {
422 cli->readbraw_supported = True;
423 cli->writebraw_supported = True;
425 /* work out if they sent us a workgroup */
426 if (smb_buflen(cli->inbuf) > 8) {
427 clistr_pull(cli, cli->server_domain,
428 smb_buf(cli->inbuf)+8, sizeof(cli->server_domain),
429 smb_buflen(cli->inbuf)-8, STR_UNICODE|STR_NOALIGN);
431 } else if (cli->protocol >= PROTOCOL_LANMAN1) {
432 cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
433 cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
434 cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
435 cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
436 cli->serverzone *= 60;
437 /* this time is converted to GMT by make_unix_date */
438 cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
439 cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
440 cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
441 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
443 /* the old core protocol */
445 cli->serverzone = TimeDiff(time(NULL));
448 cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
450 /* a way to force ascii SMB */
451 if (getenv("CLI_FORCE_ASCII")) {
452 cli->capabilities &= ~CAP_UNICODE;
459 /****************************************************************************
460 send a session request. see rfc1002.txt 4.3 and 4.3.2
461 ****************************************************************************/
462 BOOL cli_session_request(struct cli_state *cli,
463 struct nmb_name *calling, struct nmb_name *called)
467 extern pstring user_socket_options;
469 /* send a session request (RFC 1002) */
471 memcpy(&(cli->calling), calling, sizeof(*calling));
472 memcpy(&(cli->called ), called , sizeof(*called ));
474 /* put in the destination name */
476 name_mangle(cli->called .name, p, cli->called .name_type);
481 name_mangle(cli->calling.name, p, cli->calling.name_type);
484 /* setup the packet length */
485 _smb_setlen(cli->outbuf,len);
486 CVAL(cli->outbuf,0) = 0x81;
490 #endif /* WITH_SSL */
493 DEBUG(5,("Sent session request\n"));
495 if (!cli_receive_smb(cli))
498 if (CVAL(cli->inbuf,0) == 0x84) {
499 /* C. Hoch 9/14/95 Start */
500 /* For information, here is the response structure.
501 * We do the byte-twiddling to for portability.
502 struct RetargetResponse{
510 int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
511 /* SESSION RETARGET */
512 putip((char *)&cli->dest_ip,cli->inbuf+4);
514 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
518 DEBUG(3,("Retargeted\n"));
520 set_socket_options(cli->fd,user_socket_options);
527 DEBUG(0,("Retarget recursion - failing\n"));
531 ret = cli_session_request(cli, calling, called);
535 } /* C. Hoch 9/14/95 End */
538 if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */
539 if (!sslutil_fd_is_ssl(cli->fd)){
540 if (sslutil_connect(cli->fd) == 0)
544 #endif /* WITH_SSL */
546 if (CVAL(cli->inbuf,0) != 0x82) {
547 /* This is the wrong place to put the error... JRA. */
548 cli->rap_error = CVAL(cli->inbuf,4);
554 /****************************************************************************
555 open the client sockets
556 ****************************************************************************/
557 BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
559 extern struct in_addr ipzero;
560 extern pstring user_socket_options;
562 fstrcpy(cli->desthost, host);
564 if (!ip || ip_equal(*ip, ipzero)) {
565 if (!resolve_name( cli->desthost, &cli->dest_ip, 0x20)) {
568 if (ip) *ip = cli->dest_ip;
573 if (cli->port == 0) cli->port = 139; /* Set to default */
575 if (getenv("LIBSMB_PROG")) {
576 cli->fd = sock_exec(getenv("LIBSMB_PROG"));
578 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
579 cli->port, cli->timeout);
584 set_socket_options(cli->fd,user_socket_options);
589 /****************************************************************************
590 re-establishes a connection
591 ****************************************************************************/
592 BOOL cli_reestablish_connection(struct cli_state *cli)
594 struct nmb_name calling;
595 struct nmb_name called;
599 BOOL do_tcon = False;
602 if (!cli->initialised || cli->fd == -1)
604 DEBUG(3,("cli_reestablish_connection: not connected\n"));
608 /* copy the parameters necessary to re-establish the connection */
612 fstrcpy(share, cli->share);
613 fstrcpy(dev , cli->dev);
617 memcpy(&called , &(cli->called ), sizeof(called ));
618 memcpy(&calling, &(cli->calling), sizeof(calling));
619 fstrcpy(dest_host, cli->full_dest_host_name);
621 DEBUG(5,("cli_reestablish_connection: %s connecting to %s (ip %s) - %s [%s]\n",
622 nmb_namestr(&calling), nmb_namestr(&called),
623 inet_ntoa(cli->dest_ip),
624 cli->user_name, cli->domain));
628 if (cli_establish_connection(cli,
629 dest_host, &cli->dest_ip,
631 share, dev, False, do_tcon)) {
632 if ((cli->fd != oldfd) && (oldfd != -1)) {
640 /****************************************************************************
641 establishes a connection right up to doing tconX, reading in a password.
642 ****************************************************************************/
643 BOOL cli_establish_connection(struct cli_state *cli,
644 char *dest_host, struct in_addr *dest_ip,
645 struct nmb_name *calling, struct nmb_name *called,
646 char *service, char *service_type,
647 BOOL do_shutdown, BOOL do_tcon)
649 DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n",
650 nmb_namestr(calling), nmb_namestr(called), inet_ntoa(*dest_ip),
651 cli->user_name, cli->domain));
653 /* establish connection */
655 if ((!cli->initialised))
662 if (!cli_connect(cli, dest_host, dest_ip))
664 DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
665 nmb_namestr(calling), inet_ntoa(*dest_ip)));
670 if (!cli_session_request(cli, calling, called))
672 DEBUG(1,("failed session request\n"));
678 if (!cli_negprot(cli))
680 DEBUG(1,("failed negprot\n"));
686 if (cli->pwd.cleartext || cli->pwd.null_pwd)
691 if (cli->pwd.null_pwd)
693 /* attempt null session */
699 /* attempt clear-text session */
700 pwd_get_cleartext(&(cli->pwd), passwd);
701 pass_len = strlen(passwd);
704 /* attempt clear-text session */
705 if (!cli_session_setup(cli, cli->user_name,
710 DEBUG(1,("failed session setup\n"));
719 if (!cli_send_tconX(cli, service, service_type,
720 (char*)passwd, strlen(passwd)))
722 DEBUG(1,("failed tcon_X\n"));
733 /* attempt encrypted session */
734 unsigned char nt_sess_pwd[24];
735 unsigned char lm_sess_pwd[24];
737 /* creates (storing a copy of) and then obtains a 24 byte password OWF */
738 pwd_make_lm_nt_owf(&(cli->pwd), cli->cryptkey);
739 pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
741 /* attempt encrypted session */
742 if (!cli_session_setup(cli, cli->user_name,
743 (char*)lm_sess_pwd, sizeof(lm_sess_pwd),
744 (char*)nt_sess_pwd, sizeof(nt_sess_pwd),
747 DEBUG(1,("failed session setup\n"));
753 DEBUG(1,("session setup ok\n"));
755 if (*cli->server_domain || *cli->server_os || *cli->server_type)
757 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
765 if (!cli_send_tconX(cli, service, service_type,
766 (char*)nt_sess_pwd, sizeof(nt_sess_pwd)))
768 DEBUG(1,("failed tcon_X\n"));
783 /****************************************************************************
784 Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
785 ****************************************************************************/
787 BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char *desthost,
788 struct in_addr *pdest_ip)
790 struct nmb_name calling, called;
792 make_nmb_name(&calling, srchost, 0x0);
795 * If the called name is an IP address
796 * then use *SMBSERVER immediately.
799 if(is_ipaddress(desthost))
800 make_nmb_name(&called, "*SMBSERVER", 0x20);
802 make_nmb_name(&called, desthost, 0x20);
804 if (!cli_session_request(cli, &calling, &called)) {
805 struct nmb_name smbservername;
807 make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
810 * If the name wasn't *SMBSERVER then
811 * try with *SMBSERVER if the first name fails.
814 if (nmb_name_equal(&called, &smbservername)) {
817 * The name used was *SMBSERVER, don't bother with another name.
820 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
821 with error %s.\n", desthost, cli_errstr(cli) ));
828 if (!cli_initialise(cli) ||
829 !cli_connect(cli, desthost, pdest_ip) ||
830 !cli_session_request(cli, &calling, &smbservername)) {
831 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
832 name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));