s3: Change sockaddr util function names for consistency
[samba.git] / source3 / libsmb / clidfs.c
index 4cf37a250b7fe37f311038e6758c8b5128f7a70d..35922b13e9493d942160485a443e4e7a220c4067 100644 (file)
@@ -46,6 +46,7 @@ static struct cm_cred_struct {
        char *password;
        bool got_pass;
        bool use_kerberos;
+       bool fallback_after_kerberos;
        int signing_state;
 } cm_creds;
 
@@ -58,6 +59,52 @@ static struct sockaddr_storage dest_ss;
 
 static struct client_connection *connections;
 
+static bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
+                               struct cli_state *cli,
+                               const char *sharename,
+                               char **pp_newserver,
+                               char **pp_newshare,
+                               bool force_encrypt,
+                               const char *username,
+                               const char *password,
+                               const char *domain);
+
+/********************************************************************
+ Ensure a connection is encrypted.
+********************************************************************/
+
+NTSTATUS cli_cm_force_encryption(struct cli_state *c,
+                       const char *username,
+                       const char *password,
+                       const char *domain,
+                       const char *sharename)
+{
+       NTSTATUS status = cli_force_encryption(c,
+                                       username,
+                                       password,
+                                       domain);
+
+       if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) {
+               d_printf("Encryption required and "
+                       "server that doesn't support "
+                       "UNIX extensions - failing connect\n");
+       } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNKNOWN_REVISION)) {
+               d_printf("Encryption required and "
+                       "can't get UNIX CIFS extensions "
+                       "version from server.\n");
+       } else if (NT_STATUS_EQUAL(status,NT_STATUS_UNSUPPORTED_COMPRESSION)) {
+               d_printf("Encryption required and "
+                       "share %s doesn't support "
+                       "encryption.\n", sharename);
+       } else if (!NT_STATUS_IS_OK(status)) {
+               d_printf("Encryption required and "
+                       "setup failed with error %s.\n",
+                       nt_errstr(status));
+       }
+
+       return status;
+}
+       
 /********************************************************************
  Return a connection to a server.
 ********************************************************************/
@@ -65,7 +112,8 @@ static struct client_connection *connections;
 static struct cli_state *do_connect(TALLOC_CTX *ctx,
                                        const char *server,
                                        const char *share,
-                                       bool show_sessetup)
+                                       bool show_sessetup,
+                                       bool force_encrypt)
 {
        struct cli_state *c = NULL;
        struct nmb_name called, calling;
@@ -96,19 +144,22 @@ static struct cli_state *do_connect(TALLOC_CTX *ctx,
 
        server_n = server;
 
-       zero_addr(&ss);
+       zero_sockaddr(&ss);
 
        make_nmb_name(&calling, global_myname(), 0x0);
        make_nmb_name(&called , server, name_type);
 
  again:
-       zero_addr(&ss);
+       zero_sockaddr(&ss);
        if (have_ip)
                ss = dest_ss;
 
        /* have to open a new connection */
        if (!(c=cli_initialise()) || (cli_set_port(c, port) != port)) {
                d_printf("Connection to %s failed\n", server_n);
+               if (c) {
+                       cli_shutdown(c);
+               }
                return NULL;
        }
        status = cli_connect(c, server_n, &ss);
@@ -116,11 +167,13 @@ static struct cli_state *do_connect(TALLOC_CTX *ctx,
                d_printf("Connection to %s failed (Error %s)\n",
                                server_n,
                                nt_errstr(status));
+               cli_shutdown(c);
                return NULL;
        }
 
        c->protocol = max_protocol;
        c->use_kerberos = cm_creds.use_kerberos;
+       c->fallback_after_kerberos = cm_creds.fallback_after_kerberos;
        cli_setup_signing_state(c, cm_creds.signing_state);
 
        if (!cli_session_request(c, &calling, &called)) {
@@ -148,11 +201,16 @@ static struct cli_state *do_connect(TALLOC_CTX *ctx,
                return NULL;
        }
 
-       if (!cm_creds.got_pass) {
-               char *pass = getpass("Password: ");
+       if (!cm_creds.got_pass && !cm_creds.use_kerberos) {
+               char *label = NULL;
+               char *pass;
+               label = talloc_asprintf(ctx, "Enter %s's password: ",
+                       cm_creds.username);
+               pass = getpass(label);
                if (pass) {
                        cm_set_password(pass);
                }
+               TALLOC_FREE(label);
        }
 
        username = cm_creds.username ? cm_creds.username : "";
@@ -197,9 +255,14 @@ static struct cli_state *do_connect(TALLOC_CTX *ctx,
 
        if ((c->capabilities & CAP_DFS) &&
                        cli_check_msdfs_proxy(ctx, c, sharename,
-                               &newserver, &newshare)) {
+                               &newserver, &newshare,
+                               force_encrypt,
+                               username,
+                               password,
+                               lp_workgroup())) {
                cli_shutdown(c);
-               return do_connect(ctx, newserver, newshare, false);
+               return do_connect(ctx, newserver,
+                               newshare, false, force_encrypt);
        }
 
        /* must be a normal share */
@@ -211,6 +274,18 @@ static struct cli_state *do_connect(TALLOC_CTX *ctx,
                return NULL;
        }
 
+       if (force_encrypt) {
+               status = cli_cm_force_encryption(c,
+                                       username,
+                                       password,
+                                       lp_workgroup(),
+                                       sharename);
+               if (!NT_STATUS_IS_OK(status)) {
+                       cli_shutdown(c);
+                       return NULL;
+               }
+       }
+
        DEBUG(4,(" tconx ok\n"));
        return c;
 }
@@ -269,7 +344,8 @@ static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
                                        struct cli_state *referring_cli,
                                        const char *server,
                                        const char *share,
-                                       bool show_hdr)
+                                       bool show_hdr,
+                                       bool force_encrypt)
 {
        struct client_connection *node;
 
@@ -279,7 +355,7 @@ static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
                return NULL;
        }
 
-       node->cli = do_connect(ctx, server, share, show_hdr);
+       node->cli = do_connect(ctx, server, share, show_hdr, force_encrypt);
 
        if ( !node->cli ) {
                TALLOC_FREE( node );
@@ -293,9 +369,10 @@ static struct cli_state *cli_cm_connect(TALLOC_CTX *ctx,
        if (referring_cli && referring_cli->posix_capabilities) {
                uint16 major, minor;
                uint32 caplow, caphigh;
-               if (cli_unix_extensions_version(cli, &major,
+               if (cli_unix_extensions_version(node->cli, &major,
                                        &minor, &caplow, &caphigh)) {
-                       cli_set_unix_extensions_capabilities(cli, major, minor,
+                       cli_set_unix_extensions_capabilities(node->cli,
+                                       major, minor,
                                        caplow, caphigh);
                }
        }
@@ -330,7 +407,8 @@ struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
                                struct cli_state *referring_cli,
                                const char *server,
                                const char *share,
-                               bool show_hdr)
+                               bool show_hdr,
+                               bool force_encrypt)
 {
        struct cli_state *c;
 
@@ -338,7 +416,8 @@ struct cli_state *cli_cm_open(TALLOC_CTX *ctx,
 
        c = cli_cm_find(server, share);
        if (!c) {
-               c = cli_cm_connect(ctx, referring_cli, server, share, show_hdr);
+               c = cli_cm_connect(ctx, referring_cli,
+                               server, share, show_hdr, force_encrypt);
        }
 
        return c;
@@ -389,6 +468,9 @@ static void cm_set_password(const char *newpass)
        }
 }
 
+/****************************************************************************
+****************************************************************************/
+
 void cli_cm_set_credentials(void)
 {
        SAFE_FREE(cm_creds.username);
@@ -399,6 +481,7 @@ void cli_cm_set_credentials(void)
        }
 
        cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos();
+       cm_creds.fallback_after_kerberos = false;
        cm_creds.signing_state = get_cmdline_auth_info_signing_state();
 }
 
@@ -421,6 +504,51 @@ void cli_cm_set_dest_name_type(int type)
 /****************************************************************************
 ****************************************************************************/
 
+void cli_cm_set_signing_state(int state)
+{
+       cm_creds.signing_state = state;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_username(const char *username)
+{
+       SAFE_FREE(cm_creds.username);
+       cm_creds.username = SMB_STRDUP(username);
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_password(const char *newpass)
+{
+       SAFE_FREE(cm_creds.password);
+       cm_creds.password = SMB_STRDUP(newpass);
+       if (cm_creds.password) {
+               cm_creds.got_pass = true;
+       }
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_use_kerberos(void)
+{
+       cm_creds.use_kerberos = true;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+void cli_cm_set_fallback_after_kerberos(void)
+{
+       cm_creds.fallback_after_kerberos = true;
+}
+
+/****************************************************************************
+****************************************************************************/
+
 void cli_cm_set_dest_ss(struct sockaddr_storage *pss)
 {
        dest_ss = *pss;
@@ -569,7 +697,8 @@ static bool cli_dfs_check_error( struct cli_state *cli, NTSTATUS status )
  Get the dfs referral link.
 ********************************************************************/
 
-bool cli_dfs_get_referral(struct cli_state *cli,
+bool cli_dfs_get_referral(TALLOC_CTX *ctx,
+                       struct cli_state *cli,
                        const char *path,
                        CLIENT_DFS_REFERRAL**refs,
                        size_t *num_refs,
@@ -578,83 +707,116 @@ bool cli_dfs_get_referral(struct cli_state *cli,
        unsigned int data_len = 0;
        unsigned int param_len = 0;
        uint16 setup = TRANSACT2_GET_DFS_REFERRAL;
-       char param[1024+2];
+       char *param;
        char *rparam=NULL, *rdata=NULL;
        char *p;
+       char *endp;
        size_t pathlen = 2*(strlen(path)+1);
        uint16 num_referrals;
        CLIENT_DFS_REFERRAL *referrals = NULL;
+       bool ret = false;
+
+       *num_refs = 0;
+       *refs = NULL;
 
-       memset(param, 0, sizeof(param));
+       param = SMB_MALLOC_ARRAY(char, 2+pathlen+2);
+       if (!param) {
+               return false;
+       }
        SSVAL(param, 0, 0x03);  /* max referral level */
        p = &param[2];
 
-       p += clistr_push(cli, p, path, MIN(pathlen, sizeof(param)-2),
-                       STR_TERMINATE);
+       p += clistr_push(cli, p, path, pathlen, STR_TERMINATE);
        param_len = PTR_DIFF(p, param);
 
        if (!cli_send_trans(cli, SMBtrans2,
-               NULL,                        /* name */
-               -1, 0,                          /* fid, flags */
-               &setup, 1, 0,                   /* setup, length, max */
-               param, param_len, 2,            /* param, length, max */
-               NULL, 0, cli->max_xmit /* data, length, max */
-               )) {
-                       return false;
+                       NULL,                        /* name */
+                       -1, 0,                          /* fid, flags */
+                       &setup, 1, 0,                   /* setup, length, max */
+                       param, param_len, 2,            /* param, length, max */
+                       NULL, 0, cli->max_xmit /* data, length, max */
+                       )) {
+               SAFE_FREE(param);
+               return false;
        }
 
+       SAFE_FREE(param);
+
        if (!cli_receive_trans(cli, SMBtrans2,
                &rparam, &param_len,
                &rdata, &data_len)) {
                        return false;
        }
 
-       *consumed     = SVAL( rdata, 0 );
-       num_referrals = SVAL( rdata, 2 );
+       if (data_len < 4) {
+               goto out;
+       }
+
+       endp = rdata + data_len;
 
-       if ( num_referrals != 0 ) {
+       *consumed     = SVAL(rdata, 0);
+       num_referrals = SVAL(rdata, 2);
+
+       if (num_referrals != 0) {
                uint16 ref_version;
                uint16 ref_size;
                int i;
                uint16 node_offset;
 
-               referrals = SMB_XMALLOC_ARRAY( CLIENT_DFS_REFERRAL,
+               referrals = TALLOC_ARRAY(ctx, CLIENT_DFS_REFERRAL,
                                num_referrals);
 
+               if (!referrals) {
+                       goto out;
+               }
                /* start at the referrals array */
 
                p = rdata+8;
-               for ( i=0; i<num_referrals; i++ ) {
-                       ref_version = SVAL( p, 0 );
-                       ref_size    = SVAL( p, 2 );
-                       node_offset = SVAL( p, 16 );
+               for (i=0; i<num_referrals && p < endp; i++) {
+                       if (p + 18 > endp) {
+                               goto out;
+                       }
+                       ref_version = SVAL(p, 0);
+                       ref_size    = SVAL(p, 2);
+                       node_offset = SVAL(p, 16);
 
-                       if ( ref_version != 3 ) {
+                       if (ref_version != 3) {
                                p += ref_size;
                                continue;
                        }
 
-                       referrals[i].proximity = SVAL( p, 8 );
-                       referrals[i].ttl       = SVAL( p, 10 );
+                       referrals[i].proximity = SVAL(p, 8);
+                       referrals[i].ttl       = SVAL(p, 10);
 
-                       clistr_pull( cli, referrals[i].dfspath, p+node_offset,
-                               sizeof(referrals[i].dfspath), -1,
+                       if (p + node_offset > endp) {
+                               goto out;
+                       }
+                       clistr_pull_talloc(ctx, cli, &referrals[i].dfspath,
+                               p+node_offset, -1,
                                STR_TERMINATE|STR_UNICODE );
 
+                       if (!referrals[i].dfspath) {
+                               goto out;
+                       }
                        p += ref_size;
                }
+               if (i < num_referrals) {
+                       goto out;
+               }
        }
 
+       ret = true;
+
        *num_refs = num_referrals;
        *refs = referrals;
 
+  out:
+
        SAFE_FREE(rdata);
        SAFE_FREE(rparam);
-
-       return true;
+       return ret;
 }
 
-
 /********************************************************************
 ********************************************************************/
 
@@ -666,7 +828,7 @@ bool cli_resolve_path(TALLOC_CTX *ctx,
                        char **pp_targetpath)
 {
        CLIENT_DFS_REFERRAL *refs = NULL;
-       size_t num_refs;
+       size_t num_refs = 0;
        uint16 consumed;
        struct cli_state *cli_ipc = NULL;
        char *dfs_path = NULL;
@@ -741,19 +903,23 @@ bool cli_resolve_path(TALLOC_CTX *ctx,
        /* Check for the referral. */
 
        if (!(cli_ipc = cli_cm_open(ctx, rootcli,
-                                       rootcli->desthost, "IPC$", false))) {
+                                       rootcli->desthost,
+                                       "IPC$", false,
+                                       (rootcli->trans_enc_state != NULL)))) {
                return false;
        }
 
-       if (!cli_dfs_get_referral(cli_ipc, dfs_path, &refs,
+       if (!cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs,
                        &num_refs, &consumed) || !num_refs) {
                return false;
        }
 
        /* Just store the first referral for now. */
 
+       if (!refs[0].dfspath) {
+               return false;
+       }
        split_dfs_path(ctx, refs[0].dfspath, &server, &share, &extrapath );
-       SAFE_FREE(refs);
 
        if (!server || !share) {
                return false;
@@ -781,7 +947,10 @@ bool cli_resolve_path(TALLOC_CTX *ctx,
 
        /* Open the connection to the target server & share */
        if ((*targetcli = cli_cm_open(ctx, rootcli,
-                                       server, share, false)) == NULL) {
+                                       server,
+                                       share,
+                                       false,
+                                       (rootcli->trans_enc_state != NULL))) == NULL) {
                d_printf("Unable to follow dfs referral [\\%s\\%s]\n",
                        server, share );
                return false;
@@ -868,14 +1037,18 @@ bool cli_resolve_path(TALLOC_CTX *ctx,
 /********************************************************************
 ********************************************************************/
 
-bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
+static bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
                                struct cli_state *cli,
                                const char *sharename,
                                char **pp_newserver,
-                               char **pp_newshare )
+                               char **pp_newshare,
+                               bool force_encrypt,
+                               const char *username,
+                               const char *password,
+                               const char *domain)
 {
        CLIENT_DFS_REFERRAL *refs = NULL;
-       size_t num_refs;
+       size_t num_refs = 0;
        uint16 consumed;
        char *fullpath = NULL;
        bool res;
@@ -907,26 +1080,37 @@ bool cli_check_msdfs_proxy(TALLOC_CTX *ctx,
                return false;
        }
 
-       res = cli_dfs_get_referral(cli, fullpath, &refs, &num_refs, &consumed);
+       if (force_encrypt) {
+               NTSTATUS status = cli_cm_force_encryption(cli,
+                                       username,
+                                       password,
+                                       lp_workgroup(),
+                                       "IPC$");
+               if (!NT_STATUS_IS_OK(status)) {
+                       return false;
+               }
+       }
+
+       res = cli_dfs_get_referral(ctx, cli, fullpath, &refs, &num_refs, &consumed);
 
        if (!cli_tdis(cli)) {
-               SAFE_FREE(refs);
                return false;
        }
 
        cli->cnum = cnum;
 
        if (!res || !num_refs) {
-               SAFE_FREE(refs);
+               return false;
+       }
+
+       if (!refs[0].dfspath) {
                return false;
        }
 
        split_dfs_path(ctx, refs[0].dfspath, pp_newserver,
                        pp_newshare, &newextrapath );
 
-       SAFE_FREE(refs);
-
-       if (!pp_newserver || !pp_newshare) {
+       if ((*pp_newserver == NULL) || (*pp_newshare == NULL)) {
                return false;
        }