Do not build the session request if it si not used anyway
[kai/samba.git] / source3 / libsmb / cliconnect.c
index d370808bba86604269add32327a25c308766be87..8b7ac7d718496812c878fc6ddc4226dc14f936c6 100644 (file)
@@ -98,7 +98,7 @@ static NTSTATUS cli_session_setup_lanman2(struct cli_state *cli,
 
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,10, 0, True);
+       cli_set_message(cli->outbuf,10, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
        
@@ -168,7 +168,7 @@ static NTSTATUS cli_session_setup_guest(struct cli_state *cli)
        uint32 capabilities = cli_session_setup_capabilities(cli);
 
        memset(cli->outbuf, '\0', smb_size);
-       set_message(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -228,7 +228,7 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
        fstr_sprintf( lanman, "Samba %s", SAMBA_VERSION_STRING);
 
        memset(cli->outbuf, '\0', smb_size);
-       set_message(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -247,9 +247,16 @@ static NTSTATUS cli_session_setup_plaintext(struct cli_state *cli,
                p += clistr_push(cli, p, pass, -1, STR_TERMINATE); /* password */
                SSVAL(cli->outbuf,smb_vwv7,PTR_DIFF(p, smb_buf(cli->outbuf)));
        }
-       else { 
+       else {
+               /* For ucs2 passwords clistr_push calls ucs2_align, which causes
+                * the space taken by the unicode password to be one byte too
+                * long (as we're on an odd byte boundary here). Reduce the
+                * count by 1 to cope with this. Fixes smbclient against NetApp
+                * servers which can't cope. Fix from
+                * bryan.kolodziej@allenlund.com in bug #3840.
+                */
                p += clistr_push(cli, p, pass, -1, STR_UNICODE|STR_TERMINATE); /* unicode password */
-               SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf)));  
+               SSVAL(cli->outbuf,smb_vwv8,PTR_DIFF(p, smb_buf(cli->outbuf))-1);        
        }
        
        p += clistr_push(cli, p, user, -1, STR_TERMINATE); /* username */
@@ -377,7 +384,7 @@ static NTSTATUS cli_session_setup_nt1(struct cli_state *cli, const char *user,
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
 
-       set_message(cli->outbuf,13,0,True);
+       cli_set_message(cli->outbuf,13,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
        cli_setup_packet(cli);
                        
@@ -457,11 +464,11 @@ static bool cli_session_setup_blob_send(struct cli_state *cli, DATA_BLOB blob)
        /* send a session setup command */
        memset(cli->outbuf,'\0',smb_size);
 
-       set_message(cli->outbuf,12,0,True);
+       cli_set_message(cli->outbuf,12,0,True);
        SCVAL(cli->outbuf,smb_com,SMBsesssetupX);
 
        cli_setup_packet(cli);
-                       
+
        SCVAL(cli->outbuf,smb_vwv0,0xFF);
        SSVAL(cli->outbuf,smb_vwv2,CLI_BUFFER_SIZE);
        SSVAL(cli->outbuf,smb_vwv3,2);
@@ -497,10 +504,10 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
                                                  NT_STATUS_MORE_PROCESSING_REQUIRED)) {
                return blob2;
        }
-       
+
        /* use the returned vuid from now on */
        cli->vuid = SVAL(cli->inbuf,smb_uid);
-       
+
        p = smb_buf(cli->inbuf);
 
        blob2 = data_blob(p, SVAL(cli->inbuf, smb_vwv3));
@@ -509,7 +516,7 @@ static DATA_BLOB cli_session_setup_blob_receive(struct cli_state *cli)
        p += clistr_pull(cli, cli->server_os, p, sizeof(fstring), -1, STR_TERMINATE);
 
        /* w2k with kerberos doesn't properly null terminate this field */
-       len = smb_buflen(cli->inbuf) - PTR_DIFF(p, smb_buf(cli->inbuf));
+       len = smb_bufrem(cli->inbuf, p);
        p += clistr_pull(cli, cli->server_type, p, sizeof(fstring), len, 0);
 
        return blob2;
@@ -581,8 +588,8 @@ static bool cli_session_setup_blob(struct cli_state *cli, DATA_BLOB blob, DATA_B
                if (cli_is_error(cli) &&
                                !NT_STATUS_EQUAL( cli_get_nt_error(cli), 
                                        NT_STATUS_MORE_PROCESSING_REQUIRED)) {
-                       DEBUG(0, ("cli_session_setup_blob: recieve failed (%s)\n",
-                               nt_errstr(cli_get_nt_error(cli)) ));
+                       DEBUG(0, ("cli_session_setup_blob: receive failed "
+                                 "(%s)\n", nt_errstr(cli_get_nt_error(cli))));
                        cli->vuid = 0;
                        return False;
                }
@@ -627,7 +634,7 @@ static ADS_STATUS cli_session_setup_kerberos(struct cli_state *cli, const char *
        if (!cli_session_setup_blob(cli, negTokenTarg, session_key_krb5)) {
                data_blob_free(&negTokenTarg);
                data_blob_free(&session_key_krb5);
-               ADS_ERROR_NT(cli_nt_error(cli));
+               return ADS_ERROR_NT(cli_nt_error(cli));
        }
 
        cli_set_session_key(cli, session_key_krb5);
@@ -689,14 +696,14 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                                /* wrap it in SPNEGO */
                                msg1 = spnego_gen_auth(blob_out);
                        }
-               
+
                        /* now send that blob on its way */
                        if (!cli_session_setup_blob_send(cli, msg1)) {
                                DEBUG(3, ("Failed to send NTLMSSP/SPNEGO blob to server!\n"));
                                nt_status = NT_STATUS_UNSUCCESSFUL;
                        } else {
                                blob = cli_session_setup_blob_receive(cli);
-                               
+
                                nt_status = cli_nt_error(cli);
                                if (cli_is_error(cli) && NT_STATUS_IS_OK(nt_status)) {
                                        if (cli->smb_rw_error == SMB_READ_BAD_SIG) {
@@ -708,7 +715,7 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                        }
                        data_blob_free(&msg1);
                }
-               
+
                if (!blob.length) {
                        if (NT_STATUS_IS_OK(nt_status)) {
                                nt_status = NT_STATUS_UNSUCCESSFUL;
@@ -754,12 +761,12 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
                data_blob_free(&key);
 
                if (res) {
-                       
+
                        /* 'resign' the last message, so we get the right sequence numbers
                           for checking the first reply from the server */
-                       cli_calculate_sign_mac(cli);
-                       
-                       if (!cli_check_sign_mac(cli)) {
+                       cli_calculate_sign_mac(cli, cli->outbuf);
+
+                       if (!cli_check_sign_mac(cli, cli->inbuf)) {
                                nt_status = NT_STATUS_ACCESS_DENIED;
                        }
                }
@@ -778,16 +785,21 @@ static NTSTATUS cli_session_setup_ntlmssp(struct cli_state *cli, const char *use
 
 /****************************************************************************
  Do a spnego encrypted session setup.
+
+ user_domain: The shortname of the domain the user/machine is a member of.
+ dest_realm: The realm we're connecting to, if NULL we use our default realm.
 ****************************************************************************/
 
 ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user, 
-                             const char *pass, const char *domain)
+                             const char *pass, const char *user_domain,
+                             const char * dest_realm)
 {
-       char *principal;
+       char *principal = NULL;
        char *OIDs[ASN1_MAX_OIDS];
        int i;
-       bool got_kerberos_mechanism = False;
        DATA_BLOB blob;
+       const char *p = NULL;
+       char *account = NULL;
 
        DEBUG(3,("Doing spnego session setup (blob length=%lu)\n", (unsigned long)cli->secblob.length));
 
@@ -804,8 +816,10 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
        /* there is 16 bytes of GUID before the real spnego packet starts */
        blob = data_blob(cli->secblob.data+16, cli->secblob.length-16);
 
-       /* the server sent us the first part of the SPNEGO exchange in the negprot 
-          reply */
+       /* The server sent us the first part of the SPNEGO exchange in the
+        * negprot reply. It is WRONG to depend on the principal sent in the
+        * negprot reply, but right now we do it. If we don't receive one,
+        * we try to best guess, then fall back to NTLM.  */
        if (!spnego_parse_negTokenInit(blob, OIDs, &principal)) {
                data_blob_free(&blob);
                return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
@@ -817,55 +831,42 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
                DEBUG(3,("got OID=%s\n", OIDs[i]));
                if (strcmp(OIDs[i], OID_KERBEROS5_OLD) == 0 ||
                    strcmp(OIDs[i], OID_KERBEROS5) == 0) {
-                       got_kerberos_mechanism = True;
+                       cli->got_kerberos_mechanism = True;
                }
-               free(OIDs[i]);
+               talloc_free(OIDs[i]);
        }
 
        DEBUG(3,("got principal=%s\n", principal ? principal : "<null>"));
 
-       if (got_kerberos_mechanism && (principal == NULL)) {
-               /*
-                * It is WRONG to depend on the principal sent in the negprot
-                * reply, but right now we do it. So for safety (don't
-                * segfault later) disable Kerberos when no principal was
-                * sent. -- VL
-                */
-               DEBUG(1, ("Kerberos mech was offered, but no principal was "
-                         "sent, disabling Kerberos\n"));
-               cli->use_kerberos = False;
-       }
-
        fstrcpy(cli->user_name, user);
 
 #ifdef HAVE_KRB5
        /* If password is set we reauthenticate to kerberos server
         * and do not store results */
 
-       if (got_kerberos_mechanism && cli->use_kerberos) {
+       if (cli->got_kerberos_mechanism && cli->use_kerberos) {
                ADS_STATUS rc;
 
                if (pass && *pass) {
                        int ret;
-                       
+
                        use_in_memory_ccache();
                        ret = kerberos_kinit_password(user, pass, 0 /* no time correction for now */, NULL);
-                       
+
                        if (ret){
-                               SAFE_FREE(principal);
+                               TALLOC_FREE(principal);
                                DEBUG(0, ("Kinit failed: %s\n", error_message(ret)));
                                if (cli->fallback_after_kerberos)
                                        goto ntlmssp;
                                return ADS_ERROR_KRB5(ret);
                        }
                }
-               
+
                /* If we get a bad principal, try to guess it if
                   we have a valid host NetBIOS name.
                 */
-               if (strequal(principal,
-                               "not_defined_in_RFC4178@please_ignore")) {
-                       SAFE_FREE(principal);
+               if (strequal(principal, ADS_IGNORE_PRINCIPAL)) {
+                       TALLOC_FREE(principal);
                }
 
                if (principal == NULL &&
@@ -873,13 +874,33 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
                        !strequal(star_smbserver_name,
                                cli->desthost)) {
                        char *realm = NULL;
+                       char *machine = NULL;
+                       char *host = NULL;
                        DEBUG(3,("cli_session_setup_spnego: got a "
                                "bad server principal, trying to guess ...\n"));
 
-                       realm = kerberos_get_default_realm_from_ccache();
+                       host = strchr_m(cli->desthost, '.');
+                       if (host) {
+                               machine = SMB_STRNDUP(cli->desthost,
+                                       host - cli->desthost);
+                       } else {
+                               machine = SMB_STRDUP(cli->desthost);
+                       }
+                       if (machine == NULL) {
+                               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+                       }
+
+                       if (dest_realm) {
+                               realm = SMB_STRDUP(dest_realm);
+                               strupper_m(realm);
+                       } else {
+                               realm = kerberos_get_default_realm_from_ccache();
+                       }
                        if (realm && *realm) {
-                               if (asprintf(&principal, "%s$@%s",
-                                               cli->desthost, realm) < 0) {
+                               principal = talloc_asprintf(NULL, "%s$@%s",
+                                                       machine, realm);
+                               if (!principal) {
+                                       SAFE_FREE(machine);
                                        SAFE_FREE(realm);
                                        return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
                                }
@@ -887,24 +908,38 @@ ADS_STATUS cli_session_setup_spnego(struct cli_state *cli, const char *user,
                                        "server principal=%s\n",
                                        principal ? principal : "<null>"));
                        }
+                       SAFE_FREE(machine);
                        SAFE_FREE(realm);
                }
 
                if (principal) {
-                       rc = cli_session_setup_kerberos(cli, principal, domain);
+                       rc = cli_session_setup_kerberos(cli, principal,
+                               dest_realm);
                        if (ADS_ERR_OK(rc) || !cli->fallback_after_kerberos) {
-                               SAFE_FREE(principal);
+                               TALLOC_FREE(principal);
                                return rc;
                        }
                }
        }
 #endif
 
-       SAFE_FREE(principal);
+       TALLOC_FREE(principal);
 
 ntlmssp:
 
-       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, user, pass, domain));
+       account = talloc_strdup(talloc_tos(), user);
+       if (!account) {
+               return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+       }
+
+       /* when falling back to ntlmssp while authenticating with a machine
+        * account strip off the realm - gd */
+
+       if ((p = strchr_m(user, '@')) != NULL) {
+               account[PTR_DIFF(p,user)] = '\0';
+       }
+
+       return ADS_ERROR_NT(cli_session_setup_ntlmssp(cli, account, pass, user_domain));
 }
 
 /****************************************************************************
@@ -996,7 +1031,8 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
        /* if the server supports extended security then use SPNEGO */
 
        if (cli->capabilities & CAP_EXTENDED_SECURITY) {
-               ADS_STATUS status = cli_session_setup_spnego(cli, user, pass, workgroup);
+               ADS_STATUS status = cli_session_setup_spnego(cli, user, pass,
+                                                            workgroup, NULL);
                if (!ADS_ERR_OK(status)) {
                        DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status)));
                        return ads_ntstatus(status);
@@ -1019,7 +1055,6 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
        }
 
        return NT_STATUS_OK;
-
 }
 
 /****************************************************************************
@@ -1029,7 +1064,7 @@ NTSTATUS cli_session_setup(struct cli_state *cli,
 bool cli_ulogoff(struct cli_state *cli)
 {
        memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,2,0,True);
+       cli_set_message(cli->outbuf,2,0,True);
        SCVAL(cli->outbuf,smb_com,SMBulogoffX);
        cli_setup_packet(cli);
        SSVAL(cli->outbuf,smb_vwv0,0xFF);
@@ -1073,8 +1108,9 @@ bool cli_send_tconX(struct cli_state *cli,
        if ((cli->sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) &&
            *pass && passlen != 24) {
                if (!lp_client_lanman_auth()) {
-                       DEBUG(1, ("Server requested LANMAN password (share-level security) but 'client use lanman auth'"
-                                 " is disabled\n"));
+                       DEBUG(1, ("Server requested LANMAN password "
+                                 "(share-level security) but "
+                                 "'client lanman auth' is disabled\n"));
                        return False;
                }
 
@@ -1096,7 +1132,7 @@ bool cli_send_tconX(struct cli_state *cli,
                         * Non-encrypted passwords - convert to DOS codepage before using.
                         */
                        passlen = clistr_push(cli, pword, pass, sizeof(pword), STR_TERMINATE);
-                       
+
                } else {
                        if (passlen) {
                                memcpy(pword, pass, passlen);
@@ -1107,7 +1143,7 @@ bool cli_send_tconX(struct cli_state *cli,
        slprintf(fullshare, sizeof(fullshare)-1,
                 "\\\\%s\\%s", cli->desthost, share);
 
-       set_message(cli->outbuf,4, 0, True);
+       cli_set_message(cli->outbuf,4, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBtconX);
        cli_setup_packet(cli);
 
@@ -1139,7 +1175,7 @@ bool cli_send_tconX(struct cli_state *cli,
                /* almost certainly win95 - enable bug fixes */
                cli->win95 = True;
        }
-       
+
        /* Make sure that we have the optional support 16-bit field.  WCT > 2 */
        /* Avoids issues when connecting to Win9x boxes sharing files */
 
@@ -1158,15 +1194,15 @@ bool cli_send_tconX(struct cli_state *cli,
 bool cli_tdis(struct cli_state *cli)
 {
        memset(cli->outbuf,'\0',smb_size);
-       set_message(cli->outbuf,0,0,True);
+       cli_set_message(cli->outbuf,0,0,True);
        SCVAL(cli->outbuf,smb_com,SMBtdis);
        SSVAL(cli->outbuf,smb_tid,cli->cnum);
        cli_setup_packet(cli);
-       
+
        cli_send_smb(cli);
        if (!cli_receive_smb(cli))
                return False;
-       
+
        if (cli_is_error(cli)) {
                return False;
        }
@@ -1190,7 +1226,7 @@ void cli_negprot_send(struct cli_state *cli)
        memset(cli->outbuf,'\0',smb_size);
 
        /* setup the protocol strings */
-       set_message(cli->outbuf,0,0,True);
+       cli_set_message(cli->outbuf,0,0,True);
 
        p = smb_buf(cli->outbuf);
        for (numprots=0;
@@ -1229,8 +1265,8 @@ bool cli_negprot(struct cli_state *cli)
             prots[numprots].name && prots[numprots].prot<=cli->protocol;
             numprots++)
                plength += strlen(prots[numprots].name)+2;
-    
-       set_message(cli->outbuf,0,plength,True);
+
+       cli_set_message(cli->outbuf,0,plength,True);
 
        p = smb_buf(cli->outbuf);
        for (numprots=0;
@@ -1317,9 +1353,9 @@ bool cli_negprot(struct cli_state *cli)
                if (cli->capabilities & (CAP_LARGE_READX|CAP_LARGE_WRITEX)) {
                        SAFE_FREE(cli->outbuf);
                        SAFE_FREE(cli->inbuf);
-                       cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN);
-                       cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN);
-                       cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
+                       cli->outbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN);
+                       cli->inbuf = (char *)SMB_MALLOC(CLI_SAMBA_MAX_LARGE_READX_SIZE+LARGE_WRITEX_HDR_SIZE+SAFETY_MARGIN);
+                       cli->bufsize = CLI_SAMBA_MAX_LARGE_READX_SIZE + LARGE_WRITEX_HDR_SIZE;
                }
 
        } else if (cli->protocol >= PROTOCOL_LANMAN1) {
@@ -1361,9 +1397,13 @@ bool cli_session_request(struct cli_state *cli,
        char *p;
        int len = 4;
 
+       /* 445 doesn't have session request */
+       if (cli->port == 445)
+               return True;
+
        memcpy(&(cli->calling), calling, sizeof(*calling));
        memcpy(&(cli->called ), called , sizeof(*called ));
-  
+
        /* put in the destination name */
        p = cli->outbuf+len;
        name_mangle(cli->called .name, p, cli->called .name_type);
@@ -1374,10 +1414,6 @@ bool cli_session_request(struct cli_state *cli,
        name_mangle(cli->calling.name, p, cli->calling.name_type);
        len += name_len(p);
 
-       /* 445 doesn't have session request */
-       if (cli->port == 445)
-               return True;
-
        /* send a session request (RFC 1002) */
        /* setup the packet length
          * Remove four bytes from the length count, since the length
@@ -1478,7 +1514,7 @@ NTSTATUS cli_connect(struct cli_state *cli,
                *p = 0;
        }
 
-       if (!dest_ss || is_zero_addr(dest_ss)) {
+       if (!dest_ss || is_zero_addr((struct sockaddr *)dest_ss)) {
                NTSTATUS status =resolve_name_list(frame,
                                        cli->desthost,
                                        name_type,
@@ -1569,11 +1605,11 @@ NTSTATUS cli_start_connection(struct cli_state **output_cli,
 
        if (!my_name) 
                my_name = global_myname();
-       
+
        if (!(cli = cli_initialise())) {
                return NT_STATUS_NO_MEMORY;
        }
-       
+
        make_nmb_name(&calling, my_name, 0x0);
        make_nmb_name(&called , dest_host, 0x20);
 
@@ -1629,6 +1665,11 @@ again:
        else if (flags & CLI_FULL_CONNECTION_USE_KERBEROS)
                cli->use_kerberos = True;
 
+       if ((flags & CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS) &&
+            cli->use_kerberos) {
+               cli->fallback_after_kerberos = true;
+       }
+
        if (!cli_negprot(cli)) {
                DEBUG(1,("failed negprot\n"));
                nt_status = cli_nt_error(cli);
@@ -1807,7 +1848,7 @@ NTSTATUS cli_raw_tcon(struct cli_state *cli,
        memset(cli->outbuf,'\0',smb_size);
        memset(cli->inbuf,'\0',smb_size);
 
-       set_message(cli->outbuf, 0, 0, True);
+       cli_set_message(cli->outbuf, 0, 0, True);
        SCVAL(cli->outbuf,smb_com,SMBtcon);
        cli_setup_packet(cli);
 
@@ -1841,12 +1882,18 @@ struct cli_state *get_ipc_connect(char *server,
 {
         struct cli_state *cli;
        NTSTATUS nt_status;
+       uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK;
+
+       if (user_info->use_kerberos) {
+               flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+       }
 
        nt_status = cli_full_connection(&cli, NULL, server, server_ss, 0, "IPC$", "IPC", 
                                        user_info->username ? user_info->username : "",
                                        lp_workgroup(),
                                        user_info->password ? user_info->password : "",
-                                       CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK, Undefined, NULL);
+                                       flags,
+                                       Undefined, NULL);
 
        if (NT_STATUS_IS_OK(nt_status)) {
                return cli;