Пример #1
0
static bool cli_dfs_check_error(struct cli_state *cli, NTSTATUS expected,
				NTSTATUS status)
{
	/* only deal with DS when we negotiated NT_STATUS codes and UNICODE */

	if (!(smbXcli_conn_use_unicode(cli->conn))) {
		return false;
	}
	if (!(smb1cli_conn_capabilities(cli->conn) & CAP_STATUS32)) {
		return false;
	}
	if (NT_STATUS_EQUAL(status, expected)) {
		return true;
	}
	return false;
}
Пример #2
0
/****************************************************************************
  Calculate the recommended write buffer size
****************************************************************************/
static size_t cli_write_max_bufsize(struct cli_state *cli,
                                    uint16_t write_mode,
                                    uint8_t wct)
{
    uint32_t min_space;
    uint32_t data_offset;
    uint32_t useable_space = 0;

    data_offset = HDR_VWV;
    data_offset += wct * sizeof(uint16_t);
    data_offset += sizeof(uint16_t); /* byte count */
    data_offset += 1; /* pad */

    min_space = cli_state_available_size(cli, data_offset);

    if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP) {
        useable_space = 0xFFFFFF - data_offset;
    } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_WRITEX) {
        useable_space = 0x1FFFF - data_offset;
    } else {
        return min_space;
    }

    if (write_mode != 0) {
        return min_space;
    }

    if (smb1cli_conn_signing_is_active(cli->conn)) {
        return min_space;
    }

    if (smb1cli_conn_encryption_on(cli->conn)) {
        return min_space;
    }

    if (strequal(cli->dev, "LPT1:")) {
        return min_space;
    }

    return useable_space;
}
Пример #3
0
/****************************************************************************
  Calculate the recommended read buffer size
****************************************************************************/
static size_t cli_read_max_bufsize(struct cli_state *cli)
{
    uint8_t wct = 12;
    uint32_t min_space;
    uint32_t data_offset;
    uint32_t useable_space = 0;

    data_offset = HDR_VWV;
    data_offset += wct * sizeof(uint16_t);
    data_offset += sizeof(uint16_t); /* byte count */
    data_offset += 1; /* pad */

    min_space = cli_state_available_size(cli, data_offset);

    if (cli->server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP) {
        useable_space = 0xFFFFFF - data_offset;

        if (smb1cli_conn_signing_is_active(cli->conn)) {
            return min_space;
        }

        if (smb1cli_conn_encryption_on(cli->conn)) {
            return min_space;
        }

        return useable_space;
    } else if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_READX) {
        /*
         * Note: CAP_LARGE_READX also works with signing
         */
        useable_space = 0x1FFFF - data_offset;

        useable_space = MIN(useable_space, UINT16_MAX);

        return useable_space;
    }

    return min_space;
}
Пример #4
0
/*
 * Get info from an SMB server on a file. Use a qpathinfo call first
 * and if that fails, use getatr, as Win95 sometimes refuses qpathinfo
 */
bool
SMBC_getatr(SMBCCTX * context,
            SMBCSRV *srv,
            const char *path,
            uint16_t *mode,
            off_t *size,
            struct timespec *create_time_ts,
            struct timespec *access_time_ts,
            struct timespec *write_time_ts,
            struct timespec *change_time_ts,
            SMB_INO_T *ino)
{
	char *fixedpath = NULL;
	char *targetpath = NULL;
	struct cli_state *targetcli = NULL;
	time_t write_time;
	TALLOC_CTX *frame = talloc_stackframe();
	NTSTATUS status;

	if (!context || !context->internal->initialized) {
		errno = EINVAL;
		TALLOC_FREE(frame);
 		return False;
 	}

	/* path fixup for . and .. */
	if (strequal(path, ".") || strequal(path, "..")) {
		fixedpath = talloc_strdup(frame, "\\");
		if (!fixedpath) {
			errno = ENOMEM;
			TALLOC_FREE(frame);
			return False;
		}
	} else {
		fixedpath = talloc_strdup(frame, path);
		if (!fixedpath) {
			errno = ENOMEM;
			TALLOC_FREE(frame);
			return False;
		}
		trim_string(fixedpath, NULL, "\\..");
		trim_string(fixedpath, NULL, "\\.");
	}
	DEBUG(4,("SMBC_getatr: sending qpathinfo\n"));

	status = cli_resolve_path(frame, "", context->internal->auth_info,
				  srv->cli, fixedpath,
				  &targetcli, &targetpath);
	if (!NT_STATUS_IS_OK(status)) {
		d_printf("Couldn't resolve %s\n", path);
                errno = ENOENT;
		TALLOC_FREE(frame);
		return False;
	}

	if (!srv->no_pathinfo2 &&
            NT_STATUS_IS_OK(cli_qpathinfo2(targetcli, targetpath,
                           create_time_ts,
                           access_time_ts,
                           write_time_ts,
                           change_time_ts,
			   size, mode, ino))) {
		TALLOC_FREE(frame);
		return True;
        }

	srv->no_pathinfo2 = True;

	if (!srv->no_pathinfo3 &&
            NT_STATUS_IS_OK(cli_qpathinfo3(targetcli, targetpath,
                           create_time_ts,
                           access_time_ts,
                           write_time_ts,
                           change_time_ts,
			   size, mode, ino))) {
		TALLOC_FREE(frame);
		return True;
        }

	srv->no_pathinfo3 = True;

	/* if this is NT then don't bother with the getatr */
	if (smb1cli_conn_capabilities(targetcli->conn) & CAP_NT_SMBS) {
		goto all_failed;
        }

	if (NT_STATUS_IS_OK(cli_getatr(targetcli, targetpath, mode, size, &write_time))) {
                struct timespec w_time_ts;

                w_time_ts = convert_time_t_to_timespec(write_time);
                if (write_time_ts != NULL) {
			*write_time_ts = w_time_ts;
                }
                if (create_time_ts != NULL) {
                        *create_time_ts = w_time_ts;
                }
                if (access_time_ts != NULL) {
                        *access_time_ts = w_time_ts;
                }
                if (change_time_ts != NULL) {
                        *change_time_ts = w_time_ts;
                }
		if (ino) {
			*ino = 0;
		}
		TALLOC_FREE(frame);
		return True;
	}

all_failed:
	srv->no_pathinfo2 = False;
	srv->no_pathinfo3 = False;

        errno = EPERM;
	TALLOC_FREE(frame);
	return False;
}
Пример #5
0
static SMBCSRV *
SMBC_server_internal(TALLOC_CTX *ctx,
            SMBCCTX *context,
            bool connect_if_not_found,
            const char *server,
            const char *share,
            char **pp_workgroup,
            char **pp_username,
            char **pp_password,
	    bool *in_cache)
{
	SMBCSRV *srv=NULL;
	char *workgroup = NULL;
	struct cli_state *c = NULL;
	const char *server_n = server;
        int is_ipc = (share != NULL && strcmp(share, "IPC$") == 0);
	uint32 fs_attrs = 0;
        const char *username_used;
 	NTSTATUS status;
	char *newserver, *newshare;
	int flags = 0;

	ZERO_STRUCT(c);
	*in_cache = false;

	if (server[0] == 0) {
		errno = EPERM;
		return NULL;
	}

        /* Look for a cached connection */
        srv = SMBC_find_server(ctx, context, server, share,
                               pp_workgroup, pp_username, pp_password);

        /*
         * If we found a connection and we're only allowed one share per
         * server...
         */
        if (srv &&
            *share != '\0' &&
            smbc_getOptionOneSharePerServer(context)) {

                /*
                 * ... then if there's no current connection to the share,
                 * connect to it.  SMBC_find_server(), or rather the function
                 * pointed to by context->get_cached_srv_fn which
                 * was called by SMBC_find_server(), will have issued a tree
                 * disconnect if the requested share is not the same as the
                 * one that was already connected.
                 */

		/*
		 * Use srv->cli->desthost and srv->cli->share instead of
		 * server and share below to connect to the actual share,
		 * i.e., a normal share or a referred share from
		 * 'msdfs proxy' share.
		 */
                if (!cli_state_has_tcon(srv->cli)) {
                        /* Ensure we have accurate auth info */
			SMBC_call_auth_fn(ctx, context,
					  smbXcli_conn_remote_name(srv->cli->conn),
					  srv->cli->share,
                                          pp_workgroup,
                                          pp_username,
                                          pp_password);

			if (!*pp_workgroup || !*pp_username || !*pp_password) {
				errno = ENOMEM;
				cli_shutdown(srv->cli);
				srv->cli = NULL;
				smbc_getFunctionRemoveCachedServer(context)(context,
                                                                            srv);
				return NULL;
			}

			/*
			 * We don't need to renegotiate encryption
			 * here as the encryption context is not per
			 * tid.
			 */

			status = cli_tree_connect(srv->cli,
						  srv->cli->share,
						  "?????",
						  *pp_password,
						  strlen(*pp_password)+1);
			if (!NT_STATUS_IS_OK(status)) {
                                errno = map_errno_from_nt_status(status);
                                cli_shutdown(srv->cli);
				srv->cli = NULL;
                                smbc_getFunctionRemoveCachedServer(context)(context,
                                                                            srv);
                                srv = NULL;
                        }

                        /* Determine if this share supports case sensitivity */
                        if (is_ipc) {
                                DEBUG(4,
                                      ("IPC$ so ignore case sensitivity\n"));
                                status = NT_STATUS_OK;
                        } else {
                                status = cli_get_fs_attr_info(c, &fs_attrs);
                        }

                        if (!NT_STATUS_IS_OK(status)) {
                                DEBUG(4, ("Could not retrieve "
                                          "case sensitivity flag: %s.\n",
                                          nt_errstr(status)));

                                /*
                                 * We can't determine the case sensitivity of
                                 * the share. We have no choice but to use the
                                 * user-specified case sensitivity setting.
                                 */
                                if (smbc_getOptionCaseSensitive(context)) {
                                        cli_set_case_sensitive(c, True);
                                } else {
                                        cli_set_case_sensitive(c, False);
                                }
                        } else if (!is_ipc) {
                                DEBUG(4,
                                      ("Case sensitive: %s\n",
                                       (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                                        ? "True"
                                        : "False")));
                                cli_set_case_sensitive(
                                        c,
                                        (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                                         ? True
                                         : False));
                        }

                        /*
                         * Regenerate the dev value since it's based on both
                         * server and share
                         */
                        if (srv) {
				const char *remote_name =
					smbXcli_conn_remote_name(srv->cli->conn);

				srv->dev = (dev_t)(str_checksum(remote_name) ^
                                                   str_checksum(srv->cli->share));
                        }
                }
        }

        /* If we have a connection... */
        if (srv) {

                /* ... then we're done here.  Give 'em what they came for. */
		*in_cache = true;
                goto done;
        }

        /* If we're not asked to connect when a connection doesn't exist... */
        if (! connect_if_not_found) {
                /* ... then we're done here. */
                return NULL;
        }

	if (!*pp_workgroup || !*pp_username || !*pp_password) {
		errno = ENOMEM;
		return NULL;
	}

	DEBUG(4,("SMBC_server: server_n=[%s] server=[%s]\n", server_n, server));

	DEBUG(4,(" -> server_n=[%s] server=[%s]\n", server_n, server));

	status = NT_STATUS_UNSUCCESSFUL;

	if (smbc_getOptionUseKerberos(context)) {
		flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
	}

	if (smbc_getOptionFallbackAfterKerberos(context)) {
		flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
	}

	if (smbc_getOptionUseCCache(context)) {
		flags |= CLI_FULL_CONNECTION_USE_CCACHE;
	}

	if (smbc_getOptionUseNTHash(context)) {
		flags |= CLI_FULL_CONNECTION_USE_NT_HASH;
	}

        if (share == NULL || *share == '\0' || is_ipc) {
		/*
		 * Try 139 first for IPC$
		 */
		status = cli_connect_nb(server_n, NULL, NBT_SMB_PORT, 0x20,
					smbc_getNetbiosName(context),
					SMB_SIGNING_DEFAULT, flags, &c);
	}

	if (!NT_STATUS_IS_OK(status)) {
		/*
		 * No IPC$ or 139 did not work
		 */
		status = cli_connect_nb(server_n, NULL, 0, 0x20,
					smbc_getNetbiosName(context),
					SMB_SIGNING_DEFAULT, flags, &c);
	}

	if (!NT_STATUS_IS_OK(status)) {
		errno = map_errno_from_nt_status(status);
		return NULL;
	}

	cli_set_timeout(c, smbc_getTimeout(context));

	status = smbXcli_negprot(c->conn, c->timeout, PROTOCOL_CORE,
				 PROTOCOL_NT1);

	if (!NT_STATUS_IS_OK(status)) {
		cli_shutdown(c);
		errno = ETIMEDOUT;
		return NULL;
	}

        username_used = *pp_username;

	if (!NT_STATUS_IS_OK(cli_session_setup(c, username_used,
					       *pp_password,
                                               strlen(*pp_password),
					       *pp_password,
                                               strlen(*pp_password),
					       *pp_workgroup))) {

                /* Failed.  Try an anonymous login, if allowed by flags. */
                username_used = "";

                if (smbc_getOptionNoAutoAnonymousLogin(context) ||
                    !NT_STATUS_IS_OK(cli_session_setup(c, username_used,
                                                       *pp_password, 1,
                                                       *pp_password, 0,
                                                       *pp_workgroup))) {

                        cli_shutdown(c);
                        errno = EPERM;
                        return NULL;
                }
	}

	status = cli_init_creds(c, username_used,
				*pp_workgroup, *pp_password);
	if (!NT_STATUS_IS_OK(status)) {
		errno = map_errno_from_nt_status(status);
		cli_shutdown(c);
		return NULL;
	}

	DEBUG(4,(" session setup ok\n"));

	/* here's the fun part....to support 'msdfs proxy' shares
	   (on Samba or windows) we have to issues a TRANS_GET_DFS_REFERRAL
	   here before trying to connect to the original share.
	   cli_check_msdfs_proxy() will fail if it is a normal share. */

	if ((smb1cli_conn_capabilities(c->conn) & CAP_DFS) &&
			cli_check_msdfs_proxy(ctx, c, share,
				&newserver, &newshare,
				/* FIXME: cli_check_msdfs_proxy() does
				   not support smbc_smb_encrypt_level type */
				context->internal->smb_encryption_level ?
					true : false,
				*pp_username,
				*pp_password,
				*pp_workgroup)) {
		cli_shutdown(c);
		srv = SMBC_server_internal(ctx, context, connect_if_not_found,
				newserver, newshare, pp_workgroup,
				pp_username, pp_password, in_cache);
		TALLOC_FREE(newserver);
		TALLOC_FREE(newshare);
		return srv;
	}

	/* must be a normal share */

	status = cli_tree_connect(c, share, "?????", *pp_password,
				  strlen(*pp_password)+1);
	if (!NT_STATUS_IS_OK(status)) {
		errno = map_errno_from_nt_status(status);
		cli_shutdown(c);
		return NULL;
	}

	DEBUG(4,(" tconx ok\n"));

        /* Determine if this share supports case sensitivity */
	if (is_ipc) {
                DEBUG(4, ("IPC$ so ignore case sensitivity\n"));
                status = NT_STATUS_OK;
        } else {
                status = cli_get_fs_attr_info(c, &fs_attrs);
        }

        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(4, ("Could not retrieve case sensitivity flag: %s.\n",
                          nt_errstr(status)));

                /*
                 * We can't determine the case sensitivity of the share. We
                 * have no choice but to use the user-specified case
                 * sensitivity setting.
                 */
                if (smbc_getOptionCaseSensitive(context)) {
                        cli_set_case_sensitive(c, True);
                } else {
                        cli_set_case_sensitive(c, False);
                }
	} else if (!is_ipc) {
                DEBUG(4, ("Case sensitive: %s\n",
                          (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                           ? "True"
                           : "False")));
                cli_set_case_sensitive(c,
                                       (fs_attrs & FILE_CASE_SENSITIVE_SEARCH
                                        ? True
                                        : False));
        }

	if (context->internal->smb_encryption_level) {
		/* Attempt UNIX smb encryption. */
		if (!NT_STATUS_IS_OK(cli_force_encryption(c,
                                                          username_used,
                                                          *pp_password,
                                                          *pp_workgroup))) {

			/*
			 * context->smb_encryption_level == 1
			 * means don't fail if encryption can't be negotiated,
			 * == 2 means fail if encryption can't be negotiated.
			 */

			DEBUG(4,(" SMB encrypt failed\n"));

			if (context->internal->smb_encryption_level == 2) {
	                        cli_shutdown(c);
				errno = EPERM;
				return NULL;
			}
		}
		DEBUG(4,(" SMB encrypt ok\n"));
	}

	/*
	 * Ok, we have got a nice connection
	 * Let's allocate a server structure.
	 */

	srv = SMB_MALLOC_P(SMBCSRV);
	if (!srv) {
		cli_shutdown(c);
		errno = ENOMEM;
		return NULL;
	}

	ZERO_STRUCTP(srv);
	srv->cli = c;
	srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share));
        srv->no_pathinfo = False;
        srv->no_pathinfo2 = False;
        srv->no_nt_session = False;

done:
	if (!pp_workgroup || !*pp_workgroup || !**pp_workgroup) {
		workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context));
	} else {
		workgroup = *pp_workgroup;
	}
	if(!workgroup) {
		if (c != NULL) {
			cli_shutdown(c);
		}
		SAFE_FREE(srv);
		return NULL;
	}

	/* set the credentials to make DFS work */
	smbc_set_credentials_with_fallback(context,
					   workgroup,
				    	   *pp_username,
				   	   *pp_password);

	return srv;
}