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);
144 set_message(cli->outbuf,13,0,True);
145 CVAL(cli->outbuf,smb_com) = SMBsesssetupX;
146 cli_setup_packet(cli);
148 CVAL(cli->outbuf,smb_vwv0) = 0xFF;
149 SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
150 SSVAL(cli->outbuf,smb_vwv3,2);
151 SSVAL(cli->outbuf,smb_vwv4,cli->pid);
152 SIVAL(cli->outbuf,smb_vwv5,cli->sesskey);
153 SSVAL(cli->outbuf,smb_vwv7,passlen);
154 SSVAL(cli->outbuf,smb_vwv8,ntpasslen);
155 SSVAL(cli->outbuf,smb_vwv11,CAP_NT_SMBS|(cli->use_level_II_oplocks ? CAP_LEVEL_II_OPLOCKS : 0));
156 p = smb_buf(cli->outbuf);
157 memcpy(p,pword,passlen);
158 p += SVAL(cli->outbuf,smb_vwv7);
159 memcpy(p,ntpword,ntpasslen);
160 p += SVAL(cli->outbuf,smb_vwv8);
161 p += clistr_push(cli, p, user, -1, CLISTR_CONVERT|CLISTR_TERMINATE|CLISTR_UPPER);
162 p += clistr_push(cli, p, workgroup, -1, CLISTR_CONVERT|CLISTR_TERMINATE|CLISTR_UPPER);
163 p += clistr_push(cli, p, "Unix", -1, CLISTR_CONVERT|CLISTR_TERMINATE);
164 p += clistr_push(cli, p, "Samba", -1, CLISTR_CONVERT|CLISTR_TERMINATE);
165 set_message(cli->outbuf,13,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
169 if (!cli_receive_smb(cli))
172 show_msg(cli->inbuf);
174 if (CVAL(cli->inbuf,smb_rcls) != 0) {
178 /* use the returned vuid from now on */
179 cli->vuid = SVAL(cli->inbuf,smb_uid);
181 if (cli->protocol >= PROTOCOL_NT1) {
183 * Save off some of the connected server
186 char *p = smb_buf(cli->inbuf);
187 p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
188 p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
189 p += clistr_pull(cli, cli->server_domain, p, sizeof(fstring), -1, CLISTR_TERMINATE|CLISTR_CONVERT);
192 fstrcpy(cli->user_name, user);
197 /****************************************************************************
199 *****************************************************************************/
201 BOOL cli_ulogoff(struct cli_state *cli)
203 memset(cli->outbuf,'\0',smb_size);
204 set_message(cli->outbuf,2,0,True);
205 CVAL(cli->outbuf,smb_com) = SMBulogoffX;
206 cli_setup_packet(cli);
207 SSVAL(cli->outbuf,smb_vwv0,0xFF);
208 SSVAL(cli->outbuf,smb_vwv2,0); /* no additional info */
211 if (!cli_receive_smb(cli))
214 return CVAL(cli->inbuf,smb_rcls) == 0;
217 /****************************************************************************
219 ****************************************************************************/
220 BOOL cli_send_tconX(struct cli_state *cli,
221 char *share, char *dev, char *pass, int passlen)
223 fstring fullshare, pword, dos_pword;
225 memset(cli->outbuf,'\0',smb_size);
226 memset(cli->inbuf,'\0',smb_size);
228 fstrcpy(cli->share, share);
230 /* in user level security don't send a password now */
231 if (cli->sec_mode & 1) {
236 if ((cli->sec_mode & 2) && *pass && passlen != 24) {
238 * Non-encrypted passwords - convert to DOS codepage before encryption.
241 fstrcpy(dos_pword,pass);
242 unix_to_dos(dos_pword,True);
243 SMBencrypt((uchar *)dos_pword,(uchar *)cli->cryptkey,(uchar *)pword);
245 if((cli->sec_mode & 3) == 0) {
247 * Non-encrypted passwords - convert to DOS codepage before using.
249 passlen = clistr_push(cli, pword, pass, -1, CLISTR_CONVERT|CLISTR_TERMINATE);
251 memcpy(pword, pass, passlen);
255 slprintf(fullshare, sizeof(fullshare)-1,
256 "\\\\%s\\%s", cli->desthost, share);
257 unix_to_dos(fullshare, True);
260 set_message(cli->outbuf,4, 0, True);
261 CVAL(cli->outbuf,smb_com) = SMBtconX;
262 cli_setup_packet(cli);
264 SSVAL(cli->outbuf,smb_vwv0,0xFF);
265 SSVAL(cli->outbuf,smb_vwv3,passlen);
267 p = smb_buf(cli->outbuf);
268 memcpy(p,pword,passlen);
270 p += clistr_push(cli, p, fullshare, -1, CLISTR_CONVERT | CLISTR_TERMINATE);
271 fstrcpy(p, dev); p += strlen(dev)+1;
273 set_message(cli->outbuf,4,PTR_DIFF(p,smb_buf(cli->outbuf)),False);
275 SCVAL(cli->inbuf,smb_rcls, 1);
278 if (!cli_receive_smb(cli))
281 if (CVAL(cli->inbuf,smb_rcls) != 0) {
285 fstrcpy(cli->dev, "A:");
287 if (cli->protocol >= PROTOCOL_NT1) {
288 clistr_pull(cli, cli->dev, smb_buf(cli->inbuf), sizeof(fstring), -1, CLISTR_TERMINATE | CLISTR_CONVERT);
291 if (strcasecmp(share,"IPC$")==0) {
292 fstrcpy(cli->dev, "IPC");
295 /* only grab the device if we have a recent protocol level */
296 if (cli->protocol >= PROTOCOL_NT1 &&
297 smb_buflen(cli->inbuf) == 3) {
298 /* almost certainly win95 - enable bug fixes */
302 cli->cnum = SVAL(cli->inbuf,smb_tid);
307 /****************************************************************************
308 send a tree disconnect
309 ****************************************************************************/
310 BOOL cli_tdis(struct cli_state *cli)
312 memset(cli->outbuf,'\0',smb_size);
313 set_message(cli->outbuf,0,0,True);
314 CVAL(cli->outbuf,smb_com) = SMBtdis;
315 SSVAL(cli->outbuf,smb_tid,cli->cnum);
316 cli_setup_packet(cli);
319 if (!cli_receive_smb(cli))
322 return CVAL(cli->inbuf,smb_rcls) == 0;
326 /****************************************************************************
327 send a negprot command
328 ****************************************************************************/
329 void cli_negprot_send(struct cli_state *cli)
335 memset(cli->outbuf,'\0',smb_size);
337 /* setup the protocol strings */
338 for (plength=0,numprots=0;
339 prots[numprots].name && prots[numprots].prot<=cli->protocol;
341 plength += strlen(prots[numprots].name)+2;
343 set_message(cli->outbuf,0,plength,True);
345 p = smb_buf(cli->outbuf);
347 prots[numprots].name && prots[numprots].prot<=cli->protocol;
350 pstrcpy(p,prots[numprots].name);
355 CVAL(cli->outbuf,smb_com) = SMBnegprot;
356 cli_setup_packet(cli);
358 CVAL(smb_buf(cli->outbuf),0) = 2;
364 /****************************************************************************
365 send a negprot command
366 ****************************************************************************/
367 BOOL cli_negprot(struct cli_state *cli)
373 memset(cli->outbuf,'\0',smb_size);
375 /* setup the protocol strings */
376 for (plength=0,numprots=0;
377 prots[numprots].name && prots[numprots].prot<=cli->protocol;
379 plength += strlen(prots[numprots].name)+2;
381 set_message(cli->outbuf,0,plength,True);
383 p = smb_buf(cli->outbuf);
385 prots[numprots].name && prots[numprots].prot<=cli->protocol;
388 pstrcpy(p,prots[numprots].name);
393 CVAL(cli->outbuf,smb_com) = SMBnegprot;
394 cli_setup_packet(cli);
396 CVAL(smb_buf(cli->outbuf),0) = 2;
399 if (!cli_receive_smb(cli))
402 show_msg(cli->inbuf);
404 if (CVAL(cli->inbuf,smb_rcls) != 0 ||
405 ((int)SVAL(cli->inbuf,smb_vwv0) >= numprots)) {
409 cli->protocol = prots[SVAL(cli->inbuf,smb_vwv0)].prot;
412 if (cli->protocol >= PROTOCOL_NT1) {
414 cli->sec_mode = CVAL(cli->inbuf,smb_vwv1);
415 cli->max_mux = SVAL(cli->inbuf, smb_vwv1+1);
416 cli->max_xmit = IVAL(cli->inbuf,smb_vwv3+1);
417 cli->sesskey = IVAL(cli->inbuf,smb_vwv7+1);
418 cli->serverzone = SVALS(cli->inbuf,smb_vwv15+1);
419 cli->serverzone *= 60;
420 /* this time arrives in real GMT */
421 cli->servertime = interpret_long_date(cli->inbuf+smb_vwv11+1);
422 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
423 cli->capabilities = IVAL(cli->inbuf,smb_vwv9+1);
424 if (cli->capabilities & CAP_RAW_MODE) {
425 cli->readbraw_supported = True;
426 cli->writebraw_supported = True;
428 } else if (cli->protocol >= PROTOCOL_LANMAN1) {
429 cli->sec_mode = SVAL(cli->inbuf,smb_vwv1);
430 cli->max_xmit = SVAL(cli->inbuf,smb_vwv2);
431 cli->sesskey = IVAL(cli->inbuf,smb_vwv6);
432 cli->serverzone = SVALS(cli->inbuf,smb_vwv10);
433 cli->serverzone *= 60;
434 /* this time is converted to GMT by make_unix_date */
435 cli->servertime = make_unix_date(cli->inbuf+smb_vwv8);
436 cli->readbraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x1) != 0);
437 cli->writebraw_supported = ((SVAL(cli->inbuf,smb_vwv5) & 0x2) != 0);
438 memcpy(cli->cryptkey,smb_buf(cli->inbuf),8);
440 /* the old core protocol */
442 cli->serverzone = TimeDiff(time(NULL));
445 cli->max_xmit = MIN(cli->max_xmit, CLI_BUFFER_SIZE);
447 /* this ensures cli_use_unicode is setup - delete this call later (tridge) */
448 cli_setup_packet(cli);
454 /****************************************************************************
455 send a session request. see rfc1002.txt 4.3 and 4.3.2
456 ****************************************************************************/
457 BOOL cli_session_request(struct cli_state *cli,
458 struct nmb_name *calling, struct nmb_name *called)
462 extern pstring user_socket_options;
464 /* send a session request (RFC 1002) */
466 memcpy(&(cli->calling), calling, sizeof(*calling));
467 memcpy(&(cli->called ), called , sizeof(*called ));
469 /* put in the destination name */
471 name_mangle(cli->called .name, p, cli->called .name_type);
476 name_mangle(cli->calling.name, p, cli->calling.name_type);
479 /* setup the packet length */
480 _smb_setlen(cli->outbuf,len);
481 CVAL(cli->outbuf,0) = 0x81;
485 #endif /* WITH_SSL */
488 DEBUG(5,("Sent session request\n"));
490 if (!cli_receive_smb(cli))
493 if (CVAL(cli->inbuf,0) == 0x84) {
494 /* C. Hoch 9/14/95 Start */
495 /* For information, here is the response structure.
496 * We do the byte-twiddling to for portability.
497 struct RetargetResponse{
505 int port = (CVAL(cli->inbuf,8)<<8)+CVAL(cli->inbuf,9);
506 /* SESSION RETARGET */
507 putip((char *)&cli->dest_ip,cli->inbuf+4);
509 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip, port, LONG_CONNECT_TIMEOUT);
513 DEBUG(3,("Retargeted\n"));
515 set_socket_options(cli->fd,user_socket_options);
522 DEBUG(0,("Retarget recursion - failing\n"));
526 ret = cli_session_request(cli, calling, called);
530 } /* C. Hoch 9/14/95 End */
533 if (CVAL(cli->inbuf,0) == 0x83 && CVAL(cli->inbuf,4) == 0x8e){ /* use ssl */
534 if (!sslutil_fd_is_ssl(cli->fd)){
535 if (sslutil_connect(cli->fd) == 0)
539 #endif /* WITH_SSL */
541 if (CVAL(cli->inbuf,0) != 0x82) {
542 /* This is the wrong place to put the error... JRA. */
543 cli->rap_error = CVAL(cli->inbuf,4);
550 /****************************************************************************
551 open the client sockets
552 ****************************************************************************/
553 BOOL cli_connect(struct cli_state *cli, const char *host, struct in_addr *ip)
555 extern struct in_addr ipzero;
556 extern pstring user_socket_options;
558 fstrcpy(cli->desthost, host);
560 if (!ip || ip_equal(*ip, ipzero)) {
561 if (!resolve_name( cli->desthost, &cli->dest_ip, 0x20)) {
564 if (ip) *ip = cli->dest_ip;
569 if (cli->port == 0) cli->port = 139; /* Set to default */
571 cli->fd = open_socket_out(SOCK_STREAM, &cli->dest_ip,
572 cli->port, cli->timeout);
576 set_socket_options(cli->fd,user_socket_options);
581 /****************************************************************************
582 re-establishes a connection
583 ****************************************************************************/
584 BOOL cli_reestablish_connection(struct cli_state *cli)
586 struct nmb_name calling;
587 struct nmb_name called;
591 BOOL do_tcon = False;
594 if (!cli->initialised || cli->fd == -1)
596 DEBUG(3,("cli_reestablish_connection: not connected\n"));
600 /* copy the parameters necessary to re-establish the connection */
604 fstrcpy(share, cli->share);
605 fstrcpy(dev , cli->dev);
609 memcpy(&called , &(cli->called ), sizeof(called ));
610 memcpy(&calling, &(cli->calling), sizeof(calling));
611 fstrcpy(dest_host, cli->full_dest_host_name);
613 DEBUG(5,("cli_reestablish_connection: %s connecting to %s (ip %s) - %s [%s]\n",
614 nmb_namestr(&calling), nmb_namestr(&called),
615 inet_ntoa(cli->dest_ip),
616 cli->user_name, cli->domain));
620 if (cli_establish_connection(cli,
621 dest_host, &cli->dest_ip,
623 share, dev, False, do_tcon)) {
624 if ((cli->fd != oldfd) && (oldfd != -1)) {
632 /****************************************************************************
633 establishes a connection right up to doing tconX, reading in a password.
634 ****************************************************************************/
635 BOOL cli_establish_connection(struct cli_state *cli,
636 char *dest_host, struct in_addr *dest_ip,
637 struct nmb_name *calling, struct nmb_name *called,
638 char *service, char *service_type,
639 BOOL do_shutdown, BOOL do_tcon)
641 DEBUG(5,("cli_establish_connection: %s connecting to %s (%s) - %s [%s]\n",
642 nmb_namestr(calling), nmb_namestr(called), inet_ntoa(*dest_ip),
643 cli->user_name, cli->domain));
645 /* establish connection */
647 if ((!cli->initialised))
654 if (!cli_connect(cli, dest_host, dest_ip))
656 DEBUG(1,("cli_establish_connection: failed to connect to %s (%s)\n",
657 nmb_namestr(calling), inet_ntoa(*dest_ip)));
662 if (!cli_session_request(cli, calling, called))
664 DEBUG(1,("failed session request\n"));
670 if (!cli_negprot(cli))
672 DEBUG(1,("failed negprot\n"));
678 if (cli->pwd.cleartext || cli->pwd.null_pwd)
683 if (cli->pwd.null_pwd)
685 /* attempt null session */
691 /* attempt clear-text session */
692 pwd_get_cleartext(&(cli->pwd), passwd);
693 pass_len = strlen(passwd);
696 /* attempt clear-text session */
697 if (!cli_session_setup(cli, cli->user_name,
702 DEBUG(1,("failed session setup\n"));
711 if (!cli_send_tconX(cli, service, service_type,
712 (char*)passwd, strlen(passwd)))
714 DEBUG(1,("failed tcon_X\n"));
725 /* attempt encrypted session */
726 unsigned char nt_sess_pwd[24];
727 unsigned char lm_sess_pwd[24];
729 /* creates (storing a copy of) and then obtains a 24 byte password OWF */
730 pwd_make_lm_nt_owf(&(cli->pwd), cli->cryptkey);
731 pwd_get_lm_nt_owf(&(cli->pwd), lm_sess_pwd, nt_sess_pwd);
733 /* attempt encrypted session */
734 if (!cli_session_setup(cli, cli->user_name,
735 (char*)lm_sess_pwd, sizeof(lm_sess_pwd),
736 (char*)nt_sess_pwd, sizeof(nt_sess_pwd),
739 DEBUG(1,("failed session setup\n"));
745 DEBUG(1,("session setup ok\n"));
747 if (*cli->server_domain || *cli->server_os || *cli->server_type)
749 DEBUG(1,("Domain=[%s] OS=[%s] Server=[%s]\n",
757 if (!cli_send_tconX(cli, service, service_type,
758 (char*)nt_sess_pwd, sizeof(nt_sess_pwd)))
760 DEBUG(1,("failed tcon_X\n"));
775 /****************************************************************************
776 Attempt a NetBIOS session request, falling back to *SMBSERVER if needed.
777 ****************************************************************************/
779 BOOL attempt_netbios_session_request(struct cli_state *cli, char *srchost, char *desthost,
780 struct in_addr *pdest_ip)
782 struct nmb_name calling, called;
784 make_nmb_name(&calling, srchost, 0x0);
787 * If the called name is an IP address
788 * then use *SMBSERVER immediately.
791 if(is_ipaddress(desthost))
792 make_nmb_name(&called, "*SMBSERVER", 0x20);
794 make_nmb_name(&called, desthost, 0x20);
796 if (!cli_session_request(cli, &calling, &called)) {
797 struct nmb_name smbservername;
799 make_nmb_name(&smbservername , "*SMBSERVER", 0x20);
802 * If the name wasn't *SMBSERVER then
803 * try with *SMBSERVER if the first name fails.
806 if (nmb_name_equal(&called, &smbservername)) {
809 * The name used was *SMBSERVER, don't bother with another name.
812 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for name *SMBSERVER \
813 with error %s.\n", desthost, cli_errstr(cli) ));
820 if (!cli_initialise(cli) ||
821 !cli_connect(cli, desthost, pdest_ip) ||
822 !cli_session_request(cli, &calling, &smbservername)) {
823 DEBUG(0,("attempt_netbios_session_request: %s rejected the session for \
824 name *SMBSERVER with error %s\n", desthost, cli_errstr(cli) ));