diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /source3/libsmb | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/libsmb')
77 files changed, 56144 insertions, 0 deletions
diff --git a/source3/libsmb/ABI/smbclient-0.1.0.sigs b/source3/libsmb/ABI/smbclient-0.1.0.sigs new file mode 100644 index 0000000..3dea59c --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.1.0.sigs @@ -0,0 +1,172 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setUser: void (SMBCCTX *, char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.2.0.sigs b/source3/libsmb/ABI/smbclient-0.2.0.sigs new file mode 100644 index 0000000..aa85859 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.2.0.sigs @@ -0,0 +1,172 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.2.1.sigs b/source3/libsmb/ABI/smbclient-0.2.1.sigs new file mode 100644 index 0000000..1b7f6ce --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.2.1.sigs @@ -0,0 +1,174 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.2.2.sigs b/source3/libsmb/ABI/smbclient-0.2.2.sigs new file mode 100644 index 0000000..fdafdd0 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.2.2.sigs @@ -0,0 +1,176 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.2.3.sigs b/source3/libsmb/ABI/smbclient-0.2.3.sigs new file mode 100644 index 0000000..cda537c --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.2.3.sigs @@ -0,0 +1,179 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.3.0.sigs b/source3/libsmb/ABI/smbclient-0.3.0.sigs new file mode 100644 index 0000000..736f32a --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.3.0.sigs @@ -0,0 +1,179 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.3.1.sigs b/source3/libsmb/ABI/smbclient-0.3.1.sigs new file mode 100644 index 0000000..d41274d --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.3.1.sigs @@ -0,0 +1,180 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.3.2.sigs b/source3/libsmb/ABI/smbclient-0.3.2.sigs new file mode 100644 index 0000000..2f089d8 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.3.2.sigs @@ -0,0 +1,181 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setConfiguration: int (SMBCCTX *, const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.3.3.sigs b/source3/libsmb/ABI/smbclient-0.3.3.sigs new file mode 100644 index 0000000..833d0df --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.3.3.sigs @@ -0,0 +1,184 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: char *(SMBCCTX *) +smbc_getWorkgroup: char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_readdirplus: const struct libsmb_file_info *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setConfiguration: int (SMBCCTX *, const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.4.0.sigs b/source3/libsmb/ABI/smbclient-0.4.0.sigs new file mode 100644 index 0000000..170cb52 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.4.0.sigs @@ -0,0 +1,184 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: const char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: const char *(SMBCCTX *) +smbc_getWorkgroup: const char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_readdirplus: const struct libsmb_file_info *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setConfiguration: int (SMBCCTX *, const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, const char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, const char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.5.0.sigs b/source3/libsmb/ABI/smbclient-0.5.0.sigs new file mode 100644 index 0000000..b424597 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.5.0.sigs @@ -0,0 +1,185 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: const char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: const char *(SMBCCTX *) +smbc_getWorkgroup: const char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_readdirplus: const struct libsmb_file_info *(unsigned int) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setConfiguration: int (SMBCCTX *, const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, const char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, const char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.6.0.sigs b/source3/libsmb/ABI/smbclient-0.6.0.sigs new file mode 100644 index 0000000..ee758e2 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.6.0.sigs @@ -0,0 +1,188 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus2: smbc_readdirplus2_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: const char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: const char *(SMBCCTX *) +smbc_getWorkgroup: const char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_readdirplus: const struct libsmb_file_info *(unsigned int) +smbc_readdirplus2: const struct libsmb_file_info *(unsigned int, struct stat *) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setConfiguration: int (SMBCCTX *, const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn) +smbc_setFunctionReaddirPlus2: void (SMBCCTX *, smbc_readdirplus2_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, const char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, const char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/ABI/smbclient-0.7.0.sigs b/source3/libsmb/ABI/smbclient-0.7.0.sigs new file mode 100644 index 0000000..ee758e2 --- /dev/null +++ b/source3/libsmb/ABI/smbclient-0.7.0.sigs @@ -0,0 +1,188 @@ +smbc_chmod: int (const char *, mode_t) +smbc_close: int (int) +smbc_closedir: int (int) +smbc_creat: int (const char *, mode_t) +smbc_fgetxattr: int (int, const char *, const void *, size_t) +smbc_flistxattr: int (int, char *, size_t) +smbc_free_context: int (SMBCCTX *, int) +smbc_fremovexattr: int (int, const char *) +smbc_fsetxattr: int (int, const char *, const void *, size_t, int) +smbc_fstat: int (int, struct stat *) +smbc_fstatvfs: int (int, struct statvfs *) +smbc_ftruncate: int (int, off_t) +smbc_getDebug: int (SMBCCTX *) +smbc_getFunctionAddCachedServer: smbc_add_cached_srv_fn (SMBCCTX *) +smbc_getFunctionAuthData: smbc_get_auth_data_fn (SMBCCTX *) +smbc_getFunctionAuthDataWithContext: smbc_get_auth_data_with_context_fn (SMBCCTX *) +smbc_getFunctionCheckServer: smbc_check_server_fn (SMBCCTX *) +smbc_getFunctionChmod: smbc_chmod_fn (SMBCCTX *) +smbc_getFunctionClose: smbc_close_fn (SMBCCTX *) +smbc_getFunctionClosedir: smbc_closedir_fn (SMBCCTX *) +smbc_getFunctionCreat: smbc_creat_fn (SMBCCTX *) +smbc_getFunctionFstat: smbc_fstat_fn (SMBCCTX *) +smbc_getFunctionFstatVFS: smbc_fstatvfs_fn (SMBCCTX *) +smbc_getFunctionFstatdir: smbc_fstatdir_fn (SMBCCTX *) +smbc_getFunctionFtruncate: smbc_ftruncate_fn (SMBCCTX *) +smbc_getFunctionGetCachedServer: smbc_get_cached_srv_fn (SMBCCTX *) +smbc_getFunctionGetdents: smbc_getdents_fn (SMBCCTX *) +smbc_getFunctionGetxattr: smbc_getxattr_fn (SMBCCTX *) +smbc_getFunctionListPrintJobs: smbc_list_print_jobs_fn (SMBCCTX *) +smbc_getFunctionListxattr: smbc_listxattr_fn (SMBCCTX *) +smbc_getFunctionLseek: smbc_lseek_fn (SMBCCTX *) +smbc_getFunctionLseekdir: smbc_lseekdir_fn (SMBCCTX *) +smbc_getFunctionMkdir: smbc_mkdir_fn (SMBCCTX *) +smbc_getFunctionNotify: smbc_notify_fn (SMBCCTX *) +smbc_getFunctionOpen: smbc_open_fn (SMBCCTX *) +smbc_getFunctionOpenPrintJob: smbc_open_print_job_fn (SMBCCTX *) +smbc_getFunctionOpendir: smbc_opendir_fn (SMBCCTX *) +smbc_getFunctionPrintFile: smbc_print_file_fn (SMBCCTX *) +smbc_getFunctionPurgeCachedServers: smbc_purge_cached_fn (SMBCCTX *) +smbc_getFunctionRead: smbc_read_fn (SMBCCTX *) +smbc_getFunctionReaddir: smbc_readdir_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus: smbc_readdirplus_fn (SMBCCTX *) +smbc_getFunctionReaddirPlus2: smbc_readdirplus2_fn (SMBCCTX *) +smbc_getFunctionRemoveCachedServer: smbc_remove_cached_srv_fn (SMBCCTX *) +smbc_getFunctionRemoveUnusedServer: smbc_remove_unused_server_fn (SMBCCTX *) +smbc_getFunctionRemovexattr: smbc_removexattr_fn (SMBCCTX *) +smbc_getFunctionRename: smbc_rename_fn (SMBCCTX *) +smbc_getFunctionRmdir: smbc_rmdir_fn (SMBCCTX *) +smbc_getFunctionSetxattr: smbc_setxattr_fn (SMBCCTX *) +smbc_getFunctionSplice: smbc_splice_fn (SMBCCTX *) +smbc_getFunctionStat: smbc_stat_fn (SMBCCTX *) +smbc_getFunctionStatVFS: smbc_statvfs_fn (SMBCCTX *) +smbc_getFunctionTelldir: smbc_telldir_fn (SMBCCTX *) +smbc_getFunctionUnlink: smbc_unlink_fn (SMBCCTX *) +smbc_getFunctionUnlinkPrintJob: smbc_unlink_print_job_fn (SMBCCTX *) +smbc_getFunctionUtimes: smbc_utimes_fn (SMBCCTX *) +smbc_getFunctionWrite: smbc_write_fn (SMBCCTX *) +smbc_getNetbiosName: const char *(SMBCCTX *) +smbc_getOptionBrowseMaxLmbCount: int (SMBCCTX *) +smbc_getOptionCaseSensitive: smbc_bool (SMBCCTX *) +smbc_getOptionDebugToStderr: smbc_bool (SMBCCTX *) +smbc_getOptionFallbackAfterKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionFullTimeNames: smbc_bool (SMBCCTX *) +smbc_getOptionNoAutoAnonymousLogin: smbc_bool (SMBCCTX *) +smbc_getOptionOneSharePerServer: smbc_bool (SMBCCTX *) +smbc_getOptionOpenShareMode: smbc_share_mode (SMBCCTX *) +smbc_getOptionSmbEncryptionLevel: smbc_smb_encrypt_level (SMBCCTX *) +smbc_getOptionUrlEncodeReaddirEntries: smbc_bool (SMBCCTX *) +smbc_getOptionUseCCache: smbc_bool (SMBCCTX *) +smbc_getOptionUseKerberos: smbc_bool (SMBCCTX *) +smbc_getOptionUseNTHash: smbc_bool (SMBCCTX *) +smbc_getOptionUserData: void *(SMBCCTX *) +smbc_getPort: uint16_t (SMBCCTX *) +smbc_getServerCacheData: struct smbc_server_cache *(SMBCCTX *) +smbc_getTimeout: int (SMBCCTX *) +smbc_getUser: const char *(SMBCCTX *) +smbc_getWorkgroup: const char *(SMBCCTX *) +smbc_getdents: int (unsigned int, struct smbc_dirent *, int) +smbc_getxattr: int (const char *, const char *, const void *, size_t) +smbc_init: int (smbc_get_auth_data_fn, int) +smbc_init_context: SMBCCTX *(SMBCCTX *) +smbc_lgetxattr: int (const char *, const char *, const void *, size_t) +smbc_list_print_jobs: int (const char *, smbc_list_print_job_fn) +smbc_listxattr: int (const char *, char *, size_t) +smbc_llistxattr: int (const char *, char *, size_t) +smbc_lremovexattr: int (const char *, const char *) +smbc_lseek: off_t (int, off_t, int) +smbc_lseekdir: int (int, off_t) +smbc_lsetxattr: int (const char *, const char *, const void *, size_t, int) +smbc_mkdir: int (const char *, mode_t) +smbc_new_context: SMBCCTX *(void) +smbc_notify: int (int, smbc_bool, uint32_t, unsigned int, smbc_notify_callback_fn, void *) +smbc_open: int (const char *, int, mode_t) +smbc_open_print_job: int (const char *) +smbc_opendir: int (const char *) +smbc_option_get: void *(SMBCCTX *, char *) +smbc_option_set: void (SMBCCTX *, char *, ...) +smbc_print_file: int (const char *, const char *) +smbc_read: ssize_t (int, void *, size_t) +smbc_readdir: struct smbc_dirent *(unsigned int) +smbc_readdirplus: const struct libsmb_file_info *(unsigned int) +smbc_readdirplus2: const struct libsmb_file_info *(unsigned int, struct stat *) +smbc_removexattr: int (const char *, const char *) +smbc_rename: int (const char *, const char *) +smbc_rmdir: int (const char *) +smbc_setConfiguration: int (SMBCCTX *, const char *) +smbc_setDebug: void (SMBCCTX *, int) +smbc_setFunctionAddCachedServer: void (SMBCCTX *, smbc_add_cached_srv_fn) +smbc_setFunctionAuthData: void (SMBCCTX *, smbc_get_auth_data_fn) +smbc_setFunctionAuthDataWithContext: void (SMBCCTX *, smbc_get_auth_data_with_context_fn) +smbc_setFunctionCheckServer: void (SMBCCTX *, smbc_check_server_fn) +smbc_setFunctionChmod: void (SMBCCTX *, smbc_chmod_fn) +smbc_setFunctionClose: void (SMBCCTX *, smbc_close_fn) +smbc_setFunctionClosedir: void (SMBCCTX *, smbc_closedir_fn) +smbc_setFunctionCreat: void (SMBCCTX *, smbc_creat_fn) +smbc_setFunctionFstat: void (SMBCCTX *, smbc_fstat_fn) +smbc_setFunctionFstatVFS: void (SMBCCTX *, smbc_fstatvfs_fn) +smbc_setFunctionFstatdir: void (SMBCCTX *, smbc_fstatdir_fn) +smbc_setFunctionFtruncate: void (SMBCCTX *, smbc_ftruncate_fn) +smbc_setFunctionGetCachedServer: void (SMBCCTX *, smbc_get_cached_srv_fn) +smbc_setFunctionGetdents: void (SMBCCTX *, smbc_getdents_fn) +smbc_setFunctionGetxattr: void (SMBCCTX *, smbc_getxattr_fn) +smbc_setFunctionListPrintJobs: void (SMBCCTX *, smbc_list_print_jobs_fn) +smbc_setFunctionListxattr: void (SMBCCTX *, smbc_listxattr_fn) +smbc_setFunctionLseek: void (SMBCCTX *, smbc_lseek_fn) +smbc_setFunctionLseekdir: void (SMBCCTX *, smbc_lseekdir_fn) +smbc_setFunctionMkdir: void (SMBCCTX *, smbc_mkdir_fn) +smbc_setFunctionNotify: void (SMBCCTX *, smbc_notify_fn) +smbc_setFunctionOpen: void (SMBCCTX *, smbc_open_fn) +smbc_setFunctionOpenPrintJob: void (SMBCCTX *, smbc_open_print_job_fn) +smbc_setFunctionOpendir: void (SMBCCTX *, smbc_opendir_fn) +smbc_setFunctionPrintFile: void (SMBCCTX *, smbc_print_file_fn) +smbc_setFunctionPurgeCachedServers: void (SMBCCTX *, smbc_purge_cached_fn) +smbc_setFunctionRead: void (SMBCCTX *, smbc_read_fn) +smbc_setFunctionReaddir: void (SMBCCTX *, smbc_readdir_fn) +smbc_setFunctionReaddirPlus: void (SMBCCTX *, smbc_readdirplus_fn) +smbc_setFunctionReaddirPlus2: void (SMBCCTX *, smbc_readdirplus2_fn) +smbc_setFunctionRemoveCachedServer: void (SMBCCTX *, smbc_remove_cached_srv_fn) +smbc_setFunctionRemoveUnusedServer: void (SMBCCTX *, smbc_remove_unused_server_fn) +smbc_setFunctionRemovexattr: void (SMBCCTX *, smbc_removexattr_fn) +smbc_setFunctionRename: void (SMBCCTX *, smbc_rename_fn) +smbc_setFunctionRmdir: void (SMBCCTX *, smbc_rmdir_fn) +smbc_setFunctionSetxattr: void (SMBCCTX *, smbc_setxattr_fn) +smbc_setFunctionSplice: void (SMBCCTX *, smbc_splice_fn) +smbc_setFunctionStat: void (SMBCCTX *, smbc_stat_fn) +smbc_setFunctionStatVFS: void (SMBCCTX *, smbc_statvfs_fn) +smbc_setFunctionTelldir: void (SMBCCTX *, smbc_telldir_fn) +smbc_setFunctionUnlink: void (SMBCCTX *, smbc_unlink_fn) +smbc_setFunctionUnlinkPrintJob: void (SMBCCTX *, smbc_unlink_print_job_fn) +smbc_setFunctionUtimes: void (SMBCCTX *, smbc_utimes_fn) +smbc_setFunctionWrite: void (SMBCCTX *, smbc_write_fn) +smbc_setLogCallback: void (SMBCCTX *, void *, smbc_debug_callback_fn) +smbc_setNetbiosName: void (SMBCCTX *, const char *) +smbc_setOptionBrowseMaxLmbCount: void (SMBCCTX *, int) +smbc_setOptionCaseSensitive: void (SMBCCTX *, smbc_bool) +smbc_setOptionDebugToStderr: void (SMBCCTX *, smbc_bool) +smbc_setOptionFallbackAfterKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionFullTimeNames: void (SMBCCTX *, smbc_bool) +smbc_setOptionNoAutoAnonymousLogin: void (SMBCCTX *, smbc_bool) +smbc_setOptionOneSharePerServer: void (SMBCCTX *, smbc_bool) +smbc_setOptionOpenShareMode: void (SMBCCTX *, smbc_share_mode) +smbc_setOptionProtocols: smbc_bool (SMBCCTX *, const char *, const char *) +smbc_setOptionSmbEncryptionLevel: void (SMBCCTX *, smbc_smb_encrypt_level) +smbc_setOptionUrlEncodeReaddirEntries: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseCCache: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseKerberos: void (SMBCCTX *, smbc_bool) +smbc_setOptionUseNTHash: void (SMBCCTX *, smbc_bool) +smbc_setOptionUserData: void (SMBCCTX *, void *) +smbc_setPort: void (SMBCCTX *, uint16_t) +smbc_setServerCacheData: void (SMBCCTX *, struct smbc_server_cache *) +smbc_setTimeout: void (SMBCCTX *, int) +smbc_setUser: void (SMBCCTX *, const char *) +smbc_setWorkgroup: void (SMBCCTX *, const char *) +smbc_set_context: SMBCCTX *(SMBCCTX *) +smbc_set_credentials: void (const char *, const char *, const char *, smbc_bool, const char *) +smbc_set_credentials_with_fallback: void (SMBCCTX *, const char *, const char *, const char *) +smbc_setxattr: int (const char *, const char *, const void *, size_t, int) +smbc_stat: int (const char *, struct stat *) +smbc_statvfs: int (char *, struct statvfs *) +smbc_telldir: off_t (int) +smbc_unlink: int (const char *) +smbc_unlink_print_job: int (const char *, int) +smbc_urldecode: int (char *, char *, size_t) +smbc_urlencode: int (char *, char *, int) +smbc_utime: int (const char *, struct utimbuf *) +smbc_utimes: int (const char *, struct timeval *) +smbc_version: const char *(void) +smbc_write: ssize_t (int, const void *, size_t) diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c new file mode 100644 index 0000000..bcb24a4 --- /dev/null +++ b/source3/libsmb/async_smb.c @@ -0,0 +1,264 @@ +/* + Unix SMB/CIFS implementation. + Infrastructure for async SMB client requests + Copyright (C) Volker Lendecke 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "../libcli/smb/smbXcli_base.h" + +struct cli_smb_req_state { + struct cli_state *cli; + uint8_t smb_command; + struct tevent_req *req; + struct cli_smb_req_state **ptr; +}; + +static int cli_smb_req_state_destructor(struct cli_smb_req_state *state) +{ + talloc_set_destructor(state->ptr, NULL); + talloc_free(state->ptr); + return 0; +} + +static int cli_smb_req_state_ptr_destructor(struct cli_smb_req_state **ptr) +{ + struct cli_smb_req_state *state = *ptr; + void *parent = talloc_parent(state); + + talloc_set_destructor(state, NULL); + + talloc_reparent(state, parent, state->req); + talloc_free(state); + return 0; +} + +struct tevent_req *cli_smb_req_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint16_t additional_flags2, + uint8_t wct, uint16_t *vwv, + int iov_count, + struct iovec *bytes_iov) +{ + struct cli_smb_req_state *state; + uint8_t clear_flags = 0; + uint16_t clear_flags2 = 0; + + state = talloc_zero(mem_ctx, struct cli_smb_req_state); + if (state == NULL) { + return NULL; + } + state->cli = cli; + state->smb_command = smb_command; + state->ptr = talloc(state, struct cli_smb_req_state *); + if (state->ptr == NULL) { + talloc_free(state); + return NULL; + } + *state->ptr = state; + + state->req = smb1cli_req_create(state, ev, cli->conn, smb_command, + additional_flags, clear_flags, + additional_flags2, clear_flags2, + cli->timeout, + cli->smb1.pid, + cli->smb1.tcon, + cli->smb1.session, + wct, vwv, iov_count, bytes_iov); + if (state->req == NULL) { + talloc_free(state); + return NULL; + } + + talloc_reparent(state, state->req, state->ptr); + talloc_set_destructor(state, cli_smb_req_state_destructor); + talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor); + + return state->req; +} + +struct tevent_req *cli_smb_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint8_t smb_command, + uint8_t additional_flags, + uint16_t additional_flags2, + uint8_t wct, uint16_t *vwv, + uint32_t num_bytes, + const uint8_t *bytes) +{ + struct cli_smb_req_state *state; + uint8_t clear_flags = 0; + uint16_t clear_flags2 = 0; + + state = talloc_zero(mem_ctx, struct cli_smb_req_state); + if (state == NULL) { + return NULL; + } + state->cli = cli; + state->smb_command = smb_command; + state->ptr = talloc(state, struct cli_smb_req_state *); + if (state->ptr == NULL) { + talloc_free(state); + return NULL; + } + *state->ptr = state; + + state->req = smb1cli_req_send(state, ev, cli->conn, smb_command, + additional_flags, clear_flags, + additional_flags2, clear_flags2, + cli->timeout, + cli->smb1.pid, + cli->smb1.tcon, + cli->smb1.session, + wct, vwv, num_bytes, bytes); + if (state->req == NULL) { + talloc_free(state); + return NULL; + } + + talloc_reparent(state, state->req, state->ptr); + talloc_set_destructor(state, cli_smb_req_state_destructor); + talloc_set_destructor(state->ptr, cli_smb_req_state_ptr_destructor); + + return state->req; +} + +NTSTATUS cli_smb_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, uint8_t **pinbuf, + uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv, + uint32_t *pnum_bytes, uint8_t **pbytes) +{ + NTSTATUS status; + void *parent = talloc_parent(req); + struct cli_smb_req_state *state = + talloc_get_type(parent, + struct cli_smb_req_state); + struct iovec *recv_iov = NULL; + uint8_t wct = 0; + uint16_t *vwv = NULL; + uint32_t num_bytes; + uint8_t *bytes = NULL; + uint8_t *inbuf; + bool is_expected = false; + bool map_dos_errors = true; + + if (pinbuf != NULL) { + *pinbuf = NULL; + } + if (pwct != NULL) { + *pwct = 0; + } + if (pvwv != NULL) { + *pvwv = NULL; + } + if (pnum_bytes != NULL) { + *pnum_bytes = 0; + } + if (pbytes != NULL) { + *pbytes = NULL; + } + + status = smb1cli_req_recv(req, req, + &recv_iov, + NULL, /* phdr */ + &wct, + &vwv, + NULL, /* pvwv_offset */ + &num_bytes, + &bytes, + NULL, /* pbytes_offset */ + &inbuf, + NULL, 0); /* expected */ + + if (state) { + if ((state->smb_command == SMBsesssetupX) && + NT_STATUS_EQUAL(status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + /* + * NT_STATUS_MORE_PROCESSING_REQUIRED is a + * valid return code for session setup + */ + is_expected = true; + } + + map_dos_errors = state->cli->map_dos_errors; + state->cli->raw_status = status; + talloc_free(state->ptr); + state = NULL; + } + + if (NT_STATUS_IS_DOS(status) && map_dos_errors) { + uint8_t eclass = NT_STATUS_DOS_CLASS(status); + uint16_t ecode = NT_STATUS_DOS_CODE(status); + /* + * TODO: is it really a good idea to do a mapping here? + * + * The old cli_pull_error() also does it, so I do not change + * the behavior yet. + */ + status = dos_to_ntstatus(eclass, ecode); + } + + if (!NT_STATUS_IS_ERR(status)) { + is_expected = true; + } + + if (!is_expected) { + TALLOC_FREE(recv_iov); + return status; + } + + if (wct < min_wct) { + TALLOC_FREE(recv_iov); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (pwct != NULL) { + *pwct = wct; + } + if (pvwv != NULL) { + *pvwv = vwv; + } + if (pnum_bytes != NULL) { + *pnum_bytes = num_bytes; + } + if (pbytes != NULL) { + *pbytes = bytes; + } + + if (pinbuf != NULL && mem_ctx != NULL) { + if (talloc_reference_count(inbuf) == 0) { + *pinbuf = talloc_move(mem_ctx, &inbuf); + TALLOC_FREE(recv_iov); + } else { + *pinbuf = inbuf; + } + } else if (mem_ctx != NULL) { + if (talloc_reference_count(inbuf) == 0) { + (void)talloc_move(mem_ctx, &inbuf); + TALLOC_FREE(recv_iov); + } + } + + return status; +} diff --git a/source3/libsmb/auth_generic.c b/source3/libsmb/auth_generic.c new file mode 100644 index 0000000..e5120a0 --- /dev/null +++ b/source3/libsmb/auth_generic.c @@ -0,0 +1,229 @@ +/* + NLTMSSP wrappers + + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett 2001-2003,2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "auth_generic.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "librpc/rpc/dcerpc.h" +#include "lib/param/param.h" +#include "librpc/crypto/gse.h" + +NTSTATUS auth_generic_set_username(struct auth_generic_state *ans, + const char *user) +{ + cli_credentials_set_username(ans->credentials, user, CRED_SPECIFIED); + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_set_domain(struct auth_generic_state *ans, + const char *domain) +{ + cli_credentials_set_domain(ans->credentials, domain, CRED_SPECIFIED); + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_set_password(struct auth_generic_state *ans, + const char *password) +{ + cli_credentials_set_password(ans->credentials, password, CRED_SPECIFIED); + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_set_creds(struct auth_generic_state *ans, + struct cli_credentials *creds) +{ + talloc_unlink(ans->credentials, creds); + ans->credentials = creds; + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_client_prepare(TALLOC_CTX *mem_ctx, struct auth_generic_state **auth_generic_state) +{ + struct auth_generic_state *ans; + NTSTATUS nt_status; + size_t idx = 0; + struct gensec_settings *gensec_settings; + const struct gensec_security_ops **backends = NULL; + struct loadparm_context *lp_ctx; + bool ok; + + ans = talloc_zero(mem_ctx, struct auth_generic_state); + if (!ans) { + DEBUG(0,("auth_generic_start: talloc failed!\n")); + return NT_STATUS_NO_MEMORY; + } + + lp_ctx = loadparm_init_s3(ans, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + DEBUG(10, ("loadparm_init_s3 failed\n")); + TALLOC_FREE(ans); + return NT_STATUS_INVALID_SERVER_STATE; + } + + gensec_settings = lpcfg_gensec_settings(ans, lp_ctx); + if (lp_ctx == NULL) { + DEBUG(10, ("lpcfg_gensec_settings failed\n")); + TALLOC_FREE(ans); + return NT_STATUS_NO_MEMORY; + } + + backends = talloc_zero_array(gensec_settings, + const struct gensec_security_ops *, 7); + if (backends == NULL) { + TALLOC_FREE(ans); + return NT_STATUS_NO_MEMORY; + } + gensec_settings->backends = backends; + + gensec_init(); + + /* These need to be in priority order, krb5 before NTLMSSP */ +#if defined(HAVE_KRB5) + backends[idx++] = &gensec_gse_krb5_security_ops; +#endif + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP); + backends[idx++] = gensec_security_by_name(NULL, "ntlmssp_resume_ccache"); + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO); + backends[idx++] = gensec_security_by_auth_type(NULL, DCERPC_AUTH_TYPE_SCHANNEL); + backends[idx++] = gensec_security_by_auth_type(NULL, DCERPC_AUTH_TYPE_NCALRPC_AS_SYSTEM); + + nt_status = gensec_client_start(ans, &ans->gensec_security, gensec_settings); + + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(ans); + return nt_status; + } + + ans->credentials = cli_credentials_init(ans); + if (!ans->credentials) { + TALLOC_FREE(ans); + return NT_STATUS_NO_MEMORY; + } + + ok = cli_credentials_guess(ans->credentials, lp_ctx); + if (!ok) { + TALLOC_FREE(ans); + return NT_STATUS_INTERNAL_ERROR; + } + + talloc_unlink(ans, lp_ctx); + talloc_unlink(ans, gensec_settings); + + *auth_generic_state = ans; + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_client_start(struct auth_generic_state *ans, const char *oid) +{ + NTSTATUS status; + + /* Transfer the credentials to gensec */ + status = gensec_set_credentials(ans->gensec_security, ans->credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC credentials: %s\n", + nt_errstr(status))); + return status; + } + talloc_unlink(ans, ans->credentials); + ans->credentials = NULL; + + status = gensec_start_mech_by_oid(ans->gensec_security, + oid); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_client_start_by_name(struct auth_generic_state *ans, + const char *name) +{ + NTSTATUS status; + + /* Transfer the credentials to gensec */ + status = gensec_set_credentials(ans->gensec_security, ans->credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC credentials: %s\n", + nt_errstr(status))); + return status; + } + talloc_unlink(ans, ans->credentials); + ans->credentials = NULL; + + status = gensec_start_mech_by_name(ans->gensec_security, name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_client_start_by_authtype(struct auth_generic_state *ans, + uint8_t auth_type, + uint8_t auth_level) +{ + NTSTATUS status; + + /* Transfer the credentials to gensec */ + status = gensec_set_credentials(ans->gensec_security, ans->credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC credentials: %s\n", + nt_errstr(status))); + return status; + } + talloc_unlink(ans, ans->credentials); + ans->credentials = NULL; + + status = gensec_start_mech_by_authtype(ans->gensec_security, + auth_type, auth_level); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +NTSTATUS auth_generic_client_start_by_sasl(struct auth_generic_state *ans, + const char **sasl_list) +{ + NTSTATUS status; + + /* Transfer the credentials to gensec */ + status = gensec_set_credentials(ans->gensec_security, ans->credentials); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Failed to set GENSEC credentials: %s\n", + nt_errstr(status))); + return status; + } + talloc_unlink(ans, ans->credentials); + ans->credentials = NULL; + + status = gensec_start_mech_by_sasl_list(ans->gensec_security, sasl_list); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} diff --git a/source3/libsmb/cli_smb2_fnum.c b/source3/libsmb/cli_smb2_fnum.c new file mode 100644 index 0000000..13d23ed --- /dev/null +++ b/source3/libsmb/cli_smb2_fnum.c @@ -0,0 +1,5199 @@ +/* + Unix SMB/CIFS implementation. + smb2 lib + Copyright (C) Jeremy Allison 2013 + Copyright (C) Volker Lendecke 2013 + Copyright (C) Stefan Metzmacher 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + This code is a thin wrapper around the existing + cli_smb2_XXXX() functions in libcli/smb/smb2cli_XXXXX.c, + but allows the handles to be mapped to uint16_t fnums, + which are easier for smbclient to use. +*/ + +#include "includes.h" +#include "client.h" +#include "async_smb.h" +#include "../libcli/smb/smbXcli_base.h" +#include "cli_smb2_fnum.h" +#include "trans2.h" +#include "clirap.h" +#include "../libcli/smb/smb2_create_blob.h" +#include "libsmb/proto.h" +#include "lib/util/tevent_ntstatus.h" +#include "../libcli/security/security.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "lib/util_ea.h" +#include "librpc/gen_ndr/ndr_ioctl.h" +#include "ntioctl.h" +#include "librpc/gen_ndr/ndr_quota.h" +#include "lib/util/string_wrappers.h" + +struct smb2_hnd { + uint64_t fid_persistent; + uint64_t fid_volatile; +}; + +/* + * Handle mapping code. + */ + +/*************************************************************** + Allocate a new fnum between 1 and 0xFFFE from an smb2_hnd. + Ensures handle is owned by cli struct. +***************************************************************/ + +static NTSTATUS map_smb2_handle_to_fnum(struct cli_state *cli, + const struct smb2_hnd *ph, /* In */ + uint16_t *pfnum) /* Out */ +{ + int ret; + struct idr_context *idp = cli->smb2.open_handles; + struct smb2_hnd *owned_h = talloc_memdup(cli, + ph, + sizeof(struct smb2_hnd)); + + if (owned_h == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (idp == NULL) { + /* Lazy init */ + cli->smb2.open_handles = idr_init(cli); + if (cli->smb2.open_handles == NULL) { + TALLOC_FREE(owned_h); + return NT_STATUS_NO_MEMORY; + } + idp = cli->smb2.open_handles; + } + + ret = idr_get_new_above(idp, owned_h, 1, 0xFFFE); + if (ret == -1) { + TALLOC_FREE(owned_h); + return NT_STATUS_NO_MEMORY; + } + + *pfnum = (uint16_t)ret; + return NT_STATUS_OK; +} + +/*************************************************************** + Return the smb2_hnd pointer associated with the given fnum. +***************************************************************/ + +static NTSTATUS map_fnum_to_smb2_handle(struct cli_state *cli, + uint16_t fnum, /* In */ + struct smb2_hnd **pph) /* Out */ +{ + struct idr_context *idp = cli->smb2.open_handles; + + if (idp == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + *pph = (struct smb2_hnd *)idr_find(idp, fnum); + if (*pph == NULL) { + return NT_STATUS_INVALID_HANDLE; + } + return NT_STATUS_OK; +} + +/*************************************************************** + Delete the fnum to smb2_hnd mapping. Zeros out handle on + successful return. +***************************************************************/ + +static NTSTATUS delete_smb2_handle_mapping(struct cli_state *cli, + struct smb2_hnd **pph, /* In */ + uint16_t fnum) /* In */ +{ + struct idr_context *idp = cli->smb2.open_handles; + struct smb2_hnd *ph; + + if (idp == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + ph = (struct smb2_hnd *)idr_find(idp, fnum); + if (ph != *pph) { + return NT_STATUS_INVALID_PARAMETER; + } + idr_remove(idp, fnum); + TALLOC_FREE(*pph); + return NT_STATUS_OK; +} + +/*************************************************************** + Oplock mapping code. +***************************************************************/ + +static uint8_t flags_to_smb2_oplock(uint32_t create_flags) +{ + if (create_flags & REQUEST_BATCH_OPLOCK) { + return SMB2_OPLOCK_LEVEL_BATCH; + } else if (create_flags & REQUEST_OPLOCK) { + return SMB2_OPLOCK_LEVEL_EXCLUSIVE; + } + + /* create_flags doesn't do a level2 request. */ + return SMB2_OPLOCK_LEVEL_NONE; +} + +/*************************************************************** + Small wrapper that allows SMB2 create to return a uint16_t fnum. +***************************************************************/ + +struct cli_smb2_create_fnum_state { + struct cli_state *cli; + struct smb2_create_blobs in_cblobs; + struct smb2_create_blobs out_cblobs; + struct smb_create_returns cr; + uint16_t fnum; + struct tevent_req *subreq; +}; + +static void cli_smb2_create_fnum_done(struct tevent_req *subreq); +static bool cli_smb2_create_fnum_cancel(struct tevent_req *req); + +struct tevent_req *cli_smb2_create_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t impersonation_level, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + const struct smb2_create_blobs *in_cblobs) +{ + struct tevent_req *req, *subreq; + struct cli_smb2_create_fnum_state *state; + size_t fname_len = 0; + const char *startp = NULL; + const char *endp = NULL; + time_t tstamp = (time_t)0; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_create_fnum_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + if (cli->backup_intent) { + create_options |= FILE_OPEN_FOR_BACKUP_INTENT; + } + + /* Check for @GMT- paths. Remove the @GMT and turn into TWrp if so. */ + fname_len = strlen(fname); + if (clistr_is_previous_version_path(fname, &startp, &endp, &tstamp)) { + size_t len_before_gmt = startp - fname; + size_t len_after_gmt = fname + fname_len - endp; + DATA_BLOB twrp_blob; + NTTIME ntt; + + char *new_fname = talloc_array(state, char, + len_before_gmt + len_after_gmt + 1); + + if (tevent_req_nomem(new_fname, req)) { + return tevent_req_post(req, ev); + } + + memcpy(new_fname, fname, len_before_gmt); + memcpy(new_fname + len_before_gmt, endp, len_after_gmt + 1); + fname = new_fname; + fname_len = len_before_gmt + len_after_gmt; + + unix_to_nt_time(&ntt, tstamp); + twrp_blob = data_blob_const((const void *)&ntt, 8); + + status = smb2_create_blob_add( + state, + &state->in_cblobs, + SMB2_CREATE_TAG_TWRP, + twrp_blob); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + } + + if (in_cblobs != NULL) { + uint32_t i; + for (i=0; i<in_cblobs->num_blobs; i++) { + struct smb2_create_blob *b = &in_cblobs->blobs[i]; + status = smb2_create_blob_add( + state, &state->in_cblobs, b->tag, b->data); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + } + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + start in a '\' */ + if (*fname == '\\') { + fname++; + fname_len--; + } + + /* Or end in a '\' */ + if (fname_len > 0 && fname[fname_len-1] == '\\') { + char *new_fname = talloc_strdup(state, fname); + if (tevent_req_nomem(new_fname, req)) { + return tevent_req_post(req, ev); + } + new_fname[fname_len-1] = '\0'; + fname = new_fname; + } + + subreq = smb2cli_create_send(state, ev, + cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + fname, + flags_to_smb2_oplock(create_flags), + impersonation_level, + desired_access, + file_attributes, + share_access, + create_disposition, + create_options, + &state->in_cblobs); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_create_fnum_done, req); + + state->subreq = subreq; + tevent_req_set_cancel_fn(req, cli_smb2_create_fnum_cancel); + + return req; +} + +static void cli_smb2_create_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_create_fnum_state *state = tevent_req_data( + req, struct cli_smb2_create_fnum_state); + struct smb2_hnd h; + NTSTATUS status; + + status = smb2cli_create_recv( + subreq, + &h.fid_persistent, + &h.fid_volatile, &state->cr, + state, + &state->out_cblobs); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + status = map_smb2_handle_to_fnum(state->cli, &h, &state->fnum); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static bool cli_smb2_create_fnum_cancel(struct tevent_req *req) +{ + struct cli_smb2_create_fnum_state *state = tevent_req_data( + req, struct cli_smb2_create_fnum_state); + return tevent_req_cancel(state->subreq); +} + +NTSTATUS cli_smb2_create_fnum_recv( + struct tevent_req *req, + uint16_t *pfnum, + struct smb_create_returns *cr, + TALLOC_CTX *mem_ctx, + struct smb2_create_blobs *out_cblobs) +{ + struct cli_smb2_create_fnum_state *state = tevent_req_data( + req, struct cli_smb2_create_fnum_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + return status; + } + if (pfnum != NULL) { + *pfnum = state->fnum; + } + if (cr != NULL) { + *cr = state->cr; + } + if (out_cblobs != NULL) { + *out_cblobs = (struct smb2_create_blobs) { + .num_blobs = state->out_cblobs.num_blobs, + .blobs = talloc_move( + mem_ctx, &state->out_cblobs.blobs), + }; + } + state->cli->raw_status = NT_STATUS_OK; + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_create_fnum( + struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t impersonation_level, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + const struct smb2_create_blobs *in_cblobs, + uint16_t *pfid, + struct smb_create_returns *cr, + TALLOC_CTX *mem_ctx, + struct smb2_create_blobs *out_cblobs) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_create_fnum_send( + frame, + ev, + cli, + fname, + create_flags, + impersonation_level, + desired_access, + file_attributes, + share_access, + create_disposition, + create_options, + in_cblobs); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_create_fnum_recv(req, pfid, cr, mem_ctx, out_cblobs); + fail: + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Small wrapper that allows SMB2 close to use a uint16_t fnum. +***************************************************************/ + +struct cli_smb2_close_fnum_state { + struct cli_state *cli; + uint16_t fnum; + struct smb2_hnd *ph; +}; + +static void cli_smb2_close_fnum_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum) +{ + struct tevent_req *req, *subreq; + struct cli_smb2_close_fnum_state *state; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_close_fnum_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->fnum = fnum; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_close_send(state, ev, cli->conn, cli->timeout, + cli->smb2.session, cli->smb2.tcon, + 0, state->ph->fid_persistent, + state->ph->fid_volatile); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_close_fnum_done, req); + return req; +} + +static void cli_smb2_close_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_close_fnum_state *state = tevent_req_data( + req, struct cli_smb2_close_fnum_state); + NTSTATUS status; + + status = smb2cli_close_recv(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* Delete the fnum -> handle mapping. */ + status = delete_smb2_handle_mapping(state->cli, &state->ph, + state->fnum); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req) +{ + struct cli_smb2_close_fnum_state *state = tevent_req_data( + req, struct cli_smb2_close_fnum_state); + NTSTATUS status = NT_STATUS_OK; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + } + tevent_req_received(req); + return status; +} + +NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_close_fnum_send(frame, ev, cli, fnum); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_close_fnum_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_set_info_fnum_state { + uint8_t dummy; +}; + +static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_set_info_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_set_info_fnum_state *state = NULL; + struct smb2_hnd *ph = NULL; + NTSTATUS status; + + req = tevent_req_create( + mem_ctx, &state, struct cli_smb2_set_info_fnum_state); + if (req == NULL) { + return NULL; + } + + status = map_fnum_to_smb2_handle(cli, fnum, &ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_set_info_send( + state, + ev, + cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + in_info_type, + in_info_class, + in_input_buffer, + in_additional_info, + ph->fid_persistent, + ph->fid_volatile); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_set_info_fnum_done, req); + return req; +} + +static void cli_smb2_set_info_fnum_done(struct tevent_req *subreq) +{ + NTSTATUS status = smb2cli_set_info_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_smb2_set_info_fnum( + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + bool ok; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_set_info_fnum_send( + frame, + ev, + cli, + fnum, + in_info_type, + in_info_class, + in_input_buffer, + in_additional_info); + if (req == NULL) { + goto fail; + } + ok = tevent_req_poll_ntstatus(req, ev, &status); + if (!ok) { + goto fail; + } + status = cli_smb2_set_info_fnum_recv(req); +fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_delete_on_close_state { + struct cli_state *cli; + uint8_t data[1]; + DATA_BLOB inbuf; +}; + +static void cli_smb2_delete_on_close_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool flag) +{ + struct tevent_req *req = NULL; + struct cli_smb2_delete_on_close_state *state = NULL; + struct tevent_req *subreq = NULL; + uint8_t in_info_type; + uint8_t in_file_info_class; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_delete_on_close_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + /* + * setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + * level 13 (SMB_FILE_DISPOSITION_INFORMATION - 1000). + */ + in_info_type = 1; + in_file_info_class = SMB_FILE_DISPOSITION_INFORMATION - 1000; + /* Setup data array. */ + SCVAL(&state->data[0], 0, flag ? 1 : 0); + state->inbuf.data = &state->data[0]; + state->inbuf.length = 1; + + subreq = cli_smb2_set_info_fnum_send( + state, + ev, + cli, + fnum, + in_info_type, + in_file_info_class, + &state->inbuf, + 0); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_delete_on_close_done, + req); + return req; +} + +static void cli_smb2_delete_on_close_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req) +{ + struct cli_smb2_delete_on_close_state *state = + tevent_req_data(req, + struct cli_smb2_delete_on_close_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + tevent_req_received(req); + return status; + } + + state->cli->raw_status = NT_STATUS_OK; + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_delete_on_close_send(frame, ev, cli, fnum, flag); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_delete_on_close_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_mkdir_state { + struct tevent_context *ev; + struct cli_state *cli; +}; + +static void cli_smb2_mkdir_opened(struct tevent_req *subreq); +static void cli_smb2_mkdir_closed(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_mkdir_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_mkdir_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_smb2_mkdir_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + /* Ensure this is a directory. */ + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + dname, /* fname */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */ + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE, /* share_access */ + FILE_CREATE, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_mkdir_opened, req); + return req; +} + +static void cli_smb2_mkdir_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_mkdir_state *state = tevent_req_data( + req, struct cli_smb2_mkdir_state); + NTSTATUS status; + uint16_t fnum = 0xffff; + + status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_mkdir_closed, req); +} + +static void cli_smb2_mkdir_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_close_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_smb2_rmdir_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *dname; + const struct smb2_create_blobs *in_cblobs; + uint16_t fnum; + NTSTATUS status; +}; + +static void cli_smb2_rmdir_opened1(struct tevent_req *subreq); +static void cli_smb2_rmdir_opened2(struct tevent_req *subreq); +static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq); +static void cli_smb2_rmdir_closed(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_rmdir_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname, + const struct smb2_create_blobs *in_cblobs) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_rmdir_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_rmdir_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->dname = dname; + state->in_cblobs = in_cblobs; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + subreq = cli_smb2_create_fnum_send( + state, + state->ev, + state->cli, + state->dname, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + DELETE_ACCESS, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + state->in_cblobs); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_rmdir_opened1, req); + return req; +} + +static void cli_smb2_rmdir_opened1(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_rmdir_state *state = tevent_req_data( + req, struct cli_smb2_rmdir_state); + NTSTATUS status; + + status = cli_smb2_create_fnum_recv( + subreq, &state->fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + + if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + /* + * Naive option to match our SMB1 code. Assume the + * symlink path that tripped us up was the last + * component and try again. Eventually we will have to + * deal with the returned path unprocessed component. JRA. + */ + subreq = cli_smb2_create_fnum_send( + state, + state->ev, + state->cli, + state->dname, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + DELETE_ACCESS, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE| + FILE_DELETE_ON_CLOSE| + FILE_OPEN_REPARSE_POINT, /* create_options */ + state->in_cblobs); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rmdir_opened2, req); + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_smb2_delete_on_close_send( + state, state->ev, state->cli, state->fnum, true); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req); +} + +static void cli_smb2_rmdir_opened2(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_rmdir_state *state = tevent_req_data( + req, struct cli_smb2_rmdir_state); + NTSTATUS status; + + status = cli_smb2_create_fnum_recv( + subreq, &state->fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_smb2_delete_on_close_send( + state, state->ev, state->cli, state->fnum, true); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rmdir_disp_set, req); +} + +static void cli_smb2_rmdir_disp_set(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_rmdir_state *state = tevent_req_data( + req, struct cli_smb2_rmdir_state); + + state->status = cli_smb2_delete_on_close_recv(subreq); + TALLOC_FREE(subreq); + + /* + * Close the fd even if the set_disp failed + */ + + subreq = cli_smb2_close_fnum_send( + state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rmdir_closed, req); +} + +static void cli_smb2_rmdir_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_close_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req) +{ + struct cli_smb2_rmdir_state *state = tevent_req_data( + req, struct cli_smb2_rmdir_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + return state->status; +} + +/*************************************************************** + Small wrapper that allows SMB2 to unlink a pathname. +***************************************************************/ + +struct cli_smb2_unlink_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *fname; + const struct smb2_create_blobs *in_cblobs; +}; + +static void cli_smb2_unlink_opened1(struct tevent_req *subreq); +static void cli_smb2_unlink_opened2(struct tevent_req *subreq); +static void cli_smb2_unlink_closed(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_unlink_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + const struct smb2_create_blobs *in_cblobs) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_unlink_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_unlink_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->fname = fname; + state->in_cblobs = in_cblobs; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx */ + state->ev, /* tevent_context */ + state->cli, /* cli_struct */ + state->fname, /* filename */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + DELETE_ACCESS, /* desired_access */ + FILE_ATTRIBUTE_NORMAL, /* file attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DELETE_ON_CLOSE, /* create_options */ + state->in_cblobs); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_unlink_opened1, req); + return req; +} + +static void cli_smb2_unlink_opened1(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_unlink_state *state = tevent_req_data( + req, struct cli_smb2_unlink_state); + uint16_t fnum = 0xffff; + NTSTATUS status; + + status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + + if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + /* + * Naive option to match our SMB1 code. Assume the + * symlink path that tripped us up was the last + * component and try again. Eventually we will have to + * deal with the returned path unprocessed component. JRA. + */ + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx */ + state->ev, /* tevent_context */ + state->cli, /* cli_struct */ + state->fname, /* filename */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + DELETE_ACCESS, /* desired_access */ + FILE_ATTRIBUTE_NORMAL, /* file attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DELETE_ON_CLOSE| + FILE_OPEN_REPARSE_POINT, /* create_options */ + state->in_cblobs); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_unlink_opened2, req); + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req); +} + +static void cli_smb2_unlink_opened2(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_unlink_state *state = tevent_req_data( + req, struct cli_smb2_unlink_state); + uint16_t fnum = 0xffff; + NTSTATUS status; + + status = cli_smb2_create_fnum_recv(subreq, &fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_smb2_close_fnum_send(state, state->ev, state->cli, fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_unlink_closed, req); +} + +static void cli_smb2_unlink_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_close_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/*************************************************************** + Utility function to parse a SMB2_FIND_ID_BOTH_DIRECTORY_INFO reply. +***************************************************************/ + +static NTSTATUS parse_finfo_id_both_directory_info(const uint8_t *dir_data, + uint32_t dir_data_length, + struct file_info *finfo, + uint32_t *next_offset) +{ + size_t namelen = 0; + size_t slen = 0; + size_t ret = 0; + + if (dir_data_length < 4) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + *next_offset = IVAL(dir_data, 0); + + if (*next_offset > dir_data_length) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + if (*next_offset != 0) { + /* Ensure we only read what in this record. */ + dir_data_length = *next_offset; + } + + if (dir_data_length < 105) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + + finfo->btime_ts = interpret_long_date((const char *)dir_data + 8); + finfo->atime_ts = interpret_long_date((const char *)dir_data + 16); + finfo->mtime_ts = interpret_long_date((const char *)dir_data + 24); + finfo->ctime_ts = interpret_long_date((const char *)dir_data + 32); + finfo->size = IVAL2_TO_SMB_BIG_UINT(dir_data + 40, 0); + finfo->allocated_size = IVAL2_TO_SMB_BIG_UINT(dir_data + 48, 0); + finfo->attr = IVAL(dir_data + 56, 0); + finfo->ino = IVAL2_TO_SMB_BIG_UINT(dir_data + 96, 0); + namelen = IVAL(dir_data + 60,0); + if (namelen > (dir_data_length - 104)) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + slen = CVAL(dir_data + 68, 0); + if (slen > 24) { + return NT_STATUS_INFO_LENGTH_MISMATCH; + } + ret = pull_string_talloc(finfo, + dir_data, + FLAGS2_UNICODE_STRINGS, + &finfo->short_name, + dir_data + 70, + slen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + ret = pull_string_talloc(finfo, + dir_data, + FLAGS2_UNICODE_STRINGS, + &finfo->name, + dir_data + 104, + namelen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (finfo->name == NULL) { + /* Bad conversion. */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + return NT_STATUS_OK; +} + +/******************************************************************* + Given a filename - get its directory name +********************************************************************/ + +static bool windows_parent_dirname(TALLOC_CTX *mem_ctx, + const char *dir, + char **parent, + const char **name) +{ + char *p; + ptrdiff_t len; + + p = strrchr_m(dir, '\\'); /* Find final '\\', if any */ + + if (p == NULL) { + if (!(*parent = talloc_strdup(mem_ctx, "\\"))) { + return false; + } + if (name) { + *name = dir; + } + return true; + } + + len = p-dir; + + if (!(*parent = (char *)talloc_memdup(mem_ctx, dir, len+1))) { + return false; + } + (*parent)[len] = '\0'; + + if (name) { + *name = p+1; + } + return true; +} + +struct cli_smb2_list_dir_data { + uint8_t *data; + uint32_t length; +}; + +struct cli_smb2_list_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *mask; + + uint16_t fnum; + + NTSTATUS status; + struct cli_smb2_list_dir_data *response; + uint32_t offset; +}; + +static void cli_smb2_list_opened(struct tevent_req *subreq); +static void cli_smb2_list_done(struct tevent_req *subreq); +static void cli_smb2_list_closed(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_list_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *pathname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_list_state *state = NULL; + char *parent = NULL; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_list_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->status = NT_STATUS_OK; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + ok = windows_parent_dirname(state, pathname, &parent, &state->mask); + if (!ok) { + tevent_req_oom(req); + return tevent_req_post(req, ev); + } + + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + parent, /* fname */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */ + SEC_DIR_LIST|SEC_DIR_READ_ATTRIBUTE, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_list_opened, req); + return req; +} + +static void cli_smb2_list_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_list_state *state = tevent_req_data( + req, struct cli_smb2_list_state); + NTSTATUS status; + + status = cli_smb2_create_fnum_recv( + subreq, &state->fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* + * Make our caller get back to us via cli_smb2_list_recv(), + * triggering the smb2_query_directory_send() + */ + tevent_req_defer_callback(req, state->ev); + tevent_req_notify_callback(req); +} + +static void cli_smb2_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_list_state *state = tevent_req_data( + req, struct cli_smb2_list_state); + struct cli_smb2_list_dir_data *response = NULL; + + response = talloc(state, struct cli_smb2_list_dir_data); + if (tevent_req_nomem(response, req)) { + return; + } + + state->status = smb2cli_query_directory_recv( + subreq, response, &response->data, &response->length); + TALLOC_FREE(subreq); + + if (NT_STATUS_IS_OK(state->status)) { + state->response = response; + state->offset = 0; + + tevent_req_defer_callback(req, state->ev); + tevent_req_notify_callback(req); + return; + } + + TALLOC_FREE(response); + + subreq = cli_smb2_close_fnum_send( + state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_list_closed, req); +} + +static void cli_smb2_list_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_close_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +/* + * Return the next finfo directory. + * + * This parses the blob returned from QUERY_DIRECTORY step by step. If + * the blob ends, this triggers a fresh QUERY_DIRECTORY and returns + * NT_STATUS_RETRY, which will then trigger the caller again when the + * QUERY_DIRECTORY has returned with another buffer. This way we + * guarantee that no asynchronous request is open after this call + * returns an entry, so that other synchronous requests can be issued + * on the same connection while the directoy listing proceeds. + */ +NTSTATUS cli_smb2_list_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct file_info **pfinfo) +{ + struct cli_smb2_list_state *state = tevent_req_data( + req, struct cli_smb2_list_state); + struct cli_smb2_list_dir_data *response = NULL; + struct file_info *finfo = NULL; + NTSTATUS status; + uint32_t next_offset = 0; + bool in_progress; + + in_progress = tevent_req_is_in_progress(req); + + if (!in_progress) { + if (!tevent_req_is_nterror(req, &status)) { + status = NT_STATUS_NO_MORE_FILES; + } + goto fail; + } + + response = state->response; + if (response == NULL) { + struct tevent_req *subreq = NULL; + struct cli_state *cli = state->cli; + struct smb2_hnd *ph = NULL; + uint32_t max_trans, max_avail_len; + bool ok; + + if (!NT_STATUS_IS_OK(state->status)) { + status = state->status; + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, state->fnum, &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + max_trans = smb2cli_conn_max_trans_size(cli->conn); + ok = smb2cli_conn_req_possible(cli->conn, &max_avail_len); + if (ok) { + max_trans = MIN(max_trans, max_avail_len); + } + + subreq = smb2cli_query_directory_send( + state, /* mem_ctx */ + state->ev, /* ev */ + cli->conn, /* conn */ + cli->timeout, /* timeout_msec */ + cli->smb2.session, /* session */ + cli->smb2.tcon, /* tcon */ + SMB2_FIND_ID_BOTH_DIRECTORY_INFO, /* level */ + 0, /* flags */ + 0, /* file_index */ + ph->fid_persistent, /* fid_persistent */ + ph->fid_volatile, /* fid_volatile */ + state->mask, /* mask */ + max_trans); /* outbuf_len */ + if (subreq == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + tevent_req_set_callback(subreq, cli_smb2_list_done, req); + return NT_STATUS_RETRY; + } + + SMB_ASSERT(response->length > state->offset); + + finfo = talloc_zero(mem_ctx, struct file_info); + if (finfo == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + status = parse_finfo_id_both_directory_info( + response->data + state->offset, + response->length - state->offset, + finfo, + &next_offset); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = is_bad_finfo_name(state->cli, finfo); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* + * parse_finfo_id_both_directory_info() checks for overflow, + * no need to check again here. + */ + state->offset += next_offset; + + if (next_offset == 0) { + TALLOC_FREE(state->response); + } + + tevent_req_defer_callback(req, state->ev); + tevent_req_notify_callback(req); + + *pfinfo = finfo; + return NT_STATUS_OK; + +fail: + TALLOC_FREE(finfo); + tevent_req_received(req); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a path info (basic level). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, + const char *name, + SMB_STRUCT_STAT *sbuf, + uint32_t *attributes) +{ + NTSTATUS status; + struct smb_create_returns cr; + uint16_t fnum = 0xffff; + size_t namelen = strlen(name); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* SMB2 is pickier about pathnames. Ensure it doesn't + end in a '\' */ + if (namelen > 0 && name[namelen-1] == '\\') { + char *modname = talloc_strndup(talloc_tos(), name, namelen-1); + if (modname == NULL) { + return NT_STATUS_NO_MEMORY; + } + name = modname; + } + + /* This is commonly used as a 'cd'. Try qpathinfo on + a directory handle first. */ + + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL, + &fnum, + &cr, + NULL, + NULL); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_A_DIRECTORY)) { + /* Maybe a file ? */ + status = cli_smb2_create_fnum(cli, + name, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, /* desired_access */ + 0, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + 0, /* create_options */ + NULL, + &fnum, + &cr, + NULL, + NULL); + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = cli_smb2_close_fnum(cli, fnum); + + ZERO_STRUCTP(sbuf); + + sbuf->st_ex_atime = nt_time_to_unix_timespec(cr.last_access_time); + sbuf->st_ex_mtime = nt_time_to_unix_timespec(cr.last_write_time); + sbuf->st_ex_ctime = nt_time_to_unix_timespec(cr.change_time); + sbuf->st_ex_size = cr.end_of_file; + *attributes = cr.file_attributes; + + return status; +} + +struct cli_smb2_query_info_fnum_state { + DATA_BLOB outbuf; +}; + +static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_query_info_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + uint32_t in_max_output_length, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info, + uint32_t in_flags) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_query_info_fnum_state *state = NULL; + struct smb2_hnd *ph = NULL; + NTSTATUS status; + + req = tevent_req_create( + mem_ctx, &state, struct cli_smb2_query_info_fnum_state); + if (req == NULL) { + return req; + } + + status = map_fnum_to_smb2_handle(cli, fnum, &ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_query_info_send( + state, + ev, + cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + in_info_type, + in_info_class, + in_max_output_length, + in_input_buffer, + in_additional_info, + in_flags, + ph->fid_persistent, + ph->fid_volatile); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_query_info_fnum_done, req); + return req; +} + +static void cli_smb2_query_info_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_query_info_fnum_state *state = tevent_req_data( + req, struct cli_smb2_query_info_fnum_state); + DATA_BLOB outbuf; + NTSTATUS status; + + status = smb2cli_query_info_recv(subreq, state, &outbuf); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* + * We have to dup the memory here because outbuf.data is not + * returned as a talloc object by smb2cli_query_info_recv. + * It's a pointer into the received buffer. + */ + state->outbuf = data_blob_dup_talloc(state, outbuf); + + if ((outbuf.length != 0) && + tevent_req_nomem(state->outbuf.data, req)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_smb2_query_info_fnum_recv( + struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf) +{ + struct cli_smb2_query_info_fnum_state *state = tevent_req_data( + req, struct cli_smb2_query_info_fnum_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *outbuf = (DATA_BLOB) { + .data = talloc_move(mem_ctx, &state->outbuf.data), + .length = state->outbuf.length, + }; + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_query_info_fnum( + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + uint32_t in_max_output_length, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info, + uint32_t in_flags, + TALLOC_CTX *mem_ctx, + DATA_BLOB *outbuf) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + bool ok; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_query_info_fnum_send( + frame, + ev, + cli, + fnum, + in_info_type, + in_info_class, + in_max_output_length, + in_input_buffer, + in_additional_info, + in_flags); + if (req == NULL) { + goto fail; + } + ok = tevent_req_poll_ntstatus(req, ev, &status); + if (!ok) { + goto fail; + } + status = cli_smb2_query_info_fnum_recv(req, mem_ctx, outbuf); +fail: + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Helper function for pathname operations. +***************************************************************/ + +struct get_fnum_from_path_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *name; + uint32_t desired_access; + uint16_t fnum; +}; + +static void get_fnum_from_path_opened_file(struct tevent_req *subreq); +static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq); +static void get_fnum_from_path_opened_dir(struct tevent_req *subreq); + +static struct tevent_req *get_fnum_from_path_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *name, + uint32_t desired_access) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct get_fnum_from_path_state *state = NULL; + size_t namelen = strlen(name); + + req = tevent_req_create( + mem_ctx, &state, struct get_fnum_from_path_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->name = name; + state->desired_access = desired_access; + + /* + * SMB2 is pickier about pathnames. Ensure it doesn't end in a + * '\' + */ + if (namelen > 0 && name[namelen-1] == '\\') { + state->name = talloc_strndup(state, name, namelen-1); + if (tevent_req_nomem(state->name, req)) { + return tevent_req_post(req, ev); + } + } + + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx, */ + ev, /* ev */ + cli, /* cli */ + state->name, /* fname */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, /* impersonation_level */ + desired_access, /* desired_access */ + 0, /* file_attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + 0, /* create_options */ + NULL); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, get_fnum_from_path_opened_file, req); + return req; +} + +static void get_fnum_from_path_opened_file(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct get_fnum_from_path_state *state = tevent_req_data( + req, struct get_fnum_from_path_state); + NTSTATUS status; + + status = cli_smb2_create_fnum_recv( + subreq, &state->fnum, NULL, NULL, NULL); + TALLOC_FREE(subreq); + + if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + /* + * Naive option to match our SMB1 code. Assume the + * symlink path that tripped us up was the last + * component and try again. Eventually we will have to + * deal with the returned path unprocessed component. JRA. + */ + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx, */ + state->ev, /* ev */ + state->cli, /* cli */ + state->name, /* fname */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */ + state->desired_access, /* desired_access */ + 0, /* file_attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_OPEN_REPARSE_POINT, /* create_options */ + NULL); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback( + subreq, get_fnum_from_path_opened_reparse, req); + return; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { + subreq = cli_smb2_create_fnum_send( + state, /* mem_ctx, */ + state->ev, /* ev */ + state->cli, /* cli */ + state->name, /* fname */ + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, /* impersonation */ + state->desired_access, /* desired_access */ + 0, /* file_attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL); /* in_cblobs */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback( + subreq, get_fnum_from_path_opened_dir, req); + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static void get_fnum_from_path_opened_reparse(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct get_fnum_from_path_state *state = tevent_req_data( + req, struct get_fnum_from_path_state); + NTSTATUS status = cli_smb2_create_fnum_recv( + subreq, &state->fnum, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void get_fnum_from_path_opened_dir(struct tevent_req *subreq) +{ + /* Abstraction violation, but these two are just the same... */ + get_fnum_from_path_opened_reparse(subreq); +} + +static NTSTATUS get_fnum_from_path_recv( + struct tevent_req *req, uint16_t *pfnum) +{ + struct get_fnum_from_path_state *state = tevent_req_data( + req, struct get_fnum_from_path_state); + NTSTATUS status = NT_STATUS_OK; + + if (!tevent_req_is_nterror(req, &status)) { + *pfnum = state->fnum; + } + tevent_req_received(req); + return status; +} + +static NTSTATUS get_fnum_from_path(struct cli_state *cli, + const char *name, + uint32_t desired_access, + uint16_t *pfnum) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = get_fnum_from_path_send(frame, ev, cli, name, desired_access); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = get_fnum_from_path_recv(req, pfnum); + fail: + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a path info (ALTNAME level). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli, + const char *name, + fstring alt_name) +{ + NTSTATUS status; + DATA_BLOB outbuf = data_blob_null; + uint16_t fnum = 0xffff; + uint32_t altnamelen = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_smb2_query_info_fnum( + cli, + fnum, + 1, /* in_info_type */ + (SMB_FILE_ALTERNATE_NAME_INFORMATION - 1000), /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (outbuf.length < 4) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + altnamelen = IVAL(outbuf.data, 0); + if (altnamelen > outbuf.length - 4) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + if (altnamelen > 0) { + size_t ret = 0; + char *short_name = NULL; + ret = pull_string_talloc(frame, + outbuf.data, + FLAGS2_UNICODE_STRINGS, + &short_name, + outbuf.data + 4, + altnamelen, + STR_UNICODE); + if (ret == (size_t)-1) { + /* Bad conversion. */ + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + fstrcpy(alt_name, short_name); + } else { + alt_name[0] = '\0'; + } + + status = NT_STATUS_OK; + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to get pathname attributes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_getatr(struct cli_state *cli, + const char *name, + uint32_t *pattr, + off_t *size, + time_t *write_time) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + struct smb2_hnd *ph = NULL; + struct timespec write_time_ts; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + status = cli_qfileinfo_basic( + cli, + fnum, + pattr, + size, + NULL, /* create_time */ + NULL, /* access_time */ + &write_time_ts, + NULL, /* change_time */ + NULL); /* ino */ + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + if (write_time != NULL) { + *write_time = write_time_ts.tv_sec; + } + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query a pathname info (basic level). + Implement on top of cli_qfileinfo_basic(). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli, + const char *name, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, + uint32_t *pattr, + SMB_INO_T *ino) +{ + NTSTATUS status; + struct smb2_hnd *ph = NULL; + uint16_t fnum = 0xffff; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = map_fnum_to_smb2_handle(cli, + fnum, + &ph); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_qfileinfo_basic( + cli, + fnum, + pattr, + size, + create_time, + access_time, + write_time, + change_time, + ino); + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query pathname streams. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli, + const char *name, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), + level 22 (SMB2_FILE_STREAM_INFORMATION). */ + + status = cli_smb2_query_info_fnum( + cli, + fnum, + 1, /* in_info_type */ + (SMB_FILE_STREAM_INFORMATION - 1000), /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (!parse_streams_blob(mem_ctx, + outbuf.data, + outbuf.length, + pnum_streams, + pstreams)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set SMB_FILE_BASIC_INFORMATION on + a pathname. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli, + const char *name, + uint8_t in_info_type, + uint8_t in_file_info_class, + const DATA_BLOB *p_in_data) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_WRITE_ATTRIBUTES, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_smb2_set_info_fnum( + cli, + fnum, + in_info_type, + in_file_info_class, + p_in_data, /* in_input_buffer */ + 0); /* in_additional_info */ + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + + +/*************************************************************** + Wrapper that allows SMB2 to set pathname attributes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_setatr(struct cli_state *cli, + const char *name, + uint32_t attr, + time_t mtime) +{ + uint8_t inbuf_store[40]; + DATA_BLOB inbuf = data_blob_null; + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */ + + inbuf.data = inbuf_store; + inbuf.length = sizeof(inbuf_store); + data_blob_clear(&inbuf); + + /* + * SMB1 uses attr == 0 to clear all attributes + * on a file (end up with FILE_ATTRIBUTE_NORMAL), + * and attr == FILE_ATTRIBUTE_NORMAL to mean ignore + * request attribute change. + * + * SMB2 uses exactly the reverse. Unfortunately as the + * cli_setatr() ABI is exposed inside libsmbclient, + * we must make the SMB2 cli_smb2_setatr() call + * export the same ABI as the SMB1 cli_setatr() + * which calls it. This means reversing the sense + * of the requested attr argument if it's zero + * or FILE_ATTRIBUTE_NORMAL. + * + * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=12899 + */ + + if (attr == 0) { + attr = FILE_ATTRIBUTE_NORMAL; + } else if (attr == FILE_ATTRIBUTE_NORMAL) { + attr = 0; + } + + SIVAL(inbuf.data, 32, attr); + if (mtime != 0) { + put_long_date((char *)inbuf.data + 16,mtime); + } + /* Set all the other times to -1. */ + SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL); + SBVAL(inbuf.data, 8, 0xFFFFFFFFFFFFFFFFLL); + SBVAL(inbuf.data, 24, 0xFFFFFFFFFFFFFFFFLL); + + return cli_smb2_setpathinfo(cli, + name, + 1, /* in_info_type */ + /* in_file_info_class */ + SMB_FILE_BASIC_INFORMATION - 1000, + &inbuf); +} + + +/*************************************************************** + Wrapper that allows SMB2 to set file handle times. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_setattrE(struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time) +{ + uint8_t inbuf_store[40]; + DATA_BLOB inbuf = data_blob_null; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 4 (SMB_FILE_BASIC_INFORMATION - 1000). */ + + inbuf.data = inbuf_store; + inbuf.length = sizeof(inbuf_store); + data_blob_clear(&inbuf); + + SBVAL(inbuf.data, 0, 0xFFFFFFFFFFFFFFFFLL); + if (change_time != 0) { + put_long_date((char *)inbuf.data + 24, change_time); + } + if (access_time != 0) { + put_long_date((char *)inbuf.data + 8, access_time); + } + if (write_time != 0) { + put_long_date((char *)inbuf.data + 16, write_time); + } + + cli->raw_status = cli_smb2_set_info_fnum( + cli, + fnum, + 1, /* in_info_type */ + SMB_FILE_BASIC_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0); /* in_additional_info */ + + return cli->raw_status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query disk attributes (size). + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_dskattr(struct cli_state *cli, const char *path, + uint64_t *bsize, uint64_t *total, uint64_t *avail) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; + uint32_t sectors_per_unit = 0; + uint32_t bytes_per_sector = 0; + uint64_t total_size = 0; + uint64_t size_free = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* First open the top level directory. */ + status = cli_smb2_create_fnum(cli, + path, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL, + &fnum, + NULL, + NULL, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), + level 3 (SMB_FS_SIZE_INFORMATION). */ + + status = cli_smb2_query_info_fnum( + cli, + fnum, + 2, /* in_info_type */ + 3, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + if (outbuf.length != 24) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + total_size = BVAL(outbuf.data, 0); + size_free = BVAL(outbuf.data, 8); + sectors_per_unit = IVAL(outbuf.data, 16); + bytes_per_sector = IVAL(outbuf.data, 20); + + if (bsize) { + *bsize = (uint64_t)sectors_per_unit * (uint64_t)bytes_per_sector; + } + if (total) { + *total = total_size; + } + if (avail) { + *avail = size_free; + } + + status = NT_STATUS_OK; + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query file system sizes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli, + uint64_t *total_allocation_units, + uint64_t *caller_allocation_units, + uint64_t *actual_allocation_units, + uint64_t *sectors_per_allocation_unit, + uint64_t *bytes_per_sector) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* First open the top level directory. */ + status = + cli_smb2_create_fnum(cli, "", 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL, + &fnum, + NULL, + NULL, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), + level 7 (SMB_FS_FULL_SIZE_INFORMATION). */ + + status = cli_smb2_query_info_fnum( + cli, + fnum, + SMB2_0_INFO_FILESYSTEM, /* in_info_type */ + SMB_FS_FULL_SIZE_INFORMATION - 1000, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (outbuf.length < 32) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + *total_allocation_units = BIG_UINT(outbuf.data, 0); + *caller_allocation_units = BIG_UINT(outbuf.data, 8); + *actual_allocation_units = BIG_UINT(outbuf.data, 16); + *sectors_per_allocation_unit = (uint64_t)IVAL(outbuf.data, 24); + *bytes_per_sector = (uint64_t)IVAL(outbuf.data, 28); + +fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query file system attributes. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* First open the top level directory. */ + status = + cli_smb2_create_fnum(cli, "", 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL, + &fnum, + NULL, + NULL, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_smb2_query_info_fnum( + cli, + fnum, + 2, /* in_info_type */ + 5, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (outbuf.length < 12) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + *fs_attr = IVAL(outbuf.data, 0); + +fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to query file system volume info. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + char **_volume_name, + uint32_t *pserial_number, + time_t *pdate) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; + uint32_t nlen; + char *volume_name = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* First open the top level directory. */ + status = + cli_smb2_create_fnum(cli, "", 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* file attributes */ + FILE_SHARE_READ | FILE_SHARE_WRITE | + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_DIRECTORY_FILE, /* create_options */ + NULL, + &fnum, + NULL, + NULL, + NULL); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the returned handle with info_type SMB2_GETINFO_FS (2), + level 1 (SMB_FS_VOLUME_INFORMATION). */ + + status = cli_smb2_query_info_fnum( + cli, + fnum, + SMB2_0_INFO_FILESYSTEM, /* in_info_type */ + /* in_file_info_class */ + SMB_FS_VOLUME_INFORMATION - 1000, + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (outbuf.length < 24) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + if (pdate) { + struct timespec ts; + ts = interpret_long_date((char *)outbuf.data); + *pdate = ts.tv_sec; + } + if (pserial_number) { + *pserial_number = IVAL(outbuf.data,8); + } + nlen = IVAL(outbuf.data,12); + if (nlen + 18 < 18) { + /* Integer wrap. */ + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + /* + * The next check is safe as we know outbuf.length >= 24 + * from above. + */ + if (nlen > (outbuf.length - 18)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + pull_string_talloc(mem_ctx, + (const char *)outbuf.data, + 0, + &volume_name, + outbuf.data + 18, + nlen, + STR_UNICODE); + if (volume_name == NULL) { + status = map_nt_error_from_unix(errno); + goto fail; + } + + *_volume_name = volume_name; + +fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_mxac_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *fname; + struct smb2_create_blobs in_cblobs; + uint16_t fnum; + NTSTATUS status; + uint32_t mxac; +}; + +static void cli_smb2_mxac_opened(struct tevent_req *subreq); +static void cli_smb2_mxac_closed(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_mxac_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_mxac_state); + if (req == NULL) { + return NULL; + } + *state = (struct cli_smb2_mxac_state) { + .ev = ev, + .cli = cli, + .fname = fname, + }; + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + status = smb2_create_blob_add(state, + &state->in_cblobs, + SMB2_CREATE_TAG_MXAC, + data_blob(NULL, 0)); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = cli_smb2_create_fnum_send( + state, + state->ev, + state->cli, + state->fname, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_READ_ATTRIBUTES, + 0, /* file attributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, + 0, /* create_options */ + &state->in_cblobs); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_mxac_opened, req); + return req; +} + +static void cli_smb2_mxac_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_mxac_state *state = tevent_req_data( + req, struct cli_smb2_mxac_state); + struct smb2_create_blobs out_cblobs = {0}; + struct smb2_create_blob *mxac_blob = NULL; + NTSTATUS status; + + status = cli_smb2_create_fnum_recv( + subreq, &state->fnum, NULL, state, &out_cblobs); + TALLOC_FREE(subreq); + + if (tevent_req_nterror(req, status)) { + return; + } + + mxac_blob = smb2_create_blob_find(&out_cblobs, SMB2_CREATE_TAG_MXAC); + if (mxac_blob == NULL) { + state->status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto close; + } + if (mxac_blob->data.length != 8) { + state->status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto close; + } + + state->status = NT_STATUS(IVAL(mxac_blob->data.data, 0)); + state->mxac = IVAL(mxac_blob->data.data, 4); + +close: + subreq = cli_smb2_close_fnum_send( + state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_mxac_closed, req); + + return; +} + +static void cli_smb2_mxac_closed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb2_close_fnum_recv(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, uint32_t *mxac) +{ + struct cli_smb2_mxac_state *state = tevent_req_data( + req, struct cli_smb2_mxac_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (!NT_STATUS_IS_OK(state->status)) { + return state->status; + } + + *mxac = state->mxac; + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_query_mxac(struct cli_state *cli, + const char *fname, + uint32_t *_mxac) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_INTERNAL_ERROR; + bool ok; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_query_mxac_send(frame, ev, cli, fname); + if (req == NULL) { + goto fail; + } + ok = tevent_req_poll_ntstatus(req, ev, &status); + if (!ok) { + goto fail; + } + status = cli_smb2_query_mxac_recv(req, _mxac); + +fail: + cli->raw_status = status; + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_rename_fnum_state { + DATA_BLOB inbuf; +}; + +static void cli_smb2_rename_fnum_done(struct tevent_req *subreq); + +static struct tevent_req *cli_smb2_rename_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + const char *fname_dst, + bool replace) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_rename_fnum_state *state = NULL; + size_t namelen = strlen(fname_dst); + smb_ucs2_t *converted_str = NULL; + size_t converted_size_bytes = 0; + size_t inbuf_size; + bool ok; + + req = tevent_req_create( + mem_ctx, &state, struct cli_smb2_rename_fnum_state); + if (req == NULL) { + return NULL; + } + + /* + * SMB2 is pickier about pathnames. Ensure it doesn't start in + * a '\' + */ + if (*fname_dst == '\\') { + fname_dst++; + } + + /* + * SMB2 is pickier about pathnames. Ensure it doesn't end in a + * '\' + */ + if (namelen > 0 && fname_dst[namelen-1] == '\\') { + fname_dst = talloc_strndup(state, fname_dst, namelen-1); + if (tevent_req_nomem(fname_dst, req)) { + return tevent_req_post(req, ev); + } + } + + ok = push_ucs2_talloc( + state, &converted_str, fname_dst, &converted_size_bytes); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + /* + * W2K8 insists the dest name is not null terminated. Remove + * the last 2 zero bytes and reduce the name length. + */ + if (converted_size_bytes < 2) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + converted_size_bytes -= 2; + + inbuf_size = 20 + converted_size_bytes; + if (inbuf_size < 20) { + /* Integer wrap check. */ + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + /* + * The Windows 10 SMB2 server has a minimum length + * for a SMB2_FILE_RENAME_INFORMATION buffer of + * 24 bytes. It returns NT_STATUS_INFO_LENGTH_MISMATCH + * if the length is less. This isn't an alignment + * issue as Windows client happily 2-byte align + * for larget target name sizes. Also the Windows 10 + * SMB1 server doesn't have this restriction. + * + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14403 + */ + inbuf_size = MAX(inbuf_size, 24); + + state->inbuf = data_blob_talloc_zero(state, inbuf_size); + if (tevent_req_nomem(state->inbuf.data, req)) { + return tevent_req_post(req, ev); + } + + if (replace) { + SCVAL(state->inbuf.data, 0, 1); + } + + SIVAL(state->inbuf.data, 16, converted_size_bytes); + memcpy(state->inbuf.data + 20, converted_str, converted_size_bytes); + + TALLOC_FREE(converted_str); + + /* setinfo on the returned handle with info_type SMB2_GETINFO_FILE (1), + level SMB2_FILE_RENAME_INFORMATION (SMB_FILE_RENAME_INFORMATION - 1000) */ + + subreq = cli_smb2_set_info_fnum_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + fnum, /* fnum */ + 1, /* in_info_type */ + SMB_FILE_RENAME_INFORMATION - 1000, /* in_file_info_class */ + &state->inbuf, /* in_input_buffer */ + 0); /* in_additional_info */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_rename_fnum_done, req); + return req; +} + +static void cli_smb2_rename_fnum_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_smb2_rename_fnum_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/*************************************************************** + Wrapper that allows SMB2 to rename a file. +***************************************************************/ + +struct cli_smb2_rename_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *fname_dst; + bool replace; + uint16_t fnum; + + NTSTATUS rename_status; +}; + +static void cli_smb2_rename_opened(struct tevent_req *subreq); +static void cli_smb2_rename_renamed(struct tevent_req *subreq); +static void cli_smb2_rename_closed(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_rename_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_rename_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create( + mem_ctx, &state, struct cli_smb2_rename_state); + if (req == NULL) { + return NULL; + } + + /* + * Strip a MSDFS path from fname_dst if we were given one. + */ + status = cli_dfs_target_check(state, + cli, + fname_src, + fname_dst, + &fname_dst); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->ev = ev; + state->cli = cli; + state->fname_dst = fname_dst; + state->replace = replace; + + subreq = get_fnum_from_path_send( + state, ev, cli, fname_src, DELETE_ACCESS); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_rename_opened, req); + return req; +} + +static void cli_smb2_rename_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_rename_state *state = tevent_req_data( + req, struct cli_smb2_rename_state); + NTSTATUS status; + + status = get_fnum_from_path_recv(subreq, &state->fnum); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_smb2_rename_fnum_send( + state, + state->ev, + state->cli, + state->fnum, + state->fname_dst, + state->replace); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rename_renamed, req); +} + +static void cli_smb2_rename_renamed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_rename_state *state = tevent_req_data( + req, struct cli_smb2_rename_state); + + state->rename_status = cli_smb2_rename_fnum_recv(subreq); + TALLOC_FREE(subreq); + + subreq = cli_smb2_close_fnum_send( + state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_rename_closed, req); +} + +static void cli_smb2_rename_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_close_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_smb2_rename_recv(struct tevent_req *req) +{ + struct cli_smb2_rename_state *state = tevent_req_data( + req, struct cli_smb2_rename_state); + NTSTATUS status = NT_STATUS_OK; + + if (!tevent_req_is_nterror(req, &status)) { + status = state->rename_status; + } + tevent_req_received(req); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set an EA on a fnum. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli, + uint16_t fnum, + const char *ea_name, + const char *ea_val, + size_t ea_len) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + size_t bloblen = 0; + char *ea_name_ascii = NULL; + size_t namelen = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* Marshall the SMB2 EA data. */ + if (ea_len > 0xFFFF) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (!push_ascii_talloc(frame, + &ea_name_ascii, + ea_name, + &namelen)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (namelen < 2 || namelen > 0xFF) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + bloblen = 8 + ea_len + namelen; + /* Round up to a 4 byte boundary. */ + bloblen = ((bloblen + 3)&~3); + + inbuf = data_blob_talloc_zero(frame, bloblen); + if (inbuf.data == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + /* namelen doesn't include the NULL byte. */ + SCVAL(inbuf.data, 5, namelen - 1); + SSVAL(inbuf.data, 6, ea_len); + memcpy(inbuf.data + 8, ea_name_ascii, namelen); + memcpy(inbuf.data + 8 + namelen, ea_val, ea_len); + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */ + + status = cli_smb2_set_info_fnum( + cli, + fnum, + 1, /* in_info_type */ + SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0); /* in_additional_info */ + + fail: + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set an EA on a pathname. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli, + const char *name, + const char *ea_name, + const char *ea_val, + size_t ea_len) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_WRITE_EA, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = cli_set_ea_fnum(cli, + fnum, + ea_name, + ea_val, + ea_len); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to get an EA list on a pathname. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, + const char *name, + TALLOC_CTX *ctx, + size_t *pnum_eas, + struct ea_struct **pea_array) +{ + NTSTATUS status; + uint16_t fnum = 0xffff; + DATA_BLOB outbuf = data_blob_null; + struct ea_list *ea_list = NULL; + struct ea_list *eal = NULL; + size_t ea_count = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + *pnum_eas = 0; + *pea_array = NULL; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + status = get_fnum_from_path(cli, + name, + FILE_READ_EA, + &fnum); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* getinfo on the handle with info_type SMB2_GETINFO_FILE (1), + level 15 (SMB_FILE_FULL_EA_INFORMATION - 1000). */ + + status = cli_smb2_query_info_fnum( + cli, + fnum, + 1, /* in_info_type */ + SMB_FILE_FULL_EA_INFORMATION - 1000, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* Parse the reply. */ + ea_list = read_nttrans_ea_list(ctx, + (const char *)outbuf.data, + outbuf.length); + if (ea_list == NULL) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto fail; + } + + /* Convert to an array. */ + for (eal = ea_list; eal; eal = eal->next) { + ea_count++; + } + + if (ea_count) { + *pea_array = talloc_array(ctx, struct ea_struct, ea_count); + if (*pea_array == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + ea_count = 0; + for (eal = ea_list; eal; eal = eal->next) { + (*pea_array)[ea_count++] = eal->ea; + } + *pnum_eas = ea_count; + } + + fail: + + if (fnum != 0xffff) { + cli_smb2_close_fnum(cli, fnum); + } + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to get user quota. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + DATA_BLOB info_blob = data_blob_null; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + unsigned sid_len; + unsigned int offset; + struct smb2_query_quota_info query = {0}; + struct file_get_quota_info info = {0}; + enum ndr_err_code err; + struct ndr_push *ndr_push = NULL; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + sid_len = ndr_size_dom_sid(&pqt->sid, 0); + + query.return_single = 1; + + info.next_entry_offset = 0; + info.sid_length = sid_len; + info.sid = pqt->sid; + + err = ndr_push_struct_blob( + &info_blob, + frame, + &info, + (ndr_push_flags_fn_t)ndr_push_file_get_quota_info); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + + query.sid_list_length = info_blob.length; + ndr_push = ndr_push_init_ctx(frame); + if (!ndr_push) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + err = ndr_push_smb2_query_quota_info(ndr_push, + NDR_SCALARS | NDR_BUFFERS, + &query); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + + err = ndr_push_array_uint8(ndr_push, NDR_SCALARS, info_blob.data, + info_blob.length); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INTERNAL_ERROR; + goto fail; + } + inbuf.data = ndr_push->data; + inbuf.length = ndr_push->offset; + + status = cli_smb2_query_info_fnum( + cli, + quota_fnum, + 4, /* in_info_type */ + 0, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (!parse_user_quota_record(outbuf.data, outbuf.length, &offset, + pqt)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + DEBUG(0, ("Got invalid FILE_QUOTA_INFORMATION in reply.\n")); + } + +fail: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to list user quota. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int quota_fnum, + SMB_NTQUOTA_LIST **pqt_list, + bool first) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + struct smb2_query_quota_info info = {0}; + enum ndr_err_code err; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + info.restart_scan = first ? 1 : 0; + + err = ndr_push_struct_blob( + &inbuf, + frame, + &info, + (ndr_push_flags_fn_t)ndr_push_smb2_query_quota_info); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INTERNAL_ERROR; + goto cleanup; + } + + status = cli_smb2_query_info_fnum( + cli, + quota_fnum, + 4, /* in_info_type */ + 0, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + &inbuf, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + + /* + * safeguard against panic from calling parse_user_quota_list with + * NULL buffer + */ + if (NT_STATUS_IS_OK(status) && outbuf.length == 0) { + status = NT_STATUS_NO_MORE_ENTRIES; + } + + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = parse_user_quota_list(outbuf.data, outbuf.length, mem_ctx, + pqt_list); + +cleanup: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to get file system quota. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + NTSTATUS status; + DATA_BLOB outbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = cli_smb2_query_info_fnum( + cli, + quota_fnum, + 2, /* in_info_type */ + SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0, /* in_flags */ + frame, + &outbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = parse_fs_quota_buffer(outbuf.data, outbuf.length, pqt); + +cleanup: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to set user quota. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_LIST *qtl) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = build_user_quota_buffer(qtl, 0, talloc_tos(), &inbuf, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = cli_smb2_set_info_fnum( + cli, + quota_fnum, + 4, /* in_info_type */ + 0, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0); /* in_additional_info */ +cleanup: + + cli->raw_status = status; + + TALLOC_FREE(frame); + + return status; +} + +NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + NTSTATUS status; + DATA_BLOB inbuf = data_blob_null; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = build_fs_quota_buffer(talloc_tos(), pqt, &inbuf, 0); + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = cli_smb2_set_info_fnum( + cli, + quota_fnum, + 2, /* in_info_type */ + SMB_FS_QUOTA_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0); /* in_additional_info */ +cleanup: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_read_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint64_t start_offset; + uint32_t size; + uint32_t received; + uint8_t *buf; +}; + +static void cli_smb2_read_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq; + struct cli_smb2_read_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_read_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->start_offset = (uint64_t)offset; + state->size = (uint32_t)size; + state->received = 0; + state->buf = NULL; + + status = map_fnum_to_smb2_handle(cli, + fnum, + &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_read_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->size, + state->start_offset, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* minimum_count */ + 0); /* remaining_bytes */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_read_done, req); + return req; +} + +static void cli_smb2_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_read_state *state = tevent_req_data( + req, struct cli_smb2_read_state); + NTSTATUS status; + + status = smb2cli_read_recv(subreq, state, + &state->buf, &state->received); + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->received > state->size) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_smb2_read_recv(struct tevent_req *req, + ssize_t *received, + uint8_t **rcvbuf) +{ + NTSTATUS status; + struct cli_smb2_read_state *state = tevent_req_data( + req, struct cli_smb2_read_state); + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + return status; + } + /* + * As in cli_read_andx_recv() rcvbuf is talloced from the request, so + * better make sure that you copy it away before you talloc_free(req). + * "rcvbuf" is NOT a talloc_ctx of its own, so do not talloc_move it! + */ + *received = (ssize_t)state->received; + *rcvbuf = state->buf; + state->cli->raw_status = NT_STATUS_OK; + return NT_STATUS_OK; +} + +struct cli_smb2_write_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint32_t flags; + const uint8_t *buf; + uint64_t offset; + uint32_t size; + uint32_t written; +}; + +static void cli_smb2_write_written(struct tevent_req *req); + +struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq = NULL; + struct cli_smb2_write_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_write_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */ + state->flags = (uint32_t)mode; + state->buf = buf; + state->offset = (uint64_t)offset; + state->size = (uint32_t)size; + state->written = 0; + + status = map_fnum_to_smb2_handle(cli, + fnum, + &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_write_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->size, + state->offset, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* remaining_bytes */ + state->flags, /* flags */ + state->buf); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_write_written, req); + return req; +} + +static void cli_smb2_write_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_write_state *state = tevent_req_data( + req, struct cli_smb2_write_state); + NTSTATUS status; + uint32_t written; + + status = smb2cli_write_recv(subreq, &written); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->written = written; + + tevent_req_done(req); +} + +NTSTATUS cli_smb2_write_recv(struct tevent_req *req, + size_t *pwritten) +{ + struct cli_smb2_write_state *state = tevent_req_data( + req, struct cli_smb2_write_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + tevent_req_received(req); + return status; + } + + if (pwritten != NULL) { + *pwritten = (size_t)state->written; + } + state->cli->raw_status = NT_STATUS_OK; + tevent_req_received(req); + return NT_STATUS_OK; +} + +/*************************************************************** + Wrapper that allows SMB2 async write using an fnum. + This is mostly cut-and-paste from Volker's code inside + source3/libsmb/clireadwrite.c, adapted for SMB2. + + Done this way so I can reuse all the logic inside cli_push() + for free :-). +***************************************************************/ + +struct cli_smb2_writeall_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *ph; + uint32_t flags; + const uint8_t *buf; + uint64_t offset; + uint32_t size; + uint32_t written; +}; + +static void cli_smb2_writeall_written(struct tevent_req *req); + +struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size) +{ + NTSTATUS status; + struct tevent_req *req, *subreq = NULL; + struct cli_smb2_writeall_state *state = NULL; + uint32_t to_write; + uint32_t max_size; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_writeall_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + /* Both SMB1 and SMB2 use 1 in the following meaning write-through. */ + state->flags = (uint32_t)mode; + state->buf = buf; + state->offset = (uint64_t)offset; + state->size = (uint32_t)size; + state->written = 0; + + status = map_fnum_to_smb2_handle(cli, + fnum, + &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + to_write = state->size; + max_size = smb2cli_conn_max_write_size(state->cli->conn); + to_write = MIN(max_size, to_write); + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (ok) { + to_write = MIN(max_size, to_write); + } + + subreq = smb2cli_write_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + to_write, + state->offset, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* remaining_bytes */ + state->flags, /* flags */ + state->buf + state->written); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_writeall_written, req); + return req; +} + +static void cli_smb2_writeall_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_writeall_state *state = tevent_req_data( + req, struct cli_smb2_writeall_state); + NTSTATUS status; + uint32_t written, to_write; + uint32_t max_size; + bool ok; + + status = smb2cli_write_recv(subreq, &written); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->written += written; + + if (state->written > state->size) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + to_write = state->size - state->written; + + if (to_write == 0) { + tevent_req_done(req); + return; + } + + max_size = smb2cli_conn_max_write_size(state->cli->conn); + to_write = MIN(max_size, to_write); + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (ok) { + to_write = MIN(max_size, to_write); + } + + subreq = smb2cli_write_send(state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + to_write, + state->offset + state->written, + state->ph->fid_persistent, + state->ph->fid_volatile, + 0, /* remaining_bytes */ + state->flags, /* flags */ + state->buf + state->written); + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_writeall_written, req); +} + +NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req, + size_t *pwritten) +{ + struct cli_smb2_writeall_state *state = tevent_req_data( + req, struct cli_smb2_writeall_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + return status; + } + if (pwritten != NULL) { + *pwritten = (size_t)state->written; + } + state->cli->raw_status = NT_STATUS_OK; + return NT_STATUS_OK; +} + +struct cli_smb2_splice_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb2_hnd *src_ph; + struct smb2_hnd *dst_ph; + int (*splice_cb)(off_t n, void *priv); + void *priv; + off_t written; + off_t size; + off_t src_offset; + off_t dst_offset; + bool resized; + struct req_resume_key_rsp resume_rsp; + struct srv_copychunk_copy cc_copy; +}; + +static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state, + struct tevent_req *req); + +static void cli_splice_copychunk_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_splice_state *state = + tevent_req_data(req, + struct cli_smb2_splice_state); + struct smbXcli_conn *conn = state->cli->conn; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + struct srv_copychunk_rsp cc_copy_rsp; + enum ndr_err_code ndr_ret; + NTSTATUS status; + + status = smb2cli_ioctl_recv(subreq, state, + &out_input_buffer, + &out_output_buffer); + TALLOC_FREE(subreq); + if ((!NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER) || + state->resized) && tevent_req_nterror(req, status)) { + return; + } + + ndr_ret = ndr_pull_struct_blob(&out_output_buffer, state, &cc_copy_rsp, + (ndr_pull_flags_fn_t)ndr_pull_srv_copychunk_rsp); + if (ndr_ret != NDR_ERR_SUCCESS) { + DEBUG(0, ("failed to unmarshall copy chunk rsp\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_PARAMETER)) { + uint32_t max_chunks = MIN(cc_copy_rsp.chunks_written, + cc_copy_rsp.total_bytes_written / cc_copy_rsp.chunk_bytes_written); + if ((cc_copy_rsp.chunk_bytes_written > smb2cli_conn_cc_chunk_len(conn) || + max_chunks > smb2cli_conn_cc_max_chunks(conn)) && + tevent_req_nterror(req, status)) { + return; + } + + state->resized = true; + smb2cli_conn_set_cc_chunk_len(conn, cc_copy_rsp.chunk_bytes_written); + smb2cli_conn_set_cc_max_chunks(conn, max_chunks); + } else { + if ((state->src_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) || + (state->dst_offset > INT64_MAX - cc_copy_rsp.total_bytes_written) || + (state->written > INT64_MAX - cc_copy_rsp.total_bytes_written)) { + tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE); + return; + } + state->src_offset += cc_copy_rsp.total_bytes_written; + state->dst_offset += cc_copy_rsp.total_bytes_written; + state->written += cc_copy_rsp.total_bytes_written; + if (!state->splice_cb(state->written, state->priv)) { + tevent_req_nterror(req, NT_STATUS_CANCELLED); + return; + } + } + + cli_splice_copychunk_send(state, req); +} + +static void cli_splice_copychunk_send(struct cli_smb2_splice_state *state, + struct tevent_req *req) +{ + struct tevent_req *subreq; + enum ndr_err_code ndr_ret; + struct smbXcli_conn *conn = state->cli->conn; + struct srv_copychunk_copy *cc_copy = &state->cc_copy; + off_t src_offset = state->src_offset; + off_t dst_offset = state->dst_offset; + uint32_t req_len = MIN(smb2cli_conn_cc_chunk_len(conn) * smb2cli_conn_cc_max_chunks(conn), + state->size - state->written); + DATA_BLOB in_input_buffer = data_blob_null; + DATA_BLOB in_output_buffer = data_blob_null; + + if (state->size - state->written == 0) { + tevent_req_done(req); + return; + } + + cc_copy->chunk_count = 0; + while (req_len) { + cc_copy->chunks[cc_copy->chunk_count].source_off = src_offset; + cc_copy->chunks[cc_copy->chunk_count].target_off = dst_offset; + cc_copy->chunks[cc_copy->chunk_count].length = MIN(req_len, + smb2cli_conn_cc_chunk_len(conn)); + if (req_len < cc_copy->chunks[cc_copy->chunk_count].length) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + req_len -= cc_copy->chunks[cc_copy->chunk_count].length; + if ((src_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length) || + (dst_offset > INT64_MAX - cc_copy->chunks[cc_copy->chunk_count].length)) { + tevent_req_nterror(req, NT_STATUS_FILE_TOO_LARGE); + return; + } + src_offset += cc_copy->chunks[cc_copy->chunk_count].length; + dst_offset += cc_copy->chunks[cc_copy->chunk_count].length; + cc_copy->chunk_count++; + } + + ndr_ret = ndr_push_struct_blob(&in_input_buffer, state, cc_copy, + (ndr_push_flags_fn_t)ndr_push_srv_copychunk_copy); + if (ndr_ret != NDR_ERR_SUCCESS) { + DEBUG(0, ("failed to marshall copy chunk req\n")); + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + subreq = smb2cli_ioctl_send(state, state->ev, state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->dst_ph->fid_persistent, /* in_fid_persistent */ + state->dst_ph->fid_volatile, /* in_fid_volatile */ + FSCTL_SRV_COPYCHUNK_WRITE, + 0, /* in_max_input_length */ + &in_input_buffer, + 12, /* in_max_output_length */ + &in_output_buffer, + SMB2_IOCTL_FLAG_IS_FSCTL); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_splice_copychunk_done, + req); +} + +static void cli_splice_key_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_splice_state *state = + tevent_req_data(req, + struct cli_smb2_splice_state); + enum ndr_err_code ndr_ret; + NTSTATUS status; + + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + + status = smb2cli_ioctl_recv(subreq, state, + &out_input_buffer, + &out_output_buffer); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + ndr_ret = ndr_pull_struct_blob(&out_output_buffer, + state, &state->resume_rsp, + (ndr_pull_flags_fn_t)ndr_pull_req_resume_key_rsp); + if (ndr_ret != NDR_ERR_SUCCESS) { + DEBUG(0, ("failed to unmarshall resume key rsp\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + memcpy(&state->cc_copy.source_key, + &state->resume_rsp.resume_key, + sizeof state->resume_rsp.resume_key); + + cli_splice_copychunk_send(state, req); +} + +struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t src_fnum, uint16_t dst_fnum, + off_t size, off_t src_offset, off_t dst_offset, + int (*splice_cb)(off_t n, void *priv), + void *priv) +{ + struct tevent_req *req; + struct tevent_req *subreq; + struct cli_smb2_splice_state *state; + NTSTATUS status; + DATA_BLOB in_input_buffer = data_blob_null; + DATA_BLOB in_output_buffer = data_blob_null; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb2_splice_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->ev = ev; + state->splice_cb = splice_cb; + state->priv = priv; + state->size = size; + state->written = 0; + state->src_offset = src_offset; + state->dst_offset = dst_offset; + state->cc_copy.chunks = talloc_array(state, + struct srv_copychunk, + smb2cli_conn_cc_max_chunks(cli->conn)); + if (state->cc_copy.chunks == NULL) { + return NULL; + } + + status = map_fnum_to_smb2_handle(cli, src_fnum, &state->src_ph); + if (tevent_req_nterror(req, status)) + return tevent_req_post(req, ev); + + status = map_fnum_to_smb2_handle(cli, dst_fnum, &state->dst_ph); + if (tevent_req_nterror(req, status)) + return tevent_req_post(req, ev); + + subreq = smb2cli_ioctl_send(state, ev, cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + state->src_ph->fid_persistent, /* in_fid_persistent */ + state->src_ph->fid_volatile, /* in_fid_volatile */ + FSCTL_SRV_REQUEST_RESUME_KEY, + 0, /* in_max_input_length */ + &in_input_buffer, + 32, /* in_max_output_length */ + &in_output_buffer, + SMB2_IOCTL_FLAG_IS_FSCTL); + if (tevent_req_nomem(subreq, req)) { + return NULL; + } + tevent_req_set_callback(subreq, + cli_splice_key_done, + req); + + return req; +} + +NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written) +{ + struct cli_smb2_splice_state *state = tevent_req_data( + req, struct cli_smb2_splice_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->cli->raw_status = status; + tevent_req_received(req); + return status; + } + if (written != NULL) { + *written = state->written; + } + state->cli->raw_status = NT_STATUS_OK; + tevent_req_received(req); + return NT_STATUS_OK; +} + +/*************************************************************** + SMB2 enum shadow copy data. +***************************************************************/ + +struct cli_smb2_shadow_copy_data_fnum_state { + struct cli_state *cli; + uint16_t fnum; + struct smb2_hnd *ph; + DATA_BLOB out_input_buffer; + DATA_BLOB out_output_buffer; +}; + +static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq); + +static struct tevent_req *cli_smb2_shadow_copy_data_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool get_names) +{ + struct tevent_req *req, *subreq; + struct cli_smb2_shadow_copy_data_fnum_state *state; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_shadow_copy_data_fnum_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->cli = cli; + state->fnum = fnum; + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + /* + * TODO. Under SMB2 we should send a zero max_output_length + * ioctl to get the required size, then send another ioctl + * to get the data, but the current SMB1 implementation just + * does one roundtrip with a 64K buffer size. Do the same + * for now. JRA. + */ + + subreq = smb2cli_ioctl_send(state, ev, state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->ph->fid_persistent, /* in_fid_persistent */ + state->ph->fid_volatile, /* in_fid_volatile */ + FSCTL_GET_SHADOW_COPY_DATA, + 0, /* in_max_input_length */ + NULL, /* in_input_buffer */ + get_names ? + CLI_BUFFER_SIZE : 16, /* in_max_output_length */ + NULL, /* in_output_buffer */ + SMB2_IOCTL_FLAG_IS_FSCTL); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_shadow_copy_data_fnum_done, + req); + + return req; +} + +static void cli_smb2_shadow_copy_data_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data( + req, struct cli_smb2_shadow_copy_data_fnum_state); + NTSTATUS status; + + status = smb2cli_ioctl_recv(subreq, state, + &state->out_input_buffer, + &state->out_output_buffer); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_smb2_shadow_copy_data_fnum_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + bool get_names, + char ***pnames, + int *pnum_names) +{ + struct cli_smb2_shadow_copy_data_fnum_state *state = tevent_req_data( + req, struct cli_smb2_shadow_copy_data_fnum_state); + char **names = NULL; + uint32_t num_names = 0; + uint32_t num_names_returned = 0; + uint32_t dlength = 0; + uint32_t i; + uint8_t *endp = NULL; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (state->out_output_buffer.length < 16) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + num_names = IVAL(state->out_output_buffer.data, 0); + num_names_returned = IVAL(state->out_output_buffer.data, 4); + dlength = IVAL(state->out_output_buffer.data, 8); + + if (num_names > 0x7FFFFFFF) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (get_names == false) { + *pnum_names = (int)num_names; + return NT_STATUS_OK; + } + if (num_names != num_names_returned) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (dlength + 12 < 12) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + /* + * NB. The below is an allowable return if there are + * more snapshots than the buffer size we told the + * server we can receive. We currently don't support + * this. + */ + if (dlength + 12 > state->out_output_buffer.length) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (state->out_output_buffer.length + + (2 * sizeof(SHADOW_COPY_LABEL)) < + state->out_output_buffer.length) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + names = talloc_array(mem_ctx, char *, num_names_returned); + if (names == NULL) { + return NT_STATUS_NO_MEMORY; + } + + endp = state->out_output_buffer.data + + state->out_output_buffer.length; + + for (i=0; i<num_names_returned; i++) { + bool ret; + uint8_t *src; + size_t converted_size; + + src = state->out_output_buffer.data + 12 + + (i * 2 * sizeof(SHADOW_COPY_LABEL)); + + if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + ret = convert_string_talloc( + names, CH_UTF16LE, CH_UNIX, + src, 2 * sizeof(SHADOW_COPY_LABEL), + &names[i], &converted_size); + if (!ret) { + TALLOC_FREE(names); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + } + *pnum_names = num_names; + *pnames = names; + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx, + struct cli_state *cli, + uint16_t fnum, + bool get_names, + char ***pnames, + int *pnum_names) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_shadow_copy_data_fnum_send(frame, + ev, + cli, + fnum, + get_names); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_shadow_copy_data_fnum_recv(req, + mem_ctx, + get_names, + pnames, + pnum_names); + fail: + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +/*************************************************************** + Wrapper that allows SMB2 to truncate a file. + Synchronous only. +***************************************************************/ + +NTSTATUS cli_smb2_ftruncate(struct cli_state *cli, + uint16_t fnum, + uint64_t newsize) +{ + NTSTATUS status; + uint8_t buf[8] = {0}; + DATA_BLOB inbuf = { .data = buf, .length = sizeof(buf) }; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + SBVAL(buf, 0, newsize); + + /* setinfo on the handle with info_type SMB2_SETINFO_FILE (1), + level 20 (SMB_FILE_END_OF_FILE_INFORMATION - 1000). */ + + status = cli_smb2_set_info_fnum( + cli, + fnum, + 1, /* in_info_type */ + SMB_FILE_END_OF_FILE_INFORMATION-1000, /* in_file_info_class */ + &inbuf, /* in_input_buffer */ + 0); + + fail: + + cli->raw_status = status; + + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_notify_state { + struct tevent_req *subreq; + struct notify_change *changes; + size_t num_changes; +}; + +static void cli_smb2_notify_done(struct tevent_req *subreq); +static bool cli_smb2_notify_cancel(struct tevent_req *req); + +struct tevent_req *cli_smb2_notify_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint32_t buffer_size, + uint32_t completion_filter, + bool recursive) +{ + struct tevent_req *req = NULL; + struct cli_smb2_notify_state *state = NULL; + struct smb2_hnd *ph = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_notify_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + status = map_fnum_to_smb2_handle(cli, fnum, &ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->subreq = smb2cli_notify_send( + state, + ev, + cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + buffer_size, + ph->fid_persistent, + ph->fid_volatile, + completion_filter, + recursive); + if (tevent_req_nomem(state->subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->subreq, cli_smb2_notify_done, req); + tevent_req_set_cancel_fn(req, cli_smb2_notify_cancel); + return req; +} + +static bool cli_smb2_notify_cancel(struct tevent_req *req) +{ + struct cli_smb2_notify_state *state = tevent_req_data( + req, struct cli_smb2_notify_state); + bool ok; + + ok = tevent_req_cancel(state->subreq); + return ok; +} + +static void cli_smb2_notify_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_notify_state *state = tevent_req_data( + req, struct cli_smb2_notify_state); + uint8_t *base; + uint32_t len; + uint32_t ofs; + NTSTATUS status; + + status = smb2cli_notify_recv(subreq, state, &base, &len); + TALLOC_FREE(subreq); + + if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + tevent_req_done(req); + return; + } + if (tevent_req_nterror(req, status)) { + return; + } + + ofs = 0; + + while (len - ofs >= 12) { + struct notify_change *tmp; + struct notify_change *c; + uint32_t next_ofs = IVAL(base, ofs); + uint32_t file_name_length = IVAL(base, ofs+8); + size_t namelen; + bool ok; + + tmp = talloc_realloc( + state, + state->changes, + struct notify_change, + state->num_changes + 1); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->changes = tmp; + c = &state->changes[state->num_changes]; + state->num_changes += 1; + + if (smb_buffer_oob(len, ofs, next_ofs) || + smb_buffer_oob(len, ofs+12, file_name_length)) { + tevent_req_nterror( + req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + c->action = IVAL(base, ofs+4); + + ok = convert_string_talloc( + state->changes, + CH_UTF16LE, + CH_UNIX, + base + ofs + 12, + file_name_length, + &c->name, + &namelen); + if (!ok) { + tevent_req_nterror( + req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (next_ofs == 0) { + break; + } + ofs += next_ofs; + } + + tevent_req_done(req); +} + +NTSTATUS cli_smb2_notify_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct notify_change **pchanges, + uint32_t *pnum_changes) +{ + struct cli_smb2_notify_state *state = tevent_req_data( + req, struct cli_smb2_notify_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pchanges = talloc_move(mem_ctx, &state->changes); + *pnum_changes = state->num_changes; + return NT_STATUS_OK; +} + +NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum, + uint32_t buffer_size, uint32_t completion_filter, + bool recursive, TALLOC_CTX *mem_ctx, + struct notify_change **pchanges, + uint32_t *pnum_changes) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_smb2_notify_send( + frame, + ev, + cli, + fnum, + buffer_size, + completion_filter, + recursive); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb2_notify_recv(req, mem_ctx, pchanges, pnum_changes); +fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_smb2_set_reparse_point_fnum_state { + struct cli_state *cli; + uint16_t fnum; + struct smb2_hnd *ph; + DATA_BLOB input_buffer; +}; + +static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_set_reparse_point_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + DATA_BLOB in_buf) +{ + struct tevent_req *req, *subreq; + struct cli_smb2_set_reparse_point_fnum_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_set_reparse_point_fnum_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->cli = cli; + state->fnum = fnum; + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->input_buffer = data_blob_talloc(state, + in_buf.data, + in_buf.length); + if (state->input_buffer.data == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); + } + + subreq = smb2cli_ioctl_send(state, ev, state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->ph->fid_persistent, /* in_fid_persistent */ + state->ph->fid_volatile, /* in_fid_volatile */ + FSCTL_SET_REPARSE_POINT, + 0, /* in_max_input_length */ + &state->input_buffer , + 0, + NULL, + SMB2_IOCTL_FLAG_IS_FSCTL); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_set_reparse_point_fnum_done, + req); + + return req; +} + +static void cli_smb2_set_reparse_point_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_set_reparse_point_fnum_state *state = tevent_req_data( + req, struct cli_smb2_set_reparse_point_fnum_state); + NTSTATUS status; + + status = smb2cli_ioctl_recv(subreq, state, + NULL, + NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_smb2_set_reparse_point_fnum_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_smb2_get_reparse_point_fnum_state { + struct cli_state *cli; + uint16_t fnum; + struct smb2_hnd *ph; + DATA_BLOB output_buffer; +}; + +static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb2_get_reparse_point_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum) +{ + struct tevent_req *req, *subreq; + struct cli_smb2_get_reparse_point_fnum_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb2_get_reparse_point_fnum_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->cli = cli; + state->fnum = fnum; + + status = map_fnum_to_smb2_handle(cli, fnum, &state->ph); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_ioctl_send(state, ev, state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + state->cli->smb2.tcon, + state->ph->fid_persistent, /* in_fid_persistent */ + state->ph->fid_volatile, /* in_fid_volatile */ + FSCTL_GET_REPARSE_POINT, + 0, /* in_max_input_length */ + NULL, + 64*1024, + NULL, + SMB2_IOCTL_FLAG_IS_FSCTL); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb2_get_reparse_point_fnum_done, + req); + + return req; +} + +static void cli_smb2_get_reparse_point_fnum_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data( + req, struct cli_smb2_get_reparse_point_fnum_state); + struct cli_state *cli = state->cli; + NTSTATUS status; + + status = smb2cli_ioctl_recv(subreq, state, + NULL, + &state->output_buffer); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + cli->raw_status = status; + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *output) +{ + struct cli_smb2_get_reparse_point_fnum_state *state = tevent_req_data( + req, struct cli_smb2_get_reparse_point_fnum_state); + + if (tevent_req_is_nterror(req, &state->cli->raw_status)) { + NTSTATUS status = state->cli->raw_status; + tevent_req_received(req); + return status; + } + *output = data_blob_dup_talloc(mem_ctx, state->output_buffer); + if (output->data == NULL) { + tevent_req_received(req); + return NT_STATUS_NO_MEMORY; + } + tevent_req_received(req); + return NT_STATUS_OK; +} diff --git a/source3/libsmb/cli_smb2_fnum.h b/source3/libsmb/cli_smb2_fnum.h new file mode 100644 index 0000000..f3355b3 --- /dev/null +++ b/source3/libsmb/cli_smb2_fnum.h @@ -0,0 +1,331 @@ +/* + Unix SMB/CIFS implementation. + smb2 wrapper client routines + Copyright (C) Jeremy Allison 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __SMB2CLI_FNUM_H__ +#define __SMB2CLI_FNUM_H__ + +struct smbXcli_conn; +struct smbXcli_session; +struct cli_state; +struct file_info; + +struct tevent_req *cli_smb2_create_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t impersonation_level, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + const struct smb2_create_blobs *in_cblobs); +NTSTATUS cli_smb2_create_fnum_recv( + struct tevent_req *req, + uint16_t *pfnum, + struct smb_create_returns *cr, + TALLOC_CTX *mem_ctx, + struct smb2_create_blobs *out_cblobs); +NTSTATUS cli_smb2_create_fnum( + struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t impersonation_level, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + const struct smb2_create_blobs *in_cblobs, + uint16_t *pfid, + struct smb_create_returns *cr, + TALLOC_CTX *mem_ctx, + struct smb2_create_blobs *out_cblobs); + +struct tevent_req *cli_smb2_close_fnum_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum); +NTSTATUS cli_smb2_close_fnum_recv(struct tevent_req *req); +NTSTATUS cli_smb2_close_fnum(struct cli_state *cli, uint16_t fnum); +struct tevent_req *cli_smb2_delete_on_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool flag); +NTSTATUS cli_smb2_delete_on_close_recv(struct tevent_req *req); +NTSTATUS cli_smb2_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag); +struct tevent_req *cli_smb2_mkdir_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *name); +NTSTATUS cli_smb2_mkdir_recv(struct tevent_req *req); +struct tevent_req *cli_smb2_rmdir_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname, + const struct smb2_create_blobs *in_cblobs); +NTSTATUS cli_smb2_rmdir_recv(struct tevent_req *req); +struct tevent_req *cli_smb2_unlink_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + const struct smb2_create_blobs *in_cblobs); +NTSTATUS cli_smb2_unlink_recv(struct tevent_req *req); +struct tevent_req *cli_smb2_list_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *pathname); +NTSTATUS cli_smb2_list_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct file_info **pfinfo); +NTSTATUS cli_smb2_qpathinfo_basic(struct cli_state *cli, + const char *name, + SMB_STRUCT_STAT *sbuf, + uint32_t *attributes); +NTSTATUS cli_smb2_qpathinfo_alt_name(struct cli_state *cli, + const char *name, + fstring alt_name); +struct tevent_req *cli_smb2_query_info_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + uint32_t in_max_output_length, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info, + uint32_t in_flags); +NTSTATUS cli_smb2_query_info_fnum_recv( + struct tevent_req *req, TALLOC_CTX *mem_ctx, DATA_BLOB *outbuf); +struct tevent_req *cli_smb2_set_info_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info); +NTSTATUS cli_smb2_set_info_fnum_recv(struct tevent_req *req); +NTSTATUS cli_smb2_set_info_fnum( + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info); +NTSTATUS cli_smb2_query_info_fnum( + struct cli_state *cli, + uint16_t fnum, + uint8_t in_info_type, + uint8_t in_info_class, + uint32_t in_max_output_length, + const DATA_BLOB *in_input_buffer, + uint32_t in_additional_info, + uint32_t in_flags, + TALLOC_CTX *mem_ctx, + DATA_BLOB *outbuf); +NTSTATUS cli_smb2_getatr(struct cli_state *cli, + const char *name, + uint32_t *pattr, + off_t *size, + time_t *write_time); +NTSTATUS cli_smb2_qpathinfo2(struct cli_state *cli, + const char *fname, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, + uint32_t *pattr, + SMB_INO_T *ino); +NTSTATUS cli_smb2_qpathinfo_streams(struct cli_state *cli, + const char *name, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams); +NTSTATUS cli_smb2_setpathinfo(struct cli_state *cli, + const char *name, + uint8_t in_info_type, + uint8_t in_file_info_class, + const DATA_BLOB *p_in_data); +NTSTATUS cli_smb2_setatr(struct cli_state *cli, + const char *fname, + uint32_t attr, + time_t mtime); +NTSTATUS cli_smb2_setattrE(struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time); +NTSTATUS cli_smb2_dskattr(struct cli_state *cli, + const char *path, + uint64_t *bsize, + uint64_t *total, + uint64_t *avail); +NTSTATUS cli_smb2_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr); +NTSTATUS cli_smb2_get_fs_full_size_info(struct cli_state *cli, + uint64_t *total_allocation_units, + uint64_t *caller_allocation_units, + uint64_t *actual_allocation_units, + uint64_t *sectors_per_allocation_unit, + uint64_t *bytes_per_sector); +NTSTATUS cli_smb2_get_fs_volume_info(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + char **_volume_name, + uint32_t *pserial_number, + time_t *pdate); +struct tevent_req *cli_smb2_query_mxac_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_smb2_query_mxac_recv(struct tevent_req *req, + uint32_t *mxac); +NTSTATUS cli_smb2_query_mxac(struct cli_state *cli, + const char *fname, + uint32_t *mxac); +struct tevent_req *cli_smb2_rename_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace); +NTSTATUS cli_smb2_rename_recv(struct tevent_req *req); +NTSTATUS cli_smb2_set_ea_fnum(struct cli_state *cli, + uint16_t fnum, + const char *ea_name, + const char *ea_val, + size_t ea_len); +NTSTATUS cli_smb2_get_ea_list_path(struct cli_state *cli, + const char *name, + TALLOC_CTX *ctx, + size_t *pnum_eas, + struct ea_struct **pea_list); +NTSTATUS cli_smb2_set_ea_path(struct cli_state *cli, + const char *name, + const char *ea_name, + const char *ea_val, + size_t ea_len); +NTSTATUS cli_smb2_get_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt); +NTSTATUS cli_smb2_list_user_quota_step(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int quota_fnum, + SMB_NTQUOTA_LIST **pqt_list, + bool first); +NTSTATUS cli_smb2_get_fs_quota_info(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt); +NTSTATUS cli_smb2_set_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_LIST *qtl); +NTSTATUS cli_smb2_set_fs_quota_info(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt); +struct tevent_req *cli_smb2_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + off_t offset, + size_t size); +NTSTATUS cli_smb2_read_recv(struct tevent_req *req, + ssize_t *received, + uint8_t **rcvbuf); +struct tevent_req *cli_smb2_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size); +NTSTATUS cli_smb2_write_recv(struct tevent_req *req, + size_t *pwritten); +struct tevent_req *cli_smb2_writeall_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size); +NTSTATUS cli_smb2_writeall_recv(struct tevent_req *req, + size_t *pwritten); +struct tevent_req *cli_smb2_splice_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t src_fnum, uint16_t dst_fnum, + off_t size, off_t src_offset, off_t dst_offset, + int (*splice_cb)(off_t n, void *priv), void *priv); +NTSTATUS cli_smb2_splice_recv(struct tevent_req *req, off_t *written); +NTSTATUS cli_smb2_shadow_copy_data(TALLOC_CTX *mem_ctx, + struct cli_state *cli, + uint16_t fnum, + bool get_names, + char ***pnames, + int *pnum_names); +NTSTATUS cli_smb2_ftruncate(struct cli_state *cli, + uint16_t fnum, + uint64_t newsize); +struct tevent_req *cli_smb2_notify_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint32_t buffer_size, + uint32_t completion_filter, + bool recursive); +NTSTATUS cli_smb2_notify_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct notify_change **pchanges, + uint32_t *pnum_changes); +NTSTATUS cli_smb2_notify(struct cli_state *cli, uint16_t fnum, + uint32_t buffer_size, uint32_t completion_filter, + bool recursive, TALLOC_CTX *mem_ctx, + struct notify_change **pchanges, + uint32_t *pnum_changes); +struct tevent_req *cli_smb2_set_reparse_point_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + DATA_BLOB in_buf); +NTSTATUS cli_smb2_set_reparse_point_fnum_recv(struct tevent_req *req); + +struct tevent_req *cli_smb2_get_reparse_point_fnum_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum); +NTSTATUS cli_smb2_get_reparse_point_fnum_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *output); + +#endif /* __SMB2CLI_FNUM_H__ */ diff --git a/source3/libsmb/cliconnect.c b/source3/libsmb/cliconnect.c new file mode 100644 index 0000000..3e60295 --- /dev/null +++ b/source3/libsmb/cliconnect.c @@ -0,0 +1,3991 @@ +/* + Unix SMB/CIFS implementation. + client connect/disconnect routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Andrew Bartlett 2001-2003 + Copyright (C) Volker Lendecke 2011 + Copyright (C) Jeremy Allison 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmb/namequery.h" +#include "../libcli/auth/libcli_auth.h" +#include "../libcli/auth/spnego.h" +#include "smb_krb5.h" +#include "auth/credentials/credentials.h" +#include "auth/gensec/gensec.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "auth_generic.h" +#include "libads/kerberos_proto.h" +#include "krb5_env.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "libsmb/nmblib.h" +#include "librpc/ndr/libndr.h" +#include "../libcli/smb/smbXcli_base.h" +#include "../libcli/smb/smb_seal.h" +#include "lib/param/param.h" +#include "../libcli/smb/smb2_negotiate_context.h" + +#define STAR_SMBSERVER "*SMBSERVER" + +static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx, + const char *principal); + +struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx, + const char *username, + const char *domain, + const char *realm, + const char *password, + bool use_kerberos, + bool fallback_after_kerberos, + bool use_ccache, + bool password_is_nt_hash) +{ + struct loadparm_context *lp_ctx = NULL; + struct cli_credentials *creds = NULL; + const char *principal = NULL; + char *tmp = NULL; + char *p = NULL; + bool ok; + + creds = cli_credentials_init(mem_ctx); + if (creds == NULL) { + return NULL; + } + + lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + goto fail; + } + ok = cli_credentials_set_conf(creds, lp_ctx); + if (!ok) { + goto fail; + } + + if (username == NULL) { + username = ""; + } + + if (strlen(username) == 0) { + if (password != NULL && strlen(password) == 0) { + /* + * some callers pass "" as no password + * + * gensec only handles NULL as no password. + */ + password = NULL; + } + if (password == NULL) { + cli_credentials_set_anonymous(creds); + return creds; + } + } + + tmp = talloc_strdup(creds, username); + if (tmp == NULL) { + goto fail; + } + username = tmp; + + /* allow for workgroups as part of the username */ + if ((p = strchr_m(tmp, '\\')) || + (p = strchr_m(tmp, '/')) || + (p = strchr_m(tmp, *lp_winbind_separator()))) { + *p = 0; + username = p + 1; + domain = tmp; + } + + principal = username; + username = cli_session_setup_get_account(creds, principal); + if (username == NULL) { + goto fail; + } + ok = strequal(username, principal); + if (ok) { + /* + * Ok still the same, so it's not a principal + */ + principal = NULL; + } + + if (use_kerberos && fallback_after_kerberos) { + cli_credentials_set_kerberos_state(creds, + CRED_USE_KERBEROS_DESIRED, + CRED_SPECIFIED); + } else if (use_kerberos) { + cli_credentials_set_kerberos_state(creds, + CRED_USE_KERBEROS_REQUIRED, + CRED_SPECIFIED); + } else { + cli_credentials_set_kerberos_state(creds, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + } + + if (use_ccache) { + uint32_t features; + + features = cli_credentials_get_gensec_features(creds); + features |= GENSEC_FEATURE_NTLM_CCACHE; + cli_credentials_set_gensec_features(creds, + features, + CRED_SPECIFIED); + + if (password != NULL && strlen(password) == 0) { + /* + * some callers pass "" as no password + * + * GENSEC_FEATURE_NTLM_CCACHE only handles + * NULL as no password. + */ + password = NULL; + } + } + + ok = cli_credentials_set_username(creds, + username, + CRED_SPECIFIED); + if (!ok) { + goto fail; + } + + if (domain != NULL) { + ok = cli_credentials_set_domain(creds, + domain, + CRED_SPECIFIED); + if (!ok) { + goto fail; + } + } + + if (principal != NULL) { + ok = cli_credentials_set_principal(creds, + principal, + CRED_SPECIFIED); + if (!ok) { + goto fail; + } + } + + if (realm != NULL) { + ok = cli_credentials_set_realm(creds, + realm, + CRED_SPECIFIED); + if (!ok) { + goto fail; + } + } + + if (password != NULL && strlen(password) > 0) { + if (password_is_nt_hash) { + struct samr_Password nt_hash; + size_t converted; + + converted = strhex_to_str((char *)nt_hash.hash, + sizeof(nt_hash.hash), + password, + strlen(password)); + if (converted != sizeof(nt_hash.hash)) { + goto fail; + } + + ok = cli_credentials_set_nt_hash(creds, + &nt_hash, + CRED_SPECIFIED); + if (!ok) { + goto fail; + } + } else { + ok = cli_credentials_set_password(creds, + password, + CRED_SPECIFIED); + if (!ok) { + goto fail; + } + } + } + + return creds; +fail: + TALLOC_FREE(creds); + return NULL; +} + +NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli, + struct cli_credentials *creds) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char *user_principal = NULL; + const char *user_account = NULL; + const char *user_domain = NULL; + const char *pass = NULL; + char *canon_principal = NULL; + char *canon_realm = NULL; + const char *target_hostname = NULL; + enum credentials_use_kerberos krb5_state; + bool try_kerberos = false; + bool need_kinit = false; + bool auth_requested = true; + int ret; + bool ok; + + target_hostname = smbXcli_conn_remote_name(cli->conn); + + auth_requested = cli_credentials_authentication_requested(creds); + if (auth_requested) { + errno = 0; + user_principal = cli_credentials_get_principal(creds, frame); + if (errno != 0) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + user_account = cli_credentials_get_username(creds); + user_domain = cli_credentials_get_domain(creds); + pass = cli_credentials_get_password(creds); + + krb5_state = cli_credentials_get_kerberos_state(creds); + + if (krb5_state != CRED_USE_KERBEROS_DISABLED) { + try_kerberos = true; + } + + if (user_principal == NULL) { + try_kerberos = false; + } + + if (target_hostname == NULL) { + try_kerberos = false; + } else if (is_ipaddress(target_hostname)) { + try_kerberos = false; + } else if (strequal(target_hostname, "localhost")) { + try_kerberos = false; + } else if (strequal(target_hostname, STAR_SMBSERVER)) { + try_kerberos = false; + } else if (!auth_requested) { + try_kerberos = false; + } + + if (krb5_state == CRED_USE_KERBEROS_REQUIRED && !try_kerberos) { + DEBUG(0, ("Kerberos auth with '%s' (%s\\%s) to access " + "'%s' not possible\n", + user_principal, user_domain, user_account, + target_hostname)); + TALLOC_FREE(frame); + return NT_STATUS_ACCESS_DENIED; + } + + if (pass == NULL || strlen(pass) == 0) { + need_kinit = false; + } else if (krb5_state == CRED_USE_KERBEROS_REQUIRED) { + need_kinit = try_kerberos; + } else { + need_kinit = try_kerberos; + } + + if (!need_kinit) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + DBG_INFO("Doing kinit for %s to access %s\n", + user_principal, target_hostname); + + /* + * TODO: This should be done within the gensec layer + * only if required! + */ + setenv(KRB5_ENV_CCNAME, "MEMORY:cliconnect", 1); + ret = kerberos_kinit_password_ext(user_principal, + pass, + 0, + 0, + 0, + NULL, + false, + false, + 0, + frame, + &canon_principal, + &canon_realm, + NULL); + if (ret != 0) { + int dbglvl = DBGLVL_NOTICE; + + if (krb5_state == CRED_USE_KERBEROS_REQUIRED) { + dbglvl = DBGLVL_ERR; + } + + DEBUG(dbglvl, ("Kinit for %s to access %s failed: %s\n", + user_principal, target_hostname, + error_message(ret))); + if (krb5_state == CRED_USE_KERBEROS_REQUIRED) { + TALLOC_FREE(frame); + return krb5_to_nt_status(ret); + } + + /* + * Ignore the error and hope that NTLM will work + */ + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + ok = cli_credentials_set_principal(creds, + canon_principal, + CRED_SPECIFIED); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + ok = cli_credentials_set_realm(creds, + canon_realm, + CRED_SPECIFIED); + if (!ok) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + DBG_DEBUG("Successfully authenticated as %s (%s) to access %s using " + "Kerberos\n", + user_principal, + canon_principal, + target_hostname); + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} + +static NTSTATUS cli_state_update_after_sesssetup(struct cli_state *cli, + const char *native_os, + const char *native_lm, + const char *primary_domain) +{ +#define _VALID_STR(p) ((p) != NULL && (p)[0] != '\0') + + if (!_VALID_STR(cli->server_os) && _VALID_STR(native_os)) { + cli->server_os = talloc_strdup(cli, native_os); + if (cli->server_os == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + if (!_VALID_STR(cli->server_type) && _VALID_STR(native_lm)) { + cli->server_type = talloc_strdup(cli, native_lm); + if (cli->server_type == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + if (!_VALID_STR(cli->server_domain) && _VALID_STR(primary_domain)) { + cli->server_domain = talloc_strdup(cli, primary_domain); + if (cli->server_domain == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + +#undef _VALID_STRING + return NT_STATUS_OK; +} + +/******************************************************** + Utility function to ensure we always return at least + a valid char * pointer to an empty string for the + cli->server_os, cli->server_type and cli->server_domain + strings. +*******************************************************/ + +static NTSTATUS smb_bytes_talloc_string(TALLOC_CTX *mem_ctx, + const uint8_t *hdr, + char **dest, + uint8_t *src, + size_t srclen, + ssize_t *destlen) +{ + *destlen = pull_string_talloc(mem_ctx, + (const char *)hdr, + SVAL(hdr, HDR_FLG2), + dest, + (char *)src, + srclen, + STR_TERMINATE); + if (*destlen == -1) { + return NT_STATUS_NO_MEMORY; + } + + if (*dest == NULL) { + *dest = talloc_strdup(mem_ctx, ""); + if (*dest == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Work out suitable capabilities to offer the server. +****************************************************************************/ + +static uint32_t cli_session_setup_capabilities(struct cli_state *cli, + uint32_t sesssetup_capabilities) +{ + uint32_t client_capabilities = smb1cli_conn_capabilities(cli->conn); + + /* + * We only send capabilities based on the mask for: + * - client only flags + * - flags used in both directions + * + * We do not echo the server only flags, except some legacy flags. + * + * SMB_CAP_LEGACY_CLIENT_MASK contains CAP_LARGE_READX and + * CAP_LARGE_WRITEX in order to allow us to do large reads + * against old Samba releases (<= 3.6.x). + */ + client_capabilities &= (SMB_CAP_BOTH_MASK | SMB_CAP_LEGACY_CLIENT_MASK); + + /* + * Session Setup specific flags CAP_DYNAMIC_REAUTH + * and CAP_EXTENDED_SECURITY are passed by the caller. + * We need that in order to do guest logins even if + * CAP_EXTENDED_SECURITY is negotiated. + */ + client_capabilities &= ~(CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY); + sesssetup_capabilities &= (CAP_DYNAMIC_REAUTH|CAP_EXTENDED_SECURITY); + client_capabilities |= sesssetup_capabilities; + + return client_capabilities; +} + +/**************************************************************************** + Do a NT1 guest session setup. +****************************************************************************/ + +struct cli_session_setup_guest_state { + struct cli_state *cli; + uint16_t vwv[13]; + struct iovec bytes; +}; + +static void cli_session_setup_guest_done(struct tevent_req *subreq); + +struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + struct tevent_req **psmbreq) +{ + struct tevent_req *req, *subreq; + struct cli_session_setup_guest_state *state; + uint16_t *vwv; + uint8_t *bytes; + + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_guest_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + vwv = state->vwv; + + SCVAL(vwv+0, 0, 0xFF); + SCVAL(vwv+0, 1, 0); + SSVAL(vwv+1, 0, 0); + SSVAL(vwv+2, 0, CLI_BUFFER_SIZE); + SSVAL(vwv+3, 0, 2); + SSVAL(vwv+4, 0, cli_state_get_vc_num(cli)); + SIVAL(vwv+5, 0, smb1cli_conn_server_session_key(cli->conn)); + SSVAL(vwv+7, 0, 0); + SSVAL(vwv+8, 0, 0); + SSVAL(vwv+9, 0, 0); + SSVAL(vwv+10, 0, 0); + SIVAL(vwv+11, 0, cli_session_setup_capabilities(cli, 0)); + + bytes = talloc_array(state, uint8_t, 0); + + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* username */ + NULL); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, /* workgroup */ + NULL); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Unix", 5, NULL); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "Samba", 6, NULL); + + if (bytes == NULL) { + TALLOC_FREE(req); + return NULL; + } + + state->bytes.iov_base = (void *)bytes; + state->bytes.iov_len = talloc_get_size(bytes); + + subreq = cli_smb_req_create(state, ev, cli, SMBsesssetupX, 0, 0, 13, + vwv, 1, &state->bytes); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_session_setup_guest_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_session_setup_guest_create(mem_ctx, ev, cli, &subreq); + if (req == NULL) { + return NULL; + } + + status = smb1cli_req_chain_submit(&subreq, 1); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + return req; +} + +static void cli_session_setup_guest_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_setup_guest_state *state = tevent_req_data( + req, struct cli_session_setup_guest_state); + struct cli_state *cli = state->cli; + uint32_t num_bytes; + uint8_t *in; + uint8_t *inhdr; + uint8_t *bytes; + uint8_t *p; + NTSTATUS status; + ssize_t ret; + uint8_t wct; + uint16_t *vwv; + + status = cli_smb_recv(subreq, state, &in, 3, &wct, &vwv, + &num_bytes, &bytes); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + inhdr = in + NBT_HDR_SIZE; + p = bytes; + + cli_state_set_uid(state->cli, SVAL(inhdr, HDR_UID)); + smb1cli_session_set_action(cli->smb1.session, SVAL(vwv+2, 0)); + + status = smb_bytes_talloc_string(cli, + inhdr, + &cli->server_os, + p, + bytes+num_bytes-p, + &ret); + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + p += ret; + + status = smb_bytes_talloc_string(cli, + inhdr, + &cli->server_type, + p, + bytes+num_bytes-p, + &ret); + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + p += ret; + + status = smb_bytes_talloc_string(cli, + inhdr, + &cli->server_domain, + p, + bytes+num_bytes-p, + &ret); + + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/* The following is calculated from : + * (smb_size-4) = 35 + * (smb_wcnt * 2) = 24 (smb_wcnt == 12 in cli_session_setup_blob_send() ) + * (strlen("Unix") + 1 + strlen("Samba") + 1) * 2 = 22 (unicode strings at + * end of packet. + */ + +#define BASE_SESSSETUP_BLOB_PACKET_SIZE (35 + 24 + 22) + +struct cli_sesssetup_blob_state { + struct tevent_context *ev; + struct cli_state *cli; + DATA_BLOB blob; + uint16_t max_blob_size; + + DATA_BLOB this_blob; + struct iovec *recv_iov; + + NTSTATUS status; + const uint8_t *inbuf; + DATA_BLOB ret_blob; + + char *out_native_os; + char *out_native_lm; +}; + +static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state, + struct tevent_req **psubreq); +static void cli_sesssetup_blob_done(struct tevent_req *subreq); + +static struct tevent_req *cli_sesssetup_blob_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + DATA_BLOB blob) +{ + struct tevent_req *req, *subreq; + struct cli_sesssetup_blob_state *state; + uint32_t usable_space; + + req = tevent_req_create(mem_ctx, &state, + struct cli_sesssetup_blob_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->blob = blob; + state->cli = cli; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + usable_space = UINT16_MAX; + } else { + usable_space = cli_state_available_size(cli, + BASE_SESSSETUP_BLOB_PACKET_SIZE); + } + + if (usable_space == 0) { + DEBUG(1, ("cli_session_setup_blob: cli->max_xmit too small " + "(not possible to send %u bytes)\n", + BASE_SESSSETUP_BLOB_PACKET_SIZE + 1)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + state->max_blob_size = MIN(usable_space, 0xFFFF); + + if (!cli_sesssetup_blob_next(state, &subreq)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req); + return req; +} + +static bool cli_sesssetup_blob_next(struct cli_sesssetup_blob_state *state, + struct tevent_req **psubreq) +{ + struct tevent_req *subreq; + uint16_t thistime; + + thistime = MIN(state->blob.length, state->max_blob_size); + + state->this_blob.data = state->blob.data; + state->this_blob.length = thistime; + + state->blob.data += thistime; + state->blob.length -= thistime; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + subreq = smb2cli_session_setup_send(state, state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb2.session, + 0, /* in_flags */ + SMB2_CAP_DFS, /* in_capabilities */ + 0, /* in_channel */ + 0, /* in_previous_session_id */ + &state->this_blob); + if (subreq == NULL) { + return false; + } + } else { + uint16_t in_buf_size = 0; + uint16_t in_mpx_max = 0; + uint16_t in_vc_num = 0; + uint32_t in_sess_key = 0; + uint32_t in_capabilities = 0; + const char *in_native_os = NULL; + const char *in_native_lm = NULL; + + in_buf_size = CLI_BUFFER_SIZE; + in_mpx_max = smbXcli_conn_max_requests(state->cli->conn); + in_vc_num = cli_state_get_vc_num(state->cli); + in_sess_key = smb1cli_conn_server_session_key(state->cli->conn); + in_capabilities = cli_session_setup_capabilities(state->cli, + CAP_EXTENDED_SECURITY); + in_native_os = "Unix"; + in_native_lm = "Samba"; + + /* + * For now we keep the same values as before, + * we may remove these in a separate commit later. + */ + in_mpx_max = 2; + in_vc_num = 1; + in_sess_key = 0; + + subreq = smb1cli_session_setup_ext_send(state, state->ev, + state->cli->conn, + state->cli->timeout, + state->cli->smb1.pid, + state->cli->smb1.session, + in_buf_size, + in_mpx_max, + in_vc_num, + in_sess_key, + state->this_blob, + in_capabilities, + in_native_os, + in_native_lm); + if (subreq == NULL) { + return false; + } + } + *psubreq = subreq; + return true; +} + +static void cli_sesssetup_blob_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_sesssetup_blob_state *state = tevent_req_data( + req, struct cli_sesssetup_blob_state); + NTSTATUS status; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = smb2cli_session_setup_recv(subreq, state, + &state->recv_iov, + &state->ret_blob); + } else { + status = smb1cli_session_setup_ext_recv(subreq, state, + &state->recv_iov, + &state->inbuf, + &state->ret_blob, + &state->out_native_os, + &state->out_native_lm); + } + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status) + && !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + tevent_req_nterror(req, status); + return; + } + + state->status = status; + + status = cli_state_update_after_sesssetup(state->cli, + state->out_native_os, + state->out_native_lm, + NULL); + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->blob.length != 0) { + /* + * More to send + */ + if (!cli_sesssetup_blob_next(state, &subreq)) { + tevent_req_oom(req); + return; + } + tevent_req_set_callback(subreq, cli_sesssetup_blob_done, req); + return; + } + tevent_req_done(req); +} + +static NTSTATUS cli_sesssetup_blob_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *pblob, + const uint8_t **pinbuf, + struct iovec **precv_iov) +{ + struct cli_sesssetup_blob_state *state = tevent_req_data( + req, struct cli_sesssetup_blob_state); + NTSTATUS status; + struct iovec *recv_iov; + + if (tevent_req_is_nterror(req, &status)) { + TALLOC_FREE(state->cli->smb2.session); + cli_state_set_uid(state->cli, UID_FIELD_INVALID); + tevent_req_received(req); + return status; + } + + recv_iov = talloc_move(mem_ctx, &state->recv_iov); + if (pblob != NULL) { + *pblob = state->ret_blob; + } + if (pinbuf != NULL) { + *pinbuf = state->inbuf; + } + if (precv_iov != NULL) { + *precv_iov = recv_iov; + } + /* could be NT_STATUS_MORE_PROCESSING_REQUIRED */ + status = state->status; + tevent_req_received(req); + return status; +} + +/**************************************************************************** + Do a spnego/NTLMSSP encrypted session setup. +****************************************************************************/ + +struct cli_session_setup_gensec_state { + struct tevent_context *ev; + struct cli_state *cli; + struct auth_generic_state *auth_generic; + bool is_anonymous; + DATA_BLOB blob_in; + const uint8_t *inbuf; + struct iovec *recv_iov; + DATA_BLOB blob_out; + bool local_ready; + bool remote_ready; + DATA_BLOB session_key; +}; + +static int cli_session_setup_gensec_state_destructor( + struct cli_session_setup_gensec_state *state) +{ + TALLOC_FREE(state->auth_generic); + data_blob_clear_free(&state->session_key); + return 0; +} + +static void cli_session_setup_gensec_local_next(struct tevent_req *req); +static void cli_session_setup_gensec_local_done(struct tevent_req *subreq); +static void cli_session_setup_gensec_remote_next(struct tevent_req *req); +static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq); +static void cli_session_setup_gensec_ready(struct tevent_req *req); + +static struct tevent_req *cli_session_setup_gensec_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + struct cli_credentials *creds, + const char *target_service, + const char *target_hostname) +{ + struct tevent_req *req; + struct cli_session_setup_gensec_state *state; + NTSTATUS status; + const DATA_BLOB *b = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_gensec_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + talloc_set_destructor( + state, cli_session_setup_gensec_state_destructor); + + status = auth_generic_client_prepare(state, &state->auth_generic); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + status = auth_generic_set_creds(state->auth_generic, creds); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + gensec_want_feature(state->auth_generic->gensec_security, + GENSEC_FEATURE_SESSION_KEY); + + if (target_service != NULL) { + status = gensec_set_target_service( + state->auth_generic->gensec_security, + target_service); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + } + + if (target_hostname != NULL) { + status = gensec_set_target_hostname( + state->auth_generic->gensec_security, + target_hostname); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + } + + b = smbXcli_conn_server_gss_blob(cli->conn); + if (b != NULL) { + state->blob_in = *b; + } + + state->is_anonymous = cli_credentials_is_anonymous(state->auth_generic->credentials); + + status = auth_generic_client_start(state->auth_generic, + GENSEC_OID_SPNEGO); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + state->cli->smb2.session = smbXcli_session_create(cli, + cli->conn); + if (tevent_req_nomem(state->cli->smb2.session, req)) { + return tevent_req_post(req, ev); + } + } + + cli_session_setup_gensec_local_next(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void cli_session_setup_gensec_local_next(struct tevent_req *req) +{ + struct cli_session_setup_gensec_state *state = + tevent_req_data(req, + struct cli_session_setup_gensec_state); + struct tevent_req *subreq = NULL; + + if (state->local_ready) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + subreq = gensec_update_send(state, state->ev, + state->auth_generic->gensec_security, + state->blob_in); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_session_setup_gensec_local_done, req); +} + +static void cli_session_setup_gensec_local_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct cli_session_setup_gensec_state *state = + tevent_req_data(req, + struct cli_session_setup_gensec_state); + NTSTATUS status; + + status = gensec_update_recv(subreq, state, &state->blob_out); + TALLOC_FREE(subreq); + state->blob_in = data_blob_null; + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) + { + tevent_req_nterror(req, status); + return; + } + + if (NT_STATUS_IS_OK(status)) { + state->local_ready = true; + } + + if (state->local_ready && state->remote_ready) { + cli_session_setup_gensec_ready(req); + return; + } + + cli_session_setup_gensec_remote_next(req); +} + +static void cli_session_setup_gensec_remote_next(struct tevent_req *req) +{ + struct cli_session_setup_gensec_state *state = + tevent_req_data(req, + struct cli_session_setup_gensec_state); + struct tevent_req *subreq = NULL; + + if (state->remote_ready) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + subreq = cli_sesssetup_blob_send(state, state->ev, + state->cli, state->blob_out); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_session_setup_gensec_remote_done, + req); +} + +static void cli_session_setup_gensec_remote_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct cli_session_setup_gensec_state *state = + tevent_req_data(req, + struct cli_session_setup_gensec_state); + NTSTATUS status; + + state->inbuf = NULL; + TALLOC_FREE(state->recv_iov); + + status = cli_sesssetup_blob_recv(subreq, state, &state->blob_in, + &state->inbuf, &state->recv_iov); + TALLOC_FREE(subreq); + data_blob_free(&state->blob_out); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) + { + tevent_req_nterror(req, status); + return; + } + + if (NT_STATUS_IS_OK(status)) { + struct smbXcli_session *session = NULL; + bool is_guest = false; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + session = state->cli->smb2.session; + } else { + session = state->cli->smb1.session; + } + + is_guest = smbXcli_session_is_guest(session); + if (is_guest) { + /* + * We can't finish the gensec handshake, we don't + * have a negotiated session key. + * + * So just pretend we are completely done, + * we need to continue as anonymous from this point, + * as we can't get a session key. + * + * Note that smbXcli_session_is_guest() + * always returns false if we require signing. + */ + state->blob_in = data_blob_null; + state->local_ready = true; + state->is_anonymous = true; + } + + state->remote_ready = true; + } + + if (state->local_ready && state->remote_ready) { + cli_session_setup_gensec_ready(req); + return; + } + + cli_session_setup_gensec_local_next(req); +} + +static void cli_session_dump_keys(TALLOC_CTX *mem_ctx, + struct smbXcli_session *session, + DATA_BLOB session_key) +{ + NTSTATUS status; + DATA_BLOB sig = data_blob_null; + DATA_BLOB app = data_blob_null; + DATA_BLOB enc = data_blob_null; + DATA_BLOB dec = data_blob_null; + uint64_t sid = smb2cli_session_current_id(session); + + status = smb2cli_session_signing_key(session, mem_ctx, &sig); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + status = smbXcli_session_application_key(session, mem_ctx, &app); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + status = smb2cli_session_encryption_key(session, mem_ctx, &enc); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + status = smb2cli_session_decryption_key(session, mem_ctx, &dec); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + DEBUG(0, ("debug encryption: dumping generated session keys\n")); + DEBUGADD(0, ("Session Id ")); + dump_data(0, (uint8_t*)&sid, sizeof(sid)); + DEBUGADD(0, ("Session Key ")); + dump_data(0, session_key.data, session_key.length); + DEBUGADD(0, ("Signing Key ")); + dump_data(0, sig.data, sig.length); + DEBUGADD(0, ("App Key ")); + dump_data(0, app.data, app.length); + + /* In client code, ServerIn is the encryption key */ + + DEBUGADD(0, ("ServerIn Key ")); + dump_data(0, enc.data, enc.length); + DEBUGADD(0, ("ServerOut Key ")); + dump_data(0, dec.data, dec.length); + +out: + data_blob_clear_free(&sig); + data_blob_clear_free(&app); + data_blob_clear_free(&enc); + data_blob_clear_free(&dec); +} + +static void cli_session_setup_gensec_ready(struct tevent_req *req) +{ + struct cli_session_setup_gensec_state *state = + tevent_req_data(req, + struct cli_session_setup_gensec_state); + const char *server_domain = NULL; + NTSTATUS status; + + if (state->blob_in.length != 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (state->blob_out.length != 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + /* + * gensec_ntlmssp_server_domain() returns NULL + * if NTLMSSP is not used. + * + * We can remove this later + * and leave the server domain empty for SMB2 and above + * in future releases. + */ + server_domain = gensec_ntlmssp_server_domain( + state->auth_generic->gensec_security); + + if (state->cli->server_domain[0] == '\0' && server_domain != NULL) { + TALLOC_FREE(state->cli->server_domain); + state->cli->server_domain = talloc_strdup(state->cli, + server_domain); + if (state->cli->server_domain == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + } + + if (state->is_anonymous) { + /* + * Windows server does not set the + * SMB2_SESSION_FLAG_IS_NULL flag. + * + * This fix makes sure we do not try + * to verify a signature on the final + * session setup response. + */ + tevent_req_done(req); + return; + } + + status = gensec_session_key(state->auth_generic->gensec_security, + state, &state->session_key); + if (tevent_req_nterror(req, status)) { + return; + } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + struct smbXcli_session *session = state->cli->smb2.session; + + status = smb2cli_session_set_session_key(session, + state->session_key, + state->recv_iov); + if (tevent_req_nterror(req, status)) { + return; + } + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB3_00 + && lp_debug_encryption()) + { + cli_session_dump_keys(state, session, state->session_key); + } + } else { + struct smbXcli_session *session = state->cli->smb1.session; + bool active; + + status = smb1cli_session_set_session_key(session, + state->session_key); + if (tevent_req_nterror(req, status)) { + return; + } + + active = smb1cli_conn_activate_signing(state->cli->conn, + state->session_key, + data_blob_null); + if (active) { + bool ok; + + ok = smb1cli_conn_check_signing(state->cli->conn, + state->inbuf, 1); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + } + + tevent_req_done(req); +} + +static NTSTATUS cli_session_setup_gensec_recv(struct tevent_req *req) +{ + struct cli_session_setup_gensec_state *state = + tevent_req_data(req, + struct cli_session_setup_gensec_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + cli_state_set_uid(state->cli, UID_FIELD_INVALID); + return status; + } + return NT_STATUS_OK; +} + +static char *cli_session_setup_get_account(TALLOC_CTX *mem_ctx, + const char *principal) +{ + char *account, *p; + + account = talloc_strdup(mem_ctx, principal); + if (account == NULL) { + return NULL; + } + p = strchr_m(account, '@'); + if (p != NULL) { + *p = '\0'; + } + return account; +} + +/**************************************************************************** + 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. +****************************************************************************/ + +struct cli_session_setup_spnego_state { + ADS_STATUS result; +}; + +static void cli_session_setup_spnego_done(struct tevent_req *subreq); + +static struct tevent_req *cli_session_setup_spnego_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + struct cli_credentials *creds) +{ + struct tevent_req *req, *subreq; + struct cli_session_setup_spnego_state *state; + const char *target_service = NULL; + const char *target_hostname = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_spnego_state); + if (req == NULL) { + return NULL; + } + + target_service = "cifs"; + target_hostname = smbXcli_conn_remote_name(cli->conn); + + status = cli_session_creds_prepare_krb5(cli, creds); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + DBG_INFO("Connect to %s as %s using SPNEGO\n", + target_hostname, + cli_credentials_get_principal(creds, talloc_tos())); + + subreq = cli_session_setup_gensec_send(state, ev, cli, creds, + target_service, target_hostname); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_session_setup_spnego_done, req); + return req; +} + +static void cli_session_setup_spnego_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_session_setup_gensec_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + tevent_req_done(req); +} + +static ADS_STATUS cli_session_setup_spnego_recv(struct tevent_req *req) +{ + struct cli_session_setup_spnego_state *state = tevent_req_data( + req, struct cli_session_setup_spnego_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + state->result = ADS_ERROR_NT(status); + } + + return state->result; +} + +struct cli_session_setup_creds_state { + struct cli_state *cli; + DATA_BLOB apassword_blob; + DATA_BLOB upassword_blob; + DATA_BLOB lm_session_key; + DATA_BLOB session_key; + char *out_native_os; + char *out_native_lm; + char *out_primary_domain; +}; + +static void cli_session_setup_creds_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct cli_session_setup_creds_state *state = tevent_req_data( + req, struct cli_session_setup_creds_state); + + if (req_state != TEVENT_REQ_RECEIVED) { + return; + } + + /* + * We only call data_blob_clear() as + * some of the blobs point to the same memory. + * + * We let the talloc hierarchy free the memory. + */ + data_blob_clear(&state->apassword_blob); + data_blob_clear(&state->upassword_blob); + data_blob_clear(&state->lm_session_key); + data_blob_clear(&state->session_key); + ZERO_STRUCTP(state); +} + +static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq); +static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq); +static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq); + +/**************************************************************************** + Send a session setup. The username and workgroup is in UNIX character + format and must be converted to DOS codepage format before sending. If the + password is in plaintext, the same should be done. +****************************************************************************/ + +struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + struct cli_credentials *creds) +{ + struct tevent_req *req, *subreq; + struct cli_session_setup_creds_state *state; + uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn); + bool use_spnego = false; + int flags = 0; + const char *username = ""; + const char *domain = ""; + DATA_BLOB target_info = data_blob_null; + DATA_BLOB challenge = data_blob_null; + uint16_t in_buf_size = 0; + uint16_t in_mpx_max = 0; + uint16_t in_vc_num = 0; + uint32_t in_sess_key = 0; + const char *in_native_os = NULL; + const char *in_native_lm = NULL; + enum credentials_use_kerberos krb5_state = + cli_credentials_get_kerberos_state(creds); + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_session_setup_creds_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + tevent_req_set_cleanup_fn(req, cli_session_setup_creds_cleanup); + + /* + * Now work out what sort of session setup we are going to + * do. I have split this into separate functions to make the flow a bit + * easier to understand (tridge). + */ + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) { + use_spnego = false; + } else if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + use_spnego = true; + } else if (smb1cli_conn_capabilities(cli->conn) & CAP_EXTENDED_SECURITY) { + /* + * if the server supports extended security then use SPNEGO + * even for anonymous connections. + */ + use_spnego = true; + } else { + use_spnego = false; + } + + if (use_spnego) { + subreq = cli_session_setup_spnego_send( + state, ev, cli, creds); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_session_setup_creds_done_spnego, + req); + return req; + } + + if (krb5_state == CRED_USE_KERBEROS_REQUIRED) { + DBG_WARNING("Kerberos authentication requested, but " + "the server does not support SPNEGO authentication\n"); + tevent_req_nterror(req, NT_STATUS_NETWORK_CREDENTIAL_CONFLICT); + return tevent_req_post(req, ev); + } + + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN1) { + /* + * SessionSetupAndX was introduced by LANMAN 1.0. So we skip + * this step against older servers. + */ + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + if (cli_credentials_is_anonymous(creds)) { + /* + * Do an anonymous session setup + */ + goto non_spnego_creds_done; + } + + if ((sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) == 0) { + /* + * Do an anonymous session setup, + * the password is passed via the tree connect. + */ + goto non_spnego_creds_done; + } + + cli_credentials_get_ntlm_username_domain(creds, state, + &username, + &domain); + if (tevent_req_nomem(username, req)) { + return tevent_req_post(req, ev); + } + if (tevent_req_nomem(domain, req)) { + return tevent_req_post(req, ev); + } + + DBG_INFO("Connect to %s as %s using NTLM\n", domain, username); + + if ((sec_mode & NEGOTIATE_SECURITY_CHALLENGE_RESPONSE) == 0) { + bool use_unicode = smbXcli_conn_use_unicode(cli->conn); + uint8_t *bytes = NULL; + size_t bytes_len = 0; + const char *pw = cli_credentials_get_password(creds); + size_t pw_len = 0; + + if (pw == NULL) { + pw = ""; + } + pw_len = strlen(pw) + 1; + + if (!lp_client_plaintext_auth()) { + DEBUG(1, ("Server requested PLAINTEXT password but " + "'client plaintext auth = no'\n")); + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return tevent_req_post(req, ev); + } + + bytes = talloc_array(state, uint8_t, 0); + bytes = trans2_bytes_push_str(bytes, use_unicode, + pw, pw_len, &bytes_len); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (use_unicode) { + /* + * CAP_UNICODE, can only be negotiated by NT1. + */ + state->upassword_blob = data_blob_const(bytes, + bytes_len); + } else { + state->apassword_blob = data_blob_const(bytes, + bytes_len); + } + + goto non_spnego_creds_done; + } + + challenge = data_blob_const(smb1cli_conn_server_challenge(cli->conn), 8); + + if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) { + if (lp_client_ntlmv2_auth() && lp_client_use_spnego()) { + /* + * Don't send an NTLMv2 response without NTLMSSP if we + * want to use spnego support. + */ + DEBUG(1, ("Server does not support EXTENDED_SECURITY " + " but 'client use spnego = yes'" + " and 'client ntlmv2 auth = yes' is set\n")); + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return tevent_req_post(req, ev); + } + + if (lp_client_ntlmv2_auth()) { + flags |= CLI_CRED_NTLMv2_AUTH; + + /* + * note that the 'domain' here is a best + * guess - we don't know the server's domain + * at this point. Windows clients also don't + * use hostname... + */ + target_info = NTLMv2_generate_names_blob(state, + NULL, + domain); + if (tevent_req_nomem(target_info.data, req)) { + return tevent_req_post(req, ev); + } + } else { + flags |= CLI_CRED_NTLM_AUTH; + if (lp_client_lanman_auth()) { + flags |= CLI_CRED_LANMAN_AUTH; + } + } + } else { + if (!lp_client_lanman_auth()) { + DEBUG(1, ("Server requested user level LM password but " + "'client lanman auth = no' is set.\n")); + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return tevent_req_post(req, ev); + } + + flags |= CLI_CRED_LANMAN_AUTH; + } + + status = cli_credentials_get_ntlm_response(creds, state, &flags, + challenge, NULL, + target_info, + &state->apassword_blob, + &state->upassword_blob, + &state->lm_session_key, + &state->session_key); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + +non_spnego_creds_done: + + in_buf_size = CLI_BUFFER_SIZE; + in_mpx_max = smbXcli_conn_max_requests(cli->conn); + in_vc_num = cli_state_get_vc_num(cli); + in_sess_key = smb1cli_conn_server_session_key(cli->conn); + in_native_os = "Unix"; + in_native_lm = "Samba"; + + if (smbXcli_conn_protocol(cli->conn) == PROTOCOL_NT1) { + uint32_t in_capabilities = 0; + + in_capabilities = cli_session_setup_capabilities(cli, 0); + + /* + * For now we keep the same values as before, + * we may remove these in a separate commit later. + */ + in_mpx_max = 2; + + subreq = smb1cli_session_setup_nt1_send(state, ev, + cli->conn, + cli->timeout, + cli->smb1.pid, + cli->smb1.session, + in_buf_size, + in_mpx_max, + in_vc_num, + in_sess_key, + username, + domain, + state->apassword_blob, + state->upassword_blob, + in_capabilities, + in_native_os, + in_native_lm); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_session_setup_creds_done_nt1, + req); + return req; + } + + /* + * For now we keep the same values as before, + * we may remove these in a separate commit later. + */ + in_mpx_max = 2; + in_vc_num = 1; + + subreq = smb1cli_session_setup_lm21_send(state, ev, + cli->conn, + cli->timeout, + cli->smb1.pid, + cli->smb1.session, + in_buf_size, + in_mpx_max, + in_vc_num, + in_sess_key, + username, + domain, + state->apassword_blob, + in_native_os, + in_native_lm); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_session_setup_creds_done_lm21, + req); + return req; +} + +static void cli_session_setup_creds_done_spnego(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + ADS_STATUS status; + + status = cli_session_setup_spnego_recv(subreq); + TALLOC_FREE(subreq); + if (!ADS_ERR_OK(status)) { + DEBUG(3, ("SPNEGO login failed: %s\n", ads_errstr(status))); + tevent_req_nterror(req, ads_ntstatus(status)); + return; + } + tevent_req_done(req); +} + +static void cli_session_setup_creds_done_nt1(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_setup_creds_state *state = tevent_req_data( + req, struct cli_session_setup_creds_state); + struct cli_state *cli = state->cli; + NTSTATUS status; + struct iovec *recv_iov = NULL; + const uint8_t *inbuf = NULL; + bool ok; + + status = smb1cli_session_setup_nt1_recv(subreq, state, + &recv_iov, + &inbuf, + &state->out_native_os, + &state->out_native_lm, + &state->out_primary_domain); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("NT1 login failed: %s\n", nt_errstr(status))); + tevent_req_nterror(req, status); + return; + } + + status = cli_state_update_after_sesssetup(state->cli, + state->out_native_os, + state->out_native_lm, + state->out_primary_domain); + if (tevent_req_nterror(req, status)) { + return; + } + + ok = smb1cli_conn_activate_signing(cli->conn, + state->session_key, + state->upassword_blob); + if (ok) { + ok = smb1cli_conn_check_signing(cli->conn, inbuf, 1); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return; + } + } + + if (state->session_key.data) { + struct smbXcli_session *session = cli->smb1.session; + + status = smb1cli_session_set_session_key(session, + state->session_key); + if (tevent_req_nterror(req, status)) { + return; + } + } + + tevent_req_done(req); +} + +static void cli_session_setup_creds_done_lm21(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_setup_creds_state *state = tevent_req_data( + req, struct cli_session_setup_creds_state); + NTSTATUS status; + + status = smb1cli_session_setup_lm21_recv(subreq, state, + &state->out_native_os, + &state->out_native_lm); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(3, ("LM21 login failed: %s\n", nt_errstr(status))); + tevent_req_nterror(req, status); + return; + } + + status = cli_state_update_after_sesssetup(state->cli, + state->out_native_os, + state->out_native_lm, + NULL); + if (tevent_req_nterror(req, status)) { + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_session_setup_creds(struct cli_state *cli, + struct cli_credentials *creds) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_session_setup_creds_send(ev, ev, cli, creds); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_session_setup_creds_recv(req); + fail: + TALLOC_FREE(ev); + return status; +} + +NTSTATUS cli_session_setup_anon(struct cli_state *cli) +{ + NTSTATUS status; + struct cli_credentials *creds = NULL; + + creds = cli_credentials_init_anon(cli); + if (creds == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = cli_session_setup_creds(cli, creds); + TALLOC_FREE(creds); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + Send a uloggoff. +*****************************************************************************/ + +struct cli_ulogoff_state { + struct cli_state *cli; + uint16_t vwv[3]; +}; + +static void cli_ulogoff_done(struct tevent_req *subreq); + +static struct tevent_req *cli_ulogoff_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + struct cli_ulogoff_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_ulogoff_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + SCVAL(state->vwv+0, 0, 0xFF); + SCVAL(state->vwv+1, 0, 0); + SSVAL(state->vwv+2, 0, 0); + + subreq = cli_smb_send(state, ev, cli, SMBulogoffX, 0, 0, 2, state->vwv, + 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ulogoff_done, req); + return req; +} + +static void cli_ulogoff_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_ulogoff_state *state = tevent_req_data( + req, struct cli_ulogoff_state); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + cli_state_set_uid(state->cli, UID_FIELD_INVALID); + tevent_req_done(req); +} + +static NTSTATUS cli_ulogoff_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_ulogoff(struct cli_state *cli) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + status = smb2cli_logoff(cli->conn, + cli->timeout, + cli->smb2.session); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + smb2cli_session_set_id_and_flags(cli->smb2.session, + UINT64_MAX, 0); + return NT_STATUS_OK; + } + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_ulogoff_send(ev, ev, cli); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_ulogoff_recv(req); +fail: + TALLOC_FREE(ev); + return status; +} + +/**************************************************************************** + Send a tconX. +****************************************************************************/ + +struct cli_tcon_andx_state { + struct cli_state *cli; + uint16_t vwv[4]; + struct iovec bytes; +}; + +static void cli_tcon_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen, + struct tevent_req **psmbreq) +{ + struct tevent_req *req, *subreq; + struct cli_tcon_andx_state *state; + uint8_t p24[24]; + uint16_t *vwv; + char *tmp = NULL; + uint8_t *bytes; + uint16_t sec_mode = smb1cli_conn_server_security_mode(cli->conn); + uint16_t tcon_flags = 0; + + *psmbreq = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_tcon_andx_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + vwv = state->vwv; + + TALLOC_FREE(cli->smb1.tcon); + cli->smb1.tcon = smbXcli_tcon_create(cli); + if (tevent_req_nomem(cli->smb1.tcon, req)) { + return tevent_req_post(req, ev); + } + smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX); + + cli->share = talloc_strdup(cli, share); + if (!cli->share) { + return NULL; + } + + /* in user level security don't send a password now */ + if (sec_mode & NEGOTIATE_SECURITY_USER_LEVEL) { + passlen = 1; + pass = ""; + } else if (pass == NULL) { + DEBUG(1, ("Server not using user level security and no " + "password supplied.\n")); + goto access_denied; + } + + if ((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 lanman auth = no' or 'client ntlmv2 auth = yes'\n")); + goto access_denied; + } + + /* + * Non-encrypted passwords - convert to DOS codepage before + * encryption. + */ + SMBencrypt(pass, smb1cli_conn_server_challenge(cli->conn), p24); + passlen = 24; + pass = (const char *)p24; + } else { + if((sec_mode & (NEGOTIATE_SECURITY_USER_LEVEL + |NEGOTIATE_SECURITY_CHALLENGE_RESPONSE)) + == 0) { + uint8_t *tmp_pass; + + if (!lp_client_plaintext_auth() && (*pass)) { + DEBUG(1, ("Server requested PLAINTEXT " + "password but " + "'client plaintext auth = no' or 'client ntlmv2 auth = yes'\n")); + goto access_denied; + } + + /* + * Non-encrypted passwords - convert to DOS codepage + * before using. + */ + tmp_pass = talloc_array(talloc_tos(), uint8_t, 0); + if (tevent_req_nomem(tmp_pass, req)) { + return tevent_req_post(req, ev); + } + tmp_pass = trans2_bytes_push_str(tmp_pass, + false, /* always DOS */ + pass, + passlen, + NULL); + if (tevent_req_nomem(tmp_pass, req)) { + return tevent_req_post(req, ev); + } + pass = (const char *)tmp_pass; + passlen = talloc_get_size(tmp_pass); + } + } + + tcon_flags |= TCONX_FLAG_EXTENDED_RESPONSE; + tcon_flags |= TCONX_FLAG_EXTENDED_SIGNATURES; + + SCVAL(vwv+0, 0, 0xFF); + SCVAL(vwv+0, 1, 0); + SSVAL(vwv+1, 0, 0); + SSVAL(vwv+2, 0, tcon_flags); + SSVAL(vwv+3, 0, passlen); + + if (passlen && pass) { + bytes = (uint8_t *)talloc_memdup(state, pass, passlen); + } else { + bytes = talloc_array(state, uint8_t, 0); + } + + /* + * Add the sharename + */ + tmp = talloc_asprintf_strupper_m(talloc_tos(), "\\\\%s\\%s", + smbXcli_conn_remote_name(cli->conn), share); + if (tmp == NULL) { + TALLOC_FREE(req); + return NULL; + } + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), tmp, strlen(tmp)+1, + NULL); + TALLOC_FREE(tmp); + + /* + * Add the devicetype + */ + tmp = talloc_strdup_upper(talloc_tos(), dev); + if (tmp == NULL) { + TALLOC_FREE(req); + return NULL; + } + bytes = smb_bytes_push_str(bytes, false, tmp, strlen(tmp)+1, NULL); + TALLOC_FREE(tmp); + + if (bytes == NULL) { + TALLOC_FREE(req); + return NULL; + } + + state->bytes.iov_base = (void *)bytes; + state->bytes.iov_len = talloc_get_size(bytes); + + subreq = cli_smb_req_create(state, ev, cli, SMBtconX, 0, 0, 4, vwv, + 1, &state->bytes); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_tcon_andx_done, req); + *psmbreq = subreq; + return req; + + access_denied: + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return tevent_req_post(req, ev); +} + +struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_tcon_andx_create(mem_ctx, ev, cli, share, dev, pass, passlen, + &subreq); + if (req == NULL) { + return NULL; + } + if (subreq == NULL) { + return req; + } + status = smb1cli_req_chain_submit(&subreq, 1); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + return req; +} + +static void cli_tcon_andx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_tcon_andx_state *state = tevent_req_data( + req, struct cli_tcon_andx_state); + struct cli_state *cli = state->cli; + uint8_t *in; + uint8_t *inhdr; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + uint16_t optional_support = 0; + + status = cli_smb_recv(subreq, state, &in, 0, &wct, &vwv, + &num_bytes, &bytes); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + inhdr = in + NBT_HDR_SIZE; + + if (num_bytes) { + if (pull_string_talloc(cli, + (const char *)inhdr, + SVAL(inhdr, HDR_FLG2), + &cli->dev, + bytes, + num_bytes, + STR_TERMINATE|STR_ASCII) == -1) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + } else { + cli->dev = talloc_strdup(cli, ""); + if (cli->dev == NULL) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + } + + if ((smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) && (num_bytes == 3)) { + /* 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 + */ + + if ((wct > 2) && (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN2)) { + optional_support = SVAL(vwv+2, 0); + } + + if (optional_support & SMB_EXTENDED_SIGNATURES) { + smb1cli_session_protect_session_key(cli->smb1.session); + } + + smb1cli_tcon_set_values(state->cli->smb1.tcon, + SVAL(inhdr, HDR_TID), + optional_support, + 0, /* maximal_access */ + 0, /* guest_maximal_access */ + NULL, /* service */ + NULL); /* fs_type */ + + tevent_req_done(req); +} + +NTSTATUS cli_tcon_andx_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, + const char *dev, const char *pass, int passlen) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_tcon_andx_send(frame, ev, cli, share, dev, pass, passlen); + if (req == NULL) { + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_tcon_andx_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_tree_connect_state { + struct cli_state *cli; +}; + +static struct tevent_req *cli_raw_tcon_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + const char *service, const char *pass, const char *dev); +static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req, + uint16_t *max_xmit, uint16_t *tid); + +static void cli_tree_connect_smb2_done(struct tevent_req *subreq); +static void cli_tree_connect_andx_done(struct tevent_req *subreq); +static void cli_tree_connect_raw_done(struct tevent_req *subreq); + +static struct tevent_req *cli_tree_connect_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + const char *share, const char *dev, const char *pass) +{ + struct tevent_req *req, *subreq; + struct cli_tree_connect_state *state; + int passlen; + + if (pass == NULL) { + pass = ""; + } + passlen = strlen(pass) + 1; + + req = tevent_req_create(mem_ctx, &state, + struct cli_tree_connect_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + cli->share = talloc_strdup(cli, share); + if (tevent_req_nomem(cli->share, req)) { + return tevent_req_post(req, ev); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + char *unc; + + TALLOC_FREE(cli->smb2.tcon); + cli->smb2.tcon = smbXcli_tcon_create(cli); + if (tevent_req_nomem(cli->smb2.tcon, req)) { + return tevent_req_post(req, ev); + } + + unc = talloc_asprintf(state, "\\\\%s\\%s", + smbXcli_conn_remote_name(cli->conn), + share); + if (tevent_req_nomem(unc, req)) { + return tevent_req_post(req, ev); + } + + subreq = smb2cli_tcon_send(state, ev, cli->conn, cli->timeout, + cli->smb2.session, cli->smb2.tcon, + 0, /* flags */ + unc); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_tree_connect_smb2_done, + req); + return req; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_LANMAN1) { + subreq = cli_tcon_andx_send(state, ev, cli, share, dev, + pass, passlen); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_tree_connect_andx_done, + req); + return req; + } + + subreq = cli_raw_tcon_send(state, ev, cli, share, pass, dev); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_tree_connect_raw_done, req); + + return req; +} + +static void cli_tree_connect_smb2_done(struct tevent_req *subreq) +{ + tevent_req_simple_finish_ntstatus( + subreq, smb2cli_tcon_recv(subreq)); +} + +static void cli_tree_connect_andx_done(struct tevent_req *subreq) +{ + tevent_req_simple_finish_ntstatus( + subreq, cli_tcon_andx_recv(subreq)); +} + +static void cli_tree_connect_raw_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_tree_connect_state *state = tevent_req_data( + req, struct cli_tree_connect_state); + NTSTATUS status; + uint16_t max_xmit = 0; + uint16_t tid = 0; + + status = cli_raw_tcon_recv(subreq, &max_xmit, &tid); + if (tevent_req_nterror(req, status)) { + return; + } + + smb1cli_tcon_set_values(state->cli->smb1.tcon, + tid, + 0, /* optional_support */ + 0, /* maximal_access */ + 0, /* guest_maximal_access */ + NULL, /* service */ + NULL); /* fs_type */ + + tevent_req_done(req); +} + +static NTSTATUS cli_tree_connect_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share, + const char *dev, const char *pass) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_tree_connect_send(ev, ev, cli, share, dev, pass); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_tree_connect_recv(req); +fail: + TALLOC_FREE(ev); + return status; +} + +NTSTATUS cli_tree_connect_creds(struct cli_state *cli, + const char *share, const char *dev, + struct cli_credentials *creds) +{ + const char *pw = NULL; + + if (creds != NULL) { + pw = cli_credentials_get_password(creds); + } + + return cli_tree_connect(cli, share, dev, pw); +} + +/**************************************************************************** + Send a tree disconnect. +****************************************************************************/ + +struct cli_tdis_state { + struct cli_state *cli; +}; + +static void cli_tdis_done(struct tevent_req *subreq); + +static struct tevent_req *cli_tdis_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + struct cli_tdis_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_tdis_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + subreq = cli_smb_send(state, ev, cli, SMBtdis, 0, 0, 0, NULL, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_tdis_done, req); + return req; +} + +static void cli_tdis_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_tdis_state *state = tevent_req_data( + req, struct cli_tdis_state); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + TALLOC_FREE(state->cli->smb1.tcon); + tevent_req_done(req); +} + +static NTSTATUS cli_tdis_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_tdis(struct cli_state *cli) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + status = smb2cli_tdis(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon); + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(cli->smb2.tcon); + } + return status; + } + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_tdis_send(ev, ev, cli); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_tdis_recv(req); +fail: + TALLOC_FREE(ev); + return status; +} + +struct cli_connect_sock_state { + const char **called_names; + const char **calling_names; + int *called_types; + int fd; + uint16_t port; +}; + +static void cli_connect_sock_done(struct tevent_req *subreq); + +/* + * Async only if we don't have to look up the name, i.e. "pss" is set with a + * nonzero address. + */ + +static struct tevent_req *cli_connect_sock_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *host, int name_type, const struct sockaddr_storage *pss, + const char *myname, uint16_t port) +{ + struct tevent_req *req, *subreq; + struct cli_connect_sock_state *state; + struct sockaddr_storage *addrs = NULL; + unsigned i; + unsigned num_addrs = 0; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_connect_sock_state); + if (req == NULL) { + return NULL; + } + + if ((pss == NULL) || is_zero_addr(pss)) { + + /* + * Here we cheat. resolve_name_list is not async at all. So + * this call will only be really async if the name lookup has + * been done externally. + */ + + status = resolve_name_list(state, host, name_type, + &addrs, &num_addrs); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); + } + } else { + addrs = talloc_array(state, struct sockaddr_storage, 1); + if (tevent_req_nomem(addrs, req)) { + return tevent_req_post(req, ev); + } + addrs[0] = *pss; + num_addrs = 1; + } + + state->called_names = talloc_array(state, const char *, num_addrs); + if (tevent_req_nomem(state->called_names, req)) { + return tevent_req_post(req, ev); + } + state->called_types = talloc_array(state, int, num_addrs); + if (tevent_req_nomem(state->called_types, req)) { + return tevent_req_post(req, ev); + } + state->calling_names = talloc_array(state, const char *, num_addrs); + if (tevent_req_nomem(state->calling_names, req)) { + return tevent_req_post(req, ev); + } + for (i=0; i<num_addrs; i++) { + state->called_names[i] = host; + state->called_types[i] = name_type; + state->calling_names[i] = myname; + } + + subreq = smbsock_any_connect_send( + state, ev, addrs, state->called_names, state->called_types, + state->calling_names, NULL, num_addrs, port); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_connect_sock_done, req); + return req; +} + +static void cli_connect_sock_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_connect_sock_state *state = tevent_req_data( + req, struct cli_connect_sock_state); + NTSTATUS status; + + status = smbsock_any_connect_recv(subreq, &state->fd, NULL, + &state->port); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + set_socket_options(state->fd, lp_socket_options()); + tevent_req_done(req); +} + +static NTSTATUS cli_connect_sock_recv(struct tevent_req *req, + int *pfd, uint16_t *pport) +{ + struct cli_connect_sock_state *state = tevent_req_data( + req, struct cli_connect_sock_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfd = state->fd; + *pport = state->port; + return NT_STATUS_OK; +} + +struct cli_connect_nb_state { + const char *desthost; + enum smb_signing_setting signing_state; + int flags; + struct cli_state *cli; +}; + +static void cli_connect_nb_done(struct tevent_req *subreq); + +static struct tevent_req *cli_connect_nb_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *host, const struct sockaddr_storage *dest_ss, + uint16_t port, int name_type, const char *myname, + enum smb_signing_setting signing_state, int flags) +{ + struct tevent_req *req, *subreq; + struct cli_connect_nb_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_connect_nb_state); + if (req == NULL) { + return NULL; + } + state->signing_state = signing_state; + state->flags = flags; + + if (host != NULL) { + char *p = strchr(host, '#'); + + if (p != NULL) { + name_type = strtol(p+1, NULL, 16); + host = talloc_strndup(state, host, p - host); + if (tevent_req_nomem(host, req)) { + return tevent_req_post(req, ev); + } + } + + state->desthost = host; + } else if (dest_ss != NULL) { + state->desthost = print_canonical_sockaddr(state, dest_ss); + if (tevent_req_nomem(state->desthost, req)) { + return tevent_req_post(req, ev); + } + } else { + /* No host or dest_ss given. Error out. */ + tevent_req_error(req, EINVAL); + return tevent_req_post(req, ev); + } + + subreq = cli_connect_sock_send(state, ev, host, name_type, dest_ss, + myname, port); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_connect_nb_done, req); + return req; +} + +static void cli_connect_nb_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_connect_nb_state *state = tevent_req_data( + req, struct cli_connect_nb_state); + NTSTATUS status; + int fd = 0; + uint16_t port; + + status = cli_connect_sock_recv(subreq, &fd, &port); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->cli = cli_state_create(state, fd, state->desthost, + state->signing_state, state->flags); + if (tevent_req_nomem(state->cli, req)) { + close(fd); + return; + } + tevent_req_done(req); +} + +static NTSTATUS cli_connect_nb_recv(struct tevent_req *req, + struct cli_state **pcli) +{ + struct cli_connect_nb_state *state = tevent_req_data( + req, struct cli_connect_nb_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pcli = talloc_move(NULL, &state->cli); + return NT_STATUS_OK; +} + +NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss, + uint16_t port, int name_type, const char *myname, + enum smb_signing_setting signing_state, int flags, struct cli_state **pcli) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_connect_nb_send(ev, ev, host, dest_ss, port, name_type, + myname, signing_state, flags); + if (req == NULL) { + goto fail; + } + if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(20, 0))) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_connect_nb_recv(req, pcli); +fail: + TALLOC_FREE(ev); + return status; +} + +struct cli_start_connection_state { + struct tevent_context *ev; + struct cli_state *cli; + int min_protocol; + int max_protocol; +}; + +static void cli_start_connection_connected(struct tevent_req *subreq); +static void cli_start_connection_done(struct tevent_req *subreq); + +/** + establishes a connection to after the negprot. + @param output_cli A fully initialised cli structure, non-null only on success + @param dest_host The netbios name of the remote host + @param dest_ss (optional) The the destination IP, NULL for name based lookup + @param port (optional) The destination port (0 for default) +*/ + +static struct tevent_req *cli_start_connection_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *my_name, const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + enum smb_signing_setting signing_state, int flags) +{ + struct tevent_req *req, *subreq; + struct cli_start_connection_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_start_connection_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + + if (flags & CLI_FULL_CONNECTION_IPC) { + state->min_protocol = lp_client_ipc_min_protocol(); + state->max_protocol = lp_client_ipc_max_protocol(); + } else { + state->min_protocol = lp_client_min_protocol(); + state->max_protocol = lp_client_max_protocol(); + } + + if (flags & CLI_FULL_CONNECTION_FORCE_SMB1) { + state->max_protocol = MIN(state->max_protocol, + PROTOCOL_NT1); + state->min_protocol = MIN(state->min_protocol, + state->max_protocol); + } + + if (flags & CLI_FULL_CONNECTION_DISABLE_SMB1) { + state->min_protocol = MAX(state->min_protocol, + PROTOCOL_SMB2_02); + state->max_protocol = MAX(state->max_protocol, + state->min_protocol); + } + + subreq = cli_connect_nb_send(state, ev, dest_host, dest_ss, port, + 0x20, my_name, signing_state, flags); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_start_connection_connected, req); + return req; +} + +static void cli_start_connection_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_start_connection_state *state = tevent_req_data( + req, struct cli_start_connection_state); + NTSTATUS status; + + status = cli_connect_nb_recv(subreq, &state->cli); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = smbXcli_negprot_send( + state, + state->ev, + state->cli->conn, + state->cli->timeout, + state->min_protocol, + state->max_protocol, + WINDOWS_CLIENT_PURE_SMB2_NEGPROT_INITIAL_CREDIT_ASK, + NULL); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_start_connection_done, req); +} + +static void cli_start_connection_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_start_connection_state *state = tevent_req_data( + req, struct cli_start_connection_state); + NTSTATUS status; + + status = smbXcli_negprot_recv(subreq, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + /* Ensure we ask for some initial credits. */ + smb2cli_conn_set_max_credits(state->cli->conn, + DEFAULT_SMB2_MAX_CREDITS); + } + + tevent_req_done(req); +} + +static NTSTATUS cli_start_connection_recv(struct tevent_req *req, + struct cli_state **output_cli) +{ + struct cli_start_connection_state *state = tevent_req_data( + req, struct cli_start_connection_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *output_cli = state->cli; + + return NT_STATUS_OK; +} + +NTSTATUS cli_start_connection(struct cli_state **output_cli, + const char *my_name, + const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + enum smb_signing_setting signing_state, int flags) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_start_connection_send(ev, ev, my_name, dest_host, dest_ss, + port, signing_state, flags); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_start_connection_recv(req, output_cli); +fail: + TALLOC_FREE(ev); + return status; +} + +struct cli_smb1_setup_encryption_blob_state { + uint16_t setup[1]; + uint8_t param[4]; + NTSTATUS status; + DATA_BLOB out; + uint16_t enc_ctx_id; +}; + +static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq); + +static struct tevent_req *cli_smb1_setup_encryption_blob_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const DATA_BLOB in) +{ + struct tevent_req *req = NULL; + struct cli_smb1_setup_encryption_blob_state *state = NULL; + struct tevent_req *subreq = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb1_setup_encryption_blob_state); + if (req == NULL) { + return NULL; + } + + if (in.length > CLI_BUFFER_SIZE) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); + } + + SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO); + SSVAL(state->param, 0, 0); + SSVAL(state->param, 2, SMB_REQUEST_TRANSPORT_ENCRYPTION); + + subreq = smb1cli_trans_send(state, ev, cli->conn, + SMBtrans2, + 0, 0, /* _flags */ + 0, 0, /* _flags2 */ + cli->timeout, + cli->smb1.pid, + cli->smb1.tcon, + cli->smb1.session, + NULL, /* pipe_name */ + 0, /* fid */ + 0, /* function */ + 0, /* flags */ + state->setup, 1, 0, + state->param, 4, 2, + in.data, in.length, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_smb1_setup_encryption_blob_done, + req); + + return req; +} + +static void cli_smb1_setup_encryption_blob_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct cli_smb1_setup_encryption_blob_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_blob_state); + uint8_t *rparam=NULL, *rdata=NULL; + uint32_t num_rparam, num_rdata; + NTSTATUS status; + + status = smb1cli_trans_recv(subreq, state, + NULL, /* recv_flags */ + NULL, 0, NULL, /* rsetup */ + &rparam, 0, &num_rparam, + &rdata, 0, &num_rdata); + TALLOC_FREE(subreq); + state->status = status; + if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + status = NT_STATUS_OK; + } + if (tevent_req_nterror(req, status)) { + return; + } + + if (num_rparam == 2) { + state->enc_ctx_id = SVAL(rparam, 0); + } + TALLOC_FREE(rparam); + + state->out = data_blob_const(rdata, num_rdata); + + tevent_req_done(req); +} + +static NTSTATUS cli_smb1_setup_encryption_blob_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + DATA_BLOB *out, + uint16_t *enc_ctx_id) +{ + struct cli_smb1_setup_encryption_blob_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_blob_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + status = state->status; + + *out = state->out; + talloc_steal(mem_ctx, out->data); + + *enc_ctx_id = state->enc_ctx_id; + + tevent_req_received(req); + return status; +} + +struct cli_smb1_setup_encryption_state { + struct tevent_context *ev; + struct cli_state *cli; + struct smb_trans_enc_state *es; + DATA_BLOB blob_in; + DATA_BLOB blob_out; + bool local_ready; + bool remote_ready; +}; + +static void cli_smb1_setup_encryption_local_next(struct tevent_req *req); +static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq); +static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req); +static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq); +static void cli_smb1_setup_encryption_ready(struct tevent_req *req); + +static struct tevent_req *cli_smb1_setup_encryption_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + struct cli_credentials *creds) +{ + struct tevent_req *req = NULL; + struct cli_smb1_setup_encryption_state *state = NULL; + struct auth_generic_state *ags = NULL; + const DATA_BLOB *b = NULL; + bool auth_requested = false; + const char *target_service = NULL; + const char *target_hostname = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb1_setup_encryption_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + auth_requested = cli_credentials_authentication_requested(creds); + if (!auth_requested) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER_MIX); + return tevent_req_post(req, ev); + } + + target_service = "cifs"; + target_hostname = smbXcli_conn_remote_name(cli->conn); + + status = cli_session_creds_prepare_krb5(cli, creds); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->es = talloc_zero(state, struct smb_trans_enc_state); + if (tevent_req_nomem(state->es, req)) { + return tevent_req_post(req, ev); + } + + status = auth_generic_client_prepare(state->es, &ags); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + gensec_want_feature(ags->gensec_security, + GENSEC_FEATURE_SIGN); + gensec_want_feature(ags->gensec_security, + GENSEC_FEATURE_SEAL); + + status = auth_generic_set_creds(ags, creds); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + if (target_service != NULL) { + status = gensec_set_target_service(ags->gensec_security, + target_service); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + } + + if (target_hostname != NULL) { + status = gensec_set_target_hostname(ags->gensec_security, + target_hostname); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + } + + gensec_set_max_update_size(ags->gensec_security, + CLI_BUFFER_SIZE); + + b = smbXcli_conn_server_gss_blob(state->cli->conn); + if (b != NULL) { + state->blob_in = *b; + } + + status = auth_generic_client_start(ags, GENSEC_OID_SPNEGO); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + /* + * We only need the gensec_security part from here. + */ + state->es->gensec_security = talloc_move(state->es, + &ags->gensec_security); + TALLOC_FREE(ags); + + cli_smb1_setup_encryption_local_next(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void cli_smb1_setup_encryption_local_next(struct tevent_req *req) +{ + struct cli_smb1_setup_encryption_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_state); + struct tevent_req *subreq = NULL; + + if (state->local_ready) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + subreq = gensec_update_send(state, state->ev, + state->es->gensec_security, + state->blob_in); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb1_setup_encryption_local_done, req); +} + +static void cli_smb1_setup_encryption_local_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct cli_smb1_setup_encryption_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_state); + NTSTATUS status; + + status = gensec_update_recv(subreq, state, &state->blob_out); + TALLOC_FREE(subreq); + state->blob_in = data_blob_null; + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) + { + tevent_req_nterror(req, status); + return; + } + + if (NT_STATUS_IS_OK(status)) { + state->local_ready = true; + } + + /* + * We always get NT_STATUS_OK from the server even if it is not ready. + * So guess the server is ready when we are ready and already sent + * our last blob to the server. + */ + if (state->local_ready && state->blob_out.length == 0) { + state->remote_ready = true; + } + + if (state->local_ready && state->remote_ready) { + cli_smb1_setup_encryption_ready(req); + return; + } + + cli_smb1_setup_encryption_remote_next(req); +} + +static void cli_smb1_setup_encryption_remote_next(struct tevent_req *req) +{ + struct cli_smb1_setup_encryption_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_state); + struct tevent_req *subreq = NULL; + + if (state->remote_ready) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + subreq = cli_smb1_setup_encryption_blob_send(state, state->ev, + state->cli, state->blob_out); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_smb1_setup_encryption_remote_done, + req); +} + +static void cli_smb1_setup_encryption_remote_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct cli_smb1_setup_encryption_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_state); + NTSTATUS status; + + status = cli_smb1_setup_encryption_blob_recv(subreq, state, + &state->blob_in, + &state->es->enc_ctx_num); + TALLOC_FREE(subreq); + data_blob_free(&state->blob_out); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) + { + tevent_req_nterror(req, status); + return; + } + + /* + * We always get NT_STATUS_OK even if the server is not ready. + * So guess the server is ready when we are ready and sent + * our last blob to the server. + */ + if (state->local_ready) { + state->remote_ready = true; + } + + if (state->local_ready && state->remote_ready) { + cli_smb1_setup_encryption_ready(req); + return; + } + + cli_smb1_setup_encryption_local_next(req); +} + +static void cli_smb1_setup_encryption_ready(struct tevent_req *req) +{ + struct cli_smb1_setup_encryption_state *state = + tevent_req_data(req, + struct cli_smb1_setup_encryption_state); + struct smb_trans_enc_state *es = NULL; + + if (state->blob_in.length != 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (state->blob_out.length != 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + es = talloc_move(state->cli->conn, &state->es); + es->enc_on = true; + smb1cli_conn_set_encryption(state->cli->conn, es); + es = NULL; + + tevent_req_done(req); +} + +static NTSTATUS cli_smb1_setup_encryption_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli, + struct cli_credentials *creds) +{ + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_smb1_setup_encryption_send(ev, ev, cli, creds); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb1_setup_encryption_recv(req); + fail: + TALLOC_FREE(ev); + return status; +} + +/** + establishes a connection right up to doing tconX, password specified. + @param output_cli A fully initialised cli structure, non-null only on success + @param dest_host The netbios name of the remote host + @param dest_ip (optional) The the destination IP, NULL for name based lookup + @param port (optional) The destination port (0 for default) + @param service (optional) The share to make the connection to. Should be 'unqualified' in any way. + @param service_type The 'type' of serivice. + @param creds The used user credentials +*/ + +struct cli_full_connection_creds_state { + struct tevent_context *ev; + const char *service; + const char *service_type; + struct cli_credentials *creds; + int flags; + struct cli_state *cli; +}; + +static int cli_full_connection_creds_state_destructor( + struct cli_full_connection_creds_state *s) +{ + if (s->cli != NULL) { + cli_shutdown(s->cli); + s->cli = NULL; + } + return 0; +} + +static void cli_full_connection_creds_conn_done(struct tevent_req *subreq); +static void cli_full_connection_creds_sess_start(struct tevent_req *req); +static void cli_full_connection_creds_sess_done(struct tevent_req *subreq); +static void cli_full_connection_creds_enc_start(struct tevent_req *req); +static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq); +static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq); +static void cli_full_connection_creds_enc_done(struct tevent_req *subreq); +static void cli_full_connection_creds_enc_tdis(struct tevent_req *req); +static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq); +static void cli_full_connection_creds_tcon_start(struct tevent_req *req); +static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq); + +struct tevent_req *cli_full_connection_creds_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *my_name, const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + const char *service, const char *service_type, + struct cli_credentials *creds, + int flags) +{ + struct tevent_req *req, *subreq; + struct cli_full_connection_creds_state *state; + enum smb_signing_setting signing_state; + enum smb_encryption_setting encryption_state = + cli_credentials_get_smb_encryption(creds); + + req = tevent_req_create(mem_ctx, &state, + struct cli_full_connection_creds_state); + if (req == NULL) { + return NULL; + } + talloc_set_destructor(state, cli_full_connection_creds_state_destructor); + + state->ev = ev; + state->service = service; + state->service_type = service_type; + state->creds = creds; + state->flags = flags; + + if (flags & CLI_FULL_CONNECTION_IPC) { + signing_state = cli_credentials_get_smb_ipc_signing(creds); + } else { + signing_state = cli_credentials_get_smb_signing(creds); + } + + if (encryption_state == SMB_ENCRYPTION_REQUIRED) { + if (flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK) { + encryption_state = SMB_ENCRYPTION_DESIRED; + } + } + + if (encryption_state >= SMB_ENCRYPTION_DESIRED) { + signing_state = SMB_SIGNING_REQUIRED; + } + + subreq = cli_start_connection_send( + state, ev, my_name, dest_host, dest_ss, port, + signing_state, flags); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_full_connection_creds_conn_done, + req); + return req; +} + +static void cli_full_connection_creds_conn_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + NTSTATUS status; + + status = cli_start_connection_recv(subreq, &state->cli); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + cli_full_connection_creds_sess_start(req); +} + +static void cli_full_connection_creds_sess_start(struct tevent_req *req) +{ + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + struct tevent_req *subreq = NULL; + + subreq = cli_session_setup_creds_send( + state, state->ev, state->cli, state->creds); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_full_connection_creds_sess_done, + req); +} + +static void cli_full_connection_creds_sess_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + NTSTATUS status; + + status = cli_session_setup_creds_recv(subreq); + TALLOC_FREE(subreq); + + if (!NT_STATUS_IS_OK(status) && + (state->flags & CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK)) { + + state->flags &= ~CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK; + + state->creds = cli_credentials_init_anon(state); + if (tevent_req_nomem(state->creds, req)) { + return; + } + + cli_full_connection_creds_sess_start(req); + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + + cli_full_connection_creds_enc_start(req); +} + +static void cli_full_connection_creds_enc_start(struct tevent_req *req) +{ + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + enum smb_encryption_setting encryption_state = + cli_credentials_get_smb_encryption(state->creds); + struct tevent_req *subreq = NULL; + NTSTATUS status; + + if (encryption_state < SMB_ENCRYPTION_DESIRED) { + cli_full_connection_creds_tcon_start(req); + return; + } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = smb2cli_session_encryption_on(state->cli->smb2.session); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + if (encryption_state < SMB_ENCRYPTION_REQUIRED) { + cli_full_connection_creds_tcon_start(req); + return; + } + d_printf("Encryption required and " + "server doesn't support " + "SMB3 encryption - failing connect\n"); + tevent_req_nterror(req, status); + return; + } else if (!NT_STATUS_IS_OK(status)) { + d_printf("Encryption required and " + "setup failed with error %s.\n", + nt_errstr(status)); + tevent_req_nterror(req, status); + return; + } + + cli_full_connection_creds_tcon_start(req); + return; + } + + if (!SERVER_HAS_UNIX_CIFS(state->cli)) { + if (encryption_state < SMB_ENCRYPTION_REQUIRED) { + cli_full_connection_creds_tcon_start(req); + return; + } + + status = NT_STATUS_NOT_SUPPORTED; + d_printf("Encryption required and " + "server doesn't support " + "SMB1 Unix Extensions - failing connect\n"); + tevent_req_nterror(req, status); + return; + } + + /* + * We do a tcon on IPC$ just to setup the encryption, + * the real tcon will be encrypted then. + */ + subreq = cli_tree_connect_send(state, state->ev, state->cli, + "IPC$", "IPC", NULL); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_full_connection_creds_enc_tcon, req); +} + +static void cli_full_connection_creds_enc_tcon(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + NTSTATUS status; + + status = cli_tree_connect_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_unix_extensions_version_send(state, state->ev, state->cli); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_full_connection_creds_enc_ver, req); +} + +static void cli_full_connection_creds_enc_ver(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + enum smb_encryption_setting encryption_state = + cli_credentials_get_smb_encryption(state->creds); + uint16_t major, minor; + uint32_t caplow, caphigh; + NTSTATUS status; + + status = cli_unix_extensions_version_recv(subreq, + &major, &minor, + &caplow, + &caphigh); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + if (encryption_state < SMB_ENCRYPTION_REQUIRED) { + /* disconnect ipc$ followed by the real tree connect */ + cli_full_connection_creds_enc_tdis(req); + return; + } + DEBUG(10, ("%s: cli_unix_extensions_version " + "returned %s\n", __func__, nt_errstr(status))); + tevent_req_nterror(req, NT_STATUS_UNKNOWN_REVISION); + return; + } + + if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) { + if (encryption_state < SMB_ENCRYPTION_REQUIRED) { + /* disconnect ipc$ followed by the real tree connect */ + cli_full_connection_creds_enc_tdis(req); + return; + } + DEBUG(10, ("%s: CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP " + "not supported\n", __func__)); + tevent_req_nterror(req, NT_STATUS_UNSUPPORTED_COMPRESSION); + return; + } + + subreq = cli_smb1_setup_encryption_send(state, state->ev, + state->cli, + state->creds); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_full_connection_creds_enc_done, + req); +} + +static void cli_full_connection_creds_enc_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb1_setup_encryption_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* disconnect ipc$ followed by the real tree connect */ + cli_full_connection_creds_enc_tdis(req); +} + +static void cli_full_connection_creds_enc_tdis(struct tevent_req *req) +{ + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + struct tevent_req *subreq = NULL; + + subreq = cli_tdis_send(state, state->ev, state->cli); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_full_connection_creds_enc_finished, + req); +} + +static void cli_full_connection_creds_enc_finished(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_tdis_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + cli_full_connection_creds_tcon_start(req); +} + +static void cli_full_connection_creds_tcon_start(struct tevent_req *req) +{ + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + struct tevent_req *subreq = NULL; + const char *password = NULL; + + if (state->service == NULL) { + tevent_req_done(req); + return; + } + + password = cli_credentials_get_password(state->creds); + + subreq = cli_tree_connect_send(state, state->ev, + state->cli, + state->service, + state->service_type, + password); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, + cli_full_connection_creds_tcon_done, + req); +} + +static void cli_full_connection_creds_tcon_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_tree_connect_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req, + struct cli_state **output_cli) +{ + struct cli_full_connection_creds_state *state = tevent_req_data( + req, struct cli_full_connection_creds_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *output_cli = state->cli; + talloc_set_destructor(state, NULL); + return NT_STATUS_OK; +} + +NTSTATUS cli_full_connection_creds(struct cli_state **output_cli, + const char *my_name, + const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + const char *service, const char *service_type, + struct cli_credentials *creds, + int flags) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_full_connection_creds_send( + ev, ev, my_name, dest_host, dest_ss, port, service, + service_type, creds, flags); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_full_connection_creds_recv(req, output_cli); + fail: + TALLOC_FREE(ev); + return status; +} + +/**************************************************************************** + Send an old style tcon. +****************************************************************************/ +struct cli_raw_tcon_state { + uint16_t *ret_vwv; +}; + +static void cli_raw_tcon_done(struct tevent_req *subreq); + +static struct tevent_req *cli_raw_tcon_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + const char *service, const char *pass, const char *dev) +{ + struct tevent_req *req, *subreq; + struct cli_raw_tcon_state *state; + uint8_t *bytes; + + req = tevent_req_create(mem_ctx, &state, struct cli_raw_tcon_state); + if (req == NULL) { + return NULL; + } + + if (!lp_client_plaintext_auth() && (*pass)) { + DEBUG(1, ("Server requested PLAINTEXT password but 'client plaintext auth = no'" + " or 'client ntlmv2 auth = yes'\n")); + tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED); + return tevent_req_post(req, ev); + } + + TALLOC_FREE(cli->smb1.tcon); + cli->smb1.tcon = smbXcli_tcon_create(cli); + if (tevent_req_nomem(cli->smb1.tcon, req)) { + return tevent_req_post(req, ev); + } + smb1cli_tcon_set_id(cli->smb1.tcon, UINT16_MAX); + + bytes = talloc_array(state, uint8_t, 0); + bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), + service, strlen(service)+1, NULL); + bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), + pass, strlen(pass)+1, NULL); + bytes = smb_bytes_push_bytes(bytes, 4, NULL, 0); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), + dev, strlen(dev)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + subreq = cli_smb_send(state, ev, cli, SMBtcon, 0, 0, 0, NULL, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_raw_tcon_done, req); + return req; +} + +static void cli_raw_tcon_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_raw_tcon_state *state = tevent_req_data( + req, struct cli_raw_tcon_state); + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 2, NULL, &state->ret_vwv, + NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS cli_raw_tcon_recv(struct tevent_req *req, + uint16_t *max_xmit, uint16_t *tid) +{ + struct cli_raw_tcon_state *state = tevent_req_data( + req, struct cli_raw_tcon_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *max_xmit = SVAL(state->ret_vwv + 0, 0); + *tid = SVAL(state->ret_vwv + 1, 0); + return NT_STATUS_OK; +} + +NTSTATUS cli_raw_tcon(struct cli_state *cli, + const char *service, const char *pass, const char *dev, + uint16_t *max_xmit, uint16_t *tid) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_raw_tcon_send(ev, ev, cli, service, pass, dev); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_raw_tcon_recv(req, max_xmit, tid); +fail: + TALLOC_FREE(ev); + return status; +} + +/* Return a cli_state pointing at the IPC$ share for the given server */ + +struct cli_state *get_ipc_connect(char *server, + struct sockaddr_storage *server_ss, + struct cli_credentials *creds) +{ + struct cli_state *cli; + NTSTATUS nt_status; + uint32_t flags = CLI_FULL_CONNECTION_ANONYMOUS_FALLBACK; + + flags |= CLI_FULL_CONNECTION_FORCE_SMB1; + flags |= CLI_FULL_CONNECTION_IPC; + + nt_status = cli_full_connection_creds(&cli, NULL, server, server_ss, 0, "IPC$", "IPC", + creds, + flags); + + if (NT_STATUS_IS_OK(nt_status)) { + return cli; + } + if (is_ipaddress(server)) { + /* windows 9* needs a correct NMB name for connections */ + fstring remote_name; + + if (name_status_find("*", 0, 0, server_ss, remote_name)) { + cli = get_ipc_connect(remote_name, server_ss, creds); + if (cli) + return cli; + } + } + return NULL; +} + +/* + * Given the IP address of a master browser on the network, return its + * workgroup and connect to it. + * + * This function is provided to allow additional processing beyond what + * get_ipc_connect_master_ip_bcast() does, e.g. to retrieve the list of master + * browsers and obtain each master browsers' list of domains (in case the + * first master browser is recently on the network and has not yet + * synchronized with other master browsers and therefore does not yet have the + * entire network browse list) + */ + +struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx, + struct sockaddr_storage *mb_ip, + struct cli_credentials *creds, + char **pp_workgroup_out) +{ + char addr[INET6_ADDRSTRLEN]; + fstring name; + struct cli_state *cli; + struct sockaddr_storage server_ss; + + *pp_workgroup_out = NULL; + + print_sockaddr(addr, sizeof(addr), mb_ip); + DEBUG(99, ("Looking up name of master browser %s\n", + addr)); + + /* + * Do a name status query to find out the name of the master browser. + * We use <01><02>__MSBROWSE__<02>#01 if *#00 fails because a domain + * master browser will not respond to a wildcard query (or, at least, + * an NT4 server acting as the domain master browser will not). + * + * We might be able to use ONLY the query on MSBROWSE, but that's not + * yet been tested with all Windows versions, so until it is, leave + * the original wildcard query as the first choice and fall back to + * MSBROWSE if the wildcard query fails. + */ + if (!name_status_find("*", 0, 0x1d, mb_ip, name) && + !name_status_find(MSBROWSE, 1, 0x1d, mb_ip, name)) { + + DEBUG(99, ("Could not retrieve name status for %s\n", + addr)); + return NULL; + } + + if (!find_master_ip(name, &server_ss)) { + DEBUG(99, ("Could not find master ip for %s\n", name)); + return NULL; + } + + *pp_workgroup_out = talloc_strdup(ctx, name); + + DEBUG(4, ("found master browser %s, %s\n", name, addr)); + + print_sockaddr(addr, sizeof(addr), &server_ss); + cli = get_ipc_connect(addr, &server_ss, creds); + + return cli; +} diff --git a/source3/libsmb/clidfs.c b/source3/libsmb/clidfs.c new file mode 100644 index 0000000..5288a7e --- /dev/null +++ b/source3/libsmb/clidfs.c @@ -0,0 +1,1333 @@ +/* + Unix SMB/CIFS implementation. + client connect/disconnect routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Gerald (Jerry) Carter 2004 + Copyright (C) Jeremy Allison 2007-2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmb/clirap.h" +#include "msdfs.h" +#include "trans2.h" +#include "libsmb/nmblib.h" +#include "../libcli/smb/smbXcli_base.h" +#include "auth/credentials/credentials.h" + +/******************************************************************** + Important point. + + DFS paths are *always* of the form \server\share\<pathname> (the \ characters + are not C escaped here). + + - but if we're using POSIX paths then <pathname> may contain + '/' separators, not '\\' separators. So cope with '\\' or '/' + as a separator when looking at the pathname part.... JRA. +********************************************************************/ + +/******************************************************************** + Ensure a connection is encrypted. +********************************************************************/ + +static NTSTATUS cli_cm_force_encryption_creds(struct cli_state *c, + struct cli_credentials *creds, + const char *sharename) +{ + uint16_t major, minor; + uint32_t caplow, caphigh; + NTSTATUS status; + bool temp_ipc = false; + + if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { + status = smb2cli_session_encryption_on(c->smb2.session); + if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED)) { + d_printf("Encryption required and " + "server doesn't support " + "SMB3 encryption - failing connect\n"); + } else if (!NT_STATUS_IS_OK(status)) { + d_printf("Encryption required and " + "setup failed with error %s.\n", + nt_errstr(status)); + } + return status; + } + + if (!SERVER_HAS_UNIX_CIFS(c)) { + d_printf("Encryption required and " + "server that doesn't support " + "UNIX extensions - failing connect\n"); + return NT_STATUS_NOT_SUPPORTED; + } + + if (c->smb1.tcon == NULL) { + status = cli_tree_connect_creds(c, "IPC$", "IPC", creds); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Encryption required and " + "can't connect to IPC$ to check " + "UNIX CIFS extensions.\n"); + return NT_STATUS_UNKNOWN_REVISION; + } + temp_ipc = true; + } + + status = cli_unix_extensions_version(c, &major, &minor, &caplow, + &caphigh); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Encryption required and " + "can't get UNIX CIFS extensions " + "version from server.\n"); + if (temp_ipc) { + cli_tdis(c); + } + return NT_STATUS_UNKNOWN_REVISION; + } + + if (!(caplow & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)) { + d_printf("Encryption required and " + "share %s doesn't support " + "encryption.\n", sharename); + if (temp_ipc) { + cli_tdis(c); + } + return NT_STATUS_UNSUPPORTED_COMPRESSION; + } + + status = cli_smb1_setup_encryption(c, creds); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Encryption required and " + "setup failed with error %s.\n", + nt_errstr(status)); + if (temp_ipc) { + cli_tdis(c); + } + return status; + } + + if (temp_ipc) { + cli_tdis(c); + } + return NT_STATUS_OK; +} + +/******************************************************************** + Return a connection to a server. +********************************************************************/ + +static NTSTATUS do_connect(TALLOC_CTX *ctx, + const char *server, + const char *share, + struct cli_credentials *creds, + const struct sockaddr_storage *dest_ss, + int port, + int name_type, + struct cli_state **pcli) +{ + struct cli_state *c = NULL; + char *servicename; + char *sharename; + char *newserver, *newshare; + NTSTATUS status; + int flags = 0; + enum protocol_types protocol = PROTOCOL_NONE; + enum smb_signing_setting signing_state = + cli_credentials_get_smb_signing(creds); + enum smb_encryption_setting encryption_state = + cli_credentials_get_smb_encryption(creds); + + if (encryption_state >= SMB_ENCRYPTION_DESIRED) { + signing_state = SMB_SIGNING_REQUIRED; + } + + /* make a copy so we don't modify the global string 'service' */ + servicename = talloc_strdup(ctx,share); + if (!servicename) { + return NT_STATUS_NO_MEMORY; + } + sharename = servicename; + if (*sharename == '\\') { + sharename += 2; + if (server == NULL) { + server = sharename; + } + sharename = strchr_m(sharename,'\\'); + if (!sharename) { + return NT_STATUS_NO_MEMORY; + } + *sharename = 0; + sharename++; + } + if (server == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = cli_connect_nb( + server, dest_ss, port, name_type, NULL, + signing_state, + flags, &c); + + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + DBG_ERR("NetBIOS support disabled, unable to connect"); + } + + DBG_WARNING("Connection to %s failed (Error %s)\n", + server, + nt_errstr(status)); + return status; + } + + DEBUG(4,(" session request ok\n")); + + status = smbXcli_negprot(c->conn, c->timeout, + lp_client_min_protocol(), + lp_client_max_protocol()); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("protocol negotiation failed: %s\n", + nt_errstr(status)); + cli_shutdown(c); + return status; + } + protocol = smbXcli_conn_protocol(c->conn); + DEBUG(4,(" negotiated dialect[%s] against server[%s]\n", + smb_protocol_types_string(protocol), + smbXcli_conn_remote_name(c->conn))); + + if (protocol >= PROTOCOL_SMB2_02) { + /* Ensure we ask for some initial credits. */ + smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS); + } + + status = cli_session_setup_creds(c, creds); + if (!NT_STATUS_IS_OK(status)) { + /* If a password was not supplied then + * try again with a null username. */ + if (encryption_state == SMB_ENCRYPTION_REQUIRED || + smbXcli_conn_signing_mandatory(c->conn) || + cli_credentials_authentication_requested(creds) || + cli_credentials_is_anonymous(creds) || + !NT_STATUS_IS_OK(status = cli_session_setup_anon(c))) + { + d_printf("session setup failed: %s\n", + nt_errstr(status)); + if (NT_STATUS_EQUAL(status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) + d_printf("did you forget to run kinit?\n"); + cli_shutdown(c); + return status; + } + d_printf("Anonymous login successful\n"); + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("cli_init_creds() failed: %s\n", nt_errstr(status))); + cli_shutdown(c); + return status; + } + + DEBUG(4,(" session setup ok\n")); + + if (encryption_state >= SMB_ENCRYPTION_DESIRED) { + status = cli_cm_force_encryption_creds(c, + creds, + sharename); + if (!NT_STATUS_IS_OK(status)) { + switch (encryption_state) { + case SMB_ENCRYPTION_DESIRED: + break; + case SMB_ENCRYPTION_REQUIRED: + default: + cli_shutdown(c); + return status; + } + } + } + + /* 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 (smbXcli_conn_dfs_supported(c->conn) && + cli_check_msdfs_proxy(ctx, c, sharename, + &newserver, &newshare, + creds)) { + cli_shutdown(c); + return do_connect(ctx, newserver, + newshare, creds, + NULL, port, name_type, pcli); + } + + /* must be a normal share */ + + status = cli_tree_connect_creds(c, sharename, "?????", creds); + if (!NT_STATUS_IS_OK(status)) { + d_printf("tree connect failed: %s\n", nt_errstr(status)); + cli_shutdown(c); + return status; + } + + DEBUG(4,(" tconx ok\n")); + *pcli = c; + return NT_STATUS_OK; +} + +/******************************************************************** + Add a new connection to the list. + referring_cli == NULL means a new initial connection. +********************************************************************/ + +static NTSTATUS cli_cm_connect(TALLOC_CTX *ctx, + struct cli_state *referring_cli, + const char *server, + const char *share, + struct cli_credentials *creds, + const struct sockaddr_storage *dest_ss, + int port, + int name_type, + struct cli_state **pcli) +{ + struct cli_state *cli = NULL; + NTSTATUS status; + + status = do_connect(ctx, server, share, + creds, + dest_ss, port, name_type, &cli); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* + * This can't happen, this test is to satisfy static + * checkers (clang) + */ + if (cli == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* Enter into the list. */ + if (referring_cli) { + DLIST_ADD_END(referring_cli, cli); + } + + if (referring_cli && referring_cli->requested_posix_capabilities) { + uint16_t major, minor; + uint32_t caplow, caphigh; + status = cli_unix_extensions_version(cli, &major, &minor, + &caplow, &caphigh); + if (NT_STATUS_IS_OK(status)) { + cli_set_unix_extensions_capabilities(cli, + major, minor, + caplow, caphigh); + } + } + + *pcli = cli; + return NT_STATUS_OK; +} + +/******************************************************************** + Return a connection to a server on a particular share. +********************************************************************/ + +static struct cli_state *cli_cm_find(struct cli_state *cli, + const char *server, + const char *share) +{ + struct cli_state *p; + + if (cli == NULL) { + return NULL; + } + + /* Search to the start of the list. */ + for (p = cli; p; p = DLIST_PREV(p)) { + const char *remote_name = + smbXcli_conn_remote_name(p->conn); + + if (strequal(server, remote_name) && + strequal(share,p->share)) { + return p; + } + } + + /* Search to the end of the list. */ + for (p = cli->next; p; p = p->next) { + const char *remote_name = + smbXcli_conn_remote_name(p->conn); + + if (strequal(server, remote_name) && + strequal(share,p->share)) { + return p; + } + } + + return NULL; +} + +/**************************************************************************** + Open a client connection to a \\server\share. +****************************************************************************/ + +NTSTATUS cli_cm_open(TALLOC_CTX *ctx, + struct cli_state *referring_cli, + const char *server, + const char *share, + struct cli_credentials *creds, + const struct sockaddr_storage *dest_ss, + int port, + int name_type, + struct cli_state **pcli) +{ + /* Try to reuse an existing connection in this list. */ + struct cli_state *c = cli_cm_find(referring_cli, server, share); + NTSTATUS status; + + if (c) { + *pcli = c; + return NT_STATUS_OK; + } + + if (creds == NULL) { + /* Can't do a new connection + * without auth info. */ + d_printf("cli_cm_open() Unable to open connection [\\%s\\%s] " + "without client credentials\n", + server, share ); + return NT_STATUS_INVALID_PARAMETER; + } + + status = cli_cm_connect(ctx, + referring_cli, + server, + share, + creds, + dest_ss, + port, + name_type, + &c); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + *pcli = c; + return NT_STATUS_OK; +} + +/**************************************************************************** +****************************************************************************/ + +void cli_cm_display(struct cli_state *cli) +{ + int i; + + for (i=0; cli; cli = cli->next,i++ ) { + d_printf("%d:\tserver=%s, share=%s\n", + i, smbXcli_conn_remote_name(cli->conn), cli->share); + } +} + +/**************************************************************************** +****************************************************************************/ + +/**************************************************************************** +****************************************************************************/ + +#if 0 +void cli_cm_set_credentials(struct user_auth_info *auth_info) +{ + SAFE_FREE(cm_creds.username); + cm_creds.username = SMB_STRDUP(get_cmdline_auth_info_username( + auth_info)); + + if (get_cmdline_auth_info_got_pass(auth_info)) { + cm_set_password(get_cmdline_auth_info_password(auth_info)); + } + + cm_creds.use_kerberos = get_cmdline_auth_info_use_kerberos(auth_info); + cm_creds.fallback_after_kerberos = false; + cm_creds.signing_state = get_cmdline_auth_info_signing_state(auth_info); +} +#endif + +/********************************************************************** + split a dfs path into the server, share name, and extrapath components +**********************************************************************/ + +static bool split_dfs_path(TALLOC_CTX *ctx, + const char *nodepath, + char **pp_server, + char **pp_share, + char **pp_extrapath) +{ + char *p, *q; + char *path; + + *pp_server = NULL; + *pp_share = NULL; + *pp_extrapath = NULL; + + path = talloc_strdup(ctx, nodepath); + if (!path) { + goto fail; + } + + if ( path[0] != '\\' ) { + goto fail; + } + + p = strchr_m( path + 1, '\\' ); + if ( !p ) { + goto fail; + } + + *p = '\0'; + p++; + + /* Look for any extra/deep path */ + q = strchr_m(p, '\\'); + if (q != NULL) { + *q = '\0'; + q++; + *pp_extrapath = talloc_strdup(ctx, q); + } else { + *pp_extrapath = talloc_strdup(ctx, ""); + } + if (*pp_extrapath == NULL) { + goto fail; + } + + *pp_share = talloc_strdup(ctx, p); + if (*pp_share == NULL) { + goto fail; + } + + *pp_server = talloc_strdup(ctx, &path[1]); + if (*pp_server == NULL) { + goto fail; + } + + TALLOC_FREE(path); + return true; + +fail: + TALLOC_FREE(*pp_share); + TALLOC_FREE(*pp_extrapath); + TALLOC_FREE(path); + return false; +} + +/**************************************************************************** + Return the original path truncated at the directory component before + the first wildcard character. Trust the caller to provide a NULL + terminated string +****************************************************************************/ + +static char *clean_path(TALLOC_CTX *ctx, const char *path) +{ + size_t len; + char *p1, *p2, *p; + char *path_out; + + /* No absolute paths. */ + while (IS_DIRECTORY_SEP(*path)) { + path++; + } + + path_out = talloc_strdup(ctx, path); + if (!path_out) { + return NULL; + } + + p1 = strchr_m(path_out, '*'); + p2 = strchr_m(path_out, '?'); + + if (p1 || p2) { + if (p1 && p2) { + p = MIN(p1,p2); + } else if (!p1) { + p = p2; + } else { + p = p1; + } + *p = '\0'; + + /* Now go back to the start of this component. */ + p1 = strrchr_m(path_out, '/'); + p2 = strrchr_m(path_out, '\\'); + p = MAX(p1,p2); + if (p) { + *p = '\0'; + } + } + + /* Strip any trailing separator */ + + len = strlen(path_out); + if ( (len > 0) && IS_DIRECTORY_SEP(path_out[len-1])) { + path_out[len-1] = '\0'; + } + + return path_out; +} + +/**************************************************************************** +****************************************************************************/ + +static char *cli_dfs_make_full_path(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *dir) +{ + char path_sep = '\\'; + + /* Ensure the extrapath doesn't start with a separator. */ + while (IS_DIRECTORY_SEP(*dir)) { + dir++; + } + + if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + path_sep = '/'; + } + return talloc_asprintf(ctx, "%c%s%c%s%c%s", + path_sep, + smbXcli_conn_remote_name(cli->conn), + path_sep, + cli->share, + path_sep, + dir); +} + +/******************************************************************** + Get the dfs referral link. +********************************************************************/ + +NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *path, + uint16_t max_referral_level, + struct client_dfs_referral **refs, + size_t *num_refs, + size_t *consumed) +{ + unsigned int param_len = 0; + uint16_t recv_flags2; + uint8_t *param = NULL; + uint8_t *rdata = NULL; + char *p; + char *endp; + smb_ucs2_t *path_ucs; + char *consumed_path = NULL; + uint16_t consumed_ucs; + uint16_t num_referrals; + struct client_dfs_referral *referrals = NULL; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + + *num_refs = 0; + *refs = NULL; + + param = talloc_array(talloc_tos(), uint8_t, 2); + if (!param) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + SSVAL(param, 0, max_referral_level); + + param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn), + path, strlen(path)+1, + NULL); + if (!param) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + param_len = talloc_get_size(param); + path_ucs = (smb_ucs2_t *)¶m[2]; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + DATA_BLOB in_input_buffer; + DATA_BLOB in_output_buffer = data_blob_null; + DATA_BLOB out_input_buffer = data_blob_null; + DATA_BLOB out_output_buffer = data_blob_null; + + in_input_buffer.data = param; + in_input_buffer.length = param_len; + + status = smb2cli_ioctl(cli->conn, + cli->timeout, + cli->smb2.session, + cli->smb2.tcon, + UINT64_MAX, /* in_fid_persistent */ + UINT64_MAX, /* in_fid_volatile */ + FSCTL_DFS_GET_REFERRALS, + 0, /* in_max_input_length */ + &in_input_buffer, + CLI_BUFFER_SIZE, /* in_max_output_length */ + &in_output_buffer, + SMB2_IOCTL_FLAG_IS_FSCTL, + talloc_tos(), + &out_input_buffer, + &out_output_buffer); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + if (out_output_buffer.length < 4) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto out; + } + + recv_flags2 = FLAGS2_UNICODE_STRINGS; + rdata = out_output_buffer.data; + endp = (char *)rdata + out_output_buffer.length; + } else { + unsigned int data_len = 0; + uint16_t setup[1]; + + SSVAL(setup, 0, TRANSACT2_GET_DFS_REFERRAL); + + status = cli_trans(talloc_tos(), cli, SMBtrans2, + NULL, 0xffff, 0, 0, + setup, 1, 0, + param, param_len, 2, + NULL, 0, CLI_BUFFER_SIZE, + &recv_flags2, + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, + &rdata, 4, &data_len); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + endp = (char *)rdata + data_len; + } + + consumed_ucs = SVAL(rdata, 0); + num_referrals = SVAL(rdata, 2); + + /* consumed_ucs is the number of bytes + * of the UCS2 path consumed not counting any + * terminating null. We need to convert + * back to unix charset and count again + * to get the number of bytes consumed from + * the incoming path. */ + + errno = 0; + if (pull_string_talloc(talloc_tos(), + NULL, + 0, + &consumed_path, + path_ucs, + consumed_ucs, + STR_UNICODE) == 0) { + if (errno != 0) { + status = map_nt_error_from_unix(errno); + } else { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + } + goto out; + } + if (consumed_path == NULL) { + status = map_nt_error_from_unix(errno); + goto out; + } + *consumed = strlen(consumed_path); + + if (num_referrals != 0) { + uint16_t ref_version; + uint16_t ref_size; + int i; + uint16_t node_offset; + + referrals = talloc_array(ctx, struct client_dfs_referral, + num_referrals); + + if (!referrals) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + /* start at the referrals array */ + + p = (char *)rdata+8; + 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) { + p += ref_size; + continue; + } + + referrals[i].proximity = SVAL(p, 8); + referrals[i].ttl = SVAL(p, 10); + + if (p + node_offset > endp) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto out; + } + pull_string_talloc(referrals, + (const char *)rdata, + recv_flags2, + &referrals[i].dfspath, + p+node_offset, + PTR_DIFF(endp, p+node_offset), + STR_TERMINATE|STR_UNICODE); + + if (!referrals[i].dfspath) { + status = map_nt_error_from_unix(errno); + goto out; + } + p += ref_size; + } + if (i < num_referrals) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto out; + } + } + + *num_refs = num_referrals; + *refs = referrals; + + out: + + TALLOC_FREE(frame); + return status; +} + +NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *path, + struct client_dfs_referral **refs, + size_t *num_refs, + size_t *consumed) +{ + return cli_dfs_get_referral_ex(ctx, + cli, + path, + 3, + refs, /* Max referral level we want */ + num_refs, + consumed); +} + +static bool cli_conn_have_dfs(struct cli_state *cli) +{ + struct smbXcli_conn *conn = cli->conn; + struct smbXcli_tcon *tcon = NULL; + bool ok; + + if (smbXcli_conn_protocol(conn) < PROTOCOL_SMB2_02) { + uint32_t capabilities = smb1cli_conn_capabilities(conn); + + if ((capabilities & CAP_STATUS32) == 0) { + return false; + } + if ((capabilities & CAP_UNICODE) == 0) { + return false; + } + + tcon = cli->smb1.tcon; + } else { + tcon = cli->smb2.tcon; + } + + ok = smbXcli_tcon_is_dfs_share(tcon); + return ok; +} + +/******************************************************************** +********************************************************************/ +struct cli_dfs_path_split { + char *server; + char *share; + char *extrapath; +}; + +NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, + const char *mountpt, + struct cli_credentials *creds, + struct cli_state *rootcli, + const char *path, + struct cli_state **targetcli, + char **pp_targetpath) +{ + struct client_dfs_referral *refs = NULL; + size_t num_refs = 0; + size_t consumed = 0; + struct cli_state *cli_ipc = NULL; + char *dfs_path = NULL; + char *cleanpath = NULL; + char *extrapath = NULL; + int pathlen; + struct cli_state *newcli = NULL; + struct cli_state *ccli = NULL; + size_t count = 0; + char *newpath = NULL; + char *newmount = NULL; + char *ppath = NULL; + SMB_STRUCT_STAT sbuf; + uint32_t attributes; + NTSTATUS status; + struct smbXcli_tcon *target_tcon = NULL; + struct cli_dfs_path_split *dfs_refs = NULL; + bool ok; + + if ( !rootcli || !path || !targetcli ) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * Avoid more than one leading directory separator + */ + while (IS_DIRECTORY_SEP(path[0]) && IS_DIRECTORY_SEP(path[1])) { + path++; + } + + ok = cli_conn_have_dfs(rootcli); + if (!ok) { + *targetcli = rootcli; + *pp_targetpath = talloc_strdup(ctx, path); + if (!*pp_targetpath) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; + } + + *targetcli = NULL; + + /* Send a trans2_query_path_info to check for a referral. */ + + cleanpath = clean_path(ctx, path); + if (!cleanpath) { + return NT_STATUS_NO_MEMORY; + } + + dfs_path = cli_dfs_make_full_path(ctx, rootcli, cleanpath); + if (!dfs_path) { + return NT_STATUS_NO_MEMORY; + } + + status = cli_qpathinfo_basic( rootcli, dfs_path, &sbuf, &attributes); + if (NT_STATUS_IS_OK(status)) { + /* This is an ordinary path, just return it. */ + *targetcli = rootcli; + *pp_targetpath = talloc_strdup(ctx, path); + if (!*pp_targetpath) { + return NT_STATUS_NO_MEMORY; + } + goto done; + } + + /* Special case where client asked for a path that does not exist */ + + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + *targetcli = rootcli; + *pp_targetpath = talloc_strdup(ctx, path); + if (!*pp_targetpath) { + return NT_STATUS_NO_MEMORY; + } + goto done; + } + + /* We got an error, check for DFS referral. */ + + if (!NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) { + return status; + } + + /* Check for the referral. */ + + status = cli_cm_open(ctx, + rootcli, + smbXcli_conn_remote_name(rootcli->conn), + "IPC$", + creds, + NULL, /* dest_ss not needed, we reuse the transport */ + 0, + 0x20, + &cli_ipc); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = cli_dfs_get_referral(ctx, cli_ipc, dfs_path, &refs, + &num_refs, &consumed); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!num_refs || !refs[0].dfspath) { + return NT_STATUS_NOT_FOUND; + } + + /* + * Bug#10123 - DFS referal entries can be provided in a random order, + * so check the connection cache for each item to avoid unnecessary + * reconnections. + */ + dfs_refs = talloc_array(ctx, struct cli_dfs_path_split, num_refs); + if (dfs_refs == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (count = 0; count < num_refs; count++) { + if (!split_dfs_path(dfs_refs, refs[count].dfspath, + &dfs_refs[count].server, + &dfs_refs[count].share, + &dfs_refs[count].extrapath)) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NOT_FOUND; + } + + ccli = cli_cm_find(rootcli, dfs_refs[count].server, + dfs_refs[count].share); + if (ccli != NULL) { + extrapath = dfs_refs[count].extrapath; + *targetcli = ccli; + break; + } + } + + /* + * If no cached connection was found, then connect to the first live + * referral server in the list. + */ + for (count = 0; (ccli == NULL) && (count < num_refs); count++) { + /* Connect to the target server & share */ + status = cli_cm_connect(ctx, rootcli, + dfs_refs[count].server, + dfs_refs[count].share, + creds, + NULL, /* dest_ss */ + 0, /* port */ + 0x20, + targetcli); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Unable to follow dfs referral [\\%s\\%s]\n", + dfs_refs[count].server, + dfs_refs[count].share); + continue; + } else { + extrapath = dfs_refs[count].extrapath; + break; + } + } + + /* No available referral server for the connection */ + if (*targetcli == NULL) { + TALLOC_FREE(dfs_refs); + return status; + } + + /* Make sure to recreate the original string including any wildcards. */ + + dfs_path = cli_dfs_make_full_path(ctx, rootcli, path); + if (!dfs_path) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NO_MEMORY; + } + pathlen = strlen(dfs_path); + consumed = MIN(pathlen, consumed); + *pp_targetpath = talloc_strdup(ctx, &dfs_path[consumed]); + if (!*pp_targetpath) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NO_MEMORY; + } + dfs_path[consumed] = '\0'; + + /* + * *pp_targetpath is now the unconsumed part of the path. + * dfs_path is now the consumed part of the path + * (in \server\share\path format). + */ + + if (extrapath && strlen(extrapath) > 0) { + /* EMC Celerra NAS version 5.6.50 (at least) doesn't appear to */ + /* put the trailing \ on the path, so to be safe we put one in if needed */ + if (extrapath[strlen(extrapath)-1] != '\\' && **pp_targetpath != '\\') { + *pp_targetpath = talloc_asprintf(ctx, + "%s\\%s", + extrapath, + *pp_targetpath); + } else { + *pp_targetpath = talloc_asprintf(ctx, + "%s%s", + extrapath, + *pp_targetpath); + } + if (!*pp_targetpath) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NO_MEMORY; + } + } + + /* parse out the consumed mount path */ + /* trim off the \server\share\ */ + + ppath = dfs_path; + + if (*ppath != '\\') { + d_printf("cli_resolve_path: " + "dfs_path (%s) not in correct format.\n", + dfs_path ); + TALLOC_FREE(dfs_refs); + return NT_STATUS_NOT_FOUND; + } + + ppath++; /* Now pointing at start of server name. */ + + if ((ppath = strchr_m( dfs_path, '\\' )) == NULL) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NOT_FOUND; + } + + ppath++; /* Now pointing at start of share name. */ + + if ((ppath = strchr_m( ppath+1, '\\' )) == NULL) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NOT_FOUND; + } + + ppath++; /* Now pointing at path component. */ + + newmount = talloc_asprintf(ctx, "%s\\%s", mountpt, ppath ); + if (!newmount) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NOT_FOUND; + } + + /* Check for another dfs referral, note that we are not + checking for loops here. */ + + if (!strequal(*pp_targetpath, "\\") && !strequal(*pp_targetpath, "/")) { + status = cli_resolve_path(ctx, + newmount, + creds, + *targetcli, + *pp_targetpath, + &newcli, + &newpath); + if (NT_STATUS_IS_OK(status)) { + /* + * When cli_resolve_path returns true here it's always + * returning the complete path in newpath, so we're done + * here. + */ + *targetcli = newcli; + *pp_targetpath = newpath; + TALLOC_FREE(dfs_refs); + return status; + } + } + + done: + + if (smbXcli_conn_protocol((*targetcli)->conn) >= PROTOCOL_SMB2_02) { + target_tcon = (*targetcli)->smb2.tcon; + } else { + target_tcon = (*targetcli)->smb1.tcon; + } + + /* If returning true ensure we return a dfs root full path. */ + if (smbXcli_tcon_is_dfs_share(target_tcon)) { + dfs_path = talloc_strdup(ctx, *pp_targetpath); + if (!dfs_path) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NO_MEMORY; + } + *pp_targetpath = cli_dfs_make_full_path(ctx, *targetcli, dfs_path); + if (*pp_targetpath == NULL) { + TALLOC_FREE(dfs_refs); + return NT_STATUS_NO_MEMORY; + } + } + + TALLOC_FREE(dfs_refs); + return NT_STATUS_OK; +} + +/******************************************************************** +********************************************************************/ + +bool cli_check_msdfs_proxy(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *sharename, + char **pp_newserver, + char **pp_newshare, + struct cli_credentials *creds) +{ + struct client_dfs_referral *refs = NULL; + size_t num_refs = 0; + size_t consumed = 0; + char *fullpath = NULL; + bool res; + struct smbXcli_tcon *orig_tcon = NULL; + char *newextrapath = NULL; + NTSTATUS status; + const char *remote_name; + enum smb_encryption_setting encryption_state = + cli_credentials_get_smb_encryption(creds); + + if (!cli || !sharename) { + return false; + } + + remote_name = smbXcli_conn_remote_name(cli->conn); + + /* special case. never check for a referral on the IPC$ share */ + + if (strequal(sharename, "IPC$")) { + return false; + } + + /* send a trans2_query_path_info to check for a referral */ + + fullpath = talloc_asprintf(ctx, "\\%s\\%s", remote_name, sharename); + if (!fullpath) { + return false; + } + + /* Store tcon state. */ + if (cli_state_has_tcon(cli)) { + orig_tcon = cli_state_save_tcon(cli); + if (orig_tcon == NULL) { + return false; + } + } + + /* check for the referral */ + + if (!NT_STATUS_IS_OK(cli_tree_connect(cli, "IPC$", "IPC", NULL))) { + cli_state_restore_tcon(cli, orig_tcon); + return false; + } + + if (encryption_state >= SMB_ENCRYPTION_DESIRED) { + status = cli_cm_force_encryption_creds(cli, creds, "IPC$"); + if (!NT_STATUS_IS_OK(status)) { + switch (encryption_state) { + case SMB_ENCRYPTION_DESIRED: + break; + case SMB_ENCRYPTION_REQUIRED: + default: + /* + * Failed to set up encryption. + * Disconnect the temporary IPC$ + * tcon before restoring the original + * tcon so we don't leak it. + */ + cli_tdis(cli); + cli_state_restore_tcon(cli, orig_tcon); + return false; + } + } + } + + status = cli_dfs_get_referral(ctx, cli, fullpath, &refs, + &num_refs, &consumed); + res = NT_STATUS_IS_OK(status); + + status = cli_tdis(cli); + + cli_state_restore_tcon(cli, orig_tcon); + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + if (!res || !num_refs) { + return false; + } + + if (!refs[0].dfspath) { + return false; + } + + if (!split_dfs_path(ctx, refs[0].dfspath, pp_newserver, + pp_newshare, &newextrapath)) { + return false; + } + + /* check that this is not a self-referral */ + + if (strequal(remote_name, *pp_newserver) && + strequal(sharename, *pp_newshare)) { + return false; + } + + return true; +} + +/******************************************************************** + Windows and NetApp (and arguably the SMB1/2/3 specs) expect a non-DFS + path for the targets of rename and hardlink. If we have been given + a DFS path for these calls, convert it back into a local path by + stripping off the DFS prefix. +********************************************************************/ + +NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + const char **fname_dst_out) +{ + char *dfs_prefix = NULL; + size_t prefix_len = 0; + struct smbXcli_tcon *tcon = NULL; + + if (!smbXcli_conn_dfs_supported(cli->conn)) { + goto copy_fname_out; + } + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + tcon = cli->smb2.tcon; + } else { + tcon = cli->smb1.tcon; + } + if (!smbXcli_tcon_is_dfs_share(tcon)) { + goto copy_fname_out; + } + dfs_prefix = cli_dfs_make_full_path(mem_ctx, cli, ""); + if (dfs_prefix == NULL) { + return NT_STATUS_NO_MEMORY; + } + prefix_len = strlen(dfs_prefix); + if (strncmp(fname_dst, dfs_prefix, prefix_len) != 0) { + /* + * Prefix doesn't match. Assume it was + * already stripped or not added in the + * first place. + */ + goto copy_fname_out; + } + /* Return the trailing name after the prefix. */ + *fname_dst_out = &fname_dst[prefix_len]; + TALLOC_FREE(dfs_prefix); + return NT_STATUS_OK; + + copy_fname_out: + + /* + * No change to the destination name. Just + * point it at the incoming destination name. + */ + *fname_dst_out = fname_dst; + TALLOC_FREE(dfs_prefix); + return NT_STATUS_OK; +} diff --git a/source3/libsmb/clidgram.c b/source3/libsmb/clidgram.c new file mode 100644 index 0000000..ec0b1ef --- /dev/null +++ b/source3/libsmb/clidgram.c @@ -0,0 +1,482 @@ +/* + Unix SMB/CIFS implementation. + client dgram calls + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Richard Sharpe 2001 + Copyright (C) John Terpstra 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "libsmb/clidgram.h" +#include "libsmb/nmblib.h" +#include "libsmb/unexpected.h" +#include "messages.h" +#include "librpc/gen_ndr/samr.h" +#include "../lib/util/pidfile.h" +#include "lib/util/string_wrappers.h" + +/* + * cli_send_mailslot, send a mailslot for client code ... + */ + +static bool cli_prep_mailslot(bool unique, const char *mailslot, + uint16_t priority, + char *buf, int len, + const char *srcname, int src_type, + const char *dstname, int dest_type, + const struct sockaddr_storage *dest_ss, + int dgm_id, + struct packet_struct *p) +{ + struct dgram_packet *dgram = &p->packet.dgram; + char *ptr, *p2; + char tmp[4]; + char addr[INET6_ADDRSTRLEN]; + + ZERO_STRUCTP(p); + + /* + * Next, build the DGRAM ... + */ + + /* DIRECT GROUP or UNIQUE datagram. */ + dgram->header.msg_type = unique ? 0x10 : 0x11; + dgram->header.flags.node_type = M_NODE; + dgram->header.flags.first = True; + dgram->header.flags.more = False; + dgram->header.dgm_id = dgm_id; + /* source ip is filled by nmbd */ + dgram->header.dgm_length = 0; /* Let build_dgram() handle this. */ + dgram->header.packet_offset = 0; + + make_nmb_name(&dgram->source_name,srcname,src_type); + make_nmb_name(&dgram->dest_name,dstname,dest_type); + + ptr = &dgram->data[0]; + + /* Setup the smb part. */ + ptr -= 4; /* XXX Ugliness because of handling of tcp SMB length. */ + memcpy(tmp,ptr,4); + + if (smb_size + 17*2 + strlen(mailslot) + 1 + len > MAX_DGRAM_SIZE) { + DEBUG(0, ("cli_send_mailslot: Cannot write beyond end of packet\n")); + return False; + } + + cli_set_message(ptr,17,strlen(mailslot) + 1 + len,True); + memcpy(ptr,tmp,4); + + SCVAL(ptr,smb_com,SMBtrans); + SSVAL(ptr,smb_vwv1,len); + SSVAL(ptr,smb_vwv11,len); + SSVAL(ptr,smb_vwv12,70 + strlen(mailslot)); + SSVAL(ptr,smb_vwv13,3); + SSVAL(ptr,smb_vwv14,1); + SSVAL(ptr,smb_vwv15,priority); + SSVAL(ptr,smb_vwv16,2); + p2 = smb_buf(ptr); + fstrcpy(p2,mailslot); + p2 = skip_string(ptr,MAX_DGRAM_SIZE,p2); + if (!p2) { + return False; + } + + memcpy(p2,buf,len); + p2 += len; + + dgram->datasize = PTR_DIFF(p2,ptr+4); /* +4 for tcp length. */ + + p->packet_type = DGRAM_PACKET; + p->ip = ((const struct sockaddr_in *)dest_ss)->sin_addr; + p->timestamp = time(NULL); + + DEBUG(4,("send_mailslot: Sending to mailslot %s from %s ", + mailslot, nmb_namestr(&dgram->source_name))); + print_sockaddr(addr, sizeof(addr), dest_ss); + + DEBUGADD(4,("to %s IP %s\n", nmb_namestr(&dgram->dest_name), addr)); + + return true; +} + +static char *mailslot_name(TALLOC_CTX *mem_ctx, struct in_addr dc_ip) +{ + return talloc_asprintf(mem_ctx, "%s%X", + NBT_MAILSLOT_GETDC, dc_ip.s_addr); +} + +static bool prep_getdc_request(const struct sockaddr_storage *dc_ss, + const char *account_name, + uint32_t account_flags, + const char *domain_name, + const struct dom_sid *sid, + uint32_t nt_version, + const char *my_mailslot, + int dgm_id, + struct packet_struct *p) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct nbt_netlogon_packet packet; + struct NETLOGON_SAM_LOGON_REQUEST *s; + enum ndr_err_code ndr_err; + DATA_BLOB blob = data_blob_null; + struct dom_sid my_sid; + bool ret = false; + + ZERO_STRUCT(packet); + ZERO_STRUCT(my_sid); + + if (sid != NULL) { + my_sid = *sid; + } + + packet.command = LOGON_SAM_LOGON_REQUEST; + s = &packet.req.logon; + + s->request_count = 0; + s->computer_name = lp_netbios_name(); + s->user_name = account_name; + s->mailslot_name = my_mailslot; + s->acct_control = account_flags; + s->sid = my_sid; + s->nt_version = nt_version; + s->lmnt_token = 0xffff; + s->lm20_token = 0xffff; + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(nbt_netlogon_packet, &packet); + } + + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &packet, + (ndr_push_flags_fn_t)ndr_push_nbt_netlogon_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + goto fail; + } + + ret = cli_prep_mailslot(false, NBT_MAILSLOT_NTLOGON, 0, + (char *)blob.data, blob.length, + lp_netbios_name(), 0, domain_name, 0x1c, + dc_ss, dgm_id, p); +fail: + TALLOC_FREE(frame); + return ret; +} + +static bool parse_getdc_response( + struct packet_struct *packet, + TALLOC_CTX *mem_ctx, + const char *domain_name, + uint32_t *nt_version, + const char **dc_name, + struct netlogon_samlogon_response **samlogon_response) +{ + DATA_BLOB blob; + struct netlogon_samlogon_response *r; + union dgram_message_body p; + enum ndr_err_code ndr_err; + NTSTATUS status; + + const char *returned_dc = NULL; + const char *returned_domain = NULL; + + blob = data_blob_const(packet->packet.dgram.data, + packet->packet.dgram.datasize); + if (blob.length < 4) { + DEBUG(1, ("invalid length: %d\n", (int)blob.length)); + return false; + } + + if (RIVAL(blob.data,0) != DGRAM_SMB) { + DEBUG(1, ("invalid packet\n")); + return false; + } + + blob.data += 4; + blob.length -= 4; + + ndr_err = ndr_pull_union_blob_all(&blob, mem_ctx, &p, DGRAM_SMB, + (ndr_pull_flags_fn_t)ndr_pull_dgram_smb_packet); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DEBUG(1, ("failed to parse packet\n")); + return false; + } + + if (p.smb.smb_command != SMB_TRANSACTION) { + DEBUG(1, ("invalid smb_command: %d\n", p.smb.smb_command)); + return false; + } + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(dgram_smb_packet, &p); + } + + blob = p.smb.body.trans.data; + + r = talloc_zero(mem_ctx, struct netlogon_samlogon_response); + if (!r) { + return false; + } + + status = pull_netlogon_samlogon_response(&blob, r, r); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(r); + return false; + } + + map_netlogon_samlogon_response(r); + + /* do we still need this ? */ + *nt_version = r->ntver; + + returned_domain = r->data.nt5_ex.domain_name; + returned_dc = r->data.nt5_ex.pdc_name; + + if (!strequal(returned_domain, domain_name)) { + DEBUG(3, ("GetDC: Expected domain %s, got %s\n", + domain_name, returned_domain)); + TALLOC_FREE(r); + return false; + } + + if (*returned_dc == '\\') returned_dc += 1; + if (*returned_dc == '\\') returned_dc += 1; + + *dc_name = talloc_strdup(mem_ctx, returned_dc); + if (!*dc_name) { + TALLOC_FREE(r); + return false; + } + + if (samlogon_response) { + *samlogon_response = r; + } else { + TALLOC_FREE(r); + } + + DEBUG(10, ("GetDC gave name %s for domain %s\n", + *dc_name, returned_domain)); + + return True; +} + +struct nbt_getdc_state { + struct tevent_context *ev; + struct messaging_context *msg_ctx; + struct nb_packet_reader *reader; + const char *my_mailslot; + pid_t nmbd_pid; + + const struct sockaddr_storage *dc_addr; + const char *domain_name; + const struct dom_sid *sid; + uint32_t nt_version; + const char *dc_name; + struct netlogon_samlogon_response *samlogon_response; + + struct packet_struct p; +}; + +static void nbt_getdc_got_reader(struct tevent_req *subreq); +static void nbt_getdc_got_response(struct tevent_req *subreq); + +struct tevent_req *nbt_getdc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context *msg_ctx, + const struct sockaddr_storage *dc_addr, + const char *domain_name, + const struct dom_sid *sid, + const char *account_name, + uint32_t account_flags, + uint32_t nt_version) +{ + struct tevent_req *req, *subreq; + struct nbt_getdc_state *state; + uint16_t dgm_id; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct nbt_getdc_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->msg_ctx = msg_ctx; + state->dc_addr = dc_addr; + state->domain_name = domain_name; + state->sid = sid; + state->nt_version = nt_version; + + if (dc_addr->ss_family != AF_INET) { + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + state->my_mailslot = mailslot_name( + state, ((const struct sockaddr_in *)dc_addr)->sin_addr); + if (tevent_req_nomem(state->my_mailslot, req)) { + return tevent_req_post(req, ev); + } + state->nmbd_pid = pidfile_pid(lp_pid_directory(), "nmbd"); + if (state->nmbd_pid == 0) { + DEBUG(3, ("No nmbd found\n")); + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + generate_random_buffer((uint8_t *)(void *)&dgm_id, sizeof(dgm_id)); + + ok = prep_getdc_request(dc_addr, account_name, account_flags, + domain_name, sid, nt_version, + state->my_mailslot, dgm_id & 0x7fff, + &state->p); + + if (!ok) { + DEBUG(3, ("prep_getdc_request failed\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + subreq = nb_packet_reader_send(state, ev, DGRAM_PACKET, -1, + state->my_mailslot); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, nbt_getdc_got_reader, req); + return req; +} + +static void nbt_getdc_got_reader(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nbt_getdc_state *state = tevent_req_data( + req, struct nbt_getdc_state); + NTSTATUS status; + + status = nb_packet_reader_recv(subreq, state, &state->reader); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + DEBUG(10, ("nb_packet_reader_recv returned %s\n", + nt_errstr(status))); + return; + } + + status = messaging_send_buf( + state->msg_ctx, pid_to_procid(state->nmbd_pid), + MSG_SEND_PACKET, (uint8_t *)&state->p, sizeof(state->p)); + + if (tevent_req_nterror(req, status)) { + DEBUG(10, ("messaging_send_buf returned %s\n", + nt_errstr(status))); + return; + } + subreq = nb_packet_read_send(state, state->ev, state->reader); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nbt_getdc_got_response, req); +} + +static void nbt_getdc_got_response(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nbt_getdc_state *state = tevent_req_data( + req, struct nbt_getdc_state); + struct packet_struct *p; + NTSTATUS status; + bool ret; + + status = nb_packet_read_recv(subreq, state, &p); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + ret = parse_getdc_response(p, state, state->domain_name, + &state->nt_version, &state->dc_name, + &state->samlogon_response); + if (!ret) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + tevent_req_done(req); +} + +NTSTATUS nbt_getdc_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint32_t *nt_version, const char **dc_name, + struct netlogon_samlogon_response **samlogon_response) +{ + struct nbt_getdc_state *state = tevent_req_data( + req, struct nbt_getdc_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (nt_version != NULL) { + *nt_version = state->nt_version; + } + if (dc_name != NULL) { + *dc_name = talloc_move(mem_ctx, &state->dc_name); + } + if (samlogon_response != NULL) { + *samlogon_response = talloc_move( + mem_ctx, &state->samlogon_response); + } + return NT_STATUS_OK; +} + +NTSTATUS nbt_getdc(struct messaging_context *msg_ctx, + uint32_t timeout_in_seconds, + const struct sockaddr_storage *dc_addr, + const char *domain_name, + const struct dom_sid *sid, + const char *account_name, + uint32_t account_flags, + uint32_t nt_version, + TALLOC_CTX *mem_ctx, + uint32_t *pnt_version, + const char **dc_name, + struct netlogon_samlogon_response **samlogon_response) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = nbt_getdc_send(ev, ev, msg_ctx, dc_addr, domain_name, + sid, account_name, account_flags, nt_version); + if (req == NULL) { + goto fail; + } + if (!tevent_req_set_endtime(req, ev, + timeval_current_ofs(timeout_in_seconds, 0))) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = nbt_getdc_recv(req, mem_ctx, pnt_version, dc_name, + samlogon_response); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/clidgram.h b/source3/libsmb/clidgram.h new file mode 100644 index 0000000..46e11f4 --- /dev/null +++ b/source3/libsmb/clidgram.h @@ -0,0 +1,54 @@ +/* + Unix SMB/CIFS implementation. + client dgram calls + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Richard Sharpe 2001 + Copyright (C) John Terpstra 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIBSMB_CLIDGRAM_H_ +#define _LIBSMB_CLIDGRAM_H_ + +#include "../libcli/netlogon/netlogon.h" + +/* The following definitions come from libsmb/clidgram.c */ + +struct tevent_req *nbt_getdc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct messaging_context *msg_ctx, + const struct sockaddr_storage *dc_addr, + const char *domain_name, + const struct dom_sid *sid, + const char *account_name, + uint32_t account_flags, + uint32_t nt_version); +NTSTATUS nbt_getdc_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint32_t *nt_version, const char **dc_name, + struct netlogon_samlogon_response **samlogon_response); +NTSTATUS nbt_getdc(struct messaging_context *msg_ctx, + uint32_t timeout_in_seconds, + const struct sockaddr_storage *dc_addr, + const char *domain_name, + const struct dom_sid *sid, + const char *account_name, + uint32_t account_flags, + uint32_t nt_version, + TALLOC_CTX *mem_ctx, + uint32_t *pnt_version, + const char **dc_name, + struct netlogon_samlogon_response **samlogon_response); + +#endif /* _LIBSMB_CLIDGRAM_H_ */ diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c new file mode 100644 index 0000000..5f0b9da --- /dev/null +++ b/source3/libsmb/clientgen.c @@ -0,0 +1,629 @@ +/* + Unix SMB/CIFS implementation. + SMB client generic functions + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2007. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "../libcli/smb/smb_signing.h" +#include "../libcli/smb/smb_seal.h" +#include "async_smb.h" +#include "../libcli/smb/smbXcli_base.h" +#include "../libcli/smb/smb2_negotiate_context.h" +#include "../librpc/ndr/libndr.h" +#include "../include/client.h" + +/**************************************************************************** + Change the timeout (in milliseconds). +****************************************************************************/ + +unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout) +{ + unsigned int old_timeout = cli->timeout; + cli->timeout = timeout; + return old_timeout; +} + +/**************************************************************************** + Set the 'backup_intent' flag. +****************************************************************************/ + +bool cli_set_backup_intent(struct cli_state *cli, bool flag) +{ + bool old_state = cli->backup_intent; + cli->backup_intent = flag; + return old_state; +} + +/**************************************************************************** + Initialise a client structure. Always returns a talloc'ed struct. + Set the signing state (used from the command line). +****************************************************************************/ + +struct GUID cli_state_client_guid; + +struct cli_state *cli_state_create(TALLOC_CTX *mem_ctx, + int fd, + const char *remote_name, + int signing_state, int flags) +{ + struct cli_state *cli = NULL; + bool use_spnego = lp_client_use_spnego(); + bool force_dos_errors = false; + bool force_ascii = false; + bool use_level_II_oplocks = false; + uint32_t smb1_capabilities = 0; + uint32_t smb2_capabilities = 0; + struct smb311_capabilities smb3_capabilities = + smb311_capabilities_parse("client", + lp_client_smb3_signing_algorithms(), + lp_client_smb3_encryption_algorithms()); + struct GUID client_guid; + + if (!GUID_all_zero(&cli_state_client_guid)) { + client_guid = cli_state_client_guid; + } else { + const char *str = NULL; + + str = lp_parm_const_string(-1, "libsmb", "client_guid", NULL); + if (str != NULL) { + GUID_from_string(str, &client_guid); + } else { + client_guid = GUID_random(); + } + } + + /* Check the effective uid - make sure we are not setuid */ + if (is_setuid_root()) { + DEBUG(0,("libsmb based programs must *NOT* be setuid root.\n")); + return NULL; + } + + cli = talloc_zero(mem_ctx, struct cli_state); + if (!cli) { + return NULL; + } + + cli->server_domain = talloc_strdup(cli, ""); + if (!cli->server_domain) { + goto error; + } + cli->server_os = talloc_strdup(cli, ""); + if (!cli->server_os) { + goto error; + } + cli->server_type = talloc_strdup(cli, ""); + if (!cli->server_type) { + goto error; + } + + cli->raw_status = NT_STATUS_INTERNAL_ERROR; + cli->map_dos_errors = true; /* remove this */ + cli->timeout = CLIENT_TIMEOUT; + + /* Set the CLI_FORCE_DOSERR environment variable to test + client routines using DOS errors instead of STATUS32 + ones. This intended only as a temporary hack. */ + if (getenv("CLI_FORCE_DOSERR")) { + force_dos_errors = true; + } + if (flags & CLI_FULL_CONNECTION_FORCE_DOS_ERRORS) { + force_dos_errors = true; + } + + if (getenv("CLI_FORCE_ASCII")) { + force_ascii = true; + } + if (!lp_unicode()) { + force_ascii = true; + } + if (flags & CLI_FULL_CONNECTION_FORCE_ASCII) { + force_ascii = true; + } + + if (flags & CLI_FULL_CONNECTION_DONT_SPNEGO) { + use_spnego = false; + } + + if (flags & CLI_FULL_CONNECTION_OPLOCKS) { + cli->use_oplocks = true; + } + if (flags & CLI_FULL_CONNECTION_LEVEL_II_OPLOCKS) { + use_level_II_oplocks = true; + } + + if (signing_state == SMB_SIGNING_IPC_DEFAULT) { + /* + * Ensure for IPC/RPC the default is to require + * signing unless explicitly turned off by the + * administrator. + */ + signing_state = lp_client_ipc_signing(); + } + + if (signing_state == SMB_SIGNING_DEFAULT) { + signing_state = lp_client_signing(); + } + + smb1_capabilities = 0; + smb1_capabilities |= CAP_LARGE_FILES; + smb1_capabilities |= CAP_NT_SMBS | CAP_RPC_REMOTE_APIS; + smb1_capabilities |= CAP_LOCK_AND_READ | CAP_NT_FIND; + smb1_capabilities |= CAP_DFS | CAP_W2K_SMBS; + smb1_capabilities |= CAP_LARGE_READX|CAP_LARGE_WRITEX; + smb1_capabilities |= CAP_LWIO; + + if (!force_dos_errors) { + smb1_capabilities |= CAP_STATUS32; + } + + if (!force_ascii) { + smb1_capabilities |= CAP_UNICODE; + } + + if (use_spnego) { + smb1_capabilities |= CAP_EXTENDED_SECURITY; + } + + if (use_level_II_oplocks) { + smb1_capabilities |= CAP_LEVEL_II_OPLOCKS; + } + + smb2_capabilities = SMB2_CAP_ALL; + + cli->conn = smbXcli_conn_create(cli, fd, remote_name, + signing_state, + smb1_capabilities, + &client_guid, + smb2_capabilities, + &smb3_capabilities); + if (cli->conn == NULL) { + goto error; + } + + cli->smb1.pid = (uint32_t)getpid(); + cli->smb1.vc_num = cli->smb1.pid; + cli->smb1.session = smbXcli_session_create(cli, cli->conn); + if (cli->smb1.session == NULL) { + goto error; + } + + cli->initialised = 1; + return cli; + + /* Clean up after malloc() error */ + + error: + + TALLOC_FREE(cli); + return NULL; +} + +/**************************************************************************** + Close all pipes open on this session. +****************************************************************************/ + +static void cli_nt_pipes_close(struct cli_state *cli) +{ + while (cli->pipe_list != NULL) { + /* + * No TALLOC_FREE here! + */ + talloc_free(cli->pipe_list); + } +} + +/**************************************************************************** + Shutdown a client structure. +****************************************************************************/ + +static void _cli_shutdown(struct cli_state *cli) +{ + cli_nt_pipes_close(cli); + + /* + * tell our peer to free his resources. Without this, when an + * application attempts to do a graceful shutdown and calls + * smbc_free_context() to clean up all connections, some connections + * can remain active on the peer end, until some (long) timeout period + * later. This tree disconnect forces the peer to clean up, since the + * connection will be going away. + */ + if (cli_state_has_tcon(cli)) { + cli_tdis(cli); + } + + smbXcli_conn_disconnect(cli->conn, NT_STATUS_OK); + + TALLOC_FREE(cli); +} + +void cli_shutdown(struct cli_state *cli) +{ + struct cli_state *cli_head; + if (cli == NULL) { + return; + } + DLIST_HEAD(cli, cli_head); + if (cli_head == cli) { + /* + * head of a DFS list, shutdown all subsidiary DFS + * connections. + */ + struct cli_state *p, *next; + + for (p = cli_head->next; p; p = next) { + next = p->next; + DLIST_REMOVE(cli_head, p); + _cli_shutdown(p); + } + } else { + DLIST_REMOVE(cli_head, cli); + } + + _cli_shutdown(cli); +} + +uint16_t cli_state_get_vc_num(struct cli_state *cli) +{ + return cli->smb1.vc_num; +} + +/**************************************************************************** + Set the PID to use for smb messages. Return the old pid. +****************************************************************************/ + +uint32_t cli_setpid(struct cli_state *cli, uint32_t pid) +{ + uint32_t ret = cli->smb1.pid; + cli->smb1.pid = pid; + return ret; +} + +uint32_t cli_getpid(struct cli_state *cli) +{ + return cli->smb1.pid; +} + +bool cli_state_is_encryption_on(struct cli_state *cli) +{ + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return smb1cli_conn_encryption_on(cli->conn); + } + + if (cli->smb2.tcon == NULL) { + return false; + } + + return smb2cli_tcon_is_encryption_on(cli->smb2.tcon); +} + +bool cli_state_has_tcon(struct cli_state *cli) +{ + uint32_t tid; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + if (cli->smb2.tcon == NULL) { + return false; + } + tid = cli_state_get_tid(cli); + if (tid == UINT32_MAX) { + return false; + } + } else { + if (cli->smb1.tcon == NULL) { + return false; + } + tid = cli_state_get_tid(cli); + if (tid == UINT16_MAX) { + return false; + } + } + return true; +} + +uint32_t cli_state_get_tid(struct cli_state *cli) +{ + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return smb2cli_tcon_current_id(cli->smb2.tcon); + } else { + return (uint32_t)smb1cli_tcon_current_id(cli->smb1.tcon); + } +} + +uint32_t cli_state_set_tid(struct cli_state *cli, uint32_t tid) +{ + uint32_t ret; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + ret = smb2cli_tcon_current_id(cli->smb2.tcon); + smb2cli_tcon_set_id(cli->smb2.tcon, tid); + } else { + ret = smb1cli_tcon_current_id(cli->smb1.tcon); + smb1cli_tcon_set_id(cli->smb1.tcon, tid); + } + return ret; +} + +struct smbXcli_tcon *cli_state_save_tcon(struct cli_state *cli) +{ + /* + * Note. This used to make a deep copy of either + * cli->smb2.tcon or cli->smb1.tcon, but this leaves + * the original pointer in place which will then get + * TALLOC_FREE()'d when the new connection is made on + * this cli_state. + * + * As there may be pipes open on the old connection with + * talloc'ed state allocated using the tcon pointer as a + * parent we can't deep copy and then free this as that + * closes the open pipes. + * + * This call is used to temporarily swap out a tcon pointer + * to allow a new tcon on the same cli_state. + * + * Just return the raw pointer and set the old value to NULL. + * We know we MUST be calling cli_state_restore_tcon() below + * to restore before closing the session. + * + * See BUG: https://bugzilla.samba.org/show_bug.cgi?id=13992 + */ + struct smbXcli_tcon *tcon_ret = NULL; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + tcon_ret = cli->smb2.tcon; + cli->smb2.tcon = NULL; /* *Not* TALLOC_FREE(). */ + } else { + tcon_ret = cli->smb1.tcon; + cli->smb1.tcon = NULL; /* *Not* TALLOC_FREE(). */ + } + return tcon_ret; +} + +void cli_state_restore_tcon(struct cli_state *cli, struct smbXcli_tcon *tcon) +{ + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + TALLOC_FREE(cli->smb2.tcon); + cli->smb2.tcon = tcon; + } else { + TALLOC_FREE(cli->smb1.tcon); + cli->smb1.tcon = tcon; + } +} + +uint16_t cli_state_get_uid(struct cli_state *cli) +{ + return smb1cli_session_current_id(cli->smb1.session); +} + +uint16_t cli_state_set_uid(struct cli_state *cli, uint16_t uid) +{ + uint16_t ret = smb1cli_session_current_id(cli->smb1.session); + smb1cli_session_set_id(cli->smb1.session, uid); + return ret; +} + +/**************************************************************************** + Set the case sensitivity flag on the packets. Returns old state. +****************************************************************************/ + +bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive) +{ + bool ret; + uint32_t fs_attrs; + struct smbXcli_tcon *tcon; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + tcon = cli->smb2.tcon; + } else { + tcon = cli->smb1.tcon; + } + + fs_attrs = smbXcli_tcon_get_fs_attributes(tcon); + if (fs_attrs & FILE_CASE_SENSITIVE_SEARCH) { + ret = true; + } else { + ret = false; + } + if (case_sensitive) { + fs_attrs |= FILE_CASE_SENSITIVE_SEARCH; + } else { + fs_attrs &= ~FILE_CASE_SENSITIVE_SEARCH; + } + smbXcli_tcon_set_fs_attributes(tcon, fs_attrs); + + return ret; +} + +uint32_t cli_state_available_size(struct cli_state *cli, uint32_t ofs) +{ + uint32_t ret = smb1cli_conn_max_xmit(cli->conn); + + if (ofs >= ret) { + return 0; + } + + ret -= ofs; + + return ret; +} + +time_t cli_state_server_time(struct cli_state *cli) +{ + NTTIME nt; + time_t t; + + nt = smbXcli_conn_server_system_time(cli->conn); + t = nt_time_to_unix(nt); + + return t; +} + +struct cli_echo_state { + bool is_smb2; +}; + +static void cli_echo_done(struct tevent_req *subreq); + +struct tevent_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, uint16_t num_echos, + DATA_BLOB data) +{ + struct tevent_req *req, *subreq; + struct cli_echo_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_echo_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + state->is_smb2 = true; + subreq = smb2cli_echo_send(state, ev, + cli->conn, + cli->timeout); + } else { + subreq = smb1cli_echo_send(state, ev, + cli->conn, + cli->timeout, + num_echos, + data); + } + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_echo_done, req); + + return req; +} + +static void cli_echo_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_echo_state *state = tevent_req_data( + req, struct cli_echo_state); + NTSTATUS status; + + if (state->is_smb2) { + status = smb2cli_echo_recv(subreq); + } else { + status = smb1cli_echo_recv(subreq); + } + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + tevent_req_done(req); +} + +/** + * Get the result out from an echo request + * @param[in] req The async_req from cli_echo_send + * @retval Did the server reply correctly? + */ + +NTSTATUS cli_echo_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/** + * @brief Send/Receive SMBEcho requests + * @param[in] mem_ctx The memory context to put the async_req on + * @param[in] ev The event context that will call us back + * @param[in] cli The connection to send the echo to + * @param[in] num_echos How many times do we want to get the reply? + * @param[in] data The data we want to get back + * @retval Did the server reply correctly? + */ + +NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_echo_send(frame, ev, cli, num_echos, data); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_echo_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +NTSTATUS cli_smb(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t smb_command, uint8_t additional_flags, + uint8_t wct, uint16_t *vwv, + uint32_t num_bytes, const uint8_t *bytes, + struct tevent_req **result_parent, + uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv, + uint32_t *pnum_bytes, uint8_t **pbytes) +{ + struct tevent_context *ev; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(mem_ctx); + if (ev == NULL) { + goto fail; + } + req = cli_smb_send(mem_ctx, ev, cli, smb_command, additional_flags, 0, + wct, vwv, num_bytes, bytes); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_smb_recv(req, NULL, NULL, min_wct, pwct, pvwv, + pnum_bytes, pbytes); +fail: + TALLOC_FREE(ev); + if (NT_STATUS_IS_OK(status) && (result_parent != NULL)) { + *result_parent = req; + } + return status; +} diff --git a/source3/libsmb/clierror.c b/source3/libsmb/clierror.c new file mode 100644 index 0000000..f173006 --- /dev/null +++ b/source3/libsmb/clierror.c @@ -0,0 +1,166 @@ +/* + Unix SMB/CIFS implementation. + client error handling routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Jeremy Allison 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../libcli/smb/smbXcli_base.h" + +/**************************************************************************** + Return the 32-bit NT status code from the last packet. +****************************************************************************/ + +NTSTATUS cli_nt_error(struct cli_state *cli) +{ + /* Deal with socket errors first. */ + if (!cli_state_is_connected(cli)) { + return NT_STATUS_CONNECTION_DISCONNECTED; + } + + if (NT_STATUS_IS_DOS(cli->raw_status)) { + int e_class = NT_STATUS_DOS_CLASS(cli->raw_status); + int code = NT_STATUS_DOS_CODE(cli->raw_status); + return dos_to_ntstatus(e_class, code); + } + + return cli->raw_status; +} + + +/**************************************************************************** + Return the DOS error from the last packet - an error class and an error + code. +****************************************************************************/ + +void cli_dos_error(struct cli_state *cli, uint8_t *eclass, uint32_t *ecode) +{ + if (!cli_state_is_connected(cli)) { + *eclass = ERRDOS; + *ecode = ERRnotconnected; + return; + } + + if (!NT_STATUS_IS_DOS(cli->raw_status)) { + ntstatus_to_dos(cli->raw_status, eclass, ecode); + return; + } + + *eclass = NT_STATUS_DOS_CLASS(cli->raw_status); + *ecode = NT_STATUS_DOS_CODE(cli->raw_status); +} + +int cli_status_to_errno(NTSTATUS status) +{ + int err; + + if (NT_STATUS_IS_DOS(status)) { + uint8_t eclass = NT_STATUS_DOS_CLASS(status); + uint32_t ecode = NT_STATUS_DOS_CODE(status); + status = dos_to_ntstatus(eclass, ecode); + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { + /* + * Legacy code from cli_errno, see Samba up to 4.13: A + * special case for this Vista error. Since its + * high-order byte isn't 0xc0, it won't match + * correctly in map_errno_from_nt_status(). + */ + err = EACCES; + } else { + err = map_errno_from_nt_status(status); + } + + DBG_NOTICE("0x%"PRIx32" -> %d\n", NT_STATUS_V(status), err); + + return err; +} + +/* Return a UNIX errno appropriate for the error received in the last + packet. */ + +int cli_errno(struct cli_state *cli) +{ + bool connected; + int err; + + connected = cli_state_is_connected(cli); + if (!connected) { + return EPIPE; + } + + err = cli_status_to_errno(cli->raw_status); + return err; +} + +/* Return true if the last packet was in error */ + +bool cli_is_error(struct cli_state *cli) +{ + /* A socket error is always an error. */ + if (!cli_state_is_connected(cli)) { + return true; + } + + if (NT_STATUS_IS_DOS(cli->raw_status)) { + /* Return error if error class in non-zero */ + uint8_t rcls = NT_STATUS_DOS_CLASS(cli->raw_status); + return rcls != 0; + } + + return NT_STATUS_IS_ERR(cli->raw_status); +} + +/* Return true if the last error was an NT error */ + +bool cli_is_nt_error(struct cli_state *cli) +{ + /* A socket error is always an NT error. */ + if (!cli_state_is_connected(cli)) { + return true; + } + + return cli_is_error(cli) && !NT_STATUS_IS_DOS(cli->raw_status); +} + +/* Return true if the last error was a DOS error */ + +bool cli_is_dos_error(struct cli_state *cli) +{ + /* A socket error is always a DOS error. */ + if (!cli_state_is_connected(cli)) { + return true; + } + + return cli_is_error(cli) && NT_STATUS_IS_DOS(cli->raw_status); +} + +bool cli_state_is_connected(struct cli_state *cli) +{ + if (cli == NULL) { + return false; + } + + if (!cli->initialised) { + return false; + } + + return smbXcli_conn_is_connected(cli->conn); +} diff --git a/source3/libsmb/clifile.c b/source3/libsmb/clifile.c new file mode 100644 index 0000000..5c570c6 --- /dev/null +++ b/source3/libsmb/clifile.c @@ -0,0 +1,6940 @@ +/* + Unix SMB/CIFS implementation. + client file operations + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2001-2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "libsmb/clirap.h" +#include "trans2.h" +#include "ntioctl.h" +#include "libcli/security/security.h" +#include "../libcli/smb/smbXcli_base.h" + +struct cli_setpathinfo_state { + uint16_t setup; + uint8_t *param; +}; + +static void cli_setpathinfo_done(struct tevent_req *subreq); + +struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t level, + const char *path, + uint8_t *data, + size_t data_len) +{ + struct tevent_req *req, *subreq; + struct cli_setpathinfo_state *state; + uint16_t additional_flags2 = 0; + + req = tevent_req_create(mem_ctx, &state, + struct cli_setpathinfo_state); + if (req == NULL) { + return NULL; + } + + /* Setup setup word. */ + SSVAL(&state->setup, 0, TRANSACT2_SETPATHINFO); + + /* Setup param array. */ + state->param = talloc_zero_array(state, uint8_t, 6); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + SSVAL(state->param, 0, level); + + state->param = trans2_bytes_push_str( + state->param, smbXcli_conn_use_unicode(cli->conn), path, strlen(path)+1, NULL); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(path, NULL, NULL, NULL) && + !INFO_LEVEL_IS_UNIX(level)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_trans_send( + state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + additional_flags2, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + &state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + talloc_get_size(state->param), /* num param. */ + 2, /* max returned param. */ + data, /* data. */ + data_len, /* num data. */ + 0); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_setpathinfo_done, req); + return req; +} + +static void cli_setpathinfo_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL, + NULL, 0, NULL, NULL, 0, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_setpathinfo_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_setpathinfo(struct cli_state *cli, + uint16_t level, + const char *path, + uint8_t *data, + size_t data_len) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_setpathinfo_send(ev, ev, cli, level, path, data, data_len); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_setpathinfo_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_setfileinfo_state { + uint16_t setup; + uint8_t param[6]; +}; + +static void cli_setfileinfo_done(struct tevent_req *subreq); + +struct tevent_req *cli_setfileinfo_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t level, + uint8_t *data, + size_t data_len) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_setfileinfo_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_setfileinfo_state); + if (req == NULL) { + return NULL; + } + PUSH_LE_U16(&state->setup, 0, TRANSACT2_SETFILEINFO); + + PUSH_LE_U16(state->param, 0, fnum); + PUSH_LE_U16(state->param, 2, level); + + subreq = cli_trans_send(state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + 0, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + &state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + 6, /* num param. */ + 2, /* max returned param. */ + data, /* data. */ + data_len, /* num data. */ + 0); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_setfileinfo_done, req); + return req; +} + +static void cli_setfileinfo_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_trans_recv( + subreq, /* req */ + NULL, /* mem_ctx */ + NULL, /* recv_flags2 */ + NULL, /* setup */ + 0, /* min_setup */ + NULL, /* num_setup */ + NULL, /* param */ + 0, /* min_param */ + NULL, /* num_param */ + NULL, /* data */ + 0, /* min_data */ + NULL); /* num_data */ + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_setfileinfo_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/**************************************************************************** + Hard/Symlink a file (UNIX extensions). + Creates new name (sym)linked to link_target. +****************************************************************************/ + +struct cli_posix_link_internal_state { + uint8_t *data; +}; + +static void cli_posix_link_internal_done(struct tevent_req *subreq); + +static struct tevent_req *cli_posix_link_internal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t level, + const char *link_target, + const char *newname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_link_internal_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_posix_link_internal_state); + if (req == NULL) { + return NULL; + } + + /* Setup data array. */ + state->data = talloc_array(state, uint8_t, 0); + if (tevent_req_nomem(state->data, req)) { + return tevent_req_post(req, ev); + } + state->data = trans2_bytes_push_str( + state->data, smbXcli_conn_use_unicode(cli->conn), + link_target, strlen(link_target)+1, NULL); + + subreq = cli_setpathinfo_send( + state, ev, cli, level, newname, + state->data, talloc_get_size(state->data)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_link_internal_done, req); + return req; +} + +static void cli_posix_link_internal_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setpathinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_posix_link_internal_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/**************************************************************************** + Symlink a file (UNIX extensions). +****************************************************************************/ + +struct cli_posix_symlink_state { + uint8_t dummy; +}; + +static void cli_posix_symlink_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *link_target, + const char *newname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_symlink_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_symlink_state); + if (req == NULL) { + return NULL; + } + + subreq = cli_posix_link_internal_send( + mem_ctx, ev, cli, SMB_SET_FILE_UNIX_LINK, link_target, newname); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_symlink_done, req); + return req; +} + +static void cli_posix_symlink_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_posix_link_internal_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_symlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_symlink(struct cli_state *cli, + const char *link_target, + const char *newname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_symlink_send(frame, + ev, + cli, + link_target, + newname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_symlink_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Read a POSIX symlink. +****************************************************************************/ + +struct cli_posix_readlink_state { + struct cli_state *cli; + char *converted; +}; + +static void cli_posix_readlink_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_readlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_readlink_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_readlink_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + subreq = cli_qpathinfo_send( + state, + ev, + cli, + fname, + SMB_QUERY_FILE_UNIX_LINK, + 1, + UINT16_MAX); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_readlink_done, req); + return req; +} + +static void cli_posix_readlink_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_posix_readlink_state *state = tevent_req_data( + req, struct cli_posix_readlink_state); + NTSTATUS status; + uint8_t *data = NULL; + uint32_t num_data = 0; + charset_t charset; + size_t converted_size; + bool ok; + + status = cli_qpathinfo_recv(subreq, state, &data, &num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + /* + * num_data is > 1, we've given 1 as minimum to cli_qpathinfo_send + */ + if (data == NULL || data[num_data-1] != '\0') { + tevent_req_nterror(req, NT_STATUS_DATA_ERROR); + return; + } + + charset = smbXcli_conn_use_unicode(state->cli->conn) ? + CH_UTF16LE : CH_DOS; + + /* The returned data is a pushed string, not raw data. */ + ok = convert_string_talloc( + state, + charset, + CH_UNIX, + data, + num_data, + &state->converted, + &converted_size); + if (!ok) { + tevent_req_oom(req); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_posix_readlink_recv( + struct tevent_req *req, TALLOC_CTX *mem_ctx, char **target) +{ + struct cli_posix_readlink_state *state = tevent_req_data( + req, struct cli_posix_readlink_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *target = talloc_move(mem_ctx, &state->converted); + return NT_STATUS_OK; +} + +NTSTATUS cli_posix_readlink( + struct cli_state *cli, + const char *fname, + TALLOC_CTX *mem_ctx, + char **target) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_readlink_send(frame, ev, cli, fname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_readlink_recv(req, mem_ctx, target); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Hard link a file (UNIX extensions). +****************************************************************************/ + +struct cli_posix_hardlink_state { + uint8_t dummy; +}; + +static void cli_posix_hardlink_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_hardlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_hardlink_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_hardlink_state); + if (req == NULL) { + return NULL; + } + + subreq = cli_posix_link_internal_send( + state, ev, cli, SMB_SET_FILE_UNIX_HLINK, oldname, newname); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_hardlink_done, req); + return req; +} + +static void cli_posix_hardlink_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_posix_link_internal_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_hardlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_hardlink(struct cli_state *cli, + const char *oldname, + const char *newname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_hardlink_send(frame, + ev, + cli, + oldname, + newname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_hardlink_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Do a POSIX getacl - pathname based ACL get (UNIX extensions). +****************************************************************************/ + +struct getacl_state { + uint32_t num_data; + uint8_t *data; +}; + +static void cli_posix_getacl_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_getacl_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct getacl_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct getacl_state); + if (req == NULL) { + return NULL; + } + subreq = cli_qpathinfo_send(state, ev, cli, fname, SMB_QUERY_POSIX_ACL, + 0, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_getacl_done, req); + return req; +} + +static void cli_posix_getacl_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct getacl_state *state = tevent_req_data( + req, struct getacl_state); + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &state->data, + &state->num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_posix_getacl_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *prb_size, + char **retbuf) +{ + struct getacl_state *state = tevent_req_data(req, struct getacl_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *prb_size = (size_t)state->num_data; + *retbuf = (char *)talloc_move(mem_ctx, &state->data); + return NT_STATUS_OK; +} + +NTSTATUS cli_posix_getacl(struct cli_state *cli, + const char *fname, + TALLOC_CTX *mem_ctx, + size_t *prb_size, + char **retbuf) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_getacl_send(frame, + ev, + cli, + fname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_getacl_recv(req, mem_ctx, prb_size, retbuf); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Do a POSIX setacl - pathname based ACL set (UNIX extensions). +****************************************************************************/ + +struct setacl_state { + uint8_t *data; +}; + +static void cli_posix_setacl_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_setacl_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + const void *data, + size_t num_data) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct setacl_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct setacl_state); + if (req == NULL) { + return NULL; + } + state->data = talloc_memdup(state, data, num_data); + if (tevent_req_nomem(state->data, req)) { + return tevent_req_post(req, ev); + } + + subreq = cli_setpathinfo_send(state, + ev, + cli, + SMB_SET_POSIX_ACL, + fname, + state->data, + num_data); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_setacl_done, req); + return req; +} + +static void cli_posix_setacl_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setpathinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_setacl_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_setacl(struct cli_state *cli, + const char *fname, + const void *acl_buf, + size_t acl_buf_size) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_setacl_send(frame, + ev, + cli, + fname, + acl_buf, + acl_buf_size); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_setacl_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Stat a file (UNIX extensions). +****************************************************************************/ + +struct stat_state { + SMB_STRUCT_STAT *sbuf; +}; + +static void cli_posix_stat_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_stat_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + SMB_STRUCT_STAT *sbuf) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct stat_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct stat_state); + if (req == NULL) { + return NULL; + } + state->sbuf = sbuf; + + subreq = cli_qpathinfo_send(state, ev, cli, fname, + SMB_QUERY_FILE_UNIX_BASIC, 100, 100); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_stat_done, req); + return req; +} + +static void cli_posix_stat_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct stat_state *state = tevent_req_data(req, struct stat_state); + SMB_STRUCT_STAT *sbuf = state->sbuf; + uint8_t *data; + uint32_t num_data = 0; + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &data, &num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (num_data != 100) { + /* + * Paranoia, cli_qpathinfo should have guaranteed + * this, but you never know... + */ + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + *sbuf = (SMB_STRUCT_STAT) { 0 }; + + /* total size, in bytes */ + sbuf->st_ex_size = IVAL2_TO_SMB_BIG_UINT(data, 0); + + /* number of blocks allocated */ + sbuf->st_ex_blocks = IVAL2_TO_SMB_BIG_UINT(data,8); +#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE) + sbuf->st_ex_blocks /= STAT_ST_BLOCKSIZE; +#else + /* assume 512 byte blocks */ + sbuf->st_ex_blocks /= 512; +#endif + /* time of last change */ + sbuf->st_ex_ctime = interpret_long_date((char *)(data + 16)); + + /* time of last access */ + sbuf->st_ex_atime = interpret_long_date((char *)(data + 24)); + + /* time of last modification */ + sbuf->st_ex_mtime = interpret_long_date((char *)(data + 32)); + + sbuf->st_ex_uid = (uid_t) IVAL(data, 40); /* user ID of owner */ + sbuf->st_ex_gid = (gid_t) IVAL(data, 48); /* group ID of owner */ + sbuf->st_ex_mode = unix_filetype_from_wire(IVAL(data, 56)); + +#if defined(HAVE_MAKEDEV) + { + uint32_t dev_major = IVAL(data,60); + uint32_t dev_minor = IVAL(data,68); + sbuf->st_ex_rdev = makedev(dev_major, dev_minor); + } +#endif + /* inode */ + sbuf->st_ex_ino = (SMB_INO_T)IVAL2_TO_SMB_BIG_UINT(data, 76); + + /* protection */ + sbuf->st_ex_mode |= wire_perms_to_unix(IVAL(data, 84)); + + /* number of hard links */ + sbuf->st_ex_nlink = BIG_UINT(data, 92); + + tevent_req_done(req); +} + +NTSTATUS cli_posix_stat_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_stat(struct cli_state *cli, + const char *fname, + SMB_STRUCT_STAT *sbuf) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_stat_send(frame, ev, cli, fname, sbuf); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_stat_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Chmod or chown a file internal (UNIX extensions). +****************************************************************************/ + +struct cli_posix_chown_chmod_internal_state { + uint8_t data[100]; +}; + +static void cli_posix_chown_chmod_internal_done(struct tevent_req *subreq); + +static struct tevent_req *cli_posix_chown_chmod_internal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t mode, + uint32_t uid, + uint32_t gid) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_chown_chmod_internal_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_posix_chown_chmod_internal_state); + if (req == NULL) { + return NULL; + } + + memset(state->data, 0xff, 40); /* Set all sizes/times to no change. */ + memset(&state->data[40], '\0', 60); + SIVAL(state->data,40,uid); + SIVAL(state->data,48,gid); + SIVAL(state->data,84,mode); + + subreq = cli_setpathinfo_send(state, ev, cli, SMB_SET_FILE_UNIX_BASIC, + fname, state->data, sizeof(state->data)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_chown_chmod_internal_done, + req); + return req; +} + +static void cli_posix_chown_chmod_internal_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setpathinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_posix_chown_chmod_internal_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/**************************************************************************** + chmod a file (UNIX extensions). +****************************************************************************/ + +struct cli_posix_chmod_state { + uint8_t dummy; +}; + +static void cli_posix_chmod_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_chmod_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + mode_t mode) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_chmod_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_posix_chmod_state); + if (req == NULL) { + return NULL; + } + + subreq = cli_posix_chown_chmod_internal_send( + state, + ev, + cli, + fname, + unix_perms_to_wire(mode), + SMB_UID_NO_CHANGE, + SMB_GID_NO_CHANGE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_chmod_done, req); + return req; +} + +static void cli_posix_chmod_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_posix_chown_chmod_internal_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_chmod_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_chmod(struct cli_state *cli, const char *fname, mode_t mode) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_chmod_send(frame, + ev, + cli, + fname, + mode); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_chmod_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + chown a file (UNIX extensions). +****************************************************************************/ + +struct cli_posix_chown_state { + uint8_t dummy; +}; + +static void cli_posix_chown_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_chown_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uid_t uid, + gid_t gid) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_chown_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_chown_state); + if (req == NULL) { + return NULL; + } + + subreq = cli_posix_chown_chmod_internal_send( + state, + ev, + cli, + fname, + SMB_MODE_NO_CHANGE, + (uint32_t)uid, + (uint32_t)gid); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_chown_done, req); + return req; +} + +static void cli_posix_chown_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_posix_chown_chmod_internal_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_chown_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_chown(struct cli_state *cli, + const char *fname, + uid_t uid, + gid_t gid) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_chown_send(frame, + ev, + cli, + fname, + uid, + gid); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_chown_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Rename a file. +****************************************************************************/ + +struct cli_smb1_rename_state { + uint8_t *data; +}; + +static void cli_smb1_rename_done(struct tevent_req *subreq); + +static struct tevent_req *cli_smb1_rename_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace) +{ + NTSTATUS status; + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb1_rename_state *state = NULL; + smb_ucs2_t *converted_str = NULL; + size_t converted_size_bytes = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb1_rename_state); + if (req == NULL) { + return NULL; + } + + /* + * Strip a MSDFS path from fname_dst if we were given one. + */ + status = cli_dfs_target_check(state, + cli, + fname_src, + fname_dst, + &fname_dst); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (!push_ucs2_talloc(talloc_tos(), &converted_str, fname_dst, + &converted_size_bytes)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + /* W2K8 insists the dest name is not null + terminated. Remove the last 2 zero bytes + and reduce the name length. */ + + if (converted_size_bytes < 2) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + converted_size_bytes -= 2; + + state->data = + talloc_zero_array(state, uint8_t, 12 + converted_size_bytes); + if (state->data == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (replace) { + SCVAL(state->data, 0, 1); + } + + SIVAL(state->data, 8, converted_size_bytes); + memcpy(state->data + 12, converted_str, converted_size_bytes); + + TALLOC_FREE(converted_str); + + subreq = cli_setpathinfo_send( + state, ev, cli, SMB_FILE_RENAME_INFORMATION, fname_src, state->data, + talloc_get_size(state->data)); + if (tevent_req_nomem(subreq, req)) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + tevent_req_set_callback(subreq, cli_smb1_rename_done, req); + return req; + +fail: + TALLOC_FREE(converted_str); + tevent_req_nterror(req, status); + return tevent_req_post(req, ev); +} + +static void cli_smb1_rename_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setpathinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_smb1_rename_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +static void cli_cifs_rename_done(struct tevent_req *subreq); + +struct cli_cifs_rename_state { + uint16_t vwv[1]; +}; + +static struct tevent_req *cli_cifs_rename_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_cifs_rename_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, struct cli_cifs_rename_state); + if (req == NULL) { + return NULL; + } + + if (replace) { + /* + * CIFS doesn't support replace + */ + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + /* + * Strip a MSDFS path from fname_dst if we were given one. + */ + status = cli_dfs_target_check(state, + cli, + fname_src, + fname_dst, + &fname_dst); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + SSVAL(state->vwv+0, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname_src, + strlen(fname_src)+1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname_src, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + bytes = talloc_realloc(state, bytes, uint8_t, + talloc_get_size(bytes)+1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + bytes[talloc_get_size(bytes)-1] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname_dst, + strlen(fname_dst)+1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + subreq = cli_smb_send(state, ev, cli, SMBmv, additional_flags, + additional_flags2, + 1, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_cifs_rename_done, req); + return req; +} + +static void cli_cifs_rename_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb_recv( + subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_cifs_rename_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_rename_state { + uint8_t dummy; +}; + +static void cli_rename_done1(struct tevent_req *subreq); +static void cli_rename_done_cifs(struct tevent_req *subreq); +static void cli_rename_done2(struct tevent_req *subreq); + +struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_rename_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_rename_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_rename_send( + state, ev, cli, fname_src, fname_dst, replace); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_rename_done2, req); + return req; + } + + if (replace && smbXcli_conn_support_passthrough(cli->conn)) { + subreq = cli_smb1_rename_send( + state, ev, cli, fname_src, fname_dst, replace); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_rename_done1, req); + return req; + } + + subreq = cli_cifs_rename_send( + state, ev, cli, fname_src,fname_dst, replace); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_rename_done_cifs, req); + return req; +} + +static void cli_rename_done1(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb1_rename_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_rename_done_cifs(struct tevent_req *subreq) +{ + NTSTATUS status = cli_cifs_rename_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_rename_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_rename_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_rename_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_rename(struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_rename_send(frame, ev, cli, fname_src, fname_dst, replace); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_rename_recv(req); + cli->raw_status = status; /* cli_smb2_rename_recv doesn't set this */ + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + NT Rename a file. +****************************************************************************/ + +static void cli_ntrename_internal_done(struct tevent_req *subreq); + +struct cli_ntrename_internal_state { + uint16_t vwv[4]; +}; + +static struct tevent_req *cli_ntrename_internal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + uint16_t rename_flag) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_ntrename_internal_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, + struct cli_ntrename_internal_state); + if (req == NULL) { + return NULL; + } + + /* + * Strip a MSDFS path from fname_dst if we were given one. + */ + status = cli_dfs_target_check(state, + cli, + fname_src, + fname_dst, + &fname_dst); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + SSVAL(state->vwv+0, 0 ,FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY); + SSVAL(state->vwv+1, 0, rename_flag); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname_src, + strlen(fname_src)+1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname_src, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + bytes = talloc_realloc(state, bytes, uint8_t, + talloc_get_size(bytes)+1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + bytes[talloc_get_size(bytes)-1] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname_dst, + strlen(fname_dst)+1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + subreq = cli_smb_send(state, ev, cli, SMBntrename, additional_flags, + additional_flags2, + 4, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ntrename_internal_done, req); + return req; +} + +static void cli_ntrename_internal_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb_recv( + subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_ntrename_internal_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct tevent_req *cli_ntrename_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst) +{ + return cli_ntrename_internal_send(mem_ctx, + ev, + cli, + fname_src, + fname_dst, + RENAME_FLAG_RENAME); +} + +NTSTATUS cli_ntrename_recv(struct tevent_req *req) +{ + return cli_ntrename_internal_recv(req); +} + +NTSTATUS cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fname_dst) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_ntrename_send(frame, ev, cli, fname_src, fname_dst); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_ntrename_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + NT hardlink a file. +****************************************************************************/ + +static struct tevent_req *cli_nt_hardlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst) +{ + return cli_ntrename_internal_send(mem_ctx, + ev, + cli, + fname_src, + fname_dst, + RENAME_FLAG_HARD_LINK); +} + +static NTSTATUS cli_nt_hardlink_recv(struct tevent_req *req) +{ + return cli_ntrename_internal_recv(req); +} + +struct cli_smb2_hardlink_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t fnum_src; + const char *fname_dst; + bool overwrite; + NTSTATUS status; +}; + +static void cli_smb2_hardlink_opened(struct tevent_req *subreq); +static void cli_smb2_hardlink_info_set(struct tevent_req *subreq); +static void cli_smb2_hardlink_closed(struct tevent_req *subreq); + +static struct tevent_req *cli_smb2_hardlink_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool overwrite, + struct smb2_create_blobs *in_cblobs) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_smb2_hardlink_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create( + mem_ctx, &state, struct cli_smb2_hardlink_state); + if (req == NULL) { + return NULL; + } + + /* + * Strip a MSDFS path from fname_dst if we were given one. + */ + status = cli_dfs_target_check(state, + cli, + fname_src, + fname_dst, + &fname_dst); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->ev = ev; + state->cli = cli; + state->fname_dst = fname_dst; + state->overwrite = overwrite; + + subreq = cli_smb2_create_fnum_send( + state, + ev, + cli, + fname_src, + 0, /* create_flags */ + SMB2_IMPERSONATION_IMPERSONATION, + FILE_WRITE_ATTRIBUTES, + 0, /* file attributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition */ + FILE_NON_DIRECTORY_FILE, /* no hardlinks on directories */ + in_cblobs); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb2_hardlink_opened, req); + return req; +} + +static void cli_smb2_hardlink_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_hardlink_state *state = tevent_req_data( + req, struct cli_smb2_hardlink_state); + NTSTATUS status; + smb_ucs2_t *ucs2_dst; + size_t ucs2_len; + DATA_BLOB inbuf; + bool ok; + + status = cli_smb2_create_fnum_recv( + subreq, &state->fnum_src, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + ok = push_ucs2_talloc(state, &ucs2_dst, state->fname_dst, &ucs2_len); + if (!ok || (ucs2_len < 2)) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + /* Don't 0-terminate the name */ + ucs2_len -= 2; + + inbuf = data_blob_talloc_zero(state, ucs2_len + 20); + if (tevent_req_nomem(inbuf.data, req)) { + return; + } + + if (state->overwrite) { + SCVAL(inbuf.data, 0, 1); + } + SIVAL(inbuf.data, 16, ucs2_len); + memcpy(inbuf.data + 20, ucs2_dst, ucs2_len); + TALLOC_FREE(ucs2_dst); + + subreq = cli_smb2_set_info_fnum_send( + state, + state->ev, + state->cli, + state->fnum_src, + 1, /* in_info_type */ + SMB_FILE_LINK_INFORMATION - 1000, /* in_file_info_class */ + &inbuf, + 0); /* in_additional_info */ + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_hardlink_info_set, req); +} + +static void cli_smb2_hardlink_info_set(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb2_hardlink_state *state = tevent_req_data( + req, struct cli_smb2_hardlink_state); + + state->status = cli_smb2_set_info_fnum_recv(subreq); + TALLOC_FREE(subreq); + + /* ignore error here, we need to close the file */ + + subreq = cli_smb2_close_fnum_send( + state, state->ev, state->cli, state->fnum_src); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb2_hardlink_closed, req); +} + +static void cli_smb2_hardlink_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_close_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_smb2_hardlink_recv(struct tevent_req *req) +{ + struct cli_smb2_hardlink_state *state = tevent_req_data( + req, struct cli_smb2_hardlink_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + return state->status; +} + +struct cli_hardlink_state { + uint8_t dummy; +}; + +static void cli_hardlink_done(struct tevent_req *subreq); +static void cli_hardlink_done2(struct tevent_req *subreq); + +struct tevent_req *cli_hardlink_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_hardlink_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_hardlink_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_hardlink_send( + state, ev, cli, fname_src, fname_dst, false, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_hardlink_done2, req); + return req; + } + + subreq = cli_nt_hardlink_send(state, ev, cli, fname_src, fname_dst); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_hardlink_done, req); + return req; +} + +static void cli_hardlink_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_nt_hardlink_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_hardlink_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_hardlink_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_hardlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_hardlink( + struct cli_state *cli, const char *fname_src, const char *fname_dst) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_hardlink_send(frame, ev, cli, fname_src, fname_dst); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_hardlink_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Delete a file. +****************************************************************************/ + +static void cli_unlink_done(struct tevent_req *subreq); +static void cli_unlink_done2(struct tevent_req *subreq); + +struct cli_unlink_state { + uint16_t vwv[1]; +}; + +struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t mayhave_attrs) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_unlink_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_unlink_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_unlink_send(state, ev, cli, fname, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_unlink_done2, req); + return req; + } + + if (mayhave_attrs & 0xFFFF0000) { + /* + * Don't allow attributes greater than + * 16-bits for a 16-bit protocol value. + */ + if (tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER)) { + return tevent_req_post(req, ev); + } + } + + SSVAL(state->vwv+0, 0, mayhave_attrs); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname, + strlen(fname)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBunlink, additional_flags, + additional_flags2, + 1, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_unlink_done, req); + return req; +} + +static void cli_unlink_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb_recv( + subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_unlink_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_unlink_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_unlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_unlink(struct cli_state *cli, const char *fname, uint32_t mayhave_attrs) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_unlink_send(frame, ev, cli, fname, mayhave_attrs); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_unlink_recv(req); + cli->raw_status = status; /* cli_smb2_unlink_recv doesn't set this */ + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Create a directory. +****************************************************************************/ + +static void cli_mkdir_done(struct tevent_req *subreq); +static void cli_mkdir_done2(struct tevent_req *subreq); + +struct cli_mkdir_state { + int dummy; +}; + +struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_mkdir_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_mkdir_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_mkdir_send(state, ev, cli, dname); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_mkdir_done2, req); + return req; + } + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), dname, + strlen(dname)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(dname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBmkdir, additional_flags, + additional_flags2, + 0, NULL, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_mkdir_done, req); + return req; +} + +static void cli_mkdir_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static void cli_mkdir_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_mkdir_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_mkdir_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_mkdir(struct cli_state *cli, const char *dname) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_mkdir_send(frame, ev, cli, dname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_mkdir_recv(req); + cli->raw_status = status; /* cli_smb2_mkdir_recv doesn't set this */ + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Remove a directory. +****************************************************************************/ + +static void cli_rmdir_done(struct tevent_req *subreq); +static void cli_rmdir_done2(struct tevent_req *subreq); + +struct cli_rmdir_state { + int dummy; +}; + +struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_rmdir_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_rmdir_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_rmdir_send(state, ev, cli, dname, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_rmdir_done2, req); + return req; + } + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), dname, + strlen(dname)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(dname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBrmdir, additional_flags, + additional_flags2, + 0, NULL, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_rmdir_done, req); + return req; +} + +static void cli_rmdir_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb_recv( + subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_rmdir_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_rmdir_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_rmdir_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_rmdir(struct cli_state *cli, const char *dname) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_rmdir_send(frame, ev, cli, dname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_rmdir_recv(req); + cli->raw_status = status; /* cli_smb2_rmdir_recv doesn't set this */ + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Set or clear the delete on close flag. +****************************************************************************/ + +struct doc_state { + uint8_t data[1]; +}; + +static void cli_nt_delete_on_close_smb1_done(struct tevent_req *subreq); +static void cli_nt_delete_on_close_smb2_done(struct tevent_req *subreq); + +struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool flag) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct doc_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct doc_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_delete_on_close_send(state, ev, cli, + fnum, flag); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_nt_delete_on_close_smb2_done, + req); + return req; + } + + /* Setup data array. */ + SCVAL(&state->data[0], 0, flag ? 1 : 0); + + subreq = cli_setfileinfo_send( + state, + ev, + cli, + fnum, + SMB_SET_FILE_DISPOSITION_INFO, + state->data, + sizeof(state->data)); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, + cli_nt_delete_on_close_smb1_done, + req); + return req; +} + +static void cli_nt_delete_on_close_smb1_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setfileinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_nt_delete_on_close_smb2_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_delete_on_close_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_nt_delete_on_close_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_nt_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_nt_delete_on_close_send(frame, + ev, + cli, + fnum, + flag); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_nt_delete_on_close_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_ntcreate1_state { + uint16_t vwv[24]; + uint16_t fnum; + struct smb_create_returns cr; + struct tevent_req *subreq; +}; + +static void cli_ntcreate1_done(struct tevent_req *subreq); +static bool cli_ntcreate1_cancel(struct tevent_req *req); + +static struct tevent_req *cli_ntcreate1_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint32_t ImpersonationLevel, + uint8_t SecurityFlags) +{ + struct tevent_req *req, *subreq; + struct cli_ntcreate1_state *state; + uint16_t *vwv; + uint8_t *bytes; + size_t converted_len; + uint16_t additional_flags2 = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate1_state); + if (req == NULL) { + return NULL; + } + + vwv = state->vwv; + + SCVAL(vwv+0, 0, 0xFF); + SCVAL(vwv+0, 1, 0); + SSVAL(vwv+1, 0, 0); + SCVAL(vwv+2, 0, 0); + + if (cli->use_oplocks) { + CreatFlags |= (REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK); + } + SIVAL(vwv+3, 1, CreatFlags); + SIVAL(vwv+5, 1, 0x0); /* RootDirectoryFid */ + SIVAL(vwv+7, 1, DesiredAccess); + SIVAL(vwv+9, 1, 0x0); /* AllocationSize */ + SIVAL(vwv+11, 1, 0x0); /* AllocationSize */ + SIVAL(vwv+13, 1, FileAttributes); + SIVAL(vwv+15, 1, ShareAccess); + SIVAL(vwv+17, 1, CreateDisposition); + SIVAL(vwv+19, 1, CreateOptions | + (cli->backup_intent ? FILE_OPEN_FOR_BACKUP_INTENT : 0)); + SIVAL(vwv+21, 1, ImpersonationLevel); + SCVAL(vwv+23, 1, SecurityFlags); + + bytes = talloc_array(state, uint8_t, 0); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), + fname, strlen(fname)+1, + &converted_len); + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + /* sigh. this copes with broken netapp filer behaviour */ + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", 1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + SSVAL(vwv+2, 1, converted_len); + + subreq = cli_smb_send(state, ev, cli, SMBntcreateX, 0, + additional_flags2, 24, vwv, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ntcreate1_done, req); + + state->subreq = subreq; + tevent_req_set_cancel_fn(req, cli_ntcreate1_cancel); + + return req; +} + +static void cli_ntcreate1_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_ntcreate1_state *state = tevent_req_data( + req, struct cli_ntcreate1_state); + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 34, &wct, &vwv, + &num_bytes, &bytes); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + state->cr.oplock_level = CVAL(vwv+2, 0); + state->fnum = SVAL(vwv+2, 1); + state->cr.create_action = IVAL(vwv+3, 1); + state->cr.creation_time = BVAL(vwv+5, 1); + state->cr.last_access_time = BVAL(vwv+9, 1); + state->cr.last_write_time = BVAL(vwv+13, 1); + state->cr.change_time = BVAL(vwv+17, 1); + state->cr.file_attributes = IVAL(vwv+21, 1); + state->cr.allocation_size = BVAL(vwv+23, 1); + state->cr.end_of_file = BVAL(vwv+27, 1); + + tevent_req_done(req); +} + +static bool cli_ntcreate1_cancel(struct tevent_req *req) +{ + struct cli_ntcreate1_state *state = tevent_req_data( + req, struct cli_ntcreate1_state); + return tevent_req_cancel(state->subreq); +} + +static NTSTATUS cli_ntcreate1_recv(struct tevent_req *req, + uint16_t *pfnum, + struct smb_create_returns *cr) +{ + struct cli_ntcreate1_state *state = tevent_req_data( + req, struct cli_ntcreate1_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; + if (cr != NULL) { + *cr = state->cr; + } + return NT_STATUS_OK; +} + +struct cli_ntcreate_state { + struct smb_create_returns cr; + uint16_t fnum; + struct tevent_req *subreq; +}; + +static void cli_ntcreate_done_nt1(struct tevent_req *subreq); +static void cli_ntcreate_done_smb2(struct tevent_req *subreq); +static bool cli_ntcreate_cancel(struct tevent_req *req); + +struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t create_flags, + uint32_t desired_access, + uint32_t file_attributes, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint32_t impersonation_level, + uint8_t security_flags) +{ + struct tevent_req *req, *subreq; + struct cli_ntcreate_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_ntcreate_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + if (cli->use_oplocks) { + create_flags |= REQUEST_OPLOCK|REQUEST_BATCH_OPLOCK; + } + + subreq = cli_smb2_create_fnum_send( + state, + ev, + cli, + fname, + create_flags, + impersonation_level, + desired_access, + file_attributes, + share_access, + create_disposition, + create_options, + NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ntcreate_done_smb2, req); + } else { + subreq = cli_ntcreate1_send( + state, ev, cli, fname, create_flags, desired_access, + file_attributes, share_access, create_disposition, + create_options, impersonation_level, security_flags); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ntcreate_done_nt1, req); + } + + state->subreq = subreq; + tevent_req_set_cancel_fn(req, cli_ntcreate_cancel); + + return req; +} + +static void cli_ntcreate_done_nt1(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_ntcreate_state *state = tevent_req_data( + req, struct cli_ntcreate_state); + NTSTATUS status; + + status = cli_ntcreate1_recv(subreq, &state->fnum, &state->cr); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static void cli_ntcreate_done_smb2(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_ntcreate_state *state = tevent_req_data( + req, struct cli_ntcreate_state); + NTSTATUS status; + + status = cli_smb2_create_fnum_recv( + subreq, + &state->fnum, + &state->cr, + NULL, + NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static bool cli_ntcreate_cancel(struct tevent_req *req) +{ + struct cli_ntcreate_state *state = tevent_req_data( + req, struct cli_ntcreate_state); + return tevent_req_cancel(state->subreq); +} + +NTSTATUS cli_ntcreate_recv(struct tevent_req *req, uint16_t *fnum, + struct smb_create_returns *cr) +{ + struct cli_ntcreate_state *state = tevent_req_data( + req, struct cli_ntcreate_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (fnum != NULL) { + *fnum = state->fnum; + } + if (cr != NULL) { + *cr = state->cr; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_ntcreate(struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + uint16_t *pfid, + struct smb_create_returns *cr) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + uint32_t ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_ntcreate_send(frame, ev, cli, fname, CreatFlags, + DesiredAccess, FileAttributes, ShareAccess, + CreateDisposition, CreateOptions, + ImpersonationLevel, SecurityFlags); + if (req == NULL) { + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_ntcreate_recv(req, pfid, cr); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_nttrans_create_state { + uint16_t fnum; + struct smb_create_returns cr; +}; + +static void cli_nttrans_create_done(struct tevent_req *subreq); + +struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + struct security_descriptor *secdesc, + struct ea_struct *eas, + int num_eas) +{ + struct tevent_req *req, *subreq; + struct cli_nttrans_create_state *state; + uint8_t *param; + uint8_t *secdesc_buf; + size_t secdesc_len; + NTSTATUS status; + size_t converted_len; + uint16_t additional_flags2 = 0; + + req = tevent_req_create(mem_ctx, + &state, struct cli_nttrans_create_state); + if (req == NULL) { + return NULL; + } + + if (secdesc != NULL) { + status = marshall_sec_desc(talloc_tos(), secdesc, + &secdesc_buf, &secdesc_len); + if (tevent_req_nterror(req, status)) { + DEBUG(10, ("marshall_sec_desc failed: %s\n", + nt_errstr(status))); + return tevent_req_post(req, ev); + } + } else { + secdesc_buf = NULL; + secdesc_len = 0; + } + + if (num_eas != 0) { + /* + * TODO ;-) + */ + tevent_req_nterror(req, NT_STATUS_NOT_IMPLEMENTED); + return tevent_req_post(req, ev); + } + + param = talloc_array(state, uint8_t, 53); + if (tevent_req_nomem(param, req)) { + return tevent_req_post(req, ev); + } + + param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn), + fname, strlen(fname), + &converted_len); + if (tevent_req_nomem(param, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + SIVAL(param, 0, CreatFlags); + SIVAL(param, 4, 0x0); /* RootDirectoryFid */ + SIVAL(param, 8, DesiredAccess); + SIVAL(param, 12, 0x0); /* AllocationSize */ + SIVAL(param, 16, 0x0); /* AllocationSize */ + SIVAL(param, 20, FileAttributes); + SIVAL(param, 24, ShareAccess); + SIVAL(param, 28, CreateDisposition); + SIVAL(param, 32, CreateOptions | + (cli->backup_intent ? FILE_OPEN_FOR_BACKUP_INTENT : 0)); + SIVAL(param, 36, secdesc_len); + SIVAL(param, 40, 0); /* EA length*/ + SIVAL(param, 44, converted_len); + SIVAL(param, 48, 0x02); /* ImpersonationLevel */ + SCVAL(param, 52, SecurityFlags); + + subreq = cli_trans_send(state, ev, cli, + additional_flags2, /* additional_flags2 */ + SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_CREATE, 0, + NULL, 0, 0, /* setup */ + param, talloc_get_size(param), 128, /* param */ + secdesc_buf, secdesc_len, 0); /* data */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_nttrans_create_done, req); + return req; +} + +static void cli_nttrans_create_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_nttrans_create_state *state = tevent_req_data( + req, struct cli_nttrans_create_state); + uint8_t *param; + uint32_t num_param; + NTSTATUS status; + + status = cli_trans_recv(subreq, talloc_tos(), NULL, + NULL, 0, NULL, /* rsetup */ + ¶m, 69, &num_param, + NULL, 0, NULL); + if (tevent_req_nterror(req, status)) { + return; + } + state->cr.oplock_level = CVAL(param, 0); + state->fnum = SVAL(param, 2); + state->cr.create_action = IVAL(param, 4); + state->cr.creation_time = BVAL(param, 12); + state->cr.last_access_time = BVAL(param, 20); + state->cr.last_write_time = BVAL(param, 28); + state->cr.change_time = BVAL(param, 36); + state->cr.file_attributes = IVAL(param, 44); + state->cr.allocation_size = BVAL(param, 48); + state->cr.end_of_file = BVAL(param, 56); + + TALLOC_FREE(param); + tevent_req_done(req); +} + +NTSTATUS cli_nttrans_create_recv(struct tevent_req *req, + uint16_t *fnum, + struct smb_create_returns *cr) +{ + struct cli_nttrans_create_state *state = tevent_req_data( + req, struct cli_nttrans_create_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *fnum = state->fnum; + if (cr != NULL) { + *cr = state->cr; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_nttrans_create(struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + struct security_descriptor *secdesc, + struct ea_struct *eas, + int num_eas, + uint16_t *pfid, + struct smb_create_returns *cr) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_nttrans_create_send(frame, ev, cli, fname, CreatFlags, + DesiredAccess, FileAttributes, + ShareAccess, CreateDisposition, + CreateOptions, SecurityFlags, + secdesc, eas, num_eas); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_nttrans_create_recv(req, pfid, cr); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Open a file + WARNING: if you open with O_WRONLY then getattrE won't work! +****************************************************************************/ + +struct cli_openx_state { + const char *fname; + uint16_t vwv[15]; + uint16_t fnum; + struct iovec bytes; +}; + +static void cli_openx_done(struct tevent_req *subreq); + +struct tevent_req *cli_openx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, const char *fname, + int flags, int share_mode, + struct tevent_req **psmbreq) +{ + struct tevent_req *req, *subreq; + struct cli_openx_state *state; + unsigned openfn; + unsigned accessmode; + uint8_t additional_flags; + uint16_t additional_flags2 = 0; + uint8_t *bytes; + + req = tevent_req_create(mem_ctx, &state, struct cli_openx_state); + if (req == NULL) { + return NULL; + } + + openfn = 0; + if (flags & O_CREAT) { + openfn |= (1<<4); + } + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) + openfn |= (1<<1); + else + openfn |= (1<<0); + } + + accessmode = (share_mode<<4); + + if ((flags & O_ACCMODE) == O_RDWR) { + accessmode |= 2; + } else if ((flags & O_ACCMODE) == O_WRONLY) { + accessmode |= 1; + } + +#if defined(O_SYNC) + if ((flags & O_SYNC) == O_SYNC) { + accessmode |= (1<<14); + } +#endif /* O_SYNC */ + + if (share_mode == DENY_FCB) { + accessmode = 0xFF; + } + + SCVAL(state->vwv + 0, 0, 0xFF); + SCVAL(state->vwv + 0, 1, 0); + SSVAL(state->vwv + 1, 0, 0); + SSVAL(state->vwv + 2, 0, 0); /* no additional info */ + SSVAL(state->vwv + 3, 0, accessmode); + SSVAL(state->vwv + 4, 0, FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + SSVAL(state->vwv + 5, 0, 0); + SIVAL(state->vwv + 6, 0, 0); + SSVAL(state->vwv + 8, 0, openfn); + SIVAL(state->vwv + 9, 0, 0); + SIVAL(state->vwv + 11, 0, 0); + SIVAL(state->vwv + 13, 0, 0); + + additional_flags = 0; + + if (cli->use_oplocks) { + /* if using oplocks then ask for a batch oplock via + core and extended methods */ + additional_flags = + FLAG_REQUEST_OPLOCK|FLAG_REQUEST_BATCH_OPLOCK; + SSVAL(state->vwv+2, 0, SVAL(state->vwv+2, 0) | 6); + } + + bytes = talloc_array(state, uint8_t, 0); + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname, + strlen(fname)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + state->bytes.iov_base = (void *)bytes; + state->bytes.iov_len = talloc_get_size(bytes); + + subreq = cli_smb_req_create(state, ev, cli, SMBopenX, additional_flags, + additional_flags2, 15, state->vwv, 1, &state->bytes); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_openx_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_openx_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, const char *fname, + int flags, int share_mode) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_openx_create(mem_ctx, ev, cli, fname, flags, share_mode, + &subreq); + if (req == NULL) { + return NULL; + } + + status = smb1cli_req_chain_submit(&subreq, 1); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + return req; +} + +static void cli_openx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_openx_state *state = tevent_req_data( + req, struct cli_openx_state); + uint8_t wct; + uint16_t *vwv; + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 3, &wct, &vwv, NULL, + NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + state->fnum = SVAL(vwv+2, 0); + tevent_req_done(req); +} + +NTSTATUS cli_openx_recv(struct tevent_req *req, uint16_t *pfnum) +{ + struct cli_openx_state *state = tevent_req_data( + req, struct cli_openx_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; + return NT_STATUS_OK; +} + +NTSTATUS cli_openx(struct cli_state *cli, const char *fname, int flags, + int share_mode, uint16_t *pfnum) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + req = cli_openx_send(frame, ev, cli, fname, flags, share_mode); + if (req == NULL) { + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_openx_recv(req, pfnum); + fail: + TALLOC_FREE(frame); + return status; +} +/**************************************************************************** + Synchronous wrapper function that does an NtCreateX open by preference + and falls back to openX if this fails. +****************************************************************************/ + +NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, + int share_mode_in, uint16_t *pfnum) +{ + NTSTATUS status; + unsigned int openfn = 0; + unsigned int dos_deny = 0; + uint32_t access_mask, share_mode, create_disposition, create_options; + struct smb_create_returns cr = {0}; + + /* Do the initial mapping into OpenX parameters. */ + if (flags & O_CREAT) { + openfn |= (1<<4); + } + if (!(flags & O_EXCL)) { + if (flags & O_TRUNC) + openfn |= (1<<1); + else + openfn |= (1<<0); + } + + dos_deny = (share_mode_in<<4); + + if ((flags & O_ACCMODE) == O_RDWR) { + dos_deny |= 2; + } else if ((flags & O_ACCMODE) == O_WRONLY) { + dos_deny |= 1; + } + +#if defined(O_SYNC) + if ((flags & O_SYNC) == O_SYNC) { + dos_deny |= (1<<14); + } +#endif /* O_SYNC */ + + if (share_mode_in == DENY_FCB) { + dos_deny = 0xFF; + } + + if (!map_open_params_to_ntcreate(fname, dos_deny, + openfn, &access_mask, + &share_mode, &create_disposition, + &create_options, NULL)) { + goto try_openx; + } + + status = cli_ntcreate(cli, + fname, + 0, + access_mask, + 0, + share_mode, + create_disposition, + create_options, + 0, + pfnum, + &cr); + + /* Try and cope will all varients of "we don't do this call" + and fall back to openX. */ + + if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_IMPLEMENTED) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_INFO_CLASS) || + NT_STATUS_EQUAL(status,NT_STATUS_PROCEDURE_NOT_FOUND) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_LEVEL) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_REQUEST) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_STATE) || + NT_STATUS_EQUAL(status,NT_STATUS_CTL_FILE_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status,NT_STATUS_UNSUCCESSFUL)) { + goto try_openx; + } + + if (NT_STATUS_IS_OK(status) && + (create_options & FILE_NON_DIRECTORY_FILE) && + (cr.file_attributes & FILE_ATTRIBUTE_DIRECTORY)) + { + /* + * Some (broken) servers return a valid handle + * for directories even if FILE_NON_DIRECTORY_FILE + * is set. Just close the handle and set the + * error explicitly to NT_STATUS_FILE_IS_A_DIRECTORY. + */ + status = cli_close(cli, *pfnum); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + status = NT_STATUS_FILE_IS_A_DIRECTORY; + /* Set this so libsmbclient can retrieve it. */ + cli->raw_status = status; + } + + return status; + + try_openx: + + return cli_openx(cli, fname, flags, share_mode_in, pfnum); +} + +/**************************************************************************** + Close a file. +****************************************************************************/ + +struct cli_smb1_close_state { + uint16_t vwv[3]; +}; + +static void cli_smb1_close_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb1_close_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + struct tevent_req **psubreq) +{ + struct tevent_req *req, *subreq; + struct cli_smb1_close_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_smb1_close_state); + if (req == NULL) { + return NULL; + } + + SSVAL(state->vwv+0, 0, fnum); + SIVALS(state->vwv+1, 0, -1); + + subreq = cli_smb_req_create(state, ev, cli, SMBclose, 0, 0, + 3, state->vwv, 0, NULL); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_smb1_close_done, req); + *psubreq = subreq; + return req; +} + +static void cli_smb1_close_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +struct cli_close_state { + int dummy; +}; + +static void cli_close_done(struct tevent_req *subreq); + +struct tevent_req *cli_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum) +{ + struct tevent_req *req, *subreq; + struct cli_close_state *state; + NTSTATUS status; + + req = tevent_req_create(mem_ctx, &state, struct cli_close_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_close_fnum_send(state, + ev, + cli, + fnum); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } else { + struct tevent_req *ch_req = NULL; + subreq = cli_smb1_close_create(state, ev, cli, fnum, &ch_req); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + status = smb1cli_req_chain_submit(&ch_req, 1); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + } + + tevent_req_set_callback(subreq, cli_close_done, req); + return req; +} + +static void cli_close_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status = NT_STATUS_OK; + bool err = tevent_req_is_nterror(subreq, &status); + + TALLOC_FREE(subreq); + if (err) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_close_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_close(struct cli_state *cli, uint16_t fnum) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_close_send(frame, ev, cli, fnum); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_close_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Truncate a file to a specified size +****************************************************************************/ + +struct ftrunc_state { + uint8_t data[8]; +}; + +static void cli_ftruncate_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setfileinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +struct tevent_req *cli_ftruncate_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t size) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct ftrunc_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct ftrunc_state); + if (req == NULL) { + return NULL; + } + + /* Setup data array. */ + SBVAL(state->data, 0, size); + + subreq = cli_setfileinfo_send( + state, + ev, + cli, + fnum, + SMB_SET_FILE_END_OF_FILE_INFO, + state->data, + sizeof(state->data)); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ftruncate_done, req); + return req; +} + +NTSTATUS cli_ftruncate_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_ftruncate(struct cli_state *cli, uint16_t fnum, uint64_t size) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_ftruncate(cli, fnum, size); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_ftruncate_send(frame, + ev, + cli, + fnum, + size); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_ftruncate_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +static uint8_t *cli_lockingx_put_locks( + uint8_t *buf, + bool large, + uint16_t num_locks, + const struct smb1_lock_element *locks) +{ + uint16_t i; + + for (i=0; i<num_locks; i++) { + const struct smb1_lock_element *e = &locks[i]; + if (large) { + SSVAL(buf, 0, e->pid); + SSVAL(buf, 2, 0); + SOFF_T_R(buf, 4, e->offset); + SOFF_T_R(buf, 12, e->length); + buf += 20; + } else { + SSVAL(buf, 0, e->pid); + SIVAL(buf, 2, e->offset); + SIVAL(buf, 6, e->length); + buf += 10; + } + } + return buf; +} + +struct cli_lockingx_state { + uint16_t vwv[8]; + struct iovec bytes; + struct tevent_req *subreq; +}; + +static void cli_lockingx_done(struct tevent_req *subreq); +static bool cli_lockingx_cancel(struct tevent_req *req); + +struct tevent_req *cli_lockingx_create( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t typeoflock, + uint8_t newoplocklevel, + int32_t timeout, + uint16_t num_unlocks, + const struct smb1_lock_element *unlocks, + uint16_t num_locks, + const struct smb1_lock_element *locks, + struct tevent_req **psmbreq) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_lockingx_state *state = NULL; + uint16_t *vwv; + uint8_t *p; + const bool large = (typeoflock & LOCKING_ANDX_LARGE_FILES); + const size_t element_len = large ? 20 : 10; + + /* uint16->size_t, no overflow */ + const size_t num_elements = (size_t)num_locks + (size_t)num_unlocks; + + /* at most 20*2*65535 = 2621400, no overflow */ + const size_t num_bytes = num_elements * element_len; + + req = tevent_req_create(mem_ctx, &state, struct cli_lockingx_state); + if (req == NULL) { + return NULL; + } + vwv = state->vwv; + + SCVAL(vwv + 0, 0, 0xFF); + SCVAL(vwv + 0, 1, 0); + SSVAL(vwv + 1, 0, 0); + SSVAL(vwv + 2, 0, fnum); + SCVAL(vwv + 3, 0, typeoflock); + SCVAL(vwv + 3, 1, newoplocklevel); + SIVALS(vwv + 4, 0, timeout); + SSVAL(vwv + 6, 0, num_unlocks); + SSVAL(vwv + 7, 0, num_locks); + + state->bytes.iov_len = num_bytes; + state->bytes.iov_base = talloc_array(state, uint8_t, num_bytes); + if (tevent_req_nomem(state->bytes.iov_base, req)) { + return tevent_req_post(req, ev); + } + + p = cli_lockingx_put_locks( + state->bytes.iov_base, large, num_unlocks, unlocks); + cli_lockingx_put_locks(p, large, num_locks, locks); + + subreq = cli_smb_req_create( + state, ev, cli, SMBlockingX, 0, 0, 8, vwv, 1, &state->bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_lockingx_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_lockingx_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t typeoflock, + uint8_t newoplocklevel, + int32_t timeout, + uint16_t num_unlocks, + const struct smb1_lock_element *unlocks, + uint16_t num_locks, + const struct smb1_lock_element *locks) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_lockingx_state *state = NULL; + NTSTATUS status; + + req = cli_lockingx_create( + mem_ctx, + ev, + cli, + fnum, + typeoflock, + newoplocklevel, + timeout, + num_unlocks, + unlocks, + num_locks, + locks, + &subreq); + if (req == NULL) { + return NULL; + } + state = tevent_req_data(req, struct cli_lockingx_state); + state->subreq = subreq; + + status = smb1cli_req_chain_submit(&subreq, 1); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + tevent_req_set_cancel_fn(req, cli_lockingx_cancel); + return req; +} + +static void cli_lockingx_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb_recv( + subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static bool cli_lockingx_cancel(struct tevent_req *req) +{ + struct cli_lockingx_state *state = tevent_req_data( + req, struct cli_lockingx_state); + if (state->subreq == NULL) { + return false; + } + return tevent_req_cancel(state->subreq); +} + +NTSTATUS cli_lockingx_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_lockingx( + struct cli_state *cli, + uint16_t fnum, + uint8_t typeoflock, + uint8_t newoplocklevel, + int32_t timeout, + uint16_t num_unlocks, + const struct smb1_lock_element *unlocks, + uint16_t num_locks, + const struct smb1_lock_element *locks) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + unsigned int set_timeout = 0; + unsigned int saved_timeout = 0; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + if (timeout != 0) { + if (timeout == -1) { + set_timeout = 0x7FFFFFFF; + } else { + set_timeout = timeout + 2*1000; + } + saved_timeout = cli_set_timeout(cli, set_timeout); + } + + req = cli_lockingx_send( + frame, + ev, + cli, + fnum, + typeoflock, + newoplocklevel, + timeout, + num_unlocks, + unlocks, + num_locks, + locks); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_lockingx_recv(req); + + if (saved_timeout != 0) { + cli_set_timeout(cli, saved_timeout); + } +fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + send a lock with a specified locktype + this is used for testing LOCKING_ANDX_CANCEL_LOCK +****************************************************************************/ + +NTSTATUS cli_locktype(struct cli_state *cli, uint16_t fnum, + uint32_t offset, uint32_t len, + int timeout, unsigned char locktype) +{ + struct smb1_lock_element lck = { + .pid = cli_getpid(cli), + .offset = offset, + .length = len, + }; + NTSTATUS status; + + status = cli_lockingx( + cli, /* cli */ + fnum, /* fnum */ + locktype, /* typeoflock */ + 0, /* newoplocklevel */ + timeout, /* timeout */ + 0, /* num_unlocks */ + NULL, /* unlocks */ + 1, /* num_locks */ + &lck); /* locks */ + return status; +} + +/**************************************************************************** + Lock a file. + note that timeout is in units of 2 milliseconds +****************************************************************************/ + +NTSTATUS cli_lock32(struct cli_state *cli, uint16_t fnum, + uint32_t offset, uint32_t len, int timeout, + enum brl_type lock_type) +{ + NTSTATUS status; + + status = cli_locktype(cli, fnum, offset, len, timeout, + (lock_type == READ_LOCK? 1 : 0)); + return status; +} + +/**************************************************************************** + Unlock a file. +****************************************************************************/ + +struct cli_unlock_state { + struct smb1_lock_element lck; +}; + +static void cli_unlock_done(struct tevent_req *subreq); + +struct tevent_req *cli_unlock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len) + +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_unlock_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_unlock_state); + if (req == NULL) { + return NULL; + } + state->lck = (struct smb1_lock_element) { + .pid = cli_getpid(cli), + .offset = offset, + .length = len, + }; + + subreq = cli_lockingx_send( + state, /* mem_ctx */ + ev, /* tevent_context */ + cli, /* cli */ + fnum, /* fnum */ + 0, /* typeoflock */ + 0, /* newoplocklevel */ + 0, /* timeout */ + 1, /* num_unlocks */ + &state->lck, /* unlocks */ + 0, /* num_locks */ + NULL); /* locks */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_unlock_done, req); + return req; +} + +static void cli_unlock_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_lockingx_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_unlock_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_unlock(struct cli_state *cli, + uint16_t fnum, + uint32_t offset, + uint32_t len) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_unlock_send(frame, ev, cli, + fnum, offset, len); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_unlock_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Get/unlock a POSIX lock on a file - internal function. +****************************************************************************/ + +struct posix_lock_state { + uint16_t setup; + uint8_t param[4]; + uint8_t data[POSIX_LOCK_DATA_SIZE]; +}; + +static void cli_posix_unlock_internal_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL, + NULL, 0, NULL, NULL, 0, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static struct tevent_req *cli_posix_lock_internal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len, + bool wait_lock, + enum brl_type lock_type) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct posix_lock_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct posix_lock_state); + if (req == NULL) { + return NULL; + } + + /* Setup setup word. */ + SSVAL(&state->setup, 0, TRANSACT2_SETFILEINFO); + + /* Setup param array. */ + SSVAL(&state->param, 0, fnum); + SSVAL(&state->param, 2, SMB_SET_POSIX_LOCK); + + /* Setup data array. */ + switch (lock_type) { + case READ_LOCK: + SSVAL(&state->data, POSIX_LOCK_TYPE_OFFSET, + POSIX_LOCK_TYPE_READ); + break; + case WRITE_LOCK: + SSVAL(&state->data, POSIX_LOCK_TYPE_OFFSET, + POSIX_LOCK_TYPE_WRITE); + break; + case UNLOCK_LOCK: + SSVAL(&state->data, POSIX_LOCK_TYPE_OFFSET, + POSIX_LOCK_TYPE_UNLOCK); + break; + default: + return NULL; + } + + if (wait_lock) { + SSVAL(&state->data, POSIX_LOCK_FLAGS_OFFSET, + POSIX_LOCK_FLAG_WAIT); + } else { + SSVAL(state->data, POSIX_LOCK_FLAGS_OFFSET, + POSIX_LOCK_FLAG_NOWAIT); + } + + SIVAL(&state->data, POSIX_LOCK_PID_OFFSET, cli_getpid(cli)); + SOFF_T(&state->data, POSIX_LOCK_START_OFFSET, offset); + SOFF_T(&state->data, POSIX_LOCK_LEN_OFFSET, len); + + subreq = cli_trans_send(state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + 0, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + &state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + 4, /* num param. */ + 2, /* max returned param. */ + state->data, /* data. */ + POSIX_LOCK_DATA_SIZE, /* num data. */ + 0); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_unlock_internal_done, req); + return req; +} + +/**************************************************************************** + POSIX Lock a file. +****************************************************************************/ + +struct tevent_req *cli_posix_lock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len, + bool wait_lock, + enum brl_type lock_type) +{ + return cli_posix_lock_internal_send(mem_ctx, ev, cli, fnum, offset, len, + wait_lock, lock_type); +} + +NTSTATUS cli_posix_lock_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_lock(struct cli_state *cli, uint16_t fnum, + uint64_t offset, uint64_t len, + bool wait_lock, enum brl_type lock_type) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + if (lock_type != READ_LOCK && lock_type != WRITE_LOCK) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_lock_send(frame, + ev, + cli, + fnum, + offset, + len, + wait_lock, + lock_type); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_lock_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + POSIX Unlock a file. +****************************************************************************/ + +struct tevent_req *cli_posix_unlock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len) +{ + return cli_posix_lock_internal_send(mem_ctx, ev, cli, fnum, offset, len, + false, UNLOCK_LOCK); +} + +NTSTATUS cli_posix_unlock_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_unlock(struct cli_state *cli, uint16_t fnum, uint64_t offset, uint64_t len) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_unlock_send(frame, + ev, + cli, + fnum, + offset, + len); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_unlock_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Do a SMBgetattrE call. +****************************************************************************/ + +static void cli_getattrE_done(struct tevent_req *subreq); + +struct cli_getattrE_state { + uint16_t vwv[1]; + int zone_offset; + uint32_t attr; + off_t size; + time_t change_time; + time_t access_time; + time_t write_time; +}; + +struct tevent_req *cli_getattrE_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_getattrE_state *state = NULL; + uint8_t additional_flags = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_getattrE_state); + if (req == NULL) { + return NULL; + } + + state->zone_offset = smb1cli_conn_server_time_zone(cli->conn); + SSVAL(state->vwv+0,0,fnum); + + subreq = cli_smb_send(state, ev, cli, SMBgetattrE, additional_flags, 0, + 1, state->vwv, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_getattrE_done, req); + return req; +} + +static void cli_getattrE_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_getattrE_state *state = tevent_req_data( + req, struct cli_getattrE_state); + uint8_t wct; + uint16_t *vwv = NULL; + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 11, &wct, &vwv, + NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->size = (off_t)IVAL(vwv+6,0); + state->attr = SVAL(vwv+10,0); + state->change_time = make_unix_date2(vwv+0, state->zone_offset); + state->access_time = make_unix_date2(vwv+2, state->zone_offset); + state->write_time = make_unix_date2(vwv+4, state->zone_offset); + + tevent_req_done(req); +} + +NTSTATUS cli_getattrE_recv(struct tevent_req *req, + uint32_t *pattr, + off_t *size, + time_t *change_time, + time_t *access_time, + time_t *write_time) +{ + struct cli_getattrE_state *state = tevent_req_data( + req, struct cli_getattrE_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (pattr) { + *pattr = state->attr; + } + if (size) { + *size = state->size; + } + if (change_time) { + *change_time = state->change_time; + } + if (access_time) { + *access_time = state->access_time; + } + if (write_time) { + *write_time = state->write_time; + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Do a SMBgetatr call +****************************************************************************/ + +static void cli_getatr_done(struct tevent_req *subreq); + +struct cli_getatr_state { + int zone_offset; + uint32_t attr; + off_t size; + time_t write_time; +}; + +struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_getatr_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_getatr_state); + if (req == NULL) { + return NULL; + } + + state->zone_offset = smb1cli_conn_server_time_zone(cli->conn); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname, + strlen(fname)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBgetatr, additional_flags, + additional_flags2, + 0, NULL, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_getatr_done, req); + return req; +} + +static void cli_getatr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_getatr_state *state = tevent_req_data( + req, struct cli_getatr_state); + uint8_t wct; + uint16_t *vwv = NULL; + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 4, &wct, &vwv, NULL, + NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->attr = SVAL(vwv+0,0); + state->size = (off_t)IVAL(vwv+3,0); + state->write_time = make_unix_date3(vwv+1, state->zone_offset); + + tevent_req_done(req); +} + +NTSTATUS cli_getatr_recv(struct tevent_req *req, + uint32_t *pattr, + off_t *size, + time_t *write_time) +{ + struct cli_getatr_state *state = tevent_req_data( + req, struct cli_getatr_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (pattr) { + *pattr = state->attr; + } + if (size) { + *size = state->size; + } + if (write_time) { + *write_time = state->write_time; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_getatr(struct cli_state *cli, + const char *fname, + uint32_t *pattr, + off_t *size, + time_t *write_time) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_getatr(cli, + fname, + pattr, + size, + write_time); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_getatr_send(frame, ev, cli, fname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_getatr_recv(req, + pattr, + size, + write_time); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Do a SMBsetattrE call. +****************************************************************************/ + +static void cli_setattrE_done(struct tevent_req *subreq); + +struct cli_setattrE_state { + uint16_t vwv[7]; +}; + +struct tevent_req *cli_setattrE_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_setattrE_state *state = NULL; + uint8_t additional_flags = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_setattrE_state); + if (req == NULL) { + return NULL; + } + + SSVAL(state->vwv+0, 0, fnum); + push_dos_date2((uint8_t *)&state->vwv[1], 0, change_time, + smb1cli_conn_server_time_zone(cli->conn)); + push_dos_date2((uint8_t *)&state->vwv[3], 0, access_time, + smb1cli_conn_server_time_zone(cli->conn)); + push_dos_date2((uint8_t *)&state->vwv[5], 0, write_time, + smb1cli_conn_server_time_zone(cli->conn)); + + subreq = cli_smb_send(state, ev, cli, SMBsetattrE, additional_flags, 0, + 7, state->vwv, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_setattrE_done, req); + return req; +} + +static void cli_setattrE_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_setattrE_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_setattrE(struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_setattrE(cli, + fnum, + change_time, + access_time, + write_time); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_setattrE_send(frame, ev, + cli, + fnum, + change_time, + access_time, + write_time); + + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_setattrE_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Do a SMBsetatr call. +****************************************************************************/ + +static void cli_setatr_done(struct tevent_req *subreq); + +struct cli_setatr_state { + uint16_t vwv[8]; +}; + +struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t attr, + time_t mtime) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_setatr_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_setatr_state); + if (req == NULL) { + return NULL; + } + + if (attr & 0xFFFF0000) { + /* + * Don't allow attributes greater than + * 16-bits for a 16-bit protocol value. + */ + if (tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER)) { + return tevent_req_post(req, ev); + } + } + + SSVAL(state->vwv+0, 0, attr); + push_dos_date3((uint8_t *)&state->vwv[1], 0, mtime, smb1cli_conn_server_time_zone(cli->conn)); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname, + strlen(fname)+1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes = talloc_realloc(state, bytes, uint8_t, + talloc_get_size(bytes)+1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + bytes[talloc_get_size(bytes)-1] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), "", + 1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBsetatr, additional_flags, + additional_flags2, + 8, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_setatr_done, req); + return req; +} + +static void cli_setatr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_setatr_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_setatr(struct cli_state *cli, + const char *fname, + uint32_t attr, + time_t mtime) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_setatr(cli, + fname, + attr, + mtime); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_setatr_send(frame, ev, cli, fname, attr, mtime); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_setatr_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Check for existence of a dir. +****************************************************************************/ + +static void cli_chkpath_done(struct tevent_req *subreq); +static void cli_chkpath_opened(struct tevent_req *subreq); +static void cli_chkpath_closed(struct tevent_req *subreq); + +struct cli_chkpath_state { + struct tevent_context *ev; + struct cli_state *cli; +}; + +struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_chkpath_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_chkpath_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_NT1) { + subreq = cli_ntcreate_send( + state, /* mem_ctx */ + state->ev, /* ev */ + state->cli, /* cli */ + fname, /* fname */ + 0, /* create_flags */ + FILE_READ_ATTRIBUTES, /* desired_access */ + FILE_ATTRIBUTE_DIRECTORY, /* FileAttributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE| + FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* CreateDisposition */ + FILE_DIRECTORY_FILE, /* CreateOptions */ + SMB2_IMPERSONATION_IMPERSONATION, + 0); /* SecurityFlags */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_chkpath_opened, req); + return req; + } + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), fname, + strlen(fname)+1, NULL); + + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBcheckpath, additional_flags, + additional_flags2, + 0, NULL, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_chkpath_done, req); + return req; +} + +static void cli_chkpath_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb_recv( + subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_chkpath_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_chkpath_state *state = tevent_req_data( + req, struct cli_chkpath_state); + NTSTATUS status; + uint16_t fnum; + + status = cli_ntcreate_recv(subreq, &fnum, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = cli_close_send(state, state->ev, state->cli, fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_chkpath_closed, req); +} + +static void cli_chkpath_closed(struct tevent_req *subreq) +{ + NTSTATUS status = cli_close_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_chkpath_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_chkpath(struct cli_state *cli, const char *path) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + char *path2 = NULL; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + path2 = talloc_strdup(frame, path); + if (!path2) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + trim_char(path2,'\0','\\'); + if (!*path2) { + path2 = talloc_strdup(frame, "\\"); + if (!path2) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_chkpath_send(frame, ev, cli, path2); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_chkpath_recv(req); + cli->raw_status = status; /* cli_smb2_chkpath_recv doesn't set this */ + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Query disk space. +****************************************************************************/ + +static void cli_dskattr_done(struct tevent_req *subreq); + +struct cli_dskattr_state { + int bsize; + int total; + int avail; +}; + +struct tevent_req *cli_dskattr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_dskattr_state *state = NULL; + uint8_t additional_flags = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_dskattr_state); + if (req == NULL) { + return NULL; + } + + subreq = cli_smb_send(state, ev, cli, SMBdskattr, additional_flags, 0, + 0, NULL, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_dskattr_done, req); + return req; +} + +static void cli_dskattr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_dskattr_state *state = tevent_req_data( + req, struct cli_dskattr_state); + uint8_t wct; + uint16_t *vwv = NULL; + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 4, &wct, &vwv, NULL, + NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + state->bsize = SVAL(vwv+1, 0)*SVAL(vwv+2,0); + state->total = SVAL(vwv+0, 0); + state->avail = SVAL(vwv+3, 0); + tevent_req_done(req); +} + +NTSTATUS cli_dskattr_recv(struct tevent_req *req, int *bsize, int *total, int *avail) +{ + struct cli_dskattr_state *state = tevent_req_data( + req, struct cli_dskattr_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *bsize = state->bsize; + *total = state->total; + *avail = state->avail; + return NT_STATUS_OK; +} + +NTSTATUS cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_dskattr_send(frame, ev, cli); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_dskattr_recv(req, bsize, total, avail); + + fail: + TALLOC_FREE(frame); + return status; +} + +NTSTATUS cli_disk_size(struct cli_state *cli, const char *path, uint64_t *bsize, + uint64_t *total, uint64_t *avail) +{ + uint64_t sectors_per_block; + uint64_t bytes_per_sector; + int old_bsize = 0, old_total = 0, old_avail = 0; + NTSTATUS status; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_dskattr(cli, path, bsize, total, avail); + } + + /* + * Try the trans2 disk full size info call first. + * We already use this in SMBC_fstatvfs_ctx(). + * Ignore 'actual_available_units' as we only + * care about the quota for the caller. + */ + + status = cli_get_fs_full_size_info(cli, + total, + avail, + NULL, + §ors_per_block, + &bytes_per_sector); + + /* Try and cope will all varients of "we don't do this call" + and fall back to cli_dskattr. */ + + if (NT_STATUS_EQUAL(status,NT_STATUS_NOT_IMPLEMENTED) || + NT_STATUS_EQUAL(status,NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_INFO_CLASS) || + NT_STATUS_EQUAL(status,NT_STATUS_PROCEDURE_NOT_FOUND) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_LEVEL) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_PARAMETER) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_REQUEST) || + NT_STATUS_EQUAL(status,NT_STATUS_INVALID_DEVICE_STATE) || + NT_STATUS_EQUAL(status,NT_STATUS_CTL_FILE_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status,NT_STATUS_UNSUCCESSFUL)) { + goto try_dskattr; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (bsize) { + *bsize = sectors_per_block * + bytes_per_sector; + } + + return NT_STATUS_OK; + + try_dskattr: + + /* Old SMB1 core protocol fallback. */ + status = cli_dskattr(cli, &old_bsize, &old_total, &old_avail); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (bsize) { + *bsize = (uint64_t)old_bsize; + } + if (total) { + *total = (uint64_t)old_total; + } + if (avail) { + *avail = (uint64_t)old_avail; + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Create and open a temporary file. +****************************************************************************/ + +static void cli_ctemp_done(struct tevent_req *subreq); + +struct ctemp_state { + uint16_t vwv[3]; + char *ret_path; + uint16_t fnum; +}; + +struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *path) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct ctemp_state *state = NULL; + uint8_t additional_flags = 0; + uint16_t additional_flags2 = 0; + uint8_t *bytes = NULL; + + req = tevent_req_create(mem_ctx, &state, struct ctemp_state); + if (req == NULL) { + return NULL; + } + + SSVAL(state->vwv,0,0); + SIVALS(state->vwv+1,0,-1); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), path, + strlen(path)+1, NULL); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(path, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_smb_send(state, ev, cli, SMBctemp, additional_flags, + additional_flags2, + 3, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_ctemp_done, req); + return req; +} + +static void cli_ctemp_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct ctemp_state *state = tevent_req_data( + req, struct ctemp_state); + NTSTATUS status; + uint8_t wcnt; + uint16_t *vwv; + uint32_t num_bytes = 0; + uint8_t *bytes = NULL; + + status = cli_smb_recv(subreq, state, NULL, 1, &wcnt, &vwv, + &num_bytes, &bytes); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->fnum = SVAL(vwv+0, 0); + + /* From W2K3, the result is just the ASCII name */ + if (num_bytes < 2) { + tevent_req_nterror(req, NT_STATUS_DATA_ERROR); + return; + } + + if (pull_string_talloc(state, + NULL, + 0, + &state->ret_path, + bytes, + num_bytes, + STR_ASCII) == 0) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_ctemp_recv(struct tevent_req *req, + TALLOC_CTX *ctx, + uint16_t *pfnum, + char **outfile) +{ + struct ctemp_state *state = tevent_req_data(req, + struct ctemp_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; + *outfile = talloc_strdup(ctx, state->ret_path); + if (!*outfile) { + return NT_STATUS_NO_MEMORY; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_ctemp(struct cli_state *cli, + TALLOC_CTX *ctx, + const char *path, + uint16_t *pfnum, + char **out_path) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_ctemp_send(frame, ev, cli, path); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_ctemp_recv(req, ctx, pfnum, out_path); + + fail: + TALLOC_FREE(frame); + return status; +} + +/* + send a raw ioctl - used by the torture code +*/ +NTSTATUS cli_raw_ioctl(struct cli_state *cli, uint16_t fnum, uint32_t code, DATA_BLOB *blob) +{ + uint16_t vwv[3]; + NTSTATUS status; + + SSVAL(vwv+0, 0, fnum); + SSVAL(vwv+1, 0, code>>16); + SSVAL(vwv+2, 0, (code&0xFFFF)); + + status = cli_smb(talloc_tos(), cli, SMBioctl, 0, 3, vwv, 0, NULL, + NULL, 0, NULL, NULL, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + *blob = data_blob_null; + return NT_STATUS_OK; +} + +/********************************************************* + Set an extended attribute utility fn. +*********************************************************/ + +static NTSTATUS cli_set_ea(struct cli_state *cli, uint16_t setup_val, + uint8_t *param, unsigned int param_len, + const char *ea_name, + const char *ea_val, size_t ea_len) +{ + uint16_t setup[1]; + unsigned int data_len = 0; + uint8_t *data = NULL; + char *p; + size_t ea_namelen = strlen(ea_name); + NTSTATUS status; + + SSVAL(setup, 0, setup_val); + + if (ea_namelen == 0 && ea_len == 0) { + data_len = 4; + data = talloc_array(talloc_tos(), + uint8_t, + data_len); + if (!data) { + return NT_STATUS_NO_MEMORY; + } + p = (char *)data; + SIVAL(p,0,data_len); + } else { + data_len = 4 + 4 + ea_namelen + 1 + ea_len; + data = talloc_array(talloc_tos(), + uint8_t, + data_len); + if (!data) { + return NT_STATUS_NO_MEMORY; + } + p = (char *)data; + SIVAL(p,0,data_len); + p += 4; + SCVAL(p, 0, 0); /* EA flags. */ + SCVAL(p, 1, ea_namelen); + SSVAL(p, 2, ea_len); + memcpy(p+4, ea_name, ea_namelen+1); /* Copy in the name. */ + memcpy(p+4+ea_namelen+1, ea_val, ea_len); + } + + /* + * FIXME - if we want to do previous version path + * processing on an EA set call we need to turn this + * into calls to cli_trans_send()/cli_trans_recv() + * with a temporary event context, as cli_trans_send() + * have access to the additional_flags2 needed to + * send @GMT- paths. JRA. + */ + + status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, -1, 0, 0, + setup, 1, 0, + param, param_len, 2, + data, data_len, 0, + NULL, + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + NULL, 0, NULL); /* rdata */ + talloc_free(data); + return status; +} + +/********************************************************* + Set an extended attribute on a pathname. +*********************************************************/ + +NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path, + const char *ea_name, const char *ea_val, + size_t ea_len) +{ + unsigned int param_len = 0; + uint8_t *param; + NTSTATUS status; + TALLOC_CTX *frame = NULL; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_ea_path(cli, + path, + ea_name, + ea_val, + ea_len); + } + + frame = talloc_stackframe(); + + param = talloc_array(frame, uint8_t, 6); + if (!param) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + SSVAL(param,0,SMB_INFO_SET_EA); + SSVAL(param,2,0); + SSVAL(param,4,0); + + param = trans2_bytes_push_str(param, smbXcli_conn_use_unicode(cli->conn), + path, strlen(path)+1, + NULL); + param_len = talloc_get_size(param); + + status = cli_set_ea(cli, TRANSACT2_SETPATHINFO, param, param_len, + ea_name, ea_val, ea_len); + + fail: + + TALLOC_FREE(frame); + return status; +} + +/********************************************************* + Set an extended attribute on an fnum. +*********************************************************/ + +NTSTATUS cli_set_ea_fnum(struct cli_state *cli, uint16_t fnum, + const char *ea_name, const char *ea_val, + size_t ea_len) +{ + uint8_t param[6] = { 0, }; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_ea_fnum(cli, + fnum, + ea_name, + ea_val, + ea_len); + } + + SSVAL(param,0,fnum); + SSVAL(param,2,SMB_INFO_SET_EA); + + return cli_set_ea(cli, TRANSACT2_SETFILEINFO, param, 6, + ea_name, ea_val, ea_len); +} + +/********************************************************* + Get an extended attribute list utility fn. +*********************************************************/ + +static bool parse_ea_blob(TALLOC_CTX *ctx, const uint8_t *rdata, + size_t rdata_len, + size_t *pnum_eas, struct ea_struct **pea_list) +{ + struct ea_struct *ea_list = NULL; + size_t num_eas; + size_t ea_size; + const uint8_t *p; + + if (rdata_len < 4) { + return false; + } + + ea_size = (size_t)IVAL(rdata,0); + if (ea_size > rdata_len) { + return false; + } + + if (ea_size == 0) { + /* No EA's present. */ + *pnum_eas = 0; + *pea_list = NULL; + return true; + } + + p = rdata + 4; + ea_size -= 4; + + /* Validate the EA list and count it. */ + for (num_eas = 0; ea_size >= 4; num_eas++) { + unsigned int ea_namelen = CVAL(p,1); + unsigned int ea_valuelen = SVAL(p,2); + if (ea_namelen == 0) { + return false; + } + if (4 + ea_namelen + 1 + ea_valuelen > ea_size) { + return false; + } + ea_size -= 4 + ea_namelen + 1 + ea_valuelen; + p += 4 + ea_namelen + 1 + ea_valuelen; + } + + if (num_eas == 0) { + *pnum_eas = 0; + *pea_list = NULL; + return true; + } + + *pnum_eas = num_eas; + if (!pea_list) { + /* Caller only wants number of EA's. */ + return true; + } + + ea_list = talloc_array(ctx, struct ea_struct, num_eas); + if (!ea_list) { + return false; + } + + p = rdata + 4; + + for (num_eas = 0; num_eas < *pnum_eas; num_eas++ ) { + struct ea_struct *ea = &ea_list[num_eas]; + fstring unix_ea_name; + unsigned int ea_namelen = CVAL(p,1); + unsigned int ea_valuelen = SVAL(p,2); + + ea->flags = CVAL(p,0); + unix_ea_name[0] = '\0'; + pull_ascii(unix_ea_name, p + 4, sizeof(unix_ea_name), rdata_len - PTR_DIFF(p+4, rdata), STR_TERMINATE); + ea->name = talloc_strdup(ea_list, unix_ea_name); + if (!ea->name) { + goto fail; + } + /* Ensure the value is null terminated (in case it's a string). */ + ea->value = data_blob_talloc(ea_list, NULL, ea_valuelen + 1); + if (!ea->value.data) { + goto fail; + } + if (ea_valuelen) { + memcpy(ea->value.data, p+4+ea_namelen+1, ea_valuelen); + } + ea->value.data[ea_valuelen] = 0; + ea->value.length--; + p += 4 + ea_namelen + 1 + ea_valuelen; + } + + *pea_list = ea_list; + return true; + +fail: + TALLOC_FREE(ea_list); + return false; +} + +/********************************************************* + Get an extended attribute list from a pathname. +*********************************************************/ + +struct cli_get_ea_list_path_state { + uint32_t num_data; + uint8_t *data; +}; + +static void cli_get_ea_list_path_done(struct tevent_req *subreq); + +struct tevent_req *cli_get_ea_list_path_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req, *subreq; + struct cli_get_ea_list_path_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_get_ea_list_path_state); + if (req == NULL) { + return NULL; + } + subreq = cli_qpathinfo_send(state, ev, cli, fname, + SMB_INFO_QUERY_ALL_EAS, 4, + CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_get_ea_list_path_done, req); + return req; +} + +static void cli_get_ea_list_path_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_get_ea_list_path_state *state = tevent_req_data( + req, struct cli_get_ea_list_path_state); + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &state->data, + &state->num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_get_ea_list_path_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *pnum_eas, struct ea_struct **peas) +{ + struct cli_get_ea_list_path_state *state = tevent_req_data( + req, struct cli_get_ea_list_path_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (!parse_ea_blob(mem_ctx, state->data, state->num_data, + pnum_eas, peas)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_get_ea_list_path(struct cli_state *cli, const char *path, + TALLOC_CTX *ctx, + size_t *pnum_eas, + struct ea_struct **pea_list) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_get_ea_list_path(cli, + path, + ctx, + pnum_eas, + pea_list); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_get_ea_list_path_send(frame, ev, cli, path); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_get_ea_list_path_recv(req, ctx, pnum_eas, pea_list); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Convert open "flags" arg to uint32_t on wire. +****************************************************************************/ + +static uint32_t open_flags_to_wire(int flags) +{ + int open_mode = flags & O_ACCMODE; + uint32_t ret = 0; + + switch (open_mode) { + case O_WRONLY: + ret |= SMB_O_WRONLY; + break; + case O_RDWR: + ret |= SMB_O_RDWR; + break; + default: + case O_RDONLY: + ret |= SMB_O_RDONLY; + break; + } + + if (flags & O_CREAT) { + ret |= SMB_O_CREAT; + } + if (flags & O_EXCL) { + ret |= SMB_O_EXCL; + } + if (flags & O_TRUNC) { + ret |= SMB_O_TRUNC; + } +#if defined(O_SYNC) + if (flags & O_SYNC) { + ret |= SMB_O_SYNC; + } +#endif /* O_SYNC */ + if (flags & O_APPEND) { + ret |= SMB_O_APPEND; + } +#if defined(O_DIRECT) + if (flags & O_DIRECT) { + ret |= SMB_O_DIRECT; + } +#endif +#if defined(O_DIRECTORY) + if (flags & O_DIRECTORY) { + ret |= SMB_O_DIRECTORY; + } +#endif + return ret; +} + +/**************************************************************************** + Open a file - POSIX semantics. Returns fnum. Doesn't request oplock. +****************************************************************************/ + +struct cli_posix_open_internal_state { + uint16_t setup; + uint8_t *param; + uint8_t data[18]; + uint16_t fnum; /* Out */ +}; + +static void cli_posix_open_internal_done(struct tevent_req *subreq); + +static struct tevent_req *cli_posix_open_internal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t wire_flags, + mode_t mode) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_open_internal_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_open_internal_state); + if (req == NULL) { + return NULL; + } + + /* Setup setup word. */ + SSVAL(&state->setup, 0, TRANSACT2_SETPATHINFO); + + /* Setup param array. */ + state->param = talloc_zero_array(state, uint8_t, 6); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + SSVAL(state->param, 0, SMB_POSIX_PATH_OPEN); + + state->param = trans2_bytes_push_str( + state->param, + smbXcli_conn_use_unicode(cli->conn), + fname, + strlen(fname)+1, + NULL); + + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + + SIVAL(state->data,0,0); /* No oplock. */ + SIVAL(state->data,4,wire_flags); + SIVAL(state->data,8,unix_perms_to_wire(mode)); + SIVAL(state->data,12,0); /* Top bits of perms currently undefined. */ + SSVAL(state->data,16,SMB_NO_INFO_LEVEL_RETURNED); /* No info level returned. */ + + subreq = cli_trans_send(state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + 0, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + &state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + talloc_get_size(state->param),/* num param. */ + 2, /* max returned param. */ + state->data, /* data. */ + 18, /* num data. */ + 12); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_open_internal_done, req); + return req; +} + +static void cli_posix_open_internal_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_posix_open_internal_state *state = tevent_req_data( + req, struct cli_posix_open_internal_state); + NTSTATUS status; + uint8_t *data; + uint32_t num_data; + + status = cli_trans_recv( + subreq, + state, + NULL, + NULL, + 0, + NULL, + NULL, + 0, + NULL, + &data, + 12, + &num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + state->fnum = SVAL(data,2); + tevent_req_done(req); +} + +static NTSTATUS cli_posix_open_internal_recv(struct tevent_req *req, + uint16_t *pfnum) +{ + struct cli_posix_open_internal_state *state = tevent_req_data( + req, struct cli_posix_open_internal_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; + return NT_STATUS_OK; +} + +struct cli_posix_open_state { + uint16_t fnum; +}; + +static void cli_posix_open_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_open_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + int flags, + mode_t mode) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_open_state *state = NULL; + uint32_t wire_flags; + + req = tevent_req_create(mem_ctx, &state, + struct cli_posix_open_state); + if (req == NULL) { + return NULL; + } + + wire_flags = open_flags_to_wire(flags); + + subreq = cli_posix_open_internal_send( + mem_ctx, ev, cli, fname, wire_flags, mode); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_open_done, req); + return req; +} + +static void cli_posix_open_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_posix_open_state *state = tevent_req_data( + req, struct cli_posix_open_state); + NTSTATUS status; + + status = cli_posix_open_internal_recv(subreq, &state->fnum); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_open_recv(struct tevent_req *req, uint16_t *pfnum) +{ + struct cli_posix_open_state *state = tevent_req_data( + req, struct cli_posix_open_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; + return NT_STATUS_OK; +} + +/**************************************************************************** + Open - POSIX semantics. Doesn't request oplock. +****************************************************************************/ + +NTSTATUS cli_posix_open(struct cli_state *cli, const char *fname, + int flags, mode_t mode, uint16_t *pfnum) +{ + + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_posix_open_send( + frame, ev, cli, fname, flags, mode); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_posix_open_recv(req, pfnum); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_posix_mkdir_state { + struct tevent_context *ev; + struct cli_state *cli; +}; + +static void cli_posix_mkdir_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_mkdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + mode_t mode) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_mkdir_state *state = NULL; + uint32_t wire_flags; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_mkdir_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + wire_flags = SMB_O_CREAT | SMB_O_DIRECTORY; + + subreq = cli_posix_open_internal_send( + mem_ctx, ev, cli, fname, wire_flags, mode); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_mkdir_done, req); + return req; +} + +static void cli_posix_mkdir_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + uint16_t fnum; + + status = cli_posix_open_internal_recv(subreq, &fnum); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_posix_mkdir_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_posix_mkdir_send( + frame, ev, cli, fname, mode); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_posix_mkdir_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + unlink or rmdir - POSIX semantics. +****************************************************************************/ + +struct cli_posix_unlink_internal_state { + uint8_t data[2]; +}; + +static void cli_posix_unlink_internal_done(struct tevent_req *subreq); + +static struct tevent_req *cli_posix_unlink_internal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint16_t level) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_unlink_internal_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_posix_unlink_internal_state); + if (req == NULL) { + return NULL; + } + + /* Setup data word. */ + SSVAL(state->data, 0, level); + + subreq = cli_setpathinfo_send(state, ev, cli, + SMB_POSIX_PATH_UNLINK, + fname, + state->data, sizeof(state->data)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_unlink_internal_done, req); + return req; +} + +static void cli_posix_unlink_internal_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setpathinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static NTSTATUS cli_posix_unlink_internal_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_posix_unlink_state { + uint8_t dummy; +}; + +static void cli_posix_unlink_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_unlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_unlink_state *state; + + req = tevent_req_create( + mem_ctx, &state, struct cli_posix_unlink_state); + if (req == NULL) { + return NULL; + } + subreq = cli_posix_unlink_internal_send( + mem_ctx, ev, cli, fname, SMB_POSIX_UNLINK_FILE_TARGET); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_unlink_done, req); + return req; +} + +static void cli_posix_unlink_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_posix_unlink_internal_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_unlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/**************************************************************************** + unlink - POSIX semantics. +****************************************************************************/ + +NTSTATUS cli_posix_unlink(struct cli_state *cli, const char *fname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_unlink_send(frame, + ev, + cli, + fname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_unlink_recv(req); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + rmdir - POSIX semantics. +****************************************************************************/ + +struct cli_posix_rmdir_state { + uint8_t dummy; +}; + +static void cli_posix_rmdir_done(struct tevent_req *subreq); + +struct tevent_req *cli_posix_rmdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_posix_rmdir_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_posix_rmdir_state); + if (req == NULL) { + return NULL; + } + subreq = cli_posix_unlink_internal_send( + mem_ctx, ev, cli, fname, SMB_POSIX_UNLINK_DIRECTORY_TARGET); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_rmdir_done, req); + return req; +} + +static void cli_posix_rmdir_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_posix_unlink_internal_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_posix_rmdir_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_posix_rmdir(struct cli_state *cli, const char *fname) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_rmdir_send(frame, + ev, + cli, + fname); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_rmdir_recv(req, frame); + + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + filechangenotify +****************************************************************************/ + +struct cli_notify_state { + struct tevent_req *subreq; + uint8_t setup[8]; + uint32_t num_changes; + struct notify_change *changes; +}; + +static void cli_notify_done(struct tevent_req *subreq); +static void cli_notify_done_smb2(struct tevent_req *subreq); +static bool cli_notify_cancel(struct tevent_req *req); + +struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint32_t buffer_size, + uint32_t completion_filter, bool recursive) +{ + struct tevent_req *req; + struct cli_notify_state *state; + unsigned old_timeout; + + req = tevent_req_create(mem_ctx, &state, struct cli_notify_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + /* + * Notifies should not time out + */ + old_timeout = cli_set_timeout(cli, 0); + + state->subreq = cli_smb2_notify_send( + state, + ev, + cli, + fnum, + buffer_size, + completion_filter, + recursive); + + cli_set_timeout(cli, old_timeout); + + if (tevent_req_nomem(state->subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + state->subreq, cli_notify_done_smb2, req); + goto done; + } + + SIVAL(state->setup, 0, completion_filter); + SSVAL(state->setup, 4, fnum); + SSVAL(state->setup, 6, recursive); + + /* + * Notifies should not time out + */ + old_timeout = cli_set_timeout(cli, 0); + + state->subreq = cli_trans_send( + state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + 0, /* additional_flags2 */ + SMBnttrans, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + NT_TRANSACT_NOTIFY_CHANGE, /* function. */ + 0, /* flags. */ + (uint16_t *)state->setup, /* setup. */ + 4, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + NULL, /* param. */ + 0, /* num param. */ + buffer_size, /* max returned param. */ + NULL, /* data. */ + 0, /* num data. */ + 0); /* max returned data. */ + + cli_set_timeout(cli, old_timeout); + + if (tevent_req_nomem(state->subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->subreq, cli_notify_done, req); +done: + tevent_req_set_cancel_fn(req, cli_notify_cancel); + return req; +} + +static bool cli_notify_cancel(struct tevent_req *req) +{ + struct cli_notify_state *state = tevent_req_data( + req, struct cli_notify_state); + bool ok; + + ok = tevent_req_cancel(state->subreq); + return ok; +} + +static void cli_notify_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_notify_state *state = tevent_req_data( + req, struct cli_notify_state); + NTSTATUS status; + uint8_t *params; + uint32_t i, ofs, num_params; + uint16_t flags2; + + status = cli_trans_recv(subreq, talloc_tos(), &flags2, NULL, 0, NULL, + ¶ms, 0, &num_params, NULL, 0, NULL); + TALLOC_FREE(subreq); + state->subreq = NULL; + if (tevent_req_nterror(req, status)) { + DEBUG(10, ("cli_trans_recv returned %s\n", nt_errstr(status))); + return; + } + + state->num_changes = 0; + ofs = 0; + + while (num_params - ofs > 12) { + uint32_t next = IVAL(params, ofs); + state->num_changes += 1; + + if ((next == 0) || (ofs+next >= num_params)) { + break; + } + ofs += next; + } + + state->changes = talloc_array(state, struct notify_change, + state->num_changes); + if (tevent_req_nomem(state->changes, req)) { + TALLOC_FREE(params); + return; + } + + ofs = 0; + + for (i=0; i<state->num_changes; i++) { + uint32_t next = IVAL(params, ofs); + uint32_t len = IVAL(params, ofs+8); + ssize_t ret; + char *name; + + if (smb_buffer_oob(num_params, ofs + 12, len)) { + TALLOC_FREE(params); + tevent_req_nterror( + req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->changes[i].action = IVAL(params, ofs+4); + ret = pull_string_talloc(state->changes, + (char *)params, + flags2, + &name, + params+ofs+12, + len, + STR_TERMINATE|STR_UNICODE); + if (ret == -1) { + TALLOC_FREE(params); + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + state->changes[i].name = name; + ofs += next; + } + + TALLOC_FREE(params); + tevent_req_done(req); +} + +static void cli_notify_done_smb2(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_notify_state *state = tevent_req_data( + req, struct cli_notify_state); + NTSTATUS status; + + status = cli_smb2_notify_recv( + subreq, + state, + &state->changes, + &state->num_changes); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint32_t *pnum_changes, + struct notify_change **pchanges) +{ + struct cli_notify_state *state = tevent_req_data( + req, struct cli_notify_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + *pnum_changes = state->num_changes; + *pchanges = talloc_move(mem_ctx, &state->changes); + return NT_STATUS_OK; +} + +NTSTATUS cli_notify(struct cli_state *cli, uint16_t fnum, uint32_t buffer_size, + uint32_t completion_filter, bool recursive, + TALLOC_CTX *mem_ctx, uint32_t *pnum_changes, + struct notify_change **pchanges) +{ + TALLOC_CTX *frame; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_notify_send(ev, ev, cli, fnum, buffer_size, + completion_filter, recursive); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_notify_recv(req, mem_ctx, pnum_changes, pchanges); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_qpathinfo_state { + uint8_t *param; + uint8_t *data; + uint16_t setup[1]; + uint32_t min_rdata; + uint8_t *rdata; + uint32_t num_rdata; +}; + +static void cli_qpathinfo_done(struct tevent_req *subreq); + +struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, const char *fname, + uint16_t level, uint32_t min_rdata, + uint32_t max_rdata) +{ + struct tevent_req *req, *subreq; + struct cli_qpathinfo_state *state; + uint16_t additional_flags2 = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo_state); + if (req == NULL) { + return NULL; + } + state->min_rdata = min_rdata; + SSVAL(state->setup, 0, TRANSACT2_QPATHINFO); + + state->param = talloc_zero_array(state, uint8_t, 6); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + SSVAL(state->param, 0, level); + state->param = trans2_bytes_push_str( + state->param, smbXcli_conn_use_unicode(cli->conn), fname, strlen(fname)+1, NULL); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(fname, NULL, NULL, NULL) && + !INFO_LEVEL_IS_UNIX(level)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_trans_send( + state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + additional_flags2, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + talloc_get_size(state->param), /* num param. */ + 2, /* max returned param. */ + NULL, /* data. */ + 0, /* num data. */ + max_rdata); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qpathinfo_done, req); + return req; +} + +static void cli_qpathinfo_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qpathinfo_state *state = tevent_req_data( + req, struct cli_qpathinfo_state); + NTSTATUS status; + + status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL, + NULL, 0, NULL, + &state->rdata, state->min_rdata, + &state->num_rdata); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_qpathinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **rdata, uint32_t *num_rdata) +{ + struct cli_qpathinfo_state *state = tevent_req_data( + req, struct cli_qpathinfo_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (rdata != NULL) { + *rdata = talloc_move(mem_ctx, &state->rdata); + } else { + TALLOC_FREE(state->rdata); + } + if (num_rdata != NULL) { + *num_rdata = state->num_rdata; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_qpathinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli, + const char *fname, uint16_t level, uint32_t min_rdata, + uint32_t max_rdata, + uint8_t **rdata, uint32_t *num_rdata) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qpathinfo_send(frame, ev, cli, fname, level, min_rdata, + max_rdata); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_qpathinfo_recv(req, mem_ctx, rdata, num_rdata); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_qfileinfo_state { + uint16_t setup[1]; + uint8_t param[4]; + uint8_t *data; + uint16_t recv_flags2; + uint32_t min_rdata; + uint8_t *rdata; + uint32_t num_rdata; +}; + +static void cli_qfileinfo_done(struct tevent_req *subreq); + +struct tevent_req *cli_qfileinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t level, uint32_t min_rdata, + uint32_t max_rdata) +{ + struct tevent_req *req, *subreq; + struct cli_qfileinfo_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_qfileinfo_state); + if (req == NULL) { + return NULL; + } + state->min_rdata = min_rdata; + SSVAL(state->param, 0, fnum); + SSVAL(state->param, 2, level); + SSVAL(state->setup, 0, TRANSACT2_QFILEINFO); + + subreq = cli_trans_send( + state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + 0, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + sizeof(state->param), /* num param. */ + 2, /* max returned param. */ + NULL, /* data. */ + 0, /* num data. */ + max_rdata); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qfileinfo_done, req); + return req; +} + +static void cli_qfileinfo_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qfileinfo_state *state = tevent_req_data( + req, struct cli_qfileinfo_state); + NTSTATUS status; + + status = cli_trans_recv(subreq, state, + &state->recv_flags2, + NULL, 0, NULL, + NULL, 0, NULL, + &state->rdata, state->min_rdata, + &state->num_rdata); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_qfileinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint16_t *recv_flags2, + uint8_t **rdata, uint32_t *num_rdata) +{ + struct cli_qfileinfo_state *state = tevent_req_data( + req, struct cli_qfileinfo_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (recv_flags2 != NULL) { + *recv_flags2 = state->recv_flags2; + } + if (rdata != NULL) { + *rdata = talloc_move(mem_ctx, &state->rdata); + } + if (num_rdata != NULL) { + *num_rdata = state->num_rdata; + } + + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS cli_qfileinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint16_t fnum, uint16_t level, uint32_t min_rdata, + uint32_t max_rdata, uint16_t *recv_flags2, + uint8_t **rdata, uint32_t *num_rdata) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qfileinfo_send(frame, ev, cli, fnum, level, min_rdata, + max_rdata); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_qfileinfo_recv(req, mem_ctx, recv_flags2, rdata, num_rdata); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_flush_state { + uint16_t vwv[1]; +}; + +static void cli_flush_done(struct tevent_req *subreq); + +struct tevent_req *cli_flush_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum) +{ + struct tevent_req *req, *subreq; + struct cli_flush_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_flush_state); + if (req == NULL) { + return NULL; + } + SSVAL(state->vwv + 0, 0, fnum); + + subreq = cli_smb_send(state, ev, cli, SMBflush, 0, 0, 1, state->vwv, + 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_flush_done, req); + return req; +} + +static void cli_flush_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_flush_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_flush(TALLOC_CTX *mem_ctx, struct cli_state *cli, uint16_t fnum) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_flush_send(frame, ev, cli, fnum); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_flush_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_shadow_copy_data_state { + uint16_t setup[4]; + uint8_t *data; + uint32_t num_data; + bool get_names; +}; + +static void cli_shadow_copy_data_done(struct tevent_req *subreq); + +struct tevent_req *cli_shadow_copy_data_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool get_names) +{ + struct tevent_req *req, *subreq; + struct cli_shadow_copy_data_state *state; + uint32_t ret_size; + + req = tevent_req_create(mem_ctx, &state, + struct cli_shadow_copy_data_state); + if (req == NULL) { + return NULL; + } + state->get_names = get_names; + ret_size = get_names ? CLI_BUFFER_SIZE : 16; + + SIVAL(state->setup + 0, 0, FSCTL_GET_SHADOW_COPY_DATA); + SSVAL(state->setup + 2, 0, fnum); + SCVAL(state->setup + 3, 0, 1); /* isFsctl */ + SCVAL(state->setup + 3, 1, 0); /* compfilter, isFlags (WSSP) */ + + subreq = cli_trans_send( + state, ev, cli, 0, SMBnttrans, NULL, 0, NT_TRANSACT_IOCTL, 0, + state->setup, ARRAY_SIZE(state->setup), + ARRAY_SIZE(state->setup), + NULL, 0, 0, + NULL, 0, ret_size); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_shadow_copy_data_done, req); + return req; +} + +static void cli_shadow_copy_data_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_shadow_copy_data_state *state = tevent_req_data( + req, struct cli_shadow_copy_data_state); + NTSTATUS status; + + status = cli_trans_recv(subreq, state, NULL, + NULL, 0, NULL, /* setup */ + NULL, 0, NULL, /* param */ + &state->data, 12, &state->num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_shadow_copy_data_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char ***pnames, int *pnum_names) +{ + struct cli_shadow_copy_data_state *state = tevent_req_data( + req, struct cli_shadow_copy_data_state); + char **names = NULL; + uint32_t i, num_names; + uint32_t dlength; + uint8_t *endp = NULL; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (state->num_data < 16) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + num_names = IVAL(state->data, 4); + dlength = IVAL(state->data, 8); + + if (num_names > 0x7FFFFFFF) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (!state->get_names) { + *pnum_names = (int)num_names; + return NT_STATUS_OK; + } + + if (dlength + 12 < 12) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (dlength + 12 > state->num_data) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (state->num_data + (2 * sizeof(SHADOW_COPY_LABEL)) < + state->num_data) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + names = talloc_array(mem_ctx, char *, num_names); + if (names == NULL) { + return NT_STATUS_NO_MEMORY; + } + + endp = state->data + state->num_data; + + for (i=0; i<num_names; i++) { + bool ret; + uint8_t *src; + size_t converted_size; + + src = state->data + 12 + i * 2 * sizeof(SHADOW_COPY_LABEL); + + if (src + (2 * sizeof(SHADOW_COPY_LABEL)) > endp) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + ret = convert_string_talloc( + names, CH_UTF16LE, CH_UNIX, + src, 2 * sizeof(SHADOW_COPY_LABEL), + &names[i], &converted_size); + if (!ret) { + TALLOC_FREE(names); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + } + *pnum_names = (int)num_names; + *pnames = names; + return NT_STATUS_OK; +} + +NTSTATUS cli_shadow_copy_data(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint16_t fnum, bool get_names, + char ***pnames, int *pnum_names) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_shadow_copy_data(mem_ctx, + cli, + fnum, + get_names, + pnames, + pnum_names); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_shadow_copy_data_send(frame, ev, cli, fnum, get_names); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_shadow_copy_data_recv(req, mem_ctx, pnames, pnum_names); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c new file mode 100644 index 0000000..c4e2a01 --- /dev/null +++ b/source3/libsmb/clifsinfo.c @@ -0,0 +1,823 @@ +/* + Unix SMB/CIFS implementation. + FS info functions + Copyright (C) Stefan (metze) Metzmacher 2003 + Copyright (C) Jeremy Allison 2007 + Copyright (C) Andrew Bartlett 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "trans2.h" +#include "auth_generic.h" +#include "auth/gensec/gensec.h" +#include "../libcli/smb/smbXcli_base.h" +#include "auth/credentials/credentials.h" +#include "../librpc/gen_ndr/ndr_security.h" + +/**************************************************************************** + Get UNIX extensions version info. +****************************************************************************/ + +struct cli_unix_extensions_version_state { + struct cli_state *cli; + uint16_t setup[1]; + uint8_t param[2]; + uint16_t major, minor; + uint32_t caplow, caphigh; +}; + +static void cli_unix_extensions_version_done(struct tevent_req *subreq); + +struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + struct cli_unix_extensions_version_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_unix_extensions_version_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + SSVAL(state->setup, 0, TRANSACT2_QFSINFO); + SSVAL(state->param, 0, SMB_QUERY_CIFS_UNIX_INFO); + + subreq = cli_trans_send(state, ev, cli, 0, SMBtrans2, + NULL, 0, 0, 0, + state->setup, 1, 0, + state->param, 2, 0, + NULL, 0, 560); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_unix_extensions_version_done, req); + return req; +} + +static void cli_unix_extensions_version_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_unix_extensions_version_state *state = tevent_req_data( + req, struct cli_unix_extensions_version_state); + uint8_t *data; + uint32_t num_data; + NTSTATUS status; + + status = cli_trans_recv(subreq, state, NULL, NULL, 0, NULL, + NULL, 0, NULL, &data, 12, &num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + state->major = SVAL(data, 0); + state->minor = SVAL(data, 2); + state->caplow = IVAL(data, 4); + state->caphigh = IVAL(data, 8); + TALLOC_FREE(data); + tevent_req_done(req); +} + +NTSTATUS cli_unix_extensions_version_recv(struct tevent_req *req, + uint16_t *pmajor, uint16_t *pminor, + uint32_t *pcaplow, + uint32_t *pcaphigh) +{ + struct cli_unix_extensions_version_state *state = tevent_req_data( + req, struct cli_unix_extensions_version_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pmajor = state->major; + *pminor = state->minor; + *pcaplow = state->caplow; + *pcaphigh = state->caphigh; + state->cli->server_posix_capabilities = *pcaplow; + return NT_STATUS_OK; +} + +NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16_t *pmajor, + uint16_t *pminor, uint32_t *pcaplow, + uint32_t *pcaphigh) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_unix_extensions_version_send(frame, ev, cli); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_unix_extensions_version_recv(req, pmajor, pminor, pcaplow, + pcaphigh); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Set UNIX extensions capabilities. +****************************************************************************/ + +struct cli_set_unix_extensions_capabilities_state { + struct cli_state *cli; + uint16_t setup[1]; + uint8_t param[4]; + uint8_t data[12]; +}; + +static void cli_set_unix_extensions_capabilities_done( + struct tevent_req *subreq); + +struct tevent_req *cli_set_unix_extensions_capabilities_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + uint16_t major, uint16_t minor, uint32_t caplow, uint32_t caphigh) +{ + struct tevent_req *req, *subreq; + struct cli_set_unix_extensions_capabilities_state *state; + + req = tevent_req_create( + mem_ctx, &state, + struct cli_set_unix_extensions_capabilities_state); + if (req == NULL) { + return NULL; + } + + state->cli = cli; + SSVAL(state->setup+0, 0, TRANSACT2_SETFSINFO); + + SSVAL(state->param, 0, 0); + SSVAL(state->param, 2, SMB_SET_CIFS_UNIX_INFO); + + SSVAL(state->data, 0, major); + SSVAL(state->data, 2, minor); + SIVAL(state->data, 4, caplow); + SIVAL(state->data, 8, caphigh); + + subreq = cli_trans_send(state, ev, cli, 0, SMBtrans2, + NULL, 0, 0, 0, + state->setup, 1, 0, + state->param, 4, 0, + state->data, 12, 560); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_set_unix_extensions_capabilities_done, req); + return req; +} + +static void cli_set_unix_extensions_capabilities_done( + struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_set_unix_extensions_capabilities_state *state = tevent_req_data( + req, struct cli_set_unix_extensions_capabilities_state); + + NTSTATUS status = cli_trans_recv(subreq, NULL, NULL, NULL, 0, NULL, + NULL, 0, NULL, NULL, 0, NULL); + if (NT_STATUS_IS_OK(status)) { + state->cli->requested_posix_capabilities = IVAL(state->data, 4); + } + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_set_unix_extensions_capabilities_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_set_unix_extensions_capabilities(struct cli_state *cli, + uint16_t major, uint16_t minor, + uint32_t caplow, uint32_t caphigh) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_set_unix_extensions_capabilities_send( + ev, ev, cli, major, minor, caplow, caphigh); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_set_unix_extensions_capabilities_recv(req); +fail: + TALLOC_FREE(ev); + return status; +} + +struct cli_get_fs_attr_info_state { + uint16_t setup[1]; + uint8_t param[2]; + uint32_t fs_attr; +}; + +static void cli_get_fs_attr_info_done(struct tevent_req *subreq); + +struct tevent_req *cli_get_fs_attr_info_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *subreq, *req; + struct cli_get_fs_attr_info_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_get_fs_attr_info_state); + if (req == NULL) { + return NULL; + } + SSVAL(state->setup+0, 0, TRANSACT2_QFSINFO); + SSVAL(state->param+0, 0, SMB_QUERY_FS_ATTRIBUTE_INFO); + + subreq = cli_trans_send(state, ev, cli, 0, SMBtrans2, + NULL, 0, 0, 0, + state->setup, 1, 0, + state->param, 2, 0, + NULL, 0, 560); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_get_fs_attr_info_done, req); + return req; +} + +static void cli_get_fs_attr_info_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_get_fs_attr_info_state *state = tevent_req_data( + req, struct cli_get_fs_attr_info_state); + uint8_t *data; + uint32_t num_data; + NTSTATUS status; + + status = cli_trans_recv(subreq, talloc_tos(), NULL, NULL, 0, NULL, + NULL, 0, NULL, &data, 12, &num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + state->fs_attr = IVAL(data, 0); + TALLOC_FREE(data); + tevent_req_done(req); +} + +NTSTATUS cli_get_fs_attr_info_recv(struct tevent_req *req, uint32_t *fs_attr) +{ + struct cli_get_fs_attr_info_state *state = tevent_req_data( + req, struct cli_get_fs_attr_info_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *fs_attr = state->fs_attr; + return NT_STATUS_OK; +} + +NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_get_fs_attr_info(cli, fs_attr); + } + + if (smbXcli_conn_has_async_calls(cli->conn)) { + return NT_STATUS_INVALID_PARAMETER; + } + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = cli_get_fs_attr_info_send(ev, ev, cli); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_get_fs_attr_info_recv(req, fs_attr); +fail: + TALLOC_FREE(ev); + return status; +} + +NTSTATUS cli_get_fs_volume_info(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + char **_volume_name, + uint32_t *pserial_number, + time_t *pdate) +{ + NTSTATUS status; + uint16_t recv_flags2; + uint16_t setup[1]; + uint8_t param[2]; + uint8_t *rdata; + uint32_t rdata_count; + unsigned int nlen; + char *volume_name = NULL; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_get_fs_volume_info(cli, + mem_ctx, + _volume_name, + pserial_number, + pdate); + } + + SSVAL(setup, 0, TRANSACT2_QFSINFO); + SSVAL(param,0,SMB_QUERY_FS_VOLUME_INFO); + + status = cli_trans(talloc_tos(), cli, SMBtrans2, + NULL, 0, 0, 0, + setup, 1, 0, + param, 2, 0, + NULL, 0, 560, + &recv_flags2, + NULL, 0, NULL, + NULL, 0, NULL, + &rdata, 18, &rdata_count); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (pdate) { + struct timespec ts; + ts = interpret_long_date((char *)rdata); + *pdate = ts.tv_sec; + } + if (pserial_number) { + *pserial_number = IVAL(rdata,8); + } + nlen = IVAL(rdata,12); + if (nlen > (rdata_count - 18)) { + TALLOC_FREE(rdata); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + pull_string_talloc(mem_ctx, + (const char *)rdata, + recv_flags2, + &volume_name, + rdata + 18, + nlen, STR_UNICODE); + if (volume_name == NULL) { + status = map_nt_error_from_unix(errno); + TALLOC_FREE(rdata); + return status; + } + + /* todo: but not yet needed + * return the other stuff + */ + + *_volume_name = volume_name; + TALLOC_FREE(rdata); + return NT_STATUS_OK; +} + +NTSTATUS cli_get_fs_full_size_info(struct cli_state *cli, + uint64_t *total_allocation_units, + uint64_t *caller_allocation_units, + uint64_t *actual_allocation_units, + uint64_t *sectors_per_allocation_unit, + uint64_t *bytes_per_sector) +{ + uint16_t setup[1]; + uint8_t param[2]; + uint8_t *rdata = NULL; + uint32_t rdata_count; + NTSTATUS status; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_get_fs_full_size_info(cli, + total_allocation_units, + caller_allocation_units, + actual_allocation_units, + sectors_per_allocation_unit, + bytes_per_sector); + } + + SSVAL(setup, 0, TRANSACT2_QFSINFO); + SSVAL(param, 0, SMB_FS_FULL_SIZE_INFORMATION); + + status = cli_trans(talloc_tos(), cli, SMBtrans2, + NULL, 0, 0, 0, + setup, 1, 0, /* setup */ + param, 2, 0, /* param */ + NULL, 0, 560, /* data */ + NULL, + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + &rdata, 32, &rdata_count); /* rdata */ + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + if (total_allocation_units) { + *total_allocation_units = BIG_UINT(rdata, 0); + } + if (caller_allocation_units) { + *caller_allocation_units = BIG_UINT(rdata,8); + } + if (actual_allocation_units) { + *actual_allocation_units = BIG_UINT(rdata,16); + } + if (sectors_per_allocation_unit) { + *sectors_per_allocation_unit = IVAL(rdata,24); + } + if (bytes_per_sector) { + *bytes_per_sector = IVAL(rdata,28); + } + +fail: + TALLOC_FREE(rdata); + return status; +} + +NTSTATUS cli_get_posix_fs_info(struct cli_state *cli, + uint32_t *optimal_transfer_size, + uint32_t *block_size, + uint64_t *total_blocks, + uint64_t *blocks_available, + uint64_t *user_blocks_available, + uint64_t *total_file_nodes, + uint64_t *free_file_nodes, + uint64_t *fs_identifier) +{ + uint16_t setup[1]; + uint8_t param[2]; + uint8_t *rdata = NULL; + uint32_t rdata_count; + NTSTATUS status; + + SSVAL(setup, 0, TRANSACT2_QFSINFO); + SSVAL(param,0,SMB_QUERY_POSIX_FS_INFO); + + status = cli_trans(talloc_tos(), cli, SMBtrans2, NULL, 0, 0, 0, + setup, 1, 0, + param, 2, 0, + NULL, 0, 560, + NULL, + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + &rdata, 56, &rdata_count); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (optimal_transfer_size) { + *optimal_transfer_size = IVAL(rdata, 0); + } + if (block_size) { + *block_size = IVAL(rdata,4); + } + if (total_blocks) { + *total_blocks = BIG_UINT(rdata,8); + } + if (blocks_available) { + *blocks_available = BIG_UINT(rdata,16); + } + if (user_blocks_available) { + *user_blocks_available = BIG_UINT(rdata,24); + } + if (total_file_nodes) { + *total_file_nodes = BIG_UINT(rdata,32); + } + if (free_file_nodes) { + *free_file_nodes = BIG_UINT(rdata,40); + } + if (fs_identifier) { + *fs_identifier = BIG_UINT(rdata,48); + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Do a UNIX extensions SMB_QUERY_POSIX_WHOAMI call. +****************************************************************************/ + +struct posix_whoami_state { + uint16_t setup[1]; + uint8_t param[2]; + uint32_t max_rdata; + bool guest; + uint64_t uid; + uint64_t gid; + uint32_t num_gids; + uint64_t *gids; + uint32_t num_sids; + struct dom_sid *sids; +}; + +static void cli_posix_whoami_done(struct tevent_req *subreq); + +static const uint32_t posix_whoami_max_rdata = 62*1024; + +struct tevent_req *cli_posix_whoami_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct posix_whoami_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct posix_whoami_state); + if (req == NULL) { + return NULL; + } + + /* Setup setup word. */ + SSVAL(state->setup, 0, TRANSACT2_QFSINFO); + SSVAL(state->param, 0, SMB_QUERY_POSIX_WHOAMI); + + state->max_rdata = posix_whoami_max_rdata; + + subreq = cli_trans_send(state, /* mem ctx. */ + ev, /* event ctx. */ + cli, /* cli_state. */ + 0, /* additional_flags2 */ + SMBtrans2, /* cmd. */ + NULL, /* pipe name. */ + -1, /* fid. */ + 0, /* function. */ + 0, /* flags. */ + state->setup, /* setup. */ + 1, /* num setup uint16_t words. */ + 0, /* max returned setup. */ + state->param, /* param. */ + 2, /* num param. */ + 0, /* max returned param. */ + NULL, /* data. */ + 0, /* num data. */ + state->max_rdata); /* max returned data. */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_posix_whoami_done, req); + return req; +} + +static void cli_posix_whoami_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct posix_whoami_state *state = tevent_req_data( + req, struct posix_whoami_state); + uint8_t *rdata = NULL; + uint8_t *p = NULL; + uint32_t num_rdata = 0; + uint32_t i; + NTSTATUS status; + + status = cli_trans_recv(subreq, + state, + NULL, + NULL, + 0, + NULL, + NULL, + 0, + NULL, + &rdata, + 40, + &num_rdata); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* + * Not strictly needed - cli_trans_recv() + * will ensure at least 40 bytes here. Added + * as more of a reminder to be careful when + * parsing network packets in C. + */ + + if (num_rdata < 40 || num_rdata > posix_whoami_max_rdata) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->guest = (IVAL(rdata, 0) & SMB_WHOAMI_GUEST); + state->uid = BVAL(rdata, 8); + state->gid = BVAL(rdata, 16); + state->num_gids = IVAL(rdata, 24); + state->num_sids = IVAL(rdata, 28); + + /* Ensure the gid array doesn't overflow */ + if (state->num_gids > (num_rdata - 40) / sizeof(uint64_t)) { + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->gids = talloc_array(state, uint64_t, state->num_gids); + if (tevent_req_nomem(state->gids, req)) { + return; + } + state->sids = talloc_array(state, struct dom_sid, state->num_sids); + if (tevent_req_nomem(state->sids, req)) { + return; + } + + p = rdata + 40; + + for (i = 0; i < state->num_gids; i++) { + state->gids[i] = BVAL(p, 0); + p += 8; + } + + num_rdata -= (p - rdata); + + for (i = 0; i < state->num_sids; i++) { + size_t sid_size; + DATA_BLOB in = data_blob_const(p, num_rdata); + enum ndr_err_code ndr_err; + + ndr_err = ndr_pull_struct_blob(&in, + state, + &state->sids[i], + (ndr_pull_flags_fn_t)ndr_pull_dom_sid); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + sid_size = ndr_size_dom_sid(&state->sids[i], 0); + + if (sid_size > num_rdata) { + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + p += sid_size; + num_rdata -= sid_size; + } + + if (num_rdata != 0) { + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + tevent_req_done(req); +} + +NTSTATUS cli_posix_whoami_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint64_t *puid, + uint64_t *pgid, + uint32_t *pnum_gids, + uint64_t **pgids, + uint32_t *pnum_sids, + struct dom_sid **psids, + bool *pguest) +{ + NTSTATUS status; + struct posix_whoami_state *state = tevent_req_data( + req, struct posix_whoami_state); + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (puid) { + *puid = state->uid; + } + if (pgid) { + *pgid = state->gid; + } + if (pnum_gids) { + *pnum_gids = state->num_gids; + } + if (pgids) { + *pgids = talloc_move(mem_ctx, &state->gids); + } + if (pnum_sids) { + *pnum_sids = state->num_sids; + } + if (psids) { + *psids = talloc_move(mem_ctx, &state->sids); + } + if (pguest) { + *pguest = state->guest; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_posix_whoami(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint64_t *puid, + uint64_t *pgid, + uint32_t *num_gids, + uint64_t **gids, + uint32_t *num_sids, + struct dom_sid **sids, + bool *pguest) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_posix_whoami_send(frame, + ev, + cli); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_posix_whoami_recv(req, + mem_ctx, + puid, + pgid, + num_gids, + gids, + num_sids, + sids, + pguest); + + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/clilist.c b/source3/libsmb/clilist.c new file mode 100644 index 0000000..460b296 --- /dev/null +++ b/source3/libsmb/clilist.c @@ -0,0 +1,1238 @@ +/* + Unix SMB/CIFS implementation. + client directory list routines + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "trans2.h" +#include "../libcli/smb/smbXcli_base.h" + +/**************************************************************************** + Check if a returned directory name is safe. +****************************************************************************/ + +static NTSTATUS is_bad_name(bool windows_names, const char *name) +{ + const char *bad_name_p = NULL; + + bad_name_p = strchr(name, '/'); + if (bad_name_p != NULL) { + /* + * Windows and POSIX names can't have '/'. + * Server is attacking us. + */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (windows_names) { + bad_name_p = strchr(name, '\\'); + if (bad_name_p != NULL) { + /* + * Windows names can't have '\\'. + * Server is attacking us. + */ + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Check if a returned directory name is safe. Disconnect if server is + sending bad names. +****************************************************************************/ + +NTSTATUS is_bad_finfo_name(const struct cli_state *cli, + const struct file_info *finfo) +{ + NTSTATUS status = NT_STATUS_OK; + bool windows_names = true; + + if (cli->requested_posix_capabilities & CIFS_UNIX_POSIX_PATHNAMES_CAP) { + windows_names = false; + } + if (finfo->name != NULL) { + status = is_bad_name(windows_names, finfo->name); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("bad finfo->name\n"); + return status; + } + } + if (finfo->short_name != NULL) { + status = is_bad_name(windows_names, finfo->short_name); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("bad finfo->short_name\n"); + return status; + } + } + return NT_STATUS_OK; +} + +/**************************************************************************** + Calculate a safe next_entry_offset. +****************************************************************************/ + +static size_t calc_next_entry_offset(const char *base, const char *pdata_end) +{ + size_t next_entry_offset = (size_t)IVAL(base,0); + + if (next_entry_offset == 0 || + base + next_entry_offset < base || + base + next_entry_offset > pdata_end) { + next_entry_offset = pdata_end - base; + } + return next_entry_offset; +} + +/**************************************************************************** + Interpret a long filename structure - this is mostly guesses at the moment. + The length of the structure is returned + The structure of a long filename depends on the info level. + SMB_FIND_FILE_BOTH_DIRECTORY_INFO is used + by NT and SMB_FIND_EA_SIZE is used by OS/2 +****************************************************************************/ + +static size_t interpret_long_filename(TALLOC_CTX *ctx, + struct cli_state *cli, + int level, + const char *base_ptr, + uint16_t recv_flags2, + const char *p, + const char *pdata_end, + struct file_info *finfo, + uint32_t *p_resume_key, + DATA_BLOB *p_last_name_raw) +{ + int len; + size_t ret; + const char *base = p; + + data_blob_free(p_last_name_raw); + + if (p_resume_key) { + *p_resume_key = 0; + } + ZERO_STRUCTP(finfo); + + switch (level) { + case SMB_FIND_INFO_STANDARD: /* OS/2 understands this */ + /* these dates are converted to GMT by + make_unix_date */ + if (pdata_end - base < 27) { + return pdata_end - base; + } + /* + * What we're returning here as ctime_ts is + * actually the server create time. + */ + finfo->btime_ts = convert_time_t_to_timespec( + make_unix_date2(p+4, + smb1cli_conn_server_time_zone( + cli->conn))); + finfo->ctime_ts = convert_time_t_to_timespec( + make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn))); + finfo->atime_ts = convert_time_t_to_timespec( + make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn))); + finfo->mtime_ts = convert_time_t_to_timespec( + make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn))); + finfo->size = IVAL(p,16); + finfo->attr = SVAL(p,24); + len = CVAL(p, 26); + p += 27; + if (recv_flags2 & FLAGS2_UNICODE_STRINGS) { + p += ucs2_align(base_ptr, p, STR_UNICODE); + } + + /* We can safely use len here (which is required by OS/2) + * and the NAS-BASIC server instead of +2 or +1 as the + * STR_TERMINATE flag below is + * actually used as the length calculation. + * The len is merely an upper bound. + * Due to the explicit 2 byte null termination + * in cli_receive_trans/cli_receive_nt_trans + * we know this is safe. JRA + kukks + */ + + if (p + len > pdata_end) { + return pdata_end - base; + } + + /* the len+2 below looks strange but it is + important to cope with the differences + between win2000 and win9x for this call + (tridge) */ + ret = pull_string_talloc(ctx, + base_ptr, + recv_flags2, + &finfo->name, + p, + len+2, + STR_TERMINATE); + if (ret == (size_t)-1) { + return pdata_end - base; + } + p += ret; + return PTR_DIFF(p, base); + + case SMB_FIND_EA_SIZE: /* this is what OS/2 uses mostly */ + /* these dates are converted to GMT by + make_unix_date */ + if (pdata_end - base < 31) { + return pdata_end - base; + } + /* + * What we're returning here as ctime_ts is + * actually the server create time. + */ + finfo->btime_ts = convert_time_t_to_timespec( + make_unix_date2(p+4, + smb1cli_conn_server_time_zone( + cli->conn))); + finfo->ctime_ts = convert_time_t_to_timespec( + make_unix_date2(p+4, smb1cli_conn_server_time_zone(cli->conn))); + finfo->atime_ts = convert_time_t_to_timespec( + make_unix_date2(p+8, smb1cli_conn_server_time_zone(cli->conn))); + finfo->mtime_ts = convert_time_t_to_timespec( + make_unix_date2(p+12, smb1cli_conn_server_time_zone(cli->conn))); + finfo->size = IVAL(p,16); + finfo->attr = SVAL(p,24); + len = CVAL(p, 30); + p += 31; + /* check for unisys! */ + if (p + len + 1 > pdata_end) { + return pdata_end - base; + } + ret = pull_string_talloc(ctx, + base_ptr, + recv_flags2, + &finfo->name, + p, + len, + STR_NOALIGN); + if (ret == (size_t)-1) { + return pdata_end - base; + } + p += ret; + return PTR_DIFF(p, base) + 1; + + case SMB_FIND_FILE_BOTH_DIRECTORY_INFO: /* NT uses this, but also accepts 2 */ + { + size_t namelen, slen; + + if (pdata_end - base < 94) { + return pdata_end - base; + } + + p += 4; /* next entry offset */ + + if (p_resume_key) { + *p_resume_key = IVAL(p,0); + } + p += 4; /* fileindex */ + + /* Offset zero is "create time", not "change time". */ + p += 8; + finfo->atime_ts = interpret_long_date(p); + p += 8; + finfo->mtime_ts = interpret_long_date(p); + p += 8; + finfo->ctime_ts = interpret_long_date(p); + p += 8; + finfo->size = IVAL2_TO_SMB_BIG_UINT(p,0); + p += 8; + p += 8; /* alloc size */ + finfo->attr = IVAL(p,0); + p += 4; + namelen = IVAL(p,0); + p += 4; + p += 4; /* EA size */ + slen = CVAL(p, 0); + if (slen > 24) { + /* Bad short name length. */ + return pdata_end - base; + } + p += 2; + ret = pull_string_talloc(ctx, + base_ptr, + recv_flags2, + &finfo->short_name, + p, + slen, + STR_UNICODE); + if (ret == (size_t)-1) { + return pdata_end - base; + } + p += 24; /* short name? */ + if (p + namelen < p || p + namelen > pdata_end) { + return pdata_end - base; + } + ret = pull_string_talloc(ctx, + base_ptr, + recv_flags2, + &finfo->name, + p, + namelen, + 0); + if (ret == (size_t)-1) { + return pdata_end - base; + } + + /* To be robust in the face of unicode conversion failures + we need to copy the raw bytes of the last name seen here. + Namelen doesn't include the terminating unicode null, so + copy it here. */ + + if (p_last_name_raw) { + *p_last_name_raw = data_blob(NULL, namelen+2); + memcpy(p_last_name_raw->data, p, namelen); + SSVAL(p_last_name_raw->data, namelen, 0); + } + return calc_next_entry_offset(base, pdata_end); + } + } + + DEBUG(1,("Unknown long filename format %d\n",level)); + return calc_next_entry_offset(base, pdata_end); +} + +/**************************************************************************** + Interpret a short filename structure. + The length of the structure is returned. +****************************************************************************/ + +static bool interpret_short_filename(TALLOC_CTX *ctx, + struct cli_state *cli, + char *p, + struct file_info *finfo) +{ + size_t ret; + ZERO_STRUCTP(finfo); + + finfo->attr = CVAL(p,21); + + /* We don't get birth time. */ + finfo->btime_ts.tv_sec = 0; + finfo->btime_ts.tv_nsec = 0; + /* this date is converted to GMT by make_unix_date */ + finfo->ctime_ts.tv_sec = make_unix_date(p+22, smb1cli_conn_server_time_zone(cli->conn)); + finfo->ctime_ts.tv_nsec = 0; + finfo->mtime_ts.tv_sec = finfo->atime_ts.tv_sec = finfo->ctime_ts.tv_sec; + finfo->mtime_ts.tv_nsec = finfo->atime_ts.tv_nsec = 0; + finfo->size = IVAL(p,26); + ret = pull_string_talloc(ctx, + NULL, + 0, + &finfo->name, + p+30, + 12, + STR_ASCII); + if (ret == (size_t)-1) { + return false; + } + + if (finfo->name) { + finfo->short_name = talloc_strdup(ctx, finfo->name); + if (finfo->short_name == NULL) { + return false; + } + } + return true; +} + +struct cli_list_old_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t vwv[2]; + char *mask; + int num_asked; + uint32_t attribute; + uint8_t search_status[23]; + bool first; + bool done; + uint8_t *dirlist; +}; + +static void cli_list_old_done(struct tevent_req *subreq); + +static struct tevent_req *cli_list_old_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *mask, + uint32_t attribute) +{ + struct tevent_req *req, *subreq; + struct cli_list_old_state *state; + uint8_t *bytes; + static const uint16_t zero = 0; + uint32_t usable_space; + + req = tevent_req_create(mem_ctx, &state, struct cli_list_old_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->attribute = attribute; + state->first = true; + state->mask = talloc_strdup(state, mask); + if (tevent_req_nomem(state->mask, req)) { + return tevent_req_post(req, ev); + } + usable_space = cli_state_available_size(cli, 100); + state->num_asked = usable_space / DIR_STRUCT_SIZE; + + SSVAL(state->vwv + 0, 0, state->num_asked); + SSVAL(state->vwv + 1, 0, state->attribute); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(cli->conn), mask, + strlen(mask)+1, NULL); + + bytes = smb_bytes_push_bytes(bytes, 5, (const uint8_t *)&zero, 2); + if (tevent_req_nomem(bytes, req)) { + return tevent_req_post(req, ev); + } + + subreq = cli_smb_send(state, state->ev, state->cli, SMBsearch, 0, 0, + 2, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_list_old_done, req); + return req; +} + +static void cli_list_old_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_list_old_state *state = tevent_req_data( + req, struct cli_list_old_state); + NTSTATUS status; + uint8_t cmd; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + uint16_t received; + size_t dirlist_len; + uint8_t *tmp; + + status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, &num_bytes, + &bytes); + if (!NT_STATUS_IS_OK(status) + && !NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles)) + && !NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_DOS(ERRDOS, ERRnofiles)) + || NT_STATUS_EQUAL(status, STATUS_NO_MORE_FILES)) { + received = 0; + } else { + if (wct < 1) { + TALLOC_FREE(subreq); + tevent_req_nterror( + req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + received = SVAL(vwv + 0, 0); + } + + if (received > 0) { + /* + * I don't think this can wrap. received is + * initialized from a 16-bit value. + */ + if (num_bytes < ((uint32_t)received * DIR_STRUCT_SIZE + 3)) { + TALLOC_FREE(subreq); + tevent_req_nterror( + req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + dirlist_len = talloc_get_size(state->dirlist); + + tmp = talloc_realloc( + state, state->dirlist, uint8_t, + dirlist_len + received * DIR_STRUCT_SIZE); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->dirlist = tmp; + memcpy(state->dirlist + dirlist_len, bytes + 3, + received * DIR_STRUCT_SIZE); + + SSVAL(state->search_status, 0, 21); + memcpy(state->search_status + 2, + bytes + 3 + (received-1)*DIR_STRUCT_SIZE, 21); + cmd = SMBsearch; + } else { + if (state->first || state->done) { + tevent_req_done(req); + return; + } + state->done = true; + state->num_asked = 0; + cmd = SMBfclose; + } + TALLOC_FREE(subreq); + + state->first = false; + + SSVAL(state->vwv + 0, 0, state->num_asked); + SSVAL(state->vwv + 1, 0, state->attribute); + + bytes = talloc_array(state, uint8_t, 1); + if (tevent_req_nomem(bytes, req)) { + return; + } + bytes[0] = 4; + bytes = smb_bytes_push_str(bytes, smbXcli_conn_use_unicode(state->cli->conn), "", + 1, NULL); + bytes = smb_bytes_push_bytes(bytes, 5, state->search_status, + sizeof(state->search_status)); + if (tevent_req_nomem(bytes, req)) { + return; + } + subreq = cli_smb_send(state, state->ev, state->cli, cmd, 0, 0, + 2, state->vwv, talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_list_old_done, req); +} + +static NTSTATUS cli_list_old_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct file_info **pfinfo) +{ + struct cli_list_old_state *state = tevent_req_data( + req, struct cli_list_old_state); + NTSTATUS status; + size_t i, num_received; + struct file_info *finfo; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (state->dirlist == NULL) { + *pfinfo = NULL; + return NT_STATUS_OK; + } + + num_received = talloc_array_length(state->dirlist) / DIR_STRUCT_SIZE; + + finfo = talloc_array(mem_ctx, struct file_info, num_received); + if (finfo == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i<num_received; i++) { + if (!interpret_short_filename( + finfo, state->cli, + (char *)state->dirlist + i * DIR_STRUCT_SIZE, + &finfo[i])) { + TALLOC_FREE(finfo); + return NT_STATUS_NO_MEMORY; + } + if (finfo->name == NULL) { + TALLOC_FREE(finfo); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + status = is_bad_finfo_name(state->cli, finfo); + if (!NT_STATUS_IS_OK(status)) { + smbXcli_conn_disconnect(state->cli->conn, status); + TALLOC_FREE(finfo); + return status; + } + } + TALLOC_FREE(state->dirlist); + *pfinfo = finfo; + return NT_STATUS_OK; +} + +NTSTATUS cli_list_old(struct cli_state *cli, const char *mask, + uint32_t attribute, + NTSTATUS (*fn)(struct file_info *, + const char *, void *), void *state) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + struct file_info *finfo = NULL; + size_t i, num_finfo; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_list_old_send(frame, ev, cli, mask, attribute); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_list_old_recv(req, frame, &finfo); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + num_finfo = talloc_array_length(finfo); + for (i=0; i<num_finfo; i++) { + status = fn(&finfo[i], mask, state); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + } + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_list_trans_state { + struct tevent_context *ev; + struct cli_state *cli; + char *mask; + uint32_t attribute; + uint16_t info_level; + + int loop_count; + int total_received; + uint16_t max_matches; + bool first; + + int ff_eos; + int ff_dir_handle; + + uint16_t setup[1]; + uint8_t *param; + + struct file_info *finfo; +}; + +static void cli_list_trans_done(struct tevent_req *subreq); + +static struct tevent_req *cli_list_trans_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *mask, + uint32_t attribute, + uint16_t info_level) +{ + struct tevent_req *req, *subreq; + struct cli_list_trans_state *state; + size_t param_len; + uint16_t additional_flags2 = 0; + + req = tevent_req_create(mem_ctx, &state, + struct cli_list_trans_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->mask = talloc_strdup(state, mask); + if (tevent_req_nomem(state->mask, req)) { + return tevent_req_post(req, ev); + } + state->attribute = attribute; + state->info_level = info_level; + state->loop_count = 0; + state->first = true; + + state->max_matches = 1366; /* Match W2k */ + + SSVAL(&state->setup[0], 0, TRANSACT2_FINDFIRST); + + state->param = talloc_array(state, uint8_t, 12); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + + SSVAL(state->param, 0, state->attribute); + SSVAL(state->param, 2, state->max_matches); + SSVAL(state->param, 4, + FLAG_TRANS2_FIND_REQUIRE_RESUME + |FLAG_TRANS2_FIND_CLOSE_IF_END + |(cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0)); + SSVAL(state->param, 6, state->info_level); + SIVAL(state->param, 8, 0); + + state->param = trans2_bytes_push_str(state->param, smbXcli_conn_use_unicode(cli->conn), + state->mask, strlen(state->mask)+1, + NULL); + if (tevent_req_nomem(state->param, req)) { + return tevent_req_post(req, ev); + } + + if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + param_len = talloc_get_size(state->param); + + subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2, + SMBtrans2, NULL, -1, 0, 0, + state->setup, 1, 0, + state->param, param_len, 10, + NULL, 0, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_list_trans_done, req); + return req; +} + +static void cli_list_trans_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_list_trans_state *state = tevent_req_data( + req, struct cli_list_trans_state); + NTSTATUS status; + uint8_t *param; + uint32_t num_param; + uint8_t *data; + char *data_end; + uint32_t num_data; + uint32_t min_param; + struct file_info *tmp; + size_t old_num_finfo; + uint16_t recv_flags2; + int ff_searchcount; + bool ff_eos; + char *p, *p2; + uint32_t resume_key = 0; + int i; + DATA_BLOB last_name_raw; + struct file_info *finfo = NULL; + size_t param_len; + uint16_t additional_flags2 = 0; + + min_param = (state->first ? 6 : 4); + + status = cli_trans_recv(subreq, talloc_tos(), &recv_flags2, + NULL, 0, NULL, + ¶m, min_param, &num_param, + &data, 0, &num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + /* + * TODO: retry, OS/2 nofiles + */ + tevent_req_nterror(req, status); + return; + } + + if (state->first) { + state->ff_dir_handle = SVAL(param, 0); + ff_searchcount = SVAL(param, 2); + ff_eos = SVAL(param, 4) != 0; + } else { + ff_searchcount = SVAL(param, 0); + ff_eos = SVAL(param, 2) != 0; + } + + old_num_finfo = talloc_array_length(state->finfo); + + tmp = talloc_realloc(state, state->finfo, struct file_info, + old_num_finfo + ff_searchcount); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->finfo = tmp; + + p2 = p = (char *)data; + data_end = (char *)data + num_data; + last_name_raw = data_blob_null; + + for (i=0; i<ff_searchcount; i++) { + if (p2 >= data_end) { + ff_eos = true; + break; + } + if ((state->info_level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) + && (i == ff_searchcount-1)) { + /* Last entry - fixup the last offset length. */ + SIVAL(p2, 0, PTR_DIFF((data + num_data), p2)); + } + + data_blob_free(&last_name_raw); + + finfo = &state->finfo[old_num_finfo + i]; + + p2 += interpret_long_filename( + state->finfo, /* Stick fname to the array as such */ + state->cli, state->info_level, + (char *)data, recv_flags2, p2, + data_end, finfo, &resume_key, &last_name_raw); + + if (finfo->name == NULL) { + DEBUG(1, ("cli_list: Error: unable to parse name from " + "info level %d\n", state->info_level)); + tevent_req_nterror(req, + NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + status = is_bad_finfo_name(state->cli, finfo); + if (!NT_STATUS_IS_OK(status)) { + smbXcli_conn_disconnect(state->cli->conn, status); + tevent_req_nterror(req, status); + return; + } + + if (!state->first && (state->mask[0] != '\0') && + strcsequal(finfo->name, state->mask)) { + DEBUG(1, ("Error: Looping in FIND_NEXT as name %s has " + "already been seen?\n", finfo->name)); + ff_eos = true; + break; + } + } + + if (ff_searchcount == 0) { + ff_eos = true; + } + + TALLOC_FREE(param); + TALLOC_FREE(data); + + /* + * Shrink state->finfo to the real length we received + */ + tmp = talloc_realloc(state, state->finfo, struct file_info, + old_num_finfo + i); + if (tevent_req_nomem(tmp, req)) { + return; + } + state->finfo = tmp; + + state->first = false; + + if (ff_eos) { + data_blob_free(&last_name_raw); + tevent_req_done(req); + return; + } + + TALLOC_FREE(state->mask); + state->mask = talloc_strdup(state, finfo->name); + if (tevent_req_nomem(state->mask, req)) { + return; + } + + SSVAL(&state->setup[0], 0, TRANSACT2_FINDNEXT); + + param = talloc_realloc(state, state->param, uint8_t, 12); + if (tevent_req_nomem(param, req)) { + return; + } + state->param = param; + + SSVAL(param, 0, state->ff_dir_handle); + SSVAL(param, 2, state->max_matches); /* max count */ + SSVAL(param, 4, state->info_level); + /* + * For W2K servers serving out FAT filesystems we *must* set + * the resume key. If it's not FAT then it's returned as zero. + */ + SIVAL(param, 6, resume_key); /* ff_resume_key */ + /* + * NB. *DON'T* use continue here. If you do it seems that W2K + * and bretheren can miss filenames. Use last filename + * continue instead. JRA + */ + SSVAL(param, 10, (FLAG_TRANS2_FIND_REQUIRE_RESUME + |FLAG_TRANS2_FIND_CLOSE_IF_END + |(state->cli->backup_intent ? FLAG_TRANS2_FIND_BACKUP_INTENT : 0))); + if (last_name_raw.length) { + state->param = trans2_bytes_push_bytes(state->param, + last_name_raw.data, + last_name_raw.length); + if (tevent_req_nomem(state->param, req)) { + return; + } + data_blob_free(&last_name_raw); + } else { + state->param = trans2_bytes_push_str(state->param, + smbXcli_conn_use_unicode(state->cli->conn), + state->mask, + strlen(state->mask)+1, + NULL); + if (tevent_req_nomem(state->param, req)) { + return; + } + } + param_len = talloc_get_size(state->param); + + if (clistr_is_previous_version_path(state->mask, NULL, NULL, NULL)) { + additional_flags2 = FLAGS2_REPARSE_PATH; + } + + subreq = cli_trans_send(state, state->ev, state->cli, additional_flags2, + SMBtrans2, NULL, -1, 0, 0, + state->setup, 1, 0, + state->param, param_len, 10, + NULL, 0, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_list_trans_done, req); +} + +static NTSTATUS cli_list_trans_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct file_info **finfo) +{ + struct cli_list_trans_state *state = tevent_req_data( + req, struct cli_list_trans_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *finfo = talloc_move(mem_ctx, &state->finfo); + return NT_STATUS_OK; +} + +NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask, + uint32_t attribute, int info_level, + NTSTATUS (*fn)( + struct file_info *finfo, + const char *mask, + void *private_data), + void *private_data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + int i, num_finfo; + struct file_info *finfo = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_list_trans_send(frame, ev, cli, mask, attribute, info_level); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_list_trans_recv(req, frame, &finfo); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + num_finfo = talloc_array_length(finfo); + for (i=0; i<num_finfo; i++) { + status = fn(&finfo[i], mask, private_data); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + } + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_list_state { + struct tevent_context *ev; + struct tevent_req *subreq; + NTSTATUS (*recv_fn)(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct file_info **finfo); + struct file_info *finfo; + size_t num_received; +}; + +static void cli_list_done(struct tevent_req *subreq); + +struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *mask, + uint32_t attribute, + uint16_t info_level) +{ + struct tevent_req *req = NULL; + struct cli_list_state *state; + enum protocol_types proto = smbXcli_conn_protocol(cli->conn); + + req = tevent_req_create(mem_ctx, &state, struct cli_list_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + + if (proto >= PROTOCOL_SMB2_02) { + state->subreq = cli_smb2_list_send(state, ev, cli, mask); + state->recv_fn = cli_smb2_list_recv; + } else if (proto >= PROTOCOL_LANMAN2) { + state->subreq = cli_list_trans_send( + state, ev, cli, mask, attribute, info_level); + state->recv_fn = cli_list_trans_recv; + } else { + state->subreq = cli_list_old_send( + state, ev, cli, mask, attribute); + state->recv_fn = cli_list_old_recv; + } + if (tevent_req_nomem(state->subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->subreq, cli_list_done, req); + return req; +} + +static void cli_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_list_state *state = tevent_req_data( + req, struct cli_list_state); + NTSTATUS status; + + SMB_ASSERT(subreq == state->subreq); + + /* + * We don't want to be called by the lowerlevel routines + * from within state->recv_fn() + */ + tevent_req_set_callback(subreq, NULL, NULL); + + status = state->recv_fn(subreq, state, &state->finfo); + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + /* We'll get back here */ + tevent_req_set_callback(subreq, cli_list_done, req); + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_notify_callback(req); +} + +NTSTATUS cli_list_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct file_info **pfinfo) +{ + struct cli_list_state *state = tevent_req_data( + req, struct cli_list_state); + size_t num_results; + struct file_info *finfo = NULL; + NTSTATUS status; + bool in_progress; + + in_progress = tevent_req_is_in_progress(req); + + if (!in_progress) { + if (!tevent_req_is_nterror(req, &status)) { + status = NT_STATUS_NO_MORE_FILES; + } + return status; + } + + if (state->finfo == NULL) { + status = state->recv_fn(state->subreq, state, &state->finfo); + + if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { + tevent_req_set_callback( + state->subreq, cli_list_done, req); + return NT_STATUS_RETRY; + } + + if (NT_STATUS_IS_OK(status) && (state->finfo == NULL)) { + status = NT_STATUS_NO_MORE_FILES; + } + + if (tevent_req_nterror(req, status)) { + return status; + } + + state->num_received = 0; + } + + num_results = talloc_array_length(state->finfo); + + if (num_results == 1) { + finfo = talloc_move(mem_ctx, &state->finfo); + } else { + struct file_info *src_finfo = + &state->finfo[state->num_received]; + + finfo = talloc(mem_ctx, struct file_info); + if (finfo == NULL) { + return NT_STATUS_NO_MEMORY; + } + *finfo = *src_finfo; + finfo->name = talloc_move(finfo, &src_finfo->name); + finfo->short_name = talloc_move(finfo, &src_finfo->short_name); + } + + state->num_received += 1; + + if (state->num_received == num_results) { + TALLOC_FREE(state->finfo); + } + + tevent_req_defer_callback(req, state->ev); + tevent_req_notify_callback(req); + + *pfinfo = finfo; + return NT_STATUS_OK; +} + +struct cli_list_sync_state { + const char *mask; + uint32_t attribute; + NTSTATUS (*fn)(struct file_info *finfo, + const char *mask, + void *private_data); + void *private_data; + NTSTATUS status; + bool processed_file; +}; + +static void cli_list_sync_cb(struct tevent_req *subreq) +{ + struct cli_list_sync_state *state = + tevent_req_callback_data_void(subreq); + struct file_info *finfo; + bool ok; + + state->status = cli_list_recv(subreq, talloc_tos(), &finfo); + /* No TALLOC_FREE(subreq), we get here more than once */ + + if (NT_STATUS_EQUAL(state->status, NT_STATUS_RETRY)) { + /* + * The lowlevel SMB call was rearmed, we'll get back + * here when it's done. + */ + state->status = NT_STATUS_OK; + return; + } + + if (!NT_STATUS_IS_OK(state->status)) { + return; + } + + ok = dir_check_ftype(finfo->attr, state->attribute); + if (!ok) { + /* + * Only process if attributes match. On SMB1 server + * does this, so on SMB2 we need to emulate in the + * client. + * + * https://bugzilla.samba.org/show_bug.cgi?id=10260 + */ + return; + } + + state->status = state->fn(finfo, state->mask, state->private_data); + + state->processed_file = true; + + TALLOC_FREE(finfo); +} + +NTSTATUS cli_list(struct cli_state *cli, + const char *mask, + uint32_t attribute, + NTSTATUS (*fn)(struct file_info *finfo, + const char *mask, + void *private_data), + void *private_data) +{ + TALLOC_CTX *frame = NULL; + struct cli_list_sync_state state = { + .mask = mask, + .attribute = attribute, + .fn = fn, + .private_data = private_data, + }; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + uint16_t info_level; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + info_level = (smb1cli_conn_capabilities(cli->conn) & CAP_NT_SMBS) + ? SMB_FIND_FILE_BOTH_DIRECTORY_INFO : SMB_FIND_INFO_STANDARD; + + req = cli_list_send(frame, ev, cli, mask, attribute, info_level); + if (req == NULL) { + goto fail; + } + tevent_req_set_callback(req, cli_list_sync_cb, &state); + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = state.status; + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) { + status = NT_STATUS_OK; + } + + if (NT_STATUS_IS_OK(status) && !state.processed_file) { + status = NT_STATUS_NO_SUCH_FILE; + } + + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/climessage.c b/source3/libsmb/climessage.c new file mode 100644 index 0000000..fd21681 --- /dev/null +++ b/source3/libsmb/climessage.c @@ -0,0 +1,418 @@ +/* + Unix SMB/CIFS implementation. + client message handling routines + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "libsmb/libsmb.h" +#include "../libcli/smb/smbXcli_base.h" + +struct cli_message_start_state { + uint16_t grp; +}; + +static void cli_message_start_done(struct tevent_req *subreq); + +static struct tevent_req *cli_message_start_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *host, + const char *username) +{ + struct tevent_req *req, *subreq; + struct cli_message_start_state *state; + char *htmp = NULL; + char *utmp = NULL; + size_t hlen, ulen; + uint8_t *bytes, *p; + + req = tevent_req_create(mem_ctx, &state, + struct cli_message_start_state); + if (req == NULL) { + return NULL; + } + + if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, + username, strlen(username)+1, + &utmp, &ulen)) { + goto fail; + } + if (!convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, + host, strlen(host)+1, + &htmp, &hlen)) { + goto fail; + } + + bytes = talloc_array(state, uint8_t, ulen+hlen+2); + if (bytes == NULL) { + goto fail; + } + p = bytes; + + *p++ = 4; + memcpy(p, utmp, ulen); + p += ulen; + *p++ = 4; + memcpy(p, htmp, hlen); + TALLOC_FREE(htmp); + TALLOC_FREE(utmp); + + subreq = cli_smb_send(state, ev, cli, SMBsendstrt, 0, 0, 0, NULL, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_message_start_done, req); + return req; +fail: + TALLOC_FREE(htmp); + TALLOC_FREE(utmp); + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); +} + +static void cli_message_start_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_message_start_state *state = tevent_req_data( + req, struct cli_message_start_state); + NTSTATUS status; + uint8_t wct; + uint16_t *vwv; + + status = cli_smb_recv(subreq, state, NULL, 0, &wct, &vwv, + NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(subreq); + tevent_req_nterror(req, status); + return; + } + if (wct >= 1) { + state->grp = SVAL(vwv+0, 0); + } else { + state->grp = 0; + } + tevent_req_done(req); +} + +static NTSTATUS cli_message_start_recv(struct tevent_req *req, + uint16_t *pgrp) +{ + struct cli_message_start_state *state = tevent_req_data( + req, struct cli_message_start_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pgrp = state->grp; + return NT_STATUS_OK; +} + +struct cli_message_text_state { + uint16_t vwv; +}; + +static void cli_message_text_done(struct tevent_req *subreq); + +static struct tevent_req *cli_message_text_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t grp, + const char *msg, + int msglen) +{ + struct tevent_req *req, *subreq; + struct cli_message_text_state *state; + char *tmp; + size_t tmplen; + uint8_t *bytes; + + req = tevent_req_create(mem_ctx, &state, + struct cli_message_text_state); + if (req == NULL) { + return NULL; + } + + SSVAL(&state->vwv, 0, grp); + + if (convert_string_talloc(talloc_tos(), CH_UNIX, CH_DOS, msg, msglen, + &tmp, &tmplen)) { + msg = tmp; + msglen = tmplen; + } else { + DEBUG(3, ("Conversion failed, sending message in UNIX " + "charset\n")); + tmp = NULL; + } + + bytes = talloc_array(state, uint8_t, msglen+3); + if (tevent_req_nomem(bytes, req)) { + TALLOC_FREE(tmp); + return tevent_req_post(req, ev); + } + SCVAL(bytes, 0, 1); /* pad */ + SSVAL(bytes+1, 0, msglen); + memcpy(bytes+3, msg, msglen); + TALLOC_FREE(tmp); + + subreq = cli_smb_send(state, ev, cli, SMBsendtxt, 0, 0, 1, &state->vwv, + talloc_get_size(bytes), bytes); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_message_text_done, req); + return req; +} + +static void cli_message_text_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +static NTSTATUS cli_message_text_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_message_end_state { + uint16_t vwv; +}; + +static void cli_message_end_done(struct tevent_req *subreq); + +static struct tevent_req *cli_message_end_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t grp) +{ + struct tevent_req *req, *subreq; + struct cli_message_end_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_message_end_state); + if (req == NULL) { + return NULL; + } + + SSVAL(&state->vwv, 0, grp); + + subreq = cli_smb_send(state, ev, cli, SMBsendend, 0, 0, 1, &state->vwv, + 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_message_end_done, req); + return req; +} + +static void cli_message_end_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_smb_recv(subreq, NULL, NULL, 0, NULL, NULL, NULL, NULL); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +static NTSTATUS cli_message_end_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct cli_message_state { + struct tevent_context *ev; + struct cli_state *cli; + size_t sent; + const char *message; + uint16_t grp; +}; + +static void cli_message_started(struct tevent_req *subreq); +static void cli_message_sent(struct tevent_req *subreq); +static void cli_message_done(struct tevent_req *subreq); + +struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *host, const char *username, + const char *message) +{ + struct tevent_req *req, *subreq; + struct cli_message_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_message_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->sent = 0; + state->message = message; + + subreq = cli_message_start_send(state, ev, cli, host, username); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_message_started, req); + return req; +} + +static void cli_message_started(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_message_state *state = tevent_req_data( + req, struct cli_message_state); + NTSTATUS status; + size_t thistime; + + status = cli_message_start_recv(subreq, &state->grp); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + thistime = MIN(127, strlen(state->message)); + + subreq = cli_message_text_send(state, state->ev, state->cli, + state->grp, state->message, thistime); + if (tevent_req_nomem(subreq, req)) { + return; + } + state->sent += thistime; + tevent_req_set_callback(subreq, cli_message_sent, req); +} + +static void cli_message_sent(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_message_state *state = tevent_req_data( + req, struct cli_message_state); + NTSTATUS status; + size_t left, thistime; + + status = cli_message_text_recv(subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + + if (state->sent >= strlen(state->message)) { + subreq = cli_message_end_send(state, state->ev, state->cli, + state->grp); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_message_done, req); + return; + } + + left = strlen(state->message) - state->sent; + thistime = MIN(127, left); + + subreq = cli_message_text_send(state, state->ev, state->cli, + state->grp, + state->message + state->sent, + thistime); + if (tevent_req_nomem(subreq, req)) { + return; + } + state->sent += thistime; + tevent_req_set_callback(subreq, cli_message_sent, req); +} + +static void cli_message_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_message_end_recv(subreq); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_message_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_message(struct cli_state *cli, const char *host, + const char *username, const char *message) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_message_send(frame, ev, cli, host, username, message); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_message_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/clioplock.c b/source3/libsmb/clioplock.c new file mode 100644 index 0000000..01c98f1 --- /dev/null +++ b/source3/libsmb/clioplock.c @@ -0,0 +1,170 @@ +/* + Unix SMB/CIFS implementation. + SMB client oplock functions + Copyright (C) Andrew Tridgell 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "libsmb/libsmb.h" +#include "../libcli/smb/smbXcli_base.h" + +struct cli_smb_oplock_break_waiter_state { + uint16_t fnum; + uint8_t level; +}; + +static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq); + +struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli) +{ + struct tevent_req *req, *subreq; + struct cli_smb_oplock_break_waiter_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb_oplock_break_waiter_state); + if (req == NULL) { + return NULL; + } + + /* + * Create a fake SMB request that we will never send out. This is only + * used to be set into the pending queue with the right mid. + */ + subreq = smb1cli_req_create(mem_ctx, ev, cli->conn, 0, 0, 0, 0, 0, 0, + 0, NULL, NULL, 0, NULL, 0, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + smb1cli_req_set_mid(subreq, 0xffff); + + if (!smbXcli_req_set_pending(subreq)) { + tevent_req_nterror(req, NT_STATUS_NO_MEMORY); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb_oplock_break_waiter_done, req); + return req; +} + +static void cli_smb_oplock_break_waiter_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb_oplock_break_waiter_state *state = tevent_req_data( + req, struct cli_smb_oplock_break_waiter_state); + struct iovec *iov; + uint8_t wct; + uint16_t *vwv; + NTSTATUS status; + + status = smb1cli_req_recv(subreq, state, + &iov, /* piov */ + NULL, /* phdr */ + &wct, + &vwv, + NULL, /* pvwv_offset */ + NULL, /* pnum_bytes */ + NULL, /* pbytes */ + NULL, /* pbytes_offset */ + NULL, /* pinbuf */ + NULL, 0); /* expected */ + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + if (wct < 8) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + state->fnum = SVAL(vwv+2, 0); + state->level = CVAL(vwv+3, 1); + tevent_req_done(req); +} + +NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req, + uint16_t *pfnum, + uint8_t *plevel) +{ + struct cli_smb_oplock_break_waiter_state *state = tevent_req_data( + req, struct cli_smb_oplock_break_waiter_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *pfnum = state->fnum; + *plevel = state->level; + return NT_STATUS_OK; +} + +/**************************************************************************** +send an ack for an oplock break request +****************************************************************************/ + +struct cli_oplock_ack_state { + uint8_t dummy; +}; + +static void cli_oplock_ack_done(struct tevent_req *subreq); + +struct tevent_req *cli_oplock_ack_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, uint8_t level) +{ + struct tevent_req *req, *subreq; + struct cli_oplock_ack_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_oplock_ack_state); + if (req == NULL) { + return NULL; + } + + subreq = cli_lockingx_send( + state, /* mem_ctx */ + ev, /* tevent_context */ + cli, /* cli */ + fnum, /* fnum */ + LOCKING_ANDX_OPLOCK_RELEASE, /* typeoflock */ + level, /* newoplocklevel */ + 0, /* timeout */ + 0, /* num_unlocks */ + NULL, /* unlocks */ + 0, /* num_locks */ + NULL); /* locks */ + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_oplock_ack_done, req); + return req; +} + +static void cli_oplock_ack_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_lockingx_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_oplock_ack_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + diff --git a/source3/libsmb/cliprint.c b/source3/libsmb/cliprint.c new file mode 100644 index 0000000..c4ee342 --- /dev/null +++ b/source3/libsmb/cliprint.c @@ -0,0 +1,188 @@ +/* + Unix SMB/CIFS implementation. + client print routines + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmb/clirap.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/util/string_wrappers.h" + +/***************************************************************************** + Convert a character pointer in a cli_call_api() response to a form we can use. + This function contains code to prevent core dumps if the server returns + invalid data. +*****************************************************************************/ +static const char *fix_char_ptr(unsigned int datap, unsigned int converter, + char *rdata, int rdrcnt) +{ + unsigned int offset; + + if (datap == 0) { + /* turn NULL pointers into zero length strings */ + return ""; + } + + offset = datap - converter; + + if (offset >= rdrcnt) { + DEBUG(1,("bad char ptr: datap=%u, converter=%u rdrcnt=%d>", + datap, converter, rdrcnt)); + return "<ERROR>"; + } + return &rdata[offset]; +} + +/**************************************************************************** +call fn() on each entry in a print queue +****************************************************************************/ + +NTSTATUS cli_print_queue(struct cli_state *cli, + void (*fn)(struct print_job_info *)) +{ + uint8_t *rparam = NULL; + uint8_t *rdata = NULL; + char *p = NULL; + uint32_t rdrcnt, rprcnt; + char param[1024]; + int converter; + int result_code=0; + int i = -1; + NTSTATUS status; + + memset(param,'\0',sizeof(param)); + + p = param; + SSVAL(p,0,76); /* API function number 76 (DosPrintJobEnum) */ + p += 2; + strlcpy_base(p,"zWrLeh", param, sizeof(param)); /* parameter description? */ + p = skip_string(param,sizeof(param),p); + strlcpy_base(p,"WWzWWDDzz", param, sizeof(param)); /* returned data format */ + p = skip_string(param,sizeof(param),p); + strlcpy_base(p,cli->share, param, sizeof(param)); /* name of queue */ + p = skip_string(param,sizeof(param),p); + SSVAL(p,0,2); /* API function level 2, PRJINFO_2 data structure */ + SSVAL(p,2,1000); /* size of bytes of returned data buffer */ + p += 4; + strlcpy_base(p,"", param,sizeof(param)); /* subformat */ + p = skip_string(param,sizeof(param),p); + + DEBUG(4,("doing cli_print_queue for %s\n", cli->share)); + + status = cli_trans( + talloc_tos(), + cli, + SMBtrans, /* trans_cmd */ + "\\PIPE\\LANMAN", /* name */ + 0, /* fid */ + 0, /* function */ + 0, /* flags */ + NULL, /* setup */ + 0, /* num_setup */ + 0, /* max_setup */ + (uint8_t *)param, /* param */ + PTR_DIFF(p,param), /* num_param */ + 1024, /* max_param */ + NULL, /* data */ + 0, /* num_data */ + CLI_BUFFER_SIZE, /* max_data */ + NULL, /* recv_flags2 */ + NULL, /* rsetup */ + 0, /* min_rsetup */ + NULL, /* num_rsetup */ + &rparam, /* rparam */ + 8, /* min_rparam */ + &rprcnt, /* num_rparam */ + &rdata, /* rdata */ + 0, /* min_rdata */ + &rdrcnt); /* num_rdata */ + if (!NT_STATUS_IS_OK(status)) { + cli->raw_status = status; + return status; + } + + result_code = SVAL(rparam,0); + converter = SVAL(rparam,2); /* conversion factor */ + + if (result_code == 0) { + struct print_job_info job; + + p = (char *)rdata; + + for (i = 0; i < SVAL(rparam,4); ++i) { + job.id = SVAL(p,0); + job.priority = SVAL(p,2); + fstrcpy(job.user, + fix_char_ptr(SVAL(p,4), converter, + (char *)rdata, rdrcnt)); + job.t = make_unix_date3( + p + 12, smb1cli_conn_server_time_zone(cli->conn)); + job.size = IVAL(p,16); + fstrcpy(job.name,fix_char_ptr(SVAL(p,24), + converter, + (char *)rdata, rdrcnt)); + fn(&job); + p += 28; + } + } + + /* If any parameters or data were returned, free the storage. */ + TALLOC_FREE(rparam); + TALLOC_FREE(rdata); + + return NT_STATUS_OK; +} + +/**************************************************************************** + cancel a print job + ****************************************************************************/ + +int cli_printjob_del(struct cli_state *cli, int job) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int ret = -1; + char param[1024]; + + memset(param,'\0',sizeof(param)); + + p = param; + SSVAL(p,0,81); /* DosPrintJobDel() */ + p += 2; + strlcpy_base(p,"W", param,sizeof(param)); + p = skip_string(param,sizeof(param),p); + strlcpy_base(p,"", param,sizeof(param)); + p = skip_string(param,sizeof(param),p); + SSVAL(p,0,job); + p += 2; + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) { /* return data, length */ + ret = SVAL(rparam,0); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return ret; +} diff --git a/source3/libsmb/cliquota.c b/source3/libsmb/cliquota.c new file mode 100644 index 0000000..45cd328 --- /dev/null +++ b/source3/libsmb/cliquota.c @@ -0,0 +1,696 @@ +/* + Unix SMB/CIFS implementation. + client quota functions + Copyright (C) Stefan (metze) Metzmacher 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "fake_file.h" +#include "../libcli/security/security.h" +#include "trans2.h" +#include "../libcli/smb/smbXcli_base.h" +#include "librpc/gen_ndr/ndr_quota.h" + +NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum) +{ + return cli_ntcreate(cli, FAKE_FILE_NAME_QUOTA_WIN32, + 0x00000016, DESIRED_ACCESS_PIPE, + 0x00000000, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, 0x00000000, 0x03, quota_fnum, NULL); +} + +void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list) +{ + if (!qt_list || !*qt_list) { + return; + } + + if ((*qt_list)->mem_ctx) + talloc_destroy((*qt_list)->mem_ctx); + + (*qt_list) = NULL; + + return; +} + +bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx, + SMB_NTQUOTA_STRUCT *pqt, + SMB_NTQUOTA_LIST **pqt_list) +{ + SMB_NTQUOTA_LIST *tmp_list_ent; + + if ((tmp_list_ent = talloc_zero(mem_ctx, SMB_NTQUOTA_LIST)) == NULL) { + return false; + } + + if ((tmp_list_ent->quotas = talloc_zero(mem_ctx, SMB_NTQUOTA_STRUCT)) == + NULL) { + return false; + } + + *tmp_list_ent->quotas = *pqt; + tmp_list_ent->mem_ctx = mem_ctx; + + DLIST_ADD((*pqt_list), tmp_list_ent); + + return true; +} + +bool parse_user_quota_record(const uint8_t *rdata, + unsigned int rdata_count, + unsigned int *offset, + SMB_NTQUOTA_STRUCT *pqt) +{ + struct file_quota_information info = {0}; + TALLOC_CTX *frame = talloc_stackframe(); + DATA_BLOB blob; + enum ndr_err_code err; + bool result = false; + + blob.data = discard_const_p(uint8_t, rdata); + blob.length = rdata_count; + err = ndr_pull_struct_blob( + &blob, + frame, + &info, + (ndr_pull_flags_fn_t)ndr_pull_file_quota_information); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + goto out; + } + + *offset = info.next_entry_offset; + + ZERO_STRUCTP(pqt); + pqt->usedspace = info.quota_used; + + pqt->softlim = info.quota_threshold; + + pqt->hardlim = info.quota_limit; + + pqt->qtype = SMB_USER_QUOTA_TYPE; + pqt->sid = info.sid; + result = true; +out: + TALLOC_FREE(frame); + return result; +} + +NTSTATUS parse_user_quota_list(const uint8_t *curdata, + uint32_t curdata_count, + TALLOC_CTX *mem_ctx, + SMB_NTQUOTA_LIST **pqt_list) +{ + NTSTATUS status = NT_STATUS_OK; + unsigned offset; + SMB_NTQUOTA_STRUCT qt; + + while (true) { + ZERO_STRUCT(qt); + if (!parse_user_quota_record(curdata, curdata_count, &offset, + &qt)) { + DEBUG(1, ("Failed to parse the quota record\n")); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + break; + } + + if (offset > curdata_count) { + DEBUG(1, ("out of bounds offset in quota record\n")); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + break; + } + + if (curdata + offset < curdata) { + DEBUG(1, ("Pointer overflow in quota record\n")); + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + break; + } + + if (!add_record_to_ntquota_list(mem_ctx, &qt, pqt_list)) { + status = NT_STATUS_NO_MEMORY; + break; + } + + curdata += offset; + curdata_count -= offset; + + if (offset == 0) { + break; + } + } + + return status; +} + +NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, + unsigned int rdata_count, + SMB_NTQUOTA_STRUCT *pqt) +{ + SMB_NTQUOTA_STRUCT qt; + + ZERO_STRUCT(qt); + + if (rdata_count < 48) { + /* minimum length is not enforced by SMB2 client. + */ + DEBUG(1, ("small returned fs quota buffer\n")); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + /* unknown_1 24 NULL bytes in pdata*/ + + /* the soft quotas 8 bytes (uint64_t)*/ + qt.softlim = BVAL(rdata, 24); + + /* the hard quotas 8 bytes (uint64_t)*/ + qt.hardlim = BVAL(rdata, 32); + + /* quota_flags 2 bytes **/ + qt.qflags = SVAL(rdata, 40); + + qt.qtype = SMB_USER_FS_QUOTA_TYPE; + + *pqt = qt; + + return NT_STATUS_OK; +} + +NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list, + uint32_t maxlen, + TALLOC_CTX *mem_ctx, + DATA_BLOB *outbuf, + SMB_NTQUOTA_LIST **end_ptr) +{ + return fill_quota_buffer(mem_ctx, + qt_list, + false, + maxlen, + outbuf, + end_ptr); +} + +NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx, + const SMB_NTQUOTA_STRUCT *pqt, + DATA_BLOB *blob, + uint32_t maxlen) +{ + uint8_t *buf; + + if (maxlen > 0 && maxlen < 48) { + return NT_STATUS_BUFFER_TOO_SMALL; + } + + *blob = data_blob_talloc_zero(mem_ctx, 48); + + if (!blob->data) { + return NT_STATUS_NO_MEMORY; + } + + buf = blob->data; + + /* Unknown1 24 NULL bytes*/ + SBIG_UINT(buf, 0, (uint64_t)0); + SBIG_UINT(buf, 8, (uint64_t)0); + SBIG_UINT(buf, 16, (uint64_t)0); + + /* Default Soft Quota 8 bytes */ + SBIG_UINT(buf, 24, pqt->softlim); + + /* Default Hard Quota 8 bytes */ + SBIG_UINT(buf, 32, pqt->hardlim); + + /* Quota flag 4 bytes */ + SIVAL(buf, 40, pqt->qflags); + + /* 4 padding bytes */ + SIVAL(buf, 44, 0); + + return NT_STATUS_OK; +} + +NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + uint16_t setup[1]; + uint8_t *rparam = NULL, *rdata = NULL; + uint32_t rparam_count, rdata_count; + unsigned int sid_len; + unsigned int offset; + struct nttrans_query_quota_params get_quota = {0}; + struct file_get_quota_info info = {0}; + enum ndr_err_code err; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + DATA_BLOB data_blob = data_blob_null; + DATA_BLOB param_blob = data_blob_null; + + if (!cli||!pqt) { + smb_panic("cli_get_user_quota() called with NULL Pointer!"); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + TALLOC_FREE(frame); + return cli_smb2_get_user_quota(cli, quota_fnum, pqt); + } + + get_quota.fid = quota_fnum; + get_quota.return_single_entry = 1; + get_quota.restart_scan = 0; + + sid_len = ndr_size_dom_sid(&pqt->sid, 0); + + info.next_entry_offset = 0; + info.sid_length = sid_len; + info.sid = pqt->sid; + + err = ndr_push_struct_blob( + &data_blob, + frame, + &info, + (ndr_push_flags_fn_t)ndr_push_file_get_quota_info); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INTERNAL_ERROR; + goto out; + } + + get_quota.sid_list_length = data_blob.length; + get_quota.start_sid_offset = data_blob.length; + + err = ndr_push_struct_blob( + ¶m_blob, + frame, + &get_quota, + (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INTERNAL_ERROR; + goto out; + } + + status = cli_trans(talloc_tos(), cli, SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_GET_USER_QUOTA, 0, + setup, 1, 0, /* setup */ + param_blob.data, param_blob.length, 4, /* params */ + data_blob.data, data_blob.length, 112, /* data */ + NULL, /* recv_flags2 */ + NULL, 0, NULL, /* rsetup */ + &rparam, 4, &rparam_count, + &rdata, 8, &rdata_count); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("NT_TRANSACT_GET_USER_QUOTA failed: %s\n", + nt_errstr(status))); + goto out; + } + + if (!parse_user_quota_record(rdata, rdata_count, &offset, pqt)) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + DEBUG(0,("Got INVALID NT_TRANSACT_GET_USER_QUOTA reply.\n")); + } + +out: + TALLOC_FREE(rparam); + TALLOC_FREE(rdata); + TALLOC_FREE(frame); + return status; +} + +NTSTATUS +cli_set_user_quota(struct cli_state *cli, int quota_fnum, SMB_NTQUOTA_LIST *qtl) +{ + uint16_t setup[1]; + uint8_t params[2]; + DATA_BLOB data = data_blob_null; + NTSTATUS status; + + if (!cli || !qtl) { + smb_panic("cli_set_user_quota() called with NULL Pointer!"); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_user_quota(cli, quota_fnum, qtl); + } + + status = build_user_quota_buffer(qtl, 0, talloc_tos(), &data, NULL); + if (!NT_STATUS_IS_OK(status)) { + /* + * smb1 doesn't send NT_STATUS_NO_MORE_ENTRIES so swallow + * this status. + */ + if (!NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { + goto cleanup; + } + } + + SSVAL(setup + 0, 0, NT_TRANSACT_SET_USER_QUOTA); + + SSVAL(params,0,quota_fnum); + + status = cli_trans(talloc_tos(), cli, SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_SET_USER_QUOTA, 0, + setup, 1, 0, /* setup */ + params, 2, 0, /* params */ + data.data, data.length, 0, /* data */ + NULL, /* recv_flags2 */ + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparams */ + NULL, 0, NULL); /* rdata */ + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("NT_TRANSACT_SET_USER_QUOTA failed: %s\n", + nt_errstr(status))); + } + +cleanup: + data_blob_free(&data); + return status; +} + +static NTSTATUS cli_list_user_quota_step(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int quota_fnum, + SMB_NTQUOTA_LIST **pqt_list, + bool first) +{ + uint16_t setup[1]; + DATA_BLOB params_blob = data_blob_null; + uint8_t *rparam=NULL, *rdata=NULL; + uint32_t rparam_count=0, rdata_count=0; + NTSTATUS status; + struct nttrans_query_quota_params quota_params = {0}; + enum ndr_err_code err; + + TALLOC_CTX *frame = NULL; + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_list_user_quota_step(cli, mem_ctx, quota_fnum, + pqt_list, first); + } + frame = talloc_stackframe(); + + SSVAL(setup + 0, 0, NT_TRANSACT_GET_USER_QUOTA); + + quota_params.fid = quota_fnum; + if (first) { + quota_params.restart_scan = 1; + } + err = ndr_push_struct_blob( + ¶ms_blob, + frame, + "a_params, + (ndr_push_flags_fn_t)ndr_push_nttrans_query_quota_params); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + status = NT_STATUS_INVALID_PARAMETER; + goto cleanup; + } + + status = cli_trans(talloc_tos(), cli, SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_GET_USER_QUOTA, 0, + setup, 1, 0, /* setup */ + params_blob.data, params_blob.length, 4, /* params */ + NULL, 0, 2048, /* data */ + NULL, /* recv_flags2 */ + NULL, 0, NULL, /* rsetup */ + &rparam, 0, &rparam_count, + &rdata, 0, &rdata_count); + + /* compat. with smbd + safeguard against + * endless loop + */ + if (NT_STATUS_IS_OK(status) && rdata_count == 0) { + status = NT_STATUS_NO_MORE_ENTRIES; + } + + if (!NT_STATUS_IS_OK(status)) { + goto cleanup; + } + + status = parse_user_quota_list(rdata, rdata_count, mem_ctx, pqt_list); + +cleanup: + TALLOC_FREE(rparam); + TALLOC_FREE(rdata); + TALLOC_FREE(frame); + + return status; +} + +NTSTATUS cli_list_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_LIST **pqt_list) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx = NULL; + bool first = true; + + if (!cli || !pqt_list) { + smb_panic("cli_list_user_quota() called with NULL Pointer!"); + } + + *pqt_list = NULL; + + if ((mem_ctx = talloc_init("SMB_USER_QUOTA_LIST")) == NULL) { + return NT_STATUS_NO_MEMORY; + } + + do { + status = cli_list_user_quota_step(cli, mem_ctx, quota_fnum, + pqt_list, first); + first = false; + } while (NT_STATUS_IS_OK(status)); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_ENTRIES)) { + status = NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status) || *pqt_list == NULL) { + TALLOC_FREE(mem_ctx); + } + + return status; +} + +NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + uint16_t setup[1]; + uint8_t param[2]; + uint8_t *rdata=NULL; + uint32_t rdata_count=0; + NTSTATUS status; + + if (!cli||!pqt) { + smb_panic("cli_get_fs_quota_info() called with NULL Pointer!"); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_get_fs_quota_info(cli, quota_fnum, pqt); + } + + SSVAL(setup + 0, 0, TRANSACT2_QFSINFO); + + SSVAL(param,0,SMB_FS_QUOTA_INFORMATION); + + status = cli_trans(talloc_tos(), cli, SMBtrans2, + NULL, -1, /* name, fid */ + 0, 0, /* function, flags */ + setup, 1, 0, /* setup */ + param, 2, 0, /* param */ + NULL, 0, 560, /* data */ + NULL, /* recv_flags2 */ + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + &rdata, 48, &rdata_count); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n", + nt_errstr(status))); + return status; + } + + status = parse_fs_quota_buffer(rdata, rdata_count, pqt); + + TALLOC_FREE(rdata); + return status; +} + +NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt) +{ + uint16_t setup[1]; + uint8_t param[4]; + DATA_BLOB data = data_blob_null; + NTSTATUS status; + + if (!cli||!pqt) { + smb_panic("cli_set_fs_quota_info() called with NULL Pointer!"); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_set_fs_quota_info(cli, quota_fnum, pqt); + } + + status = build_fs_quota_buffer(talloc_tos(), pqt, &data, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + SSVAL(setup + 0, 0,TRANSACT2_SETFSINFO); + + SSVAL(param,0,quota_fnum); + SSVAL(param,2,SMB_FS_QUOTA_INFORMATION); + + status = cli_trans(talloc_tos(), cli, SMBtrans2, + NULL, -1, /* name, fid */ + 0, 0, /* function, flags */ + setup, 1, 0, /* setup */ + param, 4, 0, /* param */ + data.data, data.length, 0, /* data */ + NULL, /* recv_flags2 */ + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + NULL, 0, NULL); /* rdata */ + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("SMB_FS_QUOTA_INFORMATION failed: %s\n", + nt_errstr(status))); + } + + return status; +} + +NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx, + SMB_NTQUOTA_LIST *qlist, + bool return_single, + uint32_t max_data, + DATA_BLOB *blob, + SMB_NTQUOTA_LIST **end_ptr) +{ + int ndr_flags = NDR_SCALARS | NDR_BUFFERS; + struct ndr_push *qndr = NULL; + uint32_t start_offset = 0; + uint32_t padding = 0; + if (qlist == NULL) { + /* We must push at least one. */ + return NT_STATUS_NO_MORE_ENTRIES; + } + + qndr = ndr_push_init_ctx(mem_ctx); + if (qndr == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (;qlist != NULL; qlist = qlist->next) { + struct file_quota_information info = {0}; + enum ndr_err_code err; + uint32_t dsize = sizeof(info.next_entry_offset) + + sizeof(info.sid_length) + + sizeof(info.change_time) + + sizeof(info.quota_used) + + sizeof(info.quota_threshold) + + sizeof(info.quota_limit); + + + info.sid_length = ndr_size_dom_sid(&qlist->quotas->sid, 0); + + if (max_data) { + uint32_t curr_pos_no_padding = qndr->offset - padding; + uint32_t payload = dsize + info.sid_length; + uint32_t new_pos = (curr_pos_no_padding + payload); + if (new_pos < curr_pos_no_padding) { + /* Detect unlikely integer wrap */ + DBG_ERR("Integer wrap while adjusting pos " + "0x%x by offset 0x%x\n", + curr_pos_no_padding, payload); + return NT_STATUS_INTERNAL_ERROR; + } + if (new_pos > max_data) { + DBG_WARNING("Max data will be exceeded " + "writing next query info. " + "cur_pos 0x%x, sid_length 0x%x, " + "dsize 0x%x, max_data 0x%x\n", + curr_pos_no_padding, + info.sid_length, + dsize, + max_data); + break; + } + } + + start_offset = qndr->offset; + info.sid = qlist->quotas->sid; + info.quota_used = qlist->quotas->usedspace; + info.quota_threshold = qlist->quotas->softlim; + info.quota_limit = qlist->quotas->hardlim; + + err = ndr_push_file_quota_information(qndr, + ndr_flags, + &info); + + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + DBG_DEBUG("Failed to push the quota sid\n"); + return NT_STATUS_INTERNAL_ERROR; + } + + /* pidl will align to 8 bytes due to 8 byte members*/ + /* Remember how much align padding we've used. */ + padding = qndr->offset; + + err = ndr_push_align(qndr, 8); + if (!NDR_ERR_CODE_IS_SUCCESS(err)) { + DBG_DEBUG("ndr_push_align returned %s\n", + ndr_map_error2string(err)); + return ndr_map_error2ntstatus(err); + } + + padding = qndr->offset - padding; + + /* + * Overwrite next_entry_offset for this entry now + * we know what it should be. We know we're using + * LIBNDR_FLAG_LITTLE_ENDIAN here so we can use + * SIVAL. + */ + info.next_entry_offset = qndr->offset - start_offset; + SIVAL(qndr->data, start_offset, info.next_entry_offset); + + if (return_single) { + break; + } + } + + if (end_ptr != NULL) { + *end_ptr = qlist; + } + + /* Remove the padding alignment on the last element pushed. */ + blob->length = qndr->offset - padding; + blob->data = qndr->data; + + /* + * Terminate the pushed array by setting next_entry_offset + * for the last element to zero. + */ + if (blob->length >= sizeof(uint32_t)) { + SIVAL(qndr->data, start_offset, 0); + } + return NT_STATUS_OK; +} diff --git a/source3/libsmb/clirap.c b/source3/libsmb/clirap.c new file mode 100644 index 0000000..b89c7e1 --- /dev/null +++ b/source3/libsmb/clirap.c @@ -0,0 +1,1819 @@ +/* + Unix SMB/CIFS implementation. + client RAP calls + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Gerald (Jerry) Carter 2004 + Copyright (C) James Peach 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/auth/libcli_auth.h" +#include "../librpc/gen_ndr/rap.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "libsmb/libsmb.h" +#include "libsmb/clirap.h" +#include "trans2.h" +#include "../libcli/smb/smbXcli_base.h" +#include "cli_smb2_fnum.h" +#include "lib/util/string_wrappers.h" + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#define PIPE_LANMAN "\\PIPE\\LANMAN" + +/**************************************************************************** + Call a remote api +****************************************************************************/ + +bool cli_api(struct cli_state *cli, + char *param, int prcnt, int mprcnt, + char *data, int drcnt, int mdrcnt, + char **rparam, unsigned int *rprcnt, + char **rdata, unsigned int *rdrcnt) +{ + NTSTATUS status; + + uint8_t *my_rparam, *my_rdata; + uint32_t num_my_rparam, num_my_rdata; + + status = cli_trans(talloc_tos(), cli, SMBtrans, + PIPE_LANMAN, 0, /* name, fid */ + 0, 0, /* function, flags */ + NULL, 0, 0, /* setup */ + (uint8_t *)param, prcnt, mprcnt, /* Params, length, max */ + (uint8_t *)data, drcnt, mdrcnt, /* Data, length, max */ + NULL, /* recv_flags2 */ + NULL, 0, NULL, /* rsetup */ + &my_rparam, 0, &num_my_rparam, + &my_rdata, 0, &num_my_rdata); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + /* + * I know this memcpy massively hurts, but there are just tons + * of callers of cli_api that eventually need changing to + * talloc + */ + + *rparam = (char *)smb_memdup(my_rparam, num_my_rparam); + if (*rparam == NULL) { + goto fail; + } + *rprcnt = num_my_rparam; + TALLOC_FREE(my_rparam); + + *rdata = (char *)smb_memdup(my_rdata, num_my_rdata); + if (*rdata == NULL) { + goto fail; + } + *rdrcnt = num_my_rdata; + TALLOC_FREE(my_rdata); + + return true; +fail: + TALLOC_FREE(my_rdata); + TALLOC_FREE(my_rparam); + *rparam = NULL; + *rprcnt = 0; + *rdata = NULL; + *rdrcnt = 0; + return false; +} + +/**************************************************************************** + Call a NetShareEnum - try and browse available connections on a host. +****************************************************************************/ + +int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32_t, const char *, void *), void *state) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + char param[1024]; + int count = -1; + bool ok; + int res; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = param; + SSVAL(p,0,0); /* api number */ + p += 2; + strlcpy(p,"WrLeh",sizeof(param)-PTR_DIFF(p,param)); + p = skip_string(param,sizeof(param),p); + strlcpy(p,"B13BWz",sizeof(param)-PTR_DIFF(p,param)); + p = skip_string(param,sizeof(param),p); + SSVAL(p,0,1); + /* + * Win2k needs a *smaller* buffer than 0xFFFF here - + * it returns "out of server memory" with 0xFFFF !!! JRA. + */ + SSVAL(p,2,0xFFE0); + p += 4; + + ok = cli_api( + cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 0xFFE0, /* data, length, maxlen - Win2k needs a small buffer here too ! */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt); /* return data, length */ + if (!ok) { + DEBUG(4,("NetShareEnum failed\n")); + goto done; + } + + if (rprcnt < 6) { + DBG_ERR("Got invalid result: rprcnt=%u\n", rprcnt); + goto done; + } + + res = rparam? SVAL(rparam,0) : -1; + + if (res == 0 || res == ERRmoredata) { + int converter=SVAL(rparam,2); + int i; + char *rdata_end = rdata + rdrcnt; + + count=SVAL(rparam,4); + p = rdata; + + for (i=0;i<count;i++,p+=20) { + char *sname; + int type; + int comment_offset; + const char *cmnt; + const char *p1; + char *s1, *s2; + size_t len; + TALLOC_CTX *frame = talloc_stackframe(); + + if (p + 20 > rdata_end) { + TALLOC_FREE(frame); + break; + } + + sname = p; + type = SVAL(p,14); + comment_offset = (IVAL(p,16) & 0xFFFF) - converter; + if (comment_offset < 0 || + comment_offset > (int)rdrcnt) { + TALLOC_FREE(frame); + break; + } + cmnt = comment_offset?(rdata+comment_offset):""; + + /* Work out the comment length. */ + for (p1 = cmnt, len = 0; *p1 && + p1 < rdata_end; len++) + p1++; + if (!*p1) { + len++; + } + pull_string_talloc(frame,rdata,0, + &s1,sname,14,STR_ASCII); + pull_string_talloc(frame,rdata,0, + &s2,cmnt,len,STR_ASCII); + if (!s1 || !s2) { + TALLOC_FREE(frame); + continue; + } + + fn(s1, type, s2, state); + + TALLOC_FREE(frame); + } + } else { + DEBUG(4,("NetShareEnum res=%d\n", res)); + } + +done: + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return count; +} + +/**************************************************************************** + Call a NetServerEnum for the specified workgroup and servertype mask. This + function then calls the specified callback function for each name returned. + + The callback function takes 4 arguments: the machine name, the server type, + the comment and a state pointer. +****************************************************************************/ + +bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32_t stype, + void (*fn)(const char *, uint32_t, const char *, void *), + void *state) +{ + char *rparam = NULL; + char *rdata = NULL; + char *rdata_end = NULL; + unsigned int rdrcnt,rprcnt; + char *p; + char param[1024]; + int uLevel = 1; + size_t len; + uint32_t func = RAP_NetServerEnum2; + char *last_entry = NULL; + int total_cnt = 0; + int return_cnt = 0; + int res; + + errno = 0; /* reset */ + + /* + * This may take more than one transaction, so we should loop until + * we no longer get a more data to process or we have all of the + * items. + */ + do { + /* send a SMBtrans command with api NetServerEnum */ + p = param; + SIVAL(p,0,func); /* api number */ + p += 2; + + if (func == RAP_NetServerEnum3) { + strlcpy(p,"WrLehDzz", sizeof(param)-PTR_DIFF(p,param)); + } else { + strlcpy(p,"WrLehDz", sizeof(param)-PTR_DIFF(p,param)); + } + + p = skip_string(param, sizeof(param), p); + strlcpy(p,"B16BBDz", sizeof(param)-PTR_DIFF(p,param)); + + p = skip_string(param, sizeof(param), p); + SSVAL(p,0,uLevel); + SSVAL(p,2,CLI_BUFFER_SIZE); + p += 4; + SIVAL(p,0,stype); + p += 4; + + /* If we have more data, tell the server where + * to continue from. + */ + len = push_ascii(p, + workgroup, + sizeof(param) - PTR_DIFF(p,param) - 1, + STR_TERMINATE|STR_UPPER); + + if (len == 0) { + SAFE_FREE(last_entry); + return false; + } + p += len; + + if (func == RAP_NetServerEnum3) { + len = push_ascii(p, + last_entry ? last_entry : "", + sizeof(param) - PTR_DIFF(p,param) - 1, + STR_TERMINATE); + + if (len == 0) { + SAFE_FREE(last_entry); + return false; + } + p += len; + } + + /* Next time through we need to use the continue api */ + func = RAP_NetServerEnum3; + + if (!cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt)) { /* return data, return size */ + + /* break out of the loop on error */ + res = -1; + break; + } + + rdata_end = rdata + rdrcnt; + + if (rprcnt < 6) { + DBG_ERR("Got invalid result: rprcnt=%u\n", rprcnt); + res = -1; + break; + } + + res = rparam ? SVAL(rparam,0) : -1; + + if (res == 0 || res == ERRmoredata || + (res != -1 && cli_errno(cli) == 0)) { + char *sname = NULL; + int i, count; + int converter=SVAL(rparam,2); + + /* Get the number of items returned in this buffer */ + count = SVAL(rparam, 4); + + /* The next field contains the number of items left, + * including those returned in this buffer. So the + * first time through this should contain all of the + * entries. + */ + if (total_cnt == 0) { + total_cnt = SVAL(rparam, 6); + } + + /* Keep track of how many we have read */ + return_cnt += count; + p = rdata; + + /* The last name in the previous NetServerEnum reply is + * sent back to server in the NetServerEnum3 request + * (last_entry). The next reply should repeat this entry + * as the first element. We have no proof that this is + * always true, but from traces that seems to be the + * behavior from Window Servers. So first lets do a lot + * of checking, just being paranoid. If the string + * matches then we already saw this entry so skip it. + * + * NOTE: sv1_name field must be null terminated and has + * a max size of 16 (NetBIOS Name). + */ + if (last_entry && count && p && + (strncmp(last_entry, p, 16) == 0)) { + count -= 1; /* Skip this entry */ + return_cnt = -1; /* Not part of total, so don't count. */ + p = rdata + 26; /* Skip the whole record */ + } + + for (i = 0; i < count; i++, p += 26) { + int comment_offset; + const char *cmnt; + const char *p1; + char *s1, *s2; + TALLOC_CTX *frame = talloc_stackframe(); + uint32_t entry_stype; + + if (p + 26 > rdata_end) { + TALLOC_FREE(frame); + break; + } + + sname = p; + comment_offset = (IVAL(p,22) & 0xFFFF)-converter; + cmnt = comment_offset?(rdata+comment_offset):""; + + if (comment_offset < 0 || comment_offset >= (int)rdrcnt) { + TALLOC_FREE(frame); + continue; + } + + /* Work out the comment length. */ + for (p1 = cmnt, len = 0; *p1 && + p1 < rdata_end; len++) + p1++; + if (!*p1) { + len++; + } + + entry_stype = IVAL(p,18) & ~SV_TYPE_LOCAL_LIST_ONLY; + + pull_string_talloc(frame,rdata,0, + &s1,sname,16,STR_ASCII); + pull_string_talloc(frame,rdata,0, + &s2,cmnt,len,STR_ASCII); + + if (!s1 || !s2) { + TALLOC_FREE(frame); + continue; + } + + fn(s1, entry_stype, s2, state); + TALLOC_FREE(frame); + } + + /* We are done with the old last entry, so now we can free it */ + if (last_entry) { + SAFE_FREE(last_entry); /* This will set it to null */ + } + + /* We always make a copy of the last entry if we have one */ + if (sname) { + last_entry = smb_xstrdup(sname); + } + + /* If we have more data, but no last entry then error out */ + if (!last_entry && (res == ERRmoredata)) { + errno = EINVAL; + res = 0; + } + + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + } while ((res == ERRmoredata) && (total_cnt > return_cnt)); + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + SAFE_FREE(last_entry); + + if (res == -1) { + errno = cli_errno(cli); + } else { + if (!return_cnt) { + /* this is a very special case, when the domain master for the + work group isn't part of the work group itself, there is something + wild going on */ + errno = ENOENT; + } + } + + return(return_cnt > 0); +} + +/**************************************************************************** + Send a SamOEMChangePassword command. +****************************************************************************/ + +bool cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password, + const char *old_password) +{ + char param[1024]; + unsigned char data[532]; + char *p = param; + unsigned char old_pw_hash[16]; + unsigned char new_pw_hash[16]; + unsigned int data_len; + unsigned int param_len = 0; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t old_pw_key = { + .data = old_pw_hash, + .size = sizeof(old_pw_hash), + }; + int rc; + + if (strlen(user) >= sizeof(fstring)-1) { + DEBUG(0,("cli_oem_change_password: user name %s is too long.\n", user)); + return False; + } + + SSVAL(p,0,214); /* SamOEMChangePassword command. */ + p += 2; + strlcpy(p, "zsT", sizeof(param)-PTR_DIFF(p,param)); + p = skip_string(param,sizeof(param),p); + strlcpy(p, "B516B16", sizeof(param)-PTR_DIFF(p,param)); + p = skip_string(param,sizeof(param),p); + strlcpy(p,user, sizeof(param)-PTR_DIFF(p,param)); + p = skip_string(param,sizeof(param),p); + SSVAL(p,0,532); + p += 2; + + param_len = PTR_DIFF(p,param); + + /* + * Get the Lanman hash of the old password, we + * use this as the key to make_oem_passwd_hash(). + */ + E_deshash(old_password, old_pw_hash); + + encode_pw_buffer(data, new_password, STR_ASCII); + +#ifdef DEBUG_PASSWORD + DEBUG(100,("make_oem_passwd_hash\n")); + dump_data(100, data, 516); +#endif + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &old_pw_key, + NULL); + if (rc < 0) { + DBG_ERR("gnutls_cipher_init failed: %s\n", + gnutls_strerror(rc)); + return false; + } + rc = gnutls_cipher_encrypt(cipher_hnd, + data, + 516); + gnutls_cipher_deinit(cipher_hnd); + if (rc < 0) { + return false; + } + + /* + * Now place the old password hash in the data. + */ + E_deshash(new_password, new_pw_hash); + + rc = E_old_pw_hash( new_pw_hash, old_pw_hash, (uchar *)&data[516]); + if (rc != 0) { + DBG_ERR("E_old_pw_hash failed: %s\n", gnutls_strerror(rc)); + return false; + } + + data_len = 532; + + if (!cli_api(cli, + param, param_len, 4, /* param, length, max */ + (char *)data, data_len, 0, /* data, length, max */ + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + DEBUG(0,("cli_oem_change_password: Failed to send password change for user %s\n", + user )); + return False; + } + + if (rdrcnt < 2) { + cli->rap_error = ERRbadformat; + goto done; + } + + if (rparam) { + cli->rap_error = SVAL(rparam,0); + } + +done: + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return (cli->rap_error == 0); +} + +/**************************************************************************** + Send a qpathinfo call. +****************************************************************************/ + +struct cli_qpathinfo1_state { + struct cli_state *cli; + uint32_t num_data; + uint8_t *data; +}; + +static void cli_qpathinfo1_done(struct tevent_req *subreq); + +struct tevent_req *cli_qpathinfo1_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_qpathinfo1_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo1_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + subreq = cli_qpathinfo_send(state, ev, cli, fname, SMB_INFO_STANDARD, + 22, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qpathinfo1_done, req); + return req; +} + +static void cli_qpathinfo1_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qpathinfo1_state *state = tevent_req_data( + req, struct cli_qpathinfo1_state); + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &state->data, + &state->num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_qpathinfo1_recv(struct tevent_req *req, + time_t *change_time, + time_t *access_time, + time_t *write_time, + off_t *size, + uint32_t *pattr) +{ + struct cli_qpathinfo1_state *state = tevent_req_data( + req, struct cli_qpathinfo1_state); + NTSTATUS status; + + time_t (*date_fn)(const void *buf, int serverzone); + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (state->cli->win95) { + date_fn = make_unix_date; + } else { + date_fn = make_unix_date2; + } + + if (change_time) { + *change_time = date_fn(state->data+0, smb1cli_conn_server_time_zone(state->cli->conn)); + } + if (access_time) { + *access_time = date_fn(state->data+4, smb1cli_conn_server_time_zone(state->cli->conn)); + } + if (write_time) { + *write_time = date_fn(state->data+8, smb1cli_conn_server_time_zone(state->cli->conn)); + } + if (size) { + *size = IVAL(state->data, 12); + } + if (pattr) { + *pattr = SVAL(state->data, l1_attrFile); + } + return NT_STATUS_OK; +} + +NTSTATUS cli_qpathinfo1(struct cli_state *cli, + const char *fname, + time_t *change_time, + time_t *access_time, + time_t *write_time, + off_t *size, + uint32_t *pattr) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qpathinfo1_send(frame, ev, cli, fname); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_qpathinfo1_recv(req, change_time, access_time, + write_time, size, pattr); + fail: + TALLOC_FREE(frame); + return status; +} + +static void prep_basic_information_buf( + uint8_t buf[40], + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr) +{ + char *p = (char *)buf; + /* + * Add the create, last access, modification, and status change times + */ + put_long_date_full_timespec( + TIMESTAMP_SET_NT_OR_BETTER, p, &create_time); + p += 8; + + put_long_date_full_timespec( + TIMESTAMP_SET_NT_OR_BETTER, p, &access_time); + p += 8; + + put_long_date_full_timespec( + TIMESTAMP_SET_NT_OR_BETTER, p, &write_time); + p += 8; + + put_long_date_full_timespec( + TIMESTAMP_SET_NT_OR_BETTER, p, &change_time); + p += 8; + + if (attr == (uint32_t)-1 || attr == FILE_ATTRIBUTE_NORMAL) { + /* No change. */ + attr = 0; + } else if (attr == 0) { + /* Clear all existing attributes. */ + attr = FILE_ATTRIBUTE_NORMAL; + } + + /* Add attributes */ + SIVAL(p, 0, attr); + + p += 4; + + /* Add padding */ + SIVAL(p, 0, 0); + p += 4; + + SMB_ASSERT(PTR_DIFF(p, buf) == 40); +} + +NTSTATUS cli_setpathinfo_ext(struct cli_state *cli, const char *fname, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr) +{ + uint8_t buf[40]; + + prep_basic_information_buf( + buf, + create_time, + access_time, + write_time, + change_time, + attr); + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + DATA_BLOB in_data = data_blob_const(buf, sizeof(buf)); + /* + * Split out SMB2 here as we need to select + * the correct info type and level. + */ + return cli_smb2_setpathinfo(cli, + fname, + 1, /* SMB2_SETINFO_FILE */ + SMB_FILE_BASIC_INFORMATION - 1000, + &in_data); + } + + return cli_setpathinfo( + cli, SMB_FILE_BASIC_INFORMATION, fname, buf, sizeof(buf)); +} + +struct cli_setfileinfo_ext_state { + uint8_t data[40]; + DATA_BLOB in_data; +}; + +static void cli_setfileinfo_ext_done(struct tevent_req *subreq); +static void cli_setfileinfo_ext_done2(struct tevent_req *subreq); + +struct tevent_req *cli_setfileinfo_ext_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_setfileinfo_ext_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_setfileinfo_ext_state); + if (req == NULL) { + return NULL; + } + prep_basic_information_buf( + state->data, + create_time, + access_time, + write_time, + change_time, + attr); + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + state->in_data = (DATA_BLOB) { + .data = state->data, .length = sizeof(state->data), + }; + + subreq = cli_smb2_set_info_fnum_send( + state, + ev, + cli, + fnum, + SMB2_0_INFO_FILE, + SMB_FILE_BASIC_INFORMATION - 1000, + &state->in_data, + 0); /* in_additional_info */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_setfileinfo_ext_done2, req); + return req; + } + + subreq = cli_setfileinfo_send( + state, + ev, + cli, + fnum, + SMB_FILE_BASIC_INFORMATION, + state->data, + sizeof(state->data)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_setfileinfo_ext_done, req); + return req; +} + +static void cli_setfileinfo_ext_done(struct tevent_req *subreq) +{ + NTSTATUS status = cli_setfileinfo_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_setfileinfo_ext_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_setfileinfo_ext_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_setfileinfo_ext( + struct cli_state *cli, + uint16_t fnum, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + return NT_STATUS_INVALID_PARAMETER; + } + + frame = talloc_stackframe(); + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_setfileinfo_ext_send( + ev, + ev, + cli, + fnum, + create_time, + access_time, + write_time, + change_time, + attr); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_setfileinfo_ext_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Send a qpathinfo call with the SMB_QUERY_FILE_ALL_INFO info level. +****************************************************************************/ + +struct cli_qpathinfo2_state { + uint32_t num_data; + uint8_t *data; +}; + +static void cli_qpathinfo2_done(struct tevent_req *subreq); + +struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_qpathinfo2_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_qpathinfo2_state); + if (req == NULL) { + return NULL; + } + subreq = cli_qpathinfo_send(state, ev, cli, fname, + SMB_QUERY_FILE_ALL_INFO, + 68, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qpathinfo2_done, req); + return req; +} + +static void cli_qpathinfo2_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qpathinfo2_state *state = tevent_req_data( + req, struct cli_qpathinfo2_state); + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &state->data, + &state->num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_qpathinfo2_recv(struct tevent_req *req, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, uint32_t *pattr, + SMB_INO_T *ino) +{ + struct cli_qpathinfo2_state *state = tevent_req_data( + req, struct cli_qpathinfo2_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (create_time) { + *create_time = interpret_long_date((char *)state->data+0); + } + if (access_time) { + *access_time = interpret_long_date((char *)state->data+8); + } + if (write_time) { + *write_time = interpret_long_date((char *)state->data+16); + } + if (change_time) { + *change_time = interpret_long_date((char *)state->data+24); + } + if (pattr) { + /* SMB_QUERY_FILE_ALL_INFO returns 32-bit attributes. */ + *pattr = IVAL(state->data, 32); + } + if (size) { + *size = IVAL2_TO_SMB_BIG_UINT(state->data,48); + } + if (ino) { + /* + * SMB1 qpathinfo2 uses SMB_QUERY_FILE_ALL_INFO + * which doesn't return an inode number (fileid). + * We can't change this to one of the FILE_ID + * info levels as only Win2003 and above support + * these [MS-SMB: 2.2.2.3.1] and the SMB1 code + * needs to support older servers. + */ + *ino = 0; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, uint32_t *pattr, + SMB_INO_T *ino) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo2(cli, + fname, + create_time, + access_time, + write_time, + change_time, + size, + pattr, + ino); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qpathinfo2_send(frame, ev, cli, fname); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_qpathinfo2_recv(req, create_time, access_time, + write_time, change_time, size, pattr, ino); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Get the stream info +****************************************************************************/ + +struct cli_qpathinfo_streams_state { + uint32_t num_data; + uint8_t *data; +}; + +static void cli_qpathinfo_streams_done(struct tevent_req *subreq); + +struct tevent_req *cli_qpathinfo_streams_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_qpathinfo_streams_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_qpathinfo_streams_state); + if (req == NULL) { + return NULL; + } + subreq = cli_qpathinfo_send(state, ev, cli, fname, + SMB_FILE_STREAM_INFORMATION, + 0, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qpathinfo_streams_done, req); + return req; +} + +static void cli_qpathinfo_streams_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qpathinfo_streams_state *state = tevent_req_data( + req, struct cli_qpathinfo_streams_state); + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &state->data, + &state->num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_qpathinfo_streams_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams) +{ + struct cli_qpathinfo_streams_state *state = tevent_req_data( + req, struct cli_qpathinfo_streams_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (!parse_streams_blob(mem_ctx, state->data, state->num_data, + pnum_streams, pstreams)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + return NT_STATUS_OK; +} + +NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo_streams(cli, + fname, + mem_ctx, + pnum_streams, + pstreams); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qpathinfo_streams_send(frame, ev, cli, fname); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_qpathinfo_streams_recv(req, mem_ctx, pnum_streams, + pstreams); + fail: + TALLOC_FREE(frame); + return status; +} + +bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata, + size_t data_len, + unsigned int *pnum_streams, + struct stream_struct **pstreams) +{ + unsigned int num_streams; + struct stream_struct *streams; + unsigned int ofs; + + num_streams = 0; + streams = NULL; + ofs = 0; + + while ((data_len > ofs) && (data_len - ofs >= 24)) { + uint32_t nlen, len; + size_t size; + void *vstr; + struct stream_struct *tmp; + uint8_t *tmp_buf; + + tmp = talloc_realloc(mem_ctx, streams, + struct stream_struct, + num_streams+1); + + if (tmp == NULL) { + goto fail; + } + streams = tmp; + + nlen = IVAL(rdata, ofs + 0x04); + + streams[num_streams].size = IVAL_TO_SMB_OFF_T( + rdata, ofs + 0x08); + streams[num_streams].alloc_size = IVAL_TO_SMB_OFF_T( + rdata, ofs + 0x10); + + if (nlen > data_len - (ofs + 24)) { + goto fail; + } + + /* + * We need to null-terminate src, how do I do this with + * convert_string_talloc?? + */ + + tmp_buf = talloc_array(streams, uint8_t, nlen+2); + if (tmp_buf == NULL) { + goto fail; + } + + memcpy(tmp_buf, rdata+ofs+24, nlen); + tmp_buf[nlen] = 0; + tmp_buf[nlen+1] = 0; + + if (!convert_string_talloc(streams, CH_UTF16, CH_UNIX, tmp_buf, + nlen+2, &vstr, &size)) + { + TALLOC_FREE(tmp_buf); + goto fail; + } + + TALLOC_FREE(tmp_buf); + streams[num_streams].name = (char *)vstr; + num_streams++; + + len = IVAL(rdata, ofs); + if (len > data_len - ofs) { + goto fail; + } + if (len == 0) break; + ofs += len; + } + + *pnum_streams = num_streams; + *pstreams = streams; + return true; + + fail: + TALLOC_FREE(streams); + return false; +} + +/**************************************************************************** + Send a qfileinfo QUERY_FILE_NAME_INFO call. +****************************************************************************/ + +struct cli_qfileinfo_basic_state { + uint32_t attr; + off_t size; + struct timespec create_time; + struct timespec access_time; + struct timespec write_time; + struct timespec change_time; + SMB_INO_T ino; +}; + +static void cli_qfileinfo_basic_done(struct tevent_req *subreq); +static void cli_qfileinfo_basic_doneE(struct tevent_req *subreq); +static void cli_qfileinfo_basic_done2(struct tevent_req *subreq); + +struct tevent_req *cli_qfileinfo_basic_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_qfileinfo_basic_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_qfileinfo_basic_state); + if (req == NULL) { + return NULL; + } + + if ((smbXcli_conn_protocol(cli->conn) < PROTOCOL_LANMAN2) || + cli->win95) { + /* + * According to + * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-cifs/3d9d8f3e-dc70-410d-a3fc-6f4a881e8cab + * SMB_COM_TRANSACTION2 used in cli_qfileinfo_send() + * further down was introduced with the LAN Manager + * 1.2 dialect, which we encode as PROTOCOL_LANMAN2. + * + * The "win95" check was introduced with commit + * 27e5850fd3e1c8 in 1998. Hard to check these days, + * but leave it in. + * + * Use a lowerlevel fallback in both cases. + */ + + subreq = cli_getattrE_send(state, ev, cli, fnum); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_qfileinfo_basic_doneE, req); + return req; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_query_info_fnum_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + fnum, /* fnum */ + 1, /* in_info_type */ + (SMB_FILE_ALL_INFORMATION - 1000), /* in_file_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + 0, /* in_additional_info */ + 0); /* in_flags */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_qfileinfo_basic_done2, req); + return req; + } + + subreq = cli_qfileinfo_send( + state, + ev, + cli, + fnum, + SMB_QUERY_FILE_ALL_INFO, /* level */ + 68, /* min_rdata */ + CLI_BUFFER_SIZE); /* max_rdata */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qfileinfo_basic_done, req); + return req; +} + +static void cli_qfileinfo_basic_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qfileinfo_basic_state *state = tevent_req_data( + req, struct cli_qfileinfo_basic_state); + uint8_t *rdata; + uint32_t num_rdata; + NTSTATUS status; + + status = cli_qfileinfo_recv( + subreq, state, NULL, &rdata, &num_rdata); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->create_time = interpret_long_date((char *)rdata+0); + state->access_time = interpret_long_date((char *)rdata+8); + state->write_time = interpret_long_date((char *)rdata+16); + state->change_time = interpret_long_date((char *)rdata+24); + state->attr = PULL_LE_U32(rdata, 32); + state->size = PULL_LE_U64(rdata,48); + state->ino = PULL_LE_U32(rdata, 64); + TALLOC_FREE(rdata); + + tevent_req_done(req); +} + +static void cli_qfileinfo_basic_doneE(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qfileinfo_basic_state *state = tevent_req_data( + req, struct cli_qfileinfo_basic_state); + NTSTATUS status; + + status = cli_getattrE_recv( + subreq, + &state->attr, + &state->size, + &state->change_time.tv_sec, + &state->access_time.tv_sec, + &state->write_time.tv_sec); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static void cli_qfileinfo_basic_done2(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qfileinfo_basic_state *state = tevent_req_data( + req, struct cli_qfileinfo_basic_state); + DATA_BLOB outbuf = {0}; + NTSTATUS status; + + status = cli_smb2_query_info_fnum_recv(subreq, state, &outbuf); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + /* Parse the reply. */ + if (outbuf.length < 0x60) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->create_time = interpret_long_date( + (const char *)outbuf.data + 0x0); + state->access_time = interpret_long_date( + (const char *)outbuf.data + 0x8); + state->write_time = interpret_long_date( + (const char *)outbuf.data + 0x10); + state->change_time = interpret_long_date( + (const char *)outbuf.data + 0x18); + state->attr = IVAL(outbuf.data, 0x20); + state->size = BVAL(outbuf.data, 0x30); + state->ino = BVAL(outbuf.data, 0x40); + + data_blob_free(&outbuf); + + tevent_req_done(req); +} + +NTSTATUS cli_qfileinfo_basic_recv( + struct tevent_req *req, + uint32_t *attr, + off_t *size, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + SMB_INO_T *ino) +{ + struct cli_qfileinfo_basic_state *state = tevent_req_data( + req, struct cli_qfileinfo_basic_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (create_time != NULL) { + *create_time = state->create_time; + } + if (access_time != NULL) { + *access_time = state->access_time; + } + if (write_time != NULL) { + *write_time = state->write_time; + } + if (change_time != NULL) { + *change_time = state->change_time; + } + if (attr != NULL) { + *attr = state->attr; + } + if (size != NULL) { + *size = state->size; + } + if (ino) { + *ino = state->ino; + } + + return NT_STATUS_OK; +} +/**************************************************************************** + Send a qfileinfo call. +****************************************************************************/ + +NTSTATUS cli_qfileinfo_basic( + struct cli_state *cli, + uint16_t fnum, + uint32_t *attr, + off_t *size, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + SMB_INO_T *ino) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qfileinfo_basic_send(frame, ev, cli, fnum); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_qfileinfo_basic_recv( + req, + attr, + size, + create_time, + access_time, + write_time, + change_time, + ino); + + /* cli_smb2_query_info_fnum_recv doesn't set this */ + cli->raw_status = status; +fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Send a qpathinfo BASIC_INFO call. +****************************************************************************/ + +struct cli_qpathinfo_basic_state { + uint32_t num_data; + uint8_t *data; +}; + +static void cli_qpathinfo_basic_done(struct tevent_req *subreq); + +struct tevent_req *cli_qpathinfo_basic_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_qpathinfo_basic_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, + struct cli_qpathinfo_basic_state); + if (req == NULL) { + return NULL; + } + subreq = cli_qpathinfo_send(state, ev, cli, fname, + SMB_QUERY_FILE_BASIC_INFO, + 36, CLI_BUFFER_SIZE); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_qpathinfo_basic_done, req); + return req; +} + +static void cli_qpathinfo_basic_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_qpathinfo_basic_state *state = tevent_req_data( + req, struct cli_qpathinfo_basic_state); + NTSTATUS status; + + status = cli_qpathinfo_recv(subreq, state, &state->data, + &state->num_data); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req, + SMB_STRUCT_STAT *sbuf, uint32_t *attributes) +{ + struct cli_qpathinfo_basic_state *state = tevent_req_data( + req, struct cli_qpathinfo_basic_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + sbuf->st_ex_btime = interpret_long_date((char *)state->data); + sbuf->st_ex_atime = interpret_long_date((char *)state->data+8); + sbuf->st_ex_mtime = interpret_long_date((char *)state->data+16); + sbuf->st_ex_ctime = interpret_long_date((char *)state->data+24); + *attributes = IVAL(state->data, 32); + return NT_STATUS_OK; +} + +NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name, + SMB_STRUCT_STAT *sbuf, uint32_t *attributes) +{ + TALLOC_CTX *frame = NULL; + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo_basic(cli, + name, + sbuf, + attributes); + } + + frame = talloc_stackframe(); + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_qpathinfo_basic_send(frame, ev, cli, name); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_qpathinfo_basic_recv(req, sbuf, attributes); + fail: + TALLOC_FREE(frame); + return status; +} + +/**************************************************************************** + Send a qpathinfo SMB_QUERY_FILE_ALT_NAME_INFO call. +****************************************************************************/ + +NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name) +{ + uint8_t *rdata; + uint32_t num_rdata; + unsigned int len; + char *converted = NULL; + size_t converted_size = 0; + NTSTATUS status; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return cli_smb2_qpathinfo_alt_name(cli, + fname, + alt_name); + } + + status = cli_qpathinfo(talloc_tos(), cli, fname, + SMB_QUERY_FILE_ALT_NAME_INFO, + 4, CLI_BUFFER_SIZE, &rdata, &num_rdata); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + len = IVAL(rdata, 0); + + if (len > num_rdata - 4) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + /* The returned data is a pushed string, not raw data. */ + if (!convert_string_talloc(talloc_tos(), + smbXcli_conn_use_unicode(cli->conn) ? CH_UTF16LE : CH_DOS, + CH_UNIX, + rdata + 4, + len, + &converted, + &converted_size)) { + return NT_STATUS_NO_MEMORY; + } + fstrcpy(alt_name, converted); + + TALLOC_FREE(converted); + TALLOC_FREE(rdata); + + return NT_STATUS_OK; +} + +/**************************************************************************** + Send a qpathinfo SMB_QUERY_FILE_STANDARD_INFO call. +****************************************************************************/ + +NTSTATUS cli_qpathinfo_standard(struct cli_state *cli, const char *fname, + uint64_t *allocated, uint64_t *size, + uint32_t *nlinks, + bool *is_del_pending, bool *is_dir) +{ + uint8_t *rdata; + uint32_t num_rdata; + NTSTATUS status; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + return NT_STATUS_NOT_IMPLEMENTED; + } + + status = cli_qpathinfo(talloc_tos(), cli, fname, + SMB_QUERY_FILE_STANDARD_INFO, + 24, CLI_BUFFER_SIZE, &rdata, &num_rdata); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (allocated) { + *allocated = BVAL(rdata, 0); + } + + if (size) { + *size = BVAL(rdata, 8); + } + + if (nlinks) { + *nlinks = IVAL(rdata, 16); + } + + if (is_del_pending) { + *is_del_pending = CVAL(rdata, 20); + } + + if (is_dir) { + *is_dir = CVAL(rdata, 20); + } + + TALLOC_FREE(rdata); + + return NT_STATUS_OK; +} + + +/* like cli_qpathinfo2 but do not use SMB_QUERY_FILE_ALL_INFO with smb1 */ +NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, uint32_t *pattr, + SMB_INO_T *ino) +{ + NTSTATUS status = NT_STATUS_OK; + SMB_STRUCT_STAT st = { 0 }; + uint32_t attr = 0; + uint64_t pos; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + /* + * NB. cli_qpathinfo2() checks pattr is valid before + * storing a value into it, so we don't need to use + * an intermediate attr variable as below but can + * pass pattr directly. + */ + return cli_qpathinfo2(cli, fname, + create_time, access_time, write_time, change_time, + size, pattr, ino); + } + + if (create_time || access_time || write_time || change_time || pattr) { + /* + * cli_qpathinfo_basic() always indirects the passed + * in pointers so we use intermediate variables to + * collect all of them before assigning any requested + * below. + */ + status = cli_qpathinfo_basic(cli, fname, &st, &attr); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (size) { + status = cli_qpathinfo_standard(cli, fname, + NULL, &pos, NULL, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *size = pos; + } + + if (create_time) { + *create_time = st.st_ex_btime; + } + if (access_time) { + *access_time = st.st_ex_atime; + } + if (write_time) { + *write_time = st.st_ex_mtime; + } + if (change_time) { + *change_time = st.st_ex_ctime; + } + if (pattr) { + *pattr = attr; + } + if (ino) { + *ino = 0; + } + + return NT_STATUS_OK; +} diff --git a/source3/libsmb/clirap.h b/source3/libsmb/clirap.h new file mode 100644 index 0000000..b2ec19c --- /dev/null +++ b/source3/libsmb/clirap.h @@ -0,0 +1,206 @@ +/* + Samba Unix/Linux SMB client library + More client RAP (SMB Remote Procedure Calls) functions + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2007 Jeremy Allison. jra@samba.org + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Gerald (Jerry) Carter 2004 + Copyright (C) James Peach 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIBSMB_CLIRAP_H +#define _LIBSMB_CLIRAP_H + +struct cli_state; + +/* The following definitions come from libsmb/clirap.c */ + +bool cli_api(struct cli_state *cli, + char *param, int prcnt, int mprcnt, + char *data, int drcnt, int mdrcnt, + char **rparam, unsigned int *rprcnt, + char **rdata, unsigned int *rdrcnt); +int cli_RNetShareEnum(struct cli_state *cli, void (*fn)(const char *, uint32_t, const char *, void *), void *state); +bool cli_NetServerEnum(struct cli_state *cli, char *workgroup, uint32_t stype, + void (*fn)(const char *, uint32_t, const char *, void *), + void *state); +bool cli_oem_change_password(struct cli_state *cli, const char *user, const char *new_password, + const char *old_password); +struct tevent_req *cli_qpathinfo1_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_qpathinfo1_recv(struct tevent_req *req, + time_t *change_time, + time_t *access_time, + time_t *write_time, + off_t *size, + uint32_t *pattr); +NTSTATUS cli_qpathinfo1(struct cli_state *cli, + const char *fname, + time_t *change_time, + time_t *access_time, + time_t *write_time, + off_t *size, + uint32_t *pattr); +NTSTATUS cli_setpathinfo_ext(struct cli_state *cli, const char *fname, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr); +struct tevent_req *cli_setfileinfo_ext_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr); +NTSTATUS cli_setfileinfo_ext_recv(struct tevent_req *req); +NTSTATUS cli_setfileinfo_ext( + struct cli_state *cli, + uint16_t fnum, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint32_t attr); +struct tevent_req *cli_qpathinfo2_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_qpathinfo2_recv(struct tevent_req *req, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, uint32_t *pattr, + SMB_INO_T *ino); +NTSTATUS cli_qpathinfo2(struct cli_state *cli, const char *fname, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, uint32_t *pattr, + SMB_INO_T *ino); +NTSTATUS cli_qpathinfo3(struct cli_state *cli, const char *fname, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + off_t *size, uint32_t *pattr, + SMB_INO_T *ino); +struct tevent_req *cli_qpathinfo_streams_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_qpathinfo_streams_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams); +NTSTATUS cli_qpathinfo_streams(struct cli_state *cli, const char *fname, + TALLOC_CTX *mem_ctx, + unsigned int *pnum_streams, + struct stream_struct **pstreams); +bool parse_streams_blob(TALLOC_CTX *mem_ctx, const uint8_t *rdata, + size_t data_len, + unsigned int *pnum_streams, + struct stream_struct **pstreams); +struct tevent_req *cli_qfileinfo_basic_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum); +NTSTATUS cli_qfileinfo_basic_recv( + struct tevent_req *req, + uint32_t *attr, + off_t *size, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + SMB_INO_T *ino); +NTSTATUS cli_qfileinfo_basic( + struct cli_state *cli, + uint16_t fnum, + uint32_t *attr, + off_t *size, + struct timespec *create_time, + struct timespec *access_time, + struct timespec *write_time, + struct timespec *change_time, + SMB_INO_T *ino); +struct tevent_req *cli_qpathinfo_basic_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_qpathinfo_basic_recv(struct tevent_req *req, + SMB_STRUCT_STAT *sbuf, uint32_t *attributes); +NTSTATUS cli_qpathinfo_basic(struct cli_state *cli, const char *name, + SMB_STRUCT_STAT *sbuf, uint32_t *attributes); +NTSTATUS cli_qpathinfo_standard(struct cli_state *cli, const char *fname, + uint64_t *allocated, uint64_t *size, + uint32_t *nlinks, + bool *is_del_pending, bool *is_dir); +NTSTATUS cli_qpathinfo_alt_name(struct cli_state *cli, const char *fname, fstring alt_name); +struct tevent_req *cli_qpathinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, const char *fname, + uint16_t level, uint32_t min_rdata, + uint32_t max_rdata); +NTSTATUS cli_qpathinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint8_t **rdata, uint32_t *num_rdata); +NTSTATUS cli_qpathinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli, + const char *fname, uint16_t level, uint32_t min_rdata, + uint32_t max_rdata, + uint8_t **rdata, uint32_t *num_rdata); + +struct tevent_req *cli_qfileinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t level, uint32_t min_rdata, + uint32_t max_rdata); +NTSTATUS cli_qfileinfo_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint16_t *recv_flags2, + uint8_t **rdata, uint32_t *num_rdata); +NTSTATUS cli_qfileinfo(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint16_t fnum, uint16_t level, uint32_t min_rdata, + uint32_t max_rdata, uint16_t *recv_flags2, + uint8_t **rdata, uint32_t *num_rdata); + +struct tevent_req *cli_flush_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum); +NTSTATUS cli_flush_recv(struct tevent_req *req); +NTSTATUS cli_flush(TALLOC_CTX *mem_ctx, struct cli_state *cli, uint16_t fnum); + +struct tevent_req *cli_shadow_copy_data_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool get_names); +NTSTATUS cli_shadow_copy_data_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char ***pnames, int *pnum_names); +NTSTATUS cli_shadow_copy_data(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint16_t fnum, bool get_names, + char ***pnames, int *pnum_names); + +#endif /* _LIBSMB_CLIRAP_H */ diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c new file mode 100644 index 0000000..1641905 --- /dev/null +++ b/source3/libsmb/clireadwrite.c @@ -0,0 +1,1907 @@ +/* + Unix SMB/CIFS implementation. + client file read/write routines + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "trans2.h" +#include "../libcli/smb/smbXcli_base.h" + +/**************************************************************************** + 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; +} + +/**************************************************************************** + 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; +} + +struct cli_read_andx_state { + size_t size; + uint16_t vwv[12]; + NTSTATUS status; + size_t received; + uint8_t *buf; +}; + +static void cli_read_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + off_t offset, size_t size, + struct tevent_req **psmbreq) +{ + struct tevent_req *req, *subreq; + struct cli_read_andx_state *state; + uint8_t wct = 10; + + req = tevent_req_create(mem_ctx, &state, struct cli_read_andx_state); + if (req == NULL) { + return NULL; + } + state->size = size; + + SCVAL(state->vwv + 0, 0, 0xFF); + SCVAL(state->vwv + 0, 1, 0); + SSVAL(state->vwv + 1, 0, 0); + SSVAL(state->vwv + 2, 0, fnum); + SIVAL(state->vwv + 3, 0, offset); + SSVAL(state->vwv + 5, 0, size); + SSVAL(state->vwv + 6, 0, size); + SSVAL(state->vwv + 7, 0, (size >> 16)); + SSVAL(state->vwv + 8, 0, 0); + SSVAL(state->vwv + 9, 0, 0); + + if (smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) { + SIVAL(state->vwv + 10, 0, + (((uint64_t)offset)>>32) & 0xffffffff); + wct = 12; + } else { + if ((((uint64_t)offset) & 0xffffffff00000000LL) != 0) { + DEBUG(10, ("cli_read_andx_send got large offset where " + "the server does not support it\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + } + + subreq = cli_smb_req_create(state, ev, cli, SMBreadX, 0, 0, wct, + state->vwv, 0, NULL); + if (subreq == NULL) { + TALLOC_FREE(req); + return NULL; + } + tevent_req_set_callback(subreq, cli_read_andx_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_read_andx_create(mem_ctx, ev, cli, fnum, offset, size, + &subreq); + if (req == NULL) { + return NULL; + } + + status = smb1cli_req_chain_submit(&subreq, 1); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + return req; +} + +static void cli_read_andx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_read_andx_state *state = tevent_req_data( + req, struct cli_read_andx_state); + uint8_t *inbuf; + uint8_t wct; + uint16_t *vwv; + uint32_t num_bytes; + uint8_t *bytes; + + state->status = cli_smb_recv(subreq, state, &inbuf, 12, &wct, &vwv, + &num_bytes, &bytes); + TALLOC_FREE(subreq); + if (NT_STATUS_IS_ERR(state->status)) { + tevent_req_nterror(req, state->status); + return; + } + + /* size is the number of bytes the server returned. + * Might be zero. */ + state->received = SVAL(vwv + 5, 0); + state->received |= (((unsigned int)SVAL(vwv + 7, 0)) << 16); + + if (state->received > state->size) { + DEBUG(5,("server returned more than we wanted!\n")); + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } + + /* + * bcc field must be valid for small reads, for large reads the 16-bit + * bcc field can't be correct. + */ + + if ((state->received < 0xffff) && (state->received > num_bytes)) { + DEBUG(5, ("server announced more bytes than sent\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + state->buf = discard_const_p(uint8_t, smb_base(inbuf)) + SVAL(vwv+6, 0); + + if (smb_buffer_oob(smb_len_tcp(inbuf), SVAL(vwv+6, 0), state->received) + || ((state->received != 0) && (state->buf < bytes))) { + DEBUG(5, ("server returned invalid read&x data offset\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + tevent_req_done(req); +} + +/* + * Pull the data out of a finished async read_and_x request. rcvbuf is + * talloced from the request, so better make sure that you copy it away before + * you talloc_free(req). "rcvbuf" is NOT a talloc_ctx of its own, so do not + * talloc_move it! + */ + +NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received, + uint8_t **rcvbuf) +{ + struct cli_read_andx_state *state = tevent_req_data( + req, struct cli_read_andx_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *received = state->received; + *rcvbuf = state->buf; + return NT_STATUS_OK; +} + +struct cli_pull_chunk; + +struct cli_pull_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t fnum; + off_t start_offset; + off_t size; + + NTSTATUS (*sink)(char *buf, size_t n, void *priv); + void *priv; + + size_t chunk_size; + off_t next_offset; + off_t remaining; + + /* + * How many bytes did we push into "sink"? + */ + off_t pushed; + + /* + * Outstanding requests + * + * The maximum is 256: + * - which would be a window of 256 MByte + * for SMB2 with multi-credit + * or smb1 unix extensions. + */ + uint16_t max_chunks; + uint16_t num_chunks; + uint16_t num_waiting; + struct cli_pull_chunk *chunks; +}; + +struct cli_pull_chunk { + struct cli_pull_chunk *prev, *next; + struct tevent_req *req;/* This is the main request! Not the subreq */ + struct tevent_req *subreq; + off_t ofs; + uint8_t *buf; + size_t total_size; + size_t tmp_size; + bool done; +}; + +static void cli_pull_setup_chunks(struct tevent_req *req); +static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk); +static void cli_pull_chunk_done(struct tevent_req *subreq); + +/* + * Parallel read support. + * + * cli_pull sends as many read&x requests as the server would allow via + * max_mux at a time. When replies flow back in, the data is written into + * the callback function "sink" in the right order. + */ + +struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, off_t start_offset, + off_t size, size_t window_size, + NTSTATUS (*sink)(char *buf, size_t n, + void *priv), + void *priv) +{ + struct tevent_req *req; + struct cli_pull_state *state; + size_t page_size = 1024; + uint64_t tmp64; + + req = tevent_req_create(mem_ctx, &state, struct cli_pull_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->ev = ev; + state->fnum = fnum; + state->start_offset = start_offset; + state->size = size; + state->sink = sink; + state->priv = priv; + state->next_offset = start_offset; + state->remaining = size; + + if (size == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + state->chunk_size = smb2cli_conn_max_read_size(cli->conn); + } else { + state->chunk_size = cli_read_max_bufsize(cli); + } + if (state->chunk_size > page_size) { + state->chunk_size &= ~(page_size - 1); + } + + if (window_size == 0) { + /* + * We use 16 MByte as default window size. + */ + window_size = 16 * 1024 * 1024; + } + + tmp64 = window_size/state->chunk_size; + if ((window_size % state->chunk_size) > 0) { + tmp64 += 1; + } + tmp64 = MAX(tmp64, 1); + tmp64 = MIN(tmp64, 256); + state->max_chunks = tmp64; + + /* + * We defer the callback because of the complex + * substate/subfunction logic + */ + tevent_req_defer_callback(req, ev); + + cli_pull_setup_chunks(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void cli_pull_setup_chunks(struct tevent_req *req) +{ + struct cli_pull_state *state = + tevent_req_data(req, + struct cli_pull_state); + struct cli_pull_chunk *chunk, *next = NULL; + size_t i; + + for (chunk = state->chunks; chunk; chunk = next) { + /* + * Note that chunk might be removed from this call. + */ + next = chunk->next; + cli_pull_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } + } + + for (i = state->num_chunks; i < state->max_chunks; i++) { + + if (state->num_waiting > 0) { + return; + } + + if (state->remaining == 0) { + break; + } + + chunk = talloc_zero(state, struct cli_pull_chunk); + if (tevent_req_nomem(chunk, req)) { + return; + } + chunk->req = req; + chunk->ofs = state->next_offset; + chunk->total_size = MIN(state->remaining, state->chunk_size); + state->next_offset += chunk->total_size; + state->remaining -= chunk->total_size; + + DLIST_ADD_END(state->chunks, chunk); + state->num_chunks++; + state->num_waiting++; + + cli_pull_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } + } + + if (state->remaining > 0) { + return; + } + + if (state->num_chunks > 0) { + return; + } + + tevent_req_done(req); +} + +static void cli_pull_chunk_ship(struct cli_pull_chunk *chunk) +{ + struct tevent_req *req = chunk->req; + struct cli_pull_state *state = + tevent_req_data(req, + struct cli_pull_state); + bool ok; + off_t ofs; + size_t size; + + if (chunk->done) { + NTSTATUS status; + + if (chunk != state->chunks) { + /* + * this chunk is not the + * first one in the list. + * + * which means we should not + * push it into the sink yet. + */ + return; + } + + if (chunk->tmp_size == 0) { + /* + * we got a short read, we're done + */ + tevent_req_done(req); + return; + } + + status = state->sink((char *)chunk->buf, + chunk->tmp_size, + state->priv); + if (tevent_req_nterror(req, status)) { + return; + } + state->pushed += chunk->tmp_size; + + if (chunk->tmp_size < chunk->total_size) { + /* + * we got a short read, we're done + */ + tevent_req_done(req); + return; + } + + DLIST_REMOVE(state->chunks, chunk); + SMB_ASSERT(state->num_chunks > 0); + state->num_chunks--; + TALLOC_FREE(chunk); + + return; + } + + if (chunk->subreq != NULL) { + return; + } + + SMB_ASSERT(state->num_waiting > 0); + + ofs = chunk->ofs + chunk->tmp_size; + size = chunk->total_size - chunk->tmp_size; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + uint32_t max_size; + + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (!ok) { + return; + } + + /* + * downgrade depending on the available credits + */ + size = MIN(max_size, size); + + chunk->subreq = cli_smb2_read_send(chunk, + state->ev, + state->cli, + state->fnum, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + } else { + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + return; + } + + chunk->subreq = cli_read_andx_send(chunk, + state->ev, + state->cli, + state->fnum, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + } + tevent_req_set_callback(chunk->subreq, + cli_pull_chunk_done, + chunk); + + state->num_waiting--; + return; +} + +static void cli_pull_chunk_done(struct tevent_req *subreq) +{ + struct cli_pull_chunk *chunk = + tevent_req_callback_data(subreq, + struct cli_pull_chunk); + struct tevent_req *req = chunk->req; + struct cli_pull_state *state = + tevent_req_data(req, + struct cli_pull_state); + NTSTATUS status; + size_t expected = chunk->total_size - chunk->tmp_size; + ssize_t received = 0; + uint8_t *buf = NULL; + + chunk->subreq = NULL; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_read_recv(subreq, &received, &buf); + } else { + status = cli_read_andx_recv(subreq, &received, &buf); + } + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { + received = 0; + status = NT_STATUS_OK; + } + if (tevent_req_nterror(req, status)) { + return; + } + + if (received > expected) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (received == 0) { + /* + * We got EOF we're done + */ + chunk->done = true; + cli_pull_setup_chunks(req); + return; + } + + if (received == chunk->total_size) { + /* + * We got it in the first run. + * + * We don't call TALLOC_FREE(subreq) + * here and keep the returned buffer. + */ + chunk->buf = buf; + } else if (chunk->buf == NULL) { + chunk->buf = talloc_array(chunk, uint8_t, chunk->total_size); + if (tevent_req_nomem(chunk->buf, req)) { + return; + } + } + + if (received != chunk->total_size) { + uint8_t *p = chunk->buf + chunk->tmp_size; + memcpy(p, buf, received); + TALLOC_FREE(subreq); + } + + chunk->tmp_size += received; + + if (chunk->tmp_size == chunk->total_size) { + chunk->done = true; + } else { + state->num_waiting++; + } + + cli_pull_setup_chunks(req); +} + +NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received) +{ + struct cli_pull_state *state = tevent_req_data( + req, struct cli_pull_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + *received = state->pushed; + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, + off_t start_offset, off_t size, size_t window_size, + NTSTATUS (*sink)(char *buf, size_t n, void *priv), + void *priv, off_t *received) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_pull_send(frame, ev, cli, fnum, start_offset, size, + window_size, sink, priv); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_pull_recv(req, received); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_read_state { + struct cli_state *cli; + char *buf; + size_t buflen; + size_t received; +}; + +static void cli_read_done(struct tevent_req *subreq); + +struct tevent_req *cli_read_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + char *buf, + off_t offset, + size_t size) +{ + struct tevent_req *req, *subreq; + struct cli_read_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_read_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->buf = buf; + state->buflen = size; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + uint32_t max_size; + bool ok; + + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (!ok) { + tevent_req_nterror( + req, + NT_STATUS_INSUFFICIENT_RESOURCES); + return tevent_req_post(req, ev); + } + + /* + * downgrade depending on the available credits + */ + size = MIN(max_size, size); + + subreq = cli_smb2_read_send( + state, ev, cli, fnum, offset, size); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } else { + bool ok; + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + tevent_req_nterror( + req, + NT_STATUS_INSUFFICIENT_RESOURCES); + return tevent_req_post(req, ev); + } + + subreq = cli_read_andx_send( + state, ev, cli, fnum, offset, size); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } + + tevent_req_set_callback(subreq, cli_read_done, req); + + return req; +} + +static void cli_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_read_state *state = tevent_req_data( + req, struct cli_read_state); + NTSTATUS status; + ssize_t received; + uint8_t *buf = NULL; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_read_recv(subreq, &received, &buf); + } else { + status = cli_read_andx_recv(subreq, &received, &buf); + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) { + received = 0; + status = NT_STATUS_OK; + } + if (tevent_req_nterror(req, status)) { + return; + } + if ((buf == NULL) || (received < 0) || (received > state->buflen)) { + state->received = 0; + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } + + memcpy(state->buf, buf, received); + state->received = received; + tevent_req_done(req); +} + +NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received) +{ + struct cli_read_state *state = tevent_req_data( + req, struct cli_read_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (received != NULL) { + *received = state->received; + } + return NT_STATUS_OK; +} + +/* + * Helper function for cli_pull(). This takes a chunk of data (buf) read from + * a remote file and copies it into the return buffer (priv). + */ +NTSTATUS cli_read_sink(char *buf, size_t n, void *priv) +{ + char **pbuf = (char **)priv; + memcpy(*pbuf, buf, n); + *pbuf += n; + return NT_STATUS_OK; +} + +NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum, + char *buf, off_t offset, size_t size, + size_t *nread) +{ + NTSTATUS status; + off_t ret = 0; + + status = cli_pull(cli, fnum, offset, size, size, + cli_read_sink, &buf, &ret); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (nread) { + *nread = ret; + } + + return NT_STATUS_OK; +} + +/**************************************************************************** + write to a file using a SMBwrite and not bypassing 0 byte writes +****************************************************************************/ + +NTSTATUS cli_smbwrite(struct cli_state *cli, uint16_t fnum, char *buf, + off_t offset, size_t size1, size_t *ptotal) +{ + uint8_t *bytes; + ssize_t total = 0; + + /* + * 3 bytes prefix + */ + + bytes = talloc_array(talloc_tos(), uint8_t, 3); + if (bytes == NULL) { + return NT_STATUS_NO_MEMORY; + } + bytes[0] = 1; + + do { + uint32_t usable_space = cli_state_available_size(cli, 48); + size_t size = MIN(size1, usable_space); + struct tevent_req *req; + uint16_t vwv[5]; + uint16_t *ret_vwv; + NTSTATUS status; + + SSVAL(vwv+0, 0, fnum); + SSVAL(vwv+1, 0, size); + SIVAL(vwv+2, 0, offset); + SSVAL(vwv+4, 0, 0); + + bytes = talloc_realloc(talloc_tos(), bytes, uint8_t, + size+3); + if (bytes == NULL) { + return NT_STATUS_NO_MEMORY; + } + SSVAL(bytes, 1, size); + memcpy(bytes + 3, buf + total, size); + + status = cli_smb(talloc_tos(), cli, SMBwrite, 0, 5, vwv, + size+3, bytes, &req, 1, NULL, &ret_vwv, + NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(bytes); + return status; + } + + size = SVAL(ret_vwv+0, 0); + TALLOC_FREE(req); + if (size == 0) { + break; + } + size1 -= size; + total += size; + offset += size; + + } while (size1); + + TALLOC_FREE(bytes); + + if (ptotal != NULL) { + *ptotal = total; + } + return NT_STATUS_OK; +} + +/* + * Send a write&x request + */ + +struct cli_write_andx_state { + size_t size; + uint16_t vwv[14]; + size_t written; + uint8_t pad; + struct iovec iov[2]; +}; + +static void cli_write_andx_done(struct tevent_req *subreq); + +struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size, + struct tevent_req **reqs_before, + int num_reqs_before, + struct tevent_req **psmbreq) +{ + struct tevent_req *req, *subreq; + struct cli_write_andx_state *state; + bool bigoffset = ((smb1cli_conn_capabilities(cli->conn) & CAP_LARGE_FILES) != 0); + uint8_t wct = bigoffset ? 14 : 12; + size_t max_write = cli_write_max_bufsize(cli, mode, wct); + uint16_t *vwv; + + req = tevent_req_create(mem_ctx, &state, struct cli_write_andx_state); + if (req == NULL) { + return NULL; + } + + state->size = MIN(size, max_write); + + vwv = state->vwv; + + SCVAL(vwv+0, 0, 0xFF); + SCVAL(vwv+0, 1, 0); + SSVAL(vwv+1, 0, 0); + SSVAL(vwv+2, 0, fnum); + SIVAL(vwv+3, 0, offset); + SIVAL(vwv+5, 0, 0); + SSVAL(vwv+7, 0, mode); + SSVAL(vwv+8, 0, 0); + SSVAL(vwv+9, 0, (state->size>>16)); + SSVAL(vwv+10, 0, state->size); + + SSVAL(vwv+11, 0, + smb1cli_req_wct_ofs(reqs_before, num_reqs_before) + + 1 /* the wct field */ + + wct * 2 /* vwv */ + + 2 /* num_bytes field */ + + 1 /* pad */); + + if (bigoffset) { + SIVAL(vwv+12, 0, (((uint64_t)offset)>>32) & 0xffffffff); + } + + state->pad = 0; + state->iov[0].iov_base = (void *)&state->pad; + state->iov[0].iov_len = 1; + state->iov[1].iov_base = discard_const_p(void, buf); + state->iov[1].iov_len = state->size; + + subreq = cli_smb_req_create(state, ev, cli, SMBwriteX, 0, 0, wct, vwv, + 2, state->iov); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_write_andx_done, req); + *psmbreq = subreq; + return req; +} + +struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; + NTSTATUS status; + + req = cli_write_andx_create(mem_ctx, ev, cli, fnum, mode, buf, offset, + size, NULL, 0, &subreq); + if (req == NULL) { + return NULL; + } + + status = smb1cli_req_chain_submit(&subreq, 1); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + return req; +} + +static void cli_write_andx_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_write_andx_state *state = tevent_req_data( + req, struct cli_write_andx_state); + uint8_t wct; + uint16_t *vwv; + NTSTATUS status; + + status = cli_smb_recv(subreq, state, NULL, 6, &wct, &vwv, + NULL, NULL); + TALLOC_FREE(subreq); + if (NT_STATUS_IS_ERR(status)) { + tevent_req_nterror(req, status); + return; + } + state->written = SVAL(vwv+2, 0); + if (state->size > UINT16_MAX) { + /* + * It is important that we only set the + * high bits only if we asked for a large write. + * + * OS/2 print shares get this wrong and may send + * invalid values. + * + * See bug #5326. + */ + state->written |= SVAL(vwv+4, 0)<<16; + } + tevent_req_done(req); +} + +NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten) +{ + struct cli_write_andx_state *state = tevent_req_data( + req, struct cli_write_andx_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (pwritten != 0) { + *pwritten = state->written; + } + return NT_STATUS_OK; +} + +struct cli_write_state { + struct cli_state *cli; + size_t written; +}; + +static void cli_write_done(struct tevent_req *subreq); + +/* + * Used to write to a file remotely. + * This is similar in functionality to cli_push_send(), except this is a more + * finer-grain API. For example, if the data we want to write exceeds the max + * write size of the underlying connection, then it's the caller's + * responsibility to handle this. + * For writing a small amount of data to file, this is a simpler API to use. + */ +struct tevent_req *cli_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size) +{ + struct tevent_req *req = NULL; + struct cli_write_state *state = NULL; + struct tevent_req *subreq = NULL; + + req = tevent_req_create(mem_ctx, &state, struct cli_write_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + uint32_t max_size; + bool ok; + + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (!ok) { + tevent_req_nterror( + req, + NT_STATUS_INSUFFICIENT_RESOURCES); + return tevent_req_post(req, ev); + } + + /* + * downgrade depending on the available credits + */ + size = MIN(max_size, size); + + subreq = cli_smb2_write_send(state, + ev, + cli, + fnum, + mode, + buf, + offset, + size); + } else { + bool ok; + + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + tevent_req_nterror( + req, + NT_STATUS_INSUFFICIENT_RESOURCES); + return tevent_req_post(req, ev); + } + + subreq = cli_write_andx_send(state, + ev, + cli, + fnum, + mode, + buf, + offset, + size); + } + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_write_done, req); + + return req; +} + +static void cli_write_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + struct cli_write_state *state = + tevent_req_data(req, + struct cli_write_state); + NTSTATUS status; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_write_recv(subreq, &state->written); + } else { + status = cli_write_andx_recv(subreq, &state->written); + } + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_write_recv(struct tevent_req *req, size_t *pwritten) +{ + struct cli_write_state *state = + tevent_req_data(req, + struct cli_write_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + if (pwritten != NULL) { + *pwritten = state->written; + } + tevent_req_received(req); + return NT_STATUS_OK; +} + +struct cli_smb1_writeall_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t fnum; + uint16_t mode; + const uint8_t *buf; + off_t offset; + size_t size; + size_t written; +}; + +static void cli_smb1_writeall_written(struct tevent_req *req); + +static struct tevent_req *cli_smb1_writeall_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, size_t size) +{ + struct tevent_req *req, *subreq; + struct cli_smb1_writeall_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_smb1_writeall_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->fnum = fnum; + state->mode = mode; + state->buf = buf; + state->offset = offset; + state->size = size; + state->written = 0; + + subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, + state->mode, state->buf, state->offset, + state->size); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_smb1_writeall_written, req); + return req; +} + +static void cli_smb1_writeall_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_smb1_writeall_state *state = tevent_req_data( + req, struct cli_smb1_writeall_state); + NTSTATUS status; + size_t written = 0, to_write; + + status = cli_write_andx_recv(subreq, &written); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->written += written; + + if (state->written > state->size) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + to_write = state->size - state->written; + + if (to_write == 0) { + tevent_req_done(req); + return; + } + + subreq = cli_write_andx_send(state, state->ev, state->cli, state->fnum, + state->mode, + state->buf + state->written, + state->offset + state->written, to_write); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_smb1_writeall_written, req); +} + +static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req, + size_t *pwritten) +{ + struct cli_smb1_writeall_state *state = tevent_req_data( + req, struct cli_smb1_writeall_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (pwritten != NULL) { + *pwritten = state->written; + } + return NT_STATUS_OK; +} + +struct cli_writeall_state { + struct cli_state *cli; + size_t written; +}; + +static void cli_writeall_done(struct tevent_req *subreq); + +struct tevent_req *cli_writeall_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size) +{ + struct tevent_req *req, *subreq; + struct cli_writeall_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_writeall_send( + state, + ev, + cli, + fnum, + mode, + buf, + offset, + size); + } else { + subreq = cli_smb1_writeall_send( + state, + ev, + cli, + fnum, + mode, + buf, + offset, + size); + } + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_writeall_done, req); + + return req; +} + +static void cli_writeall_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_writeall_state *state = tevent_req_data( + req, struct cli_writeall_state); + NTSTATUS status; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_writeall_recv(subreq, &state->written); + } else { + status = cli_smb1_writeall_recv(subreq, &state->written); + } + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten) +{ + struct cli_writeall_state *state = tevent_req_data( + req, struct cli_writeall_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (pwritten != NULL) { + *pwritten = state->written; + } + return NT_STATUS_OK; +} + + +NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode, + const uint8_t *buf, off_t offset, size_t size, + size_t *pwritten) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_writeall_recv(req, pwritten); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_push_chunk; + +struct cli_push_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t fnum; + uint16_t mode; + off_t start_offset; + + size_t (*source)(uint8_t *buf, size_t n, void *priv); + void *priv; + + bool eof; + + size_t chunk_size; + off_t next_offset; + + /* + * Outstanding requests + * + * The maximum is 256: + * - which would be a window of 256 MByte + * for SMB2 with multi-credit + * or smb1 unix extensions. + */ + uint16_t max_chunks; + uint16_t num_chunks; + uint16_t num_waiting; + struct cli_push_chunk *chunks; +}; + +struct cli_push_chunk { + struct cli_push_chunk *prev, *next; + struct tevent_req *req;/* This is the main request! Not the subreq */ + struct tevent_req *subreq; + off_t ofs; + uint8_t *buf; + size_t total_size; + size_t tmp_size; + bool done; +}; + +static void cli_push_setup_chunks(struct tevent_req *req); +static void cli_push_chunk_ship(struct cli_push_chunk *chunk); +static void cli_push_chunk_done(struct tevent_req *subreq); + +/* + * Used to write to a file remotely. + * This is similar in functionality to cli_write_send(), except this API + * handles writing a large file by breaking the data into chunks (so we don't + * exceed the max write size of the underlying connection). To do this, the + * (*source) callback handles copying the underlying file data into a message + * buffer, one chunk at a time. + * This API is recommended when writing a potentially large amount of data, + * e.g. when copying a file (or doing a 'put'). + */ +struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, + void *priv), + void *priv) +{ + struct tevent_req *req; + struct cli_push_state *state; + size_t page_size = 1024; + uint64_t tmp64; + + req = tevent_req_create(mem_ctx, &state, struct cli_push_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + state->ev = ev; + state->fnum = fnum; + state->start_offset = start_offset; + state->mode = mode; + state->source = source; + state->priv = priv; + state->next_offset = start_offset; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + state->chunk_size = smb2cli_conn_max_write_size(cli->conn); + } else { + state->chunk_size = cli_write_max_bufsize(cli, mode, 14); + } + if (state->chunk_size > page_size) { + state->chunk_size &= ~(page_size - 1); + } + + if (window_size == 0) { + /* + * We use 16 MByte as default window size. + */ + window_size = 16 * 1024 * 1024; + } + + tmp64 = window_size/state->chunk_size; + if ((window_size % state->chunk_size) > 0) { + tmp64 += 1; + } + tmp64 = MAX(tmp64, 1); + tmp64 = MIN(tmp64, 256); + state->max_chunks = tmp64; + + /* + * We defer the callback because of the complex + * substate/subfunction logic + */ + tevent_req_defer_callback(req, ev); + + cli_push_setup_chunks(req); + if (!tevent_req_is_in_progress(req)) { + return tevent_req_post(req, ev); + } + + return req; +} + +static void cli_push_setup_chunks(struct tevent_req *req) +{ + struct cli_push_state *state = + tevent_req_data(req, + struct cli_push_state); + struct cli_push_chunk *chunk, *next = NULL; + size_t i; + + for (chunk = state->chunks; chunk; chunk = next) { + /* + * Note that chunk might be removed from this call. + */ + next = chunk->next; + cli_push_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } + } + + for (i = state->num_chunks; i < state->max_chunks; i++) { + + if (state->num_waiting > 0) { + return; + } + + if (state->eof) { + break; + } + + chunk = talloc_zero(state, struct cli_push_chunk); + if (tevent_req_nomem(chunk, req)) { + return; + } + chunk->req = req; + chunk->ofs = state->next_offset; + chunk->buf = talloc_array(chunk, + uint8_t, + state->chunk_size); + if (tevent_req_nomem(chunk->buf, req)) { + return; + } + chunk->total_size = state->source(chunk->buf, + state->chunk_size, + state->priv); + if (chunk->total_size == 0) { + /* nothing to send */ + talloc_free(chunk); + state->eof = true; + break; + } + state->next_offset += chunk->total_size; + + DLIST_ADD_END(state->chunks, chunk); + state->num_chunks++; + state->num_waiting++; + + cli_push_chunk_ship(chunk); + if (!tevent_req_is_in_progress(req)) { + return; + } + } + + if (!state->eof) { + return; + } + + if (state->num_chunks > 0) { + return; + } + + tevent_req_done(req); +} + +static void cli_push_chunk_ship(struct cli_push_chunk *chunk) +{ + struct tevent_req *req = chunk->req; + struct cli_push_state *state = + tevent_req_data(req, + struct cli_push_state); + bool ok; + const uint8_t *buf; + off_t ofs; + size_t size; + + if (chunk->done) { + DLIST_REMOVE(state->chunks, chunk); + SMB_ASSERT(state->num_chunks > 0); + state->num_chunks--; + TALLOC_FREE(chunk); + + return; + } + + if (chunk->subreq != NULL) { + return; + } + + SMB_ASSERT(state->num_waiting > 0); + + buf = chunk->buf + chunk->tmp_size; + ofs = chunk->ofs + chunk->tmp_size; + size = chunk->total_size - chunk->tmp_size; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + uint32_t max_size; + + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size); + if (!ok) { + return; + } + + /* + * downgrade depending on the available credits + */ + size = MIN(max_size, size); + + chunk->subreq = cli_smb2_write_send(chunk, + state->ev, + state->cli, + state->fnum, + state->mode, + buf, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + } else { + ok = smb1cli_conn_req_possible(state->cli->conn); + if (!ok) { + return; + } + + chunk->subreq = cli_write_andx_send(chunk, + state->ev, + state->cli, + state->fnum, + state->mode, + buf, + ofs, + size); + if (tevent_req_nomem(chunk->subreq, req)) { + return; + } + } + tevent_req_set_callback(chunk->subreq, + cli_push_chunk_done, + chunk); + + state->num_waiting--; + return; +} + +static void cli_push_chunk_done(struct tevent_req *subreq) +{ + struct cli_push_chunk *chunk = + tevent_req_callback_data(subreq, + struct cli_push_chunk); + struct tevent_req *req = chunk->req; + struct cli_push_state *state = + tevent_req_data(req, + struct cli_push_state); + NTSTATUS status; + size_t expected = chunk->total_size - chunk->tmp_size; + size_t written; + + chunk->subreq = NULL; + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + status = cli_smb2_write_recv(subreq, &written); + } else { + status = cli_write_andx_recv(subreq, &written); + } + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (written > expected) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + if (written == 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_NETWORK_RESPONSE); + return; + } + + chunk->tmp_size += written; + + if (chunk->tmp_size == chunk->total_size) { + chunk->done = true; + } else { + state->num_waiting++; + } + + cli_push_setup_chunks(req); +} + +NTSTATUS cli_push_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, void *priv), + void *priv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_OK; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + req = cli_push_send(frame, ev, cli, fnum, mode, start_offset, + window_size, source, priv); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + + status = cli_push_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +#define SPLICE_BLOCK_SIZE 1024 * 1024 + +static NTSTATUS cli_splice_fallback(TALLOC_CTX *frame, + struct cli_state *srccli, + struct cli_state *dstcli, + uint16_t src_fnum, uint16_t dst_fnum, + off_t initial_size, + off_t src_offset, off_t dst_offset, + off_t *written, + int (*splice_cb)(off_t n, void *priv), + void *priv) +{ + NTSTATUS status; + uint8_t *buf = talloc_size(frame, SPLICE_BLOCK_SIZE); + size_t nread; + off_t remaining = initial_size; + *written = 0; + + while (remaining) { + size_t to_read = MIN(remaining, SPLICE_BLOCK_SIZE); + + status = cli_read(srccli, src_fnum, + (char *)buf, src_offset, to_read, + &nread); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = cli_writeall(dstcli, dst_fnum, 0, + buf, dst_offset, nread, NULL); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if ((src_offset > INT64_MAX - nread) || + (dst_offset > INT64_MAX - nread)) { + return NT_STATUS_FILE_TOO_LARGE; + } + src_offset += nread; + dst_offset += nread; + *written += nread; + if (remaining < nread) { + return NT_STATUS_INTERNAL_ERROR; + } + remaining -= nread; + if (!splice_cb(initial_size - remaining, priv)) { + return NT_STATUS_CANCELLED; + } + } + + return NT_STATUS_OK; +} + +NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli, + uint16_t src_fnum, uint16_t dst_fnum, + off_t size, + off_t src_offset, off_t dst_offset, + off_t *written, + int (*splice_cb)(off_t n, void *priv), void *priv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + bool retry_fallback = false; + + if (smbXcli_conn_has_async_calls(srccli->conn) || + smbXcli_conn_has_async_calls(dstcli->conn)) + { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + do { + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto out; + } + if (srccli == dstcli && + smbXcli_conn_protocol(srccli->conn) >= PROTOCOL_SMB2_02 && + !retry_fallback) + { + req = cli_smb2_splice_send(frame, ev, + srccli, src_fnum, dst_fnum, + size, src_offset, dst_offset, + splice_cb, priv); + } else { + status = cli_splice_fallback(frame, + srccli, dstcli, + src_fnum, dst_fnum, + size, + src_offset, dst_offset, + written, + splice_cb, priv); + goto out; + } + if (req == NULL) { + goto out; + } + if (!tevent_req_poll(req, ev)) { + status = map_nt_error_from_unix(errno); + goto out; + } + status = cli_smb2_splice_recv(req, written); + + /* + * Older versions of Samba don't support + * FSCTL_SRV_COPYCHUNK_WRITE so use the fallback. + */ + retry_fallback = NT_STATUS_EQUAL(status, NT_STATUS_INVALID_DEVICE_REQUEST); + } while (retry_fallback); + + out: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/clisecdesc.c b/source3/libsmb/clisecdesc.c new file mode 100644 index 0000000..0a5708f --- /dev/null +++ b/source3/libsmb/clisecdesc.c @@ -0,0 +1,372 @@ +/* + Unix SMB/CIFS implementation. + client security descriptor functions + Copyright (C) Andrew Tridgell 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../libcli/security/secdesc.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/util/tevent_ntstatus.h" + +struct cli_query_security_descriptor_state { + uint8_t param[8]; + DATA_BLOB outbuf; +}; + +static void cli_query_security_descriptor_done1(struct tevent_req *subreq); +static void cli_query_security_descriptor_done2(struct tevent_req *subreq); + +struct tevent_req *cli_query_security_descriptor_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_query_security_descriptor_state *state = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct cli_query_security_descriptor_state); + if (req == NULL) { + return NULL; + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_query_info_fnum_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + fnum, /* fnum */ + 3, /* in_info_type */ + 0, /* in_info_class */ + 0xFFFF, /* in_max_output_length */ + NULL, /* in_input_buffer */ + sec_info, /* in_additional_info */ + 0); /* in_flags */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_query_security_descriptor_done2, req); + return req; + } + + PUSH_LE_U32(state->param, 0, fnum); + PUSH_LE_U32(state->param, 4, sec_info); + + subreq = cli_trans_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + 0, /* additional_flags2 */ + SMBnttrans, /* cmd */ + NULL, /* pipe_name */ + -1, /* fid */ + NT_TRANSACT_QUERY_SECURITY_DESC, /* function */ + 0, /* flags */ + NULL, /* setup */ + 0, /* num_setup */ + 0, /* max_setup */ + state->param, /* param */ + 8, /* num_param */ + 4, /* max_param */ + NULL, /* data */ + 0, /* num_data */ + 0x10000); /* max_data */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_query_security_descriptor_done1, req); + return req; +} + +static void cli_query_security_descriptor_done1(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_query_security_descriptor_state *state = tevent_req_data( + req, struct cli_query_security_descriptor_state); + NTSTATUS status; + uint32_t len; + + status = cli_trans_recv( + subreq, /* req */ + state, /* mem_ctx */ + NULL, /* recv_flags2 */ + NULL, /* setup */ + 0, /* min_setup */ + NULL, /* num_setup */ + NULL, /* param */ + 0, /* min_param */ + NULL, /* num_param */ + &state->outbuf.data, /* data */ + 0, /* min_data */ + &len); /* num_data */ + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + state->outbuf.length = len; /* uint32_t -> size_t */ + tevent_req_done(req); +} + +static void cli_query_security_descriptor_done2(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_query_security_descriptor_state *state = tevent_req_data( + req, struct cli_query_security_descriptor_state); + NTSTATUS status; + + status = cli_smb2_query_info_fnum_recv(subreq, state, &state->outbuf); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_query_security_descriptor_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct security_descriptor **sd) +{ + struct cli_query_security_descriptor_state *state = tevent_req_data( + req, struct cli_query_security_descriptor_state); + NTSTATUS status = NT_STATUS_OK; + + if (tevent_req_is_nterror(req, &status)) { + goto done; + } + if (sd != NULL) { + status = unmarshall_sec_desc( + mem_ctx, state->outbuf.data, state->outbuf.length, sd); + } +done: + tevent_req_received(req); + return status; +} + +NTSTATUS cli_query_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **sd) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_query_security_descriptor_send( + frame, ev, cli, fnum, sec_info); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_query_security_descriptor_recv(req, mem_ctx, sd); + fail: + TALLOC_FREE(frame); + return status; +} + +NTSTATUS cli_query_secdesc(struct cli_state *cli, uint16_t fnum, + TALLOC_CTX *mem_ctx, struct security_descriptor **sd) +{ + uint32_t sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; + + return cli_query_security_descriptor(cli, fnum, sec_info, mem_ctx, sd); +} + +NTSTATUS cli_query_mxac(struct cli_state *cli, + const char *filename, + uint32_t *mxac) +{ + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_SMB2_02) { + return NT_STATUS_NOT_SUPPORTED; + } + + return cli_smb2_query_mxac(cli, filename, mxac); +} + +struct cli_set_security_descriptor_state { + uint8_t param[8]; + DATA_BLOB buf; +}; + +static void cli_set_security_descriptor_done1(struct tevent_req *subreq); +static void cli_set_security_descriptor_done2(struct tevent_req *subreq); + +struct tevent_req *cli_set_security_descriptor_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd) +{ + struct tevent_req *req = NULL, *subreq = NULL; + struct cli_set_security_descriptor_state *state = NULL; + NTSTATUS status; + + req = tevent_req_create( + mem_ctx, &state, struct cli_set_security_descriptor_state); + if (req == NULL) { + return NULL; + } + + status = marshall_sec_desc( + state, sd, &state->buf.data, &state->buf.length); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_set_info_fnum_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + fnum, /* fnum */ + 3, /* in_info_type */ + 0, /* in_file_info_class */ + &state->buf, /* in_input_buffer */ + sec_info); /* in_additional_info */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_set_security_descriptor_done2, req); + return req; + } + + SIVAL(state->param, 0, fnum); + SIVAL(state->param, 4, sec_info); + + subreq = cli_trans_send( + state, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + 0, /* additional_flags2 */ + SMBnttrans, /* cmd */ + NULL, /* pipe_name */ + -1, /* fid */ + NT_TRANSACT_SET_SECURITY_DESC, /* function */ + 0, /* flags */ + NULL, /* setup */ + 0, /* num_setup */ + 0, /* max_setup */ + state->param, /* param */ + 8, /* num_param */ + 0, /* max_param */ + state->buf.data, /* data */ + state->buf.length, /* num_data */ + 0); /* max_data */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + subreq, cli_set_security_descriptor_done1, req); + return req; +} + +static void cli_set_security_descriptor_done1(struct tevent_req *subreq) +{ + NTSTATUS status = cli_trans_recv( + subreq, NULL, NULL, NULL, 0, NULL, NULL, 0, NULL, + NULL, 0, NULL); + return tevent_req_simple_finish_ntstatus(subreq, status); +} + +static void cli_set_security_descriptor_done2(struct tevent_req *subreq) +{ + NTSTATUS status = cli_smb2_set_info_fnum_recv(subreq); + tevent_req_simple_finish_ntstatus(subreq, status); +} + +NTSTATUS cli_set_security_descriptor_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +/**************************************************************************** + set the security descriptor for a open file + ****************************************************************************/ +NTSTATUS cli_set_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_set_security_descriptor_send( + frame, ev, cli, fnum, sec_info, sd); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_set_security_descriptor_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +NTSTATUS cli_set_secdesc(struct cli_state *cli, uint16_t fnum, + const struct security_descriptor *sd) +{ + uint32_t sec_info = 0; + + if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) { + sec_info |= SECINFO_DACL; + } + if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) { + sec_info |= SECINFO_SACL; + } + if (sd->owner_sid) { + sec_info |= SECINFO_OWNER; + } + if (sd->group_sid) { + sec_info |= SECINFO_GROUP; + } + + return cli_set_security_descriptor(cli, fnum, sec_info, sd); +} diff --git a/source3/libsmb/clispnego.c b/source3/libsmb/clispnego.c new file mode 100644 index 0000000..1608f6a --- /dev/null +++ b/source3/libsmb/clispnego.c @@ -0,0 +1,205 @@ +/* + Unix SMB/CIFS implementation. + simple kerberos5/SPNEGO routines + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002 + Copyright (C) Luke Howard 2003 + Copyright (C) Jeremy Allison 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../libcli/auth/spnego.h" +#include "smb_krb5.h" +#include "../lib/util/asn1.h" + +/* + parse a negTokenInit packet giving a GUID, a list of supported + OIDs (the mechanisms) and a principal name string +*/ +bool spnego_parse_negTokenInit(TALLOC_CTX *ctx, + DATA_BLOB blob, + char *OIDs[ASN1_MAX_OIDS], + char **principal, + DATA_BLOB *secblob) +{ + int i; + bool ret = false; + ASN1_DATA *data; + + for (i = 0; i < ASN1_MAX_OIDS; i++) { + OIDs[i] = NULL; + } + + if (principal) { + *principal = NULL; + } + if (secblob) { + *secblob = data_blob_null; + } + + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); + if (data == NULL) { + return false; + } + + if (!asn1_load(data, blob)) goto err; + + if (!asn1_start_tag(data,ASN1_APPLICATION(0))) goto err; + + if (!asn1_check_OID(data,OID_SPNEGO)) goto err; + + /* negTokenInit [0] NegTokenInit */ + if (!asn1_start_tag(data,ASN1_CONTEXT(0))) goto err; + if (!asn1_start_tag(data,ASN1_SEQUENCE(0))) goto err; + + /* mechTypes [0] MechTypeList OPTIONAL */ + + /* Not really optional, we depend on this to decide + * what mechanisms we have to work with. */ + + if (!asn1_start_tag(data,ASN1_CONTEXT(0))) goto err; + if (!asn1_start_tag(data,ASN1_SEQUENCE(0))) goto err; + for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) { + if (!asn1_read_OID(data,ctx, &OIDs[i])) { + goto err; + } + if (asn1_has_error(data)) { + goto err; + } + } + OIDs[i] = NULL; + if (!asn1_end_tag(data)) goto err; + if (!asn1_end_tag(data)) goto err; + + /* + Win7 + Live Sign-in Assistant attaches a mechToken + ASN1_CONTEXT(2) to the negTokenInit packet + which breaks our negotiation if we just assume + the next tag is ASN1_CONTEXT(3). + */ + + if (asn1_peek_tag(data, ASN1_CONTEXT(1))) { + uint8_t flags; + + /* reqFlags [1] ContextFlags OPTIONAL */ + if (!asn1_start_tag(data, ASN1_CONTEXT(1))) goto err; + if (!asn1_start_tag(data, ASN1_BIT_STRING)) goto err; + while (asn1_tag_remaining(data) > 0) { + if (!asn1_read_uint8(data, &flags)) goto err; + } + if (!asn1_end_tag(data)) goto err; + if (!asn1_end_tag(data)) goto err; + } + + if (asn1_peek_tag(data, ASN1_CONTEXT(2))) { + DATA_BLOB sblob = data_blob_null; + /* mechToken [2] OCTET STRING OPTIONAL */ + if (!asn1_start_tag(data, ASN1_CONTEXT(2))) goto err; + if (!asn1_read_OctetString(data, ctx, &sblob)) goto err; + if (!asn1_end_tag(data)) { + data_blob_free(&sblob); + goto err; + } + if (secblob) { + *secblob = sblob; + } else { + data_blob_free(&sblob); + } + } + + if (asn1_peek_tag(data, ASN1_CONTEXT(3))) { + char *princ = NULL; + /* mechListMIC [3] OCTET STRING OPTIONAL */ + if (!asn1_start_tag(data, ASN1_CONTEXT(3))) goto err; + if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto err; + if (!asn1_start_tag(data, ASN1_CONTEXT(0))) goto err; + if (!asn1_read_GeneralString(data, ctx, &princ)) goto err; + if (!asn1_end_tag(data)) goto err; + if (!asn1_end_tag(data)) goto err; + if (!asn1_end_tag(data)) goto err; + if (principal) { + *principal = princ; + } else { + TALLOC_FREE(princ); + } + } + + if (!asn1_end_tag(data)) goto err; + if (!asn1_end_tag(data)) goto err; + + if (!asn1_end_tag(data)) goto err; + + ret = !asn1_has_error(data); + + err: + + if (asn1_has_error(data)) { + int j; + if (principal) { + TALLOC_FREE(*principal); + } + if (secblob) { + data_blob_free(secblob); + } + for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) { + TALLOC_FREE(OIDs[j]); + } + } + + asn1_free(data); + return ret; +} + +/* + generate a krb5 GSS-API wrapper packet given a ticket +*/ +DATA_BLOB spnego_gen_krb5_wrap(TALLOC_CTX *ctx, const DATA_BLOB ticket, const uint8_t tok_id[2]) +{ + ASN1_DATA *data; + DATA_BLOB ret = data_blob_null; + + data = asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH); + if (data == NULL) { + return data_blob_null; + } + + if (!asn1_push_tag(data, ASN1_APPLICATION(0))) goto err; + if (!asn1_write_OID(data, OID_KERBEROS5)) goto err; + + if (!asn1_write(data, tok_id, 2)) goto err; + if (!asn1_write(data, ticket.data, ticket.length)) goto err; + if (!asn1_pop_tag(data)) goto err; + + if (!asn1_extract_blob(data, ctx, &ret)) { + goto err; + } + + asn1_free(data); + data = NULL; + + err: + + if (data != NULL) { + if (asn1_has_error(data)) { + DEBUG(1, ("Failed to build krb5 wrapper at offset %d\n", + (int)asn1_current_ofs(data))); + } + + asn1_free(data); + } + + return ret; +} diff --git a/source3/libsmb/clistr.c b/source3/libsmb/clistr.c new file mode 100644 index 0000000..1e5641a --- /dev/null +++ b/source3/libsmb/clistr.c @@ -0,0 +1,65 @@ +/* + Unix SMB/CIFS implementation. + client string routines + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/proto.h" + +bool clistr_is_previous_version_path(const char *path, + const char **startp, + const char **endp, + time_t *ptime) +{ + char *q; + time_t timestamp; + struct tm tm; + const char *p = strstr_m(path, "@GMT-"); + + if (p == NULL) { + return false; + } + if (p > path && (p[-1] != '\\')) { + return false; + } + q = strptime(p, GMT_FORMAT, &tm); + if (q == NULL) { + return false; + } + tm.tm_isdst = -1; + timestamp = timegm(&tm); + if (timestamp == (time_t)-1) { + return false; + } + if (q[0] != '\0' && q[0] != '\\') { + return false; + } + if (startp) { + *startp = p; + } + if (endp) { + if (q[0] == '\\') { + q++; + } + *endp = q; + } + if (ptime) { + *ptime = timestamp; + } + return true; +} diff --git a/source3/libsmb/clisymlink.c b/source3/libsmb/clisymlink.c new file mode 100644 index 0000000..d4b269c --- /dev/null +++ b/source3/libsmb/clisymlink.c @@ -0,0 +1,441 @@ +/* + * Unix SMB/CIFS implementation. + * Client implementation of setting symlinks using reparse points + * Copyright (C) Volker Lendecke 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "libsmb/clirap.h" +#include "trans2.h" +#include "libcli/security/secdesc.h" +#include "libcli/security/security.h" +#include "../libcli/smb/smbXcli_base.h" +#include "libcli/smb/reparse_symlink.h" + +struct cli_symlink_state { + struct tevent_context *ev; + struct cli_state *cli; + const char *link_target; + const char *newpath; + uint32_t flags; + + uint16_t fnum; + + uint16_t setup[4]; + NTSTATUS set_reparse_status; +}; + +static void cli_symlink_create_done(struct tevent_req *subreq); +static void cli_symlink_set_reparse_done(struct tevent_req *subreq); +static void cli_symlink_delete_on_close_done(struct tevent_req *subreq); +static void cli_symlink_close_done(struct tevent_req *subreq); + +struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *link_target, + const char *newpath, + uint32_t flags) +{ + struct tevent_req *req, *subreq; + struct cli_symlink_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_symlink_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->link_target = link_target; + state->newpath = newpath; + state->flags = flags; + + subreq = cli_ntcreate_send( + state, ev, cli, state->newpath, 0, + SYNCHRONIZE_ACCESS|DELETE_ACCESS| + FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, FILE_SHARE_NONE, FILE_CREATE, + FILE_OPEN_REPARSE_POINT|FILE_SYNCHRONOUS_IO_NONALERT| + FILE_NON_DIRECTORY_FILE, + SMB2_IMPERSONATION_IMPERSONATION, 0); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_symlink_create_done, req); + return req; +} + +static void cli_symlink_create_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_symlink_state *state = tevent_req_data( + req, struct cli_symlink_state); + DATA_BLOB data; + NTSTATUS status; + + status = cli_ntcreate_recv(subreq, &state->fnum, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (!symlink_reparse_buffer_marshall( + state->link_target, NULL, state->flags, state, + &data.data, &data.length)) { + tevent_req_oom(req); + return; + } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_set_reparse_point_fnum_send(state, + state->ev, + state->cli, + state->fnum, + data); + } else { + SIVAL(state->setup, 0, FSCTL_SET_REPARSE_POINT); + SSVAL(state->setup, 4, state->fnum); + SCVAL(state->setup, 6, 1); /* IsFcntl */ + SCVAL(state->setup, 7, 0); /* IsFlags */ + + + subreq = cli_trans_send(state, state->ev, state->cli, 0, + SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_IOCTL, 0, + state->setup, 4, 0, /* setup */ + NULL, 0, 0, /* param */ + data.data, data.length, 0); /* data */ + } + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_symlink_set_reparse_done, req); +} + +static void cli_symlink_set_reparse_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_symlink_state *state = tevent_req_data( + req, struct cli_symlink_state); + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + state->set_reparse_status = + cli_smb2_set_reparse_point_fnum_recv(subreq); + } else { + state->set_reparse_status = cli_trans_recv( + subreq, NULL, NULL, + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + NULL, 0, NULL); /* rdata */ + } + TALLOC_FREE(subreq); + + if (NT_STATUS_IS_OK(state->set_reparse_status)) { + subreq = cli_close_send(state, state->ev, state->cli, + state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_symlink_close_done, req); + return; + } + subreq = cli_nt_delete_on_close_send( + state, state->ev, state->cli, state->fnum, true); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_symlink_delete_on_close_done, req); +} + +static void cli_symlink_delete_on_close_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_symlink_state *state = tevent_req_data( + req, struct cli_symlink_state); + + /* + * Ignore status, we can't do much anyway in case of failure + */ + + (void)cli_nt_delete_on_close_recv(subreq); + TALLOC_FREE(subreq); + + subreq = cli_close_send(state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_symlink_close_done, req); +} + +static void cli_symlink_close_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_symlink_state *state = tevent_req_data( + req, struct cli_symlink_state); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + + if (tevent_req_nterror(req, status)) { + return; + } + if (tevent_req_nterror(req, state->set_reparse_status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_symlink_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +NTSTATUS cli_symlink(struct cli_state *cli, const char *link_target, + const char *newname, uint32_t flags) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_symlink_send(frame, ev, cli, link_target, newname, flags); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_symlink_recv(req); + fail: + TALLOC_FREE(frame); + return status; +} + +struct cli_readlink_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t fnum; + + uint16_t setup[4]; + NTSTATUS get_reparse_status; + uint8_t *data; + uint32_t num_data; +}; + +static void cli_readlink_opened(struct tevent_req *subreq); +static void cli_readlink_got_reparse_data(struct tevent_req *subreq); +static void cli_readlink_closed(struct tevent_req *subreq); + +struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname) +{ + struct tevent_req *req, *subreq; + struct cli_readlink_state *state; + + req = tevent_req_create(mem_ctx, &state, struct cli_readlink_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + subreq = cli_ntcreate_send( + state, ev, cli, fname, 0, FILE_READ_ATTRIBUTES | FILE_READ_EA, + 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + FILE_OPEN, FILE_OPEN_REPARSE_POINT, + SMB2_IMPERSONATION_IMPERSONATION, 0); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_readlink_opened, req); + return req; +} + +static void cli_readlink_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_readlink_state *state = tevent_req_data( + req, struct cli_readlink_state); + NTSTATUS status; + + status = cli_ntcreate_recv(subreq, &state->fnum, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + subreq = cli_smb2_get_reparse_point_fnum_send(state, + state->ev, + state->cli, + state->fnum); + } else { + SIVAL(state->setup, 0, FSCTL_GET_REPARSE_POINT); + SSVAL(state->setup, 4, state->fnum); + SCVAL(state->setup, 6, 1); /* IsFcntl */ + SCVAL(state->setup, 7, 0); /* IsFlags */ + + subreq = cli_trans_send(state, state->ev, state->cli, + 0, SMBnttrans, + NULL, -1, /* name, fid */ + NT_TRANSACT_IOCTL, 0, + state->setup, 4, 0, /* setup */ + NULL, 0, 0, /* param */ + NULL, 0, 16384); /* data */ + } + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_readlink_got_reparse_data, req); +} + +static void cli_readlink_got_reparse_data(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_readlink_state *state = tevent_req_data( + req, struct cli_readlink_state); + + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) { + DATA_BLOB recv_data; + state->get_reparse_status = + cli_smb2_get_reparse_point_fnum_recv(subreq, + state, + &recv_data); + if (NT_STATUS_IS_OK(state->get_reparse_status)) { + state->data = recv_data.data; + state->num_data = recv_data.length; + } + } else { + state->get_reparse_status = cli_trans_recv( + subreq, state, NULL, + NULL, 0, NULL, /* rsetup */ + NULL, 0, NULL, /* rparam */ + &state->data, 20, &state->num_data); /* rdata */ + } + TALLOC_FREE(subreq); + + subreq = cli_close_send(state, state->ev, state->cli, state->fnum); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_readlink_closed, req); +} + +static void cli_readlink_closed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **psubstitute_name, char **pprint_name, + uint32_t *pflags) +{ + struct cli_readlink_state *state = tevent_req_data( + req, struct cli_readlink_state); + struct symlink_reparse_struct *symlink = NULL; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + symlink = symlink_reparse_buffer_parse( + talloc_tos(), state->data, state->num_data); + if (symlink == NULL) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (psubstitute_name != NULL) { + *psubstitute_name = talloc_move( + mem_ctx, &symlink->substitute_name); + } + + if (pprint_name != NULL) { + *pprint_name = talloc_move(mem_ctx, &symlink->print_name); + } + + if (pflags != NULL) { + *pflags = symlink->flags; + } + + TALLOC_FREE(symlink); + + return NT_STATUS_OK; +} + +NTSTATUS cli_readlink(struct cli_state *cli, const char *fname, + TALLOC_CTX *mem_ctx, char **psubstitute_name, + char **pprint_name, uint32_t *pflags) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_readlink_send(frame, ev, cli, fname); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_readlink_recv(req, mem_ctx, psubstitute_name, + pprint_name, pflags); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/clitrans.c b/source3/libsmb/clitrans.c new file mode 100644 index 0000000..e5b4c4a --- /dev/null +++ b/source3/libsmb/clitrans.c @@ -0,0 +1,224 @@ +/* + Unix SMB/CIFS implementation. + client transaction calls + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../lib/util/tevent_ntstatus.h" +#include "async_smb.h" +#include "../libcli/smb/smbXcli_base.h" + +struct cli_trans_state { + struct cli_state *cli; + struct tevent_req *subreq; + uint16_t recv_flags2; + uint16_t *setup; + uint8_t num_setup; + uint8_t *param; + uint32_t num_param; + uint8_t *data; + uint32_t num_data; +}; + +static void cli_trans_done(struct tevent_req *subreq); +static bool cli_trans_cancel(struct tevent_req *req); + +struct tevent_req *cli_trans_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, uint16_t additional_flags2, uint8_t cmd, + const char *pipe_name, uint16_t fid, uint16_t function, int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data) +{ + struct tevent_req *req; + struct cli_trans_state *state; + uint8_t additional_flags = 0; + uint8_t clear_flags = 0; + uint16_t clear_flags2 = 0; + + req = tevent_req_create(mem_ctx, &state, struct cli_trans_state); + if (req == NULL) { + return NULL; + } + state->cli = cli; + + state->subreq = smb1cli_trans_send(state, ev, + cli->conn, cmd, + additional_flags, clear_flags, + additional_flags2, clear_flags2, + cli->timeout, + cli->smb1.pid, + cli->smb1.tcon, + cli->smb1.session, + pipe_name, fid, function, flags, + setup, num_setup, max_setup, + param, num_param, max_param, + data, num_data, max_data); + if (tevent_req_nomem(state->subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->subreq, cli_trans_done, req); + tevent_req_set_cancel_fn(req, cli_trans_cancel); + return req; +} + +static bool cli_trans_cancel(struct tevent_req *req) +{ + struct cli_trans_state *state = tevent_req_data( + req, struct cli_trans_state); + bool ok; + + ok = tevent_req_cancel(state->subreq); + return ok; +} + +static void cli_trans_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_trans_state *state = tevent_req_data( + req, struct cli_trans_state); + NTSTATUS status; + + status = smb1cli_trans_recv( + subreq, + state, + &state->recv_flags2, + &state->setup, 0, &state->num_setup, + &state->param, 0, &state->num_param, + &state->data, 0, &state->num_data); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint16_t *recv_flags2, + uint16_t **setup, uint8_t min_setup, + uint8_t *num_setup, + uint8_t **param, uint32_t min_param, + uint32_t *num_param, + uint8_t **data, uint32_t min_data, + uint32_t *num_data) +{ + struct cli_trans_state *state = tevent_req_data( + req, struct cli_trans_state); + NTSTATUS status = NT_STATUS_OK; + bool map_dos_errors = true; + + if (tevent_req_is_nterror(req, &status)) { + goto map_error; + } + + if ((state->num_setup < min_setup) || + (state->num_param < min_param) || + (state->num_data < min_data)) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + if (recv_flags2 != NULL) { + *recv_flags2 = state->recv_flags2; + } + if (setup != NULL) { + *setup = talloc_move(mem_ctx, &state->setup); + *num_setup = state->num_setup; + } + if (param != NULL) { + *param = talloc_move(mem_ctx, &state->param); + *num_param = state->num_param; + } + if (data != NULL) { + *data = talloc_move(mem_ctx, &state->data); + *num_data = state->num_data; + } + +map_error: + map_dos_errors = state->cli->map_dos_errors; + state->cli->raw_status = status; + + if (NT_STATUS_IS_DOS(status) && map_dos_errors) { + uint8_t eclass = NT_STATUS_DOS_CLASS(status); + uint16_t ecode = NT_STATUS_DOS_CODE(status); + /* + * TODO: is it really a good idea to do a mapping here? + * + * The old cli_pull_error() also does it, so I do not change + * the behavior yet. + */ + status = dos_to_ntstatus(eclass, ecode); + } + + return status; +} + +NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t trans_cmd, + const char *pipe_name, uint16_t fid, uint16_t function, + int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data, + uint16_t *recv_flags2, + uint16_t **rsetup, uint8_t min_rsetup, uint8_t *num_rsetup, + uint8_t **rparam, uint32_t min_rparam, uint32_t *num_rparam, + uint8_t **rdata, uint32_t min_rdata, uint32_t *num_rdata) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + if (smbXcli_conn_has_async_calls(cli->conn)) { + /* + * Can't use sync call while an async call is in flight + */ + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = cli_trans_send( + frame, /* mem_ctx */ + ev, /* ev */ + cli, /* cli */ + 0, /* additional_flags2 */ + trans_cmd, /* cmd */ + pipe_name, fid, function, flags, + setup, num_setup, max_setup, + param, num_param, max_param, + data, num_data, max_data); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = cli_trans_recv( + req, mem_ctx, recv_flags2, + rsetup, min_rsetup, num_rsetup, + rparam, min_rparam, num_rparam, + rdata, min_rdata, num_rdata); +fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/conncache.c b/source3/libsmb/conncache.c new file mode 100644 index 0000000..d5a865f --- /dev/null +++ b/source3/libsmb/conncache.c @@ -0,0 +1,227 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon connection manager + + Copyright (C) Tim Potter 2001 + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Gerald (Jerry) Carter 2003 + Copyright (C) Marc VanHeyningen 2008 + Copyright (C) Volker Lendecke 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#include "lib/gencache.h" + +/** + * @file + * Negative connection cache implemented in terms of gencache API + * + * The negative connection cache stores names of servers which have + * been unresponsive so that we don't waste time repeatedly trying + * to contact them. It used to use an in-memory linked list, but + * this limited its utility to a single process + */ + + +/** + * Marshalls the domain and server name into the key for the gencache + * record + * + * @param[in] domain required + * @param[in] server may be a FQDN or an IP address + * @return the resulting string, which the caller is responsible for + * SAFE_FREE()ing + * @retval NULL returned on error + */ +static char *negative_conn_cache_keystr(const char *domain, const char *server) +{ + char *keystr = NULL; + + if (domain == NULL) { + return NULL; + } + if (server == NULL) + server = ""; + + keystr = talloc_asprintf(talloc_tos(), "NEG_CONN_CACHE/%s,%s", + domain, server); + if (keystr == NULL) { + DEBUG(0, ("negative_conn_cache_keystr: malloc error\n")); + } + + return keystr; +} + +/** + * Marshalls the NT status into a printable value field for the gencache + * record + * + * @param[in] status + * @return the resulting string, which the caller is responsible for + * SAFE_FREE()ing + * @retval NULL returned on error + */ +static char *negative_conn_cache_valuestr(NTSTATUS status) +{ + char *valuestr = NULL; + + valuestr = talloc_asprintf(talloc_tos(), "%x", NT_STATUS_V(status)); + if (valuestr == NULL) { + DEBUG(0, ("negative_conn_cache_valuestr: malloc error\n")); + } + + return valuestr; +} + +/** + * Un-marshalls the NT status from a printable field for the gencache + * record + * + * @param[in] value The value field from the record + * @return the decoded NT status + * @retval NT_STATUS_OK returned on error + */ +static NTSTATUS negative_conn_cache_valuedecode(const char *value) +{ + unsigned int v = NT_STATUS_V(NT_STATUS_INTERNAL_ERROR); + + if (value == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + if (sscanf(value, "%x", &v) != 1) { + DEBUG(0, ("negative_conn_cache_valuedecode: unable to parse " + "value field '%s'\n", value)); + } + return NT_STATUS(v); +} + +/** + * Function passed to gencache_iterate to remove any matching items + * from the list + * + * @param[in] key Key to the record found and to be deleted + * @param[in] value Value to the record (ignored) + * @param[in] timeout Timeout remaining for the record (ignored) + * @param[in] dptr Handle for passing additional data (ignored) + */ +static void delete_matches(const char *key, const char *value, + time_t timeout, void *dptr) +{ + gencache_del(key); +} + + +/** + * Checks for a given domain/server record in the negative cache + * + * @param[in] domain + * @param[in] server may be either a FQDN or an IP address + * @return The cached failure status + * @retval NT_STATUS_OK returned if no record is found or an error occurs + */ +NTSTATUS check_negative_conn_cache( const char *domain, const char *server) +{ + NTSTATUS result = NT_STATUS_OK; + char *key = NULL; + char *value = NULL; + + key = negative_conn_cache_keystr(domain, server); + if (key == NULL) + goto done; + + if (gencache_get(key, talloc_tos(), &value, NULL)) + result = negative_conn_cache_valuedecode(value); + done: + DEBUG(9,("check_negative_conn_cache returning result %d for domain %s " + "server %s\n", NT_STATUS_V(result), domain, server)); + TALLOC_FREE(key); + TALLOC_FREE(value); + return result; +} + +/** + * Add an entry to the failed connection cache + * + * @param[in] domain + * @param[in] server may be a FQDN or an IP addr in printable form + * @param[in] result error to cache; must not be NT_STATUS_OK + */ +void add_failed_connection_entry(const char *domain, const char *server, + NTSTATUS result) +{ + char *key = NULL; + char *value = NULL; + + if (NT_STATUS_IS_OK(result)) { + /* Nothing failed here */ + return; + } + + key = negative_conn_cache_keystr(domain, server); + if (key == NULL) { + DEBUG(0, ("add_failed_connection_entry: key creation error\n")); + goto done; + } + + value = negative_conn_cache_valuestr(result); + if (value == NULL) { + DEBUG(0, ("add_failed_connection_entry: value creation error\n")); + goto done; + } + + if (gencache_set(key, value, + time(NULL) + FAILED_CONNECTION_CACHE_TIMEOUT)) + DEBUG(9,("add_failed_connection_entry: added domain %s (%s) " + "to failed conn cache\n", domain, server )); + else + DEBUG(1,("add_failed_connection_entry: failed to add " + "domain %s (%s) to failed conn cache\n", + domain, server)); + + done: + TALLOC_FREE(key); + TALLOC_FREE(value); + return; +} + +/** + * Deletes all records for a specified domain from the negative connection + * cache + * + * @param[in] domain String to match against domain portion of keys, or "*" + * to match all domains + */ +void flush_negative_conn_cache_for_domain(const char *domain) +{ + char *key_pattern = NULL; + + key_pattern = negative_conn_cache_keystr(domain,"*"); + if (key_pattern == NULL) { + DEBUG(0, ("flush_negative_conn_cache_for_domain: " + "key creation error\n")); + goto done; + } + + gencache_iterate(delete_matches, NULL, key_pattern); + DEBUG(8, ("flush_negative_conn_cache_for_domain: flushed domain %s\n", + domain)); + + done: + TALLOC_FREE(key_pattern); + return; +} diff --git a/source3/libsmb/dsgetdcname.c b/source3/libsmb/dsgetdcname.c new file mode 100644 index 0000000..e0462d5 --- /dev/null +++ b/source3/libsmb/dsgetdcname.c @@ -0,0 +1,1274 @@ +/* + Unix SMB/CIFS implementation. + + dsgetdcname + + Copyright (C) Gerald Carter 2006 + Copyright (C) Guenther Deschner 2007-2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/dsgetdcname.h" +#include "libsmb/namequery.h" +#include "libads/sitename_cache.h" +#include "../librpc/gen_ndr/ndr_netlogon.h" +#include "libads/cldap.h" +#include "lib/addns/dnsquery_srv.h" +#include "libsmb/clidgram.h" +#include "lib/gencache.h" +#include "lib/util/util_net.h" + +/* 15 minutes */ +#define DSGETDCNAME_CACHE_TTL 60*15 + +struct ip_service_name { + struct samba_sockaddr sa; + const char *hostname; +}; + +static NTSTATUS make_dc_info_from_cldap_reply( + TALLOC_CTX *mem_ctx, + uint32_t flags, + const struct samba_sockaddr *sa, + struct NETLOGON_SAM_LOGON_RESPONSE_EX *r, + struct netr_DsRGetDCNameInfo **info); + +/**************************************************************** +****************************************************************/ + +static void debug_dsdcinfo_flags(int lvl, uint32_t flags) +{ + DEBUG(lvl,("debug_dsdcinfo_flags: 0x%08x\n\t", flags)); + + if (flags & DS_FORCE_REDISCOVERY) + DEBUGADD(lvl,("DS_FORCE_REDISCOVERY ")); + if (flags & 0x000000002) + DEBUGADD(lvl,("0x00000002 ")); + if (flags & 0x000000004) + DEBUGADD(lvl,("0x00000004 ")); + if (flags & 0x000000008) + DEBUGADD(lvl,("0x00000008 ")); + if (flags & DS_DIRECTORY_SERVICE_REQUIRED) + DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_REQUIRED ")); + if (flags & DS_DIRECTORY_SERVICE_PREFERRED) + DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_PREFERRED ")); + if (flags & DS_GC_SERVER_REQUIRED) + DEBUGADD(lvl,("DS_GC_SERVER_REQUIRED ")); + if (flags & DS_PDC_REQUIRED) + DEBUGADD(lvl,("DS_PDC_REQUIRED ")); + if (flags & DS_BACKGROUND_ONLY) + DEBUGADD(lvl,("DS_BACKGROUND_ONLY ")); + if (flags & DS_IP_REQUIRED) + DEBUGADD(lvl,("DS_IP_REQUIRED ")); + if (flags & DS_KDC_REQUIRED) + DEBUGADD(lvl,("DS_KDC_REQUIRED ")); + if (flags & DS_TIMESERV_REQUIRED) + DEBUGADD(lvl,("DS_TIMESERV_REQUIRED ")); + if (flags & DS_WRITABLE_REQUIRED) + DEBUGADD(lvl,("DS_WRITABLE_REQUIRED ")); + if (flags & DS_GOOD_TIMESERV_PREFERRED) + DEBUGADD(lvl,("DS_GOOD_TIMESERV_PREFERRED ")); + if (flags & DS_AVOID_SELF) + DEBUGADD(lvl,("DS_AVOID_SELF ")); + if (flags & DS_ONLY_LDAP_NEEDED) + DEBUGADD(lvl,("DS_ONLY_LDAP_NEEDED ")); + if (flags & DS_IS_FLAT_NAME) + DEBUGADD(lvl,("DS_IS_FLAT_NAME ")); + if (flags & DS_IS_DNS_NAME) + DEBUGADD(lvl,("DS_IS_DNS_NAME ")); + if (flags & DS_TRY_NEXTCLOSEST_SITE) + DEBUGADD(lvl,("DS_TRY_NEXTCLOSEST_SITE ")); + if (flags & DS_DIRECTORY_SERVICE_6_REQUIRED) + DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_6_REQUIRED ")); + if (flags & DS_WEB_SERVICE_REQUIRED) + DEBUGADD(lvl,("DS_WEB_SERVICE_REQUIRED ")); + if (flags & DS_DIRECTORY_SERVICE_8_REQUIRED) + DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_8_REQUIRED ")); + if (flags & DS_DIRECTORY_SERVICE_9_REQUIRED) + DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_9_REQUIRED ")); + if (flags & DS_DIRECTORY_SERVICE_10_REQUIRED) + DEBUGADD(lvl,("DS_DIRECTORY_SERVICE_10_REQUIRED ")); + if (flags & 0x01000000) + DEBUGADD(lvl,("0x01000000 ")); + if (flags & 0x02000000) + DEBUGADD(lvl,("0x02000000 ")); + if (flags & 0x04000000) + DEBUGADD(lvl,("0x04000000 ")); + if (flags & 0x08000000) + DEBUGADD(lvl,("0x08000000 ")); + if (flags & 0x10000000) + DEBUGADD(lvl,("0x10000000 ")); + if (flags & 0x20000000) + DEBUGADD(lvl,("0x20000000 ")); + if (flags & DS_RETURN_DNS_NAME) + DEBUGADD(lvl,("DS_RETURN_DNS_NAME ")); + if (flags & DS_RETURN_FLAT_NAME) + DEBUGADD(lvl,("DS_RETURN_FLAT_NAME ")); + if (flags) + DEBUGADD(lvl,("\n")); +} + +/**************************************************************** +****************************************************************/ + +static char *dsgetdcname_cache_key(TALLOC_CTX *mem_ctx, const char *domain) +{ + if (!domain) { + return NULL; + } + + return talloc_asprintf_strupper_m(mem_ctx, "DSGETDCNAME/DOMAIN/%s", + domain); +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS dsgetdcname_cache_delete(TALLOC_CTX *mem_ctx, + const char *domain_name) +{ + char *key; + + key = dsgetdcname_cache_key(mem_ctx, domain_name); + if (!key) { + return NT_STATUS_NO_MEMORY; + } + + if (!gencache_del(key)) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS dsgetdcname_cache_store(TALLOC_CTX *mem_ctx, + const char *domain_name, + DATA_BLOB blob) +{ + time_t expire_time; + char *key; + bool ret = false; + + key = dsgetdcname_cache_key(mem_ctx, domain_name); + if (!key) { + return NT_STATUS_NO_MEMORY; + } + + expire_time = time(NULL) + DSGETDCNAME_CACHE_TTL; + + ret = gencache_set_data_blob(key, blob, expire_time); + + return ret ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS store_cldap_reply(TALLOC_CTX *mem_ctx, + uint32_t flags, + struct samba_sockaddr *sa, + uint32_t nt_version, + struct NETLOGON_SAM_LOGON_RESPONSE_EX *r) +{ + DATA_BLOB blob; + enum ndr_err_code ndr_err; + NTSTATUS status; + char addr[INET6_ADDRSTRLEN]; + + print_sockaddr(addr, sizeof(addr), &sa->u.ss); + + /* FIXME */ + r->sockaddr_size = 0x10; /* the w32 winsock addr size */ + r->sockaddr.sockaddr_family = 2; /* AF_INET */ + r->sockaddr.pdc_ip = talloc_strdup(mem_ctx, addr); + + ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r, + (ndr_push_flags_fn_t)ndr_push_NETLOGON_SAM_LOGON_RESPONSE_EX); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return ndr_map_error2ntstatus(ndr_err); + } + + if (r->domain_name) { + status = dsgetdcname_cache_store(mem_ctx, r->domain_name, + blob); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (r->client_site) { + sitename_store(r->domain_name, r->client_site); + } + } + if (r->dns_domain) { + status = dsgetdcname_cache_store(mem_ctx, r->dns_domain, blob); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (r->client_site) { + sitename_store(r->dns_domain, r->client_site); + } + } + + status = NT_STATUS_OK; + + done: + data_blob_free(&blob); + + return status; +} + +/**************************************************************** +****************************************************************/ + +static uint32_t get_cldap_reply_server_flags(struct netlogon_samlogon_response *r, + uint32_t nt_version) +{ + switch (nt_version & 0x0000001f) { + case 0: + case 1: + case 16: + case 17: + return 0; + case 2: + case 3: + case 18: + case 19: + return r->data.nt5.server_type; + case 4: + case 5: + case 6: + case 7: + return r->data.nt5_ex.server_type; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + return r->data.nt5_ex.server_type; + case 20: + case 21: + case 22: + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + return r->data.nt5_ex.server_type; + case 29: + case 30: + case 31: + return r->data.nt5_ex.server_type; + default: + return 0; + } +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS dsgetdcname_cache_fetch(TALLOC_CTX *mem_ctx, + const char *domain_name, + const struct GUID *domain_guid, + uint32_t flags, + struct netr_DsRGetDCNameInfo **info_p) +{ + char *key; + DATA_BLOB blob; + enum ndr_err_code ndr_err; + struct netr_DsRGetDCNameInfo *info; + struct NETLOGON_SAM_LOGON_RESPONSE_EX r; + NTSTATUS status; + + key = dsgetdcname_cache_key(mem_ctx, domain_name); + if (!key) { + return NT_STATUS_NO_MEMORY; + } + + if (!gencache_get_data_blob(key, NULL, &blob, NULL, NULL)) { + return NT_STATUS_NOT_FOUND; + } + + info = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo); + if (!info) { + data_blob_free(&blob); + return NT_STATUS_NO_MEMORY; + } + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_NETLOGON_SAM_LOGON_RESPONSE_EX); + + data_blob_free(&blob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + dsgetdcname_cache_delete(mem_ctx, domain_name); + return ndr_map_error2ntstatus(ndr_err); + } + + status = make_dc_info_from_cldap_reply(mem_ctx, flags, NULL, + &r, &info); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(netr_DsRGetDCNameInfo, info); + } + + /* check flags */ + if (!check_cldap_reply_required_flags(info->dc_flags, flags)) { + DEBUG(10,("invalid flags\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if ((flags & DS_IP_REQUIRED) && + (info->dc_address_type != DS_ADDRESS_TYPE_INET)) { + return NT_STATUS_INVALID_PARAMETER_MIX; + } + + *info_p = info; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS dsgetdcname_cached(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const struct GUID *domain_guid, + uint32_t flags, + const char *site_name, + struct netr_DsRGetDCNameInfo **info) +{ + NTSTATUS status; + + status = dsgetdcname_cache_fetch(mem_ctx, domain_name, domain_guid, + flags, info); + if (!NT_STATUS_IS_OK(status) + && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + DEBUG(10,("dsgetdcname_cached: cache fetch failed with: %s\n", + nt_errstr(status))); + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + if (flags & DS_BACKGROUND_ONLY) { + return status; + } + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + struct netr_DsRGetDCNameInfo *dc_info; + + status = dsgetdcname(mem_ctx, msg_ctx, domain_name, + domain_guid, site_name, + flags | DS_FORCE_REDISCOVERY, + &dc_info); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + *info = dc_info; + } + + return status; +} + +/**************************************************************** +****************************************************************/ + +static bool check_allowed_required_flags(uint32_t flags, + const char *site_name) +{ + uint32_t return_type = flags & (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME); + uint32_t offered_type = flags & (DS_IS_FLAT_NAME|DS_IS_DNS_NAME); + uint32_t query_type = flags & (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY); + + /* FIXME: check for DSGETDC_VALID_FLAGS and check for excluse bits + * (DS_PDC_REQUIRED, DS_KDC_REQUIRED, DS_GC_SERVER_REQUIRED) */ + + debug_dsdcinfo_flags(10, flags); + + if ((flags & DS_TRY_NEXTCLOSEST_SITE) && site_name) { + return false; + } + + if (return_type == (DS_RETURN_FLAT_NAME|DS_RETURN_DNS_NAME)) { + return false; + } + + if (offered_type == (DS_IS_DNS_NAME|DS_IS_FLAT_NAME)) { + return false; + } + + if (query_type == (DS_BACKGROUND_ONLY|DS_FORCE_REDISCOVERY)) { + return false; + } + +#if 0 + if ((flags & DS_RETURN_DNS_NAME) && (!(flags & DS_IP_REQUIRED))) { + printf("gd: here5 \n"); + return false; + } +#endif + return true; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS discover_dc_netbios(TALLOC_CTX *mem_ctx, + const char *domain_name, + uint32_t flags, + struct ip_service_name **returned_dclist, + size_t *returned_count) +{ + NTSTATUS status; + enum nbt_name_type name_type = NBT_NAME_LOGON; + struct samba_sockaddr *salist = NULL; + size_t i; + struct ip_service_name *dclist = NULL; + size_t count = 0; + static const char *resolve_order[] = { "lmhosts", "wins", "bcast", NULL }; + + if (flags & DS_PDC_REQUIRED) { + name_type = NBT_NAME_PDC; + } + + status = internal_resolve_name(mem_ctx, + domain_name, + name_type, + NULL, + &salist, + &count, + resolve_order); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("discover_dc_netbios: failed to find DC\n")); + return status; + } + + dclist = talloc_zero_array(mem_ctx, struct ip_service_name, count); + if (!dclist) { + TALLOC_FREE(salist); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i<count; i++) { + char addr[INET6_ADDRSTRLEN]; + struct ip_service_name *r = &dclist[i]; + + print_sockaddr(addr, sizeof(addr), + &salist[i].u.ss); + + r->sa = salist[i]; + r->hostname = talloc_strdup(mem_ctx, addr); + if (!r->hostname) { + TALLOC_FREE(salist); + TALLOC_FREE(dclist); + return NT_STATUS_NO_MEMORY; + } + + } + + TALLOC_FREE(salist); + + *returned_dclist = dclist; + *returned_count = count; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS discover_dc_dns(TALLOC_CTX *mem_ctx, + const char *domain_name, + const struct GUID *domain_guid, + uint32_t flags, + const char *site_name, + struct ip_service_name **returned_dclist, + size_t *return_count) +{ + size_t i; + NTSTATUS status; + struct dns_rr_srv *dcs = NULL; + size_t numdcs = 0; + struct ip_service_name *dclist = NULL; + size_t ret_count = 0; + char *query = NULL; + + if (flags & DS_PDC_REQUIRED) { + query = ads_dns_query_string_pdc(mem_ctx, domain_name); + } else if (flags & DS_GC_SERVER_REQUIRED) { + query = ads_dns_query_string_gcs(mem_ctx, domain_name); + } else if (flags & DS_KDC_REQUIRED) { + query = ads_dns_query_string_kdcs(mem_ctx, domain_name); + } else if (flags & DS_DIRECTORY_SERVICE_REQUIRED) { + query = ads_dns_query_string_dcs(mem_ctx, domain_name); + } else if (domain_guid) { + query = ads_dns_query_string_dcs_guid( + mem_ctx, domain_guid, domain_name); + } else { + query = ads_dns_query_string_dcs(mem_ctx, domain_name); + } + + if (query == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = ads_dns_query_srv( + mem_ctx, + lp_get_async_dns_timeout(), + site_name, + query, + &dcs, + &numdcs); + TALLOC_FREE(query); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (numdcs == 0) { + TALLOC_FREE(dcs); + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + /* Check for integer wrap. */ + if (numdcs + numdcs < numdcs) { + TALLOC_FREE(dcs); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * We're only returning up to 2 addresses per + * DC name, so just allocate size numdcs x 2. + */ + + dclist = talloc_zero_array(mem_ctx, + struct ip_service_name, + numdcs * 2); + if (!dclist) { + TALLOC_FREE(dcs); + return NT_STATUS_NO_MEMORY; + } + + /* + * First, copy the SRV record replies that + * have IP addresses returned with them. + */ + ret_count = 0; + for (i = 0; i < numdcs; i++) { + size_t j; + bool have_v4_addr = false; + bool have_v6_addr = false; + + if (dcs[i].num_ips == 0) { + continue; + } + + /* + * Pick up to 1 address from each address + * family (IPv4, IPv6). + * + * This is different from the previous + * code which picked a 'next ip' address + * each time, incrementing an index. + * Too complex to maintain :-(. + */ + for (j = 0; j < dcs[i].num_ips; j++) { + if ((dcs[i].ss_s[j].ss_family == AF_INET && !have_v4_addr) || + (dcs[i].ss_s[j].ss_family == AF_INET6 && !have_v6_addr)) { + bool ok; + dclist[ret_count].hostname = + talloc_strdup(dclist, dcs[i].hostname); + ok = sockaddr_storage_to_samba_sockaddr( + &dclist[ret_count].sa, + &dcs[i].ss_s[j]); + if (!ok) { + TALLOC_FREE(dcs); + TALLOC_FREE(dclist); + return NT_STATUS_INVALID_PARAMETER; + } + ret_count++; + if (dcs[i].ss_s[j].ss_family == AF_INET) { + have_v4_addr = true; + } else { + have_v6_addr = true; + } + if (have_v4_addr && have_v6_addr) { + break; + } + } + } + } + + TALLOC_FREE(dcs); + + if (ret_count == 0) { + TALLOC_FREE(dclist); + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + *returned_dclist = dclist; + *return_count = ret_count; + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS make_domain_controller_info(TALLOC_CTX *mem_ctx, + const char *dc_unc, + const char *dc_address, + uint32_t dc_address_type, + const struct GUID *domain_guid, + const char *domain_name, + const char *forest_name, + uint32_t flags, + const char *dc_site_name, + const char *client_site_name, + struct netr_DsRGetDCNameInfo **info_out) +{ + struct netr_DsRGetDCNameInfo *info; + + info = talloc_zero(mem_ctx, struct netr_DsRGetDCNameInfo); + NT_STATUS_HAVE_NO_MEMORY(info); + + if (dc_unc) { + if (!(dc_unc[0] == '\\' && dc_unc[1] == '\\')) { + info->dc_unc = talloc_asprintf(mem_ctx, "\\\\%s", + dc_unc); + } else { + info->dc_unc = talloc_strdup(mem_ctx, dc_unc); + } + NT_STATUS_HAVE_NO_MEMORY(info->dc_unc); + } + + if (dc_address) { + if (!(dc_address[0] == '\\' && dc_address[1] == '\\')) { + info->dc_address = talloc_asprintf(mem_ctx, "\\\\%s", + dc_address); + } else { + info->dc_address = talloc_strdup(mem_ctx, dc_address); + } + NT_STATUS_HAVE_NO_MEMORY(info->dc_address); + } + + info->dc_address_type = dc_address_type; + + if (domain_guid) { + info->domain_guid = *domain_guid; + } + + if (domain_name) { + info->domain_name = talloc_strdup(mem_ctx, domain_name); + NT_STATUS_HAVE_NO_MEMORY(info->domain_name); + } + + if (forest_name && *forest_name) { + info->forest_name = talloc_strdup(mem_ctx, forest_name); + NT_STATUS_HAVE_NO_MEMORY(info->forest_name); + flags |= DS_DNS_FOREST_ROOT; + } + + info->dc_flags = flags; + + if (dc_site_name) { + info->dc_site_name = talloc_strdup(mem_ctx, dc_site_name); + NT_STATUS_HAVE_NO_MEMORY(info->dc_site_name); + } + + if (client_site_name) { + info->client_site_name = talloc_strdup(mem_ctx, + client_site_name); + NT_STATUS_HAVE_NO_MEMORY(info->client_site_name); + } + + *info_out = info; + + return NT_STATUS_OK; +} + +/**************************************************************** +****************************************************************/ + +static void map_dc_and_domain_names(uint32_t flags, + const char *dc_name, + const char *domain_name, + const char *dns_dc_name, + const char *dns_domain_name, + uint32_t *dc_flags, + const char **hostname_p, + const char **domain_p) +{ + switch (flags & 0xf0000000) { + case DS_RETURN_FLAT_NAME: + if (dc_name && domain_name && + *dc_name && *domain_name) { + *hostname_p = dc_name; + *domain_p = domain_name; + break; + } + + FALL_THROUGH; + case DS_RETURN_DNS_NAME: + default: + if (dns_dc_name && dns_domain_name && + *dns_dc_name && *dns_domain_name) { + *hostname_p = dns_dc_name; + *domain_p = dns_domain_name; + *dc_flags |= DS_DNS_DOMAIN | DS_DNS_CONTROLLER; + break; + } + if (dc_name && domain_name && + *dc_name && *domain_name) { + *hostname_p = dc_name; + *domain_p = domain_name; + break; + } + } +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS make_dc_info_from_cldap_reply( + TALLOC_CTX *mem_ctx, + uint32_t flags, + const struct samba_sockaddr *sa, + struct NETLOGON_SAM_LOGON_RESPONSE_EX *r, + struct netr_DsRGetDCNameInfo **info) +{ + const char *dc_hostname = NULL; + const char *dc_domain_name = NULL; + const char *dc_address = NULL; + const char *dc_forest = NULL; + uint32_t dc_address_type = 0; + uint32_t dc_flags = 0; + struct GUID *dc_domain_guid = NULL; + const char *dc_server_site = NULL; + const char *dc_client_site = NULL; + + char addr[INET6_ADDRSTRLEN]; + + if (sa != NULL) { + print_sockaddr(addr, sizeof(addr), &sa->u.ss); + dc_address = addr; + dc_address_type = DS_ADDRESS_TYPE_INET; + } else { + if (r->sockaddr.pdc_ip) { + dc_address = r->sockaddr.pdc_ip; + dc_address_type = DS_ADDRESS_TYPE_INET; + } else { + dc_address = r->pdc_name; + dc_address_type = DS_ADDRESS_TYPE_NETBIOS; + } + } + + map_dc_and_domain_names(flags, + r->pdc_name, + r->domain_name, + r->pdc_dns_name, + r->dns_domain, + &dc_flags, + &dc_hostname, + &dc_domain_name); + + dc_flags |= r->server_type; + dc_forest = r->forest; + dc_domain_guid = &r->domain_uuid; + dc_server_site = r->server_site; + dc_client_site = r->client_site; + + return make_domain_controller_info(mem_ctx, + dc_hostname, + dc_address, + dc_address_type, + dc_domain_guid, + dc_domain_name, + dc_forest, + dc_flags, + dc_server_site, + dc_client_site, + info); +} + +/**************************************************************** +****************************************************************/ + +static uint32_t map_ds_flags_to_nt_version(uint32_t flags) +{ + uint32_t nt_version = 0; + + if (flags & DS_PDC_REQUIRED) { + nt_version |= NETLOGON_NT_VERSION_PDC; + } + + if (flags & DS_GC_SERVER_REQUIRED) { + nt_version |= NETLOGON_NT_VERSION_GC; + } + + if (flags & DS_TRY_NEXTCLOSEST_SITE) { + nt_version |= NETLOGON_NT_VERSION_WITH_CLOSEST_SITE; + } + + if (flags & DS_IP_REQUIRED) { + nt_version |= NETLOGON_NT_VERSION_IP; + } + + return nt_version; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS process_dc_dns(TALLOC_CTX *mem_ctx, + const char *domain_name, + uint32_t flags, + struct ip_service_name *dclist, + size_t num_dcs, + struct netr_DsRGetDCNameInfo **info) +{ + size_t i = 0; + bool valid_dc = false; + struct netlogon_samlogon_response *r = NULL; + uint32_t nt_version = NETLOGON_NT_VERSION_5 | + NETLOGON_NT_VERSION_5EX; + uint32_t ret_flags = 0; + NTSTATUS status; + + nt_version |= map_ds_flags_to_nt_version(flags); + + for (i=0; i<num_dcs; i++) { + char addr[INET6_ADDRSTRLEN]; + + print_sockaddr(addr, sizeof(addr), &dclist[i].sa.u.ss); + + DEBUG(10,("LDAP ping to %s (%s)\n", dclist[i].hostname, addr)); + + if (ads_cldap_netlogon(mem_ctx, &dclist[i].sa.u.ss, + domain_name, + nt_version, + &r)) + { + nt_version = r->ntver; + ret_flags = get_cldap_reply_server_flags(r, nt_version); + + if (check_cldap_reply_required_flags(ret_flags, flags)) { + valid_dc = true; + break; + } + } + + continue; + } + + if (!valid_dc) { + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].sa, + &r->data.nt5_ex, info); + if (NT_STATUS_IS_OK(status)) { + return store_cldap_reply(mem_ctx, flags, &dclist[i].sa, + nt_version, &r->data.nt5_ex); + } + + return status; +} + +/**************************************************************** +****************************************************************/ + +/**************************************************************** +****************************************************************/ + +static NTSTATUS process_dc_netbios(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + uint32_t flags, + struct ip_service_name *dclist, + size_t num_dcs, + struct netr_DsRGetDCNameInfo **info) +{ + enum nbt_name_type name_type = NBT_NAME_LOGON; + NTSTATUS status; + size_t i; + const char *dc_name = NULL; + fstring tmp_dc_name; + struct netlogon_samlogon_response *r = NULL; + bool store_cache = false; + uint32_t nt_version = NETLOGON_NT_VERSION_1 | + NETLOGON_NT_VERSION_5 | + NETLOGON_NT_VERSION_5EX_WITH_IP; + size_t len = strlen(lp_netbios_name()); + char my_acct_name[len+2]; + + if (msg_ctx == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + if (flags & DS_PDC_REQUIRED) { + name_type = NBT_NAME_PDC; + } + + nt_version |= map_ds_flags_to_nt_version(flags); + + snprintf(my_acct_name, + sizeof(my_acct_name), + "%s$", + lp_netbios_name()); + + DEBUG(10,("process_dc_netbios\n")); + + for (i=0; i<num_dcs; i++) { + uint16_t val; + + generate_random_buffer((uint8_t *)&val, 2); + + status = nbt_getdc(msg_ctx, 10, &dclist[i].sa.u.ss, domain_name, + NULL, my_acct_name, ACB_WSTRUST, nt_version, + mem_ctx, &nt_version, &dc_name, &r); + if (NT_STATUS_IS_OK(status)) { + store_cache = true; + namecache_store(dc_name, + NBT_NAME_SERVER, + 1, + &dclist[i].sa); + goto make_reply; + } + + if (name_status_find(domain_name, + name_type, + NBT_NAME_SERVER, + &dclist[i].sa.u.ss, + tmp_dc_name)) + { + struct NETLOGON_SAM_LOGON_RESPONSE_NT40 logon1; + + r = talloc_zero(mem_ctx, struct netlogon_samlogon_response); + NT_STATUS_HAVE_NO_MEMORY(r); + + ZERO_STRUCT(logon1); + + nt_version = NETLOGON_NT_VERSION_1; + + logon1.nt_version = nt_version; + logon1.pdc_name = tmp_dc_name; + logon1.domain_name = talloc_strdup_upper(mem_ctx, domain_name); + NT_STATUS_HAVE_NO_MEMORY(logon1.domain_name); + + r->data.nt4 = logon1; + r->ntver = nt_version; + + map_netlogon_samlogon_response(r); + + namecache_store(tmp_dc_name, + NBT_NAME_SERVER, + 1, + &dclist[i].sa); + + goto make_reply; + } + } + + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + + make_reply: + + status = make_dc_info_from_cldap_reply(mem_ctx, flags, &dclist[i].sa, + &r->data.nt5_ex, info); + if (NT_STATUS_IS_OK(status) && store_cache) { + return store_cldap_reply(mem_ctx, flags, &dclist[i].sa, + nt_version, &r->data.nt5_ex); + } + + return status; +} + +/**************************************************************** +****************************************************************/ + +static NTSTATUS dsgetdcname_rediscover(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const struct GUID *domain_guid, + uint32_t flags, + const char *site_name, + struct netr_DsRGetDCNameInfo **info) +{ + NTSTATUS status; + struct ip_service_name *dclist = NULL; + size_t num_dcs = 0; + + DEBUG(10,("dsgetdcname_rediscover\n")); + + if (flags & DS_IS_FLAT_NAME) { + + if (lp_disable_netbios()) { + return NT_STATUS_NOT_SUPPORTED; + } + + status = discover_dc_netbios(mem_ctx, domain_name, flags, + &dclist, &num_dcs); + NT_STATUS_NOT_OK_RETURN(status); + + return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, + dclist, num_dcs, info); + } + + if (flags & DS_IS_DNS_NAME) { + + status = discover_dc_dns(mem_ctx, domain_name, domain_guid, + flags, site_name, &dclist, &num_dcs); + NT_STATUS_NOT_OK_RETURN(status); + + return process_dc_dns(mem_ctx, domain_name, flags, + dclist, num_dcs, info); + } + + status = discover_dc_dns(mem_ctx, domain_name, domain_guid, flags, + site_name, &dclist, &num_dcs); + + if (NT_STATUS_IS_OK(status) && num_dcs != 0) { + + status = process_dc_dns(mem_ctx, domain_name, flags, dclist, + num_dcs, info); + if (NT_STATUS_IS_OK(status)) { + return status; + } + } + + if (lp_disable_netbios()) { + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + status = discover_dc_netbios(mem_ctx, domain_name, flags, &dclist, + &num_dcs); + NT_STATUS_NOT_OK_RETURN(status); + + return process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, dclist, + num_dcs, info); +} + +static bool is_closest_site(struct netr_DsRGetDCNameInfo *info) +{ + if (info->dc_flags & DS_SERVER_CLOSEST) { + return true; + } + + if (!info->client_site_name) { + return true; + } + + if (!info->dc_site_name) { + return false; + } + + if (strcmp(info->client_site_name, info->dc_site_name) == 0) { + return true; + } + + return false; +} + +/******************************************************************** + Internal dsgetdcname. +********************************************************************/ + +static NTSTATUS dsgetdcname_internal(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const struct GUID *domain_guid, + const char *site_name, + uint32_t flags, + struct netr_DsRGetDCNameInfo **info) +{ + NTSTATUS status; + struct netr_DsRGetDCNameInfo *myinfo = NULL; + bool first = true; + struct netr_DsRGetDCNameInfo *first_info = NULL; + + DEBUG(10,("dsgetdcname_internal: domain_name: %s, " + "domain_guid: %s, site_name: %s, flags: 0x%08x\n", + domain_name, + domain_guid ? GUID_string(mem_ctx, domain_guid) : "(null)", + site_name ? site_name : "(null)", flags)); + + *info = NULL; + + if (!check_allowed_required_flags(flags, site_name)) { + DEBUG(0,("invalid flags specified\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + if (flags & DS_FORCE_REDISCOVERY) { + goto rediscover; + } + + status = dsgetdcname_cached(mem_ctx, msg_ctx, domain_name, domain_guid, + flags, site_name, &myinfo); + if (NT_STATUS_IS_OK(status)) { + *info = myinfo; + goto done; + } + + if (flags & DS_BACKGROUND_ONLY) { + goto done; + } + + rediscover: + status = dsgetdcname_rediscover(mem_ctx, msg_ctx, domain_name, + domain_guid, flags, site_name, + &myinfo); + + done: + if (!NT_STATUS_IS_OK(status)) { + if (!first) { + *info = first_info; + return NT_STATUS_OK; + } + return status; + } + + if (!first) { + TALLOC_FREE(first_info); + } else if (!is_closest_site(myinfo)) { + first = false; + first_info = myinfo; + /* TODO: may use the next_closest_site here */ + site_name = myinfo->client_site_name; + goto rediscover; + } + + *info = myinfo; + return NT_STATUS_OK; +} + +/******************************************************************** + dsgetdcname. + + This will be the only public function here. +********************************************************************/ + +NTSTATUS dsgetdcname(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const struct GUID *domain_guid, + const char *site_name, + uint32_t flags, + struct netr_DsRGetDCNameInfo **info) +{ + NTSTATUS status; + const char *query_site = NULL; + char *ptr_to_free = NULL; + bool retry_query_with_null = false; + + if ((site_name == NULL) || (site_name[0] == '\0')) { + ptr_to_free = sitename_fetch(mem_ctx, domain_name); + if (ptr_to_free != NULL) { + retry_query_with_null = true; + } + query_site = ptr_to_free; + } else { + query_site = site_name; + } + + status = dsgetdcname_internal(mem_ctx, + msg_ctx, + domain_name, + domain_guid, + query_site, + flags, + info); + + TALLOC_FREE(ptr_to_free); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) { + return status; + } + + /* Should we try again with site_name == NULL ? */ + if (retry_query_with_null) { + status = dsgetdcname_internal(mem_ctx, + msg_ctx, + domain_name, + domain_guid, + NULL, + flags, + info); + } + + return status; +} + +NTSTATUS dsgetonedcname(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const char *dcname, + uint32_t flags, + struct netr_DsRGetDCNameInfo **info) +{ + NTSTATUS status; + struct sockaddr_storage *addrs; + unsigned int num_addrs, i; + const char *hostname = strip_hostname(dcname); + + status = resolve_name_list(mem_ctx, hostname, 0x20, + &addrs, &num_addrs); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + for (i = 0; i < num_addrs; i++) { + + bool ok; + struct ip_service_name dclist; + + dclist.hostname = hostname; + ok = sockaddr_storage_to_samba_sockaddr(&dclist.sa, &addrs[i]); + if (!ok) { + TALLOC_FREE(addrs); + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + } + + status = process_dc_dns(mem_ctx, domain_name, flags, + &dclist, 1, info); + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(addrs); + return NT_STATUS_OK; + } + + if (lp_disable_netbios()) { + continue; + } + + status = process_dc_netbios(mem_ctx, msg_ctx, domain_name, flags, + &dclist, 1, info); + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(addrs); + return NT_STATUS_OK; + } + } + + TALLOC_FREE(addrs); + return NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; +} diff --git a/source3/libsmb/dsgetdcname.h b/source3/libsmb/dsgetdcname.h new file mode 100644 index 0000000..241b721 --- /dev/null +++ b/source3/libsmb/dsgetdcname.h @@ -0,0 +1,43 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Volker Lendecke 2018 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMB_DSGETDCNAME_H__ +#define __LIBSMB_DSGETDCNAME_H__ + +#include "replace.h" +#include <talloc.h> +#include "librpc/gen_ndr/misc.h" + +struct netr_DsRGetDCNameInfo; +struct messaging_context; + +NTSTATUS dsgetdcname(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const struct GUID *domain_guid, + const char *site_name, + uint32_t flags, + struct netr_DsRGetDCNameInfo **info); + +NTSTATUS dsgetonedcname(TALLOC_CTX *mem_ctx, + struct messaging_context *msg_ctx, + const char *domain_name, + const char *dcname, + uint32_t flags, + struct netr_DsRGetDCNameInfo **info); +#endif diff --git a/source3/libsmb/errormap.c b/source3/libsmb/errormap.c new file mode 100644 index 0000000..ba8bc65 --- /dev/null +++ b/source3/libsmb/errormap.c @@ -0,0 +1,296 @@ +/* + * Unix SMB/CIFS implementation. + * error mapping functions + * Copyright (C) Andrew Tridgell 2001 + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Tim Potter 2000 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" + +/* dos -> nt status error map */ +static const struct { + uint8_t dos_class; + uint32_t dos_code; + NTSTATUS ntstatus; +} dos_to_ntstatus_map[] = { + {ERRDOS, ERRbadfunc, NT_STATUS_NOT_IMPLEMENTED}, + {ERRDOS, ERRbadfile, NT_STATUS_NO_SUCH_FILE}, + {ERRDOS, ERRbadpath, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRDOS, ERRnofids, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRDOS, ERRnoaccess, NT_STATUS_ACCESS_DENIED}, + {ERRDOS, ERRbadfid, NT_STATUS_INVALID_HANDLE}, + {ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRDOS, ERRbadaccess, NT_STATUS_ACCESS_DENIED}, + {ERRDOS, ERRbaddata, NT_STATUS_DATA_ERROR}, + {ERRDOS, 14, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRDOS, ERRremcd, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRDOS, ERRdiffdevice, NT_STATUS_NOT_SAME_DEVICE}, + {ERRDOS, ERRnofiles, STATUS_NO_MORE_FILES}, + {ERRDOS, 19, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRDOS, 21, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRDOS, 22, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRDOS, 23, NT_STATUS_DATA_ERROR}, + {ERRDOS, 24, NT_STATUS_DATA_ERROR}, + {ERRDOS, 26, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRDOS, 27, NT_STATUS_NONEXISTENT_SECTOR}, + {ERRDOS, 28, NT_STATUS(0x8000000e)}, + {ERRDOS, 31, NT_STATUS_UNSUCCESSFUL}, + {ERRDOS, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRDOS, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRDOS, 34, NT_STATUS_WRONG_VOLUME}, + {ERRDOS, 38, NT_STATUS_END_OF_FILE}, + {ERRDOS, ERRunsup, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRDOS, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRDOS, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRDOS, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRDOS, 54, NT_STATUS_NETWORK_BUSY}, + {ERRDOS, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRDOS, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRDOS, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRDOS, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRDOS, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRDOS, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRDOS, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRDOS, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRDOS, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRDOS, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRDOS, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRDOS, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRDOS, ERRnosuchshare, NT_STATUS_BAD_NETWORK_NAME}, + {ERRDOS, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRDOS, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRDOS, 70, NT_STATUS_SHARING_PAUSED}, + {ERRDOS, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRDOS, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRDOS, ERRfilexists, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRDOS, 86, NT_STATUS_WRONG_PASSWORD}, + {ERRDOS, 87, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRDOS, 109, NT_STATUS_PIPE_BROKEN}, + {ERRDOS, 111, STATUS_MORE_ENTRIES}, + {ERRDOS, 112, NT_STATUS_DISK_FULL}, + {ERRDOS, 121, NT_STATUS_IO_TIMEOUT}, + {ERRDOS, 122, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRDOS, ERRinvalidname, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRDOS, ERRunknownlevel, NT_STATUS_INVALID_LEVEL}, + {ERRDOS, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRDOS, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRDOS, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRDOS, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRDOS, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRDOS, 158, NT_STATUS_NOT_LOCKED}, + {ERRDOS, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRDOS, 170, NT_STATUS(0x80000011)}, + {ERRDOS, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRDOS, 183, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRDOS, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRDOS, 203, NT_STATUS(0xc0000100)}, + {ERRDOS, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRDOS, ERRbadpipe, NT_STATUS_INVALID_INFO_CLASS}, + {ERRDOS, ERRpipebusy, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRDOS, ERRpipeclosing, NT_STATUS_PIPE_CLOSING}, + {ERRDOS, ERRnotconnected, NT_STATUS_PIPE_DISCONNECTED}, + {ERRDOS, ERRmoredata, NT_STATUS_MORE_PROCESSING_REQUIRED}, + {ERRDOS, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRDOS, 254, NT_STATUS(0x80000013)}, + {ERRDOS, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRDOS, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRDOS, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRDOS, 275, NT_STATUS_EA_TOO_LARGE}, + {ERRDOS, 276, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRDOS, 277, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRDOS, 278, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRDOS, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRDOS, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRDOS, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRDOS, 299, NT_STATUS(0x8000000d)}, + {ERRDOS, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRDOS, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRDOS, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRDOS, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRDOS, 535, NT_STATUS_PIPE_CONNECTED}, + {ERRDOS, 536, NT_STATUS_PIPE_LISTENING}, + {ERRDOS, 995, NT_STATUS_CANCELLED}, + {ERRDOS, 997, NT_STATUS(0x00000103)}, + {ERRDOS, 998, NT_STATUS_ACCESS_VIOLATION}, + {ERRDOS, 999, NT_STATUS_IN_PAGE_ERROR}, + {ERRDOS, 1001, NT_STATUS_BAD_INITIAL_STACK}, + {ERRDOS, 1005, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRDOS, 1006, NT_STATUS_FILE_INVALID}, + {ERRDOS, 1007, NT_STATUS_FULLSCREEN_MODE}, + {ERRDOS, 1008, NT_STATUS_NO_TOKEN}, + {ERRDOS, 1009, NT_STATUS_REGISTRY_CORRUPT}, + {ERRDOS, 1016, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRDOS, 1017, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRDOS, 1018, NT_STATUS_KEY_DELETED}, + {ERRDOS, 1019, NT_STATUS_NO_LOG_SPACE}, + {ERRDOS, 1020, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRDOS, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRDOS, 1022, NT_STATUS(0x0000010c)}, + {ERRSRV, ERRbadpw, NT_STATUS_WRONG_PASSWORD}, + {ERRSRV, ERRbaduid, NT_STATUS_USER_SESSION_DELETED}, + {ERRSRV, ERRbadtype, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRSRV, ERRaccess, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRSRV, ERRinvnid, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRSRV, ERRinvnetname, NT_STATUS_BAD_NETWORK_NAME}, + {ERRSRV, ERRinvdevice, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRSRV, ERRqfull, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRSRV, ERRqtoobig, NT_STATUS_NO_SPOOL_SPACE}, + {ERRSRV, ERRinvpfid, NT_STATUS_PRINT_CANCELLED}, + {ERRSRV, ERRsmbcmd, NT_STATUS_NOT_IMPLEMENTED}, + {ERRSRV, ERRbadpermits, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRSRV, ERRpaused, NT_STATUS_SHARING_PAUSED}, + {ERRSRV, ERRmsgoff, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRSRV, ERRnoroom, NT_STATUS_DISK_FULL}, + {ERRSRV, ERRnoresource, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRSRV, ERRtoomanyuids, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRSRV, ERRunknownsmb, NT_STATUS_NOT_IMPLEMENTED}, + {ERRSRV, 123, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRSRV, 206, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRHRD, 1, NT_STATUS_NOT_IMPLEMENTED}, + {ERRHRD, 2, NT_STATUS_NO_SUCH_DEVICE}, + {ERRHRD, 3, NT_STATUS_OBJECT_PATH_NOT_FOUND}, + {ERRHRD, 4, NT_STATUS_TOO_MANY_OPENED_FILES}, + {ERRHRD, 5, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRHRD, 6, NT_STATUS_INVALID_HANDLE}, + {ERRHRD, 8, NT_STATUS_INSUFFICIENT_RESOURCES}, + {ERRHRD, 12, NT_STATUS_INVALID_LOCK_SEQUENCE}, + {ERRHRD, 13, NT_STATUS_DATA_ERROR}, + {ERRHRD, 14, NT_STATUS_SECTION_NOT_EXTENDED}, + {ERRHRD, 16, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, 17, NT_STATUS_NOT_SAME_DEVICE}, + {ERRHRD, 18, NT_STATUS(0x80000006)}, + {ERRHRD, ERRnowrite, NT_STATUS_MEDIA_WRITE_PROTECTED}, + {ERRHRD, ERRnotready, NT_STATUS_NO_MEDIA_IN_DEVICE}, + {ERRHRD, ERRbadcmd, NT_STATUS_INVALID_DEVICE_STATE}, + {ERRHRD, ERRdata, NT_STATUS_DATA_ERROR}, + {ERRHRD, ERRbadreq, NT_STATUS_DATA_ERROR}, + {ERRHRD, ERRbadmedia, NT_STATUS_DISK_CORRUPT_ERROR}, + {ERRHRD, ERRbadsector, NT_STATUS_NONEXISTENT_SECTOR}, + {ERRHRD, ERRnopaper, NT_STATUS(0x8000000e)}, + {ERRHRD, ERRgeneral, NT_STATUS_UNSUCCESSFUL}, + {ERRHRD, ERRbadshare, NT_STATUS_SHARING_VIOLATION}, + {ERRHRD, ERRlock, NT_STATUS_FILE_LOCK_CONFLICT}, + {ERRHRD, ERRwrongdisk, NT_STATUS_WRONG_VOLUME}, + {ERRHRD, 38, NT_STATUS_END_OF_FILE}, + {ERRHRD, ERRdiskfull, NT_STATUS_DISK_FULL}, + {ERRHRD, 50, NT_STATUS_CTL_FILE_NOT_SUPPORTED}, + {ERRHRD, 51, NT_STATUS_REMOTE_NOT_LISTENING}, + {ERRHRD, 52, NT_STATUS_DUPLICATE_NAME}, + {ERRHRD, 53, NT_STATUS_BAD_NETWORK_PATH}, + {ERRHRD, 54, NT_STATUS_NETWORK_BUSY}, + {ERRHRD, 55, NT_STATUS_DEVICE_DOES_NOT_EXIST}, + {ERRHRD, 56, NT_STATUS_TOO_MANY_COMMANDS}, + {ERRHRD, 57, NT_STATUS_ADAPTER_HARDWARE_ERROR}, + {ERRHRD, 58, NT_STATUS_INVALID_NETWORK_RESPONSE}, + {ERRHRD, 59, NT_STATUS_UNEXPECTED_NETWORK_ERROR}, + {ERRHRD, 60, NT_STATUS_BAD_REMOTE_ADAPTER}, + {ERRHRD, 61, NT_STATUS_PRINT_QUEUE_FULL}, + {ERRHRD, 62, NT_STATUS_NO_SPOOL_SPACE}, + {ERRHRD, 63, NT_STATUS_PRINT_CANCELLED}, + {ERRHRD, 64, NT_STATUS_NETWORK_NAME_DELETED}, + {ERRHRD, 65, NT_STATUS_NETWORK_ACCESS_DENIED}, + {ERRHRD, 66, NT_STATUS_BAD_DEVICE_TYPE}, + {ERRHRD, 67, NT_STATUS_BAD_NETWORK_NAME}, + {ERRHRD, 68, NT_STATUS_TOO_MANY_GUIDS_REQUESTED}, + {ERRHRD, 69, NT_STATUS_TOO_MANY_SESSIONS}, + {ERRHRD, 70, NT_STATUS_SHARING_PAUSED}, + {ERRHRD, 71, NT_STATUS_REQUEST_NOT_ACCEPTED}, + {ERRHRD, 72, NT_STATUS_REDIRECTOR_PAUSED}, + {ERRHRD, 80, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, 86, NT_STATUS_WRONG_PASSWORD}, + {ERRHRD, 87, NT_STATUS_INVALID_INFO_CLASS}, + {ERRHRD, 88, NT_STATUS_NET_WRITE_FAULT}, + {ERRHRD, 109, NT_STATUS_PIPE_BROKEN}, + {ERRHRD, 111, STATUS_MORE_ENTRIES}, + {ERRHRD, 112, NT_STATUS_DISK_FULL}, + {ERRHRD, 121, NT_STATUS_IO_TIMEOUT}, + {ERRHRD, 122, NT_STATUS_BUFFER_TOO_SMALL}, + {ERRHRD, 123, NT_STATUS_OBJECT_NAME_INVALID}, + {ERRHRD, 124, NT_STATUS_INVALID_LEVEL}, + {ERRHRD, 126, NT_STATUS_DLL_NOT_FOUND}, + {ERRHRD, 127, NT_STATUS_PROCEDURE_NOT_FOUND}, + {ERRHRD, 145, NT_STATUS_DIRECTORY_NOT_EMPTY}, + {ERRHRD, 154, NT_STATUS_INVALID_VOLUME_LABEL}, + {ERRHRD, 156, NT_STATUS_SUSPEND_COUNT_EXCEEDED}, + {ERRHRD, 158, NT_STATUS_NOT_LOCKED}, + {ERRHRD, 161, NT_STATUS_OBJECT_PATH_INVALID}, + {ERRHRD, 170, NT_STATUS(0x80000011)}, + {ERRHRD, 182, NT_STATUS_ORDINAL_NOT_FOUND}, + {ERRHRD, 183, NT_STATUS_OBJECT_NAME_COLLISION}, + {ERRHRD, 193, NT_STATUS_BAD_INITIAL_PC}, + {ERRHRD, 203, NT_STATUS(0xc0000100)}, + {ERRHRD, 206, NT_STATUS_NAME_TOO_LONG}, + {ERRHRD, 230, NT_STATUS_INVALID_INFO_CLASS}, + {ERRHRD, 231, NT_STATUS_INSTANCE_NOT_AVAILABLE}, + {ERRHRD, 232, NT_STATUS_PIPE_CLOSING}, + {ERRHRD, 233, NT_STATUS_PIPE_DISCONNECTED}, + {ERRHRD, 234, STATUS_MORE_ENTRIES}, + {ERRHRD, 240, NT_STATUS_VIRTUAL_CIRCUIT_CLOSED}, + {ERRHRD, 254, NT_STATUS(0x80000013)}, + {ERRHRD, 255, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, 259, NT_STATUS_GUIDS_EXHAUSTED}, + {ERRHRD, 267, NT_STATUS_NOT_A_DIRECTORY}, + {ERRHRD, 275, NT_STATUS_EA_TOO_LARGE}, + {ERRHRD, 276, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, 277, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, 278, NT_STATUS_NONEXISTENT_EA_ENTRY}, + {ERRHRD, ERReasnotsupported, NT_STATUS_EAS_NOT_SUPPORTED}, + {ERRHRD, 288, NT_STATUS_MUTANT_NOT_OWNED}, + {ERRHRD, 298, NT_STATUS_SEMAPHORE_LIMIT_EXCEEDED}, + {ERRHRD, 299, NT_STATUS(0x8000000d)}, + {ERRHRD, 300, NT_STATUS_OPLOCK_NOT_GRANTED}, + {ERRHRD, 301, NT_STATUS_INVALID_OPLOCK_PROTOCOL}, + {ERRHRD, 487, NT_STATUS_CONFLICTING_ADDRESSES}, + {ERRHRD, 534, NT_STATUS_INTEGER_OVERFLOW}, + {ERRHRD, 535, NT_STATUS_PIPE_CONNECTED}, + {ERRHRD, 536, NT_STATUS_PIPE_LISTENING}, + {ERRHRD, 995, NT_STATUS_CANCELLED}, + {ERRHRD, 997, NT_STATUS(0x00000103)}, + {ERRHRD, 998, NT_STATUS_ACCESS_VIOLATION}, + {ERRHRD, 999, NT_STATUS_IN_PAGE_ERROR}, + {ERRHRD, 1001, NT_STATUS_BAD_INITIAL_STACK}, + {ERRHRD, 1005, NT_STATUS_UNRECOGNIZED_VOLUME}, + {ERRHRD, 1006, NT_STATUS_FILE_INVALID}, + {ERRHRD, 1007, NT_STATUS_FULLSCREEN_MODE}, + {ERRHRD, 1008, NT_STATUS_NO_TOKEN}, + {ERRHRD, 1009, NT_STATUS_REGISTRY_CORRUPT}, + {ERRHRD, 1016, NT_STATUS_REGISTRY_IO_FAILED}, + {ERRHRD, 1017, NT_STATUS_NOT_REGISTRY_FILE}, + {ERRHRD, 1018, NT_STATUS_KEY_DELETED}, + {ERRHRD, 1019, NT_STATUS_NO_LOG_SPACE}, + {ERRHRD, 1020, NT_STATUS_KEY_HAS_CHILDREN}, + {ERRHRD, 1021, NT_STATUS_CHILD_MUST_BE_VOLATILE}, + {ERRHRD, 1022, NT_STATUS(0x0000010c)}, +}; + +/***************************************************************************** +convert a dos eclas/ecode to a NT status32 code + *****************************************************************************/ +NTSTATUS dos_to_ntstatus(uint8_t eclass, uint32_t ecode) +{ + size_t i; + if (eclass == 0) return NT_STATUS_OK; + for (i=0; i < ARRAY_SIZE(dos_to_ntstatus_map); i++) { + if (eclass == dos_to_ntstatus_map[i].dos_class && + ecode == dos_to_ntstatus_map[i].dos_code) { + return dos_to_ntstatus_map[i].ntstatus; + } + } + return NT_STATUS_UNSUCCESSFUL; +} + diff --git a/source3/libsmb/errormap_wbc.c b/source3/libsmb/errormap_wbc.c new file mode 100644 index 0000000..b7e6ec0 --- /dev/null +++ b/source3/libsmb/errormap_wbc.c @@ -0,0 +1,63 @@ +/* + * Unix SMB/CIFS implementation. + * error mapping functions + * Copyright (C) Andrew Tridgell 2001 + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Tim Potter 2000 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "nsswitch/libwbclient/wbclient.h" + +/******************************************************************************* + Map between wbcErr and NT status. +*******************************************************************************/ + +static const struct { + wbcErr wbc_err; + NTSTATUS nt_status; +} wbcErr_ntstatus_map[] = { + { WBC_ERR_SUCCESS, NT_STATUS_OK }, + { WBC_ERR_NOT_IMPLEMENTED, NT_STATUS_NOT_IMPLEMENTED }, + { WBC_ERR_UNKNOWN_FAILURE, NT_STATUS_UNSUCCESSFUL }, + { WBC_ERR_NO_MEMORY, NT_STATUS_NO_MEMORY }, + { WBC_ERR_INVALID_SID, NT_STATUS_INVALID_SID }, + { WBC_ERR_INVALID_PARAM, NT_STATUS_INVALID_PARAMETER }, + { WBC_ERR_WINBIND_NOT_AVAILABLE, NT_STATUS_SERVER_DISABLED }, + { WBC_ERR_DOMAIN_NOT_FOUND, NT_STATUS_NO_SUCH_DOMAIN }, + { WBC_ERR_INVALID_RESPONSE, NT_STATUS_INVALID_NETWORK_RESPONSE }, + { WBC_ERR_NSS_ERROR, NT_STATUS_INTERNAL_ERROR }, + { WBC_ERR_AUTH_ERROR, NT_STATUS_LOGON_FAILURE }, + { WBC_ERR_UNKNOWN_USER, NT_STATUS_NO_SUCH_USER }, + { WBC_ERR_UNKNOWN_GROUP, NT_STATUS_NO_SUCH_GROUP }, + { WBC_ERR_PWD_CHANGE_FAILED, NT_STATUS_PASSWORD_RESTRICTION } +}; + +NTSTATUS map_nt_error_from_wbcErr(wbcErr wbc_err) +{ + int i; + + /* Look through list */ + for (i=0;i<ARRAY_SIZE(wbcErr_ntstatus_map);i++) { + if (wbcErr_ntstatus_map[i].wbc_err == wbc_err) { + return wbcErr_ntstatus_map[i].nt_status; + } + } + + /* Default return */ + return NT_STATUS_UNSUCCESSFUL; +} + diff --git a/source3/libsmb/errormap_wbc.h b/source3/libsmb/errormap_wbc.h new file mode 100644 index 0000000..6323dee --- /dev/null +++ b/source3/libsmb/errormap_wbc.h @@ -0,0 +1,29 @@ +/* + * Unix SMB/CIFS implementation. + * error mapping functions + * Copyright (C) Andrew Tridgell 2001 + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Tim Potter 2000 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _LIBSMB_ERRORMAP_WBC_H_ +#define _LIBSMB_ERRORMAP_WBC_H_ + +/* The following definitions come from libsmb/errormap_wbc.c */ + +NTSTATUS map_nt_error_from_wbcErr(wbcErr wbc_err); + +#endif /* _LIBSMB_ERRORMAP_WBC_H_ */ diff --git a/source3/libsmb/libsmb.h b/source3/libsmb/libsmb.h new file mode 100644 index 0000000..6df06ae --- /dev/null +++ b/source3/libsmb/libsmb.h @@ -0,0 +1,31 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Tridgell 1992-1998,2001 + Copyright (C) Jeremy Allison 1998 + Copyright (C) Remus Koos 2001 + Copyright (C) Andrew Bartlett 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIBSMB_LIBSMB_H +#define _LIBSMB_LIBSMB_H + +#include "client.h" +#include "libads/ads_status.h" +#include "libsmb/proto.h" +#include "libsmb/cli_smb2_fnum.h" + +#endif /* _LIBSMB_LIBSMB_H */ diff --git a/source3/libsmb/libsmb_cache.c b/source3/libsmb/libsmb_cache.c new file mode 100644 index 0000000..2b32447 --- /dev/null +++ b/source3/libsmb/libsmb_cache.c @@ -0,0 +1,246 @@ +/* + Unix SMB/CIFS implementation. + SMB client library implementation (server cache) + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + +/* + * Structure we use if internal caching mechanism is used + * nothing fancy here. + */ +struct smbc_server_cache { + char *server_name; + char *share_name; + char *workgroup; + char *username; + SMBCSRV *server; + + struct smbc_server_cache *next, *prev; +}; + + + +/* + * Add a new connection to the server cache. + * This function is only used if the external cache is not enabled + */ +int +SMBC_add_cached_server(SMBCCTX * context, + SMBCSRV * newsrv, + const char * server, + const char * share, + const char * workgroup, + const char * username) +{ + struct smbc_server_cache * srvcache = NULL; + + if (!(srvcache = SMB_MALLOC_P(struct smbc_server_cache))) { + errno = ENOMEM; + DEBUG(3, ("Not enough space for server cache allocation\n")); + return 1; + } + + ZERO_STRUCTP(srvcache); + + srvcache->server = newsrv; + + srvcache->server_name = SMB_STRDUP(server); + if (!srvcache->server_name) { + errno = ENOMEM; + goto failed; + } + + srvcache->share_name = SMB_STRDUP(share); + if (!srvcache->share_name) { + errno = ENOMEM; + goto failed; + } + + srvcache->workgroup = SMB_STRDUP(workgroup); + if (!srvcache->workgroup) { + errno = ENOMEM; + goto failed; + } + + srvcache->username = SMB_STRDUP(username); + if (!srvcache->username) { + errno = ENOMEM; + goto failed; + } + + DLIST_ADD(context->internal->server_cache, srvcache); + return 0; + +failed: + SAFE_FREE(srvcache->server_name); + SAFE_FREE(srvcache->share_name); + SAFE_FREE(srvcache->workgroup); + SAFE_FREE(srvcache->username); + SAFE_FREE(srvcache); + + return 1; +} + + + +/* + * Search the server cache for a server + * returns server handle on success, NULL on error (not found) + * This function is only used if the external cache is not enabled + */ +SMBCSRV * +SMBC_get_cached_server(SMBCCTX * context, + const char * server, + const char * share, + const char * workgroup, + const char * user) +{ + struct smbc_server_cache * srv = NULL; + + /* Search the cache lines */ + for (srv = context->internal->server_cache; srv; srv = srv->next) { + + if (strcmp(server,srv->server_name) == 0 && + strcmp(workgroup,srv->workgroup) == 0 && + strcmp(user, srv->username) == 0) { + + /* If the share name matches, we're cool */ + if (strcmp(share, srv->share_name) == 0) { + return srv->server; + } + + /* + * We only return an empty share name or the attribute + * server on an exact match (which would have been + * caught above). + */ + if (*share == '\0' || strcmp(share, "*IPC$") == 0) + continue; + + /* + * Never return an empty share name or the attribute + * server if it wasn't what was requested. + */ + if (*srv->share_name == '\0' || + strcmp(srv->share_name, "*IPC$") == 0) + continue; + + /* + * If we're only allowing one share per server, then + * a connection to the server (other than the + * attribute server connection) is cool. + */ + if (smbc_getOptionOneSharePerServer(context)) { + NTSTATUS status; + /* + * The currently connected share name + * doesn't match the requested share, so + * disconnect from the current share. + */ + status = cli_tdis(srv->server->cli); + if (!NT_STATUS_IS_OK(status)) { + /* Sigh. Couldn't disconnect. */ + cli_shutdown(srv->server->cli); + srv->server->cli = NULL; + smbc_getFunctionRemoveCachedServer(context)(context, srv->server); + continue; + } + + /* + * Save the new share name. We've + * disconnected from the old share, and are + * about to connect to the new one. + */ + SAFE_FREE(srv->share_name); + srv->share_name = SMB_STRDUP(share); + if (!srv->share_name) { + /* Out of memory. */ + cli_shutdown(srv->server->cli); + srv->server->cli = NULL; + smbc_getFunctionRemoveCachedServer(context)(context, srv->server); + continue; + } + + return srv->server; + } + } + } + + return NULL; +} + + +/* + * Search the server cache for a server and remove it + * returns 0 on success + * This function is only used if the external cache is not enabled + */ +int +SMBC_remove_cached_server(SMBCCTX * context, + SMBCSRV * server) +{ + struct smbc_server_cache * srv = NULL; + + for (srv = context->internal->server_cache; srv; srv = srv->next) { + if (server == srv->server) { + + /* remove this sucker */ + DLIST_REMOVE(context->internal->server_cache, srv); + SAFE_FREE(srv->server_name); + SAFE_FREE(srv->share_name); + SAFE_FREE(srv->workgroup); + SAFE_FREE(srv->username); + SAFE_FREE(srv); + return 0; + } + } + /* server not found */ + return 1; +} + + +/* + * Try to remove all the servers in cache + * returns 1 on failure and 0 if all servers could be removed. + */ +int +SMBC_purge_cached_servers(SMBCCTX * context) +{ + struct smbc_server_cache * srv; + struct smbc_server_cache * next; + int could_not_purge_all = 0; + + for (srv = context->internal->server_cache, + next = (srv ? srv->next :NULL); + srv; + srv = next, + next = (srv ? srv->next : NULL)) { + + if (SMBC_remove_unused_server(context, srv->server)) { + /* could not be removed */ + could_not_purge_all = 1; + } + } + return could_not_purge_all; +} diff --git a/source3/libsmb/libsmb_compat.c b/source3/libsmb/libsmb_compat.c new file mode 100644 index 0000000..c916122 --- /dev/null +++ b/source3/libsmb/libsmb_compat.c @@ -0,0 +1,580 @@ +/* + Unix SMB/CIFS implementation. + SMB client library implementation (Old interface compatibility) + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#include "libsmb_internal.h" + +struct smbc_compat_fdlist { + SMBCFILE * file; + int fd; + struct smbc_compat_fdlist *next, *prev; +}; + +static SMBCCTX * statcont = NULL; +static int smbc_compat_initialized = 0; +static int smbc_compat_nextfd = 0; +static struct smbc_compat_fdlist * smbc_compat_fd_in_use = NULL; +static struct smbc_compat_fdlist * smbc_compat_fd_avail = NULL; + +/* Find an fd and return the SMBCFILE * or NULL on failure */ +static SMBCFILE * +find_fd(int fd) +{ + struct smbc_compat_fdlist * f = smbc_compat_fd_in_use; + while (f) { + if (f->fd == fd) + return f->file; + f = f->next; + } + return NULL; +} + +/* Add an fd, returns 0 on success, -1 on error with errno set */ +static int +add_fd(SMBCFILE * file) +{ + struct smbc_compat_fdlist * f = smbc_compat_fd_avail; + + if (f) { + /* We found one that's available */ + DLIST_REMOVE(smbc_compat_fd_avail, f); + } else { + /* + * None were available, so allocate one. Keep the number of + * file descriptors determinate. This allows the application + * to allocate bitmaps or mapping of file descriptors based on + * a known maximum number of file descriptors that will ever + * be returned. + */ + if (smbc_compat_nextfd >= FD_SETSIZE) { + errno = EMFILE; + return -1; + } + + f = SMB_MALLOC_P(struct smbc_compat_fdlist); + if (!f) { + errno = ENOMEM; + return -1; + } + + f->fd = SMBC_BASE_FD + smbc_compat_nextfd++; + } + + f->file = file; + DLIST_ADD(smbc_compat_fd_in_use, f); + + return f->fd; +} + + + +/* Delete an fd, returns 0 on success */ +static int +del_fd(int fd) +{ + struct smbc_compat_fdlist * f = smbc_compat_fd_in_use; + + while (f) { + if (f->fd == fd) + break; + f = f->next; + } + + if (f) { + /* found */ + DLIST_REMOVE(smbc_compat_fd_in_use, f); + f->file = NULL; + DLIST_ADD(smbc_compat_fd_avail, f); + return 0; + } + return 1; +} + + + +int +smbc_init(smbc_get_auth_data_fn fn, + int debug) +{ + if (!smbc_compat_initialized) { + statcont = smbc_new_context(); + if (!statcont) + return -1; + + smbc_setDebug(statcont, debug); + smbc_setFunctionAuthData(statcont, fn); + + if (!smbc_init_context(statcont)) { + smbc_free_context(statcont, False); + return -1; + } + + smbc_compat_initialized = 1; + + return 0; + } + return 0; +} + + +SMBCCTX * +smbc_set_context(SMBCCTX * context) +{ + SMBCCTX *old_context = statcont; + + if (context) { + /* Save provided context. It must have been initialized! */ + statcont = context; + + /* You'd better know what you're doing. We won't help you. */ + smbc_compat_initialized = 1; + } + + return old_context; +} + + +int +smbc_open(const char *furl, + int flags, + mode_t mode) +{ + SMBCFILE * file; + int fd; + + file = smbc_getFunctionOpen(statcont)(statcont, furl, flags, mode); + if (!file) + return -1; + + fd = add_fd(file); + if (fd == -1) + smbc_getFunctionClose(statcont)(statcont, file); + return fd; +} + + +int +smbc_creat(const char *furl, + mode_t mode) +{ + SMBCFILE * file; + int fd; + + file = smbc_getFunctionCreat(statcont)(statcont, furl, mode); + if (!file) + return -1; + + fd = add_fd(file); + if (fd == -1) { + /* Hmm... should we delete the file too ? I guess we could try */ + smbc_getFunctionClose(statcont)(statcont, file); + smbc_getFunctionUnlink(statcont)(statcont, furl); + } + return fd; +} + + +ssize_t +smbc_read(int fd, + void *buf, + size_t bufsize) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionRead(statcont)(statcont, file, buf, bufsize); +} + +ssize_t +smbc_write(int fd, + const void *buf, + size_t bufsize) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionWrite(statcont)(statcont, file, buf, bufsize); +} + +off_t +smbc_lseek(int fd, + off_t offset, + int whence) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionLseek(statcont)(statcont, file, offset, whence); +} + +int +smbc_close(int fd) +{ + SMBCFILE * file = find_fd(fd); + del_fd(fd); + return smbc_getFunctionClose(statcont)(statcont, file); +} + +int +smbc_unlink(const char *fname) +{ + return smbc_getFunctionUnlink(statcont)(statcont, fname); +} + +int +smbc_rename(const char *ourl, + const char *nurl) +{ + return smbc_getFunctionRename(statcont)(statcont, ourl, + statcont, nurl); +} + +int +smbc_opendir(const char *durl) +{ + SMBCFILE * file; + int fd; + + file = smbc_getFunctionOpendir(statcont)(statcont, durl); + if (!file) + return -1; + + fd = add_fd(file); + if (fd == -1) + smbc_getFunctionClosedir(statcont)(statcont, file); + + return fd; +} + +int +smbc_closedir(int dh) +{ + SMBCFILE * file = find_fd(dh); + del_fd(dh); + return smbc_getFunctionClosedir(statcont)(statcont, file); +} + +int +smbc_getdents(unsigned int dh, + struct smbc_dirent *dirp, + int count) +{ + SMBCFILE * file = find_fd(dh); + return smbc_getFunctionGetdents(statcont)(statcont, file, dirp, count); +} + +struct smbc_dirent * +smbc_readdir(unsigned int dh) +{ + SMBCFILE * file = find_fd(dh); + return smbc_getFunctionReaddir(statcont)(statcont, file); +} + +const struct libsmb_file_info *smbc_readdirplus(unsigned int dh) +{ + SMBCFILE * file = find_fd(dh); + return smbc_getFunctionReaddirPlus(statcont)(statcont, file); +} + +const struct libsmb_file_info *smbc_readdirplus2(unsigned int dh, + struct stat *st) +{ + SMBCFILE *file = find_fd(dh); + return smbc_getFunctionReaddirPlus2(statcont)(statcont, file, st); +} + +off_t +smbc_telldir(int dh) +{ + SMBCFILE * file = find_fd(dh); + return smbc_getFunctionTelldir(statcont)(statcont, file); +} + +int +smbc_lseekdir(int fd, + off_t offset) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionLseekdir(statcont)(statcont, file, offset); +} + +int +smbc_mkdir(const char *durl, + mode_t mode) +{ + return smbc_getFunctionMkdir(statcont)(statcont, durl, mode); +} + +int +smbc_rmdir(const char *durl) +{ + return smbc_getFunctionRmdir(statcont)(statcont, durl); +} + +int +smbc_notify(int dh, smbc_bool recursive, uint32_t completion_filter, + unsigned callback_timeout_ms, + smbc_notify_callback_fn cb, void *private_data) +{ + SMBCFILE *dir = find_fd(dh); + return smbc_getFunctionNotify(statcont)( + statcont, dir, recursive, completion_filter, + callback_timeout_ms, cb, private_data); +} + +int +smbc_stat(const char *url, + struct stat *st) +{ + return smbc_getFunctionStat(statcont)(statcont, url, st); +} + +int +smbc_fstat(int fd, + struct stat *st) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionFstat(statcont)(statcont, file, st); +} + +int +smbc_statvfs(char *path, + struct statvfs *st) +{ + return smbc_getFunctionStatVFS(statcont)(statcont, path, st); +} + +int +smbc_fstatvfs(int fd, + struct statvfs *st) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionFstatVFS(statcont)(statcont, file, st); +} + +int +smbc_ftruncate(int fd, + off_t size) +{ + SMBCFILE * file = find_fd(fd); + return smbc_getFunctionFtruncate(statcont)(statcont, file, size); +} + +int +smbc_chmod(const char *url, + mode_t mode) +{ + return smbc_getFunctionChmod(statcont)(statcont, url, mode); +} + +int +smbc_utimes(const char *fname, + struct timeval *tbuf) +{ + return smbc_getFunctionUtimes(statcont)(statcont, fname, tbuf); +} + +#ifdef HAVE_UTIME_H +int +smbc_utime(const char *fname, + struct utimbuf *utbuf) +{ + struct timeval tv[2]; + + if (utbuf == NULL) + return smbc_getFunctionUtimes(statcont)(statcont, fname, NULL); + + tv[0].tv_sec = utbuf->actime; + tv[1].tv_sec = utbuf->modtime; + tv[0].tv_usec = tv[1].tv_usec = 0; + + return smbc_getFunctionUtimes(statcont)(statcont, fname, tv); +} +#endif + +int +smbc_setxattr(const char *fname, + const char *name, + const void *value, + size_t size, + int flags) +{ + return smbc_getFunctionSetxattr(statcont)(statcont, + fname, name, + value, size, flags); +} + +int +smbc_lsetxattr(const char *fname, + const char *name, + const void *value, + size_t size, + int flags) +{ + return smbc_getFunctionSetxattr(statcont)(statcont, + fname, name, + value, size, flags); +} + +int +smbc_fsetxattr(int fd, + const char *name, + const void *value, + size_t size, + int flags) +{ + SMBCFILE * file = find_fd(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + return smbc_getFunctionSetxattr(statcont)(statcont, + file->fname, name, + value, size, flags); +} + +int +smbc_getxattr(const char *fname, + const char *name, + const void *value, + size_t size) +{ + return smbc_getFunctionGetxattr(statcont)(statcont, + fname, name, + value, size); +} + +int +smbc_lgetxattr(const char *fname, + const char *name, + const void *value, + size_t size) +{ + return smbc_getFunctionGetxattr(statcont)(statcont, + fname, name, + value, size); +} + +int +smbc_fgetxattr(int fd, + const char *name, + const void *value, + size_t size) +{ + SMBCFILE * file = find_fd(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + return smbc_getFunctionGetxattr(statcont)(statcont, + file->fname, name, + value, size); +} + +int +smbc_removexattr(const char *fname, + const char *name) +{ + return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name); +} + +int +smbc_lremovexattr(const char *fname, + const char *name) +{ + return smbc_getFunctionRemovexattr(statcont)(statcont, fname, name); +} + +int +smbc_fremovexattr(int fd, + const char *name) +{ + SMBCFILE * file = find_fd(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + return smbc_getFunctionRemovexattr(statcont)(statcont, + file->fname, name); +} + +int +smbc_listxattr(const char *fname, + char *list, + size_t size) +{ + return smbc_getFunctionListxattr(statcont)(statcont, + fname, list, size); +} + +int +smbc_llistxattr(const char *fname, + char *list, + size_t size) +{ + return smbc_getFunctionListxattr(statcont)(statcont, + fname, list, size); +} + +int +smbc_flistxattr(int fd, + char *list, + size_t size) +{ + SMBCFILE * file = find_fd(fd); + if (file == NULL) { + errno = EBADF; + return -1; + } + return smbc_getFunctionListxattr(statcont)(statcont, + file->fname, list, size); +} + +int +smbc_print_file(const char *fname, + const char *printq) +{ + return smbc_getFunctionPrintFile(statcont)(statcont, fname, + statcont, printq); +} + +int +smbc_open_print_job(const char *fname) +{ + SMBCFILE * file; + + file = smbc_getFunctionOpenPrintJob(statcont)(statcont, fname); + if (!file) return -1; + return file->cli_fd; +} + +int +smbc_list_print_jobs(const char *purl, + smbc_list_print_job_fn fn) +{ + return smbc_getFunctionListPrintJobs(statcont)(statcont, purl, fn); +} + +int +smbc_unlink_print_job(const char *purl, + int id) +{ + return smbc_getFunctionUnlinkPrintJob(statcont)(statcont, purl, id); +} + + diff --git a/source3/libsmb/libsmb_context.c b/source3/libsmb/libsmb_context.c new file mode 100644 index 0000000..b92f14e --- /dev/null +++ b/source3/libsmb/libsmb_context.c @@ -0,0 +1,803 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" +#include "secrets.h" +#include "../libcli/smb/smbXcli_base.h" +#include "auth/credentials/credentials.h" +#include "auth/gensec/gensec.h" +#include "lib/param/param.h" +#include "../lib/util/smb_threads.h" +#include "../lib/util/smb_threads_internal.h" + +/* + * Is the logging working / configfile read ? + */ +static bool SMBC_initialized = false; +static unsigned int initialized_ctx_count = 0; +static void *initialized_ctx_count_mutex = NULL; + +/* + * Do some module- and library-wide intializations + */ +static void +SMBC_module_init(void * punused) +{ + bool conf_loaded = False; + char *home = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + setup_logging("libsmbclient", DEBUG_STDOUT); + + /* Here we would open the smb.conf file if needed ... */ + + home = getenv("HOME"); + if (home) { + char *conf = NULL; + if (asprintf(&conf, "%s/.smb/smb.conf", home) > 0) { + if (lp_load_client(conf)) { + conf_loaded = True; + } else { + DEBUG(5, ("Could not load config file: %s\n", + conf)); + } + SAFE_FREE(conf); + } + } + + if (!conf_loaded) { + /* + * Well, if that failed, try the get_dyn_CONFIGFILE + * Which points to the standard locn, and if that + * fails, silently ignore it and use the internal + * defaults ... + */ + + if (!lp_load_client(get_dyn_CONFIGFILE())) { + DEBUG(5, ("Could not load config file: %s\n", + get_dyn_CONFIGFILE())); + } else if (home) { + char *conf; + /* + * We loaded the global config file. Now lets + * load user-specific modifications to the + * global config. + */ + if (asprintf(&conf, + "%s/.smb/smb.conf.append", + home) > 0) { + if (!lp_load_client_no_reinit(conf)) { + DEBUG(10, + ("Could not append config file: " + "%s\n", + conf)); + } + SAFE_FREE(conf); + } + } + } + + load_interfaces(); /* Load the list of interfaces ... */ + + reopen_logs(); /* Get logging working ... */ + + /* + * Block SIGPIPE (from lib/util_sock.c: write()) + * It is not needed and should not stop execution + */ + BlockSignals(True, SIGPIPE); + + /* Create the mutex we'll use to protect initialized_ctx_count */ + if (SMB_THREAD_CREATE_MUTEX("initialized_ctx_count_mutex", + initialized_ctx_count_mutex) != 0) { + smb_panic("SMBC_module_init: " + "failed to create 'initialized_ctx_count' mutex"); + } + + + TALLOC_FREE(frame); +} + + +static void +SMBC_module_terminate(void) +{ + TALLOC_CTX *frame = talloc_stackframe(); + secrets_shutdown(); + gfree_all(); + SMBC_initialized = false; + TALLOC_FREE(frame); +} + + +/* + * Get a new empty handle to fill in with your own info + */ +SMBCCTX * +smbc_new_context(void) +{ + SMBCCTX *context; + TALLOC_CTX *frame = talloc_stackframe(); + + /* The first call to this function should initialize the module */ + SMB_THREAD_ONCE(&SMBC_initialized, SMBC_module_init, NULL); + + /* + * All newly added context fields should be placed in + * SMBC_internal_data, not directly in SMBCCTX. + */ + context = SMB_MALLOC_P(SMBCCTX); + if (!context) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + + ZERO_STRUCTP(context); + + context->internal = SMB_MALLOC_P(struct SMBC_internal_data); + if (!context->internal) { + TALLOC_FREE(frame); + SAFE_FREE(context); + errno = ENOMEM; + return NULL; + } + + /* Initialize the context and establish reasonable defaults */ + ZERO_STRUCTP(context->internal); + + smbc_setDebug(context, 0); + smbc_setTimeout(context, 20000); + smbc_setPort(context, 0); + + smbc_setOptionFullTimeNames(context, False); + smbc_setOptionOpenShareMode(context, SMBC_SHAREMODE_DENY_NONE); + smbc_setOptionSmbEncryptionLevel(context, SMBC_ENCRYPTLEVEL_DEFAULT); + smbc_setOptionUseCCache(context, True); + smbc_setOptionCaseSensitive(context, False); + smbc_setOptionBrowseMaxLmbCount(context, 3); /* # LMBs to query */ + smbc_setOptionUrlEncodeReaddirEntries(context, False); + smbc_setOptionOneSharePerServer(context, False); + if (getenv("LIBSMBCLIENT_NO_CCACHE") != NULL) { + smbc_setOptionUseCCache(context, false); + } + + smbc_setFunctionAuthData(context, SMBC_get_auth_data); + smbc_setFunctionCheckServer(context, SMBC_check_server); + smbc_setFunctionRemoveUnusedServer(context, SMBC_remove_unused_server); + + smbc_setOptionUserData(context, NULL); + smbc_setFunctionAddCachedServer(context, SMBC_add_cached_server); + smbc_setFunctionGetCachedServer(context, SMBC_get_cached_server); + smbc_setFunctionRemoveCachedServer(context, SMBC_remove_cached_server); + smbc_setFunctionPurgeCachedServers(context, SMBC_purge_cached_servers); + + smbc_setFunctionOpen(context, SMBC_open_ctx); + smbc_setFunctionCreat(context, SMBC_creat_ctx); + smbc_setFunctionRead(context, SMBC_read_ctx); + smbc_setFunctionSplice(context, SMBC_splice_ctx); + smbc_setFunctionWrite(context, SMBC_write_ctx); + smbc_setFunctionClose(context, SMBC_close_ctx); + smbc_setFunctionUnlink(context, SMBC_unlink_ctx); + smbc_setFunctionRename(context, SMBC_rename_ctx); + smbc_setFunctionLseek(context, SMBC_lseek_ctx); + smbc_setFunctionFtruncate(context, SMBC_ftruncate_ctx); + smbc_setFunctionStat(context, SMBC_stat_ctx); + smbc_setFunctionStatVFS(context, SMBC_statvfs_ctx); + smbc_setFunctionFstatVFS(context, SMBC_fstatvfs_ctx); + smbc_setFunctionFstat(context, SMBC_fstat_ctx); + smbc_setFunctionOpendir(context, SMBC_opendir_ctx); + smbc_setFunctionClosedir(context, SMBC_closedir_ctx); + smbc_setFunctionReaddir(context, SMBC_readdir_ctx); + smbc_setFunctionReaddirPlus(context, SMBC_readdirplus_ctx); + smbc_setFunctionReaddirPlus2(context, SMBC_readdirplus2_ctx); + smbc_setFunctionGetdents(context, SMBC_getdents_ctx); + smbc_setFunctionMkdir(context, SMBC_mkdir_ctx); + smbc_setFunctionRmdir(context, SMBC_rmdir_ctx); + smbc_setFunctionTelldir(context, SMBC_telldir_ctx); + smbc_setFunctionLseekdir(context, SMBC_lseekdir_ctx); + smbc_setFunctionFstatdir(context, SMBC_fstatdir_ctx); + smbc_setFunctionNotify(context, SMBC_notify_ctx); + smbc_setFunctionChmod(context, SMBC_chmod_ctx); + smbc_setFunctionUtimes(context, SMBC_utimes_ctx); + smbc_setFunctionSetxattr(context, SMBC_setxattr_ctx); + smbc_setFunctionGetxattr(context, SMBC_getxattr_ctx); + smbc_setFunctionRemovexattr(context, SMBC_removexattr_ctx); + smbc_setFunctionListxattr(context, SMBC_listxattr_ctx); + + smbc_setFunctionOpenPrintJob(context, SMBC_open_print_job_ctx); + smbc_setFunctionPrintFile(context, SMBC_print_file_ctx); + smbc_setFunctionListPrintJobs(context, SMBC_list_print_jobs_ctx); + smbc_setFunctionUnlinkPrintJob(context, SMBC_unlink_print_job_ctx); + + TALLOC_FREE(frame); + return context; +} + +/* + * Free a context + * + * Returns 0 on success. Otherwise returns 1, the SMBCCTX is _not_ freed + * and thus you'll be leaking memory if not handled properly. + * + */ +int +smbc_free_context(SMBCCTX *context, + int shutdown_ctx) +{ + TALLOC_CTX *frame; + if (!context) { + errno = EBADF; + return 1; + } + + frame = talloc_stackframe(); + + if (shutdown_ctx) { + SMBCFILE * f; + DEBUG(1,("Performing aggressive shutdown.\n")); + + f = context->internal->files; + while (f) { + SMBCFILE *next = f->next; + smbc_getFunctionClose(context)(context, f); + f = next; + } + context->internal->files = NULL; + + /* First try to remove the servers the nice way. */ + if (smbc_getFunctionPurgeCachedServers(context)(context)) { + SMBCSRV * s; + SMBCSRV * next; + DEBUG(1, ("Could not purge all servers, " + "Nice way shutdown failed.\n")); + s = context->internal->servers; + while (s) { + DEBUG(1, ("Forced shutdown: %p (cli=%p)\n", + s, s->cli)); + cli_shutdown(s->cli); + smbc_getFunctionRemoveCachedServer(context)(context, + s); + next = s->next; + DLIST_REMOVE(context->internal->servers, s); + SAFE_FREE(s); + s = next; + } + context->internal->servers = NULL; + } + } + else { + /* This is the polite way */ + if (smbc_getFunctionPurgeCachedServers(context)(context)) { + DEBUG(1, ("Could not purge all servers, " + "free_context failed.\n")); + errno = EBUSY; + TALLOC_FREE(frame); + return 1; + } + if (context->internal->servers) { + DEBUG(1, ("Active servers in context, " + "free_context failed.\n")); + errno = EBUSY; + TALLOC_FREE(frame); + return 1; + } + if (context->internal->files) { + DEBUG(1, ("Active files in context, " + "free_context failed.\n")); + errno = EBUSY; + TALLOC_FREE(frame); + return 1; + } + } + + /* Things we have to clean up */ + smbc_setWorkgroup(context, NULL); + smbc_setNetbiosName(context, NULL); + smbc_setUser(context, NULL); + + DEBUG(3, ("Context %p successfully freed\n", context)); + + /* Free any DFS auth context. */ + TALLOC_FREE(context->internal->creds); + + SAFE_FREE(context->internal); + SAFE_FREE(context); + + /* Protect access to the count of contexts in use */ + if (SMB_THREAD_LOCK(initialized_ctx_count_mutex) != 0) { + smb_panic("error locking 'initialized_ctx_count'"); + } + + if (initialized_ctx_count) { + initialized_ctx_count--; + } + + if (initialized_ctx_count == 0) { + SMBC_module_terminate(); + } + + /* Unlock the mutex */ + if (SMB_THREAD_UNLOCK(initialized_ctx_count_mutex) != 0) { + smb_panic("error unlocking 'initialized_ctx_count'"); + } + + TALLOC_FREE(frame); + return 0; +} + + +/** + * Deprecated interface. Do not use. Instead, use the various + * smbc_setOption*() functions or smbc_setFunctionAuthDataWithContext(). + */ +void +smbc_option_set(SMBCCTX *context, + char *option_name, + ... /* option_value */) +{ + va_list ap; + union { + int i; + bool b; + smbc_get_auth_data_with_context_fn auth_fn; + void *v; + const char *s; + } option_value; + + TALLOC_CTX *frame = talloc_stackframe(); + + va_start(ap, option_name); + + if (strcmp(option_name, "debug_to_stderr") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionDebugToStderr(context, option_value.b); + + } else if (strcmp(option_name, "full_time_names") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionFullTimeNames(context, option_value.b); + + } else if (strcmp(option_name, "open_share_mode") == 0) { + option_value.i = va_arg(ap, int); + smbc_setOptionOpenShareMode(context, option_value.i); + + } else if (strcmp(option_name, "auth_function") == 0) { + option_value.auth_fn = + va_arg(ap, smbc_get_auth_data_with_context_fn); + smbc_setFunctionAuthDataWithContext(context, option_value.auth_fn); + + } else if (strcmp(option_name, "user_data") == 0) { + option_value.v = va_arg(ap, void *); + smbc_setOptionUserData(context, option_value.v); + + } else if (strcmp(option_name, "smb_encrypt_level") == 0) { + option_value.s = va_arg(ap, const char *); + if (strcmp(option_value.s, "none") == 0) { + smbc_setOptionSmbEncryptionLevel(context, + SMBC_ENCRYPTLEVEL_NONE); + } else if (strcmp(option_value.s, "request") == 0) { + smbc_setOptionSmbEncryptionLevel(context, + SMBC_ENCRYPTLEVEL_REQUEST); + } else if (strcmp(option_value.s, "require") == 0) { + smbc_setOptionSmbEncryptionLevel(context, + SMBC_ENCRYPTLEVEL_REQUIRE); + } + + } else if (strcmp(option_name, "browse_max_lmb_count") == 0) { + option_value.i = va_arg(ap, int); + smbc_setOptionBrowseMaxLmbCount(context, option_value.i); + + } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionUrlEncodeReaddirEntries(context, option_value.b); + + } else if (strcmp(option_name, "one_share_per_server") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionOneSharePerServer(context, option_value.b); + + } else if (strcmp(option_name, "use_kerberos") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionUseKerberos(context, option_value.b); + + } else if (strcmp(option_name, "fallback_after_kerberos") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionFallbackAfterKerberos(context, option_value.b); + + } else if (strcmp(option_name, "use_ccache") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionUseCCache(context, option_value.b); + + } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) { + option_value.b = (bool) va_arg(ap, int); + smbc_setOptionNoAutoAnonymousLogin(context, option_value.b); + } + + va_end(ap); + TALLOC_FREE(frame); +} + + +/* + * Deprecated interface. Do not use. Instead, use the various + * smbc_getOption*() functions. + */ +void * +smbc_option_get(SMBCCTX *context, + char *option_name) +{ + if (strcmp(option_name, "debug_stderr") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionDebugToStderr(context); +#else + return (void *) smbc_getOptionDebugToStderr(context); +#endif + + } else if (strcmp(option_name, "full_time_names") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionFullTimeNames(context); +#else + return (void *) smbc_getOptionFullTimeNames(context); +#endif + + } else if (strcmp(option_name, "open_share_mode") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionOpenShareMode(context); +#else + return (void *) smbc_getOptionOpenShareMode(context); +#endif + + } else if (strcmp(option_name, "auth_function") == 0) { + return (void *) smbc_getFunctionAuthDataWithContext(context); + + } else if (strcmp(option_name, "user_data") == 0) { + return smbc_getOptionUserData(context); + + } else if (strcmp(option_name, "smb_encrypt_level") == 0) { + switch(smbc_getOptionSmbEncryptionLevel(context)) + { + case SMBC_ENCRYPTLEVEL_DEFAULT: + return discard_const_p(void, "default"); + case 0: + return discard_const_p(void, "none"); + case 1: + return discard_const_p(void, "request"); + case 2: + return discard_const_p(void, "require"); + } + + } else if (strcmp(option_name, "smb_encrypt_on") == 0) { + SMBCSRV *s; + unsigned int num_servers = 0; + + for (s = context->internal->servers; s; s = s->next) { + num_servers++; + if (!cli_state_is_encryption_on(s->cli)) { + return (void *)false; + } + } +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) (bool) (num_servers > 0); +#else + return (void *) (bool) (num_servers > 0); +#endif + + } else if (strcmp(option_name, "browse_max_lmb_count") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionBrowseMaxLmbCount(context); +#else + return (void *) smbc_getOptionBrowseMaxLmbCount(context); +#endif + + } else if (strcmp(option_name, "urlencode_readdir_entries") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *)(intptr_t) smbc_getOptionUrlEncodeReaddirEntries(context); +#else + return (void *) (bool) smbc_getOptionUrlEncodeReaddirEntries(context); +#endif + + } else if (strcmp(option_name, "one_share_per_server") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionOneSharePerServer(context); +#else + return (void *) (bool) smbc_getOptionOneSharePerServer(context); +#endif + + } else if (strcmp(option_name, "use_kerberos") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionUseKerberos(context); +#else + return (void *) (bool) smbc_getOptionUseKerberos(context); +#endif + + } else if (strcmp(option_name, "fallback_after_kerberos") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *)(intptr_t) smbc_getOptionFallbackAfterKerberos(context); +#else + return (void *) (bool) smbc_getOptionFallbackAfterKerberos(context); +#endif + + } else if (strcmp(option_name, "use_ccache") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionUseCCache(context); +#else + return (void *) (bool) smbc_getOptionUseCCache(context); +#endif + + } else if (strcmp(option_name, "no_auto_anonymous_login") == 0) { +#if defined(__intptr_t_defined) || defined(HAVE_INTPTR_T) + return (void *) (intptr_t) smbc_getOptionNoAutoAnonymousLogin(context); +#else + return (void *) (bool) smbc_getOptionNoAutoAnonymousLogin(context); +#endif + } + + return NULL; +} + + +/* + * Initialize the library, etc. + * + * We accept a struct containing handle information. + * valid values for info->debug from 0 to 100, + * and insist that info->fn must be non-null. + */ +SMBCCTX * +smbc_init_context(SMBCCTX *context) +{ + int pid; + TALLOC_CTX *frame; + + if (!context) { + errno = EBADF; + return NULL; + } + + /* Do not initialise the same client twice */ + if (context->internal->initialized) { + return NULL; + } + + frame = talloc_stackframe(); + + if ((!smbc_getFunctionAuthData(context) && + !smbc_getFunctionAuthDataWithContext(context)) || + smbc_getDebug(context) < 0 || + smbc_getDebug(context) > 100) { + + TALLOC_FREE(frame); + errno = EINVAL; + return NULL; + + } + + if (!smbc_getUser(context)) { + /* + * FIXME: Is this the best way to get the user info? + */ + char *user = getenv("USER"); + /* walk around as "guest" if no username can be found */ + if (!user) { + user = SMB_STRDUP("guest"); + } else { + user = SMB_STRDUP(user); + } + + if (!user) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + + smbc_setUser(context, user); + SAFE_FREE(user); + + if (!smbc_getUser(context)) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + } + + if (!smbc_getNetbiosName(context)) { + /* + * We try to get our netbios name from the config. If that + * fails we fall back on constructing our netbios name from + * our hostname etc + */ + char *netbios_name; + if (lp_netbios_name()) { + netbios_name = SMB_STRDUP(lp_netbios_name()); + } else { + /* + * Hmmm, I want to get hostname as well, but I am too + * lazy for the moment + */ + pid = getpid(); + netbios_name = (char *)SMB_MALLOC(17); + if (!netbios_name) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + slprintf(netbios_name, 16, + "smbc%s%d", smbc_getUser(context), pid); + } + + if (!netbios_name) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + + smbc_setNetbiosName(context, netbios_name); + SAFE_FREE(netbios_name); + + if (!smbc_getNetbiosName(context)) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + } + + DEBUG(1, ("Using netbios name %s.\n", smbc_getNetbiosName(context))); + + if (!smbc_getWorkgroup(context)) { + const char *workgroup; + + if (lp_workgroup()) { + workgroup = lp_workgroup(); + } else { + /* TODO: Think about a decent default workgroup */ + workgroup = "samba"; + } + + smbc_setWorkgroup(context, workgroup); + + if (!smbc_getWorkgroup(context)) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + } + + DEBUG(1, ("Using workgroup %s.\n", smbc_getWorkgroup(context))); + + /* shortest timeout is 1 second */ + if (smbc_getTimeout(context) > 0 && smbc_getTimeout(context) < 1000) + smbc_setTimeout(context, 1000); + + context->internal->initialized = True; + + /* Protect access to the count of contexts in use */ + if (SMB_THREAD_LOCK(initialized_ctx_count_mutex) != 0) { + smb_panic("error locking 'initialized_ctx_count'"); + } + + initialized_ctx_count++; + + /* Unlock the mutex */ + if (SMB_THREAD_UNLOCK(initialized_ctx_count_mutex) != 0) { + smb_panic("error unlocking 'initialized_ctx_count'"); + } + + TALLOC_FREE(frame); + return context; +} + + +/* Return the version of samba, and thus libsmbclient */ +const char * +smbc_version(void) +{ + return samba_version_string(); +} + +/* + * Set the credentials so DFS will work when following referrals. + * This function is broken and must be removed. No SMBCCTX arg... + * JRA. + */ + +void +smbc_set_credentials(const char *workgroup, + const char *user, + const char *password, + smbc_bool use_kerberos, + const char *signing_state) +{ + d_printf("smbc_set_credentials is obsolete. Replace with smbc_set_credentials_with_fallback().\n"); +} + +void smbc_set_credentials_with_fallback(SMBCCTX *context, + const char *workgroup, + const char *user, + const char *password) +{ + struct loadparm_context *lp_ctx = NULL; + struct cli_credentials *creds = NULL; + enum credentials_use_kerberos kerberos_state = + CRED_USE_KERBEROS_DISABLED; + + if (! context) { + + return; + } + + if (! workgroup || ! *workgroup) { + workgroup = smbc_getWorkgroup(context); + } + + if (! user) { + user = smbc_getUser(context); + } + + if (! password) { + password = ""; + } + + creds = cli_credentials_init(NULL); + if (creds == NULL) { + DEBUG(0, ("smbc_set_credentials_with_fallback: allocation fail\n")); + return; + } + + lp_ctx = loadparm_init_s3(creds, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + TALLOC_FREE(creds); + return; + } + + cli_credentials_set_conf(creds, lp_ctx); + + if (smbc_getOptionUseKerberos(context)) { + kerberos_state = CRED_USE_KERBEROS_REQUIRED; + + if (smbc_getOptionFallbackAfterKerberos(context)) { + kerberos_state = CRED_USE_KERBEROS_DESIRED; + } + } + + cli_credentials_set_username(creds, user, CRED_SPECIFIED); + cli_credentials_set_password(creds, password, CRED_SPECIFIED); + cli_credentials_set_domain(creds, workgroup, CRED_SPECIFIED); + cli_credentials_set_kerberos_state(creds, + kerberos_state, + CRED_SPECIFIED); + if (smbc_getOptionUseCCache(context)) { + uint32_t gensec_features; + + gensec_features = cli_credentials_get_gensec_features(creds); + gensec_features |= GENSEC_FEATURE_NTLM_CCACHE; + cli_credentials_set_gensec_features(creds, + gensec_features, + CRED_SPECIFIED); + } + + TALLOC_FREE(context->internal->creds); + context->internal->creds = creds; +} diff --git a/source3/libsmb/libsmb_dir.c b/source3/libsmb/libsmb_dir.c new file mode 100644 index 0000000..bf7b8f8 --- /dev/null +++ b/source3/libsmb/libsmb_dir.c @@ -0,0 +1,2726 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/namequery.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_srvsvc_c.h" +#include "libsmb/nmblib.h" +#include "../libcli/smb/smbXcli_base.h" +#include "../libcli/security/security.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/util/time_basic.h" +#include "lib/util/string_wrappers.h" + +/* + * Routine to open a directory + * We accept the URL syntax explained in SMBC_parse_path(), above. + */ + +static void remove_dirplus(SMBCFILE *dir) +{ + struct smbc_dirplus_list *d = NULL; + + d = dir->dirplus_list; + while (d != NULL) { + struct smbc_dirplus_list *f = d; + d = d->next; + + SAFE_FREE(f->smb_finfo->short_name); + SAFE_FREE(f->smb_finfo->name); + SAFE_FREE(f->smb_finfo); + SAFE_FREE(f); + } + + dir->dirplus_list = NULL; + dir->dirplus_end = NULL; + dir->dirplus_next = NULL; +} + +static void +remove_dir(SMBCFILE *dir) +{ + struct smbc_dir_list *d,*f; + + d = dir->dir_list; + while (d) { + + f = d; d = d->next; + + SAFE_FREE(f->dirent); + SAFE_FREE(f); + + } + + dir->dir_list = dir->dir_end = dir->dir_next = NULL; + +} + +static int +add_dirent(SMBCFILE *dir, + const char *name, + const char *comment, + uint32_t type) +{ + struct smbc_dirent *dirent; + int size; + int name_length = (name == NULL ? 0 : strlen(name)); + int comment_len = (comment == NULL ? 0 : strlen(comment)); + + /* + * Allocate space for the dirent, which must be increased by the + * size of the name and the comment and 1 each for the null terminator. + */ + + size = sizeof(struct smbc_dirent) + name_length + comment_len + 2; + + dirent = (struct smbc_dirent *)SMB_MALLOC(size); + + if (!dirent) { + + dir->dir_error = ENOMEM; + return -1; + + } + + ZERO_STRUCTP(dirent); + + if (dir->dir_list == NULL) { + + dir->dir_list = SMB_MALLOC_P(struct smbc_dir_list); + if (!dir->dir_list) { + + SAFE_FREE(dirent); + dir->dir_error = ENOMEM; + return -1; + + } + ZERO_STRUCTP(dir->dir_list); + + dir->dir_end = dir->dir_next = dir->dir_list; + } + else { + + dir->dir_end->next = SMB_MALLOC_P(struct smbc_dir_list); + + if (!dir->dir_end->next) { + + SAFE_FREE(dirent); + dir->dir_error = ENOMEM; + return -1; + + } + ZERO_STRUCTP(dir->dir_end->next); + + dir->dir_end = dir->dir_end->next; + } + + dir->dir_end->next = NULL; + dir->dir_end->dirent = dirent; + + dirent->smbc_type = type; + dirent->namelen = name_length; + dirent->commentlen = comment_len; + dirent->dirlen = size; + + /* + * dirent->namelen + 1 includes the null (no null termination needed) + * Ditto for dirent->commentlen. + * The space for the two null bytes was allocated. + */ + strncpy(dirent->name, (name?name:""), dirent->namelen + 1); + dirent->comment = (char *)(&dirent->name + dirent->namelen + 1); + strncpy(dirent->comment, (comment?comment:""), dirent->commentlen + 1); + + return 0; + +} + +static int add_dirplus(SMBCFILE *dir, struct file_info *finfo) +{ + struct smbc_dirplus_list *new_entry = NULL; + struct libsmb_file_info *info = NULL; + + new_entry = SMB_MALLOC_P(struct smbc_dirplus_list); + if (new_entry == NULL) { + dir->dir_error = ENOMEM; + return -1; + } + ZERO_STRUCTP(new_entry); + new_entry->ino = finfo->ino; + + info = SMB_MALLOC_P(struct libsmb_file_info); + if (info == NULL) { + SAFE_FREE(new_entry); + dir->dir_error = ENOMEM; + return -1; + } + + ZERO_STRUCTP(info); + + info->btime_ts = finfo->btime_ts; + info->atime_ts = finfo->atime_ts; + info->ctime_ts = finfo->ctime_ts; + info->mtime_ts = finfo->mtime_ts; + info->gid = finfo->gid; + info->attrs = finfo->attr; + info->size = finfo->size; + info->uid = finfo->uid; + info->name = SMB_STRDUP(finfo->name); + if (info->name == NULL) { + SAFE_FREE(info); + SAFE_FREE(new_entry); + dir->dir_error = ENOMEM; + return -1; + } + + if (finfo->short_name) { + info->short_name = SMB_STRDUP(finfo->short_name); + } else { + info->short_name = SMB_STRDUP(""); + } + + if (info->short_name == NULL) { + SAFE_FREE(info->name); + SAFE_FREE(info); + SAFE_FREE(new_entry); + dir->dir_error = ENOMEM; + return -1; + } + new_entry->smb_finfo = info; + + /* Now add to the list. */ + if (dir->dirplus_list == NULL) { + /* Empty list - point everything at new_entry. */ + dir->dirplus_list = new_entry; + dir->dirplus_end = new_entry; + dir->dirplus_next = new_entry; + } else { + /* Append to list but leave the ->next cursor alone. */ + dir->dirplus_end->next = new_entry; + dir->dirplus_end = new_entry; + } + + return 0; +} + +static void +list_unique_wg_fn(const char *name, + uint32_t type, + const char *comment, + void *state) +{ + SMBCFILE *dir = (SMBCFILE *)state; + struct smbc_dir_list *dir_list; + struct smbc_dirent *dirent; + int dirent_type; + int do_remove = 0; + + dirent_type = dir->dir_type; + + if (add_dirent(dir, name, comment, dirent_type) < 0) { + /* An error occurred, what do we do? */ + /* FIXME: Add some code here */ + /* Change cli_NetServerEnum to take a fn + returning NTSTATUS... JRA. */ + } + + /* Point to the one just added */ + dirent = dir->dir_end->dirent; + + /* See if this was a duplicate */ + for (dir_list = dir->dir_list; + dir_list != dir->dir_end; + dir_list = dir_list->next) { + if (! do_remove && + strcmp(dir_list->dirent->name, dirent->name) == 0) { + /* Duplicate. End end of list need to be removed. */ + do_remove = 1; + } + + if (do_remove && dir_list->next == dir->dir_end) { + /* Found the end of the list. Remove it. */ + dir->dir_end = dir_list; + free(dir_list->next); + free(dirent); + dir_list->next = NULL; + break; + } + } +} + +static void +list_fn(const char *name, + uint32_t type, + const char *comment, + void *state) +{ + SMBCFILE *dir = (SMBCFILE *)state; + int dirent_type; + + /* + * We need to process the type a little ... + * + * Disk share = 0x00000000 + * Print share = 0x00000001 + * Comms share = 0x00000002 (obsolete?) + * IPC$ share = 0x00000003 + * + * administrative shares: + * ADMIN$, IPC$, C$, D$, E$ ... are type |= 0x80000000 + */ + + if (dir->dir_type == SMBC_FILE_SHARE) { + switch (type) { + case 0 | 0x80000000: + case 0: + dirent_type = SMBC_FILE_SHARE; + break; + + case 1: + dirent_type = SMBC_PRINTER_SHARE; + break; + + case 2: + dirent_type = SMBC_COMMS_SHARE; + break; + + case 3 | 0x80000000: + case 3: + dirent_type = SMBC_IPC_SHARE; + break; + + default: + dirent_type = SMBC_FILE_SHARE; /* FIXME, error? */ + break; + } + } + else { + dirent_type = dir->dir_type; + } + + if (add_dirent(dir, name, comment, dirent_type) < 0) { + /* An error occurred, what do we do? */ + /* FIXME: Add some code here */ + /* Change cli_NetServerEnum to take a fn + returning NTSTATUS... JRA. */ + } +} + +static NTSTATUS +dir_list_fn(struct file_info *finfo, + const char *mask, + void *state) +{ + SMBCFILE *dirp = (SMBCFILE *)state; + int ret; + + if (add_dirent((SMBCFILE *)state, finfo->name, "", + (finfo->attr&FILE_ATTRIBUTE_DIRECTORY?SMBC_DIR:SMBC_FILE)) < 0) { + SMBCFILE *dir = (SMBCFILE *)state; + return map_nt_error_from_unix(dir->dir_error); + } + ret = add_dirplus(dirp, finfo); + if (ret < 0) { + return map_nt_error_from_unix(dirp->dir_error); + } + return NT_STATUS_OK; +} + +static NTSTATUS +net_share_enum_rpc(struct cli_state *cli, + void (*fn)(const char *name, + uint32_t type, + const char *comment, + void *state), + void *state) +{ + uint32_t i; + WERROR result; + uint32_t preferred_len = 0xffffffff; + uint32_t type; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr1 ctr1; + fstring name = ""; + fstring comment = ""; + struct rpc_pipe_client *pipe_hnd = NULL; + NTSTATUS nt_status; + uint32_t resume_handle = 0; + uint32_t total_entries = 0; + struct dcerpc_binding_handle *b; + + /* Open the server service pipe */ + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc, + &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("net_share_enum_rpc pipe open fail!\n")); + goto done; + } + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(ctr1); + + info_ctr.level = 1; + info_ctr.ctr.ctr1 = &ctr1; + + b = pipe_hnd->binding_handle; + + /* Issue the NetShareEnum RPC call and retrieve the response */ + nt_status = dcerpc_srvsvc_NetShareEnumAll(b, talloc_tos(), + pipe_hnd->desthost, + &info_ctr, + preferred_len, + &total_entries, + &resume_handle, + &result); + + /* Was it successful? */ + if (!NT_STATUS_IS_OK(nt_status)) { + /* Nope. Go clean up. */ + goto done; + } + + if (!W_ERROR_IS_OK(result)) { + /* Nope. Go clean up. */ + nt_status = werror_to_ntstatus(result); + goto done; + } + + if (total_entries == 0) { + /* Nope. Go clean up. */ + nt_status = NT_STATUS_NOT_FOUND; + goto done; + } + + /* For each returned entry... */ + for (i = 0; i < info_ctr.ctr.ctr1->count; i++) { + + /* pull out the share name */ + fstrcpy(name, info_ctr.ctr.ctr1->array[i].name); + + /* pull out the share's comment */ + fstrcpy(comment, info_ctr.ctr.ctr1->array[i].comment); + + /* Get the type value */ + type = info_ctr.ctr.ctr1->array[i].type; + + /* Add this share to the list */ + (*fn)(name, type, comment, state); + } + +done: + /* Close the server service pipe */ + TALLOC_FREE(pipe_hnd); + + /* Tell 'em if it worked */ + return nt_status; +} + + +/* + * Verify that the options specified in a URL are valid + */ +int +SMBC_check_options(char *server, + char *share, + char *path, + char *options) +{ + DEBUG(4, ("SMBC_check_options(): server='%s' share='%s' " + "path='%s' options='%s'\n", + server, share, path, options)); + + /* No options at all is always ok */ + if (! *options) return 0; + + /* Currently, we don't support any options. */ + return -1; +} + + +SMBCFILE * +SMBC_opendir_ctx(SMBCCTX *context, + const char *fname) +{ + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *options = NULL; + char *workgroup = NULL; + char *path = NULL; + size_t path_len = 0; + uint16_t port = 0; + SMBCSRV *srv = NULL; + SMBCFILE *dir = NULL; + struct sockaddr_storage rem_ss; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + DEBUG(4, ("no valid context\n")); + TALLOC_FREE(frame); + errno = EINVAL + 8192; + return NULL; + + } + + if (!fname) { + DEBUG(4, ("no valid fname\n")); + TALLOC_FREE(frame); + errno = EINVAL + 8193; + return NULL; + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + &options)) { + DEBUG(4, ("no valid path\n")); + TALLOC_FREE(frame); + errno = EINVAL + 8194; + return NULL; + } + + DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' " + "path='%s' options='%s'\n", + fname, server, share, path, options)); + + /* Ensure the options are valid */ + if (SMBC_check_options(server, share, path, options)) { + DEBUG(4, ("unacceptable options (%s)\n", options)); + TALLOC_FREE(frame); + errno = EINVAL + 8195; + return NULL; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + } + + dir = SMB_MALLOC_P(SMBCFILE); + + if (!dir) { + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + + ZERO_STRUCTP(dir); + + dir->cli_fd = 0; + dir->fname = SMB_STRDUP(fname); + if (dir->fname == NULL) { + SAFE_FREE(dir); + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + dir->srv = NULL; + dir->offset = 0; + dir->file = False; + dir->dir_list = dir->dir_next = dir->dir_end = NULL; + + if (server[0] == (char)0) { + + size_t i; + size_t count = 0; + size_t max_lmb_count; + struct sockaddr_storage *ip_list; + struct sockaddr_storage server_addr; + struct cli_credentials *creds = NULL; + NTSTATUS status; + + if (share[0] != (char)0 || path[0] != (char)0) { + + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = EINVAL + 8196; + return NULL; + } + + /* Determine how many local master browsers to query */ + max_lmb_count = (smbc_getOptionBrowseMaxLmbCount(context) == 0 + ? INT_MAX + : smbc_getOptionBrowseMaxLmbCount(context)); + + creds = cli_credentials_init(frame); + if (creds == NULL) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + + (void)cli_credentials_set_username(creds, user, CRED_SPECIFIED); + (void)cli_credentials_set_password(creds, password, CRED_SPECIFIED); + + /* + * We have server and share and path empty but options + * requesting that we scan all master browsers for their list + * of workgroups/domains. This implies that we must first try + * broadcast queries to find all master browsers, and if that + * doesn't work, then try our other methods which return only + * a single master browser. + */ + + ip_list = NULL; + status = name_resolve_bcast(talloc_tos(), + MSBROWSE, + 1, + &ip_list, + &count); + if (!NT_STATUS_IS_OK(status)) + { + + TALLOC_FREE(ip_list); + + if (!find_master_ip(workgroup, &server_addr)) { + + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = ENOENT; + return NULL; + } + + ip_list = (struct sockaddr_storage *)talloc_memdup( + talloc_tos(), &server_addr, + sizeof(server_addr)); + if (ip_list == NULL) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + count = 1; + } + + for (i = 0; i < count && i < max_lmb_count; i++) { + char addr[INET6_ADDRSTRLEN]; + char *wg_ptr = NULL; + struct cli_state *cli = NULL; + + print_sockaddr(addr, sizeof(addr), &ip_list[i]); + DEBUG(99, ("Found master browser %zu of %zu: %s\n", + i+1, MAX(count, max_lmb_count), + addr)); + + cli = get_ipc_connect_master_ip(talloc_tos(), + &ip_list[i], + creds, + &wg_ptr); + /* cli == NULL is the master browser refused to talk or + could not be found */ + if (!cli) { + continue; + } + + workgroup = talloc_strdup(frame, wg_ptr); + server = talloc_strdup(frame, smbXcli_conn_remote_name(cli->conn)); + + cli_shutdown(cli); + + if (!workgroup || !server) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = ENOMEM; + return NULL; + } + + DEBUG(4, ("using workgroup %s %s\n", + workgroup, server)); + + /* + * For each returned master browser IP address, get a + * connection to IPC$ on the server if we do not + * already have one, and determine the + * workgroups/domains that it knows about. + */ + + srv = SMBC_server(frame, context, True, server, port, "IPC$", + &workgroup, &user, &password); + if (!srv) { + continue; + } + + if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) { + continue; + } + + dir->srv = srv; + dir->dir_type = SMBC_WORKGROUP; + + /* Now, list the stuff ... */ + + if (!cli_NetServerEnum(srv->cli, + workgroup, + SV_TYPE_DOMAIN_ENUM, + list_unique_wg_fn, + (void *)dir)) { + continue; + } + } + + TALLOC_FREE(ip_list); + } else { + /* + * Server not an empty string ... Check the rest and see what + * gives + */ + if (*share == '\0') { + if (*path != '\0') { + + /* Should not have empty share with path */ + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = EINVAL + 8197; + return NULL; + + } + + /* + * We don't know if <server> is really a server name + * or is a workgroup/domain name. If we already have + * a server structure for it, we'll use it. + * Otherwise, check to see if <server><1D>, + * <server><1B>, or <server><20> translates. We check + * to see if <server> is an IP address first. + */ + + /* + * See if we have an existing server. Do not + * establish a connection if one does not already + * exist. + */ + srv = SMBC_server(frame, context, False, + server, port, "IPC$", + &workgroup, &user, &password); + + /* + * If no existing server and not an IP addr, look for + * LMB or DMB + */ + if (!srv && + !is_ipaddress(server) && + (resolve_name(server, &rem_ss, 0x1d, false) || /* LMB */ + resolve_name(server, &rem_ss, 0x1b, false) )) { /* DMB */ + /* + * "server" is actually a workgroup name, + * not a server. Make this clear. + */ + char *wgroup = server; + fstring buserver; + + dir->dir_type = SMBC_SERVER; + + /* + * Get the backup list ... + */ + if (!name_status_find(wgroup, 0, 0, + &rem_ss, buserver)) { + char addr[INET6_ADDRSTRLEN]; + + print_sockaddr(addr, sizeof(addr), &rem_ss); + DEBUG(0,("Could not get name of " + "local/domain master browser " + "for workgroup %s from " + "address %s\n", + wgroup, + addr)); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = EPERM; + return NULL; + + } + + /* + * Get a connection to IPC$ on the server if + * we do not already have one + */ + srv = SMBC_server(frame, context, True, + buserver, port, "IPC$", + &workgroup, + &user, &password); + if (!srv) { + DEBUG(0, ("got no contact to IPC$\n")); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + + } + + dir->srv = srv; + + if (smbXcli_conn_protocol(srv->cli->conn) > PROTOCOL_NT1) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + /* Now, list the servers ... */ + if (!cli_NetServerEnum(srv->cli, wgroup, + 0x0000FFFE, list_fn, + (void *)dir)) { + + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + } else if (srv || + (resolve_name(server, &rem_ss, 0x20, false))) { + NTSTATUS status; + + /* + * If we hadn't found the server, get one now + */ + if (!srv) { + srv = SMBC_server(frame, context, True, + server, port, "IPC$", + &workgroup, + &user, &password); + } + + if (!srv) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + + } + + dir->dir_type = SMBC_FILE_SHARE; + dir->srv = srv; + + /* List the shares ... */ + + status = net_share_enum_rpc(srv->cli, + list_fn, + (void *)dir); + if (!NT_STATUS_IS_OK(status) && + smbXcli_conn_protocol(srv->cli->conn) <= + PROTOCOL_NT1) { + /* + * Only call cli_RNetShareEnum() + * on SMB1 connections, not SMB2+. + */ + int rc = cli_RNetShareEnum(srv->cli, + list_fn, + (void *)dir); + if (rc != 0) { + status = cli_nt_error(srv->cli); + } else { + status = NT_STATUS_OK; + } + } + if (!NT_STATUS_IS_OK(status)) { + /* + * Set cli->raw_status so SMBC_errno() + * will correctly return the error. + */ + srv->cli->raw_status = status; + if (dir != NULL) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + errno = map_errno_from_nt_status( + status); + return NULL; + } + } else { + /* Neither the workgroup nor server exists */ + errno = ECONNREFUSED; + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + } + else { + /* + * The server and share are specified ... work from + * there ... + */ + char *targetpath; + struct cli_state *targetcli; + struct cli_credentials *creds = NULL; + NTSTATUS status; + + /* We connect to the server and list the directory */ + dir->dir_type = SMBC_FILE_SHARE; + + srv = SMBC_server(frame, context, True, server, port, share, + &workgroup, &user, &password); + + if (!srv) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + dir->srv = srv; + + /* Now, list the files ... */ + + path_len = strlen(path); + path = talloc_asprintf_append(path, "\\*"); + if (!path) { + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + creds = context->internal->creds; + + status = cli_resolve_path( + frame, "", + creds, + srv->cli, path, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + TALLOC_FREE(frame); + return NULL; + } + + status = cli_list(targetcli, targetpath, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + dir_list_fn, (void *)dir); + if (!NT_STATUS_IS_OK(status)) { + int saved_errno; + if (dir) { + SAFE_FREE(dir->fname); + SAFE_FREE(dir); + } + saved_errno = cli_status_to_errno(status); + + if (saved_errno == EINVAL) { + struct stat sb = {0}; + /* + * See if they asked to opendir + * something other than a directory. + * If so, the converted error value we + * got would have been EINVAL rather + * than ENOTDIR. + */ + path[path_len] = '\0'; /* restore original path */ + + status = SMBC_getatr( + context, + srv, + path, + &sb); + if (NT_STATUS_IS_OK(status) && + !S_ISDIR(sb.st_mode)) { + + /* It is. Correct the error value */ + saved_errno = ENOTDIR; + } + } + + /* + * If there was an error and the server is no + * good any more... + */ + if (cli_is_error(targetcli) && + smbc_getFunctionCheckServer(context)(context, srv)) { + + /* ... then remove it. */ + if (smbc_getFunctionRemoveUnusedServer(context)(context, + srv)) { + /* + * We could not remove the + * server completely, remove + * it from the cache so we + * will not get it again. It + * will be removed when the + * last file/dir is closed. + */ + smbc_getFunctionRemoveCachedServer(context)(context, srv); + } + } + + TALLOC_FREE(frame); + errno = saved_errno; + return NULL; + } + } + + } + + DLIST_ADD(context->internal->files, dir); + TALLOC_FREE(frame); + return dir; + +} + +/* + * Routine to close a directory + */ + +int +SMBC_closedir_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + TALLOC_CTX *frame = NULL; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + return -1; + } + + if (dir == NULL) { + return 0; + } + + frame = talloc_stackframe(); + + if (!SMBC_dlist_contains(context->internal->files, dir)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + remove_dir(dir); /* Clean it up */ + remove_dirplus(dir); + + DLIST_REMOVE(context->internal->files, dir); + + SAFE_FREE(dir->fname); + SAFE_FREE(dir); /* Free the space too */ + + TALLOC_FREE(frame); + return 0; + +} + +static int +smbc_readdir_internal(SMBCCTX * context, + struct smbc_dirent *dest, + struct smbc_dirent *src, + int max_namebuf_len) +{ + if (smbc_getOptionUrlEncodeReaddirEntries(context)) { + int remaining_len; + + /* url-encode the name. get back remaining buffer space */ + remaining_len = + smbc_urlencode(dest->name, src->name, max_namebuf_len); + + /* -1 means no null termination. */ + if (remaining_len < 0) { + return -1; + } + + /* We now know the name length */ + dest->namelen = strlen(dest->name); + + if (dest->namelen + 1 < 1) { + /* Integer wrap. */ + return -1; + } + + if (dest->namelen + 1 >= max_namebuf_len) { + /* Out of space for comment. */ + return -1; + } + + /* Save the pointer to the beginning of the comment */ + dest->comment = dest->name + dest->namelen + 1; + + if (remaining_len < 1) { + /* No room for comment null termination. */ + return -1; + } + + /* Copy the comment */ + strlcpy(dest->comment, src->comment, remaining_len); + + /* Save other fields */ + dest->smbc_type = src->smbc_type; + dest->commentlen = strlen(dest->comment); + dest->dirlen = ((dest->comment + dest->commentlen + 1) - + (char *) dest); + } else { + + /* No encoding. Just copy the entry as is. */ + if (src->dirlen > max_namebuf_len) { + return -1; + } + memcpy(dest, src, src->dirlen); + if (src->namelen + 1 < 1) { + /* Integer wrap */ + return -1; + } + if (src->namelen + 1 >= max_namebuf_len) { + /* Comment off the end. */ + return -1; + } + dest->comment = (char *)(&dest->name + src->namelen + 1); + } + return 0; +} + +/* + * Routine to get a directory entry + */ + +struct smbc_dirent * +SMBC_readdir_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + int maxlen; + int ret; + struct smbc_dirent *dirp, *dirent; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Check that all is ok first ... */ + + if (!context || !context->internal->initialized) { + + errno = EINVAL; + DEBUG(0, ("Invalid context in SMBC_readdir_ctx()\n")); + TALLOC_FREE(frame); + return NULL; + + } + + if (!SMBC_dlist_contains(context->internal->files, dir)) { + + errno = EBADF; + DEBUG(0, ("Invalid dir in SMBC_readdir_ctx()\n")); + TALLOC_FREE(frame); + return NULL; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + DEBUG(0, ("Found file vs directory in SMBC_readdir_ctx()\n")); + TALLOC_FREE(frame); + return NULL; + + } + + if (!dir->dir_next) { + TALLOC_FREE(frame); + return NULL; + } + + dirent = dir->dir_next->dirent; + if (!dirent) { + + errno = ENOENT; + TALLOC_FREE(frame); + return NULL; + + } + + dirp = &context->internal->dirent; + maxlen = sizeof(context->internal->_dirent_name); + + ret = smbc_readdir_internal(context, dirp, dirent, maxlen); + if (ret == -1) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + dir->dir_next = dir->dir_next->next; + + /* + * If we are returning file entries, we + * have a duplicate list in dirplus. + * + * Update dirplus_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dirplus_list != NULL) { + dir->dirplus_next = dir->dirplus_next->next; + } + + TALLOC_FREE(frame); + return dirp; +} + +/* + * Routine to get a directory entry with all attributes + */ + +const struct libsmb_file_info * +SMBC_readdirplus_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + struct libsmb_file_info *smb_finfo = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Check that all is ok first ... */ + + if (context == NULL || !context->internal->initialized) { + DBG_ERR("Invalid context in SMBC_readdirplus_ctx()\n"); + TALLOC_FREE(frame); + errno = EINVAL; + return NULL; + } + + if (!SMBC_dlist_contains(context->internal->files, dir)) { + DBG_ERR("Invalid dir in SMBC_readdirplus_ctx()\n"); + TALLOC_FREE(frame); + errno = EBADF; + return NULL; + } + + if (dir->dirplus_next == NULL) { + TALLOC_FREE(frame); + return NULL; + } + + smb_finfo = dir->dirplus_next->smb_finfo; + if (smb_finfo == NULL) { + TALLOC_FREE(frame); + errno = ENOENT; + return NULL; + } + dir->dirplus_next = dir->dirplus_next->next; + + /* + * If we are returning file entries, we + * have a duplicate list in dir_list + * + * Update dir_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dir_list) { + dir->dir_next = dir->dir_next->next; + } + + TALLOC_FREE(frame); + return smb_finfo; +} + +/* + * Routine to get a directory entry plus a filled in stat structure if + * requested. + */ + +const struct libsmb_file_info *SMBC_readdirplus2_ctx(SMBCCTX *context, + SMBCFILE *dir, + struct stat *st) +{ + struct libsmb_file_info *smb_finfo = NULL; + struct smbc_dirplus_list *dp_list = NULL; + ino_t ino; + char *full_pathname = NULL; + char *workgroup = NULL; + char *server = NULL; + uint16_t port = 0; + char *share = NULL; + char *path = NULL; + char *user = NULL; + char *password = NULL; + char *options = NULL; + int rc; + TALLOC_CTX *frame = NULL; + + /* + * Allow caller to pass in NULL for stat pointer if + * required. This makes this call identical to + * smbc_readdirplus(). + */ + + if (st == NULL) { + return SMBC_readdirplus_ctx(context, dir); + } + + frame = talloc_stackframe(); + + /* Check that all is ok first ... */ + if (context == NULL || !context->internal->initialized) { + DBG_ERR("Invalid context in SMBC_readdirplus2_ctx()\n"); + TALLOC_FREE(frame); + errno = EINVAL; + return NULL; + } + + if (!SMBC_dlist_contains(context->internal->files, dir)) { + DBG_ERR("Invalid dir in SMBC_readdirplus2_ctx()\n"); + TALLOC_FREE(frame); + errno = EBADF; + return NULL; + } + + dp_list = dir->dirplus_next; + if (dp_list == NULL) { + TALLOC_FREE(frame); + return NULL; + } + + ino = (ino_t)dp_list->ino; + + smb_finfo = dp_list->smb_finfo; + if (smb_finfo == NULL) { + TALLOC_FREE(frame); + errno = ENOENT; + return NULL; + } + + full_pathname = talloc_asprintf(frame, + "%s/%s", + dir->fname, + smb_finfo->name); + if (full_pathname == NULL) { + TALLOC_FREE(frame); + errno = ENOENT; + return NULL; + } + + rc = SMBC_parse_path(frame, + context, + full_pathname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + &options); + if (rc != 0) { + TALLOC_FREE(frame); + errno = ENOENT; + return NULL; + } + + setup_stat(st, + path, + smb_finfo->size, + smb_finfo->attrs, + ino, + dir->srv->dev, + smb_finfo->atime_ts, + smb_finfo->ctime_ts, + smb_finfo->mtime_ts); + + TALLOC_FREE(full_pathname); + + dir->dirplus_next = dir->dirplus_next->next; + + /* + * If we are returning file entries, we + * have a duplicate list in dir_list + * + * Update dir_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dir_list) { + dir->dir_next = dir->dir_next->next; + } + + TALLOC_FREE(frame); + return smb_finfo; +} + +/* + * Routine to get directory entries + */ + +int +SMBC_getdents_ctx(SMBCCTX *context, + SMBCFILE *dir, + struct smbc_dirent *dirp, + int count) +{ + int rem = count; + int reqd; + int maxlen; + char *ndir = (char *)dirp; + struct smbc_dir_list *dirlist; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Check that all is ok first ... */ + + if (!context || !context->internal->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (!SMBC_dlist_contains(context->internal->files, dir)) { + + errno = EBADF; + TALLOC_FREE(frame); + return -1; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + TALLOC_FREE(frame); + return -1; + + } + + /* + * Now, retrieve the number of entries that will fit in what was passed + * We have to figure out if the info is in the list, or we need to + * send a request to the server to get the info. + */ + + while ((dirlist = dir->dir_next)) { + int ret; + struct smbc_dirent *dirent; + struct smbc_dirent *currentEntry = (struct smbc_dirent *)ndir; + + if (!dirlist->dirent) { + + errno = ENOENT; /* Bad error */ + TALLOC_FREE(frame); + return -1; + + } + + /* Do urlencoding of next entry, if so selected */ + dirent = &context->internal->dirent; + maxlen = sizeof(context->internal->_dirent_name); + ret = smbc_readdir_internal(context, dirent, + dirlist->dirent, maxlen); + if (ret == -1) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + reqd = dirent->dirlen; + + if (rem < reqd) { + + if (rem < count) { /* We managed to copy something */ + + errno = 0; + TALLOC_FREE(frame); + return count - rem; + + } + else { /* Nothing copied ... */ + + errno = EINVAL; /* Not enough space ... */ + TALLOC_FREE(frame); + return -1; + + } + + } + + memcpy(currentEntry, dirent, reqd); /* Copy the data in ... */ + + currentEntry->comment = ¤tEntry->name[0] + + dirent->namelen + 1; + + ndir += reqd; + rem -= reqd; + + /* Try and align the struct for the next entry + on a valid pointer boundary by appending zeros */ + while((rem > 0) && ((uintptr_t)ndir & (sizeof(void*) - 1))) { + *ndir = '\0'; + rem--; + ndir++; + currentEntry->dirlen++; + } + + dir->dir_next = dirlist = dirlist -> next; + + /* + * If we are returning file entries, we + * have a duplicate list in dirplus. + * + * Update dirplus_next also so readdir and + * readdirplus are kept in sync. + */ + if (dir->dirplus_list != NULL) { + dir->dirplus_next = dir->dirplus_next->next; + } + } + + TALLOC_FREE(frame); + + if (rem == count) + return 0; + else + return count - rem; + +} + +/* + * Routine to create a directory ... + */ + +int +SMBC_mkdir_ctx(SMBCCTX *context, + const char *fname, + mode_t mode) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + uint16_t port = 0; + struct cli_state *targetcli = NULL; + struct cli_credentials *creds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_mkdir(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + + } + + creds = context->internal->creds; + + /*d_printf(">>>mkdir: resolving %s\n", path);*/ + status = cli_resolve_path(frame, "", + creds, + srv->cli, path, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>mkdir: resolved path as %s\n", targetpath);*/ + + status = cli_mkdir(targetcli, targetpath); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + + } + + TALLOC_FREE(frame); + return 0; + +} + +/* + * Our list function simply checks to see if a directory is not empty + */ + +static NTSTATUS +rmdir_list_fn(struct file_info *finfo, + const char *mask, + void *state) +{ + if (strncmp(finfo->name, ".", 1) != 0 && + strncmp(finfo->name, "..", 2) != 0) { + bool *smbc_rmdir_dirempty = (bool *)state; + *smbc_rmdir_dirempty = false; + } + return NT_STATUS_OK; +} + +/* + * Routine to remove a directory + */ + +int +SMBC_rmdir_ctx(SMBCCTX *context, + const char *fname) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + uint16_t port = 0; + struct cli_state *targetcli = NULL; + struct cli_credentials *creds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_rmdir(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + + } + + creds = context->internal->creds; + + /*d_printf(">>>rmdir: resolving %s\n", path);*/ + status = cli_resolve_path(frame, "", + creds, + srv->cli, path, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>rmdir: resolved path as %s\n", targetpath);*/ + + status = cli_rmdir(targetcli, targetpath); + + if (!NT_STATUS_IS_OK(status)) { + + errno = cli_status_to_errno(status); + + if (errno == EACCES) { /* Check if the dir empty or not */ + + /* Local storage to avoid buffer overflows */ + char *lpath; + bool smbc_rmdir_dirempty = true; + + lpath = talloc_asprintf(frame, "%s\\*", + targetpath); + if (!lpath) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + + status = cli_list(targetcli, lpath, + FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, + rmdir_list_fn, + &smbc_rmdir_dirempty); + + if (!NT_STATUS_IS_OK(status)) { + /* Fix errno to ignore latest error ... */ + DBG_INFO("cli_list returned an error: %s\n", + nt_errstr(status)); + errno = EACCES; + + } + + if (smbc_rmdir_dirempty) + errno = EACCES; + else + errno = ENOTEMPTY; + + } + + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return 0; + +} + +/* + * Routine to return the current directory position + */ + +off_t +SMBC_telldir_ctx(SMBCCTX *context, + SMBCFILE *dir) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (!SMBC_dlist_contains(context->internal->files, dir)) { + + errno = EBADF; + TALLOC_FREE(frame); + return -1; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + TALLOC_FREE(frame); + return -1; + + } + + /* See if we're already at the end. */ + if (dir->dir_next == NULL) { + /* We are. */ + TALLOC_FREE(frame); + return -1; + } + + /* + * We return the pointer here as the offset + */ + TALLOC_FREE(frame); + return (off_t)(long)dir->dir_next->dirent; +} + +/* + * A routine to run down the list and see if the entry is OK + * Modifies the dir list and the dirplus list (if it exists) + * to point at the correct next entry on success. + */ + +static bool update_dir_ents(SMBCFILE *dir, struct smbc_dirent *dirent) +{ + struct smbc_dir_list *tmp_dir = dir->dir_list; + struct smbc_dirplus_list *tmp_dirplus = dir->dirplus_list; + + /* + * Run down the list looking for what we want. + * If we're enumerating files both dir_list + * and dirplus_list contain the same entry + * list, as they were seeded from the same + * cli_list callback. + * + * If we're enumerating servers then + * dirplus_list will be NULL, so don't + * update in that case. + */ + + while (tmp_dir != NULL) { + if (tmp_dir->dirent == dirent) { + dir->dir_next = tmp_dir; + if (tmp_dirplus != NULL) { + dir->dirplus_next = tmp_dirplus; + } + return true; + } + tmp_dir = tmp_dir->next; + if (tmp_dirplus != NULL) { + tmp_dirplus = tmp_dirplus->next; + } + } + return false; +} + +/* + * Routine to seek on a directory + */ + +int +SMBC_lseekdir_ctx(SMBCCTX *context, + SMBCFILE *dir, + off_t offset) +{ + long int l_offset = offset; /* Handle problems of size */ + struct smbc_dirent *dirent = (struct smbc_dirent *)l_offset; + TALLOC_CTX *frame = talloc_stackframe(); + bool ok; + + if (!context || !context->internal->initialized) { + + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (dir->file != False) { /* FIXME, should be dir, perhaps */ + + errno = ENOTDIR; + TALLOC_FREE(frame); + return -1; + + } + + /* Now, check what we were passed and see if it is OK ... */ + + if (dirent == NULL) { /* Seek to the beginning of the list */ + + dir->dir_next = dir->dir_list; + + /* Do the same for dirplus. */ + dir->dirplus_next = dir->dirplus_list; + + TALLOC_FREE(frame); + return 0; + + } + + if (offset == -1) { /* Seek to the end of the list */ + dir->dir_next = NULL; + + /* Do the same for dirplus. */ + dir->dirplus_next = NULL; + + TALLOC_FREE(frame); + return 0; + } + + /* + * Run down the list and make sure that the entry is OK. + * Update the position of both dir and dirplus lists. + */ + + ok = update_dir_ents(dir, dirent); + if (!ok) { + errno = EINVAL; /* Bad entry */ + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} + +/* + * Routine to fstat a dir + */ + +int +SMBC_fstatdir_ctx(SMBCCTX *context, + SMBCFILE *dir, + struct stat *st) +{ + + if (!context || !context->internal->initialized) { + + errno = EINVAL; + return -1; + } + + /* No code yet ... */ + return 0; +} + +int +SMBC_chmod_ctx(SMBCCTX *context, + const char *fname, + mode_t newmode) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + char *path = NULL; + uint32_t attr; + uint16_t port = 0; + struct cli_credentials *creds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_chmod(%s, 0%3o)\n", fname, (unsigned int)newmode)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + creds = context->internal->creds; + + /*d_printf(">>>unlink: resolving %s\n", path);*/ + status = cli_resolve_path(frame, "", + creds, + srv->cli, path, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + + attr = 0; + + if (!(newmode & (S_IWUSR | S_IWGRP | S_IWOTH))) attr |= FILE_ATTRIBUTE_READONLY; + if ((newmode & S_IXUSR) && lp_map_archive(-1)) attr |= FILE_ATTRIBUTE_ARCHIVE; + if ((newmode & S_IXGRP) && lp_map_system(-1)) attr |= FILE_ATTRIBUTE_SYSTEM; + if ((newmode & S_IXOTH) && lp_map_hidden(-1)) attr |= FILE_ATTRIBUTE_HIDDEN; + + status = cli_setatr(targetcli, targetpath, attr, 0); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} + +int +SMBC_utimes_ctx(SMBCCTX *context, + const char *fname, + struct timeval *tbuf) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + struct timespec access_time, write_time; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + bool ok; + + if (!context || !context->internal->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (tbuf == NULL) { + access_time = write_time = timespec_current(); + } else { + access_time = convert_timeval_to_timespec(tbuf[0]); + write_time = convert_timeval_to_timespec(tbuf[1]); + } + + if (DEBUGLVL(4)) { + struct timeval_buf abuf, wbuf; + + dbgtext("smbc_utimes(%s, atime = %s mtime = %s)\n", + fname, + timespec_string_buf(&access_time, false, &abuf), + timespec_string_buf(&write_time, false, &wbuf)); + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + ok = SMBC_setatr( + context, + srv, + path, + (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, + access_time, + write_time, + (struct timespec) { .tv_nsec = SAMBA_UTIME_OMIT }, + 0); + if (!ok) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_setatr */ + } + + TALLOC_FREE(frame); + return 0; +} + +/* + * Routine to unlink() a file + */ + +int +SMBC_unlink_ctx(SMBCCTX *context, + const char *fname) +{ + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + uint16_t port = 0; + struct cli_state *targetcli = NULL; + SMBCSRV *srv = NULL; + struct cli_credentials *creds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* SMBC_server sets errno */ + + } + + creds = context->internal->creds; + + /*d_printf(">>>unlink: resolving %s\n", path);*/ + status = cli_resolve_path(frame, "", + creds, + srv->cli, path, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>unlink: resolved path as %s\n", targetpath);*/ + + status = cli_unlink( + targetcli, + targetpath, + FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + + if (!NT_STATUS_IS_OK(status)) { + + errno = cli_status_to_errno(status); + + if (errno == EACCES) { /* Check if the file is a directory */ + + int saverr = errno; + struct stat sb = {0}; + + status = SMBC_getatr(context, srv, path, &sb); + if (!NT_STATUS_IS_OK(status)) { + /* Hmmm, bad error ... What? */ + + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + + } + else { + + if (S_ISDIR(sb.st_mode)) + errno = EISDIR; + else + errno = saverr; /* Restore this */ + + } + } + + TALLOC_FREE(frame); + return -1; + + } + + TALLOC_FREE(frame); + return 0; /* Success ... */ + +} + +/* + * Routine to rename() a file + */ + +int +SMBC_rename_ctx(SMBCCTX *ocontext, + const char *oname, + SMBCCTX *ncontext, + const char *nname) +{ + char *server1 = NULL; + char *share1 = NULL; + char *server2 = NULL; + char *share2 = NULL; + char *user1 = NULL; + char *user2 = NULL; + char *password1 = NULL; + char *password2 = NULL; + char *workgroup = NULL; + char *path1 = NULL; + char *path2 = NULL; + char *targetpath1 = NULL; + char *targetpath2 = NULL; + struct cli_state *targetcli1 = NULL; + struct cli_state *targetcli2 = NULL; + SMBCSRV *srv = NULL; + uint16_t port1 = 0; + uint16_t port2 = 0; + struct cli_credentials *ocreds = NULL; + struct cli_credentials *ncreds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!ocontext || !ncontext || + !ocontext->internal->initialized || + !ncontext->internal->initialized) { + + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!oname || !nname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_rename(%s,%s)\n", oname, nname)); + + if (SMBC_parse_path(frame, + ocontext, + oname, + &workgroup, + &server1, + &port1, + &share1, + &path1, + &user1, + &password1, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user1 || user1[0] == (char)0) { + user1 = talloc_strdup(frame, smbc_getUser(ocontext)); + if (!user1) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + if (SMBC_parse_path(frame, + ncontext, + nname, + NULL, + &server2, + &port2, + &share2, + &path2, + &user2, + &password2, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user2 || user2[0] == (char)0) { + user2 = talloc_strdup(frame, smbc_getUser(ncontext)); + if (!user2) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + if (strcmp(server1, server2) || strcmp(share1, share2) || + strcmp(user1, user2)) { + /* Can't rename across file systems, or users?? */ + errno = EXDEV; + TALLOC_FREE(frame); + return -1; + } + + srv = SMBC_server(frame, ocontext, True, + server1, port1, share1, &workgroup, &user1, &password1); + if (!srv) { + TALLOC_FREE(frame); + return -1; + + } + + /* set the credentials to make DFS work */ + smbc_set_credentials_with_fallback(ocontext, + workgroup, + user1, + password1); + + /*d_printf(">>>rename: resolving %s\n", path1);*/ + ocreds = ocontext->internal->creds; + + status = cli_resolve_path(frame, "", + ocreds, + srv->cli, path1, &targetcli1, &targetpath1); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path1); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + + /* set the credentials to make DFS work */ + smbc_set_credentials_with_fallback(ncontext, + workgroup, + user2, + password2); + + /*d_printf(">>>rename: resolved path as %s\n", targetpath1);*/ + /*d_printf(">>>rename: resolving %s\n", path2);*/ + ncreds = ncontext->internal->creds; + + status = cli_resolve_path(frame, "", + ncreds, + srv->cli, path2, &targetcli2, &targetpath2); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path2); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>rename: resolved path as %s\n", targetpath2);*/ + + if (strcmp(smbXcli_conn_remote_name(targetcli1->conn), smbXcli_conn_remote_name(targetcli2->conn)) || + strcmp(targetcli1->share, targetcli2->share)) + { + /* can't rename across file systems */ + errno = EXDEV; + TALLOC_FREE(frame); + return -1; + } + + if (!NT_STATUS_IS_OK( + cli_rename(targetcli1, targetpath1, targetpath2, false))) { + int eno = SMBC_errno(ocontext, targetcli1); + + if (eno != EEXIST || + !NT_STATUS_IS_OK(cli_unlink(targetcli1, targetpath2, + FILE_ATTRIBUTE_SYSTEM | + FILE_ATTRIBUTE_HIDDEN)) || + !NT_STATUS_IS_OK(cli_rename(targetcli1, targetpath1, + targetpath2, false))) { + + errno = eno; + TALLOC_FREE(frame); + return -1; + + } + } + + TALLOC_FREE(frame); + return 0; /* Success */ +} + +struct smbc_notify_cb_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t fnum; + bool recursive; + uint32_t completion_filter; + unsigned callback_timeout_ms; + smbc_notify_callback_fn cb; + void *private_data; +}; + +static void smbc_notify_cb_got_changes(struct tevent_req *subreq); +static void smbc_notify_cb_timedout(struct tevent_req *subreq); + +static struct tevent_req *smbc_notify_cb_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + uint16_t fnum, bool recursive, uint32_t completion_filter, + unsigned callback_timeout_ms, + smbc_notify_callback_fn cb, void *private_data) +{ + struct tevent_req *req, *subreq; + struct smbc_notify_cb_state *state; + + req = tevent_req_create(mem_ctx, &state, struct smbc_notify_cb_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->fnum = fnum; + state->recursive = recursive; + state->completion_filter = completion_filter; + state->callback_timeout_ms = callback_timeout_ms; + state->cb = cb; + state->private_data = private_data; + + subreq = cli_notify_send( + state, state->ev, state->cli, state->fnum, 1000, + state->completion_filter, state->recursive); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req); + + if (state->callback_timeout_ms == 0) { + return req; + } + + subreq = tevent_wakeup_send( + state, state->ev, + tevent_timeval_current_ofs(state->callback_timeout_ms/1000, + state->callback_timeout_ms*1000)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req); + + return req; +} + +static void smbc_notify_cb_got_changes(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbc_notify_cb_state *state = tevent_req_data( + req, struct smbc_notify_cb_state); + uint32_t num_changes; + struct notify_change *changes; + NTSTATUS status; + int cb_ret; + + status = cli_notify_recv(subreq, state, &num_changes, &changes); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + { + struct smbc_notify_callback_action actions[num_changes]; + uint32_t i; + + for (i=0; i<num_changes; i++) { + actions[i].action = changes[i].action; + actions[i].filename = changes[i].name; + } + + cb_ret = state->cb(actions, num_changes, state->private_data); + } + + TALLOC_FREE(changes); + + if (cb_ret != 0) { + tevent_req_done(req); + return; + } + + subreq = cli_notify_send( + state, state->ev, state->cli, state->fnum, 1000, + state->completion_filter, state->recursive); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smbc_notify_cb_got_changes, req); +} + +static void smbc_notify_cb_timedout(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbc_notify_cb_state *state = tevent_req_data( + req, struct smbc_notify_cb_state); + int cb_ret; + bool ok; + + ok = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ok) { + tevent_req_oom(req); + return; + } + + cb_ret = state->cb(NULL, 0, state->private_data); + if (cb_ret != 0) { + tevent_req_done(req); + return; + } + + subreq = tevent_wakeup_send( + state, state->ev, + tevent_timeval_current_ofs(state->callback_timeout_ms/1000, + state->callback_timeout_ms*1000)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smbc_notify_cb_timedout, req); +} + +static NTSTATUS smbc_notify_cb_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +static NTSTATUS smbc_notify_cb(struct cli_state *cli, uint16_t fnum, + bool recursive, uint32_t completion_filter, + unsigned callback_timeout_ms, + smbc_notify_callback_fn cb, void *private_data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = smbc_notify_cb_send(frame, ev, cli, fnum, recursive, + completion_filter, + callback_timeout_ms, cb, private_data); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = smbc_notify_cb_recv(req); + TALLOC_FREE(req); +fail: + TALLOC_FREE(frame); + return status; +} + +int +SMBC_notify_ctx(SMBCCTX *context, SMBCFILE *dir, smbc_bool recursive, + uint32_t completion_filter, unsigned callback_timeout_ms, + smbc_notify_callback_fn cb, void *private_data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct cli_state *cli; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *options = NULL; + char *workgroup = NULL; + char *path = NULL; + uint16_t port; + NTSTATUS status; + uint16_t fnum; + + if ((context == NULL) || !context->internal->initialized) { + TALLOC_FREE(frame); + errno = EINVAL; + return -1; + } + if (!SMBC_dlist_contains(context->internal->files, dir)) { + TALLOC_FREE(frame); + errno = EBADF; + return -1; + } + + if (SMBC_parse_path(frame, + context, + dir->fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + &options)) { + DEBUG(4, ("no valid path\n")); + TALLOC_FREE(frame); + errno = EINVAL + 8194; + return -1; + } + + DEBUG(4, ("parsed path: fname='%s' server='%s' share='%s' " + "path='%s' options='%s'\n", + dir->fname, server, share, path, options)); + + DEBUG(4, ("%s(%p, %d, %"PRIu32")\n", __func__, dir, + (int)recursive, completion_filter)); + + cli = dir->srv->cli; + status = cli_ntcreate( + cli, path, 0, FILE_READ_DATA, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, 0, 0, &fnum, NULL); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + status = smbc_notify_cb(cli, fnum, recursive != 0, completion_filter, + callback_timeout_ms, cb, private_data); + if (!NT_STATUS_IS_OK(status)) { + cli_close(cli, fnum); + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + cli_close(cli, fnum); + + TALLOC_FREE(frame); + return 0; +} diff --git a/source3/libsmb/libsmb_file.c b/source3/libsmb/libsmb_file.c new file mode 100644 index 0000000..9875075 --- /dev/null +++ b/source3/libsmb/libsmb_file.c @@ -0,0 +1,797 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" +#include "../libcli/smb/smbXcli_base.h" + +/* + * Routine to open() a file ... + */ + +SMBCFILE * +SMBC_open_ctx(SMBCCTX *context, + const char *fname, + int flags, + mode_t mode) +{ + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + SMBCSRV *srv = NULL; + SMBCFILE *file = NULL; + uint16_t fd; + uint16_t port = 0; + NTSTATUS status = NT_STATUS_OBJECT_PATH_INVALID; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return NULL; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + if (!srv) { + if (errno == EPERM) errno = EACCES; + TALLOC_FREE(frame); + return NULL; /* SMBC_server sets errno */ + } + + /* Hmmm, the test for a directory is suspect here ... FIXME */ + + if (strlen(path) > 0 && path[strlen(path) - 1] == '\\') { + status = NT_STATUS_OBJECT_PATH_INVALID; + } else { + struct cli_credentials *creds = NULL; + + file = SMB_MALLOC_P(SMBCFILE); + if (!file) { + errno = ENOMEM; + TALLOC_FREE(frame); + return NULL; + } + + ZERO_STRUCTP(file); + + creds = context->internal->creds; + /*d_printf(">>>open: resolving %s\n", path);*/ + status = cli_resolve_path( + frame, "", + creds, + srv->cli, path, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + errno = ENOENT; + SAFE_FREE(file); + TALLOC_FREE(frame); + return NULL; + } + /*d_printf(">>>open: resolved %s as %s\n", path, targetpath);*/ + + status = cli_open(targetcli, targetpath, flags, + context->internal->share_mode, &fd); + if (!NT_STATUS_IS_OK(status)) { + + /* Handle the error ... */ + + SAFE_FREE(file); + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return NULL; + } + + /* Fill in file struct */ + + file->cli_fd = fd; + file->fname = SMB_STRDUP(fname); + file->srv = srv; + file->offset = 0; + file->file = True; + /* + * targetcli is either equal to srv->cli or + * is a subsidiary DFS connection. Either way + * file->cli_fd belongs to it so we must cache + * it for read/write/close, not re-resolve each time. + * Re-resolving is both slow and incorrect. + */ + file->targetcli = targetcli; + + DLIST_ADD(context->internal->files, file); + + /* + * If the file was opened in O_APPEND mode, all write + * operations should be appended to the file. To do that, + * though, using this protocol, would require a getattrE() + * call for each and every write, to determine where the end + * of the file is. (There does not appear to be an append flag + * in the protocol.) Rather than add all of that overhead of + * retrieving the current end-of-file offset prior to each + * write operation, we'll assume that most append operations + * will continuously write, so we'll just set the offset to + * the end of the file now and hope that's adequate. + * + * Note to self: If this proves inadequate, and O_APPEND + * should, in some cases, be forced for each write, add a + * field in the context options structure, for + * "strict_append_mode" which would select between the current + * behavior (if FALSE) or issuing a getattrE() prior to each + * write and forcing the write to the end of the file (if + * TRUE). Adding that capability will likely require adding + * an "append" flag into the _SMBCFILE structure to track + * whether a file was opened in O_APPEND mode. -- djl + */ + if (flags & O_APPEND) { + if (SMBC_lseek_ctx(context, file, 0, SEEK_END) < 0) { + (void) SMBC_close_ctx(context, file); + errno = ENXIO; + TALLOC_FREE(frame); + return NULL; + } + } + + TALLOC_FREE(frame); + return file; + } + + /* Check if opendir needed ... */ + + if (!NT_STATUS_IS_OK(status)) { + file = smbc_getFunctionOpendir(context)(context, fname); + TALLOC_FREE(frame); + if (file == NULL) { + errno = cli_status_to_errno(status); + } + return file; + } + + errno = EINVAL; /* FIXME, correct errno ? */ + TALLOC_FREE(frame); + return NULL; +} + +/* + * Routine to create a file + */ + +SMBCFILE * +SMBC_creat_ctx(SMBCCTX *context, + const char *path, + mode_t mode) +{ + if (!context || !context->internal->initialized) { + errno = EINVAL; + return NULL; + } + + return SMBC_open_ctx(context, path, + O_WRONLY | O_CREAT | O_TRUNC, mode); +} + +/* + * Routine to read() a file ... + */ + +ssize_t +SMBC_read_ctx(SMBCCTX *context, + SMBCFILE *file, + void *buf, + size_t count) +{ + size_t ret; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + /* + * offset: + * + * Compiler bug (possibly) -- gcc (GCC) 3.3.5 (Debian 1:3.3.5-2) -- + * appears to pass file->offset (which is type off_t) differently than + * a local variable of type off_t. Using local variable "offset" in + * the call to cli_read() instead of file->offset fixes a problem + * retrieving data at an offset greater than 4GB. + */ + off_t offset; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_read(%p, %zu)\n", file, count)); + + if (!SMBC_dlist_contains(context->internal->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + offset = file->offset; + + /* Check that the buffer exists ... */ + + if (buf == NULL) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + status = cli_read(file->targetcli, file->cli_fd, (char *)buf, offset, + count, &ret); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + file->offset += ret; + + DEBUG(4, (" --> %zu\n", ret)); + + TALLOC_FREE(frame); + return ret; /* Success, ret bytes of data ... */ +} + +off_t +SMBC_splice_ctx(SMBCCTX *context, + SMBCFILE *srcfile, + SMBCFILE *dstfile, + off_t count, + int (*splice_cb)(off_t n, void *priv), + void *priv) +{ + off_t written = 0; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, srcfile)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, dstfile)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + status = cli_splice(srcfile->targetcli, dstfile->targetcli, + srcfile->cli_fd, dstfile->cli_fd, + count, srcfile->offset, dstfile->offset, &written, + splice_cb, priv); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + srcfile->offset += written; + dstfile->offset += written; + + TALLOC_FREE(frame); + return written; +} + +/* + * Routine to write() a file ... + */ + +ssize_t +SMBC_write_ctx(SMBCCTX *context, + SMBCFILE *file, + const void *buf, + size_t count) +{ + off_t offset; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + /* First check all pointers before dereferencing them */ + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + /* Check that the buffer exists ... */ + + if (buf == NULL) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + offset = file->offset; /* See "offset" comment in SMBC_read_ctx() */ + + status = cli_writeall(file->targetcli, file->cli_fd, + 0, (const uint8_t *)buf, offset, count, NULL); + if (!NT_STATUS_IS_OK(status)) { + errno = map_errno_from_nt_status(status); + TALLOC_FREE(frame); + return -1; + } + + file->offset += count; + + TALLOC_FREE(frame); + return count; /* Success, 0 bytes of data ... */ +} + +/* + * Routine to close() a file ... + */ + +int +SMBC_close_ctx(SMBCCTX *context, + SMBCFILE *file) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + /* IS a dir ... */ + if (!file->file) { + TALLOC_FREE(frame); + return smbc_getFunctionClosedir(context)(context, file); + } + + status = cli_close(file->targetcli, file->cli_fd); + if (!NT_STATUS_IS_OK(status)) { + SMBCSRV *srv; + DEBUG(3, ("cli_close failed on %s. purging server.\n", + file->fname)); + /* Deallocate slot and remove the server + * from the server cache if unused */ + srv = file->srv; + DLIST_REMOVE(context->internal->files, file); + SAFE_FREE(file->fname); + SAFE_FREE(file); + smbc_getFunctionRemoveUnusedServer(context)(context, srv); + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + DLIST_REMOVE(context->internal->files, file); + SAFE_FREE(file->fname); + SAFE_FREE(file); + TALLOC_FREE(frame); + return 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 + */ +NTSTATUS +SMBC_getatr(SMBCCTX * context, + SMBCSRV *srv, + const char *path, + struct stat *sb) +{ + char *fixedpath = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + uint32_t attr = 0; + off_t size = 0; + struct timespec create_time_ts = {0}; + struct timespec access_time_ts = {0}; + struct timespec write_time_ts = {0}; + struct timespec change_time_ts = {0}; + struct timespec w_time_ts = {0}; + time_t write_time = 0; + SMB_INO_T ino = 0; + struct cli_credentials *creds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + TALLOC_FREE(frame); + return NT_STATUS_INVALID_PARAMETER; + } + + /* path fixup for . and .. */ + if (ISDOT(path) || ISDOTDOT(path)) { + fixedpath = talloc_strdup(frame, "\\"); + if (!fixedpath) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } else { + fixedpath = talloc_strdup(frame, path); + if (!fixedpath) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + trim_string(fixedpath, NULL, "\\.."); + trim_string(fixedpath, NULL, "\\."); + } + DEBUG(4,("SMBC_getatr: sending qpathinfo\n")); + + creds = context->internal->creds; + + status = cli_resolve_path(frame, "", + creds, + srv->cli, fixedpath, + &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Couldn't resolve %s\n", path); + TALLOC_FREE(frame); + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (!srv->no_pathinfo2) { + bool not_supported_error = false; + status = cli_qpathinfo2(targetcli, + targetpath, + &create_time_ts, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &size, + &attr, + &ino); + if (NT_STATUS_IS_OK(status)) { + goto setup_stat; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + not_supported_error = true; + } + if (!not_supported_error) { + /* "Normal error". Just return it to caller. */ + TALLOC_FREE(frame); + return status; + } + } + + srv->no_pathinfo2 = True; + + if (!srv->no_pathinfo3) { + bool not_supported_error = false; + status = cli_qpathinfo3(targetcli, + targetpath, + &create_time_ts, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &size, + &attr, + &ino); + if (NT_STATUS_IS_OK(status)) { + goto setup_stat; + } + if (NT_STATUS_EQUAL(status, NT_STATUS_INVALID_LEVEL) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + not_supported_error = true; + } + if (!not_supported_error) { + /* "Normal error". Just return it to caller. */ + TALLOC_FREE(frame); + return status; + } + } + + 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; + } + + status = cli_getatr(targetcli, targetpath, &attr, &size, &write_time); + if (!NT_STATUS_IS_OK(status)) { + goto all_failed; + } + w_time_ts = convert_time_t_to_timespec(write_time); + access_time_ts = change_time_ts = write_time_ts = w_time_ts; + +setup_stat: + setup_stat(sb, + path, + size, + attr, + ino, + srv->dev, + access_time_ts, + change_time_ts, + write_time_ts); + + TALLOC_FREE(frame); + return NT_STATUS_OK; + +all_failed: + srv->no_pathinfo2 = False; + srv->no_pathinfo3 = False; + + TALLOC_FREE(frame); + return status; +} + +/* + * Set file info on an SMB server. Use setpathinfo call first. If that + * fails, use setattrE.. + * + * Access and modification time parameters are always used and must be + * provided. Create time, if zero, will be determined from the actual create + * time of the file. If non-zero, the create time will be set as well. + * + * "attr" (attributes) parameter may be set to -1 if it is not to be set. + */ +bool +SMBC_setatr(SMBCCTX * context, SMBCSRV *srv, char *path, + struct timespec create_time, + struct timespec access_time, + struct timespec write_time, + struct timespec change_time, + uint16_t attr) +{ + uint16_t fd; + uint32_t lattr = (uint32_t)attr; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + + if (attr == (uint16_t)-1) { + /* + * External ABI only passes in + * 16-bits of attribute. Make + * sure we correctly map to + * (uint32_t)-1 meaning don't + * change attributes if attr was + * passed in as 16-bit -1. + */ + lattr = (uint32_t)-1; + } + + + /* + * First, try setpathinfo (if qpathinfo succeeded), for it is the + * modern function for "new code" to be using, and it works given a + * filename rather than requiring that the file be opened to have its + * attributes manipulated. + */ + if (srv->no_pathinfo || + !NT_STATUS_IS_OK(cli_setpathinfo_ext(srv->cli, path, + create_time, + access_time, + write_time, + change_time, + lattr))) { + + /* + * setpathinfo is not supported; go to plan B. + * + * cli_setatr() does not work on win98, and it also doesn't + * support setting the access time (only the modification + * time), so in all cases, we open the specified file and use + * cli_setattrE() which should work on all OS versions, and + * supports both times. + */ + + /* Don't try {q,set}pathinfo() again, with this server */ + srv->no_pathinfo = True; + + /* Open the file */ + status = cli_open(srv->cli, path, O_RDWR, DENY_NONE, &fd); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return False; + } + + /* Set the new attributes */ + status = cli_setattrE( + srv->cli, + fd, + change_time.tv_sec, + access_time.tv_sec, + write_time.tv_sec); + + /* Close the file */ + cli_close(srv->cli, fd); + + /* + * Unfortunately, setattrE() doesn't have a provision for + * setting the access attr (attributes). We'll have to try + * cli_setatr() for that, and with only this parameter, it + * seems to work on win98. + */ + if (NT_STATUS_IS_OK(status) && attr != (uint16_t) -1) { + status = cli_setatr(srv->cli, path, (uint32_t)attr, 0); + } + + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return False; + } + } + + TALLOC_FREE(frame); + return True; +} + +/* + * A routine to lseek() a file + */ + +off_t +SMBC_lseek_ctx(SMBCCTX *context, + SMBCFILE *file, + off_t offset, + int whence) +{ + off_t size; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + if (!file->file) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; /* Can't lseek a dir ... */ + } + + switch (whence) { + case SEEK_SET: + file->offset = offset; + break; + case SEEK_CUR: + file->offset += offset; + break; + case SEEK_END: + if (!NT_STATUS_IS_OK(cli_qfileinfo_basic( + file->targetcli, file->cli_fd, NULL, + &size, NULL, NULL, NULL, NULL, + NULL))) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + file->offset = size + offset; + break; + default: + errno = EINVAL; + break; + } + + TALLOC_FREE(frame); + return file->offset; +} + + +/* + * Routine to truncate a file given by its file descriptor, to a specified size + */ + +int +SMBC_ftruncate_ctx(SMBCCTX *context, + SMBCFILE *file, + off_t length) +{ + off_t size = length; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + if (!file->file) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!NT_STATUS_IS_OK(cli_ftruncate(file->targetcli, file->cli_fd, (uint64_t)size))) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} diff --git a/source3/libsmb/libsmb_misc.c b/source3/libsmb/libsmb_misc.c new file mode 100644 index 0000000..28e2ca8 --- /dev/null +++ b/source3/libsmb/libsmb_misc.c @@ -0,0 +1,77 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * check if an element is part of the list. + */ +bool SMBC_dlist_contains(SMBCFILE * list, SMBCFILE *p) +{ + if ((p == NULL) || (list == NULL)) { + return false; + } + do { + if (p == list) { + return true; + } + list = list->next; + } while (list != NULL); + return false; +} + + +/* + * Convert an SMB error into a UNIX error ... + */ +int +SMBC_errno(SMBCCTX *context, + struct cli_state *c) +{ + int ret = cli_errno(c); + + if (cli_is_dos_error(c)) { + uint8_t eclass; + uint32_t ecode; + + cli_dos_error(c, &eclass, &ecode); + + DEBUG(3,("smbc_error %d %d (0x%x) -> %d\n", + (int)eclass, (int)ecode, (int)ecode, ret)); + } else { + NTSTATUS status; + + status = cli_nt_error(c); + + DEBUG(3,("smbc errno %s -> %d\n", + nt_errstr(status), ret)); + } + + return ret; +} + diff --git a/source3/libsmb/libsmb_path.c b/source3/libsmb/libsmb_path.c new file mode 100644 index 0000000..73dc374 --- /dev/null +++ b/source3/libsmb/libsmb_path.c @@ -0,0 +1,422 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * smbc_urldecode() + * and urldecode_talloc() (internal fn.) + * + * Convert strings of %xx to their single character equivalent. Each 'x' must + * be a valid hexadecimal digit, or that % sequence is left undecoded. + * + * dest may, but need not be, the same pointer as src. + * + * Returns the number of % sequences which could not be converted due to lack + * of two following hexadecimal digits. + */ +static int +urldecode_talloc(TALLOC_CTX *ctx, char **pp_dest, const char *src) +{ + int old_length = strlen(src); + int i = 0; + int err_count = 0; + size_t newlen = 1; + char *p, *dest; + + if (old_length == 0) { + return 0; + } + + *pp_dest = NULL; + for (i = 0; i < old_length; ) { + unsigned char character = src[i++]; + + if (character == '%') { + uint8_t v; + bool ok = hex_byte(&src[i], &v); + if (ok) { + character = v; + if (character == '\0') { + break; /* Stop at %00 */ + } + i += 2; + } else { + err_count++; + } + } + newlen++; + } + + dest = talloc_array(ctx, char, newlen); + if (!dest) { + return err_count; + } + + err_count = 0; + for (p = dest, i = 0; i < old_length; ) { + unsigned char character = src[i++]; + + if (character == '%') { + uint8_t v; + bool ok = hex_byte(&src[i], &v); + if (ok) { + character = v; + if (character == '\0') { + break; /* Stop at %00 */ + } + i += 2; + } else { + err_count++; + } + } + *p++ = character; + } + + *p = '\0'; + *pp_dest = dest; + return err_count; +} + +int +smbc_urldecode(char *dest, + char *src, + size_t max_dest_len) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *pdest; + int ret = urldecode_talloc(frame, &pdest, src); + + if (pdest) { + strlcpy(dest, pdest, max_dest_len); + } + TALLOC_FREE(frame); + return ret; +} + +/* + * smbc_urlencode() + * + * Convert any characters not specifically allowed in a URL into their %xx + * equivalent. + * + * Returns the remaining buffer length. + */ +int +smbc_urlencode(char *dest, + char *src, + int max_dest_len) +{ + char hex[] = "0123456789ABCDEF"; + + for (; *src != '\0' && max_dest_len >= 3; src++) { + + if ((*src < '0' && + *src != '-' && + *src != '.') || + (*src > '9' && + *src < 'A') || + (*src > 'Z' && + *src < 'a' && + *src != '_') || + (*src > 'z')) { + *dest++ = '%'; + *dest++ = hex[(*src >> 4) & 0x0f]; + *dest++ = hex[*src & 0x0f]; + max_dest_len -= 3; + } else { + *dest++ = *src; + max_dest_len--; + } + } + + if (max_dest_len <= 0) { + /* Ensure we return -1 if no null termination. */ + return -1; + } + + *dest++ = '\0'; + max_dest_len--; + + return max_dest_len; +} + +/* + * Function to parse a path and turn it into components + * + * The general format of an SMB URI is explain in Christopher Hertel's CIFS + * book, at http://ubiqx.org/cifs/Appendix-D.html. We accept a subset of the + * general format ("smb:" only; we do not look for "cifs:"). + * + * + * We accept: + * smb://[[[domain;]user[:password]@]server[:port][/share[/path[/file]]]] + * [?options] + * + * Meaning of URLs: + * + * smb:// Show all workgroups. + * + * The method of locating the list of workgroups varies + * depending upon the setting of the context variable + * context->options.browse_max_lmb_count. This value + * determines the maximum number of local master browsers to + * query for the list of workgroups. In order to ensure that + * a complete list of workgroups is obtained, all master + * browsers must be queried, but if there are many + * workgroups, the time spent querying can begin to add up. + * For small networks (not many workgroups), it is suggested + * that this variable be set to 0, indicating query all local + * master browsers. When the network has many workgroups, a + * reasonable setting for this variable might be around 3. + * + * smb://name/ if name<1D> or name<1B> exists, list servers in + * workgroup, else, if name<20> exists, list all shares + * for server ... + * + * If "options" are provided, this function returns the entire option list as a + * string, for later parsing by the caller. Note that currently, no options + * are supported. + */ + +#define SMBC_PREFIX "smb:" + +int +SMBC_parse_path(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *fname, + char **pp_workgroup, + char **pp_server, + uint16_t *p_port, + char **pp_share, + char **pp_path, + char **pp_user, + char **pp_password, + char **pp_options) +{ + char *s; + const char *p; + char *q, *r; + char *workgroup = NULL; + int len; + + /* Ensure these returns are at least valid pointers. */ + *pp_server = talloc_strdup(ctx, ""); + *p_port = smbc_getPort(context); + *pp_share = talloc_strdup(ctx, ""); + *pp_path = talloc_strdup(ctx, ""); + *pp_user = talloc_strdup(ctx, ""); + *pp_password = talloc_strdup(ctx, ""); + + if (!*pp_server || !*pp_share || !*pp_path || + !*pp_user || !*pp_password) { + return -1; + } + + /* + * Assume we won't find an authentication domain to parse, so default + * to the workgroup in the provided context. + */ + if (pp_workgroup != NULL) { + *pp_workgroup = + talloc_strdup(ctx, smbc_getWorkgroup(context)); + } + + if (pp_options) { + *pp_options = talloc_strdup(ctx, ""); + } + s = talloc_strdup(ctx, fname); + + /* see if it has the right prefix */ + len = strlen(SMBC_PREFIX); + if (strncmp(s,SMBC_PREFIX,len) || (s[len] != '/' && s[len] != 0)) { + return -1; /* What about no smb: ? */ + } + + p = s + len; + + /* Watch the test below, we are testing to see if we should exit */ + + if (strncmp(p, "//", 2) && strncmp(p, "\\\\", 2)) { + DEBUG(1, ("Invalid path (does not begin with smb://")); + return -1; + } + + p += 2; /* Skip the double slash */ + + /* See if any options were specified */ + if ((q = strrchr(p, '?')) != NULL ) { + /* There are options. Null terminate here and point to them */ + *q++ = '\0'; + + DEBUG(4, ("Found options '%s'", q)); + + /* Copy the options */ + if (pp_options && *pp_options != NULL) { + TALLOC_FREE(*pp_options); + *pp_options = talloc_strdup(ctx, q); + } + } + + if (*p == '\0') { + goto decoding; + } + + if (*p == '/') { + int wl = strlen(smbc_getWorkgroup(context)); + + if (wl > 16) { + wl = 16; + } + + *pp_server = talloc_strdup(ctx, smbc_getWorkgroup(context)); + if (!*pp_server) { + return -1; + } + (*pp_server)[wl] = '\0'; + return 0; + } + + /* + * ok, its for us. Now parse out the server, share etc. + * + * However, we want to parse out [[domain;]user[:password]@] if it + * exists ... + */ + + /* check that '@' occurs before '/', if '/' exists at all */ + q = strchr_m(p, '@'); + r = strchr_m(p, '/'); + if (q && (!r || q < r)) { + char *userinfo = NULL; + const char *u; + + next_token_no_ltrim_talloc(ctx, &p, &userinfo, "@"); + if (!userinfo) { + return -1; + } + u = userinfo; + + if (strchr_m(u, ';')) { + next_token_no_ltrim_talloc(ctx, &u, &workgroup, ";"); + if (!workgroup) { + return -1; + } + if (pp_workgroup) { + *pp_workgroup = workgroup; + } + } + + if (strchr_m(u, ':')) { + next_token_no_ltrim_talloc(ctx, &u, pp_user, ":"); + if (!*pp_user) { + return -1; + } + *pp_password = talloc_strdup(ctx, u); + if (!*pp_password) { + return -1; + } + } else { + *pp_user = talloc_strdup(ctx, u); + if (!*pp_user) { + return -1; + } + } + } + + if (!next_token_talloc(ctx, &p, pp_server, "/")) { + return -1; + } + + /* + * Does *pp_server contain a ':' ? If so + * this denotes the port. + */ + q = strchr_m(*pp_server, ':'); + if (q != NULL) { + long int port; + char *endptr = NULL; + *q = '\0'; + q++; + if (*q == '\0') { + /* Bad port. */ + return -1; + } + port = strtol(q, &endptr, 10); + if (*endptr != '\0') { + /* Bad port. */ + return -1; + } + *p_port = (uint16_t)port; + } + + if (*p == (char)0) { + goto decoding; /* That's it ... */ + } + + if (!next_token_talloc(ctx, &p, pp_share, "/")) { + return -1; + } + + /* + * Prepend a leading slash if there's a file path, as required by + * NetApp filers. + */ + if (*p != '\0') { + *pp_path = talloc_asprintf(ctx, + "\\%s", + p); + } else { + *pp_path = talloc_strdup(ctx, ""); + } + if (!*pp_path) { + return -1; + } + string_replace(*pp_path, '/', '\\'); + +decoding: + (void) urldecode_talloc(ctx, pp_path, *pp_path); + (void) urldecode_talloc(ctx, pp_server, *pp_server); + (void) urldecode_talloc(ctx, pp_share, *pp_share); + (void) urldecode_talloc(ctx, pp_user, *pp_user); + (void) urldecode_talloc(ctx, pp_password, *pp_password); + + if (!workgroup) { + workgroup = talloc_strdup(ctx, smbc_getWorkgroup(context)); + } + if (!workgroup) { + return -1; + } + + /* set the credentials to make DFS work */ + smbc_set_credentials_with_fallback(context, + workgroup, + *pp_user, + *pp_password); + return 0; +} + diff --git a/source3/libsmb/libsmb_printjob.c b/source3/libsmb/libsmb_printjob.c new file mode 100644 index 0000000..cb56723 --- /dev/null +++ b/source3/libsmb/libsmb_printjob.c @@ -0,0 +1,337 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* + * Open a print file to be written to by other calls + */ + +SMBCFILE * +SMBC_open_print_job_ctx(SMBCCTX *context, + const char *fname) +{ + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *path = NULL; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + DEBUG(4, ("SMBC_open_print_job_ctx(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + NULL, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return NULL; + } + + /* What if the path is empty, or the file exists? */ + + TALLOC_FREE(frame); + return smbc_getFunctionOpen(context)(context, fname, O_WRONLY, 666); +} + +/* + * Routine to print a file on a remote server ... + * + * We open the file, which we assume to be on a remote server, and then + * copy it to a print file on the share specified by printq. + */ + +int +SMBC_print_file_ctx(SMBCCTX *c_file, + const char *fname, + SMBCCTX *c_print, + const char *printq) +{ + SMBCFILE *fid1; + SMBCFILE *fid2; + smbc_open_fn f_open1; + smbc_open_print_job_fn f_open_pj2; + int bytes; + int saverr; + int tot_bytes = 0; + char buf[4096]; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!c_file || !c_file->internal->initialized || + !c_print || !c_print->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname && !printq) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + /* Try to open the file for reading ... */ + f_open1 = smbc_getFunctionOpen(c_file); + if (f_open1 == NULL) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + fid1 = f_open1(c_file, fname, O_RDONLY, 0666); + if (fid1 == NULL) { + DEBUG(3, ("Error, fname=%s, errno=%i\n", fname, errno)); + TALLOC_FREE(frame); + return -1; /* smbc_open sets errno */ + } + + /* Now, try to open the printer file for writing */ + f_open_pj2 = smbc_getFunctionOpenPrintJob(c_print); + if (f_open_pj2 == NULL) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + fid2 = f_open_pj2(c_print, printq); + if (fid2 == NULL) { + saverr = errno; /* Save errno */ + smbc_getFunctionClose(c_file)(c_file, fid1); + errno = saverr; + TALLOC_FREE(frame); + return -1; + } + + while ((bytes = smbc_getFunctionRead(c_file)(c_file, fid1, + buf, sizeof(buf))) > 0) { + tot_bytes += bytes; + + if ((smbc_getFunctionWrite(c_print)(c_print, fid2, + buf, bytes)) < 0) { + saverr = errno; + smbc_getFunctionClose(c_file)(c_file, fid1); + smbc_getFunctionClose(c_print)(c_print, fid2); + errno = saverr; + } + } + + saverr = errno; + + smbc_getFunctionClose(c_file)(c_file, fid1); + smbc_getFunctionClose(c_print)(c_print, fid2); + + if (bytes < 0) { + errno = saverr; + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return tot_bytes; +} + +/* + * Routine to list print jobs on a printer share ... + */ + +int +SMBC_list_print_jobs_ctx(SMBCCTX *context, + const char *fname, + smbc_list_print_job_fn fn) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_list_print_jobs(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + status = cli_print_queue(srv->cli, + (void (*)(struct print_job_info *))fn); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} + +/* + * Delete a print job from a remote printer share + */ + +int +SMBC_unlink_print_job_ctx(SMBCCTX *context, + const char *fname, + int id) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + int err; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_unlink_print_job(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if ((err = cli_printjob_del(srv->cli, id)) != 0) { + if (err < 0) + errno = SMBC_errno(context, srv->cli); + else if (err == ERRnosuchprintjob) + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} + diff --git a/source3/libsmb/libsmb_server.c b/source3/libsmb/libsmb_server.c new file mode 100644 index 0000000..175e0b2 --- /dev/null +++ b/source3/libsmb/libsmb_server.c @@ -0,0 +1,906 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + Copyright (C) SATOH Fumiyasu <fumiyas@osstech.co.jp> 2009. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" +#include "../librpc/gen_ndr/ndr_lsa.h" +#include "rpc_client/cli_pipe.h" +#include "rpc_client/cli_lsarpc.h" +#include "libcli/security/security.h" +#include "libsmb/nmblib.h" +#include "../libcli/smb/smbXcli_base.h" + +/* + * Check a server for being alive and well. + * returns 0 if the server is in shape. Returns 1 on error + * + * Also useable outside libsmbclient to enable external cache + * to do some checks too. + */ +int +SMBC_check_server(SMBCCTX * context, + SMBCSRV * server) +{ + time_t now; + + if (!cli_state_is_connected(server->cli)) { + return 1; + } + + now = time_mono(NULL); + + if (server->last_echo_time == (time_t)0 || + now > server->last_echo_time + + (server->cli->timeout/1000)) { + unsigned char data[16] = {0}; + NTSTATUS status = cli_echo(server->cli, + 1, + data_blob_const(data, sizeof(data))); + if (!NT_STATUS_IS_OK(status)) { + bool ok = false; + /* + * Some SMB2 servers (not Samba or Windows) + * check the session status on SMB2_ECHO and return + * NT_STATUS_USER_SESSION_DELETED + * if the session was not set. That's OK, they still + * replied. + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13218 + */ + if (smbXcli_conn_protocol(server->cli->conn) >= + PROTOCOL_SMB2_02) { + if (NT_STATUS_EQUAL(status, + NT_STATUS_USER_SESSION_DELETED)) { + ok = true; + } + } + /* + * Some NetApp servers return + * NT_STATUS_INVALID_PARAMETER.That's OK, they still + * replied. + * BUG: https://bugzilla.samba.org/show_bug.cgi?id=13007 + */ + if (NT_STATUS_EQUAL(status, + NT_STATUS_INVALID_PARAMETER)) { + ok = true; + } + if (!ok) { + return 1; + } + } + server->last_echo_time = now; + } + return 0; +} + +/* + * Remove a server from the cached server list it's unused. + * On success, 0 is returned. 1 is returned if the server could not be removed. + * + * Also useable outside libsmbclient + */ +int +SMBC_remove_unused_server(SMBCCTX * context, + SMBCSRV * srv) +{ + SMBCFILE * file; + + /* are we being fooled ? */ + if (!context || !context->internal->initialized || !srv) { + return 1; + } + + /* Check all open files/directories for a relation with this server */ + for (file = context->internal->files; file; file = file->next) { + if (file->srv == srv) { + /* Still used */ + DEBUG(3, ("smbc_remove_usused_server: " + "%p still used by %p.\n", + srv, file)); + return 1; + } + } + + DLIST_REMOVE(context->internal->servers, srv); + + cli_shutdown(srv->cli); + srv->cli = NULL; + + DEBUG(3, ("smbc_remove_usused_server: %p removed.\n", srv)); + + smbc_getFunctionRemoveCachedServer(context)(context, srv); + + SAFE_FREE(srv); + return 0; +} + +/**************************************************************** + * Call the auth_fn with fixed size (fstring) buffers. + ***************************************************************/ +static void +SMBC_call_auth_fn(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *server, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + fstring workgroup = { 0 }; + fstring username = { 0 }; + fstring password = { 0 }; + smbc_get_auth_data_with_context_fn auth_with_context_fn; + + if (*pp_workgroup != NULL) { + strlcpy(workgroup, *pp_workgroup, sizeof(workgroup)); + } + if (*pp_username != NULL) { + strlcpy(username, *pp_username, sizeof(username)); + } + if (*pp_password != NULL) { + strlcpy(password, *pp_password, sizeof(password)); + } + + /* See if there's an authentication with context function provided */ + auth_with_context_fn = smbc_getFunctionAuthDataWithContext(context); + if (auth_with_context_fn) + { + (* auth_with_context_fn)(context, + server, share, + workgroup, sizeof(workgroup), + username, sizeof(username), + password, sizeof(password)); + } + else + { + smbc_getFunctionAuthData(context)(server, share, + workgroup, sizeof(workgroup), + username, sizeof(username), + password, sizeof(password)); + } + + TALLOC_FREE(*pp_workgroup); + TALLOC_FREE(*pp_username); + TALLOC_FREE(*pp_password); + + *pp_workgroup = talloc_strdup(ctx, workgroup); + *pp_username = talloc_strdup(ctx, username); + *pp_password = talloc_strdup(ctx, password); +} + + +void +SMBC_get_auth_data(const char *server, const char *share, + char *workgroup_buf, int workgroup_buf_len, + char *username_buf, int username_buf_len, + char *password_buf, int password_buf_len) +{ + /* Default function just uses provided data. Nothing to do. */ +} + + + +SMBCSRV * +SMBC_find_server(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *server, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + SMBCSRV *srv; + int auth_called = 0; + + if (!pp_workgroup || !pp_username || !pp_password) { + return NULL; + } + +check_server_cache: + + srv = smbc_getFunctionGetCachedServer(context)(context, + server, share, + *pp_workgroup, + *pp_username); + + if (!auth_called && !srv && (!*pp_username || !(*pp_username)[0] || + !*pp_password || !(*pp_password)[0])) { + SMBC_call_auth_fn(ctx, context, server, share, + pp_workgroup, pp_username, pp_password); + + /* + * However, smbc_auth_fn may have picked up info relating to + * an existing connection, so try for an existing connection + * again ... + */ + auth_called = 1; + goto check_server_cache; + + } + + if (srv) { + if (smbc_getFunctionCheckServer(context)(context, srv)) { + /* + * This server is no good anymore + * Try to remove it and check for more possible + * servers in the cache + */ + if (smbc_getFunctionRemoveUnusedServer(context)(context, + srv)) { + /* + * We could not remove the server completely, + * remove it from the cache so we will not get + * it again. It will be removed when the last + * file/dir is closed. + */ + smbc_getFunctionRemoveCachedServer(context)(context, + srv); + } + + /* + * Maybe there are more cached connections to this + * server + */ + goto check_server_cache; + } + + return srv; + } + + return NULL; +} + +static struct cli_credentials *SMBC_auth_credentials(TALLOC_CTX *mem_ctx, + SMBCCTX *context, + const char *domain, + const char *username, + const char *password) +{ + struct cli_credentials *creds = NULL; + bool use_kerberos = false; + bool fallback_after_kerberos = false; + bool use_ccache = false; + bool pw_nt_hash = false; + + use_kerberos = smbc_getOptionUseKerberos(context); + fallback_after_kerberos = smbc_getOptionFallbackAfterKerberos(context); + use_ccache = smbc_getOptionUseCCache(context); + pw_nt_hash = smbc_getOptionUseNTHash(context); + + creds = cli_session_creds_init(mem_ctx, + username, + domain, + NULL, /* realm */ + password, + use_kerberos, + fallback_after_kerberos, + use_ccache, + pw_nt_hash); + if (creds == NULL) { + return NULL; + } + + switch (context->internal->smb_encryption_level) { + case SMBC_ENCRYPTLEVEL_DEFAULT: + /* Use the config option */ + break; + case SMBC_ENCRYPTLEVEL_NONE: + (void)cli_credentials_set_smb_encryption( + creds, + SMB_ENCRYPTION_OFF, + CRED_SPECIFIED); + break; + case SMBC_ENCRYPTLEVEL_REQUEST: + (void)cli_credentials_set_smb_encryption( + creds, + SMB_ENCRYPTION_DESIRED, + CRED_SPECIFIED); + break; + case SMBC_ENCRYPTLEVEL_REQUIRE: + default: + (void)cli_credentials_set_smb_encryption( + creds, + SMB_ENCRYPTION_REQUIRED, + CRED_SPECIFIED); + break; + } + + + return creds; +} + +/* + * Connect to a server, possibly on an existing connection + * + * Here, what we want to do is: If the server and username + * match an existing connection, reuse that, otherwise, establish a + * new connection. + * + * If we have to create a new connection, call the auth_fn to get the + * info we need, unless the username and password were passed in. + */ + +static SMBCSRV * +SMBC_server_internal(TALLOC_CTX *ctx, + SMBCCTX *context, + bool connect_if_not_found, + const char *server, + uint16_t port, + 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_t fs_attrs = 0; + const char *username_used = NULL; + const char *password_used = NULL; + NTSTATUS status; + char *newserver, *newshare; + int flags = 0; + struct smbXcli_tcon *tcon = NULL; + int signing_state = SMB_SIGNING_DEFAULT; + struct cli_credentials *creds = NULL; + + *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 != NULL && *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); + if (!NT_STATUS_IS_OK(status)) { + cli_shutdown(srv->cli); + errno = map_errno_from_nt_status(status); + srv->cli = NULL; + smbc_getFunctionRemoveCachedServer(context)(context, + srv); + srv = NULL; + goto not_found; + } + + /* 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(srv->cli, &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(srv->cli, true); + } else { + cli_set_case_sensitive(srv->cli, false); + } + } else if (!is_ipc) { + DEBUG(4, + ("Case sensitive: %s\n", + (fs_attrs & FILE_CASE_SENSITIVE_SEARCH + ? "True" + : "False"))); + cli_set_case_sensitive( + srv->cli, + (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)); + } + } + } + + not_found: + + /* 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 (context->internal->smb_encryption_level > SMBC_ENCRYPTLEVEL_NONE) { + signing_state = SMB_SIGNING_REQUIRED; + } + + if (port == 0) { + 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), + signing_state, flags, &c); + } + } + + if (!NT_STATUS_IS_OK(status)) { + /* + * No IPC$ or 139 did not work + */ + status = cli_connect_nb(server_n, NULL, port, 0x20, + smbc_getNetbiosName(context), + signing_state, flags, &c); + } + + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + DBG_ERR("NetBIOS support disabled, unable to connect"); + } + + errno = map_errno_from_nt_status(status); + return NULL; + } + + cli_set_timeout(c, smbc_getTimeout(context)); + + status = smbXcli_negprot(c->conn, c->timeout, + lp_client_min_protocol(), + lp_client_max_protocol()); + if (!NT_STATUS_IS_OK(status)) { + cli_shutdown(c); + errno = map_errno_from_nt_status(status); + return NULL; + } + + if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { + /* Ensure we ask for some initial credits. */ + smb2cli_conn_set_max_credits(c->conn, DEFAULT_SMB2_MAX_CREDITS); + } + + username_used = *pp_username; + password_used = *pp_password; + + creds = SMBC_auth_credentials(c, + context, + *pp_workgroup, + username_used, + password_used); + if (creds == NULL) { + cli_shutdown(c); + errno = ENOMEM; + return NULL; + } + + status = cli_session_setup_creds(c, creds); + if (!NT_STATUS_IS_OK(status)) { + + /* Failed. Try an anonymous login, if allowed by flags. */ + username_used = ""; + password_used = ""; + + if (smbc_getOptionNoAutoAnonymousLogin(context) || + !NT_STATUS_IS_OK(cli_session_setup_anon(c))) { + + cli_shutdown(c); + errno = map_errno_from_nt_status(status); + 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 (smbXcli_conn_dfs_supported(c->conn) && + cli_check_msdfs_proxy(ctx, c, share, + &newserver, &newshare, + creds)) { + cli_shutdown(c); + srv = SMBC_server_internal(ctx, context, connect_if_not_found, + newserver, port, 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_creds(c, share, "?????", creds); + if (!NT_STATUS_IS_OK(status)) { + cli_shutdown(c); + errno = map_errno_from_nt_status(status); + return NULL; + } + + DEBUG(4,(" tconx ok\n")); + + if (smbXcli_conn_protocol(c->conn) >= PROTOCOL_SMB2_02) { + tcon = c->smb2.tcon; + } else { + tcon = c->smb1.tcon; + } + + /* 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"))); + smbXcli_tcon_set_fs_attributes(tcon, fs_attrs); + } + + /* + * 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); + DLIST_ADD(srv->cli, c); + srv->dev = (dev_t)(str_checksum(server) ^ str_checksum(share)); + srv->no_pathinfo = False; + srv->no_pathinfo2 = False; + srv->no_pathinfo3 = 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; +} + +SMBCSRV * +SMBC_server(TALLOC_CTX *ctx, + SMBCCTX *context, + bool connect_if_not_found, + const char *server, + uint16_t port, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + SMBCSRV *srv=NULL; + bool in_cache = false; + + srv = SMBC_server_internal(ctx, context, connect_if_not_found, + server, port, share, pp_workgroup, + pp_username, pp_password, &in_cache); + + if (!srv) { + return NULL; + } + if (in_cache) { + return srv; + } + + /* Now add it to the cache (internal or external) */ + /* Let the cache function set errno if it wants to */ + errno = 0; + if (smbc_getFunctionAddCachedServer(context)(context, srv, + server, share, + *pp_workgroup, + *pp_username)) { + int saved_errno = errno; + DEBUG(3, (" Failed to add server to cache\n")); + errno = saved_errno; + if (errno == 0) { + errno = ENOMEM; + } + SAFE_FREE(srv); + return NULL; + } + + DEBUG(2, ("Server connect ok: //%s/%s: %p\n", + server, share, srv)); + + DLIST_ADD(context->internal->servers, srv); + return srv; +} + +/* + * Connect to a server for getting/setting attributes, possibly on an existing + * connection. This works similarly to SMBC_server(). + */ +SMBCSRV * +SMBC_attr_server(TALLOC_CTX *ctx, + SMBCCTX *context, + const char *server, + uint16_t port, + const char *share, + char **pp_workgroup, + char **pp_username, + char **pp_password) +{ + int flags; + struct cli_state *ipc_cli = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + NTSTATUS nt_status; + SMBCSRV *srv=NULL; + SMBCSRV *ipc_srv=NULL; + + /* + * 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. + */ + srv = SMBC_server(ctx, context, true, server, port, share, + pp_workgroup, pp_username, pp_password); + if (!srv) { + return NULL; + } + server = smbXcli_conn_remote_name(srv->cli->conn); + share = srv->cli->share; + + /* + * See if we've already created this special connection. Reference + * our "special" share name '*IPC$', which is an impossible real share + * name due to the leading asterisk. + */ + ipc_srv = SMBC_find_server(ctx, context, server, "*IPC$", + pp_workgroup, pp_username, pp_password); + if (!ipc_srv) { + struct cli_credentials *creds = NULL; + + /* We didn't find a cached connection. Get the password */ + if (!*pp_password || (*pp_password)[0] == '\0') { + /* ... then retrieve it now. */ + SMBC_call_auth_fn(ctx, context, server, share, + pp_workgroup, + pp_username, + pp_password); + if (!*pp_workgroup || !*pp_username || !*pp_password) { + errno = ENOMEM; + return NULL; + } + } + + flags = 0; + + creds = SMBC_auth_credentials(NULL, + context, + *pp_workgroup, + *pp_username, + *pp_password); + if (creds == NULL) { + errno = ENOMEM; + return NULL; + } + + nt_status = cli_full_connection_creds(&ipc_cli, + lp_netbios_name(), server, + NULL, 0, "IPC$", "?????", + creds, + flags); + if (! NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(creds); + DEBUG(1,("cli_full_connection failed! (%s)\n", + nt_errstr(nt_status))); + errno = ENOTSUP; + return NULL; + } + talloc_steal(ipc_cli, creds); + + ipc_srv = SMB_MALLOC_P(SMBCSRV); + if (!ipc_srv) { + errno = ENOMEM; + cli_shutdown(ipc_cli); + return NULL; + } + + ZERO_STRUCTP(ipc_srv); + DLIST_ADD(ipc_srv->cli, ipc_cli); + + nt_status = cli_rpc_pipe_open_noauth( + ipc_srv->cli, &ndr_table_lsarpc, &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("cli_nt_session_open fail!\n")); + errno = ENOTSUP; + cli_shutdown(ipc_srv->cli); + free(ipc_srv); + return NULL; + } + + /* + * Some systems don't support + * SEC_FLAG_MAXIMUM_ALLOWED, but NT sends 0x2000000 + * so we might as well do it too. + */ + + nt_status = rpccli_lsa_open_policy( + pipe_hnd, + talloc_tos(), + True, + GENERIC_EXECUTE_ACCESS, + &ipc_srv->pol); + + if (!NT_STATUS_IS_OK(nt_status)) { + cli_shutdown(ipc_srv->cli); + free(ipc_srv); + errno = cli_status_to_errno(nt_status); + return NULL; + } + + /* now add it to the cache (internal or external) */ + + errno = 0; /* let cache function set errno if it likes */ + if (smbc_getFunctionAddCachedServer(context)(context, ipc_srv, + server, + "*IPC$", + *pp_workgroup, + *pp_username)) { + DEBUG(3, (" Failed to add server to cache\n")); + if (errno == 0) { + errno = ENOMEM; + } + cli_shutdown(ipc_srv->cli); + free(ipc_srv); + return NULL; + } + + DLIST_ADD(context->internal->servers, ipc_srv); + } + + return ipc_srv; +} diff --git a/source3/libsmb/libsmb_setget.c b/source3/libsmb/libsmb_setget.c new file mode 100644 index 0000000..d071efa --- /dev/null +++ b/source3/libsmb/libsmb_setget.c @@ -0,0 +1,1136 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#define __LIBSMBCLIENT_INTERNAL__ +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/** Get the netbios name used for making connections */ +const char * +smbc_getNetbiosName(SMBCCTX *c) +{ + return c->netbios_name; +} + +/** Set the netbios name used for making connections */ +void +smbc_setNetbiosName(SMBCCTX *c, const char *netbios_name) +{ + SAFE_FREE(c->netbios_name); + if (netbios_name) { + c->netbios_name = SMB_STRDUP(netbios_name); + } +} + +/** Get the workgroup used for making connections */ +const char * +smbc_getWorkgroup(SMBCCTX *c) +{ + return c->workgroup; +} + +/** Set the workgroup used for making connections */ +void +smbc_setWorkgroup(SMBCCTX *c, const char *workgroup) +{ + SAFE_FREE(c->workgroup); + if (workgroup) { + c->workgroup = SMB_STRDUP(workgroup); + } +} + +/** Get the username used for making connections */ +const char * +smbc_getUser(SMBCCTX *c) +{ + return c->user; +} + +/** Set the username used for making connections */ +void +smbc_setUser(SMBCCTX *c, const char *user) +{ + SAFE_FREE(c->user); + if (user) { + c->user = SMB_STRDUP(user); + } +} + +/** Get the debug level */ +int +smbc_getDebug(SMBCCTX *c) +{ + return c->debug; +} + +/** Set the debug level */ +void +smbc_setDebug(SMBCCTX *c, int debug) +{ + char buf[32]; + TALLOC_CTX *frame = talloc_stackframe(); + snprintf(buf, sizeof(buf), "%d", debug); + c->debug = debug; + lp_set_cmdline("log level", buf); + TALLOC_FREE(frame); +} + +/** set callback function which will be called for logging */ +void +smbc_setLogCallback(SMBCCTX *c, void *private_ptr, + smbc_debug_callback_fn fn) +{ + debug_set_callback(private_ptr, fn); +} + +/** set configuration file */ +int smbc_setConfiguration(SMBCCTX *c, const char *file) +{ + bool ok; + + ok = lp_load_client_no_reinit(file); + if (!ok) { + DBG_WARNING("Could not load config file: %s\n", file); + errno = ENOENT; + return -1; + } + + DBG_NOTICE("Configuration loaded successfully: %s\n", file); + return 0; +} +/** + * Get the timeout used for waiting on connections and response data + * (in milliseconds) + */ +int +smbc_getTimeout(SMBCCTX *c) +{ + return c->timeout; +} + +/** + * Set the timeout used for waiting on connections and response data + * (in milliseconds) + */ +void +smbc_setTimeout(SMBCCTX *c, int timeout) +{ + c->timeout = timeout; +} + +/** + * Get the TCP port used to connect. + */ +uint16_t +smbc_getPort(SMBCCTX *c) +{ + return c->internal->port; +} + +/** + * Set the TCP port used to connect. + */ +void +smbc_setPort(SMBCCTX *c, uint16_t port) +{ + c->internal->port = port; +} + + +/** Get whether to log to standard error instead of standard output */ +smbc_bool +smbc_getOptionDebugToStderr(SMBCCTX *c) +{ + smbc_bool ret; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Because this is a global concept, it is better to check + * what is really set, rather than what we wanted set + * (particularly as you cannot go back to stdout). */ + ret = debug_get_output_is_stderr(); + TALLOC_FREE(frame); + return ret; +} + +/** Set whether to log to standard error instead of standard output. + * This option is 'sticky' - once set to true, it cannot be set to + * false again, as it is global to the process, as once we have been + * told that it is not safe to safe to write to stdout, we shouldn't + * go back as we don't know it was this context that set it that way. + */ +void +smbc_setOptionDebugToStderr(SMBCCTX *c, smbc_bool b) +{ + TALLOC_CTX *frame = talloc_stackframe(); + if (b) { + /* + * We do not have a unique per-thread debug state? For + * now, we'll just leave it up to the user. If any one + * context spefies debug to stderr then all will be (and + * will stay that way, as it is unsafe to flip back if + * stdout is in use for other things) + */ + setup_logging("libsmbclient", DEBUG_STDERR); + } + TALLOC_FREE(frame); +} + +/** + * Get whether to use new-style time attribute names, e.g. WRITE_TIME rather + * than the old-style names such as M_TIME. This allows also setting/getting + * CREATE_TIME which was previously unimplemented. (Note that the old C_TIME + * was supposed to be CHANGE_TIME but was confused and sometimes referred to + * CREATE_TIME.) + */ +smbc_bool +smbc_getOptionFullTimeNames(SMBCCTX *c) +{ + return c->internal->full_time_names; +} + +/** + * Set whether to use new-style time attribute names, e.g. WRITE_TIME rather + * than the old-style names such as M_TIME. This allows also setting/getting + * CREATE_TIME which was previously unimplemented. (Note that the old C_TIME + * was supposed to be CHANGE_TIME but was confused and sometimes referred to + * CREATE_TIME.) + */ +void +smbc_setOptionFullTimeNames(SMBCCTX *c, smbc_bool b) +{ + c->internal->full_time_names = b; +} + +/** + * Get the share mode to use for files opened with SMBC_open_ctx(). The + * default is SMBC_SHAREMODE_DENY_NONE. + */ +smbc_share_mode +smbc_getOptionOpenShareMode(SMBCCTX *c) +{ + return c->internal->share_mode; +} + +/** + * Set the share mode to use for files opened with SMBC_open_ctx(). The + * default is SMBC_SHAREMODE_DENY_NONE. + */ +void +smbc_setOptionOpenShareMode(SMBCCTX *c, smbc_share_mode share_mode) +{ + c->internal->share_mode = share_mode; +} + +/** Retrieve a previously set user data handle */ +void * +smbc_getOptionUserData(SMBCCTX *c) +{ + return c->internal->user_data; +} + +/** Save a user data handle */ +void +smbc_setOptionUserData(SMBCCTX *c, void *user_data) +{ + c->internal->user_data = user_data; +} + +/** Get the encoded value for encryption level. */ +smbc_smb_encrypt_level +smbc_getOptionSmbEncryptionLevel(SMBCCTX *c) +{ + return c->internal->smb_encryption_level; +} + +/** Set the encoded value for encryption level. */ +void +smbc_setOptionSmbEncryptionLevel(SMBCCTX *c, smbc_smb_encrypt_level level) +{ + c->internal->smb_encryption_level = level; +} + +/** + * Get whether to treat file names as case-sensitive if we can't determine + * when connecting to the remote share whether the file system is case + * sensitive. This defaults to FALSE since it's most likely that if we can't + * retrieve the file system attributes, it's a very old file system that does + * not support case sensitivity. + */ +smbc_bool +smbc_getOptionCaseSensitive(SMBCCTX *c) +{ + return c->internal->case_sensitive; +} + +/** + * Set whether to treat file names as case-sensitive if we can't determine + * when connecting to the remote share whether the file system is case + * sensitive. This defaults to FALSE since it's most likely that if we can't + * retrieve the file system attributes, it's a very old file system that does + * not support case sensitivity. + */ +void +smbc_setOptionCaseSensitive(SMBCCTX *c, smbc_bool b) +{ + c->internal->case_sensitive = b; +} + +/** + * Get from how many local master browsers should the list of workgroups be + * retrieved. It can take up to 12 minutes or longer after a server becomes a + * local master browser, for it to have the entire browse list (the list of + * workgroups/domains) from an entire network. Since a client never knows + * which local master browser will be found first, the one which is found + * first and used to retrieve a browse list may have an incomplete or empty + * browse list. By requesting the browse list from multiple local master + * browsers, a more complete list can be generated. For small networks (few + * workgroups), it is recommended that this value be set to 0, causing the + * browse lists from all found local master browsers to be retrieved and + * merged. For networks with many workgroups, a suitable value for this + * variable is probably somewhere around 3. (Default: 3). + */ +int +smbc_getOptionBrowseMaxLmbCount(SMBCCTX *c) +{ + return c->options.browse_max_lmb_count; +} + +/** + * Set from how many local master browsers should the list of workgroups be + * retrieved. It can take up to 12 minutes or longer after a server becomes a + * local master browser, for it to have the entire browse list (the list of + * workgroups/domains) from an entire network. Since a client never knows + * which local master browser will be found first, the one which is found + * first and used to retrieve a browse list may have an incomplete or empty + * browse list. By requesting the browse list from multiple local master + * browsers, a more complete list can be generated. For small networks (few + * workgroups), it is recommended that this value be set to 0, causing the + * browse lists from all found local master browsers to be retrieved and + * merged. For networks with many workgroups, a suitable value for this + * variable is probably somewhere around 3. (Default: 3). + */ +void +smbc_setOptionBrowseMaxLmbCount(SMBCCTX *c, int count) +{ + c->options.browse_max_lmb_count = count; +} + +/** + * Get whether to url-encode readdir entries. + * + * There is a difference in the desired return strings from + * smbc_readdir() depending upon whether the filenames are to + * be displayed to the user, or whether they are to be + * appended to the path name passed to smbc_opendir() to call + * a further smbc_ function (e.g. open the file with + * smbc_open()). In the former case, the filename should be + * in "human readable" form. In the latter case, the smbc_ + * functions expect a URL which must be url-encoded. Those + * functions decode the URL. If, for example, smbc_readdir() + * returned a file name of "abc%20def.txt", passing a path + * with this file name attached to smbc_open() would cause + * smbc_open to attempt to open the file "abc def.txt" since + * the %20 is decoded into a space. + * + * Set this option to True if the names returned by + * smbc_readdir() should be url-encoded such that they can be + * passed back to another smbc_ call. Set it to False if the + * names returned by smbc_readdir() are to be presented to the + * user. + * + * For backwards compatibility, this option defaults to False. + */ +smbc_bool +smbc_getOptionUrlEncodeReaddirEntries(SMBCCTX *c) +{ + return c->options.urlencode_readdir_entries; +} + +/** + * Set whether to url-encode readdir entries. + * + * There is a difference in the desired return strings from + * smbc_readdir() depending upon whether the filenames are to + * be displayed to the user, or whether they are to be + * appended to the path name passed to smbc_opendir() to call + * a further smbc_ function (e.g. open the file with + * smbc_open()). In the former case, the filename should be + * in "human readable" form. In the latter case, the smbc_ + * functions expect a URL which must be url-encoded. Those + * functions decode the URL. If, for example, smbc_readdir() + * returned a file name of "abc%20def.txt", passing a path + * with this file name attached to smbc_open() would cause + * smbc_open to attempt to open the file "abc def.txt" since + * the %20 is decoded into a space. + * + * Set this option to True if the names returned by + * smbc_readdir() should be url-encoded such that they can be + * passed back to another smbc_ call. Set it to False if the + * names returned by smbc_readdir() are to be presented to the + * user. + * + * For backwards compatibility, this option defaults to False. + */ +void +smbc_setOptionUrlEncodeReaddirEntries(SMBCCTX *c, smbc_bool b) +{ + c->options.urlencode_readdir_entries = b; +} + +/** + * Get whether to use the same connection for all shares on a server. + * + * Some Windows versions appear to have a limit to the number + * of concurrent SESSIONs and/or TREE CONNECTions. In + * one-shot programs (i.e. the program runs and then quickly + * ends, thereby shutting down all connections), it is + * probably reasonable to establish a new connection for each + * share. In long-running applications, the limitation can be + * avoided by using only a single connection to each server, + * and issuing a new TREE CONNECT when the share is accessed. + */ +smbc_bool +smbc_getOptionOneSharePerServer(SMBCCTX *c) +{ + return c->options.one_share_per_server; +} + +/** + * Set whether to use the same connection for all shares on a server. + * + * Some Windows versions appear to have a limit to the number + * of concurrent SESSIONs and/or TREE CONNECTions. In + * one-shot programs (i.e. the program runs and then quickly + * ends, thereby shutting down all connections), it is + * probably reasonable to establish a new connection for each + * share. In long-running applications, the limitation can be + * avoided by using only a single connection to each server, + * and issuing a new TREE CONNECT when the share is accessed. + */ +void +smbc_setOptionOneSharePerServer(SMBCCTX *c, smbc_bool b) +{ + c->options.one_share_per_server = b; +} + +/** Get whether to enable use of kerberos */ +smbc_bool +smbc_getOptionUseKerberos(SMBCCTX *c) +{ + return c->flags & SMB_CTX_FLAG_USE_KERBEROS ? True : False; +} + +/** Set whether to enable use of kerberos */ +void +smbc_setOptionUseKerberos(SMBCCTX *c, smbc_bool b) +{ + if (b) { + c->flags |= SMB_CTX_FLAG_USE_KERBEROS; + } else { + c->flags &= ~SMB_CTX_FLAG_USE_KERBEROS; + } +} + +/** Get whether to fallback after kerberos */ +smbc_bool +smbc_getOptionFallbackAfterKerberos(SMBCCTX *c) +{ + return c->flags & SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS ? True : False; +} + +/** Set whether to fallback after kerberos */ +void +smbc_setOptionFallbackAfterKerberos(SMBCCTX *c, smbc_bool b) +{ + if (b) { + c->flags |= SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS; + } else { + c->flags &= ~SMB_CTX_FLAG_FALLBACK_AFTER_KERBEROS; + } +} + +/** Get whether to automatically select anonymous login */ +smbc_bool +smbc_getOptionNoAutoAnonymousLogin(SMBCCTX *c) +{ + return c->flags & SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON ? True : False; +} + +/** Set whether to automatically select anonymous login */ +void +smbc_setOptionNoAutoAnonymousLogin(SMBCCTX *c, smbc_bool b) +{ + if (b) { + c->flags |= SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON; + } else { + c->flags &= ~SMBCCTX_FLAG_NO_AUTO_ANONYMOUS_LOGON; + } +} + +/** Get whether to enable use of the winbind ccache */ +smbc_bool +smbc_getOptionUseCCache(SMBCCTX *c) +{ + return c->flags & SMB_CTX_FLAG_USE_CCACHE ? True : False; +} + +/** Set whether to enable use of the winbind ccache */ +void +smbc_setOptionUseCCache(SMBCCTX *c, smbc_bool b) +{ + if (b) { + c->flags |= SMB_CTX_FLAG_USE_CCACHE; + } else { + c->flags &= ~SMB_CTX_FLAG_USE_CCACHE; + } +} + +/** Get indication whether the password supplied is the NT hash */ +smbc_bool +smbc_getOptionUseNTHash(SMBCCTX *c) +{ + return (c->flags & SMB_CTX_FLAG_USE_NT_HASH) != 0; +} + +/** Set indication that the password supplied is the NT hash */ +void +smbc_setOptionUseNTHash(SMBCCTX *c, smbc_bool b) +{ + if (b) { + c->flags |= SMB_CTX_FLAG_USE_NT_HASH; + } else { + c->flags &= ~SMB_CTX_FLAG_USE_NT_HASH; + } +} + +smbc_bool +smbc_setOptionProtocols(SMBCCTX *c, + const char *min_proto, + const char *max_proto) +{ + bool ok = true; + + if (min_proto != NULL) { + ok = lp_set_cmdline("client min protocol", min_proto); + } + + if (max_proto != NULL) { + ok &= lp_set_cmdline("client max protocol", max_proto); + } + + return ok; +} + +/** Get the function for obtaining authentication data */ +smbc_get_auth_data_fn +smbc_getFunctionAuthData(SMBCCTX *c) +{ + smbc_get_auth_data_fn ret; + TALLOC_CTX *frame = talloc_stackframe(); + ret = c->callbacks.auth_fn; + TALLOC_FREE(frame); + return ret; +} + +/** Set the function for obtaining authentication data */ +void +smbc_setFunctionAuthData(SMBCCTX *c, smbc_get_auth_data_fn fn) +{ + c->internal->auth_fn_with_context = NULL; + c->callbacks.auth_fn = fn; +} + +/** Get the new-style authentication function which includes the context. */ +smbc_get_auth_data_with_context_fn +smbc_getFunctionAuthDataWithContext(SMBCCTX *c) +{ + return c->internal->auth_fn_with_context; +} + +/** Set the new-style authentication function which includes the context. */ +void +smbc_setFunctionAuthDataWithContext(SMBCCTX *c, + smbc_get_auth_data_with_context_fn fn) +{ + c->callbacks.auth_fn = NULL; + c->internal->auth_fn_with_context = fn; +} + +/** Get the function for checking if a server is still good */ +smbc_check_server_fn +smbc_getFunctionCheckServer(SMBCCTX *c) +{ + return c->callbacks.check_server_fn; +} + +/** Set the function for checking if a server is still good */ +void +smbc_setFunctionCheckServer(SMBCCTX *c, smbc_check_server_fn fn) +{ + c->callbacks.check_server_fn = fn; +} + +/** Get the function for removing a server if unused */ +smbc_remove_unused_server_fn +smbc_getFunctionRemoveUnusedServer(SMBCCTX *c) +{ + return c->callbacks.remove_unused_server_fn; +} + +/** Set the function for removing a server if unused */ +void +smbc_setFunctionRemoveUnusedServer(SMBCCTX *c, + smbc_remove_unused_server_fn fn) +{ + c->callbacks.remove_unused_server_fn = fn; +} + +/** Get the function for adding a cached server */ +smbc_add_cached_srv_fn +smbc_getFunctionAddCachedServer(SMBCCTX *c) +{ + return c->callbacks.add_cached_srv_fn; +} + +/** Set the function for adding a cached server */ +void +smbc_setFunctionAddCachedServer(SMBCCTX *c, smbc_add_cached_srv_fn fn) +{ + c->callbacks.add_cached_srv_fn = fn; +} + +/** Get the function for server cache lookup */ +smbc_get_cached_srv_fn +smbc_getFunctionGetCachedServer(SMBCCTX *c) +{ + return c->callbacks.get_cached_srv_fn; +} + +/** Set the function for server cache lookup */ +void +smbc_setFunctionGetCachedServer(SMBCCTX *c, smbc_get_cached_srv_fn fn) +{ + c->callbacks.get_cached_srv_fn = fn; +} + +/** Get the function for server cache removal */ +smbc_remove_cached_srv_fn +smbc_getFunctionRemoveCachedServer(SMBCCTX *c) +{ + return c->callbacks.remove_cached_srv_fn; +} + +/** Set the function for server cache removal */ +void +smbc_setFunctionRemoveCachedServer(SMBCCTX *c, + smbc_remove_cached_srv_fn fn) +{ + c->callbacks.remove_cached_srv_fn = fn; +} + +/** + * Get the function for server cache purging. This function tries to + * remove all cached servers (e.g. on disconnect) + */ +smbc_purge_cached_fn +smbc_getFunctionPurgeCachedServers(SMBCCTX *c) +{ + return c->callbacks.purge_cached_fn; +} + +/** Set the function to store private data of the server cache */ +void smbc_setServerCacheData(SMBCCTX *c, struct smbc_server_cache * cache) +{ + c->internal->server_cache = cache; +} + +/** Get the function to store private data of the server cache */ +struct smbc_server_cache * smbc_getServerCacheData(SMBCCTX *c) +{ + return c->internal->server_cache; +} + + +/** + * Set the function for server cache purging. This function tries to + * remove all cached servers (e.g. on disconnect) + */ +void +smbc_setFunctionPurgeCachedServers(SMBCCTX *c, smbc_purge_cached_fn fn) +{ + c->callbacks.purge_cached_fn = fn; +} + +/** + * Callable functions for files. + */ + +smbc_open_fn +smbc_getFunctionOpen(SMBCCTX *c) +{ + return c->open; +} + +void +smbc_setFunctionOpen(SMBCCTX *c, smbc_open_fn fn) +{ + c->open = fn; +} + +smbc_creat_fn +smbc_getFunctionCreat(SMBCCTX *c) +{ + return c->creat; +} + +void +smbc_setFunctionCreat(SMBCCTX *c, smbc_creat_fn fn) +{ + c->creat = fn; +} + +smbc_read_fn +smbc_getFunctionRead(SMBCCTX *c) +{ + return c->read; +} + +void +smbc_setFunctionRead(SMBCCTX *c, smbc_read_fn fn) +{ + c->read = fn; +} + +smbc_write_fn +smbc_getFunctionWrite(SMBCCTX *c) +{ + return c->write; +} + +void +smbc_setFunctionWrite(SMBCCTX *c, smbc_write_fn fn) +{ + c->write = fn; +} + +smbc_splice_fn +smbc_getFunctionSplice(SMBCCTX *c) +{ + return c->internal->smb.splice_fn; +} + +void +smbc_setFunctionSplice(SMBCCTX *c, smbc_splice_fn fn) +{ + c->internal->smb.splice_fn = fn; +} + +smbc_unlink_fn +smbc_getFunctionUnlink(SMBCCTX *c) +{ + return c->unlink; +} + +void +smbc_setFunctionUnlink(SMBCCTX *c, smbc_unlink_fn fn) +{ + c->unlink = fn; +} + +smbc_rename_fn +smbc_getFunctionRename(SMBCCTX *c) +{ + return c->rename; +} + +void +smbc_setFunctionRename(SMBCCTX *c, smbc_rename_fn fn) +{ + c->rename = fn; +} + +smbc_lseek_fn +smbc_getFunctionLseek(SMBCCTX *c) +{ + return c->lseek; +} + +void +smbc_setFunctionLseek(SMBCCTX *c, smbc_lseek_fn fn) +{ + c->lseek = fn; +} + +smbc_stat_fn +smbc_getFunctionStat(SMBCCTX *c) +{ + return c->stat; +} + +void +smbc_setFunctionStat(SMBCCTX *c, smbc_stat_fn fn) +{ + c->stat = fn; +} + +smbc_fstat_fn +smbc_getFunctionFstat(SMBCCTX *c) +{ + return c->fstat; +} + +void +smbc_setFunctionFstat(SMBCCTX *c, smbc_fstat_fn fn) +{ + c->fstat = fn; +} + +smbc_statvfs_fn +smbc_getFunctionStatVFS(SMBCCTX *c) +{ + return c->internal->posix_emu.statvfs_fn; +} + +void +smbc_setFunctionStatVFS(SMBCCTX *c, smbc_statvfs_fn fn) +{ + c->internal->posix_emu.statvfs_fn = fn; +} + +smbc_fstatvfs_fn +smbc_getFunctionFstatVFS(SMBCCTX *c) +{ + return c->internal->posix_emu.fstatvfs_fn; +} + +void +smbc_setFunctionFstatVFS(SMBCCTX *c, smbc_fstatvfs_fn fn) +{ + c->internal->posix_emu.fstatvfs_fn = fn; +} + +smbc_ftruncate_fn +smbc_getFunctionFtruncate(SMBCCTX *c) +{ + return c->internal->posix_emu.ftruncate_fn; +} + +void +smbc_setFunctionFtruncate(SMBCCTX *c, smbc_ftruncate_fn fn) +{ + c->internal->posix_emu.ftruncate_fn = fn; +} + +smbc_close_fn +smbc_getFunctionClose(SMBCCTX *c) +{ + return c->close_fn; +} + +void +smbc_setFunctionClose(SMBCCTX *c, smbc_close_fn fn) +{ + c->close_fn = fn; +} + + +/** + * Callable functions for directories. + */ + +smbc_opendir_fn +smbc_getFunctionOpendir(SMBCCTX *c) +{ + return c->opendir; +} + +void +smbc_setFunctionOpendir(SMBCCTX *c, smbc_opendir_fn fn) +{ + c->opendir = fn; +} + +smbc_closedir_fn +smbc_getFunctionClosedir(SMBCCTX *c) +{ + return c->closedir; +} + +void +smbc_setFunctionClosedir(SMBCCTX *c, smbc_closedir_fn fn) +{ + c->closedir = fn; +} + +smbc_readdir_fn +smbc_getFunctionReaddir(SMBCCTX *c) +{ + return c->readdir; +} + +void +smbc_setFunctionReaddir(SMBCCTX *c, smbc_readdir_fn fn) +{ + c->readdir = fn; +} + +smbc_readdirplus_fn smbc_getFunctionReaddirPlus(SMBCCTX *c) +{ + return c->readdirplus; +} + +void smbc_setFunctionReaddirPlus(SMBCCTX *c, smbc_readdirplus_fn fn) +{ + c->readdirplus = fn; +} + +smbc_readdirplus2_fn smbc_getFunctionReaddirPlus2(SMBCCTX *c) +{ + return c->readdirplus2; +} + +void smbc_setFunctionReaddirPlus2(SMBCCTX *c, smbc_readdirplus2_fn fn) +{ + c->readdirplus2 = fn; +} + +smbc_getdents_fn +smbc_getFunctionGetdents(SMBCCTX *c) +{ + return c->getdents; +} + +void +smbc_setFunctionGetdents(SMBCCTX *c, smbc_getdents_fn fn) +{ + c->getdents = fn; +} + +smbc_mkdir_fn +smbc_getFunctionMkdir(SMBCCTX *c) +{ + return c->mkdir; +} + +void +smbc_setFunctionMkdir(SMBCCTX *c, smbc_mkdir_fn fn) +{ + c->mkdir = fn; +} + +smbc_rmdir_fn +smbc_getFunctionRmdir(SMBCCTX *c) +{ + return c->rmdir; +} + +void +smbc_setFunctionRmdir(SMBCCTX *c, smbc_rmdir_fn fn) +{ + c->rmdir = fn; +} + +smbc_telldir_fn +smbc_getFunctionTelldir(SMBCCTX *c) +{ + return c->telldir; +} + +void +smbc_setFunctionTelldir(SMBCCTX *c, smbc_telldir_fn fn) +{ + c->telldir = fn; +} + +smbc_lseekdir_fn +smbc_getFunctionLseekdir(SMBCCTX *c) +{ + return c->lseekdir; +} + +void +smbc_setFunctionLseekdir(SMBCCTX *c, smbc_lseekdir_fn fn) +{ + c->lseekdir = fn; +} + +smbc_fstatdir_fn +smbc_getFunctionFstatdir(SMBCCTX *c) +{ + return c->fstatdir; +} + +void +smbc_setFunctionFstatdir(SMBCCTX *c, smbc_fstatdir_fn fn) +{ + c->fstatdir = fn; +} + +smbc_notify_fn +smbc_getFunctionNotify(SMBCCTX *c) +{ + return c->internal->smb.notify_fn; +} + +void +smbc_setFunctionNotify(SMBCCTX *c, smbc_notify_fn fn) +{ + c->internal->smb.notify_fn = fn; +} + + +/** + * Callable functions applicable to both files and directories. + */ + +smbc_chmod_fn +smbc_getFunctionChmod(SMBCCTX *c) +{ + return c->chmod; +} + +void +smbc_setFunctionChmod(SMBCCTX *c, smbc_chmod_fn fn) +{ + c->chmod = fn; +} + +smbc_utimes_fn +smbc_getFunctionUtimes(SMBCCTX *c) +{ + return c->utimes; +} + +void +smbc_setFunctionUtimes(SMBCCTX *c, smbc_utimes_fn fn) +{ + c->utimes = fn; +} + +smbc_setxattr_fn +smbc_getFunctionSetxattr(SMBCCTX *c) +{ + return c->setxattr; +} + +void +smbc_setFunctionSetxattr(SMBCCTX *c, smbc_setxattr_fn fn) +{ + c->setxattr = fn; +} + +smbc_getxattr_fn +smbc_getFunctionGetxattr(SMBCCTX *c) +{ + return c->getxattr; +} + +void +smbc_setFunctionGetxattr(SMBCCTX *c, smbc_getxattr_fn fn) +{ + c->getxattr = fn; +} + +smbc_removexattr_fn +smbc_getFunctionRemovexattr(SMBCCTX *c) +{ + return c->removexattr; +} + +void +smbc_setFunctionRemovexattr(SMBCCTX *c, smbc_removexattr_fn fn) +{ + c->removexattr = fn; +} + +smbc_listxattr_fn +smbc_getFunctionListxattr(SMBCCTX *c) +{ + return c->listxattr; +} + +void +smbc_setFunctionListxattr(SMBCCTX *c, smbc_listxattr_fn fn) +{ + c->listxattr = fn; +} + + +/** + * Callable functions related to printing + */ + +smbc_print_file_fn +smbc_getFunctionPrintFile(SMBCCTX *c) +{ + return c->print_file; +} + +void +smbc_setFunctionPrintFile(SMBCCTX *c, smbc_print_file_fn fn) +{ + c->print_file = fn; +} + +smbc_open_print_job_fn +smbc_getFunctionOpenPrintJob(SMBCCTX *c) +{ + return c->open_print_job; +} + +void +smbc_setFunctionOpenPrintJob(SMBCCTX *c, + smbc_open_print_job_fn fn) +{ + c->open_print_job = fn; +} + +smbc_list_print_jobs_fn +smbc_getFunctionListPrintJobs(SMBCCTX *c) +{ + return c->list_print_jobs; +} + +void +smbc_setFunctionListPrintJobs(SMBCCTX *c, + smbc_list_print_jobs_fn fn) +{ + c->list_print_jobs = fn; +} + +smbc_unlink_print_job_fn +smbc_getFunctionUnlinkPrintJob(SMBCCTX *c) +{ + return c->unlink_print_job; +} + +void +smbc_setFunctionUnlinkPrintJob(SMBCCTX *c, + smbc_unlink_print_job_fn fn) +{ + c->unlink_print_job = fn; +} + diff --git a/source3/libsmb/libsmb_stat.c b/source3/libsmb/libsmb_stat.c new file mode 100644 index 0000000..aa0b2db --- /dev/null +++ b/source3/libsmb/libsmb_stat.c @@ -0,0 +1,538 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" +#include "../libcli/smb/smbXcli_base.h" +#include "lib/util/time.h" + +/* + * Generate an inode number from file name for those things that need it + */ + +static ino_t generate_inode(const char *name) +{ + if (name == NULL) { + return (ino_t)-1; + } + return (ino_t)str_checksum(name); +} + +/* + * Routine to put basic stat info into a stat structure ... Used by stat and + * fstat below. + */ + +void setup_stat(struct stat *st, + const char *fname, + off_t size, + int attr, + ino_t ino, + dev_t dev, + struct timespec access_time_ts, + struct timespec change_time_ts, + struct timespec write_time_ts) +{ + st->st_mode = 0; + + if (IS_DOS_DIR(attr)) { + st->st_mode = SMBC_DIR_MODE; + } else { + st->st_mode = SMBC_FILE_MODE; + } + + if (IS_DOS_ARCHIVE(attr)) { + st->st_mode |= S_IXUSR; + } + if (IS_DOS_SYSTEM(attr)) { + st->st_mode |= S_IXGRP; + } + if (IS_DOS_HIDDEN(attr)) { + st->st_mode |= S_IXOTH; + } + if (!IS_DOS_READONLY(attr)) { + st->st_mode |= S_IWUSR; + } + + st->st_size = size; +#ifdef HAVE_STAT_ST_BLKSIZE + st->st_blksize = 512; +#endif +#ifdef HAVE_STAT_ST_BLOCKS + st->st_blocks = (size+511)/512; +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + st->st_rdev = 0; +#endif + st->st_uid = getuid(); + st->st_gid = getgid(); + + if (IS_DOS_DIR(attr)) { + st->st_nlink = 2; + } else { + st->st_nlink = 1; + } + + if (ino != 0) { + st->st_ino = ino; + } else { + st->st_ino = generate_inode(fname); + } + + st->st_dev = dev; + + st->st_atime = access_time_ts.tv_sec; + set_atimensec(st, access_time_ts.tv_nsec); + + st->st_ctime = change_time_ts.tv_sec; + set_ctimensec(st, change_time_ts.tv_nsec); + + st->st_mtime = write_time_ts.tv_sec; + set_mtimensec(st, write_time_ts.tv_nsec); +} + +void setup_stat_from_stat_ex(const struct stat_ex *stex, + const char *fname, + struct stat *st) +{ + st->st_atime = stex->st_ex_atime.tv_sec; + set_atimensec(st, stex->st_ex_atime.tv_nsec); + + st->st_ctime = stex->st_ex_ctime.tv_sec; + set_ctimensec(st, stex->st_ex_ctime.tv_nsec); + + st->st_mtime = stex->st_ex_mtime.tv_sec; + set_mtimensec(st, stex->st_ex_mtime.tv_nsec); + + st->st_mode = stex->st_ex_mode; + st->st_size = stex->st_ex_size; +#ifdef HAVE_STAT_ST_BLKSIZE + st->st_blksize = 512; +#endif +#ifdef HAVE_STAT_ST_BLOCKS + st->st_blocks = (st->st_size + 511) / 512; +#endif +#ifdef HAVE_STRUCT_STAT_ST_RDEV + st->st_rdev = 0; +#endif + st->st_uid = stex->st_ex_uid; + st->st_gid = stex->st_ex_gid; + + st->st_nlink = stex->st_ex_nlink; + + if (stex->st_ex_ino == 0) { + st->st_ino = 0; + if (fname != NULL) { + st->st_ino = generate_inode(fname); + } + } else { + st->st_ino = stex->st_ex_ino; + } + + st->st_dev = stex->st_ex_dev; + +} + +/* + * Routine to stat a file given a name + */ + +int +SMBC_stat_ctx(SMBCCTX *context, + const char *fname, + struct stat *st) +{ + SMBCSRV *srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + uint16_t port = 0; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_stat(%s)\n", fname)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + status = SMBC_getatr(context, srv, path, st); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + errno = cli_status_to_errno(status); + return -1; + } + + TALLOC_FREE(frame); + return 0; +} + +/* + * Routine to stat a file given an fd + */ + +int +SMBC_fstat_ctx(SMBCCTX *context, + SMBCFILE *file, + struct stat *st) +{ + struct timespec change_time_ts; + struct timespec access_time_ts; + struct timespec write_time_ts; + off_t size; + uint32_t attr; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *path = NULL; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + SMB_INO_T ino = 0; + uint16_t port = 0; + struct cli_credentials *creds = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + if (!context || !context->internal->initialized) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!SMBC_dlist_contains(context->internal->files, file)) { + errno = EBADF; + TALLOC_FREE(frame); + return -1; + } + + if (!file->file) { + TALLOC_FREE(frame); + return smbc_getFunctionFstatdir(context)(context, file, st); + } + + /*d_printf(">>>fstat: parsing %s\n", file->fname);*/ + if (SMBC_parse_path(frame, + context, + file->fname, + NULL, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + creds = context->internal->creds; + + /*d_printf(">>>fstat: resolving %s\n", path);*/ + status = cli_resolve_path(frame, "", + creds, + file->srv->cli, path, + &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Could not resolve %s\n", path); + errno = ENOENT; + TALLOC_FREE(frame); + return -1; + } + /*d_printf(">>>fstat: resolved path as %s\n", targetpath);*/ + + if (!NT_STATUS_IS_OK(cli_qfileinfo_basic( + targetcli, file->cli_fd, &attr, &size, + NULL, + &access_time_ts, + &write_time_ts, + &change_time_ts, + &ino))) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + setup_stat(st, + path, + size, + attr, + ino, + file->srv->dev, + access_time_ts, + change_time_ts, + write_time_ts); + + TALLOC_FREE(frame); + return 0; +} + + +/* + * Routine to obtain file system information given a path + */ +int +SMBC_statvfs_ctx(SMBCCTX *context, + char *path, + struct statvfs *st) +{ + int ret; + bool bIsDir; + struct stat statbuf; + SMBCFILE * pFile; + TALLOC_CTX *frame = talloc_stackframe(); + + /* Determine if the provided path is a file or a folder */ + if (SMBC_stat_ctx(context, path, &statbuf) < 0) { + TALLOC_FREE(frame); + return -1; + } + + /* Is it a file or a directory? */ + if (S_ISDIR(statbuf.st_mode)) { + /* It's a directory. */ + if ((pFile = SMBC_opendir_ctx(context, path)) == NULL) { + TALLOC_FREE(frame); + return -1; + } + bIsDir = true; + } else if (S_ISREG(statbuf.st_mode)) { + /* It's a file. */ + if ((pFile = SMBC_open_ctx(context, path, + O_RDONLY, 0)) == NULL) { + TALLOC_FREE(frame); + return -1; + } + bIsDir = false; + } else { + /* It's neither a file nor a directory. Not supported. */ + TALLOC_FREE(frame); + errno = ENOSYS; + return -1; + } + + /* Now we have an open file handle, so just use SMBC_fstatvfs */ + ret = SMBC_fstatvfs_ctx(context, pFile, st); + + /* Close the file or directory */ + if (bIsDir) { + SMBC_closedir_ctx(context, pFile); + } else { + SMBC_close_ctx(context, pFile); + } + + TALLOC_FREE(frame); + return ret; +} + + +/* + * Routine to obtain file system information given an fd + */ + +int +SMBC_fstatvfs_ctx(SMBCCTX *context, + SMBCFILE *file, + struct statvfs *st) +{ + unsigned long flags = 0; + uint32_t fs_attrs = 0; + struct cli_state *cli = file->srv->cli; + struct smbXcli_tcon *tcon; + TALLOC_CTX *frame = talloc_stackframe(); + + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) { + tcon = cli->smb2.tcon; + } else { + tcon = cli->smb1.tcon; + } + + /* Initialize all fields (at least until we actually use them) */ + ZERO_STRUCTP(st); + + /* + * The state of each flag is such that the same bits are unset as + * would typically be unset on a local file system on a POSIX OS. Thus + * the bit is on, for example, only for case-insensitive file systems + * since most POSIX file systems are case sensitive and fstatvfs() + * would typically return zero in these bits on such a local file + * system. + */ + + /* See if the server has UNIX CIFS support */ + if (! SERVER_HAS_UNIX_CIFS(cli)) { + uint64_t total_allocation_units; + uint64_t caller_allocation_units; + uint64_t actual_allocation_units; + uint64_t sectors_per_allocation_unit; + uint64_t bytes_per_sector; + NTSTATUS status; + + /* Nope. If size data is available... */ + status = cli_get_fs_full_size_info(cli, + &total_allocation_units, + &caller_allocation_units, + &actual_allocation_units, + §ors_per_allocation_unit, + &bytes_per_sector); + if (NT_STATUS_IS_OK(status)) { + + /* ... then provide it */ + st->f_bsize = + (unsigned long) bytes_per_sector; +#ifdef HAVE_FRSIZE + st->f_frsize = + (unsigned long) sectors_per_allocation_unit; +#endif + st->f_blocks = + (fsblkcnt_t) total_allocation_units; + st->f_bfree = + (fsblkcnt_t) actual_allocation_units; + st->f_bavail = + (fsblkcnt_t) caller_allocation_units; + } + + flags |= SMBC_VFS_FEATURE_NO_UNIXCIFS; + } else { + uint32_t optimal_transfer_size; + uint32_t block_size; + uint64_t total_blocks; + uint64_t blocks_available; + uint64_t user_blocks_available; + uint64_t total_file_nodes; + uint64_t free_file_nodes; + uint64_t fs_identifier; + NTSTATUS status; + + /* Has UNIXCIFS. If POSIX filesystem info is available... */ + status = cli_get_posix_fs_info(cli, + &optimal_transfer_size, + &block_size, + &total_blocks, + &blocks_available, + &user_blocks_available, + &total_file_nodes, + &free_file_nodes, + &fs_identifier); + if (NT_STATUS_IS_OK(status)) { + + /* ... then what's provided here takes precedence. */ + st->f_bsize = + (unsigned long) block_size; + st->f_blocks = + (fsblkcnt_t) total_blocks; + st->f_bfree = + (fsblkcnt_t) blocks_available; + st->f_bavail = + (fsblkcnt_t) user_blocks_available; + st->f_files = + (fsfilcnt_t) total_file_nodes; + st->f_ffree = + (fsfilcnt_t) free_file_nodes; +#ifdef HAVE_FSID_INT + st->f_fsid = + (unsigned long) fs_identifier; +#endif + } + } + + /* See if the share is case sensitive */ + if (!NT_STATUS_IS_OK(cli_get_fs_attr_info(cli, &fs_attrs))) { + /* + * 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)) { + flags |= SMBC_VFS_FEATURE_CASE_INSENSITIVE; + } + } else { + if (! (fs_attrs & FILE_CASE_SENSITIVE_SEARCH)) { + flags |= SMBC_VFS_FEATURE_CASE_INSENSITIVE; + } + } + + /* See if DFS is supported */ + if (smbXcli_conn_dfs_supported(cli->conn) && + smbXcli_tcon_is_dfs_share(tcon)) + { + flags |= SMBC_VFS_FEATURE_DFS; + } + +#if defined(HAVE_STATVFS_F_FLAG) + st->f_flag = flags; +#elif defined(HAVE_STATVFS_F_FLAGS) + st->f_flags = flags; +#endif + + TALLOC_FREE(frame); + return 0; +} diff --git a/source3/libsmb/libsmb_thread_impl.c b/source3/libsmb/libsmb_thread_impl.c new file mode 100644 index 0000000..c0ed636 --- /dev/null +++ b/source3/libsmb/libsmb_thread_impl.c @@ -0,0 +1,127 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Derrell Lipman 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/** + * Initialize for an arbitrary thread implementation. The caller should + * provide, as parameters, pointers to functions to implement the requisite + * low-level thread functionality. A function must be provided for each + * parameter; none may be null. + * + * If the thread implementation is POSIX Threads (pthreads), then the much + * simpler smbc_thread_pthread() function may be used instead of this one. + * + * @param create_mutex + * Create a mutex. This function should expect three parameters: lockname, + * pplock, and location. It should create a unique mutex for each unique + * lockname it is provided, and return the mutex identifier in *pplock. The + * location parameter can be used for debugging, as it contains the + * compiler-provided __location__ of the call. + * + * @param destroy_mutex + * Destroy a mutex. This function should expect two parameters: plock and + * location. It should destroy the mutex associated with the identifier + * plock. The location parameter can be used for debugging, as it contains + * the compiler-provided __location__ of the call. + * + * @param lock_mutex + * Lock a mutex. This function should expect three parameters: plock, + * lock_type, and location. The mutex aassociated with identifier plock + * should be locked if lock_type is 1, and unlocked if lock_type is 2. The + * location parameter can be used for debugging, as it contains the + * compiler-provided __location__ of the call. + * + * @param create_tls + * Create thread local storage. This function should expect three + * parameters: keyname, ppkey, and location. It should allocate an + * implementation-specific amount of memory and assign the pointer to that + * allocated memory to *ppkey. The location parameter can be used for + * debugging, as it contains the compiler-provided __location__ of the + * call. This function should return 0 upon success, non-zero upon failure. + * + * @param destroy_tls + * Destroy thread local storage. This function should expect two parameters: + * ppkey and location. The ppkey parameter points to a variable containing a + * thread local storage key previously provided by the create_tls + * function. The location parameter can be used for debugging, as it + * contains the compiler-provided __location__ of the call. + * + * @param set_tls + * Set a thread local storage variable's value. This function should expect + * three parameters: pkey, pval, and location. The pkey parameter is a + * thread local storage key previously provided by the create_tls + * function. The (void *) pval parameter contains the value to be placed in + * the thread local storage variable identified by pkey. The location + * parameter can be used for debugging, as it contains the compiler-provided + * __location__ of the call. This function should return 0 upon success; + * non-zero otherwise. + * + * @param get_tls + * Retrieve a thread local storage variable's value. This function should + * expect two parameters: pkey and location. The pkey parameter is a thread + * local storage key previously provided by the create_tls function, and + * which has previously been used in a call to the set_tls function to + * initialize a thread local storage variable. The location parameter can be + * used for debugging, as it contains the compiler-provided __location__ of + * the call. This function should return the (void *) value stored in the + * variable identified by pkey. + * + * @return {void} + */ +void +smbc_thread_impl( + /* Mutex functions. */ + int (*create_mutex)(const char *lockname, + void **pplock, + const char *location), + void (*destroy_mutex)(void *plock, + const char *location), + int (*lock_mutex)(void *plock, + int lock_type, + const char *location), + + /* Thread local storage. */ + int (*create_tls)(const char *keyname, + void **ppkey, + const char *location), + void (*destroy_tls)(void **ppkey, + const char *location), + int (*set_tls)(void *pkey, + const void *pval, + const char *location), + void *(*get_tls)(void *pkey, + const char *location) + ) +{ + static struct smb_thread_functions tf; + + tf.create_mutex = create_mutex; + tf.destroy_mutex = destroy_mutex; + tf.lock_mutex = lock_mutex; + tf.create_tls = create_tls; + tf.destroy_tls = destroy_tls; + tf.set_tls = set_tls; + tf.get_tls = get_tls; + + smb_thread_set_functions(&tf); +} diff --git a/source3/libsmb/libsmb_thread_posix.c b/source3/libsmb/libsmb_thread_posix.c new file mode 100644 index 0000000..8c8299d --- /dev/null +++ b/source3/libsmb/libsmb_thread_posix.c @@ -0,0 +1,53 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Derrell Lipman 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#ifdef HAVE_PTHREAD_H +#include <pthread.h> +#endif +#include "libsmbclient.h" +#include "libsmb_internal.h" + + +/* Get rid of the malloc checker */ +#ifdef malloc +#undef malloc +#endif + +/* + * Define the functions which implement the pthread interface + */ +SMB_THREADS_DEF_PTHREAD_IMPLEMENTATION(tf); + + +/** + * Initialize for threads using the Posix Threads (pthread) + * implementation. This is a built-in implementation, avoiding the need to + * implement the component functions of the thread interface. If this function + * is used, it is not necessary to call smbc_thread_impl(). + * + * @return {void} + */ +void +smbc_thread_posix(void) +{ + smb_thread_set_functions(&tf); +} + diff --git a/source3/libsmb/libsmb_xattr.c b/source3/libsmb/libsmb_xattr.c new file mode 100644 index 0000000..c8aa074 --- /dev/null +++ b/source3/libsmb/libsmb_xattr.c @@ -0,0 +1,2409 @@ +/* + Unix SMB/Netbios implementation. + SMB client library implementation + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Richard Sharpe 2000, 2002 + Copyright (C) John Terpstra 2000 + Copyright (C) Tom Jansen (Ninja ISD) 2002 + Copyright (C) Derrell Lipman 2003-2008 + Copyright (C) Jeremy Allison 2007, 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "libsmbclient.h" +#include "libsmb_internal.h" +#include "../librpc/gen_ndr/ndr_lsa.h" +#include "rpc_client/rpc_client.h" +#include "rpc_client/cli_lsarpc.h" +#include "../libcli/security/security.h" +#include "lib/util/string_wrappers.h" + +/* + * Find an lsa pipe handle associated with a cli struct. + */ +static struct rpc_pipe_client * +find_lsa_pipe_hnd(struct cli_state *ipc_cli) +{ + struct rpc_pipe_client *pipe_hnd; + + for (pipe_hnd = ipc_cli->pipe_list; + pipe_hnd; + pipe_hnd = pipe_hnd->next) { + if (ndr_syntax_id_equal(&pipe_hnd->abstract_syntax, + &ndr_table_lsarpc.syntax_id)) { + return pipe_hnd; + } + } + return NULL; +} + +/* + * Sort ACEs according to the documentation at + * http://support.microsoft.com/kb/269175, at least as far as it defines the + * order. + */ + +static int +ace_compare(struct security_ace *ace1, + struct security_ace *ace2) +{ + bool b1; + bool b2; + + /* If the ACEs are equal, we have nothing more to do. */ + if (security_ace_equal(ace1, ace2)) { + return 0; + } + + /* Inherited follow non-inherited */ + b1 = ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0); + b2 = ((ace2->flags & SEC_ACE_FLAG_INHERITED_ACE) != 0); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* + * What shall we do with AUDITs and ALARMs? It's undefined. We'll + * sort them after DENY and ALLOW. + */ + b1 = (ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED && + ace1->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT && + ace1->type != SEC_ACE_TYPE_ACCESS_DENIED && + ace1->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + b2 = (ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED && + ace2->type != SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT && + ace2->type != SEC_ACE_TYPE_ACCESS_DENIED && + ace2->type != SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* Allowed ACEs follow denied ACEs */ + b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED || + ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT); + b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED || + ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* + * ACEs applying to an entity's object follow those applying to the + * entity itself + */ + b1 = (ace1->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace1->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + b2 = (ace2->type == SEC_ACE_TYPE_ACCESS_ALLOWED_OBJECT || + ace2->type == SEC_ACE_TYPE_ACCESS_DENIED_OBJECT); + if (b1 != b2) { + return (b1 ? 1 : -1); + } + + /* + * If we get this far, the ACEs are similar as far as the + * characteristics we typically care about (those defined by the + * referenced MS document). We'll now sort by characteristics that + * just seems reasonable. + */ + + if (ace1->type != ace2->type) { + return ace2->type - ace1->type; + } + + if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) { + return dom_sid_compare(&ace1->trustee, &ace2->trustee); + } + + if (ace1->flags != ace2->flags) { + return ace1->flags - ace2->flags; + } + + if (ace1->access_mask != ace2->access_mask) { + return ace1->access_mask - ace2->access_mask; + } + + if (ace1->size != ace2->size) { + return ace1->size - ace2->size; + } + + return memcmp(ace1, ace2, sizeof(struct security_ace)); +} + + +static void +sort_acl(struct security_acl *the_acl) +{ + uint32_t i; + if (!the_acl) return; + + TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare); + + for (i=1;i<the_acl->num_aces;) { + if (security_ace_equal(&the_acl->aces[i-1], + &the_acl->aces[i])) { + ARRAY_DEL_ELEMENT( + the_acl->aces, i, the_acl->num_aces); + the_acl->num_aces--; + } else { + i++; + } + } +} + +/* convert a SID to a string, either numeric or username/group */ +static void +convert_sid_to_string(struct cli_state *ipc_cli, + struct policy_handle *pol, + fstring str, + bool numeric, + struct dom_sid *sid) +{ + char **domains = NULL; + char **names = NULL; + enum lsa_SidType *types = NULL; + struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli); + TALLOC_CTX *ctx; + + sid_to_fstring(str, sid); + + if (numeric) { + return; /* no lookup desired */ + } + + if (!pipe_hnd) { + return; + } + + /* Ask LSA to convert the sid to a name */ + + ctx = talloc_stackframe(); + + if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(pipe_hnd, ctx, + pol, 1, sid, &domains, + &names, &types)) || + !domains || !domains[0] || !names || !names[0]) { + TALLOC_FREE(ctx); + return; + } + + /* Converted OK */ + + fstr_sprintf(str, "%s%s%s", + domains[0], lp_winbind_separator(), names[0]); + + TALLOC_FREE(ctx); +} + +/* convert a string to a SID, either numeric or username/group */ +static bool +convert_string_to_sid(struct cli_state *ipc_cli, + struct policy_handle *pol, + bool numeric, + struct dom_sid *sid, + const char *str) +{ + enum lsa_SidType *types = NULL; + struct dom_sid *sids = NULL; + bool result = True; + TALLOC_CTX *ctx = NULL; + struct rpc_pipe_client *pipe_hnd = find_lsa_pipe_hnd(ipc_cli); + + if (!pipe_hnd) { + return False; + } + + if (numeric) { + if (strncmp(str, "S-", 2) == 0) { + return string_to_sid(sid, str); + } + + result = False; + goto done; + } + + ctx = talloc_stackframe(); + if (!NT_STATUS_IS_OK(rpccli_lsa_lookup_names(pipe_hnd, ctx, + pol, 1, &str, + NULL, 1, &sids, + &types))) { + result = False; + goto done; + } + + sid_copy(sid, &sids[0]); +done: + TALLOC_FREE(ctx); + return result; +} + + +/* parse an struct security_ace in the same format as print_ace() */ +static bool +parse_ace(struct cli_state *ipc_cli, + struct policy_handle *pol, + struct security_ace *ace, + bool numeric, + char *str) +{ + char *p; + const char *cp; + char *tok; + unsigned int atype; + unsigned int aflags; + unsigned int amask; + struct dom_sid sid; + uint32_t mask; + const struct perm_value *v; + struct perm_value { + const char perm[7]; + uint32_t mask; + }; + TALLOC_CTX *frame = talloc_stackframe(); + + /* These values discovered by inspection */ + static const struct perm_value special_values[] = { + { "R", 0x00120089 }, + { "W", 0x00120116 }, + { "X", 0x001200a0 }, + { "D", 0x00010000 }, + { "P", 0x00040000 }, + { "O", 0x00080000 }, + { "", 0 }, + }; + + static const struct perm_value standard_values[] = { + { "READ", 0x001200a9 }, + { "CHANGE", 0x001301bf }, + { "FULL", 0x001f01ff }, + { "", 0 }, + }; + + ZERO_STRUCTP(ace); + p = strchr_m(str,':'); + if (!p) { + TALLOC_FREE(frame); + return False; + } + *p = '\0'; + p++; + /* Try to parse numeric form */ + + if (sscanf(p, "%u/%u/%u", &atype, &aflags, &amask) == 3 && + convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) { + goto done; + } + + /* Try to parse text form */ + + if (!convert_string_to_sid(ipc_cli, pol, numeric, &sid, str)) { + TALLOC_FREE(frame); + return false; + } + + cp = p; + if (!next_token_talloc(frame, &cp, &tok, "/")) { + TALLOC_FREE(frame); + return false; + } + + if (strncasecmp_m(tok, "ALLOWED", strlen("ALLOWED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_ALLOWED; + } else if (strncasecmp_m(tok, "DENIED", strlen("DENIED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_DENIED; + } else { + TALLOC_FREE(frame); + return false; + } + + /* Only numeric form accepted for flags at present */ + + if (!(next_token_talloc(frame, &cp, &tok, "/") && + sscanf(tok, "%u", &aflags))) { + TALLOC_FREE(frame); + return false; + } + + if (!next_token_talloc(frame, &cp, &tok, "/")) { + TALLOC_FREE(frame); + return false; + } + + if (strncmp(tok, "0x", 2) == 0) { + if (sscanf(tok, "%u", &amask) != 1) { + TALLOC_FREE(frame); + return false; + } + goto done; + } + + for (v = standard_values; v != NULL; v++) { + if (strcmp(tok, v->perm) == 0) { + amask = v->mask; + goto done; + } + } + + p = tok; + + while(*p) { + bool found = False; + + for (v = special_values; v != NULL; v++) { + if (v->perm[0] == *p) { + amask |= v->mask; + found = True; + } + } + + if (!found) { + TALLOC_FREE(frame); + return false; + } + p++; + } + + if (*p) { + TALLOC_FREE(frame); + return false; + } + +done: + mask = amask; + init_sec_ace(ace, &sid, atype, mask, aflags); + TALLOC_FREE(frame); + return true; +} + +/* add an struct security_ace to a list of struct security_aces in a struct security_acl */ +static bool +add_ace(struct security_acl **the_acl, + const struct security_ace *ace, + TALLOC_CTX *ctx) +{ + struct security_acl *acl = *the_acl; + + if (acl == NULL) { + acl = make_sec_acl(ctx, 3, 0, NULL); + if (acl == NULL) { + return false; + } + } + + if (acl->num_aces == UINT32_MAX) { + return false; + } + ADD_TO_ARRAY( + acl, struct security_ace, *ace, &acl->aces, &acl->num_aces); + *the_acl = acl; + return True; +} + + +/* parse a ascii version of a security descriptor */ +static struct security_descriptor * +sec_desc_parse(TALLOC_CTX *ctx, + struct cli_state *ipc_cli, + struct policy_handle *pol, + bool numeric, + const char *str) +{ + const char *p = str; + char *tok; + struct security_descriptor *ret = NULL; + size_t sd_size; + struct dom_sid owner_sid = { .num_auths = 0 }; + struct dom_sid group_sid = { .num_auths = 0 }; + bool have_owner = false, have_group = false; + struct security_acl *dacl=NULL; + int revision=1; + + while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) { + + if (strncasecmp_m(tok,"REVISION:", 9) == 0) { + revision = strtol(tok+9, NULL, 16); + continue; + } + + if (strncasecmp_m(tok,"OWNER:", 6) == 0) { + if (have_owner) { + DEBUG(5,("OWNER specified more than once!\n")); + goto done; + } + if (!convert_string_to_sid(ipc_cli, pol, + numeric, + &owner_sid, tok+6)) { + DEBUG(5, ("Failed to parse owner sid\n")); + goto done; + } + have_owner = true; + continue; + } + + if (strncasecmp_m(tok,"OWNER+:", 7) == 0) { + if (have_owner) { + DEBUG(5,("OWNER specified more than once!\n")); + goto done; + } + if (!convert_string_to_sid(ipc_cli, pol, + False, + &owner_sid, tok+7)) { + DEBUG(5, ("Failed to parse owner sid\n")); + goto done; + } + have_owner = true; + continue; + } + + if (strncasecmp_m(tok,"GROUP:", 6) == 0) { + if (have_group) { + DEBUG(5,("GROUP specified more than once!\n")); + goto done; + } + if (!convert_string_to_sid(ipc_cli, pol, + numeric, + &group_sid, tok+6)) { + DEBUG(5, ("Failed to parse group sid\n")); + goto done; + } + have_group = true; + continue; + } + + if (strncasecmp_m(tok,"GROUP+:", 7) == 0) { + if (have_group) { + DEBUG(5,("GROUP specified more than once!\n")); + goto done; + } + if (!convert_string_to_sid(ipc_cli, pol, + False, + &group_sid, tok+6)) { + DEBUG(5, ("Failed to parse group sid\n")); + goto done; + } + have_group = true; + continue; + } + + if (strncasecmp_m(tok,"ACL:", 4) == 0) { + struct security_ace ace; + if (!parse_ace(ipc_cli, pol, &ace, numeric, tok+4)) { + DEBUG(5, ("Failed to parse ACL %s\n", tok)); + goto done; + } + if(!add_ace(&dacl, &ace, ctx)) { + DEBUG(5, ("Failed to add ACL %s\n", tok)); + goto done; + } + continue; + } + + if (strncasecmp_m(tok,"ACL+:", 5) == 0) { + struct security_ace ace; + if (!parse_ace(ipc_cli, pol, &ace, False, tok+5)) { + DEBUG(5, ("Failed to parse ACL %s\n", tok)); + goto done; + } + if(!add_ace(&dacl, &ace, ctx)) { + DEBUG(5, ("Failed to add ACL %s\n", tok)); + goto done; + } + continue; + } + + DEBUG(5, ("Failed to parse security descriptor\n")); + goto done; + } + + ret = make_sec_desc( + ctx, + revision, + SEC_DESC_SELF_RELATIVE, + have_owner ? &owner_sid : NULL, + have_group ? &group_sid : NULL, + NULL, + dacl, + &sd_size); + +done: + return ret; +} + + +/* Obtain the current dos attributes */ +static struct DOS_ATTR_DESC * +dos_attr_query(SMBCCTX *context, + TALLOC_CTX *ctx, + const char *filename, + SMBCSRV *srv) +{ + struct stat sb = {0}; + struct DOS_ATTR_DESC *ret = NULL; + NTSTATUS status; + + ret = talloc(ctx, struct DOS_ATTR_DESC); + if (!ret) { + errno = ENOMEM; + return NULL; + } + + /* Obtain the DOS attributes */ + status = SMBC_getatr(context, srv, filename, &sb); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("dos_attr_query Failed to query old attributes\n")); + TALLOC_FREE(ret); + errno = cli_status_to_errno(status); + return NULL; + } + + ret->mode = sb.st_mode; + ret->size = sb.st_size; + ret->create_time = sb.st_ctime; + ret->access_time = sb.st_atime; + ret->write_time = sb.st_mtime; + ret->change_time = sb.st_mtime; + ret->inode = sb.st_ino; + + return ret; +} + + +/* parse a ascii version of a security descriptor */ +static void +dos_attr_parse(SMBCCTX *context, + struct DOS_ATTR_DESC *dad, + SMBCSRV *srv, + char *str) +{ + int n; + const char *p = str; + char *tok = NULL; + TALLOC_CTX *frame = NULL; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + + /* Determine whether to use old-style or new-style attribute names */ + if (context->internal->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "CREATE_TIME"; + attr_strings.access_time_attr = "ACCESS_TIME"; + attr_strings.write_time_attr = "WRITE_TIME"; + attr_strings.change_time_attr = "CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "A_TIME"; + attr_strings.write_time_attr = "M_TIME"; + attr_strings.change_time_attr = "C_TIME"; + } + + /* if this is to set the entire ACL... */ + if (*str == '*') { + /* ... then increment past the first colon if there is one */ + if ((p = strchr(str, ':')) != NULL) { + ++p; + } else { + p = str; + } + } + + frame = talloc_stackframe(); + while (next_token_talloc(frame, &p, &tok, "\t,\r\n")) { + if (strncasecmp_m(tok, "MODE:", 5) == 0) { + long request = strtol(tok+5, NULL, 16); + if (request == 0) { + dad->mode = (request | + (IS_DOS_DIR(dad->mode) + ? FILE_ATTRIBUTE_DIRECTORY + : FILE_ATTRIBUTE_NORMAL)); + } else { + dad->mode = request; + } + continue; + } + + if (strncasecmp_m(tok, "SIZE:", 5) == 0) { + dad->size = (off_t)atof(tok+5); + continue; + } + + n = strlen(attr_strings.access_time_attr); + if (strncasecmp_m(tok, attr_strings.access_time_attr, n) == 0) { + dad->access_time = (time_t)strtol(tok+n+1, NULL, 10); + continue; + } + + n = strlen(attr_strings.change_time_attr); + if (strncasecmp_m(tok, attr_strings.change_time_attr, n) == 0) { + dad->change_time = (time_t)strtol(tok+n+1, NULL, 10); + continue; + } + + n = strlen(attr_strings.write_time_attr); + if (strncasecmp_m(tok, attr_strings.write_time_attr, n) == 0) { + dad->write_time = (time_t)strtol(tok+n+1, NULL, 10); + continue; + } + + if (attr_strings.create_time_attr != NULL) { + n = strlen(attr_strings.create_time_attr); + if (strncasecmp_m(tok, attr_strings.create_time_attr, + n) == 0) { + dad->create_time = (time_t)strtol(tok+n+1, + NULL, 10); + continue; + } + } + + if (strncasecmp_m(tok, "INODE:", 6) == 0) { + dad->inode = (SMB_INO_T)atof(tok+6); + continue; + } + } + TALLOC_FREE(frame); +} + +/***************************************************** + Retrieve the acls for a file. +*******************************************************/ + +static int +cacl_get(SMBCCTX *context, + TALLOC_CTX *ctx, + SMBCSRV *srv, + struct cli_state *ipc_cli, + struct policy_handle *pol, + const char *filename, + const char *attr_name, + char *buf, + int bufsize) +{ + uint32_t i; + int n = 0; + int n_used; + bool all; + bool all_nt; + bool all_nt_acls; + bool all_dos; + bool some_nt; + bool some_dos; + bool exclude_nt_revision = False; + bool exclude_nt_owner = False; + bool exclude_nt_group = False; + bool exclude_nt_acl = False; + bool exclude_dos_mode = False; + bool exclude_dos_size = False; + bool exclude_dos_create_time = False; + bool exclude_dos_access_time = False; + bool exclude_dos_write_time = False; + bool exclude_dos_change_time = False; + bool exclude_dos_inode = False; + bool numeric = True; + bool determine_size = (bufsize == 0); + uint16_t fnum; + struct security_descriptor *sd; + fstring sidstr; + fstring name_sandbox; + char *name; + char *pExclude; + char *p; + struct cli_state *cli = srv->cli; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } excl_attr_strings; + + /* Determine whether to use old-style or new-style attribute names */ + if (context->internal->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "CREATE_TIME"; + attr_strings.access_time_attr = "ACCESS_TIME"; + attr_strings.write_time_attr = "WRITE_TIME"; + attr_strings.change_time_attr = "CHANGE_TIME"; + + excl_attr_strings.create_time_attr = "CREATE_TIME"; + excl_attr_strings.access_time_attr = "ACCESS_TIME"; + excl_attr_strings.write_time_attr = "WRITE_TIME"; + excl_attr_strings.change_time_attr = "CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "A_TIME"; + attr_strings.write_time_attr = "M_TIME"; + attr_strings.change_time_attr = "C_TIME"; + + excl_attr_strings.create_time_attr = NULL; + excl_attr_strings.access_time_attr = "dos_attr.A_TIME"; + excl_attr_strings.write_time_attr = "dos_attr.M_TIME"; + excl_attr_strings.change_time_attr = "dos_attr.C_TIME"; + } + + /* Copy name so we can strip off exclusions (if any are specified) */ + fstrcpy(name_sandbox, attr_name); + + /* Ensure name is null terminated */ + name_sandbox[sizeof(name_sandbox) - 1] = '\0'; + + /* Play in the sandbox */ + name = name_sandbox; + + /* If there are any exclusions, point to them and mask them from name */ + if ((pExclude = strchr(name, '!')) != NULL) + { + *pExclude++ = '\0'; + } + + all = (strncasecmp_m(name, "system.*", 8) == 0); + all_nt = (strncasecmp_m(name, "system.nt_sec_desc.*", 20) == 0); + all_nt_acls = (strncasecmp_m(name, "system.nt_sec_desc.acl.*", 24) == 0); + all_dos = (strncasecmp_m(name, "system.dos_attr.*", 17) == 0); + some_nt = (strncasecmp_m(name, "system.nt_sec_desc.", 19) == 0); + some_dos = (strncasecmp_m(name, "system.dos_attr.", 16) == 0); + numeric = (* (name + strlen(name) - 1) != '+'); + + /* Look for exclusions from "all" requests */ + if (all || all_nt || all_dos) { + /* Exclusions are delimited by '!' */ + for (; + pExclude != NULL; + pExclude = (p == NULL ? NULL : p + 1)) { + + /* Find end of this exclusion name */ + if ((p = strchr(pExclude, '!')) != NULL) + { + *p = '\0'; + } + + /* Which exclusion name is this? */ + if (strcasecmp_m(pExclude, + "nt_sec_desc.revision") == 0) { + exclude_nt_revision = True; + } + else if (strcasecmp_m(pExclude, + "nt_sec_desc.owner") == 0) { + exclude_nt_owner = True; + } + else if (strcasecmp_m(pExclude, + "nt_sec_desc.group") == 0) { + exclude_nt_group = True; + } + else if (strcasecmp_m(pExclude, + "nt_sec_desc.acl") == 0) { + exclude_nt_acl = True; + } + else if (strcasecmp_m(pExclude, + "dos_attr.mode") == 0) { + exclude_dos_mode = True; + } + else if (strcasecmp_m(pExclude, + "dos_attr.size") == 0) { + exclude_dos_size = True; + } + else if (excl_attr_strings.create_time_attr != NULL && + strcasecmp_m(pExclude, + excl_attr_strings.change_time_attr) == 0) { + exclude_dos_create_time = True; + } + else if (strcasecmp_m(pExclude, + excl_attr_strings.access_time_attr) == 0) { + exclude_dos_access_time = True; + } + else if (strcasecmp_m(pExclude, + excl_attr_strings.write_time_attr) == 0) { + exclude_dos_write_time = True; + } + else if (strcasecmp_m(pExclude, + excl_attr_strings.change_time_attr) == 0) { + exclude_dos_change_time = True; + } + else if (strcasecmp_m(pExclude, "dos_attr.inode") == 0) { + exclude_dos_inode = True; + } + else { + DEBUG(5, ("cacl_get received unknown exclusion: %s\n", + pExclude)); + errno = ENOATTR; + return -1; + } + } + } + + n_used = 0; + + /* + * If we are (possibly) talking to an NT or new system and some NT + * attributes have been requested... + */ + if (ipc_cli && (all || some_nt || all_nt_acls)) { + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + struct cli_credentials *creds = NULL; + NTSTATUS status; + + /* Point to the portion after "system.nt_sec_desc." */ + name += 19; /* if (all) this will be invalid but unused */ + + creds = context->internal->creds; + + status = cli_resolve_path( + ctx, "", + creds, + cli, filename, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("cacl_get Could not resolve %s\n", + filename)); + errno = ENOENT; + return -1; + } + + /* ... then obtain any NT attributes which were requested */ + status = cli_ntcreate( + targetcli, /* cli */ + targetpath, /* fname */ + 0, /* CreatFlags */ + READ_CONTROL_ACCESS, /* DesiredAccess */ + 0, /* FileAttributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE, /* ShareAccess */ + FILE_OPEN, /* CreateDisposition */ + 0x0, /* CreateOptions */ + 0x0, /* SecurityFlags */ + &fnum, /* pfid */ + NULL); /* cr */ + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("cacl_get failed to open %s: %s\n", + targetpath, nt_errstr(status))); + errno = 0; + return -1; + } + + status = cli_query_secdesc(targetcli, fnum, ctx, &sd); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5,("cacl_get Failed to query old descriptor " + "of %s: %s\n", + targetpath, nt_errstr(status))); + errno = 0; + return -1; + } + + cli_close(targetcli, fnum); + + if (! exclude_nt_revision) { + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, + "REVISION:%d", + sd->revision); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "REVISION:%d", + sd->revision); + } + } else if (strcasecmp_m(name, "revision") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%d", + sd->revision); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%d", + sd->revision); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_nt_owner) { + /* Get owner and group sid */ + if (sd->owner_sid) { + convert_sid_to_string(ipc_cli, pol, + sidstr, + numeric, + sd->owner_sid); + } else { + fstrcpy(sidstr, ""); + } + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, ",OWNER:%s", + sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else if (sidstr[0] != '\0') { + n = snprintf(buf, bufsize, + ",OWNER:%s", sidstr); + } + } else if (strncasecmp_m(name, "owner", 5) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, "%s", + sidstr); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_nt_group) { + if (sd->group_sid) { + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, + sd->group_sid); + } else { + fstrcpy(sidstr, ""); + } + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf(ctx, ",GROUP:%s", + sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else if (sidstr[0] != '\0') { + n = snprintf(buf, bufsize, + ",GROUP:%s", sidstr); + } + } else if (strncasecmp_m(name, "group", 5) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%s", sidstr); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%s", sidstr); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_nt_acl) { + /* Add aces to value buffer */ + for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + + struct security_ace *ace = &sd->dacl->aces[i]; + convert_sid_to_string(ipc_cli, pol, + sidstr, numeric, + &ace->trustee); + + if (all || all_nt) { + if (determine_size) { + p = talloc_asprintf( + ctx, + ",ACL:" + "%s:%d/%d/0x%08x", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf( + buf, bufsize, + ",ACL:%s:%d/%d/0x%08x", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + } + } else if ((strncasecmp_m(name, "acl", 3) == 0 && + strcasecmp_m(name+3, sidstr) == 0) || + (strncasecmp_m(name, "acl+", 4) == 0 && + strcasecmp_m(name+4, sidstr) == 0)) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%d/%d/0x%08x", + ace->type, + ace->flags, + ace->access_mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%d/%d/0x%08x", + ace->type, + ace->flags, + ace->access_mask); + } + } else if (all_nt_acls) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%s%s:%d/%d/0x%08x", + i ? "," : "", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%s%s:%d/%d/0x%08x", + i ? "," : "", + sidstr, + ace->type, + ace->flags, + ace->access_mask); + } + } + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + } + + /* Restore name pointer to its original value */ + name -= 19; + } + + if (all || some_dos) { + struct stat sb = {0}; + time_t create_time = (time_t)0; + time_t write_time = (time_t)0; + time_t access_time = (time_t)0; + time_t change_time = (time_t)0; + off_t size = 0; + uint16_t mode = 0; + SMB_INO_T ino = 0; + NTSTATUS status; + + /* Point to the portion after "system.dos_attr." */ + name += 16; /* if (all) this will be invalid but unused */ + + /* Obtain the DOS attributes */ + status = SMBC_getatr(context, srv, filename, &sb); + if (!NT_STATUS_IS_OK(status)) { + errno = cli_status_to_errno(status); + return -1; + } + + create_time = sb.st_ctime; + access_time = sb.st_atime; + write_time = sb.st_mtime; + change_time = sb.st_mtime; + size = sb.st_size; + mode = sb.st_mode; + ino = sb.st_ino; + + if (! exclude_dos_mode) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + "%sMODE:0x%x", + (ipc_cli && + (all || some_nt) + ? "," + : ""), + mode); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%sMODE:0x%x", + (ipc_cli && + (all || some_nt) + ? "," + : ""), + mode); + } + } else if (strcasecmp_m(name, "mode") == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "0x%x", mode); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "0x%x", mode); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_size) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf( + ctx, + ",SIZE:%.0f", + (double)size); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",SIZE:%.0f", + (double)size); + } + } else if (strcasecmp_m(name, "size") == 0) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%.0f", + (double)size); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%.0f", + (double)size); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_create_time && + attr_strings.create_time_attr != NULL) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.create_time_attr, + (unsigned long) create_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.create_time_attr, + (unsigned long) create_time); + } + } else if (strcasecmp_m(name, attr_strings.create_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", (unsigned long) create_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", (unsigned long) create_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_access_time) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.access_time_attr, + (unsigned long) access_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.access_time_attr, + (unsigned long) access_time); + } + } else if (strcasecmp_m(name, attr_strings.access_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", (unsigned long) access_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", (unsigned long) access_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_write_time) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.write_time_attr, + (unsigned long) write_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.write_time_attr, + (unsigned long) write_time); + } + } else if (strcasecmp_m(name, attr_strings.write_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", (unsigned long) write_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", (unsigned long) write_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_change_time) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf(ctx, + ",%s:%lu", + attr_strings.change_time_attr, + (unsigned long) change_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",%s:%lu", + attr_strings.change_time_attr, + (unsigned long) change_time); + } + } else if (strcasecmp_m(name, attr_strings.change_time_attr) == 0) { + if (determine_size) { + p = talloc_asprintf(ctx, "%lu", (unsigned long) change_time); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%lu", (unsigned long) change_time); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + if (! exclude_dos_inode) { + if (all || all_dos) { + if (determine_size) { + p = talloc_asprintf( + ctx, + ",INODE:%.0f", + (double)ino); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + ",INODE:%.0f", + (double) ino); + } + } else if (strcasecmp_m(name, "inode") == 0) { + if (determine_size) { + p = talloc_asprintf( + ctx, + "%.0f", + (double) ino); + if (!p) { + errno = ENOMEM; + return -1; + } + n = strlen(p); + } else { + n = snprintf(buf, bufsize, + "%.0f", + (double) ino); + } + } + + if (!determine_size && n > bufsize) { + errno = ERANGE; + return -1; + } + buf += n; + n_used += n; + bufsize -= n; + n = 0; + } + + /* Restore name pointer to its original value */ + name -= 16; + } + + if (n_used == 0) { + errno = ENOATTR; + return -1; + } + + return n_used; +} + +/***************************************************** +set the ACLs on a file given an ascii description +*******************************************************/ +static int +cacl_set(SMBCCTX *context, + TALLOC_CTX *ctx, + struct cli_state *cli, + struct cli_state *ipc_cli, + struct policy_handle *pol, + const char *filename, + char *the_acl, + int mode, + int flags) +{ + uint16_t fnum = (uint16_t)-1; + int err = 0; + struct security_descriptor *sd = NULL, *old; + struct security_acl *dacl = NULL; + struct dom_sid *owner_sid = NULL; + struct dom_sid *group_sid = NULL; + uint32_t i, j; + size_t sd_size; + int ret = 0; + char *p; + bool numeric = True; + char *targetpath = NULL; + struct cli_state *targetcli = NULL; + struct cli_credentials *creds = NULL; + NTSTATUS status; + + /* the_acl will be null for REMOVE_ALL operations */ + if (the_acl) { + numeric = ((p = strchr(the_acl, ':')) != NULL && + p > the_acl && + p[-1] != '+'); + + /* if this is to set the entire ACL... */ + if (*the_acl == '*') { + /* ... then increment past the first colon */ + the_acl = p + 1; + } + + sd = sec_desc_parse(ctx, ipc_cli, pol, numeric, the_acl); + if (!sd) { + errno = EINVAL; + return -1; + } + } + + /* SMBC_XATTR_MODE_REMOVE_ALL is the only caller + that doesn't deref sd */ + + if (!sd && (mode != SMBC_XATTR_MODE_REMOVE_ALL)) { + errno = EINVAL; + return -1; + } + + creds = context->internal->creds; + + status = cli_resolve_path(ctx, "", + creds, + cli, filename, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5,("cacl_set: Could not resolve %s\n", filename)); + errno = ENOENT; + return -1; + } + + /* The desired access below is the only one I could find that works + with NT4, W2KP and Samba */ + + status = cli_ntcreate( + targetcli, /* cli */ + targetpath, /* fname */ + 0, /* CreatFlags */ + READ_CONTROL_ACCESS, /* DesiredAccess */ + 0, /* FileAttributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE, /* ShareAccess */ + FILE_OPEN, /* CreateDisposition */ + 0x0, /* CreateOptions */ + 0x0, /* SecurityFlags */ + &fnum, /* pfid */ + NULL); /* cr */ + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("cacl_set failed to open %s: %s\n", + targetpath, nt_errstr(status))); + errno = 0; + return -1; + } + + status = cli_query_secdesc(targetcli, fnum, ctx, &old); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5,("cacl_set Failed to query old descriptor of %s: %s\n", + targetpath, nt_errstr(status))); + errno = 0; + return -1; + } + + cli_close(targetcli, fnum); + + switch (mode) { + case SMBC_XATTR_MODE_REMOVE_ALL: + old->dacl->num_aces = 0; + dacl = old->dacl; + break; + + case SMBC_XATTR_MODE_REMOVE: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (security_ace_equal(&sd->dacl->aces[i], + &old->dacl->aces[j])) { + uint32_t k; + for (k=j; k<old->dacl->num_aces-1;k++) { + old->dacl->aces[k] = + old->dacl->aces[k+1]; + } + old->dacl->num_aces--; + found = True; + dacl = old->dacl; + break; + } + } + + if (!found) { + err = ENOATTR; + ret = -1; + goto failed; + } + } + break; + + case SMBC_XATTR_MODE_ADD: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (dom_sid_equal(&sd->dacl->aces[i].trustee, + &old->dacl->aces[j].trustee)) { + if (!(flags & SMBC_XATTR_FLAG_CREATE)) { + err = EEXIST; + ret = -1; + goto failed; + } + old->dacl->aces[j] = sd->dacl->aces[i]; + ret = -1; + found = True; + } + } + + if (!found && (flags & SMBC_XATTR_FLAG_REPLACE)) { + err = ENOATTR; + ret = -1; + goto failed; + } + + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + add_ace(&old->dacl, &sd->dacl->aces[i], ctx); + } + } + dacl = old->dacl; + break; + + case SMBC_XATTR_MODE_SET: + old = sd; + owner_sid = old->owner_sid; + group_sid = old->group_sid; + dacl = old->dacl; + break; + + case SMBC_XATTR_MODE_CHOWN: + owner_sid = sd->owner_sid; + break; + + case SMBC_XATTR_MODE_CHGRP: + group_sid = sd->group_sid; + break; + } + + /* Denied ACE entries must come before allowed ones */ + sort_acl(old->dacl); + + /* Create new security descriptor and set it */ + sd = make_sec_desc(ctx, old->revision, SEC_DESC_SELF_RELATIVE, + owner_sid, group_sid, NULL, dacl, &sd_size); + + status = cli_ntcreate(targetcli, targetpath, 0, + WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, + 0x0, 0x0, &fnum, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("cacl_set failed to open %s: %s\n", + targetpath, nt_errstr(status))); + errno = 0; + return -1; + } + + status = cli_set_secdesc(targetcli, fnum, sd); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("ERROR: secdesc set failed: %s\n", + nt_errstr(status))); + ret = -1; + } + + /* Clean up */ + +failed: + cli_close(targetcli, fnum); + + if (err != 0) { + errno = err; + } + + return ret; +} + + +int +SMBC_setxattr_ctx(SMBCCTX *context, + const char *fname, + const char *name, + const void *value, + size_t size, + int flags) +{ + int ret; + int ret2; + SMBCSRV *srv = NULL; + SMBCSRV *ipc_srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + struct DOS_ATTR_DESC *dad = NULL; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_setxattr(%s, %s, %.*s)\n", + fname, name, (int) size, (const char*)value)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (! srv->no_nt_session) { + ipc_srv = SMBC_attr_server(frame, context, server, port, share, + &workgroup, &user, &password); + if (! ipc_srv) { + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; + } + + /* + * Are they asking to set the entire set of known attributes? + */ + if (strcasecmp_m(name, "system.*") == 0 || + strcasecmp_m(name, "system.*+") == 0) { + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+7, (const char *) value); + if (! namevalue) { + errno = ENOMEM; + ret = -1; + TALLOC_FREE(frame); + return -1; + } + + if (ipc_srv) { + ret = cacl_set(context, talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, + (*namevalue == '*' + ? SMBC_XATTR_MODE_SET + : SMBC_XATTR_MODE_ADD), + flags); + } else { + ret = 0; + } + + /* get a DOS Attribute Descriptor with current attributes */ + dad = dos_attr_query(context, talloc_tos(), path, srv); + if (dad) { + bool ok; + + /* Overwrite old with new, using what was provided */ + dos_attr_parse(context, dad, srv, namevalue); + + /* Set the new DOS attributes */ + ok = SMBC_setatr( + context, + srv, + path, + (struct timespec) { + .tv_sec = dad->create_time }, + (struct timespec) { + .tv_sec = dad->access_time }, + (struct timespec) { + .tv_sec = dad->write_time }, + (struct timespec) { + .tv_sec = dad->change_time }, + dad->mode); + if (!ok) { + /* cause failure if NT failed too */ + dad = NULL; + } + } + + /* we only fail if both NT and DOS sets failed */ + if (ret < 0 && ! dad) { + ret = -1; /* in case dad was null */ + } + else { + ret = 0; + } + + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to set an access control element or to set + * the entire access control list? + */ + if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 || + strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 || + strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) { + + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+19, (const char *) value); + + if (! ipc_srv) { + ret = -1; /* errno set by SMBC_server() */ + } + else if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + ret = cacl_set(context, talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, + (*namevalue == '*' + ? SMBC_XATTR_MODE_SET + : SMBC_XATTR_MODE_ADD), + flags); + } + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to set the owner? + */ + if (strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0) { + + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+19, (const char *) value); + + if (! ipc_srv) { + ret = -1; /* errno set by SMBC_server() */ + } + else if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + ret = cacl_set(context, talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, SMBC_XATTR_MODE_CHOWN, 0); + } + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to set the group? + */ + if (strcasecmp_m(name, "system.nt_sec_desc.group") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.group+") == 0) { + + /* Yup. */ + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+19, (const char *) value); + + if (! ipc_srv) { + /* errno set by SMBC_server() */ + ret = -1; + } + else if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + ret = cacl_set(context, talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + namevalue, SMBC_XATTR_MODE_CHGRP, 0); + } + TALLOC_FREE(frame); + return ret; + } + + /* Determine whether to use old-style or new-style attribute names */ + if (context->internal->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME"; + attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME"; + attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME"; + attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "system.dos_attr.A_TIME"; + attr_strings.write_time_attr = "system.dos_attr.M_TIME"; + attr_strings.change_time_attr = "system.dos_attr.C_TIME"; + } + + /* + * Are they asking to set a DOS attribute? + */ + if (strcasecmp_m(name, "system.dos_attr.*") == 0 || + strcasecmp_m(name, "system.dos_attr.mode") == 0 || + (attr_strings.create_time_attr != NULL && + strcasecmp_m(name, attr_strings.create_time_attr) == 0) || + strcasecmp_m(name, attr_strings.access_time_attr) == 0 || + strcasecmp_m(name, attr_strings.write_time_attr) == 0 || + strcasecmp_m(name, attr_strings.change_time_attr) == 0) { + + /* get a DOS Attribute Descriptor with current attributes */ + dad = dos_attr_query(context, talloc_tos(), path, srv); + if (dad) { + char *namevalue = + talloc_asprintf(talloc_tos(), "%s:%s", + name+16, (const char *) value); + if (! namevalue) { + errno = ENOMEM; + ret = -1; + } else { + /* Overwrite old with provided new params */ + dos_attr_parse(context, dad, srv, namevalue); + + /* Set the new DOS attributes */ + ret2 = SMBC_setatr( + context, + srv, + path, + (struct timespec) { + .tv_sec = dad->create_time }, + (struct timespec) { + .tv_sec = dad->access_time }, + (struct timespec) { + .tv_sec = dad->write_time }, + (struct timespec) { + .tv_sec = dad->change_time }, + dad->mode); + + /* ret2 has True (success) / False (failure) */ + if (ret2) { + ret = 0; + } else { + ret = -1; + } + } + } else { + ret = -1; + } + + TALLOC_FREE(frame); + return ret; + } + + /* Unsupported attribute name */ + errno = EINVAL; + TALLOC_FREE(frame); + return -1; +} + +int +SMBC_getxattr_ctx(SMBCCTX *context, + const char *fname, + const char *name, + const void *value, + size_t size) +{ + int ret; + SMBCSRV *srv = NULL; + SMBCSRV *ipc_srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + struct { + const char * create_time_attr; + const char * access_time_attr; + const char * write_time_attr; + const char * change_time_attr; + } attr_strings; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_getxattr(%s, %s)\n", fname, name)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == '\0') { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (! srv->no_nt_session) { + ipc_srv = SMBC_attr_server(frame, context, server, port, share, + &workgroup, &user, &password); + /* + * SMBC_attr_server() can cause the original + * server to be removed from the cache. + * If so we must error out here as the srv + * pointer has been freed. + */ + if (smbc_getFunctionGetCachedServer(context)(context, + server, + share, + workgroup, + user) != srv) { +#if defined(ECONNRESET) + errno = ECONNRESET; +#else + errno = ETIMEDOUT; +#endif + TALLOC_FREE(frame); + return -1; + } + if (! ipc_srv) { + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; + } + + /* Determine whether to use old-style or new-style attribute names */ + if (context->internal->full_time_names) { + /* new-style names */ + attr_strings.create_time_attr = "system.dos_attr.CREATE_TIME"; + attr_strings.access_time_attr = "system.dos_attr.ACCESS_TIME"; + attr_strings.write_time_attr = "system.dos_attr.WRITE_TIME"; + attr_strings.change_time_attr = "system.dos_attr.CHANGE_TIME"; + } else { + /* old-style names */ + attr_strings.create_time_attr = NULL; + attr_strings.access_time_attr = "system.dos_attr.A_TIME"; + attr_strings.write_time_attr = "system.dos_attr.M_TIME"; + attr_strings.change_time_attr = "system.dos_attr.C_TIME"; + } + + /* Are they requesting a supported attribute? */ + if (strcasecmp_m(name, "system.*") == 0 || + strncasecmp_m(name, "system.*!", 9) == 0 || + strcasecmp_m(name, "system.*+") == 0 || + strncasecmp_m(name, "system.*+!", 10) == 0 || + strcasecmp_m(name, "system.nt_sec_desc.*") == 0 || + strncasecmp_m(name, "system.nt_sec_desc.*!", 21) == 0 || + strcasecmp_m(name, "system.nt_sec_desc.*+") == 0 || + strncasecmp_m(name, "system.nt_sec_desc.*+!", 22) == 0 || + strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.group") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 || + strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 || + strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0 || + strcasecmp_m(name, "system.dos_attr.*") == 0 || + strncasecmp_m(name, "system.dos_attr.*!", 18) == 0 || + strcasecmp_m(name, "system.dos_attr.mode") == 0 || + strcasecmp_m(name, "system.dos_attr.size") == 0 || + (attr_strings.create_time_attr != NULL && + strcasecmp_m(name, attr_strings.create_time_attr) == 0) || + strcasecmp_m(name, attr_strings.access_time_attr) == 0 || + strcasecmp_m(name, attr_strings.write_time_attr) == 0 || + strcasecmp_m(name, attr_strings.change_time_attr) == 0 || + strcasecmp_m(name, "system.dos_attr.inode") == 0) { + + /* Yup. */ + const char *filename = name; + ret = cacl_get(context, talloc_tos(), srv, + ipc_srv == NULL ? NULL : ipc_srv->cli, + &ipc_srv->pol, path, + filename, + discard_const_p(char, value), + size); + if (ret < 0 && errno == 0) { + errno = SMBC_errno(context, srv->cli); + } + TALLOC_FREE(frame); + /* + * static function cacl_get returns a value greater than zero + * on success. Map this to zero meaning success. + */ + return ret < 0 ? -1 : 0; + } + + /* Unsupported attribute name */ + errno = EINVAL; + TALLOC_FREE(frame); + return -1; +} + + +int +SMBC_removexattr_ctx(SMBCCTX *context, + const char *fname, + const char *name) +{ + int ret; + SMBCSRV *srv = NULL; + SMBCSRV *ipc_srv = NULL; + char *server = NULL; + char *share = NULL; + char *user = NULL; + char *password = NULL; + char *workgroup = NULL; + char *path = NULL; + uint16_t port = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + if (!context || !context->internal->initialized) { + errno = EINVAL; /* Best I can think of ... */ + TALLOC_FREE(frame); + return -1; + } + + if (!fname) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + DEBUG(4, ("smbc_removexattr(%s, %s)\n", fname, name)); + + if (SMBC_parse_path(frame, + context, + fname, + &workgroup, + &server, + &port, + &share, + &path, + &user, + &password, + NULL)) { + errno = EINVAL; + TALLOC_FREE(frame); + return -1; + } + + if (!user || user[0] == (char)0) { + user = talloc_strdup(frame, smbc_getUser(context)); + if (!user) { + errno = ENOMEM; + TALLOC_FREE(frame); + return -1; + } + } + + srv = SMBC_server(frame, context, True, + server, port, share, &workgroup, &user, &password); + if (!srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_server */ + } + + if (! srv->no_nt_session) { + int saved_errno; + ipc_srv = SMBC_attr_server(frame, context, server, port, share, + &workgroup, &user, &password); + saved_errno = errno; + /* + * SMBC_attr_server() can cause the original + * server to be removed from the cache. + * If so we must error out here as the srv + * pointer has been freed. + */ + if (smbc_getFunctionGetCachedServer(context)(context, + server, + share, + workgroup, + user) != srv) { +#if defined(ECONNRESET) + errno = ECONNRESET; +#else + errno = ETIMEDOUT; +#endif + TALLOC_FREE(frame); + return -1; + } + if (! ipc_srv) { + errno = saved_errno; + srv->no_nt_session = True; + } + } else { + ipc_srv = NULL; + } + + if (! ipc_srv) { + TALLOC_FREE(frame); + return -1; /* errno set by SMBC_attr_server */ + } + + /* Are they asking to set the entire ACL? */ + if (strcasecmp_m(name, "system.nt_sec_desc.*") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.*+") == 0) { + + /* Yup. */ + ret = cacl_set(context, talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + NULL, SMBC_XATTR_MODE_REMOVE_ALL, 0); + TALLOC_FREE(frame); + return ret; + } + + /* + * Are they asking to remove one or more specific security descriptor + * attributes? + */ + if (strcasecmp_m(name, "system.nt_sec_desc.revision") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.owner") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.owner+") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.group") == 0 || + strcasecmp_m(name, "system.nt_sec_desc.group+") == 0 || + strncasecmp_m(name, "system.nt_sec_desc.acl", 22) == 0 || + strncasecmp_m(name, "system.nt_sec_desc.acl+", 23) == 0) { + + /* Yup. */ + ret = cacl_set(context, talloc_tos(), srv->cli, + ipc_srv->cli, &ipc_srv->pol, path, + discard_const_p(char, name) + 19, + SMBC_XATTR_MODE_REMOVE, 0); + TALLOC_FREE(frame); + return ret; + } + + /* Unsupported attribute name */ + errno = EINVAL; + TALLOC_FREE(frame); + return -1; +} + +int +SMBC_listxattr_ctx(SMBCCTX *context, + const char *fname, + char *list, + size_t size) +{ + /* + * This isn't quite what listxattr() is supposed to do. This returns + * the complete set of attribute names, always, rather than only those + * attribute names which actually exist for a file. Hmmm... + */ + size_t retsize; + const char supported_old[] = + "system.*\0" + "system.*+\0" + "system.nt_sec_desc.revision\0" + "system.nt_sec_desc.owner\0" + "system.nt_sec_desc.owner+\0" + "system.nt_sec_desc.group\0" + "system.nt_sec_desc.group+\0" + "system.nt_sec_desc.acl.*\0" + "system.nt_sec_desc.acl\0" + "system.nt_sec_desc.acl+\0" + "system.nt_sec_desc.*\0" + "system.nt_sec_desc.*+\0" + "system.dos_attr.*\0" + "system.dos_attr.mode\0" + "system.dos_attr.c_time\0" + "system.dos_attr.a_time\0" + "system.dos_attr.m_time\0" + ; + const char supported_new[] = + "system.*\0" + "system.*+\0" + "system.nt_sec_desc.revision\0" + "system.nt_sec_desc.owner\0" + "system.nt_sec_desc.owner+\0" + "system.nt_sec_desc.group\0" + "system.nt_sec_desc.group+\0" + "system.nt_sec_desc.acl.*\0" + "system.nt_sec_desc.acl\0" + "system.nt_sec_desc.acl+\0" + "system.nt_sec_desc.*\0" + "system.nt_sec_desc.*+\0" + "system.dos_attr.*\0" + "system.dos_attr.mode\0" + "system.dos_attr.create_time\0" + "system.dos_attr.access_time\0" + "system.dos_attr.write_time\0" + "system.dos_attr.change_time\0" + ; + const char * supported; + + if (context->internal->full_time_names) { + supported = supported_new; + retsize = sizeof(supported_new); + } else { + supported = supported_old; + retsize = sizeof(supported_old); + } + + if (size == 0) { + return retsize; + } + + if (retsize > size) { + errno = ERANGE; + return -1; + } + + /* this can't be strcpy() because there are embedded null characters */ + memcpy(list, supported, retsize); + return retsize; +} diff --git a/source3/libsmb/namecache.c b/source3/libsmb/namecache.c new file mode 100644 index 0000000..66147ba --- /dev/null +++ b/source3/libsmb/namecache.c @@ -0,0 +1,478 @@ +/* + Unix SMB/CIFS implementation. + + NetBIOS name cache module on top of gencache mechanism. + + Copyright (C) Tim Potter 2002 + Copyright (C) Rafal Szczesniak 2002 + Copyright (C) Jeremy Allison 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/gencache.h" +#include "libsmb/namequery.h" + +#define IPSTR_LIST_SEP "," +#define IPSTR_LIST_CHAR ',' + +/** + * Allocate and initialise an ipstr list using samba_sockaddr ip adresses + * passed as arguments. + * + * @param ctx TALLOC_CTX to use + * @param ip_list array of ip addresses to place in the list + * @param ip_count number of addresses stored in ip_list + * @return pointer to allocated ip string + **/ + +static char *ipstr_list_make_sa(TALLOC_CTX *ctx, + const struct samba_sockaddr *sa_list, + size_t ip_count) +{ + char *ipstr_list = NULL; + size_t i; + + /* arguments checking */ + if (sa_list == NULL) { + return NULL; + } + + /* process ip addresses given as arguments */ + for (i = 0; i < ip_count; i++) { + char addr_buf[INET6_ADDRSTRLEN]; + char *new_str = NULL; + + print_sockaddr(addr_buf, + sizeof(addr_buf), + &sa_list[i].u.ss); + + if (sa_list[i].u.ss.ss_family == AF_INET) { + /* IPv4 - port no longer used, store 0 */ + new_str = talloc_asprintf(ctx, + "%s:%d", + addr_buf, + 0); + } else { + /* IPv6 - port no longer used, store 0 */ + new_str = talloc_asprintf(ctx, + "[%s]:%d", + addr_buf, + 0); + } + if (new_str == NULL) { + TALLOC_FREE(ipstr_list); + return NULL; + } + + if (ipstr_list == NULL) { + /* First ip address. */ + ipstr_list = new_str; + } else { + /* + * Append the separator "," and then the new + * ip address to the existing list. + * + * The efficiency here is horrible, but + * ip_count should be small enough we can + * live with it. + */ + char *tmp = talloc_asprintf(ctx, + "%s%s%s", + ipstr_list, + IPSTR_LIST_SEP, + new_str); + if (tmp == NULL) { + TALLOC_FREE(new_str); + TALLOC_FREE(ipstr_list); + return NULL; + } + TALLOC_FREE(new_str); + TALLOC_FREE(ipstr_list); + ipstr_list = tmp; + } + } + + return ipstr_list; +} + +/** + * Parse given ip string list into array of ip addresses + * (as ip_service structures) + * e.g. [IPv6]:port,192.168.1.100:389,192.168.1.78, ... + * + * @param ipstr ip string list to be parsed + * @param ip_list pointer to array of ip addresses which is + * talloced by this function and must be freed by caller + * @return number of successfully parsed addresses + **/ + +static int ipstr_list_parse(TALLOC_CTX *ctx, + const char *ipstr_list, + struct samba_sockaddr **sa_list_out) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct samba_sockaddr *sa_list = NULL; + char *token_str = NULL; + size_t i, count; + size_t array_size; + + *sa_list_out = NULL; + + array_size = count_chars(ipstr_list, IPSTR_LIST_CHAR) + 1; + sa_list = talloc_zero_array(frame, + struct samba_sockaddr, + array_size); + if (sa_list == NULL) { + TALLOC_FREE(frame); + return 0; + } + + count = 0; + for (i=0; next_token_talloc(frame, &ipstr_list, &token_str, + IPSTR_LIST_SEP); i++ ) { + bool ok; + char *s = token_str; + char *p = strrchr(token_str, ':'); + struct sockaddr_storage ss; + + /* Ensure we don't overrun. */ + if (count >= array_size) { + break; + } + + if (p) { + *p = 0; + /* We now ignore the port. */ + } + + /* convert single token to ip address */ + if (token_str[0] == '[') { + /* IPv6 address. */ + s++; + p = strchr(token_str, ']'); + if (!p) { + continue; + } + *p = '\0'; + } + ok = interpret_string_addr(&ss, s, AI_NUMERICHOST); + if (!ok) { + continue; + } + ok = sockaddr_storage_to_samba_sockaddr(&sa_list[count], + &ss); + if (!ok) { + continue; + } + count++; + } + if (count > 0) { + *sa_list_out = talloc_move(ctx, &sa_list); + } + TALLOC_FREE(frame); + return count; +} + +#define NBTKEY_FMT "NBT/%s#%02X" + +/** + * Generates a key for netbios name lookups on basis of + * netbios name and type. + * The caller must free returned key string when finished. + * + * @param name netbios name string (case insensitive) + * @param name_type netbios type of the name being looked up + * + * @return string consisted of uppercased name and appended + * type number + */ + +static char *namecache_key(TALLOC_CTX *ctx, + const char *name, + int name_type) +{ + return talloc_asprintf_strupper_m(ctx, + NBTKEY_FMT, + name, + name_type); +} + +/** + * Store a name(s) in the name cache - samba_sockaddr version. + * + * @param name netbios names array + * @param name_type integer netbios name type + * @param num_names number of names being stored + * @param ip_list array of in_addr structures containing + * ip addresses being stored + **/ + +bool namecache_store(const char *name, + int name_type, + size_t num_names, + struct samba_sockaddr *sa_list) +{ + time_t expiry; + char *key = NULL; + char *value_string = NULL; + size_t i; + bool ret = false; + TALLOC_CTX *frame = talloc_stackframe(); + + if (name_type > 255) { + /* Don't store non-real name types. */ + goto out; + } + + if ( DEBUGLEVEL >= 5 ) { + char *addr = NULL; + + DBG_INFO("storing %zu address%s for %s#%02x: ", + num_names, num_names == 1 ? "": "es", name, name_type); + + for (i = 0; i < num_names; i++) { + addr = print_canonical_sockaddr(frame, + &sa_list[i].u.ss); + if (!addr) { + continue; + } + DEBUGADD(5, ("%s%s", addr, + (i == (num_names - 1) ? "" : ","))); + + } + DEBUGADD(5, ("\n")); + } + + key = namecache_key(frame, name, name_type); + if (!key) { + goto out; + } + + expiry = time(NULL) + lp_name_cache_timeout(); + + /* + * Generate string representation of ip addresses list + */ + value_string = ipstr_list_make_sa(frame, sa_list, num_names); + if (value_string == NULL) { + goto out; + } + + /* set the entry */ + ret = gencache_set(key, value_string, expiry); + + out: + + TALLOC_FREE(key); + TALLOC_FREE(value_string); + TALLOC_FREE(frame); + return ret; +} + +/** + * Look up a name in the cache. + * + * @param name netbios name to look up for + * @param name_type netbios name type of @param name + * @param ip_list talloced list of IP addresses if found in the cache, + * NULL otherwise + * @param num_names number of entries found + * + * @return true upon successful fetch or + * false if name isn't found in the cache or has expired + **/ + +bool namecache_fetch(TALLOC_CTX *ctx, + const char *name, + int name_type, + struct samba_sockaddr **sa_list, + size_t *num_names) +{ + char *key, *value; + time_t timeout; + + if (name_type > 255) { + return false; /* Don't fetch non-real name types. */ + } + + *num_names = 0; + + /* + * Use gencache interface - lookup the key + */ + key = namecache_key(talloc_tos(), name, name_type); + if (!key) { + return false; + } + + if (!gencache_get(key, talloc_tos(), &value, &timeout)) { + DBG_INFO("no entry for %s#%02X found.\n", name, name_type); + TALLOC_FREE(key); + return false; + } + + DBG_INFO("name %s#%02X found.\n", name, name_type); + + /* + * Split up the stored value into the list of IP adresses + */ + *num_names = ipstr_list_parse(ctx, value, sa_list); + + TALLOC_FREE(key); + TALLOC_FREE(value); + + return *num_names > 0; /* true only if some ip has been fetched */ +} + +/** + * Remove a namecache entry. Needed for site support. + * + **/ + +bool namecache_delete(const char *name, int name_type) +{ + bool ret; + char *key; + + if (name_type > 255) { + return false; /* Don't fetch non-real name types. */ + } + + key = namecache_key(talloc_tos(), name, name_type); + if (!key) { + return false; + } + ret = gencache_del(key); + TALLOC_FREE(key); + return ret; +} + +/** + * Delete single namecache entry. Look at the + * gencache_iterate definition. + * + **/ + +static void flush_netbios_name(const char *key, + const char *value, + time_t timeout, + void *dptr) +{ + gencache_del(key); + DBG_INFO("Deleting entry %s\n", key); +} + +/** + * Flush all names from the name cache. + * It's done by gencache_iterate() + * + * @return true upon successful deletion or + * false in case of an error + **/ + +void namecache_flush(void) +{ + /* + * iterate through each NBT cache's entry and flush it + * by flush_netbios_name function + */ + gencache_iterate(flush_netbios_name, NULL, "NBT/*"); + DBG_INFO("Namecache flushed\n"); +} + +/* Construct a name status record key. */ + +static char *namecache_status_record_key(TALLOC_CTX *ctx, + const char *name, + int name_type1, + int name_type2, + const struct sockaddr_storage *keyip) +{ + char addr[INET6_ADDRSTRLEN]; + + print_sockaddr(addr, sizeof(addr), keyip); + return talloc_asprintf_strupper_m(ctx, + "NBT/%s#%02X.%02X.%s", + name, + name_type1, + name_type2, + addr); +} + +/* Store a name status record. */ + +bool namecache_status_store(const char *keyname, int keyname_type, + int name_type, const struct sockaddr_storage *keyip, + const char *srvname) +{ + char *key; + time_t expiry; + bool ret; + + key = namecache_status_record_key(talloc_tos(), + keyname, + keyname_type, + name_type, + keyip); + if (!key) + return false; + + expiry = time(NULL) + lp_name_cache_timeout(); + ret = gencache_set(key, srvname, expiry); + + if (ret) { + DBG_INFO("entry %s -> %s\n", key, srvname); + } else { + DBG_INFO("entry %s store failed.\n", key); + } + + TALLOC_FREE(key); + return ret; +} + +/* Fetch a name status record. */ + +bool namecache_status_fetch(const char *keyname, + int keyname_type, + int name_type, + const struct sockaddr_storage *keyip, + char *srvname_out) +{ + char *key = NULL; + char *value = NULL; + time_t timeout; + + key = namecache_status_record_key(talloc_tos(), + keyname, + keyname_type, + name_type, + keyip); + if (!key) + return false; + + if (!gencache_get(key, talloc_tos(), &value, &timeout)) { + DBG_INFO("no entry for %s found.\n", key); + TALLOC_FREE(key); + return false; + } else { + DBG_INFO("key %s -> %s\n", key, value); + } + + strlcpy(srvname_out, value, 16); + TALLOC_FREE(key); + TALLOC_FREE(value); + return true; +} diff --git a/source3/libsmb/namequery.c b/source3/libsmb/namequery.c new file mode 100644 index 0000000..047fff2 --- /dev/null +++ b/source3/libsmb/namequery.c @@ -0,0 +1,3538 @@ +/* + Unix SMB/CIFS implementation. + name query routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2007. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "libsmb/namequery.h" +#include "../lib/util/tevent_ntstatus.h" +#include "libads/sitename_cache.h" +#include "../lib/addns/dnsquery.h" +#include "../lib/addns/dnsquery_srv.h" +#include "../libcli/netlogon/netlogon.h" +#include "lib/async_req/async_sock.h" +#include "lib/tsocket/tsocket.h" +#include "libsmb/nmblib.h" +#include "libsmb/unexpected.h" +#include "../libcli/nbt/libnbt.h" +#include "libads/kerberos_proto.h" +#include "lib/gencache.h" +#include "librpc/gen_ndr/dns.h" +#include "lib/util/util_net.h" +#include "lib/util/string_wrappers.h" + +/* nmbd.c sets this to True. */ +bool global_in_nmbd = False; + +/* + * Utility function to convert from a sockaddr_storage + * array to a struct samba_sockaddr array. + */ + +static NTSTATUS sockaddr_array_to_samba_sockaddr_array( + TALLOC_CTX *ctx, + struct samba_sockaddr **sa_out, + size_t *count_out, + const struct sockaddr_storage *ss_in, + size_t count_in) +{ + struct samba_sockaddr *sa = NULL; + size_t i; + size_t count = 0; + + if (count_in == 0) { + /* + * Zero length arrays are returned as NULL. + * in the name resolution code. + */ + *count_out = 0; + *sa_out = NULL; + return NT_STATUS_OK; + } + sa = talloc_zero_array(ctx, + struct samba_sockaddr, + count_in); + if (sa == NULL) { + return NT_STATUS_NO_MEMORY; + } + count = 0; + for (i = 0; i < count_in; i++) { + bool ok; + + /* Filter out zero addresses. */ + if (is_zero_addr(&ss_in[i])) { + continue; + } + ok = sockaddr_storage_to_samba_sockaddr(&sa[count], + &ss_in[i]); + if (!ok) { + continue; + } + count++; + } + if (count == 0) { + /* + * Zero length arrays are returned as NULL. + * in the name resolution code. + */ + TALLOC_FREE(sa); + } + *count_out = count; + *sa_out = sa; + return NT_STATUS_OK; +} + +/**************************** + * SERVER AFFINITY ROUTINES * + ****************************/ + + /* Server affinity is the concept of preferring the last domain + controller with whom you had a successful conversation */ + +/**************************************************************************** +****************************************************************************/ +#define SAFKEY_FMT "SAF/DOMAIN/%s" +#define SAF_TTL 900 +#define SAFJOINKEY_FMT "SAFJOIN/DOMAIN/%s" +#define SAFJOIN_TTL 3600 + +static char *saf_key(TALLOC_CTX *mem_ctx, const char *domain) +{ + return talloc_asprintf_strupper_m(mem_ctx, SAFKEY_FMT, domain); +} + +static char *saf_join_key(TALLOC_CTX *mem_ctx, const char *domain) +{ + return talloc_asprintf_strupper_m(mem_ctx, SAFJOINKEY_FMT, domain); +} + +/**************************************************************************** +****************************************************************************/ + +bool saf_store( const char *domain, const char *servername ) +{ + char *key; + time_t expire; + bool ret = False; + + if ( !domain || !servername ) { + DEBUG(2,("saf_store: " + "Refusing to store empty domain or servername!\n")); + return False; + } + + if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) { + DEBUG(0,("saf_store: " + "refusing to store 0 length domain or servername!\n")); + return False; + } + + key = saf_key(talloc_tos(), domain); + if (key == NULL) { + DEBUG(1, ("saf_key() failed\n")); + return false; + } + expire = time( NULL ) + lp_parm_int(-1, "saf","ttl", SAF_TTL); + + DEBUG(10,("saf_store: domain = [%s], server = [%s], expire = [%u]\n", + domain, servername, (unsigned int)expire )); + + ret = gencache_set( key, servername, expire ); + + TALLOC_FREE( key ); + + return ret; +} + +bool saf_join_store( const char *domain, const char *servername ) +{ + char *key; + time_t expire; + bool ret = False; + + if ( !domain || !servername ) { + DEBUG(2,("saf_join_store: Refusing to store empty domain or servername!\n")); + return False; + } + + if ( (strlen(domain) == 0) || (strlen(servername) == 0) ) { + DEBUG(0,("saf_join_store: refusing to store 0 length domain or servername!\n")); + return False; + } + + key = saf_join_key(talloc_tos(), domain); + if (key == NULL) { + DEBUG(1, ("saf_join_key() failed\n")); + return false; + } + expire = time( NULL ) + lp_parm_int(-1, "saf","join ttl", SAFJOIN_TTL); + + DEBUG(10,("saf_join_store: domain = [%s], server = [%s], expire = [%u]\n", + domain, servername, (unsigned int)expire )); + + ret = gencache_set( key, servername, expire ); + + TALLOC_FREE( key ); + + return ret; +} + +bool saf_delete( const char *domain ) +{ + char *key; + bool ret = False; + + if ( !domain ) { + DEBUG(2,("saf_delete: Refusing to delete empty domain\n")); + return False; + } + + key = saf_join_key(talloc_tos(), domain); + if (key == NULL) { + DEBUG(1, ("saf_join_key() failed\n")); + return false; + } + ret = gencache_del(key); + TALLOC_FREE(key); + + if (ret) { + DEBUG(10,("saf_delete[join]: domain = [%s]\n", domain )); + } + + key = saf_key(talloc_tos(), domain); + if (key == NULL) { + DEBUG(1, ("saf_key() failed\n")); + return false; + } + ret = gencache_del(key); + TALLOC_FREE(key); + + if (ret) { + DEBUG(10,("saf_delete: domain = [%s]\n", domain )); + } + + return ret; +} + +/**************************************************************************** +****************************************************************************/ + +char *saf_fetch(TALLOC_CTX *mem_ctx, const char *domain ) +{ + char *server = NULL; + time_t timeout; + bool ret = False; + char *key = NULL; + + if ( !domain || strlen(domain) == 0) { + DEBUG(2,("saf_fetch: Empty domain name!\n")); + return NULL; + } + + key = saf_join_key(talloc_tos(), domain); + if (key == NULL) { + DEBUG(1, ("saf_join_key() failed\n")); + return NULL; + } + + ret = gencache_get( key, mem_ctx, &server, &timeout ); + + TALLOC_FREE( key ); + + if ( ret ) { + DEBUG(5,("saf_fetch[join]: Returning \"%s\" for \"%s\" domain\n", + server, domain )); + return server; + } + + key = saf_key(talloc_tos(), domain); + if (key == NULL) { + DEBUG(1, ("saf_key() failed\n")); + return NULL; + } + + ret = gencache_get( key, mem_ctx, &server, &timeout ); + + TALLOC_FREE( key ); + + if ( !ret ) { + DEBUG(5,("saf_fetch: failed to find server for \"%s\" domain\n", + domain )); + } else { + DEBUG(5,("saf_fetch: Returning \"%s\" for \"%s\" domain\n", + server, domain )); + } + + return server; +} + +static void set_socket_addr_v4(struct samba_sockaddr *addr) +{ + if (!interpret_string_addr(&addr->u.ss, lp_nbt_client_socket_address(), + AI_NUMERICHOST|AI_PASSIVE)) { + zero_sockaddr(&addr->u.ss); + /* zero_sockaddr sets family to AF_INET. */ + addr->sa_socklen = sizeof(struct sockaddr_in); + } + if (addr->u.ss.ss_family != AF_INET) { + zero_sockaddr(&addr->u.ss); + /* zero_sockaddr sets family to AF_INET. */ + addr->sa_socklen = sizeof(struct sockaddr_in); + } +} + +static struct in_addr my_socket_addr_v4(void) +{ + struct samba_sockaddr my_addr = {0}; + + set_socket_addr_v4(&my_addr); + return my_addr.u.in.sin_addr; +} + +/**************************************************************************** + Generate a random trn_id. +****************************************************************************/ + +static int generate_trn_id(void) +{ + uint16_t id; + + generate_random_buffer((uint8_t *)&id, sizeof(id)); + + return id % (unsigned)0x7FFF; +} + +/**************************************************************************** + Parse a node status response into an array of structures. +****************************************************************************/ + +static struct node_status *parse_node_status(TALLOC_CTX *mem_ctx, char *p, + size_t *num_names, + struct node_status_extra *extra) +{ + struct node_status *ret; + size_t i; + size_t result_count = 0; + + result_count = CVAL(p,0); + + if (result_count == 0) + return NULL; + + ret = talloc_array(mem_ctx, struct node_status,result_count); + if (!ret) + return NULL; + + p++; + for (i=0;i< result_count;i++) { + strlcpy(ret[i].name,p,16); + trim_char(ret[i].name,'\0',' '); + ret[i].type = CVAL(p,15); + ret[i].flags = p[16]; + p += 18; + DEBUG(10, ("%s#%02x: flags = 0x%02x\n", ret[i].name, + ret[i].type, ret[i].flags)); + } + /* + * Also, pick up the MAC address ... + */ + if (extra) { + memcpy(&extra->mac_addr, p, 6); /* Fill in the mac addr */ + } + *num_names = result_count; + return ret; +} + +struct sock_packet_read_state { + struct tevent_context *ev; + enum packet_type type; + int trn_id; + + struct nb_packet_reader *reader; + struct tevent_req *reader_req; + + struct tdgram_context *sock; + struct tevent_req *socket_req; + uint8_t *buf; + struct tsocket_address *addr; + + bool (*validator)(struct packet_struct *p, + void *private_data); + void *private_data; + + struct packet_struct *packet; +}; + +static void sock_packet_read_got_packet(struct tevent_req *subreq); +static void sock_packet_read_got_socket(struct tevent_req *subreq); + +static struct tevent_req *sock_packet_read_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct tdgram_context *sock, + struct nb_packet_reader *reader, + enum packet_type type, + int trn_id, + bool (*validator)(struct packet_struct *p, void *private_data), + void *private_data) +{ + struct tevent_req *req; + struct sock_packet_read_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct sock_packet_read_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->reader = reader; + state->sock = sock; + state->type = type; + state->trn_id = trn_id; + state->validator = validator; + state->private_data = private_data; + + if (reader != NULL) { + state->reader_req = nb_packet_read_send(state, ev, reader); + if (tevent_req_nomem(state->reader_req, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + state->reader_req, sock_packet_read_got_packet, req); + } + + state->socket_req = tdgram_recvfrom_send(state, ev, state->sock); + if (tevent_req_nomem(state->socket_req, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->socket_req, sock_packet_read_got_socket, + req); + + return req; +} + +static void sock_packet_read_got_packet(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct sock_packet_read_state *state = tevent_req_data( + req, struct sock_packet_read_state); + NTSTATUS status; + + status = nb_packet_read_recv(subreq, state, &state->packet); + + TALLOC_FREE(state->reader_req); + + if (!NT_STATUS_IS_OK(status)) { + if (state->socket_req != NULL) { + /* + * Still waiting for socket + */ + return; + } + /* + * Both socket and packet reader failed + */ + tevent_req_nterror(req, status); + return; + } + + if ((state->validator != NULL) && + !state->validator(state->packet, state->private_data)) { + DEBUG(10, ("validator failed\n")); + + TALLOC_FREE(state->packet); + + state->reader_req = nb_packet_read_send(state, state->ev, + state->reader); + if (tevent_req_nomem(state->reader_req, req)) { + return; + } + tevent_req_set_callback( + state->reader_req, sock_packet_read_got_packet, req); + return; + } + + TALLOC_FREE(state->socket_req); + tevent_req_done(req); +} + +static void sock_packet_read_got_socket(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct sock_packet_read_state *state = tevent_req_data( + req, struct sock_packet_read_state); + struct samba_sockaddr addr = {0}; + ssize_t ret; + ssize_t received; + int err; + bool ok; + + received = tdgram_recvfrom_recv(subreq, &err, state, + &state->buf, &state->addr); + + TALLOC_FREE(state->socket_req); + + if (received == -1) { + if (state->reader_req != NULL) { + /* + * Still waiting for reader + */ + return; + } + /* + * Both socket and reader failed + */ + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + ok = tsocket_address_is_inet(state->addr, "ipv4"); + if (!ok) { + goto retry; + } + ret = tsocket_address_bsd_sockaddr(state->addr, + &addr.u.sa, + sizeof(addr.u.in)); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return; + } + + state->packet = parse_packet_talloc( + state, (char *)state->buf, received, state->type, + addr.u.in.sin_addr, addr.u.in.sin_port); + if (state->packet == NULL) { + DEBUG(10, ("parse_packet failed\n")); + goto retry; + } + if ((state->trn_id != -1) && + (state->trn_id != packet_trn_id(state->packet))) { + DEBUG(10, ("Expected transaction id %d, got %d\n", + state->trn_id, packet_trn_id(state->packet))); + goto retry; + } + + if ((state->validator != NULL) && + !state->validator(state->packet, state->private_data)) { + DEBUG(10, ("validator failed\n")); + goto retry; + } + + tevent_req_done(req); + return; + +retry: + TALLOC_FREE(state->packet); + TALLOC_FREE(state->buf); + TALLOC_FREE(state->addr); + + state->socket_req = tdgram_recvfrom_send(state, state->ev, state->sock); + if (tevent_req_nomem(state->socket_req, req)) { + return; + } + tevent_req_set_callback(state->socket_req, sock_packet_read_got_socket, + req); +} + +static NTSTATUS sock_packet_read_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct packet_struct **ppacket) +{ + struct sock_packet_read_state *state = tevent_req_data( + req, struct sock_packet_read_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *ppacket = talloc_move(mem_ctx, &state->packet); + return NT_STATUS_OK; +} + +struct nb_trans_state { + struct tevent_context *ev; + struct tdgram_context *sock; + struct nb_packet_reader *reader; + + struct tsocket_address *src_addr; + struct tsocket_address *dst_addr; + uint8_t *buf; + size_t buflen; + enum packet_type type; + int trn_id; + + bool (*validator)(struct packet_struct *p, + void *private_data); + void *private_data; + + struct packet_struct *packet; +}; + +static void nb_trans_got_reader(struct tevent_req *subreq); +static void nb_trans_done(struct tevent_req *subreq); +static void nb_trans_sent(struct tevent_req *subreq); +static void nb_trans_send_next(struct tevent_req *subreq); + +static struct tevent_req *nb_trans_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct samba_sockaddr *_my_addr, + const struct samba_sockaddr *_dst_addr, + bool bcast, + uint8_t *buf, size_t buflen, + enum packet_type type, int trn_id, + bool (*validator)(struct packet_struct *p, + void *private_data), + void *private_data) +{ + const struct sockaddr *my_addr = &_my_addr->u.sa; + size_t my_addr_len = sizeof(_my_addr->u.in); /*We know it's AF_INET.*/ + const struct sockaddr *dst_addr = &_dst_addr->u.sa; + size_t dst_addr_len = sizeof(_dst_addr->u.in); /*We know it's AF_INET.*/ + struct tevent_req *req, *subreq; + struct nb_trans_state *state; + int ret; + + req = tevent_req_create(mem_ctx, &state, struct nb_trans_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->buf = buf; + state->buflen = buflen; + state->type = type; + state->trn_id = trn_id; + state->validator = validator; + state->private_data = private_data; + + ret = tsocket_address_bsd_from_sockaddr(state, + my_addr, my_addr_len, + &state->src_addr); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + + ret = tsocket_address_bsd_from_sockaddr(state, + dst_addr, dst_addr_len, + &state->dst_addr); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + + ret = tdgram_inet_udp_broadcast_socket(state->src_addr, state, + &state->sock); + if (ret == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + + subreq = nb_packet_reader_send(state, ev, type, state->trn_id, NULL); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, nb_trans_got_reader, req); + return req; +} + +static void nb_trans_got_reader(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_trans_state *state = tevent_req_data( + req, struct nb_trans_state); + NTSTATUS status; + + status = nb_packet_reader_recv(subreq, state, &state->reader); + TALLOC_FREE(subreq); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("nmbd not around\n")); + state->reader = NULL; + } + + subreq = sock_packet_read_send( + state, state->ev, state->sock, + state->reader, state->type, state->trn_id, + state->validator, state->private_data); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_trans_done, req); + + subreq = tdgram_sendto_send(state, state->ev, + state->sock, + state->buf, state->buflen, + state->dst_addr); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_trans_sent, req); +} + +static void nb_trans_sent(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_trans_state *state = tevent_req_data( + req, struct nb_trans_state); + ssize_t sent; + int err; + + sent = tdgram_sendto_recv(subreq, &err); + TALLOC_FREE(subreq); + if (sent == -1) { + DEBUG(10, ("sendto failed: %s\n", strerror(err))); + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + subreq = tevent_wakeup_send(state, state->ev, + timeval_current_ofs(1, 0)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_trans_send_next, req); +} + +static void nb_trans_send_next(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_trans_state *state = tevent_req_data( + req, struct nb_trans_state); + bool ret; + + ret = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ret) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + subreq = tdgram_sendto_send(state, state->ev, + state->sock, + state->buf, state->buflen, + state->dst_addr); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_trans_sent, req); +} + +static void nb_trans_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_trans_state *state = tevent_req_data( + req, struct nb_trans_state); + NTSTATUS status; + + status = sock_packet_read_recv(subreq, state, &state->packet); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS nb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct packet_struct **ppacket) +{ + struct nb_trans_state *state = tevent_req_data( + req, struct nb_trans_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *ppacket = talloc_move(mem_ctx, &state->packet); + return NT_STATUS_OK; +} + +/**************************************************************************** + Do a NBT node status query on an open socket and return an array of + structures holding the returned names or NULL if the query failed. +**************************************************************************/ + +struct node_status_query_state { + struct samba_sockaddr my_addr; + struct samba_sockaddr addr; + uint8_t buf[1024]; + ssize_t buflen; + struct packet_struct *packet; +}; + +static bool node_status_query_validator(struct packet_struct *p, + void *private_data); +static void node_status_query_done(struct tevent_req *subreq); + +struct tevent_req *node_status_query_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nmb_name *name, + const struct sockaddr_storage *addr) +{ + struct tevent_req *req, *subreq; + struct node_status_query_state *state; + struct packet_struct p; + struct nmb_packet *nmb = &p.packet.nmb; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct node_status_query_state); + if (req == NULL) { + return NULL; + } + + if (addr->ss_family != AF_INET) { + /* Can't do node status to IPv6 */ + tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS); + return tevent_req_post(req, ev); + } + + ok = sockaddr_storage_to_samba_sockaddr(&state->addr, addr); + if (!ok) { + /* node status must be IPv4 */ + tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS); + return tevent_req_post(req, ev); + } + state->addr.u.in.sin_port = htons(NMB_PORT); + + set_socket_addr_v4(&state->my_addr); + + ZERO_STRUCT(p); + nmb->header.name_trn_id = generate_trn_id(); + nmb->header.opcode = 0; + nmb->header.response = false; + nmb->header.nm_flags.bcast = false; + nmb->header.nm_flags.recursion_available = false; + nmb->header.nm_flags.recursion_desired = false; + nmb->header.nm_flags.trunc = false; + nmb->header.nm_flags.authoritative = false; + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + nmb->question.question_name = *name; + nmb->question.question_type = 0x21; + nmb->question.question_class = 0x1; + + state->buflen = build_packet((char *)state->buf, sizeof(state->buf), + &p); + if (state->buflen == 0) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + DEBUG(10, ("build_packet failed\n")); + return tevent_req_post(req, ev); + } + + subreq = nb_trans_send(state, + ev, + &state->my_addr, + &state->addr, + false, + state->buf, + state->buflen, + NMB_PACKET, + nmb->header.name_trn_id, + node_status_query_validator, + NULL); + if (tevent_req_nomem(subreq, req)) { + DEBUG(10, ("nb_trans_send failed\n")); + return tevent_req_post(req, ev); + } + if (!tevent_req_set_endtime(req, ev, timeval_current_ofs(10, 0))) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, node_status_query_done, req); + return req; +} + +static bool node_status_query_validator(struct packet_struct *p, + void *private_data) +{ + struct nmb_packet *nmb = &p->packet.nmb; + debug_nmb_packet(p); + + if (nmb->header.opcode != 0 || + nmb->header.nm_flags.bcast || + nmb->header.rcode || + !nmb->header.ancount || + nmb->answers->rr_type != 0x21) { + /* + * XXXX what do we do with this? could be a redirect, + * but we'll discard it for the moment + */ + return false; + } + return true; +} + +static void node_status_query_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct node_status_query_state *state = tevent_req_data( + req, struct node_status_query_state); + NTSTATUS status; + + status = nb_trans_recv(subreq, state, &state->packet); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS node_status_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct node_status **pnode_status, + size_t *pnum_names, + struct node_status_extra *extra) +{ + struct node_status_query_state *state = tevent_req_data( + req, struct node_status_query_state); + struct node_status *node_status; + size_t num_names = 0; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + node_status = parse_node_status( + mem_ctx, &state->packet->packet.nmb.answers->rdata[0], + &num_names, extra); + if (node_status == NULL) { + return NT_STATUS_NO_MEMORY; + } + *pnode_status = node_status; + *pnum_names = num_names; + return NT_STATUS_OK; +} + +NTSTATUS node_status_query(TALLOC_CTX *mem_ctx, struct nmb_name *name, + const struct sockaddr_storage *addr, + struct node_status **pnode_status, + size_t *pnum_names, + struct node_status_extra *extra) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = node_status_query_send(ev, ev, name, addr); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = node_status_query_recv(req, mem_ctx, pnode_status, + pnum_names, extra); + fail: + TALLOC_FREE(frame); + return status; +} + +static bool name_status_lmhosts(const struct sockaddr_storage *paddr, + int qname_type, fstring pname) +{ + FILE *f; + char *name; + int name_type; + struct samba_sockaddr addr_in = {0}; + struct samba_sockaddr addr = {0}; + bool ok; + + ok = sockaddr_storage_to_samba_sockaddr(&addr_in, paddr); + if (!ok) { + return false; + } + if (addr_in.u.ss.ss_family != AF_INET) { + return false; + } + + f = startlmhosts(get_dyn_LMHOSTSFILE()); + if (f == NULL) { + return false; + } + + while (getlmhostsent(talloc_tos(), f, &name, &name_type, &addr.u.ss)) { + if (addr.u.ss.ss_family != AF_INET) { + continue; + } + if (name_type != qname_type) { + continue; + } + if (sockaddr_equal(&addr_in.u.sa, &addr.u.sa)) { + fstrcpy(pname, name); + endlmhosts(f); + return true; + } + } + endlmhosts(f); + return false; +} + +/**************************************************************************** + Find the first type XX name in a node status reply - used for finding + a servers name given its IP. Return the matched name in *name. +**************************************************************************/ + +bool name_status_find(const char *q_name, + int q_type, + int type, + const struct sockaddr_storage *to_ss, + fstring name) +{ + char addr[INET6_ADDRSTRLEN]; + struct node_status *addrs = NULL; + struct nmb_name nname; + size_t count = 0, i; + bool result = false; + NTSTATUS status; + + if (lp_disable_netbios()) { + DEBUG(5,("name_status_find(%s#%02x): netbios is disabled\n", + q_name, q_type)); + return False; + } + + print_sockaddr(addr, sizeof(addr), to_ss); + + DEBUG(10, ("name_status_find: looking up %s#%02x at %s\n", q_name, + q_type, addr)); + + /* Check the cache first. */ + + if (namecache_status_fetch(q_name, q_type, type, to_ss, name)) { + return True; + } + + if (to_ss->ss_family != AF_INET) { + /* Can't do node status to IPv6 */ + return false; + } + + result = name_status_lmhosts(to_ss, type, name); + if (result) { + DBG_DEBUG("Found name %s in lmhosts\n", name); + namecache_status_store(q_name, q_type, type, to_ss, name); + return true; + } + + /* W2K PDC's seem not to respond to '*'#0. JRA */ + make_nmb_name(&nname, q_name, q_type); + status = node_status_query(talloc_tos(), &nname, to_ss, + &addrs, &count, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + for (i=0;i<count;i++) { + /* Find first one of the requested type that's not a GROUP. */ + if (addrs[i].type == type && ! (addrs[i].flags & 0x80)) + break; + } + if (i == count) + goto done; + + pull_ascii_nstring(name, sizeof(fstring), addrs[i].name); + + /* Store the result in the cache. */ + /* but don't store an entry for 0x1c names here. Here we have + a single host and DOMAIN<0x1c> names should be a list of hosts */ + + if ( q_type != 0x1c ) { + namecache_status_store(q_name, q_type, type, to_ss, name); + } + + result = true; + + done: + TALLOC_FREE(addrs); + + DEBUG(10, ("name_status_find: name %sfound", result ? "" : "not ")); + + if (result) + DEBUGADD(10, (", name %s ip address is %s", name, addr)); + + DEBUG(10, ("\n")); + + return result; +} + +/* + comparison function used by sort_addr_list +*/ + +static int addr_compare(const struct sockaddr_storage *ss1, + const struct sockaddr_storage *ss2) +{ + int max_bits1=0, max_bits2=0; + int num_interfaces = iface_count(); + int i; + struct samba_sockaddr sa1; + struct samba_sockaddr sa2; + bool ok; + + ok = sockaddr_storage_to_samba_sockaddr(&sa1, ss1); + if (!ok) { + return 0; /* No change. */ + } + + ok = sockaddr_storage_to_samba_sockaddr(&sa2, ss2); + if (!ok) { + return 0; /* No change. */ + } + + /* Sort IPv4 addresses first. */ + if (sa1.u.ss.ss_family != sa2.u.ss.ss_family) { + if (sa2.u.ss.ss_family == AF_INET) { + return 1; + } else { + return -1; + } + } + + /* Here we know both addresses are of the same + * family. */ + + for (i=0;i<num_interfaces;i++) { + struct samba_sockaddr sif = {0}; + const unsigned char *p_ss1 = NULL; + const unsigned char *p_ss2 = NULL; + const unsigned char *p_if = NULL; + size_t len = 0; + int bits1, bits2; + + ok = sockaddr_storage_to_samba_sockaddr(&sif, iface_n_bcast(i)); + if (!ok) { + return 0; /* No change. */ + } + if (sif.u.ss.ss_family != sa1.u.ss.ss_family) { + /* Ignore interfaces of the wrong type. */ + continue; + } + if (sif.u.ss.ss_family == AF_INET) { + p_if = (const unsigned char *)&sif.u.in.sin_addr; + p_ss1 = (const unsigned char *)&sa1.u.in.sin_addr; + p_ss2 = (const unsigned char *)&sa2.u.in.sin_addr; + len = 4; + } +#if defined(HAVE_IPV6) + if (sif.u.ss.ss_family == AF_INET6) { + p_if = (const unsigned char *)&sif.u.in6.sin6_addr; + p_ss1 = (const unsigned char *)&sa1.u.in6.sin6_addr; + p_ss2 = (const unsigned char *)&sa2.u.in6.sin6_addr; + len = 16; + } +#endif + if (!p_ss1 || !p_ss2 || !p_if || len == 0) { + continue; + } + bits1 = matching_len_bits(p_ss1, p_if, len); + bits2 = matching_len_bits(p_ss2, p_if, len); + max_bits1 = MAX(bits1, max_bits1); + max_bits2 = MAX(bits2, max_bits2); + } + + /* Bias towards directly reachable IPs */ + if (iface_local(&sa1.u.sa)) { + if (sa1.u.ss.ss_family == AF_INET) { + max_bits1 += 32; + } else { + max_bits1 += 128; + } + } + if (iface_local(&sa2.u.sa)) { + if (sa2.u.ss.ss_family == AF_INET) { + max_bits2 += 32; + } else { + max_bits2 += 128; + } + } + return max_bits2 - max_bits1; +} + +/* + sort an IP list so that names that are close to one of our interfaces + are at the top. This prevents the problem where a WINS server returns an IP + that is not reachable from our subnet as the first match +*/ + +static void sort_addr_list(struct sockaddr_storage *sslist, size_t count) +{ + if (count <= 1) { + return; + } + + TYPESAFE_QSORT(sslist, count, addr_compare); +} + +static int samba_sockaddr_compare(struct samba_sockaddr *sa1, + struct samba_sockaddr *sa2) +{ + return addr_compare(&sa1->u.ss, &sa2->u.ss); +} + +static void sort_sa_list(struct samba_sockaddr *salist, size_t count) +{ + if (count <= 1) { + return; + } + + TYPESAFE_QSORT(salist, count, samba_sockaddr_compare); +} + +/********************************************************************** + Remove any duplicate address/port pairs in the samba_sockaddr array. + *********************************************************************/ + +size_t remove_duplicate_addrs2(struct samba_sockaddr *salist, size_t count ) +{ + size_t i, j; + + DBG_DEBUG("looking for duplicate address/port pairs\n"); + + /* One loop to set duplicates to a zero addr. */ + for (i=0; i < count; i++) { + if (is_zero_addr(&salist[i].u.ss)) { + continue; + } + + for (j=i+1; j<count; j++) { + if (sockaddr_equal(&salist[i].u.sa, &salist[j].u.sa)) { + zero_sockaddr(&salist[j].u.ss); + } + } + } + + /* Now remove any addresses set to zero above. */ + for (i = 0; i < count; i++) { + while (i < count && + is_zero_addr(&salist[i].u.ss)) { + ARRAY_DEL_ELEMENT(salist, i, count); + count--; + } + } + + return count; +} + +static bool prioritize_ipv4_list(struct samba_sockaddr *salist, size_t count) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct samba_sockaddr *salist_new = talloc_array(frame, + struct samba_sockaddr, + count); + size_t i, j; + + if (salist_new == NULL) { + TALLOC_FREE(frame); + return false; + } + + j = 0; + + /* Copy IPv4 first. */ + for (i = 0; i < count; i++) { + if (salist[i].u.ss.ss_family == AF_INET) { + salist_new[j++] = salist[i]; + } + } + + /* Copy IPv6. */ + for (i = 0; i < count; i++) { + if (salist[i].u.ss.ss_family != AF_INET) { + salist_new[j++] = salist[i]; + } + } + + memcpy(salist, salist_new, sizeof(struct samba_sockaddr)*count); + TALLOC_FREE(frame); + return true; +} + +/**************************************************************************** + Do a netbios name query to find someones IP. + Returns an array of IP addresses or NULL if none. + *count will be set to the number of addresses returned. + *timed_out is set if we failed by timing out +****************************************************************************/ + +struct name_query_state { + struct samba_sockaddr my_addr; + struct samba_sockaddr addr; + bool bcast; + bool bcast_star_query; + + + uint8_t buf[1024]; + ssize_t buflen; + + NTSTATUS validate_error; + uint8_t flags; + + struct sockaddr_storage *addrs; + size_t num_addrs; +}; + +static bool name_query_validator(struct packet_struct *p, void *private_data); +static void name_query_done(struct tevent_req *subreq); + +struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *name, int name_type, + bool bcast, bool recurse, + const struct sockaddr_storage *addr) +{ + struct tevent_req *req, *subreq; + struct name_query_state *state; + struct packet_struct p; + struct nmb_packet *nmb = &p.packet.nmb; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct name_query_state); + if (req == NULL) { + return NULL; + } + state->bcast = bcast; + + if (addr->ss_family != AF_INET) { + /* Can't do node status to IPv6 */ + tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS); + return tevent_req_post(req, ev); + } + + if (lp_disable_netbios()) { + DEBUG(5,("name_query(%s#%02x): netbios is disabled\n", + name, name_type)); + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + ok = sockaddr_storage_to_samba_sockaddr(&state->addr, addr); + if (!ok) { + /* Node status must be IPv4 */ + tevent_req_nterror(req, NT_STATUS_INVALID_ADDRESS); + return tevent_req_post(req, ev); + } + state->addr.u.in.sin_port = htons(NMB_PORT); + + set_socket_addr_v4(&state->my_addr); + + ZERO_STRUCT(p); + nmb->header.name_trn_id = generate_trn_id(); + nmb->header.opcode = 0; + nmb->header.response = false; + nmb->header.nm_flags.bcast = bcast; + nmb->header.nm_flags.recursion_available = false; + nmb->header.nm_flags.recursion_desired = recurse; + nmb->header.nm_flags.trunc = false; + nmb->header.nm_flags.authoritative = false; + nmb->header.rcode = 0; + nmb->header.qdcount = 1; + nmb->header.ancount = 0; + nmb->header.nscount = 0; + nmb->header.arcount = 0; + + if (bcast && (strcmp(name, "*")==0)) { + /* + * We're doing a broadcast query for all + * names in the area. Remember this so + * we will wait for all names within + * the timeout period. + */ + state->bcast_star_query = true; + } + + make_nmb_name(&nmb->question.question_name,name,name_type); + + nmb->question.question_type = 0x20; + nmb->question.question_class = 0x1; + + state->buflen = build_packet((char *)state->buf, sizeof(state->buf), + &p); + if (state->buflen == 0) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + DEBUG(10, ("build_packet failed\n")); + return tevent_req_post(req, ev); + } + + subreq = nb_trans_send(state, + ev, + &state->my_addr, + &state->addr, + bcast, + state->buf, + state->buflen, + NMB_PACKET, + nmb->header.name_trn_id, + name_query_validator, + state); + if (tevent_req_nomem(subreq, req)) { + DEBUG(10, ("nb_trans_send failed\n")); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, name_query_done, req); + return req; +} + +static bool name_query_validator(struct packet_struct *p, void *private_data) +{ + struct name_query_state *state = talloc_get_type_abort( + private_data, struct name_query_state); + struct nmb_packet *nmb = &p->packet.nmb; + struct sockaddr_storage *tmp_addrs; + bool got_unique_netbios_name = false; + int i; + + debug_nmb_packet(p); + + /* + * If we get a Negative Name Query Response from a WINS + * server, we should report it and give up. + */ + if( 0 == nmb->header.opcode /* A query response */ + && !state->bcast /* from a WINS server */ + && nmb->header.rcode /* Error returned */ + ) { + + if( DEBUGLVL( 3 ) ) { + /* Only executed if DEBUGLEVEL >= 3 */ + dbgtext( "Negative name query " + "response, rcode 0x%02x: ", + nmb->header.rcode ); + switch( nmb->header.rcode ) { + case 0x01: + dbgtext("Request was invalidly formatted.\n"); + break; + case 0x02: + dbgtext("Problem with NBNS, cannot process " + "name.\n"); + break; + case 0x03: + dbgtext("The name requested does not " + "exist.\n"); + break; + case 0x04: + dbgtext("Unsupported request error.\n"); + break; + case 0x05: + dbgtext("Query refused error.\n"); + break; + default: + dbgtext("Unrecognized error code.\n" ); + break; + } + } + + /* + * We accept this packet as valid, but tell the upper + * layers that it's a negative response. + */ + state->validate_error = NT_STATUS_NOT_FOUND; + return true; + } + + if (nmb->header.opcode != 0 || + nmb->header.nm_flags.bcast || + nmb->header.rcode || + !nmb->header.ancount) { + /* + * XXXX what do we do with this? Could be a redirect, + * but we'll discard it for the moment. + */ + return false; + } + + tmp_addrs = talloc_realloc( + state, state->addrs, struct sockaddr_storage, + state->num_addrs + nmb->answers->rdlength/6); + if (tmp_addrs == NULL) { + state->validate_error = NT_STATUS_NO_MEMORY; + return true; + } + state->addrs = tmp_addrs; + + DEBUG(2,("Got a positive name query response " + "from %s ( ", inet_ntoa(p->ip))); + + for (i=0; i<nmb->answers->rdlength/6; i++) { + uint16_t flags; + struct in_addr ip; + struct sockaddr_storage addr; + struct samba_sockaddr sa = {0}; + bool ok; + size_t j; + + flags = RSVAL(&nmb->answers->rdata[i*6], 0); + got_unique_netbios_name |= ((flags & 0x8000) == 0); + + putip((char *)&ip,&nmb->answers->rdata[2+i*6]); + in_addr_to_sockaddr_storage(&addr, ip); + + ok = sockaddr_storage_to_samba_sockaddr(&sa, &addr); + if (!ok) { + continue; + } + + if (is_zero_addr(&sa.u.ss)) { + continue; + } + + for (j=0; j<state->num_addrs; j++) { + struct samba_sockaddr sa_j = {0}; + + ok = sockaddr_storage_to_samba_sockaddr(&sa_j, + &state->addrs[j]); + if (!ok) { + continue; + } + if (sockaddr_equal(&sa.u.sa, &sa_j.u.sa)) { + break; + } + } + if (j < state->num_addrs) { + /* Already got it */ + continue; + } + + DEBUGADD(2,("%s ",inet_ntoa(ip))); + + state->addrs[state->num_addrs] = addr; + /* wrap check. */ + if (state->num_addrs + 1 < state->num_addrs) { + return false; + } + state->num_addrs += 1; + } + DEBUGADD(2,(")\n")); + + /* We add the flags back ... */ + if (nmb->header.response) + state->flags |= NM_FLAGS_RS; + if (nmb->header.nm_flags.authoritative) + state->flags |= NM_FLAGS_AA; + if (nmb->header.nm_flags.trunc) + state->flags |= NM_FLAGS_TC; + if (nmb->header.nm_flags.recursion_desired) + state->flags |= NM_FLAGS_RD; + if (nmb->header.nm_flags.recursion_available) + state->flags |= NM_FLAGS_RA; + if (nmb->header.nm_flags.bcast) + state->flags |= NM_FLAGS_B; + + if (state->bcast) { + /* + * We have to collect all entries coming in from broadcast + * queries. If we got a unique name and we are not querying + * all names registered within broadcast area (query + * for the name '*', so state->bcast_star_query is set), + * we're done. + */ + return (got_unique_netbios_name && !state->bcast_star_query); + } + /* + * WINS responses are accepted when they are received + */ + return true; +} + +static void name_query_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct name_query_state *state = tevent_req_data( + req, struct name_query_state); + NTSTATUS status; + struct packet_struct *p = NULL; + + status = nb_trans_recv(subreq, state, &p); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + if (!NT_STATUS_IS_OK(state->validate_error)) { + tevent_req_nterror(req, state->validate_error); + return; + } + tevent_req_done(req); +} + +NTSTATUS name_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, size_t *num_addrs, + uint8_t *flags) +{ + struct name_query_state *state = tevent_req_data( + req, struct name_query_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + if (state->bcast && + NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + /* + * In the broadcast case we collect replies until the + * timeout. + */ + status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + if (state->num_addrs == 0) { + return NT_STATUS_NOT_FOUND; + } + *addrs = talloc_move(mem_ctx, &state->addrs); + sort_addr_list(*addrs, state->num_addrs); + *num_addrs = state->num_addrs; + if (flags != NULL) { + *flags = state->flags; + } + return NT_STATUS_OK; +} + +NTSTATUS name_query(const char *name, int name_type, + bool bcast, bool recurse, + const struct sockaddr_storage *to_ss, + TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs, uint8_t *flags) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + struct timeval timeout; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = name_query_send(ev, ev, name, name_type, bcast, recurse, to_ss); + if (req == NULL) { + goto fail; + } + if (bcast) { + timeout = timeval_current_ofs(0, 250000); + } else { + timeout = timeval_current_ofs(2, 0); + } + if (!tevent_req_set_endtime(req, ev, timeout)) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = name_query_recv(req, mem_ctx, addrs, num_addrs, flags); + fail: + TALLOC_FREE(frame); + return status; +} + +struct name_queries_state { + struct tevent_context *ev; + const char *name; + int name_type; + bool bcast; + bool recurse; + const struct sockaddr_storage *addrs; + size_t num_addrs; + int wait_msec; + int timeout_msec; + + struct tevent_req **subreqs; + size_t num_received; + size_t num_sent; + + size_t received_index; + struct sockaddr_storage *result_addrs; + size_t num_result_addrs; + uint8_t flags; +}; + +static void name_queries_done(struct tevent_req *subreq); +static void name_queries_next(struct tevent_req *subreq); + +/* + * Send a name query to multiple destinations with a wait time in between + */ + +static struct tevent_req *name_queries_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *name, int name_type, + bool bcast, bool recurse, + const struct sockaddr_storage *addrs, + size_t num_addrs, int wait_msec, int timeout_msec) +{ + struct tevent_req *req, *subreq; + struct name_queries_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct name_queries_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->name = name; + state->name_type = name_type; + state->bcast = bcast; + state->recurse = recurse; + state->addrs = addrs; + state->num_addrs = num_addrs; + state->wait_msec = wait_msec; + state->timeout_msec = timeout_msec; + + state->subreqs = talloc_zero_array( + state, struct tevent_req *, num_addrs); + if (tevent_req_nomem(state->subreqs, req)) { + return tevent_req_post(req, ev); + } + state->num_sent = 0; + + subreq = name_query_send( + state->subreqs, state->ev, name, name_type, bcast, recurse, + &state->addrs[state->num_sent]); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + if (!tevent_req_set_endtime( + subreq, state->ev, + timeval_current_ofs(0, state->timeout_msec * 1000))) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, name_queries_done, req); + + state->subreqs[state->num_sent] = subreq; + state->num_sent += 1; + + if (state->num_sent < state->num_addrs) { + subreq = tevent_wakeup_send( + state, state->ev, + timeval_current_ofs(0, state->wait_msec * 1000)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, name_queries_next, req); + } + return req; +} + +static void name_queries_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct name_queries_state *state = tevent_req_data( + req, struct name_queries_state); + size_t i; + NTSTATUS status; + + status = name_query_recv(subreq, state, &state->result_addrs, + &state->num_result_addrs, &state->flags); + + for (i=0; i<state->num_sent; i++) { + if (state->subreqs[i] == subreq) { + break; + } + } + if (i == state->num_sent) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + TALLOC_FREE(state->subreqs[i]); + + /* wrap check. */ + if (state->num_received + 1 < state->num_received) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + state->num_received += 1; + + if (!NT_STATUS_IS_OK(status)) { + + if (state->num_received >= state->num_addrs) { + tevent_req_nterror(req, status); + return; + } + /* + * Still outstanding requests, just wait + */ + return; + } + state->received_index = i; + tevent_req_done(req); +} + +static void name_queries_next(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct name_queries_state *state = tevent_req_data( + req, struct name_queries_state); + + if (!tevent_wakeup_recv(subreq)) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + subreq = name_query_send( + state->subreqs, state->ev, + state->name, state->name_type, state->bcast, state->recurse, + &state->addrs[state->num_sent]); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, name_queries_done, req); + if (!tevent_req_set_endtime( + subreq, state->ev, + timeval_current_ofs(0, state->timeout_msec * 1000))) { + return; + } + state->subreqs[state->num_sent] = subreq; + state->num_sent += 1; + + if (state->num_sent < state->num_addrs) { + subreq = tevent_wakeup_send( + state, state->ev, + timeval_current_ofs(0, state->wait_msec * 1000)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, name_queries_next, req); + } +} + +static NTSTATUS name_queries_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **result_addrs, + size_t *num_result_addrs, uint8_t *flags, + size_t *received_index) +{ + struct name_queries_state *state = tevent_req_data( + req, struct name_queries_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (result_addrs != NULL) { + *result_addrs = talloc_move(mem_ctx, &state->result_addrs); + } + if (num_result_addrs != NULL) { + *num_result_addrs = state->num_result_addrs; + } + if (flags != NULL) { + *flags = state->flags; + } + if (received_index != NULL) { + *received_index = state->received_index; + } + return NT_STATUS_OK; +} + +/******************************************************** + Resolve via "bcast" method. +*********************************************************/ + +struct name_resolve_bcast_state { + struct sockaddr_storage *addrs; + size_t num_addrs; +}; + +static void name_resolve_bcast_done(struct tevent_req *subreq); + +struct tevent_req *name_resolve_bcast_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *name, + int name_type) +{ + struct tevent_req *req, *subreq; + struct name_resolve_bcast_state *state; + struct sockaddr_storage *bcast_addrs; + size_t i, num_addrs, num_bcast_addrs; + + req = tevent_req_create(mem_ctx, &state, + struct name_resolve_bcast_state); + if (req == NULL) { + return NULL; + } + + if (lp_disable_netbios()) { + DEBUG(5, ("name_resolve_bcast(%s#%02x): netbios is disabled\n", + name, name_type)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + /* + * "bcast" means do a broadcast lookup on all the local interfaces. + */ + + DEBUG(3, ("name_resolve_bcast: Attempting broadcast lookup " + "for name %s<0x%x>\n", name, name_type)); + + num_addrs = iface_count(); + bcast_addrs = talloc_array(state, struct sockaddr_storage, num_addrs); + if (tevent_req_nomem(bcast_addrs, req)) { + return tevent_req_post(req, ev); + } + + /* + * Lookup the name on all the interfaces, return on + * the first successful match. + */ + num_bcast_addrs = 0; + + for (i=0; i<num_addrs; i++) { + const struct sockaddr_storage *pss = iface_n_bcast(i); + + if (pss->ss_family != AF_INET) { + continue; + } + bcast_addrs[num_bcast_addrs] = *pss; + num_bcast_addrs += 1; + } + + subreq = name_queries_send(state, ev, name, name_type, true, true, + bcast_addrs, num_bcast_addrs, 0, 250); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, name_resolve_bcast_done, req); + return req; +} + +static void name_resolve_bcast_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct name_resolve_bcast_state *state = tevent_req_data( + req, struct name_resolve_bcast_state); + NTSTATUS status; + + status = name_queries_recv(subreq, state, + &state->addrs, &state->num_addrs, + NULL, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +NTSTATUS name_resolve_bcast_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs) +{ + struct name_resolve_bcast_state *state = tevent_req_data( + req, struct name_resolve_bcast_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + *addrs = talloc_move(mem_ctx, &state->addrs); + *num_addrs = state->num_addrs; + return NT_STATUS_OK; +} + +NTSTATUS name_resolve_bcast(TALLOC_CTX *mem_ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = name_resolve_bcast_send(frame, ev, name, name_type); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = name_resolve_bcast_recv(req, mem_ctx, return_iplist, + return_count); + fail: + TALLOC_FREE(frame); + return status; +} + +struct query_wins_list_state { + struct tevent_context *ev; + const char *name; + uint8_t name_type; + struct in_addr *servers; + size_t num_servers; + struct sockaddr_storage server; + size_t num_sent; + + struct sockaddr_storage *addrs; + size_t num_addrs; + uint8_t flags; +}; + +static void query_wins_list_done(struct tevent_req *subreq); + +/* + * Query a list of (replicating) wins servers in sequence, call them + * dead if they don't reply + */ + +static struct tevent_req *query_wins_list_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct in_addr src_ip, const char *name, uint8_t name_type, + struct in_addr *servers, size_t num_servers) +{ + struct tevent_req *req, *subreq; + struct query_wins_list_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct query_wins_list_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->name = name; + state->name_type = name_type; + state->servers = servers; + state->num_servers = num_servers; + + if (state->num_servers == 0) { + tevent_req_nterror(req, NT_STATUS_NOT_FOUND); + return tevent_req_post(req, ev); + } + + in_addr_to_sockaddr_storage( + &state->server, state->servers[state->num_sent]); + + subreq = name_query_send(state, state->ev, + state->name, state->name_type, + false, true, &state->server); + + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + + /* wrap check */ + if (state->num_sent + 1 < state->num_sent) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->num_sent += 1; + if (!tevent_req_set_endtime(subreq, state->ev, + timeval_current_ofs(2, 0))) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, query_wins_list_done, req); + return req; +} + +static void query_wins_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct query_wins_list_state *state = tevent_req_data( + req, struct query_wins_list_state); + NTSTATUS status; + + status = name_query_recv(subreq, state, + &state->addrs, &state->num_addrs, + &state->flags); + TALLOC_FREE(subreq); + if (NT_STATUS_IS_OK(status)) { + tevent_req_done(req); + return; + } + if (!NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT)) { + tevent_req_nterror(req, status); + return; + } + wins_srv_died(state->servers[state->num_sent-1], + my_socket_addr_v4()); + + if (state->num_sent == state->num_servers) { + tevent_req_nterror(req, NT_STATUS_NOT_FOUND); + return; + } + + in_addr_to_sockaddr_storage( + &state->server, state->servers[state->num_sent]); + + subreq = name_query_send(state, state->ev, + state->name, state->name_type, + false, true, &state->server); + state->num_sent += 1; + if (tevent_req_nomem(subreq, req)) { + return; + } + if (!tevent_req_set_endtime(subreq, state->ev, + timeval_current_ofs(2, 0))) { + return; + } + tevent_req_set_callback(subreq, query_wins_list_done, req); +} + +static NTSTATUS query_wins_list_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs, + uint8_t *flags) +{ + struct query_wins_list_state *state = tevent_req_data( + req, struct query_wins_list_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (addrs != NULL) { + *addrs = talloc_move(mem_ctx, &state->addrs); + } + if (num_addrs != NULL) { + *num_addrs = state->num_addrs; + } + if (flags != NULL) { + *flags = state->flags; + } + return NT_STATUS_OK; +} + +struct resolve_wins_state { + size_t num_sent; + size_t num_received; + + struct sockaddr_storage *addrs; + size_t num_addrs; + uint8_t flags; +}; + +static void resolve_wins_done(struct tevent_req *subreq); + +struct tevent_req *resolve_wins_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *name, + int name_type) +{ + struct tevent_req *req, *subreq; + struct resolve_wins_state *state; + char **wins_tags = NULL; + struct sockaddr_storage src_ss; + struct samba_sockaddr src_sa = {0}; + struct in_addr src_ip; + size_t i, num_wins_tags; + bool ok; + + req = tevent_req_create(mem_ctx, &state, + struct resolve_wins_state); + if (req == NULL) { + return NULL; + } + + if (wins_srv_count() < 1) { + DEBUG(3,("resolve_wins: WINS server resolution selected " + "and no WINS servers listed.\n")); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + /* the address we will be sending from */ + if (!interpret_string_addr(&src_ss, lp_nbt_client_socket_address(), + AI_NUMERICHOST|AI_PASSIVE)) { + zero_sockaddr(&src_ss); + } + + ok = sockaddr_storage_to_samba_sockaddr(&src_sa, &src_ss); + if (!ok) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + if (src_sa.u.ss.ss_family != AF_INET) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), &src_sa.u.ss); + DEBUG(3,("resolve_wins: cannot receive WINS replies " + "on IPv6 address %s\n", + addr)); + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + src_ip = src_sa.u.in.sin_addr; + + wins_tags = wins_srv_tags(); + if (wins_tags == NULL) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + + num_wins_tags = 0; + while (wins_tags[num_wins_tags] != NULL) { + /* wrap check. */ + if (num_wins_tags + 1 < num_wins_tags) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + goto fail; + } + num_wins_tags += 1; + } + + for (i=0; i<num_wins_tags; i++) { + size_t num_servers, num_alive; + struct in_addr *servers, *alive; + size_t j; + + if (!wins_server_tag_ips(wins_tags[i], talloc_tos(), + &servers, &num_servers)) { + DEBUG(10, ("wins_server_tag_ips failed for tag %s\n", + wins_tags[i])); + continue; + } + + alive = talloc_array(state, struct in_addr, num_servers); + if (tevent_req_nomem(alive, req)) { + goto fail; + } + + num_alive = 0; + for (j=0; j<num_servers; j++) { + struct in_addr wins_ip = servers[j]; + + if (global_in_nmbd && ismyip_v4(wins_ip)) { + /* yikes! we'll loop forever */ + continue; + } + /* skip any that have been unresponsive lately */ + if (wins_srv_is_dead(wins_ip, src_ip)) { + continue; + } + DEBUG(3, ("resolve_wins: using WINS server %s " + "and tag '%s'\n", + inet_ntoa(wins_ip), wins_tags[i])); + alive[num_alive] = wins_ip; + num_alive += 1; + } + TALLOC_FREE(servers); + + if (num_alive == 0) { + continue; + } + + subreq = query_wins_list_send( + state, ev, src_ip, name, name_type, + alive, num_alive); + if (tevent_req_nomem(subreq, req)) { + goto fail; + } + tevent_req_set_callback(subreq, resolve_wins_done, req); + state->num_sent += 1; + } + + if (state->num_sent == 0) { + tevent_req_nterror(req, NT_STATUS_NOT_FOUND); + goto fail; + } + + wins_srv_tags_free(wins_tags); + return req; +fail: + wins_srv_tags_free(wins_tags); + return tevent_req_post(req, ev); +} + +static void resolve_wins_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct resolve_wins_state *state = tevent_req_data( + req, struct resolve_wins_state); + NTSTATUS status; + + status = query_wins_list_recv(subreq, state, &state->addrs, + &state->num_addrs, &state->flags); + if (NT_STATUS_IS_OK(status)) { + tevent_req_done(req); + return; + } + + /* wrap check. */ + if (state->num_received + 1 < state->num_received) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return; + } + + state->num_received += 1; + + if (state->num_received < state->num_sent) { + /* + * Wait for the others + */ + return; + } + tevent_req_nterror(req, status); +} + +NTSTATUS resolve_wins_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs, uint8_t *flags) +{ + struct resolve_wins_state *state = tevent_req_data( + req, struct resolve_wins_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (addrs != NULL) { + *addrs = talloc_move(mem_ctx, &state->addrs); + } + if (num_addrs != NULL) { + *num_addrs = state->num_addrs; + } + if (flags != NULL) { + *flags = state->flags; + } + return NT_STATUS_OK; +} + +/******************************************************** + Resolve via "wins" method. +*********************************************************/ + +NTSTATUS resolve_wins(TALLOC_CTX *mem_ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count) +{ + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(talloc_tos()); + if (ev == NULL) { + goto fail; + } + req = resolve_wins_send(ev, ev, name, name_type); + if (req == NULL) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = resolve_wins_recv(req, mem_ctx, return_iplist, return_count, + NULL); +fail: + TALLOC_FREE(ev); + return status; +} + + +/******************************************************** + Resolve via "hosts" method. +*********************************************************/ + +static NTSTATUS resolve_hosts(TALLOC_CTX *mem_ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count) +{ + /* + * "host" means do a localhost, or dns lookup. + */ + struct addrinfo hints; + struct addrinfo *ailist = NULL; + struct addrinfo *res = NULL; + int ret = -1; + size_t i = 0; + size_t ret_count = 0; + struct sockaddr_storage *iplist = NULL; + + if ( name_type != 0x20 && name_type != 0x0) { + DEBUG(5, ("resolve_hosts: not appropriate " + "for name type <0x%x>\n", + name_type)); + return NT_STATUS_INVALID_PARAMETER; + } + + DEBUG(3,("resolve_hosts: Attempting host lookup for name %s<0x%x>\n", + name, name_type)); + + ZERO_STRUCT(hints); + /* By default make sure it supports TCP. */ + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_ADDRCONFIG; + +#if !defined(HAVE_IPV6) + /* Unless we have IPv6, we really only want IPv4 addresses back. */ + hints.ai_family = AF_INET; +#endif + + ret = getaddrinfo(name, + NULL, + &hints, + &ailist); + if (ret) { + DEBUG(3,("resolve_hosts: getaddrinfo failed for name %s [%s]\n", + name, + gai_strerror(ret) )); + } + + for (res = ailist; res; res = res->ai_next) { + struct sockaddr_storage ss = {0}; + struct sockaddr_storage *tmp = NULL; + + if ((res->ai_addr == NULL) || + (res->ai_addrlen == 0) || + (res->ai_addrlen > sizeof(ss))) { + continue; + } + + memcpy(&ss, res->ai_addr, res->ai_addrlen); + + if (is_zero_addr(&ss)) { + continue; + } + + /* wrap check. */ + if (ret_count + 1 < ret_count) { + freeaddrinfo(ailist); + TALLOC_FREE(iplist); + return NT_STATUS_INVALID_PARAMETER; + } + ret_count += 1; + + tmp = talloc_realloc( + mem_ctx, iplist, struct sockaddr_storage, + ret_count); + if (tmp == NULL) { + DEBUG(3,("resolve_hosts: malloc fail !\n")); + freeaddrinfo(ailist); + TALLOC_FREE(iplist); + return NT_STATUS_NO_MEMORY; + } + iplist = tmp; + iplist[i] = ss; + i++; + } + if (ailist) { + freeaddrinfo(ailist); + } + if (ret_count == 0) { + return NT_STATUS_UNSUCCESSFUL; + } + *return_count = ret_count; + *return_iplist = iplist; + return NT_STATUS_OK; +} + +/******************************************************** + Resolve via "ADS" method. +*********************************************************/ + +/* Special name type used to cause a _kerberos DNS lookup. */ +#define KDC_NAME_TYPE 0xDCDC + +static NTSTATUS resolve_ads(TALLOC_CTX *ctx, + const char *name, + int name_type, + const char *sitename, + struct sockaddr_storage **return_addrs, + size_t *return_count) +{ + size_t i; + NTSTATUS status; + struct dns_rr_srv *dcs = NULL; + size_t numdcs = 0; + size_t num_srv_addrs = 0; + struct sockaddr_storage *srv_addrs = NULL; + char *query = NULL; + + if ((name_type != 0x1c) && (name_type != KDC_NAME_TYPE) && + (name_type != 0x1b)) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = NT_STATUS_OK; + + switch (name_type) { + case 0x1b: + DEBUG(5,("resolve_ads: Attempting to resolve " + "PDC for %s using DNS\n", name)); + query = ads_dns_query_string_pdc(ctx, name); + break; + + case 0x1c: + DEBUG(5,("resolve_ads: Attempting to resolve " + "DCs for %s using DNS\n", name)); + query = ads_dns_query_string_dcs(ctx, name); + break; + case KDC_NAME_TYPE: + DEBUG(5,("resolve_ads: Attempting to resolve " + "KDCs for %s using DNS\n", name)); + query = ads_dns_query_string_kdcs(ctx, name); + break; + default: + status = NT_STATUS_INVALID_PARAMETER; + break; + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (query == NULL) { + return NT_STATUS_NO_MEMORY; + } + + DBG_DEBUG("SRV query for %s\n", query); + + status = ads_dns_query_srv( + ctx, + lp_get_async_dns_timeout(), + sitename, + query, + &dcs, + &numdcs); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (numdcs == 0) { + *return_addrs = NULL; + *return_count = 0; + TALLOC_FREE(dcs); + return NT_STATUS_OK; + } + + /* First count the sizes of each array. */ + for(i = 0; i < numdcs; i++) { + if (dcs[i].ss_s == NULL) { + /* + * Nothing received or timeout in A/AAAA reqs + */ + continue; + } + + if (num_srv_addrs + dcs[i].num_ips < num_srv_addrs) { + /* Wrap check. */ + TALLOC_FREE(dcs); + return NT_STATUS_INVALID_PARAMETER; + } + /* Add in the number of addresses we got. */ + num_srv_addrs += dcs[i].num_ips; + } + + /* Allocate the list of IP addresses we already have. */ + srv_addrs = talloc_zero_array(ctx, + struct sockaddr_storage, + num_srv_addrs); + if (srv_addrs == NULL) { + TALLOC_FREE(dcs); + return NT_STATUS_NO_MEMORY; + } + + num_srv_addrs = 0; + for(i = 0; i < numdcs; i++) { + /* Copy all the IP addresses from the SRV response */ + size_t j; + for (j = 0; j < dcs[i].num_ips; j++) { + char addr[INET6_ADDRSTRLEN]; + + srv_addrs[num_srv_addrs] = dcs[i].ss_s[j]; + if (is_zero_addr(&srv_addrs[num_srv_addrs])) { + continue; + } + + DBG_DEBUG("SRV lookup %s got IP[%zu] %s\n", + name, + j, + print_sockaddr(addr, + sizeof(addr), + &srv_addrs[num_srv_addrs])); + + num_srv_addrs++; + } + } + + TALLOC_FREE(dcs); + + *return_addrs = srv_addrs; + *return_count = num_srv_addrs; + return NT_STATUS_OK; +} + +static const char **filter_out_nbt_lookup(TALLOC_CTX *mem_ctx, + const char **resolve_order) +{ + size_t i, len, result_idx; + const char **result; + + len = 0; + while (resolve_order[len] != NULL) { + len += 1; + } + + result = talloc_array(mem_ctx, const char *, len+1); + if (result == NULL) { + return NULL; + } + + result_idx = 0; + + for (i=0; i<len; i++) { + const char *tok = resolve_order[i]; + + if (strequal(tok, "lmhosts") || strequal(tok, "wins") || + strequal(tok, "bcast")) { + continue; + } + result[result_idx++] = tok; + } + result[result_idx] = NULL; + + return result; +} + +/******************************************************************* + Samba interface to resolve a name into an IP address. + Use this function if the string is either an IP address, DNS + or host name or NetBIOS name. This uses the name switch in the + smb.conf to determine the order of name resolution. + + Added support for ip addr/port to support ADS ldap servers. + the only place we currently care about the port is in the + resolve_hosts() when looking up DC's via SRV RR entries in DNS +**********************************************************************/ + +NTSTATUS internal_resolve_name(TALLOC_CTX *ctx, + const char *name, + int name_type, + const char *sitename, + struct samba_sockaddr **return_salist, + size_t *return_count, + const char **resolve_order) +{ + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + size_t i; + size_t nc_count = 0; + size_t ret_count = 0; + bool ok; + struct sockaddr_storage *ss_list = NULL; + struct samba_sockaddr *sa_list = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + DBG_DEBUG("looking up %s#%x (sitename %s)\n", + name, name_type, sitename ? sitename : "(null)"); + + if (is_ipaddress(name)) { + struct sockaddr_storage ss; + + /* if it's in the form of an IP address then get the lib to interpret it */ + ok = interpret_string_addr(&ss, name, AI_NUMERICHOST); + if (!ok) { + DBG_WARNING("interpret_string_addr failed on %s\n", + name); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_PARAMETER; + } + if (is_zero_addr(&ss)) { + TALLOC_FREE(frame); + return NT_STATUS_UNSUCCESSFUL; + } + + status = sockaddr_array_to_samba_sockaddr_array(frame, + &sa_list, + &ret_count, + &ss, + 1); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return status; + } + + *return_salist = talloc_move(ctx, &sa_list); + *return_count = 1; + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + /* Check name cache */ + + ok = namecache_fetch(frame, + name, + name_type, + &sa_list, + &nc_count); + if (ok) { + /* + * remove_duplicate_addrs2() has the + * side effect of removing zero addresses, + * so use it here. + */ + nc_count = remove_duplicate_addrs2(sa_list, nc_count); + if (nc_count == 0) { + TALLOC_FREE(sa_list); + TALLOC_FREE(frame); + return NT_STATUS_UNSUCCESSFUL; + } + *return_count = nc_count; + *return_salist = talloc_move(ctx, &sa_list); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + /* set the name resolution order */ + + if (resolve_order && strcmp(resolve_order[0], "NULL") == 0) { + DBG_DEBUG("all lookups disabled\n"); + TALLOC_FREE(frame); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!resolve_order || !resolve_order[0]) { + static const char *host_order[] = { "host", NULL }; + resolve_order = host_order; + } + + if ((strlen(name) > MAX_NETBIOSNAME_LEN - 1) || + (strchr(name, '.') != NULL)) { + /* + * Don't do NBT lookup, the name would not fit anyway + */ + resolve_order = filter_out_nbt_lookup(frame, resolve_order); + if (resolve_order == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + } + + /* iterate through the name resolution backends */ + + for (i=0; resolve_order[i]; i++) { + const char *tok = resolve_order[i]; + + if ((strequal(tok, "host") || strequal(tok, "hosts"))) { + status = resolve_hosts(talloc_tos(), + name, + name_type, + &ss_list, + &ret_count); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + goto done; + } + + if (strequal(tok, "kdc")) { + /* deal with KDC_NAME_TYPE names here. + * This will result in a SRV record lookup */ + status = resolve_ads(talloc_tos(), + name, + KDC_NAME_TYPE, + sitename, + &ss_list, + &ret_count); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + /* Ensure we don't namecache + * this with the KDC port. */ + name_type = KDC_NAME_TYPE; + goto done; + } + + if (strequal(tok, "ads")) { + /* deal with 0x1c and 0x1b names here. + * This will result in a SRV record lookup */ + status = resolve_ads(talloc_tos(), + name, + name_type, + sitename, + &ss_list, + &ret_count); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + goto done; + } + + if (strequal(tok, "lmhosts")) { + status = resolve_lmhosts_file_as_sockaddr( + talloc_tos(), + get_dyn_LMHOSTSFILE(), + name, + name_type, + &ss_list, + &ret_count); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + goto done; + } + + if (strequal(tok, "wins")) { + /* don't resolve 1D via WINS */ + if (name_type == 0x1D) { + continue; + } + status = resolve_wins(talloc_tos(), + name, + name_type, + &ss_list, + &ret_count); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + goto done; + } + + if (strequal(tok, "bcast")) { + status = name_resolve_bcast( + talloc_tos(), + name, + name_type, + &ss_list, + &ret_count); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + goto done; + } + + DBG_ERR("unknown name switch type %s\n", tok); + } + + /* All of the resolve_* functions above have returned false. */ + + TALLOC_FREE(frame); + *return_count = 0; + + return status; + + done: + + status = sockaddr_array_to_samba_sockaddr_array(frame, + &sa_list, + &ret_count, + ss_list, + ret_count); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + /* Remove duplicate entries. Some queries, notably #1c (domain + controllers) return the PDC in iplist[0] and then all domain + controllers including the PDC in iplist[1..n]. Iterating over + the iplist when the PDC is down will cause two sets of timeouts. */ + + ret_count = remove_duplicate_addrs2(sa_list, ret_count); + + /* Save in name cache */ + if ( DEBUGLEVEL >= 100 ) { + for (i = 0; i < ret_count && DEBUGLEVEL == 100; i++) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), + &sa_list[i].u.ss); + DEBUG(100, ("Storing name %s of type %d (%s:0)\n", + name, + name_type, + addr)); + } + } + + if (ret_count) { + namecache_store(name, + name_type, + ret_count, + sa_list); + } + + /* Display some debugging info */ + + if ( DEBUGLEVEL >= 10 ) { + DBG_DEBUG("returning %zu addresses: ", + ret_count); + + for (i = 0; i < ret_count; i++) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), + &sa_list[i].u.ss); + DEBUGADD(10, ("%s ", addr)); + } + DEBUG(10, ("\n")); + } + + *return_count = ret_count; + *return_salist = talloc_move(ctx, &sa_list); + + TALLOC_FREE(frame); + return status; +} + +/******************************************************** + Internal interface to resolve a name into one IP address. + Use this function if the string is either an IP address, DNS + or host name or NetBIOS name. This uses the name switch in the + smb.conf to determine the order of name resolution. +*********************************************************/ + +bool resolve_name(const char *name, + struct sockaddr_storage *return_ss, + int name_type, + bool prefer_ipv4) +{ + struct samba_sockaddr *sa_list = NULL; + char *sitename = NULL; + size_t count = 0; + NTSTATUS status; + TALLOC_CTX *frame = NULL; + + if (is_ipaddress(name)) { + return interpret_string_addr(return_ss, name, AI_NUMERICHOST); + } + + frame = talloc_stackframe(); + + sitename = sitename_fetch(frame, lp_realm()); /* wild guess */ + + status = internal_resolve_name(frame, + name, + name_type, + sitename, + &sa_list, + &count, + lp_name_resolve_order()); + if (NT_STATUS_IS_OK(status)) { + size_t i; + + if (prefer_ipv4) { + for (i=0; i<count; i++) { + if (!is_broadcast_addr(&sa_list[i].u.sa) && + (sa_list[i].u.ss.ss_family == AF_INET)) { + *return_ss = sa_list[i].u.ss; + TALLOC_FREE(sa_list); + TALLOC_FREE(frame); + return True; + } + } + } + + /* only return valid addresses for TCP connections */ + for (i=0; i<count; i++) { + if (!is_broadcast_addr(&sa_list[i].u.sa)) { + *return_ss = sa_list[i].u.ss; + TALLOC_FREE(sa_list); + TALLOC_FREE(frame); + return True; + } + } + } + + TALLOC_FREE(sa_list); + TALLOC_FREE(frame); + return False; +} + +/******************************************************** + Internal interface to resolve a name into a list of IP addresses. + Use this function if the string is either an IP address, DNS + or host name or NetBIOS name. This uses the name switch in the + smb.conf to determine the order of name resolution. +*********************************************************/ + +NTSTATUS resolve_name_list(TALLOC_CTX *ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_ss_arr, + unsigned int *p_num_entries) +{ + struct samba_sockaddr *sa_list = NULL; + char *sitename = NULL; + size_t count = 0; + size_t i; + unsigned int num_entries = 0; + struct sockaddr_storage *result_arr = NULL; + NTSTATUS status; + + if (is_ipaddress(name)) { + result_arr = talloc(ctx, struct sockaddr_storage); + if (result_arr == NULL) { + return NT_STATUS_NO_MEMORY; + } + if (!interpret_string_addr(result_arr, name, AI_NUMERICHOST)) { + TALLOC_FREE(result_arr); + return NT_STATUS_BAD_NETWORK_NAME; + } + *p_num_entries = 1; + *return_ss_arr = result_arr; + return NT_STATUS_OK; + } + + sitename = sitename_fetch(ctx, lp_realm()); /* wild guess */ + + status = internal_resolve_name(ctx, + name, + name_type, + sitename, + &sa_list, + &count, + lp_name_resolve_order()); + TALLOC_FREE(sitename); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* only return valid addresses for TCP connections */ + for (i=0, num_entries = 0; i<count; i++) { + if (!is_zero_addr(&sa_list[i].u.ss) && + !is_broadcast_addr(&sa_list[i].u.sa)) { + num_entries++; + } + } + if (num_entries == 0) { + status = NT_STATUS_BAD_NETWORK_NAME; + goto done; + } + + result_arr = talloc_array(ctx, + struct sockaddr_storage, + num_entries); + if (result_arr == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0, num_entries = 0; i<count; i++) { + if (!is_zero_addr(&sa_list[i].u.ss) && + !is_broadcast_addr(&sa_list[i].u.sa)) { + result_arr[num_entries++] = sa_list[i].u.ss; + } + } + + if (num_entries == 0) { + TALLOC_FREE(result_arr); + status = NT_STATUS_BAD_NETWORK_NAME; + goto done; + } + + status = NT_STATUS_OK; + *p_num_entries = num_entries; + *return_ss_arr = result_arr; +done: + TALLOC_FREE(sa_list); + return status; +} + +/******************************************************** + Find the IP address of the master browser or DMB for a workgroup. +*********************************************************/ + +bool find_master_ip(const char *group, struct sockaddr_storage *master_ss) +{ + struct samba_sockaddr *sa_list = NULL; + size_t count = 0; + NTSTATUS status; + + if (lp_disable_netbios()) { + DEBUG(5,("find_master_ip(%s): netbios is disabled\n", group)); + return false; + } + + status = internal_resolve_name(talloc_tos(), + group, + 0x1D, + NULL, + &sa_list, + &count, + lp_name_resolve_order()); + if (NT_STATUS_IS_OK(status)) { + *master_ss = sa_list[0].u.ss; + TALLOC_FREE(sa_list); + return true; + } + + TALLOC_FREE(sa_list); + + status = internal_resolve_name(talloc_tos(), + group, + 0x1B, + NULL, + &sa_list, + &count, + lp_name_resolve_order()); + if (NT_STATUS_IS_OK(status)) { + *master_ss = sa_list[0].u.ss; + TALLOC_FREE(sa_list); + return true; + } + + TALLOC_FREE(sa_list); + return false; +} + +/******************************************************** + Get the IP address list of the primary domain controller + for a domain. +*********************************************************/ + +bool get_pdc_ip(const char *domain, struct sockaddr_storage *pss) +{ + struct samba_sockaddr *sa_list = NULL; + size_t count = 0; + NTSTATUS status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND; + static const char *ads_order[] = { "ads", NULL }; + /* Look up #1B name */ + + if (lp_security() == SEC_ADS) { + status = internal_resolve_name(talloc_tos(), + domain, + 0x1b, + NULL, + &sa_list, + &count, + ads_order); + } + + if (!NT_STATUS_IS_OK(status) || count == 0) { + TALLOC_FREE(sa_list); + status = internal_resolve_name(talloc_tos(), + domain, + 0x1b, + NULL, + &sa_list, + &count, + lp_name_resolve_order()); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(sa_list); + return false; + } + } + + /* if we get more than 1 IP back we have to assume it is a + multi-homed PDC and not a mess up */ + + if ( count > 1 ) { + DBG_INFO("PDC has %zu IP addresses!\n", count); + sort_sa_list(sa_list, count); + } + + *pss = sa_list[0].u.ss; + TALLOC_FREE(sa_list); + return true; +} + +/* Private enum type for lookups. */ + +enum dc_lookup_type { DC_NORMAL_LOOKUP, DC_ADS_ONLY, DC_KDC_ONLY }; + +/******************************************************** + Get the IP address list of the domain controllers for + a domain. +*********************************************************/ + +static NTSTATUS get_dc_list(TALLOC_CTX *ctx, + const char *domain, + const char *sitename, + struct samba_sockaddr **sa_list_ret, + size_t *ret_count, + enum dc_lookup_type lookup_type, + bool *ordered) +{ + const char **resolve_order = NULL; + char *saf_servername = NULL; + char *pserver = NULL; + const char *p; + char *name; + size_t num_addresses = 0; + size_t local_count = 0; + size_t i; + struct samba_sockaddr *auto_sa_list = NULL; + struct samba_sockaddr *return_salist = NULL; + bool done_auto_lookup = false; + size_t auto_count = 0; + NTSTATUS status; + TALLOC_CTX *frame = talloc_stackframe(); + int auto_name_type = 0x1C; + + *ordered = False; + + /* if we are restricted to solely using DNS for looking + up a domain controller, make sure that host lookups + are enabled for the 'name resolve order'. If host lookups + are disabled and ads_only is True, then set the string to + NULL. */ + + resolve_order = lp_name_resolve_order(); + if (!resolve_order) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + if (lookup_type == DC_ADS_ONLY) { + if (str_list_check_ci(resolve_order, "host")) { + static const char *ads_order[] = { "ads", NULL }; + resolve_order = ads_order; + + /* DNS SRV lookups used by the ads resolver + are already sorted by priority and weight */ + *ordered = true; + } else { + /* this is quite bizarre! */ + static const char *null_order[] = { "NULL", NULL }; + resolve_order = null_order; + } + } else if (lookup_type == DC_KDC_ONLY) { + static const char *kdc_order[] = { "kdc", NULL }; + /* DNS SRV lookups used by the ads/kdc resolver + are already sorted by priority and weight */ + *ordered = true; + resolve_order = kdc_order; + auto_name_type = KDC_NAME_TYPE; + } + + /* fetch the server we have affinity for. Add the + 'password server' list to a search for our domain controllers */ + + saf_servername = saf_fetch(frame, domain); + + if (strequal(domain, lp_workgroup()) || strequal(domain, lp_realm())) { + pserver = talloc_asprintf(frame, "%s, %s", + saf_servername ? saf_servername : "", + lp_password_server()); + } else { + pserver = talloc_asprintf(frame, "%s, *", + saf_servername ? saf_servername : ""); + } + + TALLOC_FREE(saf_servername); + if (!pserver) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + DEBUG(3,("get_dc_list: preferred server list: \"%s\"\n", pserver )); + + /* + * if '*' appears in the "password server" list then add + * an auto lookup to the list of manually configured + * DC's. If any DC is listed by name, then the list should be + * considered to be ordered + */ + + p = pserver; + while (next_token_talloc(frame, &p, &name, LIST_SEP)) { + if (!done_auto_lookup && strequal(name, "*")) { + done_auto_lookup = true; + + status = internal_resolve_name(frame, + domain, + auto_name_type, + sitename, + &auto_sa_list, + &auto_count, + resolve_order); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + /* Wrap check. */ + if (num_addresses + auto_count < num_addresses) { + TALLOC_FREE(auto_sa_list); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + num_addresses += auto_count; + DBG_DEBUG("Adding %zu DC's from auto lookup\n", + auto_count); + } else { + /* Wrap check. */ + if (num_addresses + 1 < num_addresses) { + TALLOC_FREE(auto_sa_list); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + num_addresses++; + } + } + + /* if we have no addresses and haven't done the auto lookup, then + just return the list of DC's. Or maybe we just failed. */ + + if (num_addresses == 0) { + struct samba_sockaddr *dc_salist = NULL; + size_t dc_count = 0; + + if (done_auto_lookup) { + DEBUG(4,("get_dc_list: no servers found\n")); + status = NT_STATUS_NO_LOGON_SERVERS; + goto out; + } + /* talloc off frame, only move to ctx on success. */ + status = internal_resolve_name(frame, + domain, + auto_name_type, + sitename, + &dc_salist, + &dc_count, + resolve_order); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + return_salist = dc_salist; + local_count = dc_count; + goto out; + } + + return_salist = talloc_zero_array(frame, + struct samba_sockaddr, + num_addresses); + if (return_salist == NULL) { + DEBUG(3,("get_dc_list: malloc fail !\n")); + status = NT_STATUS_NO_MEMORY; + goto out; + } + + p = pserver; + local_count = 0; + + /* fill in the return list now with real IP's */ + + while ((local_count<num_addresses) && + next_token_talloc(frame, &p, &name, LIST_SEP)) { + struct samba_sockaddr name_sa = {0}; + + /* copy any addresses from the auto lookup */ + + if (strequal(name, "*")) { + size_t j; + for (j=0; j<auto_count; j++) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, + sizeof(addr), + &auto_sa_list[j].u.ss); + /* Check for and don't copy any + * known bad DC IP's. */ + if(!NT_STATUS_IS_OK(check_negative_conn_cache( + domain, + addr))) { + DEBUG(5,("get_dc_list: " + "negative entry %s removed " + "from DC list\n", + addr)); + continue; + } + return_salist[local_count] = auto_sa_list[j]; + local_count++; + } + continue; + } + + /* explicit lookup; resolve_name() will + * handle names & IP addresses */ + if (resolve_name(name, &name_sa.u.ss, 0x20, true)) { + char addr[INET6_ADDRSTRLEN]; + bool ok; + + /* + * Ensure we set sa_socklen correctly. + * Doesn't matter now, but eventually we + * will remove ip_service and return samba_sockaddr + * arrays directly. + */ + ok = sockaddr_storage_to_samba_sockaddr( + &name_sa, + &name_sa.u.ss); + if (!ok) { + status = NT_STATUS_INVALID_ADDRESS; + goto out; + } + + print_sockaddr(addr, + sizeof(addr), + &name_sa.u.ss); + + /* Check for and don't copy any known bad DC IP's. */ + if( !NT_STATUS_IS_OK(check_negative_conn_cache(domain, + addr)) ) { + DEBUG(5,("get_dc_list: negative entry %s " + "removed from DC list\n", + name )); + continue; + } + + return_salist[local_count] = name_sa; + local_count++; + *ordered = true; + } + } + + /* need to remove duplicates in the list if we have any + explicit password servers */ + + local_count = remove_duplicate_addrs2(return_salist, local_count ); + + /* For DC's we always prioritize IPv4 due to W2K3 not + * supporting LDAP, KRB5 or CLDAP over IPv6. */ + + if (local_count && return_salist != NULL) { + prioritize_ipv4_list(return_salist, local_count); + } + + if ( DEBUGLEVEL >= 4 ) { + DEBUG(4,("get_dc_list: returning %zu ip addresses " + "in an %sordered list\n", + local_count, + *ordered ? "":"un")); + DEBUG(4,("get_dc_list: ")); + for ( i=0; i<local_count; i++ ) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, + sizeof(addr), + &return_salist[i].u.ss); + DEBUGADD(4,("%s ", addr)); + } + DEBUGADD(4,("\n")); + } + + status = (local_count != 0 ? NT_STATUS_OK : NT_STATUS_NO_LOGON_SERVERS); + + out: + + if (NT_STATUS_IS_OK(status)) { + *sa_list_ret = talloc_move(ctx, &return_salist); + *ret_count = local_count; + } + TALLOC_FREE(return_salist); + TALLOC_FREE(auto_sa_list); + TALLOC_FREE(frame); + return status; +} + +/********************************************************************* + Small wrapper function to get the DC list and sort it if neccessary. + Returns a samba_sockaddr array. +*********************************************************************/ + +NTSTATUS get_sorted_dc_list(TALLOC_CTX *ctx, + const char *domain, + const char *sitename, + struct samba_sockaddr **sa_list_ret, + size_t *ret_count, + bool ads_only) +{ + bool ordered = false; + NTSTATUS status; + enum dc_lookup_type lookup_type = DC_NORMAL_LOOKUP; + struct samba_sockaddr *sa_list = NULL; + size_t count = 0; + + DBG_INFO("attempting lookup for name %s (sitename %s)\n", + domain, + sitename ? sitename : "NULL"); + + if (ads_only) { + lookup_type = DC_ADS_ONLY; + } + + status = get_dc_list(ctx, + domain, + sitename, + &sa_list, + &count, + lookup_type, + &ordered); + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_LOGON_SERVERS) + && sitename) { + DBG_NOTICE("no server for name %s available" + " in site %s, fallback to all servers\n", + domain, + sitename); + status = get_dc_list(ctx, + domain, + NULL, + &sa_list, + &count, + lookup_type, + &ordered); + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* only sort if we don't already have an ordered list */ + if (!ordered) { + sort_sa_list(sa_list, count); + } + + *ret_count = count; + *sa_list_ret = sa_list; + return status; +} + +/********************************************************************* + Get the KDC list - re-use all the logic in get_dc_list. + Returns a samba_sockaddr array. +*********************************************************************/ + +NTSTATUS get_kdc_list(TALLOC_CTX *ctx, + const char *realm, + const char *sitename, + struct samba_sockaddr **sa_list_ret, + size_t *ret_count) +{ + size_t count = 0; + struct samba_sockaddr *sa_list = NULL; + bool ordered = false; + NTSTATUS status; + + status = get_dc_list(ctx, + realm, + sitename, + &sa_list, + &count, + DC_KDC_ONLY, + &ordered); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* only sort if we don't already have an ordered list */ + if (!ordered ) { + sort_sa_list(sa_list, count); + } + + *ret_count = count; + *sa_list_ret = sa_list; + return status; +} diff --git a/source3/libsmb/namequery.h b/source3/libsmb/namequery.h new file mode 100644 index 0000000..16559f9 --- /dev/null +++ b/source3/libsmb/namequery.h @@ -0,0 +1,116 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Volker Lendecke 2018 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMB_NAMEQUERY_H__ +#define __LIBSMB_NAMEQUERY_H__ + +#include "includes.h" +#include <tevent.h> + +/* The following definitions come from libsmb/namequery.c */ +bool saf_store( const char *domain, const char *servername ); +bool saf_join_store( const char *domain, const char *servername ); +bool saf_delete( const char *domain ); +char *saf_fetch(TALLOC_CTX *mem_ctx, const char *domain ); +struct tevent_req *node_status_query_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nmb_name *name, + const struct sockaddr_storage *addr); +NTSTATUS node_status_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct node_status **pnode_status, + size_t *pnum_names, + struct node_status_extra *extra); +NTSTATUS node_status_query(TALLOC_CTX *mem_ctx, struct nmb_name *name, + const struct sockaddr_storage *addr, + struct node_status **pnode_status, + size_t *pnum_names, + struct node_status_extra *extra); +bool name_status_find(const char *q_name, + int q_type, + int type, + const struct sockaddr_storage *to_ss, + fstring name); +size_t remove_duplicate_addrs2(struct samba_sockaddr *salist, size_t count); +struct tevent_req *name_query_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *name, int name_type, + bool bcast, bool recurse, + const struct sockaddr_storage *addr); +NTSTATUS name_query_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, size_t *num_addrs, + uint8_t *flags); +NTSTATUS name_query(const char *name, int name_type, + bool bcast, bool recurse, + const struct sockaddr_storage *to_ss, + TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs, uint8_t *flags); +struct tevent_req *name_resolve_bcast_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *name, + int name_type); +NTSTATUS name_resolve_bcast_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs); +NTSTATUS name_resolve_bcast(TALLOC_CTX *mem_ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count); +struct tevent_req *resolve_wins_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const char *name, + int name_type); +NTSTATUS resolve_wins_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct sockaddr_storage **addrs, + size_t *num_addrs, uint8_t *flags); +NTSTATUS resolve_wins(TALLOC_CTX *mem_ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_iplist, + size_t *return_count); +NTSTATUS internal_resolve_name(TALLOC_CTX *ctx, + const char *name, + int name_type, + const char *sitename, + struct samba_sockaddr **return_salist, + size_t *ret_count, + const char **resolve_order); +bool resolve_name(const char *name, + struct sockaddr_storage *return_ss, + int name_type, + bool prefer_ipv4); +NTSTATUS resolve_name_list(TALLOC_CTX *ctx, + const char *name, + int name_type, + struct sockaddr_storage **return_ss_arr, + unsigned int *p_num_entries); +bool find_master_ip(const char *group, struct sockaddr_storage *master_ss); +bool get_pdc_ip(const char *domain, struct sockaddr_storage *pss); +NTSTATUS get_sorted_dc_list(TALLOC_CTX *ctx, + const char *domain, + const char *sitename, + struct samba_sockaddr **sa_list_ret, + size_t *ret_count, + bool ads_only); +NTSTATUS get_kdc_list(TALLOC_CTX *ctx, + const char *realm, + const char *sitename, + struct samba_sockaddr **sa_list_ret, + size_t *ret_count); +#endif diff --git a/source3/libsmb/namequery_dc.c b/source3/libsmb/namequery_dc.c new file mode 100644 index 0000000..f2598ed --- /dev/null +++ b/source3/libsmb/namequery_dc.c @@ -0,0 +1,262 @@ +/* + Unix SMB/CIFS implementation. + + Winbind daemon connection manager + + Copyright (C) Tim Potter 2001 + Copyright (C) Andrew Bartlett 2002 + Copyright (C) Gerald Carter 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include "includes.h" +#include "libsmb/namequery.h" +#include "libads/sitename_cache.h" +#include "ads.h" +#include "../librpc/gen_ndr/nbt.h" +#include "lib/param/loadparm.h" +#include "lib/util/string_wrappers.h" + +/********************************************************************** + Is this our primary domain ? +**********************************************************************/ + +#ifdef HAVE_ADS +static bool is_our_primary_domain(const char *domain) +{ + int role = lp_server_role(); + + if ((role == ROLE_DOMAIN_MEMBER) && strequal(lp_workgroup(), domain)) { + return True; + } else if (strequal(get_global_sam_name(), domain)) { + return True; + } + return False; +} +#endif + +/************************************************************************** + Find the name and IP address for a server in the realm/domain + *************************************************************************/ + +static bool ads_dc_name(const char *domain, + const char *realm, + struct sockaddr_storage *dc_ss, + fstring srv_name) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + bool ok = false; + ADS_STRUCT *ads; + char *sitename; + int i; + char addr[INET6_ADDRSTRLEN]; + + if (!realm && strequal(domain, lp_workgroup())) { + realm = lp_realm(); + } + + sitename = sitename_fetch(tmp_ctx, realm); + + /* Try this 3 times then give up. */ + for( i =0 ; i < 3; i++) { + ads = ads_init(tmp_ctx, realm, domain, NULL, ADS_SASL_PLAIN); + if (!ads) { + ok = false; + goto out; + } + + DEBUG(4,("ads_dc_name: domain=%s\n", domain)); + +#ifdef HAVE_ADS + /* we don't need to bind, just connect */ + ads->auth.flags |= ADS_AUTH_NO_BIND; + ads_connect(ads); +#endif + + if (!ads->config.realm) { + ok = false; + goto out; + } + + /* Now we've found a server, see if our sitename + has changed. If so, we need to re-do the DNS query + to ensure we only find servers in our site. */ + + if (stored_sitename_changed(realm, sitename)) { + sitename = sitename_fetch(tmp_ctx, realm); + TALLOC_FREE(ads); + /* Ensure we don't cache the DC we just connected to. */ + namecache_delete(realm, 0x1C); + namecache_delete(domain, 0x1C); + continue; + } + +#ifdef HAVE_ADS + if (is_our_primary_domain(domain) && (ads->config.flags & NBT_SERVER_KDC)) { + if (ads_closest_dc(ads)) { + /* We're going to use this KDC for this realm/domain. + If we are using sites, then force the krb5 libs + to use this KDC. */ + + create_local_private_krb5_conf_for_domain(realm, + domain, + sitename, + &ads->ldap.ss); + } else { + create_local_private_krb5_conf_for_domain(realm, + domain, + NULL, + &ads->ldap.ss); + } + } +#endif + break; + } + + if (i == 3) { + DEBUG(1,("ads_dc_name: sitename (now \"%s\") keeps changing ???\n", + sitename ? sitename : "")); + ok = false; + goto out; + } + + fstrcpy(srv_name, ads->config.ldap_server_name); + if (!strupper_m(srv_name)) { + ok = false; + goto out; + } +#ifdef HAVE_ADS + *dc_ss = ads->ldap.ss; +#else + zero_sockaddr(dc_ss); +#endif + print_sockaddr(addr, sizeof(addr), dc_ss); + DEBUG(4,("ads_dc_name: using server='%s' IP=%s\n", + srv_name, addr)); + + ok = true; +out: + TALLOC_FREE(tmp_ctx); + + return ok; +} + +/**************************************************************************** + Utility function to return the name of a DC. The name is guaranteed to be + valid since we have already done a name_status_find on it + ***************************************************************************/ + +static bool rpc_dc_name(const char *domain, + fstring srv_name, + struct sockaddr_storage *ss_out) +{ + struct samba_sockaddr *sa_list = NULL; + size_t count = 0; + struct sockaddr_storage dc_ss; + size_t i; + NTSTATUS result; + char addr[INET6_ADDRSTRLEN]; + + /* get a list of all domain controllers */ + + result = get_sorted_dc_list(talloc_tos(), + domain, + NULL, + &sa_list, + &count, + false); + if (!NT_STATUS_IS_OK(result)) { + DEBUG(3, ("Could not look up dc's for domain %s\n", domain)); + return False; + } + + /* Remove the entry we've already failed with (should be the PDC). */ + + for (i = 0; i < count; i++) { + if (is_zero_addr(&sa_list[i].u.ss)) + continue; + + if (name_status_find(domain, 0x1c, 0x20, &sa_list[i].u.ss, srv_name)) { + result = check_negative_conn_cache( domain, srv_name ); + if ( NT_STATUS_IS_OK(result) ) { + dc_ss = sa_list[i].u.ss; + goto done; + } + } + } + + TALLOC_FREE(sa_list); + + /* No-one to talk to )-: */ + return False; /* Boo-hoo */ + + done: + /* We have the netbios name and IP address of a domain controller. + Ideally we should sent a SAMLOGON request to determine whether + the DC is alive and kicking. If we can catch a dead DC before + performing a cli_connect() we can avoid a 30-second timeout. */ + + print_sockaddr(addr, sizeof(addr), &dc_ss); + DEBUG(3, ("rpc_dc_name: Returning DC %s (%s) for domain %s\n", srv_name, + addr, domain)); + + *ss_out = dc_ss; + TALLOC_FREE(sa_list); + + return True; +} + +/********************************************************************** + wrapper around ads and rpc methods of finds DC's +**********************************************************************/ + +bool get_dc_name(const char *domain, + const char *realm, + fstring srv_name, + struct sockaddr_storage *ss_out) +{ + struct sockaddr_storage dc_ss; + bool ret; + bool our_domain = False; + + zero_sockaddr(&dc_ss); + + ret = False; + + if ( strequal(lp_workgroup(), domain) || strequal(lp_realm(), realm) ) + our_domain = True; + + /* always try to obey what the admin specified in smb.conf + (for the local domain) */ + + if ( (our_domain && lp_security()==SEC_ADS) || realm ) { + ret = ads_dc_name(domain, realm, &dc_ss, srv_name); + } + + if (!domain) { + /* if we have only the realm we can't do anything else */ + return False; + } + + if (!ret) { + /* fall back on rpc methods if the ADS methods fail */ + ret = rpc_dc_name(domain, srv_name, &dc_ss); + } + + *ss_out = dc_ss; + + return ret; +} diff --git a/source3/libsmb/nmblib.c b/source3/libsmb/nmblib.c new file mode 100644 index 0000000..607470f --- /dev/null +++ b/source3/libsmb/nmblib.c @@ -0,0 +1,1466 @@ +/* + Unix SMB/CIFS implementation. + NBT netbios library routines + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jeremy Allison 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "libsmb/nmblib.h" +#include "lib/util/string_wrappers.h" + +static const struct opcode_names { + const char *nmb_opcode_name; + int opcode; +} nmb_header_opcode_names[] = { + {"Query", 0 }, + {"Registration", 5 }, + {"Release", 6 }, + {"WACK", 7 }, + {"Refresh", 8 }, + {"Refresh(altcode)", 9 }, + {"Multi-homed Registration", 15 }, + {0, -1 } +}; + +/**************************************************************************** + Lookup a nmb opcode name. +****************************************************************************/ + +static const char *lookup_opcode_name( int opcode ) +{ + const struct opcode_names *op_namep; + int i; + + for(i = 0; nmb_header_opcode_names[i].nmb_opcode_name != 0; i++) { + op_namep = &nmb_header_opcode_names[i]; + if(opcode == op_namep->opcode) + return op_namep->nmb_opcode_name; + } + return "<unknown opcode>"; +} + +/**************************************************************************** + Print out a res_rec structure. +****************************************************************************/ + +static void debug_nmb_res_rec(struct res_rec *res, const char *hdr) +{ + int i, j; + + DEBUGADD( 4, ( " %s: nmb_name=%s rr_type=%d rr_class=%d ttl=%d\n", + hdr, + nmb_namestr(&res->rr_name), + res->rr_type, + res->rr_class, + res->ttl ) ); + + if (res->rdlength == 0) { + return; + } + + for (i = 0; i < res->rdlength; i+= MAX_NETBIOSNAME_LEN) { + DEBUGADD(4, (" %s %3x char ", hdr, i)); + + for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) { + unsigned char x = res->rdata[i+j]; + if (x < 32 || x > 127) + x = '.'; + + if (i+j >= res->rdlength) + break; + DEBUGADD(4, ("%c", x)); + } + + DEBUGADD(4, (" hex ")); + + for (j = 0; j < MAX_NETBIOSNAME_LEN; j++) { + if (i+j >= res->rdlength) + break; + DEBUGADD(4, ("%02X", (unsigned char)res->rdata[i+j])); + } + + DEBUGADD(4, ("\n")); + } +} + +/**************************************************************************** + Process a nmb packet. +****************************************************************************/ + +void debug_nmb_packet(struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + + if( DEBUGLVL( 4 ) ) { + dbgtext( "nmb packet from %s(%d) header: id=%d " + "opcode=%s(%d) response=%s\n", + inet_ntoa(p->ip), p->port, + nmb->header.name_trn_id, + lookup_opcode_name(nmb->header.opcode), + nmb->header.opcode, + BOOLSTR(nmb->header.response) ); + dbgtext( " header: flags: bcast=%s rec_avail=%s " + "rec_des=%s trunc=%s auth=%s\n", + BOOLSTR(nmb->header.nm_flags.bcast), + BOOLSTR(nmb->header.nm_flags.recursion_available), + BOOLSTR(nmb->header.nm_flags.recursion_desired), + BOOLSTR(nmb->header.nm_flags.trunc), + BOOLSTR(nmb->header.nm_flags.authoritative) ); + dbgtext( " header: rcode=%d qdcount=%d ancount=%d " + "nscount=%d arcount=%d\n", + nmb->header.rcode, + nmb->header.qdcount, + nmb->header.ancount, + nmb->header.nscount, + nmb->header.arcount ); + } + + if (nmb->header.qdcount) { + DEBUGADD( 4, ( " question: q_name=%s q_type=%d q_class=%d\n", + nmb_namestr(&nmb->question.question_name), + nmb->question.question_type, + nmb->question.question_class) ); + } + + if (nmb->answers && nmb->header.ancount) { + debug_nmb_res_rec(nmb->answers,"answers"); + } + if (nmb->nsrecs && nmb->header.nscount) { + debug_nmb_res_rec(nmb->nsrecs,"nsrecs"); + } + if (nmb->additional && nmb->header.arcount) { + debug_nmb_res_rec(nmb->additional,"additional"); + } +} + +/******************************************************************* + Handle "compressed" name pointers. +******************************************************************/ + +static bool handle_name_ptrs(unsigned char *ubuf,int *offset,int length, + bool *got_pointer,int *ret) +{ + int loop_count=0; + + while ((ubuf[*offset] & 0xC0) == 0xC0) { + if (!*got_pointer) + (*ret) += 2; + (*got_pointer)=True; + if (*offset > length - 2) { + return False; + } + (*offset) = ((ubuf[*offset] & ~0xC0)<<8) | ubuf[(*offset)+1]; + if (loop_count++ == 10 || + (*offset) < 0 || (*offset)>(length-2)) { + return False; + } + } + return True; +} + +/******************************************************************* + Parse a nmb name from "compressed" format to something readable + return the space taken by the name, or 0 if the name is invalid +******************************************************************/ + +static int parse_nmb_name(char *inbuf,int ofs,int length, struct nmb_name *name) +{ + size_t m,n=0; + unsigned char *ubuf = (unsigned char *)inbuf; + int ret = 0; + bool got_pointer=False; + size_t loop_count=0; + int offset = ofs; + + if (length - offset < 2) + return(0); + + /* handle initial name pointers */ + if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) + return(0); + + m = ubuf[offset]; + + /* m must be 32 to exactly fill in the 16 bytes of the netbios name */ + if (m != 32) { + return 0; + } + /* Cannot go past length. */ + if (offset+m+2 > length) { + return 0; + } + + memset((char *)name,'\0',sizeof(*name)); + + /* the "compressed" part */ + if (!got_pointer) + ret += m + 2; + offset++; + while (m > 0) { + unsigned char c1,c2; + c1 = ubuf[offset++]-'A'; + c2 = ubuf[offset++]-'A'; + if ((c1 & 0xF0) || (c2 & 0xF0)) { + return(0); + } + if (n >= sizeof(name->name)) { + return 0; + } + name->name[n++] = (c1<<4) | c2; + m -= 2; + } + /* + * RFC1002: For a valid NetBIOS name, exiting from the above, + * n *must* be MAX_NETBIOSNAME_LEN (16). + */ + if (n != MAX_NETBIOSNAME_LEN) { + return 0; + } + + /* parse out the name type, its always + * in the 16th byte of the name */ + name->name_type = ((unsigned char)name->name[15]) & 0xff; + + /* remove trailing spaces */ + name->name[15] = 0; + n = 14; + while (n && name->name[n]==' ') + name->name[n--] = 0; + + /* now the domain parts (if any) */ + n = 0; + while (ubuf[offset]) { + /* we can have pointers within the domain part as well */ + if (!handle_name_ptrs(ubuf,&offset,length,&got_pointer,&ret)) + return(0); + + m = ubuf[offset]; + /* + * Don't allow null domain parts. + */ + if (!m) + return(0); + if (!got_pointer) + ret += m+1; + if (n) + name->scope[n++] = '.'; + if (m+2+offset>length || n+m+1>sizeof(name->scope)) + return(0); + offset++; + while (m--) + name->scope[n++] = (char)ubuf[offset++]; + + /* + * Watch for malicious loops. + */ + if (loop_count++ == 10) + return 0; + } + name->scope[n++] = 0; + + return(ret); +} + +/**************************************************************************** + Put a netbios name, padding(s) and a name type into a 16 character buffer. + name is already in DOS charset. + [15 bytes name + padding][1 byte name type]. +****************************************************************************/ + +void put_name(char *dest, const char *name, int pad, unsigned int name_type) +{ + size_t len = strlen(name); + + memcpy(dest, name, (len < MAX_NETBIOSNAME_LEN) ? + len : MAX_NETBIOSNAME_LEN - 1); + if (len < MAX_NETBIOSNAME_LEN - 1) { + memset(dest + len, pad, MAX_NETBIOSNAME_LEN - 1 - len); + } + dest[MAX_NETBIOSNAME_LEN - 1] = name_type; +} + +/******************************************************************* + Put a compressed nmb name into a buffer. Return the length of the + compressed name. + + Compressed names are really weird. The "compression" doubles the + size. The idea is that it also means that compressed names conform + to the doman name system. See RFC1002. + + If buf == NULL this is a length calculation. +******************************************************************/ + +static int put_nmb_name(char *buf, size_t buflen, int offset,struct nmb_name *name) +{ + int ret,m; + nstring buf1; + char *p; + + if (strcmp(name->name,"*") == 0) { + /* special case for wildcard name */ + put_name(buf1, "*", '\0', name->name_type); + } else { + put_name(buf1, name->name, ' ', name->name_type); + } + + if (buf) { + if (offset >= buflen) { + return 0; + } + buf[offset] = 0x20; + } + + ret = 34; + + for (m=0;m<MAX_NETBIOSNAME_LEN;m++) { + if (buf) { + if (offset+2+2*m >= buflen) { + return 0; + } + buf[offset+1+2*m] = 'A' + ((buf1[m]>>4)&0xF); + buf[offset+2+2*m] = 'A' + (buf1[m]&0xF); + } + } + offset += 33; + + if (buf) { + if (offset >= buflen) { + return 0; + } + buf[offset] = 0; + } + + if (name->scope[0]) { + /* XXXX this scope handling needs testing */ + size_t scopenamelen = strlen(name->scope) + 1; + ret += scopenamelen; + if (buf) { + if (offset+1+scopenamelen >= buflen) { + return 0; + } + strlcpy(&buf[offset+1],name->scope, + buflen - (offset+1)); + + p = &buf[offset+1]; + while ((p = strchr_m(p,'.'))) { + buf[offset] = PTR_DIFF(p,&buf[offset+1]); + offset += (buf[offset] + 1); + if (offset+1 >= buflen) { + return 0; + } + p = &buf[offset+1]; + } + buf[offset] = strlen(&buf[offset+1]); + } + } + + return ret; +} + +/******************************************************************* + Useful for debugging messages. +******************************************************************/ + +char *nmb_namestr(const struct nmb_name *n) +{ + fstring name; + char *result; + + pull_ascii_fstring(name, n->name); + if (!n->scope[0]) + result = talloc_asprintf(talloc_tos(), "%s<%02x>", name, + n->name_type); + else + result = talloc_asprintf(talloc_tos(), "%s<%02x>.%s", name, + n->name_type, n->scope); + + SMB_ASSERT(result != NULL); + return result; +} + +/******************************************************************* + Allocate and parse some resource records. +******************************************************************/ + +static bool parse_alloc_res_rec(char *inbuf,int *offset,int length, + struct res_rec **recs, int count) +{ + int i; + + *recs = SMB_MALLOC_ARRAY(struct res_rec, count); + if (!*recs) + return(False); + + memset((char *)*recs,'\0',sizeof(**recs)*count); + + for (i=0;i<count;i++) { + int l = parse_nmb_name(inbuf,*offset,length, + &(*recs)[i].rr_name); + (*offset) += l; + if (!l || (*offset)+10 > length) { + SAFE_FREE(*recs); + return(False); + } + (*recs)[i].rr_type = RSVAL(inbuf,(*offset)); + (*recs)[i].rr_class = RSVAL(inbuf,(*offset)+2); + (*recs)[i].ttl = RIVAL(inbuf,(*offset)+4); + (*recs)[i].rdlength = RSVAL(inbuf,(*offset)+8); + (*offset) += 10; + if ((*recs)[i].rdlength>sizeof((*recs)[i].rdata) || + (*offset)+(*recs)[i].rdlength > length) { + SAFE_FREE(*recs); + return(False); + } + memcpy((*recs)[i].rdata,inbuf+(*offset),(*recs)[i].rdlength); + (*offset) += (*recs)[i].rdlength; + } + return(True); +} + +/******************************************************************* + Put a resource record into a packet. + If buf == NULL this is a length calculation. +******************************************************************/ + +static int put_res_rec(char *buf, size_t buflen, int offset,struct res_rec *recs,int count) +{ + int ret=0; + int i; + + for (i=0;i<count;i++) { + int l = put_nmb_name(buf,buflen,offset,&recs[i].rr_name); + offset += l; + ret += l; + if (buf) { + RSSVAL(buf,offset,recs[i].rr_type); + RSSVAL(buf,offset+2,recs[i].rr_class); + RSIVAL(buf,offset+4,(unsigned int)recs[i].ttl); + RSSVAL(buf,offset+8,recs[i].rdlength); + memcpy(buf+offset+10,recs[i].rdata,recs[i].rdlength); + } + offset += 10+recs[i].rdlength; + ret += 10+recs[i].rdlength; + } + + return ret; +} + +/******************************************************************* + Put a compressed name pointer record into a packet. + If buf == NULL this is a length calculation. +******************************************************************/ + +static int put_compressed_name_ptr(unsigned char *buf, + int offset, + struct res_rec *rec, + int ptr_offset) +{ + int ret=offset; + if (buf) { + buf[offset] = (0xC0 | ((ptr_offset >> 8) & 0xFF)); + buf[offset+1] = (ptr_offset & 0xFF); + } + offset += 2; + if (buf) { + RSSVAL(buf,offset,rec->rr_type); + RSSVAL(buf,offset+2,rec->rr_class); + RSIVAL(buf,offset+4,rec->ttl); + RSSVAL(buf,offset+8,rec->rdlength); + memcpy(buf+offset+10,rec->rdata,rec->rdlength); + } + offset += 10+rec->rdlength; + ret = (offset - ret); + + return ret; +} + +/******************************************************************* + Parse a dgram packet. Return False if the packet can't be parsed + or is invalid for some reason, True otherwise. + + This is documented in section 4.4.1 of RFC1002. +******************************************************************/ + +static bool parse_dgram(char *inbuf,int length,struct dgram_packet *dgram) +{ + size_t offset; + int flags; + + memset((char *)dgram,'\0',sizeof(*dgram)); + + if (length < 14) + return(False); + + dgram->header.msg_type = CVAL(inbuf,0); + flags = CVAL(inbuf,1); + dgram->header.flags.node_type = (enum node_type)((flags>>2)&3); + if (flags & 1) + dgram->header.flags.more = True; + if (flags & 2) + dgram->header.flags.first = True; + dgram->header.dgm_id = RSVAL(inbuf,2); + putip((char *)&dgram->header.source_ip,inbuf+4); + dgram->header.source_port = RSVAL(inbuf,8); + dgram->header.dgm_length = RSVAL(inbuf,10); + dgram->header.packet_offset = RSVAL(inbuf,12); + + offset = 14; + + if (dgram->header.msg_type == 0x10 || + dgram->header.msg_type == 0x11 || + dgram->header.msg_type == 0x12) { + offset += parse_nmb_name(inbuf,offset,length, + &dgram->source_name); + offset += parse_nmb_name(inbuf,offset,length, + &dgram->dest_name); + } + + if (offset >= length || (length-offset > sizeof(dgram->data))) + return(False); + + dgram->datasize = length-offset; + memcpy(dgram->data,inbuf+offset,dgram->datasize); + + /* Paranioa. Ensure the last 2 bytes in the dgram buffer are + zero. This should be true anyway, just enforce it for + paranioa sake. JRA. */ + SMB_ASSERT(dgram->datasize <= (sizeof(dgram->data)-2)); + memset(&dgram->data[sizeof(dgram->data)-2], '\0', 2); + + return(True); +} + +/******************************************************************* + Parse a nmb packet. Return False if the packet can't be parsed + or is invalid for some reason, True otherwise. +******************************************************************/ + +static bool parse_nmb(char *inbuf,int length,struct nmb_packet *nmb) +{ + int nm_flags,offset; + + memset((char *)nmb,'\0',sizeof(*nmb)); + + if (length < 12) + return(False); + + /* parse the header */ + nmb->header.name_trn_id = RSVAL(inbuf,0); + + DEBUG(10,("parse_nmb: packet id = %d\n", nmb->header.name_trn_id)); + + nmb->header.opcode = (CVAL(inbuf,2) >> 3) & 0xF; + nmb->header.response = ((CVAL(inbuf,2)>>7)&1)?True:False; + nm_flags = ((CVAL(inbuf,2) & 0x7) << 4) + (CVAL(inbuf,3)>>4); + nmb->header.nm_flags.bcast = (nm_flags&1)?True:False; + nmb->header.nm_flags.recursion_available = (nm_flags&8)?True:False; + nmb->header.nm_flags.recursion_desired = (nm_flags&0x10)?True:False; + nmb->header.nm_flags.trunc = (nm_flags&0x20)?True:False; + nmb->header.nm_flags.authoritative = (nm_flags&0x40)?True:False; + nmb->header.rcode = CVAL(inbuf,3) & 0xF; + nmb->header.qdcount = RSVAL(inbuf,4); + nmb->header.ancount = RSVAL(inbuf,6); + nmb->header.nscount = RSVAL(inbuf,8); + nmb->header.arcount = RSVAL(inbuf,10); + + if (nmb->header.qdcount) { + offset = parse_nmb_name(inbuf,12,length, + &nmb->question.question_name); + if (!offset) + return(False); + + if (length - (12+offset) < 4) + return(False); + nmb->question.question_type = RSVAL(inbuf,12+offset); + nmb->question.question_class = RSVAL(inbuf,12+offset+2); + + offset += 12+4; + } else { + offset = 12; + } + + /* and any resource records */ + if (nmb->header.ancount && + !parse_alloc_res_rec(inbuf,&offset,length,&nmb->answers, + nmb->header.ancount)) + return(False); + + if (nmb->header.nscount && + !parse_alloc_res_rec(inbuf,&offset,length,&nmb->nsrecs, + nmb->header.nscount)) + return(False); + + if (nmb->header.arcount && + !parse_alloc_res_rec(inbuf,&offset,length, + &nmb->additional, nmb->header.arcount)) + return(False); + + return(True); +} + +/******************************************************************* + 'Copy constructor' for an nmb packet. +******************************************************************/ + +static struct packet_struct *copy_nmb_packet(struct packet_struct *packet) +{ + struct nmb_packet *nmb; + struct nmb_packet *copy_nmb; + struct packet_struct *pkt_copy; + + if(( pkt_copy = SMB_MALLOC_P(struct packet_struct)) == NULL) { + DEBUG(0,("copy_nmb_packet: malloc fail.\n")); + return NULL; + } + + /* Structure copy of entire thing. */ + + *pkt_copy = *packet; + + /* Ensure this copy is not locked. */ + pkt_copy->locked = False; + pkt_copy->recv_fd = -1; + pkt_copy->send_fd = -1; + + /* Ensure this copy has no resource records. */ + nmb = &packet->packet.nmb; + copy_nmb = &pkt_copy->packet.nmb; + + copy_nmb->answers = NULL; + copy_nmb->nsrecs = NULL; + copy_nmb->additional = NULL; + + /* Now copy any resource records. */ + + if (nmb->answers) { + if((copy_nmb->answers = SMB_MALLOC_ARRAY( + struct res_rec,nmb->header.ancount)) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->answers, (char *)nmb->answers, + nmb->header.ancount * sizeof(struct res_rec)); + } + if (nmb->nsrecs) { + if((copy_nmb->nsrecs = SMB_MALLOC_ARRAY( + struct res_rec, nmb->header.nscount)) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->nsrecs, (char *)nmb->nsrecs, + nmb->header.nscount * sizeof(struct res_rec)); + } + if (nmb->additional) { + if((copy_nmb->additional = SMB_MALLOC_ARRAY( + struct res_rec, nmb->header.arcount)) == NULL) + goto free_and_exit; + memcpy((char *)copy_nmb->additional, (char *)nmb->additional, + nmb->header.arcount * sizeof(struct res_rec)); + } + + return pkt_copy; + + free_and_exit: + + SAFE_FREE(copy_nmb->answers); + SAFE_FREE(copy_nmb->nsrecs); + SAFE_FREE(copy_nmb->additional); + SAFE_FREE(pkt_copy); + + DEBUG(0,("copy_nmb_packet: malloc fail in resource records.\n")); + return NULL; +} + +/******************************************************************* + 'Copy constructor' for a dgram packet. +******************************************************************/ + +static struct packet_struct *copy_dgram_packet(struct packet_struct *packet) +{ + struct packet_struct *pkt_copy; + + if(( pkt_copy = SMB_MALLOC_P(struct packet_struct)) == NULL) { + DEBUG(0,("copy_dgram_packet: malloc fail.\n")); + return NULL; + } + + /* Structure copy of entire thing. */ + + *pkt_copy = *packet; + + /* Ensure this copy is not locked. */ + pkt_copy->locked = False; + pkt_copy->recv_fd = -1; + pkt_copy->send_fd = -1; + + /* There are no additional pointers in a dgram packet, + we are finished. */ + return pkt_copy; +} + +/******************************************************************* + 'Copy constructor' for a generic packet. +******************************************************************/ + +struct packet_struct *copy_packet(struct packet_struct *packet) +{ + if(packet->packet_type == NMB_PACKET) + return copy_nmb_packet(packet); + else if (packet->packet_type == DGRAM_PACKET) + return copy_dgram_packet(packet); + return NULL; +} + +/******************************************************************* + Free up any resources associated with an nmb packet. +******************************************************************/ + +static void free_nmb_packet(struct nmb_packet *nmb) +{ + SAFE_FREE(nmb->answers); + SAFE_FREE(nmb->nsrecs); + SAFE_FREE(nmb->additional); +} + +/******************************************************************* + Free up any resources associated with a dgram packet. +******************************************************************/ + +static void free_dgram_packet(struct dgram_packet *nmb) +{ + /* We have nothing to do for a dgram packet. */ +} + +/******************************************************************* + Free up any resources associated with a packet. +******************************************************************/ + +void free_packet(struct packet_struct *packet) +{ + if (packet->locked) + return; + if (packet->packet_type == NMB_PACKET) + free_nmb_packet(&packet->packet.nmb); + else if (packet->packet_type == DGRAM_PACKET) + free_dgram_packet(&packet->packet.dgram); + ZERO_STRUCTPN(packet); + SAFE_FREE(packet); +} + +int packet_trn_id(struct packet_struct *p) +{ + int result; + switch (p->packet_type) { + case NMB_PACKET: + result = p->packet.nmb.header.name_trn_id; + break; + case DGRAM_PACKET: + result = p->packet.dgram.header.dgm_id; + break; + default: + result = -1; + } + return result; +} + +/******************************************************************* + Parse a packet buffer into a packet structure. +******************************************************************/ + +struct packet_struct *parse_packet(char *buf,int length, + enum packet_type packet_type, + struct in_addr ip, + int port) +{ + struct packet_struct *p; + bool ok=False; + + p = SMB_MALLOC_P(struct packet_struct); + if (!p) + return(NULL); + + ZERO_STRUCTP(p); /* initialize for possible padding */ + + p->next = NULL; + p->prev = NULL; + p->ip = ip; + p->port = port; + p->locked = False; + p->timestamp = time(NULL); + p->packet_type = packet_type; + + switch (packet_type) { + case NMB_PACKET: + ok = parse_nmb(buf,length,&p->packet.nmb); + break; + + case DGRAM_PACKET: + ok = parse_dgram(buf,length,&p->packet.dgram); + break; + } + + if (!ok) { + free_packet(p); + return NULL; + } + + return p; +} + +static struct packet_struct *copy_packet_talloc( + TALLOC_CTX *mem_ctx, const struct packet_struct *src) +{ + struct packet_struct *pkt; + + pkt = talloc_memdup(mem_ctx, src, sizeof(struct packet_struct)); + if (pkt == NULL) { + return NULL; + } + pkt->locked = false; + pkt->recv_fd = -1; + pkt->send_fd = -1; + + if (src->packet_type == NMB_PACKET) { + const struct nmb_packet *nsrc = &src->packet.nmb; + struct nmb_packet *ndst = &pkt->packet.nmb; + + if (nsrc->answers != NULL) { + ndst->answers = talloc_memdup( + pkt, nsrc->answers, + sizeof(struct res_rec) * nsrc->header.ancount); + if (ndst->answers == NULL) { + goto fail; + } + } + if (nsrc->nsrecs != NULL) { + ndst->nsrecs = talloc_memdup( + pkt, nsrc->nsrecs, + sizeof(struct res_rec) * nsrc->header.nscount); + if (ndst->nsrecs == NULL) { + goto fail; + } + } + if (nsrc->additional != NULL) { + ndst->additional = talloc_memdup( + pkt, nsrc->additional, + sizeof(struct res_rec) * nsrc->header.arcount); + if (ndst->additional == NULL) { + goto fail; + } + } + } + + return pkt; + + /* + * DGRAM packets have no substructures + */ + +fail: + TALLOC_FREE(pkt); + return NULL; +} + +struct packet_struct *parse_packet_talloc(TALLOC_CTX *mem_ctx, + char *buf,int length, + enum packet_type packet_type, + struct in_addr ip, + int port) +{ + struct packet_struct *pkt, *result; + + pkt = parse_packet(buf, length, packet_type, ip, port); + if (pkt == NULL) { + return NULL; + } + result = copy_packet_talloc(mem_ctx, pkt); + free_packet(pkt); + return result; +} + +/******************************************************************* + Send a udp packet on a already open socket. +******************************************************************/ + +static bool send_udp(int fd,char *buf,int len,struct in_addr ip,int port) +{ + bool ret = False; + int i; + struct sockaddr_in sock_out; + + /* set the address and port */ + memset((char *)&sock_out,'\0',sizeof(sock_out)); + putip((char *)&sock_out.sin_addr,(char *)&ip); + sock_out.sin_port = htons( port ); + sock_out.sin_family = AF_INET; + + DEBUG( 5, ( "Sending a packet of len %d to (%s) on port %d\n", + len, inet_ntoa(ip), port ) ); + + /* + * Patch to fix asynch error notifications from Linux kernel. + */ + + for (i = 0; i < 5; i++) { + ret = (sendto(fd,buf,len,0,(struct sockaddr *)&sock_out, + sizeof(sock_out)) >= 0); + if (ret || errno != ECONNREFUSED) + break; + } + + if (!ret) + DEBUG(0,("Packet send failed to %s(%d) ERRNO=%s\n", + inet_ntoa(ip),port,strerror(errno))); + + return(ret); +} + +/******************************************************************* + Build a dgram packet ready for sending. + If buf == NULL this is a length calculation. +******************************************************************/ + +static int build_dgram(char *buf, size_t len, struct dgram_packet *dgram) +{ + unsigned char *ubuf = (unsigned char *)buf; + int offset=0; + + /* put in the header */ + if (buf) { + ubuf[0] = dgram->header.msg_type; + ubuf[1] = (((int)dgram->header.flags.node_type)<<2); + if (dgram->header.flags.more) + ubuf[1] |= 1; + if (dgram->header.flags.first) + ubuf[1] |= 2; + RSSVAL(ubuf,2,dgram->header.dgm_id); + putip(ubuf+4,(char *)&dgram->header.source_ip); + RSSVAL(ubuf,8,dgram->header.source_port); + RSSVAL(ubuf,12,dgram->header.packet_offset); + } + + offset = 14; + + if (dgram->header.msg_type == 0x10 || + dgram->header.msg_type == 0x11 || + dgram->header.msg_type == 0x12) { + offset += put_nmb_name((char *)ubuf,len,offset,&dgram->source_name); + offset += put_nmb_name((char *)ubuf,len,offset,&dgram->dest_name); + } + + if (buf) { + memcpy(ubuf+offset,dgram->data,dgram->datasize); + } + offset += dgram->datasize; + + /* automatically set the dgm_length + * NOTE: RFC1002 says the dgm_length does *not* + * include the fourteen-byte header. crh + */ + dgram->header.dgm_length = (offset - 14); + if (buf) { + RSSVAL(ubuf,10,dgram->header.dgm_length); + } + + return offset; +} + +/******************************************************************* + Build a nmb name +*******************************************************************/ + +void make_nmb_name( struct nmb_name *n, const char *name, int type) +{ + fstring unix_name; + memset( (char *)n, '\0', sizeof(struct nmb_name) ); + fstrcpy(unix_name, name); + (void)strupper_m(unix_name); + push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE); + n->name_type = (unsigned int)type & 0xFF; + push_ascii(n->scope, lp_netbios_scope(), 64, STR_TERMINATE); +} + +/******************************************************************* + Compare two nmb names +******************************************************************/ + +bool nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2) +{ + return ((n1->name_type == n2->name_type) && + strequal(n1->name ,n2->name ) && + strequal(n1->scope,n2->scope)); +} + +/******************************************************************* + Build a nmb packet ready for sending. + If buf == NULL this is a length calculation. +******************************************************************/ + +static int build_nmb(char *buf, size_t len, struct nmb_packet *nmb) +{ + unsigned char *ubuf = (unsigned char *)buf; + int offset=0; + + if (len && len < 12) { + return 0; + } + + /* put in the header */ + if (buf) { + RSSVAL(ubuf,offset,nmb->header.name_trn_id); + ubuf[offset+2] = (nmb->header.opcode & 0xF) << 3; + if (nmb->header.response) + ubuf[offset+2] |= (1<<7); + if (nmb->header.nm_flags.authoritative && + nmb->header.response) + ubuf[offset+2] |= 0x4; + if (nmb->header.nm_flags.trunc) + ubuf[offset+2] |= 0x2; + if (nmb->header.nm_flags.recursion_desired) + ubuf[offset+2] |= 0x1; + if (nmb->header.nm_flags.recursion_available && + nmb->header.response) + ubuf[offset+3] |= 0x80; + if (nmb->header.nm_flags.bcast) + ubuf[offset+3] |= 0x10; + ubuf[offset+3] |= (nmb->header.rcode & 0xF); + + RSSVAL(ubuf,offset+4,nmb->header.qdcount); + RSSVAL(ubuf,offset+6,nmb->header.ancount); + RSSVAL(ubuf,offset+8,nmb->header.nscount); + RSSVAL(ubuf,offset+10,nmb->header.arcount); + } + + offset += 12; + if (nmb->header.qdcount) { + /* XXXX this doesn't handle a qdcount of > 1 */ + if (len) { + /* Length check. */ + int extra = put_nmb_name(NULL,0,offset, + &nmb->question.question_name); + if (offset + extra > len) { + return 0; + } + } + offset += put_nmb_name((char *)ubuf,len,offset, + &nmb->question.question_name); + if (buf) { + RSSVAL(ubuf,offset,nmb->question.question_type); + RSSVAL(ubuf,offset+2,nmb->question.question_class); + } + offset += 4; + } + + if (nmb->header.ancount) { + if (len) { + /* Length check. */ + int extra = put_res_rec(NULL,0,offset,nmb->answers, + nmb->header.ancount); + if (offset + extra > len) { + return 0; + } + } + offset += put_res_rec((char *)ubuf,len,offset,nmb->answers, + nmb->header.ancount); + } + + if (nmb->header.nscount) { + if (len) { + /* Length check. */ + int extra = put_res_rec(NULL,0,offset,nmb->nsrecs, + nmb->header.nscount); + if (offset + extra > len) { + return 0; + } + } + offset += put_res_rec((char *)ubuf,len,offset,nmb->nsrecs, + nmb->header.nscount); + } + + /* + * The spec says we must put compressed name pointers + * in the following outgoing packets : + * NAME_REGISTRATION_REQUEST, NAME_REFRESH_REQUEST, + * NAME_RELEASE_REQUEST. + */ + + if((nmb->header.response == False) && + ((nmb->header.opcode == NMB_NAME_REG_OPCODE) || + (nmb->header.opcode == NMB_NAME_RELEASE_OPCODE) || + (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_8) || + (nmb->header.opcode == NMB_NAME_REFRESH_OPCODE_9) || + (nmb->header.opcode == NMB_NAME_MULTIHOMED_REG_OPCODE)) && + (nmb->header.arcount == 1)) { + + if (len) { + /* Length check. */ + int extra = put_compressed_name_ptr(NULL,offset, + nmb->additional,12); + if (offset + extra > len) { + return 0; + } + } + offset += put_compressed_name_ptr(ubuf,offset, + nmb->additional,12); + } else if (nmb->header.arcount) { + if (len) { + /* Length check. */ + int extra = put_res_rec(NULL,0,offset,nmb->additional, + nmb->header.arcount); + if (offset + extra > len) { + return 0; + } + } + offset += put_res_rec((char *)ubuf,len,offset,nmb->additional, + nmb->header.arcount); + } + return offset; +} + +/******************************************************************* + Linearise a packet. +******************************************************************/ + +int build_packet(char *buf, size_t buflen, struct packet_struct *p) +{ + int len = 0; + + switch (p->packet_type) { + case NMB_PACKET: + len = build_nmb(buf,buflen,&p->packet.nmb); + break; + + case DGRAM_PACKET: + len = build_dgram(buf,buflen,&p->packet.dgram); + break; + } + + return len; +} + +/******************************************************************* + Send a packet_struct. +******************************************************************/ + +bool send_packet(struct packet_struct *p) +{ + char buf[1024]; + int len=0; + + memset(buf,'\0',sizeof(buf)); + + len = build_packet(buf, sizeof(buf), p); + + if (!len) + return(False); + + return(send_udp(p->send_fd,buf,len,p->ip,p->port)); +} + +/**************************************************************************** + Receive a UDP/138 packet either via UDP or from the unexpected packet + queue. The packet must be a reply packet and have the specified mailslot name + The timeout is in milliseconds. +***************************************************************************/ + +/**************************************************************************** + See if a datagram has the right mailslot name. +***************************************************************************/ + +bool match_mailslot_name(struct packet_struct *p, const char *mailslot_name) +{ + struct dgram_packet *dgram = &p->packet.dgram; + char *buf; + + buf = &dgram->data[0]; + buf -= 4; + + buf = smb_buf(buf); + + if (memcmp(buf, mailslot_name, strlen(mailslot_name)+1) == 0) { + return True; + } + + return False; +} + +/**************************************************************************** + Return the number of bits that match between two len character buffers +***************************************************************************/ + +int matching_len_bits(const unsigned char *p1, const unsigned char *p2, size_t len) +{ + size_t i, j; + int ret = 0; + for (i=0; i<len; i++) { + if (p1[i] != p2[i]) + break; + ret += 8; + } + + if (i==len) + return ret; + + for (j=0; j<8; j++) { + if ((p1[i] & (1<<(7-j))) != (p2[i] & (1<<(7-j)))) + break; + ret++; + } + + return ret; +} + +static unsigned char sort_ip[4]; + +/**************************************************************************** + Compare two query reply records. +***************************************************************************/ + +static int name_query_comp(unsigned char *p1, unsigned char *p2) +{ + return matching_len_bits(p2+2, sort_ip, 4) - + matching_len_bits(p1+2, sort_ip, 4); +} + +/**************************************************************************** + Sort a set of 6 byte name query response records so that the IPs that + have the most leading bits in common with the specified address come first. +***************************************************************************/ + +void sort_query_replies(char *data, int n, struct in_addr ip) +{ + if (n <= 1) + return; + + putip(sort_ip, (char *)&ip); + + /* TODO: + this can't use TYPESAFE_QSORT() as the types are wrong. + It should be fixed to use a real type instead of char* + */ + qsort(data, n, 6, QSORT_CAST name_query_comp); +} + +/**************************************************************************** + Interpret the weird netbios "name" into a unix fstring. Return the name type. + Returns -1 on error. +****************************************************************************/ + +static int name_interpret(unsigned char *buf, size_t buf_len, + unsigned char *in, fstring name) +{ + unsigned char *end_ptr = buf + buf_len; + int ret; + unsigned int len; + fstring out_string; + unsigned char *out = (unsigned char *)out_string; + + *out=0; + + if (in >= end_ptr) { + return -1; + } + len = (*in++) / 2; + + if (len<1) { + return -1; + } + + while (len--) { + if (&in[1] >= end_ptr) { + return -1; + } + if (in[0] < 'A' || in[0] > 'P' || in[1] < 'A' || in[1] > 'P') { + *out = 0; + return(0); + } + *out = ((in[0]-'A')<<4) + (in[1]-'A'); + in += 2; + out++; + if (PTR_DIFF(out,out_string) >= sizeof(fstring)) { + return -1; + } + } + ret = out[-1]; + out[-1] = 0; + + pull_ascii_fstring(name, out_string); + + return(ret); +} + +/**************************************************************************** + Mangle a name into netbios format. + Note: <Out> must be (33 + strlen(scope) + 2) bytes long, at minimum. +****************************************************************************/ + +char *name_mangle(TALLOC_CTX *mem_ctx, const char *In, char name_type) +{ + int i; + int len; + nstring buf; + char *result; + char *p; + + result = talloc_array(mem_ctx, char, 33 + strlen(lp_netbios_scope()) + 2); + if (result == NULL) { + return NULL; + } + p = result; + + /* Safely copy the input string, In, into buf[]. */ + if (strcmp(In,"*") == 0) + put_name(buf, "*", '\0', 0x00); + else { + /* We use an fstring here as mb dos names can expend x3 when + going to utf8. */ + fstring buf_unix; + nstring buf_dos; + + pull_ascii_fstring(buf_unix, In); + if (!strupper_m(buf_unix)) { + return NULL; + } + + push_ascii_nstring(buf_dos, buf_unix); + put_name(buf, buf_dos, ' ', name_type); + } + + /* Place the length of the first field into the output buffer. */ + p[0] = 32; + p++; + + /* Now convert the name to the rfc1001/1002 format. */ + for( i = 0; i < MAX_NETBIOSNAME_LEN; i++ ) { + p[i*2] = ( (buf[i] >> 4) & 0x000F ) + 'A'; + p[(i*2)+1] = (buf[i] & 0x000F) + 'A'; + } + p += 32; + p[0] = '\0'; + + /* Add the scope string. */ + for( i = 0, len = 0; *(lp_netbios_scope()) != '\0'; i++, len++ ) { + switch( (lp_netbios_scope())[i] ) { + case '\0': + p[0] = len; + if( len > 0 ) + p[len+1] = 0; + return result; + case '.': + p[0] = len; + p += (len + 1); + len = -1; + break; + default: + p[len+1] = (lp_netbios_scope())[i]; + break; + } + } + + return result; +} + +/**************************************************************************** + Find a pointer to a netbios name. +****************************************************************************/ + +static unsigned char *name_ptr(unsigned char *buf, size_t buf_len, unsigned int ofs) +{ + unsigned char c = 0; + + if (ofs > buf_len || buf_len < 1) { + return NULL; + } + + c = *(unsigned char *)(buf+ofs); + if ((c & 0xC0) == 0xC0) { + uint16_t l = 0; + + if (ofs > buf_len - 1) { + return NULL; + } + l = RSVAL(buf, ofs) & 0x3FFF; + if (l > buf_len) { + return NULL; + } + DEBUG(5,("name ptr to pos %d from %d is %s\n",l,ofs,buf+l)); + return(buf + l); + } else { + return(buf+ofs); + } +} + +/**************************************************************************** + Extract a netbios name from a buf (into a unix string) return name type. + Returns -1 on error. +****************************************************************************/ + +int name_extract(unsigned char *buf, size_t buf_len, unsigned int ofs, fstring name) +{ + unsigned char *p = name_ptr(buf,buf_len,ofs); + + name[0] = '\0'; + if (p == NULL) { + return -1; + } + return(name_interpret(buf,buf_len,p,name)); +} + +/**************************************************************************** + Return the total storage length of a mangled name. + Returns -1 on error. +****************************************************************************/ + +int name_len(unsigned char *s1, size_t buf_len) +{ + /* NOTE: this argument _must_ be unsigned */ + unsigned char *s = (unsigned char *)s1; + int len = 0; + + if (buf_len < 1) { + return -1; + } + /* If the two high bits of the byte are set, return 2. */ + if (0xC0 == (*s & 0xC0)) { + if (buf_len < 2) { + return -1; + } + return(2); + } + + /* Add up the length bytes. */ + for (len = 1; (*s); s += (*s) + 1) { + len += *s + 1; + if (len > buf_len) { + return -1; + } + } + + return(len); +} + +/******************************************************************* + Setup the word count and byte count for a client smb message. +********************************************************************/ + +int cli_set_message(char *buf,int num_words,int num_bytes,bool zero) +{ + if (zero && (num_words || num_bytes)) { + memset(buf + smb_size,'\0',num_words*2 + num_bytes); + } + SCVAL(buf,smb_wct,num_words); + SSVAL(buf,smb_vwv + num_words*SIZEOFWORD,num_bytes); + smb_setlen(buf,smb_size + num_words*2 + num_bytes - 4); + return (smb_size + num_words*2 + num_bytes); +} diff --git a/source3/libsmb/nmblib.h b/source3/libsmb/nmblib.h new file mode 100644 index 0000000..52600a4 --- /dev/null +++ b/source3/libsmb/nmblib.h @@ -0,0 +1,59 @@ +/* + Unix SMB/CIFS implementation. + handle unexpected packets + NBT netbios library routines + Copyright (C) Andrew Tridgell 1994-1998, 2000 + Copyright (C) Jeremy Allison 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#ifndef _LIBSMB_NMBLIB_H_ +#define _LIBSMB_NMBLIB_H_ + +/* The following definitions come from libsmb/unexpected.c */ + +#include "nameserv.h" + +/* The following definitions come from libsmb/nmblib.c */ + +void debug_nmb_packet(struct packet_struct *p); +void put_name(char *dest, const char *name, int pad, unsigned int name_type); +char *nmb_namestr(const struct nmb_name *n); +struct packet_struct *copy_packet(struct packet_struct *packet); +void free_packet(struct packet_struct *packet); +int packet_trn_id(struct packet_struct *p); +struct packet_struct *parse_packet(char *buf,int length, + enum packet_type packet_type, + struct in_addr ip, + int port); +struct packet_struct *parse_packet_talloc(TALLOC_CTX *mem_ctx, + char *buf,int length, + enum packet_type packet_type, + struct in_addr ip, + int port); +void make_nmb_name( struct nmb_name *n, const char *name, int type); +bool nmb_name_equal(struct nmb_name *n1, struct nmb_name *n2); +int build_packet(char *buf, size_t buflen, struct packet_struct *p); +bool send_packet(struct packet_struct *p); +bool match_mailslot_name(struct packet_struct *p, const char *mailslot_name); +int matching_len_bits(const unsigned char *p1, const unsigned char *p2, size_t len); +void sort_query_replies(char *data, int n, struct in_addr ip); +char *name_mangle(TALLOC_CTX *mem_ctx, const char *In, char name_type); +int name_extract(unsigned char *buf,size_t buf_len, unsigned int ofs, fstring name); +int name_len(unsigned char *s1, size_t buf_len); +int cli_set_message(char *buf,int num_words,int num_bytes,bool zero); + +#endif /* _LIBSMB_NMBLIB_H_ */ diff --git a/source3/libsmb/passchange.c b/source3/libsmb/passchange.c new file mode 100644 index 0000000..dff5ad1 --- /dev/null +++ b/source3/libsmb/passchange.c @@ -0,0 +1,319 @@ +/* + Unix SMB/CIFS implementation. + SMB client password change routine + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../librpc/gen_ndr/ndr_samr.h" +#include "rpc_client/cli_pipe.h" +#include "rpc_client/cli_samr.h" +#include "libsmb/libsmb.h" +#include "libsmb/clirap.h" +#include "libsmb/nmblib.h" +#include "../libcli/smb/smbXcli_base.h" + +/************************************************************* + Change a password on a remote machine using IPC calls. +*************************************************************/ + +NTSTATUS remote_password_change(const char *remote_machine, + const char *domain, const char *user_name, + const char *old_passwd, const char *new_passwd, + char **err_str) +{ + struct cli_state *cli = NULL; + struct cli_credentials *creds = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + NTSTATUS status; + NTSTATUS result; + bool pass_must_change = False; + + *err_str = NULL; + + result = cli_connect_nb(remote_machine, NULL, 0, 0x20, NULL, + SMB_SIGNING_IPC_DEFAULT, 0, &cli); + if (!NT_STATUS_IS_OK(result)) { + if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) { + if (asprintf(err_str, "Unable to connect to SMB server on " + "machine %s. NetBIOS support disabled\n", + remote_machine) == -1) { + *err_str = NULL; + } + } else { + if (asprintf(err_str, "Unable to connect to SMB server on " + "machine %s. Error was : %s.\n", + remote_machine, nt_errstr(result))==-1) { + *err_str = NULL; + } + } + return result; + } + + creds = cli_session_creds_init(cli, + user_name, + domain, + NULL, /* realm */ + old_passwd, + false, /* use_kerberos */ + false, /* fallback_after_kerberos */ + false, /* use_ccache */ + false); /* password_is_nt_hash */ + SMB_ASSERT(creds != NULL); + + result = smbXcli_negprot(cli->conn, cli->timeout, + lp_client_ipc_min_protocol(), + lp_client_ipc_max_protocol()); + + if (!NT_STATUS_IS_OK(result)) { + if (asprintf(err_str, "machine %s rejected the negotiate " + "protocol. Error was : %s.\n", + remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + + /* Given things like SMB signing, restrict anonymous and the like, + try an authenticated connection first */ + result = cli_session_setup_creds(cli, creds); + + if (!NT_STATUS_IS_OK(result)) { + + /* Password must change or Password expired are the only valid + * error conditions here from where we can proceed, the rest like + * account locked out or logon failure will lead to errors later + * anyway */ + + if (!NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_MUST_CHANGE) && + !NT_STATUS_EQUAL(result, NT_STATUS_PASSWORD_EXPIRED)) { + if (asprintf(err_str, "Could not connect to machine %s: " + "%s\n", remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + + pass_must_change = True; + + /* + * We should connect as the anonymous user here, in case + * the server has "must change password" checked... + * Thanks to <Nicholas.S.Jenkins@cdc.com> for this fix. + */ + + result = cli_session_setup_anon(cli); + + if (!NT_STATUS_IS_OK(result)) { + if (asprintf(err_str, "machine %s rejected the session " + "setup. Error was : %s.\n", + remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + } + + result = cli_tree_connect(cli, "IPC$", "IPC", NULL); + if (!NT_STATUS_IS_OK(result)) { + if (asprintf(err_str, "machine %s rejected the tconX on the " + "IPC$ share. Error was : %s.\n", + remote_machine, nt_errstr(result))) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + + /* Try not to give the password away too easily */ + + if (!pass_must_change) { + const struct sockaddr_storage *remote_sockaddr = + smbXcli_conn_remote_sockaddr(cli->conn); + + result = cli_rpc_pipe_open_with_creds(cli, + &ndr_table_samr, + NCACN_NP, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_PRIVACY, + remote_machine, + remote_sockaddr, + creds, + &pipe_hnd); + } else { + /* + * If the user password must be changed the ntlmssp bind will + * fail the same way as the session setup above did. The + * difference ist that with a pipe bind we don't get a good + * error message, the result will be that the rpc call below + * will just fail. So we do it anonymously, there's no other + * way. + */ + result = cli_rpc_pipe_open_noauth( + cli, &ndr_table_samr, &pipe_hnd); + } + + if (!NT_STATUS_IS_OK(result)) { + if (lp_client_lanman_auth()) { + /* Use the old RAP method. */ + if (!cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { + result = cli_nt_error(cli); + if (asprintf(err_str, "machine %s rejected the " + "password change: Error was : %s.\n", + remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + } else { + if (asprintf(err_str, "SAMR connection to machine %s " + "failed. Error was %s, but LANMAN password " + "changes are disabled\n", + remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + } + + status = dcerpc_samr_chgpasswd_user4(pipe_hnd->binding_handle, + talloc_tos(), + pipe_hnd->srv_name_slash, + user_name, + old_passwd, + new_passwd, + &result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(result)) { + /* All good, password successfully changed. */ + cli_shutdown(cli); + return NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, + NT_STATUS_RPC_PROCNUM_OUT_OF_RANGE) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED) || + NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)) { + /* DO NOT FALLBACK TO RC4 */ + if (lp_weak_crypto() == SAMBA_WEAK_CRYPTO_DISALLOWED) { + cli_shutdown(cli); + return NT_STATUS_STRONG_CRYPTO_NOT_SUPPORTED; + } + } + } else { + if (!NT_STATUS_IS_OK(result)) { + int rc = asprintf( + err_str, + "machine %s rejected to change the password" + "with error: %s", + remote_machine, + get_friendly_nt_error_msg(result)); + if (rc <= 0) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + } + + result = rpccli_samr_chgpasswd_user2(pipe_hnd, talloc_tos(), + user_name, new_passwd, old_passwd); + if (NT_STATUS_IS_OK(result)) { + /* Great - it all worked! */ + cli_shutdown(cli); + return NT_STATUS_OK; + + } else if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) + || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { + /* it failed, but for reasons such as wrong password, too short etc ... */ + + if (asprintf(err_str, "machine %s rejected the password change: " + "Error was : %s.\n", + remote_machine, get_friendly_nt_error_msg(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + + /* OK, that failed, so try again... */ + TALLOC_FREE(pipe_hnd); + + /* Try anonymous NTLMSSP... */ + result = NT_STATUS_UNSUCCESSFUL; + + /* OK, this is ugly, but... try an anonymous pipe. */ + result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr, + &pipe_hnd); + + if ( NT_STATUS_IS_OK(result) && + (NT_STATUS_IS_OK(result = rpccli_samr_chgpasswd_user2( + pipe_hnd, talloc_tos(), user_name, + new_passwd, old_passwd)))) { + /* Great - it all worked! */ + cli_shutdown(cli); + return NT_STATUS_OK; + } else { + if (!(NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) + || NT_STATUS_EQUAL(result, NT_STATUS_UNSUCCESSFUL))) { + /* it failed, but again it was due to things like new password too short */ + + if (asprintf(err_str, "machine %s rejected the " + "(anonymous) password change: Error was : " + "%s.\n", remote_machine, + get_friendly_nt_error_msg(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } + + /* We have failed to change the user's password, and we think the server + just might not support SAMR password changes, so fall back */ + + if (lp_client_lanman_auth()) { + /* Use the old RAP method. */ + if (cli_oem_change_password(cli, user_name, new_passwd, old_passwd)) { + /* SAMR failed, but the old LanMan protocol worked! */ + + cli_shutdown(cli); + return NT_STATUS_OK; + } + + result = cli_nt_error(cli); + if (asprintf(err_str, "machine %s rejected the password " + "change: Error was : %s.\n", + remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return result; + } else { + if (asprintf(err_str, "SAMR connection to machine %s " + "failed. Error was %s, but LANMAN password " + "changes are disabled\n", + remote_machine, nt_errstr(result)) == -1) { + *err_str = NULL; + } + cli_shutdown(cli); + return NT_STATUS_UNSUCCESSFUL; + } + } +} diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h new file mode 100644 index 0000000..ec66582 --- /dev/null +++ b/source3/libsmb/proto.h @@ -0,0 +1,1054 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Andrew Bartlett 2001-2003 + Copyright (C) Andrew Tridgell 1994-1998,2000-2001 + Copyright (C) Gerald (Jerry) Carter 2004 + Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Jeremy Allison 2001-2009,2011 + Copyright (C) Stefan Metzmacher 2003,2009 + Copyright (C) Volker Lendecke 2011 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _LIBSMB_PROTO_H_ +#define _LIBSMB_PROTO_H_ + +struct smb_trans_enc_state; +struct cli_credentials; +struct cli_state; +struct file_info; +struct print_job_info; + +/* The following definitions come from libsmb/cliconnect.c */ + +struct cli_credentials *cli_session_creds_init(TALLOC_CTX *mem_ctx, + const char *username, + const char *domain, + const char *realm, + const char *password, + bool use_kerberos, + bool fallback_after_kerberos, + bool use_ccache, + bool password_is_nt_hash); +NTSTATUS cli_session_creds_prepare_krb5(struct cli_state *cli, + struct cli_credentials *creds); +struct tevent_req *cli_session_setup_creds_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + struct cli_credentials *creds); +NTSTATUS cli_session_setup_creds_recv(struct tevent_req *req); +NTSTATUS cli_session_setup_creds(struct cli_state *cli, + struct cli_credentials *creds); +NTSTATUS cli_session_setup_anon(struct cli_state *cli); +struct tevent_req *cli_session_setup_guest_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + struct tevent_req **psmbreq); +struct tevent_req *cli_session_setup_guest_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli); +NTSTATUS cli_session_setup_guest_recv(struct tevent_req *req); +NTSTATUS cli_ulogoff(struct cli_state *cli); +struct tevent_req *cli_tcon_andx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen, + struct tevent_req **psmbreq); +struct tevent_req *cli_tcon_andx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *share, const char *dev, + const char *pass, int passlen); +NTSTATUS cli_tcon_andx_recv(struct tevent_req *req); +NTSTATUS cli_tcon_andx(struct cli_state *cli, const char *share, + const char *dev, const char *pass, int passlen); +NTSTATUS cli_tree_connect_creds(struct cli_state *cli, + const char *share, const char *dev, + struct cli_credentials *creds); +NTSTATUS cli_tree_connect(struct cli_state *cli, const char *share, + const char *dev, const char *pass); +NTSTATUS cli_tdis(struct cli_state *cli); +NTSTATUS cli_connect_nb(const char *host, const struct sockaddr_storage *dest_ss, + uint16_t port, int name_type, const char *myname, + enum smb_signing_setting signing_state, int flags, struct cli_state **pcli); +NTSTATUS cli_start_connection(struct cli_state **output_cli, + const char *my_name, + const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + enum smb_signing_setting signing_state, int flags); +NTSTATUS cli_smb1_setup_encryption(struct cli_state *cli, + struct cli_credentials *creds); +struct tevent_req *cli_full_connection_creds_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + const char *my_name, const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + const char *service, const char *service_type, + struct cli_credentials *creds, + int flags); +NTSTATUS cli_full_connection_creds_recv(struct tevent_req *req, + struct cli_state **output_cli); +NTSTATUS cli_full_connection_creds(struct cli_state **output_cli, + const char *my_name, + const char *dest_host, + const struct sockaddr_storage *dest_ss, int port, + const char *service, const char *service_type, + struct cli_credentials *creds, + int flags); +NTSTATUS cli_raw_tcon(struct cli_state *cli, + const char *service, const char *pass, const char *dev, + uint16_t *max_xmit, uint16_t *tid); +struct cli_state *get_ipc_connect(char *server, + struct sockaddr_storage *server_ss, + struct cli_credentials *creds); +struct cli_state *get_ipc_connect_master_ip(TALLOC_CTX *ctx, + struct sockaddr_storage *mb_ip, + struct cli_credentials *creds, + char **pp_workgroup_out); + +/* The following definitions come from libsmb/clidfs.c */ + +NTSTATUS cli_cm_open(TALLOC_CTX *ctx, + struct cli_state *referring_cli, + const char *server, + const char *share, + struct cli_credentials *creds, + const struct sockaddr_storage *dest_ss, + int port, + int name_type, + struct cli_state **pcli); +void cli_cm_display(struct cli_state *c); +struct client_dfs_referral; +NTSTATUS cli_dfs_get_referral_ex(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *path, + uint16_t max_referral_level, + struct client_dfs_referral **refs, + size_t *num_refs, + size_t *consumed); +NTSTATUS cli_dfs_get_referral(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *path, + struct client_dfs_referral **refs, + size_t *num_refs, + size_t *consumed); +NTSTATUS cli_resolve_path(TALLOC_CTX *ctx, + const char *mountpt, + struct cli_credentials *creds, + struct cli_state *rootcli, + const char *path, + struct cli_state **targetcli, + char **pp_targetpath); + +bool cli_check_msdfs_proxy(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *sharename, + char **pp_newserver, + char **pp_newshare, + struct cli_credentials *creds); + +NTSTATUS cli_dfs_target_check(TALLOC_CTX *mem_ctx, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + const char **fname_dst_out); + +/* The following definitions come from libsmb/clientgen.c */ + +unsigned int cli_set_timeout(struct cli_state *cli, unsigned int timeout); +bool cli_set_backup_intent(struct cli_state *cli, bool flag); +extern struct GUID cli_state_client_guid; +struct cli_state *cli_state_create(TALLOC_CTX *mem_ctx, + int fd, + const char *remote_name, + enum smb_signing_setting signing_state, + int flags); +void cli_shutdown(struct cli_state *cli); +uint16_t cli_state_get_vc_num(struct cli_state *cli); +uint32_t cli_setpid(struct cli_state *cli, uint32_t pid); +uint32_t cli_getpid(struct cli_state *cli); +bool cli_state_is_encryption_on(struct cli_state *cli); +bool cli_state_has_tcon(struct cli_state *cli); +uint32_t cli_state_get_tid(struct cli_state *cli); +uint32_t cli_state_set_tid(struct cli_state *cli, uint32_t tid); +struct smbXcli_tcon; +struct smbXcli_tcon *cli_state_save_tcon(struct cli_state *cli); +void cli_state_restore_tcon(struct cli_state *cli, struct smbXcli_tcon *tcon); +uint16_t cli_state_get_uid(struct cli_state *cli); +uint16_t cli_state_set_uid(struct cli_state *cli, uint16_t uid); +bool cli_set_case_sensitive(struct cli_state *cli, bool case_sensitive); +uint32_t cli_state_available_size(struct cli_state *cli, uint32_t ofs); +time_t cli_state_server_time(struct cli_state *cli); +struct tevent_req *cli_echo_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, uint16_t num_echos, + DATA_BLOB data); +NTSTATUS cli_echo_recv(struct tevent_req *req); +NTSTATUS cli_echo(struct cli_state *cli, uint16_t num_echos, DATA_BLOB data); +NTSTATUS cli_smb(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t smb_command, uint8_t additional_flags, + uint8_t wct, uint16_t *vwv, + uint32_t num_bytes, const uint8_t *bytes, + struct tevent_req **result_parent, + uint8_t min_wct, uint8_t *pwct, uint16_t **pvwv, + uint32_t *pnum_bytes, uint8_t **pbytes); + +/* The following definitions come from libsmb/clierror.c */ + +NTSTATUS cli_nt_error(struct cli_state *cli); +void cli_dos_error(struct cli_state *cli, uint8_t *eclass, uint32_t *ecode); +int cli_status_to_errno(NTSTATUS status); +int cli_errno(struct cli_state *cli); +bool cli_is_error(struct cli_state *cli); +bool cli_is_nt_error(struct cli_state *cli); +bool cli_is_dos_error(struct cli_state *cli); +bool cli_state_is_connected(struct cli_state *cli); + +/* The following definitions come from libsmb/clifile.c */ + +struct tevent_req *cli_setpathinfo_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t level, + const char *path, + uint8_t *data, + size_t data_len); +NTSTATUS cli_setpathinfo_recv(struct tevent_req *req); +NTSTATUS cli_setpathinfo(struct cli_state *cli, + uint16_t level, + const char *path, + uint8_t *data, + size_t data_len); +struct tevent_req *cli_setfileinfo_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t level, + uint8_t *data, + size_t data_len); +NTSTATUS cli_setfileinfo_recv(struct tevent_req *req); + +struct tevent_req *cli_posix_symlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname); +NTSTATUS cli_posix_symlink_recv(struct tevent_req *req); +NTSTATUS cli_posix_symlink(struct cli_state *cli, + const char *oldname, + const char *newname); +struct tevent_req *cli_posix_readlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_posix_readlink_recv( + struct tevent_req *req, TALLOC_CTX *mem_ctx, char **target); +NTSTATUS cli_posix_readlink( + struct cli_state *cli, + const char *fname, + TALLOC_CTX *mem_ctx, + char **target); +struct tevent_req *cli_posix_hardlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *oldname, + const char *newname); +NTSTATUS cli_posix_hardlink_recv(struct tevent_req *req); +NTSTATUS cli_posix_hardlink(struct cli_state *cli, + const char *oldname, + const char *newname); +uint32_t unix_perms_to_wire(mode_t perms); +mode_t wire_perms_to_unix(uint32_t perms); +struct tevent_req *cli_posix_getacl_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_posix_getacl_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + size_t *prb_size, + char **retbuf); +NTSTATUS cli_posix_getacl(struct cli_state *cli, + const char *fname, + TALLOC_CTX *mem_ctx, + size_t *prb_size, + char **retbuf); +struct tevent_req *cli_posix_setacl_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + const void *acl_buf, + size_t acl_buf_size); +NTSTATUS cli_posix_setacl_recv(struct tevent_req *req); +NTSTATUS cli_posix_setacl(struct cli_state *cli, + const char *fname, + const void *acl_buf, + size_t acl_buf_size); +struct tevent_req *cli_posix_stat_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + SMB_STRUCT_STAT *sbuf); +NTSTATUS cli_posix_stat_recv(struct tevent_req *req); +NTSTATUS cli_posix_stat(struct cli_state *cli, + const char *fname, + SMB_STRUCT_STAT *sbuf); +struct tevent_req *cli_posix_chmod_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + mode_t mode); +NTSTATUS cli_posix_chmod_recv(struct tevent_req *req); +NTSTATUS cli_posix_chmod(struct cli_state *cli, const char *fname, mode_t mode); +struct tevent_req *cli_posix_chown_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uid_t uid, + gid_t gid); +NTSTATUS cli_posix_chown_recv(struct tevent_req *req); +NTSTATUS cli_posix_chown(struct cli_state *cli, + const char *fname, + uid_t uid, + gid_t gid); +struct tevent_req *cli_rename_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace); +NTSTATUS cli_rename_recv(struct tevent_req *req); +NTSTATUS cli_rename(struct cli_state *cli, + const char *fname_src, + const char *fname_dst, + bool replace); +struct tevent_req *cli_ntrename_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst); +NTSTATUS cli_ntrename_recv(struct tevent_req *req); +NTSTATUS cli_ntrename(struct cli_state *cli, const char *fname_src, const char *fname_dst); + +struct tevent_req *cli_hardlink_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname_src, + const char *fname_dst); +NTSTATUS cli_hardlink_recv(struct tevent_req *req); +NTSTATUS cli_hardlink( + struct cli_state *cli, + const char *fname_src, + const char *fname_dst); + +struct tevent_req *cli_unlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t mayhave_attrs); +NTSTATUS cli_unlink_recv(struct tevent_req *req); +NTSTATUS cli_unlink(struct cli_state *cli, const char *fname, uint32_t mayhave_attrs); + +struct tevent_req *cli_mkdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname); +NTSTATUS cli_mkdir_recv(struct tevent_req *req); +NTSTATUS cli_mkdir(struct cli_state *cli, const char *dname); +struct tevent_req *cli_rmdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *dname); +NTSTATUS cli_rmdir_recv(struct tevent_req *req); +NTSTATUS cli_rmdir(struct cli_state *cli, const char *dname); +struct tevent_req *cli_nt_delete_on_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + bool flag); +NTSTATUS cli_nt_delete_on_close_recv(struct tevent_req *req); +NTSTATUS cli_nt_delete_on_close(struct cli_state *cli, uint16_t fnum, bool flag); +struct tevent_req *cli_ntcreate_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint32_t ImpersonationLevel, + uint8_t SecurityFlags); +NTSTATUS cli_ntcreate_recv(struct tevent_req *req, + uint16_t *pfnum, + struct smb_create_returns *cr); +NTSTATUS cli_ntcreate(struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + uint16_t *pfid, + struct smb_create_returns *cr); +struct tevent_req *cli_openx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, const char *fname, + int flags, int share_mode, + struct tevent_req **psmbreq); +struct tevent_req *cli_openx_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, const char *fname, + int flags, int share_mode); +NTSTATUS cli_openx_recv(struct tevent_req *req, uint16_t *fnum); +NTSTATUS cli_openx(struct cli_state *cli, const char *fname, int flags, int share_mode, uint16_t *pfnum); +NTSTATUS cli_open(struct cli_state *cli, const char *fname, int flags, int share_mode, uint16_t *pfnum); +struct tevent_req *cli_smb1_close_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + struct tevent_req **psubreq); +struct tevent_req *cli_close_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum); +NTSTATUS cli_close_recv(struct tevent_req *req); +NTSTATUS cli_close(struct cli_state *cli, uint16_t fnum); +struct tevent_req *cli_ftruncate_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t size); +NTSTATUS cli_ftruncate_recv(struct tevent_req *req); +NTSTATUS cli_ftruncate(struct cli_state *cli, uint16_t fnum, uint64_t size); +NTSTATUS cli_locktype(struct cli_state *cli, uint16_t fnum, + uint32_t offset, uint32_t len, + int timeout, unsigned char locktype); +struct smb1_lock_element { + uint16_t pid; + uint64_t offset; + uint64_t length; +}; + +struct tevent_req *cli_lockingx_create( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t typeoflock, + uint8_t newoplocklevel, + int32_t timeout, + uint16_t num_unlocks, + const struct smb1_lock_element *unlocks, + uint16_t num_locks, + const struct smb1_lock_element *locks, + struct tevent_req **psmbreq); +struct tevent_req *cli_lockingx_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint8_t typeoflock, + uint8_t newoplocklevel, + int32_t timeout, + uint16_t num_unlocks, + const struct smb1_lock_element *unlocks, + uint16_t num_locks, + const struct smb1_lock_element *locks); +NTSTATUS cli_lockingx_recv(struct tevent_req *req); +NTSTATUS cli_lockingx( + struct cli_state *cli, + uint16_t fnum, + uint8_t typeoflock, + uint8_t newoplocklevel, + int32_t timeout, + uint16_t num_unlocks, + const struct smb1_lock_element *unlocks, + uint16_t num_locks, + const struct smb1_lock_element *locks); + +NTSTATUS cli_lock32(struct cli_state *cli, uint16_t fnum, uint32_t offset, + uint32_t len, int timeout, enum brl_type lock_type); +struct tevent_req *cli_unlock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len); +NTSTATUS cli_unlock_recv(struct tevent_req *req); +NTSTATUS cli_unlock(struct cli_state *cli, uint16_t fnum, uint32_t offset, uint32_t len); +struct tevent_req *cli_posix_lock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len, + bool wait_lock, + enum brl_type lock_type); +NTSTATUS cli_posix_lock_recv(struct tevent_req *req); +NTSTATUS cli_posix_lock(struct cli_state *cli, uint16_t fnum, + uint64_t offset, uint64_t len, + bool wait_lock, enum brl_type lock_type); +struct tevent_req *cli_posix_unlock_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint64_t offset, + uint64_t len); +NTSTATUS cli_posix_unlock_recv(struct tevent_req *req); +NTSTATUS cli_posix_unlock(struct cli_state *cli, uint16_t fnum, uint64_t offset, uint64_t len); +struct tevent_req *cli_getattrE_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum); +NTSTATUS cli_getattrE_recv(struct tevent_req *req, + uint32_t *pattr, + off_t *size, + time_t *change_time, + time_t *access_time, + time_t *write_time); +struct tevent_req *cli_setattrE_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time); +NTSTATUS cli_setattrE_recv(struct tevent_req *req); +NTSTATUS cli_setattrE(struct cli_state *cli, + uint16_t fnum, + time_t change_time, + time_t access_time, + time_t write_time); +struct tevent_req *cli_getatr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_getatr_recv(struct tevent_req *req, + uint32_t *pattr, + off_t *size, + time_t *write_time); +NTSTATUS cli_getatr(struct cli_state *cli, + const char *fname, + uint32_t *pattr, + off_t *size, + time_t *write_time); +struct tevent_req *cli_setatr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t attr, + time_t mtime); +NTSTATUS cli_setatr_recv(struct tevent_req *req); +NTSTATUS cli_setatr(struct cli_state *cli, + const char *fname, + uint32_t attr, + time_t mtime); +struct tevent_req *cli_chkpath_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_chkpath_recv(struct tevent_req *req); +NTSTATUS cli_chkpath(struct cli_state *cli, const char *path); +struct tevent_req *cli_dskattr_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli); +NTSTATUS cli_dskattr_recv(struct tevent_req *req, int *bsize, int *total, + int *avail); +NTSTATUS cli_dskattr(struct cli_state *cli, int *bsize, int *total, int *avail); +NTSTATUS cli_disk_size(struct cli_state *cli, const char *path, uint64_t *bsize, + uint64_t *total, uint64_t *avail); +struct tevent_req *cli_ctemp_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *path); +NTSTATUS cli_ctemp_recv(struct tevent_req *req, + TALLOC_CTX *ctx, + uint16_t *pfnum, + char **outfile); +NTSTATUS cli_ctemp(struct cli_state *cli, + TALLOC_CTX *ctx, + const char *path, + uint16_t *pfnum, + char **out_path); +NTSTATUS cli_raw_ioctl(struct cli_state *cli, uint16_t fnum, uint32_t code, DATA_BLOB *blob); +NTSTATUS cli_set_ea_path(struct cli_state *cli, const char *path, + const char *ea_name, const char *ea_val, + size_t ea_len); +NTSTATUS cli_set_ea_fnum(struct cli_state *cli, uint16_t fnum, + const char *ea_name, const char *ea_val, + size_t ea_len); +struct tevent_req *cli_get_ea_list_path_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_get_ea_list_path_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + size_t *pnum_eas, struct ea_struct **peas); +NTSTATUS cli_get_ea_list_path(struct cli_state *cli, const char *path, + TALLOC_CTX *ctx, + size_t *pnum_eas, + struct ea_struct **pea_list); +struct tevent_req *cli_posix_open_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + int flags, + mode_t mode); +NTSTATUS cli_posix_open_recv(struct tevent_req *req, uint16_t *pfnum); +NTSTATUS cli_posix_open(struct cli_state *cli, const char *fname, + int flags, mode_t mode, uint16_t *fnum); +struct tevent_req *cli_posix_mkdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + mode_t mode); +NTSTATUS cli_posix_mkdir_recv(struct tevent_req *req); +NTSTATUS cli_posix_mkdir(struct cli_state *cli, const char *fname, mode_t mode); + +struct tevent_req *cli_posix_unlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_posix_unlink_recv(struct tevent_req *req); +NTSTATUS cli_posix_unlink(struct cli_state *cli, const char *fname); + +struct tevent_req *cli_posix_rmdir_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_posix_rmdir_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx); +NTSTATUS cli_posix_rmdir(struct cli_state *cli, const char *fname); +struct tevent_req *cli_notify_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint32_t buffer_size, + uint32_t completion_filter, bool recursive); +NTSTATUS cli_notify_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint32_t *pnum_changes, + struct notify_change **pchanges); +NTSTATUS cli_notify(struct cli_state *cli, uint16_t fnum, uint32_t buffer_size, + uint32_t completion_filter, bool recursive, + TALLOC_CTX *mem_ctx, uint32_t *pnum_changes, + struct notify_change **pchanges); + +struct tevent_req *cli_nttrans_create_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + struct security_descriptor *secdesc, + struct ea_struct *eas, + int num_eas); +NTSTATUS cli_nttrans_create_recv(struct tevent_req *req, + uint16_t *fnum, + struct smb_create_returns *cr); +NTSTATUS cli_nttrans_create(struct cli_state *cli, + const char *fname, + uint32_t CreatFlags, + uint32_t DesiredAccess, + uint32_t FileAttributes, + uint32_t ShareAccess, + uint32_t CreateDisposition, + uint32_t CreateOptions, + uint8_t SecurityFlags, + struct security_descriptor *secdesc, + struct ea_struct *eas, + int num_eas, + uint16_t *pfid, + struct smb_create_returns *cr); + +/* The following definitions come from libsmb/clifsinfo.c */ + +struct tevent_req *cli_unix_extensions_version_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli); +NTSTATUS cli_unix_extensions_version_recv(struct tevent_req *req, + uint16_t *pmajor, uint16_t *pminor, + uint32_t *pcaplow, + uint32_t *pcaphigh); +NTSTATUS cli_unix_extensions_version(struct cli_state *cli, uint16_t *pmajor, + uint16_t *pminor, uint32_t *pcaplow, + uint32_t *pcaphigh); +struct tevent_req *cli_set_unix_extensions_capabilities_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct cli_state *cli, + uint16_t major, uint16_t minor, uint32_t caplow, uint32_t caphigh); +NTSTATUS cli_set_unix_extensions_capabilities_recv(struct tevent_req *req); +NTSTATUS cli_set_unix_extensions_capabilities(struct cli_state *cli, + uint16_t major, uint16_t minor, + uint32_t caplow, uint32_t caphigh); +struct tevent_req *cli_get_fs_attr_info_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli); +NTSTATUS cli_get_fs_attr_info_recv(struct tevent_req *req, uint32_t *fs_attr); +NTSTATUS cli_get_fs_attr_info(struct cli_state *cli, uint32_t *fs_attr); +NTSTATUS cli_get_fs_volume_info(struct cli_state *cli, + TALLOC_CTX *mem_ctx, char **volume_name, + uint32_t *pserial_number, time_t *pdate); +NTSTATUS cli_get_fs_full_size_info(struct cli_state *cli, + uint64_t *total_allocation_units, + uint64_t *caller_allocation_units, + uint64_t *actual_allocation_units, + uint64_t *sectors_per_allocation_unit, + uint64_t *bytes_per_sector); +NTSTATUS cli_get_posix_fs_info(struct cli_state *cli, + uint32_t *optimal_transfer_size, + uint32_t *block_size, + uint64_t *total_blocks, + uint64_t *blocks_available, + uint64_t *user_blocks_available, + uint64_t *total_file_nodes, + uint64_t *free_file_nodes, + uint64_t *fs_identifier); +struct tevent_req *cli_posix_whoami_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli); +NTSTATUS cli_posix_whoami_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint64_t *puid, + uint64_t *pgid, + uint32_t *pnum_gids, + uint64_t **pgids, + uint32_t *pnum_sids, + struct dom_sid **psids, + bool *pguest); +NTSTATUS cli_posix_whoami(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + uint64_t *puid, + uint64_t *pgid, + uint32_t *num_gids, + uint64_t **gids, + uint32_t *num_sids, + struct dom_sid **sids, + bool *pguest); + +/* The following definitions come from libsmb/clilist.c */ + +NTSTATUS is_bad_finfo_name(const struct cli_state *cli, + const struct file_info *finfo); + +NTSTATUS cli_list_old(struct cli_state *cli,const char *Mask,uint32_t attribute, + NTSTATUS (*fn)(struct file_info *, + const char *, void *), void *state); +NTSTATUS cli_list_trans(struct cli_state *cli, const char *mask, + uint32_t attribute, int info_level, + NTSTATUS (*fn)( + struct file_info *finfo, + const char *mask, + void *private_data), + void *private_data); +struct tevent_req *cli_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *mask, + uint32_t attribute, + uint16_t info_level); +NTSTATUS cli_list_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct file_info **pfinfo); +NTSTATUS cli_list(struct cli_state *cli, + const char *mask, + uint32_t attribute, + NTSTATUS (*fn)(struct file_info *finfo, + const char *mask, + void *private_data), + void *private_data); + +/* The following definitions come from libsmb/climessage.c */ + +struct tevent_req *cli_message_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *host, const char *username, + const char *message); +NTSTATUS cli_message_recv(struct tevent_req *req); +NTSTATUS cli_message(struct cli_state *cli, const char *host, + const char *username, const char *message); + +/* The following definitions come from libsmb/clioplock.c */ + +struct tevent_req *cli_smb_oplock_break_waiter_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli); +NTSTATUS cli_smb_oplock_break_waiter_recv(struct tevent_req *req, + uint16_t *pfnum, + uint8_t *plevel); + +struct tevent_req *cli_oplock_ack_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, uint8_t level); +NTSTATUS cli_oplock_ack_recv(struct tevent_req *req); + +/* The following definitions come from libsmb/cliprint.c */ + +NTSTATUS cli_print_queue(struct cli_state *cli, + void (*fn)(struct print_job_info *)); +int cli_printjob_del(struct cli_state *cli, int job); + +/* The following definitions come from libsmb/cliquota.c */ + +NTSTATUS cli_get_quota_handle(struct cli_state *cli, uint16_t *quota_fnum); +void free_ntquota_list(SMB_NTQUOTA_LIST **qt_list); +bool parse_user_quota_record(const uint8_t *rdata, + unsigned int rdata_count, + unsigned int *offset, + SMB_NTQUOTA_STRUCT *pqt); +bool add_record_to_ntquota_list(TALLOC_CTX *mem_ctx, + SMB_NTQUOTA_STRUCT *pqt, + SMB_NTQUOTA_LIST **pqt_list); +NTSTATUS parse_user_quota_list(const uint8_t *curdata, + uint32_t curdata_size, + TALLOC_CTX *mem_ctx, + SMB_NTQUOTA_LIST **pqt_list); +NTSTATUS parse_fs_quota_buffer(const uint8_t *rdata, + unsigned int rdata_count, + SMB_NTQUOTA_STRUCT *pqt); +NTSTATUS build_user_quota_buffer(SMB_NTQUOTA_LIST *qt_list, + uint32_t maxlen, + TALLOC_CTX *mem_ctx, + DATA_BLOB *outbuf, + SMB_NTQUOTA_LIST **end_ptr); +NTSTATUS build_fs_quota_buffer(TALLOC_CTX *mem_ctx, + const SMB_NTQUOTA_STRUCT *pqt, + DATA_BLOB *blob, + uint32_t maxlen); +NTSTATUS cli_get_user_quota(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt); +NTSTATUS cli_set_user_quota(struct cli_state *cli, + int quota_fnum, + SMB_NTQUOTA_LIST *qtl); +NTSTATUS cli_list_user_quota(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_LIST **pqt_list); +NTSTATUS cli_get_fs_quota_info(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt); +NTSTATUS cli_set_fs_quota_info(struct cli_state *cli, int quota_fnum, + SMB_NTQUOTA_STRUCT *pqt); + +/* The following definitions come from libsmb/clireadwrite.c */ + +struct tevent_req *cli_read_andx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + off_t offset, size_t size, + struct tevent_req **psmbreq); +struct tevent_req *cli_read_andx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + off_t offset, size_t size); +NTSTATUS cli_read_andx_recv(struct tevent_req *req, ssize_t *received, + uint8_t **rcvbuf); +struct tevent_req *cli_pull_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, off_t start_offset, + off_t size, size_t window_size, + NTSTATUS (*sink)(char *buf, size_t n, + void *priv), + void *priv); +NTSTATUS cli_pull_recv(struct tevent_req *req, off_t *received); +NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum, + off_t start_offset, off_t size, size_t window_size, + NTSTATUS (*sink)(char *buf, size_t n, void *priv), + void *priv, off_t *received); +NTSTATUS cli_read_sink(char *buf, size_t n, void *priv); +struct tevent_req *cli_read_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + char *buf, + off_t offset, + size_t size); +NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received); +NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum, + char *buf, off_t offset, size_t size, + size_t *nread); +NTSTATUS cli_smbwrite(struct cli_state *cli, uint16_t fnum, char *buf, + off_t offset, size_t size1, size_t *ptotal); +struct tevent_req *cli_write_andx_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size, + struct tevent_req **reqs_before, + int num_reqs_before, + struct tevent_req **psmbreq); +struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size); +NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten); + +struct tevent_req *cli_write_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, uint16_t fnum, + uint16_t mode, const uint8_t *buf, + off_t offset, size_t size); +NTSTATUS cli_write_recv(struct tevent_req *req, size_t *pwritten); + +struct tevent_req *cli_writeall_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint16_t mode, + const uint8_t *buf, + off_t offset, + size_t size); +NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten); +NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode, + const uint8_t *buf, off_t offset, size_t size, + size_t *pwritten); + +struct tevent_req *cli_push_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, + void *priv), + void *priv); +NTSTATUS cli_push_recv(struct tevent_req *req); +NTSTATUS cli_push(struct cli_state *cli, uint16_t fnum, uint16_t mode, + off_t start_offset, size_t window_size, + size_t (*source)(uint8_t *buf, size_t n, void *priv), + void *priv); + +NTSTATUS cli_splice(struct cli_state *srccli, struct cli_state *dstcli, + uint16_t src_fnum, uint16_t dst_fnum, + off_t size, + off_t src_offset, off_t dst_offset, + off_t *written, + int (*splice_cb)(off_t n, void *priv), void *priv); + +/* The following definitions come from libsmb/clisecdesc.c */ + +struct tevent_req *cli_query_security_descriptor_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info); +NTSTATUS cli_query_security_descriptor_recv( + struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct security_descriptor **sd); +NTSTATUS cli_query_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + TALLOC_CTX *mem_ctx, + struct security_descriptor **sd); +NTSTATUS cli_query_secdesc(struct cli_state *cli, uint16_t fnum, + TALLOC_CTX *mem_ctx, struct security_descriptor **sd); +struct tevent_req *cli_set_security_descriptor_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd); +NTSTATUS cli_set_security_descriptor_recv(struct tevent_req *req); +NTSTATUS cli_set_security_descriptor(struct cli_state *cli, + uint16_t fnum, + uint32_t sec_info, + const struct security_descriptor *sd); +NTSTATUS cli_set_secdesc(struct cli_state *cli, uint16_t fnum, + const struct security_descriptor *sd); + +NTSTATUS cli_query_mxac(struct cli_state *cli, + const char *filename, + uint32_t *mxac); + +/* The following definitions come from libsmb/clistr.c */ + +bool clistr_is_previous_version_path(const char *path, + const char **startp, + const char **endp, + time_t *ptime); + +/* The following definitions come from libsmb/clitrans.c */ + +struct tevent_req *cli_trans_send( + TALLOC_CTX *mem_ctx, struct tevent_context *ev, + struct cli_state *cli, uint16_t additional_flags2, uint8_t cmd, + const char *pipe_name, uint16_t fid, uint16_t function, int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data); +NTSTATUS cli_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + uint16_t *recv_flags2, + uint16_t **setup, uint8_t min_setup, + uint8_t *num_setup, + uint8_t **param, uint32_t min_param, + uint32_t *num_param, + uint8_t **data, uint32_t min_data, + uint32_t *num_data); +NTSTATUS cli_trans(TALLOC_CTX *mem_ctx, struct cli_state *cli, + uint8_t trans_cmd, + const char *pipe_name, uint16_t fid, uint16_t function, + int flags, + uint16_t *setup, uint8_t num_setup, uint8_t max_setup, + uint8_t *param, uint32_t num_param, uint32_t max_param, + uint8_t *data, uint32_t num_data, uint32_t max_data, + uint16_t *recv_flags2, + uint16_t **rsetup, uint8_t min_rsetup, uint8_t *num_rsetup, + uint8_t **rparam, uint32_t min_rparam, uint32_t *num_rparam, + uint8_t **rdata, uint32_t min_rdata, uint32_t *num_rdata); + +/* The following definitions come from libsmb/clisymlink.c */ + +struct tevent_req *cli_symlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *oldpath, + const char *newpath, + uint32_t flags); +NTSTATUS cli_symlink_recv(struct tevent_req *req); +NTSTATUS cli_symlink(struct cli_state *cli, const char *oldname, + const char *newname, uint32_t flags); + +struct tevent_req *cli_readlink_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *fname); +NTSTATUS cli_readlink_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + char **psubstitute_name, char **pprint_name, + uint32_t *pflags); +NTSTATUS cli_readlink(struct cli_state *cli, const char *fname, + TALLOC_CTX *mem_ctx, char **psubstitute_name, + char **pprint_name, uint32_t *pflags); + +NTSTATUS fill_quota_buffer(TALLOC_CTX *mem_ctx, + SMB_NTQUOTA_LIST *tmp_list, + bool return_single, + uint32_t max_data, + DATA_BLOB *blob, + SMB_NTQUOTA_LIST **end_ptr); +/* The following definitions come from libsmb/passchange.c */ + +NTSTATUS remote_password_change(const char *remote_machine, + const char *domain, const char *user_name, + const char *old_passwd, const char *new_passwd, + char **err_str); + +#endif /* _LIBSMB_PROTO_H_ */ diff --git a/source3/libsmb/pylibsmb.c b/source3/libsmb/pylibsmb.c new file mode 100644 index 0000000..b498d3a --- /dev/null +++ b/source3/libsmb/pylibsmb.c @@ -0,0 +1,1986 @@ +/* + * Unix SMB/CIFS implementation. + * + * SMB client Python bindings used internally by Samba (for things like + * samba-tool). These Python bindings may change without warning, and so + * should not be used outside of the Samba codebase. + * + * Copyright (C) Volker Lendecke 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <Python.h> +#include "includes.h" +#include "python/py3compat.h" +#include "python/modules.h" +#include "libcli/smb/smbXcli_base.h" +#include "libsmb/libsmb.h" +#include "libcli/security/security.h" +#include "system/select.h" +#include "source4/libcli/util/pyerrors.h" +#include "auth/credentials/pycredentials.h" +#include "trans2.h" +#include "libsmb/clirap.h" +#include "librpc/rpc/pyrpc_util.h" + +#define LIST_ATTRIBUTE_MASK \ + (FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN) + +static PyTypeObject *dom_sid_Type = NULL; + +static PyTypeObject *get_pytype(const char *module, const char *type) +{ + PyObject *mod; + PyTypeObject *result; + + mod = PyImport_ImportModule(module); + if (mod == NULL) { + PyErr_Format(PyExc_RuntimeError, + "Unable to import %s to check type %s", + module, type); + return NULL; + } + result = (PyTypeObject *)PyObject_GetAttrString(mod, type); + Py_DECREF(mod); + if (result == NULL) { + PyErr_Format(PyExc_RuntimeError, + "Unable to find type %s in module %s", + module, type); + return NULL; + } + return result; +} + +/* + * We're using "const char * const *" for keywords, + * PyArg_ParseTupleAndKeywords expects a "char **". Confine the + * inevitable warnings to just one place. + */ +static int ParseTupleAndKeywords(PyObject *args, PyObject *kw, + const char *format, const char * const *keywords, + ...) +{ + char **_keywords = discard_const_p(char *, keywords); + va_list a; + int ret; + va_start(a, keywords); + ret = PyArg_VaParseTupleAndKeywords(args, kw, format, + _keywords, a); + va_end(a); + return ret; +} + +struct py_cli_thread; + +struct py_cli_oplock_break { + uint16_t fnum; + uint8_t level; +}; + +struct py_cli_state { + PyObject_HEAD + struct cli_state *cli; + struct tevent_context *ev; + int (*req_wait_fn)(struct tevent_context *ev, + struct tevent_req *req); + struct py_cli_thread *thread_state; + + struct tevent_req *oplock_waiter; + struct py_cli_oplock_break *oplock_breaks; + struct py_tevent_cond *oplock_cond; +}; + +#ifdef HAVE_PTHREAD + +#include <pthread.h> + +struct py_cli_thread { + + /* + * Pipe to make the poll thread wake up in our destructor, so + * that we can exit and join the thread. + */ + int shutdown_pipe[2]; + struct tevent_fd *shutdown_fde; + bool do_shutdown; + pthread_t id; + + /* + * Thread state to release the GIL during the poll(2) syscall + */ + PyThreadState *py_threadstate; +}; + +static void *py_cli_state_poll_thread(void *private_data) +{ + struct py_cli_state *self = (struct py_cli_state *)private_data; + struct py_cli_thread *t = self->thread_state; + PyGILState_STATE gstate; + + gstate = PyGILState_Ensure(); + + while (!t->do_shutdown) { + int ret; + ret = tevent_loop_once(self->ev); + assert(ret == 0); + } + PyGILState_Release(gstate); + return NULL; +} + +static void py_cli_state_trace_callback(enum tevent_trace_point point, + void *private_data) +{ + struct py_cli_state *self = (struct py_cli_state *)private_data; + struct py_cli_thread *t = self->thread_state; + + switch(point) { + case TEVENT_TRACE_BEFORE_WAIT: + assert(t->py_threadstate == NULL); + t->py_threadstate = PyEval_SaveThread(); + break; + case TEVENT_TRACE_AFTER_WAIT: + assert(t->py_threadstate != NULL); + PyEval_RestoreThread(t->py_threadstate); + t->py_threadstate = NULL; + break; + default: + break; + } +} + +static void py_cli_state_shutdown_handler(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct py_cli_state *self = (struct py_cli_state *)private_data; + struct py_cli_thread *t = self->thread_state; + + if ((flags & TEVENT_FD_READ) == 0) { + return; + } + TALLOC_FREE(t->shutdown_fde); + t->do_shutdown = true; +} + +static int py_cli_thread_destructor(struct py_cli_thread *t) +{ + char c = 0; + ssize_t written; + int ret; + + do { + /* + * This will wake the poll thread from the poll(2) + */ + written = write(t->shutdown_pipe[1], &c, 1); + } while ((written == -1) && (errno == EINTR)); + + /* + * Allow the poll thread to do its own cleanup under the GIL + */ + Py_BEGIN_ALLOW_THREADS + ret = pthread_join(t->id, NULL); + Py_END_ALLOW_THREADS + assert(ret == 0); + + if (t->shutdown_pipe[0] != -1) { + close(t->shutdown_pipe[0]); + t->shutdown_pipe[0] = -1; + } + if (t->shutdown_pipe[1] != -1) { + close(t->shutdown_pipe[1]); + t->shutdown_pipe[1] = -1; + } + return 0; +} + +static int py_tevent_cond_req_wait(struct tevent_context *ev, + struct tevent_req *req); + +static bool py_cli_state_setup_mt_ev(struct py_cli_state *self) +{ + struct py_cli_thread *t = NULL; + int ret; + + self->ev = tevent_context_init_byname(NULL, "poll_mt"); + if (self->ev == NULL) { + goto fail; + } + samba_tevent_set_debug(self->ev, "pylibsmb_tevent_mt"); + tevent_set_trace_callback(self->ev, py_cli_state_trace_callback, self); + + self->req_wait_fn = py_tevent_cond_req_wait; + + self->thread_state = talloc_zero(NULL, struct py_cli_thread); + if (self->thread_state == NULL) { + goto fail; + } + t = self->thread_state; + + ret = pipe(t->shutdown_pipe); + if (ret == -1) { + goto fail; + } + t->shutdown_fde = tevent_add_fd( + self->ev, self->ev, t->shutdown_pipe[0], TEVENT_FD_READ, + py_cli_state_shutdown_handler, self); + if (t->shutdown_fde == NULL) { + goto fail; + } + + PyEval_InitThreads(); + + ret = pthread_create(&t->id, NULL, py_cli_state_poll_thread, self); + if (ret != 0) { + goto fail; + } + talloc_set_destructor(self->thread_state, py_cli_thread_destructor); + return true; + +fail: + if (t != NULL) { + TALLOC_FREE(t->shutdown_fde); + + if (t->shutdown_pipe[0] != -1) { + close(t->shutdown_pipe[0]); + t->shutdown_pipe[0] = -1; + } + if (t->shutdown_pipe[1] != -1) { + close(t->shutdown_pipe[1]); + t->shutdown_pipe[1] = -1; + } + } + + TALLOC_FREE(self->thread_state); + TALLOC_FREE(self->ev); + return false; +} + +struct py_tevent_cond { + pthread_mutex_t mutex; + pthread_cond_t cond; + bool is_done; +}; + +static void py_tevent_signalme(struct tevent_req *req); + +static int py_tevent_cond_wait(struct py_tevent_cond *cond) +{ + int ret, result; + + result = pthread_mutex_init(&cond->mutex, NULL); + if (result != 0) { + goto fail; + } + result = pthread_cond_init(&cond->cond, NULL); + if (result != 0) { + goto fail_mutex; + } + + result = pthread_mutex_lock(&cond->mutex); + if (result != 0) { + goto fail_cond; + } + + cond->is_done = false; + + while (!cond->is_done) { + + Py_BEGIN_ALLOW_THREADS + result = pthread_cond_wait(&cond->cond, &cond->mutex); + Py_END_ALLOW_THREADS + + if (result != 0) { + goto fail_unlock; + } + } + +fail_unlock: + ret = pthread_mutex_unlock(&cond->mutex); + assert(ret == 0); +fail_cond: + ret = pthread_cond_destroy(&cond->cond); + assert(ret == 0); +fail_mutex: + ret = pthread_mutex_destroy(&cond->mutex); + assert(ret == 0); +fail: + return result; +} + +static int py_tevent_cond_req_wait(struct tevent_context *ev, + struct tevent_req *req) +{ + struct py_tevent_cond cond; + tevent_req_set_callback(req, py_tevent_signalme, &cond); + return py_tevent_cond_wait(&cond); +} + +static void py_tevent_cond_signal(struct py_tevent_cond *cond) +{ + int ret; + + ret = pthread_mutex_lock(&cond->mutex); + assert(ret == 0); + + cond->is_done = true; + + ret = pthread_cond_signal(&cond->cond); + assert(ret == 0); + ret = pthread_mutex_unlock(&cond->mutex); + assert(ret == 0); +} + +static void py_tevent_signalme(struct tevent_req *req) +{ + struct py_tevent_cond *cond = (struct py_tevent_cond *) + tevent_req_callback_data_void(req); + + py_tevent_cond_signal(cond); +} + +#endif + +static int py_tevent_req_wait(struct tevent_context *ev, + struct tevent_req *req); + +static bool py_cli_state_setup_ev(struct py_cli_state *self) +{ + self->ev = tevent_context_init(NULL); + if (self->ev == NULL) { + return false; + } + + samba_tevent_set_debug(self->ev, "pylibsmb_tevent"); + + self->req_wait_fn = py_tevent_req_wait; + + return true; +} + +static int py_tevent_req_wait(struct tevent_context *ev, + struct tevent_req *req) +{ + while (tevent_req_is_in_progress(req)) { + int ret; + + ret = tevent_loop_once(ev); + if (ret != 0) { + return ret; + } + } + return 0; +} + +static bool py_tevent_req_wait_exc(struct py_cli_state *self, + struct tevent_req *req) +{ + int ret; + + if (req == NULL) { + PyErr_NoMemory(); + return false; + } + ret = self->req_wait_fn(self->ev, req); + if (ret != 0) { + TALLOC_FREE(req); + errno = ret; + PyErr_SetFromErrno(PyExc_RuntimeError); + return false; + } + return true; +} + +static PyObject *py_cli_state_new(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + struct py_cli_state *self; + + self = (struct py_cli_state *)type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + self->cli = NULL; + self->ev = NULL; + self->thread_state = NULL; + self->oplock_waiter = NULL; + self->oplock_cond = NULL; + self->oplock_breaks = NULL; + return (PyObject *)self; +} + +static void py_cli_got_oplock_break(struct tevent_req *req); + +static int py_cli_state_init(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + NTSTATUS status; + char *host, *share; + PyObject *creds = NULL; + struct cli_credentials *cli_creds; + PyObject *py_lp = Py_None; + PyObject *py_multi_threaded = Py_False; + bool multi_threaded = false; + PyObject *py_force_smb1 = Py_False; + bool force_smb1 = false; + PyObject *py_ipc = Py_False; + bool use_ipc = false; + struct tevent_req *req; + bool ret; + int flags = 0; + + static const char *kwlist[] = { + "host", "share", "lp", "creds", + "multi_threaded", "force_smb1", + "ipc", + NULL + }; + + PyTypeObject *py_type_Credentials = get_pytype( + "samba.credentials", "Credentials"); + if (py_type_Credentials == NULL) { + return -1; + } + + ret = ParseTupleAndKeywords( + args, kwds, "ssO|O!OOO", kwlist, + &host, &share, &py_lp, + py_type_Credentials, &creds, + &py_multi_threaded, + &py_force_smb1, + &py_ipc); + + Py_DECREF(py_type_Credentials); + + if (!ret) { + return -1; + } + + multi_threaded = PyObject_IsTrue(py_multi_threaded); + force_smb1 = PyObject_IsTrue(py_force_smb1); + + if (force_smb1) { + /* + * As most of the cli_*_send() function + * don't support SMB2 (it's only plugged + * into the sync wrapper functions currently) + * we have a way to force SMB1. + */ + flags = CLI_FULL_CONNECTION_FORCE_SMB1; + } + + use_ipc = PyObject_IsTrue(py_ipc); + if (use_ipc) { + flags |= CLI_FULL_CONNECTION_IPC; + } + + if (multi_threaded) { +#ifdef HAVE_PTHREAD + ret = py_cli_state_setup_mt_ev(self); + if (!ret) { + return -1; + } +#else + PyErr_SetString(PyExc_RuntimeError, + "No PTHREAD support available"); + return -1; +#endif + } else { + ret = py_cli_state_setup_ev(self); + if (!ret) { + return -1; + } + } + + if (creds == NULL) { + cli_creds = cli_credentials_init_anon(NULL); + } else { + cli_creds = PyCredentials_AsCliCredentials(creds); + } + + req = cli_full_connection_creds_send( + NULL, self->ev, "myname", host, NULL, 0, share, "?????", + cli_creds, flags); + if (!py_tevent_req_wait_exc(self, req)) { + return -1; + } + status = cli_full_connection_creds_recv(req, &self->cli); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return -1; + } + + /* + * Oplocks require a multi threaded connection + */ + if (self->thread_state == NULL) { + return 0; + } + + self->oplock_waiter = cli_smb_oplock_break_waiter_send( + self->ev, self->ev, self->cli); + if (self->oplock_waiter == NULL) { + PyErr_NoMemory(); + return -1; + } + tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break, + self); + return 0; +} + +static void py_cli_got_oplock_break(struct tevent_req *req) +{ + struct py_cli_state *self = (struct py_cli_state *) + tevent_req_callback_data_void(req); + struct py_cli_oplock_break b; + struct py_cli_oplock_break *tmp; + size_t num_breaks; + NTSTATUS status; + + status = cli_smb_oplock_break_waiter_recv(req, &b.fnum, &b.level); + TALLOC_FREE(req); + self->oplock_waiter = NULL; + + if (!NT_STATUS_IS_OK(status)) { + return; + } + + num_breaks = talloc_array_length(self->oplock_breaks); + tmp = talloc_realloc(self->ev, self->oplock_breaks, + struct py_cli_oplock_break, num_breaks+1); + if (tmp == NULL) { + return; + } + self->oplock_breaks = tmp; + self->oplock_breaks[num_breaks] = b; + + if (self->oplock_cond != NULL) { + py_tevent_cond_signal(self->oplock_cond); + } + + self->oplock_waiter = cli_smb_oplock_break_waiter_send( + self->ev, self->ev, self->cli); + if (self->oplock_waiter == NULL) { + return; + } + tevent_req_set_callback(self->oplock_waiter, py_cli_got_oplock_break, + self); +} + +static PyObject *py_cli_get_oplock_break(struct py_cli_state *self, + PyObject *args) +{ + size_t num_oplock_breaks; + + if (!PyArg_ParseTuple(args, "")) { + return NULL; + } + + if (self->thread_state == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "get_oplock_break() only possible on " + "a multi_threaded connection"); + return NULL; + } + + if (self->oplock_cond != NULL) { + errno = EBUSY; + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + + num_oplock_breaks = talloc_array_length(self->oplock_breaks); + + if (num_oplock_breaks == 0) { + struct py_tevent_cond cond; + int ret; + + self->oplock_cond = &cond; + ret = py_tevent_cond_wait(&cond); + self->oplock_cond = NULL; + + if (ret != 0) { + errno = ret; + PyErr_SetFromErrno(PyExc_RuntimeError); + return NULL; + } + } + + num_oplock_breaks = talloc_array_length(self->oplock_breaks); + if (num_oplock_breaks > 0) { + PyObject *result; + + result = Py_BuildValue( + "{s:i,s:i}", + "fnum", self->oplock_breaks[0].fnum, + "level", self->oplock_breaks[0].level); + + memmove(&self->oplock_breaks[0], &self->oplock_breaks[1], + sizeof(self->oplock_breaks[0]) * + (num_oplock_breaks - 1)); + self->oplock_breaks = talloc_realloc( + NULL, self->oplock_breaks, struct py_cli_oplock_break, + num_oplock_breaks - 1); + + return result; + } + Py_RETURN_NONE; +} + +static void py_cli_state_dealloc(struct py_cli_state *self) +{ + TALLOC_FREE(self->thread_state); + TALLOC_FREE(self->oplock_waiter); + TALLOC_FREE(self->ev); + + if (self->cli != NULL) { + cli_shutdown(self->cli); + self->cli = NULL; + } + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static PyObject *py_cli_settimeout(struct py_cli_state *self, PyObject *args) +{ + unsigned int nmsecs = 0; + unsigned int omsecs = 0; + + if (!PyArg_ParseTuple(args, "I", &nmsecs)) { + return NULL; + } + + omsecs = cli_set_timeout(self->cli, nmsecs); + + return PyLong_FromLong(omsecs); +} + +static PyObject *py_cli_echo(struct py_cli_state *self, + PyObject *Py_UNUSED(ignored)) +{ + DATA_BLOB data = data_blob_string_const("keepalive"); + struct tevent_req *req = NULL; + NTSTATUS status; + + req = cli_echo_send(NULL, self->ev, self->cli, 1, data); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_echo_recv(req); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + Py_RETURN_NONE; +} + +static PyObject *py_cli_create(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + char *fname; + unsigned CreateFlags = 0; + unsigned DesiredAccess = FILE_GENERIC_READ; + unsigned FileAttributes = 0; + unsigned ShareAccess = 0; + unsigned CreateDisposition = FILE_OPEN; + unsigned CreateOptions = 0; + unsigned ImpersonationLevel = SMB2_IMPERSONATION_IMPERSONATION; + unsigned SecurityFlags = 0; + uint16_t fnum; + struct tevent_req *req; + NTSTATUS status; + + static const char *kwlist[] = { + "Name", "CreateFlags", "DesiredAccess", "FileAttributes", + "ShareAccess", "CreateDisposition", "CreateOptions", + "ImpersonationLevel", "SecurityFlags", NULL }; + + if (!ParseTupleAndKeywords( + args, kwds, "s|IIIIIIII", kwlist, + &fname, &CreateFlags, &DesiredAccess, &FileAttributes, + &ShareAccess, &CreateDisposition, &CreateOptions, + &ImpersonationLevel, &SecurityFlags)) { + return NULL; + } + + req = cli_ntcreate_send(NULL, self->ev, self->cli, fname, CreateFlags, + DesiredAccess, FileAttributes, ShareAccess, + CreateDisposition, CreateOptions, + ImpersonationLevel, SecurityFlags); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_ntcreate_recv(req, &fnum, NULL); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + return Py_BuildValue("I", (unsigned)fnum); +} + +static PyObject *py_cli_close(struct py_cli_state *self, PyObject *args) +{ + struct tevent_req *req; + int fnum; + NTSTATUS status; + + if (!PyArg_ParseTuple(args, "i", &fnum)) { + return NULL; + } + + req = cli_close_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_close_recv(req); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *py_cli_rename( + struct py_cli_state *self, PyObject *args, PyObject *kwds) +{ + char *fname_src = NULL, *fname_dst = NULL; + int replace = false; + struct tevent_req *req = NULL; + NTSTATUS status; + bool ok; + + static const char *kwlist[] = { "src", "dst", "replace", NULL }; + + ok = ParseTupleAndKeywords( + args, kwds, "ss|p", kwlist, &fname_src, &fname_dst, &replace); + if (!ok) { + return NULL; + } + + req = cli_rename_send( + NULL, self->ev, self->cli, fname_src, fname_dst, replace); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_rename_recv(req); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_RETURN_NONE; +} + + +struct push_state { + char *data; + off_t nread; + off_t total_data; +}; + +/* + * cli_push() helper to write a chunk of data to a remote file + */ +static size_t push_data(uint8_t *buf, size_t n, void *priv) +{ + struct push_state *state = (struct push_state *)priv; + char *curr_ptr = NULL; + off_t remaining; + size_t copied_bytes; + + if (state->nread >= state->total_data) { + return 0; + } + + curr_ptr = state->data + state->nread; + remaining = state->total_data - state->nread; + copied_bytes = MIN(remaining, n); + + memcpy(buf, curr_ptr, copied_bytes); + state->nread += copied_bytes; + return copied_bytes; +} + +/* + * Writes a file with the contents specified + */ +static PyObject *py_smb_savefile(struct py_cli_state *self, PyObject *args) +{ + uint16_t fnum; + const char *filename = NULL; + char *data = NULL; + Py_ssize_t size = 0; + NTSTATUS status; + struct tevent_req *req = NULL; + struct push_state state; + + if (!PyArg_ParseTuple(args, "s"PYARG_BYTES_LEN":savefile", &filename, + &data, &size)) { + return NULL; + } + + /* create a new file handle for writing to */ + req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0, + FILE_WRITE_DATA, FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OVERWRITE_IF, FILE_NON_DIRECTORY_FILE, + SMB2_IMPERSONATION_IMPERSONATION, 0); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_ntcreate_recv(req, &fnum, NULL); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + /* write the new file contents */ + state.data = data; + state.nread = 0; + state.total_data = size; + + req = cli_push_send(NULL, self->ev, self->cli, fnum, 0, 0, 0, + push_data, &state); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_push_recv(req); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + /* close the file handle */ + req = cli_close_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_close_recv(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + Py_RETURN_NONE; +} + +static PyObject *py_cli_write(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + int fnum; + unsigned mode = 0; + char *buf; + Py_ssize_t buflen; + unsigned long long offset; + struct tevent_req *req; + NTSTATUS status; + size_t written; + + static const char *kwlist[] = { + "fnum", "buffer", "offset", "mode", NULL }; + + if (!ParseTupleAndKeywords( + args, kwds, "i" PYARG_BYTES_LEN "K|I", kwlist, + &fnum, &buf, &buflen, &offset, &mode)) { + return NULL; + } + + req = cli_write_send(NULL, self->ev, self->cli, fnum, mode, + (uint8_t *)buf, offset, buflen); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_write_recv(req, &written); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + return Py_BuildValue("K", (unsigned long long)written); +} + +/* + * Returns the size of the given file + */ +static NTSTATUS py_smb_filesize(struct py_cli_state *self, uint16_t fnum, + off_t *size) +{ + NTSTATUS status; + struct tevent_req *req = NULL; + + req = cli_qfileinfo_basic_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self, req)) { + return NT_STATUS_INTERNAL_ERROR; + } + status = cli_qfileinfo_basic_recv( + req, NULL, size, NULL, NULL, NULL, NULL, NULL); + TALLOC_FREE(req); + return status; +} + +/* + * Loads the specified file's contents and returns it + */ +static PyObject *py_smb_loadfile(struct py_cli_state *self, PyObject *args) +{ + NTSTATUS status; + const char *filename = NULL; + struct tevent_req *req = NULL; + uint16_t fnum; + off_t size; + char *buf = NULL; + off_t nread = 0; + PyObject *result = NULL; + + if (!PyArg_ParseTuple(args, "s:loadfile", &filename)) { + return NULL; + } + + /* get a read file handle */ + req = cli_ntcreate_send(NULL, self->ev, self->cli, filename, 0, + FILE_READ_DATA | FILE_READ_ATTRIBUTES, + FILE_ATTRIBUTE_NORMAL, + FILE_SHARE_READ, FILE_OPEN, 0, + SMB2_IMPERSONATION_IMPERSONATION, 0); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_ntcreate_recv(req, &fnum, NULL); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + /* get a buffer to hold the file contents */ + status = py_smb_filesize(self, fnum, &size); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + result = PyBytes_FromStringAndSize(NULL, size); + if (result == NULL) { + return NULL; + } + + /* read the file contents */ + buf = PyBytes_AS_STRING(result); + req = cli_pull_send(NULL, self->ev, self->cli, fnum, 0, size, + size, cli_read_sink, &buf); + if (!py_tevent_req_wait_exc(self, req)) { + Py_XDECREF(result); + return NULL; + } + status = cli_pull_recv(req, &nread); + TALLOC_FREE(req); + if (!NT_STATUS_IS_OK(status)) { + Py_XDECREF(result); + PyErr_SetNTSTATUS(status); + return NULL; + } + + /* close the file handle */ + req = cli_close_send(NULL, self->ev, self->cli, fnum); + if (!py_tevent_req_wait_exc(self, req)) { + Py_XDECREF(result); + return NULL; + } + status = cli_close_recv(req); + TALLOC_FREE(req); + if (!NT_STATUS_IS_OK(status)) { + Py_XDECREF(result); + PyErr_SetNTSTATUS(status); + return NULL; + } + + /* sanity-check we read the expected number of bytes */ + if (nread > size) { + Py_XDECREF(result); + PyErr_Format(PyExc_IOError, + "read invalid - got %zu requested %zu", + nread, size); + return NULL; + } + + if (nread < size) { + if (_PyBytes_Resize(&result, nread) < 0) { + return NULL; + } + } + + return result; +} + +static PyObject *py_cli_read(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + int fnum; + unsigned long long offset; + unsigned size; + struct tevent_req *req; + NTSTATUS status; + char *buf; + size_t received; + PyObject *result; + + static const char *kwlist[] = { + "fnum", "offset", "size", NULL }; + + if (!ParseTupleAndKeywords( + args, kwds, "iKI", kwlist, &fnum, &offset, + &size)) { + return NULL; + } + + result = PyBytes_FromStringAndSize(NULL, size); + if (result == NULL) { + return NULL; + } + buf = PyBytes_AS_STRING(result); + + req = cli_read_send(NULL, self->ev, self->cli, fnum, + buf, offset, size); + if (!py_tevent_req_wait_exc(self, req)) { + Py_XDECREF(result); + return NULL; + } + status = cli_read_recv(req, &received); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + Py_XDECREF(result); + PyErr_SetNTSTATUS(status); + return NULL; + } + + if (received > size) { + Py_XDECREF(result); + PyErr_Format(PyExc_IOError, + "read invalid - got %zu requested %u", + received, size); + return NULL; + } + + if (received < size) { + if (_PyBytes_Resize(&result, received) < 0) { + return NULL; + } + } + + return result; +} + +static PyObject *py_cli_ftruncate(struct py_cli_state *self, PyObject *args, + PyObject *kwds) +{ + int fnum; + unsigned long long size; + struct tevent_req *req; + NTSTATUS status; + + static const char *kwlist[] = { + "fnum", "size", NULL }; + + if (!ParseTupleAndKeywords( + args, kwds, "IK", kwlist, &fnum, &size)) { + return NULL; + } + + req = cli_ftruncate_send(NULL, self->ev, self->cli, fnum, size); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_ftruncate_recv(req); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject *py_cli_delete_on_close(struct py_cli_state *self, + PyObject *args, + PyObject *kwds) +{ + unsigned fnum, flag; + struct tevent_req *req; + NTSTATUS status; + + static const char *kwlist[] = { + "fnum", "flag", NULL }; + + if (!ParseTupleAndKeywords( + args, kwds, "II", kwlist, &fnum, &flag)) { + return NULL; + } + + req = cli_nt_delete_on_close_send(NULL, self->ev, self->cli, fnum, + flag); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_nt_delete_on_close_recv(req); + TALLOC_FREE(req); + + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + return NULL; + } + Py_RETURN_NONE; +} + +struct py_cli_notify_state { + PyObject_HEAD + struct py_cli_state *py_cli_state; + struct tevent_req *req; +}; + +static void py_cli_notify_state_dealloc(struct py_cli_notify_state *self) +{ + TALLOC_FREE(self->req); + if (self->py_cli_state != NULL) { + Py_DECREF(self->py_cli_state); + self->py_cli_state = NULL; + } + Py_TYPE(self)->tp_free(self); +} + +static PyTypeObject py_cli_notify_state_type; + +static PyObject *py_cli_notify(struct py_cli_state *self, + PyObject *args, + PyObject *kwds) +{ + static const char *kwlist[] = { + "fnum", + "buffer_size", + "completion_filter", + "recursive", + NULL + }; + unsigned fnum = 0; + unsigned buffer_size = 0; + unsigned completion_filter = 0; + PyObject *py_recursive = Py_False; + bool recursive = false; + struct tevent_req *req = NULL; + struct tevent_queue *send_queue = NULL; + struct tevent_req *flush_req = NULL; + bool ok; + struct py_cli_notify_state *py_notify_state = NULL; + struct timeval endtime; + + ok = ParseTupleAndKeywords(args, + kwds, + "IIIO", + kwlist, + &fnum, + &buffer_size, + &completion_filter, + &py_recursive); + if (!ok) { + return NULL; + } + + recursive = PyObject_IsTrue(py_recursive); + + req = cli_notify_send(NULL, + self->ev, + self->cli, + fnum, + buffer_size, + completion_filter, + recursive); + if (req == NULL) { + PyErr_NoMemory(); + return NULL; + } + + /* + * Just wait for the request being submitted to + * the kernel/socket/wire. + */ + send_queue = smbXcli_conn_send_queue(self->cli->conn); + flush_req = tevent_queue_wait_send(req, + self->ev, + send_queue); + endtime = timeval_current_ofs_msec(self->cli->timeout); + ok = tevent_req_set_endtime(flush_req, + self->ev, + endtime); + if (!ok) { + TALLOC_FREE(req); + PyErr_NoMemory(); + return NULL; + } + ok = py_tevent_req_wait_exc(self, flush_req); + if (!ok) { + TALLOC_FREE(req); + return NULL; + } + TALLOC_FREE(flush_req); + + py_notify_state = (struct py_cli_notify_state *) + py_cli_notify_state_type.tp_alloc(&py_cli_notify_state_type, 0); + if (py_notify_state == NULL) { + TALLOC_FREE(req); + PyErr_NoMemory(); + return NULL; + } + Py_INCREF(self); + py_notify_state->py_cli_state = self; + py_notify_state->req = req; + + return (PyObject *)py_notify_state; +} + +static PyObject *py_cli_notify_get_changes(struct py_cli_notify_state *self, + PyObject *args, + PyObject *kwds) +{ + struct py_cli_state *py_cli_state = self->py_cli_state; + struct tevent_req *req = self->req; + uint32_t i; + uint32_t num_changes = 0; + struct notify_change *changes = NULL; + PyObject *result = NULL; + NTSTATUS status; + bool ok; + static const char *kwlist[] = { + "wait", + NULL + }; + PyObject *py_wait = Py_False; + bool wait = false; + bool pending; + + ok = ParseTupleAndKeywords(args, + kwds, + "O", + kwlist, + &py_wait); + if (!ok) { + return NULL; + } + + wait = PyObject_IsTrue(py_wait); + + if (req == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "TODO req == NULL " + "- missing change notify request?"); + return NULL; + } + + pending = tevent_req_is_in_progress(req); + if (pending && !wait) { + Py_RETURN_NONE; + } + + if (pending) { + struct timeval endtime; + + endtime = timeval_current_ofs_msec(py_cli_state->cli->timeout); + ok = tevent_req_set_endtime(req, + py_cli_state->ev, + endtime); + if (!ok) { + TALLOC_FREE(req); + PyErr_NoMemory(); + return NULL; + } + } + + ok = py_tevent_req_wait_exc(py_cli_state, req); + self->req = NULL; + Py_DECREF(self->py_cli_state); + self->py_cli_state = NULL; + if (!ok) { + return NULL; + } + + status = cli_notify_recv(req, req, &num_changes, &changes); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(req); + PyErr_SetNTSTATUS(status); + return NULL; + } + + result = Py_BuildValue("[]"); + if (result == NULL) { + TALLOC_FREE(req); + return NULL; + } + + for (i = 0; i < num_changes; i++) { + PyObject *change = NULL; + int ret; + + change = Py_BuildValue("{s:s,s:I}", + "name", changes[i].name, + "action", changes[i].action); + if (change == NULL) { + Py_XDECREF(result); + TALLOC_FREE(req); + return NULL; + } + + ret = PyList_Append(result, change); + Py_DECREF(change); + if (ret == -1) { + Py_XDECREF(result); + TALLOC_FREE(req); + return NULL; + } + } + + TALLOC_FREE(req); + return result; +} + +static PyMethodDef py_cli_notify_state_methods[] = { + { + .ml_name = "get_changes", + .ml_meth = (PyCFunction)py_cli_notify_get_changes, + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = "Wait for change notifications: \n" + "N.get_changes(wait=BOOLEAN) -> " + "change notifications as a dictionary\n" + "\t\tList contents of a directory. The keys are, \n" + "\t\t\tname: name of changed object\n" + "\t\t\taction: type of the change\n" + "None is returned if there's no response jet and " + "wait=False is passed" + }, + { + .ml_name = NULL + } +}; + +static PyTypeObject py_cli_notify_state_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "libsmb_samba_cwrapper.Notify", + .tp_basicsize = sizeof(struct py_cli_notify_state), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "notify request", + .tp_dealloc = (destructor)py_cli_notify_state_dealloc, + .tp_methods = py_cli_notify_state_methods, +}; + +/* + * Helper to add directory listing entries to an overall Python list + */ +static NTSTATUS list_helper(struct file_info *finfo, + const char *mask, void *state) +{ + PyObject *result = (PyObject *)state; + PyObject *file = NULL; + PyObject *size = NULL; + int ret; + + /* suppress '.' and '..' in the results we return */ + if (ISDOT(finfo->name) || ISDOTDOT(finfo->name)) { + return NT_STATUS_OK; + } + size = PyLong_FromUnsignedLongLong(finfo->size); + /* + * Build a dictionary representing the file info. + * Note: Windows does not always return short_name (so it may be None) + */ + file = Py_BuildValue("{s:s,s:i,s:s,s:O,s:l}", + "name", finfo->name, + "attrib", (int)finfo->attr, + "short_name", finfo->short_name, + "size", size, + "mtime", + convert_timespec_to_time_t(finfo->mtime_ts)); + + Py_CLEAR(size); + + if (file == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ret = PyList_Append(result, file); + Py_CLEAR(file); + if (ret == -1) { + return NT_STATUS_INTERNAL_ERROR; + } + + return NT_STATUS_OK; +} + +struct do_listing_state { + const char *mask; + NTSTATUS (*callback_fn)( + struct file_info *finfo, + const char *mask, + void *private_data); + void *private_data; + NTSTATUS status; +}; + +static void do_listing_cb(struct tevent_req *subreq) +{ + struct do_listing_state *state = tevent_req_callback_data_void(subreq); + struct file_info *finfo = NULL; + + state->status = cli_list_recv(subreq, NULL, &finfo); + if (!NT_STATUS_IS_OK(state->status)) { + return; + } + state->callback_fn(finfo, state->mask, state->private_data); + TALLOC_FREE(finfo); +} + +static NTSTATUS do_listing(struct py_cli_state *self, + const char *base_dir, const char *user_mask, + uint16_t attribute, + NTSTATUS (*callback_fn)(struct file_info *, + const char *, void *), + void *priv) +{ + char *mask = NULL; + unsigned int info_level = SMB_FIND_FILE_BOTH_DIRECTORY_INFO; + struct do_listing_state state = { + .mask = mask, + .callback_fn = callback_fn, + .private_data = priv, + }; + struct tevent_req *req = NULL; + NTSTATUS status; + + if (user_mask == NULL) { + mask = talloc_asprintf(NULL, "%s\\*", base_dir); + } else { + mask = talloc_asprintf(NULL, "%s\\%s", base_dir, user_mask); + } + + if (mask == NULL) { + return NT_STATUS_NO_MEMORY; + } + dos_format(mask); + + req = cli_list_send(NULL, self->ev, self->cli, mask, attribute, + info_level); + if (req == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + tevent_req_set_callback(req, do_listing_cb, &state); + + if (!py_tevent_req_wait_exc(self, req)) { + return NT_STATUS_INTERNAL_ERROR; + } + TALLOC_FREE(req); + + status = state.status; + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_FILES)) { + status = NT_STATUS_OK; + } + +done: + TALLOC_FREE(mask); + return status; +} + +static PyObject *py_cli_list(struct py_cli_state *self, + PyObject *args, + PyObject *kwds) +{ + char *base_dir; + char *user_mask = NULL; + unsigned int attribute = LIST_ATTRIBUTE_MASK; + NTSTATUS status; + PyObject *result = NULL; + const char *kwlist[] = { "directory", "mask", "attribs", NULL }; + + if (!ParseTupleAndKeywords(args, kwds, "z|sI:list", kwlist, + &base_dir, &user_mask, &attribute)) { + return NULL; + } + + result = Py_BuildValue("[]"); + if (result == NULL) { + return NULL; + } + + status = do_listing(self, base_dir, user_mask, attribute, + list_helper, result); + + if (!NT_STATUS_IS_OK(status)) { + Py_XDECREF(result); + PyErr_SetNTSTATUS(status); + return NULL; + } + + return result; +} + +static PyObject *py_smb_unlink(struct py_cli_state *self, PyObject *args) +{ + NTSTATUS status; + const char *filename = NULL; + struct tevent_req *req = NULL; + const uint32_t attrs = (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN); + + if (!PyArg_ParseTuple(args, "s:unlink", &filename)) { + return NULL; + } + + req = cli_unlink_send(NULL, self->ev, self->cli, filename, attrs); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_unlink_recv(req); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + Py_RETURN_NONE; +} + +static PyObject *py_smb_rmdir(struct py_cli_state *self, PyObject *args) +{ + NTSTATUS status; + struct tevent_req *req = NULL; + const char *dirname = NULL; + + if (!PyArg_ParseTuple(args, "s:rmdir", &dirname)) { + return NULL; + } + + req = cli_rmdir_send(NULL, self->ev, self->cli, dirname); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_rmdir_recv(req); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + Py_RETURN_NONE; +} + +/* + * Create a directory + */ +static PyObject *py_smb_mkdir(struct py_cli_state *self, PyObject *args) +{ + NTSTATUS status; + const char *dirname = NULL; + struct tevent_req *req = NULL; + + if (!PyArg_ParseTuple(args, "s:mkdir", &dirname)) { + return NULL; + } + + req = cli_mkdir_send(NULL, self->ev, self->cli, dirname); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_mkdir_recv(req); + TALLOC_FREE(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + Py_RETURN_NONE; +} + +/* + * Does a whoami call + */ +static PyObject *py_smb_posix_whoami(struct py_cli_state *self, + PyObject *Py_UNUSED(ignored)) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + struct tevent_req *req = NULL; + uint64_t uid; + uint64_t gid; + uint32_t num_gids; + uint64_t *gids = NULL; + uint32_t num_sids; + struct dom_sid *sids = NULL; + bool guest; + PyObject *py_gids = NULL; + PyObject *py_sids = NULL; + PyObject *py_guest = NULL; + PyObject *py_ret = NULL; + Py_ssize_t i; + + req = cli_posix_whoami_send(frame, self->ev, self->cli); + if (!py_tevent_req_wait_exc(self, req)) { + goto fail; + } + status = cli_posix_whoami_recv(req, + frame, + &uid, + &gid, + &num_gids, + &gids, + &num_sids, + &sids, + &guest); + if (!NT_STATUS_IS_OK(status)) { + PyErr_SetNTSTATUS(status); + goto fail; + } + if (num_gids > PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "posix_whoami: Too many GIDs"); + goto fail; + } + if (num_sids > PY_SSIZE_T_MAX) { + PyErr_SetString(PyExc_OverflowError, "posix_whoami: Too many SIDs"); + goto fail; + } + + py_gids = PyList_New(num_gids); + if (!py_gids) { + goto fail; + } + for (i = 0; i < num_gids; ++i) { + int ret; + PyObject *py_item = PyLong_FromUnsignedLongLong(gids[i]); + if (!py_item) { + goto fail2; + } + + ret = PyList_SetItem(py_gids, i, py_item); + if (ret) { + goto fail2; + } + } + py_sids = PyList_New(num_sids); + if (!py_sids) { + goto fail2; + } + for (i = 0; i < num_sids; ++i) { + int ret; + struct dom_sid *sid; + PyObject *py_item; + + sid = dom_sid_dup(frame, &sids[i]); + if (!sid) { + PyErr_NoMemory(); + goto fail3; + } + + py_item = pytalloc_steal(dom_sid_Type, sid); + if (!py_item) { + PyErr_NoMemory(); + goto fail3; + } + + ret = PyList_SetItem(py_sids, i, py_item); + if (ret) { + goto fail3; + } + } + + py_guest = guest ? Py_True : Py_False; + + py_ret = Py_BuildValue("KKNNO", + uid, + gid, + py_gids, + py_sids, + py_guest); + if (!py_ret) { + goto fail3; + } + + TALLOC_FREE(frame); + return py_ret; + +fail3: + Py_CLEAR(py_sids); + +fail2: + Py_CLEAR(py_gids); + +fail: + TALLOC_FREE(frame); + return NULL; +} + +/* + * Checks existence of a directory + */ +static bool check_dir_path(struct py_cli_state *self, const char *path) +{ + NTSTATUS status; + struct tevent_req *req = NULL; + + req = cli_chkpath_send(NULL, self->ev, self->cli, path); + if (!py_tevent_req_wait_exc(self, req)) { + return false; + } + status = cli_chkpath_recv(req); + TALLOC_FREE(req); + + return NT_STATUS_IS_OK(status); +} + +static PyObject *py_smb_chkpath(struct py_cli_state *self, PyObject *args) +{ + const char *path = NULL; + bool dir_exists; + + if (!PyArg_ParseTuple(args, "s:chkpath", &path)) { + return NULL; + } + + dir_exists = check_dir_path(self, path); + return PyBool_FromLong(dir_exists); +} + +static PyObject *py_smb_get_sd(struct py_cli_state *self, PyObject *args) +{ + int fnum; + unsigned sinfo; + struct tevent_req *req = NULL; + struct security_descriptor *sd = NULL; + NTSTATUS status; + + if (!PyArg_ParseTuple(args, "iI:get_acl", &fnum, &sinfo)) { + return NULL; + } + + req = cli_query_security_descriptor_send( + NULL, self->ev, self->cli, fnum, sinfo); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + status = cli_query_security_descriptor_recv(req, NULL, &sd); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + return py_return_ndr_struct( + "samba.dcerpc.security", "descriptor", sd, sd); +} + +static PyObject *py_smb_set_sd(struct py_cli_state *self, PyObject *args) +{ + PyObject *py_sd = NULL; + struct tevent_req *req = NULL; + struct security_descriptor *sd = NULL; + uint16_t fnum; + unsigned int sinfo; + NTSTATUS status; + + if (!PyArg_ParseTuple(args, "iOI:set_sd", &fnum, &py_sd, &sinfo)) { + return NULL; + } + + sd = pytalloc_get_type(py_sd, struct security_descriptor); + if (!sd) { + PyErr_Format(PyExc_TypeError, + "Expected dcerpc.security.descriptor as argument, got %s", + pytalloc_get_name(py_sd)); + return NULL; + } + + req = cli_set_security_descriptor_send( + NULL, self->ev, self->cli, fnum, sinfo, sd); + if (!py_tevent_req_wait_exc(self, req)) { + return NULL; + } + + status = cli_set_security_descriptor_recv(req); + PyErr_NTSTATUS_NOT_OK_RAISE(status); + + Py_RETURN_NONE; +} + +static PyMethodDef py_cli_state_methods[] = { + { "settimeout", (PyCFunction)py_cli_settimeout, METH_VARARGS, + "settimeout(new_timeout_msecs) => return old_timeout_msecs" }, + { "echo", (PyCFunction)py_cli_echo, METH_NOARGS, + "Ping the server connection" }, + { "create", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_create), + METH_VARARGS|METH_KEYWORDS, + "Open a file" }, + { "close", (PyCFunction)py_cli_close, METH_VARARGS, + "Close a file handle" }, + { "write", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_write), + METH_VARARGS|METH_KEYWORDS, + "Write to a file handle" }, + { "read", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_read), + METH_VARARGS|METH_KEYWORDS, + "Read from a file handle" }, + { "truncate", PY_DISCARD_FUNC_SIG(PyCFunction, + py_cli_ftruncate), + METH_VARARGS|METH_KEYWORDS, + "Truncate a file" }, + { "delete_on_close", PY_DISCARD_FUNC_SIG(PyCFunction, + py_cli_delete_on_close), + METH_VARARGS|METH_KEYWORDS, + "Set/Reset the delete on close flag" }, + { "notify", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_notify), + METH_VARARGS|METH_KEYWORDS, + "Wait for change notifications: \n" + "notify(fnum, buffer_size, completion_filter...) -> " + "libsmb_samba_internal.Notify request handle\n" }, + { "list", PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_list), + METH_VARARGS|METH_KEYWORDS, + "list(directory, mask='*', attribs=DEFAULT_ATTRS) -> " + "directory contents as a dictionary\n" + "\t\tDEFAULT_ATTRS: FILE_ATTRIBUTE_SYSTEM | " + "FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_ARCHIVE\n\n" + "\t\tList contents of a directory. The keys are, \n" + "\t\t\tname: Long name of the directory item\n" + "\t\t\tshort_name: Short name of the directory item\n" + "\t\t\tsize: File size in bytes\n" + "\t\t\tattrib: Attributes\n" + "\t\t\tmtime: Modification time\n" }, + { "get_oplock_break", (PyCFunction)py_cli_get_oplock_break, + METH_VARARGS, "Wait for an oplock break" }, + { "unlink", (PyCFunction)py_smb_unlink, + METH_VARARGS, + "unlink(path) -> None\n\n \t\tDelete a file." }, + { "mkdir", (PyCFunction)py_smb_mkdir, METH_VARARGS, + "mkdir(path) -> None\n\n \t\tCreate a directory." }, + { "posix_whoami", (PyCFunction)py_smb_posix_whoami, METH_NOARGS, + "posix_whoami() -> (uid, gid, gids, sids, guest)" }, + { "rmdir", (PyCFunction)py_smb_rmdir, METH_VARARGS, + "rmdir(path) -> None\n\n \t\tDelete a directory." }, + { "rename", + PY_DISCARD_FUNC_SIG(PyCFunction, py_cli_rename), + METH_VARARGS|METH_KEYWORDS, + "rename(src,dst) -> None\n\n \t\tRename a file." }, + { "chkpath", (PyCFunction)py_smb_chkpath, METH_VARARGS, + "chkpath(dir_path) -> True or False\n\n" + "\t\tReturn true if directory exists, false otherwise." }, + { "savefile", (PyCFunction)py_smb_savefile, METH_VARARGS, + "savefile(path, bytes) -> None\n\n" + "\t\tWrite bytes to file." }, + { "loadfile", (PyCFunction)py_smb_loadfile, METH_VARARGS, + "loadfile(path) -> file contents as a bytes object" + "\n\n\t\tRead contents of a file." }, + { "get_sd", (PyCFunction)py_smb_get_sd, METH_VARARGS, + "get_sd(fnum[, security_info=0]) -> security_descriptor object\n\n" + "\t\tGet security descriptor for opened file." }, + { "set_sd", (PyCFunction)py_smb_set_sd, METH_VARARGS, + "set_sd(fnum, security_descriptor[, security_info=0]) -> None\n\n" + "\t\tSet security descriptor for opened file." }, + { NULL, NULL, 0, NULL } +}; + +static PyTypeObject py_cli_state_type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "libsmb_samba_cwrapper.LibsmbCConn", + .tp_basicsize = sizeof(struct py_cli_state), + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_doc = "libsmb cwrapper connection", + .tp_new = py_cli_state_new, + .tp_init = (initproc)py_cli_state_init, + .tp_dealloc = (destructor)py_cli_state_dealloc, + .tp_methods = py_cli_state_methods, +}; + +static PyMethodDef py_libsmb_methods[] = { + {0}, +}; + +void initlibsmb_samba_cwrapper(void); + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "libsmb_samba_cwrapper", + .m_doc = "libsmb wrapper", + .m_size = -1, + .m_methods = py_libsmb_methods, +}; + +MODULE_INIT_FUNC(libsmb_samba_cwrapper) +{ + PyObject *m = NULL; + PyObject *mod = NULL; + + talloc_stackframe(); + + if (PyType_Ready(&py_cli_state_type) < 0) { + return NULL; + } + if (PyType_Ready(&py_cli_notify_state_type) < 0) { + return NULL; + } + + m = PyModule_Create(&moduledef); + if (m == NULL) { + return m; + } + + /* Import dom_sid type from dcerpc.security */ + mod = PyImport_ImportModule("samba.dcerpc.security"); + if (mod == NULL) { + return NULL; + } + + dom_sid_Type = (PyTypeObject *)PyObject_GetAttrString(mod, "dom_sid"); + if (dom_sid_Type == NULL) { + Py_DECREF(mod); + return NULL; + } + + Py_INCREF(&py_cli_state_type); + PyModule_AddObject(m, "LibsmbCConn", (PyObject *)&py_cli_state_type); + +#define ADD_FLAGS(val) PyModule_AddObject(m, #val, PyLong_FromLong(val)) + + ADD_FLAGS(FILE_ATTRIBUTE_READONLY); + ADD_FLAGS(FILE_ATTRIBUTE_HIDDEN); + ADD_FLAGS(FILE_ATTRIBUTE_SYSTEM); + ADD_FLAGS(FILE_ATTRIBUTE_VOLUME); + ADD_FLAGS(FILE_ATTRIBUTE_DIRECTORY); + ADD_FLAGS(FILE_ATTRIBUTE_ARCHIVE); + ADD_FLAGS(FILE_ATTRIBUTE_DEVICE); + ADD_FLAGS(FILE_ATTRIBUTE_NORMAL); + ADD_FLAGS(FILE_ATTRIBUTE_TEMPORARY); + ADD_FLAGS(FILE_ATTRIBUTE_SPARSE); + ADD_FLAGS(FILE_ATTRIBUTE_REPARSE_POINT); + ADD_FLAGS(FILE_ATTRIBUTE_COMPRESSED); + ADD_FLAGS(FILE_ATTRIBUTE_OFFLINE); + ADD_FLAGS(FILE_ATTRIBUTE_NONINDEXED); + ADD_FLAGS(FILE_ATTRIBUTE_ENCRYPTED); + ADD_FLAGS(FILE_ATTRIBUTE_ALL_MASK); + + ADD_FLAGS(FILE_SHARE_READ); + ADD_FLAGS(FILE_SHARE_WRITE); + ADD_FLAGS(FILE_SHARE_DELETE); + + /* change notify completion filter flags */ + ADD_FLAGS(FILE_NOTIFY_CHANGE_FILE_NAME); + ADD_FLAGS(FILE_NOTIFY_CHANGE_DIR_NAME); + ADD_FLAGS(FILE_NOTIFY_CHANGE_ATTRIBUTES); + ADD_FLAGS(FILE_NOTIFY_CHANGE_SIZE); + ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_WRITE); + ADD_FLAGS(FILE_NOTIFY_CHANGE_LAST_ACCESS); + ADD_FLAGS(FILE_NOTIFY_CHANGE_CREATION); + ADD_FLAGS(FILE_NOTIFY_CHANGE_EA); + ADD_FLAGS(FILE_NOTIFY_CHANGE_SECURITY); + ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_NAME); + ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_SIZE); + ADD_FLAGS(FILE_NOTIFY_CHANGE_STREAM_WRITE); + ADD_FLAGS(FILE_NOTIFY_CHANGE_NAME); + ADD_FLAGS(FILE_NOTIFY_CHANGE_ALL); + + /* change notify action results */ + ADD_FLAGS(NOTIFY_ACTION_ADDED); + ADD_FLAGS(NOTIFY_ACTION_REMOVED); + ADD_FLAGS(NOTIFY_ACTION_MODIFIED); + ADD_FLAGS(NOTIFY_ACTION_OLD_NAME); + ADD_FLAGS(NOTIFY_ACTION_NEW_NAME); + ADD_FLAGS(NOTIFY_ACTION_ADDED_STREAM); + ADD_FLAGS(NOTIFY_ACTION_REMOVED_STREAM); + ADD_FLAGS(NOTIFY_ACTION_MODIFIED_STREAM); + + return m; +} diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c new file mode 100644 index 0000000..ab81b43 --- /dev/null +++ b/source3/libsmb/samlogon_cache.c @@ -0,0 +1,405 @@ +/* + Unix SMB/CIFS implementation. + Net_sam_logon info3 helpers + Copyright (C) Alexander Bokovoy 2002. + Copyright (C) Andrew Bartlett 2002. + Copyright (C) Gerald Carter 2003. + Copyright (C) Tim Potter 2003. + Copyright (C) Guenther Deschner 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "samlogon_cache.h" +#include "system/filesys.h" +#include "system/time.h" +#include "lib/util/debug.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/memory.h" /* for SAFE_FREE() */ +#include "source3/lib/util_path.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "../libcli/security/security.h" +#include "util_tdb.h" + +#define NETSAMLOGON_TDB "netsamlogon_cache.tdb" + +static TDB_CONTEXT *netsamlogon_tdb = NULL; + +/*********************************************************************** + open the tdb + ***********************************************************************/ + +bool netsamlogon_cache_init(void) +{ + bool first_try = true; + char *path = NULL; + int ret; + struct tdb_context *tdb; + + if (netsamlogon_tdb) { + return true; + } + + path = cache_path(talloc_tos(), NETSAMLOGON_TDB); + if (path == NULL) { + return false; + } +again: + tdb = tdb_open_log(path, 0, TDB_DEFAULT|TDB_INCOMPATIBLE_HASH, + O_RDWR | O_CREAT, 0600); + if (tdb == NULL) { + DEBUG(0,("tdb_open_log('%s') - failed\n", path)); + goto clear; + } + + ret = tdb_check(tdb, NULL, NULL); + if (ret != 0) { + tdb_close(tdb); + DEBUG(0,("tdb_check('%s') - failed\n", path)); + goto clear; + } + + netsamlogon_tdb = tdb; + talloc_free(path); + return true; + +clear: + if (!first_try) { + talloc_free(path); + return false; + } + first_try = false; + + DEBUG(0,("retry after truncate for '%s'\n", path)); + ret = truncate(path, 0); + if (ret == -1) { + DBG_ERR("truncate failed: %s\n", strerror(errno)); + talloc_free(path); + return false; + } + + goto again; +} + +/*********************************************************************** + Clear cache getpwnam and getgroups entries from the winbindd cache +***********************************************************************/ + +void netsamlogon_clear_cached_user(const struct dom_sid *user_sid) +{ + struct dom_sid_buf keystr; + + if (!netsamlogon_cache_init()) { + DEBUG(0,("netsamlogon_clear_cached_user: cannot open " + "%s for write!\n", + NETSAMLOGON_TDB)); + return; + } + + /* Prepare key as DOMAIN-SID/USER-RID string */ + dom_sid_str_buf(user_sid, &keystr); + + DBG_DEBUG("SID [%s]\n", keystr.buf); + + tdb_delete_bystring(netsamlogon_tdb, keystr.buf); +} + +/*********************************************************************** + Store a netr_SamInfo3 structure in a tdb for later user + username should be in UTF-8 format +***********************************************************************/ + +bool netsamlogon_cache_store(const char *username, struct netr_SamInfo3 *info3) +{ + uint8_t dummy = 0; + TDB_DATA data = { .dptr = &dummy, .dsize = sizeof(dummy) }; + struct dom_sid_buf keystr; + bool result = false; + struct dom_sid user_sid; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + DATA_BLOB blob; + enum ndr_err_code ndr_err; + struct netsamlogoncache_entry r; + int ret; + + if (!info3) { + goto fail; + } + + if (!netsamlogon_cache_init()) { + D_WARNING("netsamlogon_cache_store: cannot open %s for write!\n", + NETSAMLOGON_TDB); + goto fail; + } + + /* + * First write a record with just the domain sid for + * netsamlogon_cache_domain_known. Use TDB_INSERT to avoid + * overwriting potentially other data. We're just interested + * in the existence of that record. + */ + dom_sid_str_buf(info3->base.domain_sid, &keystr); + + ret = tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_INSERT); + + if ((ret == -1) && (tdb_error(netsamlogon_tdb) != TDB_ERR_EXISTS)) { + D_WARNING("Could not store domain marker for %s: %s\n", + keystr.buf, tdb_errorstr(netsamlogon_tdb)); + goto fail; + } + + sid_compose(&user_sid, info3->base.domain_sid, info3->base.rid); + + /* Prepare key as DOMAIN-SID/USER-RID string */ + dom_sid_str_buf(&user_sid, &keystr); + + DBG_DEBUG("SID [%s]\n", keystr.buf); + + /* Prepare data */ + + if (info3->base.full_name.string == NULL) { + struct netr_SamInfo3 *cached_info3; + const char *full_name = NULL; + + cached_info3 = netsamlogon_cache_get(tmp_ctx, &user_sid); + if (cached_info3 != NULL) { + full_name = cached_info3->base.full_name.string; + } + + if (full_name != NULL) { + info3->base.full_name.string = talloc_strdup(info3, full_name); + if (info3->base.full_name.string == NULL) { + goto fail; + } + } + } + + /* only Samba fills in the username, not sure why NT doesn't */ + /* so we fill it in since winbindd_getpwnam() makes use of it */ + + if (!info3->base.account_name.string) { + info3->base.account_name.string = talloc_strdup(info3, username); + if (info3->base.account_name.string == NULL) { + goto fail; + } + } + + r.timestamp = time(NULL); + r.info3 = *info3; + + /* avoid storing secret information */ + ZERO_STRUCT(r.info3.base.key); + ZERO_STRUCT(r.info3.base.LMSessKey); + + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(netsamlogoncache_entry, &r); + } + + ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, &r, + (ndr_push_flags_fn_t)ndr_push_netsamlogoncache_entry); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_WARNING("failed to push entry to cache: %s\n", + ndr_errstr(ndr_err)); + goto fail; + } + + data.dsize = blob.length; + data.dptr = blob.data; + + if (tdb_store_bystring(netsamlogon_tdb, keystr.buf, data, TDB_REPLACE) == 0) { + result = true; + } + +fail: + TALLOC_FREE(tmp_ctx); + return result; +} + +/*********************************************************************** + Retrieves a netr_SamInfo3 structure from a tdb. Caller must + free the user_info struct (talloced memory) +***********************************************************************/ + +struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct dom_sid *user_sid) +{ + struct netr_SamInfo3 *info3 = NULL; + TDB_DATA data; + struct dom_sid_buf keystr; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + struct netsamlogoncache_entry r; + + if (!netsamlogon_cache_init()) { + DEBUG(0,("netsamlogon_cache_get: cannot open %s for write!\n", + NETSAMLOGON_TDB)); + return NULL; + } + + /* Prepare key as DOMAIN-SID/USER-RID string */ + dom_sid_str_buf(user_sid, &keystr); + DBG_DEBUG("SID [%s]\n", keystr.buf); + data = tdb_fetch_bystring( netsamlogon_tdb, keystr.buf ); + + if (!data.dptr) { + D_DEBUG("tdb fetch for %s is empty\n", keystr.buf); + return NULL; + } + + info3 = talloc_zero(mem_ctx, struct netr_SamInfo3); + if (!info3) { + goto done; + } + + blob = data_blob_const(data.dptr, data.dsize); + + ndr_err = ndr_pull_struct_blob_all( + &blob, mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + D_WARNING("netsamlogon_cache_get: failed to pull entry from cache\n"); + tdb_delete_bystring(netsamlogon_tdb, keystr.buf); + TALLOC_FREE(info3); + goto done; + } + + NDR_PRINT_DEBUG_LEVEL(DBGLVL_DEBUG, netsamlogoncache_entry, &r); + + info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3, + sizeof(r.info3)); + + done: + SAFE_FREE(data.dptr); + + return info3; +} + +bool netsamlogon_cache_have(const struct dom_sid *sid) +{ + struct dom_sid_buf keystr; + bool ok; + + if (!netsamlogon_cache_init()) { + DBG_WARNING("Cannot open %s\n", NETSAMLOGON_TDB); + return false; + } + + dom_sid_str_buf(sid, &keystr); + + ok = tdb_exists(netsamlogon_tdb, string_term_tdb_data(keystr.buf)); + return ok; +} + +struct netsamlog_cache_forall_state { + TALLOC_CTX *mem_ctx; + int (*cb)(const char *sid_str, + time_t when_cached, + struct netr_SamInfo3 *, + void *private_data); + void *private_data; +}; + +static int netsamlog_cache_traverse_cb(struct tdb_context *tdb, + TDB_DATA key, + TDB_DATA data, + void *private_data) +{ + struct netsamlog_cache_forall_state *state = + (struct netsamlog_cache_forall_state *)private_data; + TALLOC_CTX *mem_ctx = NULL; + DATA_BLOB blob; + const char *sid_str = NULL; + struct dom_sid sid; + struct netsamlogoncache_entry r; + enum ndr_err_code ndr_err; + int ret; + bool ok; + + if (key.dsize == 0) { + return 0; + } + if (key.dptr[key.dsize - 1] != '\0') { + return 0; + } + if (data.dptr == NULL) { + return 0; + } + sid_str = (char *)key.dptr; + + ok = string_to_sid(&sid, sid_str); + if (!ok) { + DBG_ERR("String to SID failed for %s\n", sid_str); + return -1; + } + + if (sid.num_auths != 5) { + return 0; + } + + mem_ctx = talloc_new(state->mem_ctx); + if (mem_ctx == NULL) { + return -1; + } + + blob = data_blob_const(data.dptr, data.dsize); + + ndr_err = ndr_pull_struct_blob( + &blob, state->mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry); + + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_ERR("failed to pull entry from cache\n"); + return -1; + } + + ret = state->cb(sid_str, r.timestamp, &r.info3, state->private_data); + + TALLOC_FREE(mem_ctx); + return ret; +} + +int netsamlog_cache_for_all(int (*cb)(const char *sid_str, + time_t when_cached, + struct netr_SamInfo3 *, + void *private_data), + void *private_data) +{ + int ret; + TALLOC_CTX *mem_ctx = NULL; + struct netsamlog_cache_forall_state state; + + if (!netsamlogon_cache_init()) { + DBG_ERR("Cannot open %s\n", NETSAMLOGON_TDB); + return -1; + } + + mem_ctx = talloc_init("netsamlog_cache_for_all"); + if (mem_ctx == NULL) { + return -1; + } + + state = (struct netsamlog_cache_forall_state) { + .mem_ctx = mem_ctx, + .cb = cb, + .private_data = private_data, + }; + + ret = tdb_traverse_read(netsamlogon_tdb, + netsamlog_cache_traverse_cb, + &state); + + TALLOC_FREE(state.mem_ctx); + return ret; +} diff --git a/source3/libsmb/samlogon_cache.h b/source3/libsmb/samlogon_cache.h new file mode 100644 index 0000000..29e0cea --- /dev/null +++ b/source3/libsmb/samlogon_cache.h @@ -0,0 +1,46 @@ +/* + * Unix SMB/CIFS implementation. + * Net_sam_logon info3 helpers + * Copyright (C) Alexander Bokovoy 2002. + * Copyright (C) Andrew Bartlett 2002. + * Copyright (C) Gerald Carter 2003. + * Copyright (C) Tim Potter 2003. + * Copyright (C) Guenther Deschner 2008. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMB_SAMLOGON_CACHE_H__ +#define __LIBSMB_SAMLOGON_CACHE_H__ + +#include "replace.h" +#include <talloc.h> + +struct dom_sid; +struct netr_SamInfo3; + +bool netsamlogon_cache_init(void); +void netsamlogon_clear_cached_user(const struct dom_sid *user_sid); +bool netsamlogon_cache_store(const char *username, + struct netr_SamInfo3 *info3); +struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, + const struct dom_sid *user_sid); +bool netsamlogon_cache_have(const struct dom_sid *sid); +int netsamlog_cache_for_all(int (*cb)(const char *sid_str, + time_t when_cached, + struct netr_SamInfo3 *, + void *private_data), + void *private_data); + +#endif diff --git a/source3/libsmb/smbclient.pc.in b/source3/libsmb/smbclient.pc.in new file mode 100644 index 0000000..bcef2f2 --- /dev/null +++ b/source3/libsmb/smbclient.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: smbclient +Description: A SMB library interface +Version: @PACKAGE_VERSION@ +Libs: @LIB_RPATH@ -L${libdir} -lsmbclient +Cflags: -I${includedir} +URL: http://www.samba.org/ diff --git a/source3/libsmb/smberr.c b/source3/libsmb/smberr.c new file mode 100644 index 0000000..854f9bb --- /dev/null +++ b/source3/libsmb/smberr.c @@ -0,0 +1,224 @@ +/* + Unix SMB/CIFS implementation. + Copyright (C) Andrew Tridgell 1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" + +/* error code stuff - put together by Merik Karman + merik@blackadder.dsh.oz.au */ + + +/* There is a big list of error codes and their meanings at: + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/errlist_7oz7.asp + + and if you don't like MSDN try: + + http://www.siris.gr/computers/library/error.htm + +*/ + +typedef const struct +{ + const char *name; + int code; + const char *message; +} err_code_struct; + +/* Dos Error Messages */ +static err_code_struct dos_msgs[] = { + {"ERRbadfunc",ERRbadfunc,"Invalid function."}, + {"ERRbadfile",ERRbadfile,"File not found."}, + {"ERRbadpath",ERRbadpath,"Directory invalid."}, + {"ERRnofids",ERRnofids,"No file descriptors available"}, + {"ERRnoaccess",ERRnoaccess,"Access denied."}, + {"ERRbadfid",ERRbadfid,"Invalid file handle."}, + {"ERRbadmcb",ERRbadmcb,"Memory control blocks destroyed."}, + {"ERRnomem",ERRnomem,"Insufficient server memory to perform the requested function."}, + {"ERRbadmem",ERRbadmem,"Invalid memory block address."}, + {"ERRbadenv",ERRbadenv,"Invalid environment."}, + {"ERRbadformat",11,"Invalid format."}, + {"ERRbadaccess",ERRbadaccess,"Invalid open mode."}, + {"ERRbaddata",ERRbaddata,"Invalid data."}, + {"ERRres",ERRres,"reserved."}, + {"ERRbaddrive",ERRbaddrive,"Invalid drive specified."}, + {"ERRremcd",ERRremcd,"A Delete Directory request attempted to remove the server's current directory."}, + {"ERRdiffdevice",ERRdiffdevice,"Not same device."}, + {"ERRnofiles",ERRnofiles,"A File Search command can find no more files matching the specified criteria."}, + {"ERRbadshare",ERRbadshare,"The sharing mode specified for an Open conflicts with existing FIDs on the file."}, + {"ERRlock",ERRlock,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRunsup", ERRunsup, "The operation is unsupported"}, + {"ERRnosuchshare", ERRnosuchshare, "You specified an invalid share name"}, + {"ERRfilexists",ERRfilexists,"The file named in a Create Directory, Make New File or Link request already exists."}, + {"ERRinvalidname",ERRinvalidname, "Invalid name"}, + {"ERRbadpipe",ERRbadpipe,"Pipe invalid."}, + {"ERRpipebusy",ERRpipebusy,"All instances of the requested pipe are busy."}, + {"ERRpipeclosing",ERRpipeclosing,"Pipe close in progress."}, + {"ERRnotconnected",ERRnotconnected,"No process on other end of pipe."}, + {"ERRmoredata",ERRmoredata,"There is more data to be returned."}, + {"ERRinvgroup",ERRinvgroup,"Invalid workgroup (try the -W option)"}, + {"ERRlogonfailure",ERRlogonfailure,"Logon failure"}, + {"ERRdiskfull",ERRdiskfull,"Disk full"}, + {"ERRgeneral",ERRgeneral, "General failure"}, + {"ERRbaddirectory", ERRbaddirectory, "Bad directory name"}, + {"ERRunknownlevel",ERRunknownlevel, "Unknown info level"}, + {NULL,-1,NULL}}; + +/* Server Error Messages */ +static err_code_struct server_msgs[] = { + {"ERRerror",1,"Non-specific error code."}, + {"ERRbadpw",2,"Bad password - name/password pair in a Tree Connect or Session Setup are invalid."}, + {"ERRbadtype",3,"reserved."}, + {"ERRaccess",4,"The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID."}, + {"ERRinvnid",5,"The tree ID (TID) specified in a command was invalid."}, + {"ERRinvnetname",6,"Invalid network name in tree connect."}, + {"ERRinvdevice",7,"Invalid device - printer request made to non-printer connection or non-printer request made to printer connection."}, + {"ERRqfull",49,"Print queue full (files) -- returned by open print file."}, + {"ERRqtoobig",50,"Print queue full -- no space."}, + {"ERRqeof",51,"EOF on print queue dump."}, + {"ERRinvpfid",52,"Invalid print file FID."}, + {"ERRsmbcmd",64,"The server did not recognize the command received."}, + {"ERRsrverror",65,"The server encountered an internal error, e.g., system file unavailable."}, + {"ERRfilespecs",67,"The file handle (FID) and pathname parameters contained an invalid combination of values."}, + {"ERRreserved",68,"reserved."}, + {"ERRbadpermits",69,"The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute."}, + {"ERRreserved",70,"reserved."}, + {"ERRsetattrmode",71,"The attribute mode in the Set File Attribute request is invalid."}, + {"ERRpaused",81,"Server is paused."}, + {"ERRmsgoff",82,"Not receiving messages."}, + {"ERRnoroom",83,"No room to buffer message."}, + {"ERRrmuns",87,"Too many remote user names."}, + {"ERRtimeout",88,"Operation timed out."}, + {"ERRnoresource",89,"No resources currently available for request."}, + {"ERRtoomanyuids",90,"Too many UIDs active on this session."}, + {"ERRbaduid",91,"The UID is not known as a valid ID on this session."}, + {"ERRusempx",250,"Temp unable to support Raw, use MPX mode."}, + {"ERRusestd",251,"Temp unable to support Raw, use standard read/write."}, + {"ERRcontmpx",252,"Continue in MPX mode."}, + {"ERRreserved",253,"reserved."}, + {"ERRreserved",254,"reserved."}, + {"ERRnosupport",0xFFFF,"Function not supported."}, + {NULL,-1,NULL}}; + +/* Hard Error Messages */ +static err_code_struct hard_msgs[] = { + {"ERRnowrite",19,"Attempt to write on write-protected diskette."}, + {"ERRbadunit",20,"Unknown unit."}, + {"ERRnotready",21,"Drive not ready."}, + {"ERRbadcmd",22,"Unknown command."}, + {"ERRdata",23,"Data error (CRC)."}, + {"ERRbadreq",24,"Bad request structure length."}, + {"ERRseek",25 ,"Seek error."}, + {"ERRbadmedia",26,"Unknown media type."}, + {"ERRbadsector",27,"Sector not found."}, + {"ERRnopaper",28,"Printer out of paper."}, + {"ERRwrite",29,"Write fault."}, + {"ERRread",30,"Read fault."}, + {"ERRgeneral",31,"General failure."}, + {"ERRbadshare",32,"An open conflicts with an existing open."}, + {"ERRlock",33,"A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process."}, + {"ERRwrongdisk",34,"The wrong disk was found in a drive."}, + {"ERRFCBUnavail",35,"No FCBs are available to process request."}, + {"ERRsharebufexc",36,"A sharing buffer has been exceeded."}, + {NULL,-1,NULL}}; + + +static const struct +{ + int code; + const char *e_class; + err_code_struct *err_msgs; +} err_classes[] = { + {0,"SUCCESS",NULL}, + {0x01,"ERRDOS",dos_msgs}, + {0x02,"ERRSRV",server_msgs}, + {0x03,"ERRHRD",hard_msgs}, + {0x04,"ERRXOS",NULL}, + {0xE1,"ERRRMX1",NULL}, + {0xE2,"ERRRMX2",NULL}, + {0xE3,"ERRRMX3",NULL}, + {0xFF,"ERRCMD",NULL}, + {-1,NULL,NULL}}; + + +/**************************************************************************** +return a SMB error name from a class and code +****************************************************************************/ +const char *smb_dos_err_name(uint8_t e_class, uint16_t num) +{ + char *result; + int i,j; + + for (i=0;err_classes[i].e_class;i++) + if (err_classes[i].code == e_class) { + if (err_classes[i].err_msgs) { + err_code_struct *err = err_classes[i].err_msgs; + for (j=0;err[j].name;j++) + if (num == err[j].code) { + return err[j].name; + } + } + result = talloc_asprintf(talloc_tos(), "%d", num); + SMB_ASSERT(result != NULL); + return result; + } + + result = talloc_asprintf(talloc_tos(), "Error: Unknown error class " + "(%d,%d)", e_class,num); + SMB_ASSERT(result != NULL); + return result; +} + +/* Return a string for a DOS error */ + +const char *get_dos_error_msg(WERROR result) +{ + uint16_t errnum; + + errnum = W_ERROR_V(result); + + return smb_dos_err_name(ERRDOS, errnum); +} + +/**************************************************************************** +return a SMB error class name as a string. +****************************************************************************/ +const char *smb_dos_err_class(uint8_t e_class) +{ + char *result; + int i; + + for (i=0;err_classes[i].e_class;i++) { + if (err_classes[i].code == e_class) { + return err_classes[i].e_class; + } + } + + result = talloc_asprintf(talloc_tos(), "Error: Unknown class (%d)", + e_class); + SMB_ASSERT(result != NULL); + return result; +} + +/***************************************************************************** +map a unix errno to a win32 error + *****************************************************************************/ +WERROR map_werror_from_unix(int error) +{ + NTSTATUS status = map_nt_error_from_unix(error); + return ntstatus_to_werror(status); +} diff --git a/source3/libsmb/smbsock_connect.c b/source3/libsmb/smbsock_connect.c new file mode 100644 index 0000000..80ea743 --- /dev/null +++ b/source3/libsmb/smbsock_connect.c @@ -0,0 +1,871 @@ +/* + Unix SMB/CIFS implementation. + Connect to 445 and 139/nbsesssetup + Copyright (C) Volker Lendecke 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "../lib/async_req/async_sock.h" +#include "../lib/util/tevent_ntstatus.h" +#include "../lib/util/tevent_unix.h" +#include "client.h" +#include "async_smb.h" +#include "../libcli/smb/read_smb.h" +#include "libsmb/nmblib.h" + +struct cli_session_request_state { + struct tevent_context *ev; + int sock; + uint32_t len_hdr; + struct iovec iov[3]; + uint8_t nb_session_response; +}; + +static void cli_session_request_sent(struct tevent_req *subreq); +static void cli_session_request_recvd(struct tevent_req *subreq); + +static struct tevent_req *cli_session_request_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int sock, + const struct nmb_name *called, + const struct nmb_name *calling) +{ + struct tevent_req *req, *subreq; + struct cli_session_request_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct cli_session_request_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->sock = sock; + + state->iov[1].iov_base = name_mangle( + state, called->name, called->name_type); + if (tevent_req_nomem(state->iov[1].iov_base, req)) { + return tevent_req_post(req, ev); + } + state->iov[1].iov_len = name_len( + (unsigned char *)state->iov[1].iov_base, + talloc_get_size(state->iov[1].iov_base)); + + state->iov[2].iov_base = name_mangle( + state, calling->name, calling->name_type); + if (tevent_req_nomem(state->iov[2].iov_base, req)) { + return tevent_req_post(req, ev); + } + state->iov[2].iov_len = name_len( + (unsigned char *)state->iov[2].iov_base, + talloc_get_size(state->iov[2].iov_base)); + + _smb_setlen(((char *)&state->len_hdr), + state->iov[1].iov_len + state->iov[2].iov_len); + SCVAL((char *)&state->len_hdr, 0, 0x81); + + state->iov[0].iov_base = &state->len_hdr; + state->iov[0].iov_len = sizeof(state->len_hdr); + + subreq = writev_send(state, ev, NULL, sock, true, state->iov, 3); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, cli_session_request_sent, req); + return req; +} + +static void cli_session_request_sent(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_request_state *state = tevent_req_data( + req, struct cli_session_request_state); + ssize_t ret; + int err; + + ret = writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (ret == -1) { + tevent_req_error(req, err); + return; + } + subreq = read_smb_send(state, state->ev, state->sock); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, cli_session_request_recvd, req); +} + +static void cli_session_request_recvd(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct cli_session_request_state *state = tevent_req_data( + req, struct cli_session_request_state); + uint8_t *buf; + ssize_t ret; + int err; + + ret = read_smb_recv(subreq, talloc_tos(), &buf, &err); + TALLOC_FREE(subreq); + + if (ret < 4) { + ret = -1; + err = EIO; + } + if (ret == -1) { + tevent_req_error(req, err); + return; + } + /* + * In case of an error there is more information in the data + * portion according to RFC1002. We're not subtle enough to + * respond to the different error conditions, so drop the + * error info here. + */ + state->nb_session_response = CVAL(buf, 0); + tevent_req_done(req); +} + +static bool cli_session_request_recv(struct tevent_req *req, int *err, uint8_t *resp) +{ + struct cli_session_request_state *state = tevent_req_data( + req, struct cli_session_request_state); + + if (tevent_req_is_unix_error(req, err)) { + return false; + } + *resp = state->nb_session_response; + return true; +} + +struct nb_connect_state { + struct tevent_context *ev; + const struct sockaddr_storage *addr; + const char *called_name; + int sock; + struct tevent_req *session_subreq; + struct nmb_name called; + struct nmb_name calling; +}; + +static void nb_connect_cleanup(struct tevent_req *req, + enum tevent_req_state req_state); +static void nb_connect_connected(struct tevent_req *subreq); +static void nb_connect_done(struct tevent_req *subreq); + +static struct tevent_req *nb_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct sockaddr_storage *addr, + const char *called_name, + int called_type, + const char *calling_name, + int calling_type) +{ + struct tevent_req *req, *subreq; + struct nb_connect_state *state; + + req = tevent_req_create(mem_ctx, &state, struct nb_connect_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->called_name = called_name; + state->addr = addr; + + state->sock = -1; + make_nmb_name(&state->called, called_name, called_type); + make_nmb_name(&state->calling, calling_name, calling_type); + + tevent_req_set_cleanup_fn(req, nb_connect_cleanup); + + subreq = open_socket_out_send(state, ev, addr, NBT_SMB_PORT, 5000); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, nb_connect_connected, req); + return req; +} + +static void nb_connect_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct nb_connect_state *state = tevent_req_data( + req, struct nb_connect_state); + + /* + * we need to free a pending request before closing the + * socket, see bug #11141 + */ + TALLOC_FREE(state->session_subreq); + + if (req_state == TEVENT_REQ_DONE) { + /* + * we keep the socket open for the caller to use + */ + return; + } + + if (state->sock != -1) { + close(state->sock); + state->sock = -1; + } + + return; +} + +static void nb_connect_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_connect_state *state = tevent_req_data( + req, struct nb_connect_state); + NTSTATUS status; + + status = open_socket_out_recv(subreq, &state->sock); + TALLOC_FREE(subreq); + if (!NT_STATUS_IS_OK(status)) { + tevent_req_nterror(req, status); + return; + } + subreq = cli_session_request_send(state, state->ev, state->sock, + &state->called, &state->calling); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_connect_done, req); + state->session_subreq = subreq; +} + +static void nb_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_connect_state *state = tevent_req_data( + req, struct nb_connect_state); + bool ret; + int err; + uint8_t resp; + + state->session_subreq = NULL; + + ret = cli_session_request_recv(subreq, &err, &resp); + TALLOC_FREE(subreq); + if (!ret) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + + /* + * RFC1002: 0x82 - POSITIVE SESSION RESPONSE + */ + + if (resp != 0x82) { + /* + * The server did not like our session request + */ + close(state->sock); + state->sock = -1; + + if (strequal(state->called_name, "*SMBSERVER")) { + /* + * Here we could try a name status request and + * use the first 0x20 type name. + */ + tevent_req_nterror( + req, NT_STATUS_RESOURCE_NAME_NOT_FOUND); + return; + } + + /* + * We could be subtle and distinguish between + * different failure modes, but what we do here + * instead is just retry with *SMBSERVER type 0x20. + */ + state->called_name = "*SMBSERVER"; + make_nmb_name(&state->called, state->called_name, 0x20); + + subreq = open_socket_out_send(state, state->ev, state->addr, + NBT_SMB_PORT, 5000); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_connect_connected, req); + return; + } + + tevent_req_done(req); + return; +} + +static NTSTATUS nb_connect_recv(struct tevent_req *req, int *sock) +{ + struct nb_connect_state *state = tevent_req_data( + req, struct nb_connect_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + *sock = state->sock; + state->sock = -1; + tevent_req_received(req); + return NT_STATUS_OK; +} + +struct smbsock_connect_state { + struct tevent_context *ev; + const struct sockaddr_storage *addr; + const char *called_name; + uint8_t called_type; + const char *calling_name; + uint8_t calling_type; + struct tevent_req *req_139; + struct tevent_req *req_445; + int sock; + uint16_t port; +}; + +static void smbsock_connect_cleanup(struct tevent_req *req, + enum tevent_req_state req_state); +static void smbsock_connect_connected(struct tevent_req *subreq); +static void smbsock_connect_do_139(struct tevent_req *subreq); + +struct tevent_req *smbsock_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct sockaddr_storage *addr, + uint16_t port, + const char *called_name, + int called_type, + const char *calling_name, + int calling_type) +{ + struct tevent_req *req; + struct smbsock_connect_state *state; + + req = tevent_req_create(mem_ctx, &state, struct smbsock_connect_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->addr = addr; + state->sock = -1; + state->called_name = + (called_name != NULL) ? called_name : "*SMBSERVER"; + state->called_type = + (called_type != -1) ? called_type : 0x20; + state->calling_name = + (calling_name != NULL) ? calling_name : lp_netbios_name(); + state->calling_type = + (calling_type != -1) ? calling_type : 0x00; + + tevent_req_set_cleanup_fn(req, smbsock_connect_cleanup); + + if (port == NBT_SMB_PORT) { + if (lp_disable_netbios()) { + tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED); + return tevent_req_post(req, ev); + } + + state->req_139 = nb_connect_send(state, state->ev, state->addr, + state->called_name, + state->called_type, + state->calling_name, + state->calling_type); + if (tevent_req_nomem(state->req_139, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + state->req_139, smbsock_connect_connected, req); + return req; + } + if (port != 0) { + state->req_445 = open_socket_out_send(state, ev, addr, port, + 5000); + if (tevent_req_nomem(state->req_445, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback( + state->req_445, smbsock_connect_connected, req); + return req; + } + + /* + * port==0, try both + */ + + state->req_445 = open_socket_out_send(state, ev, addr, TCP_SMB_PORT, 5000); + if (tevent_req_nomem(state->req_445, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->req_445, smbsock_connect_connected, + req); + + /* + * Check for disable_netbios + */ + if (lp_disable_netbios()) { + return req; + } + + /* + * After 5 msecs, fire the 139 (NBT) request + */ + state->req_139 = tevent_wakeup_send( + state, ev, timeval_current_ofs(0, 5000)); + if (tevent_req_nomem(state->req_139, req)) { + TALLOC_FREE(state->req_445); + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->req_139, smbsock_connect_do_139, + req); + return req; +} + +static void smbsock_connect_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct smbsock_connect_state *state = tevent_req_data( + req, struct smbsock_connect_state); + + /* + * we need to free a pending request before closing the + * socket, see bug #11141 + */ + TALLOC_FREE(state->req_445); + TALLOC_FREE(state->req_139); + + if (req_state == TEVENT_REQ_DONE) { + /* + * we keep the socket open for the caller to use + */ + return; + } + + if (state->sock != -1) { + close(state->sock); + state->sock = -1; + } + + return; +} + +static void smbsock_connect_do_139(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbsock_connect_state *state = tevent_req_data( + req, struct smbsock_connect_state); + bool ret; + + ret = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ret) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + state->req_139 = nb_connect_send(state, state->ev, state->addr, + state->called_name, + state->called_type, + state->calling_name, + state->calling_type); + if (tevent_req_nomem(state->req_139, req)) { + return; + } + tevent_req_set_callback(state->req_139, smbsock_connect_connected, + req); +} + +static void smbsock_connect_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbsock_connect_state *state = tevent_req_data( + req, struct smbsock_connect_state); + struct tevent_req *unfinished_req; + NTSTATUS status; + + if (subreq == state->req_445) { + + status = open_socket_out_recv(subreq, &state->sock); + TALLOC_FREE(state->req_445); + unfinished_req = state->req_139; + state->port = TCP_SMB_PORT; + + } else if (subreq == state->req_139) { + + status = nb_connect_recv(subreq, &state->sock); + TALLOC_FREE(state->req_139); + unfinished_req = state->req_445; + state->port = NBT_SMB_PORT; + + } else { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + if (NT_STATUS_IS_OK(status)) { + TALLOC_FREE(unfinished_req); + state->req_139 = NULL; + state->req_445 = NULL; + tevent_req_done(req); + return; + } + if (unfinished_req == NULL) { + /* + * Both requests failed + */ + tevent_req_nterror(req, status); + return; + } + /* + * Do nothing, wait for the second request to come here. + */ +} + +NTSTATUS smbsock_connect_recv(struct tevent_req *req, int *sock, + uint16_t *ret_port) +{ + struct smbsock_connect_state *state = tevent_req_data( + req, struct smbsock_connect_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + *sock = state->sock; + state->sock = -1; + if (ret_port != NULL) { + *ret_port = state->port; + } + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS smbsock_connect(const struct sockaddr_storage *addr, uint16_t port, + const char *called_name, int called_type, + const char *calling_name, int calling_type, + int *pfd, uint16_t *ret_port, int sec_timeout) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = smbsock_connect_send(frame, ev, addr, port, + called_name, called_type, + calling_name, calling_type); + if (req == NULL) { + goto fail; + } + if ((sec_timeout != 0) && + !tevent_req_set_endtime( + req, ev, timeval_current_ofs(sec_timeout, 0))) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = smbsock_connect_recv(req, pfd, ret_port); + fail: + TALLOC_FREE(frame); + return status; +} + +struct smbsock_any_connect_state { + struct tevent_context *ev; + const struct sockaddr_storage *addrs; + const char **called_names; + int *called_types; + const char **calling_names; + int *calling_types; + size_t num_addrs; + uint16_t port; + + struct tevent_req **requests; + size_t num_sent; + size_t num_received; + + int fd; + uint16_t chosen_port; + size_t chosen_index; +}; + +static void smbsock_any_connect_cleanup(struct tevent_req *req, + enum tevent_req_state req_state); +static bool smbsock_any_connect_send_next( + struct tevent_req *req, struct smbsock_any_connect_state *state); +static void smbsock_any_connect_trynext(struct tevent_req *subreq); +static void smbsock_any_connect_connected(struct tevent_req *subreq); + +struct tevent_req *smbsock_any_connect_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + const struct sockaddr_storage *addrs, + const char **called_names, + int *called_types, + const char **calling_names, + int *calling_types, + size_t num_addrs, uint16_t port) +{ + struct tevent_req *req, *subreq; + struct smbsock_any_connect_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct smbsock_any_connect_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->addrs = addrs; + state->num_addrs = num_addrs; + state->called_names = called_names; + state->called_types = called_types; + state->calling_names = calling_names; + state->calling_types = calling_types; + state->port = port; + state->fd = -1; + + tevent_req_set_cleanup_fn(req, smbsock_any_connect_cleanup); + + if (num_addrs == 0) { + tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER); + return tevent_req_post(req, ev); + } + + state->requests = talloc_zero_array(state, struct tevent_req *, + num_addrs); + if (tevent_req_nomem(state->requests, req)) { + return tevent_req_post(req, ev); + } + if (!smbsock_any_connect_send_next(req, state)) { + return tevent_req_post(req, ev); + } + if (state->num_sent >= state->num_addrs) { + return req; + } + subreq = tevent_wakeup_send(state, ev, timeval_current_ofs(0, 10000)); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbsock_any_connect_trynext, req); + return req; +} + +static void smbsock_any_connect_cleanup(struct tevent_req *req, + enum tevent_req_state req_state) +{ + struct smbsock_any_connect_state *state = tevent_req_data( + req, struct smbsock_any_connect_state); + + TALLOC_FREE(state->requests); + + if (req_state == TEVENT_REQ_DONE) { + /* + * Keep the socket open for the caller. + */ + return; + } + + if (state->fd != -1) { + close(state->fd); + state->fd = -1; + } +} + +static void smbsock_any_connect_trynext(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbsock_any_connect_state *state = tevent_req_data( + req, struct smbsock_any_connect_state); + bool ret; + + ret = tevent_wakeup_recv(subreq); + TALLOC_FREE(subreq); + if (!ret) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + if (!smbsock_any_connect_send_next(req, state)) { + return; + } + if (state->num_sent >= state->num_addrs) { + return; + } + subreq = tevent_wakeup_send(state, state->ev, + tevent_timeval_set(0, 10000)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, smbsock_any_connect_trynext, req); +} + +static bool smbsock_any_connect_send_next( + struct tevent_req *req, struct smbsock_any_connect_state *state) +{ + struct tevent_req *subreq; + + if (state->num_sent >= state->num_addrs) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return false; + } + subreq = smbsock_connect_send( + state->requests, state->ev, &state->addrs[state->num_sent], + state->port, + (state->called_names != NULL) + ? state->called_names[state->num_sent] : NULL, + (state->called_types != NULL) + ? state->called_types[state->num_sent] : -1, + (state->calling_names != NULL) + ? state->calling_names[state->num_sent] : NULL, + (state->calling_types != NULL) + ? state->calling_types[state->num_sent] : -1); + if (tevent_req_nomem(subreq, req)) { + return false; + } + tevent_req_set_callback(subreq, smbsock_any_connect_connected, req); + + state->requests[state->num_sent] = subreq; + state->num_sent += 1; + + return true; +} + +static void smbsock_any_connect_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct smbsock_any_connect_state *state = tevent_req_data( + req, struct smbsock_any_connect_state); + NTSTATUS status; + int fd = 0; + uint16_t chosen_port = 0; + size_t i; + size_t chosen_index = 0; + + for (i=0; i<state->num_sent; i++) { + if (state->requests[i] == subreq) { + chosen_index = i; + break; + } + } + if (i == state->num_sent) { + tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); + return; + } + + status = smbsock_connect_recv(subreq, &fd, &chosen_port); + + TALLOC_FREE(subreq); + state->requests[chosen_index] = NULL; + + if (NT_STATUS_IS_OK(status)) { + /* + * tevent_req_done() will kill all the other requests + * via smbsock_any_connect_cleanup(). + */ + state->fd = fd; + state->chosen_port = chosen_port; + state->chosen_index = chosen_index; + tevent_req_done(req); + return; + } + + state->num_received += 1; + if (state->num_received < state->num_addrs) { + /* + * More addrs pending, wait for the others + */ + return; + } + + /* + * This is the last response, none succeeded. + */ + tevent_req_nterror(req, status); + return; +} + +NTSTATUS smbsock_any_connect_recv(struct tevent_req *req, int *pfd, + size_t *chosen_index, + uint16_t *chosen_port) +{ + struct smbsock_any_connect_state *state = tevent_req_data( + req, struct smbsock_any_connect_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + *pfd = state->fd; + state->fd = -1; + if (chosen_index != NULL) { + *chosen_index = state->chosen_index; + } + if (chosen_port != NULL) { + *chosen_port = state->chosen_port; + } + tevent_req_received(req); + return NT_STATUS_OK; +} + +NTSTATUS smbsock_any_connect(const struct sockaddr_storage *addrs, + const char **called_names, + int *called_types, + const char **calling_names, + int *calling_types, + size_t num_addrs, + uint16_t port, + int sec_timeout, + int *pfd, size_t *chosen_index, + uint16_t *chosen_port) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev; + struct tevent_req *req; + NTSTATUS status = NT_STATUS_NO_MEMORY; + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + req = smbsock_any_connect_send(frame, ev, addrs, + called_names, called_types, + calling_names, calling_types, + num_addrs, port); + if (req == NULL) { + goto fail; + } + if ((sec_timeout != 0) && + !tevent_req_set_endtime( + req, ev, timeval_current_ofs(sec_timeout, 0))) { + goto fail; + } + if (!tevent_req_poll_ntstatus(req, ev, &status)) { + goto fail; + } + status = smbsock_any_connect_recv(req, pfd, chosen_index, chosen_port); + fail: + TALLOC_FREE(frame); + return status; +} diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c new file mode 100644 index 0000000..71e1a35 --- /dev/null +++ b/source3/libsmb/trusts_util.c @@ -0,0 +1,629 @@ +/* + * Unix SMB/CIFS implementation. + * Routines to operate on various trust relationships + * Copyright (C) Andrew Bartlett 2001 + * Copyright (C) Rafal Szczesniak 2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "../libcli/auth/libcli_auth.h" +#include "../libcli/auth/netlogon_creds_cli.h" +#include "rpc_client/cli_netlogon.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_netlogon.h" +#include "librpc/gen_ndr/secrets.h" +#include "secrets.h" +#include "passdb.h" +#include "libsmb/libsmb.h" +#include "source3/include/messages.h" +#include "source3/include/g_lock.h" +#include "lib/util/util_tdb.h" + +/********************************************************* + Change the domain password on the PDC. + Do most of the legwork ourselfs. Caller must have + already setup the connection to the NETLOGON pipe +**********************************************************/ + +struct trust_pw_change_state { + struct g_lock_ctx *g_ctx; + char *g_lock_key; +}; + +static int trust_pw_change_state_destructor(struct trust_pw_change_state *state) +{ + g_lock_unlock(state->g_ctx, + string_term_tdb_data(state->g_lock_key)); + return 0; +} + +char *trust_pw_new_value(TALLOC_CTX *mem_ctx, + enum netr_SchannelType sec_channel_type, + int security) +{ + /* + * use secure defaults, which match + * what windows uses for computer passwords. + * + * We used to have min=128 and max=255 here, but + * it's a bad idea because of bugs in the Windows + * RODC/RWDC PasswordUpdateForward handling via + * NetrLogonSendToSam. + * + * See https://bugzilla.samba.org/show_bug.cgi?id=14984 + */ + size_t min = 120; + size_t max = 120; + + switch (sec_channel_type) { + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + if (security == SEC_DOMAIN) { + /* + * The maximum length of a trust account password. + * Used when we randomly create it, 15 char passwords + * exceed NT4's max password length. + */ + min = 14; + max = 14; + } + break; + case SEC_CHAN_DNS_DOMAIN: + /* + * new_len * 2 = 498 bytes is the largest possible length + * NL_PASSWORD_VERSION consumes the rest of the possible 512 bytes + * and a confounder with at least 2 bytes is required. + * + * Windows uses new_len = 120 => 240 bytes (utf16) + */ + min = 120; + max = 120; + break; + case SEC_CHAN_DOMAIN: + /* + * The maximum length of a trust account password. + * Used when we randomly create it, 15 char passwords + * exceed NT4's max password length. + */ + min = 14; + max = 14; + break; + default: + break; + } + + /* + * Create a random machine account password + * We create a random buffer and convert that to utf8. + * This is similar to what windows is doing. + */ + return generate_random_machine_password(mem_ctx, min, max); +} + +/* + * Temporary function to wrap cli_auth in a lck + */ + +static NTSTATUS netlogon_creds_cli_lck_auth( + struct netlogon_creds_cli_context *context, + struct dcerpc_binding_handle *b, + uint8_t num_nt_hashes, + const struct samr_Password * const *nt_hashes, + uint8_t *idx_nt_hashes) +{ + struct netlogon_creds_cli_lck *lck; + NTSTATUS status; + + status = netlogon_creds_cli_lck( + context, NETLOGON_CREDS_CLI_LCK_EXCLUSIVE, + talloc_tos(), &lck); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("netlogon_creds_cli_lck failed: %s\n", + nt_errstr(status)); + return status; + } + + status = netlogon_creds_cli_auth(context, b, num_nt_hashes, nt_hashes, + idx_nt_hashes); + TALLOC_FREE(lck); + + return status; +} + +NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, + struct messaging_context *msg_ctx, + struct dcerpc_binding_handle *b, + const char *domain, + const char *dcname, + bool force) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char *context_name = NULL; + struct trust_pw_change_state *state; + struct cli_credentials *creds = NULL; + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *prev = NULL; + const struct samr_Password *current_nt_hash = NULL; + const struct samr_Password *previous_nt_hash = NULL; + uint8_t num_nt_hashes = 0; + uint8_t idx = 0; + const struct samr_Password *nt_hashes[1+3] = { NULL, }; + uint8_t idx_nt_hashes = 0; + uint8_t idx_current = UINT8_MAX; + enum netr_SchannelType sec_channel_type = SEC_CHAN_NULL; + time_t pass_last_set_time; + uint32_t old_version = 0; + struct pdb_trusted_domain *td = NULL; + struct timeval g_timeout = { 0, }; + int timeout = 0; + struct timeval tv = { 0, }; + char *new_trust_pw_str = NULL; + size_t len = 0; + DATA_BLOB new_trust_pw_blob = data_blob_null; + uint32_t new_version = 0; + uint32_t *new_trust_version = NULL; + NTSTATUS status; + bool ok; + + state = talloc_zero(frame, struct trust_pw_change_state); + if (state == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + state->g_ctx = g_lock_ctx_init(state, msg_ctx); + if (state->g_ctx == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + state->g_lock_key = talloc_asprintf(state, + "trust_password_change_%s", + domain); + if (state->g_lock_key == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + g_timeout = timeval_current_ofs(10, 0); + status = g_lock_lock(state->g_ctx, + string_term_tdb_data(state->g_lock_key), + G_LOCK_WRITE, g_timeout); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("could not get g_lock on [%s]!\n", + state->g_lock_key)); + TALLOC_FREE(frame); + return status; + } + + talloc_set_destructor(state, trust_pw_change_state_destructor); + + status = pdb_get_trust_credentials(domain, NULL, frame, &creds); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("could not fetch domain creds for domain %s - %s!\n", + domain, nt_errstr(status))); + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + + current_nt_hash = cli_credentials_get_nt_hash(creds, frame); + if (current_nt_hash == NULL) { + DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + previous_nt_hash = cli_credentials_get_old_nt_hash(creds, frame); + + old_version = cli_credentials_get_kvno(creds); + pass_last_set_time = cli_credentials_get_password_last_changed_time(creds); + sec_channel_type = cli_credentials_get_secure_channel_type(creds); + + new_version = old_version + 1; + + switch (sec_channel_type) { + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + break; + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + status = pdb_get_trusted_domain(frame, domain, &td); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("pdb_get_trusted_domain() failed for domain %s - %s!\n", + domain, nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + new_trust_version = &new_version; + break; + default: + TALLOC_FREE(frame); + return NT_STATUS_NOT_SUPPORTED; + } + + timeout = lp_machine_password_timeout(); + if (timeout == 0) { + if (!force) { + DEBUG(10,("machine password never expires\n")); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + } + + tv.tv_sec = pass_last_set_time; + DEBUG(10, ("password last changed %s\n", + timeval_string(talloc_tos(), &tv, false))); + tv.tv_sec += timeout; + DEBUGADD(10, ("password valid until %s\n", + timeval_string(talloc_tos(), &tv, false))); + + if (!force && !timeval_expired(&tv)) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + context_name = netlogon_creds_cli_debug_string(context, talloc_tos()); + if (context_name == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + /* + * Create a random machine account password + * We create a random buffer and convert that to utf8. + * This is similar to what windows is doing. + */ + new_trust_pw_str = trust_pw_new_value(frame, sec_channel_type, + lp_security()); + if (new_trust_pw_str == NULL) { + DEBUG(0, ("trust_pw_new_value() failed\n")); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + len = strlen(new_trust_pw_str); + ok = convert_string_talloc(frame, CH_UNIX, CH_UTF16, + new_trust_pw_str, len, + (void **)&new_trust_pw_blob.data, + &new_trust_pw_blob.length); + if (!ok) { + status = NT_STATUS_UNMAPPABLE_CHARACTER; + if (errno == ENOMEM) { + status = NT_STATUS_NO_MEMORY; + } + DBG_ERR("convert_string_talloc(CH_UTF16MUNGED, CH_UNIX) " + "failed for of %s - %s\n", + domain, nt_errstr(status)); + TALLOC_FREE(frame); + return status; + } + + switch (sec_channel_type) { + + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + status = secrets_prepare_password_change(domain, dcname, + new_trust_pw_str, + frame, &info, &prev); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + TALLOC_FREE(new_trust_pw_str); + + if (prev != NULL) { + /* + * We had a failure before we changed the password. + */ + nt_hashes[idx++] = &prev->password->nt_hash; + + DEBUG(0,("%s : %s(%s): A password change was already " + "started against '%s' at %s. Trying to " + "recover...\n", + current_timestring(talloc_tos(), false), + __func__, domain, + prev->password->change_server, + nt_time_string(talloc_tos(), + prev->password->change_time))); + DEBUG(0,("%s : %s(%s): Last failure local[%s] remote[%s] " + "against '%s' at %s.\n", + current_timestring(talloc_tos(), false), + __func__, domain, + nt_errstr(prev->local_status), + nt_errstr(prev->remote_status), + prev->change_server, + nt_time_string(talloc_tos(), + prev->change_time))); + } + + idx_current = idx; + nt_hashes[idx++] = &info->password->nt_hash; + if (info->old_password != NULL) { + nt_hashes[idx++] = &info->old_password->nt_hash; + } + if (info->older_password != NULL) { + nt_hashes[idx++] = &info->older_password->nt_hash; + } + + /* + * We use the password that's already persistent in + * our database in order to handle failures. + */ + data_blob_clear_free(&new_trust_pw_blob); + new_trust_pw_blob = info->next_change->password->cleartext_blob; + break; + + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + idx_current = idx; + nt_hashes[idx++] = current_nt_hash; + if (previous_nt_hash != NULL) { + nt_hashes[idx++] = previous_nt_hash; + } + break; + + default: + smb_panic("Unsupported secure channel type"); + break; + } + num_nt_hashes = idx; + + DEBUG(0,("%s : %s(%s): Verifying passwords remotely %s.\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + /* + * Check which password the dc knows about. + * + * TODO: + * If the previous password is the only password in common with the dc, + * we better skip the password change, or use something like + * ServerTrustPasswordsGet() or netr_ServerGetTrustInfo() to fix our + * local secrets before doing the change. + */ + status = netlogon_creds_cli_lck_auth(context, b, + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for old passwords (%u) - %s!\n", + context_name, num_nt_hashes, nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + if (prev != NULL && idx_nt_hashes == 0) { + DEBUG(0,("%s : %s(%s): Verified new password remotely " + "without changing %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + status = secrets_finish_password_change(prev->password->change_server, + prev->password->change_time, + info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_prepare_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + DEBUG(0,("%s : %s(%s): Recovered previous password change.\n", + current_timestring(talloc_tos(), false), + __func__, domain)); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + if (idx_nt_hashes != idx_current) { + DEBUG(0,("%s : %s(%s): Verified older password remotely " + "skip changing %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + if (info == NULL) { + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + + status = secrets_defer_password_change(dcname, + NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE, + NT_STATUS_NOT_COMMITTED, + info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_defer_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + + DEBUG(0,("%s : %s(%s): Verified old password remotely using %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + /* + * Return the result of trying to write the new password + * back into the trust account file. + */ + + switch (sec_channel_type) { + + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + /* + * we called secrets_prepare_password_change() above. + */ + break; + + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + /* + * we need to get the sid first for the + * pdb_set_trusteddom_pw call + */ + ok = pdb_set_trusteddom_pw(domain, new_trust_pw_str, + &td->security_identifier); + if (!ok) { + DEBUG(0, ("pdb_set_trusteddom_pw() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + TALLOC_FREE(new_trust_pw_str); + break; + + default: + smb_panic("Unsupported secure channel type"); + break; + } + + DEBUG(0,("%s : %s(%s): Changed password locally\n", + current_timestring(talloc_tos(), false), __func__, domain)); + + status = netlogon_creds_cli_ServerPasswordSet(context, b, + &new_trust_pw_blob, + new_trust_version); + if (!NT_STATUS_IS_OK(status)) { + NTSTATUS status2; + const char *fn = NULL; + + ok = dcerpc_binding_handle_is_connected(b); + + DEBUG(0,("%s : %s(%s) remote password change with %s failed " + "- %s (%s)\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name, + nt_errstr(status), + ok ? "connected": "disconnected")); + + if (!ok) { + /* + * The connection is broken, we don't + * know if the password was changed, + * we hope to have more luck next time. + */ + status2 = secrets_failed_password_change(dcname, + NT_STATUS_NOT_COMMITTED, + status, + info); + fn = "secrets_failed_password_change"; + } else { + /* + * The server rejected the change, we don't + * retry and defer the change to the next + * "machine password timeout" interval. + */ + status2 = secrets_defer_password_change(dcname, + NT_STATUS_NOT_COMMITTED, + status, + info); + fn = "secrets_defer_password_change"; + } + if (!NT_STATUS_IS_OK(status2)) { + DEBUG(0, ("%s() failed for domain %s!\n", + fn, domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + TALLOC_FREE(frame); + return status; + } + + DEBUG(0,("%s : %s(%s): Changed password remotely using %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + switch (sec_channel_type) { + + case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: + status = secrets_finish_password_change( + info->next_change->change_server, + info->next_change->change_time, + info); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("secrets_finish_password_change() failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + DEBUG(0,("%s : %s(%s): Finished password change.\n", + current_timestring(talloc_tos(), false), + __func__, domain)); + break; + + case SEC_CHAN_DNS_DOMAIN: + case SEC_CHAN_DOMAIN: + /* + * we used pdb_set_trusteddom_pw(). + */ + break; + + default: + smb_panic("Unsupported secure channel type"); + break; + } + + ok = cli_credentials_set_utf16_password(creds, + &new_trust_pw_blob, + CRED_SPECIFIED); + if (!ok) { + DEBUG(0, ("cli_credentials_set_password failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_NO_MEMORY; + } + + current_nt_hash = cli_credentials_get_nt_hash(creds, frame); + if (current_nt_hash == NULL) { + DEBUG(0, ("cli_credentials_get_nt_hash failed for domain %s!\n", + domain)); + TALLOC_FREE(frame); + return NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE; + } + + /* + * Now we verify the new password. + */ + idx = 0; + nt_hashes[idx++] = current_nt_hash; + num_nt_hashes = idx; + status = netlogon_creds_cli_lck_auth(context, b, + num_nt_hashes, + nt_hashes, + &idx_nt_hashes); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("netlogon_creds_cli_auth(%s) failed for new password - %s!\n", + context_name, nt_errstr(status))); + TALLOC_FREE(frame); + return status; + } + + DEBUG(0,("%s : %s(%s): Verified new password remotely using %s\n", + current_timestring(talloc_tos(), false), + __func__, domain, context_name)); + + TALLOC_FREE(frame); + return NT_STATUS_OK; +} diff --git a/source3/libsmb/unexpected.c b/source3/libsmb/unexpected.c new file mode 100644 index 0000000..ced4696 --- /dev/null +++ b/source3/libsmb/unexpected.c @@ -0,0 +1,749 @@ +/* + Unix SMB/CIFS implementation. + handle unexpected packets + Copyright (C) Andrew Tridgell 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "libsmb/unexpected.h" +#include "../lib/util/tevent_ntstatus.h" +#include "lib/util_tsock.h" +#include "libsmb/nmblib.h" +#include "lib/tsocket/tsocket.h" +#include "lib/util/sys_rw.h" + +static const char *nmbd_socket_dir(void) +{ + return lp_parm_const_string(-1, "nmbd", "socket dir", + get_dyn_NMBDSOCKETDIR()); +} + +struct nb_packet_query { + enum packet_type type; + size_t mailslot_namelen; + int trn_id; +}; + +struct nb_packet_client; + +struct nb_packet_server { + struct tevent_context *ev; + int listen_sock; + struct tevent_fd *listen_fde; + int max_clients; + int num_clients; + struct nb_packet_client *clients; +}; + +struct nb_packet_client { + struct nb_packet_client *prev, *next; + struct nb_packet_server *server; + + enum packet_type type; + int trn_id; + char *mailslot_name; + + struct { + uint8_t byte; + struct iovec iov[1]; + } ack; + + struct tstream_context *sock; + struct tevent_queue *out_queue; +}; + +static int nb_packet_server_destructor(struct nb_packet_server *s); +static void nb_packet_server_listener(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data); + +NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int max_clients, + struct nb_packet_server **presult) +{ + struct nb_packet_server *result; + NTSTATUS status; + int rc; + + result = talloc_zero(mem_ctx, struct nb_packet_server); + if (result == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + result->ev = ev; + result->max_clients = max_clients; + + result->listen_sock = create_pipe_sock( + nmbd_socket_dir(), "unexpected", 0755); + if (result->listen_sock == -1) { + status = map_nt_error_from_unix(errno); + goto fail; + } + rc = listen(result->listen_sock, 5); + if (rc < 0) { + status = map_nt_error_from_unix(errno); + goto fail; + } + talloc_set_destructor(result, nb_packet_server_destructor); + + result->listen_fde = tevent_add_fd(ev, result, + result->listen_sock, + TEVENT_FD_READ, + nb_packet_server_listener, + result); + if (result->listen_fde == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + *presult = result; + return NT_STATUS_OK; +fail: + TALLOC_FREE(result); + return status; +} + +static int nb_packet_server_destructor(struct nb_packet_server *s) +{ + TALLOC_FREE(s->listen_fde); + + if (s->listen_sock != -1) { + close(s->listen_sock); + s->listen_sock = -1; + } + return 0; +} + +static int nb_packet_client_destructor(struct nb_packet_client *c); +static ssize_t nb_packet_client_more(uint8_t *buf, size_t buflen, + void *private_data); +static void nb_packet_got_query(struct tevent_req *req); +static void nb_packet_client_ack_done(struct tevent_req *req); +static void nb_packet_client_read_done(struct tevent_req *req); + +static void nb_packet_server_listener(struct tevent_context *ev, + struct tevent_fd *fde, + uint16_t flags, + void *private_data) +{ + struct nb_packet_server *server = talloc_get_type_abort( + private_data, struct nb_packet_server); + struct nb_packet_client *client; + struct tevent_req *req; + struct sockaddr_un sunaddr; + socklen_t len; + int sock; + int ret; + + len = sizeof(sunaddr); + + sock = accept(server->listen_sock, (struct sockaddr *)(void *)&sunaddr, + &len); + if (sock == -1) { + return; + } + smb_set_close_on_exec(sock); + DEBUG(6,("accepted socket %d\n", sock)); + + client = talloc_zero(server, struct nb_packet_client); + if (client == NULL) { + DEBUG(10, ("talloc failed\n")); + close(sock); + return; + } + ret = tstream_bsd_existing_socket(client, sock, &client->sock); + if (ret != 0) { + DEBUG(10, ("tstream_bsd_existing_socket failed\n")); + TALLOC_FREE(client); + close(sock); + return; + } + + client->server = server; + + client->out_queue = tevent_queue_create( + client, "unexpected packet output"); + if (client->out_queue == NULL) { + DEBUG(10, ("tevent_queue_create failed\n")); + TALLOC_FREE(client); + return; + } + + req = tstream_read_packet_send(client, ev, client->sock, + sizeof(struct nb_packet_query), + nb_packet_client_more, NULL); + if (req == NULL) { + DEBUG(10, ("tstream_read_packet_send failed\n")); + TALLOC_FREE(client); + return; + } + tevent_req_set_callback(req, nb_packet_got_query, client); + + DLIST_ADD(server->clients, client); + server->num_clients += 1; + + talloc_set_destructor(client, nb_packet_client_destructor); + + if (server->num_clients > server->max_clients) { + DEBUG(10, ("Too many clients, dropping oldest\n")); + + /* + * no TALLOC_FREE here, don't mess with the list structs + */ + talloc_free(server->clients->prev); + } +} + +static ssize_t nb_packet_client_more(uint8_t *buf, size_t buflen, + void *private_data) +{ + struct nb_packet_query q; + if (buflen > sizeof(struct nb_packet_query)) { + return 0; + } + /* Take care of alignment */ + memcpy(&q, buf, sizeof(q)); + if (q.mailslot_namelen > 1024) { + DEBUG(10, ("Got invalid mailslot namelen %d\n", + (int)q.mailslot_namelen)); + return -1; + } + return q.mailslot_namelen; +} + +static int nb_packet_client_destructor(struct nb_packet_client *c) +{ + tevent_queue_stop(c->out_queue); + TALLOC_FREE(c->sock); + + DLIST_REMOVE(c->server->clients, c); + c->server->num_clients -= 1; + return 0; +} + +static void nb_packet_got_query(struct tevent_req *req) +{ + struct nb_packet_client *client = tevent_req_callback_data( + req, struct nb_packet_client); + struct nb_packet_query q; + uint8_t *buf; + ssize_t nread; + int err; + + nread = tstream_read_packet_recv(req, talloc_tos(), &buf, &err); + TALLOC_FREE(req); + if (nread < (ssize_t)sizeof(struct nb_packet_query)) { + DEBUG(10, ("read_packet_recv returned %d (%s)\n", + (int)nread, + (nread == -1) ? strerror(err) : "wrong length")); + TALLOC_FREE(client); + return; + } + + /* Take care of alignment */ + memcpy(&q, buf, sizeof(q)); + + if ((size_t)nread != + sizeof(struct nb_packet_query) + q.mailslot_namelen) { + DEBUG(10, ("nb_packet_got_query: Invalid mailslot namelength\n")); + TALLOC_FREE(client); + return; + } + + client->trn_id = q.trn_id; + client->type = q.type; + if (q.mailslot_namelen > 0) { + client->mailslot_name = talloc_strndup( + client, (char *)buf + sizeof(q), + q.mailslot_namelen); + if (client->mailslot_name == NULL) { + TALLOC_FREE(client); + return; + } + } + + client->ack.byte = 0; + client->ack.iov[0].iov_base = &client->ack.byte; + client->ack.iov[0].iov_len = 1; + req = tstream_writev_queue_send(client, client->server->ev, + client->sock, + client->out_queue, + client->ack.iov, 1); + if (req == NULL) { + DEBUG(10, ("tstream_writev_queue_send failed\n")); + TALLOC_FREE(client); + return; + } + tevent_req_set_callback(req, nb_packet_client_ack_done, client); + + req = tstream_read_packet_send(client, client->server->ev, + client->sock, 1, NULL, NULL); + if (req == NULL) { + DEBUG(10, ("Could not activate reader for client exit " + "detection\n")); + TALLOC_FREE(client); + return; + } + tevent_req_set_callback(req, nb_packet_client_read_done, + client); +} + +static void nb_packet_client_ack_done(struct tevent_req *req) +{ + struct nb_packet_client *client = tevent_req_callback_data( + req, struct nb_packet_client); + ssize_t nwritten; + int err; + + nwritten = tstream_writev_queue_recv(req, &err); + + TALLOC_FREE(req); + + if (nwritten == -1) { + DEBUG(10, ("tstream_writev_queue_recv failed: %s\n", + strerror(err))); + TALLOC_FREE(client); + return; + } +} + +static void nb_packet_client_read_done(struct tevent_req *req) +{ + struct nb_packet_client *client = tevent_req_callback_data( + req, struct nb_packet_client); + ssize_t nread; + uint8_t *buf; + int err; + + nread = tstream_read_packet_recv(req, talloc_tos(), &buf, &err); + TALLOC_FREE(req); + if (nread == 1) { + DEBUG(10, ("Protocol error, received data on write-only " + "unexpected socket: 0x%2.2x\n", (*buf))); + } + TALLOC_FREE(client); +} + +static void nb_packet_client_send(struct nb_packet_client *client, + struct packet_struct *p); + +void nb_packet_dispatch(struct nb_packet_server *server, + struct packet_struct *p) +{ + struct nb_packet_client *c; + uint16_t trn_id; + + switch (p->packet_type) { + case NMB_PACKET: + trn_id = p->packet.nmb.header.name_trn_id; + break; + case DGRAM_PACKET: + trn_id = p->packet.dgram.header.dgm_id; + break; + default: + DEBUG(10, ("Got invalid packet type %d\n", + (int)p->packet_type)); + return; + } + for (c = server->clients; c != NULL; c = c->next) { + + if (c->type != p->packet_type) { + DEBUG(10, ("client expects packet %d, got %d\n", + c->type, p->packet_type)); + continue; + } + + if (p->packet_type == NMB_PACKET) { + /* + * See if the client specified transaction + * ID. Filter if it did. + */ + if ((c->trn_id != -1) && + (c->trn_id != trn_id)) { + DEBUG(10, ("client expects trn %d, got %d\n", + c->trn_id, trn_id)); + continue; + } + } else { + /* + * See if the client specified a mailslot + * name. Filter if it did. + */ + if ((c->mailslot_name != NULL) && + !match_mailslot_name(p, c->mailslot_name)) { + continue; + } + } + nb_packet_client_send(c, p); + } +} + +struct nb_packet_client_header { + size_t len; + enum packet_type type; + time_t timestamp; + struct in_addr ip; + int port; +}; + +struct nb_packet_client_state { + struct nb_packet_client *client; + struct iovec iov[2]; + struct nb_packet_client_header hdr; + char buf[1024]; +}; + +static void nb_packet_client_send_done(struct tevent_req *req); + +static void nb_packet_client_send(struct nb_packet_client *client, + struct packet_struct *p) +{ + struct nb_packet_client_state *state; + struct tevent_req *req; + + if (tevent_queue_length(client->out_queue) > 10) { + /* + * Skip clients that don't listen anyway, some form of DoS + * protection + */ + return; + } + + state = talloc_zero(client, struct nb_packet_client_state); + if (state == NULL) { + DEBUG(10, ("talloc failed\n")); + return; + } + + state->client = client; + + state->hdr.ip = p->ip; + state->hdr.port = p->port; + state->hdr.timestamp = p->timestamp; + state->hdr.type = p->packet_type; + state->hdr.len = build_packet(state->buf, sizeof(state->buf), p); + + state->iov[0].iov_base = (char *)&state->hdr; + state->iov[0].iov_len = sizeof(state->hdr); + state->iov[1].iov_base = state->buf; + state->iov[1].iov_len = state->hdr.len; + + req = tstream_writev_queue_send(state, client->server->ev, + client->sock, + client->out_queue, + state->iov, 2); + if (req == NULL) { + DEBUG(10, ("tstream_writev_queue_send failed\n")); + return; + } + tevent_req_set_callback(req, nb_packet_client_send_done, state); +} + +static void nb_packet_client_send_done(struct tevent_req *req) +{ + struct nb_packet_client_state *state = tevent_req_callback_data( + req, struct nb_packet_client_state); + struct nb_packet_client *client = state->client; + ssize_t nwritten; + int err; + + nwritten = tstream_writev_queue_recv(req, &err); + + TALLOC_FREE(req); + TALLOC_FREE(state); + + if (nwritten == -1) { + DEBUG(10, ("tstream_writev_queue failed: %s\n", strerror(err))); + TALLOC_FREE(client); + return; + } +} + +struct nb_packet_reader { + struct tstream_context *sock; +}; + +struct nb_packet_reader_state { + struct tevent_context *ev; + struct nb_packet_query query; + const char *mailslot_name; + struct iovec iov[2]; + struct nb_packet_reader *reader; +}; + +static void nb_packet_reader_connected(struct tevent_req *subreq); +static void nb_packet_reader_sent_query(struct tevent_req *subreq); +static void nb_packet_reader_got_ack(struct tevent_req *subreq); + +struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + enum packet_type type, + int trn_id, + const char *mailslot_name) +{ + struct tevent_req *req, *subreq; + struct nb_packet_reader_state *state; + struct tsocket_address *laddr; + char *rpath; + struct tsocket_address *raddr; + int ret; + + req = tevent_req_create(mem_ctx, &state, + struct nb_packet_reader_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->query.trn_id = trn_id; + state->query.type = type; + state->mailslot_name = mailslot_name; + + if (mailslot_name != NULL) { + state->query.mailslot_namelen = strlen(mailslot_name); + } + + state->reader = talloc_zero(state, struct nb_packet_reader); + if (tevent_req_nomem(state->reader, req)) { + return tevent_req_post(req, ev); + } + + ret = tsocket_address_unix_from_path(state, NULL, &laddr); + if (ret != 0) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + rpath = talloc_asprintf(state, "%s/%s", nmbd_socket_dir(), + "unexpected"); + if (tevent_req_nomem(rpath, req)) { + return tevent_req_post(req, ev); + } + ret = tsocket_address_unix_from_path(state, rpath, &raddr); + if (ret != 0) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return tevent_req_post(req, ev); + } + + subreq = tstream_unix_connect_send(state, ev, laddr, raddr); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, nb_packet_reader_connected, req); + return req; +} + +static void nb_packet_reader_connected(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_packet_reader_state *state = tevent_req_data( + req, struct nb_packet_reader_state); + int res, err; + int num_iovecs = 1; + + res = tstream_unix_connect_recv(subreq, &err, state->reader, + &state->reader->sock); + TALLOC_FREE(subreq); + if (res == -1) { + DEBUG(10, ("tstream_unix_connect failed: %s\n", strerror(err))); + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + + state->iov[0].iov_base = (char *)&state->query; + state->iov[0].iov_len = sizeof(state->query); + + if (state->mailslot_name != NULL) { + num_iovecs = 2; + state->iov[1].iov_base = discard_const_p( + char, state->mailslot_name); + state->iov[1].iov_len = state->query.mailslot_namelen; + } + + subreq = tstream_writev_send(state, state->ev, state->reader->sock, + state->iov, num_iovecs); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_packet_reader_sent_query, req); +} + +static void nb_packet_reader_sent_query(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_packet_reader_state *state = tevent_req_data( + req, struct nb_packet_reader_state); + ssize_t written; + int err; + + written = tstream_writev_recv(subreq, &err); + TALLOC_FREE(subreq); + if (written == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + if ((size_t)written != + sizeof(state->query) + state->query.mailslot_namelen) { + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } + subreq = tstream_read_packet_send(state, state->ev, + state->reader->sock, + 1, NULL, NULL); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, nb_packet_reader_got_ack, req); +} + +static void nb_packet_reader_got_ack(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_packet_reader_state *state = tevent_req_data( + req, struct nb_packet_reader_state); + ssize_t nread; + int err; + uint8_t *buf; + + nread = tstream_read_packet_recv(subreq, state, &buf, &err); + TALLOC_FREE(subreq); + if (nread == -1) { + DEBUG(10, ("read_packet_recv returned %s\n", + strerror(err))); + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + if (nread != 1) { + DBG_DEBUG("read = %zd, expected 1\n", nread); + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR); + return; + } + tevent_req_done(req); +} + +NTSTATUS nb_packet_reader_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct nb_packet_reader **preader) +{ + struct nb_packet_reader_state *state = tevent_req_data( + req, struct nb_packet_reader_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + *preader = talloc_move(mem_ctx, &state->reader); + tevent_req_received(req); + return NT_STATUS_OK; +} + +struct nb_packet_read_state { + struct nb_packet_client_header hdr; + uint8_t *buf; + size_t buflen; +}; + +static ssize_t nb_packet_read_more(uint8_t *buf, size_t buflen, void *p); +static void nb_packet_read_done(struct tevent_req *subreq); + +struct tevent_req *nb_packet_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nb_packet_reader *reader) +{ + struct tevent_req *req, *subreq; + struct nb_packet_read_state *state; + + req = tevent_req_create(mem_ctx, &state, struct nb_packet_read_state); + if (req == NULL) { + return NULL; + } + subreq = tstream_read_packet_send(state, ev, reader->sock, + sizeof(struct nb_packet_client_header), + nb_packet_read_more, state); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, nb_packet_read_done, req); + return req; +} + +static ssize_t nb_packet_read_more(uint8_t *buf, size_t buflen, void *p) +{ + struct nb_packet_read_state *state = talloc_get_type_abort( + p, struct nb_packet_read_state); + + if (buflen > sizeof(struct nb_packet_client_header)) { + /* + * Been here, done + */ + return 0; + } + memcpy(&state->hdr, buf, sizeof(struct nb_packet_client_header)); + return state->hdr.len; +} + +static void nb_packet_read_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct nb_packet_read_state *state = tevent_req_data( + req, struct nb_packet_read_state); + ssize_t nread; + int err; + + nread = tstream_read_packet_recv(subreq, state, &state->buf, &err); + if (nread == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + state->buflen = nread; + tevent_req_done(req); +} + +NTSTATUS nb_packet_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct packet_struct **ppacket) +{ + struct nb_packet_read_state *state = tevent_req_data( + req, struct nb_packet_read_state); + struct nb_packet_client_header hdr; + struct packet_struct *packet; + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + tevent_req_received(req); + return status; + } + + memcpy(&hdr, state->buf, sizeof(hdr)); + + packet = parse_packet_talloc( + mem_ctx, + (char *)state->buf + sizeof(struct nb_packet_client_header), + state->buflen - sizeof(struct nb_packet_client_header), + state->hdr.type, state->hdr.ip, state->hdr.port); + if (packet == NULL) { + tevent_req_received(req); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + *ppacket = packet; + tevent_req_received(req); + return NT_STATUS_OK; +} diff --git a/source3/libsmb/unexpected.h b/source3/libsmb/unexpected.h new file mode 100644 index 0000000..270976b --- /dev/null +++ b/source3/libsmb/unexpected.h @@ -0,0 +1,49 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Volker Lendecke 2018 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __LIBSMB_UNEXPECTED_H__ +#define __LIBSMB_UNEXPECTED_H__ + +#include "replace.h" +#include <tevent.h> +#include "libcli/util/ntstatus.h" +#include "nameserv.h" + +struct nb_packet_server; +struct nb_packet_reader; + +NTSTATUS nb_packet_server_create(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + int max_clients, + struct nb_packet_server **presult); +void nb_packet_dispatch(struct nb_packet_server *server, + struct packet_struct *p); +struct tevent_req *nb_packet_reader_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + enum packet_type type, + int trn_id, + const char *mailslot_name); +NTSTATUS nb_packet_reader_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct nb_packet_reader **preader); +struct tevent_req *nb_packet_read_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct nb_packet_reader *reader); +NTSTATUS nb_packet_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, + struct packet_struct **ppacket); + +#endif diff --git a/source3/libsmb/wscript b/source3/libsmb/wscript new file mode 100644 index 0000000..61503d0 --- /dev/null +++ b/source3/libsmb/wscript @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +def build(bld): + bld.SAMBA3_LIBRARY('smbclient', + source=''' + libsmb_cache.c + libsmb_compat.c + libsmb_context.c + libsmb_dir.c + libsmb_file.c + libsmb_misc.c + libsmb_path.c + libsmb_printjob.c + libsmb_server.c + libsmb_stat.c + libsmb_xattr.c + libsmb_setget.c''', + public_deps=''' + pthread + talloc + smbconf + libsmb + KRBCLIENT + msrpc3 + libcli_lsa3''', + public_headers='../include/libsmbclient.h', + abi_directory='ABI', + abi_match='smbc_*', + vnum='0.7.0', + pc_files='smbclient.pc') |