From e60433efa182484c9fd480d831a21970775e0c99 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 7 Dec 2014 12:08:55 +0100 Subject: [PATCH 01/97] lib: Simplify load_case_tables_library() We don't really need a talloc context here Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- lib/util/charset/codepoints.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/util/charset/codepoints.c b/lib/util/charset/codepoints.c index 0984164d48..499cea42f0 100644 --- a/lib/util/charset/codepoints.c +++ b/lib/util/charset/codepoints.c @@ -47,15 +47,16 @@ This is the function that should be called from library code. ********************************************************************/ void load_case_tables_library(void) { - TALLOC_CTX *mem_ctx; + const char *codepagedir = get_dyn_CODEPAGEDIR(); + size_t codepagedir_len = strlen(codepagedir); + char buf[codepagedir_len+13]; + + snprintf(buf, sizeof(buf), "%s/upcase.dat", codepagedir); + upcase_table = map_file(buf, 0x20000); + + snprintf(buf, sizeof(buf), "%s/lowcase.dat", codepagedir); + lowcase_table = map_file(buf, 0x20000); - mem_ctx = talloc_init("load_case_tables"); - if (!mem_ctx) { - smb_panic("No memory for case_tables"); - } - upcase_table = map_file(talloc_asprintf(mem_ctx, "%s/upcase.dat", get_dyn_CODEPAGEDIR()), 0x20000); - lowcase_table = map_file(talloc_asprintf(mem_ctx, "%s/lowcase.dat", get_dyn_CODEPAGEDIR()), 0x20000); - talloc_free(mem_ctx); if (upcase_table == NULL) { DEBUG(1, ("Failed to load upcase.dat, will use lame ASCII-only case sensitivity rules\n")); upcase_table = (void *)-1; From c9235deee0fc49c99cfaf2329b7af526d9dd12d0 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 15:31:19 -0800 Subject: [PATCH 02/97] s3: smbd: Fix *allocate* calls to follow POSIX error return convention. vfswrap_fallocate() is broken in that it can call posix_fallocate() which returns an int error (and doesn't set errno) but can also call Linux fallocate() which returns -1 and sets errno. Standardize on the -1,errno convention. Reported by Jones who provided the initial patch. This patch tested and confirmed working by him as well. https://bugzilla.samba.org/show_bug.cgi?id=10982 Signed-off-by: Jeremy Allison Reviewed-by: David Disseldorp --- source3/modules/vfs_default.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index 1e1c318a89..f5b8f9be11 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -1864,15 +1864,14 @@ static int strict_allocate_ftruncate(vfs_handle_struct *handle, files_struct *fs return ENOTSUP or EINVAL in cases like that. */ ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_EXTEND_SIZE, pst->st_ex_size, space_to_write); - if (ret == ENOSPC) { - errno = ENOSPC; + if (ret == -1 && errno == ENOSPC) { return -1; } if (ret == 0) { return 0; } DEBUG(10,("strict_allocate_ftruncate: SMB_VFS_FALLOCATE failed with " - "error %d. Falling back to slow manual allocation\n", ret)); + "error %d. Falling back to slow manual allocation\n", errno)); /* available disk space is enough or not? */ space_avail = get_dfree_info(fsp->conn, @@ -1888,8 +1887,7 @@ static int strict_allocate_ftruncate(vfs_handle_struct *handle, files_struct *fs /* Write out the real space on disk. */ ret = vfs_slow_fallocate(fsp, pst->st_ex_size, space_to_write); if (ret != 0) { - errno = ret; - ret = -1; + return -1; } return 0; @@ -1974,6 +1972,15 @@ static int vfswrap_fallocate(vfs_handle_struct *handle, START_PROFILE(syscall_fallocate); if (mode == VFS_FALLOCATE_EXTEND_SIZE) { result = sys_posix_fallocate(fsp->fh->fd, offset, len); + /* + * posix_fallocate returns 0 on success, errno on error + * and doesn't set errno. Make it behave like fallocate() + * which returns -1, and sets errno on failure. + */ + if (result != 0) { + errno = result; + result = -1; + } } else if (mode == VFS_FALLOCATE_KEEP_SIZE) { result = sys_fallocate(fsp->fh->fd, mode, offset, len); } else { From cc1f91cec627cb3e4fc89b96aae1e7e4c539cd1c Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 15:34:12 -0800 Subject: [PATCH 03/97] s3: smbd: Fix *allocate* calls to follow POSIX error return convention. Fix vfs_allocate_file_space(), vfs_slow_fallocate(), vfs_fill_sparse() to follow the -1,errno convention for errors. Standardize on the -1,errno convention. Reported by Jones who provided the initial patch. This patch tested and confirmed working by him as well. https://bugzilla.samba.org/show_bug.cgi?id=10982 Signed-off-by: Jeremy Allison Reviewed-by: David Disseldorp --- source3/smbd/vfs.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 51362c3c3b..d10e0d67e9 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -586,6 +586,10 @@ int vfs_allocate_file_space(files_struct *fsp, uint64_t len) return 0; } + if (ret == -1 && errno == ENOSPC) { + return -1; + } + len -= fsp->fsp_name->st.st_ex_size; len /= 1024; /* Len is now number of 1k blocks needed. */ space_avail = get_dfree_info(conn, fsp->fsp_name->base_name, false, @@ -640,7 +644,7 @@ int vfs_set_filelen(files_struct *fsp, off_t len) fails. Needs to be outside of the default version of SMB_VFS_FALLOCATE as this is also called from the default SMB_VFS_FTRUNCATE code. Always extends the file size. - Returns 0 on success, errno on failure. + Returns 0 on success, -1 on failure. ****************************************************************************/ #define SPARSE_BUF_WRITE_SIZE (32*1024) @@ -654,7 +658,7 @@ int vfs_slow_fallocate(files_struct *fsp, off_t offset, off_t len) sparse_buf = SMB_CALLOC_ARRAY(char, SPARSE_BUF_WRITE_SIZE); if (!sparse_buf) { errno = ENOMEM; - return ENOMEM; + return -1; } } @@ -663,10 +667,12 @@ int vfs_slow_fallocate(files_struct *fsp, off_t offset, off_t len) pwrite_ret = SMB_VFS_PWRITE(fsp, sparse_buf, curr_write_size, offset + total); if (pwrite_ret == -1) { + int saved_errno = errno; DEBUG(10,("vfs_slow_fallocate: SMB_VFS_PWRITE for file " "%s failed with error %s\n", - fsp_str_dbg(fsp), strerror(errno))); - return errno; + fsp_str_dbg(fsp), strerror(saved_errno))); + errno = saved_errno; + return -1; } total += pwrite_ret; } @@ -724,9 +730,7 @@ int vfs_fill_sparse(files_struct *fsp, off_t len) * return ENOTSUP or EINVAL in cases like that. */ ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_EXTEND_SIZE, offset, num_to_write); - if (ret == ENOSPC) { - errno = ENOSPC; - ret = -1; + if (ret == -1 && errno == ENOSPC) { goto out; } if (ret == 0) { @@ -737,10 +741,6 @@ int vfs_fill_sparse(files_struct *fsp, off_t len) } ret = vfs_slow_fallocate(fsp, offset, num_to_write); - if (ret != 0) { - errno = ret; - ret = -1; - } out: From 2845e1c29f8bce6306a73d546184c401bf89cfea Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 15:37:11 -0800 Subject: [PATCH 04/97] s3: modules: Fix *allocate* calls to follow POSIX error return convention. Fix up the ceph, fruit, time_audit and streams_xattr modules to follow the -1,errno convention for errors. Reported by Jones who provided the initial patch. This patch tested and confirmed working by him as well. Signed-off-by: Jeremy Allison Reviewed-by: David Disseldorp Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Mon Dec 8 02:59:43 CET 2014 on sn-devel-104 --- source3/modules/vfs_ceph.c | 13 +++---------- source3/modules/vfs_fruit.c | 5 +++-- source3/modules/vfs_streams_xattr.c | 5 +++-- source3/modules/vfs_time_audit.c | 8 +++++++- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/source3/modules/vfs_ceph.c b/source3/modules/vfs_ceph.c index e402ff1141..b0a00249f1 100644 --- a/source3/modules/vfs_ceph.c +++ b/source3/modules/vfs_ceph.c @@ -795,15 +795,14 @@ static int strict_allocate_ftruncate(struct vfs_handle_struct *handle, files_str return ENOTSUP or EINVAL in cases like that. */ ret = SMB_VFS_FALLOCATE(fsp, VFS_FALLOCATE_EXTEND_SIZE, pst->st_ex_size, space_to_write); - if (ret == ENOSPC) { - errno = ENOSPC; + if (ret == -1 && errno == ENOSPC) { return -1; } if (ret == 0) { return 0; } DEBUG(10,("[CEPH] strict_allocate_ftruncate: SMB_VFS_FALLOCATE failed with " - "error %d. Falling back to slow manual allocation\n", ret)); + "error %d. Falling back to slow manual allocation\n", errno)); /* available disk space is enough or not? */ space_avail = get_dfree_info(fsp->conn, @@ -817,13 +816,7 @@ static int strict_allocate_ftruncate(struct vfs_handle_struct *handle, files_str } /* Write out the real space on disk. */ - ret = vfs_slow_fallocate(fsp, pst->st_ex_size, space_to_write); - if (ret != 0) { - errno = ret; - ret = -1; - } - - return 0; + return vfs_slow_fallocate(fsp, pst->st_ex_size, space_to_write); } static int cephwrap_ftruncate(struct vfs_handle_struct *handle, files_struct *fsp, off_t len) diff --git a/source3/modules/vfs_fruit.c b/source3/modules/vfs_fruit.c index 18a6823bb0..a8bf7b4fd6 100644 --- a/source3/modules/vfs_fruit.c +++ b/source3/modules/vfs_fruit.c @@ -3050,11 +3050,12 @@ static int fruit_fallocate(struct vfs_handle_struct *handle, } if (!fruit_fsp_recheck(ad, fsp)) { - return errno; + return -1; } /* Let the pwrite code path handle it. */ - return ENOSYS; + errno = ENOSYS; + return -1; } static int fruit_ftruncate(struct vfs_handle_struct *handle, diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index f0ab7321e0..5c5a9a17fa 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -1103,11 +1103,12 @@ static int streams_xattr_fallocate(struct vfs_handle_struct *handle, } if (!streams_xattr_recheck(sio)) { - return errno; + return -1; } /* Let the pwrite code path handle it. */ - return ENOSYS; + errno = ENOSYS; + return -1; } diff --git a/source3/modules/vfs_time_audit.c b/source3/modules/vfs_time_audit.c index 4ce9238a2c..a1e825aa09 100644 --- a/source3/modules/vfs_time_audit.c +++ b/source3/modules/vfs_time_audit.c @@ -1216,18 +1216,24 @@ static int smb_time_audit_fallocate(vfs_handle_struct *handle, off_t len) { int result; + int saved_errno = 0; struct timespec ts1,ts2; double timediff; clock_gettime_mono(&ts1); result = SMB_VFS_NEXT_FALLOCATE(handle, fsp, mode, offset, len); + if (result == -1) { + saved_errno = errno; + } clock_gettime_mono(&ts2); timediff = nsec_time_diff(&ts2,&ts1)*1.0e-9; if (timediff > audit_timeout) { smb_time_audit_log_fsp("fallocate", timediff, fsp); } - + if (result == -1) { + errno = saved_errno; + } return result; } From 6214dbd5741e1e3e0e02b7e8eb42a76168a44192 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 2 Dec 2014 01:53:18 +0100 Subject: [PATCH 05/97] s4-ldap: Pass on-wire transmitted control down the chain Change-Id: Ic3a117f74e8a67c45584fdb520d92e4f0cb01c5e Signed-off-by: Kamen Mazdrashki Reviewed-by: Andrew Bartlett --- source4/ldap_server/ldap_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/ldap_server/ldap_backend.c b/source4/ldap_server/ldap_backend.c index b0877d2986..fd2b57936c 100644 --- a/source4/ldap_server/ldap_backend.c +++ b/source4/ldap_server/ldap_backend.c @@ -459,7 +459,7 @@ static int ldapsrv_rename_with_controls(struct ldapsrv_call *call, ret = ldb_build_rename_req(&req, ldb, ldb, olddn, newdn, - NULL, + controls, res, ldb_modify_default_callback, NULL); From 29732b0d427472041bf3a586f3eeb281ccd408d5 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 2 Dec 2014 05:04:40 +0100 Subject: [PATCH 06/97] s4-tests/env_loadparm: Throw KeyError in case SMB_CONF_PATH A bit more specific for the caller to "know" that env key is missing Change-Id: I4d4c2121af868d79f46f865f420336222bc67347 Signed-off-by: Kamen Mazdrashki Reviewed-by: Jelmer Vernooij Autobuild-User(master): Kamen Mazdrashki Autobuild-Date(master): Mon Dec 8 05:27:34 CET 2014 on sn-devel-104 --- python/samba/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py index 8e662ed156..cca82bbd67 100644 --- a/python/samba/tests/__init__.py +++ b/python/samba/tests/__init__.py @@ -91,7 +91,7 @@ def env_loadparm(): try: lp.load(os.environ["SMB_CONF_PATH"]) except KeyError: - raise Exception("SMB_CONF_PATH not set") + raise KeyError("SMB_CONF_PATH not set") return lp From 78e8bafb322ec69c5ff4b32a5e1c5679c9dea6bf Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Wed, 26 Nov 2014 10:18:34 +0100 Subject: [PATCH 07/97] socket_wrapper: Add missing prototype check for eventfd. BUG: https://bugzilla.samba.org/show_bug.cgi?id=10965 Newer glibc versions use and unsinged integer for the count instead of an integer. Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher --- lib/socket_wrapper/wscript | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/socket_wrapper/wscript b/lib/socket_wrapper/wscript index 279f57793c..91d23d150b 100644 --- a/lib/socket_wrapper/wscript +++ b/lib/socket_wrapper/wscript @@ -88,6 +88,11 @@ def configure(conf): 'int ioctl(int s, int r, ...)', define='HAVE_IOCTL_INT', headers='unistd.h sys/ioctl.h') + if conf.CONFIG_SET("HAVE_EVENTFD"): + conf.CHECK_C_PROTOTYPE('eventfd', + 'int eventfd(unsigned int count, int flags)', + define='HAVE_EVENTFD_UNSIGNED_INT', headers='sys/eventfd.h') + # Create full path to socket_wrapper srcdir = os.path.realpath(conf.srcdir) libsocket_wrapper_so_path = srcdir + '/bin/default/lib/socket_wrapper/libsocket-wrapper.so' From 266323dac64977c236ff19679aaf90f69a1ec245 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 8 Dec 2014 10:07:42 +0100 Subject: [PATCH 08/97] smbd: Add missing include for iov_buflen(). Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher --- source3/smbd/smb2_server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 3f23e2a797..4a2c875414 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -28,6 +28,7 @@ #include "smbprofile.h" #include "../lib/util/bitmap.h" #include "../librpc/gen_ndr/krb5pac.h" +#include "lib/iov_buf.h" #include "auth.h" static void smbd_smb2_connection_handler(struct tevent_context *ev, From 4acf171ab9480dbf23d95bd9624e2e4ec6723316 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 8 Dec 2014 10:09:29 +0100 Subject: [PATCH 09/97] vfs: Add missing include for sys_pread() in cacheprime module. Signed-off-by: Andreas Schneider Reviewed-by: Stefan Metzmacher Autobuild-User(master): Stefan Metzmacher Autobuild-Date(master): Mon Dec 8 16:54:51 CET 2014 on sn-devel-104 --- source3/modules/vfs_cacheprime.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/modules/vfs_cacheprime.c b/source3/modules/vfs_cacheprime.c index 65e63e2b39..e90e09a17f 100644 --- a/source3/modules/vfs_cacheprime.c +++ b/source3/modules/vfs_cacheprime.c @@ -17,6 +17,7 @@ #include "includes.h" #include "smbd/smbd.h" +#include "lib/sys_rw.h" /* Cache priming module. * From 2cd9a5c3209e947a6e63ad019da869790d01d0aa Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:56:03 -0800 Subject: [PATCH 10/97] s3:locking: pass servicename_new to leases_db_rename() Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/locking/leases_db.c | 1 + source3/locking/leases_db.h | 1 + source3/locking/locking.c | 1 + 3 files changed, 3 insertions(+) diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index 7e000aa075..4cb38f0091 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -389,6 +389,7 @@ NTSTATUS leases_db_parse(const struct GUID *client_guid, NTSTATUS leases_db_rename(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicename_new, const char *filename_new, const char *stream_name_new) { diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index 906a99bc61..f3988632b5 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -45,6 +45,7 @@ NTSTATUS leases_db_parse(const struct GUID *client_guid, NTSTATUS leases_db_rename(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicepath_new, const char *filename_new, const char *stream_name_new); #endif /* _LEASES_DB_H_ */ diff --git a/source3/locking/locking.c b/source3/locking/locking.c index dd73f683af..221d6ee8d6 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -575,6 +575,7 @@ bool rename_share_filename(struct messaging_context *msg_ctx, status = leases_db_rename(&l->client_guid, &l->lease_key, &id, + d->servicepath, d->base_name, d->stream_name); if (!NT_STATUS_IS_OK(status)) { From 5661198d718496201ea2a6bb75d043a8b255b578 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:47:52 -0800 Subject: [PATCH 11/97] s3:locking: prepare the data model for leases_db to cope with dynamic path renames. interface leases_db { typedef [public] struct { GUID client_guid; smb2_lease_key lease_key; } leases_db_key; typedef [public] struct { file_id id; [string,charset(UTF8)] char *servicepath; [string,charset(UTF8)] char *base_name; [string,charset(UTF8)] char *stream_name; } leases_db_file; typedef [public] struct { uint32 num_files; [size_is(num_files)] leases_db_file files[]; } leases_db_value; } As designed by metze. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/librpc/idl/leases_db.idl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl index 2ab1591b76..bdb875defe 100644 --- a/source3/librpc/idl/leases_db.idl +++ b/source3/librpc/idl/leases_db.idl @@ -14,6 +14,13 @@ interface leases_db smb2_lease_key lease_key; } leases_db_key; + typedef [public] struct { + file_id id; + [string,charset(UTF8)] char *servicepath; + [string,charset(UTF8)] char *base_name; + [string,charset(UTF8)] char *stream_name; + } leases_db_file; + typedef [public] struct { uint32 num_file_ids; [size_is(num_file_ids)] file_id ids[]; From b3b878eea4ecdd13828fc8f912ad95904e8a6c5e Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:57:24 -0800 Subject: [PATCH 12/97] s3:locking: Add new utility function leases_db_copy_file_ids() Will be used by lease db parsers. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/locking/leases_db.c | 20 ++++++++++++++++++++ source3/locking/leases_db.h | 5 +++++ 2 files changed, 25 insertions(+) diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index 4cb38f0091..ed4f09a0ff 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -408,3 +408,23 @@ NTSTATUS leases_db_rename(const struct GUID *client_guid, filename_new, stream_name_new); } + +NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx, + uint32_t num_files, + const struct leases_db_file *files, + struct file_id **pp_ids) +{ + uint32_t i; + struct file_id *ids = talloc_array(mem_ctx, + struct file_id, + num_files); + if (ids == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (i = 0; i < num_files; i++) { + ids[i] = files[i].id; + } + *pp_ids = ids; + return NT_STATUS_OK; +} diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index f3988632b5..0daa0ecade 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -24,6 +24,7 @@ struct GUID; struct smb2_lease_key; struct file_id; +struct leases_db_file; bool leases_db_init(bool read_only); NTSTATUS leases_db_add(const struct GUID *client_guid, @@ -48,4 +49,8 @@ NTSTATUS leases_db_rename(const struct GUID *client_guid, const char *servicepath_new, const char *filename_new, const char *stream_name_new); +NTSTATUS leases_db_copy_file_ids(TALLOC_CTX *mem_ctx, + uint32_t num_files, + const struct leases_db_file *files, + struct file_id **pp_ids); #endif /* _LEASES_DB_H_ */ From 708f87b79dcdfc58e2219e90473160eb5a22ecb6 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:58:39 -0800 Subject: [PATCH 13/97] s3:locking: pass down servicepath to leases_db_add() Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/locking/leases_db.c | 2 ++ source3/locking/leases_db.h | 1 + source3/smbd/open.c | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index ed4f09a0ff..a9e2566652 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -85,6 +85,7 @@ static bool leases_db_key(TALLOC_CTX *mem_ctx, NTSTATUS leases_db_add(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicepath, const char *filename, const char *stream_name) { @@ -405,6 +406,7 @@ NTSTATUS leases_db_rename(const struct GUID *client_guid, return leases_db_add(client_guid, lease_key, id, + servicename_new, filename_new, stream_name_new); } diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index 0daa0ecade..20ec522920 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -30,6 +30,7 @@ bool leases_db_init(bool read_only); NTSTATUS leases_db_add(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, + const char *servicepath, const char *filename, const char *stream_name); NTSTATUS leases_db_del(const struct GUID *client_guid, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 8f19a36cb6..c1a8ee0f32 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -1677,8 +1677,11 @@ static NTSTATUS grant_fsp_lease(struct files_struct *fsp, .epoch = fsp->lease->lease.lease_epoch, }; - status = leases_db_add(client_guid, &lease->lease_key, - &fsp->file_id, fsp->fsp_name->base_name, + status = leases_db_add(client_guid, + &lease->lease_key, + &fsp->file_id, + fsp->conn->connectpath, + fsp->fsp_name->base_name, fsp->fsp_name->stream_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("%s: leases_db_add failed: %s\n", __func__, From 5ebb1903858b4d1aadfa4e04644ec1b2b218b914 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 5 Dec 2014 12:47:52 -0800 Subject: [PATCH 14/97] s3:locking: Change the data model for leases_db to cope with dynamic path renames. interface leases_db { typedef [public] struct { GUID client_guid; smb2_lease_key lease_key; } leases_db_key; typedef [public] struct { file_id id; [string,charset(UTF8)] char *servicepath; [string,charset(UTF8)] char *base_name; [string,charset(UTF8)] char *stream_name; } leases_db_file; typedef [public] struct { uint32 num_files; [size_is(num_files)] leases_db_file files[]; } leases_db_value; } As designed by metze. Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Tue Dec 9 03:44:04 CET 2014 on sn-devel-104 --- source3/librpc/idl/leases_db.idl | 6 +- source3/locking/leases_db.c | 62 +++++++------ source3/locking/leases_db.h | 6 +- source3/smbd/open.c | 144 ++++++++++++++++++++++++------- source3/smbd/smb2_break.c | 16 ++-- 5 files changed, 161 insertions(+), 73 deletions(-) diff --git a/source3/librpc/idl/leases_db.idl b/source3/librpc/idl/leases_db.idl index bdb875defe..d0218756ad 100644 --- a/source3/librpc/idl/leases_db.idl +++ b/source3/librpc/idl/leases_db.idl @@ -22,9 +22,7 @@ interface leases_db } leases_db_file; typedef [public] struct { - uint32 num_file_ids; - [size_is(num_file_ids)] file_id ids[]; - [string,charset(UTF8)] char *filename; - [string,charset(UTF8)] char *stream_name; + uint32 num_files; + [size_is(num_files)] leases_db_file files[]; } leases_db_value; } diff --git a/source3/locking/leases_db.c b/source3/locking/leases_db.c index a9e2566652..0700ba94b8 100644 --- a/source3/locking/leases_db.c +++ b/source3/locking/leases_db.c @@ -86,7 +86,7 @@ NTSTATUS leases_db_add(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, const struct file_id *id, const char *servicepath, - const char *filename, + const char *base_name, const char *stream_name) { TDB_DATA db_key, db_value; @@ -95,6 +95,7 @@ NTSTATUS leases_db_add(const struct GUID *client_guid, NTSTATUS status; bool ok; struct leases_db_value new_value; + struct leases_db_file new_file; struct leases_db_value *value = NULL; enum ndr_err_code ndr_err; @@ -140,31 +141,40 @@ NTSTATUS leases_db_add(const struct GUID *client_guid, } /* id must be unique. */ - for (i = 0; i < value->num_file_ids; i++) { - if (file_id_equal(id, &value->ids[i])) { + for (i = 0; i < value->num_files; i++) { + if (file_id_equal(id, &value->files[i].id)) { status = NT_STATUS_OBJECT_NAME_COLLISION; goto out; } } - value->ids = talloc_realloc(value, value->ids, struct file_id, - value->num_file_ids + 1); - if (value->ids == NULL) { + value->files = talloc_realloc(value, value->files, + struct leases_db_file, + value->num_files + 1); + if (value->files == NULL) { status = NT_STATUS_NO_MEMORY; goto out; } - value->ids[value->num_file_ids] = *id; - value->num_file_ids += 1; + value->files[value->num_files].id = *id; + value->files[value->num_files].servicepath = servicepath; + value->files[value->num_files].base_name = base_name; + value->files[value->num_files].stream_name = stream_name; + value->num_files += 1; } else { DEBUG(10, ("%s: new record\n", __func__)); - new_value = (struct leases_db_value) { - .num_file_ids = 1, - .ids = discard_const_p(struct file_id, id), - .filename = filename, + new_file = (struct leases_db_file) { + .id = *id, + .servicepath = servicepath, + .base_name = base_name, .stream_name = stream_name, }; + + new_value = (struct leases_db_value) { + .num_files = 1, + .files = &new_file, + }; value = &new_value; } @@ -253,21 +263,21 @@ NTSTATUS leases_db_del(const struct GUID *client_guid, } /* id must exist. */ - for (i = 0; i < value->num_file_ids; i++) { - if (file_id_equal(id, &value->ids[i])) { + for (i = 0; i < value->num_files; i++) { + if (file_id_equal(id, &value->files[i].id)) { break; } } - if (i == value->num_file_ids) { + if (i == value->num_files) { status = NT_STATUS_NOT_FOUND; goto out; } - value->ids[i] = value->ids[value->num_file_ids-1]; - value->num_file_ids -= 1; + value->files[i] = value->files[value->num_files-1]; + value->num_files -= 1; - if (value->num_file_ids == 0) { + if (value->num_files == 0) { DEBUG(10, ("%s: deleting record\n", __func__)); status = dbwrap_record_delete(rec); } else { @@ -304,9 +314,9 @@ NTSTATUS leases_db_del(const struct GUID *client_guid, } struct leases_db_fetch_state { - void (*parser)(uint32_t num_file_ids, - struct file_id *ids, const char *filename, - const char *stream_name, void *private_data); + void (*parser)(uint32_t num_files, + const struct leases_db_file *files, + void *private_data); void *private_data; NTSTATUS status; }; @@ -341,8 +351,8 @@ static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data) NDR_PRINT_DEBUG(leases_db_value, value); } - state->parser(value->num_file_ids, - value->ids, value->filename, value->stream_name, + state->parser(value->num_files, + value->files, state->private_data); TALLOC_FREE(value); @@ -351,10 +361,8 @@ static void leases_db_parser(TDB_DATA key, TDB_DATA data, void *private_data) NTSTATUS leases_db_parse(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, - void (*parser)(uint32_t num_file_ids, - struct file_id *ids, - const char *filename, - const char *stream_name, + void (*parser)(uint32_t num_files, + const struct leases_db_file *files, void *private_data), void *private_data) { diff --git a/source3/locking/leases_db.h b/source3/locking/leases_db.h index 20ec522920..383575a2d8 100644 --- a/source3/locking/leases_db.h +++ b/source3/locking/leases_db.h @@ -38,10 +38,8 @@ NTSTATUS leases_db_del(const struct GUID *client_guid, const struct file_id *id); NTSTATUS leases_db_parse(const struct GUID *client_guid, const struct smb2_lease_key *lease_key, - void (*parser)(uint32_t num_file_ids, - struct file_id *ids, - const char *filename, - const char *stream_name, + void (*parser)(uint32_t num_files, + const struct leases_db_file *files, void *private_data), void *private_data); NTSTATUS leases_db_rename(const struct GUID *client_guid, diff --git a/source3/smbd/open.c b/source3/smbd/open.c index c1a8ee0f32..06770e04fb 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -36,6 +36,7 @@ #include "messages.h" #include "source3/lib/dbwrap/dbwrap_watch.h" #include "locking/leases_db.h" +#include "librpc/gen_ndr/ndr_leases_db.h" extern const struct generic_mapping file_generic_mapping; @@ -4127,8 +4128,10 @@ static NTSTATUS inherit_new_acl(files_struct *fsp) * used for a different file name. */ -struct lease_fname_match_state { +struct lease_match_state { /* Input parameters. */ + TALLOC_CTX *mem_ctx; + const char *servicepath; const struct smb_filename *fname; bool file_existed; struct file_id id; @@ -4138,57 +4141,139 @@ struct lease_fname_match_state { NTSTATUS match_status; }; -static void lease_fname_match_parser( - uint32_t num_file_ids, - struct file_id *ids, const char *filename, const char *stream_name, +/************************************************************* + File doesn't exist but this lease key+guid is already in use. + + This is only allowable in the dynamic share case where the + service path must be different. + + There is a small race condition here in the multi-connection + case where a client sends two create calls on different connections, + where the file doesn't exist and one smbd creates the leases_db + entry first, but this will get fixed by the multichannel cleanup + when all identical client_guids get handled by a single smbd. +**************************************************************/ + +static void lease_match_parser_new_file( + uint32_t num_files, + const struct leases_db_file *files, + struct lease_match_state *state) +{ + uint32_t i; + + for (i = 0; i < num_files; i++) { + const struct leases_db_file *f = &files[i]; + if (strequal(state->servicepath, f->servicepath)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + return; + } + } + + /* Dynamic share case. Break leases on all other files. */ + state->match_status = leases_db_copy_file_ids(state->mem_ctx, + num_files, + files, + &state->ids); + if (!NT_STATUS_IS_OK(state->match_status)) { + return; + } + + state->num_file_ids = num_files; + state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; + return; +} + +static void lease_match_parser( + uint32_t num_files, + const struct leases_db_file *files, void *private_data) { - struct lease_fname_match_state *state = - (struct lease_fname_match_state *)private_data; + struct lease_match_state *state = + (struct lease_match_state *)private_data; + uint32_t i; - if (!strequal(filename, state->fname->base_name) || - !strequal(stream_name, state->fname->stream_name)) - { - /* Names don't match lease key. */ - state->match_status = NT_STATUS_INVALID_PARAMETER; + if (!state->file_existed) { + /* + * Deal with name mismatch or + * possible dynamic share case separately + * to make code clearer. + */ + lease_match_parser_new_file(num_files, + files, + state); return; } - if (state->file_existed && - num_file_ids == 1 && - file_id_equal(&ids[0],&state->id)) - { - /* Common case - non-dynamic share. We're ok.. */ - state->match_status = NT_STATUS_OK; + /* File existed. */ + state->match_status = NT_STATUS_OK; + + for (i = 0; i < num_files; i++) { + const struct leases_db_file *f = &files[i]; + + /* Everything should be the same. */ + if (!file_id_equal(&state->id, &f->id)) { + /* This should catch all dynamic share cases. */ + state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; + break; + } + if (!strequal(f->servicepath, state->servicepath)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + break; + } + if (!strequal(f->base_name, state->fname->base_name)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + break; + } + if (!strequal(f->stream_name, state->fname->stream_name)) { + state->match_status = NT_STATUS_INVALID_PARAMETER; + break; + } + } + + if (NT_STATUS_IS_OK(state->match_status)) { + /* + * Common case - just opening another handle on a + * file on a non-dynamic share. + */ + return; + } + + if (NT_STATUS_EQUAL(state->match_status, NT_STATUS_INVALID_PARAMETER)) { + /* Mismatched path. Error back to client. */ return; } /* - * More than one file id, or not equal, or new file - * being created and there's already an existing lease - * on this (client_guid, lease id) pair. + * File id mismatch. Dynamic share case NT_STATUS_OPLOCK_NOT_GRANTED. * Don't allow leases. */ - state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; - state->num_file_ids = num_file_ids; - state->ids = talloc_memdup(talloc_tos(), - ids, - num_file_ids * sizeof(struct file_id)); - if (state->ids == NULL) { - state->match_status = NT_STATUS_NO_MEMORY; + state->match_status = leases_db_copy_file_ids(state->mem_ctx, + num_files, + files, + &state->ids); + if (!NT_STATUS_IS_OK(state->match_status)) { + return; } + + state->num_file_ids = num_files; + state->match_status = NT_STATUS_OPLOCK_NOT_GRANTED; + return; } static NTSTATUS lease_match(connection_struct *conn, struct smb_request *req, struct smb2_lease_key *lease_key, + const char *servicepath, const struct smb_filename *fname, uint16_t *p_version, uint16_t *p_epoch) { struct smbd_server_connection *sconn = req->sconn; - struct lease_fname_match_state state = { + TALLOC_CTX *tos = talloc_tos(); + struct lease_match_state state = { + .mem_ctx = tos, + .servicepath = servicepath, .fname = fname, .match_status = NT_STATUS_OK }; @@ -4203,7 +4288,7 @@ static NTSTATUS lease_match(connection_struct *conn, } status = leases_db_parse(&sconn->client->connections->smb2.client.guid, - lease_key, lease_fname_match_parser, &state); + lease_key, lease_match_parser, &state); if (!NT_STATUS_IS_OK(status)) { /* * Not found or error means okay: We can make the lease pass @@ -4356,6 +4441,7 @@ static NTSTATUS create_file_unixpath(connection_struct *conn, status = lease_match(conn, req, &lease->lease_key, + conn->connectpath, smb_fname, &version, &epoch); diff --git a/source3/smbd/smb2_break.c b/source3/smbd/smb2_break.c index 126bf7898f..5eab0a19a2 100644 --- a/source3/smbd/smb2_break.c +++ b/source3/smbd/smb2_break.c @@ -340,21 +340,19 @@ struct lease_lookup_state { }; static void lease_parser( - uint32_t num_file_ids, - struct file_id *ids, const char *filename, const char *stream_name, + uint32_t num_files, + const struct leases_db_file *files, void *private_data) { struct lease_lookup_state *lls = (struct lease_lookup_state *)private_data; lls->status = NT_STATUS_OK; - lls->num_file_ids = num_file_ids; - lls->ids = talloc_memdup(lls->mem_ctx, - ids, - num_file_ids * sizeof(struct file_id)); - if (lls->ids == NULL) { - lls->status = NT_STATUS_NO_MEMORY; - } + lls->num_file_ids = num_files; + lls->status = leases_db_copy_file_ids(lls->mem_ctx, + num_files, + files, + &lls->ids); } static struct tevent_req *smbd_smb2_lease_break_send( From 4debc30117a33fca1cc2667869cc6718afd62bbe Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 7 Dec 2014 14:09:29 +0100 Subject: [PATCH 15/97] lib: Fix a typo Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- lib/util/charset/iconv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/charset/iconv.c b/lib/util/charset/iconv.c index c5b45fe975..f4815f1717 100644 --- a/lib/util/charset/iconv.c +++ b/lib/util/charset/iconv.c @@ -219,7 +219,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, } #ifdef HAVE_NATIVE_ICONV - /* the from and to varaibles indicate a samba module or + /* the from and to variables indicate a samba module or * internal conversion, ret->pull and ret->push are * initialised only in this block for iconv based * conversions */ From e2ee1a42cf5b57e09604792f46dd173182a014e5 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 7 Dec 2014 14:13:51 +0100 Subject: [PATCH 16/97] lib: Fix blank line endings Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- lib/util/charset/iconv.c | 68 ++++++++++++++++++++-------------------- lib/util/idtree.c | 16 +++++----- lib/util/util_file.c | 26 +++++++-------- 3 files changed, 55 insertions(+), 55 deletions(-) diff --git a/lib/util/charset/iconv.c b/lib/util/charset/iconv.c index f4815f1717..bf561f2661 100644 --- a/lib/util/charset/iconv.c +++ b/lib/util/charset/iconv.c @@ -1,19 +1,19 @@ -/* +/* Unix SMB/CIFS implementation. minimal iconv implementation Copyright (C) Andrew Tridgell 2001 Copyright (C) Jelmer Vernooij 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 . */ @@ -80,7 +80,7 @@ static const struct charset_functions builtin_functions[] = { {"ASCII", ascii_pull, ascii_push}, {"646", ascii_pull, ascii_push}, {"ISO-8859-1", latin1_pull, latin1_push}, -#ifdef DEVELOPER +#ifdef DEVELOPER {"WEIRD", weird_pull, weird_push, true}, #endif #ifdef DARWINOS @@ -94,12 +94,12 @@ static const struct charset_functions builtin_functions[] = { /* if there was an error then reset the internal state, this ensures that we don't have a shift state remaining for character sets like SJIS */ -static size_t sys_iconv(void *cd, +static size_t sys_iconv(void *cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { - size_t ret = iconv((iconv_t)cd, - discard_const_p(char *, inbuf), inbytesleft, + size_t ret = iconv((iconv_t)cd, + discard_const_p(char *, inbuf), inbytesleft, outbuf, outbytesleft); if (ret == (size_t)-1) iconv(cd, NULL, NULL, NULL, NULL); return ret; @@ -112,13 +112,13 @@ static size_t sys_iconv(void *cd, * It only knows about a very small number of character sets - just * enough that Samba works on systems that don't have iconv. **/ -_PUBLIC_ size_t smb_iconv(smb_iconv_t cd, +_PUBLIC_ size_t smb_iconv(smb_iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) { /* in many cases we can go direct */ if (cd->direct) { - return cd->direct(cd->cd_direct, + return cd->direct(cd->cd_direct, inbuf, inbytesleft, outbuf, outbytesleft); } @@ -189,7 +189,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, int i; ret = (smb_iconv_t)talloc_named(mem_ctx, - sizeof(*ret), + sizeof(*ret), "iconv(%s,%s)", tocode, fromcode); if (!ret) { errno = ENOMEM; @@ -211,7 +211,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, from = &builtin_functions[i]; } } - if (strcasecmp(tocode, builtin_functions[i].name) == 0) { + if (strcasecmp(tocode, builtin_functions[i].name) == 0) { if (use_builtin_handlers || builtin_functions[i].samba_internal_charset) { to = &builtin_functions[i]; } @@ -232,7 +232,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, ret->pull = sys_iconv; } } - + if (to == NULL) { ret->cd_push = iconv_open(tocode, "UTF-16LE"); if (ret->cd_push == (iconv_t)-1) @@ -246,7 +246,7 @@ _PUBLIC_ smb_iconv_t smb_iconv_open_ex(TALLOC_CTX *mem_ctx, const char *tocode, if (ret->pull == NULL && from == NULL) { goto failed; } - + if (ret->push == NULL && to == NULL) { goto failed; } @@ -340,7 +340,7 @@ static size_t ascii_pull(void *cd, const char **inbuf, size_t *inbytesleft, errno = E2BIG; return -1; } - + return 0; } @@ -379,7 +379,7 @@ static size_t ascii_push(void *cd, const char **inbuf, size_t *inbytesleft, errno = E2BIG; return -1; } - + return ir_count; } @@ -469,7 +469,7 @@ static size_t ucs2hex_pull(void *cd, const char **inbuf, size_t *inbytesleft, errno = EINVAL; return -1; } - + if (sscanf(&(*inbuf)[1], "%04x", &v) != 1) { errno = EILSEQ; return -1; @@ -487,7 +487,7 @@ static size_t ucs2hex_pull(void *cd, const char **inbuf, size_t *inbytesleft, errno = E2BIG; return -1; } - + return 0; } @@ -497,7 +497,7 @@ static size_t ucs2hex_push(void *cd, const char **inbuf, size_t *inbytesleft, while (*inbytesleft >= 2 && *outbytesleft >= 1) { char buf[6]; - if ((*inbuf)[1] == 0 && + if ((*inbuf)[1] == 0 && ((*inbuf)[0] & 0x80) == 0 && (*inbuf)[0] != '@') { (*outbuf)[0] = (*inbuf)[0]; @@ -528,7 +528,7 @@ static size_t ucs2hex_push(void *cd, const char **inbuf, size_t *inbytesleft, errno = E2BIG; return -1; } - + return 0; } @@ -618,7 +618,7 @@ static size_t utf8_pull(void *cd, const char **inbuf, size_t *inbytesleft, if ((c[0] & 0xf0) == 0xe0) { if (in_left < 3 || - (c[1] & 0xc0) != 0x80 || + (c[1] & 0xc0) != 0x80 || (c[2] & 0xc0) != 0x80) { errno = EILSEQ; goto error; @@ -635,15 +635,15 @@ static size_t utf8_pull(void *cd, const char **inbuf, size_t *inbytesleft, if ((c[0] & 0xf8) == 0xf0) { unsigned int codepoint; if (in_left < 4 || - (c[1] & 0xc0) != 0x80 || + (c[1] & 0xc0) != 0x80 || (c[2] & 0xc0) != 0x80 || (c[3] & 0xc0) != 0x80) { errno = EILSEQ; goto error; } - codepoint = - (c[3]&0x3f) | - ((c[2]&0x3f)<<6) | + codepoint = + (c[3]&0x3f) | + ((c[2]&0x3f)<<6) | ((c[1]&0x3f)<<12) | ((c[0]&0x7)<<18); if (codepoint < 0x10000) { @@ -758,7 +758,7 @@ static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft, c[0] = 0xe0 | (codepoint >> 12); c[1] = 0x80 | ((codepoint >> 6) & 0x3f); c[2] = 0x80 | (codepoint & 0x3f); - + in_left -= 2; out_left -= 3; uc += 2; @@ -775,9 +775,9 @@ static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft, errno = EILSEQ; goto error; } - codepoint = 0x10000 + (uc[2] | ((uc[3] & 0x3)<<8) | + codepoint = 0x10000 + (uc[2] | ((uc[3] & 0x3)<<8) | (uc[0]<<10) | ((uc[1] & 0x3)<<18)); - + if (out_left < 4) { errno = E2BIG; goto error; @@ -786,7 +786,7 @@ static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft, c[1] = 0x80 | ((codepoint >> 12) & 0x3f); c[2] = 0x80 | ((codepoint >> 6) & 0x3f); c[3] = 0x80 | (codepoint & 0x3f); - + in_left -= 4; out_left -= 4; uc += 4; @@ -807,7 +807,7 @@ static size_t utf8_push(void *cd, const char **inbuf, size_t *inbytesleft, *outbytesleft = out_left; *inbuf = (const char *)uc; *outbuf = (char *)c; - + return 0; error: @@ -853,7 +853,7 @@ static size_t utf16_munged_pull(void *cd, const char **inbuf, size_t *inbyteslef unsigned int codepoint2; if (in_left < 4) { codepoint = 0xfffd; - goto codepoint16; + goto codepoint16; } codepoint2 = uc[2] | (uc[3]<<8); if ((codepoint2 & 0xfc00) != 0xdc00) { @@ -883,12 +883,12 @@ static size_t utf16_munged_pull(void *cd, const char **inbuf, size_t *inbyteslef codepoint16: c[0] = codepoint & 0xFF; c[1] = (codepoint>>8) & 0xFF; - + in_left -= 2; out_left -= 2; uc += 2; c += 2; - continue; + continue; } if (in_left == 1) { @@ -905,7 +905,7 @@ static size_t utf16_munged_pull(void *cd, const char **inbuf, size_t *inbyteslef *outbytesleft = out_left; *inbuf = (const char *)uc; *outbuf = (char *)c; - + return 0; error: diff --git a/lib/util/idtree.c b/lib/util/idtree.c index 6f1a4ac9dd..0056c0961a 100644 --- a/lib/util/idtree.c +++ b/lib/util/idtree.c @@ -1,4 +1,4 @@ -/* +/* Unix SMB/CIFS implementation. very efficient functions to manage mapping a id (such as a fnum) to @@ -6,20 +6,20 @@ Copyright (C) Andrew Tridgell 2004 - This code is derived from lib/idr.c in the 2.6 Linux kernel, which was + This code is derived from lib/idr.c in the 2.6 Linux kernel, which was written by Jim Houston jim.houston@ccur.com, and is Copyright (C) 2002 by Concurrent Computer Corporation - + 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 2 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 . */ @@ -53,7 +53,7 @@ #define set_bit(bit, v) (v) |= (1<<(bit)) #define clear_bit(bit, v) (v) &= ~(1<<(bit)) #define test_bit(bit, v) ((v) & (1<<(bit))) - + struct idr_layer { uint32_t bitmap; struct idr_layer *ary[IDR_SIZE]; @@ -195,7 +195,7 @@ static int idr_get_new_above_int(struct idr_context *idp, void *ptr, int startin int layers, v, id; idr_pre_get(idp); - + id = starting_id; build_up: p = idp->top; @@ -309,7 +309,7 @@ static int _idr_remove(struct idr_context *idp, int id) return -1; } - if ( idp->top && idp->top->count == 1 && + if ( idp->top && idp->top->count == 1 && (idp->layers > 1) && idp->top->ary[0]) { /* We can drop a layer */ diff --git a/lib/util/util_file.c b/lib/util/util_file.c index 886bcae477..83c746b654 100644 --- a/lib/util/util_file.c +++ b/lib/util/util_file.c @@ -4,17 +4,17 @@ * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995. * * Added afdgets() Jelmer Vernooij 2005 - * + * * 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 . */ @@ -32,7 +32,7 @@ */ /** -read a line from a file with possible \ continuation chars. +read a line from a file with possible \ continuation chars. Blanks at the start or end of a line are stripped. The string will be allocated if s2 is NULL **/ @@ -78,7 +78,7 @@ _PUBLIC_ char *fgets_slash(char *s2,int maxlen,XFILE *f) } return(s); case EOF: - if (len <= 0 && !s2) + if (len <= 0 && !s2) SAFE_FREE(s); return(len>0?s:NULL); case ' ': @@ -93,7 +93,7 @@ _PUBLIC_ char *fgets_slash(char *s2,int maxlen,XFILE *f) if (!s2 && len > maxlen-3) { char *t; - + maxlen *= 2; t = realloc_p(s, char, maxlen); if (!t) { @@ -107,7 +107,7 @@ _PUBLIC_ char *fgets_slash(char *s2,int maxlen,XFILE *f) } /** - * Read one line (data until next newline or eof) and allocate it + * Read one line (data until next newline or eof) and allocate it */ _PUBLIC_ char *afdgets(int fd, TALLOC_CTX *mem_ctx, size_t hint) { @@ -200,7 +200,7 @@ _PUBLIC_ char *file_load(const char *fname, size_t *size, size_t maxsize, TALLOC char *p; if (!fname || !*fname) return NULL; - + fd = open(fname,O_RDONLY); if (fd == -1) return NULL; @@ -256,7 +256,7 @@ bool unmap_file(void *start, size_t size) #ifdef HAVE_MMAP if (munmap( start, size ) != 0) { DEBUG( 1, ("map_file: Failed to unmap address %p " - "of size %u - %s\n", + "of size %u - %s\n", start, (unsigned int)size, strerror(errno) )); return false; } @@ -286,10 +286,10 @@ char **file_lines_parse(char *p, size_t size, int *numlines, TALLOC_CTX *mem_ctx if (!ret) { talloc_free(p); return NULL; - } - + } + talloc_steal(ret, p); - + memset(ret, 0, sizeof(ret[0])*(i+2)); ret[0] = p; @@ -315,7 +315,7 @@ char **file_lines_parse(char *p, size_t size, int *numlines, TALLOC_CTX *mem_ctx /** load a file into memory and return an array of pointers to lines in the file -must be freed with talloc_free(). +must be freed with talloc_free(). **/ _PUBLIC_ char **file_lines_load(const char *fname, int *numlines, size_t maxsize, TALLOC_CTX *mem_ctx) { From 3a6a6f19410606a9028861ca95ac80d2651e2830 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 13 Nov 2014 10:38:40 +0000 Subject: [PATCH 17/97] messaging3: Avoid self-send complexity With the notify code I've hit another case where self-sends caused a problem. This time messages were lost because we tried to do multiple dispatch_rec calls from within a single inotify callback. Only the first one was being taken care of, the others did not find receivers. This patch makes self-sends go through the kernel as well, the kernel queues everything nicely for us. With dgram messaging this should be pretty fast. If it turns out to be a performance problem, we can solve it later by doing proper queueing in user space. We need to completely decouple any processing from callbacks. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/lib/messages.c | 113 +------------------- source3/torture/test_messaging_fd_passing.c | 2 +- source3/torture/test_messaging_read.c | 2 +- 3 files changed, 5 insertions(+), 112 deletions(-) diff --git a/source3/lib/messages.c b/source3/lib/messages.c index 5b4daa2932..1fd7601f82 100644 --- a/source3/lib/messages.c +++ b/source3/lib/messages.c @@ -507,36 +507,6 @@ NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx, return NT_STATUS_OK; } - if (server_id_same_process(&msg_ctx->id, &server)) { - struct messaging_rec rec; - uint8_t *buf; - - /* - * Self-send, directly dispatch - */ - - if (num_fds > 0) { - return NT_STATUS_NOT_SUPPORTED; - } - - buf = iov_buf(talloc_tos(), iov, iovlen); - if (buf == NULL) { - return NT_STATUS_NO_MEMORY; - } - - rec = (struct messaging_rec) { - .msg_version = MESSAGE_VERSION, - .msg_type = msg_type & MSG_TYPE_MASK, - .dest = server, - .src = msg_ctx->id, - .buf = data_blob_const(buf, talloc_get_size(buf)), - }; - - messaging_dispatch_rec(msg_ctx, &rec); - TALLOC_FREE(buf); - return NT_STATUS_OK; - } - ZERO_STRUCT(hdr); hdr = (struct messaging_hdr) { .msg_type = msg_type, @@ -826,68 +796,6 @@ static bool messaging_append_new_waiters(struct messaging_context *msg_ctx) return true; } -struct messaging_defer_callback_state { - struct messaging_context *msg_ctx; - struct messaging_rec *rec; - void (*fn)(struct messaging_context *msg, void *private_data, - uint32_t msg_type, struct server_id server_id, - DATA_BLOB *data); - void *private_data; -}; - -static void messaging_defer_callback_trigger(struct tevent_context *ev, - struct tevent_immediate *im, - void *private_data); - -static void messaging_defer_callback( - struct messaging_context *msg_ctx, struct messaging_rec *rec, - void (*fn)(struct messaging_context *msg, void *private_data, - uint32_t msg_type, struct server_id server_id, - DATA_BLOB *data), - void *private_data) -{ - struct messaging_defer_callback_state *state; - struct tevent_immediate *im; - - state = talloc(msg_ctx, struct messaging_defer_callback_state); - if (state == NULL) { - DEBUG(1, ("talloc failed\n")); - return; - } - state->msg_ctx = msg_ctx; - state->fn = fn; - state->private_data = private_data; - - state->rec = messaging_rec_dup(state, rec); - if (state->rec == NULL) { - DEBUG(1, ("talloc failed\n")); - TALLOC_FREE(state); - return; - } - - im = tevent_create_immediate(state); - if (im == NULL) { - DEBUG(1, ("tevent_create_immediate failed\n")); - TALLOC_FREE(state); - return; - } - tevent_schedule_immediate(im, msg_ctx->event_ctx, - messaging_defer_callback_trigger, state); -} - -static void messaging_defer_callback_trigger(struct tevent_context *ev, - struct tevent_immediate *im, - void *private_data) -{ - struct messaging_defer_callback_state *state = talloc_get_type_abort( - private_data, struct messaging_defer_callback_state); - struct messaging_rec *rec = state->rec; - - state->fn(state->msg_ctx, state->private_data, rec->msg_type, rec->src, - &rec->buf); - TALLOC_FREE(state); -} - /* Dispatch one messaging_rec */ @@ -914,24 +822,9 @@ void messaging_dispatch_rec(struct messaging_context *msg_ctx, rec->num_fds = 0; rec->fds = NULL; - if (server_id_same_process(&rec->src, &rec->dest)) { - /* - * This is a self-send. We are called here from - * messaging_send(), and we don't want to directly - * recurse into the callback but go via a - * tevent_loop_once - */ - messaging_defer_callback(msg_ctx, rec, cb->fn, - cb->private_data); - } else { - /* - * This comes from a different process. we are called - * from the event loop, so we should call back - * directly. - */ - cb->fn(msg_ctx, cb->private_data, rec->msg_type, - rec->src, &rec->buf); - } + cb->fn(msg_ctx, cb->private_data, rec->msg_type, + rec->src, &rec->buf); + /* * we continue looking for matching messages after finding * one. This matters for subsystems like the internal notify diff --git a/source3/torture/test_messaging_fd_passing.c b/source3/torture/test_messaging_fd_passing.c index abd142fdd3..7bee41b598 100644 --- a/source3/torture/test_messaging_fd_passing.c +++ b/source3/torture/test_messaging_fd_passing.c @@ -63,7 +63,7 @@ bool run_messaging_fdpass1(int dummy) status = messaging_send_iov(msg_ctx, dst, MSG_PING, NULL, 0, pass_fds, 1); - if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + if (!NT_STATUS_EQUAL(status, NT_STATUS_OK)) { fprintf(stderr, "messaging_send_iov gave: %s\n", nt_errstr(status)); goto fail; diff --git a/source3/torture/test_messaging_read.c b/source3/torture/test_messaging_read.c index 9c4017c36f..802b4fe734 100644 --- a/source3/torture/test_messaging_read.c +++ b/source3/torture/test_messaging_read.c @@ -122,7 +122,7 @@ bool run_messaging_read1(int dummy) goto fail; } - for (i=0; i<2; i++) { + for (i=0; i<3; i++) { if (tevent_loop_once(ev) != 0) { fprintf(stderr, "tevent_loop_once failed\n"); goto fail; From d775c386e498d4c2062f8fc65f515f991d127dc1 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sat, 6 Dec 2014 11:22:35 +0100 Subject: [PATCH 18/97] lib: Simplify iov_buf[len] This makes iov_buf independent of talloc Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/lib/iov_buf.c | 46 ++++++++++++++---------------------- source3/lib/iov_buf.h | 4 +++- source3/lib/messages_ctdbd.c | 11 +++++++-- source3/wscript_build | 2 +- 4 files changed, 31 insertions(+), 32 deletions(-) diff --git a/source3/lib/iov_buf.c b/source3/lib/iov_buf.c index dd99da3625..e05dfc9524 100644 --- a/source3/lib/iov_buf.c +++ b/source3/lib/iov_buf.c @@ -23,43 +23,33 @@ ssize_t iov_buflen(const struct iovec *iov, int iovcnt) { - size_t buflen = 0; + return iov_buf(iov, iovcnt, NULL, 0); +} + +ssize_t iov_buf(const struct iovec *iov, int iovcnt, + uint8_t *buf, size_t buflen) +{ + size_t needed = 0; + uint8_t *p = buf; int i; for (i=0; i #include +#include ssize_t iov_buflen(const struct iovec *iov, int iovlen); -uint8_t *iov_buf(TALLOC_CTX *mem_ctx, const struct iovec *iov, int iovcnt); +ssize_t iov_buf(const struct iovec *iov, int iovcnt, + uint8_t *buf, size_t buflen); #endif diff --git a/source3/lib/messages_ctdbd.c b/source3/lib/messages_ctdbd.c index 59f5976da0..53aeb1fd05 100644 --- a/source3/lib/messages_ctdbd.c +++ b/source3/lib/messages_ctdbd.c @@ -100,16 +100,23 @@ static int messaging_ctdb_send(struct server_id src, backend->private_data, struct messaging_ctdbd_context); struct messaging_rec msg; uint8_t *buf; + ssize_t buflen; NTSTATUS status; if (num_fds > 0) { return ENOSYS; } - buf = iov_buf(talloc_tos(), iov, iovlen); - if (buf == NULL) { + buflen = iov_buflen(iov, iovlen); + if (buflen == -1) { + return EMSGSIZE; + } + + buf = talloc_array(talloc_tos(), uint8_t, buflen); + if (buflen == NULL) { return ENOMEM; } + iov_buf(iov, iovlen, buf, buflen); msg = (struct messaging_rec) { .msg_version = MESSAGE_VERSION, diff --git a/source3/wscript_build b/source3/wscript_build index 18f6b6db7c..51d72e78b6 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -260,7 +260,7 @@ bld.SAMBA3_LIBRARY('sys_rw', bld.SAMBA3_LIBRARY('iov_buf', source='lib/iov_buf.c', - deps='replace talloc', + deps='replace', private_library=True) bld.SAMBA3_SUBSYSTEM('samba3util', From dc38f646af4582a3adffda20d7bb0e449df5e22e Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sat, 6 Dec 2014 11:28:16 +0100 Subject: [PATCH 19/97] lib: Use iov_buf in unix_msg Now that iov_buf does not pull in talloc we can use it Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/lib/unix_msg/unix_msg.c | 25 ++----------------------- source3/lib/unix_msg/wscript_build | 2 +- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/source3/lib/unix_msg/unix_msg.c b/source3/lib/unix_msg/unix_msg.c index 7ca506a85c..78b29c2d07 100644 --- a/source3/lib/unix_msg/unix_msg.c +++ b/source3/lib/unix_msg/unix_msg.c @@ -23,6 +23,7 @@ #include "system/network.h" #include "dlinklist.h" #include "pthreadpool/pthreadpool.h" +#include "lib/iov_buf.h" #include /* @@ -77,7 +78,6 @@ struct unix_dgram_ctx { char path[]; }; -static ssize_t iov_buflen(const struct iovec *iov, int iovlen); static void unix_dgram_recv_handler(struct poll_watch *w, int fd, short events, void *private_data); @@ -583,10 +583,7 @@ static int queue_msg(struct unix_dgram_send_queue *q, } #endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ - for (i=0; imsgs, msg, struct unix_dgram_msg); return 0; @@ -1106,21 +1103,3 @@ int unix_msg_free(struct unix_msg_ctx *ctx) free(ctx); return 0; } - -static ssize_t iov_buflen(const struct iovec *iov, int iovlen) -{ - size_t buflen = 0; - int i; - - for (i=0; i Date: Mon, 27 Oct 2014 10:26:46 +0000 Subject: [PATCH 20/97] smbd: Move lp_parm_bool out of notify_inotify.c Notifyd should be as independent of Samba infrastructure as possible, and it will call notify_inotify Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/modules/vfs_default.c | 3 +++ source3/smbd/notify_inotify.c | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index f5b8f9be11..dad6bb7440 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -2119,6 +2119,9 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle, */ #ifdef HAVE_INOTIFY if (lp_kernel_change_notify(vfs_handle->conn->params)) { + if (!lp_parm_bool(-1, "notify", "inotify", True)) { + return NT_STATUS_INVALID_SYSTEM_SERVICE; + } return inotify_watch(ctx, path, filter, subdir_filter, callback, private_data, handle); } diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 5fbc7f22ee..554277cabe 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -255,10 +255,6 @@ static NTSTATUS inotify_setup(struct sys_notify_context *ctx) { struct inotify_private *in; - if (!lp_parm_bool(-1, "notify", "inotify", True)) { - return NT_STATUS_INVALID_SYSTEM_SERVICE; - } - in = talloc(ctx, struct inotify_private); NT_STATUS_HAVE_NO_MEMORY(in); in->fd = inotify_init(); From 18b682250457e60ae75d4352c7f0b88686eb1dd8 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Sun, 26 Oct 2014 09:13:41 +0000 Subject: [PATCH 21/97] notify_inotify: Simplify filter_match Early returns make code simpler Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/smbd/notify_inotify.c | 39 +++++++++++++++++------------------ 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 554277cabe..2425bb4a5b 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -75,6 +75,8 @@ static int inotify_destructor(struct inotify_private *in) static bool filter_match(struct inotify_watch_context *w, struct inotify_event *e) { + bool ok; + DEBUG(10, ("filter_match: e->mask=%x, w->mask=%x, w->filter=%x\n", e->mask, w->mask, w->filter)); @@ -86,28 +88,25 @@ static bool filter_match(struct inotify_watch_context *w, /* SMB separates the filters for files and directories */ if (e->mask & IN_ISDIR) { - if ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) == 0) { - return False; - } - } else { - if ((e->mask & IN_ATTRIB) && - (w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES| - FILE_NOTIFY_CHANGE_LAST_WRITE| - FILE_NOTIFY_CHANGE_LAST_ACCESS| - FILE_NOTIFY_CHANGE_EA| - FILE_NOTIFY_CHANGE_SECURITY))) { - return True; - } - if ((e->mask & IN_MODIFY) && - (w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) { - return True; - } - if ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) == 0) { - return False; - } + ok = ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) != 0); + return ok; + } + + if ((e->mask & IN_ATTRIB) && + (w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES| + FILE_NOTIFY_CHANGE_LAST_WRITE| + FILE_NOTIFY_CHANGE_LAST_ACCESS| + FILE_NOTIFY_CHANGE_EA| + FILE_NOTIFY_CHANGE_SECURITY))) { + return True; + } + if ((e->mask & IN_MODIFY) && + (w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) { + return True; } - return True; + ok = ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) != 0); + return ok; } From 608774d8c4a14863d1e603d0b2f0dac94e8f69a7 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 27 Oct 2014 13:07:03 +0000 Subject: [PATCH 22/97] notify_inotify: Add a NULL check Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/smbd/notify_inotify.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 2425bb4a5b..56f4941066 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -253,6 +253,7 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde, static NTSTATUS inotify_setup(struct sys_notify_context *ctx) { struct inotify_private *in; + struct tevent_fd *fde; in = talloc(ctx, struct inotify_private); NT_STATUS_HAVE_NO_MEMORY(in); @@ -269,7 +270,13 @@ static NTSTATUS inotify_setup(struct sys_notify_context *ctx) talloc_set_destructor(in, inotify_destructor); /* add a event waiting for the inotify fd to be readable */ - tevent_add_fd(ctx->ev, in, in->fd, TEVENT_FD_READ, inotify_handler, in); + fde = tevent_add_fd(ctx->ev, in, in->fd, TEVENT_FD_READ, + inotify_handler, in); + if (fde == NULL) { + ctx->private_data = NULL; + TALLOC_FREE(in); + return NT_STATUS_NO_MEMORY; + } return NT_STATUS_OK; } From eb10a36a96f5b4da4ab4677761b8dab7ceeec7b0 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 27 Oct 2014 13:09:44 +0000 Subject: [PATCH 23/97] notify_inotify: Make inotify_setup return 0/errno This gets rid of one NT_STATUS_HAVE_NO_MEMORY with its implicit return; :-) Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/smbd/notify_inotify.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 56f4941066..613b0387d7 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -250,18 +250,22 @@ static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde, setup the inotify handle - called the first time a watch is added on this context */ -static NTSTATUS inotify_setup(struct sys_notify_context *ctx) +static int inotify_setup(struct sys_notify_context *ctx) { struct inotify_private *in; struct tevent_fd *fde; in = talloc(ctx, struct inotify_private); - NT_STATUS_HAVE_NO_MEMORY(in); + if (in == NULL) { + return ENOMEM; + } + in->fd = inotify_init(); if (in->fd == -1) { - DEBUG(0,("Failed to init inotify - %s\n", strerror(errno))); + int ret = errno; + DEBUG(0, ("Failed to init inotify - %s\n", strerror(ret))); talloc_free(in); - return map_nt_error_from_unix(errno); + return ret; } in->ctx = ctx; in->watches = NULL; @@ -275,10 +279,9 @@ static NTSTATUS inotify_setup(struct sys_notify_context *ctx) if (fde == NULL) { ctx->private_data = NULL; TALLOC_FREE(in); - return NT_STATUS_NO_MEMORY; + return ENOMEM; } - - return NT_STATUS_OK; + return 0; } @@ -360,9 +363,11 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, /* maybe setup the inotify fd */ if (ctx->private_data == NULL) { - NTSTATUS status; - status = inotify_setup(ctx); - NT_STATUS_NOT_OK_RETURN(status); + int ret; + ret = inotify_setup(ctx); + if (ret != 0) { + return map_nt_error_from_unix(ret); + } } in = talloc_get_type(ctx->private_data, struct inotify_private); From 01a167818d0d927dc9190e4c4b196a701110207e Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 27 Oct 2014 13:15:12 +0000 Subject: [PATCH 24/97] notify_inotify: Slightly simplify inotify_watch tallocing first avoids having to call inotify_rm_watch This even fixes a real error: We share inotifies between different instances, so the rm_watch in the error paths destroys other legitimate watches Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/smbd/notify_inotify.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 613b0387d7..b141b92116 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -355,7 +355,6 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, void *handle_p) { struct inotify_private *in; - int wd; uint32_t mask; struct inotify_watch_context *w; uint32_t orig_filter = *filter; @@ -382,38 +381,37 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, watch descriptor for multiple watches on the same path */ mask |= (IN_MASK_ADD | IN_ONLYDIR); - /* get a new watch descriptor for this path */ - wd = inotify_add_watch(in->fd, path, mask); - if (wd == -1) { - *filter = orig_filter; - DEBUG(1, ("inotify_add_watch returned %s\n", strerror(errno))); - return map_nt_error_from_unix(errno); - } - - DEBUG(10, ("inotify_add_watch for %s mask %x returned wd %d\n", - path, mask, wd)); - w = talloc(in, struct inotify_watch_context); if (w == NULL) { - inotify_rm_watch(in->fd, wd); *filter = orig_filter; return NT_STATUS_NO_MEMORY; } w->in = in; - w->wd = wd; w->callback = callback; w->private_data = private_data; w->mask = mask; w->filter = orig_filter; w->path = talloc_strdup(w, path); if (w->path == NULL) { - inotify_rm_watch(in->fd, wd); *filter = orig_filter; TALLOC_FREE(w); return NT_STATUS_NO_MEMORY; } + /* get a new watch descriptor for this path */ + w->wd = inotify_add_watch(in->fd, path, mask); + if (w->wd == -1) { + int err = errno; + *filter = orig_filter; + TALLOC_FREE(w); + DEBUG(1, ("inotify_add_watch returned %s\n", strerror(err))); + return map_nt_error_from_unix(err); + } + + DEBUG(10, ("inotify_add_watch for %s mask %x returned wd %d\n", + path, mask, w->wd)); + (*handle) = w; DLIST_ADD(in->watches, w); From 9b474456b8133bd82c14cd097a6df2380b55b423 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 27 Oct 2014 13:20:15 +0000 Subject: [PATCH 25/97] notify_inotify: Slightly simplify watch_destructor Another case of an early return Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/smbd/notify_inotify.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index b141b92116..3e4e2e6a56 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -324,17 +324,19 @@ static int watch_destructor(struct inotify_watch_context *w) int wd = w->wd; DLIST_REMOVE(w->in->watches, w); - /* only rm the watch if its the last one with this wd */ for (w=in->watches;w;w=w->next) { - if (w->wd == wd) break; - } - if (w == NULL) { - DEBUG(10, ("Deleting inotify watch %d\n", wd)); - if (inotify_rm_watch(in->fd, wd) == -1) { - DEBUG(1, ("inotify_rm_watch returned %s\n", - strerror(errno))); + if (w->wd == wd) { + /* + * Another inotify_watch_context listens on this path, + * leave the kernel level watch in place + */ + return 0; } + } + DEBUG(10, ("Deleting inotify watch %d\n", wd)); + if (inotify_rm_watch(in->fd, wd) == -1) { + DEBUG(1, ("inotify_rm_watch returned %s\n", strerror(errno))); } return 0; } From f530d6d97bfb7c64ede37ad8d2ee33118ddcf3d1 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Mon, 27 Oct 2014 13:26:35 +0000 Subject: [PATCH 26/97] notify_inotify: Make inotify_watch return 0/errno More like a cleanup, but I want to use inotify_watch in notifyd that I would like to keep as light as possible Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/modules/vfs_default.c | 9 +++++++-- source3/smbd/notify_inotify.c | 30 +++++++++++++++--------------- source3/smbd/proto.h | 18 +++++++++--------- 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index dad6bb7440..eaa1c2be4f 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -2119,11 +2119,16 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle, */ #ifdef HAVE_INOTIFY if (lp_kernel_change_notify(vfs_handle->conn->params)) { + int ret; if (!lp_parm_bool(-1, "notify", "inotify", True)) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } - return inotify_watch(ctx, path, filter, subdir_filter, - callback, private_data, handle); + ret = inotify_watch(ctx, path, filter, subdir_filter, + callback, private_data, handle); + if (ret != 0) { + return map_nt_error_from_unix(ret); + } + return NT_STATUS_OK; } #endif /* diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 3e4e2e6a56..ad670affcd 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -346,15 +346,15 @@ static int watch_destructor(struct inotify_watch_context *w) add a watch. The watch is removed when the caller calls talloc_free() on *handle */ -NTSTATUS inotify_watch(struct sys_notify_context *ctx, - const char *path, - uint32_t *filter, - uint32_t *subdir_filter, - void (*callback)(struct sys_notify_context *ctx, - void *private_data, - struct notify_event *ev), - void *private_data, - void *handle_p) +int inotify_watch(struct sys_notify_context *ctx, + const char *path, + uint32_t *filter, + uint32_t *subdir_filter, + void (*callback)(struct sys_notify_context *ctx, + void *private_data, + struct notify_event *ev), + void *private_data, + void *handle_p) { struct inotify_private *in; uint32_t mask; @@ -367,7 +367,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, int ret; ret = inotify_setup(ctx); if (ret != 0) { - return map_nt_error_from_unix(ret); + return ret; } } @@ -376,7 +376,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, mask = inotify_map(filter); if (mask == 0) { /* this filter can't be handled by inotify */ - return NT_STATUS_INVALID_PARAMETER; + return EINVAL; } /* using IN_MASK_ADD allows us to cope with inotify() returning the same @@ -386,7 +386,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, w = talloc(in, struct inotify_watch_context); if (w == NULL) { *filter = orig_filter; - return NT_STATUS_NO_MEMORY; + return ENOMEM; } w->in = in; @@ -398,7 +398,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, if (w->path == NULL) { *filter = orig_filter; TALLOC_FREE(w); - return NT_STATUS_NO_MEMORY; + return ENOMEM; } /* get a new watch descriptor for this path */ @@ -408,7 +408,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, *filter = orig_filter; TALLOC_FREE(w); DEBUG(1, ("inotify_add_watch returned %s\n", strerror(err))); - return map_nt_error_from_unix(err); + return err; } DEBUG(10, ("inotify_add_watch for %s mask %x returned wd %d\n", @@ -421,7 +421,7 @@ NTSTATUS inotify_watch(struct sys_notify_context *ctx, /* the caller frees the handle to stop watching */ talloc_set_destructor(w, watch_destructor); - return NT_STATUS_OK; + return 0; } #endif diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 9980d03132..0e43aaf0df 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -531,15 +531,15 @@ struct sys_notify_context *sys_notify_context_create(TALLOC_CTX *mem_ctx, /* The following definitions come from smbd/notify_inotify.c */ -NTSTATUS inotify_watch(struct sys_notify_context *ctx, - const char *path, - uint32_t *filter, - uint32_t *subdir_filter, - void (*callback)(struct sys_notify_context *ctx, - void *private_data, - struct notify_event *ev), - void *private_data, - void *handle_p); +int inotify_watch(struct sys_notify_context *ctx, + const char *path, + uint32_t *filter, + uint32_t *subdir_filter, + void (*callback)(struct sys_notify_context *ctx, + void *private_data, + struct notify_event *ev), + void *private_data, + void *handle_p); /* The following definitions come from smbd/notify_internal.c */ From de4a1c90a70b31f99e987fa109683862bd0823d1 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 5 Nov 2014 11:44:42 +0000 Subject: [PATCH 27/97] notify: Add "dir" to notify_event "notify_event" only reports names relative to some path that is only implicitly known via "private_data". Right now "private_data" is the fsp of the directory holding this notify. I want to use inotify_watch in a notify-daemon that does not have a fsp available and has more problems getting the path right out of "private_data". notify_inotify has the directory under which the event happened available, so make it known to the callback. Right now no caller uses it yet. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- librpc/idl/notify.idl | 1 + source3/smbd/notify_inotify.c | 2 ++ source3/smbd/notify_internal.c | 10 +++++++--- source4/ntvfs/common/notify.c | 1 + source4/ntvfs/sysdep/inotify.c | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/librpc/idl/notify.idl b/librpc/idl/notify.idl index ec81e8c64a..66422ec9f9 100644 --- a/librpc/idl/notify.idl +++ b/librpc/idl/notify.idl @@ -65,6 +65,7 @@ interface notify /* structure sent between servers in notify messages */ typedef [public] struct { uint32 action; + utf8string dir; utf8string path; pointer private_data; } notify_event; diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index ad670affcd..a3c30f1431 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -165,6 +165,7 @@ static void inotify_dispatch(struct inotify_private *in, for (w=in->watches;w;w=next) { next = w->next; if (w->wd == e->wd && filter_match(w, e)) { + ne.dir = w->path; w->callback(in->ctx, w->private_data, &ne); } } @@ -184,6 +185,7 @@ static void inotify_dispatch(struct inotify_private *in, next = w->next; if (w->wd == e->wd && filter_match(w, e) && !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) { + ne.dir = w->path; w->callback(in->ctx, w->private_data, &ne); } } diff --git a/source3/smbd/notify_internal.c b/source3/smbd/notify_internal.c index 9d54891152..9c0b1901f8 100644 --- a/source3/smbd/notify_internal.c +++ b/source3/smbd/notify_internal.c @@ -831,9 +831,13 @@ static void notify_handler(struct messaging_context *msg_ctx, } m = (struct notify_msg *)data->data; - e.action = m->action; - e.path = m->path; - e.private_data = m->private_data; + + e = (struct notify_event) { + .action = m->action, + .path = m->path, + .private_data = m->private_data, + .dir = discard_const_p(char, "") + }; for (listel=notify->list;listel;listel=listel->next) { if (listel->private_data == m->private_data) { diff --git a/source4/ntvfs/common/notify.c b/source4/ntvfs/common/notify.c index 57142c7f38..61da0b8291 100644 --- a/source4/ntvfs/common/notify.c +++ b/source4/ntvfs/common/notify.c @@ -550,6 +550,7 @@ static void notify_send(struct notify_context *notify, struct notify_entry *e, TALLOC_CTX *tmp_ctx; ev.action = action; + ev.dir = discard_const_p(char, ""); ev.path = path; ev.private_data = e->private_data; diff --git a/source4/ntvfs/sysdep/inotify.c b/source4/ntvfs/sysdep/inotify.c index 0680b4b52b..091ca1da28 100644 --- a/source4/ntvfs/sysdep/inotify.c +++ b/source4/ntvfs/sysdep/inotify.c @@ -144,6 +144,7 @@ static void inotify_dispatch(struct inotify_private *in, for (w=in->watches;w;w=next) { next = w->next; if (w->wd == e->wd && filter_match(w, e)) { + ne.dir = w->path; w->callback(in->ctx, w->private_data, &ne); } } @@ -163,6 +164,7 @@ static void inotify_dispatch(struct inotify_private *in, next = w->next; if (w->wd == e->wd && filter_match(w, e) && !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) { + ne.dir = w->path; w->callback(in->ctx, w->private_data, &ne); } } From a5a5326152a090e737201612f5e29a7ecc457c85 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Wed, 5 Nov 2014 12:18:31 +0000 Subject: [PATCH 28/97] notify_inotify: inotify_watch now takes a mem_ctx This will make it easier to integrate into proper memory hierarchies. Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/modules/vfs_default.c | 9 ++++++++- source3/smbd/notify_inotify.c | 5 +++-- source3/smbd/proto.h | 3 ++- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index eaa1c2be4f..5634cc0fe9 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -2123,7 +2123,14 @@ static NTSTATUS vfswrap_notify_watch(vfs_handle_struct *vfs_handle, if (!lp_parm_bool(-1, "notify", "inotify", True)) { return NT_STATUS_INVALID_SYSTEM_SERVICE; } - ret = inotify_watch(ctx, path, filter, subdir_filter, + /* + * "ctx->private_data" is not obvious as a talloc context + * here. Without modifying the VFS we don't have a mem_ctx + * available here, and ctx->private_data was used by + * inotify_watch before it got a real talloc parent. + */ + ret = inotify_watch(ctx->private_data, ctx, + path, filter, subdir_filter, callback, private_data, handle); if (ret != 0) { return map_nt_error_from_unix(ret); diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index a3c30f1431..fedf9b522f 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -348,7 +348,8 @@ static int watch_destructor(struct inotify_watch_context *w) add a watch. The watch is removed when the caller calls talloc_free() on *handle */ -int inotify_watch(struct sys_notify_context *ctx, +int inotify_watch(TALLOC_CTX *mem_ctx, + struct sys_notify_context *ctx, const char *path, uint32_t *filter, uint32_t *subdir_filter, @@ -385,7 +386,7 @@ int inotify_watch(struct sys_notify_context *ctx, watch descriptor for multiple watches on the same path */ mask |= (IN_MASK_ADD | IN_ONLYDIR); - w = talloc(in, struct inotify_watch_context); + w = talloc(mem_ctx, struct inotify_watch_context); if (w == NULL) { *filter = orig_filter; return ENOMEM; diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 0e43aaf0df..44ea6f0c75 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -531,7 +531,8 @@ struct sys_notify_context *sys_notify_context_create(TALLOC_CTX *mem_ctx, /* The following definitions come from smbd/notify_inotify.c */ -int inotify_watch(struct sys_notify_context *ctx, +int inotify_watch(TALLOC_CTX *mem_ctx, + struct sys_notify_context *ctx, const char *path, uint32_t *filter, uint32_t *subdir_filter, From 658ffb19826b3cfde323c3e16fde4f713e88ffd8 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Tue, 18 Nov 2014 11:28:20 +0000 Subject: [PATCH 29/97] smbd: Compile notify_inotify only if available Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/smbd/notify_inotify.c | 4 ---- source3/wscript_build | 8 ++++++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index fedf9b522f..944f27a155 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -26,8 +26,6 @@ #include "smbd/smbd.h" #include "lib/sys_rw_data.h" -#ifdef HAVE_INOTIFY - #include /* glibc < 2.5 headers don't have these defines */ @@ -426,5 +424,3 @@ int inotify_watch(TALLOC_CTX *mem_ctx, return 0; } - -#endif diff --git a/source3/wscript_build b/source3/wscript_build index 51d72e78b6..5128b96bfd 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -506,6 +506,11 @@ bld.SAMBA3_SUBSYSTEM('sysquotas', allow_warnings=True, deps='samba3-util samba-util') +NOTIFY_SOURCES='' + +if bld.CONFIG_SET("HAVE_INOTIFY"): + NOTIFY_SOURCES += ' smbd/notify_inotify.c' + bld.SAMBA3_LIBRARY('smbd_base', source=''' smbd/server_reload.c @@ -595,9 +600,8 @@ bld.SAMBA3_LIBRARY('smbd_base', smbd/oplock_irix.c smbd/oplock_linux.c smbd/notify.c - smbd/notify_inotify.c smbd/notify_internal.c - smbd/build_options.c''', + smbd/build_options.c''' + NOTIFY_SOURCES, deps=''' talloc tevent From 9bb0728f7c71cf72e31ac15c74912479a803f323 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 4 Dec 2014 16:01:17 +0000 Subject: [PATCH 30/97] notify_inotify: Simplify inotify_dispatch Normally, I'm trying to simplify things with early returns. But in this case I think the reverse makes the if-condition easier to understand Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Tue Dec 9 06:37:24 CET 2014 on sn-devel-104 --- source3/smbd/notify_inotify.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/source3/smbd/notify_inotify.c b/source3/smbd/notify_inotify.c index 944f27a155..8f4712404f 100644 --- a/source3/smbd/notify_inotify.c +++ b/source3/smbd/notify_inotify.c @@ -168,23 +168,25 @@ static void inotify_dispatch(struct inotify_private *in, } } - /* SMB expects a file rename to generate three events, two for - the rename and the other for a modify of the - destination. Strange! */ - if (ne.action != NOTIFY_ACTION_NEW_NAME || - (e->mask & IN_ISDIR) != 0) { - return; - } + if ((ne.action == NOTIFY_ACTION_NEW_NAME) && + ((e->mask & IN_ISDIR) == 0)) { - ne.action = NOTIFY_ACTION_MODIFIED; - e->mask = IN_ATTRIB; + /* + * SMB expects a file rename to generate three events, two for + * the rename and the other for a modify of the + * destination. Strange! + */ - for (w=in->watches;w;w=next) { - next = w->next; - if (w->wd == e->wd && filter_match(w, e) && - !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) { - ne.dir = w->path; - w->callback(in->ctx, w->private_data, &ne); + ne.action = NOTIFY_ACTION_MODIFIED; + e->mask = IN_ATTRIB; + + for (w=in->watches;w;w=next) { + next = w->next; + if (w->wd == e->wd && filter_match(w, e) && + !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) { + ne.dir = w->path; + w->callback(in->ctx, w->private_data, &ne); + } } } } From 8c569893250f87baf2c0b887c0dea076ca8249cf Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Thu, 27 Nov 2014 21:44:18 +0100 Subject: [PATCH 31/97] torture: Fix raw.open.chained-ntcreatex sizeof(buf) is 5. On FreeBSD10/clang this overwrites "ret". Not good. Same as 574750777a Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Tue Dec 9 19:43:31 CET 2014 on sn-devel-104 --- source4/torture/raw/open.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/torture/raw/open.c b/source4/torture/raw/open.c index 616ba5c16e..d1c09bc247 100644 --- a/source4/torture/raw/open.c +++ b/source4/torture/raw/open.c @@ -1722,8 +1722,8 @@ static bool test_chained_ntcreatex_readx(struct torture_context *tctx, struct sm /* readx parameters */ io.ntcreatexreadx.in.offset = 0; - io.ntcreatexreadx.in.mincnt = sizeof(buf); - io.ntcreatexreadx.in.maxcnt = sizeof(buf); + io.ntcreatexreadx.in.mincnt = sizeof(buf2); + io.ntcreatexreadx.in.maxcnt = sizeof(buf2); io.ntcreatexreadx.in.remaining = 0; io.ntcreatexreadx.out.data = (uint8_t *)buf2; From a4fa9ca5a7a4c0b770079ab126f8172ff6d6851c Mon Sep 17 00:00:00 2001 From: Poornima G Date: Thu, 30 Oct 2014 17:14:51 +0530 Subject: [PATCH 32/97] vfs_glusterfs: In vfs_gluster_sys_acl_get_file/fd, reduce the number of getxattr calls. Signed-off-by: Poornima G Reviewed-by: Ira Cooper Reviewed-by: Guenther Deschner --- source3/modules/vfs_glusterfs.c | 42 +++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c index ba2d8e8b57..c47013bdf8 100644 --- a/source3/modules/vfs_glusterfs.c +++ b/source3/modules/vfs_glusterfs.c @@ -1006,6 +1006,8 @@ static int vfs_gluster_set_offline(struct vfs_handle_struct *handle, #define GLUSTER_ACL_HEADER_SIZE 4 #define GLUSTER_ACL_ENTRY_SIZE 8 +#define GLUSTER_ACL_SIZE(n) (GLUSTER_ACL_HEADER_SIZE + (n * GLUSTER_ACL_ENTRY_SIZE)) + static SMB_ACL_T gluster_to_smb_acl(const char *buf, size_t xattr_size, TALLOC_CTX *mem_ctx) { @@ -1275,7 +1277,7 @@ static SMB_ACL_T vfs_gluster_sys_acl_get_file(struct vfs_handle_struct *handle, struct smb_acl_t *result; char *buf; const char *key; - ssize_t ret; + ssize_t ret, size = GLUSTER_ACL_SIZE(20); switch (type) { case SMB_ACL_TYPE_ACCESS: @@ -1289,13 +1291,22 @@ static SMB_ACL_T vfs_gluster_sys_acl_get_file(struct vfs_handle_struct *handle, return NULL; } - ret = glfs_getxattr(handle->data, path_p, key, 0, 0); - if (ret <= 0) { + buf = alloca(size); + if (!buf) { return NULL; } - buf = alloca(ret); - ret = glfs_getxattr(handle->data, path_p, key, buf, ret); + ret = glfs_getxattr(handle->data, path_p, key, buf, size); + if (ret == -1 && errno == ERANGE) { + ret = glfs_getxattr(handle->data, path_p, key, 0, 0); + if (ret > 0) { + buf = alloca(ret); + if (!buf) { + return NULL; + } + ret = glfs_getxattr(handle->data, path_p, key, buf, ret); + } + } if (ret <= 0) { return NULL; } @@ -1310,18 +1321,29 @@ static SMB_ACL_T vfs_gluster_sys_acl_get_fd(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx) { struct smb_acl_t *result; - int ret; + ssize_t ret, size = GLUSTER_ACL_SIZE(20); char *buf; glfs_fd_t *glfd; glfd = *(glfs_fd_t **)VFS_FETCH_FSP_EXTENSION(handle, fsp); - ret = glfs_fgetxattr(glfd, "system.posix_acl_access", 0, 0); - if (ret <= 0) { + + buf = alloca(size); + if (!buf) { return NULL; } - buf = alloca(ret); - ret = glfs_fgetxattr(glfd, "system.posix_acl_access", buf, ret); + ret = glfs_fgetxattr(glfd, "system.posix_acl_access", buf, size); + if (ret == -1 && errno == ERANGE) { + ret = glfs_fgetxattr(glfd, "system.posix_acl_access", 0, 0); + if (ret > 0) { + buf = alloca(ret); + if (!buf) { + return NULL; + } + ret = glfs_fgetxattr(glfd, "system.posix_acl_access", + buf, ret); + } + } if (ret <= 0) { return NULL; } From 2fb4317352a165d0dd4af78fcf0a576fa246eab2 Mon Sep 17 00:00:00 2001 From: raghavendra talur Date: Wed, 10 Dec 2014 12:26:34 +0530 Subject: [PATCH 33/97] vfs_glusterfs: Remember the connect path too for reopening. As Samba allows us to share subdirs, lets re-use preopened connections to glusterfs only if they are for same volume AND same connectpath. Signed-off-by: raghavendra talur Reviewed-by: Guenther Deschner Reviewed-by: Michael Adam --- source3/modules/vfs_glusterfs.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c index c47013bdf8..2dd9d71667 100644 --- a/source3/modules/vfs_glusterfs.c +++ b/source3/modules/vfs_glusterfs.c @@ -78,13 +78,14 @@ static void smb_stat_ex_from_stat(struct stat_ex *dst, const struct stat *src) static struct glfs_preopened { char *volume; + char *connectpath; glfs_t *fs; int ref; struct glfs_preopened *next, *prev; } *glfs_preopened; -static int glfs_set_preopened(const char *volume, glfs_t *fs) +static int glfs_set_preopened(const char *volume, const char *connectpath, glfs_t *fs) { struct glfs_preopened *entry = NULL; @@ -101,6 +102,13 @@ static int glfs_set_preopened(const char *volume, glfs_t *fs) return -1; } + entry->connectpath = talloc_strdup(entry, connectpath); + if (entry->connectpath == NULL) { + talloc_free(entry); + errno = ENOMEM; + return -1; + } + entry->fs = fs; entry->ref = 1; @@ -109,12 +117,14 @@ static int glfs_set_preopened(const char *volume, glfs_t *fs) return 0; } -static glfs_t *glfs_find_preopened(const char *volume) +static glfs_t *glfs_find_preopened(const char *volume, const char *connectpath) { struct glfs_preopened *entry = NULL; for (entry = glfs_preopened; entry; entry = entry->next) { - if (strcmp(entry->volume, volume) == 0) { + if (strcmp(entry->volume, volume) == 0 && + strcmp(entry->connectpath, connectpath) == 0) + { entry->ref++; return entry->fs; } @@ -176,7 +186,7 @@ static int vfs_gluster_connect(struct vfs_handle_struct *handle, volume = service; } - fs = glfs_find_preopened(volume); + fs = glfs_find_preopened(volume, handle->conn->connectpath); if (fs) { goto done; } @@ -214,7 +224,7 @@ static int vfs_gluster_connect(struct vfs_handle_struct *handle, goto done; } - ret = glfs_set_preopened(volume, fs); + ret = glfs_set_preopened(volume, handle->conn->connectpath, fs); if (ret < 0) { DEBUG(0, ("%s: Failed to register volume (%s)\n", volume, strerror(errno))); From a12ae815b01b372cbf8697d32d5d45d7b2d5f93d Mon Sep 17 00:00:00 2001 From: raghavendra talur Date: Wed, 10 Dec 2014 12:30:10 +0530 Subject: [PATCH 34/97] vfs_glusterfs: Set connectpath as snapdir-entry-path. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit snapview client in gluster would require connectpath in order add snapdir entry to readdir result. Signed-off-by: raghavendra talur Reviewed-by: Guenther Deschner Reviewed-by: Michael Adam Autobuild-User(master): Günther Deschner Autobuild-Date(master): Wed Dec 10 16:38:27 CET 2014 on sn-devel-104 --- source3/modules/vfs_glusterfs.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source3/modules/vfs_glusterfs.c b/source3/modules/vfs_glusterfs.c index 2dd9d71667..c22216478f 100644 --- a/source3/modules/vfs_glusterfs.c +++ b/source3/modules/vfs_glusterfs.c @@ -210,6 +210,17 @@ static int vfs_gluster_connect(struct vfs_handle_struct *handle, goto done; } + + ret = glfs_set_xlator_option(fs, "*-snapview-client", + "snapdir-entry-path", + handle->conn->connectpath); + if (ret < 0) { + DEBUG(0, ("%s: Failed to set xlator option:" + " snapdir-entry-path\n", volume)); + glfs_fini(fs); + return -1; + } + ret = glfs_set_logging(fs, logfile, loglevel); if (ret < 0) { DEBUG(0, ("%s: Failed to set logfile %s loglevel %d\n", From b299409410751ff3c8c775bd073e34d914a54efc Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Wed, 11 Dec 2013 14:59:20 +1300 Subject: [PATCH 35/97] netapi: Move DC check to NetJoinDomain() where it is needed. This partially reverts 15f6e27bd5a9065c8b781fa21f5989ce2c355776. BUG: https://bugzilla.samba.org/show_bug.cgi?id=10891 Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam Reviewed-by: Andreas Schneider --- source3/lib/netapi/joindomain.c | 4 ++++ source3/libnet/libnet_join.c | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/lib/netapi/joindomain.c b/source3/lib/netapi/joindomain.c index 9da1bdc6e1..632c8c6638 100644 --- a/source3/lib/netapi/joindomain.c +++ b/source3/lib/netapi/joindomain.c @@ -115,6 +115,10 @@ WERROR NetJoinDomain_r(struct libnetapi_ctx *ctx, struct dcerpc_binding_handle *b; DATA_BLOB session_key; + if (IS_DC) { + return WERR_SETUP_DOMAIN_CONTROLLER; + } + werr = libnetapi_open_pipe(ctx, r->in.server, &ndr_table_wkssvc, &pipe_cli); diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 7c70d35cfe..73c9a57387 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -1957,10 +1957,6 @@ static WERROR libnet_join_pre_processing(TALLOC_CTX *mem_ctx, return WERR_INVALID_PARAM; } - if (IS_DC) { - return WERR_SETUP_DOMAIN_CONTROLLER; - } - if (!r->in.admin_domain) { char *admin_domain = NULL; char *admin_account = NULL; From 6d6c673c6d33ceb1379c66d6b4d78a52077b928a Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Thu, 11 Dec 2014 16:41:55 +0100 Subject: [PATCH 36/97] selftest: Add 'net dom join' test which fails cause we are a DC Signed-off-by: Andreas Schneider Reviewed-by: Guenther Deschner --- .../script/tests/test_net_dom_join_fail_dc.sh | 22 +++++++++++++++++++ source3/selftest/tests.py | 5 +++++ 2 files changed, 27 insertions(+) create mode 100755 source3/script/tests/test_net_dom_join_fail_dc.sh diff --git a/source3/script/tests/test_net_dom_join_fail_dc.sh b/source3/script/tests/test_net_dom_join_fail_dc.sh new file mode 100755 index 0000000000..135e1daa07 --- /dev/null +++ b/source3/script/tests/test_net_dom_join_fail_dc.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +if [ $# -lt 4 ]; then +cat < Date: Wed, 11 Dec 2013 15:39:38 +1300 Subject: [PATCH 37/97] auth: Allow domain join to itself when we are a PDC BUG: https://bugzilla.samba.org/show_bug.cgi?id=10891 Signed-off-by: Andrew Bartlett Reviewed-by: Garming Sam Reviewed-by: Andreas Schneider --- source3/libnet/libnet_join.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source3/libnet/libnet_join.c b/source3/libnet/libnet_join.c index 73c9a57387..dd87c6d425 100644 --- a/source3/libnet/libnet_join.c +++ b/source3/libnet/libnet_join.c @@ -2139,7 +2139,9 @@ static WERROR libnet_join_check_config(TALLOC_CTX *mem_ctx, switch (r->out.domain_is_ad) { case false: - valid_security = (lp_security() == SEC_DOMAIN); + valid_security = (lp_security() == SEC_DOMAIN) + || (lp_server_role() == ROLE_DOMAIN_PDC) + || (lp_server_role() == ROLE_DOMAIN_BDC); if (valid_workgroup && valid_security) { /* nothing to be done */ return WERR_OK; From e6ec265a405e76e5d4ea59b8025da0f57b3d3ad1 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 23 Oct 2014 12:28:48 +1300 Subject: [PATCH 38/97] libsmb: Allow change of BDC trust account password This account is otherwise just like the workstation trust acocunt, so use that code. BUG: https://bugzilla.samba.org/show_bug.cgi?id=10891 Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett Reviewed-by: Andreas Schneider --- source3/libsmb/trusts_util.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source3/libsmb/trusts_util.c b/source3/libsmb/trusts_util.c index bb2e977cc5..7503ef0997 100644 --- a/source3/libsmb/trusts_util.c +++ b/source3/libsmb/trusts_util.c @@ -111,6 +111,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, switch (sec_channel_type) { case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: pwd = secrets_fetch_machine_password(domain, &pass_last_set_time, NULL); @@ -188,6 +189,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, switch (sec_channel_type) { case SEC_CHAN_WKSTA: + case SEC_CHAN_BDC: if (!secrets_store_machine_password(new_trust_passwd, domain, sec_channel_type)) { TALLOC_FREE(frame); return NT_STATUS_INTERNAL_DB_CORRUPTION; @@ -206,6 +208,7 @@ NTSTATUS trust_pw_change(struct netlogon_creds_cli_context *context, break; default: + smb_panic("Unsupported secure channel type"); break; } From 0da3ab96739df436b54fcf6c7e138229271b0866 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 23 Oct 2014 12:38:15 +1300 Subject: [PATCH 39/97] selftest: Add test for joining a Samba classic DC as a BDC This does not join the DC itself, so as not to pertrub the test environment mid-run, but does confirm that the join works and the password can be changed. Pair-programmed-with: Garming Sam Signed-off-by: Andrew Bartlett Reviewed-by: Andreas Schneider Autobuild-User(master): Andreas Schneider Autobuild-Date(master): Thu Dec 11 21:40:27 CET 2014 on sn-devel-104 --- source3/script/tests/test_net_rpc_join.sh | 25 +++++++++++++++++++++++ source3/selftest/tests.py | 4 ++++ 2 files changed, 29 insertions(+) create mode 100755 source3/script/tests/test_net_rpc_join.sh diff --git a/source3/script/tests/test_net_rpc_join.sh b/source3/script/tests/test_net_rpc_join.sh new file mode 100755 index 0000000000..a7810a98b6 --- /dev/null +++ b/source3/script/tests/test_net_rpc_join.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +if [ $# -lt 4 ]; then +cat < Date: Thu, 11 Dec 2014 19:06:11 +0100 Subject: [PATCH 40/97] vfs_streams_xattr: add missing call to SMB_VFS_NEXT_CONNECT Signed-off-by: Ralph Boehme Rewviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Dec 12 02:09:24 CET 2014 on sn-devel-104 --- source3/modules/vfs_streams_xattr.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source3/modules/vfs_streams_xattr.c b/source3/modules/vfs_streams_xattr.c index 5c5a9a17fa..b3c1df15c3 100644 --- a/source3/modules/vfs_streams_xattr.c +++ b/source3/modules/vfs_streams_xattr.c @@ -879,6 +879,12 @@ static int streams_xattr_connect(vfs_handle_struct *handle, struct streams_xattr_config *config; const char *default_prefix = SAMBA_XATTR_DOSSTREAM_PREFIX; const char *prefix; + int rc; + + rc = SMB_VFS_NEXT_CONNECT(handle, service, user); + if (rc != 0) { + return rc; + } config = talloc_zero(handle->conn, struct streams_xattr_config); if (config == NULL) { From 0d0a0f79d387c45e697d9de46ffe5c46a1f39295 Mon Sep 17 00:00:00 2001 From: Martin Schwenke Date: Fri, 12 Dec 2014 10:57:37 +1100 Subject: [PATCH 41/97] lib/util: Fix incorrect month in timestamps BUG: https://bugzilla.samba.org/show_bug.cgi?id=11003 Reported-by: Youzhong Yang Signed-off-by: Martin Schwenke Reviewed-by: Amitay Isaacs Autobuild-User(master): Amitay Isaacs Autobuild-Date(master): Fri Dec 12 05:02:32 CET 2014 on sn-devel-104 --- lib/util/time_basic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util/time_basic.c b/lib/util/time_basic.c index 0eeb441371..e4b088610b 100644 --- a/lib/util/time_basic.c +++ b/lib/util/time_basic.c @@ -66,7 +66,7 @@ char *timeval_str_buf(const struct timeval *tp, bool rfc5424, bool hires, (rfc5424 ? "%04d-%02d-%02dT%02d:%02d:%02d" : "%04d/%02d/%02d %02d:%02d:%02d"), - 1900 + tm->tm_year, tm->tm_mon, tm->tm_mday, + 1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); if ((rfc5424 || hires) && (len < sizeof(dst->buf))) { From a6ecef4532e4529a819219cd814e2979c2df0797 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Thu, 20 Nov 2014 14:21:06 +0100 Subject: [PATCH 42/97] s4:dsdb/rootdse: expand extended dn values with the AS_SYSTEM control Otherwise we can't find the GUID of the 'serverName' attribute as ANONYMOUS. This results in root@ub1204-161:~# ldbsearch -U% -H ldap://172.31.9.161 -b '' -s base --extended-dn serverName search error - LDAP error 1 LDAP_OPERATIONS_ERROR - <00002020: operations error at ../source4/dsdb/samdb/ldb_modules/rootdse.c:567> <> While it works as system: root@ub1204-161:~# ldbsearch -U% -H /var/lib/samba/private/sam.ldb -b '' -s base --extended-dn serverName # record 1 dn: serverName: ;CN=UB1204-161,CN=Serve rs,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=s4xdom,DC=base # returned 1 records # 1 entries # 0 referrals Bug: https://bugzilla.samba.org/show_bug.cgi?id=10949 Signed-off-by: Stefan Metzmacher Reviewed-by: Guenther Deschner --- source4/dsdb/samdb/ldb_modules/rootdse.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/rootdse.c b/source4/dsdb/samdb/ldb_modules/rootdse.c index b13dc9e5c5..111266f035 100644 --- a/source4/dsdb/samdb/ldb_modules/rootdse.c +++ b/source4/dsdb/samdb/ldb_modules/rootdse.c @@ -142,10 +142,8 @@ static int expand_dn_in_message(struct ldb_module *module, struct ldb_message *m return ret; } - - ret = ldb_request_add_control(req2, - LDB_CONTROL_EXTENDED_DN_OID, - edn_control->critical, edn); + ret = dsdb_request_add_controls(req2, DSDB_FLAG_AS_SYSTEM | + DSDB_SEARCH_SHOW_EXTENDED_DN); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ldb_error(ldb, ret, "Failed to add control"); From 7e81fe282540a5b52dcb8c5396321a67733790d2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 21 Nov 2014 14:11:54 +0100 Subject: [PATCH 43/97] testprogs/test_ldb: check rootdse search with extended-dn control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Verifies BUG: https://bugzilla.samba.org/show_bug.cgi?id=10949 Signed-off-by: Stefan Metzmacher Reviewed-by: Guenther Deschner Autobuild-User(master): Günther Deschner Autobuild-Date(master): Fri Dec 12 20:15:46 CET 2014 on sn-devel-104 --- testprogs/blackbox/test_ldb.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testprogs/blackbox/test_ldb.sh b/testprogs/blackbox/test_ldb.sh index f326672670..60bad44ebb 100755 --- a/testprogs/blackbox/test_ldb.sh +++ b/testprogs/blackbox/test_ldb.sh @@ -37,6 +37,8 @@ export PATH="$BINDIR:$PATH" ldbsearch="$VALGRIND ldbsearch" check "RootDSE" $ldbsearch $CONFIGURATION $options --basedn='' -H $p://$SERVER -s base DUMMY=x dnsHostName highestCommittedUSN || failed=`expr $failed + 1` +check "RootDSE (full)" $ldbsearch $CONFIGURATION $options --basedn='' -H $p://$SERVER -s base '(objectClass=*)' || failed=`expr $failed + 1` +check "RootDSE (extended)" $ldbsearch $CONFIGURATION $options --basedn='' -H $p://$SERVER -s base '(objectClass=*)' --extended-dn || failed=`expr $failed + 1` echo "Getting defaultNamingContext" BASEDN=`$ldbsearch $CONFIGURATION $options --basedn='' -H $p://$SERVER -s base DUMMY=x defaultNamingContext | grep defaultNamingContext | awk '{print $2}'` From 7c73946f3ae3cee509276ae69bae92c21f711684 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Dec 2014 11:39:25 +0100 Subject: [PATCH 44/97] libsmb: Fix removing invalid samlogon cache entries Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/libsmb/samlogon_cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c index 1f1ab1d46e..0041a77c88 100644 --- a/source3/libsmb/samlogon_cache.c +++ b/source3/libsmb/samlogon_cache.c @@ -247,7 +247,7 @@ struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct do if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(0,("netsamlogon_cache_get: failed to pull entry from cache\n")); - tdb_delete(netsamlogon_tdb, data); + tdb_delete_bystring(netsamlogon_tdb, keystr); TALLOC_FREE(info3); goto done; } From 8a56911a4ad272ecbc5a0df67fe0c1fb5aabe8b3 Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Dec 2014 11:41:14 +0100 Subject: [PATCH 45/97] libsmb: Only print decoded netsamlogon cache entries Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/libsmb/samlogon_cache.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c index 0041a77c88..9e194b5142 100644 --- a/source3/libsmb/samlogon_cache.c +++ b/source3/libsmb/samlogon_cache.c @@ -241,10 +241,6 @@ struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct do ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, (ndr_pull_flags_fn_t)ndr_pull_netsamlogoncache_entry); - if (DEBUGLEVEL >= 10) { - NDR_PRINT_DEBUG(netsamlogoncache_entry, &r); - } - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(0,("netsamlogon_cache_get: failed to pull entry from cache\n")); tdb_delete_bystring(netsamlogon_tdb, keystr); @@ -252,6 +248,10 @@ struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct do goto done; } + if (DEBUGLEVEL >= 10) { + NDR_PRINT_DEBUG(netsamlogoncache_entry, &r); + } + info3 = (struct netr_SamInfo3 *)talloc_memdup(mem_ctx, &r.info3, sizeof(r.info3)); From 81f99bd5c1894302ad0d6549f5fb28fa13df021c Mon Sep 17 00:00:00 2001 From: Volker Lendecke Date: Fri, 12 Dec 2014 11:44:06 +0100 Subject: [PATCH 46/97] libsmb: Simplify netsamlogon_cache_get Signed-off-by: Volker Lendecke Reviewed-by: Jeremy Allison --- source3/libsmb/samlogon_cache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source3/libsmb/samlogon_cache.c b/source3/libsmb/samlogon_cache.c index 9e194b5142..4e50c78688 100644 --- a/source3/libsmb/samlogon_cache.c +++ b/source3/libsmb/samlogon_cache.c @@ -211,7 +211,7 @@ struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct do { struct netr_SamInfo3 *info3 = NULL; TDB_DATA data; - fstring keystr, tmp; + fstring keystr; enum ndr_err_code ndr_err; DATA_BLOB blob; struct netsamlogoncache_entry r; @@ -223,7 +223,7 @@ struct netr_SamInfo3 *netsamlogon_cache_get(TALLOC_CTX *mem_ctx, const struct do } /* Prepare key as DOMAIN-SID/USER-RID string */ - slprintf(keystr, sizeof(keystr), "%s", sid_to_fstring(tmp, user_sid)); + sid_to_fstring(keystr, user_sid); DEBUG(10,("netsamlogon_cache_get: SID [%s]\n", keystr)); data = tdb_fetch_bystring( netsamlogon_tdb, keystr ); From 382019656ee164fd21455ed7d7b5e9e18bd0ca72 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 12 Dec 2014 09:22:15 +0100 Subject: [PATCH 47/97] s3:smb2_server: allow reauthentication without signing If signing is not required we should not require it for reauthentication. Windows clients would otherwise fail to reauthenticate. Bug: https://bugzilla.samba.org/show_bug.cgi?id=10958 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison --- source3/smbd/smb2_server.c | 5 ----- source3/smbd/smb2_sesssetup.c | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 4a2c875414..46bf6f96ee 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1990,11 +1990,6 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) if (x != NULL) { signing_required = x->global->signing_required; encryption_required = x->global->encryption_required; - - if (opcode == SMB2_OP_SESSSETUP && - x->global->signing_key.length > 0) { - signing_required = true; - } } req->do_signing = false; diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 78cafe8f78..2f58e44f55 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -422,6 +422,10 @@ static NTSTATUS smbd_smb2_reauth_generic_return(struct smbXsrv_session *session, conn_clear_vuid_caches(smb2req->sconn, session->compat->vuid); + if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { + smb2req->do_signing = true; + } + *out_session_id = session->global->session_wire_id; return NT_STATUS_OK; From daff0f5d709eca621a7f319c892ecaba7b03e5c2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 12 Dec 2014 13:55:38 +0000 Subject: [PATCH 48/97] libcli/smb: only force signing of smb2 session setups when binding a new session Bug: https://bugzilla.samba.org/show_bug.cgi?id=10958 Signed-off-by: Stefan Metzmacher Reviewed-by: Jeremy Allison Autobuild-User(master): Jeremy Allison Autobuild-Date(master): Fri Dec 12 23:11:40 CET 2014 on sn-devel-104 --- libcli/smb/smbXcli_base.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index a3a7ecb801..8aa6020190 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -2682,7 +2682,12 @@ struct tevent_req *smb2cli_req_create(TALLOC_CTX *mem_ctx, state->smb2.should_encrypt = session->smb2->should_encrypt; if (cmd == SMB2_OP_SESSSETUP && - session->smb2->signing_key.length != 0) { + session->smb2_channel.signing_key.length == 0 && + session->smb2->signing_key.length != 0) + { + /* + * a session bind needs to be signed + */ state->smb2.should_sign = true; } From 13250780fbf4614df980509a3f0f9315f12bc1a4 Mon Sep 17 00:00:00 2001 From: Nadezhda Ivanova Date: Tue, 21 Oct 2014 16:35:30 +0300 Subject: [PATCH 49/97] s4-dsdb-tests: Some tests for deleted objects undelete operation Based on MS-ADTS 3.1.1.5.3.7.2 Signed-off-by: Nadezhda Ivanova Change-Id: I650b315601fce574f9302435f812d1dd4b177e68 --- source4/dsdb/tests/python/deletetest.py | 203 +++++++++++++++++++++++- 1 file changed, 198 insertions(+), 5 deletions(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index 370f56cdea..cb08db404d 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -13,9 +13,9 @@ import samba.getopt as options from samba.auth import system_session -from ldb import SCOPE_BASE, LdbError -from ldb import ERR_NO_SUCH_OBJECT, ERR_NOT_ALLOWED_ON_NON_LEAF -from ldb import ERR_UNWILLING_TO_PERFORM +from ldb import SCOPE_BASE, LdbError, Message, MessageElement, Dn, FLAG_MOD_ADD, FLAG_MOD_DELETE, FLAG_MOD_REPLACE +from ldb import ERR_NO_SUCH_OBJECT, ERR_NOT_ALLOWED_ON_NON_LEAF, ERR_ENTRY_ALREADY_EXISTS, ERR_ATTRIBUTE_OR_VALUE_EXISTS +from ldb import ERR_UNWILLING_TO_PERFORM, ERR_OPERATIONS_ERROR from samba.samdb import SamDB from samba.tests import delete_force @@ -39,13 +39,13 @@ lp = sambaopts.get_loadparm() creds = credopts.get_credentials(lp) -class BasicDeleteTests(samba.tests.TestCase): +class BaseDeleteTests(samba.tests.TestCase): def GUID_string(self, guid): return self.ldb.schema_format_value("objectGUID", guid) def setUp(self): - super(BasicDeleteTests, self).setUp() + super(BaseDeleteTests, self).setUp() self.ldb = SamDB(host, credentials=creds, session_info=system_session(lp), lp=lp) self.base_dn = self.ldb.domain_dn() @@ -69,6 +69,12 @@ def search_dn(self,dn): self.assertEquals(len(res), 1) return res[0] + +class BasicDeleteTests(BaseDeleteTests): + + def setUp(self): + super(BasicDeleteTests, self).setUp() + def del_attr_values(self, delObj): print "Checking attributes for %s" % delObj["dn"] @@ -373,6 +379,193 @@ def test_all(self): self.assertFalse("CN=Deleted Objects" in str(objDeleted6.dn)) self.assertFalse("CN=Deleted Objects" in str(objDeleted7.dn)) +class BasicUndeleteTests(BaseDeleteTests): + + def setUp(self): + super(BasicUndeleteTests, self).setUp() + + def enable_recycle_bin(self): + msg = Message() + msg.dn = Dn(ldb, "") + msg["enableOptionalFeature"] = MessageElement( + "CN=Partitions," + self.configuration_dn + ":766ddcd8-acd0-445e-f3b9-a7f9b6744f2a", + FLAG_MOD_ADD, "enableOptionalFeature") + try: + ldb.modify(msg) + except LdbError, (num, _): + self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + def get_ldb_connection(self, target_username, target_password): + creds_tmp = Credentials() + creds_tmp.set_username(target_username) + creds_tmp.set_password(target_password) + creds_tmp.set_domain(creds.get_domain()) + creds_tmp.set_realm(creds.get_realm()) + creds_tmp.set_workstation(creds.get_workstation()) + creds_tmp.set_gensec_features(creds_tmp.get_gensec_features() + | gensec.FEATURE_SEAL) + creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop + ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp) + return ldb_target + + def undelete_deleted(self, olddn, newdn, samldb): + msg = Message() + msg.dn = Dn(ldb, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + res = samldb.modify(msg, ["show_deleted:1"]) + + def undelete_deleted_with_mod(self, olddn, newdn): + msg = Message() + msg.dn = Dn(ldb, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") + res = ldb.modify(msg, ["show_deleted:1"]) + + + def test_undelete(self): + print "Testing standard undelete operation" + usr1="cn=testuser,cn=users," + self.base_dn + delete_force(self.ldb, usr1) + ldb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + ldb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + self.undelete_deleted(str(objDeleted1.dn), usr1, ldb) + objLive2 = self.search_dn(usr1) + self.assertEqual(str(objLive2.dn),str(objLive1.dn)) + delete_force(self.ldb, usr1) + + def test_rename(self): + print "Testing attempt to rename deleted object" + usr1="cn=testuser,cn=users," + self.base_dn + ldb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + ldb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + #just to make sure we get the correct error if the show deleted is missing + try: + ldb.rename(str(objDeleted1.dn), usr1) + self.fail() + except LdbError, (num, _): + self.assertEquals(num,ERR_NO_SUCH_OBJECT) + + try: + ldb.rename(str(objDeleted1.dn), usr1, ["show_deleted:1"]) + self.fail() + except LdbError, (num, _): + self.assertEquals(num,ERR_UNWILLING_TO_PERFORM) + + def test_undelete_with_mod(self): + print "Testing standard undelete operation with modification of additional attributes" + usr1="cn=testuser,cn=users," + self.base_dn + ldb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + ldb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + self.undelete_deleted_with_mod(str(objDeleted1.dn), usr1) + objLive2 = self.search_dn(usr1) + self.assertEqual(objLive2["url"][0],"www.samba.org") + delete_force(self.ldb, usr1) + + def test_undelete_newuser(self): + print "Testing undelete user with a different dn" + usr1="cn=testuser,cn=users," + self.base_dn + usr2="cn=testuser2,cn=users," + self.base_dn + delete_force(self.ldb, usr1) + ldb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + ldb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + self.undelete_deleted(str(objDeleted1.dn), usr2, ldb) + objLive2 = self.search_dn(usr2) + delete_force(self.ldb, usr1) + delete_force(self.ldb, usr2) + + def test_undelete_existing(self): + print "Testing undelete user after a user with the same dn has been created" + usr1="cn=testuser,cn=users," + self.base_dn + ldb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + ldb.delete(usr1) + ldb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objDeleted1 = self.search_guid(guid1) + try: + self.undelete_deleted(str(objDeleted1.dn), usr1, ldb) + self.fail() + except LdbError, (num, _): + self.assertEquals(num,ERR_ENTRY_ALREADY_EXISTS) + + def test_undelete_cross_nc(self): + print "Cross NC undelete" + c1 = "cn=ldaptestcontainer," + self.base_dn; + c2 = "cn=ldaptestcontainer2," + self.configuration_dn + c3 = "cn=ldaptestcontainer," + self.configuration_dn + c4 = "cn=ldaptestcontainer2," + self.base_dn; + ldb.add({ + "dn": c1, + "objectclass": "container"}) + ldb.add({ + "dn": c2, + "objectclass": "container"}) + objLive1 = self.search_dn(c1) + objLive2 = self.search_dn(c2) + guid1=objLive1["objectGUID"][0] + guid2=objLive2["objectGUID"][0] + ldb.delete(c1) + ldb.delete(c2) + objDeleted1 = self.search_guid(guid1) + objDeleted2 = self.search_guid(guid2) + #try to undelete from base dn to config + try: + self.undelete_deleted(str(objDeleted1.dn), c3, ldb) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_OPERATIONS_ERROR) + #try to undelete from config to base dn + try: + self.undelete_deleted(str(objDeleted2.dn), c4, ldb) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_OPERATIONS_ERROR) + #assert undeletion will work in same nc + self.undelete_deleted(str(objDeleted1.dn), c4, ldb) + self.undelete_deleted(str(objDeleted2.dn), c3, ldb) + delete_force(self.ldb, c3) + delete_force(self.ldb, c4) + + + if not "://" in host: if os.path.isfile(host): host = "tdb://%s" % host From d6eea17c914c8fa4fdb5a3a19663a92f7378dcaa Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 23 Oct 2014 08:14:40 +0200 Subject: [PATCH 50/97] s4-dsdb: Initial implementation for Tombstone reanimation module At the moment it works for basic scenario: - add user - delete user - restore deleted user TODO: - security checks - flags verification - cross-NC checks - asynchronous implementation (may not be needed, but anyway) Change-Id: If396a6dfc766c224acfeb7e93ca75703e08c26e6 Signed-off-by: Kamen Mazdrashki --- .../samdb/ldb_modules/tombstone_reanimate.c | 256 ++++++++++++++++++ .../samdb/ldb_modules/wscript_build_server | 10 + 2 files changed, 266 insertions(+) create mode 100644 source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c new file mode 100644 index 0000000000..39da21d496 --- /dev/null +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -0,0 +1,256 @@ +/* + ldb database library + + Copyright (C) Kamen Mazdrashki 2014 + + 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 . +*/ + +/* + * Name: tombstone_reanimate + * + * Component: Handle Tombstone reanimation requests + * + * Description: + * Tombstone reanimation requests are plain ldap modify request like: + * dn: CN=tombi 1\0ADEL:e6e17ff7-8986-4cdd-87ad-afb683ccbb89,CN=Deleted Objects,DC=samba4,DC=devel + * changetype: modify + * delete: isDeleted + * - + * replace: distinguishedName + * distinguishedName: CN=Tombi 1,CN=Users,DC=samba4,DC=devel + * - + * + * Usually we don't allow distinguishedName modifications (see rdn_name.c) + * Reanimating Tombstones is described here: + * - TBD + * + * Author: Kamen Mazdrashki + */ + + +#include "includes.h" +#include "ldb_module.h" +#include "dsdb/samdb/samdb.h" +#include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_security.h" +#include "libcli/security/security.h" +#include "auth/auth.h" +#include "param/param.h" +#include "../libds/common/flags.h" +#include "dsdb/samdb/ldb_modules/util.h" + +struct tr_context { + + struct ldb_module *module; + struct ldb_request *req; + const struct dsdb_schema *schema; + + struct ldb_reply *search_res; + struct ldb_reply *search_res2; + + int (*step_fn)(struct tr_context *); +}; + +static struct tr_context *tr_init_context(struct ldb_module *module, + struct ldb_request *req) +{ + struct ldb_context *ldb; + struct tr_context *ac; + + ldb = ldb_module_get_ctx(module); + + ac = talloc_zero(req, struct tr_context); + if (ac == NULL) { + ldb_oom(ldb); + return NULL; + } + + ac->module = module; + ac->req = req; + ac->schema = dsdb_get_schema(ldb, ac); + + return ac; +} + + +static bool is_tombstone_reanimate_request(struct ldb_request *req, struct ldb_message_element **pel_dn) +{ + struct ldb_message_element *el_dn; + struct ldb_message_element *el_deleted; + + /* check distinguishedName requirement */ + el_dn = ldb_msg_find_element(req->op.mod.message, "distinguishedName"); + if (el_dn == NULL || el_dn->flags != LDB_FLAG_MOD_REPLACE) { + return false; + } + + /* check isDeleted requirement */ + el_deleted = ldb_msg_find_element(req->op.mod.message, "isDeleted"); + if (el_deleted == NULL || el_deleted->flags != LDB_FLAG_MOD_DELETE) { + return false; + } + + *pel_dn = el_dn; + return true; +} + +static int _tr_make_object_category(struct tr_context *ac, struct ldb_message *obj, + TALLOC_CTX *mem_ctx, const char **pobjectcategory) +{ + int ret; + struct ldb_context *ldb; + const struct dsdb_class *objectclass; + struct ldb_message_element *objectclass_element; + + ldb = ldb_module_get_ctx(ac->module); + + objectclass_element = ldb_msg_find_element(obj, "objectClass"); + if (!objectclass_element) { + ldb_asprintf_errstring(ldb, "tombstone_reanimate: Cannot add %s, no objectclass specified!", + ldb_dn_get_linearized(obj->dn)); + return LDB_ERR_OBJECT_CLASS_VIOLATION; + } + if (objectclass_element->num_values == 0) { + ldb_asprintf_errstring(ldb, "tombstone_reanimate: Cannot add %s, at least one (structural) objectclass has to be specified!", + ldb_dn_get_linearized(obj->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + /* Now do the sorting */ + ret = dsdb_sort_objectClass_attr(ldb, ac->schema, + objectclass_element, obj, + objectclass_element); + if (ret != LDB_SUCCESS) { + return ret; + } + + /* + * Get the new top-most structural object class and check for + * unrelated structural classes + */ + objectclass = dsdb_get_last_structural_class(ac->schema, + objectclass_element); + if (objectclass == NULL) { + ldb_asprintf_errstring(ldb, + "Failed to find a structural class for %s", + ldb_dn_get_linearized(obj->dn)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + *pobjectcategory = talloc_strdup(mem_ctx, objectclass->defaultObjectCategory); + if (*pobjectcategory == NULL) { + return ldb_oom(ldb); + } + + return LDB_SUCCESS; +} + +static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req) +{ + int ret; + struct ldb_context *ldb; + struct ldb_dn *dn_new; + struct ldb_dn *objectcategory; + struct ldb_message_element *el_dn = NULL; + struct ldb_message *msg; + struct ldb_result *res_obj; + struct tr_context *ac; + + ldb = ldb_module_get_ctx(module); + + ldb_debug(ldb, LDB_DEBUG_TRACE, "%s\n", __PRETTY_FUNCTION__); + + /* do not manipulate our control entries */ + if (ldb_dn_is_special(req->op.mod.message->dn)) { + return ldb_next_request(module, req); + } + + ac = tr_init_context(module, req); + if (ac == NULL) { + return ldb_operr(ldb); + } + + /* Check if this is a reanimate request */ + if (!is_tombstone_reanimate_request(req, &el_dn)) { + return ldb_next_request(module, req); + } + + /* Load original object */ + ret = dsdb_module_search_dn(module, req, &res_obj, req->op.mod.message->dn, NULL, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req); + if (ret != LDB_SUCCESS) { + return ldb_operr(ldb); + } + /* check if it a Deleted Object */ + if (!ldb_msg_find_attr_as_bool(res_obj->msgs[0], "isDeleted", false)) { + return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, "Trying to restore not deleted object\n"); + } + + /* Simple implementation */ + /* Rename request to modify distinguishedName */ + dn_new = ldb_dn_from_ldb_val(req, ldb, &el_dn->values[0]); + if (dn_new == NULL) { + return ldb_oom(ldb); + } + ret = dsdb_module_rename(module, req->op.mod.message->dn, dn_new, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req); + if (ret != LDB_SUCCESS) { + ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret)); + return ret; + } + + /* Modify request to: */ + msg = ldb_msg_copy_shallow(ac, req->op.mod.message); + if (msg == NULL) { + return ldb_module_oom(ac->module); + } + msg->dn = dn_new; + /* - delete isDeleted */ + ldb_msg_remove_attr(msg, "distinguishedName"); + + /* - restore objectCategory if not present */ + objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg, + "objectCategory"); + if (objectcategory == NULL) { + const char *value; + + ret = _tr_make_object_category(ac, res_obj->msgs[0], msg, &value); + if (ret != LDB_SUCCESS) { + return ret; + } + + ret = ldb_msg_add_string(msg, "objectCategory", value); + if (ret != LDB_SUCCESS) { + return ret; + } + msg->elements[msg->num_elements-1].flags = LDB_FLAG_MOD_ADD; + } + ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, req); + if (ret != LDB_SUCCESS) { + return ldb_operr(ldb); + } + + return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS); +} + + +static const struct ldb_module_ops ldb_reanimate_module_ops = { + .name = "tombstone_reanimate", + .modify = tombstone_reanimate_modify, +}; + +int ldb_tombstone_reanimate_module_init(const char *version) +{ + LDB_MODULE_CHECK_VERSION(version); + return ldb_register_module(&ldb_reanimate_module_ops); +} diff --git a/source4/dsdb/samdb/ldb_modules/wscript_build_server b/source4/dsdb/samdb/ldb_modules/wscript_build_server index 0307aeaf9f..ec2466e621 100755 --- a/source4/dsdb/samdb/ldb_modules/wscript_build_server +++ b/source4/dsdb/samdb/ldb_modules/wscript_build_server @@ -364,3 +364,13 @@ bld.SAMBA_MODULE('ldb_dirsync', internal_module=False, deps='talloc samba-security samdb DSDB_MODULE_HELPERS' ) + + +bld.SAMBA_MODULE('tombstone_reanimate', + source='tombstone_reanimate.c', + subsystem='ldb', + init_function='ldb_tombstone_reanimate_module_init', + module_init_name='ldb_init_module', + internal_module=False, + deps='talloc samba-util DSDB_MODULE_HELPERS' + ) From edc56c3be844eb2f10f6dc8c7c356dafa3b2974b Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 23 Oct 2014 08:15:23 +0200 Subject: [PATCH 51/97] s4-dsdb: Insert tombstone_reanimate module in ldb modules chain after objectclass Change-Id: Id9748f36f0aefe40b1894ecd2e5071e3b9c8a6d6 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/samba_dsdb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c index 086b11fb54..55ccb9a79a 100644 --- a/source4/dsdb/samdb/ldb_modules/samba_dsdb.c +++ b/source4/dsdb/samdb/ldb_modules/samba_dsdb.c @@ -273,6 +273,7 @@ static int samba_dsdb_init(struct ldb_module *module) NULL }; /* extended_dn_in or extended_dn_in_openldap goes here */ static const char *modules_list1a[] = {"objectclass", + "tombstone_reanimate", "descriptor", "acl", "aclread", From 3803f3b0d4684e33f760094c3659ab4c3e6e9328 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 26 Oct 2014 03:42:45 +0100 Subject: [PATCH 52/97] s4-dsdb-tests: Remove trailing ';' in deletetest.py Change-Id: Ic1ad6bbda55be56cbf7ae78a8ad988b8e479a40c Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index cb08db404d..8833d32ac0 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -528,10 +528,10 @@ def test_undelete_existing(self): def test_undelete_cross_nc(self): print "Cross NC undelete" - c1 = "cn=ldaptestcontainer," + self.base_dn; + c1 = "cn=ldaptestcontainer," + self.base_dn c2 = "cn=ldaptestcontainer2," + self.configuration_dn c3 = "cn=ldaptestcontainer," + self.configuration_dn - c4 = "cn=ldaptestcontainer2," + self.base_dn; + c4 = "cn=ldaptestcontainer2," + self.base_dn ldb.add({ "dn": c1, "objectclass": "container"}) From 856240b830980a1a603877dd053b5b1fd54f748d Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 26 Oct 2014 03:43:29 +0100 Subject: [PATCH 53/97] s4-dsdb-tests: Remove unused method get_ldap_connection() Change-Id: Ie50f77dbba724dbd3c2822de5c2cfff41016fac6 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index 8833d32ac0..c4b38223a3 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -395,19 +395,6 @@ def enable_recycle_bin(self): except LdbError, (num, _): self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - def get_ldb_connection(self, target_username, target_password): - creds_tmp = Credentials() - creds_tmp.set_username(target_username) - creds_tmp.set_password(target_password) - creds_tmp.set_domain(creds.get_domain()) - creds_tmp.set_realm(creds.get_realm()) - creds_tmp.set_workstation(creds.get_workstation()) - creds_tmp.set_gensec_features(creds_tmp.get_gensec_features() - | gensec.FEATURE_SEAL) - creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop - ldb_target = SamDB(url=ldaphost, credentials=creds_tmp, lp=lp) - return ldb_target - def undelete_deleted(self, olddn, newdn, samldb): msg = Message() msg.dn = Dn(ldb, olddn) From 680b63ab99be1f4045b37c951da2449626b6c9a6 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 26 Oct 2014 04:29:16 +0100 Subject: [PATCH 54/97] s4-dsdb-tests: Make unique object names to test with in deletetest This way we can re-run the test again and again Change-Id: I29bd878b77073d94a279c38bd0afc2f0befa6f9d Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index c4b38223a3..1d0848bea9 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -252,13 +252,21 @@ def test_all(self): print self.base_dn - usr1="cn=testuser,cn=users," + self.base_dn - usr2="cn=testuser2,cn=users," + self.base_dn - grp1="cn=testdelgroup1,cn=users," + self.base_dn - sit1="cn=testsite1,cn=sites," + self.configuration_dn - ss1="cn=NTDS Site Settings,cn=testsite1,cn=sites," + self.configuration_dn - srv1="cn=Servers,cn=testsite1,cn=sites," + self.configuration_dn - srv2="cn=TESTSRV,cn=Servers,cn=testsite1,cn=sites," + self.configuration_dn + # user current time in ms to make unique objects + import time + marker = str(int(round(time.time()*1000))) + usr1_name = "u_" + marker + usr2_name = "u2_" + marker + grp_name = "g1_" + marker + site_name = "s1_" + marker + + usr1 = "cn=%s,cn=users,%s" % (usr1_name, self.base_dn) + usr2 = "cn=%s,cn=users,%s" % (usr2_name, self.base_dn) + grp1 = "cn=%s,cn=users,%s" % (grp_name, self.base_dn) + sit1 = "cn=%s,cn=sites,%s" % (site_name, self.configuration_dn) + ss1 = "cn=NTDS Site Settings,cn=%s,cn=sites,%s" % (site_name, self.configuration_dn) + srv1 = "cn=Servers,cn=%s,cn=sites,%s" % (site_name, self.configuration_dn) + srv2 = "cn=TESTSRV,cn=Servers,cn=%s,cn=sites,%s" % (site_name, self.configuration_dn) delete_force(self.ldb, usr1) delete_force(self.ldb, usr2) @@ -272,19 +280,19 @@ def test_all(self): "dn": usr1, "objectclass": "user", "description": "test user description", - "samaccountname": "testuser"}) + "samaccountname": usr1_name}) self.ldb.add({ "dn": usr2, "objectclass": "user", "description": "test user 2 description", - "samaccountname": "testuser2"}) + "samaccountname": usr2_name}) self.ldb.add({ "dn": grp1, "objectclass": "group", "description": "test group", - "samaccountname": "testdelgroup1", + "samaccountname": grp_name, "member": [ usr1, usr2 ], "isDeleted": "FALSE" }) From 7976d75d6d61c85d058415f4e563312658f19972 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 26 Oct 2014 04:29:49 +0100 Subject: [PATCH 55/97] s4-dsdb-tests: Fix whitespace in deletetest.py Change-Id: Ic2924b0aa9cffd29fe0c857317ccb65ba53a1c21 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index 1d0848bea9..6170509c81 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -519,7 +519,7 @@ def test_undelete_existing(self): self.undelete_deleted(str(objDeleted1.dn), usr1, ldb) self.fail() except LdbError, (num, _): - self.assertEquals(num,ERR_ENTRY_ALREADY_EXISTS) + self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) def test_undelete_cross_nc(self): print "Cross NC undelete" From 8939d5ceecd713965e621da662eb49deec10c41d Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 26 Oct 2014 04:31:41 +0100 Subject: [PATCH 56/97] s4-dsdb: Return error codes as windows does for Tombstone reanimation Tested against Windows Server 2008 R2 In case we try to restore to already existing object, windows returns: LDB_ERR_ENTRY_ALREADY_EXISTS Otherwise it is: LDB_ERR_OPERATIONS_ERROR Change-Id: I6b5fea1e327416ccf5069d97a4a378a527a25f80 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 39da21d496..c5ef4586cb 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -206,6 +206,10 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ ret = dsdb_module_rename(module, req->op.mod.message->dn, dn_new, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req); if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret)); + if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) { + /* Windows returns Operations Error in case we can't rename the object */ + return LDB_ERR_OPERATIONS_ERROR; + } return ret; } From c0daf358547d747dcde8fd5d72917208530fc51d Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Mon, 27 Oct 2014 05:31:54 +0100 Subject: [PATCH 57/97] s4-dsdb: Initialize module context only we are to handle Tombstone request Change-Id: I73bd2043e96907e3d1a669bdbd943ddee1df8c0a Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index c5ef4586cb..67d9de77b6 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -177,16 +177,16 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ return ldb_next_request(module, req); } - ac = tr_init_context(module, req); - if (ac == NULL) { - return ldb_operr(ldb); - } - /* Check if this is a reanimate request */ if (!is_tombstone_reanimate_request(req, &el_dn)) { return ldb_next_request(module, req); } + ac = tr_init_context(module, req); + if (ac == NULL) { + return ldb_operr(ldb); + } + /* Load original object */ ret = dsdb_module_search_dn(module, req, &res_obj, req->op.mod.message->dn, NULL, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req); if (ret != LDB_SUCCESS) { From 57a413731b87502cb77cf91dabc51b31346d73fc Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 28 Oct 2014 06:10:56 +0100 Subject: [PATCH 58/97] s4-dsdb: Make most specific objectCategory for an object This is lightweight implementation and should be used on objects with already verified objectClass attribute value - eg. valid classes, sorted properly, etc. Checkout objectclass.c module for heavy weight implementation. Change-Id: Ifa7880d26246f67e2f982496fcc6c77e6648d56f Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/util.c | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c index a71c49b042..4c81a1dd32 100644 --- a/source4/dsdb/samdb/ldb_modules/util.c +++ b/source4/dsdb/samdb/ldb_modules/util.c @@ -1445,3 +1445,70 @@ int dsdb_fix_dn_rdncase(struct ldb_context *ldb, struct ldb_dn *dn) } return LDB_SUCCESS; } + +/** + * Make most specific objectCategory for the objectClass of passed object + * NOTE: In this implementation we count that it is called on already + * verified objectClass attribute value. See objectclass.c thorough + * implementation for all the magic that involves + * + * @param ldb ldb context + * @param schema cached schema for ldb. We may get it, but it is very time consuming. + * Hence leave the responsibility to the caller. + * @param obj AD object to determint objectCategory for + * @param mem_ctx Memory context - usually it is obj actually + * @param pobjectcategory location to store found objectCategory + * + * @return LDB_SUCCESS or error including out of memory error + */ +int dsdb_make_object_category(struct ldb_context *ldb, const struct dsdb_schema *schema, + struct ldb_message *obj, + TALLOC_CTX *mem_ctx, const char **pobjectcategory) +{ + const struct dsdb_class *objectclass; + struct ldb_message_element *objectclass_element; + struct dsdb_extended_dn_store_format *dn_format; + + objectclass_element = ldb_msg_find_element(obj, "objectClass"); + if (!objectclass_element) { + ldb_asprintf_errstring(ldb, "dsdb: Cannot add %s, no objectclass specified!", + ldb_dn_get_linearized(obj->dn)); + return LDB_ERR_OBJECT_CLASS_VIOLATION; + } + if (objectclass_element->num_values == 0) { + ldb_asprintf_errstring(ldb, "dsdb: Cannot add %s, at least one (structural) objectclass has to be specified!", + ldb_dn_get_linearized(obj->dn)); + return LDB_ERR_CONSTRAINT_VIOLATION; + } + + /* + * Get the new top-most structural object class and check for + * unrelated structural classes + */ + objectclass = dsdb_get_last_structural_class(schema, + objectclass_element); + if (objectclass == NULL) { + ldb_asprintf_errstring(ldb, + "Failed to find a structural class for %s", + ldb_dn_get_linearized(obj->dn)); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + + dn_format = talloc_get_type(ldb_get_opaque(ldb, DSDB_EXTENDED_DN_STORE_FORMAT_OPAQUE_NAME), + struct dsdb_extended_dn_store_format); + if (dn_format && dn_format->store_extended_dn_in_ldb == false) { + /* Strip off extended components */ + struct ldb_dn *dn = ldb_dn_new(mem_ctx, ldb, + objectclass->defaultObjectCategory); + *pobjectcategory = ldb_dn_alloc_linearized(mem_ctx, dn); + talloc_free(dn); + } else { + *pobjectcategory = talloc_strdup(mem_ctx, objectclass->defaultObjectCategory); + } + + if (*pobjectcategory == NULL) { + return ldb_oom(ldb); + } + + return LDB_SUCCESS; +} From 420020599c45c5c09c7bd769b4d47edb234939aa Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 28 Oct 2014 06:11:31 +0100 Subject: [PATCH 59/97] s4-dsdb: Make use dsdb_make_object_category() for objectCategory Change-Id: If65c54a653ad7078ca7a535b5c247db2746b5be7 Signed-off-by: Kamen Mazdrashki --- .../samdb/ldb_modules/tombstone_reanimate.c | 53 +------------------ 1 file changed, 1 insertion(+), 52 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 67d9de77b6..80a0de352c 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -106,57 +106,6 @@ static bool is_tombstone_reanimate_request(struct ldb_request *req, struct ldb_m return true; } -static int _tr_make_object_category(struct tr_context *ac, struct ldb_message *obj, - TALLOC_CTX *mem_ctx, const char **pobjectcategory) -{ - int ret; - struct ldb_context *ldb; - const struct dsdb_class *objectclass; - struct ldb_message_element *objectclass_element; - - ldb = ldb_module_get_ctx(ac->module); - - objectclass_element = ldb_msg_find_element(obj, "objectClass"); - if (!objectclass_element) { - ldb_asprintf_errstring(ldb, "tombstone_reanimate: Cannot add %s, no objectclass specified!", - ldb_dn_get_linearized(obj->dn)); - return LDB_ERR_OBJECT_CLASS_VIOLATION; - } - if (objectclass_element->num_values == 0) { - ldb_asprintf_errstring(ldb, "tombstone_reanimate: Cannot add %s, at least one (structural) objectclass has to be specified!", - ldb_dn_get_linearized(obj->dn)); - return LDB_ERR_CONSTRAINT_VIOLATION; - } - - /* Now do the sorting */ - ret = dsdb_sort_objectClass_attr(ldb, ac->schema, - objectclass_element, obj, - objectclass_element); - if (ret != LDB_SUCCESS) { - return ret; - } - - /* - * Get the new top-most structural object class and check for - * unrelated structural classes - */ - objectclass = dsdb_get_last_structural_class(ac->schema, - objectclass_element); - if (objectclass == NULL) { - ldb_asprintf_errstring(ldb, - "Failed to find a structural class for %s", - ldb_dn_get_linearized(obj->dn)); - return LDB_ERR_UNWILLING_TO_PERFORM; - } - - *pobjectcategory = talloc_strdup(mem_ctx, objectclass->defaultObjectCategory); - if (*pobjectcategory == NULL) { - return ldb_oom(ldb); - } - - return LDB_SUCCESS; -} - static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req) { int ret; @@ -228,7 +177,7 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ if (objectcategory == NULL) { const char *value; - ret = _tr_make_object_category(ac, res_obj->msgs[0], msg, &value); + ret = dsdb_make_object_category(ldb, ac->schema, res_obj->msgs[0], msg, &value); if (ret != LDB_SUCCESS) { return ret; } From 50a1bb1f00a4117196ac700140b5ef71567b717d Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 28 Oct 2014 15:03:59 +0100 Subject: [PATCH 60/97] TODO SIGN-OFF s4-dsdb: Define internal dsdb control to mark Tombstone reanimation requests Tombstone reanimation requries some special handling which is going to affect several modules. Most notably: - a bit different access checks in acl.c - restore certain attributes during modify requests in samldb.c Change-Id: Ief4f7dabbbdc2570924fae48c30ac9c531a701f4 TODO Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/samdb.h | 8 ++++++++ source4/setup/schema_samba4.ldif | 1 + 2 files changed, 9 insertions(+) diff --git a/source4/dsdb/samdb/samdb.h b/source4/dsdb/samdb/samdb.h index 7f77d4e382..19d4fd7706 100644 --- a/source4/dsdb/samdb/samdb.h +++ b/source4/dsdb/samdb/samdb.h @@ -135,6 +135,14 @@ struct dsdb_control_password_change { */ #define DSDB_CONTROL_SEC_DESC_PROPAGATION_OID "1.3.6.1.4.1.7165.4.3.21" +/* + * Internal control to mark requests as being part of Tombstone restoring + * procedure - it requires slightly special behavior like: + * - a bit different security checks + * - restoring certain attributes to their default values, etc + */ +#define DSDB_CONTROL_RESTORE_TOMBSTONE_OID "1.3.6.1.4.1.7165.4.3.24" + #define DSDB_EXTENDED_REPLICATED_OBJECTS_OID "1.3.6.1.4.1.7165.4.4.1" struct dsdb_extended_replicated_object { struct ldb_message *msg; diff --git a/source4/setup/schema_samba4.ldif b/source4/setup/schema_samba4.ldif index 94aedb099d..04a0262355 100644 --- a/source4/setup/schema_samba4.ldif +++ b/source4/setup/schema_samba4.ldif @@ -197,6 +197,7 @@ #Allocated: DSDB_CONTROL_DBCHECK_MODIFY_RO_REPLICA 1.3.6.1.4.1.7165.4.3.19.1 #Allocated: DSDB_CONTROL_PASSWORD_BYPASS_LAST_SET_OID 1.3.6.1.4.1.7165.4.3.20 #Allocated: DSDB_CONTROL_SEC_DESC_PROPAGATION_OID 1.3.6.1.4.1.7165.4.3.21 +#Allocated: DSDB_CONTROL_RESTORE_TOMBSTONE_OID 1.3.6.1.4.1.7165.4.3.24 # Extended 1.3.6.1.4.1.7165.4.4.x #Allocated: DSDB_EXTENDED_REPLICATED_OBJECTS_OID 1.3.6.1.4.1.7165.4.4.1 From 266695fc4bf4f41a12f22691e686eb93a5d14892 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 2 Nov 2014 17:11:20 +0100 Subject: [PATCH 61/97] s4-tests: Print out what the error is in delete_force() Change-Id: Iaa631179dc79fa756416be8eaf8c55e3b0c1a29f Signed-off-by: Kamen Mazdrashki --- python/samba/tests/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py index cca82bbd67..6cbc81d7fc 100644 --- a/python/samba/tests/__init__.py +++ b/python/samba/tests/__init__.py @@ -238,5 +238,5 @@ def connect_samdb_ex(samdb_url, lp=None, session_info=None, credentials=None, def delete_force(samdb, dn): try: samdb.delete(dn) - except ldb.LdbError, (num, _): - assert(num == ldb.ERR_NO_SUCH_OBJECT) + except ldb.LdbError, (num, errstr): + assert num == ldb.ERR_NO_SUCH_OBJECT, "ldb.delete() failed: %s" % errstr From cc19ee2e32bfef79f035bad42c8294a03ab4ec6c Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Mon, 3 Nov 2014 04:58:20 +0100 Subject: [PATCH 62/97] s4-dsdb: Add documentation link for Tombstone Reanimation Change-Id: Ib779c8b0839889371f25ad5751c9cda1a510eb54 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 80a0de352c..819e73e402 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -34,7 +34,7 @@ * * Usually we don't allow distinguishedName modifications (see rdn_name.c) * Reanimating Tombstones is described here: - * - TBD + * - http://msdn.microsoft.com/en-us/library/cc223467.aspx * * Author: Kamen Mazdrashki */ From b3477a3e536f8f9890d7bbd9888860d8188180de Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 4 Nov 2014 04:10:16 +0100 Subject: [PATCH 63/97] s4-dsdb: Implement rename/modify requests as local for the module The aim is for us to be able to fine tune the implementation and also add custom LDAP controls to mark all requests as being part of Reanimation procedure Change-Id: I9f1c04cd21bf032146eb2626d6495711fcadf10c Signed-off-by: Kamen Mazdrashki --- .../samdb/ldb_modules/tombstone_reanimate.c | 98 ++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 819e73e402..5b2b06858f 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -106,6 +106,100 @@ static bool is_tombstone_reanimate_request(struct ldb_request *req, struct ldb_m return true; } +/** + * Local rename implementation based on dsdb_module_rename() + * so we could fine tune it and add more controls + */ +static int _tr_do_rename(struct ldb_module *module, struct ldb_request *parent_req, + struct ldb_dn *dn_from, struct ldb_dn *dn_to) +{ + int ret; + struct ldb_request *req; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(parent_req); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + + ret = ldb_build_rename_req(&req, ldb, tmp_ctx, + dn_from, + dn_to, + NULL, + res, + ldb_modify_default_callback, + parent_req); + LDB_REQ_SET_LOCATION(req); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + ret = dsdb_request_add_controls(req, DSDB_SEARCH_SHOW_DELETED); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + /* + * Run request from the top module + * so we get show_deleted control OID resolved + */ + ret = ldb_request(ldb_module_get_ctx(module), req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} + +/** + * Local rename implementation based on dsdb_module_modify() + * so we could fine tune it and add more controls + */ +static int _tr_do_modify(struct ldb_module *module, struct ldb_request *parent_req, struct ldb_message *msg) +{ + int ret; + struct ldb_request *mod_req; + struct ldb_context *ldb = ldb_module_get_ctx(module); + TALLOC_CTX *tmp_ctx = talloc_new(parent_req); + struct ldb_result *res; + + res = talloc_zero(tmp_ctx, struct ldb_result); + if (!res) { + talloc_free(tmp_ctx); + return ldb_oom(ldb_module_get_ctx(module)); + } + + ret = ldb_build_mod_req(&mod_req, ldb, tmp_ctx, + msg, + NULL, + res, + ldb_modify_default_callback, + parent_req); + LDB_REQ_SET_LOCATION(mod_req); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + + /* Run request from Next module */ + ret = ldb_next_request(module, mod_req); + if (ret == LDB_SUCCESS) { + ret = ldb_wait(mod_req->handle, LDB_WAIT_ALL); + } + + talloc_free(tmp_ctx); + return ret; +} + +/** + * Handle special LDAP modify request to restore deleted objects + */ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_request *req) { int ret; @@ -152,7 +246,7 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ if (dn_new == NULL) { return ldb_oom(ldb); } - ret = dsdb_module_rename(module, req->op.mod.message->dn, dn_new, DSDB_FLAG_TOP_MODULE | DSDB_SEARCH_SHOW_DELETED, req); + ret = _tr_do_rename(module, req, req->op.mod.message->dn, dn_new); if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret)); if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) { @@ -188,7 +282,7 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ } msg->elements[msg->num_elements-1].flags = LDB_FLAG_MOD_ADD; } - ret = dsdb_module_modify(module, msg, DSDB_FLAG_NEXT_MODULE, req); + ret = _tr_do_modify(module, req, msg); if (ret != LDB_SUCCESS) { return ldb_operr(ldb); } From 4abb1a76fb37e3642c1823f691bc77f9fd3984fa Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 4 Nov 2014 04:17:35 +0100 Subject: [PATCH 64/97] s4-dsdb: Mark request during Tombstone reanimation with custom LDAP control We are going to need this so that underlying modules (acl.c) can treat those requests properly Change-Id: I6c12069aa6e7e01197dddda6c610d930d3fd9cb0 Signed-off-by: Kamen Mazdrashki --- .../dsdb/samdb/ldb_modules/tombstone_reanimate.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 5b2b06858f..6d72f422aa 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -144,6 +144,13 @@ static int _tr_do_rename(struct ldb_module *module, struct ldb_request *parent_r return ret; } + /* mark request as part of Tombstone reanimation */ + ret = ldb_request_add_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID, false, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + /* * Run request from the top module * so we get show_deleted control OID resolved @@ -187,6 +194,13 @@ static int _tr_do_modify(struct ldb_module *module, struct ldb_request *parent_r return ret; } + /* mark request as part of Tombstone reanimation */ + ret = ldb_request_add_control(mod_req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID, false, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + /* Run request from Next module */ ret = ldb_next_request(module, mod_req); if (ret == LDB_SUCCESS) { From b68c32ca540c5e4954538d01cf57f8f0a248cbe9 Mon Sep 17 00:00:00 2001 From: Nadezhda Ivanova Date: Tue, 4 Nov 2014 20:08:58 +0200 Subject: [PATCH 65/97] TODO SIGN-OFF s4-dsdb: Tests for security checks on undelete operation Implemented according to MS-ADTS 3.1.1.5.3.7.1. Unfortunately it appears LC is also necessary, and it is not granted by default to anyone but System and Administrator, so tests had to be done negatively TODO Signed-off-by: Nadezhda Ivanova TODO: Changed to not override passed in controls in sd_utils.py Change-Id: Ic03b8fc4e222e7842ec8a9645a1bb33e7df9c438 --- python/samba/sd_utils.py | 8 +- source4/dsdb/tests/python/acl.py | 132 ++++++++++++++++++++++++++++++- 2 files changed, 135 insertions(+), 5 deletions(-) diff --git a/python/samba/sd_utils.py b/python/samba/sd_utils.py index ded9bfc192..7592a2982a 100644 --- a/python/samba/sd_utils.py +++ b/python/samba/sd_utils.py @@ -62,7 +62,7 @@ def get_object_sid(self, object_dn): def dacl_add_ace(self, object_dn, ace): """Add an ACE to an objects security descriptor """ - desc = self.read_sd_on_dn(object_dn) + desc = self.read_sd_on_dn(object_dn,["show_deleted:1"]) desc_sddl = desc.as_sddl(self.domain_sid) if ace in desc_sddl: return @@ -71,10 +71,10 @@ def dacl_add_ace(self, object_dn, ace): desc_sddl[desc_sddl.index("("):]) else: desc_sddl = desc_sddl + ace - self.modify_sd_on_dn(object_dn, desc_sddl) + self.modify_sd_on_dn(object_dn, desc_sddl, ["show_deleted:1"]) - def get_sd_as_sddl(self, object_dn, controls=None): + def get_sd_as_sddl(self, object_dn, controls=[]): """Return object nTSecutiryDescriptor in SDDL format """ - desc = self.read_sd_on_dn(object_dn, controls=controls) + desc = self.read_sd_on_dn(object_dn, controls + ["show_deleted:1"]) return desc.as_sddl(self.domain_sid) diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py index 4acc12349f..d8e8962993 100755 --- a/source4/dsdb/tests/python/acl.py +++ b/source4/dsdb/tests/python/acl.py @@ -20,7 +20,7 @@ from ldb import ERR_CONSTRAINT_VIOLATION from ldb import ERR_OPERATIONS_ERROR from ldb import Message, MessageElement, Dn -from ldb import FLAG_MOD_REPLACE, FLAG_MOD_ADD +from ldb import FLAG_MOD_REPLACE, FLAG_MOD_ADD, FLAG_MOD_DELETE from samba.dcerpc import security, drsuapi, misc from samba.auth import system_session @@ -1637,6 +1637,136 @@ def test_ntSecurityDescriptor(self): self.assertEqual(len(res),1) self.assertTrue("nTSecurityDescriptor" in res[0].keys()) +class AclUndeleteTests(AclTests): + + def setUp(self): + super(AclUndeleteTests, self).setUp() + self.regular_user = "undeleter1" + self.ou1 = "OU=undeleted_ou," + self.testuser1 = "to_be_undeleted1" + self.testuser2 = "to_be_undeleted2" + self.testuser3 = "to_be_undeleted3" + self.testuser4 = "to_be_undeleted4" + self.testuser5 = "to_be_undeleted5" + self.testuser6 = "to_be_undeleted6" + + self.new_dn_ou = "CN="+ self.testuser4 + "," + self.ou1 + self.base_dn + + # Create regular user + self.testuser1_dn = self.get_user_dn(self.testuser1) + self.testuser2_dn = self.get_user_dn(self.testuser2) + self.testuser3_dn = self.get_user_dn(self.testuser3) + self.testuser4_dn = self.get_user_dn(self.testuser4) + self.testuser5_dn = self.get_user_dn(self.testuser5) + self.deleted_dn1 = self.create_delete_user(self.testuser1) + self.deleted_dn2 = self.create_delete_user(self.testuser2) + self.deleted_dn3 = self.create_delete_user(self.testuser3) + self.deleted_dn4 = self.create_delete_user(self.testuser4) + self.deleted_dn5 = self.create_delete_user(self.testuser5) + + self.ldb_admin.create_ou(self.ou1 + self.base_dn) + + self.ldb_admin.newuser(self.regular_user, self.user_pass) + self.ldb_admin.add_remove_group_members("Domain Admins", [self.regular_user], + add_members_operation=True) + self.ldb_user = self.get_ldb_connection(self.regular_user, self.user_pass) + self.sid = self.sd_utils.get_object_sid(self.get_user_dn(self.regular_user)) + + def tearDown(self): + super(AclUndeleteTests, self).tearDown() + delete_force(self.ldb_admin, self.get_user_dn(self.regular_user)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser1)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser2)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser3)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser4)) + delete_force(self.ldb_admin, self.get_user_dn(self.testuser5)) + delete_force(self.ldb_admin, self.new_dn_ou) + delete_force(self.ldb_admin, self.ou1 + self.base_dn) + + def GUID_string(self, guid): + return ldb.schema_format_value("objectGUID", guid) + + def create_delete_user(self, new_user): + self.ldb_admin.newuser(new_user, self.user_pass) + + res = self.ldb_admin.search(expression="(objectClass=*)", + base=self.get_user_dn(new_user), + scope=SCOPE_BASE, + controls=["show_deleted:1"]) + guid = res[0]["objectGUID"][0] + self.ldb_admin.delete(self.get_user_dn(new_user)) + res = self.ldb_admin.search(base="" % self.GUID_string(guid), + scope=SCOPE_BASE, controls=["show_deleted:1"]) + self.assertEquals(len(res), 1) + return str(res[0].dn) + + def undelete_deleted(self, olddn, newdn): + msg = Message() + msg.dn = Dn(self.ldb_user, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + res = self.ldb_user.modify(msg, ["show_recycled:1"]) + + def undelete_deleted_with_mod(self, olddn, newdn): + msg = Message() + msg.dn = Dn(ldb, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") + res = self.ldb_user.modify(msg, ["show_deleted:1"]) + + def test_undelete(self): + # it appears the user has to have LC on the old parent to be able to move the object + # otherwise we get no such object. Since only System can modify the SD on deleted object + # we cannot grant this permission via LDAP, and this leaves us with "negative" tests at the moment + + # deny write property on rdn, should fail + mod = "(OD;;WP;bf967a0e-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn1, mod) + try: + self.undelete_deleted(self.deleted_dn1, self.testuser1_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) + + # seems that permissions on isDeleted and distinguishedName are irrelevant + mod = "(OD;;WP;bf96798f-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn2, mod) + mod = "(OD;;WP;bf9679e4-0de6-11d0-a285-00aa003049e2;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn2, mod) + self.undelete_deleted(self.deleted_dn2, self.testuser2_dn) + + # attempt undelete with simultanious addition of url, WP to which is denied + mod = "(OD;;WP;9a9a0221-4a5b-11d1-a9c3-0000f80367c1;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn3, mod) + try: + self.undelete_deleted_with_mod(self.deleted_dn3, self.testuser3_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) + + # undelete in an ou, in which we have no right to create children + mod = "(D;;CC;;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.ou1 + self.base_dn, mod) + try: + self.undelete_deleted(self.deleted_dn4, self.new_dn_ou) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) + + # delete is not required + mod = "(D;;SD;;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.deleted_dn5, mod) + self.undelete_deleted(self.deleted_dn5, self.testuser5_dn) + + # deny Reanimate-Tombstone, should fail + mod = "(OD;;CR;45ec5156-db7e-47bb-b53f-dbeb2d03c40f;;%s)" % str(self.sid) + self.sd_utils.dacl_add_ace(self.base_dn, mod) + try: + self.undelete_deleted(self.deleted_dn4, self.testuser4_dn) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_INSUFFICIENT_ACCESS_RIGHTS) class AclSPNTests(AclTests): From 677bfffdbd1e5661bb732a607a049308114ff214 Mon Sep 17 00:00:00 2001 From: Nadezhda Ivanova Date: Tue, 4 Nov 2014 20:21:57 +0200 Subject: [PATCH 66/97] TODO SIGN-OFF s4-dsdb: Implementation of access checks on a undelete operation Special Reanimate-Tombstone access right is required, as well as most of the checks on a standard rename. Change-Id: Idae5101a5df4cd0d54fe4ab2f7e5ad7fc1c23648 TODO Signed-off-by: Nadezhda Ivanova --- librpc/idl/security.idl | 1 + source4/dsdb/samdb/ldb_modules/acl.c | 97 ++++++++++++++++++++++------ 2 files changed, 80 insertions(+), 18 deletions(-) diff --git a/librpc/idl/security.idl b/librpc/idl/security.idl index eb80a869b0..39a0ec25a1 100644 --- a/librpc/idl/security.idl +++ b/librpc/idl/security.idl @@ -682,6 +682,7 @@ interface security const string GUID_DRS_RO_REPL_SECRET_SYNC = "1131f6ae-9c07-11d1-f79f-00c04fc2dcd2"; const string GUID_DRS_USER_CHANGE_PASSWORD = "ab721a53-1e2f-11d0-9819-00aa0040529b"; const string GUID_DRS_FORCE_CHANGE_PASSWORD = "00299570-246d-11d0-a768-00aa006e0529"; + const string GUID_DRS_REANIMATE_TOMBSTONE = "45ec5156-db7e-47bb-b53f-dbeb2d03c40f"; /***************************************************************/ /* validated writes guids */ diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c index e75fb2a3a3..6d34c3dac8 100644 --- a/source4/dsdb/samdb/ldb_modules/acl.c +++ b/source4/dsdb/samdb/ldb_modules/acl.c @@ -1028,6 +1028,7 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) struct security_descriptor *sd; struct dom_sid *sid = NULL; struct ldb_control *as_system; + struct ldb_control *is_undelete; bool userPassword; TALLOC_CTX *tmp_ctx; const struct ldb_message *msg = req->op.mod.message; @@ -1047,6 +1048,8 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) as_system->critical = 0; } + is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID); + /* Don't print this debug statement if elements[0].name is going to be NULL */ if (msg->num_elements > 0) { DEBUG(10, ("ldb:acl_modify: %s\n", msg->elements[0].name)); @@ -1193,6 +1196,14 @@ static int acl_modify(struct ldb_module *module, struct ldb_request *req) if (ret != LDB_SUCCESS) { goto fail; } + } else if (is_undelete != NULL && (ldb_attr_cmp("isDeleted", el->name) == 0)) { + /* + * in case of undelete op permissions on + * isDeleted are irrelevant and + * distinguishedName is removed by the + * tombstone_reanimate module + */ + continue; } else { ret = acl_check_access_on_attribute(module, tmp_ctx, @@ -1346,6 +1357,42 @@ static int acl_delete(struct ldb_module *module, struct ldb_request *req) return ldb_next_request(module, req); } +static int acl_check_reanimate_tombstone(TALLOC_CTX *mem_ctx, + struct ldb_module *module, + struct ldb_request *req, + struct ldb_dn *nc_root) +{ + int ret; + struct ldb_result *acl_res; + struct security_descriptor *sd = NULL; + struct dom_sid *sid = NULL; + static const char *acl_attrs[] = { + "nTSecurityDescriptor", + "objectClass", + "objectSid", + NULL + }; + + ret = dsdb_module_search_dn(module, mem_ctx, &acl_res, + nc_root, acl_attrs, + DSDB_FLAG_NEXT_MODULE | + DSDB_FLAG_AS_SYSTEM | + DSDB_SEARCH_SHOW_RECYCLED, req); + if (ret != LDB_SUCCESS) { + DEBUG(10,("acl: failed to find object %s\n", + ldb_dn_get_linearized(nc_root))); + return ret; + } + + ret = dsdb_get_sd_from_ldb_message(mem_ctx, req, acl_res->msgs[0], &sd); + sid = samdb_result_dom_sid(mem_ctx, acl_res->msgs[0], "objectSid"); + if (ret != LDB_SUCCESS || !sd) { + return ldb_operr(ldb_module_get_ctx(module)); + } + return acl_check_extended_right(mem_ctx, sd, acl_user_token(module), + GUID_DRS_REANIMATE_TOMBSTONE, + SEC_ADS_CONTROL_ACCESS, sid); +} static int acl_rename(struct ldb_module *module, struct ldb_request *req) { @@ -1361,6 +1408,7 @@ static int acl_rename(struct ldb_module *module, struct ldb_request *req) struct ldb_result *acl_res; struct ldb_dn *nc_root; struct ldb_control *as_system; + struct ldb_control *is_undelete; TALLOC_CTX *tmp_ctx; const char *rdn_name; static const char *acl_attrs[] = { @@ -1413,6 +1461,17 @@ static int acl_rename(struct ldb_module *module, struct ldb_request *req) return ldb_module_done(req, NULL, NULL, LDB_ERR_UNWILLING_TO_PERFORM); } + + /* special check for undelete operation */ + is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID); + if (is_undelete != NULL) { + is_undelete->critical = 0; + ret = acl_check_reanimate_tombstone(tmp_ctx, module, req, nc_root); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + } talloc_free(nc_root); /* Look for the parent */ @@ -1526,25 +1585,27 @@ static int acl_rename(struct ldb_module *module, struct ldb_request *req) } /* do we have delete object on the object? */ - ret = acl_check_access_on_objectclass(module, tmp_ctx, sd, sid, - SEC_STD_DELETE, - objectclass); - if (ret == LDB_SUCCESS) { - talloc_free(tmp_ctx); - return ldb_next_request(module, req); - } - /* what about delete child on the current parent */ - ret = dsdb_module_check_access_on_dn(module, req, oldparent, - SEC_ADS_DELETE_CHILD, - &objectclass->schemaIDGUID, - req); - if (ret != LDB_SUCCESS) { - ldb_asprintf_errstring(ldb_module_get_ctx(module), - "acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)); - talloc_free(tmp_ctx); - return ldb_module_done(req, NULL, NULL, ret); + /* this access is not necessary for undelete ops */ + if (is_undelete == NULL) { + ret = acl_check_access_on_objectclass(module, tmp_ctx, sd, sid, + SEC_STD_DELETE, + objectclass); + if (ret == LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ldb_next_request(module, req); + } + /* what about delete child on the current parent */ + ret = dsdb_module_check_access_on_dn(module, req, oldparent, + SEC_ADS_DELETE_CHILD, + &objectclass->schemaIDGUID, + req); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb_module_get_ctx(module), + "acl:access_denied renaming %s", ldb_dn_get_linearized(req->op.rename.olddn)); + talloc_free(tmp_ctx); + return ldb_module_done(req, NULL, NULL, ret); + } } - talloc_free(tmp_ctx); return ldb_next_request(module, req); From 6ff9561c622910d8b248b4be90bae5ba426f7d41 Mon Sep 17 00:00:00 2001 From: Nadezhda Ivanova Date: Tue, 4 Nov 2014 20:24:11 +0200 Subject: [PATCH 67/97] s4-dsdb: Some minor fixes in tombstone_reanimate, to make it work with acl Change-Id: Idad221c7ecf778fd24f6017bb4c6eacac541086a Signed-off-by: Nadezhda Ivanova --- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 6d72f422aa..89d3a8aa91 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -138,7 +138,7 @@ static int _tr_do_rename(struct ldb_module *module, struct ldb_request *parent_r return ret; } - ret = dsdb_request_add_controls(req, DSDB_SEARCH_SHOW_DELETED); + ret = ldb_request_add_control(req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return ret; @@ -155,7 +155,7 @@ static int _tr_do_rename(struct ldb_module *module, struct ldb_request *parent_r * Run request from the top module * so we get show_deleted control OID resolved */ - ret = ldb_request(ldb_module_get_ctx(module), req); + ret = ldb_next_request(module, req); if (ret == LDB_SUCCESS) { ret = ldb_wait(req->handle, LDB_WAIT_ALL); } @@ -263,7 +263,7 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ ret = _tr_do_rename(module, req, req->op.mod.message->dn, dn_new); if (ret != LDB_SUCCESS) { ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret)); - if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS) { + if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) { /* Windows returns Operations Error in case we can't rename the object */ return LDB_ERR_OPERATIONS_ERROR; } @@ -298,7 +298,7 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ } ret = _tr_do_modify(module, req, msg); if (ret != LDB_SUCCESS) { - return ldb_operr(ldb); + return ret; } return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS); From c81fa23c5ce703e93a66a464c20773ecce221c5c Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Wed, 5 Nov 2014 06:26:25 +0100 Subject: [PATCH 68/97] TODO s4-dsdb-test: Implement samdb_connect_env() to rely solely on environment this is to help me port Python tests to be more Unit test alike and remove all global handling Starting from a new test suite - tombstone_reanimation.py TODO: I don't really like this way of running things, as the environment variables may not show up in the failed command --abartlet Change-Id: I29445c42cdcafede3897c8dd1f1529222a74afc9 Signed-off-by: Kamen Mazdrashki --- python/samba/tests/__init__.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py index 6cbc81d7fc..dbd07c0a77 100644 --- a/python/samba/tests/__init__.py +++ b/python/samba/tests/__init__.py @@ -23,6 +23,7 @@ import samba.auth from samba import param from samba.samdb import SamDB +from samba import credentials import subprocess import tempfile @@ -235,6 +236,23 @@ def connect_samdb_ex(samdb_url, lp=None, session_info=None, credentials=None, return (sam_db, res[0]) +def connect_samdb_env(env_url, env_username, env_password, lp=None): + """Connect to SamDB by getting URL and Credentials from environment + + :param env_url: Environment variable name to get lsb url from + :param env_username: Username environment variable + :param env_password: Password environment variable + :return: sam_db_connection + """ + samdb_url = env_get_var_value(env_url) + creds = credentials.Credentials() + if lp: + creds.guess(lp) + creds.set_username(env_get_var_value(env_username)) + creds.set_password(env_get_var_value(env_password)) + return connect_samdb(samdb_url, credentials=creds, lp=lp) + + def delete_force(samdb, dn): try: samdb.delete(dn) From 7abbf2ada2560ef819406befbdb7b5c1a067c673 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Wed, 5 Nov 2014 06:27:22 +0100 Subject: [PATCH 69/97] s4-dsdb-test: Initial implementation for Tombstone restore test suite Change-Id: Ib35ff930b6e7cee14317328b6fe25b59eec5262c Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 source4/dsdb/tests/python/tombstone_reanimation.py diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py new file mode 100644 index 0000000000..9c9f5e46ff --- /dev/null +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# Tombstone reanimation tests +# +# Copyright (C) Kamen Mazdrashki 2014 +# +# 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 . + +import sys +import optparse +import unittest + +sys.path.insert(0, "bin/python") +import samba +samba.ensure_external_module("testtools", "testtools") +samba.ensure_external_module("subunit", "subunit/python") + +import samba.tests +import samba.getopt as options +from ldb import (SCOPE_BASE, FLAG_MOD_DELETE, FLAG_MOD_REPLACE, Dn, Message, MessageElement) + + +class RestoredObjectAttributesBaseTestCase(samba.tests.TestCase): + """ verify Samba restores required attributes when + user restores a Deleted object + """ + + def setUp(self): + super(RestoredObjectAttributesBaseTestCase, self).setUp() + # load LoadParm + lp = options.SambaOptions(optparse.OptionParser()).get_loadparm() + self.samdb = samba.tests.connect_samdb_env("DC_SERVER", "DC_USERNAME", "DC_PASSWORD", lp=lp) + self.base_dn = self.samdb.domain_dn() + self.schema_dn = self.samdb.get_schema_basedn().get_linearized() + # Get the old "dSHeuristics" if it was set + self.dsheuristics = self.samdb.get_dsheuristics() + # Set the "dSHeuristics" to activate the correct "userPassword" behaviour + self.samdb.set_dsheuristics("000000001") + # Get the old "minPwdAge" + self.minPwdAge = self.samdb.get_minPwdAge() + # Set it temporary to "0" + self.samdb.set_minPwdAge("0") + + def tearDown(self): + super(RestoredObjectAttributesBaseTestCase, self).tearDown() + # Reset the "dSHeuristics" as they were before + self.samdb.set_dsheuristics(self.dsheuristics) + # Reset the "minPwdAge" as it was before + self.samdb.set_minPwdAge(self.minPwdAge) + + def GUID_string(self, guid): + return self.samdb.schema_format_value("objectGUID", guid) + + def search_guid(self, guid): + res = self.samdb.search(base="" % self.GUID_string(guid), + scope=SCOPE_BASE, controls=["show_deleted:1"]) + self.assertEquals(len(res), 1) + return res[0] + + def search_dn(self, dn): + res = self.samdb.search(expression="(objectClass=*)", + base=dn, + scope=SCOPE_BASE, + controls=["show_recycled:1"]) + self.assertEquals(len(res), 1) + return res[0] + + def _class_must_attrs(self, class_name): + res = self.samdb.search(self.schema_dn, + expression="(&(objectClass=classSchema)(lDAPDisplayName=%s))" % class_name, + attrs=["subClassOf", "mustContain", "systemMustContain", + "auxiliaryClass", "systemAuxiliaryClass"]) + self.assertEqual(len(res), 1) + attr_set = set() + # make the list from immediate class + if "mustContain" in res[0]: + attr_set |= set([attr for attr in res[0]["mustContain"]]) + if "systemMustContain" in res[0]: + attr_set |= set([attr for attr in res[0]["systemMustContain"]]) + # now trace Auxiliary classes + if "auxiliaryClass" in res[0]: + for aux_class in res[0]["auxiliaryClass"]: + attr_set |= self.make_class_all_must_list(aux_class) + if "systemAuxiliaryClass" in res[0]: + for aux_class in res[0]["systemAuxiliaryClass"]: + attr_set |= self.make_class_all_must_list(aux_class) + return str(res[0]["subClassOf"]), attr_set + + def make_class_all_must_list(self, class_name): + (parent_class, attr_list) = self._class_must_attrs(class_name) + sub_set = set() + if class_name != "top": + sub_set = self.make_class_all_must_list(parent_class) + attr_list |= sub_set + return attr_list + + +if __name__ == '__main__': + unittest.main() From 2755a4cca8b47e85bd332e274f93fc3361e340fb Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 6 Nov 2014 03:01:54 +0100 Subject: [PATCH 70/97] s4-dsdb-test: Use case insensitive comparison for DNs in undelete test Change-Id: I4a009bb7ed58ab857ac74a235bb5f580911f0d92 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index 6170509c81..67b37a70e2 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -434,7 +434,7 @@ def test_undelete(self): objDeleted1 = self.search_guid(guid1) self.undelete_deleted(str(objDeleted1.dn), usr1, ldb) objLive2 = self.search_dn(usr1) - self.assertEqual(str(objLive2.dn),str(objLive1.dn)) + self.assertEqual(str(objLive2.dn).lower(),str(objLive1.dn).lower()) delete_force(self.ldb, usr1) def test_rename(self): From 4b1dfb0d150b678e280706a0ba398bcdd6f97845 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 6 Nov 2014 03:03:56 +0100 Subject: [PATCH 71/97] s4-dsdb-test: Restore User test case implementation Change-Id: Iffd42b5b2fe5aef7d7381c91a6c635436b5e2e99 Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 9c9f5e46ff..66a82aa786 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -105,6 +105,42 @@ def make_class_all_must_list(self, class_name): attr_list |= sub_set return attr_list + def test_restore_user(self): + print "Test restored user attributes" + username = "restore_user" + usr_dn = "cn=%s,cn=users,%s" % (username, self.base_dn) + samba.tests.delete_force(self.samdb, usr_dn) + self.samdb.add({ + "dn": usr_dn, + "objectClass": "user", + "description": "test user description", + "sAMAccountName": username}) + obj = self.search_dn(usr_dn) + guid = obj["objectGUID"][0] + self.samdb.delete(usr_dn) + obj_del = self.search_guid(guid) + # restore the user + msg = Message() + msg.dn = obj_del.dn + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([usr_dn], FLAG_MOD_REPLACE, "distinguishedName") + # add some attributes + # strip off "sAMAccountType" + # for attr in ['dSCorePropagationData', 'primaryGroupID', 'badPwdCount', 'logonCount', 'countryCode', 'pwdLastSet', 'codePage', 'lastLogon', 'adminCount', 'accountExpires', 'operatorCount', 'badPasswordTime', 'lastLogoff']: + # msg[attr] = "0" + self.samdb.modify(msg, ["show_deleted:1"]) + # find restored object and check attributes + obj_restore = self.search_guid(guid) + keys_restored = obj_restore.keys() + restored_user_attr = ['dn', 'objectClass', 'cn', 'distinguishedName', 'instanceType', 'whenCreated', + 'whenChanged', 'uSNCreated', 'uSNChanged', 'name', 'objectGUID', 'userAccountControl', + 'badPwdCount', 'codePage', 'countryCode', 'badPasswordTime', 'lastLogoff', 'lastLogon', + 'pwdLastSet', 'primaryGroupID', 'operatorCount', 'objectSid', 'adminCount', + 'accountExpires', 'logonCount', 'sAMAccountName', 'sAMAccountType', 'lastKnownParent', + 'objectCategory', 'dSCorePropagationData'] + print set(restored_user_attr) - set(keys_restored) + pass + if __name__ == '__main__': unittest.main() From d01b9a5e91ab6ab70949e20b8bf04bf85d1ffa29 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 6 Nov 2014 04:10:42 +0100 Subject: [PATCH 72/97] s4-dsdb-test: Fix Undelete tests after subunit upgrade work Change-Id: I4712a2a2163a57fde037511afcc1cb7bee05f12e Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 62 ++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index 67b37a70e2..60bb689485 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -394,70 +394,70 @@ def setUp(self): def enable_recycle_bin(self): msg = Message() - msg.dn = Dn(ldb, "") + msg.dn = Dn(self.ldb, "") msg["enableOptionalFeature"] = MessageElement( "CN=Partitions," + self.configuration_dn + ":766ddcd8-acd0-445e-f3b9-a7f9b6744f2a", FLAG_MOD_ADD, "enableOptionalFeature") try: - ldb.modify(msg) + self.ldb.modify(msg) except LdbError, (num, _): self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) def undelete_deleted(self, olddn, newdn, samldb): msg = Message() - msg.dn = Dn(ldb, olddn) + msg.dn = Dn(samldb, olddn) msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") - res = samldb.modify(msg, ["show_deleted:1"]) + samldb.modify(msg, ["show_deleted:1"]) def undelete_deleted_with_mod(self, olddn, newdn): msg = Message() - msg.dn = Dn(ldb, olddn) + msg.dn = Dn(self.ldb, olddn) msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") - res = ldb.modify(msg, ["show_deleted:1"]) + self.ldb.modify(msg, ["show_deleted:1"]) def test_undelete(self): print "Testing standard undelete operation" usr1="cn=testuser,cn=users," + self.base_dn delete_force(self.ldb, usr1) - ldb.add({ + self.ldb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) guid1=objLive1["objectGUID"][0] - ldb.delete(usr1) + self.ldb.delete(usr1) objDeleted1 = self.search_guid(guid1) - self.undelete_deleted(str(objDeleted1.dn), usr1, ldb) + self.undelete_deleted(str(objDeleted1.dn), usr1, self.ldb) objLive2 = self.search_dn(usr1) self.assertEqual(str(objLive2.dn).lower(),str(objLive1.dn).lower()) delete_force(self.ldb, usr1) - def test_rename(self): + def __test_rename(self): print "Testing attempt to rename deleted object" usr1="cn=testuser,cn=users," + self.base_dn - ldb.add({ + self.ldb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) guid1=objLive1["objectGUID"][0] - ldb.delete(usr1) + self.ldb.delete(usr1) objDeleted1 = self.search_guid(guid1) #just to make sure we get the correct error if the show deleted is missing try: - ldb.rename(str(objDeleted1.dn), usr1) + self.ldb.rename(str(objDeleted1.dn), usr1) self.fail() except LdbError, (num, _): self.assertEquals(num,ERR_NO_SUCH_OBJECT) try: - ldb.rename(str(objDeleted1.dn), usr1, ["show_deleted:1"]) + self.ldb.rename(str(objDeleted1.dn), usr1, ["show_deleted:1"]) self.fail() except LdbError, (num, _): self.assertEquals(num,ERR_UNWILLING_TO_PERFORM) @@ -465,14 +465,14 @@ def test_rename(self): def test_undelete_with_mod(self): print "Testing standard undelete operation with modification of additional attributes" usr1="cn=testuser,cn=users," + self.base_dn - ldb.add({ + self.ldb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) guid1=objLive1["objectGUID"][0] - ldb.delete(usr1) + self.ldb.delete(usr1) objDeleted1 = self.search_guid(guid1) self.undelete_deleted_with_mod(str(objDeleted1.dn), usr1) objLive2 = self.search_dn(usr1) @@ -484,16 +484,16 @@ def test_undelete_newuser(self): usr1="cn=testuser,cn=users," + self.base_dn usr2="cn=testuser2,cn=users," + self.base_dn delete_force(self.ldb, usr1) - ldb.add({ + self.ldb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) guid1=objLive1["objectGUID"][0] - ldb.delete(usr1) + self.ldb.delete(usr1) objDeleted1 = self.search_guid(guid1) - self.undelete_deleted(str(objDeleted1.dn), usr2, ldb) + self.undelete_deleted(str(objDeleted1.dn), usr2, self.ldb) objLive2 = self.search_dn(usr2) delete_force(self.ldb, usr1) delete_force(self.ldb, usr2) @@ -501,22 +501,22 @@ def test_undelete_newuser(self): def test_undelete_existing(self): print "Testing undelete user after a user with the same dn has been created" usr1="cn=testuser,cn=users," + self.base_dn - ldb.add({ + self.ldb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) guid1=objLive1["objectGUID"][0] - ldb.delete(usr1) - ldb.add({ + self.ldb.delete(usr1) + self.ldb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objDeleted1 = self.search_guid(guid1) try: - self.undelete_deleted(str(objDeleted1.dn), usr1, ldb) + self.undelete_deleted(str(objDeleted1.dn), usr1, self.ldb) self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) @@ -527,35 +527,35 @@ def test_undelete_cross_nc(self): c2 = "cn=ldaptestcontainer2," + self.configuration_dn c3 = "cn=ldaptestcontainer," + self.configuration_dn c4 = "cn=ldaptestcontainer2," + self.base_dn - ldb.add({ + self.ldb.add({ "dn": c1, "objectclass": "container"}) - ldb.add({ + self.ldb.add({ "dn": c2, "objectclass": "container"}) objLive1 = self.search_dn(c1) objLive2 = self.search_dn(c2) guid1=objLive1["objectGUID"][0] guid2=objLive2["objectGUID"][0] - ldb.delete(c1) - ldb.delete(c2) + self.ldb.delete(c1) + self.ldb.delete(c2) objDeleted1 = self.search_guid(guid1) objDeleted2 = self.search_guid(guid2) #try to undelete from base dn to config try: - self.undelete_deleted(str(objDeleted1.dn), c3, ldb) + self.undelete_deleted(str(objDeleted1.dn), c3, self.ldb) self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_OPERATIONS_ERROR) #try to undelete from config to base dn try: - self.undelete_deleted(str(objDeleted2.dn), c4, ldb) + self.undelete_deleted(str(objDeleted2.dn), c4, self.ldb) self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_OPERATIONS_ERROR) #assert undeletion will work in same nc - self.undelete_deleted(str(objDeleted1.dn), c4, ldb) - self.undelete_deleted(str(objDeleted2.dn), c3, ldb) + self.undelete_deleted(str(objDeleted1.dn), c4, self.ldb) + self.undelete_deleted(str(objDeleted2.dn), c3, self.ldb) delete_force(self.ldb, c3) delete_force(self.ldb, c4) From 5a4868a187e5e961e736cece10188f9de74bf039 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 6 Nov 2014 06:38:23 +0100 Subject: [PATCH 73/97] s4-dsdb-test: Restore user test case update - a bit more generic Change-Id: Iebf78244eaca51351d09e973fdd079f167c97ba1 Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 66a82aa786..5208f0e341 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -23,8 +23,6 @@ sys.path.insert(0, "bin/python") import samba -samba.ensure_external_module("testtools", "testtools") -samba.ensure_external_module("subunit", "subunit/python") import samba.tests import samba.getopt as options @@ -105,6 +103,19 @@ def make_class_all_must_list(self, class_name): attr_list |= sub_set return attr_list + @staticmethod + def restore_deleted_object(samdb, del_dn, new_dn): + """Restores a deleted object + :param samdb: SamDB connection to SAM + :param del_dn: str Deleted object DN + :param new_dn: str Where to restore the object + """ + msg = Message() + msg.dn = Dn(samdb, str(del_dn)) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([str(new_dn)], FLAG_MOD_REPLACE, "distinguishedName") + samdb.modify(msg, ["show_deleted:1"]) + def test_restore_user(self): print "Test restored user attributes" username = "restore_user" @@ -113,33 +124,20 @@ def test_restore_user(self): self.samdb.add({ "dn": usr_dn, "objectClass": "user", - "description": "test user description", "sAMAccountName": username}) obj = self.search_dn(usr_dn) guid = obj["objectGUID"][0] self.samdb.delete(usr_dn) obj_del = self.search_guid(guid) - # restore the user - msg = Message() - msg.dn = obj_del.dn - msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") - msg["distinguishedName"] = MessageElement([usr_dn], FLAG_MOD_REPLACE, "distinguishedName") - # add some attributes - # strip off "sAMAccountType" - # for attr in ['dSCorePropagationData', 'primaryGroupID', 'badPwdCount', 'logonCount', 'countryCode', 'pwdLastSet', 'codePage', 'lastLogon', 'adminCount', 'accountExpires', 'operatorCount', 'badPasswordTime', 'lastLogoff']: - # msg[attr] = "0" - self.samdb.modify(msg, ["show_deleted:1"]) - # find restored object and check attributes + # restore the user and fetch what's restored + self.restore_deleted_object(self.samdb, obj_del.dn, usr_dn) obj_restore = self.search_guid(guid) - keys_restored = obj_restore.keys() - restored_user_attr = ['dn', 'objectClass', 'cn', 'distinguishedName', 'instanceType', 'whenCreated', - 'whenChanged', 'uSNCreated', 'uSNChanged', 'name', 'objectGUID', 'userAccountControl', - 'badPwdCount', 'codePage', 'countryCode', 'badPasswordTime', 'lastLogoff', 'lastLogon', - 'pwdLastSet', 'primaryGroupID', 'operatorCount', 'objectSid', 'adminCount', - 'accountExpires', 'logonCount', 'sAMAccountName', 'sAMAccountType', 'lastKnownParent', - 'objectCategory', 'dSCorePropagationData'] - print set(restored_user_attr) - set(keys_restored) - pass + # check original attributes and restored one are same + orig_attrs = set(obj.keys()) + # windows restore more attributes that originally we have + orig_attrs.update(['adminCount', 'operatorCount', 'lastKnownParent']) + rest_attrs = set(obj_restore.keys()) + self.assertSetEqual(orig_attrs, rest_attrs) if __name__ == '__main__': From 813a7a314b1d50d52b5d20526b1c70f04e34e727 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 7 Nov 2014 07:02:51 +0100 Subject: [PATCH 74/97] s4-dsdb-util: Mark attributes with ADD flag in samdb_find_or_add_attribute() At the moment no flags are set and it works fine, since this function is solely used in samldb during ADD requests handling. Pre-setting a flag make it usefull for other modules and request handlers too Change-Id: I7e43dcbe2a8f34e3b0ec16ae2db80ef436df8bfe Signed-off-by: Kamen Mazdrashki --- source4/dsdb/common/util.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c index a892f2da0f..045a0bf8b1 100644 --- a/source4/dsdb/common/util.c +++ b/source4/dsdb/common/util.c @@ -774,6 +774,7 @@ struct ldb_message_element *samdb_find_attribute(struct ldb_context *ldb, int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg, const char *name, const char *set_value) { + int ret; struct ldb_message_element *el; el = ldb_msg_find_element(msg, name); @@ -781,7 +782,12 @@ int samdb_find_or_add_attribute(struct ldb_context *ldb, struct ldb_message *msg return LDB_SUCCESS; } - return ldb_msg_add_string(msg, name, set_value); + ret = ldb_msg_add_string(msg, name, set_value); + if (ret != LDB_SUCCESS) { + return ret; + } + msg->elements[msg->num_elements - 1].flags = LDB_FLAG_MOD_ADD; + return LDB_SUCCESS; } /* From dda27fc7dbc69b9203caf57cfa1a7829164e8db5 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 7 Nov 2014 07:04:30 +0100 Subject: [PATCH 75/97] s4-dsdb/reanimate: Implement attribute_restore function At the moment it works for objects with objectClass user + a common case of removing isRecycled attribute Change-Id: I70b0ef0ef65c13d3def82ca53ace52a85a078a37 Signed-off-by: Kamen Mazdrashki --- .../samdb/ldb_modules/tombstone_reanimate.c | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 89d3a8aa91..6ad1187158 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -50,6 +50,7 @@ #include "param/param.h" #include "../libds/common/flags.h" #include "dsdb/samdb/ldb_modules/util.h" +#include "libds/common/flag_mapping.h" struct tr_context { @@ -211,6 +212,95 @@ static int _tr_do_modify(struct ldb_module *module, struct ldb_request *parent_r return ret; } +static int _tr_restore_attributes(struct ldb_context *ldb, struct ldb_message *cur_msg, struct ldb_message *new_msg) +{ + int ret; + struct ldb_message_element *el; + uint32_t account_type, user_account_control; + + + /* remove isRecycled */ + ret = ldb_msg_add_empty(new_msg, "isRecycled", LDB_FLAG_MOD_DELETE, NULL); + if (ret != LDB_SUCCESS) { + ldb_asprintf_errstring(ldb, "Failed to reset isRecycled attribute: %s", ldb_strerror(ret)); + return LDB_ERR_OPERATIONS_ERROR; + } + + /* objectClass is USER */ + if (samdb_find_attribute(ldb, cur_msg, "objectclass", "user") != NULL) { + /* restoring 'user' instance attribute is heavily borrowed from samldb.c */ + + /* Default values */ + ret = samdb_find_or_add_attribute(ldb, new_msg, + "accountExpires", "9223372036854775807"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "badPasswordTime", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "badPwdCount", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "codePage", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "countryCode", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "lastLogoff", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "lastLogon", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "logonCount", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "pwdLastSet", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "adminCount", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "operatorCount", "0"); + if (ret != LDB_SUCCESS) return ret; + + /* restore "sAMAccountType" */ + user_account_control = ldb_msg_find_attr_as_uint(cur_msg, "userAccountControl", (uint32_t)-1); + if (user_account_control == (uint32_t)-1) { + return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, + "reanimate: No 'userAccountControl' attribute found!"); + } + account_type = ds_uf2atype(user_account_control); + if (account_type == 0) { + ldb_set_errstring(ldb, "reanimate: Unrecognized account type!"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } + ret = samdb_msg_add_uint(ldb, new_msg, new_msg, "sAMAccountType", account_type); + if (ret != LDB_SUCCESS) { + return ret; + } + el = ldb_msg_find_element(new_msg, "sAMAccountType"); + el->flags = LDB_FLAG_MOD_REPLACE; + + /* "userAccountControl" -> "primaryGroupID" mapping */ + if (!ldb_msg_find_element(new_msg, "primaryGroupID")) { + uint32_t rid = ds_uf2prim_group_rid(user_account_control); + + ret = samdb_msg_add_uint(ldb, new_msg, new_msg, + "primaryGroupID", rid); + if (ret != LDB_SUCCESS) { + return ret; + } + el = ldb_msg_find_element(new_msg, "primaryGroupID"); + el->flags = LDB_FLAG_MOD_REPLACE; + } + + } + + return LDB_SUCCESS; +} + /** * Handle special LDAP modify request to restore deleted objects */ From d8c90c685d9295de5a2642b32d48fc64e6cdce69 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 7 Nov 2014 07:05:56 +0100 Subject: [PATCH 76/97] s4-dsdb/samldb: Fix type "omputer" -> "computer" Change-Id: Ic56c6945528b7f60becc4f0b318429f4c22c3d2e Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/samldb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 7619bbb066..ec47cf5956 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -1528,7 +1528,7 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac) "lockoutTime", 0); old_is_critical = ldb_msg_find_attr_as_bool(res->msgs[0], "isCriticalSystemObject", 0); - /* When we do not have objectclass "omputer" we cannot switch to a (read-only) DC */ + /* When we do not have objectclass "computer" we cannot switch to a (read-only) DC */ el = ldb_msg_find_element(res->msgs[0], "objectClass"); if (el == NULL) { return ldb_operr(ldb); From 7dfc15b54978ed006a98790db8126d5131dcc3b1 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 7 Nov 2014 07:07:07 +0100 Subject: [PATCH 77/97] s4-dsdb/samldb: Skip 'sAMAccountType' and 'primaryGroupID' during Tombstone reanimate tombstone_reanimate.c module is going to restore those attributes and it needs a way to propagate them to DB Change-Id: I36f30b33fa204fd28329eab01044a125f7a3f08e Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/samldb.c | 34 +++++++++++++++++-------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index ec47cf5956..96ffcd4765 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -2444,6 +2444,7 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) struct ldb_context *ldb; struct samldb_ctx *ac; struct ldb_message_element *el, *el2; + struct ldb_control *is_undelete; bool modified = false; int ret; @@ -2454,6 +2455,13 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) ldb = ldb_module_get_ctx(module); + /* + * we are going to need some special handling if in Undelete call. + * Since tombstone_reanimate module will restore certain attributes, + * we need to relax checks for: sAMAccountType, primaryGroupID + */ + is_undelete = ldb_request_get_control(req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID); + /* make sure that "objectSid" is not specified */ el = ldb_msg_find_element(req->op.mod.message, "objectSid"); if (el != NULL) { @@ -2463,12 +2471,14 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) return LDB_ERR_UNWILLING_TO_PERFORM; } } - /* make sure that "sAMAccountType" is not specified */ - el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType"); - if (el != NULL) { - ldb_set_errstring(ldb, - "samldb: sAMAccountType must not be specified!"); - return LDB_ERR_UNWILLING_TO_PERFORM; + if (is_undelete == NULL) { + /* make sure that "sAMAccountType" is not specified */ + el = ldb_msg_find_element(req->op.mod.message, "sAMAccountType"); + if (el != NULL) { + ldb_set_errstring(ldb, + "samldb: sAMAccountType must not be specified!"); + return LDB_ERR_UNWILLING_TO_PERFORM; + } } /* make sure that "isCriticalSystemObject" is not specified */ el = ldb_msg_find_element(req->op.mod.message, "isCriticalSystemObject"); @@ -2512,11 +2522,13 @@ static int samldb_modify(struct ldb_module *module, struct ldb_request *req) return ldb_operr(ldb); } - el = ldb_msg_find_element(ac->msg, "primaryGroupID"); - if (el != NULL) { - ret = samldb_prim_group_trigger(ac); - if (ret != LDB_SUCCESS) { - return ret; + if (is_undelete == NULL) { + el = ldb_msg_find_element(ac->msg, "primaryGroupID"); + if (el != NULL) { + ret = samldb_prim_group_trigger(ac); + if (ret != LDB_SUCCESS) { + return ret; + } } } From 5c372d306a333e695acbcb95f07042939653dce2 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 7 Nov 2014 07:08:29 +0100 Subject: [PATCH 78/97] s4-dsdb/reanimate: Use 'show deleted' control in modify operations too Before committing changes, object is still deleted - isDeleted = true Change-Id: Ie1ab53dc594d1bfaf5b9e06316e7a1fc0dd4b8cb Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 6ad1187158..afd8aca8a1 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -195,6 +195,13 @@ static int _tr_do_modify(struct ldb_module *module, struct ldb_request *parent_r return ret; } + /* We need this since object is 'delete' atm */ + ret = ldb_request_add_control(mod_req, LDB_CONTROL_SHOW_DELETED_OID, false, NULL); + if (ret != LDB_SUCCESS) { + talloc_free(tmp_ctx); + return ret; + } + /* mark request as part of Tombstone reanimation */ ret = ldb_request_add_control(mod_req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID, false, NULL); if (ret != LDB_SUCCESS) { From 99bc8df711aac8aec5b4a911f4e8a3c534f16d54 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 7 Nov 2014 07:11:59 +0100 Subject: [PATCH 79/97] s4-dsdb/reanimate: Swap rename->modify operations to modify->rename sequence This way it is more visible that we work on 'deleted object' during modify and also will help us to handle 'stop rename for deletec objects' propertly in future [MS-ADTS]: 3.1.1.5.3.7.3 Undelete Processing Specifics Change-Id: I9bb644e099a4a2afcb261ad22515c9c4ce4875bb Signed-off-by: Kamen Mazdrashki --- .../samdb/ldb_modules/tombstone_reanimate.c | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index afd8aca8a1..9b0c0d80de 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -352,30 +352,21 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ } /* Simple implementation */ - /* Rename request to modify distinguishedName */ - dn_new = ldb_dn_from_ldb_val(req, ldb, &el_dn->values[0]); - if (dn_new == NULL) { - return ldb_oom(ldb); - } - ret = _tr_do_rename(module, req, req->op.mod.message->dn, dn_new); - if (ret != LDB_SUCCESS) { - ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret)); - if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) { - /* Windows returns Operations Error in case we can't rename the object */ - return LDB_ERR_OPERATIONS_ERROR; - } - return ret; - } /* Modify request to: */ msg = ldb_msg_copy_shallow(ac, req->op.mod.message); if (msg == NULL) { return ldb_module_oom(ac->module); } - msg->dn = dn_new; - /* - delete isDeleted */ + /* - remove distinguishedName - we don't need it */ ldb_msg_remove_attr(msg, "distinguishedName"); + /* restore attributed depending on objectClass */ + ret = _tr_restore_attributes(ldb, res_obj->msgs[0], msg); + if (ret != LDB_SUCCESS) { + return ret; + } + /* - restore objectCategory if not present */ objectcategory = ldb_msg_find_attr_as_dn(ldb, ac, msg, "objectCategory"); @@ -398,6 +389,21 @@ static int tombstone_reanimate_modify(struct ldb_module *module, struct ldb_requ return ret; } + /* Rename request to modify distinguishedName */ + dn_new = ldb_dn_from_ldb_val(req, ldb, &el_dn->values[0]); + if (dn_new == NULL) { + return ldb_oom(ldb); + } + ret = _tr_do_rename(module, req, req->op.mod.message->dn, dn_new); + if (ret != LDB_SUCCESS) { + ldb_debug(ldb, LDB_DEBUG_ERROR, "Renaming object to %s has failed with %s\n", el_dn->values[0].data, ldb_strerror(ret)); + if (ret != LDB_ERR_ENTRY_ALREADY_EXISTS && ret != LDB_ERR_INSUFFICIENT_ACCESS_RIGHTS ) { + /* Windows returns Operations Error in case we can't rename the object */ + return LDB_ERR_OPERATIONS_ERROR; + } + return ret; + } + return ldb_module_done(ac->req, NULL, NULL, LDB_SUCCESS); } From c2d23a7e8a05a39fb7c4a40d240ee91134b55225 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Wed, 12 Nov 2014 13:08:12 +0100 Subject: [PATCH 80/97] s4-dsdb-test: Create separate TestCase for User objects Change-Id: I73f8cb147d6dd528428588a520826d1318a72d9f Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/tombstone_reanimation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 5208f0e341..58b55acc72 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -116,6 +116,10 @@ def restore_deleted_object(samdb, del_dn, new_dn): msg["distinguishedName"] = MessageElement([str(new_dn)], FLAG_MOD_REPLACE, "distinguishedName") samdb.modify(msg, ["show_deleted:1"]) + +class RestoreUserObjectTestCase(RestoredObjectAttributesBaseTestCase): + """Test cases for delete/reanimate user objects""" + def test_restore_user(self): print "Test restored user attributes" username = "restore_user" From 4cd86edf4adc32b55ca1ac2950ee4c95d792ed3b Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 13 Nov 2014 04:06:50 +0100 Subject: [PATCH 81/97] s4-dsdb-test: Test tombstone reanimation for Group objects Change-Id: Ic400c9877455e2a252c696275cab99429e1aef05 Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 58b55acc72..dff0da9a78 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -103,6 +103,31 @@ def make_class_all_must_list(self, class_name): attr_list |= sub_set return attr_list + def assertAttributesEqual(self, obj_orig, attrs_orig, obj_restored, attrs_rest): + self.assertSetEqual(attrs_orig, attrs_rest) + # remove volatile attributes, they can't be equal + attrs_orig -= set(["uSNChanged", "dSCorePropagationData", "whenChanged"]) + for attr in attrs_orig: + # convert original attr value to ldif + orig_val = obj_orig.get(attr) + if orig_val is None: + continue + if not isinstance(orig_val, MessageElement): + orig_val = MessageElement(str(orig_val), 0, attr ) + m = Message() + m.add(orig_val) + orig_ldif = self.samdb.write_ldif(m, 0) + # convert restored attr value to ldif + rest_val = obj_restored.get(attr) + self.assertIsNotNone(rest_val) + m = Message() + if not isinstance(rest_val, MessageElement): + rest_val = MessageElement(str(rest_val), 0, attr) + m.add(rest_val) + rest_ldif = self.samdb.write_ldif(m, 0) + # compare generated ldif's + self.assertEqual(orig_ldif.lower(), rest_ldif.lower()) + @staticmethod def restore_deleted_object(samdb, del_dn, new_dn): """Restores a deleted object @@ -144,5 +169,82 @@ def test_restore_user(self): self.assertSetEqual(orig_attrs, rest_attrs) +class RestoreGroupObjectTestCase(RestoredObjectAttributesBaseTestCase): + """Test different scenarios for delete/reanimate group objects""" + + def _make_object_dn(self, name): + return "cn=%s,cn=users,%s" % (name, self.base_dn) + + def _create_test_user(self, user_name): + user_dn = self._make_object_dn(user_name) + ldif = { + "dn": user_dn, + "objectClass": "user", + "sAMAccountName": user_name, + } + # delete an object if leftover from previous test + samba.tests.delete_force(self.samdb, user_dn) + # finally, create the group + self.samdb.add(ldif) + return self.search_dn(user_dn) + + def _create_test_group(self, group_name, members=None): + group_dn = self._make_object_dn(group_name) + ldif = { + "dn": group_dn, + "objectClass": "group", + "sAMAccountName": group_name, + } + try: + ldif["member"] = [str(usr_dn) for usr_dn in members] + except TypeError: + pass + # delete an object if leftover from previous test + samba.tests.delete_force(self.samdb, group_dn) + # finally, create the group + self.samdb.add(ldif) + return self.search_dn(group_dn) + + def test_plain_group(self): + print "Test restored Group attributes" + # create test group + obj = self._create_test_group("r_group") + guid = obj["objectGUID"][0] + # delete the group + self.samdb.delete(str(obj.dn)) + obj_del = self.search_guid(guid) + # restore the Group and fetch what's restored + self.restore_deleted_object(self.samdb, obj_del.dn, obj.dn) + obj_restore = self.search_guid(guid) + # check original attributes and restored one are same + attr_orig = set(obj.keys()) + # windows restore more attributes that originally we have + attr_orig.update(['adminCount', 'operatorCount', 'lastKnownParent']) + attr_rest = set(obj_restore.keys()) + self.assertAttributesEqual(obj, attr_orig, obj_restore, attr_rest) + + def test_group_with_members(self): + print "Test restored Group with members attributes" + # create test group + usr1 = self._create_test_user("r_user_1") + usr2 = self._create_test_user("r_user_2") + obj = self._create_test_group("r_group", [usr1.dn, usr2.dn]) + guid = obj["objectGUID"][0] + # delete the group + self.samdb.delete(str(obj.dn)) + obj_del = self.search_guid(guid) + # restore the Group and fetch what's restored + self.restore_deleted_object(self.samdb, obj_del.dn, obj.dn) + obj_restore = self.search_guid(guid) + # check original attributes and restored one are same + attr_orig = set(obj.keys()) + # windows restore more attributes that originally we have + attr_orig.update(['adminCount', 'operatorCount', 'lastKnownParent']) + # and does not restore following attributes + attr_orig.remove("member") + attr_rest = set(obj_restore.keys()) + self.assertAttributesEqual(obj, attr_orig, obj_restore, attr_rest) + + if __name__ == '__main__': unittest.main() From a7ea5616d2b74c5ab70d69a651183c15911d0c3c Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 13 Nov 2014 04:10:19 +0100 Subject: [PATCH 82/97] s4-dsdb-test: More generic implementation for attribute list for class hierarchy Change-Id: I61be257568a778de3c4dc3d18d895d81283a0531 Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index dff0da9a78..e457feefbe 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -74,32 +74,31 @@ def search_dn(self, dn): self.assertEquals(len(res), 1) return res[0] - def _class_must_attrs(self, class_name): + def _attributes_for_class(self, class_name, attr_names): res = self.samdb.search(self.schema_dn, expression="(&(objectClass=classSchema)(lDAPDisplayName=%s))" % class_name, - attrs=["subClassOf", "mustContain", "systemMustContain", - "auxiliaryClass", "systemAuxiliaryClass"]) + attrs=["subClassOf", + "auxiliaryClass", "systemAuxiliaryClass"].append(attr_names)) self.assertEqual(len(res), 1) attr_set = set() # make the list from immediate class - if "mustContain" in res[0]: - attr_set |= set([attr for attr in res[0]["mustContain"]]) - if "systemMustContain" in res[0]: - attr_set |= set([attr for attr in res[0]["systemMustContain"]]) + for attr_name in attr_names: + if attr_name in res[0]: + attr_set |= set([attr for attr in res[0][attr_name]]) # now trace Auxiliary classes if "auxiliaryClass" in res[0]: for aux_class in res[0]["auxiliaryClass"]: - attr_set |= self.make_class_all_must_list(aux_class) + attr_set |= self.walk_class_hierarchy(aux_class, attr_names) if "systemAuxiliaryClass" in res[0]: for aux_class in res[0]["systemAuxiliaryClass"]: - attr_set |= self.make_class_all_must_list(aux_class) + attr_set |= self.walk_class_hierarchy(aux_class, attr_names) return str(res[0]["subClassOf"]), attr_set - def make_class_all_must_list(self, class_name): - (parent_class, attr_list) = self._class_must_attrs(class_name) + def walk_class_hierarchy(self, class_name, attr_names): + (parent_class, attr_list) = self._attributes_for_class(class_name, attr_names) sub_set = set() if class_name != "top": - sub_set = self.make_class_all_must_list(parent_class) + sub_set = self.walk_class_hierarchy(parent_class, attr_names) attr_list |= sub_set return attr_list From a4cfa4b3ceb32ce5fe03ebe7e40639a833e1fda9 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 13 Nov 2014 04:11:08 +0100 Subject: [PATCH 83/97] s4-dsdb/reanimate: Group objects reanimation implementation Change-Id: Iea92924ff6b33fa3723b104d5dfff1ce5a7a09b0 Signed-off-by: Kamen Mazdrashki --- .../samdb/ldb_modules/tombstone_reanimate.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c index 9b0c0d80de..1704535968 100644 --- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c +++ b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c @@ -302,7 +302,43 @@ static int _tr_restore_attributes(struct ldb_context *ldb, struct ldb_message *c el = ldb_msg_find_element(new_msg, "primaryGroupID"); el->flags = LDB_FLAG_MOD_REPLACE; } + } + + /* objectClass is GROUP */ + if (samdb_find_attribute(ldb, cur_msg, "objectclass", "group") != NULL) { + /* "groupType" -> "sAMAccountType" */ + uint32_t group_type; + + el = ldb_msg_find_element(cur_msg, "groupType"); + if (el == NULL) { + return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, + "reanimate: Unexpected: missing groupType attribute."); + } + + group_type = ldb_msg_find_attr_as_uint(cur_msg, + "groupType", 0); + + account_type = ds_gtype2atype(group_type); + if (account_type == 0) { + return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM, + "reanimate: Unrecognized account type!"); + } + ret = samdb_msg_add_uint(ldb, new_msg, new_msg, + "sAMAccountType", account_type); + if (ret != LDB_SUCCESS) { + return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR, + "reanimate: Failed to add sAMAccountType to restored object."); + } + el = ldb_msg_find_element(new_msg, "sAMAccountType"); + el->flags = LDB_FLAG_MOD_REPLACE; + /* Default values set by Windows */ + ret = samdb_find_or_add_attribute(ldb, new_msg, + "adminCount", "0"); + if (ret != LDB_SUCCESS) return ret; + ret = samdb_find_or_add_attribute(ldb, new_msg, + "operatorCount", "0"); + if (ret != LDB_SUCCESS) return ret; } return LDB_SUCCESS; From 10097deaf89deee0b531a649a881f2cecefe9447 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sat, 15 Nov 2014 05:00:20 +0100 Subject: [PATCH 84/97] s4-dsdb-test: tombstone reanimation for containers test case At the moment tests "organizationalUnit" and "container" objects Change-Id: I1d39b97064b2b3cda22313c521e7573b14888eca Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index e457feefbe..bd9201ae77 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -74,6 +74,13 @@ def search_dn(self, dn): self.assertEquals(len(res), 1) return res[0] + def _create_object(self, msg): + """:param msg: dict with dn and attributes to create an object from""" + # delete an object if leftover from previous test + samba.tests.delete_force(self.samdb, msg['dn']) + self.samdb.add(msg) + return self.search_dn(msg['dn']) + def _attributes_for_class(self, class_name, attr_names): res = self.samdb.search(self.schema_dn, expression="(&(objectClass=classSchema)(lDAPDisplayName=%s))" % class_name, @@ -245,5 +252,62 @@ def test_group_with_members(self): self.assertAttributesEqual(obj, attr_orig, obj_restore, attr_rest) +class RestoreContainerObjectTestCase(RestoredObjectAttributesBaseTestCase): + """Test different scenarios for delete/reanimate OU/container objects""" + + def _create_test_ou(self, rdn, name=None, description=None): + ou_dn = "OU=%s,%s" % (rdn, self.base_dn) + # delete an object if leftover from previous test + samba.tests.delete_force(self.samdb, ou_dn) + # create ou and return created object + self.samdb.create_ou(ou_dn, name=name, description=description) + return self.search_dn(ou_dn) + + def test_ou_with_name_description(self): + print "Test OU reanimation" + # create OU to test with + obj = self._create_test_ou(rdn="r_ou", + name="r_ou name", + description="r_ou description") + guid = obj["objectGUID"][0] + # delete the object + self.samdb.delete(str(obj.dn)) + obj_del = self.search_guid(guid) + # restore the Object and fetch what's restored + self.restore_deleted_object(self.samdb, obj_del.dn, obj.dn) + obj_restore = self.search_guid(guid) + # check original attributes and restored one are same + attr_orig = set(obj.keys()) + attr_rest = set(obj_restore.keys()) + # windows restore more attributes that originally we have + attr_orig.update(["lastKnownParent"]) + # and does not restore following attributes + attr_orig -= {"description"} + self.assertAttributesEqual(obj, attr_orig, obj_restore, attr_rest) + + def test_container(self): + print "Test Container reanimation" + # create test Container + obj = self._create_object({ + "dn": "CN=r_container,CN=Users,%s" % self.base_dn, + "objectClass": "container" + }) + guid = obj["objectGUID"][0] + # delete the object + self.samdb.delete(str(obj.dn)) + obj_del = self.search_guid(guid) + # restore the Object and fetch what's restored + self.restore_deleted_object(self.samdb, obj_del.dn, obj.dn) + obj_restore = self.search_guid(guid) + # check original attributes and restored one are same + attr_orig = set(obj.keys()) + attr_rest = set(obj_restore.keys()) + # windows restore more attributes that originally we have + attr_orig.update(["lastKnownParent"]) + # and does not restore following attributes + attr_orig -= {"showInAdvancedViewOnly"} + self.assertAttributesEqual(obj, attr_orig, obj_restore, attr_rest) + + if __name__ == '__main__': unittest.main() From 82e73c84c4cbdbe3b2a2d55a0d1a6783fc9cff68 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 16 Nov 2014 03:34:22 +0100 Subject: [PATCH 85/97] s4-dsdb-test: remove trailing ';' in ldap.py Change-Id: I5edc6e017b576791c1575f71a625c49ccc88fe8f Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/ldap.py | 64 +++++++++++++++---------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/source4/dsdb/tests/python/ldap.py b/source4/dsdb/tests/python/ldap.py index 7139f3c814..13e0018a51 100755 --- a/source4/dsdb/tests/python/ldap.py +++ b/source4/dsdb/tests/python/ldap.py @@ -1152,7 +1152,7 @@ def test_rdn_name(self): # what the rDN length constraints are def DISABLED_test_largeRDN(self): """Testing large rDN (limit 64 characters)""" - rdn = "CN=a012345678901234567890123456789012345678901234567890123456789012"; + rdn = "CN=a012345678901234567890123456789012345678901234567890123456789012" delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn)) ldif = """ dn: %s,%s""" % (rdn,self.base_dn) + """ @@ -1161,7 +1161,7 @@ def DISABLED_test_largeRDN(self): self.ldb.add_ldif(ldif) delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn)) - rdn = "CN=a0123456789012345678901234567890123456789012345678901234567890120"; + rdn = "CN=a0123456789012345678901234567890123456789012345678901234567890120" delete_force(self.ldb, "%s,%s" % (rdn, self.base_dn)) try: ldif = """ @@ -1337,20 +1337,20 @@ def test_parentGUID(self): self.ldb.add({ "dn": "cn=parentguidtest,cn=users," + self.base_dn, "objectclass":"user", - "samaccountname":"parentguidtest"}); + "samaccountname":"parentguidtest"}) res1 = ldb.search(base="cn=parentguidtest,cn=users," + self.base_dn, scope=SCOPE_BASE, - attrs=["parentGUID", "samaccountname"]); + attrs=["parentGUID", "samaccountname"]) res2 = ldb.search(base="cn=users," + self.base_dn,scope=SCOPE_BASE, - attrs=["objectGUID"]); + attrs=["objectGUID"]) res3 = ldb.search(base=self.base_dn, scope=SCOPE_BASE, - attrs=["parentGUID"]); + attrs=["parentGUID"]) res4 = ldb.search(base=self.configuration_dn, scope=SCOPE_BASE, - attrs=["parentGUID"]); + attrs=["parentGUID"]) res5 = ldb.search(base=self.schema_dn, scope=SCOPE_BASE, - attrs=["parentGUID"]); + attrs=["parentGUID"]) """Check if the parentGUID is valid """ - self.assertEquals(res1[0]["parentGUID"], res2[0]["objectGUID"]); + self.assertEquals(res1[0]["parentGUID"], res2[0]["objectGUID"]) """Check if it returns nothing when there is no parent object - default NC""" has_parentGUID = False @@ -1358,7 +1358,7 @@ def test_parentGUID(self): if key == "parentGUID": has_parentGUID = True break - self.assertFalse(has_parentGUID); + self.assertFalse(has_parentGUID) """Check if it returns nothing when there is no parent object - configuration NC""" has_parentGUID = False @@ -1366,7 +1366,7 @@ def test_parentGUID(self): if key == "parentGUID": has_parentGUID = True break - self.assertFalse(has_parentGUID); + self.assertFalse(has_parentGUID) """Check if it returns nothing when there is no parent object - schema NC""" has_parentGUID = False @@ -1374,7 +1374,7 @@ def test_parentGUID(self): if key == "parentGUID": has_parentGUID = True break - self.assertFalse(has_parentGUID); + self.assertFalse(has_parentGUID) """Ensures that if you look for another object attribute after the constructed parentGUID, it will return correctly""" @@ -1385,21 +1385,21 @@ def test_parentGUID(self): break self.assertTrue(has_another_attribute) self.assertTrue(len(res1[0]["samaccountname"]) == 1) - self.assertEquals(res1[0]["samaccountname"][0], "parentguidtest"); + self.assertEquals(res1[0]["samaccountname"][0], "parentguidtest") # Testing parentGUID behaviour on rename\ self.ldb.add({ "dn": "cn=testotherusers," + self.base_dn, - "objectclass":"container"}); + "objectclass":"container"}) res1 = ldb.search(base="cn=testotherusers," + self.base_dn,scope=SCOPE_BASE, - attrs=["objectGUID"]); + attrs=["objectGUID"]) ldb.rename("cn=parentguidtest,cn=users," + self.base_dn, - "cn=parentguidtest,cn=testotherusers," + self.base_dn); + "cn=parentguidtest,cn=testotherusers," + self.base_dn) res2 = ldb.search(base="cn=parentguidtest,cn=testotherusers," + self.base_dn, scope=SCOPE_BASE, - attrs=["parentGUID"]); - self.assertEquals(res1[0]["objectGUID"], res2[0]["parentGUID"]); + attrs=["parentGUID"]) + self.assertEquals(res1[0]["objectGUID"], res2[0]["parentGUID"]) delete_force(self.ldb, "cn=parentguidtest,cn=testotherusers," + self.base_dn) delete_force(self.ldb, "cn=testotherusers," + self.base_dn) @@ -1562,10 +1562,10 @@ def test_groupType_int32(self): """Test groupType (int32) behaviour (should appear to be casted to a 32 bit signed integer before comparsion)""" res1 = ldb.search(base=self.base_dn, scope=SCOPE_SUBTREE, - attrs=["groupType"], expression="groupType=2147483653"); + attrs=["groupType"], expression="groupType=2147483653") res2 = ldb.search(base=self.base_dn, scope=SCOPE_SUBTREE, - attrs=["groupType"], expression="groupType=-2147483643"); + attrs=["groupType"], expression="groupType=-2147483643") self.assertEquals(len(res1), len(res2)) @@ -1741,23 +1741,23 @@ def test_all(self): }) # Testing ldb.search for (&(cn=ldaptestcomputer3)(objectClass=user)) - res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))"); + res = ldb.search(self.base_dn, expression="(&(cn=ldaptestcomputer3)(objectClass=user))") self.assertEquals(len(res), 1, "Found only %d for (&(cn=ldaptestcomputer3)(objectClass=user))" % len(res)) - self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn)); - self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3"); - self.assertEquals(res[0]["name"][0], "ldaptestcomputer3"); - self.assertEquals(res[0]["objectClass"][0], "top"); - self.assertEquals(res[0]["objectClass"][1], "person"); - self.assertEquals(res[0]["objectClass"][2], "organizationalPerson"); - self.assertEquals(res[0]["objectClass"][3], "user"); - self.assertEquals(res[0]["objectClass"][4], "computer"); + self.assertEquals(str(res[0].dn), ("CN=ldaptestcomputer3,CN=Computers," + self.base_dn)) + self.assertEquals(res[0]["cn"][0], "ldaptestcomputer3") + self.assertEquals(res[0]["name"][0], "ldaptestcomputer3") + self.assertEquals(res[0]["objectClass"][0], "top") + self.assertEquals(res[0]["objectClass"][1], "person") + self.assertEquals(res[0]["objectClass"][2], "organizationalPerson") + self.assertEquals(res[0]["objectClass"][3], "user") + self.assertEquals(res[0]["objectClass"][4], "computer") self.assertTrue("objectGUID" in res[0]) self.assertTrue("whenCreated" in res[0]) self.assertEquals(res[0]["objectCategory"][0], ("CN=Computer,%s" % ldb.get_schema_basedn())) - self.assertEquals(int(res[0]["primaryGroupID"][0]), 513); - self.assertEquals(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT); - self.assertEquals(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE); + self.assertEquals(int(res[0]["primaryGroupID"][0]), 513) + self.assertEquals(int(res[0]["sAMAccountType"][0]), ATYPE_NORMAL_ACCOUNT) + self.assertEquals(int(res[0]["userAccountControl"][0]), UF_NORMAL_ACCOUNT | UF_PASSWD_NOTREQD | UF_ACCOUNTDISABLE) delete_force(self.ldb, "cn=ldaptestcomputer3,cn=computers," + self.base_dn) From 7fefcc641ec19a7438b1e923dda1d53d1ff6b5ae Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Sun, 16 Nov 2014 03:35:01 +0100 Subject: [PATCH 86/97] s4-dsdb/objectclass: remove duplicated declaration for objectclass_do_add Change-Id: Ib88a45cea64fb661a41ca3b4a3df9dabf509fc6c Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/objectclass.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/objectclass.c b/source4/dsdb/samdb/ldb_modules/objectclass.c index 8c361e9723..bceeda9145 100644 --- a/source4/dsdb/samdb/ldb_modules/objectclass.c +++ b/source4/dsdb/samdb/ldb_modules/objectclass.c @@ -246,8 +246,6 @@ static int fix_dn(struct ldb_context *ldb, } -static int objectclass_do_add(struct oc_context *ac); - static int objectclass_add(struct ldb_module *module, struct ldb_request *req) { struct ldb_context *ldb; From b6a550dc03b7177bb8cf6bc1e15ebdeb97e30b71 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Fri, 21 Nov 2014 19:31:25 +0100 Subject: [PATCH 87/97] s4-dsdb-test: Fix duplicated key in a dictionary in sam.py Change-Id: Ie33d92bd308262d9bfda553d6d5e2cfd98f6d7b3 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/sam.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/tests/python/sam.py b/source4/dsdb/tests/python/sam.py index 31b5a399d6..44295d340b 100755 --- a/source4/dsdb/tests/python/sam.py +++ b/source4/dsdb/tests/python/sam.py @@ -2608,9 +2608,9 @@ def test_sam_description_attribute(self): self.ldb.add({ "dn": "cn=ldaptestgroup,cn=users," + self.base_dn, - "description": "desc2", "objectclass": "group", - "description": "desc1"}) + "description": "desc1" + }) res = ldb.search("cn=ldaptestgroup,cn=users," + self.base_dn, scope=SCOPE_BASE, attrs=["description"]) From 11ef8dca6d416ddd03c2c7b4cda06e889d94148e Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Wed, 26 Nov 2014 06:23:51 +0100 Subject: [PATCH 88/97] s4-dsdb-tests: Move base tests for Tombstone reanimation in tombstone_reanimation module So we have them all in one place. While moving, I have: * inherited from the base class for Tombstone reanimations * replace self.ldb with self.samdb Change-Id: Id3e4f02cc2e0877d736da812c14c91e2311203d2 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/deletetest.py | 173 ----------------- .../tests/python/tombstone_reanimation.py | 180 +++++++++++++++++- 2 files changed, 179 insertions(+), 174 deletions(-) diff --git a/source4/dsdb/tests/python/deletetest.py b/source4/dsdb/tests/python/deletetest.py index 60bb689485..6044c9f7c0 100755 --- a/source4/dsdb/tests/python/deletetest.py +++ b/source4/dsdb/tests/python/deletetest.py @@ -387,179 +387,6 @@ def test_all(self): self.assertFalse("CN=Deleted Objects" in str(objDeleted6.dn)) self.assertFalse("CN=Deleted Objects" in str(objDeleted7.dn)) -class BasicUndeleteTests(BaseDeleteTests): - - def setUp(self): - super(BasicUndeleteTests, self).setUp() - - def enable_recycle_bin(self): - msg = Message() - msg.dn = Dn(self.ldb, "") - msg["enableOptionalFeature"] = MessageElement( - "CN=Partitions," + self.configuration_dn + ":766ddcd8-acd0-445e-f3b9-a7f9b6744f2a", - FLAG_MOD_ADD, "enableOptionalFeature") - try: - self.ldb.modify(msg) - except LdbError, (num, _): - self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - - def undelete_deleted(self, olddn, newdn, samldb): - msg = Message() - msg.dn = Dn(samldb, olddn) - msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") - msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") - samldb.modify(msg, ["show_deleted:1"]) - - def undelete_deleted_with_mod(self, olddn, newdn): - msg = Message() - msg.dn = Dn(self.ldb, olddn) - msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") - msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") - msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") - self.ldb.modify(msg, ["show_deleted:1"]) - - - def test_undelete(self): - print "Testing standard undelete operation" - usr1="cn=testuser,cn=users," + self.base_dn - delete_force(self.ldb, usr1) - self.ldb.add({ - "dn": usr1, - "objectclass": "user", - "description": "test user description", - "samaccountname": "testuser"}) - objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] - self.ldb.delete(usr1) - objDeleted1 = self.search_guid(guid1) - self.undelete_deleted(str(objDeleted1.dn), usr1, self.ldb) - objLive2 = self.search_dn(usr1) - self.assertEqual(str(objLive2.dn).lower(),str(objLive1.dn).lower()) - delete_force(self.ldb, usr1) - - def __test_rename(self): - print "Testing attempt to rename deleted object" - usr1="cn=testuser,cn=users," + self.base_dn - self.ldb.add({ - "dn": usr1, - "objectclass": "user", - "description": "test user description", - "samaccountname": "testuser"}) - objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] - self.ldb.delete(usr1) - objDeleted1 = self.search_guid(guid1) - #just to make sure we get the correct error if the show deleted is missing - try: - self.ldb.rename(str(objDeleted1.dn), usr1) - self.fail() - except LdbError, (num, _): - self.assertEquals(num,ERR_NO_SUCH_OBJECT) - - try: - self.ldb.rename(str(objDeleted1.dn), usr1, ["show_deleted:1"]) - self.fail() - except LdbError, (num, _): - self.assertEquals(num,ERR_UNWILLING_TO_PERFORM) - - def test_undelete_with_mod(self): - print "Testing standard undelete operation with modification of additional attributes" - usr1="cn=testuser,cn=users," + self.base_dn - self.ldb.add({ - "dn": usr1, - "objectclass": "user", - "description": "test user description", - "samaccountname": "testuser"}) - objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] - self.ldb.delete(usr1) - objDeleted1 = self.search_guid(guid1) - self.undelete_deleted_with_mod(str(objDeleted1.dn), usr1) - objLive2 = self.search_dn(usr1) - self.assertEqual(objLive2["url"][0],"www.samba.org") - delete_force(self.ldb, usr1) - - def test_undelete_newuser(self): - print "Testing undelete user with a different dn" - usr1="cn=testuser,cn=users," + self.base_dn - usr2="cn=testuser2,cn=users," + self.base_dn - delete_force(self.ldb, usr1) - self.ldb.add({ - "dn": usr1, - "objectclass": "user", - "description": "test user description", - "samaccountname": "testuser"}) - objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] - self.ldb.delete(usr1) - objDeleted1 = self.search_guid(guid1) - self.undelete_deleted(str(objDeleted1.dn), usr2, self.ldb) - objLive2 = self.search_dn(usr2) - delete_force(self.ldb, usr1) - delete_force(self.ldb, usr2) - - def test_undelete_existing(self): - print "Testing undelete user after a user with the same dn has been created" - usr1="cn=testuser,cn=users," + self.base_dn - self.ldb.add({ - "dn": usr1, - "objectclass": "user", - "description": "test user description", - "samaccountname": "testuser"}) - objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] - self.ldb.delete(usr1) - self.ldb.add({ - "dn": usr1, - "objectclass": "user", - "description": "test user description", - "samaccountname": "testuser"}) - objDeleted1 = self.search_guid(guid1) - try: - self.undelete_deleted(str(objDeleted1.dn), usr1, self.ldb) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) - - def test_undelete_cross_nc(self): - print "Cross NC undelete" - c1 = "cn=ldaptestcontainer," + self.base_dn - c2 = "cn=ldaptestcontainer2," + self.configuration_dn - c3 = "cn=ldaptestcontainer," + self.configuration_dn - c4 = "cn=ldaptestcontainer2," + self.base_dn - self.ldb.add({ - "dn": c1, - "objectclass": "container"}) - self.ldb.add({ - "dn": c2, - "objectclass": "container"}) - objLive1 = self.search_dn(c1) - objLive2 = self.search_dn(c2) - guid1=objLive1["objectGUID"][0] - guid2=objLive2["objectGUID"][0] - self.ldb.delete(c1) - self.ldb.delete(c2) - objDeleted1 = self.search_guid(guid1) - objDeleted2 = self.search_guid(guid2) - #try to undelete from base dn to config - try: - self.undelete_deleted(str(objDeleted1.dn), c3, self.ldb) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_OPERATIONS_ERROR) - #try to undelete from config to base dn - try: - self.undelete_deleted(str(objDeleted2.dn), c4, self.ldb) - self.fail() - except LdbError, (num, _): - self.assertEquals(num, ERR_OPERATIONS_ERROR) - #assert undeletion will work in same nc - self.undelete_deleted(str(objDeleted1.dn), c4, self.ldb) - self.undelete_deleted(str(objDeleted2.dn), c3, self.ldb) - delete_force(self.ldb, c3) - delete_force(self.ldb, c4) - - if not "://" in host: if os.path.isfile(host): diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index bd9201ae77..49028b9b6e 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -3,6 +3,7 @@ # Tombstone reanimation tests # # Copyright (C) Kamen Mazdrashki 2014 +# Copyright (C) Nadezhda Ivanova 2014 # # 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 @@ -26,7 +27,10 @@ import samba.tests import samba.getopt as options -from ldb import (SCOPE_BASE, FLAG_MOD_DELETE, FLAG_MOD_REPLACE, Dn, Message, MessageElement) +from ldb import (SCOPE_BASE, FLAG_MOD_ADD, FLAG_MOD_DELETE, FLAG_MOD_REPLACE, Dn, Message, + MessageElement, LdbError, + ERR_ATTRIBUTE_OR_VALUE_EXISTS, ERR_NO_SUCH_OBJECT, ERR_ENTRY_ALREADY_EXISTS, + ERR_OPERATIONS_ERROR, ERR_UNWILLING_TO_PERFORM) class RestoredObjectAttributesBaseTestCase(samba.tests.TestCase): @@ -41,6 +45,7 @@ def setUp(self): self.samdb = samba.tests.connect_samdb_env("DC_SERVER", "DC_USERNAME", "DC_PASSWORD", lp=lp) self.base_dn = self.samdb.domain_dn() self.schema_dn = self.samdb.get_schema_basedn().get_linearized() + self.configuration_dn = self.samdb.get_config_basedn().get_linearized() # Get the old "dSHeuristics" if it was set self.dsheuristics = self.samdb.get_dsheuristics() # Set the "dSHeuristics" to activate the correct "userPassword" behaviour @@ -148,6 +153,179 @@ def restore_deleted_object(samdb, del_dn, new_dn): samdb.modify(msg, ["show_deleted:1"]) +class BaseRestoreObjectTestCase(RestoredObjectAttributesBaseTestCase): + + def setUp(self): + super(BaseRestoreObjectTestCase, self).setUp() + + def enable_recycle_bin(self): + msg = Message() + msg.dn = Dn(self.samdb, "") + msg["enableOptionalFeature"] = MessageElement( + "CN=Partitions," + self.configuration_dn + ":766ddcd8-acd0-445e-f3b9-a7f9b6744f2a", + FLAG_MOD_ADD, "enableOptionalFeature") + try: + self.samdb.modify(msg) + except LdbError, (num, _): + self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) + + def undelete_deleted(self, olddn, newdn, samldb): + msg = Message() + msg.dn = Dn(samldb, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + samldb.modify(msg, ["show_deleted:1"]) + + def undelete_deleted_with_mod(self, olddn, newdn): + msg = Message() + msg.dn = Dn(self.samdb, olddn) + msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") + msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") + msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") + self.samdb.modify(msg, ["show_deleted:1"]) + + + def test_undelete(self): + print "Testing standard undelete operation" + usr1="cn=testuser,cn=users," + self.base_dn + samba.tests.delete_force(self.samdb, usr1) + self.samdb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + self.samdb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + self.undelete_deleted(str(objDeleted1.dn), usr1, self.samdb) + objLive2 = self.search_dn(usr1) + self.assertEqual(str(objLive2.dn).lower(),str(objLive1.dn).lower()) + samba.tests.delete_force(self.samdb, usr1) + + def test_rename(self): + print "Testing attempt to rename deleted object" + usr1="cn=testuser,cn=users," + self.base_dn + self.samdb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + self.samdb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + #just to make sure we get the correct error if the show deleted is missing + try: + self.samdb.rename(str(objDeleted1.dn), usr1) + self.fail() + except LdbError, (num, _): + self.assertEquals(num,ERR_NO_SUCH_OBJECT) + + try: + self.samdb.rename(str(objDeleted1.dn), usr1, ["show_deleted:1"]) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_UNWILLING_TO_PERFORM) + + def test_undelete_with_mod(self): + print "Testing standard undelete operation with modification of additional attributes" + usr1="cn=testuser,cn=users," + self.base_dn + self.samdb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + self.samdb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + self.undelete_deleted_with_mod(str(objDeleted1.dn), usr1) + objLive2 = self.search_dn(usr1) + self.assertEqual(objLive2["url"][0],"www.samba.org") + samba.tests.delete_force(self.samdb, usr1) + + def test_undelete_newuser(self): + print "Testing undelete user with a different dn" + usr1="cn=testuser,cn=users," + self.base_dn + usr2="cn=testuser2,cn=users," + self.base_dn + samba.tests.delete_force(self.samdb, usr1) + self.samdb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + self.samdb.delete(usr1) + objDeleted1 = self.search_guid(guid1) + self.undelete_deleted(str(objDeleted1.dn), usr2, self.samdb) + objLive2 = self.search_dn(usr2) + samba.tests.delete_force(self.samdb, usr1) + samba.tests.delete_force(self.samdb, usr2) + + def test_undelete_existing(self): + print "Testing undelete user after a user with the same dn has been created" + usr1="cn=testuser,cn=users," + self.base_dn + self.samdb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objLive1 = self.search_dn(usr1) + guid1=objLive1["objectGUID"][0] + self.samdb.delete(usr1) + self.samdb.add({ + "dn": usr1, + "objectclass": "user", + "description": "test user description", + "samaccountname": "testuser"}) + objDeleted1 = self.search_guid(guid1) + try: + self.undelete_deleted(str(objDeleted1.dn), usr1, self.samdb) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) + + def test_undelete_cross_nc(self): + print "Cross NC undelete" + c1 = "cn=ldaptestcontainer," + self.base_dn + c2 = "cn=ldaptestcontainer2," + self.configuration_dn + c3 = "cn=ldaptestcontainer," + self.configuration_dn + c4 = "cn=ldaptestcontainer2," + self.base_dn + self.samdb.add({ + "dn": c1, + "objectclass": "container"}) + self.samdb.add({ + "dn": c2, + "objectclass": "container"}) + objLive1 = self.search_dn(c1) + objLive2 = self.search_dn(c2) + guid1=objLive1["objectGUID"][0] + guid2=objLive2["objectGUID"][0] + self.samdb.delete(c1) + self.samdb.delete(c2) + objDeleted1 = self.search_guid(guid1) + objDeleted2 = self.search_guid(guid2) + #try to undelete from base dn to config + try: + self.undelete_deleted(str(objDeleted1.dn), c3, self.samdb) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_OPERATIONS_ERROR) + #try to undelete from config to base dn + try: + self.undelete_deleted(str(objDeleted2.dn), c4, self.samdb) + self.fail() + except LdbError, (num, _): + self.assertEquals(num, ERR_OPERATIONS_ERROR) + #assert undeletion will work in same nc + self.undelete_deleted(str(objDeleted1.dn), c4, self.samdb) + self.undelete_deleted(str(objDeleted2.dn), c3, self.samdb) + samba.tests.delete_force(self.samdb, c3) + samba.tests.delete_force(self.samdb, c4) + + class RestoreUserObjectTestCase(RestoredObjectAttributesBaseTestCase): """Test cases for delete/reanimate user objects""" From 4f0afd9ea28ae04abfa4927ce9cdce7eaae7b7db Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Wed, 26 Nov 2014 06:59:09 +0100 Subject: [PATCH 89/97] s4-dsdb-test/reanimate: Fix whitespaces according to PEP8 Change-Id: I7b46992c80178d40a0531b5afd71a7783068a9dd Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 49028b9b6e..ec00fc7b7b 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -154,7 +154,6 @@ def restore_deleted_object(samdb, del_dn, new_dn): class BaseRestoreObjectTestCase(RestoredObjectAttributesBaseTestCase): - def setUp(self): super(BaseRestoreObjectTestCase, self).setUp() @@ -162,7 +161,7 @@ def enable_recycle_bin(self): msg = Message() msg.dn = Dn(self.samdb, "") msg["enableOptionalFeature"] = MessageElement( - "CN=Partitions," + self.configuration_dn + ":766ddcd8-acd0-445e-f3b9-a7f9b6744f2a", + "CN=Partitions," + self.configuration_dn + ":766ddcd8-acd0-445e-f3b9-a7f9b6744f2a", FLAG_MOD_ADD, "enableOptionalFeature") try: self.samdb.modify(msg) @@ -187,7 +186,7 @@ def undelete_deleted_with_mod(self, olddn, newdn): def test_undelete(self): print "Testing standard undelete operation" - usr1="cn=testuser,cn=users," + self.base_dn + usr1 = "cn=testuser,cn=users," + self.base_dn samba.tests.delete_force(self.samdb, usr1) self.samdb.add({ "dn": usr1, @@ -195,32 +194,32 @@ def test_undelete(self): "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] + guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) self.undelete_deleted(str(objDeleted1.dn), usr1, self.samdb) objLive2 = self.search_dn(usr1) - self.assertEqual(str(objLive2.dn).lower(),str(objLive1.dn).lower()) + self.assertEqual(str(objLive2.dn).lower(), str(objLive1.dn).lower()) samba.tests.delete_force(self.samdb, usr1) def test_rename(self): print "Testing attempt to rename deleted object" - usr1="cn=testuser,cn=users," + self.base_dn + usr1 = "cn=testuser,cn=users," + self.base_dn self.samdb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] + guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) - #just to make sure we get the correct error if the show deleted is missing + # just to make sure we get the correct error if the show deleted is missing try: self.samdb.rename(str(objDeleted1.dn), usr1) self.fail() except LdbError, (num, _): - self.assertEquals(num,ERR_NO_SUCH_OBJECT) + self.assertEquals(num, ERR_NO_SUCH_OBJECT) try: self.samdb.rename(str(objDeleted1.dn), usr1, ["show_deleted:1"]) @@ -230,25 +229,25 @@ def test_rename(self): def test_undelete_with_mod(self): print "Testing standard undelete operation with modification of additional attributes" - usr1="cn=testuser,cn=users," + self.base_dn + usr1 = "cn=testuser,cn=users," + self.base_dn self.samdb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] + guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) self.undelete_deleted_with_mod(str(objDeleted1.dn), usr1) objLive2 = self.search_dn(usr1) - self.assertEqual(objLive2["url"][0],"www.samba.org") + self.assertEqual(objLive2["url"][0], "www.samba.org") samba.tests.delete_force(self.samdb, usr1) def test_undelete_newuser(self): print "Testing undelete user with a different dn" - usr1="cn=testuser,cn=users," + self.base_dn - usr2="cn=testuser2,cn=users," + self.base_dn + usr1 = "cn=testuser,cn=users," + self.base_dn + usr2 = "cn=testuser2,cn=users," + self.base_dn samba.tests.delete_force(self.samdb, usr1) self.samdb.add({ "dn": usr1, @@ -256,7 +255,7 @@ def test_undelete_newuser(self): "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] + guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) self.undelete_deleted(str(objDeleted1.dn), usr2, self.samdb) @@ -266,14 +265,14 @@ def test_undelete_newuser(self): def test_undelete_existing(self): print "Testing undelete user after a user with the same dn has been created" - usr1="cn=testuser,cn=users," + self.base_dn + usr1 = "cn=testuser,cn=users," + self.base_dn self.samdb.add({ "dn": usr1, "objectclass": "user", "description": "test user description", "samaccountname": "testuser"}) objLive1 = self.search_dn(usr1) - guid1=objLive1["objectGUID"][0] + guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) self.samdb.add({ "dn": usr1, @@ -301,13 +300,13 @@ def test_undelete_cross_nc(self): "objectclass": "container"}) objLive1 = self.search_dn(c1) objLive2 = self.search_dn(c2) - guid1=objLive1["objectGUID"][0] - guid2=objLive2["objectGUID"][0] + guid1 = objLive1["objectGUID"][0] + guid2 = objLive2["objectGUID"][0] self.samdb.delete(c1) self.samdb.delete(c2) objDeleted1 = self.search_guid(guid1) objDeleted2 = self.search_guid(guid2) - #try to undelete from base dn to config + # try to undelete from base dn to config try: self.undelete_deleted(str(objDeleted1.dn), c3, self.samdb) self.fail() From 92eb9878670ba294d5d2152fc71415a97b92a7d2 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Wed, 26 Nov 2014 21:53:53 +0100 Subject: [PATCH 90/97] s4-dsdb/samdb: Don't relax contraint checking during rename for Deleted objects Now we have a module to handle to handle Tombstone reanimation and it is better we do all the check here as usual Change-Id: Ia5d28d64e99f7a961cfe8b9aa7cc96e4ca56192e Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/samldb.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 96ffcd4765..a047792fa7 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -2736,9 +2736,6 @@ static int check_rename_constraints(struct ldb_message *msg, if (ldb_request_get_control(ac->req, LDB_CONTROL_RELAX_OID) != NULL) { return LDB_SUCCESS; } - if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) { - return LDB_SUCCESS; - } /* Objects under CN=System */ From a8aebe24e5168c7988a282d304e33b38556f90e2 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 27 Nov 2014 05:15:58 +0100 Subject: [PATCH 91/97] s4-dsdb/samldb: Relax a bit restrictions in Config partition while restoring deleted object Change-Id: Iead460d24058b160b46cf3ddedaf4d84b844da4d Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/samldb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index a047792fa7..99815a5cfd 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -2832,7 +2832,8 @@ static int check_rename_constraints(struct ldb_message *msg, talloc_free(dn2); } - if (!limited_move) { + if (!limited_move + && ldb_request_get_control(ac->req, DSDB_CONTROL_RESTORE_TOMBSTONE_OID) == NULL) { ldb_asprintf_errstring(ldb, "subtree_rename: Cannot move %s to %s in config partition", ldb_dn_get_linearized(olddn), ldb_dn_get_linearized(newdn)); From 452c972862adb4b82c1556bbc55527b7fa4051e8 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 27 Nov 2014 05:20:22 +0100 Subject: [PATCH 92/97] s4-dsdb/test: Delete any leftover objects in the beginning of Cross-NC test This way we ensure that samdb is clean before we make the test Change-Id: I3c6fc94763807394e52b6df41548e9aba8b452c1 Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/tombstone_reanimation.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index ec00fc7b7b..3bcde1f3f8 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -292,6 +292,10 @@ def test_undelete_cross_nc(self): c2 = "cn=ldaptestcontainer2," + self.configuration_dn c3 = "cn=ldaptestcontainer," + self.configuration_dn c4 = "cn=ldaptestcontainer2," + self.base_dn + samba.tests.delete_force(self.samdb, c1) + samba.tests.delete_force(self.samdb, c2) + samba.tests.delete_force(self.samdb, c3) + samba.tests.delete_force(self.samdb, c4) self.samdb.add({ "dn": c1, "objectclass": "container"}) @@ -321,8 +325,6 @@ def test_undelete_cross_nc(self): #assert undeletion will work in same nc self.undelete_deleted(str(objDeleted1.dn), c4, self.samdb) self.undelete_deleted(str(objDeleted2.dn), c3, self.samdb) - samba.tests.delete_force(self.samdb, c3) - samba.tests.delete_force(self.samdb, c4) class RestoreUserObjectTestCase(RestoredObjectAttributesBaseTestCase): From ca84796179487f5087db390365cab1c9d1ee3711 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 27 Nov 2014 06:20:33 +0100 Subject: [PATCH 93/97] s4-dsdb/samldb: Don't allow rename requests on Deleted object Windows behavior in case of renaming Deleted object is: * return ERR_NO_SUCH_OBJECT in case client is not providing SHOW_DELETED control * ERR_UNWILLING_TO_PERFORM otherwise Renaming of Deleted objects is allowed only through special Tombstone reanimation modify request Change-Id: I1eb33fc294a5de44917f6037988ea6362e6e21fc Signed-off-by: Kamen Mazdrashki --- source4/dsdb/samdb/ldb_modules/samldb.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c index 99815a5cfd..c4b36ec54e 100644 --- a/source4/dsdb/samdb/ldb_modules/samldb.c +++ b/source4/dsdb/samdb/ldb_modules/samldb.c @@ -2737,6 +2737,17 @@ static int check_rename_constraints(struct ldb_message *msg, return LDB_SUCCESS; } + if (ldb_msg_find_attr_as_bool(msg, "isDeleted", false)) { + /* + * check originating request if we are supposed + * to "see" this record in first place. + */ + if (ldb_request_get_control(ac->req, LDB_CONTROL_SHOW_DELETED_OID) == NULL) { + return LDB_ERR_NO_SUCH_OBJECT; + } + return LDB_ERR_UNWILLING_TO_PERFORM; + } + /* Objects under CN=System */ dn1 = ldb_dn_copy(ac, ldb_get_default_basedn(ldb)); From 538a7120a1cebad0c47e4120abd7e0e94fa2a820 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Thu, 27 Nov 2014 17:49:15 +0100 Subject: [PATCH 94/97] s4-dsdb-test: Use common base method for restoring Deleted objects Change-Id: I266b58ced814cf7ea3616862506df5b55f4f1d8c Signed-off-by: Kamen Mazdrashki --- .../tests/python/tombstone_reanimation.py | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 3bcde1f3f8..2cfb669b42 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -140,16 +140,21 @@ def assertAttributesEqual(self, obj_orig, attrs_orig, obj_restored, attrs_rest): self.assertEqual(orig_ldif.lower(), rest_ldif.lower()) @staticmethod - def restore_deleted_object(samdb, del_dn, new_dn): + def restore_deleted_object(samdb, del_dn, new_dn, new_attrs=None): """Restores a deleted object :param samdb: SamDB connection to SAM :param del_dn: str Deleted object DN :param new_dn: str Where to restore the object + :param new_attrs: dict Additional attributes to set """ msg = Message() msg.dn = Dn(samdb, str(del_dn)) msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") msg["distinguishedName"] = MessageElement([str(new_dn)], FLAG_MOD_REPLACE, "distinguishedName") + if new_attrs is not None: + assert isinstance(new_attrs, dict) + for attr in new_attrs: + msg[attr] = MessageElement(new_attrs[attr], FLAG_MOD_REPLACE, attr) samdb.modify(msg, ["show_deleted:1"]) @@ -168,22 +173,6 @@ def enable_recycle_bin(self): except LdbError, (num, _): self.assertEquals(num, ERR_ATTRIBUTE_OR_VALUE_EXISTS) - def undelete_deleted(self, olddn, newdn, samldb): - msg = Message() - msg.dn = Dn(samldb, olddn) - msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") - msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") - samldb.modify(msg, ["show_deleted:1"]) - - def undelete_deleted_with_mod(self, olddn, newdn): - msg = Message() - msg.dn = Dn(self.samdb, olddn) - msg["isDeleted"] = MessageElement([], FLAG_MOD_DELETE, "isDeleted") - msg["distinguishedName"] = MessageElement([newdn], FLAG_MOD_REPLACE, "distinguishedName") - msg["url"] = MessageElement(["www.samba.org"], FLAG_MOD_REPLACE, "url") - self.samdb.modify(msg, ["show_deleted:1"]) - - def test_undelete(self): print "Testing standard undelete operation" usr1 = "cn=testuser,cn=users," + self.base_dn @@ -197,7 +186,7 @@ def test_undelete(self): guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) - self.undelete_deleted(str(objDeleted1.dn), usr1, self.samdb) + self.restore_deleted_object(self.samdb, objDeleted1.dn, usr1) objLive2 = self.search_dn(usr1) self.assertEqual(str(objLive2.dn).lower(), str(objLive1.dn).lower()) samba.tests.delete_force(self.samdb, usr1) @@ -239,7 +228,7 @@ def test_undelete_with_mod(self): guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) - self.undelete_deleted_with_mod(str(objDeleted1.dn), usr1) + self.restore_deleted_object(self.samdb, objDeleted1.dn, usr1, {"url": "www.samba.org"}) objLive2 = self.search_dn(usr1) self.assertEqual(objLive2["url"][0], "www.samba.org") samba.tests.delete_force(self.samdb, usr1) @@ -258,7 +247,7 @@ def test_undelete_newuser(self): guid1 = objLive1["objectGUID"][0] self.samdb.delete(usr1) objDeleted1 = self.search_guid(guid1) - self.undelete_deleted(str(objDeleted1.dn), usr2, self.samdb) + self.restore_deleted_object(self.samdb, objDeleted1.dn, usr2) objLive2 = self.search_dn(usr2) samba.tests.delete_force(self.samdb, usr1) samba.tests.delete_force(self.samdb, usr2) @@ -281,7 +270,7 @@ def test_undelete_existing(self): "samaccountname": "testuser"}) objDeleted1 = self.search_guid(guid1) try: - self.undelete_deleted(str(objDeleted1.dn), usr1, self.samdb) + self.restore_deleted_object(self.samdb, objDeleted1.dn, usr1) self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_ENTRY_ALREADY_EXISTS) @@ -312,19 +301,19 @@ def test_undelete_cross_nc(self): objDeleted2 = self.search_guid(guid2) # try to undelete from base dn to config try: - self.undelete_deleted(str(objDeleted1.dn), c3, self.samdb) + self.restore_deleted_object(self.samdb, objDeleted1.dn, c3) self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_OPERATIONS_ERROR) #try to undelete from config to base dn try: - self.undelete_deleted(str(objDeleted2.dn), c4, self.samdb) + self.restore_deleted_object(self.samdb, objDeleted2.dn, c4) self.fail() except LdbError, (num, _): self.assertEquals(num, ERR_OPERATIONS_ERROR) #assert undeletion will work in same nc - self.undelete_deleted(str(objDeleted1.dn), c4, self.samdb) - self.undelete_deleted(str(objDeleted2.dn), c3, self.samdb) + self.restore_deleted_object(self.samdb, objDeleted1.dn, c4) + self.restore_deleted_object(self.samdb, objDeleted2.dn, c3) class RestoreUserObjectTestCase(RestoredObjectAttributesBaseTestCase): From 5118bea39697d983cd7795d32bdb9176a4291826 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 2 Dec 2014 03:49:29 +0100 Subject: [PATCH 95/97] s4-test: Fix a check for missing LoadParm argument in connect_samdb_env Change-Id: I87b5cc4e2b26b04d99de100a91c83cbe74e3c218 Signed-off-by: Kamen Mazdrashki --- python/samba/tests/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/samba/tests/__init__.py b/python/samba/tests/__init__.py index dbd07c0a77..db0921597c 100644 --- a/python/samba/tests/__init__.py +++ b/python/samba/tests/__init__.py @@ -246,7 +246,7 @@ def connect_samdb_env(env_url, env_username, env_password, lp=None): """ samdb_url = env_get_var_value(env_url) creds = credentials.Credentials() - if lp: + if lp is not None: creds.guess(lp) creds.set_username(env_get_var_value(env_username)) creds.set_password(env_get_var_value(env_password)) From 98158752a5f2a0bcf0bc2d74d57d7f323ace9f54 Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 2 Dec 2014 05:27:52 +0100 Subject: [PATCH 96/97] TODO s4-test/tombstone_reanimation: Use LoadParm directly for default settings No need to do anything fancy - we are going to provide user/pass anyway TODO: Why do we need this? --abartlet Signed-off-by: Kamen Mazdrashki --- source4/dsdb/tests/python/tombstone_reanimation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source4/dsdb/tests/python/tombstone_reanimation.py b/source4/dsdb/tests/python/tombstone_reanimation.py index 2cfb669b42..d7a9ceab65 100644 --- a/source4/dsdb/tests/python/tombstone_reanimation.py +++ b/source4/dsdb/tests/python/tombstone_reanimation.py @@ -24,6 +24,7 @@ sys.path.insert(0, "bin/python") import samba +from samba.param import LoadParm import samba.tests import samba.getopt as options @@ -41,7 +42,8 @@ class RestoredObjectAttributesBaseTestCase(samba.tests.TestCase): def setUp(self): super(RestoredObjectAttributesBaseTestCase, self).setUp() # load LoadParm - lp = options.SambaOptions(optparse.OptionParser()).get_loadparm() + # lp = self.get_loadparm(True) + lp = LoadParm() self.samdb = samba.tests.connect_samdb_env("DC_SERVER", "DC_USERNAME", "DC_PASSWORD", lp=lp) self.base_dn = self.samdb.domain_dn() self.schema_dn = self.samdb.get_schema_basedn().get_linearized() From 11abf88d075da3b36004fc7f0633fbd4661f921e Mon Sep 17 00:00:00 2001 From: Kamen Mazdrashki Date: Tue, 2 Dec 2014 05:38:23 +0100 Subject: [PATCH 97/97] TODO s4-tests: Add tombstone_reanimation test case to s4 test suite TODO: I don't like the $USERNAME becoming $DC_USERNAME in the test. This will just cause confusion --abartlet Change-Id: I84ff628496bfa3e0538011400328585d080f21b8 Signed-off-by: Kamen Mazdrashki --- source4/selftest/tests.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py index 7362b64c23..c8ab98c3a1 100755 --- a/source4/selftest/tests.py +++ b/source4/selftest/tests.py @@ -483,6 +483,11 @@ def planoldpythontestsuite(env, module, name=None, extra_path=[], environ={}, ex # therefore skip it in that configuration plantestsuite_loadlist("samba4.ldap.passwords.python(%s)" % env, env, [python, os.path.join(samba4srcdir, "dsdb/tests/python/passwords.py"), "$SERVER", '-U"$USERNAME%$PASSWORD"', "-W$DOMAIN", '$LOADLIST', '$LISTOPT']) plantestsuite_loadlist("samba4.ldap.password_lockout.python(%s)" % env, env, [python, os.path.join(samba4srcdir, "dsdb/tests/python/password_lockout.py"), "$SERVER", '-U"$USERNAME%$PASSWORD"', "-W$DOMAIN", "--realm=$REALM", '$LOADLIST', '$LISTOPT']) + planoldpythontestsuite(env, "tombstone_reanimation", + name="samba4.tombstone_reanimation.python", + environ={'DC_SERVER': '$SERVER', 'DC_USERNAME': '$USERNAME', 'DC_PASSWORD': '$PASSWORD'}, + extra_path=[os.path.join(samba4srcdir, 'dsdb/tests/python')] + ) planpythontestsuite("dc:local", "samba.tests.upgradeprovisionneeddc") planpythontestsuite("plugin_s4_dc:local", "samba.tests.posixacl")